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
Virinchi BairisettyVirinchi Bairisetty 

How can i send an email when an opportunity closed date has passed todays date ??

Hey Guys,

Can any body help me how can i send an email alert to a user(opportunity owner), of all my existing opportunities if the closed date has passed todays date. i tried to write a work flow but it does not work for the exsisting records. how can i over come this and send an emai notification to the user if todays date is passed.

Thanks in advance,
Virinchi.
Best Answer chosen by Virinchi Bairisetty
Mahesh DMahesh D
Hi Virinchi,

There are 2 ways we can achieve this:

(1) Workflow: You can configure Workflow with Time Dependent Email Alert action.

But here you need to retrieve and update all existing records to fire the worklfow for existing records.

(2) Batch Apex:

We have to write a Batch Apex and send an email.

Here is the code for Batch Apex:

 
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);
    }
}

Please do let me know if it helps you.

Regards,
Mahesh

All Answers

Mahesh DMahesh D
Hi Virinchi,

There are 2 ways we can achieve this:

(1) Workflow: You can configure Workflow with Time Dependent Email Alert action.

But here you need to retrieve and update all existing records to fire the worklfow for existing records.

(2) Batch Apex:

We have to write a Batch Apex and send an email.

Here is the code for Batch Apex:

 
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);
    }
}

Please do let me know if it helps you.

Regards,
Mahesh
This was selected as the best answer
Virinchi BairisettyVirinchi Bairisetty
Hi Mahesh,

Thanks for the replay. Here is my scenario if the opportunity has passed the closed date and if the opportunity is still open then the work flow action should be fired. By using the 1st way configuring the Work flow using time based work flow action if i give the rule like this
>> Opportunity:Close date is greater than Today 
>> Opportuity:Open/Close equals Open

Time based Action:  0 hours after the Closed date
Send the email to the User.

What if the opportunity is closed in between these days. does the time based trigger send the email or not. ??

And also could you help me writing that workflow with the time based action in my scenario.

Thanks,
Virinchi.
Mahesh DMahesh D
Hi Virinchi,

If the Opportunity chnaged to closed then the time dependent action will automatically deleted.

Also find the screenshot.


User-added image

Please do let me know if it helps you.

Regards,
Mahesh
Virinchi BairisettyVirinchi Bairisetty
Can you also tell me how to update all the existing records in the opprotunities.
Mahesh DMahesh D
Hi Virinchi,

You can extract / export using the Apex Data Loader and update them back to Salesforce.

Regards,
​Mahesh