3

I'm playing around in my Dev org and am using a trigger to send a basic e-mail to System Admins when an account is created as follows:

private static void doNotifyAdmin(List<Account> acc) {
  Id p = [SELECT Id FROM Profile WHERE Name = 'System Administrator'].Id;
  List<String> toAddresses = new List<String>();

  for (User u : [SELECT Email FROM User WHERE ProfileId = :p]) {
    toAddresses.add(u.Email);
  }

  for (Account a : acc) {
    Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
    email.setSubject('New Account Created!');
    email.setToAddresses(toAddresses);
    email.setHTMLBody('The account: ' + a.Name + ' was added to the system!');

    Messaging.SendEmailResult[] r = Messaging.sendEmail(
      new Messaging.SingleEmailMessage[] {
        email
      }
    );
  }
}

This all works fine, but I was wondering whether or not I'm doing it to best practice, particularly in the for loop sending the SingleEmailMessage.

In debug logs I noticed the following line:

Number of Email Invocations: 1 out of 10

As I'm the only account in the dev org, this isn't a big deal but if I were to send e-mails to 20 people in this way, for instance, would this fail? Or would it be fine given I'm querying addresses to send to in the toAddresses list?

Just curious to know if I'm engaging in best practices here!

Thanks!

4 Answers 4

4

If I were the Sysad, I would want to only get a summary of all Accounts created once per day (or maybe once per x hours) rather than be spammed every time an Account was created.

In this model, you would use a scheduled APEX class to run every x hours, looking for Accounts where some field has_sysad_been_notified__c is false. Collect all the account names into the body of the email, send a message whose subject contains the number of accounts, and then update the Account's has_sysad_been_notified__c to true. Alternatively, you could use Tasks under the Account to record whether notification has been sent and when (probably a better idea the more I think about it)

In all cases, when using apex outbound email, reserve how many you intend to send and handle any exceptions with a retry model (where the scheduled class comes in useful). Orgs tend to have many outbound email processes over time and it is easy to hit the daily limit.

4
  • To that effect, why not just use Scheduled Reports and omit Apex altogether?
    – Adrian Larson
    Commented Dec 17, 2015 at 18:02
  • so true - sometimes one sees Apex as the hammer to all problems :-) ; only issue is that scheduled reports can run at most once per day
    – cropredy
    Commented Dec 17, 2015 at 18:06
  • Both of those approaches (Schedule Apex or Reports) would also avoid the 1000 Email Limit in Apex per day which is very important. Commented Dec 17, 2015 at 20:14
  • To be honest I was thinking about this, this is just me playing with stuff in a Dev org and the idea of best practise for sending e-mails came to mind. What I've done thus far really could be done in a Workflow. So this is a much better use case.
    – Dan Jones
    Commented Dec 21, 2015 at 9:10
6

According to the Salesforce Limits Quick Reference Guide the limit for SingleEmailMessage is 100 emails per SingleEmailMessage and 1000 emails per day. Also it says when specifying email addresse in addressesTo it counts against this limit but when you use setTargetObjectId it does not count against the limit.

By using setTargetObjectId you should be safe with your approach.

4
  • The trigger would only fire on insert currently, would that still cause a problem if, say, Accounts were mass loaded (after 50, as you say?).
    – Dan Jones
    Commented Dec 17, 2015 at 11:09
  • @Poet i rephrased my answer after having a look into the limit documentation. Commented Dec 17, 2015 at 12:11
  • setTargetObjectId only ignores the limit if you set it to User, no?
    – Adrian Larson
    Commented Dec 17, 2015 at 15:06
  • yes. I think limits are ignored by default on leads and contacts but for users you have to specify them in setTargetObjectId. Commented Dec 17, 2015 at 15:23
2

Our org has a class for sending emails which uses a number of factors to evaluate which email sending option to use given the parameters it is being passed.

E.g. if your TargetObjectIds store activities, setActivityAs defaults to true, and if you use singleEmailMessage, the system automatically inserts each of these activities (tasks) individually, which can cause you to hit governor limits if you have triggers on task inserts, but if you set the attribute to FALSE, and manually insert the tasks yourself, you lose HTMLTracking if you choose to use that at some point with a template. So there are trade-offs, and what you use depends on what information you need, how many emails you are sending, and what the emails will look like.

With what you've shown though, if I were you, I'd look into setting up a template instead of setHTMLBody (the template can be HTML or VF), so you can use setTemplateId instead of using setHTMLBody, and that would allow you to use MassEmailMessage instead, which is better for high volume sending. But massEmailMessage doesn't allow use of the setHTMLBody attribute. In the template you can put {!Account.Name} and pass the AccountId values as WhatIds, which will allow the template to populate that information for the user.

Long story short - there's a lot to consider if you plan on sending more emails than just this in the future. Your best bet is to read sendEmail(), singleEmailMessage(), and massEmailMessage() and create a spreadsheet of what the latter two can and cannot do, and build an infrastructure to handle and process the different types accordingly.

1
List<Messaging.SingleEmailMessage> lstSingleEmailMessage = new List<Messaging.SingleEmailMessage>();
for (Account a : acc) {
    Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
    email.setSubject('New Account Created!');
    email.setToAddresses(toAddresses);
    email.setHTMLBody('The account: ' + a.Name + ' was added to the system!');
    lstSingleEmailMessage.add(email);    
}
Messaging.SendEmailResult[] r = Messaging.sendEmail(lstSingleEmailMessage);

In the above method, a list of Messaging.SingleEmailMessage is declared before for loop and the same is populated with the Messaging.SingleEmailMessage created inside for loop. Outside of for loop the list of Messaging.SingleEmailMessage is sent using Messaging.sendEmail which takes list of Messaging.SingleEmailMessage and counts as a single invocation.

Hope it helps.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .