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
Sangeeta10Sangeeta10 

Status of each record processed in a batch apex

 I created a batch apex and schedulable apex and scheduled apex to send emails to all contacts on an Account associated with an open case of a specific sub-category. The batch apex works but there are some batch failures, I am unable to know status of each record in a batch as to whom email was sent and not and the reason behind batch failure. On an average there are about 833 records processed in 85 batches out of which 52 batches got failed. Not sure how many records in each batch got failed and why. How can I get status of 833 records which ones got processed and which ones got failed? Below is my batch class and schedulable class.
Batch Apex:
global class CaseNotifications implements Database.Batchable<sObject>,Database.Stateful {
    global string contactIds;
    global integer count = 0;

      global Database.QueryLocator start(Database.BatchableContext BC)
      {
          string subcategory1='Hardware';  
          string subcategory2='Software';
          return Database.getQueryLocator('select id from contact where accountId in (select accountId from case where Isclosed= false and (sub_category__c=\''+subcategory1+'\' or sub_category__c=\''+subcategory2+'\'))' );      
      }
    
    global void execute(Database.BatchableContext BC, List<sObject> scopeList) {
     List<Messaging.SingleEmailMessage> emails = new list<Messaging.SingleEmailMessage>();
        List<Contact> toAddresses=new List<Contact> ([select email from contact where id in :scopelist]);
        system.debug('ContactIds size'+toaddresses.size());
        if(toAddresses.size()>0){
         for(Contact c:toAddresses){
             if(c.email!=null){
               count++;

         Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
         Message.setTargetObjectId(c.id);
         Message.setTemplateId('00Xj00000011tbR');
         Message.setOrgWideEmailAddressId('0D2j00000000BAM');    
         emails.add(Message); 
             }}
        Messaging.sendEmail(emails);
        }
                 
         }
    
    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='Batches Processed ' + aaj.TotalJobItems + 'Total number of records processed ' + count+ 'with '+ aaj.NumberOfErrors + ' batch failures.\n';
        mail.setPlainTextBody(bodyText);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }

}

Schedulable Apex:
global class BackupCaseNotifications implements schedulable {
    global void execute(SchedulableContext ctx) {
        CaseNotifications b=new CaseNotifications();
        Database.executeBatch(b,10);
    }

}
Karan Shekhar KaulKaran Shekhar Kaul

Hi Sangeeta,

You can create a custom object to log errors.Create a object with name "Logs" & add some fields like Classname(Error origination clas),method name(Error origination method),StackTrace(Stacktrace of error),Error message(Error/exceptipon message),Batch Id(Id of failed batch)

Create a instance varaible of this type List<Log object> in the batch. Use this instnace to store your logs while batch is running(execute method) and insert them all at once in the finish() method. You will have to implement Database.Stateful interface ,which you have already done,to ensure that your instance variable is retains state across transactions.

Hope this helps.

Regards,

Karan

Lokesh KumarLokesh Kumar
HI Sangeeta,

Kindly use Messaging.SendEmailResult[] like below pseudo code.
 
Case c = new Case();
insert c;

EmailMessage e = new EmailMessage();
e.parentid = c.id;
// Set to draft status.
// This status is required 
// for sendEmailMessage().
e.Status = '5'; 
e.TextBody = 
  'Sample email message.';
e.Subject = 'Apex sample';
e.ToAddress = 'customer@email.com';
insert e;

List<Messaging.SendEmailResult> 
  results = 
  Messaging.sendEmailMessage(new ID[] 
    { e.id });

System.assertEquals(1, results.size());
System.assertEquals(true, 
                    results[0].success);

Knowledge link: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_email_outbound_messaging.htm

Thanks
lokesh


 
Sangeeta10Sangeeta10
Thanks for responding. I got my code working. Below is my updated code:

global class CaseNotifications implements Database.Batchable<sObject>,Database.Stateful {
    global string contactIds;
    global integer count = 0;

      global Database.QueryLocator start(Database.BatchableContext BC)
      {
          string subcategory1='Hardware';  
          string subcategory2='Software';
          return Database.getQueryLocator('select id from contact where accountId in (select accountId from case where Isclosed= false and (sub_category__c=\''+subcategory1+'\' or sub_category__c=\''+subcategory2+'\'))' );      
      }
    
    global void execute(Database.BatchableContext BC, List<sObject> scopeList) {
     List<Messaging.SingleEmailMessage> emails = new list<Messaging.SingleEmailMessage>();
        List<Contact> toAddresses=new List<Contact> ([select email from contact where id in :scopelist]);
        system.debug('ContactIds size'+toaddresses.size());
        if(toAddresses.size()>0){
         for(Contact c:toAddresses){
             if(c.email!=null){
               count++;

         Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
         Message.setTargetObjectId(c.id);
         Message.setTemplateId('00Xj00000011tbR');
         Message.setOrgWideEmailAddressId('0D2j00000000BAM');    
         emails.add(Message); 
             }}
        Messaging.SendEmailResult[] results = Messaging.sendEmail(emails,false);
        inspectResults(results);    

        }
                 
         }
    
    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='Batches Processed ' + aaj.TotalJobItems + 'Total number of records processed ' + count+ 'with '+ aaj.NumberOfErrors + ' batch failures.\n';
        mail.setPlainTextBody(bodyText);
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }

private static Boolean inspectResults(Messaging.SendEmailResult[] results) {
        Boolean sendResult = true;
        
        // sendEmail returns an array of result objects.
        // Iterate through the list to inspect results. 
        // In this class, the methods send only one email, 
        // so we should have only one result.
        for (Messaging.SendEmailResult res : results) {
            if (res.isSuccess()) {
                System.debug('Email sent successfully');
            }
            else {
                sendResult = false;
                System.debug('The following errors occurred: ' + res.getErrors());
                //email error
                Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();    
        mail.setSaveAsActivity(false);
        mail.setTargetObjectId('005j000000GFiun');      
        mail.setSubject('error for email in the batch'+ res.getErrors());
        mail.setBccSender(false);
        mail.setUseSignature(false);
        mail.setPlainTextBody('error for email in the batch' + res.getErrors()); 
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });       
            }
        }
        
      return sendResult;
  }

}