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:
- 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.
- 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, [email protected]
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" />
<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;
}
}