Sending email from Apex looks very interesting and powerful feature at first glance however by time your application evolves to more complex structure or more data, chances are very high that you will be hitting many Apex email limit related errors. As Salesforce is not mass email sending tool, it makes sense to have limits on total number of outgoing emails per 24 hours.
At a time of writing this post, mainly there would be two types of error while sending email from Apex as explained below
- Transaction based errors
- We can send 100 emails per SingleEmailMessage
- Per day (24 hour) email limit
- We can send 1000 emails per day
One point to note here that we can send unlimited emails to internal users including community users. More details on email limits can be found here (Salesforce governor limits)
I have seen multiple questions on Salesforce developer community on how to resolve error related to sending email per 24 hour in Apex. Below solution has been used by me for a long time and it is working to send thousands of email per day without any problem.
Let’s see how we can resolve this error with below design.
Email tracking Custom Object
As we are talking about sending email from Apex code, that means it is very clear that we are ready for some coding. In this case as well, whenever we want to send email , we will create a temporary record in email tracking object (Temporary Object). This object could be child of Object which needs to send email. You may or may not need this object, it completely depends on your project situation. Only reason we need object is to trigger workflow rule and have some basic informations like Email address of users to whom email should be sent. Same object can be used for reporting purpose as well to know how many emails are sent from Apex and who were recipient.
Email recipients
This is tricky situation to resolve and this might be step because of which this solution may not work for you. Basically, even from Apex email, we need to set recipients in Email. So, instead of setting recipients in Apex email we need to set email addresses of users in temporary custom object. if we have many email recipients then we need to figure out maximum number of email fields in custom object and set email address in those fields. You may choose to have email field names as Email1, Email2 etc. We have limit of 500 fields in Enterprise and 800 fields in Unlimited edition anyway.
So till now, we have a custom Object which have information about all email addresses which needs to receive Email.
Use Workflow rule to send Emails
Now, we need to create a workflow rule on temporary custom Object saying “Whenever record is created” send email.
Till this point you may not notice how powerful this approach could be and question may be popping up in your mind that off-course we know about Workflow rule and it was not solving purpose that’s why we used Apex approach. Well, you are right but we are yet not done.
Unleashing Visualforce Email template hidden power
Whatever you can do with Apex, trigger, almost everything can be done in Visualforce email template. Again you may think that we cannot have apex controller behind it and therefore we cannot control much about output of Visualforce template. Not really; we have workaround here. We cannot have Apex controller for Visualforce Email template but we can have controller for Visualforce component and component can be easily embedded into template.
Lets run by example so that it would make more sense. Whenever any new Contact is created , Send email to newly created contact with information about all related contacts exists for that Account.
First we need to create Visualforce component, which will get AccountId and ContactId as input and display all related contacts in tabular format
global class GetSampleAccount{ public Id con {get;set;} public Id acc {get;set;} public Account accObj {get;set;} /** * get all sibling Contacts */ public List<Contact> getAllContact(){ if(acc != null) { accObj = [SELECT NAME FROM Account WHERE Id = : acc ]; return [SELECT Id, Name, FirstName, LastName FROM Contact Where AccountId =: acc ]; } return null; } }
Component Source code:
<apex:component controller="GetSampleAccount" access="global" > <apex:attribute name="contactId" type="Id" description="Id of the Contact" assignTo="{!con}"/> <apex:attribute name="accId" type="Id" description="Id of the Account" assignTo="{!acc}"/> Below are all related Contacts of Account - <u> {!accObj.Name} </u> <table style="border: 1px solid black;border-collapse: collapse;"> <thead> <th style="border: 1px solid black"> First Name </th> <th style="border: 1px solid black" > Last Name </th> </thead> <tbody> <apex:repeat value="{!allContact}" var="c" id="conRepeater"> <tr> <td style="border: 1px solid black" > {!c.FirstName} </td> <td style="border: 1px solid black" > {!c.LastName} </td> </tr> </apex:repeat> </tbody> </table> Thanks, Jitendra Zaa </apex:component>
In order to use any component in Visualforce template, its access must be “global”. We will be using above component in Visualforce email template. All heavy lifting is done in above Visualforce component and its controller class.
To create Visualforce email template,
- Search “Email Templates” in Quick find box.
- Click “New Template”
- Select type as “Visualforce”
- In next screen, select folder where it should be saved.
- Provide Template name
- Select “Available for use” checkbox
- In recipient Type, select “User”. It will not make any different to select any option because anyway email address will come from contact in my case and from temporary object in your case
- In Related to dropdown, Select Object type from where merge fields will be used. In our case we are using Contact. However in many of cases as discussed above, it might be temporary object from where email will be initiated using workflow rules.
Click on “Save” button after all above steps. Now its time to edit template. Click on “Edit Template” and add below code
<messaging:emailTemplate subject="Welcome to Account" recipientType="Contact" relatedToType="Contact"> <messaging:htmlEmailBody > <c:ParentAccount contactId="{!relatedTo.Id}" accId="{!relatedTo.Account.Id}" /> </messaging:htmlEmailBody> </messaging:emailTemplate>
And we are done with template. Only part remaining is to create a Workflow rule to send this email template to any email field present in Contact or temporary email tracking object.
How to send Visualforce page as an Email attachment
Sending attachment is also very easy. To send Visualforce as attachment, we need to use Component (move VF code to component) and embed it in “messaging:attachment” tag. You can refer this Salesforce documentation for sending attachment in Visualforce email template.
Lets say we want to send email and same email body should also be added as PDF file. We can change email template to look like
<messaging:emailTemplate subject="Welcome to Account" recipientType="Contact" relatedToType="Contact"> <messaging:htmlEmailBody > <c:ParentAccount contactId="{!relatedTo.Id}" accId="{!relatedTo.Account.Id}" /> </messaging:htmlEmailBody> <messaging:attachment filename="relatedContacts.pdf" renderAs="PDF"> <c:ParentAccount contactId="{!relatedTo.Id}" accId="{!relatedTo.Account.Id}" /> </messaging:attachment> </messaging:emailTemplate>
As we can see in above code, we are using “messaging:attachment” tag of Visualforce email template. and content of that tag is again same Visualforce component. Read more about Visualforce Email template here
Advantage of this solution
- Total email limit using workflow rule is 1000 x Total number of Salesforce or force.com user license.
- It is hybrid solution where we are using Visualforce components but at the same time we are using Workflow rules which can add more actions on same transaction or switch ON – Off of functionality anytime
- As you may create record (child record) each time so that workflow rule should execute, in that case you can have reporting capabilities as well. Currently there is no way to report on emails sent from Apex
- You can capture other fields as well for reporting purpose
- It also supports attachment which can have any dynamic content as discussed in this post
Trade-off
- You may need to create extra object/record which will use your organizational storage
- You should know in advance that at a time emails would be sent to how many users. If maximum 10 users needs to get email than temporary object needs to have 10 email fields, may be name of field could be Email1, Email2, Email3 and so on.
- If you are not interested in tracking emails sent then this temporary object is of no use and it will use Salesforce storage. So, we need to have scheduler in place to periodically cleanup temporary object.
- We cannot send existing attachment of record using this approach as explained in below section.
It is possible that they you may not like this solution because of some trade-off, please do share your suggestions so that we can discuss it.
Sending existing attachment (Vote this Idea) – failed attempt
I tried many ways to send attachment stored in Salesforce as an email however all attempt failed. Salesforce saves attachment in Base64 encoded form as a blob even if we are able to send attachments, its not readable. This is nearest I was able to go, still posting this code if someone try to figure it out how we can resolve this.
Visualforce component to send attachment
<apex:component access="global" Controller="GetAttachment"> <apex:attribute name="contactId" type="Id" description="Id of the Contact" assignTo="{!con}"/> <messaging:attachment filename="{!fileToAttach.Name}" renderAs="{!fileToAttach.ContentType}"> {!fileToAttach.Body} </messaging:attachment> </apex:component>
Yes, you are seeing right. I have used “messaging:attachment” tag in Visualforce component not in template and it works.
Apex class :
/** * This is controller class of component * */ public class GetAttachment { public Id con {get;set;} public Attachment fileToAttach { get{ return [SELECT ParentId, Name, ContentType, Body FROM Attachment Limit 1] ; } set; } }
Visualforce Email template code :
<messaging:emailTemplate subject="Welcome to Account" recipientType="Contact" relatedToType="Contact"> <messaging:htmlEmailBody > <c:ParentAccount contactId="{!relatedTo.Id}" accId="{!relatedTo.Account.Id}" /> </messaging:htmlEmailBody> <c:SendAttachment contactId="{!relatedTo.Id}" ></c:SendAttachment> </messaging:emailTemplate>
This Visualforce template is sending attachment but its not in readable format.
Leave a Reply