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 

Single Email Message generating duplicate emails

I am writing a batch class to send the emails based on certian criteria. I see that the parameters are passed right, no errors, but I end up receiving multiple duplicate emails when I run it. Here is the code

Two issues that I am trying to resolve

1. When there are less than 10 emails to be sent out, the code works fine, but sends duplicate emails
2. When there are more than more than 10 emails in the list that I am sending to, I get an error saying 'FATAL_ERROR|System.LimitException: Too many Email Invocations: 11'
 
global void execute(Database.BatchableContext BC, List<OrderApi__Badge__c> scope){
     
		system.debug('scope'+scope.size());  
     for(Order_c fbdge : [select Id,Description__c,Type__c,Contact__c,
 			ontact__r.Date__c,Contact__r.FirstName,Contact__r.LastName,
        	Contact__r.Years__c,Contact__r.Email,Active__c,Type__r.Name
From Order__c where (Type__r.Name ='A' or Type__r.Name ='B') and OrderApi__Is_Active__c =true 
                                and Date__c = 2017-06-30
        			  			and Contact__r.Date__c = 2017-06-30 and Contact__r.Years__c<10.00
                                and (Contact__r.Cat__c <> ' Life')]){
                                          system.debug('create list');
                                     bge.add(fbdge);
                                     system.debug('Bge' +bge.add(fbdge));
                                     contact.put(fbdge.Contact__c, fbdge.Contact__r.Email);
                                  
                                      }
     if(contact.size()>0){
         
     			 List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMessage>();
                                 List<String> email = new List<String>();
                                 List<String> ccemail = new List<String>();
          Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); 	
         try{
             for(Id ids : contact.keyset()){

          email.clear();
                 for(Order__c bdg :[select id,Contact__r.Email from Order__c where Contact__r.Email=:contact.get(ids) LIMIT 1]){
                     email.add(bdg.Contact__r.Email);
                     system.debug('size of emails' +email.size());
                 }
                                                                           
    Contact c = [select id, Email from Contact where email <> null limit 1];
     							
     EmailTemplate templateId = [Select id from EmailTemplate where name = 'Test'];
                                                            
                                            system.debug(email);
                                            system.debug(email.size());
                                            mail.setToAddresses(email);
                                            mail.setccAddresses(ccemail);
                                       
                                           	mail.setTargetObjectId(c.Id);
           									mail.setTreatTargetObjectAsRecipient(false);
                                           	mail.setTemplateID(templateId.Id); 
                                            mail.setSaveAsActivity(false); 
                                            mails.add(mail);
                                            system.debug(mail);
                         
     							//	messages.add(mail);
    if(mails.size())>0{                        
     Messaging.sendEmail(mails);
        						system.debug('mails');
        system.debug(mails.size());
           
 }        
}         
         } catch(exception ex){
             system.debug('Exception occured'+ex.getMessage());
         }
     }
 }
 global void finish(Database.BatchableContext BC){
    }
}

 
VaasuVaasu
Use SET instead of list to store emails, it will clear duplicates.

Set<String> email = new Set<String>();
Set<String> ccemail = new Set<String>();
34213421
I received this if I use set "Method does not exist or incorrect signature: void setToAddresses(Set<String>) from the type Messaging.SingleEmailMessaging
34213421
I did add that, but its the same error that I am receiving
 
Set<String> email = new Set<String>();
List<String> ccemail = new List<String>();
List <string> uniqueEmails = new List<String>();

          Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); 	
       
             for(Id ids : contact.keyset()){
                
          email.clear();
   for(Order__c bdg :[select id,Contact__r.Email from Order__c where Contact__r.Email=:contact.get(ids) LIMIT 1]){
                     email.add(bdg.OrderApi__Contact__r.Email);
                    
                 }
      
Contact c = [select id, Email from Contact where email <> null limit 1];
     EmailTemplate templateId = [Select id from EmailTemplate where name = 'Test'];
   
                                            system.debug(email.size());
                                            mail.setToAddresses(uniqueEmails.addAll(email));
                                            mail.setccAddresses(ccemail);

 
VaasuVaasu
You have to convert it back to list before adding to 'setToAddresses'.
Set <String> email = new Set<String>():
List <string> uniqueEmails = new LIst<String>();
mail.setToAddresses(uniqueEmails.addAll(email));

OR
Check if email is already in the list then do not add. This way you dont have to go back and forth using SET and LISTS
if(!email.contains(bdg.Contact__r.Email))
email.add(bdg.Contact__r.Email);
34213421
targetId is required when we have an email template correct? But I am setting that to false 
mail.setTreatTargetObjectAsRecipient(false);

Not sure what's missing. I will try the second approach too just in case
VaasuVaasu
Should have asked this earlier. 

Why are you setting both targetID and ToAddress? I think you dont have to add both. Since you are using template, I'd think you are using merge fields. So, keep targetID and remove toAddress.Email would go to the contact.
VaasuVaasu
Few Observations..
1. Is query on order is needed?
2.  Is it necessary to use ToAddress? 
Sample code , assuming you do not have to query Order object and use 'ToAdress'.
 
global void execute(Database.BatchableContext BC, List<OrderApi__Badge__c> scope){
     
		system.debug('scope'+scope.size());  
     for(Order_c fbdge : [select Id,Description__c,Type__c,Contact__c,
 			ontact__r.Date__c,Contact__r.FirstName,Contact__r.LastName,
        	Contact__r.Years__c,Contact__r.Email,Active__c,Type__r.Name
From Order__c where (Type__r.Name ='A' or Type__r.Name ='B') and OrderApi__Is_Active__c =true 
                                and Date__c = 2017-06-30
        			  			and Contact__r.Date__c = 2017-06-30 and Contact__r.Years__c<10.00
                                and (Contact__r.Cat__c <> ' Life')]){
                                          system.debug('create list');
                                     bge.add(fbdge);
                                     system.debug('Bge' +bge.add(fbdge));

                                      contact.put(fbdge.Contact__c, fbdge.Contact__r.Email);
                                  
                                      }
         List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMessage>();
        List<String> email = new List<String>();
         Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();    
         EmailTemplate templateId = [Select id from EmailTemplate where name = 'Test'];



     							
     
     for(String key : contact.keyset())
         {

            mail.setTargetObjectId(key);
            //mail.setTreatTargetObjectAsRecipient(false);
            mail.setTemplateID(templateId.Id); 
            mail.setSaveAsActivity(false); 
            mails.add(mail);
            system.debug(mail);
        }
                                                            
                                           
                         
     //	messages.add(mail);
        if(mails.size())>0{                        
         Messaging.sendEmail(mails);
          system.debug('mails');
            system.debug(mails.size());
               
        }              
        
}

 global void finish(Database.BatchableContext BC){
    }
}

 
34213421
I did try that too, since I do not need another for loop for Order. But in either case, the wierd part is I see different emails in each loop ( 11 different emails), but when I receive the emails  I see that 11 emails are sent to only the last recipient instead of 11 different people. Looks like the after coming out of the loop, I am missing something before sendEmail method
34213421
Looks like there is an errror saying already output.
Addresses=null;getCcAddresses=(already output);getTargetObjectId=003o000000VV8SGAA1;;getCcAddresses=(already output);getCharset=null;

 
VaasuVaasu
Can you send whole batch class? you are not using scope in execute method, which is not right.