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
Patrick Marks 2Patrick Marks 2 

Help fixing trigger/class for Campaign lookup fields

Hey everyone-

I have a couple of custom Campaign lookup fields on the Contact object called First Campaign and Most Recent Campaign. These lookup fields need to get populated with the ID of the appropriate Campaign (should be pretty self explanatory-First Campaign would lookup to the first Campaign that the Contact was added to, Most Recent Campaign would lookup to the most recent Campaign that the Contact was added to). This action is supposed to take place when either of a couple of other custom Date/Time fields (Most Recent PQC Date/Most Recent Upsell PQC Date) on the Contact are updated. I would really appreciate help figuring out why the code isn't working as intended and what the fix is. Here is the trigger:
trigger ContactTrigger on Contact ( after update) {

    if(Trigger.isUpdate && Trigger.isafter && RecursionClass.fireContactUpdate)
    {
        ContactTriggerHandler hndl = new ContactTriggerHandler ();  
        hndl.UpdateContact(trigger.OldMap ,trigger.NewMap);
    }
}

Here is the handler class:
public class ContactTriggerHandler {
    ContactTriggerAction act = new ContactTriggerAction();
    public void UpdateContact(Map<id ,Contact> OldMap , Map<id , Contact> NewMap)
    {
        act.UpdateContactCampaignField(OldMap,NewMap);
    }
}

Here is the class that performs the action:
public class ContactTriggerAction {
    
    //METHOD TO SET CAMPAIGN NAME ON CONTACT IF PQC DATE FIELDS ARE UPDATED
    public void UpdateContactCampaignField(Map<id ,Contact> oldMap , Map<id , Contact> newMap)
    {
        List<Id> filteredContactIds = new List<Id>();
        
        //check for fields if updated
        for(Contact newCon :newMap.values())
        {
           Contact oldCon = oldMap.get(newCon.id);
           
           system.debug('___newCon__ContactStatus__c___'+newCon.ContactStatus__c);
           
           //FILTER CONTACTS WHOSE MostRecentPQCDate OR MostRecentUpsellPQCDate IS UPDATED 

           if( (newCon.MostRecentMQCDate__c!=null && oldCon.MostRecentMQCDate__c != newCon.MostRecentMQCDate__c) || (newCon.MostRecentUpsellMQCDate__c !=null && oldCon.MostRecentUpsellMQCDate__c!= newCon.MostRecentUpsellMQCDate__c) )
               filteredContactIds.add(newCon.Id);
        }
        
        system.debug('___filteredContactIds____'+filteredContactIds+'__current__userId___'+UserInfo.getUserId());
        
        //Functionality doesn't run if trigger is fired by integration user to avoid assignment issues
        if(filteredContactIds!=null && filteredContactIds.size()>0 && UserInfo.getUserId() != System.Label.Integration_User_Id)
            CheckRecentCampaignmembers(filteredContactIds);        
    }
    
    //METHOD TO SET RECENT CAMPAIGN AND FRIST CAMPAIGN ON CONTACT 
    @future
    public static void CheckRecentCampaignmembers(List<Id> contactIdLst)
    {
    
        system.debug('___entered____CheckRecentCampaignmembers____');
             
        List<Contact> cLstToUpdate = new List<Contact>();
        
        //SETTING MostRecentCampaign AND FirstCampaign FIELDS OF CONTACT WITH LastModified CAMPAIGNID
        //FETCH CAMPAIGN MEMBER RECORDS TO WHICH CONTACT IS LINKED 
        for(Contact cont : [Select Id, Name, FirstCampaign__c, MostRecentCampaign__c ,(Select Id, CampaignId , HasResponded from CampaignMembers where HasResponded=TRUE ORDER BY LastModifiedDate desc limit 1) from Contact where Id In :contactIdLst])
        {    
        
            Contact con_update = new Contact(Id = cont.Id);
            
           // system.debug('______cont_____'+cont+'____size____'+cont.CampaignMembers.size()+'___campaignId____'+cont.CampaignMembers[0].CampaignId);
            
            //UPDATING MostRecentCampaign AND FirstCampaign FIELDS ON CONTACT WITH CAMPAIGN NAME
            if(cont.CampaignMembers.size()>0){
                
                con_update.MostRecentCampaign__c  = cont.CampaignMembers[0].CampaignId;
                
                //CHECKS IF FIRST CAMPAIGN FIELD IS BLANK
                if(cont.FirstCampaign__c == null)
                {
                    con_update.FirstCampaign__c = cont.CampaignMembers[0].CampaignId;  
                }
            }
            
            system.debug('____con_update_____'+con_update);
            
            cLstToUpdate.add(con_update);

        }
        
        if(cLstToUpdate!=null && cLstToUpdate.size()>0){
            RecursionClass.fireContactUpdate = false;
            update cLstToUpdate ;

        }
    }
      
}

And here is the recursion class:
Public class RecursionClass{
    
    public static Boolean fireContactUpdate = true;
}
Thanks again for your help!
​​​​​​​​​​​​​​
Alain CabonAlain Cabon
Hi,

The problem could be when you add/remove the campaign member itself (a trigger already on this object? ).

A trigger should exist like: trigger CampaignMemberTrigger on CampainMember ( after insert, after delete ) {  // process the fields of contact   }

You can reduce the cLstToUpdate size but globally that should work.
// system.debug('______cont_____'+cont+'____size____'+cont.CampaignMembers.size()+'___campaignId____'+cont.CampaignMembers[0].CampaignId);
            
            //UPDATING MostRecentCampaign AND FirstCampaign FIELDS ON CONTACT WITH CAMPAIGN NAME

            if(cont.CampaignMembers.size()>0){
                
                con_update.MostRecentCampaign__c  = cont.CampaignMembers[0].CampaignId;
                
                //CHECKS IF FIRST CAMPAIGN FIELD IS BLANK
                if(cont.FirstCampaign__c == null)
                {
                    con_update.FirstCampaign__c = cont.CampaignMembers[0].CampaignId;  
                }
                system.debug('____con_update_____'+con_update); 
                cLstToUpdate.add(con_update);
            }
                 


 
Alain CabonAlain Cabon
The best thing to do is to write a test class ( @istest ) with the scenario that you think doesn't work and to post this test class with the assertions that fail..

Your code should work as expected but the scenario is very restricted (what happens when you add/remove a campaign member ?)
 
Patrick Marks 2Patrick Marks 2
Hey Alain-I actually just needed to remove the @future method in line 29 and change fireContactUpdate to true in line 65, which seems to have addressed my issue. I appreciate your feedback though!
Alain CabonAlain Cabon
Hi Patrick,

Good point if you solved your problem. The asynchronous treatment (@future) and recursionclass are common and could be even a good practice and your code could work despite them but needed a test class to be sure (other triggers and processes could be activated and it is impossible to test for us).

change fireContactUpdate to true? seems useless given that the value is already true (more logical to be false). Your initial code was clear and correct (excepted @future perhaps but we use very often future method with triggers (mandatory in some cases  @future(callout=true)).

Interesting feedback though (usefull for the future)