function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Justin DenningJustin Denning 

Data Entry Screen Inserting Multiple Records

I have a VF page that serves as a data entry screen for regional budgets.  All regions are displayed on the screen, and when a user fills in any of the budget input fields, the record is saved.  The issue is if the user tabs through the data entry fields fast enough, multiple records for the same region are created.  I do not want to make the user wait until the record is created to continue their data entry or use a submit button.  How can I stop multiple records from being created?

VF except......
 <apex:column width="80px" headerValue="Estimated Budget"> <apex:inputText size="10" value="{!budgetMap[budget].Estimated_Budget__c}">
            <apex:actionSupport event="onchange" action="{!processBudget}" status="topstatus"  rerender="contentLoading"   > 
            <apex:param name="RegionName" value="{!budget}"/>

<apex:column width="80px" headerValue="Discounts"> <apex:inputText size="10" value="{!budgetMap[budget].Discounts__c}"> <apex:actionSupport event="onchange" action="{!processBudget}" status="topstatus" rerender="contentLoading" > <apex:param name="RegionName" value="{!budget}"/> </apex:actionSupport> </apex:inputText> </apex:column>

public void processBudget()
    {//Get Region to be processed
     if(ApexPages.currentPage().getParameters().get('RegionName') != null && ApexPages.currentPage().getParameters().get('RegionName') != '' && ApexPages.currentPage().getParameters().get('RegionName') != 'null')
    //Get budget record from Map- may be an existing record or one not yet inserted
    budget__c budget = budgetMap.get(ApexPages.currentPage().getParameters().get('RegionName'));
          //If record has already been inserted, update it. 
          if(budget.Id <> NULL ) update budget;
          //If not yet inserted, insert it.
           else if (budget.Id == NULL) 
           budget = budgetMap.get(ApexPages.currentPage().getParameters().get('RegionName'));
            insert budget;
            //Put the budget back into the Map now that it has an Id
            budgetMap.put(ApexPages.currentPage().getParameters().get('RegionName'), budget);
           // ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.CONFIRM, budget.Region__r.Name + ' Updated Successfully.'));
        Catch(Exception e)
            ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.Error, e.getMessage()));
            System.debug('ERROR ' + e.getMessage());

The problem is that the first change calls processBudget (via AJAX), and before the AJAX completes, another change calls processBudget again.  Each call runs independently of the other, so there's no way to know that a record is already being inserted before you insert another.  Even if you could detect this (and there are ways), you can't update a record until it has finished being inserted.  So, the second change would have to be ignored.

IMO, the best solution is to use an actionStatus to disable the inputText that is being updated so that it can't be changed again until its AJAX call is complete.  Other inputTexts could be updated during this time.  You would put the inputText you have (including the actionSupport) in the stop facet of the actionStatus and put a similar but disabled inputText in the start facet.  I think that should work.

BTW: lines 16 and 19 of your class can be deleted.  On line 16, you already have a reference to the budget__c record from the map, so you don't need to get it again.  On line 19, the map still has a reference to the budget__c record (you haven't removed it), so you don't need to put it in the map.  Note that the fact that values within the budget__c record have changed don't matter.  The map only has a reference to the record, not an independent copy of the record.