2

I have two before Triggers, one on my Project object & the other on Opportunities.

The Project Trigger takes the value from the Project record's field MPM4_BASE__Deadline__c and copies it into the related Opportunity's field Implementation_Revenue__c & the Opportunity trigger copies the updated value from that field into the Opportunity_Imp_Revenue_Com_Date__c field of a record for junction object ProjectxOpp__c which relates Projects to Opportunities.

Lastly, the value from the ProjectxOpp__c's Opportunity_Imp_Revenue_Com_Date__c is rolled up, using a standard rollup field, to the Project record.

When I edit the MPM4_BASE__Deadline__c field on the Project record, it causes an error

execution of BeforeUpdate caused by: System.DmlException: Update failed. SELF_REFERENCE_FROM_TRIGGER, Object (id = a5E7000000008zR) is currently in trigger WE_IMProjCmpltnUp, therefore it cannot recursively update itself: [] Trigger.WE_ProjxOppUp: line 61, column 1: []: Trigger.WE_IMProjCmpltnUp: line 55, column 1.

Line 61 for Trigger WE_ProjxOppUp is update juncRecs; & line 55 for Trigger WE_IMProjCmpltnUp is update updOpps;.

I've seen several, posts about the error & the knowledge article but none seem to explain the cause of my error. Can anyone see a potential cause?

trigger WE_IMProjCmpltnUp on MPM4_BASE__Milestone1_Project__c (before insert, before update) {


//section of code removed, creates validRecordTypeIds list

    Set<Id> projects = new Set<Id>();
    Map<Id,Date> deadlineDates = new Map<Id,Date>();
    List<Opportunity> updOpps = new List<Opportunity>();

    If(Trigger.isInsert){
        for(MPM4_BASE__Milestone1_Project__c p : Trigger.new){
            if(validRecordTypeIds.contains(p.RecordTypeId) && p.MPM4_BASE__Deadline__c != null)
            {
                projects.add(p.Id);
                deadlineDates.put(p.Id, p.MPM4_BASE__Deadline__c);
            }
        }
    }

    If(Trigger.isUpdate){
                for(MPM4_BASE__Milestone1_Project__c p : Trigger.new){
                    if(validRecordTypeIds.contains(p.RecordTypeId) && p.MPM4_BASE__Deadline__c != null)
                    {
                        MPM4_BASE__Milestone1_Project__c oldP = Trigger.oldMap.get(p.Id);

                        if(oldP.MPM4_BASE__Deadline__c != p.MPM4_BASE__Deadline__c){
                            projects.add(p.Id);
                            deadlineDates.put(p.Id, p.MPM4_BASE__Deadline__c);
                        }
                    }
                }
    }

    if(projects.size() > 0){

        for(ProjectxOpp__c junc : [SELECT Project__c, Opportunity__r.Implementation_Revenue__c FROM ProjectxOpp__c
                                    WHERE Project__c In :projects])
        {
            Opportunity o = junc.Opportunity__r;
            Date newCompDate =  deadlineDates.get(junc.Project__c);
            if(newCompDate > o.Implementation_Revenue__c){
                o.Implementation_Revenue__c = newCompDate;
            }
            updOpps.add(o);
        }
        update updOpps;
    }
}

trigger WE_ProjxOppUp on Opportunity (before update) {

//section of code removed, creates validRecordTypeIds list

    Set<Id> opps = new Set<Id>();
    Map<Id,Date> oppCls = new Map<Id,Date>();
    Map<Id,Decimal> oppNetFSR = new Map<Id,Decimal>();
    Map<Id,Date> oppImpCom = new Map<Id,Date>();
    List<ProjectxOpp__c> juncRecs = new List<ProjectxOpp__c>();

    for(Opportunity o : Trigger.New){
        if(validRecordTypeIds.contains(o.RecordTypeId)){
            Opportunity oldO = Trigger.oldMap.get(o.Id);

            if(oldO.CloseDate != o.CloseDate){
                opps.add(o.Id);
                oppCls.put(o.Id, o.CloseDate);
            }
            if(oldO.Net_Full_Service_Revenue__c != o.Net_Full_Service_Revenue__c){
                opps.add(o.Id);
                oppNetFSR.put(o.Id, o.Net_Full_Service_Revenue__c);
            }               
             if(oldO.Implementation_Revenue__c != o.Implementation_Revenue__c){
                 opps.add(o.Id);
                 oppImpCom.put(o.Id, o.Implementation_Revenue__c);
             }
        }
    }

    if(opps.size() > 0){

        for(ProjectxOpp__c junc : [SELECT Opportunity__c, Opportunity_Net_FSR__c, Opportunity_Close_Date__c, Opportunity_Imp_Revenue_Com_Date__c FROM ProjectxOpp__c
                                    WHERE Opportunity__c IN :opps])
        {
            Date clsDt = oppCls.get(junc.Opportunity__c);
            if(clsDt != null){
                junc.Opportunity_Close_Date__c = clsDt;
            }
            Decimal netFSR = oppNetFSR.get(junc.Opportunity__c);
            if(clsDt != null){
                junc.Opportunity_Net_FSR__c = netFSR;
            }
            Date impRevComDt = oppImpCom.get(junc.Opportunity__c);
            if(impRevComDt != null){
                junc.Opportunity_Imp_Revenue_Com_Date__c = impRevComDt;
            }
            juncRecs.add(junc);
        }
        update juncRecs;
    }
}
  • is there any trigger or some other operation (flow / process) on the junction object ProjectxOpp__c that's updating back the milestone project ? – Vamsi Krishna Jul 13 '15 at 14:40
  • @VamsiKrishna thanks for the suggestion, there's no workflows, flows, processes or triggers for the ProjectxOpp__c object at all. – Alex S Jul 13 '15 at 14:44
  • @VamsiKrishna but just to be clear, the object that's being referred to as a5E7000000008zR in the error message is the Project. – Alex S Jul 13 '15 at 15:28
  • @VamsiKrishna I've just posted the resolution for my issue, I'd be interested to hear your thoughts on why changing the context of my Trigger from before to after has solved the issue because I have no idea! – Alex S Jul 16 '15 at 9:27
1

The cause of the issue was my Project roll-up fields, which were rolling up values from the ProjxOpp junction object and would have caused the triggers on the Project to execute again.

But interestingly, in this instance Salesforce seems to have been anticipating the roll-ups causing the second Project save procedure because the Project triggers didn't appear in my my debug logs a second time, before the error occurred.

Since my WE_IMProjCmpltnUp Trigger isn't updating the Projectobject, I was able to change it's execution to after insert, after update and this prevents the error from occurring.
This is because you can't update a record from within it's Before trigger, even though the roll-up actually causes the recalculation in the After context.
Again, it looks like this is Salesforce anticipating the change but not taking into account the context, when that change will be made.

Note - it wasn't necessary to also change the WE_ProjxOppUp Trigger to after update & doing that but not changing WE_IMProjCmpltnUp does not solve the issue.

Applying the solution from the Salesforce Knowledge Article on this topic - How to avoid Recursive trigger - which also seems to have been proposed in this answer didn't solve my issue.

  • Did you get to understand the issue conceptually? I ran in to the same issue today and changing the context from before to after solved my problem too. – sam_s May 3 '16 at 23:54
  • @sam_s I'm pretty new to development and I'm not absolutely sure that "Salesforce anticipating the roll-up calculation" is correct, to be honest but this is still my best guess at the reason for the issue & as you say, the solution works. If you spot anything that suggests another cause of the problem, please do let me know though! – Alex S May 4 '16 at 14:20

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.