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
Mayank.msMayank.ms 

Apex Trigger sent email Twice - How to prevent it ?

I have a opportuity trigger that sent an email before update the opporunity. Email sent properly but it send twice. I have also tried to update customer field and checked it in the condition but not solved. We have mulitiple triggers on opportunities, so may be possible it executes same time. Please help me on it.
trigger billingSubscriptionEmail on Opportunity (before update) {

List<Addon_Opportunity__c> addOppList = new List<Addon_Opportunity__c>([SELECT Name,Amount__c,Quantity__c FROM Addon_Opportunity__c where Id__c IN :Trigger.newMap.keySet()]); 

List<Contact> conList = new List<Contact>([select Email from Contact where Id In (select ContactId from OpportunityContactRole where OpportunityId IN :Trigger.newMap.keySet())]); 

 Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
  
    for(Opportunity opp : trigger.new){
    
            if(opp.StageName=='Closed Won' && Trigger.oldMap.get(opp.Id).StageName!='Closed Won'){
                
                for(Contact c:conList){
               
                 String[] toAddresses = new String[] {'ahmeds@webgility.com'};
                 String billingCycle;
                 String promo='';
                   
                    mail.setToAddresses(toAddresses );   
                    mail.setSubject('New Subscription - '+opp.Accounting_Platform__c+' - '+c.Email);   
                   if(opp.Billing_Cycle__c=='Monthly'){
                     billingCycle = 'Monthly';
                   }else{
                      billingCycle = 'Annual';  
                   }
                   
                  if(opp.promo_code__c!=null){
                      promo = opp.promo_code__c;
                  }else{
                      promo = 'None';
                  }
                    String template = 'Billing & Subscription Info:\n\n';
                     template+= '----------------------------------\n';
                     template+= billingCycle+' Recurring Amount: $'+opp.Recurring_Revenue__c+'\n';
                     template+= 'Plan: '+opp.Plan__c+'\n';
                     template+= 'Account Email: '+c.Email+'\n';
                     template+= 'Total Upfront Payment: $'+ opp.One_Time_Fee__c+'\n'; 
                     template+= '----------------------------------\n';
                     for(Addon_Opportunity__c addon :addOppList){
                        
                         template+= addon.Quantity__c+' '+addon.Name+' : $'+addon.Amount__c;
                         template+= '\n';
                         
                    }
                    template+= '\n';
                    template+= 'One Time Fee: $'+opp.One_Time_Fee__c+'\n';
                    template+= '----------------------------------\n';
                    template+= 'Recurring billing start date :'+opp.Purchase_Date__c+'\n';
                    template+= 'Promo Code :'+promo;
                    List<String> args = new List<String>();
               String formattedHtml = String.format(template, args);
               
                mail.setPlainTextBody(formattedHtml);
                
              
                Messaging.SendEmail(new Messaging.SingleEmailMessage[] {mail});
              
                
              }  
           }   

    }


}



Thanks in advance
Best Answer chosen by Mayank.ms
Amit Chaudhary 8Amit Chaudhary 8
Hi ,
It look like your trigger below recursive. Please check below post how to resolve this issue
http://amitsalesforce.blogspot.in/2015/03/how-to-stop-recursive-trigger-in.html

Solution :-
you can create a class with a static Boolean variable with default value true. In the trigger, before executing your code keep a check that the variable is true or not. Once you check make the variable false.

Step 1:- create velow class
public class OpportunityTriggerHandler
{
     public static Boolean isFirstTime = true;
}
Step 2:- Update your trigger like below :-
trigger billingSubscriptionEmail on Opportunity (before update) {

if(OpportunityTriggerHandler.isFirstTime)
{
OpportunityTriggerHandler.isFirstTime = false;

	List<Addon_Opportunity__c> addOppList = new List<Addon_Opportunity__c>([SELECT Name,Amount__c,Quantity__c FROM Addon_Opportunity__c where Id__c IN :Trigger.newMap.keySet()]); 

	List<Contact> conList = new List<Contact>([select Email from Contact where Id In (select ContactId from OpportunityContactRole where OpportunityId IN :Trigger.newMap.keySet())]); 

	 Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
	  
		for(Opportunity opp : trigger.new){
		
				if(opp.StageName=='Closed Won' && Trigger.oldMap.get(opp.Id).StageName!='Closed Won'){
					
					for(Contact c:conList){
				   
					 String[] toAddresses = new String[] {'ahmeds@webgility.com'};
					 String billingCycle;
					 String promo='';
					   
						mail.setToAddresses(toAddresses );   
						mail.setSubject('New Subscription - '+opp.Accounting_Platform__c+' - '+c.Email);   
					   if(opp.Billing_Cycle__c=='Monthly'){
						 billingCycle = 'Monthly';
					   }else{
						  billingCycle = 'Annual';  
					   }
					   
					  if(opp.promo_code__c!=null){
						  promo = opp.promo_code__c;
					  }else{
						  promo = 'None';
					  }
						String template = 'Billing & Subscription Info:\n\n';
						 template+= '----------------------------------\n';
						 template+= billingCycle+' Recurring Amount: $'+opp.Recurring_Revenue__c+'\n';
						 template+= 'Plan: '+opp.Plan__c+'\n';
						 template+= 'Account Email: '+c.Email+'\n';
						 template+= 'Total Upfront Payment: $'+ opp.One_Time_Fee__c+'\n'; 
						 template+= '----------------------------------\n';
						 for(Addon_Opportunity__c addon :addOppList){
							
							 template+= addon.Quantity__c+' '+addon.Name+' : $'+addon.Amount__c;
							 template+= '\n';
							 
						}
						template+= '\n';
						template+= 'One Time Fee: $'+opp.One_Time_Fee__c+'\n';
						template+= '----------------------------------\n';
						template+= 'Recurring billing start date :'+opp.Purchase_Date__c+'\n';
						template+= 'Promo Code :'+promo;
						List<String> args = new List<String>();
				   String formattedHtml = String.format(template, args);
				   
					mail.setPlainTextBody(formattedHtml);
					
				  
					Messaging.SendEmail(new Messaging.SingleEmailMessage[] {mail});
				  
					
				  }  
			   }   

		}


}

}
Please let us know if this will help you

Thanks
Amit Chaudhary

All Answers

William TranWilliam Tran
If you really don't want multiple emails even if there are multiple records then move the

Messaging.SendEmail(new Messaging.SingleEmailMessage[] {mail});

outside of the for loop.

Thx.
Mayank.msMayank.ms
Thanks William for the reply!

But when we put it outside the for loop its gives the error.

"Sandbox: Developer script exception from Webgility : billingSubscriptionEmail : billingSubscriptionEmail: execution of BeforeUpdate caused by: System.EmailException: SendEmail failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Missing target address (target, to, cc, bcc): [] Trigger.billingSubscriptionEmail: line 61, column 1"

Now i have put it outside the conList loop
 for(Contact c:conList){
---

But still getting 2 emails




 
William TranWilliam Tran
All your declaration has to be outside too (out of all loops) , which you so do in the first place

String[] toAddresses = new String[] {'ahmeds@webgility.com'};
String billingCycle;
String promo='';

why declare them in a loop?

"Now i have put it outside the conList loop
 for(Contact c:conList){"

Outside of ALL loops: that is outside of this loop: for(Opportunity opp : trigger.new)

Again, this is back to you want to do.

Do you want 1 email total?  Does that make sense.  if yes, then why are you even looping? 

What are your business rules?

Thx.
 
Amit Chaudhary 8Amit Chaudhary 8
Hi ,
It look like your trigger below recursive. Please check below post how to resolve this issue
http://amitsalesforce.blogspot.in/2015/03/how-to-stop-recursive-trigger-in.html

Solution :-
you can create a class with a static Boolean variable with default value true. In the trigger, before executing your code keep a check that the variable is true or not. Once you check make the variable false.

Step 1:- create velow class
public class OpportunityTriggerHandler
{
     public static Boolean isFirstTime = true;
}
Step 2:- Update your trigger like below :-
trigger billingSubscriptionEmail on Opportunity (before update) {

if(OpportunityTriggerHandler.isFirstTime)
{
OpportunityTriggerHandler.isFirstTime = false;

	List<Addon_Opportunity__c> addOppList = new List<Addon_Opportunity__c>([SELECT Name,Amount__c,Quantity__c FROM Addon_Opportunity__c where Id__c IN :Trigger.newMap.keySet()]); 

	List<Contact> conList = new List<Contact>([select Email from Contact where Id In (select ContactId from OpportunityContactRole where OpportunityId IN :Trigger.newMap.keySet())]); 

	 Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
	  
		for(Opportunity opp : trigger.new){
		
				if(opp.StageName=='Closed Won' && Trigger.oldMap.get(opp.Id).StageName!='Closed Won'){
					
					for(Contact c:conList){
				   
					 String[] toAddresses = new String[] {'ahmeds@webgility.com'};
					 String billingCycle;
					 String promo='';
					   
						mail.setToAddresses(toAddresses );   
						mail.setSubject('New Subscription - '+opp.Accounting_Platform__c+' - '+c.Email);   
					   if(opp.Billing_Cycle__c=='Monthly'){
						 billingCycle = 'Monthly';
					   }else{
						  billingCycle = 'Annual';  
					   }
					   
					  if(opp.promo_code__c!=null){
						  promo = opp.promo_code__c;
					  }else{
						  promo = 'None';
					  }
						String template = 'Billing & Subscription Info:\n\n';
						 template+= '----------------------------------\n';
						 template+= billingCycle+' Recurring Amount: $'+opp.Recurring_Revenue__c+'\n';
						 template+= 'Plan: '+opp.Plan__c+'\n';
						 template+= 'Account Email: '+c.Email+'\n';
						 template+= 'Total Upfront Payment: $'+ opp.One_Time_Fee__c+'\n'; 
						 template+= '----------------------------------\n';
						 for(Addon_Opportunity__c addon :addOppList){
							
							 template+= addon.Quantity__c+' '+addon.Name+' : $'+addon.Amount__c;
							 template+= '\n';
							 
						}
						template+= '\n';
						template+= 'One Time Fee: $'+opp.One_Time_Fee__c+'\n';
						template+= '----------------------------------\n';
						template+= 'Recurring billing start date :'+opp.Purchase_Date__c+'\n';
						template+= 'Promo Code :'+promo;
						List<String> args = new List<String>();
				   String formattedHtml = String.format(template, args);
				   
					mail.setPlainTextBody(formattedHtml);
					
				  
					Messaging.SendEmail(new Messaging.SingleEmailMessage[] {mail});
				  
					
				  }  
			   }   

		}


}

}
Please let us know if this will help you

Thanks
Amit Chaudhary
This was selected as the best answer
Mayank.msMayank.ms
Thank you very much Amit,

You are great :). Its working fine.

 
Leena Wani 21Leena Wani 21
Hey Do we need to create a test class well ?