4

I can't seem to get my head wrapped around this one. We have main account that has many child account and what I'm trying to do is to use a trigger to automatically add up a field (eg. FieldA) and write it in to the main account. So the users would be entering values into child account-Field A and the main account-FieldA would automatically get calculated. The code I wrote is returning the trigger depth limit exceeded error, which I think is because of the way I wrote it, it is running recursively (accident if true, not trying to do that)

here is my code

trigger HQDisplayTotal on Account (after insert, after update)
{
    Set<String> accId = new Set<String>();
    Map<String, Decimal> EGC = new Map<String, Decimal>(); 

    for(Account acc : Trigger.new)
    {
        if(acc.Id != null)
        {
            if(acc.ParentId != null)
            {
                accId.add(acc.ParentId);
            }
            else
            {
                accId.add(acc.Id);
            }
        }
    }

    //------------------------------------------------------------
    List<Account> locData = [SELECT Id, ParentId, Evoke_Gallery_Cabinet__c
                            FROM Account WHERE ParentId IN :accId];
    Decimal val = 0;

    for(Account acc : locData)
    {
        if(acc.Evoke_Gallery_Cabinet__c != null)
                val = acc.Evoke_Gallery_Cabinet__c;
            else
                val = 0;

        if(acc.ParentId != null)
        {
            if(EGC.get(acc.ParentId) == null)
            {
                    EGC.put(acc.ParentId, val);
            }
            else
            {
                EGC.put(acc.ParentId, EGC.get(acc.ParentId) + val);
            }
        }
    }

    List<Account> parData = [SELECT Id, Evoke_Gallery_Cabinet__c
                            FROM Account WHERE Id IN :accId];

    for(Account acc : parData)
    {
        acc.Evoke_Gallery_Cabinet__c = EGC.get(acc.Id);
    }

    update parData;
}
1
2

An important factor here is how deep the hierarchy is. To sum from say a grand-child, to a child and then up to a (root) parent requires trigger recursion. As long as the number of levels is small (this post suggests the recursion limit is 16 which is surprisingly high) the recursion is just an elegant way of achieving the multi-level summing.

Assuming it is a small number of levels, rather than blocking with a static variable the recursion can be allowed with the (root) parent Accounts being identified by checking for a null ParentId field to terminate the recursion.

A trigger of this form (note not compiled or tested) should work:

trigger HQDisplayTotal on Account (after insert, after update) {
    Set<Id> parentIds = new Set<Id>();
    for (Account acc : Trigger.new) {
        if (acc.ParentId != null) {
            Account old = Trigger.oldMap.get(acc.Id);
            // Only recalculate on insert or when an update changes the values
            if (Trigger.isInsert
                    || acc.Evoke_Gallery_Cabinet__c != old.Evoke_Gallery_Cabinet__c) {
                parentIds.add(acc.ParentId);
            }
        } else {
            // Stop the recursion
        }
    }
    if (parentIds.size() > 0) {
        List<Account> updates = new List<Account>();
        // Leave the job of calculating the sum to the database
        for (AggregateResult ar : [
                select ParentId p, sum(Evoke_Gallery_Cabinet__c)
                from Account
                where Id in :parentIds
                group by ParentId
                ]) {
            // Update without the cost of a preceding query
            updates.add(new Account(
                    Id = (Id) ar.get('p'),
                    Evoke_Gallery_Cabinet__c = (Decimal) ar.get('s');
                    ));
        }
        // This update will cause this trigger to fire again
        update updates;
    }
}

It automatically supports the simple case of just a parent and one level of children.

The trigger posted in the question always updates the (root) parent Accounts even when there is no change in the sums and so always hits the recursion trigger depth limit.

To keep the numbers correct under all changes, changes to the Account ParentId field also need to be handled.

4
  • 2
    Or, just "disable" the recursion type trigger and have the current trigger perform all the rollups. However, this is usually the approach that I take, assuming the level of recursion is less than 16 (and yes, it is 16, located in the Governor Limits documentation).
    – sfdcfox
    Jul 23 '14 at 1:09
  • Thank you so much, I've still got a lot to learn I did have one question though, I was getting "DML requires SObject or SObject list type Map<Id,Account>" error on update, so I modified it to query for the Parent account right before the update so I can update the changes there instead. Is this the wrong way to get around that problem? I didn't really understand why it's saying updates isn't Map<Id, Account> modified code: <code>updates.put((Id) ar.get('p'), new Account(Id = (Id) ar.get('p'),Evoke_Gallery_Cabinet__c = (Decimal) ar.get('s')));</code>
    – Nataku
    Jul 23 '14 at 15:26
  • 1
    @Nataku The problem was my code was wrong - I've updated it. That field should be a list not a map. Maps are commonly used to ensure there is only one object for an ID but in this case the aggregate query ensures that so a simple list is enough. The DML methods all expect a single SObject or a list of SObjects.
    – Keith C
    Jul 23 '14 at 15:41
  • @Keith C Ohhh, Ok Thanks :) you are always such a great help
    – Nataku
    Jul 23 '14 at 15:46
1

I create a class that contains static variables, and it has a public static Boolean accountRecursion = false;

In the trigger: I set the accountRecursion variable to true right before doing the DML in the trigger. I have an IF statement at the top of the trigger that only executes the trigger if accountRecursion is false.

Basically, that will prevent your recursion. For more examples, see Jeff Douglas' post. http://appirio.com/category/tech-blog/2009/10/preventing-recursive-future-method-calls-in-salesforce/ (option 2)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.