I have an object that gets its record type changed by the user and based on that change I want to create some new records of another object.
So in the trigger I compare Trigger.old to Trigger.new and voilà - I have my condition and everything works fine. That is until I introduce some workflow rules to the party that update the record itself during the record update. This leads to duplicated records.
The Apex Developer Guide has an interesting paragraph under "Triggers and Order of Execution".
Trigger.old contains a version of the objects before the specific update that fired the trigger. However, there is an exception. When a record is updated and subsequently triggers a workflow rule field update, Trigger.old in the last update trigger won’t contain the version of the object immediately prior to the workflow update, but the object before the initial update was made.
So the workflow rule fires the trigger a second time but since the record type is not changed (and will not be changed) yet, the condition is met again and thous I get another copy of the record I want.
My current workaround is a static variable and I only insert my new records the first time my criteria are met. This works as long as I'm not going to (for whatever reason) update my record twice in a row - because then I would get the trigger only once, not twice. So apparently this works but I think it is a pretty bad "solution".
Has anybody else stumbled across this problem? How did you fix or work around it?
UPDATE:
I did a few tests to help others and myself to understand the problem better. So here are my findings explained through a simple example.
Case 1: Update on object A (one update, one increment)
- Trigger on object A fires --> Trigger increments field on object B (+1)
Workflow rule is added to object A that updates itself.
Case 2: Update on object A (one update, two increments)
- Trigger on object A fires --> Trigger increments field on object B (+1)
- Workflow trigger on object A fires --> Trigger increments field on object B (+1)
Case 3: Update on object A - with static variable firstRun set to false (one update, one increment)
- Trigger on object A fires --> firstRun true, set to false --> Trigger increments field on object B (+1)
- Workflow trigger on object A fires --> firstRun false, no increment on object B
Trigger logic is introduced on object B that does an update on object A if it hasn't been done before.
Case 4: Update on object A (two updates, four increments)
- Trigger on object A fires --> Trigger increments field on object B (+1)
- Workflow trigger on object A fires --> Trigger increments field on object B (+1)
- Object B triggers update on object A
- Trigger on object A fires --> Trigger increments field on object B (+1)
- Workflow trigger on object A fires --> Trigger increments field on object B (+1)
Case 5: Update on object A - with static variable firstRun set to false (two updates, one increment)
- Trigger on object A fires --> firstRun true, set to false --> Trigger increments field on object B (+1)
- Workflow trigger on object A fires --> firstRun false, no increment on object B
- Object B triggers update on object A
- Trigger on object A fires --> firstRun still false --> nothing happens
So we want to make sure that our increment count is the same as our update count. This only works for case 1 (no workflow rules at all) and case 3 (workflow rules but no further triggering of the same "source" update). What we really want is to have N updates and N increments - they should always be the same. But that isn't the case for example number 5.
I know this case is probably very rare but in our orgs there's a lot of ping-pong going on at times - hence the problem. You can easily test the behaviour with two updates directly after one another (or the first one triggering the second one).
For now I have no solution beside the static variable workaround plus preventing multiple updates in the same execution context, if possible - circumventing the problem as good as I can.
As one of the respondents pointed out: If you're in control of the triggering then you can reset firstRun in between the updates. If you're not, you're out of luck. Sometimes Salesforce is making our lives really REALLY hard.