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
Leo DarkstarLeo Darkstar 

How to call a class with this trigger ?

My question is two-fold :

 

I am trying to call a Class with a Trigger upon the editing of a field value. However, that Class also has a callout, and apparently we cannot be executed when there is an uncommitted transaction. 

Here :

You have uncommitted work pending... (https://help.salesforce.com/articleView?id=000328873&type=1&mode=1)

So, I'm wondering if I am able to call this Class using a 'before update' setting in the trigger. 

If I can do that, I would need to put the necessary code into this already existing trigger for the object : 

trigger Trigger_ProgramContactRole on Program_Contact_Role__c (after insert, after update, before insert, before update ,after delete) {
    //Checking for the event type
    if(Trigger.isAfter){
        
        //Checking for the request type
        if(Trigger.isInsert){
            
            // Update Servicer on contact
            ProgramContactRoleTriggerHelper.updateServicerOnPCR(Trigger.new);
            
        }
        
        //Checking for the request type
        if(Trigger.isUpdate){
            
            // call deleteProgramContactRoleRecors
            ProgramContactRoleTriggerHelper.deleteProgramContactRoleRecors(Trigger.new);
            
        }


That would need to call this Class :

public class UpdateContactPCRController {

    @InvocableMethod
    public static String  updateContact(String recordId){        
       
        String contactId = '';       
        
        List<Contact> contactToBeupdate = new List<Contact>();        
        
        if(String.isNotBlank(recordId)){            
            
            List<Program_Contact_Role__c> programContacList = [SELECT Id,Contact__c,Program_Name__c FROM Program_Contact_Role__c WHERE Id =:recordId AND Contact__c != null];
            contactId = programContacList[0].Contact__c;
            
            if(String.isNotBlank(contactId)){
                
                contactToBeupdate = [Select Id,Pardot_Action_Trigger__c,PCR_Register_Button_Link__c,PCR_URL_for_UI__c FROM Contact Where Id =: contactId Limit 1];                
                
                contactToBeupdate[0].Program_Contact_Role_Id__c = recordId;
            }
                        List<Program_Communication_Recipients__c> programCommunicationRecs = [Select Id,Name,
                                  Program_Communication__r.Program__r.Buyer__r.Name,Receipient__c,                
                                              From Program_Communication_Recipients__c Where 
                                                           Program_Communication__r.Program__c =: programContacList[0].Program_Name__c AND 
                                                           Receipient__c =: programContacList[0].Id];                                           
                                
                
                    contactToBeupdate[0].PCR_Welcome_Message__c = String.valueOf(programCommunicationRecs[0].Program_Communication__r.Welcome_Message__c);                                                    

        if(contactToBeupdate.size() > 0){
            update contactToBeupdate;  
        }
        
        return 'Updated Successfully';
    }
}
    //Calling  Http_Utility_Pardot class pardotCreateProspect method for the creation of Prospect record.
    String returnedResponseFromPardot = Http_Utility_Pardot.pardotCreateProspect(new Set<Id> {contactToBeupdate[0].Id});


The Pardot callout which is causing the error is coming from the last two lines of the class. ​​​​​​​

How can this be incorporated into this trigger - and only done when a field is changed ? 

Thank you very much for any help you can give. 

Alain CabonAlain Cabon
Callouts are not allowed when there is an uncommitted transaction pending.
https://help.salesforce.com/articleView?id=000328873&type=1&mode=1 (https://help.salesforce.com/articleView?id=000328873&type=1&mode=1)

The common work-around is to create a new context with a new asynchronous treatment (a new transaction) with the @future annotation.
 ...
 pardotCallout(contactToBeupdate[0].Id);
}

@future(callout=true) 
public static void pardotCallout(String contactId) {
    String returnedResponseFromPardot = Http_Utility_Pardot.pardotCreateProspect(new Set<Id> {contactId});
    // new treatment here with the returned value included DML operations.
}

Easy but the big problem is the return value: returnedResponseFromPardot because that value cannot be returned (always void). 

Future methods must be static methods, and can only return a void type.
 
Callouts aren't allowed after DML operations (update, create, delete) in the same transaction because DML operations result in pending uncommitted work that prevents callouts from executing. Since Salesforce does not have a explicit Commit, if you try doing DML and then Callout, you will get 'You have uncommitted work pending.

But DML operations are allowed after a callout.
Leo DarkstarLeo Darkstar
@Alain - thank you so much for your help. I have some questions. What you have here is an addition to the class, correct ? If so, how is the class called with that trigger I have here ? I would like for the current record's recordid to be included when the class is started. I also would like for it to not get invoked unless a field is changed (IsChanged(CustomField__c). Would all of this go into the 'AfterUpdate' criteria ? It's confusing because there's even an update within the class (and the update needs to happen before the callout). Thank you again. I really appreciate the help.  
Leo DarkstarLeo Darkstar

Also -  I'm now getting the following error when attempting to use the code you gave.

Error: Unexpected token '@'.
 

This is how my class looks when I am attempting to save it : 

public class UpdateContactPCRController {

    @InvocableMethod
    public static String  updateContact(String recordId){        
       
        String contactId = '';       
        
        List<Contact> contactToBeupdate = new List<Contact>();        
        
        if(String.isNotBlank(recordId)){            
            
            List<Program_Contact_Role__c> programContacList = [SELECT Id,Contact__c,Program_Name__c FROM Program_Contact_Role__c WHERE Id =:recordId AND Contact__c != null];
            contactId = programContacList[0].Contact__c;
            
            if(String.isNotBlank(contactId)){
                
                contactToBeupdate = [Select Id,Pardot_Action_Trigger__c,PCR_Register_Button_Link__c,PCR_URL_for_UI__c FROM Contact Where Id =: contactId Limit 1];                
                
                contactToBeupdate[0].Program_Contact_Role_Id__c = recordId;
            }
                        List<Program_Communication_Recipients__c> programCommunicationRecs = [Select Id,Name,
                                  Program_Communication__r.Program__r.Buyer__r.Name,Receipient__c,                
                                              From Program_Communication_Recipients__c Where 
                                                           Program_Communication__r.Program__c =: programContacList[0].Program_Name__c AND 
                                                           Receipient__c =: programContacList[0].Id];                                           
                                
                
                    contactToBeupdate[0].PCR_Welcome_Message__c = String.valueOf(programCommunicationRecs[0].Program_Communication__r.Welcome_Message__c);                                                    

 pardotCallout(contactToBeupdate[0].Id);
    {

        @future(callout=true) 
        public static void pardotCallout(String contactId) {
            String returnedResponseFromPardot = Http_Utility_Pardot.pardotCreateProspect(new Set<Id> {contactId});
            // new treatment here with the returned value included DML operations.
    }

        if(contactToBeupdate.size() > 0){
            update contactToBeupdate;  
        }
        
        return 'Updated Successfully';
    }
}

 

Also - should I be removing the @InvocableMethod functionality at the beginning of the class, or is that not a problem ?

I'm still confused how to start this class w/ a trigger if this is supposed to only run upon a field change if I can't do an update before a callout. 

 

Thank you again. 

Leo DarkstarLeo Darkstar
and ftr - when I asked about the @InvocableMethod functionality I wasn't asking as a reason for the "Unexpected Token '@'". That error was specifically naming the line with @future(callout=true). I was just wondering if @InvocableMethod was no longer needed in this class because it would no longer be needed.
Leo DarkstarLeo Darkstar

@Alain, 

 

I'm still trying to use the code you provided but I can't get that method to start. I think it's because I'm trying to call it from a Process Builder and I need to be able to use an @InvocableMethod annotation with it - but I'm already using a @Future with it. Is there a way I can use both ?

 

Here is how the code looks at the end of my class right now : 

@future(callout=true) 
    public static void pardotCallout(String contactId) {
        String returnedResponseFromPardot = Http_Utility_Pardot.pardotCreateProspect(new Set<Id> {contactId});
        System.debug('TESTER DEBUGGER-POST-FUTURE');

    }

Thank you for any help you can give.