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;
}
}
Best Answer
The cause of the issue was my
Project
roll-up fields, which were rolling up values from theProjxOpp
junction object and would have caused the triggers on theProject
to execute again.But interestingly, in this instance Salesforce seems to have been anticipating the roll-ups causing the second
Project
save procedure because theProject
triggers didn't appear in my my debug logs a second time, before the error occurred.Since my
WE_IMProjCmpltnUp
Trigger isn't updating theProject
object, I was able to change it's execution toafter 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 toafter update
& doing that but not changingWE_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.