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
Khemais MenzliKhemais Menzli 

I get an exception when Apex Job starts

Hello every one,

I started working with SF apex to add some business to my company.
I have a requirement to update each opportunity with data coming from a contact, to do it I created an apex scheduler that runs over all OpportunityContactRole in my Database and for each row I get the Contact+Opportunity then I do some logic to update opportunity with data coming from the corresponding contact.
I choosed to use a scheduler instead of a trigger because event on object OpportunityContactRole are not fired!!

I scheduled my apex-class (the scheduler) to be launched each day at 3 A.M
But when I go to «Apex Jobs» page to monitor the status of My Apex job, I see that the Job is failed with this status
JobType : Batch Apex
Status : Completed
StatusDetail :
First error: Update failed. First exception on row 0 with id 0060j0000024tVGAAY; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, OpportunityTrackerTrigger: execution of AfterUpdate
caused by: System.AsyncException: Future method cannot be called fr...
Total Baches : 1
Baches proceeded : 1
Failures : 1

Any help is appriciated, I'm open to all suggestions
Thank's
LBKLBK
Hi Khemais,

You have an AFTER UPDATE trigger named OpportunityTrackerTrigger in your ORG which is causing trouble.

Can you post the code in the Trigger and your Batch Job as well? It will help us debug it better.
Khemais MenzliKhemais Menzli
Thank you for your reply
Code of OpportunityTrackerTrigger is (running as expected on production environment :
trigger OpportunityTrackerTrigger on Opportunity (after update) {
    String parameters = '';
    for (Opportunity opp : Trigger.new) {
        // Access the "old" record by its ID in Trigger.oldMap
        Opportunity oldOpp = Trigger.oldMap.get(opp.Id);

        parameters += 'newName='+EncodingUtil.urlEncode(opp.Name, 'UTF-8');
        if(oldOpp.stageName != opp.stageName) {
            parameters += '&oldstageName='+EncodingUtil.urlEncode(oldOpp.stageName, 'UTF-8');
            parameters += '&newstageName='+EncodingUtil.urlEncode(opp.stageName, 'UTF-8');
        }
        parameters += '&oldamount='+oldOpp.Amount;
        parameters += '&newamount='+opp.Amount;
        parameters += '&oldclosedate='+oldOpp.CloseDate.format();
        parameters += '&newclosedate='+opp.CloseDate.format();
        String olddescription = '';
        String newdescription = '';
        if(oldOpp.Description != null) {
          olddescription = oldOpp.Description;
        }
        if(opp.Description != null) {
          newdescription = opp.Description;
        }
        parameters += '&olddescription='+olddescription;
        parameters += '&newdescription='+newdescription;
        
        if (!Test.isRunningTest()) {        
            HttpCallout.getContent(ConfigurationManager.CALLOUT_ENDPOINT+'salesforce/addupdatecomment/'+EncodingUtil.urlEncode(oldOpp.Name, 'UTF-8')+'?'+parameters);
        }
    }
}

Code of Scheduler is :
global class UpdateOppLeadsBasedOnContactScheduler implements Schedulable 
{
    global void execute(SchedulableContext sc) 
    {
        //--- The scheduler itself : may be we can run it without need to BatchJob
        UpdateOppLeadsBasedOnContactBatchJob b = new UpdateOppLeadsBasedOnContactBatchJob(); 
        //--- Start Scheduler 14/03/2017 : mod-1948
        database.executebatch(b);
        
    }
}


Code of Job is :
 
global class UpdateOppLeadsBasedOnContactBatchJob implements Database.Batchable<sObject> {
    
    global UpdateOppLeadsBasedOnContactBatchJob(){
              // Batch Constructor
     }
     
    // Start Method
     global Database.QueryLocator start(Database.BatchableContext BC){
        String query = 'SELECT Id, IsPrimary, ContactId, OpportunityId FROM OpportunityContactRole where IsPrimary = true ';
        
        return Database.getQueryLocator(query);
     } 
     // Execute Logic
     global void execute(Database.BatchableContext BC, List<OpportunityContactRole> ocrList){
            // Logic to be Executed batch wise
             if(!ocrList.isEmpty()){
                     
                    for(OpportunityContactRole ocr : ocrList) {
                        // check all contact who have .....
                        List<Contact> contacts = [SELECT Name, Email, LeadSource, Lead_Source_Sub__c FROM Contact WHERE ID = :ocr.ContactID];
                        //--- Get Opportunity List From OCR
                        List<Opportunity> opportunities = [SELECT id, name,Opportunity_Source_Sub__c,Opportunity_Source__c from Opportunity where ID = :ocr.OpportunityID];
                        if(!contacts.isEmpty() && !opportunities.isEmpty()){
                            //--- Get current contact
                            Contact c = contacts[0];
                            //--- Get current opportunity
                            Opportunity o = opportunities[0];
                            //--- Check of opp lead-source is null or equals to 'direct
                            if ((String.isBlank(o.Opportunity_Source__c)) || (o.Opportunity_Source__c.contains('direct'))) {
                                //--- Check if contact is not equels to null and contact lead-source is equals to 'Direct Contact'
                                if ((String.isNotBlank(c.LeadSource)) || (!c.LeadSource.contains('direct'))) {
                                     System.debug('==== Start phase OpportunityVSContact phase 1====');
                                     System.debug('Contact LeadSource value is '+c.LeadSource);
                                     //--- Put lead-source coming from contact into the current opp 
                                    o.Opportunity_Source__c = c.LeadSource;
                                    o.Opportunity_Source_Sub__c = c.Lead_Source_Sub__c;
                                    //--- Update the opp
                                    update o;
                                }
                            }            
                        }
                    
                    }
             }
   
     }
     
     global void finish(Database.BatchableContext BC){
   
     }
    
}



I have Used the SFDC UI to setup when the scheduler is launched
Khemais MenzliKhemais Menzli
@LBK : any news from your side?
 
Khemais MenzliKhemais Menzli
When I disable the Tigger called : OpportunityTrackerTrigger, the Scheduler seams to run, BUT I can't desable this trigger in production (business constraint that involves to keep this trigger running)

I'm clueless, please I need help
LBKLBK
Hi Khemais,

I have did some reading on your issue.

And, I have found that the getContent() and getContentAsPDF() cannot be called from a Batch Apex.

Here are some articles that discuss about this and provide some work around.

https://developer.salesforce.com/forums/?id=906F0000000BP6DIAW
https://developer.salesforce.com/forums/?id=906F0000000AlGoIAK

Is your trigger tries to create a BLOB through the HTTPCallout.getContent()?

We may have to figure out an alternative way to incorporate this logic in the Batch Apex.