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
Shruthi GM 4Shruthi GM 4 

How to write schedulable apex class to send email on daily basis?

Please help.
I have a requirement to send email on daily basis to a set of people.How do I perform this action?

Please help.
Thanks inadvance.
Best Answer chosen by Shruthi GM 4
Amit Chaudhary 8Amit Chaudhary 8
Please check below post for batch job and scheduler class.
1) http://amitsalesforce.blogspot.in/2016/02/batch-apex-in-salesforce-test-class-for.html
2) https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_scheduler.htm

Apex Scheduler Notes and Best Practices
  1. Salesforce schedules the class for execution at the specified time. Actual execution may be delayed based on service availability.
  2. Use extreme care if you’re planning to schedule a class from a trigger. You must be able to guarantee that the trigger won’t add more scheduled classes than the limit. In particular, consider API bulk updates, import wizards, mass record changes through the user interface, and all cases where more than one record can be updated at a time.
  3. Though it's possible to do additional processing in the execute method, we recommend that all processing take place in a separate class.
  4. Synchronous Web service callouts are not supported from scheduled Apex. To be able to make callouts, make an asynchronous callout by placing the callout in a method annotated with @future(callout=true) and call this method from scheduled Apex. However, if your scheduled Apex executes a batch job, callouts are supported from the batch class. See Using Batch Apex.
  5. Apex jobs scheduled to run during a Salesforce service maintenance downtime will be scheduled to run after the service comes back up, when system resources become available. If a scheduled Apex job was running when downtime occurred, the job is rolled back and scheduled again after the service comes back up. Note that after major service upgrades, there might be longer delays than usual for starting scheduled Apex jobs because of system usage spikes.


We use Apex Scheduler to schedule a controller to execute it at a given time in future. For this make an Apex Scheduler Controller and to schedule this controller go to...
 
Administration Setup->Monitoring->Scheduled Jobs from there we select that Controller class and then provide some time and date to execute it in future.


Below is a sample code of the Apex Scheduler Controller to send an email :
global class ApexScheduledClass Implements Schedulable
            {
                        global void execute(SchedulableContext sc)
                        {
                                    sendmail();
                        }
 
                        public void sendmail()
                        {// Please add your logic according to requirement
                                    Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
                                    string [] toaddress= New string[]{'demo@gmail.com'};
                                    email.setSubject('Testing Apex Scheduler-Subject');
                                    email.setPlainTextBody('Testing Apex Scheduler-Body');
                                    email.setToAddresses(toaddress);
                                    Messaging.sendEmail(New Messaging.SingleEmailMessage[]{email});
                        }
            }

Let us know if this will help you

Thanks
Amit Chaudhary

 

All Answers

Mahesh DMahesh D
Hi Shruthi,

Please check the below code:
 
global class SendNotificationBatch implements Database.Batchable<sObject>, Schedulable, Database.Stateful {

    //Variable Section
    global FINAL String strQuery;
    global List<String> errorMessages = new List<String>();
    
    global SendNotificationBatch() { 
        this.strQuery = getBatchQuery();
    }
    
    //Returns the Query String to Batch constructor to fetch right records.
    private String getBatchQuery() {
        String strQuery = 'SELECT Id, CloseDate, Owner.Email FROM Opportunity WHERE CloseDate < TODAY'; 
        return strQuery;
    }
    
    //Batch Start method
    global Database.QueryLocator start(Database.BatchableContext BC) {
        return Database.getQueryLocator(strQuery);
    }

    //Batch Execute method calls findCostForWoD method
    global void execute(Database.BatchableContext BC, List<sObject> scopeList) {
        System.debug(LoggingLevel.INFO, '== scopeList size ==' + scopeList.size());
        
        List<Opportunity> oppList = (List<Opportunity>) scopeList;
        if(!oppList.isEmpty()) { 
            List<Messaging.SingleEmailMessage> mailList = new List<Messaging.SingleEmailMessage>();
            for (Opportunity prod : oppList)
            {               
                
                Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage(); 
                String[] toAddresses = new String[] {prod.Owner.Email};
                Message.setToAddresses(toAddresses); 
                Message.SaveAsActivity = false;
                mailList.add(Message);
                
            }
            if(!mailList.isEmpty()) {
                try{
                    Messaging.sendEmail(mailList);
                }
                catch (Exception ex) {
                    errorMessages.add('Unable to send email to Tech: '+ ex.getStackTraceString());
                }
            }
        }
    }  

    //Batch Finish method for after execution of batch work
    global void finish(Database.BatchableContext BC) { 
        AsyncApexJob aaj = [Select Id, Status, NumberOfErrors, JobItemsProcessed, MethodName, TotalJobItems, CreatedBy.Email from AsyncApexJob where Id =:BC.getJobId()];
        
        // Send an email to the Apex job's submitter notifying of job completion.
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddresses = new String[] {aaj.CreatedBy.Email};
        mail.setToAddresses(toAddresses);
        mail.setSubject('JOB Salesforce Send Notification Batch: ' + aaj.Status);
        String bodyText='Total Job Items ' + aaj.TotalJobItems + ' Number of records processed ' + aaj.JobItemsProcessed + ' with '+ aaj.NumberOfErrors + ' failures.\n';
        bodyText += 'Number of Error Messages ' + errorMessages.size() + '\n';
        bodyText += 'Error Message' + String.join(errorMessages, '\n');
        mail.setPlainTextBody(bodyText);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
    
    //Method which schedules the ProductDownloadBatch
    global void execute(SchedulableContext sc) {        
        SendNotificationBatch snInstance = new SendNotificationBatch();
        ID batchprocessid = Database.executeBatch(snInstance);
    }
}

Batch Apex
A Batch class allows you to define a single job that can be broken up into manageable chunks that will be processed separately.

When to use Batch Apex
One example is if you need to make a field update to every Account in your organization. If you have 10,001 Account records in your org, this is impossible without some way of breaking it up. So in the start() method, you define the query you're going to use in this batch context: 'select Id from Account'. Then the execute() method runs, but only receives a relatively short list of records (default 200). Within the execute(), everything runs in its own transactional context, which means almost all of the governor limits only apply to that block. Thus each time execute() is run, you are allowed 150 queries and 50,000 DML rows and so on. When that execute() is complete, a new one is instantiated with the next group of 200 Accounts, with a brand new set of governor limits. Finally the finish() method wraps up any loose ends as necessary, like sending a status email.

Sample Batch Apex
1) Start Method is automatically called at the beginning of the apex job. This method will collect record or objects on which the operation should be performed. These record are divided into subtasks & passes those to execute method.

2) Execute Method performs operation which we want to perform on the records fetched from start method.

3) Finish Method executes after all batches are processed. Use this method to send confirmation email notifications.

When to use Batch Apex
One example is if you need to make a field update to every Account in your organization. If you have 10,001 Account records in your org, this is impossible without some way of breaking it up. So in the start() method, you define the query you're going to use in this batch context: 'select Id from Account'. Then the execute() method runs, but only receives a relatively short list of records (default 200). Within the execute(), everything runs in its own transactional context, which means almost all of the governor limits only apply to that block. Thus each time execute() is run, you are allowed 150 queries and 50,000 DML rows and so on. When that execute() is complete, a new one is instantiated with the next group of 200 Accounts, with a brand new set of governor limits. Finally the finish() method wraps up any loose ends as necessary, like sending a status email.

Limits in Batch Apex
1) Up to five queued or active batch jobs are allowed for Apex
2) Cursor limits for different Force.com features are tracked separately. For example, you can have 50 Apex query cursors, 50 batch cursors, and 50 Visualforce cursors open at the same time.
3) A maximum of 50 million records can be returned in the Database.QueryLocator object. If more than 50 million records are returned, the batch job is immediately terminated and marked as Failed
4) If the start method returns a QueryLocator, the optional scope parameter of Database.executeBatch can have a maximum value of 2,000. If set to a higher value, Salesforce chunks the records returned by the QueryLocator into smaller batches of up to 2,000 records. If the start method returns an iterable, the scope parameter value has no upper limit; however, if you use a very high number, you may run into other limits.
5) If no size is specified with the optional scope parameter of Database.executeBatch, Salesforce chunks the records returned by the start method into batches of 200, and then passes each batch to the execute method.Apex governor limits are reset for each execution of execute.
6) The start, execute, and finish methods can implement up to 10 callouts each
7) The maximum number of batch executions is 250,000 per 24 hours
8) Only one batch Apex job's start method can run at a time in an organization. Batch jobs that haven’t started yet remain in the queue until they're started. Note that this limit doesn’t cause any batch job to fail and execute methods of batch Apex jobs still run in parallel if more than one job is running

Need of Batch Apex: - As you all might know about the salesforce governor limits on its data. When you want to fetch thousands of records or fire DML on thousands of rows on objects it is very complex in salesforce and it does not allow you to operate on more than certain number of records which satisfies the Governor limits.
But for medium to large enterprises, it is essential to manage thousands of records every day. Adding/editing/deleting them when needed.
Salesforce has come up with a powerful concept called Batch Apex. Batch Apex allows you to handle more number of records and manipulate them by using a specific syntax.

1) When you want to process large number of records on daily basis or even on specific time of interval then you could go for Batch Apex
2) Also, when you want an operation to be asynchronous then you could implement the Batch Apex. Batch Apex is exposed as an interface that must be implemented by the developer. Batch jobs can be programmatically invoked at runtime using Apex. Batch Apex operates over small batches of records, covering your entire record set and breaking the processing down to manageable chunks of data

Please go through the below links:

https://developer.salesforce.com/forums/ForumsMain?id=906F0000000DBtv


Please do let me know if it helps you.

Regards,
Mahesh
Shruthi GM 4Shruthi GM 4
Hi Mahesh,
How to check whether this code works or not?
Amit Chaudhary 8Amit Chaudhary 8
Please check below post for batch job and scheduler class.
1) http://amitsalesforce.blogspot.in/2016/02/batch-apex-in-salesforce-test-class-for.html
2) https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_scheduler.htm

Apex Scheduler Notes and Best Practices
  1. Salesforce schedules the class for execution at the specified time. Actual execution may be delayed based on service availability.
  2. Use extreme care if you’re planning to schedule a class from a trigger. You must be able to guarantee that the trigger won’t add more scheduled classes than the limit. In particular, consider API bulk updates, import wizards, mass record changes through the user interface, and all cases where more than one record can be updated at a time.
  3. Though it's possible to do additional processing in the execute method, we recommend that all processing take place in a separate class.
  4. Synchronous Web service callouts are not supported from scheduled Apex. To be able to make callouts, make an asynchronous callout by placing the callout in a method annotated with @future(callout=true) and call this method from scheduled Apex. However, if your scheduled Apex executes a batch job, callouts are supported from the batch class. See Using Batch Apex.
  5. Apex jobs scheduled to run during a Salesforce service maintenance downtime will be scheduled to run after the service comes back up, when system resources become available. If a scheduled Apex job was running when downtime occurred, the job is rolled back and scheduled again after the service comes back up. Note that after major service upgrades, there might be longer delays than usual for starting scheduled Apex jobs because of system usage spikes.


We use Apex Scheduler to schedule a controller to execute it at a given time in future. For this make an Apex Scheduler Controller and to schedule this controller go to...
 
Administration Setup->Monitoring->Scheduled Jobs from there we select that Controller class and then provide some time and date to execute it in future.


Below is a sample code of the Apex Scheduler Controller to send an email :
global class ApexScheduledClass Implements Schedulable
            {
                        global void execute(SchedulableContext sc)
                        {
                                    sendmail();
                        }
 
                        public void sendmail()
                        {// Please add your logic according to requirement
                                    Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
                                    string [] toaddress= New string[]{'demo@gmail.com'};
                                    email.setSubject('Testing Apex Scheduler-Subject');
                                    email.setPlainTextBody('Testing Apex Scheduler-Body');
                                    email.setToAddresses(toaddress);
                                    Messaging.sendEmail(New Messaging.SingleEmailMessage[]{email});
                        }
            }

Let us know if this will help you

Thanks
Amit Chaudhary

 
This was selected as the best answer
Amit Chaudhary 8Amit Chaudhary 8
Please check below post how to schedule scheduler class in salesforce with screen shot
http://amitsalesforce.blogspot.in/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.
2) By Developer console

System Scheduler.

Step 1) Click on Setup->Apex class. Then search Schedule Apex button.
Step 2) Select the scheduler class and set Time like below screen shot.
By Developer console 

Execute below code from developer console :-

ApexScheduledClass  m = new ApexScheduledClass ();
String sch = '20 30 8 10 2 ?';
String jobID = system.schedule('Merge Job', sch, m);

Let us know if this will help you

Thanks
Amit Chaudhary
Mahesh DMahesh D
Hi Shruthi,

I would like to inform you that, you need to change the above query like on what basis you want to send an email. Once you modify the query, you can open the developer console and execure the below 2 lines of code for testing.
 
SendNotificationBatch snInstance = new SendNotificationBatch();
ID batchprocessid = Database.executeBatch(snInstance);

Once the testing is done, you can schedule it using the Salesforce scheduler.

Please do let me know if it helps you.

Regards,
Mahesh
Shruthi GM 4Shruthi GM 4
Hi Amit,
I used ur simple code to send email.
Since it was urgent to test I used developer console to run that code.
There I am getting as success but actually am not receiving any mail.
Please help.
Amit Chaudhary 8Amit Chaudhary 8
Hi Shruthi,

I added hardcoded email id. Please add your email id for test in below line

string [] toaddress= New string[]{'demo@gmail.com'};
 
Shruthi GM 4Shruthi GM 4
I have added my mail id only.
Amit Chaudhary 8Amit Chaudhary 8
You Can track the status of your schedule job with below step
Setup-->Monitoring--->Scheduled Jobs

I tesed above code and also got email
User-added image

Try below code for testing
Sample 1 :-
ApexScheduledClass  m = new ApexScheduledClass ();
String sch = '0 0 * * * ?';
String jobID = system.schedule('Merge Job1', sch, m);

Sample 2:-
ApexScheduledClass  m = new ApexScheduledClass ();

String seconds = '0'; //Execute at Zero Seconds
String minutes = '10'; //Execute at every 10th minute of hour
String hours = '*'; // Execute Every Hour
String dayOfMonth = '*'; // Execute Every Day of the Month
String month = '11'; //Execute only in November(11)
String dayOfWeek = '?'; //Execute on all 7 days of the Week
String year = '2016'; //Execute only for year 2009

//Seconds Minutes Hours Day_of_month Month Day_of_week optional_year
String sch = seconds + ' ' + minutes + ' ' + hours + ' ' + dayOfMonth + ' ' + month + ' ' + dayOfWeek + ' ' + year;
//String sch = '0 10 * * 11 ? 2016';

system.schedule('Email Job1', sch, m);



 
Shruthi GM 4Shruthi GM 4
Thanks Amit.It is working fine now.
I received the mail.
Shruthi GM 4Shruthi GM 4
Hi amit,
One more help!


global class ApexScheduledClass Implements Schedulable
02            {
03                        global void execute(SchedulableContext sc)
04                        {
05                                    sendmail();
06                        }
07  
08                        public void sendmail()
09                        {// Please add your logic according to requirement
10                                    Messaging.SingleEmailMessage email = newMessaging.SingleEmailMessage();
11                                    string [] toaddress= New string[]{'demo@gmail.com'};
12                                    email.setSubject('Testing Apex Scheduler-Subject');
13                                    email.setPlainTextBody('Testing Apex Scheduler-Body');
14                                    email.setToAddresses(toaddress);
15                                    Messaging.sendEmail(New Messaging.SingleEmailMessage[]{email});
16                        }
17            }

What if I want to send one more extra email only on wednesdays along with the existing one how would I implement that inside this class.I have created two Email template.But only on wednesdays I will have to call the second email template.How would I implement that inside this class?
Any idea?
Amit Chaudhary 8Amit Chaudhary 8
That will better if you can create one more Scheduler class and schedule that class from UI
1) http://amitsalesforce.blogspot.in/2016/02/batch-apex-in-salesforce-test-class-for.html

User-added image

System Scheduler.

Step 1) Click on Setup->Apex class. Then search Schedule Apex button.
Step 2) Select the scheduler class and set Time like below screen shot.
 
Rahul Test 9Rahul Test 9
Hi,
Can someone help me out with the Batch Apex Class and it's test class to generate a report and send through an email in the form of a link
phanidevphanidev
@Rahul 
Were you able to figure it out I am currently looking for the same functionality.Any help is appreciated. Thanks
Satya Ranjan MohantySatya Ranjan Mohanty
Hi,
I have a schdulable batch class whih sends mail to the users here problem is as i have scduled the job its taking my email id in To Addresse can any help me how to avoid my mail id from to addresss 

thanks
joshua peter 10joshua peter 10
Thanks for asking this question. this information really helped my mlm software  (https://cloudmlmsoftware.com/)
mlm software demo (https://cloudmlmsoftware.com/free-demo)  
Hari SignHari Sign
Do we have other options without coding? - Neo software (https://neomlmsoftware.com/)
Robert Filip 5Robert Filip 5
Really helped well for my MLM software  (https://www.hybridmlm.io)
Dhayananth Sridhar 2Dhayananth Sridhar 2
@amit Chaudhary 8 : In my org there is a "Scheduled Apex Job" which is running daily. For this particular job there is an email trigger where the admin use to get the email.I just want to add some more email in the the existing E- mail trigger. How can I add the emails?