+ Start a Discussion
Mike FerraroMike Ferraro 

Longtime Apex Trigger failing after 8+ years... help!

Hi Everyone, recently a time based workflow has started throwing an error after working since 2012.  This process triggers a checkbox field that creates a new opportunity for contracts expiring in 120 days.


Error:Apex trigger UpdateAccountDummyVariable caused an unexpected exception, contact your administrator: UpdateAccountDummyVariable: execution of BeforeUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id 0015000000Oa1XaAAJ; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, CreateOpportunity: execution of BeforeUpdate caused by: System.DmlException: Insert failed. First exception on row 0; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = 0015000000Oa1Xa) is currently in trigger CreateOpportunity, therefore it cannot recursively update itself: [] Trigger.CreateOpportunity: line 21, column 1: []: Trigger.UpdateAccountDummyVariable: line 9, column 1


Here are the two triggers referenced:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
trigger CreateOpportunity on Account (before update) {

    List<Opportunity> o = new List<Opportunity>();  
        for (Account thisAccount : Trigger.new) {
               // A workflow rule will enable dummy_COW variable which in turn will tell the trigger to create the opportunity.
               if (thisAccount.dummy_CreateOpportunityWorkflow__c) {
                    o.add (new Opportunity (                   
                    Name = thisAccount.name + ' 2020 Renewal',                 
                    OwnerId = thisAccount.OwnerID,
                    Probability = 15,
                    Opportunity_Value__c = thisAccount.Current_Contract_Value__c,
                Expiring_Contract_Value__c = thisAccount.Current_Contract_Value__c,
                    Description = 'Auto-created by workflow.',
                Renewal__c = true,
                    CloseDate = date.today()+120,
                    Current_Contract_Expiration__c = date.today()+120,
                    AccountId = thisAccount.id,     
                    StageName = 'Analysis Needed'));                        
        }  
        if (!o.isEmpty()) {
        insert o;
        // clear the variable so subsequent edits do not create a new opportunity.
        thisAccount.dummy_CreateOpportunityWorkflow__c = false;
     }  }              

}



1
2
3
4
5
6
7
8
9
10
11
12
13
trigger UpdateAccountDummyVariable on Contract (before update) {

    for (Contract thisContract : Trigger.new)
    {
    if (thisContract.Dummy_Create_New_Opportunity_Workflow__c) 
        { 
            Account a = new Account(id = thisContract.AccountId);
          a.Dummy_CreateOpportunityWorkflow__c = true;
      update a;
      thisContract.Dummy_Create_New_Opportunity_Workflow__c = false;
        }
    }
}



Thanks for any suggestions!

Mike


 
ayu sharma devayu sharma dev
Hello Mike Ferraro

I can see that recursion is happening between your triggers or workflows. I can guess the flow of execution as :
  1. UpdateAccountDummyVariable:  This trigger executes and updates the Account.
  2. CreateOpportunity: On Account update in the previous trigger, this trigger executes and updates the Opportunity.
  3. Now there might be a new Flow, Process Builder or Trigger on Opportunity which updates its parent Account.
  4. So again the Account Trigger executes and creates the recursion.
To solve this problem we usually use a Static Boolean variable in Class and runs the Trigger method if the variable is true and after the first run we set it to false so recursion won't happen. But it requires to separate your logic from trigger into a public class. That's why it is always recommended not to write logic inside your trigger and use a class with methods instead.

Thanks and Regards
Ayush Sharma

 
Mike FerraroMike Ferraro

Ayush,

Thanks so much for your help on this.  I'm new to using Apex and don't come from a technical background... can you or someone outline the steps I would take to fix these?  Do I need to write new triggers or classes, or just alter the above?
 

Thanks again!

Mike

ayu sharma devayu sharma dev
Hello Mike

First, let's create a helper class for Trigger CreateOpportunity:
public class CreateOpportunityHelper{
    public static List<Account> newAccounts = new List<Account>();
    public static Boolean runTrigger = true;

    public static void insertOpps(){
        List<Opportunity> o = new List<Opportunity>();  
        for (Account thisAccount : newAccounts) {
            // A workflow rule will enable dummy_COW variable which in turn will tell the trigger to create the opportunity.
            if (thisAccount.dummy_CreateOpportunityWorkflow__c) {
                o.add (new Opportunity (                   
                Name = thisAccount.name + ' 2020 Renewal',                 
                OwnerId = thisAccount.OwnerID,
                Probability = 15,
                Opportunity_Value__c = thisAccount.Current_Contract_Value__c,
            Expiring_Contract_Value__c = thisAccount.Current_Contract_Value__c,
                Description = 'Auto-created by workflow.',
            Renewal__c = true,
                CloseDate = date.today()+120,
                Current_Contract_Expiration__c = date.today()+120,
                AccountId = thisAccount.id,     
                StageName = 'Analysis Needed'));                        
            }  
        }
        if (!o.isEmpty() && runTrigger = true) {
            insert o;
            runTrigger = false;
            // clear the variable so subsequent edits do not create a new opportunity.
            thisAccount.dummy_CreateOpportunityWorkflow__c = false;
        }     
    }
}

Now modify your CreateOpportunity Trigger as:
 
trigger CreateOpportunity on Account (before update) {

    CreateOpportunityHelper.newAccounts = Trigger.New;

    if( Trigger.isUpdate && Trigger.isBefore ){
        CreateOpportunityHelper.insertOpps();
    }

}

Do the same steps for UpdateAccountDummyVariable as follow:

 
public class UpdateAccountDummyHelper{

    public static List<Contract> newContracts = new List<Contract>();

    public static Boolean runTrigger = true;

    public static void updateAccountDum(){
        List<Account> accountsToUpdate = new List<Account>();
        for (Contract thisContract : newContracts){
            if (thisContract.Dummy_Create_New_Opportunity_Workflow__c){ 
                Account a = new Account(id = thisContract.AccountId);
                a.Dummy_CreateOpportunityWorkflow__c = true;
                accountsToUpdate.add( a );
                thisContract.Dummy_Create_New_Opportunity_Workflow__c = false;
            }
        }
        if( accountsToUpdate.size() > 0 ){
            update accountsToUpdate;
        }
    }

}

and Trigger
 
trigger UpdateAccountDummyVariable on Contract (before update){

    UpdateAccountDummyHelper.newContracts = Trigger.New;

    if( !UpdateAccountDummyHelper.runTrigger ){
        return;
    }

    if( Trigger.isUpdate && Trigger.isBefore ){
        UpdateAccountDummyHelper.insertOpps();
    }

}

 Please try the above steps. And before doing code make sure to take the backup of your present Triggers.

Try and let me know if it works or not. 

Thanks and Regards
​​​​​​​Ayush Sharma
 
Mike FerraroMike Ferraro

Ayush, 

Thanks again for your help; it only allowed me to create the class for UpdateAccountDummyHelper; the rest came back as errors:

User-added image


User-added image

User-added image

This is all in a Sandbox I created to work on this.  Any ideas what the issues are?
 

Thanks again!

Mike
 

ayu sharma devayu sharma dev
Hello Mike

Sorry for the inconvenience please try the below code.
 
public class CreateOpportunityHelper{
    public static List<Account> newAccounts = new List<Account>();
    public static Boolean runTrigger = true;

    public static void insertOpps(){
        List<Opportunity> o = new List<Opportunity>();  
        for (Account thisAccount : newAccounts) {
            // A workflow rule will enable dummy_COW variable which in turn will tell the trigger to create the opportunity.
            if (thisAccount.dummy_CreateOpportunityWorkflow__c) {
                o.add (new Opportunity (                   
                Name = thisAccount.name + ' 2020 Renewal',                 
                OwnerId = thisAccount.OwnerID,
                Probability = 15,
                Opportunity_Value__c = thisAccount.Current_Contract_Value__c,
            Expiring_Contract_Value__c = thisAccount.Current_Contract_Value__c,
                Description = 'Auto-created by workflow.',
            Renewal__c = true,
                CloseDate = date.today()+120,
                Current_Contract_Expiration__c = date.today()+120,
                AccountId = thisAccount.id,     
                StageName = 'Analysis Needed'));                        
            }  
        }
        if (!o.isEmpty() && runTrigger) {
            insert o;
            runTrigger = false;
            // clear the variable so subsequent edits do not create a new opportunity.
            thisAccount.dummy_CreateOpportunityWorkflow__c = false;
        }     
    }
}
 
trigger UpdateAccountDummyVariable on Contract (before update){

    UpdateAccountDummyHelper.newContracts = Trigger.New;

    if( !UpdateAccountDummyHelper.runTrigger ){
        return;
    }

    if( Trigger.isUpdate && Trigger.isBefore ){
        UpdateAccountDummyHelper.updateAccountDum();
    }

}

Please try and let me know if the error still occurs.

Thanks
Mike FerraroMike Ferraro
This time the trigger worked, but the CreateOpportunityHelper class is still giving me an error:

User-added image

Thanks!

Mike
ayu sharma devayu sharma dev
Sorry, I cant compiled this code in Salesforce as it is dependent on your org, Try below code.
 
public class CreateOpportunityHelper{
    public static List<Account> newAccounts = new List<Account>();
    public static Boolean runTrigger = true;

    public static void insertOpps(){
        List<Opportunity> o = new List<Opportunity>();  
        for (Account thisAccount : newAccounts) {
            // A workflow rule will enable dummy_COW variable which in turn will tell the trigger to create the opportunity.
            if (thisAccount.dummy_CreateOpportunityWorkflow__c) {
                o.add (new Opportunity (                   
                Name = thisAccount.name + ' 2020 Renewal',                 
                OwnerId = thisAccount.OwnerID,
                Probability = 15,
                Opportunity_Value__c = thisAccount.Current_Contract_Value__c,
            Expiring_Contract_Value__c = thisAccount.Current_Contract_Value__c,
                Description = 'Auto-created by workflow.',
            Renewal__c = true,
                CloseDate = date.today()+120,
                Current_Contract_Expiration__c = date.today()+120,
                AccountId = thisAccount.id,     
                StageName = 'Analysis Needed'));                        
            } 
            if (!o.isEmpty() && runTrigger) {
                insert o;
                runTrigger = false;
                // clear the variable so subsequent edits do not create a new opportunity.
                thisAccount.dummy_CreateOpportunityWorkflow__c = false;
            }  
        }
            
    }
}

 
Mike FerraroMike Ferraro

Ayush,

That worked!  I'm going to test on Sandbox... I can't thank you enough for your guidance.

Stay safe and healthy.

Mike

ayu sharma devayu sharma dev
Mike

You too take care. 
If an error occurs again let me know. We will solve this problem don't worry. 

Ayush
Mike FerraroMike Ferraro
Ayush,

Everything worked overnight but now I"m having trouble deploying to production.  What components do I need to add to the change set?  I added both helper classes and the apex triggers but got this:

User-added image

Thank you!

Mike
Mike FerraroMike Ferraro
Here are the current Tests:


@isTest
private class Test_CreateOpportunity {

    static testMethod void myOpportunityCreateUnitTest() {
       Account a = new Account(Name = 'Test Account', Current_Contract_Value__c=10000);
       insert a;
       a.dummy_CreateOpportunityWorkflow__c = true;
       update a;

    List<opportunity> newOpportunity = [SELECT Name, Description, Renewal__c, StageName, Expiring_Contract_Value__c, Current_Contract_Expiration__c, Probability, Opportunity_Value__c, CloseDate
                                        FROM Opportunity
                                        WHERE AccountID = :a.Id];
        
        for (Opportunity o : newOpportunity) {
            System.assertEquals('Auto-created by workflow.',o.Description);
            System.assertEquals(true,o.Renewal__c);
            System.assertEquals(a.Name + ' 2020 Renewal',o.Name);
            System.assertEquals('Analysis Needed',o.StageName);
            System.assertEquals(15,o.Probability);  
            System.assertEquals(a.Current_Contract_Value__c,o.Expiring_Contract_Value__c);
            System.assertEquals(a.Current_Contract_Value__c,o.Opportunity_Value__c);
            System.assertEquals(date.today()+120,o.CloseDate);
            System.assertEquals(date.today()+120,o.Current_Contract_Expiration__c);
            
        }
    
    }
}





@isTest
private class Test_UpdateAccountDummyVariable {

    static testMethod void updateDummyUnitTest() {
     Account a = new Account(Name = 'Test Account');
        insert a;
       Contract c = new Contract(Name = 'My Test Contract',
       AccountID = a.Id,
       Total_Contracted_Amount__c = 10000,
       Annualized_Cappex_Value__c = 10000,
     Included_Product_s__c = 'Cappex',
       Unit_Price__c = 15000,
       StartDate = Date.today(),
     EndDate = Date.today()+365,
       ContractTerm = 12);
        insert c;
       c.Dummy_Create_New_Opportunity_Workflow__c = true;                                                        
     update c;

      List<contract> newContract = [SELECT Name, Dummy_Create_New_Opportunity_Workflow__c
                                     FROM Contract
                                     WHERE Id = :c.Id];
        
       List<account> newAccount = [SELECT Name, Description, Dummy_CreateOpportunityWorkflow__c
                                     FROM Account
                                     WHERE Id = :a.Id];

       for (Contract thisContract : newContract) {
            System.assertEquals(false,thisContract.Dummy_Create_New_Opportunity_Workflow__c);  // after execution make sure it resets to false
        }

       for (Account thisAccount : newAccount) {
            System.assertEquals(false,thisAccount.Dummy_CreateOpportunityWorkflow__c); // after execution make sure it resets to false
        }

    }
}



 
ayu sharma devayu sharma dev
Hello Mike

Have you tested it in Sandbox first before deploying on Production?
 
Mike FerraroMike Ferraro
Ayush,

I did!  The workflow behaved as expected, I set it up to run last night and it worked correctly!

Mike
Mike FerraroMike Ferraro
I tried to deploy this morning but the tests are failing as I showed above
ayu sharma devayu sharma dev
Mike,

If test classes are running successfully in the sandbox then make sure you are uploaded triggers and classes on the Production. By any chance if you have forgotten to add triggers to change set?

Ayush 
Mike FerraroMike Ferraro
I added Triggers, the new classes you wrote, and the tests that I currently had, like this:

User-added image

Mike
Mike FerraroMike Ferraro
Ayush,

The Class is throwing the following error:

Error: Compile Error: Type name already in use: CreateOpportunityHelper at line 7 column 18

 
ayu sharma devayu sharma dev
Mike,
 
public class CreateOpportunityHelper{
        public static List<Account> newAccounts = new List<Account>();
        public static Boolean runTrigger = true;
    
        public static void insertOpps(){
            List<Opportunity> o = new List<Opportunity>();  
            for (Account thisAccount : newAccounts) {
                // A workflow rule will enable dummy_COW variable which in turn will tell the trigger to create the opportunity.
                if (thisAccount.dummy_CreateOpportunityWorkflow__c) {
                    o.add (new Opportunity (                   
                    Name = thisAccount.name + ' 2020 Renewal',                 
                    OwnerId = thisAccount.OwnerID,
                    Probability = 15,
                    Opportunity_Value__c = thisAccount.Current_Contract_Value__c,
                Expiring_Contract_Value__c = thisAccount.Current_Contract_Value__c,
                    Description = 'Auto-created by workflow.',
                Renewal__c = true,
                    CloseDate = date.today()+120,
                    Current_Contract_Expiration__c = date.today()+120,
                    AccountId = thisAccount.id,     
                    StageName = 'Analysis Needed'));                        
                } 
                // clear the variable so subsequent edits do not create a new opportunity.
                thisAccount.dummy_CreateOpportunityWorkflow__c = false;
            }
            if (!o.isEmpty() && runTrigger) {
                runTrigger = false;
                insert o;
                runTrigger = true;
            }  
                
        }
    }
}


trigger CreateOpportunity on Account (before update) {

    CreateOpportunityHelper.newAccounts = Trigger.New;

    if( !CreateOpportunityHelper.runTrigger ){
        return;
    }

    if( Trigger.isUpdate && Trigger.isBefore ){
        CreateOpportunityHelper.insertOpps();
    }

}

Please Update class and Trigger.

Ayush
Mike FerraroMike Ferraro

Hi Ayush,
 

I updated the Class and Trigger, then Uploaded and Deployed, and got the same error.  Any idea where to go from here?

User-added image

Mike

ayu sharma devayu sharma dev
Hello Mike 

Try Converting Contract Trigger on After Update:
 
trigger UpdateAccountDummyVariable on Contract (After update)


As I assume we might be updating the Account which is already in the context of Before Account Trigger. I am not sure but lets troubleshoot. I know it gets frustrating sometimes but lets do our best to solve this.

Ayush 
Mike FerraroMike Ferraro
Thanks Ayush, I will run the time based workflow again to make sure it's still working and let you know

Mike
Mike FerraroMike Ferraro
Ayush, unfortunately the time based workflow did not trigger with this change, so I've changed it back... I'm thinking I should try "After Update" on the other trigger?

Mike
Mike FerraroMike Ferraro
After trying that, it did not work, so both are back to "Before Update"... that's how it worked before in the Sandbox.

Mike