Salesforce Roll-up Summary Utility for Lookup Fields with Filter

  • submit to reddit
Salesforce Roll-Up Summary Utility

Salesforce Roll-Up Summary Utility

A while back I wrote a Salesforce roll-up summary trigger which mimics the declarative roll-up summary functionality provided by Salesforce.com.  I received several comments from the community, some relating to additional functionality (such as MAX, MIN, and COUNT) others about filters and some about code structure. So I decided to revisit my original solution and put together something a bit more generic and robust.

*Note: I should mention that this utility can be used for objects with Master-Detail relationships as well as Look-up relationships. Also, this utility only provides summary for numeric fields (double, integer, decimal primitive types), it DOES NOT support text, date or any other primitive types. Finally, while this utility and the examples shown here are written for “bulk” processing, they have not been tested with large data sets (50K records or more).

Update (03/16/2012): I’ve updated the utility to support multiple fields with multiple operations. The utility is optimized for bulk processing.  This video outlines the changes:

Using the Salesforce sObject Method and Dynamic SOQL

In order to provide maximum flexibility for this utility class, I am employing the use of the sObject methods and dynamic SOQL.  Why is this useful? Good question. First, this allows the utility to be used across any sObject type (e.g. Standard and Custom Objects) without having to write more code (or rather, without having to write object specific code). Second, I wanted to build something that a beginner could use with minimal effort.

Salesforce Roll-up Summary Trigger with Utility Example

Below is a usage example of how this utility can be leveraged to create a simple roll-up summary trigger. For this example we go back to the Opportunity (parent) and Payments__c (child) scenario: we want to populate the Opportunity.Total_Payments__c field with sum of all Payments__c.Amount__c fields.

trigger OpportunityRollUpPayments on Payment__c (after delete, after insert, 
after update, after undelete) {
	
    if(trigger.isInsert || trigger.isUpdate || trigger.isUnDelete){
		
		list<RollUpSummaryUtility.fieldDefinition> fieldDefinitions = 
		new list<RollUpSummaryUtility.fieldDefinition> {
			new RollUpSummaryUtility.fieldDefinition('SUM', 'Amount__c', 
			'Total_Payments__c'), 
			new RollUpSummaryUtility.fieldDefinition('MAX', 'Amount_2__c', 
			'Total_Payments_2__c')
		};
        
        RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, trigger.new, 
        'Payment__c', 'Opportunity__c', 'Opportunity', '');
        
    }
    
    if(trigger.isDelete){
        
        list<RollUpSummaryUtility.fieldDefinition> fieldDefinitions = 
        new list<RollUpSummaryUtility.fieldDefinition> {
            new RollUpSummaryUtility.fieldDefinition('SUM', 'Amount__c', 
            'Total_Payments__c'), 
            new RollUpSummaryUtility.fieldDefinition('MAX', 'Amount_2__c', 
            'Total_Payments_2__c')
        };
        
        RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, trigger.old, 
        'Payment__c', 'Opportunity__c', 'Opportunity', '');
        
    }
	
}

Pretty simple, right? All we’re doing is passing a few variables across to the utility and it handles the rest for you. Now, let’s talk about these variables. I’ve updated the demo page to use this utility, feel free to try out the Salesforce Roll-up Summary utility demo.

Salesforce Roll-up Summary Utility Variables and Considerations

The utility accepts the following variables:


RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, records, childObject, 
childParentLookupField, parentObject, queryFilter);

fieldDefinitions (list<RollUpSummaryUtility.fieldDefinition>) – The list of field definitions.  The fieldDefinition class is comprised of three variables:

  1. operation (String) – Can be either SUM, MAX, MIN, or COUNT. When using COUNT, the childField must ALWAYS be ‘ID’.
  2. childField (String) – The api name for the child object’s field that contains the values you wish to summarize. Remember, when using the COUNT operation, the value for this variable must ALWAYS be ‘ID’.
  3. parentField (String) – The api name for the parent object’s field that where the summarized value of the child object field will be stored.

Here’s an example of how you can create a fieldDefinitions list:


list<RollUpSummaryUtility.fieldDefinition> fieldDefinitions = 
new list<RollUpSummaryUtility.fieldDefinition> {
new RollUpSummaryUtility.fieldDefinition('SUM', 'Amount__c', 'Total_Payments__c'), 
new RollUpSummaryUtility.fieldDefinition('MAX', 'Amount_2__c', 'Total_Payments_2__c')
};

records (list<sObject>) – The list of records being passed to the utility. When using with a trigger this can either be trigger.new or trigger.old. Tip: Use trigger.old when executing during a trigger.isDelete operation.

childObject (String) – The API name for the child object. This object must have either a look-up or master-detail relationship to a parent object.

childParentLookupField (String) – The API name for the child object field that performs a look-up to a parent object where the summary results will be stored.

parentObject (String) – The API name for the parent object related to the child object. The childParentLookupField variable must be related to the object supplied in this variable.

queryFilter (String) – An SOQL statement that will be used to filter the child object records. The string must always begin with ‘and’ if it’s being used. If no filter is needed simply supply an empty text value of ”, never supply a NULL value. Let’s say that based on the above example we only want to get a sum of payments made by check, our code would look like this:


RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, trigger.old, 'Payment__c', 
'Opportunity__c', 'Opportunity', 'and Type__c = \'Check\'');

Code for Salesforce Roll-up Summary Utility

Finally, the star of the show. Here’s the code used by the utility.  I have not written a test class for it but you should be able to get started by looking at the original trigger I published 9 months ago. If I get some time I’ll put together the unit tests and publish them here but they will be specific to the example above. If you use the utility with, let’s say, an Account trigger, you will have to write your test class around the Account object.

public class RollUpSummaryUtility {
	
	//the following class will be used to house the field names
    //and desired operations
    public class fieldDefinition {
        public String operation {get;set;}
        public String childField {get;set;}
        public String parentField {get;set;}
        
        public fieldDefinition (String o, String c, String p) {
            operation = o;
            childField = c;
            parentField = p;
        }
    }
    
    public static void rollUpTrigger(list<fieldDefinition> fieldDefinitions,
    list<sObject> records, String childObject, String childParentLookupField, 
    String parentObject, String queryFilter) {
        
        //Limit the size of list by using Sets which do not contain duplicate
        //elements prevents hitting governor limits
        set<Id> parentIds = new set<Id>();
        
        for(sObject s : records) {
            parentIds.add((Id)s.get(childParentLookupField));
        }
        
        //populate query text strings to be used in child aggregrator and 
        //parent value assignment
        String fieldsToAggregate = '';
        String parentFields = '';
        
        for(fieldDefinition d : fieldDefinitions) {
            fieldsToAggregate += d.operation + '(' + d.childField + ') ' + 
            ', ';
            parentFields += d.parentField + ', ';
        }
        
        //Using dynamic SOQL with aggergate results to populate parentValueMap
        String aggregateQuery = 'Select ' + fieldsToAggregate + 
        childParentLookupField + ' from ' + childObject + ' where  ' + 
        childParentLookupField + ' IN :parentIds ' + queryFilter + ' ' +
        ' group by ' + childParentLookupField;
        
        //Map will contain one parent record Id per one aggregate object
        map<Id, AggregateResult> parentValueMap = 
        new map <Id, AggregateResult>();
        
        for(AggregateResult q : Database.query(aggregateQuery)){
            parentValueMap.put((Id)q.get(childParentLookupField), q);
        }
        
        //list of parent object records to update
        list<sObject> parentsToUpdate = new list<sObject>();
        
        String parentQuery = 'select ' + parentFields + ' Id ' +
         ' from ' + parentObject + ' where Id IN :parentIds';
        
        //for each affected parent object, retrieve aggregate results and 
        //for each field definition add aggregate value to parent field
        for(sObject s : Database.query(parentQuery)) {
            
            Integer row = 0; //row counter reset for every parent record
            for(fieldDefinition d : fieldDefinitions) {
            	String field = 'expr' + row.format();
                AggregateResult r = parentValueMap.get(s.Id);
                //r will be null if no records exist 
                //(e.g. last record deleted)
                if(r != null) { 
                    Decimal value = ((Decimal)r.get(field) == null ) ? 0 : 
                        (Decimal)r.get(field);
                    s.put(d.parentField, value);
                } else {
                    s.put(d.parentField, 0);
                }
                row += 1; //plus 1 for every field definition after first
            }
            parentsToUpdate.add(s);
        }
        
        //if parent records exist, perform update of all parent records 
        //with a single DML statement
        if(parentsToUpdate.Size() > 0) {
            update parentsToUpdate;
        }
        
    }

}

Please feel free to slice and dice this code all you want and please I encourage you to share your feed back (good or bad). I’m going to try to devote more time to this website so if you have any questions or need assistance please leave a comment here and I’ll get to you as soon as I can.


I have been working with the Salesforce.com platform since 2003 in both administrative and development roles. I also have experience in web design working with HTML, CSS, Javascript, and PHP. I hold certification for the Salesforce.com Developer program. I am also an employee of Model Metrics, a Salesforce.com company.

  • Troy Hoshor

    I just stumbled on this when looking for a separate issue.  Ironically, I’ve built something very similar to this for my current org that (unfortunately) isn’t nearly as generic as yours– so I should first say: kudos!  It’s fantastic to see a use-case scenario of this, and to know I’m not the only SF developer working around the roll-up summary limit with Apex.

    One question I have for you is how you plan to address SOQL query limits.  Obviously SF has increased the limit to 100 over the last year, but if your organization is particularly roll-up heavy, like mine is, it might be of concern.

    Using this formula, say, 15-20x on a given trigger change (say: Opportunity Update) means that’s 15-20 queries absorbed by this code alone.

    Do you have any suggestions for aggregating this code to work in groups if there are multiple custom roll-up replacements per object?

  • http://www.anthonyvictorio.com Anthony Victorio

    Hello Troy, thanks for the kudos!

    You don’t have to worry about the 100 query limit with this method because it uses sets, maps and lists outside of “for” loops to store and manipulate the data.  It also only uses 1 DML statement to process all parent records.

    In total, this method uses 3 SOQL queries and 1 DML statement per execution regardless of how many records are processed in that execution. The only real limit you have to worry about is the size of the records populated in any of the lists (the current limit is 50,000 records). However, that is unlikely to be a problem as it will be dependent on how the data is loaded.  If you’re loading (or deleting) mass number of records (as I have, over 50K records) but the records are batched with each batch being under 2,000 records (which I believe is the maximum batch size for data loader), then you will never hit that limit.

  • http://www.anthonyvictorio.com Anthony Victorio

    On that note Troy, I haven’t tried my hardest to break this trigger so by all means, put it to the test and let me know what kind of limitations you hit and we can work together to overcome them.

  • Troy Hoshor

    Hm– perhaps I’m mistunderstanding– dynamic DML is not my area of expertise :) What if you desire to aggregate, 3 fields over the maximum roll-up summary limit (to 28 instead of 25).  Wouldn’t you need multiple calls to this Utility code each time you wanted to calculate a different roll-up?  Abbreviated example:

    if (trigger.isUpdate) {

    RollUpSummaryUtility.rollUpTrigger(‘SUM’, trigger.new, FieldA__c, ….., ParentFieldA__c);

    RollUpSummaryUtility.rollUpTrigger(‘SUM’, trigger.new, FieldB__c, …..,
    ParentFieldB__c);

    RollUpSummaryUtility.rollUpTrigger(‘SUM’, trigger.new, FieldC__c, …..,

    ParentFieldC__c);

    }

    Let me know.  Nice to hear back from you so fast!

  • http://www.anthonyvictorio.com Anthony Victorio

    You have two options at that point from what I can see. 1) modify the utility to support more than one field so that all your summaries take place with a single execution of the trigger or 2) as you pointed out, call the utility multiple times (in which case, after a while, you may begin to hit governor limits).

    I think I can modify this utility so that you can pass multiple field names and simply separate them by a coma (,) or a semi colon (;). It certainly sounds like an interesting challenge to me :)

  • Troy Hoshor

     I was thinking about that as well.  Possibly you could pass a map of aggregate field name + parent field names to the Utility class, turn those maps into a long string (for passing to a single SOQL query).  The only complication that I can see is different filter requirements (the ‘ AND asjdksajldsa’ section).

    Maybe it’s worth forking to create one single field utility vs. multiple field utility?

  • http://www.anthonyvictorio.com Anthony Victorio

    I agree, I think it’s worth devising a method to support multiple fields.  I like your idea of passing across a map instead of a long text field, I’ll see what I can put together over the weekend.  Feel free to give me a shout if you think of anything else :)

  • raptor_demon

    Hi,

    thanks for the code, this is awesome!!

    for the test classes, would the tests go on the Trigger or on the Apex class?

    thanks
    Raptor

  • http://www.anthonyvictorio.com Anthony Victorio

    Thanks Raptor! You will need to create a test class for the trigger. As the trigger will employ the class method, when the test runs it “should” provide coverage for both trigger and class.

  • raptor_demon

    Thanks,

    You don’t happen to ahve an example of the tests to get me started, tests have always been my weak point.
    Raptor

  • http://www.anthonyvictorio.com Anthony Victorio

    Raptor, see the test class on this page: 
    http://www.anthonyvictorio.com/salesforce/roll-up-summary-trigger/.  I’ve just tested it with the use of this utility and it works perfectly, 100% coverage.  You’ll have to modify it, of course, to fit the schema of your objects. 

  • http://www.anthonyvictorio.com Anthony Victorio

    Troy, so I began toying around with the idea of updating this method to support multiple fields but I don’t think it can be done efficiently. Using a map or a class to store the child/parent field definitions along with the operators (sum, max, min, etc) in order to dynamically create an aggregate query is possible with minimal affect to the number of queries executed by the method. The problem lies with assigning the values from the aggregate results to the parent records in a dynamic manner.  It can be done but it would require a for loop inside the parent query, so for each parent record you would perform an additional query for each field you’re aggregating which would easily send into governor limit land.  I can put the solution together if you’d like to experiment with it.

  • Charlie Voss

    Anthony, thank you for this!!!  Works great!

  • http://www.anthonyvictorio.com Anthony Victorio

    Thanks Charlie :) Be sure to checkout the latest changes, I just updated this blog post about one minute ago.

  • http://www.anthonyvictorio.com Anthony Victorio

    So I was entirely wrong. I couldn’t help my self from being curious about this so I decided to give it a go and it works beautifully.  I’ve updated this blog post with the latest revisions to the code.

  • Charlie Voss

    Thanks Anthony.  This new utility is even better!  I got it to work just fine.  Word of caution to other users though … Initially everything was bombing out because the API names on some of my fields were very long, and the error messages said the aliases in the class couldn’t handle anything larger than 25 characters.  But once I shortened the API names on my fields everything worked great.

    I can’t tell you how much I appreciate this utility!

  • http://www.anthonyvictorio.com Anthony Victorio

    Good call Charlie, I’ll see what I can do in order to accommodate for that. I may just have to rely on the order in which the fieldDefinitions list is populated.

  • http://www.anthonyvictorio.com Anthony Victorio

    Charlie, thanks again for pointing out that bug. I’ve updated the utility so that it doesn’t rely on aliases to retrieve field values from the map. Instead it’s using a row counter to match up the child fields based on the order they were entered in the fieldDefinitions list. Meaning, this utility will now work with any size field name. :)

  • Charlie Voss

    Thanks Anthony.  Sadly it appears I won’t be able to deploy this solution to Production.  Ultimately I was going to need 16 Triggers across 4 Objects (so 4 per object, required due to the Where clause).

    I finally got everything built in Sandbox and when I performed the Run All Tests action, I received the following failure message on every Test class:

    System.LimitException: Too many SOQL queries: 101 

    I’m bummed!

  • http://www.anthonyvictorio.com Anthony Victorio

    Hmm, I’m bummed too. Let me know if you’d like to a do a code review one of these days and we’ll see if we can get to the bottom of things.

  • Miranda

    This is fantastic – thank you!  Works like a charm – you are one of the angels of the internet!!! 

  • http://www.anthonyvictorio.com Anthony Victorio

    Thank you Miranda :) please let me know if you run into any challenges.

  • Cwbeall

    Hey Anthony,
    Thanks for taking the time to go through this. It is really a huge benefit. 

    Unfortunately, I come from an admin background and so the coding is a new challenge for me.  I’ve been reading up on Java and Apex to try to get my bearings, but I keep getting errors in this code that disallow saving.  My particular task is trying to get a Rollup Trigger from a Child Account to a parent Account. 

    Here is the Code for the trigger that I have so far (based on the above). I am getting an “Unexpected token: ‘}’ ” for Line 11.  Not sure what I need to do, but any help would be appreciated. 

    ——————————–
    trigger AccountRollUpSample on Account (after delete, after insert,after update, after undelete) {     if(trigger.isInsert || trigger.isUpdate || trigger.isUnDelete){         list fieldDefinitions =        new list {            new RollUpSummaryUtility.fieldDefinition(‘SUM’, ‘X2012_YTD_PGP__c’,            ‘X2012_YTD_PGP_Customer__c’),        };         RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, trigger.new,        ‘Account’, ‘ParentId’, ‘Account’, ”);     }     if(trigger.isDelete){         list fieldDefinitions =        new list {            new RollUpSummaryUtility.fieldDefinition(‘SUM’, ‘X2012_YTD_PGP__c’,            ‘X2012_YTD_PGP_Customer__c’),        };         RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, trigger.old,        ‘Account’, ‘ParentId’, ‘Account’, ”);     } }

  • Chris

    Lets try to post that code again, shall we:
    trigger AccountRollUpSample on Account (after delete, after insert,after update, after undelete) {     if(trigger.isInsert || trigger.isUpdate || trigger.isUnDelete){
              list fieldDefinitions =        
               new list {
                      new RollUpSummaryUtility.fieldDefinition(‘SUM’, ‘X2012_YTD_PGP__c’,            ‘X2012_YTD_PGP_Customer__c’),

           };         

             RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, trigger.new,
           ‘Account’, ‘ParentId’, ‘Account’, ‘ ‘);

         }     

            if(trigger.isDelete){

                   list fieldDefinitions =
                  new list {
                        new RollUpSummaryUtility.fieldDefinition(‘SUM’, ‘X2012_YTD_PGP__c’,            ‘X2012_YTD_PGP_Customer__c’),

                   };

                   RollUpSummaryUtility.rollUpTrigger(fieldDefinitions, trigger.old,
                  ‘Account’, ‘ParentId’, ‘Account’, ‘ ‘);
          } 

    }

    Test

  • http://www.anthonyvictorio.com Anthony Victorio

    Looks like you have a coma in the wrong place for your “fieldDefinitions” list, right after “X2012_YTD_PGP_Customer__c”. It should look something like this:

    list fieldDefinitions = new list {new RollUpSummaryUtility.fieldDefinition(‘SUM’, ‘X2012_YTD_PGP__c’, ‘X2012_YTD_PGP_Customer__c’)};

  • http://www.anthonyvictorio.com Anthony Victorio

    Looks like you have a coma in the wrong place for your “fieldDefinitions” list, right after “X2012_YTD_PGP_Customer__c”. It should look something like this:

    list fieldDefinitions =
    new list {
    new RollUpSummaryUtility.fieldDefinition('SUM', 'X2012_YTD_PGP__c', 'X2012_YTD_PGP_Customer__c')
    };

  • Chris

    Oh, Man.  Thank you thank you thank you.  It works like a charm.  

    Damn commas.  I hate it when it is something simple like that.  

    Now, to write a test. 

  • http://www.anthonyvictorio.com Anthony Victorio

    It happens to best of us :) Please let me know if I can be of any more help.

  • Chris

    This is an new error.  We have nearly 500,00 Accounts, and this Trigger is rolling up data from the Child Account to the Parent.  Thoughts? (and Thanks)

    Error: Invalid Data.
    Review all error messages below to correct your data.
    Apex
    trigger CustomerRollupFields2013 caused an unexpected exception,
    contact your administrator: CustomerRollupFields2013: execution of
    AfterInsert

    caused by: System.QueryException: Non-selective query against large
    object type (more than 100000 rows). Consider an indexed filter or
    contact salesforce.com about custom indexing.
    Even if a field is indexed a filter might still not be selective when:
    1. The filter value includes null (for instance binding with a list that
    contains null)
    2. Data skew exists whereby the number of matching rows is very large
    (for instance, filtering for a particular foreign key value that occurs
    many times): Class.RollUpSummaryUtility.rollUpTrigger: line 50, column 1

  • http://www.anthonyvictorio.com Anthony Victorio

    @9660a47295687d005f78e36cfc1e9bdc:disqus 
    Chris, the error that you’re getting is likely caused by a query filter that’s using a non-indexed field such as a standard text field or a formula field.  SOQL only supports those types of fields as filters for lists containing less than 100K records. Though there may be something else at play here.  When is this error occurring? If its occurring during some sort of data load, then the above may be the issue and workarounds exist.  If it’s happening at other times, there may be something wrong with the structure of the utility. Feel free to contact me via Skype if you’d like some help troubleshooting this. My skype handle is: anthony.victorio

  • Charlie Voss

    Anthony – I finally had a chance to get back to this, and figured out why I’m getting the “System.LimitException: Too many SOQL queries: 101″ error.  I have a recursive trigger issue.

    Here’s what I’m trying to do, and perhaps you can direct me on how to accomplish:

    The Roll-up Summary Trigger is running on a custom object called Revenue__c.

    Revenue__c records are stored by Year, by Account Team Group, by Account Team, by Client, by Business Unit and by Product Line (all are Lookup Fields, except Year)

    I’m trying to use your utility to populate eight (8) fields on each of those objects:
    2009 Revenue
    2009 Forecast
    2010 Revenue
    2010 Forecast
    2011 Revenue
    2011 Forecast
    2012 Revenue
    2012 Forecast

    Since I have to use a Where Clause (i.e., ‘and Year__c = ‘2009’) for the Year, it’s resulting in a total of 4 Triggers (2009, 2010, 2011 and 2012) per object. Every time one fires, it causes the others to fire, and that’s causing the recursive issue.

    Is there a way to use your utility to create a single trigger for each object?

    Thanks Anthony. I’d really love to figure out a way to use your utility to make this work!

    Charlie

  • http://www.anthonyvictorio.com/ Anthony Victorio

    Well I do enjoy a good challenge and that definitely seems like trying to push some boundaries with governor limits. Just so that we’re in the same page, is this what your object model looks like (attached)?

  • Charlie Voss

    Yep – that’s it!  Year is actually a formula; “Month” is what’s actually stored (as Text) in the format of 2012-04; Year is a formula which decodes Month into Year.  But other than that, you nailed it!

  • http://www.anthonyvictorio.com/ Anthony Victorio

    Ah formula field, that presents a challenge of its own if you handle large data sets but I don’t think we’re there yet. Let me see if I can reproduce the error you’re getting on my environment first.  Do you mind copying and pasting the exact error message you’re receiving? And maybe your trigger as well?

  • http://www.anthonyvictorio.com/ Anthony Victorio

    One more thing, from the preliminary tests I’m running it seems that the utility is generating 2 SOQL queries per field. For 8 fields in 5 objects, or 40 fields in total, the trigger should generate a max of 80 SOQL requests. Still high but within limits, do you have any other triggers invoked by the Revenue object?

  • Charlie Voss

    Sure.  How do I paste code into comments?  “pre” tag or “code” tag or other tag?

  • Charlie Voss

    No, no other Triggers.  As a matter of fact, these are the only Triggers in the entire environment.

  • http://www.anthonyvictorio.com/ Anthony Victorio

    Good point, Disqus is not very code friendly, have you tried 
    http://pastie.org ? Here’s a sample of the trigger I’m putting together: 
    http://pastie.org/pastes/3777228

  • http://www.anthonyvictorio.com/ Anthony Victorio

    How about workflow rules? Any that use field updates?

  • Charlie Voss

    No Workflow Rules, either.  Here is error message I’m receiving:

    System.LimitException: Too many SOQL queries: 101 

    Here is Stack Trace:
    Class.testAT_RollUp2009Revenues.myUnitTest: line 64, column 1 

    Here is Test Class:
    http://pastie.org/3777247 

    And here is Trigger itself:
    http://pastie.org/3777265 

  • Charlie Voss

    Anthony – I’ve replied to your questions but my responses aren’t appearing here; message said Moderator must approve.  Just FYI.

  • http://www.anthonyvictorio.com/ Anthony Victorio

    Perfect, let me digest this over night and I’ll get back to you by tomorrow morning. Thanks Charlie!

  • http://www.anthonyvictorio.com/ Anthony Victorio

    Charlie, I think I solved your issue, take a look at this trigger:

    http://pastie.org/3784621 

    I’ve made a video that highlights what the trigger does and how it performs:

    http://www.youtube.com/watch?v=x1dMvTGGKJ4 

  • Charlie Voss

    Anthony – You’ve really gone above and beyond the call of duty with this.  Thank you very much!  I’ll start working on revising my Triggers using your updated code and writing updated Text classes.  I’ll be sure to let you know how things turn out.

    Thank you again!

  • Troy

    Could this be used to do a Rollup on custom fields from Activities/Tasks? We want the Case to have a RollupSummary field to sum the time tracking we saved in tasks.

  • http://www.anthonyvictorio.com/ Anthony Victorio

    Hello guys, sorry I’ve neglected this page.  I’ve been busy with projects and certifications but I plan to get back to everyone here over the weekend.  I’ve added a badge to the top right corner of this page that will allow you to chat with me when I’m online, so I hope that helps as well (I’m online most days).  

  • http://twitter.com/Two_Wheeler Bradlee Duncan

    Hey Anthony, thank you so much for all of this work. I’m not a coder at all so this is super helpful. I’m just running into a problem that I hope you can help me with. I’ve built this in my sandbox and everything is working as it should. I am using Force.com’s Milestones PM so I have Projects as the child and Opportunity as the parent. I’m trying to roll up all hours from all projects on a field on the opportunity (usually it’s just one Project per Opp). But when I go to deploy it (upload the inbound change set), I get the attached error. I know that Milestones PM has various triggers that run so I’m thinking that is affecting things here. Is there a way that I can deploy it in an inactive state and then apply it on chunks of opportunities at a time? I would assume that this is fairly common, but I don’t know how to work around it. Please help, I feel I’m so close! :-)

  • http://www.anthonyvictorio.com/ Anthony Victorio

    Hi Bradlee, I tried to duplicate the failure you’re receiving by installing the Milestones PM app on my dev org but no cigar.  Here’s a copy of the trigger I setup which updates the Opportunity.Projects_Hours_Budget__c field based on Milestone1_Project__c.Total_Hours_Budget__c field.

    http://pastie.org/4212185

    Please review and let me know if you need additional assistance. 

  • http://twitter.com/Two_Wheeler Bradlee Duncan

    Hey Anthony,
    In addition to my last comment (which I don’t think is visible yet), I did some more digging and it looks like I have ~2,050 Milestones Tasks (as opposed to SF tasks). I believe this is where the limit is running into problems. It seems that one of the built-in triggers in Milestones is throwing this error because of the number of tasks it has. But in my first iteration of the utility (which also works in my sandbox), and does not call MIlestones code at all I’m still seeing the error. It’s as if SF is running some test when I go to deploy it where it calls the Milestones triggers and then we hit the SOQL query limit, does that sound about right? If so, any suggestions?

  • kf

    This is awesome, thank you Anthony!

    I’m a clicks-not-code type of person and I was able to take your utility and trigger and get it working in my Sandbox for my own objects (the tests are a different story).

    I do have two questions and I’ll refer to Opportunity and Payments for simplicity purposes…

    1) I found that if I updated a Payment such that I deleted its Opportunity, the roll-up field on Opportunity was not being decremented as expected. I made a small change to the trigger — changed if(trigger.isDelete){ to if(trigger.isDelete || trigger.isUpdate){ — and that seems to do the trick. Anyone else come across this problem? Is my solution valid?

    2) I need multiple roll-ups on Opportunity — for Payments (with each having a unique query filter). Is it best practice to have one trigger on Payment per Opp custom field? one trigger for the Payment object?

Read previous post:
Salesforce Roll-Up Summary Trigger
Salesforce Roll-Up Summary Trigger Revised

Why on earth would anybody want a Salesforce trigger to perform a roll-up summary type function when Salesforce already provides...

Close