We know that we can create rollup summary fields for master-detail relationships on salesforce platform. But, there might be a situation where we need rollup summary details for lookup relationship as well. There is no out-of-the-box functionality for this scenario and we have to implement a custom solution for it. Also, this is the most common scenario question asked in salesforce code challenges.
We have four types of rollup summary i.e., COUNT, SUM, MIN, MAX. Let's go through an example for COUNT rollup summary type for lookup relation. As every salesforce org comes up with Account and Contact standard objects by default, I am going to use these objects in this example.
Scenario: Display total number of Contacts associated to an Account on Account record.
Solution:
- Create a number field
TotalContacts__c
on the Account object. - Create a trigger on Contact object.
- As we are going to update the count on Account object, include after trigger contexts in the contact's trigger. Valid trigger contexts are:
after insert
,after update
,after delete
andafter undelete
. - Implement the logic to update the total number of contacts on the associated Account record.
Assuming there is a trigger framework already in place, call our method from contexts shown below.
//After Insert, After Undelete handleAfterInsert(){ ContactTriggerHandler.rollupContactsToAccount(Trigger.new, null); } //After Update handleAfterUpdate(){ ContactTriggerHandler.rollupContactsToAccount(Trigger.new, Trigger.oldMap); } //After Delete handleAfterDelete(){ ContactTriggerHandler.rollupContactsToAccount(Trigger.old, null); } //ContactTriggerHandler.rollupContactsToAccount public static void rollupContactsToAccount(List<Contact> contacts, Map<Id,Contact> oldMap) { Set<Id> accountIds = new Set<Id>(); for (Contact c : contacts) { if (oldMap == null && String.isNotBlank(c.AccountId)) accountIds.add(c.AccountId); else if (oldMap != null && c.AccountId != oldMap.get(c.Id).AccountId) { if (String.isNotBlank(oldMap.get(c.Id).AccountId)) accountIds.add(oldMap.get(c.Id).AccountId); if (String.isNotBlank(c.AccountId)) accountIds.add(c.AccountId); } } if (accountIds.isEmpty()) return; Map<Id, Integer> accountContactCountMap = new Map<Id, Integer>(); for (AggregateResult ar : [ SELECT AccountId, COUNT(Id) contCount FROM Contact WHERE AccountId IN :accountIds GROUP BY AccountId ]) { accountContactCountMap.put((Id) ar.get('AccountId'), (Integer) ar.get('contCount')); } List<Account> accs2Update = new List<Account>(); for (Id accId : accountIds) { accs2Update.add( new Account( Id = accId, TotalContacts__c = accountContactCountMap.containsKey(accId) ? accountContactCountMap.get(accId) : 0 ) ); } update accs2Update; }