You need to sign in to do that
Don't have an account?
Eric Blaxton 11
Database.executeBatch cannot be called from a batch start, batch execute, or future method
Hi and thanks in advance for any guidance.
Background: This code works great in getting a JSON response and creating cases. The issues come when I schedule the class, as shown in my code snippet below, 'Class global class MaintenanceRequest_Sch implements Schedulable '. It's calling the Asynchronous method in 'Maint. Request class.
Here's my code snippets:
Background: This code works great in getting a JSON response and creating cases. The issues come when I schedule the class, as shown in my code snippet below, 'Class global class MaintenanceRequest_Sch implements Schedulable '. It's calling the Asynchronous method in 'Maint. Request class.
Here's my code snippets:
public class MaintenanceRequest { // need this for scheduling callouts. @future(callout=true) public static void getMaintenanceTicketsAsync() { getMaintenanceTickets(); } public static void getMaintenanceTickets() { // Call public class ApiBearerKey to get new bearer key String tokenkey = ApiBearerKey.getBearerKey(); // use Custom Settings in the future String prdEndpoint = system.label.Portal_Prod_Endpoint; //Date variables for Endpoint //Put these in Custom settings, so I can adjust without redeploying Date d1= Date.today()-30; String geDate = DateTime.newInstance(d1.year(),d1.month(),d1.day()).format('YYYY-MM-dd'); Date d2 = Date.today(); String leDate = DateTime.newInstance(d2.year(),d2.month(),d2.day()).format('YYYY-MM-dd'); String Jan12021 = '2021-01-01';//users only want to load cases with report date 1/1/2021 //Variable created to exclude all Cases with Short Description contains "Variance" String exclude1 = '\'Variance\''; HTTP h = new HTTP(); HTTPRequest req = new HTTPRequest(); //Odata query. We can get this running once Modified date is populated. //req.setEndpoint(prdEndpoint + '?$filter=ODATA quere sample req.setTimeout(120000); req.setMethod('GET'); req.setHeader('Authorization', 'Bearer ' + tokenkey); HTTPResponse res = h.send(req); // This call returns 1.43 (1443502) MB. Using Modified dates for 10 days Blob reqBody = res.getBodyAsBlob(); system.debug('getsize ' + reqbody.size()); system.debug('getsize ' + res); String strResponse = res.getBody(); if(res.getstatusCode() == 200 && res.getbody() != null) //system.debug('Response: ' + res.getBody()); { Map<String,Object> newMap = (Map<String, Object>)JSON.deserializeUntyped(strResponse); //strResponse gets top level and lower level details //List of records passed. List<Object > valuelist = (List<Object>) newMap.get('value'); createMaintCase.upsertCases(valuelist); } }
global class MaintenanceRequest_Sch implements Schedulable { public void execute(SchedulableContext context) { MaintenanceRequest.getMaintenanceTicketsAsync(); } }
public class createMaintCase { //List of Cases passed in from MaintenanceRequest.apxc //public static List<String> upsertCases(List <Object> caseList) public static void upsertCases(List <Object> caseList) { //system.debug('Case List ' + caseList); // Instantiate new list List<Case> toBeUpsertCases = new List<Case>(); //List holding Maint Request Id's List<String> ticketIds = new List<String>(); //List holding ticketIds, not to be confused with ticketId which holds maintrequestid's //List<String> listOfTicketIds = new List<String>(); // Id caseMaintRecordTypeId = Schema.Sobjecttype.Case.getRecordTypeInfosByDeveloperName().get('SAP_Cases').getRecordTypeId(); // Loop through list and load up received values in List toBeUpsertedCases Set<String> setReferenceId = new Set<String>(); List<String> allids = new List<String>(); system.debug('1st Case List: ' + caseList.size() ); for(Object obj: caseList) { Map<String,Object> valueMap = (Map<String, Object> )obj; // all Values Map<String,Object> locdetails = (Map<String, Object>)valueMap.get('location'); // Location details if(null != locdetails) { // add Null check because some location details don't have a SAP Reference Id if (null != locdetails.get('sapReferenceId')) { String sapReferenceId= String.valueOf(locdetails.get('sapReferenceId')); if(null != sapReferenceId) { setReferenceId.add(sapReferenceId); // Load up sapReference Id's in Set allids.add(sapReferenceId); // I load a map of all id's, but debug is forcefully truncated by SF. Will need to store in an object or write to a file. } } } } if(!setReferenceId.isEmpty()) { // put variables into custom setting. to save from redeploying. custom setting will allow // me to vary sizes for testing. if (setReferenceId.size() > 25) { system.debug('setReferenceId.size: ' + setReferenceId.size()); system.debug('caseList.size: ' + caseList.size()); Database.executeBatch(new CreateMaintCase_Batch(setReferenceId, caseList), 25); return; } //Fetching the assignment rules on case AssignmentRule AR = new AssignmentRule(); // Get Case Active assignment rule AR = [Select id from AssignmentRule where SobjectType = 'Case' and Active = true limit 1]; //Creating the DMLOptions for "Assign using active assignment rules" checkbox Database.DMLOptions dmlOpts = new Database.DMLOptions(); dmlOpts.assignmentRuleHeader.assignmentRuleId= AR.id; Map<String,Account> mapReferenceIdVsAccount = new Map<String, Account>(); //Map<String,Account> mapRefIdNotFound = new Map<String, Account>(); //Load SAP Reference ID (Bus Loc) into Map for(Account acc: [Select Id, Name, AccountNumber From Account Where AccountNumber IN: setReferenceId]) { mapReferenceIdVsAccount.put(acc.AccountNumber , acc); } //System.debug('### mapReferenceIdVsAccount = '+ mapReferenceIdVsAccount); if(!mapReferenceIdVsAccount.isEmpty()) { //This is where we pass caseList to batch class CreateMaintCase_Batch for (Object obj : caseList ) { Map<String,Object> valueMap = (Map<String, Object> )obj; string ticketid= String.valueOf(valueMap.get('ticketId')); string shortDescription= String.valueOf(valueMap.get('shortDescription')); string ticketNumber = String.valueOf(valueMap.get('ticketNumber')); string longDescription= String.valueOf(valueMap.get('longDescription')); Datetime reportDate; Datetime statusDate; Datetime closeDate; //Create cases from JSON values // Loop through and add case to List toBeUpsertCases.add(newcase); } //end if(mapReferenceIdVsAccount.containsKey(sapReferenceId)) }//end if (null != String.valueOf(locdetails.get('sapReferenceId'))) } }// end for (Object obj : caseList ) } } // Upsert Maint Request cases //system.debug('toBeUpsertCases: ' + toBeUpsertCases.size() ); Schema.SObjectField f = Case.Ticket_ID__c; if(!toBeUpsertCases.isEmpty()){ Database.UpsertResult [] cr = Database.upsert(toBeUpsertCases, f, false); system.debug('CR from create maintenance case : ' + cr.size()); }// end public static void upsertCases(List <Object> caseList) }// end public class createMaintCase
global class CreateMaintCase_Batch implements Database.Batchable<sObject>, Database.Stateful { List<Case> toBeUpsertCases; //Fetching the assignment rules on case AssignmentRule AR = new AssignmentRule(); string query; Set<String> setReferenceId; List <Object> caseList; String emailBody; Integer numberOfFailedCases; global CreateMaintCase_Batch(Set<String> setReferenceId, List <Object> caseList) { this.setReferenceId = setReferenceId; this.caseList = caseList; //Create Query query = 'Select Id, Name, AccountNumber From Account Where AccountNumber IN :setReferenceId'; system.debug('Query ' + query); toBeUpsertCases = new List<Case>(); emailBody = 'Failed Cases\n'; numberOfFailedCases = 0; } //Makes it batchable global Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator(query); } //Execute code global void execute(Database.BatchableContext bc, List<Object> accounts) { // Get Case Active assignment rule AR = [Select id from AssignmentRule where SobjectType = 'Case' and Active = true limit 1]; //Creating the DMLOptions for "Assign using active assignment rules" checkbox Database.DMLOptions dmlOpts = new Database.DMLOptions(); dmlOpts.assignmentRuleHeader.assignmentRuleId= AR.id; Map<String, Account> mapReferenceIdVsAccount = new Map<String, Account>(); for(Account acc: (List<Account>)accounts) { mapReferenceIdVsAccount.put(acc.AccountNumber , acc); } Id caseMaintRecordTypeId = Schema.Sobjecttype.Case.getRecordTypeInfosByDeveloperName().get('SAP_Cases').getRecordTypeId(); //Populate cases off Json values // Loop through and add case to List toBeUpsertCases.add(newcase); processedSapIds.add(sapReferenceId); // Stop once list sizes don't match if (processedSapIds.size() == mapReferenceIdVsAccount.size()) { break; } } //end if(mapReferenceIdVsAccount.containsKey(sapReferenceId)) }//end if (null != String.valueOf(locdetails.get('sapReferenceId'))) } }// end for (Object obj : caseList ) Schema.SObjectField f = Case.Ticket_ID__c; // This defines Ticket_Number__c as the External ID key system.debug('Before upsert: ' + tobeUpsertCases.size()); if(!toBeUpsertCases.isEmpty()){ Database.UpsertResult [] cr = Database.upsert(toBeUpsertCases, f, false); //Clear list before next batch processes tobeUpsertCases.clear(); system.debug('After upsert: ' + tobeUpsertCases.size()); } //Finish the Batch global void finish(Database.BatchableContext bc) { } }
I recommend reviewing the following resources to learn more
https://techemizen.medium.com/how-to-invoke-batch-apex-from-another-batch-apex-in-salesforce-1a7148a6b078
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm
Let me know if this helps
Appreciate your time.
I know the reason it's happening. The articles you sent don't apply to my situation as I am not trying to call one batch from another. The issue comes in because of the @future method I am using coupled with the database.executebatch. My design is flawed. Do you have any advice to overcome this obstacle?
1. Could using queable class help solve my problem, which is not being able to Schedule the batch?
2. Do I need to redesign? One idea is to push all the data into a temp object and then create my cases as a Batch.
Regards,
Eric