[SalesForce] Visualforce Input Fields not being passed to Controller

We're running into an issue where values being entered into a Visualforce page are not being passed at all to the controller for the save actions that take place.

I'm sure this is something simple that I'm missing but can't figure out what the underlying issue is.

We use the same page for editing and creating a new record (Account). Strangely when going through the new record process the values are passed to the controller as expected. Also, when editing an existing record, the fields are correctly populated with the existing data.

The goal of the page / code is to hold a Managed package component (PCA) so we can perform lookups for the account address fields although I've removed them from this example as their inclusion doesn't affect the issue we're facing. We have an approval process for accounts so when we save we want the code to submit the approval request as well although I've removed this section in previous tests of the code to make sure we're not redirecting when an error is showing.

API Versions:
AccountCreation.page = 43.0
AccountCreationController.cls = 43.0

Looking online the only I have found some common occurrences but none of these happen in this code:
1. The exclusion of an apex:actionregion on the Visualforce page but I have defined these in the markup.
2. The overwriting of the variable in the controller. I can't see this being the case as we debug the variable as soon as it hits the controller when the "Save & Submit" button is clicked and the change to one of the fields in the field set is not picked up (in the example below changing company type to "Sole trader").

EDIT: 28/09/2018:

Weirdly enough if I move the field set outside of the first action region into the second action region the data is passed to the controller and saved as expected.

This leads me to believe that it is a simple markup error but I can't locate were I'm going wrong.

END EDIT

Here is a simplified version of the Visualforce Page:

<apex:page standardController="Account" extensions="AccountCreationController" sidebar="false" id="acPage" docType="html-5.0">
<apex:pagemessages id="mainpagemessages" showDetail="true"/>
<apex:pageMessage summary="{!IF(isLocked,'This record is locked, please close the page and either recall the approval or wait for a response','')}" severity="info" strength="3" id="lockedrecord" rendered="{!isLocked}"/>
<apex:form id="main_form">
    <apex:sectionHeader title="Account" subtitle="{!anaccount.Name}"/>
    <apex:pageBlock title="New Account" mode="edit">
        <apex:actionRegion>
            <apex:pageBlockSection id="details_Section" title="Details" columns="2">
                <apex:repeat value="{!$ObjectType.Account.FieldSets.Account_Creation_FS}" var="f">
                    <apex:outputField value="{!anaccount[f.fieldPath]}" rendered="{!OR(CONTAINS(f.FieldPath,'__r'), isLocked)}"/>
                    <apex:inputField value="{!anaccount[f.fieldPath]}" required="{!OR(f.required, f.dbrequired)}" rendered="{!!OR(CONTAINS(f.FieldPath,'__r'), isLocked)}"/>
                </apex:repeat>
            </apex:pageBlockSection>
        </apex:actionRegion>
    </apex:pageBlock>
    <apex:actionRegion>
        <apex:actionStatus id="SaveStatus">
            <apex:facet name="stop">
                <apex:outputPanel>
                    <apex:commandButton value="Save & Submit" action="{!submit}" status="SaveStatus" rendered="{!!isLocked}" reRender="mainpagemessages"/>
                    <apex:commandButton value="Cancel" action="{!close}" status="SaveStatus" reRender="mainpagemessages"/>
                </apex:outputPanel>
            </apex:facet>
            <apex:facet name="start">
                <apex:outputPanel>
                    <apex:commandButton value="Loading..." rendered="{!!isLocked}" disabled="true"/>
                    <apex:commandButton value="Loading..." disabled="true"/>
                </apex:outputPanel>
            </apex:facet>
        </apex:actionStatus>
    </apex:actionRegion>
</apex:form>

And here is the simplified controller:

public with sharing class AccountCreationController {
private ApexPages.StandardController Controller = null;
private final static Id BusinessAccountCreationRT = Schema.SObjectType.Account.getRecordTypeInfosByName().get('Business Account Creation').getRecordTypeId();

public Id accountId {get;set;}
public String RecordTypeString {get;set;}
public Id accRecordTypeId {get;set;}
public Account theaccount {get;set;}
public static Id currentUserId = UserInfo.getUserId();
public boolean getisLocked(){
    if(accountId != null)
        return Approval.isLocked(accountId);
    return null;
}

public AccountCreationController(ApexPages.StandardController ctl){
    this.Controller = ctl;

    system.debug('reaches AccountCreationController @ AccountCreationController.');

    if(this.Controller != null){
        accountId = ApexPages.currentPage().getParameters().get('id');
        RecordTypeString = ApexPages.currentPage().getParameters().get('RecordType');
        if(RecordTypeString != null){
            accRecordTypeId = Id.valueOf(ApexPages.currentPage().getParameters().get('RecordType'));
        } else {
            accRecordTypeId = BusinessAccountCreationRT;
        }

        if(accountId != null){
            this.theaccount = findAccountDetails(accountId);
        } else {
            this.theaccount = new Account(OwnerId = currentUserId,
                                          MobileEngagementStatus__c = 'Prospect',
                                          SolutionsEngagementStatus__c = 'Prospect',
                                          RecordTypeId = accRecordTypeId
                                         );
        }
    }
}

private static Account findAccountDetails(Id accId){
    List<Account> acc = new List<Account>();

    system.debug('reaches findAccountDetails @ AccountCreationController.');

    acc = [SELECT Id, OwnerId, AccountSource, Name, AccountApprovalStatus__c, Type, RecordTypeId, RecordType.Name, CompanyType__c, 
                  Campaign__c, CompanyRegistrationNumber__c, ParentId, Marketing_opt_out__c, External_Datasource__c, 
                  MobileEngagementStatus__c, SolutionsEngagementStatus__c, CustomerStatus__c, Phone, Phone__c, OtherPhone__c, 
                  Ext__c, RegisteredAddress1__c, RegisteredAddress2__c, RegisteredAddress3__c, RegisteredTown__c, 
                  RegisteredCity__c, RegisteredCounty__c, RegisteredCountyLookup__c, RegisteredCountry__c, RegisteredPostCode__c,
                  BillingAddress1__c, BillingAddress2__c, BillingAddress3__c, BillingTown__c, BillingCity__c, BillingCounty__c,
                  BillingCountry__c, BillingPostCode__c, SameasRegisteredAddress__c
           FROM Account
           WHERE Id =: accId
           LIMIT 1
          ];

    if(!acc.isEmpty()){
        return acc[0];
    } else {
        return null;
    }
}

public PageReference submit(){
    system.debug('reaches submit @ AccountCreationController.');
    Savepoint sp = Database.setSavepoint();
    Boolean actionsuccess = false;

    system.debug('this.theaccount @ submit - AccountCreationController. this.theaccount: ' + this.theaccount);

    if(this.theaccount.Id != null)
        accountId = this.theaccount.Id;

    if(accountId != null){
        actionsuccess = save(this.theaccount);
    } else {
        actionsuccess = insertaccount(this.theaccount);
        accountId = this.theaccount.Id;
    }

    if(actionsuccess){
        Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
        req.setObjectId(accountId);
        req.setSubmitterId(currentUserId);
        req.setProcessDefinitionNameOrId(null);

        try {
            system.debug('req to process @ submit - AccountCreationController. req: ' + req);
            Approval.ProcessResult result = Approval.process(req);

            if(result.isSuccess()){
                system.debug('request was succesfull @ submit - AccountCreationController');
                PageReference redirect = close();
                return redirect;
            } else {
                system.debug('An error has occured when submitting the account for approval @ submit - AccountCreationController. result.getEntityId(): ' + result.getEntityId() + ', result.getErrors(): ' + result.getErrors());
                ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR, 'An error has occured when submitting the account for approval. Errors: ' + result.getErrors());
                ApexPages.addMessage(msg);
                return null;
            }
        } catch(Exception e) {
            system.debug('An error has occured when saving the account @ submit - AccountCreationController e.getLineNumber(): ' + e.getLineNumber() + ', e.getMessage: ' + e.getMessage());
            ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR, 'An error has occured when submitting this account for approval. Line Number: ' + e.getLineNumber() + ', Error Message: ' + e.getMessage());
            ApexPages.addMessage(msg);
            return null;
        }
    } else {
        Database.rollback(sp);
        return null;
    }
}

private boolean save(Account acc){
    system.debug('reaches save @ AccountCreationController.');
    try{
        update acc;
        return true;
    } catch (Exception e) {
        ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR, 'An error has occured when saving the account. Line Number: ' + e.getLineNumber() + ', Error Message: ' + e.getMessage());
        ApexPages.addMessage(msg);
        return false;
    }
}

private boolean insertaccount(Account acc){
    system.debug('reaches insertaccount @ AccountCreationController.');
    try{
        insert acc;
        return true;
    } catch (Exception e) {
        ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR, 'An error has occured when creating the account. Line Number: ' + e.getLineNumber() + ', Error Message: ' + e.getMessage());
        ApexPages.addMessage(msg);
        return false;
    }
}

public PageReference close(){
    system.debug('reaches close @ AccountCreationController.');
    string retURL = ApexPages.currentPage().getParameters().get('retURL');

    if(accountId == null && retURL == null){
        ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR, 'Error returning to the previous page. There is no account for you to return to.');
        ApexPages.addMessage(msg);
        return null;
    } else {
        PageReference redirect = null;

        if(retURL != null){
            redirect = new PageReference('/' + retURL);
        } else {
            redirect = new PageReference('/' + accountId);
        }

        redirect.setRedirect(true);

        if(redirect != null){
            try{
                return redirect;
            } catch(Exception e) {
                ApexPages.Message msg = new ApexPages.Message(ApexPages.Severity.ERROR, 'Error returning to the previous page.');
                ApexPages.addMessage(msg);
                return null;
            }
        } else {
            return null;
        }
    }
}
}

And finally the debug logs
Page Load:

10:35:22:051 USER_DEBUG [19]|DEBUG|reaches AccountCreationController @ AccountCreationController.
10:35:22:051 USER_DEBUG [45]|DEBUG|reaches findAccountDetails @ AccountCreationController.

"Save & Submit" button after changing Company Type to "Sole trader":

10:38:58:033 USER_DEBUG [67]|DEBUG|reaches submit @ AccountCreationController.
10:38:58:039 USER_DEBUG [71]|DEBUG|this.theaccount @ submit - AccountCreationController. this.theaccount: Account:{Id=0010Q000002j4g0QAA, OwnerId=00558000001LjOgAAK, AccountSource=Employee Referral, Name=Raptor Pre School, AccountApprovalStatus__c=Awaiting Approval, Type=Business, RecordTypeId=01258000000DTl6AAG, CompanyType__c=Business Partnership, Campaign__c=a110Q0000008V5iQAE, CompanyRegistrationNumber__c=15245845, Marketing_opt_out__c=true, MobileEngagementStatus__c=Active, SolutionsEngagementStatus__c=Active, CustomerStatus__c=Active, Phone__c=+44, OtherPhone__c=+44, RegisteredAddress1__c=Unit 2, RegisteredAddress2__c=13 Shore Lane, RegisteredTown__c=Aberdeen, RegisteredCity__c=Aberdeen, RegisteredCounty__c=Aberdeenshire, RegisteredCountyLookup__c=a0C0Q0000000g0EUAQ, RegisteredCountry__c=United Kingdom, RegisteredPostCode__c=AB11 5BF, BillingAddress1__c=FDC Unit 2, BillingAddress2__c=Fulcrum 4, BillingAddress3__c=Solent Way, BillingCity__c=Whiteley, BillingCounty__c=Hampshire, BillingCountry__c=United Kingdom, BillingPostCode__c=Hampshire, SameasRegisteredAddress__c=false}
10:38:58:039 USER_DEBUG [118]|DEBUG|reaches save @ AccountCreationController.
10:38:58:204 USER_DEBUG [242]|DEBUG|reaches beforeUpdate @ AccountTriggerHandler
10:38:58:204 USER_DEBUG [242]|DEBUG|reaches beforeUpdate @ AccountTriggerHandler
10:38:58:507 USER_DEBUG [91]|DEBUG|req to process @ submit - AccountCreationController. req: Approval.ProcessSubmitRequest[getObjectId=0010Q000002j4g0QAA;getProcessDefinitionNameOrId=null;getSkipEntryCriteria=null;getSubmitterId=00558000000yvZ0AAI;]
10:38:59:064 USER_DEBUG [95]|DEBUG|request was succesfull @ submit - AccountCreationController
10:38:59:064 USER_DEBUG [144]|DEBUG|reaches close @ AccountCreationController.

Any help would be greatly received.

EDIT: 25/09/2018: added research that has already been done.

EDIT: 28/09/2018: added further findings.

Best Answer

You should look for the problem with the action regions.. The way it's set up now, the buttons are in a separate action region. That means that when you click the button, no other values will be submitted to the controller.

On occasion it can be nice to have an actionRegion with only a button, but usually it doesn't make sense. I think the intended use of actionRegions is when you have onchange events firing on elements, you wrap these in separate actionRegions so the entire form does not get submitted (or validated) when the onchange triggers.

You can check the documentation for actionRegions for more information.

Related Topic