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
Lena ChristyLena Christy 

Schedule an Apex Class

Hi everyone,
I tried to create a class to get executed once a year:
global class CalculOnAccount implements Database.Batchable<sObject>{
    global Database.QueryLocator start(Database.BatchableContext BC) {
       String query = 'SELECT field1__c,field2__c, cal__c FROM Account';
       return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext BC, List<Account> scope) {
         for(Account a : scope)
         {
             a.cal__c= ((a.field1__c- a.field2__c)*100 )/(a.field2__c);        
         }
         insert scope;
    }  
     
    global void finish(Database.BatchableContext BC) {
    }
}


My questions are:
How to schedule this to get executed in december each year for example? And what I want also is, depending on the result of cal__c, another field in Account will get updated, where I should put this treatment part? in the finish() ?
Thanks in advance
Best Answer chosen by Lena Christy
Amit Chaudhary 8Amit Chaudhary 8
Please check below post for batch job and scheduler i hope that will help you
1) http://amitsalesforce.blogspot.com/2016/02/batch-apex-in-salesforce-test-class-for.html

How to Schedule scheduler class
There are two option we have schedule the scheduler classes.
1) By System Scheduler :-

Step 1)
Click on Setup->Apex class. Then search Schedule Apex button.
Step 2) Select the scheduler class and set Time 

Scheduler class for you
global class AccountUpdateBatchJobscheduled implements Schedulable 
{
    global void execute(SchedulableContext sc) 
    {
        CalculOnAccount  b = new CalculOnAccount (); 
        database.executebatch(b);
    }
}
2) By Developer console

Execute below code from developer console :-
CalculOnAccount m = new CalculOnAccount();
String sch = '20 30 8 10 2 ?'; // we need change this only for Year End
String jobID = system.schedule('Account Job', sch, m);

Please let us know if this will help you

Thanks,
Amit Chaudhary

All Answers

John Pipkin 14John Pipkin 14
Lena, 

In order for a class to be able to be scheduled, you must implement a Schedulable interface. Like:
global class CalculOnAccount implements Schedulable, Database.Batchable<sObject>

At that point, you can schedule it via Setup by going to Develop > Apex Classes > Schedule Apex Class (button)

For the the result of cal__c, it would be best to store the result outside of the field and process in the same transaction. For example:
 
Decimal calResult = ((a.field1__c- a.field2__c)*100 )/(a.field2__c);
a.cal__c= calResult;
if(calResult > 0)
//etc etc

The finish method is only called 1 time after all the batch "executes" are completed so you would not be able to perform record-level logic there. 

Hope that helps
 
John Pipkin 14John Pipkin 14
Also, see https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_scheduler.htm for more information on implementing a schedulable interface
Lena ChristyLena Christy

Thank you so much for your reply John ! I tried what you said, here is my code updated:

global class CalculOnAccount implements Schedulable,Database.Batchable<sObject>{
    global Database.QueryLocator start(Database.BatchableContext BC) {
       String query = 'SELECT field1__c,field2__c, cal__c FROM Account';
       return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext BC, List<Account> scope) {
         for(Account a : scope)
         {          
          Decimal calResult  = ((a.field1__c- a.field2__c)*100 )/(a.field2__c);      
          a.cal_c=  calResult  ;
         }
         insert scope;
    }  
     
    global void finish(Database.BatchableContext BC) {
    }
global void execute(SchedulableContext sc){
    CalculOnAccount calcul = new CalculOnAccount ();
    Database.executeBatch(calcul);
}


I sheduled it but nothing happend, I didn't find a value in cal__c and I find this error
First error: Attempt to de-reference a null object

Any more help please I would be thankful..

John Pipkin 14John Pipkin 14
That is probably because Field1__c or Field2__c value is null. You would want to add a check to only evaluate if those 2 fields have a value in them. 

 
Amit Chaudhary 8Amit Chaudhary 8
Please check below post for batch job and scheduler i hope that will help you
1) http://amitsalesforce.blogspot.com/2016/02/batch-apex-in-salesforce-test-class-for.html

How to Schedule scheduler class
There are two option we have schedule the scheduler classes.
1) By System Scheduler :-

Step 1)
Click on Setup->Apex class. Then search Schedule Apex button.
Step 2) Select the scheduler class and set Time 

Scheduler class for you
global class AccountUpdateBatchJobscheduled implements Schedulable 
{
    global void execute(SchedulableContext sc) 
    {
        CalculOnAccount  b = new CalculOnAccount (); 
        database.executebatch(b);
    }
}
2) By Developer console

Execute below code from developer console :-
CalculOnAccount m = new CalculOnAccount();
String sch = '20 30 8 10 2 ?'; // we need change this only for Year End
String jobID = system.schedule('Account Job', sch, m);

Please let us know if this will help you

Thanks,
Amit Chaudhary
This was selected as the best answer
John Pipkin 14John Pipkin 14
Without some more debug statements, it'll be hard to tell exactly what the issue is but here are some high possibilities. 

- Field1__c and/or Field2__c being returned from the query are null
- Field2__c being returned from the query equals 0

Use this code and turn on the debug log:
global class CalculOnAccount implements Schedulable,Database.Batchable<sObject>{
    global Database.QueryLocator start(Database.BatchableContext BC) {
       String query = 'SELECT field1__c,field2__c, cal__c FROM Account';
       return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext BC, List<Account> scope) {
         try{
             for(Account a : scope)
             {     
               System.debug(LoggingLevel.ERROR,'values being executed: ' + a.Field1__c + ',' + a.Field2__c);
               Decimal calResult  = ((a.field1__c- a.field2__c)*100 )/(a.field2__c);      
               a.cal_c=  calResult  ;
             }
         
             insert scope;
         }catch(Exception ex){
             system.debug(LoggingLevel.ERROR,'ERROR: ' + ex.getMessage());
         }
    }  
     
    global void finish(Database.BatchableContext BC) {
    }
global void execute(SchedulableContext sc){
    CalculOnAccount calcul = new CalculOnAccount ();
    Database.executeBatch(calcul);
}

Look at the debug logs and check those debug print statements for any nulls or zero demoninators.
Amit Chaudhary 8Amit Chaudhary 8
Please update your batch job like below
1) add null check.
2) remove insert and use update.
3) Add zero check on field2__c field as well other divide by zero you will get
global class CalculOnAccount implements Database.Batchable<sObject>
{
    global Database.QueryLocator start(Database.BatchableContext BC) 
	{
       String query = 'SELECT field1__c,field2__c, cal__c FROM Account';
       return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext BC, List<Account> scope) 
	{
        for(Account a : scope)
        {
			if(a.field1__c != null && a.field2__c != null && a.field2__c != 0)
			{
				a.cal__c= ((a.field1__c- a.field2__c)*100 )/(a.field2__c);        
			}	
        }
        update scope;
    }  
     
    global void finish(Database.BatchableContext BC) {
    }
}
Let us know if this will help you

Thanks
Amit Chaudhary

 
John Pipkin 14John Pipkin 14
Amit, good catch on the "update" call. I completely overlooked that
Lena ChristyLena Christy

A lot of thanks for your replies, II tried both of your solutions with different fields

For John I tried yours and I put "update" and the Batch Apex is completed with no failures, but I did'nt find the result in the fied.

For your solution Amit Chaudhary, I got this Error: First error: Update failed. First exception on row 0 with id 001580000049cHmAAI; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, ConversionAcc: execution of BeforeUpdate

caused by: System.NullPointerException: Attempt to de-reference a null ...

 

the ConversionAcc is a trigger already set for field1__c . what do you think about this?

Matthew CokeMatthew Coke

What are the values for field1__c and field2__c?


Run a test case for this to confirm - field1__c equal to 5 and field2__c equal to 10 for instance. Then let us know what you get  

Amit Chaudhary 8Amit Chaudhary 8
Hi lena,

Issue not coming in your batch job now issue is coming in ConversionAcc trigger. Please post the code of ConversionAcc trigger so that we can help you or just deactive the ConversionAcc trigger and try above Batch job.
For your solution Amit Chaudhary, I got this Error: First error: Update failed. First exception on row 0 with id 001580000049cHmAAI; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, ConversionAcc: execution of BeforeUpdate

caused by: System.NullPointerException: Attempt to de-reference a null ...
Let us know if this will help you

Thanks
Amit Chaudhary

 
Lena ChristyLena Christy

Hi Amit, sorry I couldn't answer before,
When I tried to desactivate the trigger, the batch job work perfectly, so the issue is from the trigger which is a trigger (before insert, before update)  to the field1__c with some simple calculation from other fields.

Thanks a lot for you help Amit.

Lena ChristyLena Christy
Ok thanks a lot, but the last question please, is their a way to let the trigger active and run the batch job ?
Amit Chaudhary 8Amit Chaudhary 8
You need to fix your Trigger for NULL pointer issue