1

I have written a batch class for sending SMSs using SMS Magic. Everything seems fine but it is failing while insertion. Please help. Below is the batch class.

Global class BatchSmsAfterSevenDaysOFDisburmentEFS implements Database.Batchable<sObject>
{
    Public date varDate = date.today();
    Public string str = 'Disbursed';
    list<smagicinteract__smsMagic__c> lstSmsMagic = new list<smagicinteract__smsMagic__c>();
    smagicinteract__smsMagic__c objSmsMagic = new smagicinteract__smsMagic__c();
    string Query = 'Select Id, Client_Name_EFS__c, Actual_Disbursement_date__c,(Select id, Account.phone, Account.Name from opportunities__r) from Application__c where Application_Status__c =\'' + str + '\'';
    global Database.QueryLocator start(Database.batchableContext bc)
    {
        system.debug('inside start');
        return database.getQueryLocator(Query);
    }

    global void execute(Database.batchableContext bc, list<Application__c> scope)
    {    
        system.debug('inside Execute start' + scope);
        for(Application__c ObjApplication : scope)
        {
            system.debug('@@@@@@@@@@@ inside application for loop');
            //objSmsMagic.smagicinteract__Name__c = ObjApplication.Client_Name_EFS__c;
            if(ObjApplication.Actual_Disbursement_date__c.daysbetween(varDate) == 7)
            {
                system.debug('@@@@@@@@ date checking');
                for(opportunity objOpp : ObjApplication.opportunities__r )
                {
                    system.debug('@@@@@@@@ inside opportunity for loop');
                      If(objOpp.Account.Phone.length() == 10 || objOpp.Account.Phone.isNumeric())
                      {
                        System.Debug('^^^^^Inside IFF^^^^^ ');
                        objSmsMagic.smagicinteract__Name__c = objOpp.Account.Name;
                        objSmsMagic.smagicinteract__PhoneNumber__c=objOpp.Account.Phone;
                        objSmsMagic.smagicinteract__SenderId__c='Transactional';
                        objSmsMagic.smagicinteract__external_field__c = smagicinteract.ApexAPI.generateUniqueKey();
                        objSmsMagic.smagicinteract__SMSText__c= LoanMessageBasedOnApplicationStatus.AfterSevenDaysofDisbursmentMessage;
                        lstSmsMagic.add(objSmsMagic);
                        system.debug('>>> >>> Inside for Second loop ' + lstSmsMagic);
                      }     
                }  
            }    
        }
        try
        {    
            if(lstSmsMagic.size()> 0)
            {
                system.debug('@@@@@@@@ before sending' + lstSmsMagic.size() + ' and  ' + lstSmsMagic);
                Database.insert(lstSmsMagic, false);
                system.debug('@@@@@@@@ after sending');
            }
        }
        catch(DmlException e)
        {
             system.debug('********++++++++++*******'+e.getMessage());            
        }
    }


    global void finish(Database.batchableContext bc)
    {
    }
}
5
  • What is the error you are getting?
    – javanoob
    Dec 17, 2016 at 15:22
  • Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, smagicinteract.SendSMSTrigger: execution of AfterInsert caused by: System.AsyncException: Future method cannot be called from a future or batch method: smagicinteract.SMSUtility.callSMSAPI(String) Class.smagicinteract.SMSUtility.sendSMS: line 438, column 1 Trigger.smagicinteract.SendSMSTrigger: line 2, column 1: []
    – R Suri
    Dec 18, 2016 at 19:12
  • Problem is not with the batch. Check other posts on this forum for the error: "Future method cannot be called from a future or batch method". You should use System.isBatch() or System.isFuture() methods to resolve this error. From the error you posted, it seems like your trigger is trying to make a API call. Because this is running in batch context, you can make a synchronous callout instead of using Future method. That should resolve this error.
    – javanoob
    Dec 18, 2016 at 19:31
  • I am not using any trigger. I am trying to send SMS straightaway from batch class and later on i will schedule this batch. Please suggest.
    – R Suri
    Dec 19, 2016 at 6:45
  • When you insert smsmagic record from batch class, there is a trigger fired as per the exception and that trigger name is : smagicinteract
    – javanoob
    Dec 19, 2016 at 12:03

3 Answers 3

0

It would seem that you need to declare:

list<smagicinteract__smsMagic__c> lstSmsMagic = new list<smagicinteract__smsMagic__c>();

and

smagicinteract__smsMagic__c objSmsMagic = new smagicinteract__smsMagic__c();

from within your Execute Method. You've not named them Public and they need to be declared from within each batch that runs if you're to insert them separately.

0

While you can declare class fields - such as your Public date varDate - the values of those fields are not preserved across the multiple asynchronous method calls by default (so typically causing null pointer exceptions). Making the class implement the marker interface Database.Stateful changes that, but most batchables are better created using just local variables in the execute method because that makes the code simpler and clearer and less likely to have incorrectly initialised variables.

For your example, the class would look something like this:

public class BatchSmsAfterSevenDaysOFDisburmentEFS
        implements Database.Batchable<sObject>, Database.AllowsCallouts
{
    public Database.QueryLocator start(Database.batchableContext bc)
    {
        string str = 'Disbursed';
        return database.getQueryLocator([
                select Id, Client_Name_EFS__c, Actual_Disbursement_date__c,
                        (
                             select id, Account.phone, Account.Name
                             from opportunities__r
                             where Account.Phone != null
                        )
                from Application__c
                where Application_Status__c = :str
                and Actual_Disbursement_date__c != null
                ]);
    }

    public void execute(Database.batchableContext bc, list<Application__c> scope)
    {
        date varDate = date.today()
        list<smagicinteract__smsMagic__c> ss = new list<smagicinteract__smsMagic__c>();
        for(Application__c a : scope)
        {
            if(a.Actual_Disbursement_date__c.daysbetween(varDate) == 7)
            {
                for(opportunity o : a.opportunities__r )
                {
                   if(o.Account.Phone.length() == 10 || o.Account.Phone.isNumeric()))
                   {
                       smagicinteract__smsMagic__c s = new smagicinteract__smsMagic__c(
                               smagicinteract__Name__c = o.Account.Name;
                               smagicinteract__PhoneNumber__c = o.Account.Phone;
                               smagicinteract__SenderId__c = 'Transactional';
                               smagicinteract__external_field__c = smagicinteract.ApexAPI.generateUniqueKey();
                               smagicinteract__SMSText__c= LoanMessageBasedOnApplicationStatus.AfterSevenDaysofDisbursmentMessage;
                               );
                        ss.add(s);
                    }     
                }  
            }    
        }
        insert ss;
    }


    public void finish(Database.batchableContext bc)
    {
    }
}

Note the extra where terms in the query to avoid the risk of null pointer exceptions in the execute method. The query is usually better done using static SOQL so the query is checked at compile time and less prone to SOQL injection risks.

PS

From the error you have posted in the comment it looks like e.g. a trigger is calling a method marked with @future which won't work from a batchable. I've added the marker interface Database.AllowsCallouts so that this batchable code could call (directly or indirectly) a synchronous method that does a callout: check if the smagicinteract package has such a method and call that instead.

3
  • I am getting the below written error :
    – R Suri
    Dec 18, 2016 at 19:10
  • Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, smagicinteract.SendSMSTrigger: execution of AfterInsert caused by: System.AsyncException: Future method cannot be called from a future or batch method: smagicinteract.SMSUtility.callSMSAPI(String) Class.smagicinteract.SMSUtility.sendSMS: line 438, column 1 Trigger.smagicinteract.SendSMSTrigger: line 2, column 1: []
    – R Suri
    Dec 18, 2016 at 19:12
  • @RamanSuri See the PS I added.
    – Keith C
    Dec 19, 2016 at 9:34
0

Thankyou all for help.

The above implementation calls a future method in the SMS Magic package and if you are calling this from batch class then you will definitely go through the error which i have faced.

So the solution of this is you have to use pushSMSCallout API from SMS Magic which is not a future method. But first make sure that your package has the feature to use pushSMSCallout API.

You can take a reference from here http://www.screen-magic.com/pushsmscallout/.

I hope it will save the time of many :)

Thanks Raman Suri

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.