Table of Contents
Prerequisite
Create a Custom Field on the Account Object “Number of Contacts” with a Number Data Type that does not have any decimal point in it.
Introduction to Apex Trigger Scenario
Requirement (Roll-up Summary Trigger)
Develop a Solution that will count the related contact related to the Account and store the information in the “Number of Contacts” field.
Note:- The contact can be created/deleted/undeleted and updated from the Account Record. So please keep that in mind
Questions to be asked
Question?
- Which Object
- Contact
- Events
- after insert, after delete, after undelete, after update
- Functionality
- Develop a Solution that will count the related contact related to the Account and store the information in the “Number of Contacts” field.
Hands-On
Apex Trigger
trigger ContactTrigger on Contact (after insert, after update, after delete, after undelete) {
ContactTriggerDispatcher.dispatch(Trigger.OperationType);
}
Dispatcher Class
public class ContactTriggerDispatcher {
public static void dispatch(System.TriggerOperation operationType){
switch on operationType{
WHEN AFTER_INSERT{
ContactTriggerHandler.handleAfterInsert((Map)Trigger.newMap); // Map
}
WHEN AFTER_UPDATE{
ContactTriggerHandler.handleAfterUpdate((Map)Trigger.newMap, (Map)Trigger.oldMap);
}
WHEN AFTER_DELETE{
ContactTriggerHandler.handleAfterDelete((Map)Trigger.oldMap);
}
WHEN AFTER_UNDELETE{
ContactTriggerHandler.handleAfterUndelete((Map)Trigger.newMap); // Map
}
}
}
}
Handler Class
public class ContactTriggerHandler {
public static void handleAfterInsert(Map newRecordsMap){
ContactTriggerHelper.countContact(newRecordsMap);
}
public static void handleAfterUndelete(Map newRecordsMap){
ContactTriggerHelper.countContact(newRecordsMap);
}
public static void handleAfterDelete(Map oldRecordsMap){
ContactTriggerHelper.countContact(oldRecordsMap);
}
public static void handleAfterUpdate(Map newRecordsMap, Map oldRecordsMap){
Set accountIdsSet = new Set();
for(Contact newRecord : newRecordsMap.values() ){
Contact oldRecord = oldRecordsMap.get(newRecord.Id);
if(oldRecord.AccountId <> newRecord.AccountId){
accountIdsSet.add(oldRecord.AccountId); // ABC
accountIdsSet.add(newRecord.AccountId); // XYZ
}
}
List aggregateList = [SELECT count(Id), AccountId
FROM Contact
WHERE AccountId IN:accountIdsSet
Group By AccountId];
List accountListToUpdate = new List();
for(AggregateResult ag : aggregateList){
String accountId = (String)ag.get('AccountId'); // Object
Integer totalContact = (Integer)ag.get('expr0'); // Object
Account accountRecord = new Account();
accountRecord.Id = accountId;
accountRecord.Number_of_Contacts__c = totalContact;
accountListToUpdate.add(accountRecord);
}
update accountListToUpdate;
}
}
Helper Class
public class ContactTriggerHelper {
public static void countContact(Map newRecordsMap){
// key - RecordId
// value - Record
// keySet() - Set - Set
// values() - List - List
/* Step1 - Get Account Ids */
Set accountIdsSet = new Set();
for(Contact con: newRecordsMap.values() ){
if(con.AccountId <> null){ // check if contact is related to account
accountIdsSet.add(con.AccountId);
}
}
/* Solution #1 - Inner Query
List accountList = new List();
accountList = [SELECT Id, (SELECT Id FROM CONTACTS) FROM Account WHERE Id IN: accountIdsSet]; //50K
for(Account acc : accountList){
List contList = acc.CONTACTS; // List of Contact
if(contList != null){
Integer size = contList.size();
acc.Number_of_Contacts__c = size;
}
}
update accountList;
*/
/* Solution #1 will fail in case of Bulk Recods */
/* Solution #2 - Aggregate Query (Recomended) - SUM, COUNT, MIN, MAX, AVG */
/* Aggregate Query - LIMIT 2000 */
List aggregateList = [SELECT count(Id), AccountId
FROM Contact
WHERE AccountId IN:accountIdsSet
Group By AccountId];
//(AggregateResult:{expr0=3, AccountId=0012w00001Pz63SAAR})
//(AggregateResult:{totalContact=3, AccountId=0012w00001Pz63SAAR})
System.debug(aggregateList);
List accountListToUpdate = new List();
for(AggregateResult ag : aggregateList){
String accountId = (String)ag.get('AccountId'); // Object
Integer totalContact = (Integer)ag.get('expr0'); // Object
Account accountRecord = new Account();
accountRecord.Id = accountId;
accountRecord.Number_of_Contacts__c = totalContact;
accountListToUpdate.add(accountRecord);
}
update accountListToUpdate;
}
}
Watch Complete Video
Assignments
→ Apex Trigger 10
Prerequisite
Create the Custom Fields on the Account Object “Sum of Closed Opportunity Amount” & “Sum of Open Opportunity Amount” with a Currency Data Type with 2 Decimal Points.
Requirement (Rollup Summary Trigger)
Develop an Apex trigger to SUM the Opportunity Amount of the Closed Opportunity and Open Opportunities. The closed opportunities are the ones with the Status equal to “Closed Won” OR “Closed Lost”. And store the result in the “Sum of Closed Opportunity Amount” & “Sum of Open Opportunity Amount” fields respectively in the related Account for the opportunity.
Note:- The Opportunity can be created/deleted/undeleted and updated from the Account Record. So please keep that in mind
Scenarios for Update DML
- The account has been changed
- The amount has been changed
Resources
- https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers.htm
- https://trailhead.salesforce.com/content/learn/modules/apex_triggers
- https://trailhead.salesforce.com/content/learn/projects/quick-start-apex-coding-for-admins/create-a-trigger
- https://trailhead.salesforce.com/content/learn/modules/apex_testing/apex_testing_triggers
- https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_triggers_bulk
- https://www.pantherschools.com/category/developer/