+ Start a Discussion
Baylor PeakBaylor Peak 

How to update Rollup count on Trigger when an Activity is deleted?

Hello all,

I am still getting my feet wet with Apex but have come a good way, but still would be very gracilous for this awesome community's input please:)

I have a Trigger on Task after activities are either inserted or updated that performs a rollup for touchpoints on open activities.

The issue is if a user deletes an activity, the rollup is unaffected resulting in an erroneus count for the touchpoints.

This is my code:

trigger TaskRollupSummaryTrigger on Task (after insert, after update) {
    Set<ID> contactIdSet = new Set<ID>();

    for (Task t : trigger.new) {
        if (t.whoId != NULL) {
            contactIdSet.add(t.whoId);
        }
    }
    
    if (contactIdSet.size() > 0) {
        AggregateResult[] groupedResults = [SELECT WhoId, Type, Count(ID) FROM Task WHERE WhoId IN :contactIdSet AND Type IN ('Call','Email','In-Person Meeting') GROUP BY WhoId, Type];
        
        Map<ID, Contact> contactMap = new Map<ID, Contact>([SELECT Id, Total_Call_Touchpoints__c FROM Contact WHERE Id IN :contactIdSet]);
    
        for (AggregateResult g : groupedResults) {    
            System.debug('####' + g.get('WhoId') + ' : ' + g.get('expr0'));
            Contact c = contactMap.get((String)g.get('WhoId'));
            
            if ((String)g.get('Type') == 'Call') {
                c.Total_Call_Touchpoints__c = (Decimal)g.get('expr0');
            } else if ((String)g.get('Type') == 'Email') {
                c.Total_Email_Touchpoints__c = (Decimal)g.get('expr0');
            } else if ((String)g.get('Type') == 'In-Person Meeting') {
                c.Total_Meeting_Touchpoints__c = (Decimal)g.get('expr0');
            }
        }
        update contactMap.values();
    }
}

First question is why isn't the SELECT counting acxtivities that have been deleted?

Is there a way to get the trigger to see when activities are deleted and refelct that in the ttouchpoint rollups?

Best Answer chosen by Baylor Peak
@anilbathula@@anilbathula@
Hi Baylor,

You need to tell the trigger to fire on delete, mention after delete in the trigger.

use this code:
 
trigger TaskRollupSummaryTrigger on Task (after insert, after update,after delete) {
    Set<ID> contactIdSet = new Set<ID>();
	if(Trigger.isInsert || Trigger.isupdate){
		for (Task t : trigger.new) {
			if (t.whoId != NULL) {
				contactIdSet.add(t.whoId);
			}
		}
	}

	if(Trigger.isDelete){
		For(task t : Trigger.old){
			contactIdSet.add(t.whoId);
		}
	}
    
    if (contactIdSet.size() > 0) {
        AggregateResult[] groupedResults = [SELECT WhoId, Type, Count(ID) FROM Task WHERE WhoId IN :contactIdSet AND Type IN ('Call','Email','In-Person Meeting') GROUP BY WhoId, Type];
        
        Map<ID, Contact> contactMap = new Map<ID, Contact>([SELECT Id, Total_Call_Touchpoints__c FROM Contact WHERE Id IN :contactIdSet]);
    
        for (AggregateResult g : groupedResults) {    
            System.debug('####' + g.get('WhoId') + ' : ' + g.get('expr0'));
            Contact c = contactMap.get((String)g.get('WhoId'));
            
            if ((String)g.get('Type') == 'Call') {
                c.Total_Call_Touchpoints__c = (Decimal)g.get('expr0');
            } else if ((String)g.get('Type') == 'Email') {
                c.Total_Email_Touchpoints__c = (Decimal)g.get('expr0');
            } else if ((String)g.get('Type') == 'In-Person Meeting') {
                c.Total_Meeting_Touchpoints__c = (Decimal)g.get('expr0');
            }
        }
        update contactMap.values();
    }
}

Thanks
Anil.B​​​​​​​

All Answers

@anilbathula@@anilbathula@
Hi Baylor,

You need to tell the trigger to fire on delete, mention after delete in the trigger.

use this code:
 
trigger TaskRollupSummaryTrigger on Task (after insert, after update,after delete) {
    Set<ID> contactIdSet = new Set<ID>();
	if(Trigger.isInsert || Trigger.isupdate){
		for (Task t : trigger.new) {
			if (t.whoId != NULL) {
				contactIdSet.add(t.whoId);
			}
		}
	}

	if(Trigger.isDelete){
		For(task t : Trigger.old){
			contactIdSet.add(t.whoId);
		}
	}
    
    if (contactIdSet.size() > 0) {
        AggregateResult[] groupedResults = [SELECT WhoId, Type, Count(ID) FROM Task WHERE WhoId IN :contactIdSet AND Type IN ('Call','Email','In-Person Meeting') GROUP BY WhoId, Type];
        
        Map<ID, Contact> contactMap = new Map<ID, Contact>([SELECT Id, Total_Call_Touchpoints__c FROM Contact WHERE Id IN :contactIdSet]);
    
        for (AggregateResult g : groupedResults) {    
            System.debug('####' + g.get('WhoId') + ' : ' + g.get('expr0'));
            Contact c = contactMap.get((String)g.get('WhoId'));
            
            if ((String)g.get('Type') == 'Call') {
                c.Total_Call_Touchpoints__c = (Decimal)g.get('expr0');
            } else if ((String)g.get('Type') == 'Email') {
                c.Total_Email_Touchpoints__c = (Decimal)g.get('expr0');
            } else if ((String)g.get('Type') == 'In-Person Meeting') {
                c.Total_Meeting_Touchpoints__c = (Decimal)g.get('expr0');
            }
        }
        update contactMap.values();
    }
}

Thanks
Anil.B​​​​​​​
This was selected as the best answer
Baylor PeakBaylor Peak

Wow!, that was it, thank you so mucg sir. 

I have a followup question please?

Why when I just so a SOQL querry in  dev console, am I still getting back a blank result for WhoID when I am querring on the following:

SELECT WhoId, Type, Count(ID) FROM Task WHERE Type IN ('Call', 'Email', 'In-Person Meeting') GROUP BY WhoId, Type ORDER BY Type

Subsequently, in my sandbox, I have deleted every activity for all contacts, yet on both the contact page for their touchpoints, it is still showing a single record for the rollup and as well on my SOQL result, it is showing a single record, however, the result for WhoID is blank?

regards to your guidance on this:)

@anilbathula@@anilbathula@
Hi Baylor,

just add a new task for that contact and see whether the roll up is update or not?

Thanks
Anil.B
Baylor PeakBaylor Peak

See that's teh issue the conact is blank. When I just do a SELECT WhoId FROM Task

It resturns two rols but the WhoId is blank and if I run teh entire querry SELECT WhoId, Type, Count(ID) FROM Task WHERE Type IN ('Call', 'Email', 'In-Person Meeting')

It returns one row with no data in the WhoId, the Type is Email and Count(ID) 2

So, if I have deleted all activities(tasks) from all contacts, how is thsi occuring?
 

@anilbathula@@anilbathula@
Hi Baylor ,

share your query and out put as screen shot.

Thanks
Anil.B
Baylor PeakBaylor Peak
SELECT WhoId, Type, Count(ID) FROM Task WHERE Type IN ('Call', 'Email', 'In-Person Meeting') GROUP BY WhoId, Type ORDER BY Type

User-added image
@anilbathula@@anilbathula@
Hi Baylor,

There are two orphan task which are not linked with any contact  in your org and in your query you are not specifying whether Whoid !=null .
Thats the reason its counting as 2 ,if you put Whoid!=null it will display correct results.

Thanks
Anil.B
Baylor PeakBaylor Peak

Ahh ok, again thank you:)

So last part then. thsi is what I have added to my test class but the test is failing...

   Account acc =[SELECT Name from Account where name = 'test account'];
        delete acc;
        
        Contact con = [SELECT Name from Contact where AccountId=:acct.Id];
        delete con;


The entire testclass is:

@isTest
public class TaskRollupSummaryTriggerTest {
    static testMethod void testTaskTrigger(){
        //First, prepare 200 tasks for the test data
        Account acct = new Account(name='test account');
        insert acct;
        
        Contact contact = new Contact(AccountId=acct.Id, FirstName='John', LastName='Doe');
        insert contact;
        
        Account acc =[SELECT Name from Account where name = 'test account'];
        delete acc;
        
        Contact con = [SELECT Name from Contact where AccountId=:acct.Id];
        delete con;
        
        Task[] tasksToCreate = new Task[]{};
        for(Integer x=0; x<100;x++){
            Task t = new Task(WhoId=contact.Id, Type='Call');
            tasksToCreate.add(t);
        }
        
        for(Integer x=0; x<49;x++){
            Task t = new Task(WhoId=contact.Id, Type='Email');
            tasksToCreate.add(t);
        }
        
        for(Integer x=0; x<49;x++){
            Task t = new Task(WhoId=contact.Id, Type='In-Person Meeting');
            tasksToCreate.add(t);
        }
        
        //Now insert data causing an contact trigger to fire. 
        Test.startTest();
        insert tasksToCreate;
        Test.stopTest();    
    }   
}

@anilbathula@@anilbathula@
try these code:-

 
@isTest
public class TaskRollupSummaryTriggerTest {
    static testMethod void testTaskTrigger(){
	
	   Test.startTest();
        
        Account acct = new Account(name='test account');
        insert acct;
        
        Contact contact = new Contact(AccountId=acct.Id, FirstName='John', LastName='Doe');
        insert contact;
		
		List<task>tsk=new list<Task>();
		task t=new task();		 
        t.Subject='Email';
		t.type='Email';
        t.Status='In Progress';
        t.whoid=contact.id;
		t.priority='Normal';
        tsk.add(t);
		
		task t1=new task();		 
        t1.Subject='Call';
		t1.type='Call';
        t1.Status='In Progress';
        t1.whoid=contact.id;
		t1.priority='Normal';
        tsk.add(t1);
		
		Insert tsk;
		Delete tsk;
		
		Test.StopTest();
	}
}
Thanks
Anil.B
Baylor PeakBaylor Peak
That was it! the Delete at the end of the test is what I was missing. I was messing with System.assert and all kinds of stuff. Thank you sir!:D
 
Baylor PeakBaylor Peak
Helllo Anil.
Ran into a snag that I can't figure out why it's occuring.

I've notcied that my Total Touchpoint fields are not reflecting correctly when there are no Activities under Activity History. So letys say Total Contacs is showing 10, I can add and delete Tasks and it updates correctly until I delete the last Task. Once I delete the last Task under Activity History, it leaves 1 for the Total Contacts field. Beating my head aroudn this....

Here is my whole code and I would love some input on whay it's not working like it shouyld be unless I am missing something?:D

trigger TaskRollupSummaryTrigger on Task (after insert, after update, after delete) {
    Set<ID> contactIdSet = new Set<ID>();
    if(Trigger.isInsert || Trigger.isupdate){
        for (Task t : trigger.new) {
            if (t.whoId != NULL) {
                contactIdSet.add(t.whoId);
            }
        }
    }

    if(Trigger.isDelete){
        For(Task t : trigger.old){
            contactIdSet.add(t.whoId);
        }
    }
    
    if (contactIdSet.size() > 0) {
        AggregateResult[] groupedResults = [SELECT WhoId, Type, Count(ID) FROM Task WHERE WhoId IN :contactIdSet AND Type IN ('Call','Email','In-Person Meeting') GROUP BY WhoId, Type];
        
        Map<ID, Contact> contactMap = new Map<ID, Contact>([SELECT Id, Total_Call_Touchpoints__c FROM Contact WHERE Id IN :contactIdSet]);
    
        for (AggregateResult g : groupedResults) {    
            System.debug('####' + g.get('WhoId') + ' : ' + g.get('expr0'));
            Contact c = contactMap.get((String)g.get('WhoId'));
            
            if ((String)g.get('Type') == 'Call') {
                c.Total_Call_Touchpoints__c = (Decimal)g.get('expr0');
            } else if ((String)g.get('Type') == 'Email') {
                c.Total_Email_Touchpoints__c = (Decimal)g.get('expr0');
            } else if ((String)g.get('Type') == 'In-Person Meeting') {
                c.Total_Meeting_Touchpoints__c = (Decimal)g.get('expr0');
            }
        }
        update contactMap.values();
    }
}

Regards for all your help:)
@anilbathula@@anilbathula@
Hi Baylor,

when you delete the last record the aggregate query will become null and it wont enter into the for loop to update fileds.
Instead of doing everyting in the aggregate query just change the code to the below.
 
trigger TaskRollupSummaryTrigger on Task (after insert, after update,after delete) {
    Set<ID> contactIdSet = new Set<ID>();
    if(Trigger.isInsert || Trigger.isupdate){
        for (Task t : trigger.new) {
            if (t.whoId != NULL) {
                contactIdSet.add(t.whoId);
            }
        }
    }

    if(Trigger.isDelete){
        For(task t : Trigger.old){
            contactIdSet.add(t.whoId);
        }
    }
    
    if (!contactIdSet.isempty()) {
    
        Map<Id,Decimal>Mapofcall=new Map<ID,Decimal>();
        Map<Id,Decimal>MapofEmail=new Map<ID,Decimal>();
        Map<Id,Decimal>MapofMeeting=new Map<ID,Decimal>();
        
        AggregateResult[] groupedResults = [SELECT WhoId, Type, Count(ID) FROM Task WHERE WhoId IN :contactIdSet AND Type IN ('Call','Email','In-Person Meeting') GROUP BY WhoId, Type];
       
    
        for (AggregateResult g : groupedResults) {  
            
            if ((String)g.get('Type') == 'Call') {
                Mapofcall.put((Id)g.get('Whoid'),(Decimal)g.get('expr0'));
            } else if ((String)g.get('Type') == 'Email') {
               MapofEmail.put((Id)g.get('Whoid'),(Decimal)g.get('expr0'));
            } else if ((String)g.get('Type') == 'In-Person Meeting') {
                MapofMeeting.put((Id)g.get('Whoid'),(Decimal)g.get('expr0'));
            }
        }
       
       list<contact>lstcon=[SELECT Id, Total_Call_Touchpoints__c,Total_Email_Touchpoints__c,Total_Meeting_Touchpoints__c  FROM Contact WHERE Id IN :contactIdSet];
       for(contact c:lstcon){
         c.Total_Call_Touchpoints__c=Mapofcall.get(c.id);
         c.Total_Email_Touchpoints__c=MapofEmail.get(c.id);
         c.Total_Meeting_Touchpoints__c =MapofMeeting.get(c.id);         
       }
       update lstcon;
    }
}

Thanks
Anil.B​​​​​​​