To prevent a recursive call, you should make sure your trigger only executes one time. Add a class with a static boolean variable. In the trigger, have a condition that checks the value of the boolean. Once the trigger executes, change the value to false.
trigger Opportuniwith sIdentifierHandler on Opportunity (before insert,before update) {
if(checkRecursion.runOnce()){
If(Trigger.isInsert || Trigger.isUpdate){
AutoNumber.OpportunityAutoNumber(Trigger.new);
}
}
}
****Utility class****
public Class checkRecursion{
private static boolean run = true;
public static boolean runOnce(){
if(run){
run=false;
return true;
}else{
return run;
}
}
}
After what you mentioned in the comments, it looks like updating the budget allocation causes an update to the opportunity, which is in turn in the middle of being updated by a trigger, which may cause the exception.
Perhaps you can work around this by adding future methods. Add these 2 methods to your class:
public with sharing class InsertBudgetOpp {
// existing code still goes here...
@future
public static void updateChildFuture (Set <Id> oppId, Date startDate, Date endDate, Integer totalBudget) {
Map < String, Budget_Allocation__c > baMap = updateChild (oppId, startDate, endDate, totalBudget);
upsert baMap.values();
}
@future
public static void insertChildFuture (Set <Id> oppId, Date startDate, Date endDate, Integer tb)
{
List <Budget_Allocation__c> baList = insertChild(Set <Id> oppId, Date startDate, Date endDate, Integer tb);
insert baList;
}
}
Then modify your trigger to call these future methods:
trigger EstimateBudgetPerMonth on Opportunity__c(after insert, before update) {
Date startDate = Date.newInstance(2008, 1, 1);
Date endDate = Date.newInstance(2008, 1, 30);
Set < Id > oppId = new Set < Id > ();
Integer totalBudget;
for (Opportunity__c opp: Trigger.new) {
startDate = opp.Campaign_Start_Date1__c;
endDate = opp.Campaign_End_Date1__c;
totalBudget = Integer.valueOf(opp.Budget1__c);
oppId.add(opp.Id);
}
if (Trigger.isInsert) {
System.debug('--===Insert==--#--===Insert==--#--===Insert==--');
InsertBudgetOpp.insertChildFuture(oppId, startDate, endDate, totalBudget);
} else if (Trigger.isUpdate) {
System.debug('--===Update==--#--===Update==--#--===Update==--');
InsertBudgetOpp.updateChildFuture(oppId, startDate, endDate, totalBudget);
}
}
What this will do is, when the trigger fires, start an asynchronous method (perhaps on a different thread) that will update the child budget allocation objects. They will complete sometime in the future (generally it takes less than a minute, but Salesforce will never guarantee a timeframe). This may or may not be adequate for your solution, but it might prevent the exception.
Also note that when you end a test (with Test.stopTest()), this will guarantee that all future methods are executed.
For more on future methods, read: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_invoking_future_methods.htm
Best Answer
You are trying to using a
BEFORE
trigger, and in your trigger, you are trying to do a DML call on records that are in the trigger. InBEFORE
triggers, any manipulation you do to the data happens before the records undergo their DML call, so you don't need to perform a DML call on the trigger records -- it's already gonna happen. However, you would need to do a DML call onAFTER
triggers. They happen after the trigger, so anything you doAFTER
the trigger needs to be "saved" via a DML call.So, for your case, just remove the
upsert rows;
and it should work.To note, I usually use
BEFORE
triggers if I need alter data that is being fed into the trigger while I useAFTER
triggers if I need to alter data that is related to the data being fed into the trigger.BEFORE
Example:AFTER
Example:Also, one bit of advice on triggers: write all the code you need for your trigger in its own class, and then just call the class in the trigger (and make sure you use all the trigger goodies when you can --
trigger.new
,trigger.old
,trigger.newMap
,trigger.oldMap
,trigger.isInsert
,trigger.isUpdate
,trigger.isDelete
).It's good to do this to keep your triggers as short and simple as possible. Then, I also recommend that you have only 1 trigger per custom object (so you can easily tell what exactly is being triggered in any situation). And if you need to do separate things in your trigger, just call the classes as needed.
Example:
Class code: public class trigger_CreateNewChildren(){
Trigger code: