Saturday, August 14, 2010

Triggers, Workflows, and Salesforce's Order of Execution

One caveat that is easy to overlook while developing on the SalesForce.com platform is the fact that when a piece of code you've written executes, it is solely, if ever, the only thing going on. This can cause a developer headaches and can affect many areas of a SalesForce Organization, both at the time of writing the code and in the future. In this post, I want to focus specifically on how Apex Triggers interact with Workflows.

Let’s say that you have an Apex Trigger that runs before an update on the Contact object. This Trigger will send an email to the Contact to alert them of a change made to their record, The Trigger may look like the following:

trigger NotifyContactOfChange on Contact (before update) {
for(Contact triggered : Trigger.new){ // Get affected Contacts
// Add code to send email here ...
}
}


So nothing new here, it’s just a simple Apex Trigger which fires off an email every time a change is made, and it should work just fine. Now let’s say that we also want to add a Workflow to Contact with similar functionality. To keep it simple, lets just say that this hypothetical Workflow fires whenever a change to Contact.MobilePhone is made and sets the Contact.Phone field to this new value.

Now that everything is set up, lets run through a scenario that causes the Trigger to not work as intended. Assume that you have a Contact record for “John Doe” and he has just given you his new mobile phone number. You look up his Contact record and make the change. Now, you are expecting two things to happen, (1) John Doe will receive an email alerting him of this change (via the Trigger) and (2) the Contact’s Phone field will be updated to reflect the MobilePhone field (via the Workflow). Here’s where things go wrong, John Doe will not receive one email, he will receive two. Why is that? While at first it may not seem obvious, it is actually very simple if you take a look at SalesForce’s Order of Execution, specifically numbers 1, 7, 10, and 11 on the list.

1. Executes all before triggers.

7. Executes workflow rules.

10. If there are workflow field updates, updates the record again.

11. If the record was updated with workflow field updates, fires before and after triggers one more time (and only one more time).


Notice that before triggers execute at number 1, and workflows at number 7. The caveat comes when you look at number 11, which says that triggers will fire once more if there were Workflow updates. If you refer to the example Trigger code above, you’ll notice that the email will be sent as long as Trigger.new is not empty, meaning that if a Contact record is updated in such a way that the Workflow fires (updating MobilePhone), the email will be sent twice, once at execution stage number 1, and again at execution stage 11. The consequences in our example are more annoying than anything else, but consider what would happen if the Trigger performed other actions, such as creating or deleting records. The effects could be well beyond merely ‘annoying’.

A simple fix to this problem would be to create a static variable which detects whether the email has been sent. Static variables cannot be declared directly in a Trigger, so an outside class would have to be created. Here’s the updated code:

public class trackEmail {

public static Boolean sentEmail = false; // Tracks whether email has been sent
}

trigger NotifyContactOfChange on Contact (before update) {
for(Contact triggered : Trigger.new){ // Get affected Contacts
if(!sentEmail){ // Email has not been sent

// Add code to send email here ...

trackEmail.sentEmail = true; // Set static variable

}
}
}



The only downside to this solution is that an outside class has to be created to accommodate the sentEmail variable. Other than that, adding a static variable to track whether or not a Trigger has fired is simple and should be easy to implement in most Orgs.

More on Apex static variables can be found at Salesforce's developer docs.

4 comments:

  1. That is an great example and well explained

    -ashish
    www.salesforcegeek.com

    ReplyDelete
  2. Here i find the exact code which i need to fix the problem in my development activity. I have a similar issue and this post guide me properly to how to fix this error. Thanks! full service digital marketing agency

    ReplyDelete
  3. Are this execution can be done f I put it in online store website?
    Maybe it can help the buyer to know how much they buy and it will make them easier to count how much they spend in the store.
    Thank you!

    ReplyDelete