1

I created a batch class that calling an API and then update the records. This batch running every 5 minutes. And there`s a custom button, if I click that custom button, the batch will run only for that single record (Resync Button).

This batch works fine for a single record update (resync button) but error

"You have uncommitted work pending. Please commit or rollback before calling out, at line 83"

when processing a bulk record (around 8k records). I had set the batch size, production and sandbox, 35 batch size. I tried to run this batch in sandbox, with 35 records (1 batch), and it worked just fine, but failed in production, with more than 5k records, some records are updated, but some records failed.

This is line 83 :

responseObj = httpObj.send(requestObj);

and this is my code :

Global class InvoiceHeaderContactBatch implements Database.Batchable<SObject>, Database.AllowsCallouts {     
private String endPointURL = label.BP_Invoice_Contact;
private String apiKey = label.BP_Api_Key;

global List<Invoice__c> invoiceListToUpsert = new List<Invoice__c>();
private static final String ACCOMODATION = 'Accomodation';
private static final String EOM = 'End of Month';
private static final String TM = 'This Month';
private static final String NM = 'Next Month';
private static final String COL_1 = 'COL 1';
private static final String COL_2 = 'COL 2';
private static final String COL_3 = 'COL 3';
private static final String COL_4 = 'COL 4';
private static final String CURRENT = 'Current';
private String invoiceStartDate{get;set;}
private String invoiceEndDate{get;set;}
private String invoiceNumber{get;set;}
private Date todayDate = System.today();

global InvoiceHeaderContactBatch(String startDate, String endDate, String invNumber) {
    this.invoiceStartDate = startDate;
    this.invoiceEndDate = endDate;
    this.invoiceNumber = invNumber;
}

global Database.QueryLocator start(Database.BatchableContext bc) {

    DescribeSObjectResult dr = Invoice__c.getSObjectType().getDescribe();
    List<String> fieldList = new List<String>(dr.fields.getMap().keySet());
    String fields = String.join(fieldList, ',');
    String q = ' SELECT ' + fields + ' FROM Invoice__c WHERE Invoice_Status_EBP__c != null';
    if (String.isNotBlank(invoiceStartDate) && String.isNotBlank(invoiceEndDate)) {
        Datetime startDatetime = (Datetime) JSON.deserialize('"'+invoiceStartDate+'"', DateTime.class);
        Datetime endDatetime = (Datetime) JSON.deserialize('"'+invoiceEndDate+'"', DateTime.class);
        String invoiceN = invoiceNumber;
        q = q + ' AND ((Invoice_Datetime__c >= :startDatetime AND Invoice_Datetime__c <= :endDatetime) OR (EBP_Last_Modified_Date__c >= :startDatetime AND EBP_Last_Modified_Date__c <= :endDatetime))';
    }
    if (String.isNotBlank(invoiceNumber)) {
        q = q + ' AND Invoice_Number__c = :invoiceNumber';
    }
    q = q + ' ORDER BY CreatedDate DESC';

    return Database.getQueryLocator(q);
}

global void execute(Database.BatchableContext bc, List<Invoice__c> scope) {
    System.debug(LoggingLevel.DEBUG, '***SCOPE.size(): ' + scope.size());
    System.debug(LoggingLevel.DEBUG, '***execute InvoiceHeaderContactBatch');
    for (Invoice__c invoiceObj : scope) {
        Invoice__c invoiceToUpsert = invoiceObj.clone();
        Invoice__c iObj = invoiceObj;
        invoiceToUpsert.Invoice_Number__c = invoiceObj.Invoice_Number__c;

        //invoiceToUpsert.Account_Payable_EBP__c = '';

        String responseString = '';
        System.debug('***Heap: ' + Limits.getHeapSize());
        Http httpObj = new Http();
        HttpRequest requestObj = new HttpRequest();

        String reqEndpoint = endPointURL+'?apiKey='+apiKey+'&key=businessPartnerId&value=ACT%23'+iObj.TERA_Hotel_ID__c;    
        requestObj.setEndpoint(reqEndpoint);
        requestObj.setMethod('GET');
        requestObj.setHeader('Content-Type', 'application/json');
        System.debug('***reqEndpoint' + reqEndpoint);
        System.debug(LoggingLevel.DEBUG, '***requestObj' + requestObj);
        System.debug(LoggingLevel.DEBUG, '***request content-type: ' + requestObj.getHeader('Content-Type'));
        HttpResponse responseObj;

        try {
            if (test.isRunningTest()) {
                StaticResource sr = [SELECT Id, Body FROM StaticResource WHERE Name = 'InvoiceHeaderContactResponse'];
                responseString = sr.Body.toString();    
            }
            else {
                responseObj = httpObj.send(requestObj); 
                responseString = responseObj.getBody();
            }
            system.debug('response ==>'+responseString);

            JSONParser parser = JSON.createParser(String.valueof(responseString));
            while (parser.nextToken() != null) {
                // Start at the array of entities.
                if (parser.getCurrentToken() == JSONToken.START_ARRAY) {
                    while (parser.nextToken() != null) {
                        // Mark where the object start and find next entities statement object.
                        if (parser.getCurrentToken() == JSONToken.START_OBJECT) {
                            // Read entire invoice contact BP object, including its array of line items.
                            entities entt = (entities)parser.readValueAs(entities.class);
                            if (entt.attributes.contactTypeId.Contains('PayableEmail')){
                                invoiceToUpsert.Account_Payable_EBP__c += entt.attributes.contactEntry+';';
                            }
                            if (entt.attributes.contactTypeId.Contains('MarketManagerEmail')){
                                invoiceToUpsert.Market_Manager_Email_EBP__c = entt.attributes.contactEntry; 
                            }

                            // Skip the child start array and start object markers.
                            parser.skipChildren();
                    }
                }
                    //Remove Semicolon for Account Payable
                    String acPayable = invoiceToUpsert.Account_Payable_EBP__c;
                    Integer lengthOfField;  
                    lengthOfField = acPayable.length(); 
                    if (String.isNotBlank(acPayable) || String.isNotEmpty(acPayable)){
                        if (invoiceToUpsert.Account_Payable_EBP__c.Right(1) == ';') invoiceToUpsert.Account_Payable_EBP__c = invoiceToUpsert.Account_Payable_EBP__c.left(lengthOfField - 1);       
                    }
                    else {
                        invoiceToUpsert.Account_Payable_EBP__c = iObj.Account_Payable_1__c;
                        acPayable = invoiceToUpsert.Account_Payable_EBP__c;
                        lengthOfField = acPayable.length();
                        if (invoiceToUpsert.Account_Payable_EBP__c.Right(1) == ';') invoiceToUpsert.Account_Payable_EBP__c = invoiceToUpsert.Account_Payable_EBP__c.left(lengthOfField - 1);   
                    }   
                    invoiceListToUpsert.add(invoiceToUpsert);
                    //system.debug('Account Payable Email ==>'+invoiceListToUpsert[0].Account_Payable_EBP__c);
                    //system.debug('MM Email ==>'+invoiceListToUpsert[0].Market_Manager_Email_EBP__c);
            }
        } 
    } 

        catch (Exception e) {
            ErrorEventLogUtil.insertLog(System.now(), 'ERROR', '', 'InvoiceHeaderContactBatch - ' + e.getMessage() + '; Line ' + e.getLineNumber(), 'EXCEPTION ERROR');
        }
    }            
        Schema.SObjectField externalIdField = Invoice__c.Invoice_Number__c;
        try {
            Database.upsert(invoiceListToUpsert, externalIdField);
        }
        catch (Exception e) {
            ErrorEventLogUtil.insertLog(System.now(), 'ERROR', '', 'InvoiceHeaderContactBatch - ' + e.getMessage() + '; Line ' + e.getLineNumber(), 'UPSERT FAILED');
        }
    }

//Inner Classes for JSON Parsing Purposes
public class entities {
    public String id;
    public attributes attributes;
}

public class attributes {
    public String businessPartnerId;
    public String contactTypeId;
    public String contactEntry;
}
//End of JSON Parsing Inner Class

global void finish(Database.BatchableContext bc) {
    System.debug(LoggingLevel.DEBUG, '***finish InvoiceHeaderContactBatch');
    String INVOICE_CASE_BATCH = 'InvoiceHeaderCaseBatch';
    String COMPLETED = 'Completed';
    String ABORTED = 'Aborted';

    List<AsyncApexJob> jobList = [SELECT Id FROM AsyncApexJob WHERE ApexClass.Name = :INVOICE_CASE_BATCH AND (Status != :COMPLETED AND Status != :ABORTED) AND TotalJobItems > 1 ORDER BY CreatedDate DESC];
    System.debug(LoggingLevel.DEBUG, '***Joblist.size : '+joblist.size());
    System.debug(LoggingLevel.DEBUG, '***invoiceStartDate : '+invoiceStartDate);
    System.debug(LoggingLevel.DEBUG, '***invoiceEndDate : '+invoiceEndDate);
    System.debug(LoggingLevel.DEBUG, '***invoiceNumber : '+invoiceNumber);
    if (jobList != null && jobList.size() == 0) {
        InvoiceHeaderCaseBatch ihcb = new InvoiceHeaderCaseBatch(invoiceStartDate, invoiceEndDate, invoiceNumber);
        Database.executeBatch(ihcb, 10);
    }
}
}

**This batch is called by another batch process (chain batch)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.