I have a service that is making callouts to another system and if it fails for some reason send email notifications. When I do the service for one record it is fine (e.g. call it from a trigger). However there is also a batch class that runs for daily sync that calls it, and when it hits an error and sends an email everything after that dies out with You have uncommitted work pending. Please commit or rollback before calling out
Is this an issue of how I'm sending emails in the batch?
Batch class:
global class SyncWithSomeSystem implements Database.Batchable<SObject>, Database.AllowsCallouts, Schedulable {
public void execute(SchedulableContext sc) {
Database.executeBatch(new SyncWithSomeSystem(), 50);
}
global Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator([Some querry]);
}
global void execute(Database.BatchableContext BC, List<Contract__c> scope) {
SomeSystemService service = new SomeSystemService();
for (Contract__c contract : scope) {
service.handleSubscription(contract.Id);
}
}
global void finish(Database.BatchableContext BC) {}
}
Service class:
public class SomeSystemService {
public void handleSubscription(Id contractId) {
try {
List<ContractLineItem__c> lineItems = this.getLineItems(contractId);
if(lineItems.isEmpty()) { return; }
Contact contact = this.getContact(lineItems[0].Contract__r.Account__c);
for(ContractLineItem__c lineItem : lineItems) {
Subscription subcription = new Subscription(lineItem, contact);
List<Subscription> existingSubscriptions = this.getSubscriptions(contact, subcription.plan_currency);
if (existingSubscriptions.isEmpty()) {
this.createSubscription(subcription);
} else if (!this.subscriptionExists(subcription, existingSubscriptions)) {
this.updateSubscription(subcription);
}
}
} catch (SomeSystemServiceException e) {
this.emailNotification(e);
}
}
private Boolean subscriptionExists(Subscription subscription, List<Subscription> existingSubscriptions) {
for(Subscription existing : existingSubscriptions) {
if(existing.effective_date == subscription.effective_date &&
existing.value == subscription.value) {
return true;
}
}
return false;
}
private List<Subscription> getSubscriptions(Contact contact, string currencyIsoCode) {
//GET callout
}
private void createSubscription(Subscription request) {
//POST Callout
}
private void updateSubscription(Subscription request) {
//PUT Callout
}
public void emailNotification(SomeSystemServiceException error) {
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
String[] toAddresses = new String[] { SomeSystemSettingsUtil.errorEmailRecipient() };
mail.setToAddresses(toAddresses);
mail.setSenderDisplayName('Salesforce SomeSystem Integration');
mail.setSubject('Creating subscription failed');
String messageBody = 'Could not create a subscription for Contract.\n';
messageBody += '\n\nError details:';
messageBody += '\n' + error.getMessage();
mail.setPlainTextBody(messageBody);
this.sendEmail(mail);
}
public void sendEmail(Messaging.SingleEmailMessage mail) {
try {
Messaging.reserveSingleEmailCapacity(1);
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
} catch (NoAccessException e) {
System.debug('could not send email: ' + e.getMessage());
}
}
public class SomeSystemServiceException extends Exception { }
}