2

Here's my code stripped of the usual professional infrastructure for illustration:

trigger LeadTrigger on Lead (after insert, after update, before insert, before update) {

    if ( ! Util.byPassAllTriggers) {
        if (Trigger.isBefore && Trigger.isInsert ) {
             
        } else if (Trigger.isBefore && Trigger.isUpdate ) {
            for (Lead l:Trigger.new){lc.set(l);}
        } else if (Trigger.isAfter && Trigger.isInsert ) {
        } else if (Trigger.isAfter && Trigger.isUpdate ) {
            for (Lead l:Trigger.new){lc.set(l);}
        }
    }
}

And here's the usual run lead assignment code

public with sharing class lc{
public static void set(Lead l){
Database.DMLOptions dmlOpts = new Database.DMLOptions();
        AssignmentRule AR = new AssignmentRule();
        AR = [select id from AssignmentRule where SobjectType = 'Lead' and Active = true limit 1];
        //Creating the DMLOptions for "Assign using active assignment rules" checkbox
        //dmlOpts.assignmentRuleHeader.assignmentRuleId= AR.id;
        dmlOpts.assignmentRuleHeader.UseDefaultRule = true;
l.setOptions(dmlOpts);
}}
        

Again for illustration, I'm just trying to trigger the autoassign rule in the lead trigger on the leads we've updated. I've tried the code in both these places and it runs, but the assignment doesn't kick it. If I do this outside the Lead and then trigger it, it works. So I must not be putting it in the right place or doing it correctly. What's the right place to set this option?

This guy just gave up. Need help with Apex Trigger to run Lead Assignment Rules

Here's an attempt I did which seems to have worked, but I don't fully understand its implications

trigger LeadTrigger on Lead (after insert, after update, before insert, before update) {

    if ( ! Util.byPassAllTriggers) {
        if (Trigger.isAfter && Trigger.isUpdate ) {
            handler.handle(Trigger.new)
        }
    }
}
public class handler{
public class handle(Lead[] ls){
if ( ! System.isBatch() && ! System.isFuture() )  {
            reassign(ls); 
        }
}}

@future
public static void reassign(Set<Id> ls){
        List<Lead> lUpdList = [SELECT Id FROM Lead WHERE Id IN :ls];
      for (Lead l:lUpdList){
        AssignmentRule AR = new AssignmentRule();
        AR = [select id from AssignmentRule where SobjectType = 'Lead' and Active = true limit 1];
        Database.DMLOptions dmlOpts = new Database.DMLOptions();
        dmlOpts.assignmentRuleHeader.assignmentRuleId= AR.id;
        l.setOptions(dmlOpts);
        
      }

      update(lUpdList);
    }
}

I did't understand the recursive function because the recursiveUpdate was, at the very highest, in the scope of the trigger and when you call Database.update, it would be a new boolean in a new instance of the trigger, which means the guard would guard nothing because it's above the reassignment in the new instance. The only way I could understand it is if the boolean was above all the triggers and would prevent other triggers from firing their runAssignmentRules because a prior trigger already made it true. Anyway, my solution still feels to good to be true, so I'm going to keep testing.

1 Answer 1

5

The assignment rule that will run is fixed at the instant the DML operation starts. In other words, by the time your trigger has started running, it is far too late to set an assignment rule. You would instead need to make a recursive update. Further, there are known limitations on the sObject.setOptions method; it would be recommended to use the Database.update(sObjects, dmlOptions) method. This also allows us to bulkify the logic.


static Boolean recursiveUpdate = false;
public static void runAssignmentRules(Lead[] records) {
  if(recursiveUpdate) {
    return;
  }
  Database.DmlOptions dmlOptions = new Database.DmlOptions();
  dmlOpts.assignmentRuleHeader.UseDefaultRule = true;
  recursiveUpdate = true;
  Database.update(records, dmlOptions);
  recursiveUpdate = false;
}

First, we check if we're already running recursively, and if so, we return early. Next, we set up the DML options we want to use and set the recursive flag to avoid infinite loops. After that, we run the DML operation. Finally, we reset the recursive flag in order to avoid disabling our trigger logic for legitimate repeat calls (partial save logic, more than 200 records in a transaction, multiple DML operations in the same transaction, etc).

Also, this logic needs to be run in an after update/after insert context. In a before insert/before update context, the record either doesn't yet have an ID, or will be locked against further DML operations.

3
  • I have two questions. 1. What context is this method in? Do I have to have that boolean above the trigger? Second. You’re doing a database update. I know I can’t do update lead in the after trigger. Can I do it with this database update thing? Jun 14, 2022 at 22:28
  • @HenriettaMartingale 1. A trigger helper class. 2. No, it goes in a class (e.g. lc). And yes, you can recursively update a record in an after trigger. Of course, we have to use the "recursion blocking" technique to avoid an infinite recursion.
    – sfdcfox
    Jun 14, 2022 at 22:58
  • I get it. The helper doesn't get reinstantiated when the trigger loops again so the old Boolean remains alive Jun 15, 2022 at 13:04

You must log in to answer this question.

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