You want to write a trigger that creates a new record as part of its processing logic; however, that record may then cause another trigger to fire, which in turn causes another to fire, and so on. You don't know how to stop that recursion.
Solution
Use a static variable in an Apex class to avoid an infinite loop. Static variables are local to the context of a Web request (or test method during a call to runTests()), so all triggers that fire as a result of a user's action have access to it.
For example, consider the following scenario: frequently a Salesforce.com user wants to follow up with a customer the day after logging a call with that customer. Because this is such a common use case, you want to provide your users with a helpful checkbox on a task that allows them to automatically create a follow-up task scheduled for the next day.
You can use a before insert trigger on Task to insert the follow-up task, but this, in turn, refires the before insert trigger before the follow-up task is inserted. To exit out of this recursion, set a static class boolean variable during the first pass through the trigger to inform the second trigger that it should not insert another follow-up task:
Note
For this Apex script to work properly, you first must define a custom checkbox field on Task. In this example, this field is named Create_Follow_Up_Task__c.
The following code defines the class with the static class variable:
public class FollowUpTaskHelper {
// Static variables are local to the context of a Web request
// (or testMethod during a runTests call)
// Therefore, this variable will be initialized as false
// at the beginning of each Web request which accesses it.
private static boolean alreadyCreatedTasks = false;
public static boolean hasAlreadyCreatedFollowUpTasks() {
return alreadyCreatedTasks;
}
// By setting the variable to true, it maintains this
// new value throughout the duration of the request
// (or testMethod)
public static void setAlreadyCreatedFollowUpTasks() {
alreadyCreatedTasks = true;
}
public static String getFollowUpSubject(String subject) {
return 'Follow Up: ' + subject;
}
}
The following code defines the trigger:
trigger AutoCreateFollowUpTasks on Task (before insert) {
// Before cloning and inserting the follow-up tasks,
// make sure the current trigger context isn't operating
// on a set of cloned follow-up tasks.
if (!FollowUpTaskHelper.hasAlreadyCreatedFollowUpTasks()) {
List<Task> followUpTasks = new List<Task>();
for (Task t : Trigger.new) {
if (t.Create_Follow_Up_Task__c) {
// False indicates that the ID should NOT
// be preserved
Task followUpTask = t.clone(false);
System.assertEquals(null, followUpTask.id);
followUpTask.subject =
FollowUpTaskHelper.getFollowUpSubect(followUpTask.subject);
if (followUpTask.ActivityDate != null) {
followUpTask.ActivityDate =
followUpTask.ActivityDate + 1; //The day after
}
followUpTasks.add(followUpTask);
}
}
FollowUpTaskHelper.setAlreadyCreatedFollowUpTasks();
insert followUpTasks;
}
}
The following code defines the test methods:
// This class includes the test methods for the
// AutoCreateFollowUpTasks trigger.
public class FollowUpTaskTester {
private static integer NUMBER_TO_CREATE = 4;
private static String UNIQUE_SUBJECT =
'Testing follow-up tasks';
static testMethod void testCreateFollowUpTasks() {
List<Task> tasksToCreate = new List<Task>();
for (Integer i = 0; i < NUMBER_TO_CREATE; i++) {
Task newTask = new Task(subject = UNIQUE_SUBJECT,
ActivityDate = System.today(),
Create_Follow_Up_Task__c = true );
System.assert(newTask.Create_Follow_Up_Task__c);
tasksToCreate.add(newTask);
}
insert tasksToCreate;
System.assertEquals(NUMBER_TO_CREATE,
[select count()
from Task
where subject = :UNIQUE_SUBJECT
and ActivityDate = :System.today()]);
// Make sure there are follow-up tasks created
System.assertEquals(NUMBER_TO_CREATE,
[select count()
from Task
where subject =
:FollowUpTaskHelper.getFollowUpSubject(UNIQUE_SUBJECT)
and ActivityDate = :System.today()+1]);
}
static testMethod void assertNormalTasksArentFollowedUp() {
List<Task> tasksToCreate = new List<Task>();
for (integer i = 0; i < NUMBER_TO_CREATE; i++) {
Task newTask = new Task(subject=UNIQUE_SUBJECT,
ActivityDate = System.today(),
Create_Follow_Up_Task__c = false);
tasksToCreate.add(newTask);
}
insert tasksToCreate;
System.assertEquals(NUMBER_TO_CREATE,
[select count()
from Task
where subject=:UNIQUE_SUBJECT
and ActivityDate =:System.today()]);
// There should be no follow-up tasks created
System.assertEquals(0,
[select count()
from Task
where subject=
:FollowUpTaskHelper.getFollowUpSubject(UNIQUE_SUBJECT)
and ActivityDate =:(System.today() +1)]);
}
}
See Also
No comments:
Post a Comment