0

We have a custom field on the Campaign object called Number_of_Attendees__c that is populated by Rollup Helper using a @future method (set up by Rollup Helper, so I have no access to the code).

The Number_of_Attendees__c field is displayed as an output field on the Visualforce page, and we're having two issues with the field:

  1. When the field is updated by Rollup Helper, the output field does not get updated on the VF page. It does get updated on the record detail page, so I know the Rollup Helper @future method has already run.
  2. When any of the input fields on the VF page are updated, we have an Update Status button that triggers a quicksave, and this causes the Number_of_Attendees__c output field on the VF page to override the value of the Number_of_Attendees__c field on the record detail page.

Any idea what could be causing these issues and how to resolve?

Here's the set of fields on displayed on the VF page:

<apex:pageBlockSection columns="2" >
   <apex:outputField value="{!campaign.Name}" label="VIP Event Name"/>
   <apex:outputField value="{!Campaign.Office__c}" label="Affiliate" />
   <apex:outputField value="{!Campaign.Event_Name__c}" label="Venue"/>         
   <apex:outputField value="{!Campaign.EndDate}" label="Date"/>   
   <apex:outputField value="{!Campaign.Event_Address__c}" label="Address"/> 
   <apex:inputField value="{!Campaign.Status}" label="Status" /> 
   <apex:inputField value="{!Campaign.Deposit_Date__c}" label="Deposit Date" /> 
   <apex:outputField value="{!Campaign.AmountWonOpportunities}" label="Total Revenue"/>
   <apex:outputField value="{!Campaign.Number_of_Attendees__c}" label="Total Attendees"/>
</apex:pageBlockSection>

<apex:pageBlockButtons location="bottom">
    <div>
       <table style="width: 100%; text-align: left;">
        <tr>
        <td><apex:commandButton action="{!quickSave}" Value="Update Status" /> </td> 
        <td><apex:commandButton value="Virtual Terminal" onclick="window.location='/madd/CnP_PaaS__VirtualTerminal?sfCampaignid={!Campaign.Id}&CnPAID={!Campaign.CnP_PaaS__CnP_Account_Number__c}'; return false;"/> </td> 
        </tr>
        </table>
     </div>
</apex:pageBlockButtons>

And here's the code for the quicksave:

// save the campaign
public PageReference quickSave() {

    PageReference nextPage = null;

    try{
        update campaign;
        nextPage = ApexPages.currentPage();
        Id id = nextPage.getParameters().get('Id');
        nextPage.getParameters().clear();
        nextPage.getParameters().put('id', id);
        nextPage.setRedirect(true);

    } catch(Exception e){
         ApexPages.addMessages(e); 

    } 

    return nextPage;

}

Here is the full code for the visualforce page:

<apex:page docType="html-5.0"  standardController="Opportunity" extensions="SiteMonitorCampaign4Extension" recordSetVar="opportunities" > 
<apex:includeScript value="https://service.apsona.com/sfdc/apsona_inline.min.js"/>
<script type="text/javascript">
    window.onload = function() { }
// script to make sure Apsona has session ID
sforce.connection.sessionId = "{!$Api.Session_ID}";
// Test added 6/23/2016 by Sridhar, sridhar@apsona.com
sforce.connection.serverUrl = UserContext.siteUrlPrefix + sforce.connection.serverUrl; 
// For Apsona Merge Action (Cash Control Form link in VIP Attendees section) added by Michelle Regal @nimadmin on 6/27/2016
function callApsona(){
    apsona.runAction ('buttonMerge', { 
            'element': this,
            'actionId': 'a2OF0000002GRXhMAO', 
            'recordId': '{!Campaign.Id}', 
            'linkFieldId': 'Id'                 
    });
}

</script>
<!-- <vs:importvisualstrap theme="simplex" />    -->

<apex:variable var="showDetail" value="{!$Profile.Name != 'VIP Partner Community User' || ($Profile.Name == 'VIP Partner Community User' && campaign.RecordType.Name != 'VIP Event')}" />
<apex:variable var="showSiteMonitor" value="{!campaign.RecordType.Name == 'VIP Event'}" />

<apex:pageMessages id="pgmsg"> </apex:pageMessages>

<apex:outputPanel id="DetailPage" >
    <apex:detail rendered="{!showDetail}" showChatter="true"/>
</apex:outputPanel>

<apex:outputPanel rendered="{!showSiteMonitor}" id="VIPPanel">
   <!--   <vs:visualstrapblock >
       <vs:panel title="VIP Panel Attendees" type="primary"> -->
           <apex:form id="campaignForm">
               <apex:pageBlock mode="maindetail" title="VIP Panel Attendees">
                   <apex:pageBlockSection columns="2" >
                        <apex:outputField value="{!campaign.Name}" label="VIP Event Name"/>
                        <apex:outputField value="{!Campaign.Office__c}" label="Affiliate" />
                        <apex:outputField value="{!Campaign.Event_Name__c}" label="Venue"/>         
                        <apex:outputField value="{!Campaign.EndDate}" label="Date"/>   
                        <apex:outputField value="{!Campaign.Event_Address__c}" label="Address"/> 
                        <apex:inputField value="{!Campaign.Status}" label="Status" /> 
                        <apex:inputField value="{!Campaign.Deposit_Date__c}" label="Deposit Date" /> 
                        <apex:outputField value="{!Campaign.AmountWonOpportunities}" label="Total Revenue"/>
                        <apex:outputField value="{!Campaign.Number_of_Attendees__c}" label="Total Attendees"/>
                    </apex:pageBlockSection>

                    <div>
                        <table style="width: 100%; text-align: center;">
                            <tr>
                                <td><apex:outputLink value="/00OF0000006wM7H?pv0={!Campaign.Id}">Attendance Report</apex:outputLink></td>
                                <!-- <td><apex:outputLink value="/00OF0000006oRf4?pv0={!Campaign.Id}">Deposit Report</apex:outputLink></td> -->
                                <!-- Apsona Merge Action Link added by Michelle Regal @nimadmin on 6/27/2016-->
                                <td><span onclick="callApsona();return false;" style="text-decoration:underline;">Cash Control Form</span></td> 
                                <td><apex:outputLink target="_blank" value="/00OF0000006wM7M?pv0={!Campaign.Casesafe_ID__c}">Revenue Report</apex:outputLink></td>
                            </tr><tr>
                                <!-- <td><apex:outputLink target="_blank" value="https://na10.salesforce.com/flow/VIP_Walk_Ups?CampaignEndDate={!URLENCODE(TEXT(Campaign.EndDate))}&CampaignTicketPrice={!URLENCODE(TEXT(Campaign.Ticket_Price__c))}">Walk Up Registration</apex:outputLink></td>-->
                                <td><apex:outputLink target="_blank" value="{!URLFOR('VisualFlow', null, [id=Campaign.Id])}">Walk Up Registration</apex:outputLink></td>
                                <td><apex:outputLink target="_blank" value="{!URLFOR('Enter_Cash', null, [id=Campaign.Id])}">Enter Donation</apex:outputLink></td>
                                <td><apex:outputLink target="_blank" value="apex/BalanceDetailReport?id={!Campaign.Id}">Balance Detail</apex:outputLink></td>
                                <td><apex:outputLink target="_blank" value="{!URLFOR('AttendanceOverride', null, [id=Campaign.Id])}">(TESTING) Attendance Override</apex:outputLink></td>
                            </tr>
                        </table>
                   </div>
                    <apex:pageBlockButtons location="bottom">
                        <div>
                            <table style="width: 100%; text-align: left;">
                                <tr>
                                    <td><apex:commandButton action="{!quickSave}" Value="Update Status" /> </td> 
                                    <td><apex:commandButton value="Virtual Terminal" onclick="window.location='/madd/CnP_PaaS__VirtualTerminal?sfCampaignid={!Campaign.Id}&CnPAID={!Campaign.CnP_PaaS__CnP_Account_Number__c}'; return false;"/> </td> 
                                </tr>
                            </table>
                        </div>
                    </apex:pageBlockButtons>
                </apex:pageBlock>
            </apex:form> 

            <apex:variable var="readOnly" value="{!Campaign.Status == 'Completed'}" />


            <!-- GRIDBUDDY -->
            <!--  <vs:panel title="Attendee List" type="primary" > -->
            <apex:messages />
                <apex:form id="opportunityForm">
                    <apex:pageBlock id="thePageBlock" title="Attendee List">
                        <apex:pageBlockTable value="{!opportunities}" var="myOpportunity" id="thePageBlockTable">               

                            <apex:column >
                                <apex:outputLink value="{!URLFOR($Action.Opportunity.View, myOpportunity.id)}" >{!myOpportunity.npsp__Primary_Contact__r.LastName}</apex:outputLink>
                                <!--<apex:outputField value="{!myOpportunity.npsp__Primary_Contact__r.Name}"/>--> 
                                <apex:facet name="header">
                                    <apex:commandLink action="{!viewData}" value="Last Name {!IF(sortExpression=='npsp__Primary_Contact__r.LastName',IF(sortDirection='DESC','▼','▲'),'')}" >
                                    <apex:param value="npsp__Primary_Contact__r.LastName" name="column" assignTo="{!sortExpression}" ></apex:param>
                                   </apex:commandLink>
                                </apex:facet>
                            </apex:column>

                            <apex:column >
                                <apex:outputLink value="{!URLFOR($Action.Opportunity.View, myOpportunity.id)}" >{!myOpportunity.npsp__Primary_Contact__r.FirstName}</apex:outputLink>
                                <!--<apex:outputField value="{!myOpportunity.npsp__Primary_Contact__r.Name}"/>--> 
                                <apex:facet name="header">
                                    <apex:commandLink action="{!viewData}" value="First Name {!IF(sortExpression=='npsp__Primary_Contact__r.FirstName',IF(sortDirection='DESC','▼','▲'),'')}" >
                                    <apex:param value="npsp__Primary_Contact__r.FirstName" name="column" assignTo="{!sortExpression}" ></apex:param>
                                   </apex:commandLink>
                                </apex:facet>
                            </apex:column>

                            <apex:column >
                                <apex:inputField value="{!myOpportunity.Attended__c}" rendered="{!!readOnly}" /> 
                                <apex:outputField value="{!myOpportunity.Attended__c}" rendered="{!readOnly}"  /> 

                                <apex:facet name="header">
                                    <apex:commandLink action="{!viewData}" value="Attended {!IF(sortExpression=='Attended__c',IF(sortDirection='DESC','▼','▲'),'')}" >
                                    <apex:param value="Attended__c" name="column" assignTo="{!sortExpression}" ></apex:param>
                                   </apex:commandLink>
                                </apex:facet>
                            </apex:column>

                            <apex:column >
                                <apex:inputField value="{!myOpportunity.Paid__c}" rendered="{!!readOnly}" /> 
                                <apex:outputField value="{!myOpportunity.Paid__c}" rendered="{!readOnly}" />

                                <apex:facet name="header">
                                    <apex:commandLink action="{!viewData}" value="Paid {!IF(sortExpression=='Paid__c',IF(sortDirection='DESC','▼','▲'),'')}" >
                                    <apex:param value="Paid__c" name="column" assignTo="{!sortExpression}" ></apex:param>
                                    </apex:commandLink>
                                </apex:facet>
                            </apex:column>

                            <apex:column >
                                <apex:inputField value="{!myOpportunity.Campaignid}" rendered="{!!readOnly}" />
                                <apex:outputField value="{!myOpportunity.Campaign.name}" rendered="{!readOnly}" />

                                <apex:facet name="header">
                                    <apex:commandLink action="{!viewData}" value="Campaign {!IF(sortExpression=='Campaign.Name',IF(sortDirection='DESC','▼','▲'),'')}" >
                                    <apex:param value="Campaign.Name" name="column" assignTo="{!sortExpression}" ></apex:param>
                                    </apex:commandLink>
                                </apex:facet>
                            </apex:column>

                            <apex:column >
                                <!-- <apex:inputField value="{!myOpportunity.npsp__Primary_Contact__r.Balance_Currency__c}" rendered="{!!readOnly}" />  
                                <apex:outputField value="{!myOpportunity.npsp__Primary_Contact__r.Balance_Currency__c}" rendered="{!readOnly}" />-->
                                <apex:outputText value="{!IF(myOpportunity.npsp__Primary_Contact__r.Balance_Currency__c=0 , '' , myOpportunity.npsp__Primary_Contact__r.Balance_Currency__c)}" />

                                <apex:facet name="header">
                                    <apex:commandLink action="{!viewData}" value="Balance {!IF(sortExpression=='npsp__Primary_Contact__r.Balance_Currency__c',IF(sortDirection='DESC','▼','▲'),'')}" >
                                    <apex:param value="npsp__Primary_Contact__r.Balance_Currency__c" name="column" assignTo="{!sortExpression}" ></apex:param>
                                    </apex:commandLink>
                                </apex:facet>
                            </apex:column>

                            <apex:column >
                                <apex:inputField value="{!myOpportunity.Proceed_to_Virtual_Terminal__c}" rendered="{!!readOnly}" /> 
                                <apex:outputField value="{!myOpportunity.Proceed_to_Virtual_Terminal__c}" rendered="{!readOnly}" />

                                <apex:facet name="header">
                                    <apex:commandLink action="{!viewData}" value="C&P URL {!IF(sortExpression=='Proceed_to_Virtual_Terminal__c',IF(sortDirection='DESC','▼','▲'),'')}" >
                                    <apex:param value="Proceed_to_Virtual_Terminal__c" name="column" assignTo="{!sortExpression}" ></apex:param>
                                    </apex:commandLink>
                                </apex:facet>
                            </apex:column>

                        </apex:pageBlockTable>

                        <apex:pageBlockButtons > 
                            <apex:outputText value="Campaign" />&nbsp;
                            <apex:inputField value="{!BlankOpportunity.CampaignId}" label="Campaign"/>
                            <apex:commandButton value="Set Campaign" action="{!setCampaign}" />               
                            <apex:commandButton value="Mark All as Paid" action="{!markAllAsPaid}" id="markAllAsPaidButton"/>
                            <apex:commandButton value="Mark All as Attended" action="{!markAllAsAttended}" id="markAllAsAttendedButton"/>
                            <apex:commandButton value="Save" action="{!save}" id="saveButton"/>
                            <apex:commandButton value="Cancel" action="{!cancel}" id="cancelButton"   />            
                            <apex:commandButton value="Previous" action="{!previous}" rendered="{!hasPrevious}" title="Previous Page" id="prevBtn"/>
                            <apex:commandButton value="Next" action="{!next}" rendered="{!hasNext}" title="Next Page" id="nextBtn"/>     
                        </apex:pageBlockButtons>

                    </apex:pageBlock>
                </apex:form>       

</apex:outputPanel>

</apex:page>

And here is the extension:

/**
*  @author Nineta Martinov, April 2016
*  Extension controller for SiteMonitorCampaign4 page.
*/
public without sharing class SiteMonitorCampaign4Extension {
private Campaign campaign;
private Id campaignId;    
private List<Opportunity> opportunities { get; set; }
private boolean saveComplete = false;
public static Integer PAGE_SIZE = 25;
private Opportunity blankOpportunity;

private string sortDirection = 'ASC';
private string sortExp = 'npsp__Primary_Contact__r.LastName';

// constructor
public SiteMonitorCampaign4Extension (ApexPages.StandardSetController stdController) {
  campaignid = ApexPages.currentPage().getParameters().get('id');
  campaign = [select Id, Name, Office__c,Affiliate__c, Site__c, StartDate, Site_Address__c, Event_Address__c, Event_Name__c, Status, Id_Short__c, RecordType.Name,
                EndDate, Ticket_Price__c, Deposit_Date__c, AmountWonOpportunities, Casesafe_ID__c, CnP_PaaS__CnP_Account_Number__c, Number_of_Attendees__c
                from Campaign where id = :campaignId];
}

// standard set controller
public ApexPages.StandardSetController setCon {
    get {
        if(setCon == null) {
            string sortFullExp = sortExpression  + ' ' + sortDirection;
            string query = 'select Id, npsp__Primary_Contact__c, npsp__Primary_Contact__r.Name, npsp__Primary_Contact__r.FirstName, npsp__Primary_Contact__r.LastName, Attended__c, ' +
                            'Paid__c, Campaign.Name, ' + 
                            'npsp__Primary_Contact__r.Balance_Currency__c, ' +
                            'C_P_URL_Referrer__c, Proceed_to_Virtual_Terminal__c ' +
                            'from Opportunity where CampaignId = :campaignId and StageName != \'Closed Lost\' and StageName != \'Open\' and npsp__Primary_Contact__c != null and RecordType.Name = \'Event Registration\' ' +
                            'order by ' + sortFullExp;
            System.debug(query);
            setCon = new ApexPages.StandardSetController(Database.getQueryLocator(query));
            setCon.setPageSize(PAGE_SIZE);          
        }
        return setCon;
    }
    set;
}

// return campaign
public Campaign getCampaign() {
    return campaign;
}

// set the campaign for all opps
public void setCampaign() {
    system.debug('blankOpportunity.CampaignId=' + blankOpportunity.CampaignId);
    if (blankOpportunity.CampaignId != null) {
        List<Opportunity> opps = getOpportunities();
        for (Opportunity opp : opps) {
            opp.CampaignId = blankOpportunity.CampaignId;
        }
        update opps;
    }
}

// returns a new opportunity to be used for the campaign field
public Opportunity getBlankOpportunity () {
    blankOpportunity = new Opportunity();
    return blankOpportunity;
}

// get tickets
public List<Opportunity> getOpportunities() {
    return (List<Opportunity>) setCon.getRecords();
}

public PageReference viewData() {
    return null;
}

// save the campaign
public PageReference quickSave() {

    PageReference nextPage = null;

    try{
        update campaign;
        nextPage = ApexPages.currentPage();
        Id id = nextPage.getParameters().get('Id');
        nextPage.getParameters().clear();
        nextPage.getParameters().put('id', id);
        nextPage.setRedirect(true);

    } catch(Exception e){
         ApexPages.addMessages(e); 

    } 

    return nextPage;

}

// save the tickets
public void save() {
    Integer pageNumber = setCon.getPageNumber();
    update setCon.getRecords();     

    //clear previous and next
    setCon = null;
    if(pageNumber!= 1) {
        setCon.setPageNumber(pageNumber - 1);
        if (hasNext) {
            setCon.next();
        }
    }
    saveComplete = true;      
}

// mark all as paid
public void markAllAsPaid() {
    List<Opportunity> opps = setCon.getRecords();
    for(Opportunity opp : opps) {
        opp.Paid__c = true;
    }
    update opps;
}

// mark all as attended
public void markAllAsAttended() {
    List<Opportunity> opps = setCon.getRecords();
    for(Opportunity opp : opps) {
        opp.Attended__c = true;
    }
    update opps;
}

// cancel method; do nothing; reload the page
public PageReference cancel() {
    integer pageNumber = setCon.getPageNumber();  
    setCon = null;
    setCon.setPageNumber(pageNumber);
    return null;
}

public Boolean hasNext {
    get {
        return setCon.getHasNext();
    }
    set;
}

public Boolean hasPrevious {
    get {
        return setCon.getHasPrevious();
    }
    set;
}     

public void previous() {
    try {
        saveComplete = false;
        setCon.previous();
    } catch (Exception ex) {
        ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR, 'You have modified opportunities on this page. Click Save or Cancel before you leave the page.'));
    }
}

public void next() {
    try {
        saveComplete = false;
        setCon.next();  
    } catch (Exception ex) {
        ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR, 'You have modified opportunities on this page. Click Save or Cancel before you leave the page.'));
    }
}

public string sortExpression
{
 get
 {
    return sortExp;
 }
 set
 {
   if (value == sortExp) 
        sortDirection = (sortDirection == 'ASC') ? 'DESC' : 'ASC';
   else
     sortDirection = 'ASC';

   sortExp = value;
   setCon = null;
 }
}

public string getSortDirection()
 {
    //if not column is selected 
    if (sortExpression == null || sortExpression == '')
      return 'ASC';
    else
     return sortDirection;
 }

 public void setSortDirection(String value)
 {  
   sortDirection = value;
   setCon = null;
 }
}
  • You did a great job setting up the scenario and including all relevant code, but you haven't really asked a question. That said, which rollup helper are you using? Andrew Fawcett's DLRS tool can run synchronously. – Adrian Larson Apr 20 '17 at 13:35
  • @AdrianLarson Thanks for the comment. My question is "Any idea what could be causing these issues and how to resolve?" - I've gone ahead and added this in. We are using Rollup Helper v 9.24 and Rollup Helper Real Time v 8.0. We need the rollups to be asynchronous because of other processes that are running on the campaign object. – TheDevAdmin Apr 20 '17 at 14:21
1

If the update is happening via @future, it likely wouldn't be updated when the page refreshes. If you refresh again after a few seconds, is it correct? If so, consider using an action poller to refresh the page every few seconds until the value does change.

Re the campaign value on the page overwriting the updated value, that is quite possible since although it is being displayed as an output value on the page, it is still a value on the object. So when you call update campaign, it just takes all the values you have for that record and updates the database - it isn't just using the ones setup as input values. So if the output value isn't updated to the most recent update, it will overwrite the value back to the original one when you loaded the page.

You will either need to fix the first issue to make sure the values on campaign match the values in the database, or get the attendee value separately on load so that when you hit save, it does not send that value to the database.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.