Wednesday, 25 March 2015

Count Activities against an Opportunity

Counting Activities logged against an Opportunity

Whenever I get a request for a new Trigger I usually hit Google first to see whether some developer has kindly shared a similar challenge and code solution.

On this occasion I was asked to display a count of all activities logged against an Opportunity record. This should answer the question of "how much work did we do for this?".

I found this first example of a class which would perform roll up summary type triggers for Open Activities across Account, Lead, Contact, and Opportunity and started looking at the structure.

http://rjpalombo.com/2013/11/counting-open-activities-salesforce/


After a bit more searching though I found another example Trigger which seemed closer to my requirements:
http://www.radialweb.com/2010/08/summarizing_salesforce_fields_with_triggers/

With a little modificiation this is what I came up with:

Trigger to count how many calls have been made relating to an Opportunity

Steps:

1. Create a custom field on the Opportunity with Field Name Activity_Count. You can change the label field to be more meaningful though like “Completed calls” but Field API name has to be this.

2. Create the trigger TaskUpdateOpportunity

 
trigger TaskUpdateOpportunity on Task (after delete, after insert, after undelete, after update) {


//This trigger counts calls made against an Opportunity

Set<ID> oppIds = new Set<ID>();

//We only care about tasks linked to opportunities.

String prefix = OpportunityActivityCount.oppPrefix;

//Add any opportunity ids coming from the new data

if (Trigger.new != null) {

for (Task t : Trigger.new) {

if (t.WhatId != null && String.valueOf(t.whatId).startsWith(prefix) ) {

oppIds.add(t.whatId);

}

}

}


//Also add any opportunity ids coming from the old data (deletes, moving an activity from one opportunity to another)

if (Trigger.old != null) {

for (Task t : Trigger.old) {

if (t.WhatId != null && String.valueOf(t.whatId).startsWith(prefix) ) {

oppIds.add(t.whatId);

}

}

}



if (oppIds.size() > 0)

OpportunityActivityCount.updateOpportunityCounts(oppIds);



}


3. Create an Apex Class called OpportunityActivityCount

 

public with sharing class OpportunityActivityCount {

public static Boolean didRun = false;
public static String oppPrefix = Opportunity.sObjectType.getDescribe().getKeyPrefix();

/*
* Takes a set of opportunity IDs, queries those opportunities, and updates the activity count if appropriate
*/
public static void updateOpportunityCounts(Set<ID> oppIds) {

if (didRun == false) { //We only want this operation to run once per transaction.
didRun = true;

//Query all the opportunites, including the tasks child relationships
List<Opportunity> opps = [SELECT ID, activity_count__c, (SELECT ID FROM Tasks where Type='Call' AND Status='Completed'), (SELECT ID FROM Events) FROM Opportunity WHERE ID IN :oppIds];
List<Opportunity> updateOpps = new List<Opportunity>();

for (Opportunity o : opps) {
Integer count = o.tasks.size() + o.events.size();

if (o.activity_count__c != count) {
o.activity_count__c = count;
updateOpps.add(o); //we're only doing updates to opps that have changed...no need to modify the others
}
}

//Update the appropriate opportunities
try {
update updateOpps;
} catch (Exception e) {
//This is controversial. Anything could happen when updating the opportunity..validation rule, security, etc. The key is we don't
//want the event update to fail...so we put a try catch around the opp update to make sure we don't stop that from happening.
}
}
}



}