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
Stuart Miles 25Stuart Miles 25 

apex trigger - Only fire on update of a contact where the criteria change

TRIGGER 1

trigger ContactCountsOnFirm on Contact (after insert,after update,after delete,after undelete) {

  List<Contact> contacts = Trigger.isdelete ? trigger.old : trigger.new;
  Set<Id> accountIds = new Set<Id>();

  for (Contact c : contacts) {
     if (c.accountid != null) {
       accountIds.add(c.accountid);
    } 
  }

  {

list<account> ActiveAdvisers = [select Total_Number_of_Active_Advisers_on_Firm__c,(select id from contacts where ZZ_Active_Adviser_Count__c > 0) from account where id In :accountIds];

 for(account a :ActiveAdvisers)  {
   a.Total_Number_of_Active_Advisers_on_Firm__c = a.contacts.size();
 }
 update ActiveAdvisers;
 
   }
   
  {

list<account> AllIndividuals = [select Total_Number_of_Individuals_on_Firm__c,(select id from contacts ) from account where id In :accountIds];

 for(account a :AllIndividuals)  {
   a.Total_Number_of_Individuals_on_Firm__c = a.contacts.size();
 }
 update AllIndividuals;
 
   }
        
  {

list<account> CampaignEngagedIndividuals = [select Total_No_of_Campaign_Eng_Individuals_on_Firm__c,(select id from contacts where 

ZZ_Campaign_1_Engaged_Individuals_Count__c > 0 or ZZ_Campaign_2_Engaged_Individuals_Count__c > 0 ) from account where id In 

:accountIds];

 for(account a :CampaignEngagedIndividuals)  {
   a.Total_No_of_Campaign_Individuals_on_Firm__c = a.contacts.size();
 }
 update CampaignEngagedIndividuals;
 
   }
   
   {

list<account> CampaignEngagedActAdvisers = [select Total_No_of_Campaign_Eng_Act_Advisers_on_Firm__c,(select id from contacts where 

ZZ_Campaign_1_Engaged_Act_Advisers_Count__c > 0 or ZZ_Campaign_2_Engaged_Act_Advisers_Count__c > 0 ) from account where id In 

:accountIds];

 for(account a :CampaignEngagedActAdvisers)  {
   a.Total_No_of_Campaign_Eng_Act_Advisers_on_Firm__c = a.contacts.size();
 }
 update CampaignEngagedActAdvisers;
 
   }    
 
 }

TRIGGER 2

trigger Update_1and2_EngagementScoreTrigger on Contact (after delete, after insert, after undelete, after update) {

Contact[] cons;
if (Trigger.isDelete) 
    cons = Trigger.old;
else
    cons = Trigger.new;

Set<ID> acctIds = new Set<ID>();
for (Contact con : cons) {
   acctIds.add(con.AccountId);
}

Map<ID, Contact> contactsForAccounts = new Map<ID, Contact>([SELECT Id ,AccountId, Total_Ind_1_Engagement_Score__c, Total_Ind_2_Engagement_Score__c, ZZ_Active_Adviser_Count__c FROM Contact WHERE AccountId IN :acctIds]);

Map<ID, Account> acctsToUpdate = new Map<ID, Account>([SELECT Id, Total_1_Engagement_Score__c, Total_2_Engagement_Score__c, Total_1_Act_Adviser_Eng_Score__c, Total_2_Act_Adviser_Eng_Score__c FROM Account WHERE Id IN :acctIds]);

for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.Total_Ind_1_Engagement_Score__c != NULL) {
        totalValue += con.Total_Ind_1_Engagement_Score__c; 
    }
}
acct.Total_1_Engagement_Score__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}

for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.Total_Ind_2_Engagement_Score__c != NULL) {
        totalValue += con.Total_Ind_2_Engagement_Score__c; 
    }
}
acct.Total_2_Engagement_Score__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}

for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.Total_Ind_1_Engagement_Score__c != NULL && con.ZZ_Active_Adviser_Count__c > 0) {
        totalValue += con.Total_Ind_1_Engagement_Score__c; 
    }
}
acct.Total_1_Act_Adviser_Eng_Score__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}

for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.Total_Ind_2_Engagement_Score__c != NULL && con.ZZ_Active_Adviser_Count__c > 0) {
        totalValue += con.Total_Ind_2_Engagement_Score__c; 
    }
}
acct.Total_2_Act_Adviser_Eng_Score__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}

}

 
Stuart Miles 25Stuart Miles 25
Hi All!
I have broken a number of APEX coding rules due to my current limited coding skills!
I have 2 triggers on the contact object. There are no other triggers on Contact in our Org except for 2 others but they use using roll up helper – free licence).
The combined objective of both is to roll up counts and scores to the related (firm/account) when a contact is either updated, deleted or created. There is a simple formula with count 1 or 0 on each contact for:
1) each contact,
2) active contact and
3) campaign 1 engaged contact
4) campaign 2 engaged contact,

scores also accumulated for the contacts for the same above i.e.

each contact,
active contact and
campaign 1 engaged contact
campaign 2 engaged contact,

We have tested both triggers in isolation and when both active they do exactly what we want them to do i.e. create roll up counts and scores on update, delete and creation of new and existing contacts on an account(firm).  Problem is however we are getting CPU time outs on both triggers and on a larger batch trigger that runs for hours overnight on the Account object as this updates every single contact on the system (this trigger has been in production for years so we don’t want to change anything on that one) The current set up can either cause the batch apex job to fail or either of my 2 triggers.
We want to pursue the apex coding trigger route although we fully appreciate alternatives. Ideally I need to:

1) Consolidate 2 triggers into one.
2) Only fire on update of a contact where the criteria change in the trigger is used rather than on any field update on a contact record.
3) Rewrite the code to make more efficient.

I am hoping that by achieving 1,2, 3 we can avoid CPU timeouts when the batch job runs as it clearly fires my triggers on every single contact. Any help would be much appreciated!
Thanks in advance!
PRAKASH JADA 13PRAKASH JADA 13
Trigger Logic:
-------------------------------------------------------------------------------------
/**
 * Author: 
 * Created Date: 
*/
trigger ContactCountsOnFirm on Contact (after insert, after update, after delete, after undelete) {
    if(Trigger.isDelete) {
        ContactHandler.onBeforeSave(Trigger.old);
    } else {
        ContactHandler.onBeforeSave(Trigger.New);
    }
}

Handler code:
-------------------------------------------
public class ContactHandler {
    
    private static Set<Id> accountIds = new Set<Id>();
    
    public static void onBeforeSave(List<Contact> conList) {
        Map<Id, Contact> contactMap = new Map<Id, Contact>();
        
        Integer ActiveAdvisers              = 0;
        Integer AllIndividuals              = 0;
        Integer CampaignEngagedIndividuals     = 0;
        Integer CampaignEngagedActAdvisers     = 0;
        
        // Iterate over the list of contacts
        for(Contact con : conList) {
            if(con.AccountId != null) {
                accountIds.add(con.AccountId); // To store the unique set of Account Id's
                contactMap.put(con.AccountId, con);
            }
        }
        
        // Condition to check the size of the Account set
        if(accountIds.size() > 0) {
            List<Account> accounts = getAccounts(accountIds);
            
            // Condition to check for the size fo the Account Map
            if(accounts.size() > 0) {
                
                // Loop to iterate over the List of Accounts
                for(Account account : accounts) {
                    
                    // Condition to check for the Account Id that exists in Contact Map
                    if(contactMap.containskey(account.Id) ) {
                        
                        // Condition to check for the Active Advisers
                        if(contactMap.get(account.Id).ZZ_Active_Adviser_Count__c  > 0) {
                            account.Total_Number_of_Active_Advisers_on_Firm__c  += ActiveAdvisers;
                        }
                        
                        // Condition to check for the Campaign Engaged Individuals 
                        if(contactMap.get(account.Id).ZZ_Campaign_1_Engaged_Individuals_Count__c   > 0 || 
                           contactMap.get(account.Id).ZZ_Campaign_2_Engaged_Individuals_Count__c   > 0 ) {
                               account.Total_No_of_Campaign_Individuals_on_Firm__c  += CampaignEngagedIndividuals;
                           }
                        
                        // Condition to check for the Campaign Engaged Act Advisers 
                        if(contactMap.get(account.Id).ZZ_Campaign_1_Engaged_Act_Advisers_Count__c    > 0 || 
                           contactMap.get(account.Id).ZZ_Campaign_2_Engaged_Act_Advisers_Count__c    > 0) {
                               account.Total_No_of_Campaign_Eng_Act_Advisers_on_Firm__c  += CampaignEngagedActAdvisers;
                           }
                        
                        account.Total_Number_of_Individuals_on_Firm__c += AllIndividuals; 
                        
                        /* ******************** Second trigger code ****************************** */
                        
                        // Condition to check for the Total Ind 1 Engagement Score
                        
                        if(contactMap.get(account.Id).Total_Ind_1_Engagement_Score__c  != null) {
                            account.Total_1_Engagement_Score__c  += contactMap.get(account.Id).Total_Ind_1_Engagement_Score__c;
                            
                            // Inner if condition
                            if(contactMap.get(account.Id).ZZ_Active_Adviser_Count__c   > 0) {
                                account.Total_1_Act_Adviser_Eng_Score__c  += account.Total_1_Engagement_Score__c;
                            }
                        }
                        
                        // Condition to check for the Total Ind 2 Engagement Score
                        if(contactMap.get(account.Id).Total_Ind_2_Engagement_Score__c  != null) {
                            account.Total_2_Engagement_Score__c  += contactMap.get(account.Id).Total_Ind_2_Engagement_Score__c;
                            // Inner if condition
                            if(contactMap.get(account.Id).ZZ_Active_Adviser_Count__c > 0) {
                                account.Total_2_Act_Adviser_Eng_Score__c  += account.Total_2_Engagement_Score__c;
                            }
                        }
                    }
                }

               // Performing the DML operation
                update accounts;
            }
        }
        
    }
    
    // Method to get all the accounts
    private static List<Account> getAccounts(Set<Id> accountIds) {
        List<Account> accounts = [SELECT Id, Total_Number_of_Active_Advisers_on_Firm__c, Total_Number_of_Individuals_on_Firm__c, 
                                    Total_No_of_Campaign_Eng_Individuals_on_Firm__c, Total_No_of_Campaign_Eng_Act_Advisers_on_Firm__c,  
                                    Total_1_Engagement_Score__c, Total_2_Engagement_Score__c, Total_1_Act_Adviser_Eng_Score__c, 
                                    Total_2_Act_Adviser_Eng_Score__c FROM account WHERE Id = :accountIds];
        return accounts;
    }

}

Here only one DML is used for both your trigger logics and the only query is used. Hope this will resolve your issue. 
Stuart Miles 25Stuart Miles 25
Hi there Prakash. I seem to be getting somewhere so many thanks. So just to summarise what i have done.... I now have a very simple trigger called "ContactCountsOnFirm" and a Handler class called "ContactHandler". Should i also be separating the last bit of code ie.e the Method to get all the accounts? Also i am getting the following error at the moment >>

Error:Apex trigger ContactCountsOnFirm caused an unexpected exception, contact your administrator: ContactCountsOnFirm: execution of AfterUpdate caused by: System.NullPointerException: Attempt to de-reference a null object: Class.ContactHandler.onBeforeSave: line 51, column 1 - My line 51 is this account.Total_Number_of_Individuals_on_Firm__c += AllIndividuals;

Total_Number_of_Individuals_on_Firm__c 
field on account is defaulted to 0 but should i be initialising it in the code?

thanks, Stuart
Stuart Miles 25Stuart Miles 25
.. also seems to be accumulating engagement scores rather than recalculating by that i mean a contact only has a campaign engagement score of 200 say but each time the contact gets updated the score gets added again ie.e to 400. Appreciate any guidance. thanks
PRAKASH JADA 13PRAKASH JADA 13
Did the code saved successfully? Or do you see any errors while you are saving the code?
Stuart Miles 25Stuart Miles 25
Code saving ok yes, thanks 
Stuart Miles 25Stuart Miles 25
I have now consolidated the trigger and totals roll up nicely but i am still not sure whether the code is efficient. Is it doing more than 2 queries when the trigger fires? getting apex errors caused by: System.LimitException: Apex CPU time limit exceeded. Also i tested the behaviour of this trigger when i updated a debug flag on all contacts on the system using dataloader as this is what our overnight batch job does. but didnt update just got System.LimitException: Apex heap size too large: 7513898 in the error log. 

**********Trigger***********


/**
 * xx 
 * Created Date: 24/09/2019
*/
trigger ContactCountsandScoresOnFirm on Contact (after insert, after update, after delete, after undelete) {
    if(Trigger.isDelete) {
        ContactHandler.onBeforeSave(Trigger.old);
    } else {
        ContactHandler.onBeforeSave(Trigger.New);
    }
}


**********Apex Class***********


public class ContactHandler {
    
    private static Set<Id> accountIds = new Set<Id>();
    
    public static void onBeforeSave(List<Contact> conList) {
        
        
 
  List<Contact> contacts = Trigger.isdelete ? trigger.old : trigger.new;
  Set<Id> accountIds = new Set<Id>();

  for (Contact c : contacts) {
     if (c.accountid != null) {
       accountIds.add(c.accountid);
    } 
  }
  

Map<ID, Contact> contactsForAccounts = new Map<ID, Contact>([SELECT Id ,AccountId, ZZ_Individuals_in_Firm_Count__c, ZZ_Active_Adviser_Count__c, ZZ_CAMPAIGN_1_Engaged_Individuals_Count__c, ZZ_CAMPAIGN_2_Engaged_Individuals_Count__c,Total_Ind_CAMPAIGN_1_Engagement_Score__c, Total_Ind_CAMPAIGN_2_Engagement_Score__c, ZZ_CAMPAIGN_1_Engaged_Act_Advisers_Count__c, ZZ_CAMPAIGN_2_Engaged_Act_Advisers_Count__c FROM Contact WHERE AccountId IN :accountIds]);

Map<ID, Account> acctsToUpdate = new Map<ID, Account>([SELECT Id, Total_Number_of_Active_Advisers_on_Firm__c, Total_Number_of_Individuals_on_Firm__c, Total_No_of_CAMPAIGN_Eng_Individuals_on_Firm__c, Total_No_of_CAMPAIGN_Eng_Act_Advisers_on_Firm__c, Total_CAMPAIGN_1_Engagement_Score__c, Total_CAMPAIGN_2_Engagement_Score__c, Total_CAMPAIGN_1_Act_Adviser_Eng_Score__c, Total_CAMPAIGN_2_Act_Adviser_Eng_Score__c FROM Account WHERE Id IN :accountIds]);


/* ******************** check map to count up all individuals in firm ****************************** */

for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.ZZ_Individuals_in_Firm_Count__c > 0) {
        totalValue += con.ZZ_Individuals_in_Firm_Count__c; 
    }
}
acct.Total_Number_of_Individuals_on_Firm__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}


/* ******************** check map to count up all active advisers in firm ****************************** */

for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.ZZ_Active_Adviser_Count__c > 0) {
        totalValue += con.ZZ_Active_Adviser_Count__c; 
    }
}
acct.Total_Number_of_Active_Advisers_on_Firm__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}


/* ******************** check map to count up all actively engaged individuals at PAM 1 or PAM 2 stage in firm ****************************** */


for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && ((con.ZZ_CAMPAIGN_1_Engaged_Individuals_Count__c > 0) || (con.ZZ_CAMPAIGN_2_Engaged_Individuals_Count__c > 0)))  {
        totalValue += 1; 
    }
}
acct.Total_No_of_CAMPAIGN_Eng_Individuals_on_Firm__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}


/* ******************** check map to count up all actively engaged active advisers at PAM 1 or PAM 2 stage in firm ****************************** */


for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && ((con.ZZ_CAMPAIGN_1_Engaged_Act_Advisers_Count__c > 0) || (con.ZZ_CAMPAIGN_2_Engaged_Act_Advisers_Count__c > 0)))  {
        totalValue += 1; 
    }
}
acct.Total_No_of_CAMPAIGN_Eng_Act_Advisers_on_Firm__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}

   
/* ******************** check map to count up all individuals PAM 1 engagement scores in firm ****************************** */
        
 
 for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.Total_Ind_CAMPAIGN_1_Engagement_Score__c != NULL) {
        totalValue += con.Total_Ind_CAMPAIGN_1_Engagement_Score__c; 
    }
}
acct.Total_CAMPAIGN_1_Engagement_Score__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}


/* ******************** check map to count up all individuals PAM 2 engagement scores in firm ****************************** */

for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.Total_Ind_CAMPAIGN_2_Engagement_Score__c != NULL) {
        totalValue += con.Total_Ind_CAMPAIGN_2_Engagement_Score__c; 
    }
}
acct.Total_CAMPAIGN_2_Engagement_Score__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}

/* ******************** check map to count up all active advisers PAM 1 engagement scores in firm ****************************** */

for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.Total_Ind_CAMPAIGN_1_Engagement_Score__c != NULL && con.ZZ_Active_Adviser_Count__c > 0) {
        totalValue += con.Total_Ind_CAMPAIGN_1_Engagement_Score__c; 
    }
}
acct.Total_CAMPAIGN_1_Act_Adviser_Eng_Score__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}


/* ******************** check map to count up all active advisers PAM 2 engagement scores in firm ****************************** */

for (Account acct : acctsToUpdate.values()) {
Set<Id> conIds = new Set<Id>();
Decimal totalValue = 0;
for (Contact con : contactsForAccounts.values()) {
    if (con.AccountId == acct.Id && con.Total_Ind_CAMPAIGN_2_Engagement_Score__c != NULL && con.ZZ_Active_Adviser_Count__c > 0) {
        totalValue += con.Total_Ind_CAMPAIGN_2_Engagement_Score__c; 
    }
}
acct.Total_CAMPAIGN_2_Act_Adviser_Eng_Score__c = totalValue;
}
if(acctsToUpdate.values().size() > 0) {
    update acctsToUpdate.values();
}

}
}

Any help would be most appreciated!