function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
dnvansantdnvansant 

Is it possible to avoid this self reference error?

I'm not sure this is even possible. I want to fire a trigger any time an opportunity is edited. For the trigger opportunity, I want to find all opportunities with the same AccountID and then populate the ASP__c field on all matching opportunities with the sum of the Amount field for those matching opportunities. The code below does this beautifully for the trigger opportunity, but every attempt I make to update all other opportunities with the same AccountID results in a self reference error.

 

CODE FOR UPDATING TRIGGER OPPORTUNITY ONLY

 

trigger ASP On Opportunity (before update){
    public Id accid;
    public Id oppid;
       
for(Opportunity p : Trigger.new){
accid = p.AccountId;
oppid = p.Id;

if(p.isclosed == False)

{
List<AggregateResult> groupedResults = [SELECT sum(Amount)aver FROM Opportunity where AccountId=:accid];
Decimal decimalRevenue = 0 ;
if(groupedResults.size() > 0)
{
    String str = '' + groupedResults[0].get('aver') ;
    decimalRevenue = Decimal.ValueOf(str) ;
    System.debug('decimalRevenue ::::: ' + decimalRevenue) ;
}

p.ASP__c = decimalRevenue;
p.ASP_Count__c = [SELECT Count()FROM Opportunity where AccountId=:accid];
p.ASp_updated__c = True;

}}}

 

 

BELOW IS THE CODE I TRIED FOR ALL OPPORTUNITIES THAT PRODUCED THE SELF REFERENCE ERROR

 

trigger ASP On Opportunity (before update){
    public Id accid;
    public Id oppid;
    public list<opportunity> LeadsToUpdate = new List<opportunity>();
       
for(Opportunity p : Trigger.new){
accid = p.AccountId;
oppid = p.Id;

if(p.isclosed == False)

{
List<AggregateResult> groupedResults = [SELECT sum(Amount)aver FROM Opportunity where AccountId=:accid];
Decimal decimalRevenue = 0 ;
if(groupedResults.size() > 0)
{
    String str = '' + groupedResults[0].get('aver') ;
    decimalRevenue = Decimal.ValueOf(str) ;
    System.debug('decimalRevenue ::::: ' + decimalRevenue) ;
}

p.ASP__c = decimalRevenue;
p.ASP_Count__c = [SELECT Count()FROM Opportunity where AccountId=:accid];
p.ASp_updated__c = True;

for(Opportunity o: [Select o.Id, o.amount, o.asp__c From opportunity o where AccountId=:accid and ID!=:oppid])
LeadsToUpdate.add(new Opportunity(Id=o.Id, ASP__c = decimalRevenue));
if(LeadsToUpdate.size()>0){update LeadsToUpdate;}
}}}

 

 

Anyone got any ideas for updating all opportunities with the same AccountID without self referencing the trigger opportunity or creating an infinite loop?

Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox

Of course it is possible to avoid the error, you just have to be a bit creative.

 

Almost any approach you come across would probably involve the usage of a static variable to monitor which records have already been processed. Conceivably, this might be as follows:

 

public class OppUtil {
    public static Set<Id> processed = new Set<Id>();
}

Then, you could use two triggers:

 

trigger oppBeforeCalc on Opportunity (before update) {
   // update the records involved in this trigger
   // mark those records as processed:
   OppUtil.processed.addAll(Trigger.newMap.keySet());
}

Basically, this performs the necessary logic we need and just flags those records as processed. Then...

 

trigger afterOppCalc on Opportunity (after update) {
    Set<Id> accounts = new Set<Id>();
    for(Opportunity o:Trigger.new)
        accounts.add(o.accountid);
    for(opportunity[] o:[select id from opportunity where accountid in :accounts and id not in :opputil.processed])
        update o;
}

This isn't the only way to do this, nor is this necessarily the most efficient, but the principle holds. By performing your updates in a before-update trigger, you prevent infinite recursion, and by using a static variable you prevent processing records that are already locked.

All Answers

sfdcfoxsfdcfox

Of course it is possible to avoid the error, you just have to be a bit creative.

 

Almost any approach you come across would probably involve the usage of a static variable to monitor which records have already been processed. Conceivably, this might be as follows:

 

public class OppUtil {
    public static Set<Id> processed = new Set<Id>();
}

Then, you could use two triggers:

 

trigger oppBeforeCalc on Opportunity (before update) {
   // update the records involved in this trigger
   // mark those records as processed:
   OppUtil.processed.addAll(Trigger.newMap.keySet());
}

Basically, this performs the necessary logic we need and just flags those records as processed. Then...

 

trigger afterOppCalc on Opportunity (after update) {
    Set<Id> accounts = new Set<Id>();
    for(Opportunity o:Trigger.new)
        accounts.add(o.accountid);
    for(opportunity[] o:[select id from opportunity where accountid in :accounts and id not in :opputil.processed])
        update o;
}

This isn't the only way to do this, nor is this necessarily the most efficient, but the principle holds. By performing your updates in a before-update trigger, you prevent infinite recursion, and by using a static variable you prevent processing records that are already locked.

This was selected as the best answer
dnvansantdnvansant

Thank you. I tinkered with something similar, but your guidance will be a big help in me actually executing the approach successfully. I will report back after I've had a chance to work with you suggestions.

 

-Derek 

dnvansantdnvansant

Thanks sfdcfox. I've implemented your suggestions and things seem to be running smoothly.

 

-Derek