• Muhammad Jawwad 16
  • NEWBIE
  • 90 Points
  • Member since 2019

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 22
    Questions
  • 35
    Replies
public with sharing class ScheduleMyFlow implements Schedulable {

  public void execute(SchedulableContext sc) {
    //  calling a flow 
    new Flow.Interview.MyFlow(new Map<String,Object>()).start();

    
  }

}

 

Hi friends,

I'm trying to run 5 flows from batch apex but the batch was not working as expected. Here Is my code

global class reminderTextAutomationBatch implements Database.Batchable<sObject> {
   
    global Database.QueryLocator start(Database.BatchableContext BC) {
       //String num = '+923417670738'; 
        String query = 'SELECT Id,Mogli_Number__c,Delivery_Date_Time__c,PCC_Other__c,Pick_Up_Date_Time__c,Prescription_Date__c,RecordTypeId,Status__c,Prescription_Length__c,Due_Date__c FROM Prescription__c';
        return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext BC, List<Prescription__c> PrescriptionList) {
       
        // process each batch of records

        Id GraymontPrescriptionRecordTypeId = Schema.SObjectType.Prescription__c.getRecordTypeInfosByName().get('Graymont Prescription').getRecordTypeId(); 
        
        for(Prescription__c Pres : PrescriptionList)
        {       
            
            Map<String, Object> params = new Map<String, Object>();
            params.put('recordId', Pres.Id); 
            if (Pres.Delivery_Date_Time__c != null && Pres.PCC_Other__c =='LIVE' && Pres.Pick_Up_Date_Time__c == null && 
                    Pres.RecordTypeID ==GraymontPrescriptionRecordTypeId && Pres.Status__c != 'Cancelled' && Pres.Prescription_Date__c == Date.today()+1 && Pres.Mogli_Number__c !=null) {
                        Flow.Interview.GM_Text_Reminder_Surgery_Confirmation myFlow = new Flow.Interview.GM_Text_Reminder_Surgery_Confirmation(params);
                        myFlow.start();
                        System.debug('mai andar agaya');
               }
            else if (Pres.Delivery_Date_Time__c == Date.today()+1 && (Pres.PCC_Other__c !='ORTHOCOR' || Pres.PCC_Other__c !='IN-CLINIC FITTERS') && Pres.Pick_Up_Date_Time__c == null && 
                    Pres.RecordTypeID ==GraymontPrescriptionRecordTypeId && Pres.Status__c != 'Cancelled' && Pres.Mogli_Number__c !=null ) {
                        Flow.Interview.GM_Text_Reminder_Delivery myFlow = new Flow.Interview.GM_Text_Reminder_Delivery(params);
                        myFlow.start();
                        System.debug('mai andar agaya1');
               }
            else if ((Pres.PCC_Other__c !='ORTHOCOR' || Pres.PCC_Other__c !='IN-CLINIC FITTERS') && Pres.Pick_Up_Date_Time__c == Date.today()+1 && 
                    Pres.RecordTypeID ==GraymontPrescriptionRecordTypeId && Pres.Status__c != 'Cancelled' && Pres.Mogli_Number__c !=null ) {
                        Flow.Interview.GM_Text_Reminder_Pick_Up myFlow = new Flow.Interview.GM_Text_Reminder_Pick_Up(params);
                        myFlow.start();
                        System.debug('mai andar agaya2');
               }
            else if (Pres.Due_Date__c == Date.today()+3 &&(Pres.PCC_Other__c !='ORTHOCOR' || Pres.PCC_Other__c !='IN-CLINIC FITTERS') && Pres.Pick_Up_Date_Time__c == null && Pres.Prescription_Length__c > 0
                    && Pres.RecordTypeID ==GraymontPrescriptionRecordTypeId && Pres.Status__c != 'Cancelled'&& Pres.Mogli_Number__c !=null ) {
                        Flow.Interview.GM_Text_Reminder_Due_Date myFlow = new Flow.Interview.GM_Text_Reminder_Due_Date(params);
                        myFlow.start();
                        System.debug('mai andar agaya3');
               }
            else if (Pres.Due_Date__c == Date.today()+3 && Pres.PCC_Other__c !='IN-CLINIC FITTERS' && Pres.Pick_Up_Date_Time__c == null && Pres.Prescription_Length__c > 0
                    && Pres.RecordTypeID ==GraymontPrescriptionRecordTypeId && Pres.Status__c != 'Cancelled'&& Pres.Mogli_Number__c !=null  ) {
                        Flow.Interview.GM_Text_Reminder_In_Clinic myFlow = new Flow.Interview.GM_Text_Reminder_In_Clinic(params);
                        myFlow.start();
                        System.debug('mai andar agaya4');
               }

           }
        }
    
    global void finish(Database.BatchableContext BC) {
    	// execute any post-processing operations
  }
}

Please tell me where I'm wrong in this code and suggest the best solution.

Hi,

I'm trying to redirect to the newly created record in visualforce, but failed.

Anybody help me, how to redirect after clicking the save button to the newly created record?

Please help

Thanks

Muhammad Jawwad

Please find my trigger  in code sample and suggest me how to write a test class for helper class.

Trigger :

trigger ContentDocument on ContentDocument (after insert) 
{
 if(Trigger.isAfter && Trigger.isInsert)
   {
   
  //1. SELECT THE OPPORTUNITY TEAM
  SET<ID> SetOfContentDocumentIDs = new SET<ID>();
  for(ContentDocument cdoc: Trigger.New)
  {
    SetOfContentDocumentIDs.add(cdoc.Id);
  } 
 
  //2. Sharing Files With Opportunity Team
  ContentDocumentHelper.shareOpportunityPrivateFileWithOppTeam(SetOfContentDocumentIDs);
  }
  
}

Helper Class:

public class ContentDocumentHelper{

 @future
 public static void shareOpportunityPrivateFileWithOppTeam(SET<ID> SetOfContentDocumentIDs)
 {
    
    System.Debug('AM: SetOfContentDocumentIDs :' + SetOfContentDocumentIDs);
    List<ContentDocumentLink> ListOfContentDocumentLink = new List<ContentDocumentLink>();
    
    integer i = 0;
    while(i <=80 && ListOfContentDocumentLink.size()<=1 )
    {
      ListOfContentDocumentLink = [ SELECT LinkedEntityId,ContentDocumentId 
                                    FROM ContentDocumentLink 
                                    WHERE ContentDocumentId IN : SetOfContentDocumentIDs];
      i++;
    }
                                                             
  //2.Get OpportunityTeamMember  
  SET<ID> SetOfOpportunityIDs = new SET<ID>();
  for(ContentDocumentLink cdl : ListOfContentDocumentLink )
  {
    SetOfOpportunityIDs.add(cdl.LinkedEntityId);
  } 
  
  System.Debug('AM: SetOfOpportunityIDs : ' + SetOfOpportunityIDs );
  
   
  List<OpportunityTeamMember> ListofOpportunityTeamMember =  [SELECT UserId,OpportunityId 
                                                              FROM OpportunityTeamMember 
                                                              WHERE OpportunityId IN : SetOfOpportunityIDs];
  
  
  System.Debug('AM: ListofOpportunityTeamMember :' + ListofOpportunityTeamMember );
  
  MAP<ID,SET<ID>> MapOfOppIDSetUsersID = new MAP<ID,SET<ID>>();
  
  for(OpportunityTeamMember oppteam :ListofOpportunityTeamMember )
  {
    if(MapOfOppIDSetUsersID.get(oppteam.OpportunityId) == null)
    {
      SET<ID> SetofUserID = new SET<ID>();
      SetofUserID.add(oppteam.UserId);
      MapOfOppIDSetUsersID.put(oppteam.OpportunityId,SetofUserID);
    }
    else
    {
      SET<ID> SetofUserID = MapOfOppIDSetUsersID.get(oppteam.OpportunityId);
      SetofUserID.add(oppteam.UserId);
      MapOfOppIDSetUsersID.put(oppteam.OpportunityId,SetofUserID);
      
    }
  }
  
  System.Debug('AM: MapOfOppIDSetUsersID: ' + MapOfOppIDSetUsersID);
 
  //3.Process the documents and share with users
  List<ContentDocumentLink > ListofUserShare = new List<ContentDocumentLink >();
  
  for(ContentDocumentLink cdl: ListOfContentDocumentLink)
  {
    
   SET<ID> SetofUserIDs = new SET<ID>();
   SetofUserIDs = MapOfOppIDSetUsersID.get(cdl.LinkedEntityId);
   if(SetofUserIDs!=null)
   { 
    if(SetofUserIDs.size()>0)
    {
     for(ID userID: SetofUserIDs)
     {
       if(!SetOfOpportunityIDs.contains(userID))
       {
         ListofUserShare.add(
            new ContentDocumentLink (
                  ContentDocumentId = cdl.ContentDocumentId ,
                  LinkedEntityId = userID,
                  ShareType = 'C'
                )
         );
       }
    }
   }
  }  
  } 
  
  System.Debug('AM: ListofUserShare : ' + ListofUserShare);
  
  if(ListofUserShare.size()>0)
  {
    insert ListofUserShare;
  }
}

}


Test Class:

@isTest
public class ContentDocumentTest {
@istest    
 public static  void test() {
            
           
        
                    
           Opportunity o = New Opportunity ();
           o.StageName ='Open';
           
           o.CloseDate=System.today() + 5;
           o.Name='Test NL Opportunity';
           
           insert o;             
            
            
        List<ContentDocument> documents = [SELECT Id, Title, LatestPublishedVersionId FROM ContentDocument];

        ContentDocumentLink contentlink=new ContentDocumentLink();
        contentlink.LinkedEntityId=o.Id;
        contentlink.ShareType= 'v';
        contentlink.LinkedEntityId = o.id;
        contentlink.ContentDocumentId=documents[0].Id;
        contentlink.Visibility = 'AllUsers'; 
        insert contentlink;          
            
               
             
           
        } 
}

Please help​​​​​​​
public class BatchApexController{
 
      public void callBatch(){
      
        database.executebatch(new LoanofficerBatch());
    database.executebatch(new RealtorBatch());
    database.executebatch(new BuilderBatch());
      }
}
please help
 
How do I test schedule apex? please help
global class MonthlyPipelineReportScheduled implements Schedulable 
{
    global void execute(SchedulableContext sc) 
    {
    

        LoanofficerBatch lob = new LoanofficerBatch(); 
        database.executebatch(lob);
        
        RealtorBatch rb = new RealtorBatch(); 
        database.executebatch(rb);
        
        BuilderBatch bb = new BuilderBatch(); 
        database.executebatch(bb);
    }
}

 
I have three batch classes, I want to execute all three batch classes through button click using visualforce page at the same time.
LoanofficerBatch lob = new LoanofficerBatch(); 
        database.executebatch(lob);
        
        RealtorBatch rb = new RealtorBatch(); 
        database.executebatch(rb);
        
        BuilderBatch bb = new BuilderBatch(); 
        database.executebatch(bb);

please help
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, ConvertedOpportunityId, Name, Phone,' 
                          +  'Status, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead'; 
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate = LAST_MONTH  AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        
        
         
        List<String> convertedOppId = new List<String>();
         for(Lead l: allLeads){
	         convertedOppId.add(l.ConvertedOpportunityId); 
             }

         Map<Id, Opportunity> opptyMap = new Map<Id, Opportunity>();  
         for(Opportunity o : [SELECT Id,Name,Contact__r.Name,Contact__r.Phone,Starting_Credit_Score__c,Enrolled_On__c,Est_Re_Pull_Date__c,StageName FROM Opportunity WHERE Id IN: convertedOppId]){
	      opptyMap.put(o.Id, o);
          }
        
        
        
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Status</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    
                    for(Lead l: leadMap.get(i))  
                    if(l.ConvertedOpportunityId==null)
                    {                    {
                        
                        
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Status+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                                               }
                    leadsTable += '</table>';
                    
                     String opptyTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Enrolled On</td><td>Estimated Pull Date</td><td>StageName</td></tr>';
                    
                    for(Id idKey: opptyMap.keySet()){
                        Opportunity o = opptyMap.get(idKey);
                        
                        
                        opptyTable += '<tr><td>'+o.Contact__r.Name+'</td><td>'+o.Contact__r.Phone+'</td><td>'+o.Starting_Credit_Score__c+'</td><td>'+o.Enrolled_On__c+'</td>'
                            +'<td>'+o.Est_Re_Pull_Date__c+'</td><td>'+o.StageName+'</td></tr>';
                    }
                    opptyTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('||Opportunity||',opptyTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}
please help
 
Can anyone please help writing a test class?
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, ConvertedOpportunityId, Name, Phone,' 
                          +  'Status, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead'; 
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate = THIS_MONTH  AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        
        
         
        List<String> convertedOppId = new List<String>();
         for(Lead l: allLeads){
	         convertedOppId.add(l.ConvertedOpportunityId); 
             }

         Map<Id, Opportunity> opptyMap = new Map<Id, Opportunity>();  
         for(Opportunity o : [SELECT Id,Name,Contact__r.Name,Contact__r.Phone,Starting_Credit_Score__c,Enrolled_On__c,Est_Re_Pull_Date__c,StageName FROM Opportunity WHERE Id IN: convertedOppId]){
	      opptyMap.put(o.Id, o);
          }
        
        
        
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Status</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    
                    for(Lead l: leadMap.get(i))  
                    if(l.ConvertedOpportunityId==null)
                    {                    {
                        
                        
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Status+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                                               }
                    leadsTable += '</table>';
                    
                     String opptyTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Enrolled On</td><td>Estimated Pull Date</td><td>StageName</td></tr>';
                    
                    for(Id idKey: opptyMap.keySet()){
                        Opportunity o = opptyMap.get(idKey);
                        
                        
                        opptyTable += '<tr><td>'+o.Contact__r.Name+'</td><td>'+o.Contact__r.Phone+'</td><td>'+o.Starting_Credit_Score__c+'</td><td>'+o.Enrolled_On__c+'</td>'
                            +'<td>'+o.Est_Re_Pull_Date__c+'</td><td>'+o.StageName+'</td></tr>';
                    }
                    opptyTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('||Opportunity||',opptyTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}
I'm trying to get associated opportunities to form lead but I'm getting an error: First error: Attempt to de-reference a null object. Please Help
global class testoppty implements Database.Batchable<sObject> {
    public String query = 'SELECT Realtor__c,Realtor__r.Email, ConvertedOpportunityId, Name, Phone,  ' 
                          +  'Status, Loan_Officer_Lookup_Name__c ' 
                          +   ' FROM Lead';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'RealtorRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate = LAST_MONTH  AND Realtor__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        
        List<String> convertedOppId = new List<String>();
        for(Lead l: allLeads){
	         convertedOppId.add(l.ConvertedOpportunityId); 
        }
        Map<Id, Opportunity> opptyMap = new Map<Id, Opportunity>([SELECT Id,Name,StageName FROM Opportunity WHERE Id IN: convertedOppId ]);  
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Realtor__c)){
                    leadMap.put(l.Realtor__c, new List<lead>());
                }
                leadMap.get(l.Realtor__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||RealtorName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Status</td><td>Loan Officer Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Status+'</td>'+
                            '<td>'+l.Loan_Officer_Lookup_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    String opptyTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Stage Name</td></tr>';
                    
                    for(Lead l: leadMap.get(i)){
                        Opportunity o = opptyMap.get(l.ConvertedOpportunityId);                        
                        opptyTable += '<tr><td>'+o.Name+'</td>'
                            +'<td>'+o.StageName+'</td></tr>';
                    }
                    opptyTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('||Opportunity||',opptyTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {}
}
Please review my code and tell me where i'm wrong to getting data from oppty map and populate into table?
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, ConvertedOpportunityId, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead'; /*JMA:: add  ConvertedOpportunityId field*/ 
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        
        //JMA:: Create a map of <Id, Opportunity>
        //JMA:: Query Opportunities by using Lead.ConvertedOpportunityId. You need to first create a set of Id from Lead.ConvertedOpportunityId
        //JMA:: populate the map <Id, Opportunity>
         List<String> convertedOppId = new List<String>();
         for(Lead l: allLeads){
	         convertedOppId.add(l.ConvertedOpportunityId); 
             }

         Map<Id, Opportunity> opptymap = new Map<Id, Opportunity>();  
         for(Opportunity o : [SELECT Id FROM Opportunity WHERE Id IN: convertedOppId]){
	      opptymap.put(o.Id, o);
          }
        
        
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    
                    for(Lead l: leadMap.get(i)){
                        //JMA:: for each lead you can get the associated Opportunity from map <Id, Opportunity>
                        
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    String opptyTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Starting Credit Score</td><td>StageNmae</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    
                    for(Opportunity o: opptymap.get(i)){
                        //JMA:: for each lead you can get the associated Opportunity from map <Id, Opportunity>
                        
                        opptyTable += '<tr><td>'+o.Name+'</td>'+
                            '<td>'+o.Starting_Credit_Score__c+'</td><td>'+o.StageName+'</td><td>'+o.Enrolled_On__c+'</td>'+
                            '<td>'+o.Est_Re_Pull_Date__c+'</td><td>'+o.Realtor_ID__r.Name+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, ConvertedOpportunityId, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead'; /*JMA:: add  ConvertedOpportunityId field*/ 
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        
        //JMA:: Create a map of <Id, Opportunity>
        Map<Id,List<Opportunity>> OpptyMap = new Map<Id,List<Opportunity>>();
        //JMA:: Query Opportunities by using Lead.ConvertedOpportunityId. You need to first create a set of Id from Lead.ConvertedOpportunityId
        //JMA:: populate the map <Id, Opportunity>
        
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        //JMA:: for each lead you can get the associated Opportunity from map <Id, Opportunity>
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}

 
I have two classes from which two separate mail is going on. I'm going to go through the same batch within a single mail and the objects records .
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}
global class LoanOfficerOppBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1__c,Loan_Officer_1__r.Email, Name, Starting_Credit_Score__c, ' 
                          +  ' StageName, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_ID__r.Name ' 
                          +   ' FROM Opportunity';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Opportunity> alloppty) {
        Map<Id,List<Opportunity>> OpptyMap = new Map<Id,List<Opportunity>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(alloppty != null && alloppty.size() > 0){
            for(Opportunity l: alloppty){
                if(!OpptyMap.containsKey(l.Loan_Officer_1__c)){
                    OpptyMap.put(l.Loan_Officer_1__c, new List<Opportunity>());
                }
                OpptyMap.get(l.Loan_Officer_1__c).add(l);
            }
        }
        if(OpptyMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: OpptyMap.keySet()]);
            for(Id i: OpptyMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String OpptyTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Opportunity l: OpptyMap.get(i)){
                        OpptyTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.StageName+'</td><td>'+l.Realtor_ID__r.Name+'</td></tr>';
                    }
                    OpptyTable += '</table>';
                    html = html.replace('||Leads||',OpptyTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}


 
Hi,

I have two standard objects like Lead and Opportunity. For these two objects given a lookup relationship to Contact object. 

Actually, I have tried for the lead object and below is the code but I need the same scenario for opportunity object. Can anybody please help me with this.
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead';

opportunity???????


    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}

 
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead';

opportunity????
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

 
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead';
    
    public String query1 = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Opportunity';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];
    
    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        query1 += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}

 
Scenario is: If Quote stage exceeds 7 days, send email reminder to record owner to follow up with quote
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}

I'm tring to write test class following is the test class;
@isTest
private class LoanOfficerBatchTest {

	static testMethod void exTest1(){
		
		
		 Date startDate = Date.today().addMonths(-1);
        startDate = Date.newInstance(startDate.year(),startDate.month(),1);
        Date endDate = startDate;
        Integer days = Date.daysInMonth(endDate.year(), endDate.month());
		
		Contact c   = new Contact();
       c.FirstName = 'Stephen';
       c.LastName  = 'Curry';
       c.Email     = 'stephcurry@gsw.com';
       insert c;
	   
		Lead ls =new Lead(Company = 'JohnMiller', LastName = 'Mike', Status = 'Open' ,Loan_Officer_1a__c =c.Id);
		insert ls ;
		Test.setCreatedDate(ls.Id, startDate);

		
		Lead ls1 =new Lead(Company = 'JohnMiller', LastName = 'Mike', Status = 'Open' );
		insert ls1 ;
		Test.setCreatedDate(ls1.Id, startDate);

	test.startTest();

	
		test.stopTest();
	}
}

I'm not covering a code coverage of 75%. It is not increasing more than 17% please suggest. 
global class MonPipelineReportBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {
        Date startDate = Date.today().addMonths(-1);
        startDate = Date.newInstance(startDate.year(),startDate.month(),1);
        Date endDate = startDate;
        Integer days = Date.daysInMonth(endDate.year(), endDate.month());
        endDate = Date.newInstance(startDate.year(),startDate.month(),days);

        query += ' WHERE CreatedDate >=: ' + startDate + ' AND CreatedDate <=: ' + endDate + ' AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

 

Hi,

I'm trying to redirect to the newly created record in visualforce, but failed.

Anybody help me, how to redirect after clicking the save button to the newly created record?

Please help

Thanks

Muhammad Jawwad

Please find my trigger  in code sample and suggest me how to write a test class for helper class.

Trigger :

trigger ContentDocument on ContentDocument (after insert) 
{
 if(Trigger.isAfter && Trigger.isInsert)
   {
   
  //1. SELECT THE OPPORTUNITY TEAM
  SET<ID> SetOfContentDocumentIDs = new SET<ID>();
  for(ContentDocument cdoc: Trigger.New)
  {
    SetOfContentDocumentIDs.add(cdoc.Id);
  } 
 
  //2. Sharing Files With Opportunity Team
  ContentDocumentHelper.shareOpportunityPrivateFileWithOppTeam(SetOfContentDocumentIDs);
  }
  
}

Helper Class:

public class ContentDocumentHelper{

 @future
 public static void shareOpportunityPrivateFileWithOppTeam(SET<ID> SetOfContentDocumentIDs)
 {
    
    System.Debug('AM: SetOfContentDocumentIDs :' + SetOfContentDocumentIDs);
    List<ContentDocumentLink> ListOfContentDocumentLink = new List<ContentDocumentLink>();
    
    integer i = 0;
    while(i <=80 && ListOfContentDocumentLink.size()<=1 )
    {
      ListOfContentDocumentLink = [ SELECT LinkedEntityId,ContentDocumentId 
                                    FROM ContentDocumentLink 
                                    WHERE ContentDocumentId IN : SetOfContentDocumentIDs];
      i++;
    }
                                                             
  //2.Get OpportunityTeamMember  
  SET<ID> SetOfOpportunityIDs = new SET<ID>();
  for(ContentDocumentLink cdl : ListOfContentDocumentLink )
  {
    SetOfOpportunityIDs.add(cdl.LinkedEntityId);
  } 
  
  System.Debug('AM: SetOfOpportunityIDs : ' + SetOfOpportunityIDs );
  
   
  List<OpportunityTeamMember> ListofOpportunityTeamMember =  [SELECT UserId,OpportunityId 
                                                              FROM OpportunityTeamMember 
                                                              WHERE OpportunityId IN : SetOfOpportunityIDs];
  
  
  System.Debug('AM: ListofOpportunityTeamMember :' + ListofOpportunityTeamMember );
  
  MAP<ID,SET<ID>> MapOfOppIDSetUsersID = new MAP<ID,SET<ID>>();
  
  for(OpportunityTeamMember oppteam :ListofOpportunityTeamMember )
  {
    if(MapOfOppIDSetUsersID.get(oppteam.OpportunityId) == null)
    {
      SET<ID> SetofUserID = new SET<ID>();
      SetofUserID.add(oppteam.UserId);
      MapOfOppIDSetUsersID.put(oppteam.OpportunityId,SetofUserID);
    }
    else
    {
      SET<ID> SetofUserID = MapOfOppIDSetUsersID.get(oppteam.OpportunityId);
      SetofUserID.add(oppteam.UserId);
      MapOfOppIDSetUsersID.put(oppteam.OpportunityId,SetofUserID);
      
    }
  }
  
  System.Debug('AM: MapOfOppIDSetUsersID: ' + MapOfOppIDSetUsersID);
 
  //3.Process the documents and share with users
  List<ContentDocumentLink > ListofUserShare = new List<ContentDocumentLink >();
  
  for(ContentDocumentLink cdl: ListOfContentDocumentLink)
  {
    
   SET<ID> SetofUserIDs = new SET<ID>();
   SetofUserIDs = MapOfOppIDSetUsersID.get(cdl.LinkedEntityId);
   if(SetofUserIDs!=null)
   { 
    if(SetofUserIDs.size()>0)
    {
     for(ID userID: SetofUserIDs)
     {
       if(!SetOfOpportunityIDs.contains(userID))
       {
         ListofUserShare.add(
            new ContentDocumentLink (
                  ContentDocumentId = cdl.ContentDocumentId ,
                  LinkedEntityId = userID,
                  ShareType = 'C'
                )
         );
       }
    }
   }
  }  
  } 
  
  System.Debug('AM: ListofUserShare : ' + ListofUserShare);
  
  if(ListofUserShare.size()>0)
  {
    insert ListofUserShare;
  }
}

}


Test Class:

@isTest
public class ContentDocumentTest {
@istest    
 public static  void test() {
            
           
        
                    
           Opportunity o = New Opportunity ();
           o.StageName ='Open';
           
           o.CloseDate=System.today() + 5;
           o.Name='Test NL Opportunity';
           
           insert o;             
            
            
        List<ContentDocument> documents = [SELECT Id, Title, LatestPublishedVersionId FROM ContentDocument];

        ContentDocumentLink contentlink=new ContentDocumentLink();
        contentlink.LinkedEntityId=o.Id;
        contentlink.ShareType= 'v';
        contentlink.LinkedEntityId = o.id;
        contentlink.ContentDocumentId=documents[0].Id;
        contentlink.Visibility = 'AllUsers'; 
        insert contentlink;          
            
               
             
           
        } 
}

Please help​​​​​​​
Can anyone please help writing a test class?
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, ConvertedOpportunityId, Name, Phone,' 
                          +  'Status, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead'; 
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate = THIS_MONTH  AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        
        
         
        List<String> convertedOppId = new List<String>();
         for(Lead l: allLeads){
	         convertedOppId.add(l.ConvertedOpportunityId); 
             }

         Map<Id, Opportunity> opptyMap = new Map<Id, Opportunity>();  
         for(Opportunity o : [SELECT Id,Name,Contact__r.Name,Contact__r.Phone,Starting_Credit_Score__c,Enrolled_On__c,Est_Re_Pull_Date__c,StageName FROM Opportunity WHERE Id IN: convertedOppId]){
	      opptyMap.put(o.Id, o);
          }
        
        
        
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Status</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    
                    for(Lead l: leadMap.get(i))  
                    if(l.ConvertedOpportunityId==null)
                    {                    {
                        
                        
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Status+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                                               }
                    leadsTable += '</table>';
                    
                     String opptyTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Enrolled On</td><td>Estimated Pull Date</td><td>StageName</td></tr>';
                    
                    for(Id idKey: opptyMap.keySet()){
                        Opportunity o = opptyMap.get(idKey);
                        
                        
                        opptyTable += '<tr><td>'+o.Contact__r.Name+'</td><td>'+o.Contact__r.Phone+'</td><td>'+o.Starting_Credit_Score__c+'</td><td>'+o.Enrolled_On__c+'</td>'
                            +'<td>'+o.Est_Re_Pull_Date__c+'</td><td>'+o.StageName+'</td></tr>';
                    }
                    opptyTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('||Opportunity||',opptyTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}
I'm trying to get associated opportunities to form lead but I'm getting an error: First error: Attempt to de-reference a null object. Please Help
global class testoppty implements Database.Batchable<sObject> {
    public String query = 'SELECT Realtor__c,Realtor__r.Email, ConvertedOpportunityId, Name, Phone,  ' 
                          +  'Status, Loan_Officer_Lookup_Name__c ' 
                          +   ' FROM Lead';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'RealtorRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate = LAST_MONTH  AND Realtor__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        
        List<String> convertedOppId = new List<String>();
        for(Lead l: allLeads){
	         convertedOppId.add(l.ConvertedOpportunityId); 
        }
        Map<Id, Opportunity> opptyMap = new Map<Id, Opportunity>([SELECT Id,Name,StageName FROM Opportunity WHERE Id IN: convertedOppId ]);  
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Realtor__c)){
                    leadMap.put(l.Realtor__c, new List<lead>());
                }
                leadMap.get(l.Realtor__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||RealtorName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Status</td><td>Loan Officer Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Status+'</td>'+
                            '<td>'+l.Loan_Officer_Lookup_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    String opptyTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Stage Name</td></tr>';
                    
                    for(Lead l: leadMap.get(i)){
                        Opportunity o = opptyMap.get(l.ConvertedOpportunityId);                        
                        opptyTable += '<tr><td>'+o.Name+'</td>'
                            +'<td>'+o.StageName+'</td></tr>';
                    }
                    opptyTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('||Opportunity||',opptyTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {}
}
Please review my code and tell me where i'm wrong to getting data from oppty map and populate into table?
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, ConvertedOpportunityId, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead'; /*JMA:: add  ConvertedOpportunityId field*/ 
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        
        //JMA:: Create a map of <Id, Opportunity>
        //JMA:: Query Opportunities by using Lead.ConvertedOpportunityId. You need to first create a set of Id from Lead.ConvertedOpportunityId
        //JMA:: populate the map <Id, Opportunity>
         List<String> convertedOppId = new List<String>();
         for(Lead l: allLeads){
	         convertedOppId.add(l.ConvertedOpportunityId); 
             }

         Map<Id, Opportunity> opptymap = new Map<Id, Opportunity>();  
         for(Opportunity o : [SELECT Id FROM Opportunity WHERE Id IN: convertedOppId]){
	      opptymap.put(o.Id, o);
          }
        
        
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    
                    for(Lead l: leadMap.get(i)){
                        //JMA:: for each lead you can get the associated Opportunity from map <Id, Opportunity>
                        
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    String opptyTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Starting Credit Score</td><td>StageNmae</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    
                    for(Opportunity o: opptymap.get(i)){
                        //JMA:: for each lead you can get the associated Opportunity from map <Id, Opportunity>
                        
                        opptyTable += '<tr><td>'+o.Name+'</td>'+
                            '<td>'+o.Starting_Credit_Score__c+'</td><td>'+o.StageName+'</td><td>'+o.Enrolled_On__c+'</td>'+
                            '<td>'+o.Est_Re_Pull_Date__c+'</td><td>'+o.Realtor_ID__r.Name+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, ConvertedOpportunityId, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead'; /*JMA:: add  ConvertedOpportunityId field*/ 
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        
        //JMA:: Create a map of <Id, Opportunity>
        Map<Id,List<Opportunity>> OpptyMap = new Map<Id,List<Opportunity>>();
        //JMA:: Query Opportunities by using Lead.ConvertedOpportunityId. You need to first create a set of Id from Lead.ConvertedOpportunityId
        //JMA:: populate the map <Id, Opportunity>
        
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        //JMA:: for each lead you can get the associated Opportunity from map <Id, Opportunity>
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}

 
I have two classes from which two separate mail is going on. I'm going to go through the same batch within a single mail and the objects records .
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}
global class LoanOfficerOppBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1__c,Loan_Officer_1__r.Email, Name, Starting_Credit_Score__c, ' 
                          +  ' StageName, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_ID__r.Name ' 
                          +   ' FROM Opportunity';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Opportunity> alloppty) {
        Map<Id,List<Opportunity>> OpptyMap = new Map<Id,List<Opportunity>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(alloppty != null && alloppty.size() > 0){
            for(Opportunity l: alloppty){
                if(!OpptyMap.containsKey(l.Loan_Officer_1__c)){
                    OpptyMap.put(l.Loan_Officer_1__c, new List<Opportunity>());
                }
                OpptyMap.get(l.Loan_Officer_1__c).add(l);
            }
        }
        if(OpptyMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: OpptyMap.keySet()]);
            for(Id i: OpptyMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String OpptyTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Opportunity l: OpptyMap.get(i)){
                        OpptyTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.StageName+'</td><td>'+l.Realtor_ID__r.Name+'</td></tr>';
                    }
                    OpptyTable += '</table>';
                    html = html.replace('||Leads||',OpptyTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            //Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}


 
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead';

opportunity????
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];

    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

 
global class LoanOfficerBatch implements Database.Batchable<sObject> {
    public String query = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Lead';
    
    public String query1 = 'SELECT Loan_Officer_1a__c,Loan_Officer_1a__r.Email, Name, Phone, Starting_Credit_Score__c, ' 
                          +  'Status, Enrolled_On__c, Est_Re_Pull_Date__c, Realtor_Name__c ' 
                          +   ' FROM Opportunity';
    public EmailTemplate templateId = [Select Id,HtmlValue,Subject from EmailTemplate where name = 'LoanOfficerRecord' LIMIT 1];
    
    global Database.QueryLocator start(Database.BatchableContext bc) {

        query += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        query1 += ' WHERE CreatedDate >= LAST_MONTH AND CreatedDate <= THIS_MONTH AND Loan_Officer_1a__c != null';
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, list<Lead> allLeads) {
        Map<Id,List<Lead>> leadMap = new Map<Id,List<Lead>>();
        List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMEssage>();
        if(allLeads != null && allLeads.size() > 0){
            for(Lead l: allLeads){
                if(!leadMap.containsKey(l.Loan_Officer_1a__c)){
                    leadMap.put(l.Loan_Officer_1a__c, new List<lead>());
                }
                leadMap.get(l.Loan_Officer_1a__c).add(l);
            }
        }
        if(leadMap.keySet().size() > 0){
            Map<Id,Contact> officers = new Map<Id,Contact>([SELECT Id,Email,Name FROM Contact WHERE Id IN: leadMap.keySet()]);
            for(Id i: leadMap.keySet()){
                Contact con = officers.get(i);
                System.debug(con);
                if(String.isnOtBlank(con.Email)){
                    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                    mail.setToAddresses(new String[]{con.EMail});
                    mail.setSubject(templateId.Subject);
                    String html = templateId.HtmlValue;
                    html = html.replace('||OfficerName||',con.Name);
                    String leadsTable = '<table cellpadding="3" cellspacing="3" width="100%" align="center" border="1" style="border-collapse:collapse;">'+
                        '<tr style="font-weight:bold;"><td>Name</td><td>Phone</td><td>Starting Credit Score</td><td>Status</td><td>Enrolled On</td><td>Est. Re Pull Date</td><td>Realtor Name</td></tr>';
                    for(Lead l: leadMap.get(i)){
                        leadsTable += '<tr><td>'+l.Name+'</td>'+
                            '<td>'+l.Phone+'</td><td>'+l.Starting_Credit_Score__c+'</td><td>'+l.Status+'</td><td>'+l.Enrolled_On__c+'</td>'+
                            '<td>'+l.Est_Re_Pull_Date__c+'</td><td>'+l.Realtor_Name__c+'</td></tr>';
                    }
                    leadsTable += '</table>';
                    html = html.replace('||Leads||',leadsTable);
                    html = html.replace('null',' ');
                    mail.setHTMLBody(html);
                    mails.add(mail);
                }
            }
        }
        if(mails.size() > 0){
            Messaging.sendEmail(mails);
        }
    }

    global void finish(Database.BatchableContext BC) {

    }
   
}