[SalesForce] Using Batch APEX to generate template body dynamically with merge field values

I am using Amazon SES to send mails to the all opportunities using batch apex with the body of email template. I'm using the solution proposed in an answer to Using APEX to assemble HTML Letterhead Emails. Basically i'm fencing the Messaging.sendEmail() between a savepoint and rollback in order to get the template applied.

Here is the code

Global class Class1 implements
    Database.Batchable<sObject>,Database.AllowsCallouts{

    Global Database.QueryLocator start(Database.BatchableContext BC) {
        String query = 'SELECT Id,name,accountid,contactid__c,email__c from opportunity';
        return Database.getQueryLocator(query);
    }

    Global void execute(Database.BatchableContext BC, List<Opportunity> Scope) {
        for(Opportunity oop:scope){
            list<string> toAddresses = new list<string>();
            toAddresses.add('sample@gmail.com');

Here i am using email technique with merge field values, there i am receiving complete body

    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();     
    mail.setWhatId(oop.Id);     mail.settargetObjectId(opp.contactid__c);
    mail.setTemplateId(tempbody); 
    mail.setToAddresses( toAddresses);

    Savepoint sp = Database.setSavepoint(); 
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail }); 
    Database.rollback(sp);

    tempbody = mail.getPlainTextBody();
    httpReq.setEndpoint('https://email.us-east-1.amazonaws.com');
    [..]        

Here i am calling http callout to send mail by using the Amazon SES

            httpReq.setBody(email);
            System.debug(httpReq.getBody());
            Http http = new Http();
            HttpResponse response = http.send(httpReq);        
        } 
    }   
}

Here i am getting error like , You have uncommitted work pending. Please commit or rollback before calling out

I feel that calling http callout and commit and rollback in same causing the issue.
Please suggest me where i am doing wrong, Thanks in advance.

Best Answer

Although the error says to commit or rollback before making the callout, and you are doing the rollback, SFDC seems to ignore this when the callout is executed - this could be because of batch context but I didn't have time to explore that.

OK, here are some ways to work around the error

Option 1 You will need two batchable classes and one custom object Deferred_Callouts__c.

  • Batchable class 1 - use the Database.savepoint - rollback sequence to get the rendered template. Then save the email recipient id and body in the custom object Deferred_Callouts__c. insert these all in the finish() method - which then invokes Batchable class 2
  • Batchable class 2 - Read all the Deferred_Callouts__c and construct the callouts

Considerations:

  • You are limited in the # of callouts so you'll need to be careful of limits in the batch size passed as scope.

Option 2 You will need one batchable class, one custom object Deferred_Callouts__c, and a workflow

  • Batchable class 1 - use the Database.savepoint - rollback sequence to get the rendered template. Then save the email recipient id and body in the custom object Deferred_Callouts__c. insert these all in the finish() method

  • Use a workflow that executes upon Create of Deferred_Callouts__c that uses an outbound message action to send via SOAP to the end point (assumes endpoint supports SOAP)

Option 3

You will need to use the Dan Appleman Async Framework as presented at Dreamforce that allows for unlimited async operations. This is well worth understanding.

This is my personal favorite as the solution is extensible to all your async needs over time

Option 4 Do what Alexander suggested and use the simulated merge field approach here . If you have formulas in the template, this will be difficult. This avoids altogether the DML operations.

Related Topic