2

I've put together a Visualforce email template for cases and I'm running into an issue with it that only occurs when the case is initially created. The component pulls a list of related records from a junction object - when I fire off an email using the same template (using a custom button on a VF page), the records from the junction object are pulled in correctly, but when the email is sent via a workflow rule, the table containing the records is empty.

For the scenario I'm working on, I have some logic in the account trigger that creates a case & records in the CaseTrip__c junction object. My goal is to send an email via workflow rules that contains info about the case & the CaseTrip__c records whenever a case is created. Since I'm creating the case & the CaseTrip__c records in the same transaction, my understanding (based on this article) is that the WF emails are the last thing to execute, so the CaseTrip__c records should already be saved to the database & should then be returned in the controller's query. But obviously I'm missing something since the records aren't included in the initial WF email.

Here is the code for the component:

 <apex:component controller="TripsForCaseController" access="global">    
        <apex:facet name="header">Related Trips</apex:facet>     
        <apex:column headerValue="Traveler" value="{!trip.Traveler__r.AccountNumberStr__c} - {!trip.Traveler__r.Name}"/>
        <apex:column headerValue="Is Primary Booker">
            <c:CheckboxTransform cb="{!trip.IsPrimary__c}"/>
        </apex:column>
        <apex:column headerValue="Order Paid In Full">
            <c:CheckboxTransform cb="{!trip.OrderPaidInFull__c}"/>
        </apex:column>
        <apex:column headerValue="Status" value="{!trip.Status__c}"/>
        <apex:column headerValue="Date Booked" value="{!trip.DateBooked__c}"/>
            <apex:column headerValue="Tour Code" value="{!trip.TourCode__c}" rendered="false"/>
        <apex:column headerValue="Tour Date" value="{!trip.TourDate__c}" rendered="false"/>
        <apex:column headerValue="Accepted T&Cs" value="{!trip.AcceptedTC__c}"/>
        <apex:column headerValue="Provided Emergency Contacts" value="{!trip.EmergencyContacts__c}"/>
        <apex:column headerValue="TSA Confirmed" value="{!trip.TSAConfirmed__c}"/>
    </apex:dataTable>   
</apex:component>

And here is the controller:

public without sharing class TripsForCaseController {
    /***
        Gets a list of any trip related to the case
        Trips can be related either through the field case.Order__c or through the CaseTrip__c junction object
    ***/
    public final Id caseId {get; set;}
    public final Id orderId {get; set;}
    private final Case cas;
    private final Order__c order;
    private final Set<Trip__c> trips;

    private Case getCase(Id caseId) {
        return caseId != null ? Database.query(QueryUtils.getCaseQuery() + 'WHERE Id = :caseId') : new Case();
    }

    private Order__c getOrder(Id orderId) {
        return orderId != null ? Database.query(QueryUtils.getOrderQuery() + 'WHERE Id = :orderId') : new Order__c();
    }

    public List<Trip__c> getTrips() {
        //VF seems to have issues with directly returning the set, so return a list instead
        //Use a set to make sure we don't have any duplicate trips
        Set<Trip__c> tripSet = new Set<Trip__c>();
        tripSet.addAll(getCaseTrips());
        tripSet.addAll(getOrderTrips());

        //Populate the list with the values in the set
        List<Trip__c> trips = new List<Trip__c>();
        trips.addAll(tripSet);

        return trips;
    }

    private List<Trip__c> getCaseTrips() {
        //Get all the trips linked to the case via the CaseTrip__c junction object
        List<Trip__c> trips = new List<Trip__c>();

        List<CaseTrip__c> caseTrips = new List<CaseTrip__c>();
        if(caseId != null) {
            caseTrips = [SELECT Id, Trip__c FROM CaseTrip__c WHERE Case__c = :caseId];
            Set<Id> tripIds = new Set<Id>();
            for(CaseTrip__c caseTrip : caseTrips) {
                tripIds.add(caseTrip.Trip__c);
            }
            if(!tripIds.isEmpty()) {
                trips = Database.query(QueryUtils.getTripQuery() + 'WHERE Id IN :tripIds');
            }
        }       

        return trips;
    }

    private List<Trip__c> getOrderTrips() {
        //Get all the trips linked to the case via the Order__c lookup field
        List<Trip__c> trips = new List<Trip__c>();

        //Case cas = [SELECT Id, Order__c FROM Case WHERE Id = :caseId];        
        if(orderId != null) {
            trips = Database.query(QueryUtils.getTripQuery() + 'WHERE Order__c = :orderId');
        }

        return trips;
    }
}
1
  • One way to workaround this issue is to create a RSF field on Case called CaseTrip_Count__c and when the count is > 0, fire the workflow. This way, the parent and all children are available for the VF custom controller
    – cropredy
    Jan 6, 2015 at 19:57

1 Answer 1

1

Looking at the doc and reading the answer from this post: 'After' trigger's SOQL - will query fetch current value of the inserted/updated record?

Step 6 says

Saves the record to the database, but doesn't commit yet.

And look at sfdcfox's answer. Save only makes it available for the transaction in flight but does not make the data available for the rest of the system, the only time the data will become available for the workflow is only when the data gets committed to the database.

The commit to database part happens only at step 20 Commits all DML operations to the database.

1
  • Ah, I think my issue is that step 21 is "Executes post-commit logic, such as sending email," which I assumed meant that the queries contained in the component's controller weren't executed until after step 20 when everything is committed, but that seems to not be the case.
    – jongpie
    Jan 7, 2015 at 15:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .