function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
RudrAbhishekRudrAbhishek 

Issues with CronTrigger in Batch Apex finish method

I have created a Batch Apex in which under the finish method I am trying to launch an email to the corncerned user who is scheduling the job or executing the job / batch from the Developer console, mail is delivering instantly but not getting the START TIME & END TIME of the Scheduled Apex Job rest of the information such as NUMBER OF OPPORTUNITIES CLOSED WON TODAY & TOTAL OPPORTUNITY AMOUNT FOR TODAY:

 

global class OppAmountAgg implements Database.Batchable<sObject>, Database.Stateful{
global double totalAmount = 0;
String str = 'Closed Won';
global double totalSize = 0;
global Database.querylocator start(Database.BatchableContext BC){
  String query = 'SELECT StageName, Ex_Revenue__c, IsAggregated__c, Amount FROM Opportunity WHERE CreatedDate = today AND IsAggregated__c = false AND  StageName=\'' + str + '\'';
    return Database.getQueryLocator(query);
    }

global void execute(Database.BatchableContext BC, List<sObject> scope){
     List<Opportunity> opportunities = new List<Opportunity>();
  for(sObject sobjectIter : scope){
         Opportunity opportunity = (Opportunity)sobjectIter;
         opportunity.IsAggregated__c = true;
         if(opportunity.Amount != null){
          totalAmount = totalAmount+opportunity.Amount;
          opportunities.add(opportunity);
         }
        }
  totalSize = opportunities.size();
  update opportunities;
    }    

global void finish(Database.BatchableContext BC){
//CronTrigger ct = [SELECT Id, StartTime, EndTime, TimesTriggered, NextFireTime FROM CronTrigger WHERE Id =:job.Id];
  String email = 'abc.def@ghi.com';
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(new String[] {email});
//mail.setReplyTo('abc.def@ghi.com');
mail.setSenderDisplayName('Batch Processing');
mail.setSubject('Daily Opportunity Amount Aggregator Job Complete');
system.debug('***CronTrigger.StartTime**' + CronTrigger.StartTime);
system.debug('***CronTrigger.EndTime**' + CronTrigger.EndTime);
mail.setPlainTextBody('Start DateTime: ' + CronTrigger.StartTime +' End DateTime: ' + CronTrigger.EndTime +' Number of Opportunities Closed Won Today: '+totalSize+' Total Opportunity Amount for today: '+totalAmount);

Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}

Could you please let me know that how we can also provide the StartTime and EndTime of the scheduled job to notify the user who's scheduling / executing the batch because as of now it's only sending Mail Body as follows:

"Start DateTime: StartTime End DateTime: EndTime Number of Opportunities Closed Won Today: 2.0 Total Opportunity Amount for today: 5750.0"

DevADSDevADS
Hey Abhishek,

Basically CronTrigger is used to monitor the Scheduled job. To monitor Batch Job use AsyncApexJob
Replace your code with following field, Set the email field & check the status.

Batch Class:
global class OppAmountAgg implements Database.Batchable<sObject>{
    global double totalAmount = 0;
    String str = 'Closed Won';
    global double totalSize = 0;
    Time StartTime;
   
    public OppAmountAgg(){  StartTime = Datetime.now().time();}
   
    global Database.querylocator start(Database.BatchableContext BC){
        String query = 'SELECT StageName, Ex_Revenue__c, IsAggregated__c, Amount FROM Opportunity WHERE CreatedDate = today AND IsAggregated__c = false AND  StageName=\'' + str + '\'';
        return Database.getQueryLocator(query);
    }
   
    global void execute(Database.BatchableContext BC, List<sObject> scope){
        List<Opportunity> opportunities = new List<Opportunity>();
        for(sObject sobjectIter : scope){
            Opportunity opportunity = (Opportunity)sobjectIter;
            opportunity.IsAggregated__c = true;
            if(opportunity.Amount != null){
                totalAmount = totalAmount+opportunity.Amount;
                opportunities.add(opportunity);
            }
        }
        totalSize = opportunities.size();
        update opportunities;
    }   
   
    global void finish(Database.BatchableContext BC){
        Time EndTime;
        AsyncApexJob apexJob = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email
          FROM AsyncApexJob WHERE Id = :BC.getJobId()];
        if(apexJob.status=='Completed')
            EndTime = Datetime.now().time();
        String email = 'shingavi.a@gmail.com';
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        mail.setToAddresses(new String[] {email});
        //mail.setReplyTo('abc.def@ghi.com');
        mail.setSenderDisplayName('Batch Processing');
        mail.setSubject('Daily Opportunity Amount Aggregator Job Complete');
        system.debug('***CronTrigger.StartTime**' + apexJob );
        mail.setPlainTextBody('===StartTime==='+StartTime+'\n'+'===EndTime==='+EndTime+'\n'+' Number of Opportunities Closed Won Today: '+totalSize+'\n'+' Total Opportunity Amount for today: '+totalAmount );
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
}

Happy Coding!! If you have any issues, Let me know!
RudrAbhishekRudrAbhishek

Amit,

I have done some modifications in the code but after making comment on Constructor in which working with StartTime and AsyncApexJob in finish method, it's still working fine and getting StartTime and EndTime correctly in the mail:

global class OppAmountAgg implements Database.Batchable<sObject>, Database.Stateful{

global double totalAmount = 0;
String str = 'Closed Won';
global double totalSize = 0;
Time StartTime;

//public OppAmountAgg(){  StartTime = Datetime.now().time();}

global Database.querylocator start(Database.BatchableContext BC){
  StartTime = Datetime.now().time();
  String query = 'SELECT StageName, Ex_Revenue__c, IsAggregated__c, Amount FROM Opportunity WHERE CreatedDate = today AND IsAggregated__c = false AND  StageName=\'' + str + '\'';
    return Database.getQueryLocator(query);
    }

global void execute(Database.BatchableContext BC, List<sObject> scope){
 
     List<Opportunity> opportunities = new List<Opportunity>();
  for(sObject sobjectIter : scope){
         Opportunity opportunity = (Opportunity)sobjectIter;
         opportunity.IsAggregated__c = true;
         if(opportunity.Amount != null){
          totalAmount = totalAmount+opportunity.Amount;
          opportunities.add(opportunity);
         }
        }
  totalSize = opportunities.size();
  update opportunities;
    }    

global void finish(Database.BatchableContext BC){
  Time EndTime;
    //AsyncApexJob apexJob = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email
    //         FROM AsyncApexJob
    //         WHERE Id = :BC.getJobId()];
    //if(apexJob.status=='Completed')
   
    EndTime = Datetime.now().time();
    String email = 'abc@def.com';
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(new String[] {email});
mail.setSenderDisplayName('Batch Processing');
mail.setSubject('Daily Opportunity Amount Aggregator Job Complete');
system.debug('***CronTrigger.StartTime**' + StartTime );
    mail.setPlainTextBody('===StartTime==='+StartTime+'\n'+'===EndTime==='+EndTime+'\n'+' Number of Opportunities Closed Won Today: '+totalSize+'\n'+' Total Opportunity Amount for today: '+totalAmount );
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
}
}

So, could you please let me know that what exactly we are trying to do in the Constructor even without that it's working and why there is a requirement of AsyncApexJob which is only used to track the progress of the job as previously it was used just for the sake of applying the apexjob status check whether it's completed or not.

Thanks in Advance !!!

BR,

Rudra

 

DevADSDevADS
Just getting the time is not your final destination.
  • Whenever you call the batch, first your Constructor will call, so that is your batch's starting time, put the Start time in Constructor, don't assign it in Start method.
  • The end time scenario should be like whenever batch will complete, you will assign the End Time, In all other cases, You shouldn't get the end time. So that we are checking if the batch is completed successfully then only we are assigning the End time.
Let me know if you still facing any doubts/issues & change your code. Happy Coding!!

Thanks,
Amit