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
Andrew NorthAndrew North 

Please help dianose CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY error

Hi all

I'm attempting to deploy a change within Salesforce but failing to do so as there is this one outstanding issue that appears when validating the deployment.

As an admin I'm struggling to understand what the issue is here. Could somebody kindly shed some light on whats causing the issue or help me understand what the apex classes are doing? My only 'fix' is to commment out the offending line of apex (marked in bold) but I dont want to introduce any new errors by doing so.

Thank you in advance.

 

Test

@isTest
private class BW_RevenueScheduleBatchTest{
        static testMethod void testBW_RevenueScheduleBatch(){
                Profile p;
                User u;
                Account parentAcc;

                Account acc;
                Opportunity opp;
                Profit_Centre_Split__c secondaryPcs;
                Framework__c fw;
                Sector_Head_Mappings__c sectorHeadCS;
                p = TestData.createProfile();

                u = TestData.createTestUser(p);
                                TestData.u=u;
                parentAcc=TestData.createAccount();
                sectorHeadCS = TestData.createSectorCustomSetting(u);

                acc = TestData.createAccount(parentAcc);
                fw =  TestData.createFrameWork(acc);
                opp = TestData.createOpportunity(acc.id,fw.id);
                opp.Legacy_Source_System__c='XYZ';
                update opp;
                secondaryPcs = TestData.createSecondaryProfitCentre(opp);

                    database.executeBatch(new BW_RevenueScheduleBatch());
        }
}
 
global class BW_RevenueScheduleBatch implements Database.Batchable<sObject> {
   
    global Database.QueryLocator start(Database.BatchableContext bc) {
            String query = 'select id,Contract_Length_Mths__c,Contract_start_date__c  from opportunity where Legacy_Source_System__c=\'XYZ\'';
            //String query = 'select id,Contract_Length_Mths__c,Contract_start_date__c   from opportunity where id=\'0069E00000BCdj3QAD\'';
            return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext bc, List<opportunity> scope) {
            list<Profit_Centre_Split__c > lstProfitCenterSplits = new list<Profit_Centre_Split__c >();


               for(opportunity opp: scope){

                for(Profit_Centre_Split__c pcs:[select id,Opportunity__c,Opportunity__r.Contract_Length_Mths__c  , PCAmount__c, Share__c, Profit_Centre__c, Revenue_Schedule_Type__c
                from Profit_Centre_Split__c
                where Opportunity__c =:opp.id]){

                        // BW_PCRevenueController ctrl = new BW_PCRevenueController();
                         List<Double> vals = new list<Double>();
                         system.debug('@@'+pcs.Opportunity__r.Contract_Length_Mths__c);
                         vals = RevenueDistributionHelper.getMonthlyPercentages('Flat', pcs.Opportunity__r.Contract_Length_Mths__c.intValue());  
                         Decimal totalAllocated = 0;
                         Integer i = 0;
                         List<Revenue_Schedule__c> schedules= new list<Revenue_Schedule__c>();
                         /*We check if there are schedules in system if yes we skip that record else we enter into logic at line 27*/
                         schedules=[select id from Revenue_Schedule__c  where Opportunity__c=:pcs.Opportunity__c];
                         if(schedules.isEmpty()){
                         schedules = createNewFlatSchedule(pcs.PCAmount__c, pcs.Id,opp);
                             for(Revenue_Schedule__c rs : schedules){
                                    rs.Opportunity__c=pcs.Opportunity__c;
    
                                    if(i == vals.size() - 1){
                                        rs.Amount__c = pcs.PCAmount__c - totalAllocated;                
                                    }
                                    else{
                                        rs.Amount__c = pcs.PCAmount__c * vals[i];
                                    }
                                    totalAllocated += rs.Amount__c;
                                    i++;
                                }
                              
                            if(!schedules.isEmpty()){
                               upsert schedules;
                            }
                      }
                     }
                     }
               
    }
    global void finish(Database.BatchableContext bc) {
    }
    global List<Revenue_Schedule__c> createNewFlatSchedule(Decimal amount, Id pcsId,opportunity opp){
        List<Revenue_Schedule__c> newSchedules = new List<Revenue_Schedule__c>();
        Integer scheduleamount;
        if(opp.Contract_Length_Mths__c!=null){
            scheduleamount= (amount / opp.Contract_Length_Mths__c).intValue();
            }
        decimal totalAllocated = 0;
        //selectedRevenueType = 'Flat';

        for(Integer i = 0; i < opp.Contract_Length_Mths__c ; i++){
            Revenue_Schedule__c schedule = new Revenue_Schedule__c();
            schedule.Schedule_Frequency__c = 'Monthly';
            schedule.Profit_Centre_Split__c = pcsId;
            schedule.Payment_Date__c = opp.Contract_start_date__c.addMonths(i);
            
            if(i == opp.Contract_Length_Mths__c - 1){
                schedule.Amount__c = amount - totalAllocated;
                totalAllocated += schedule.Amount__c;
            }
            else{
                schedule.Amount__c = scheduleAmount;
                totalAllocated += scheduleAmount;
            }
            
            newSchedules.add(schedule);
        }

        return newSchedules;
    }
}



Error:

Class name: BW_RevenueScheduleBatchTest

Method Name: testBW_RevenueScheduleBatch

System.DmlException: Upsert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, OpportunityTrigger: execution of AfterUpdate caused by: System.AsyncException: Future method cannot be called from a future or batch method: SubscriberHelper.evaluateOpptyRulesFuture(Set<Id>) Trigger.OpportunityTrigger: line 29, column 1: []
Stack Trace: Class.BW_RevenueScheduleBatch.execute: line 44, column 1
Best Answer chosen by Andrew North
Sai PraveenSai Praveen (Salesforce Developers) 
Hi,

Please add the below code and run the test class. This should not fail now.

Now evaluateOpptyRulesFuture method will only be called if it is not from batch.
trigger OpportunityTrigger on Opportunity (
    before insert, 
    before update, 
    before delete, 
    after insert, 
    after update, 
    after delete, 
    after undelete) {
        
        ComponentDisabler__mdt disabler = [SELECT Id, OpportunityTriggerDisabled__c 
                                           FROM ComponentDisabler__mdt 
                                           WHERE DeveloperName='Trigger_Disabler'];

        if ( disabler.OpportunityTriggerDisabled__c ) { return; }
        
        if (Trigger.isBefore && StaticHelper.triggerFirstRun) {
            if(Trigger.isUpdate){
                OpportunityTriggerHelper.createContracts(Trigger.new, Trigger.oldMap);
            }
            else if(Trigger.isInsert) {
                OpportunityTriggerHelper.createContracts(Trigger.new);
            }
            StaticHelper.triggerFirstRun = false;
        } 
        else if (Trigger.isAfter) {
            if(Trigger.isUpdate){
                RevenueScheduleHelper.updateProfitCentreSplits(Trigger.new, Trigger.oldMap);
                OpportunityTriggerHelper.setPrimaryProfitCentre(Trigger.new);
                if(!System.isBatch())
                SubscriberHelper.evaluateOpptyRulesFuture(Trigger.newMap.KeySet());
                SectorHeadHelper.setSectorHeads(Trigger.newMap.KeySet());
            }
            else if(Trigger.isInsert){
                OpportunityTriggerHelper.populateOppsOnContracts(Trigger.new);
                OpportunityTriggerHelper.createPrimaryPCSplit(Trigger.new);
                if(!System.isBatch())
                SubscriberHelper.evaluateOpptyRulesFuture(Trigger.newMap.KeySet());
                SectorHeadHelper.setSectorHeads(Trigger.newMap.KeySet());
            }
            else if(Trigger.isDelete){
                OpportunityTriggerHelper.deleteContracts(Trigger.old);
            }
        }
}
If this solution helps, Please mark it as best answer

Thanks,

All Answers

Sai PraveenSai Praveen (Salesforce Developers) 
Hi Andrew,

Because of the batch class it is updating some record related to the opportunity and which is why the apex trigger on opportunity if getting fired. In the opportunity trigger it is calling the Future method.
But as per salesforce we cannot call Future method from another future method or batch.

So one fix would be to bypass the code of that future methos when running from the batch by using
 
System.isBatch()

The fix to comment the highlited code wil effect total batch class. The batch will not work at all. If you want to fix this error for deployment and fix the later part of the error you can continue with this.

If this solution helps, Please mark as best answer

Thanks
Andrew NorthAndrew North
Hi Sal

Thank you very much for replying to my post, it's appreaciated.

Where would I need to add in the below?
System.isBatch()

Thank you

Sai PraveenSai Praveen (Salesforce Developers) 
Hi Andrew,

You need to add the below piece of code on the future method you had on opportunityTrigger. 
 
!System.isBatch()
Please note if you add this piece of code this future method will not be called by the records which are getting updated by the batch. 

Thanks,
Andrew NorthAndrew North

trigger OpportunityTrigger on Opportunity (
    before insert, 
    before update, 
    before delete, 
    after insert, 
    after update, 
    after delete, 
    after undelete) {
        
        ComponentDisabler__mdt disabler = [SELECT Id, OpportunityTriggerDisabled__c 
                                           FROM ComponentDisabler__mdt 
                                           WHERE DeveloperName='Trigger_Disabler'];

        if ( disabler.OpportunityTriggerDisabled__c ) { return; }
        
        if (Trigger.isBefore && StaticHelper.triggerFirstRun) {
            if(Trigger.isUpdate){
                OpportunityTriggerHelper.createContracts(Trigger.new, Trigger.oldMap);
            }
            else if(Trigger.isInsert) {
                OpportunityTriggerHelper.createContracts(Trigger.new);
            }
            StaticHelper.triggerFirstRun = false;
        } 
        else if (Trigger.isAfter) {
            if(Trigger.isUpdate){
                RevenueScheduleHelper.updateProfitCentreSplits(Trigger.new, Trigger.oldMap);
                OpportunityTriggerHelper.setPrimaryProfitCentre(Trigger.new);
                SubscriberHelper.evaluateOpptyRulesFuture(Trigger.newMap.KeySet());
                SectorHeadHelper.setSectorHeads(Trigger.newMap.KeySet());
            }
            else if(Trigger.isInsert){
                OpportunityTriggerHelper.populateOppsOnContracts(Trigger.new);
                OpportunityTriggerHelper.createPrimaryPCSplit(Trigger.new);
                SubscriberHelper.evaluateOpptyRulesFuture(Trigger.newMap.KeySet());
                SectorHeadHelper.setSectorHeads(Trigger.newMap.KeySet());
            }
            else if(Trigger.isDelete){
                OpportunityTriggerHelper.deleteContracts(Trigger.old);
            }
        }
}

Hi Sal

Sorry for all the questions, I have very limited dev skills.

I've included of the Opportunity Trigger above but there doesnt appear to be a Future Method being listed?

Sai PraveenSai Praveen (Salesforce Developers) 
Hi,

Please add the below code and run the test class. This should not fail now.

Now evaluateOpptyRulesFuture method will only be called if it is not from batch.
trigger OpportunityTrigger on Opportunity (
    before insert, 
    before update, 
    before delete, 
    after insert, 
    after update, 
    after delete, 
    after undelete) {
        
        ComponentDisabler__mdt disabler = [SELECT Id, OpportunityTriggerDisabled__c 
                                           FROM ComponentDisabler__mdt 
                                           WHERE DeveloperName='Trigger_Disabler'];

        if ( disabler.OpportunityTriggerDisabled__c ) { return; }
        
        if (Trigger.isBefore && StaticHelper.triggerFirstRun) {
            if(Trigger.isUpdate){
                OpportunityTriggerHelper.createContracts(Trigger.new, Trigger.oldMap);
            }
            else if(Trigger.isInsert) {
                OpportunityTriggerHelper.createContracts(Trigger.new);
            }
            StaticHelper.triggerFirstRun = false;
        } 
        else if (Trigger.isAfter) {
            if(Trigger.isUpdate){
                RevenueScheduleHelper.updateProfitCentreSplits(Trigger.new, Trigger.oldMap);
                OpportunityTriggerHelper.setPrimaryProfitCentre(Trigger.new);
                if(!System.isBatch())
                SubscriberHelper.evaluateOpptyRulesFuture(Trigger.newMap.KeySet());
                SectorHeadHelper.setSectorHeads(Trigger.newMap.KeySet());
            }
            else if(Trigger.isInsert){
                OpportunityTriggerHelper.populateOppsOnContracts(Trigger.new);
                OpportunityTriggerHelper.createPrimaryPCSplit(Trigger.new);
                if(!System.isBatch())
                SubscriberHelper.evaluateOpptyRulesFuture(Trigger.newMap.KeySet());
                SectorHeadHelper.setSectorHeads(Trigger.newMap.KeySet());
            }
            else if(Trigger.isDelete){
                OpportunityTriggerHelper.deleteContracts(Trigger.old);
            }
        }
}
If this solution helps, Please mark it as best answer

Thanks,
This was selected as the best answer
Andrew NorthAndrew North
Thank you very much for taking the time to answer my questions Sal, you're a life saver!