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
34213421 

Unable to get the non duplicate Contact Emails from Child records

I have a lookup relationship between Order Line and Contact Objects where Contact is the parent. I am writing a batch class which looks for certain criteria on Order Line and send email to Contacts excluding the duplicates. 

Right now I have  2 duplicate Order Lines for the Contact and the batch is supposed to send 1 email. But in this code, there are 3 emails sent out. Can someone help with this?
 
global class BatchSendEmails implements Database.Batchable<SObject>
{
global Database.QueryLocator start(Database.BatchableContext BC){
	return Database.getQueryLocator([select id,Contact__r.Email,Contact__c,Account__c from Order_Line__c 
                                     where Order__r.OrderApi__Date__c = LAST_N_DAYS:3]);
}
      
    global void execute (Database.BatchableContext BC, List<Order_Line__c> scope){
         List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMessage>();
                                 List<String> email = new List<String>(); 
      
       List<Order_Line__c> sollist = [SELECT Id, Item__c, Contact__c,Order__c ,Contact__r.Email FROM Order_Line__c
        WHERE Order__r.Date__c = LAST_N_DAYS:3 AND Item__r.Name = 'Test'];
       
        Set<Id> solids = new Set<Id>();
        for(Order_Line__c ol : ollist){
            if(ol.id != null){
               olids.add(ol.Contact__c);
                system.debug('Added Contacts'+olids);
            }
        }
        
        AggregateResult[] groupedResults = [
        SELECT COUNT(Id) dups, Item__c, Contact__c,Order__c,Contact__r.Email
        FROM Order_Line__c
        WHERE Contact__c =:solids and Sales_Order__r.Date__c = LAST_N_DAYS:3 
        AND Item__r.Name = 'Test'    
        GROUP BY Item__c,Contact__c,Contact__r.Email,Order__c
         HAVING COUNT(Id) = 1];

        for(AggregateResult ar:groupedResults){
            String custid = (ID)ar.get('OrderApi__Contact__c');
            
          //	email.add(custid);
            
        List<Order_Line__c> sollist1 = [select id, Contact__r.Email,Order__c from Order_Line__c where id=: ar.Id limit 1];
            email.clear();
                email.add(ollist[0].OrderApi__Contact__r.Email); */
                                 List<String> ccemail = new List<String>();                                 
                                 ccemail.add('test@test.com');
    							 Contact c = [select id, Email from Contact where email <> null limit 1];
            try{                             
EmailTemplate templateId = [Select id from EmailTemplate where name = 'Email'];                                                   
                    system.debug(email);
                Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                 OrgWideEmailAddress owa = [select id, DisplayName, Address from OrgWideEmailAddress where address = 'testing@test.org' limit 1];
                                            system.debug(email.size());
                                            mail.setToAddresses(email);
                                            mail.setccAddresses(ccemail);
                                            mail.setOrgWideEmailAddressId(owa.id);
                                            mail.setTargetObjectId(c.Id);
           									mail.setTreatTargetObjectAsRecipient(false);
                                            mail.setTemplateID(templateId.Id); 
                                            mail.setSaveAsActivity(false); 
                                            mails.add(mail);
                                            system.debug(mail);
											
                          
                                 Messaging.sendEmail(mails);
            }catch(Exception e){
                system.debug('Exception occured' +e);
            }
                
    }
    }
        
    global void finish(Database.BatchableContext BC){
        
    }
}

 
NagendraNagendra (Salesforce Developers) 
Hi,

You have several logical mistakes, syntax errors, and negative practices in your code, which is very messy and will not in the state that you have posted it compile.
  • You are sending email in a loop, and what's worse you're sending it based on a list that you do not clear after sending it. This is your core problem, and it's why you get duplicate emails.
  • You are running no fewer than three queries in a loop, none of which need to be there.
  • You completely ignore scope and perform a rather different query in execute() than you do in start(), which kills the point of the batch class and will make it hard to figure out your class's behavior.
  • You have several undeclared variable references (solids versus olids).
  • You are trying to extract a field OrderApi__Contact__c from your AggregateResult, which you do not query.
  • Your aggregate query does not make sense. You do not even use most of the fields you query.
  • You almost certainly do not need this class to be global.
This is not an exhaustive list. I would encourage you, when you get into a bind like this, to step backand think through your logic and control structures. Your code is such a mess that it is very difficult to see what it's doing at all. If you find yourself tempted to copy-paste a lot of snippets and basically throw code at the wall to see what sticks, take that as a suggestion to step back, refactor your code, and help yourself find clarity in your logical structure so you can iron out where you need to fix the details.

I'd like to try to support you in this project by sketching out in pseudo-code how I think this method should work, based on your description of what you intend it to do. I'd suggest you start over and try to implement this method in a straightforward way.
global void execute (Database.BatchableContext bc, List<Order_Line__c> scope) {

    Create a new variable `contactIds` as a `Set<Id>`.
    Iterate over `scope`. 
        For each Order_Line__c, add its Contact Id to the `contactIds` set.

    Create a new variable `emails` as a `List<Messaging.SingleEmailMessage>`.
    Query for your email template.
    Iterate over `contactIds`.
        For each Id, create a SingleEmailMessage and add it to the list.

    Make exactly one call to sendEmail with the list of email messages.
}
Your query in start() should, ideally, ensure that you have exactly the Order Lines you need. You shouldn't need to re-query them in execute() in most cases.

One additional wrinkle that I do not cover here is how to ensure that you do not duplicate emails to the same Contact across batches. When you get this working, I would point you to the Database.Stateful interface and think about using another Set<Id> to track which Contacts you've already emailed throughout the process.

Hope this helps.

Kindly mark this as solved if it's resolved.

Thanks,
Nagendra