You need to sign in to do that
Don't have an account?

System.AsyncException: Future method cannot be called from a future or batch method: SendVFAsAttachment.sendVF(String, String, String, String, Id, String, String, String)
So my use case is, when a BatchApex runs for every one hour, it inserts records on QNews custom object.
After Trigger fire every time when a record is inserted on QNews object and sends an email with a PDF attachment.
I’m getting following error when Batch Apex runs, FATAL_ERROR|System.AsyncException: Future method cannot be called from a future or batch method: SendVFAsAttachment.sendVF(String, String, String, String, Id, String, String, String).
My ApexBatch:
global with sharing class ArticleBatch implements Database.Batchable<string>{
public Iterable<string> start(Database.BatchableContext bc)
return new ArticleBatchIterable(); }
public void execute(Database.BatchableContext bc, List<string> scope){
string articleTypeName = scope[0];
QNewsUpdateBatch batchClassObj = new QNewsUpdateBatch();
batchClassObj.insertQnewsData(articleTypeName);
}
global void finish(Database.BatchableContext bc){
//code that sends an email for execution of Batch
}
My Trigger:
trigger Send_PDF_to_SharePointhelp on QNews__c (after insert, after update) {
for(QNews__c qn: Trigger.new){
string filename = qn.Article_Title__c+'_'+qn.Article_Version_Number__c+'.pdf';
String emailbody='This attachment is the PDF copy of Article: '+qn.Article_Title__c+' with version number '+qn.Article_Version_Number__c; SendVFAsAttachment.sendVF('vijaku@vsp.com',qn.Data_Category__c,emailbody,UserInfo.getSessionId(),qn.Article_ID__c,qn.Article_Title__c,filename,qn.Article_Type__c);
}
}
Future Method:
public class SendVFAsAttachment{
@future(callout=true)
public static void sendVF(String EmailIdCSV, String Subject,String body,String userSessionId, ID articleid,String ArticleTitle,String attachmentfilename,String ArticleType)
{
string newID = String.valueOf(articleid);
String addr = 'https://vsp--kmbuild.cs10.my.salesforce.com/services/apexrest/sendPDFEmail';
HttpRequest req = new HttpRequest();
req.setEndpoint( addr );
req.setMethod('POST');
req.setHeader('Authorization', 'OAuth ' + userSessionId);
req.setHeader('Content-Type','application/json');
Map<String,String> postBody = new Map<String,String>();
postBody.put('EmailIdCSV',EmailIdCSV);
postBody.put('Subject',Subject);
postBody.put('body',body);
postBody.put('newID',newID);
postBody.put('attachmentfilename',attachmentfilename);
postBody.put('ArticleTitle',ArticleTitle);
postBody.put('ArticleType',ArticleType);
String reqBody = JSON.serialize(postBody);
req.setBody(reqBody);
Http http = new Http();
HttpResponse response = http.send(req);
}
}
Class to send email, exposed to REST API:
@RestResource(urlMapping='/sendPDFEmail/*')
Global class GETPDFContent{
@HttpPost
global static void sendEmail(String EmailIdCSV, String Subject, String body,string newID,string attachmentfilename,string ArticleTitle,string ArticleType) {
List<String> EmailIds = EmailIdCSV.split(',');
PageReference ref = Page.Multi_Topic_PDF;
if(ArticleType=='Multi_Topic__kav'){
ref = Page.Multi_Topic_PDF;
}
if(ArticleType=='Additional_Benefits__kav'){
ref = Page.AdditionalBenefits_PDF;
}
if(ArticleType=='Core_Benefit__kav'){
ref = Page.CoreBenefit_PDF;
}
if(ArticleType=='Multi_Topic_Details__kav'){
ref = Page.MultiTopicDetails_PDF;
}
if(ArticleType=='Open_Enrollment__kav'){
ref = Page.OpenEnrollment_PDF;
}
if(ArticleType=='Single_Topic__kav'){
ref = Page.SingleTopic_PDF;
}
ref.getParameters().put('id',newID);
ref.setRedirect(true);
Blob b = ref.getContentAsPDF();
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
efa1.setFileName(attachmentfilename);
efa1.setBody(b);
String addresses;
email.setSubject( Subject );
email.setToAddresses(EmailIds);
email.setPlainTextBody(Body);
email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});
}
}
After Trigger fire every time when a record is inserted on QNews object and sends an email with a PDF attachment.
I’m getting following error when Batch Apex runs, FATAL_ERROR|System.AsyncException: Future method cannot be called from a future or batch method: SendVFAsAttachment.sendVF(String, String, String, String, Id, String, String, String).
My ApexBatch:
global with sharing class ArticleBatch implements Database.Batchable<string>{
public Iterable<string> start(Database.BatchableContext bc)
return new ArticleBatchIterable(); }
public void execute(Database.BatchableContext bc, List<string> scope){
string articleTypeName = scope[0];
QNewsUpdateBatch batchClassObj = new QNewsUpdateBatch();
batchClassObj.insertQnewsData(articleTypeName);
}
global void finish(Database.BatchableContext bc){
//code that sends an email for execution of Batch
}
My Trigger:
trigger Send_PDF_to_SharePointhelp on QNews__c (after insert, after update) {
for(QNews__c qn: Trigger.new){
string filename = qn.Article_Title__c+'_'+qn.Article_Version_Number__c+'.pdf';
String emailbody='This attachment is the PDF copy of Article: '+qn.Article_Title__c+' with version number '+qn.Article_Version_Number__c; SendVFAsAttachment.sendVF('vijaku@vsp.com',qn.Data_Category__c,emailbody,UserInfo.getSessionId(),qn.Article_ID__c,qn.Article_Title__c,filename,qn.Article_Type__c);
}
}
Future Method:
public class SendVFAsAttachment{
@future(callout=true)
public static void sendVF(String EmailIdCSV, String Subject,String body,String userSessionId, ID articleid,String ArticleTitle,String attachmentfilename,String ArticleType)
{
string newID = String.valueOf(articleid);
String addr = 'https://vsp--kmbuild.cs10.my.salesforce.com/services/apexrest/sendPDFEmail';
HttpRequest req = new HttpRequest();
req.setEndpoint( addr );
req.setMethod('POST');
req.setHeader('Authorization', 'OAuth ' + userSessionId);
req.setHeader('Content-Type','application/json');
Map<String,String> postBody = new Map<String,String>();
postBody.put('EmailIdCSV',EmailIdCSV);
postBody.put('Subject',Subject);
postBody.put('body',body);
postBody.put('newID',newID);
postBody.put('attachmentfilename',attachmentfilename);
postBody.put('ArticleTitle',ArticleTitle);
postBody.put('ArticleType',ArticleType);
String reqBody = JSON.serialize(postBody);
req.setBody(reqBody);
Http http = new Http();
HttpResponse response = http.send(req);
}
}
Class to send email, exposed to REST API:
@RestResource(urlMapping='/sendPDFEmail/*')
Global class GETPDFContent{
@HttpPost
global static void sendEmail(String EmailIdCSV, String Subject, String body,string newID,string attachmentfilename,string ArticleTitle,string ArticleType) {
List<String> EmailIds = EmailIdCSV.split(',');
PageReference ref = Page.Multi_Topic_PDF;
if(ArticleType=='Multi_Topic__kav'){
ref = Page.Multi_Topic_PDF;
}
if(ArticleType=='Additional_Benefits__kav'){
ref = Page.AdditionalBenefits_PDF;
}
if(ArticleType=='Core_Benefit__kav'){
ref = Page.CoreBenefit_PDF;
}
if(ArticleType=='Multi_Topic_Details__kav'){
ref = Page.MultiTopicDetails_PDF;
}
if(ArticleType=='Open_Enrollment__kav'){
ref = Page.OpenEnrollment_PDF;
}
if(ArticleType=='Single_Topic__kav'){
ref = Page.SingleTopic_PDF;
}
ref.getParameters().put('id',newID);
ref.setRedirect(true);
Blob b = ref.getContentAsPDF();
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
Messaging.EmailFileAttachment efa1 = new Messaging.EmailFileAttachment();
efa1.setFileName(attachmentfilename);
efa1.setBody(b);
String addresses;
email.setSubject( Subject );
email.setToAddresses(EmailIds);
email.setPlainTextBody(Body);
email.setFileAttachments(new Messaging.EmailFileAttachment[] {efa1});
Messaging.SendEmailResult [] r = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {email});
}
}
The solution/workaround I can think now is,
1) Refactor/Overload your public static void sendVF method to have two versions, one using future and on with out future.
2) Add a new flag to your QNews__c object
3) On the Trigger call SendVFAsAttachment.sendVF only if the flag is false
4) On the batch code make the new flag as True while inserting into QNews
5) On the batch, call the method wich is defined with out future
the Future methods cannot be called from Batch processes, you could check in the trigger if insert/update is triggered by Future, Batch or not
for(QNews__c qn: Trigger.new){
if (System.isFuture() || System.isBatch())
sendVF(............);
else
sendVFNoFuture(............);
}
@future(callout=true)
public static void sendVF(String EmailIdCSV, String Subject,String body,String userSessionId, ID articleid,String ArticleTitle,String attachmentfilename,String ArticleType)
{
sendVFNoFuture(............);
}
public void sendVFNoFuture(.............)
{
string newID = String.valueOf(articleid);
String addr = 'https://vsp--kmbuild.cs10.my.salesforce.com/services/apexrest/sendPDFEmail';
HttpRequest req = new HttpRequest();
req.setEndpoint( addr );
req.setMethod('POST');
req.setHeader('Authorization', 'OAuth ' + userSessionId);
req.setHeader('Content-Type','application/json');
Map<String,String> postBody = new Map<String,String>();
postBody.put('EmailIdCSV',EmailIdCSV);
postBody.put('Subject',Subject);
postBody.put('body',body);
postBody.put('newID',newID);
postBody.put('attachmentfilename',attachmentfilename);
postBody.put('ArticleTitle',ArticleTitle);
postBody.put('ArticleType',ArticleType);
String reqBody = JSON.serialize(postBody);
req.setBody(reqBody);
Http http = new Http();
HttpResponse response = http.send(req);
}
it is good point, we can achive using System.isFuture() || System.isBatch() with out using a flag.
but Since Trigger does not allow call outs the sendVFNoFuture method should be called from Batch Process and not from the Trigger.
The Trigger should have
if (!System.isFuture() && !System.isBatch())
sendVF(............);
}
My Trigger:
trigger Send_PDF_to_SharePointhelp on QNews__c (after insert, after update) {
for(QNews__c qn: Trigger.new){
string filename = qn.Article_Title__c+'_'+qn.Article_Version_Number__c+'.pdf';
String emailbody='This attachment is the PDF copy of Article: '+qn.Article_Title__c+' with version number '+qn.Article_Version_Number__c;
if (!System.isFuture() && !System.isBatch()){
SendVFAsAttachment.sendVF('vijaku@xxx.com',qn.Data_Category__c,emailbody,UserInfo.getSessionId(),qn.Article_ID__c,qn.Article_Title__c,filename,qn.Article_Type__c);
}
}
}
I dont see other limitations except that when you write any other batch process or future method you need to remember to call the sendVF no future version and it is little more maintanence.