5

I have a date field (in my below code it has api name Payment_Follow_Up_Date__c) on Opportunity. With the use of Schedulable and batachable interface I am trying to send sms to client as a reminder. I wrote simple code. When I debug the scope it is not having the record in which I have updated the above said field. Only if system.today() = paymentReminder and StageName != 'Closed won' a message will go. It seems I am missing something as when I debug the date field it is coming to NULL in each iteration. It means the record which is fulfilling the above condition is not coming in scope variable. Here is my code which is required in this context, if any body require complete code let me know.

  1. Schedule Class

    global class OpportunityPaymentReminder implements Schedulable {
    
    global void execute(SchedulableContext ctx){
    
    try{
    
        CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime
                                 FROM CronTrigger 
                                 WHERE 
                                 Id = :ctx.getTriggerId()];
    
        system.debug('--cron expression--'+ct.CronExpression);
        system.debug('-cron trigger run--'+ ct.TimesTriggered);
        Date todayDate = system.today();
    
        list<opportunity> test = [SELECT id,Name,StageName,Enquiry_For__c,contact__c,Contact_Name__c,ContactPhone__c,Payment_Follow_Up_Date__c,Owner_Email__c
                                      From Opportunity Where StageName != 'Closed Won' AND Payment_Follow_Up_Date__c =: todayDate   ];
    
        system.debug('-test-'+ test);                              
    
        OpportunityPaymentReminderSMS oprs = new OpportunityPaymentReminderSMS();
    
        system.debug('--todayDate--' + todayDate);
        oprs.query = 'SELECT id,Name,StageName,Enquiry_For__c,contact__c,Contact_Name__c,ContactPhone__c,owner_mobile__c,'+
                     'Owner_Name__c,Client_Interest__c,Payment_Follow_Up_Date__c,Owner_Email__c '+
                     'From Opportunity Where StageName != \'Closed Won\' AND Payment_Follow_Up_Date__c =: todayDate';
        integer scopeSize = 100;
    
        Database.executeBatch(oprs, scopeSize);
        //cleaning(); // to clean deleted or aborted schedule jobs.
    
    }catch(exception ex){
        system.debug('--Exception is--'+ ex.getMessage());
    }
    

    }

}

  1. Batch Class

    global class OpportunityPaymentReminderSMS implements Database.Batchable<sObject>, Database.AllowsCallOuts, Database.Stateful {
    
    //some variables are defined here for later use
    
    global OpportunityPaymentReminderSMS(){
    
    }
    
    global Database.Querylocator start(Database.BatchableContext BC){
    
        system.debug('--payment follow-up query is' + query);
    
        return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext BC, list<sObject> scope){
    try{
        system.debug('-scope-'+ scope + '-BC-'+ BC);
        dt = system.today();
        todayDate = date.newInstance(dt.year(), dt.month(), dt.day());
    
        for(sObject s : scope){
            Opportunity scheduled = (Opportunity)s;
    
            system.debug( '-Payment FollowUp Date-'+ scheduled.Payment_Follow_Up_Date__c+'-smsTemplate-'+ smsTemplate );
            //in the above debug Payment_Follow_up_date__c value is null , so below if condition is not satisfied.
    
            if(scheduled.StageName != 'Closed Won'
                && scheduled.Payment_Follow_Up_Date__c == todayDate
                && scheduled.Payment_Follow_Up_Date__c != null){
                system.debug('-Payment follow up date--'+ scheduled.Payment_Follow_Up_Date__c);
    
                smsTemplate = 'Dear '+ opportunityContact +','
    

Kindly help.

8

The schedule's class 'todayDate' variable isn't visible in the batch class' scope, so when you pass a string query to the other class, it doesn't have anything to bind to when it eventually gets to executing the query. Therefore, when the system tries to perform the query in the start method, it doesn't find the variable todayDate and assigns a value of null to it.

In general, it's a bad idea to have one class give another class queries; the batchable class should be responsible for generating the query, which generally avoids scoping issues and makes it more obvious as to what data is being queried. The exception to this rule are those batchable classes that are polymorphic, such as a generic data processing class (e.g. a data loader, etc).

At minimum, you should assign the date value to the batchable class directly. Ideally, you shouldn't construct a query outside the class, because this means that the batchable class becomes dependent upon another outside class.

On the other hand, you can implement batchable and schedulable in the same class, which avoids the issue entirely:

public class X implements Database.Batchable<SObject>, Schedulable {
    public void execute(SchedulableContext sc) {
        // Do initial setup
        Database.executeBatch(this, scopeSize);
    }

    public Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator(query);
    }

    public void execute(Database.BatchableContext bc) {
        // process
    }

    public void finish(Database.BatchableContext bc) {

    }
}

Using this design, you can make todayDate a class variable and share its scope across the schedulable and batchable states.

1
  • Thanks. I will surely gonna test and implement your suggestions.
    – Rohit
    May 31 '14 at 5:02

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.