function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
jduhlsjduhls 

using DMLOptions in trigger - not working

any reason why the assignment rules won't trigger for new cases in this trigger:

 

trigger WebCaseAssign on Case (before insert) {
    integer i = 0;

    for (Case theCase:trigger.new) {
        if(theCase.Origin == 'Web') {
            Database.DMLOptions dmo = new Database.DMLOptions();
            dmo.assignmentRuleHeader.useDefaultRule = true;
            theCase.setOptions(dmo);   
        }
       
    i++;       
    }
   
}

 

I assume it's related to this issue/bug with Salesforce:

http://community.salesforce.com/sforce/board/message?board.id=Visualforce&message.id=10690

Message Edited by jduhls on 04-22-2009 11:31 AM
Best Answer chosen by Admin (Salesforce Developers) 
jduhlsjduhls

Here's the magical code that works (from the friendly folks at Salesforce Support).  The key was "theCase.setOptions(dmo);"  and only "after insert" apparently:

 

trigger WebCaseAssign on Case (after insert) {
    if(trigger.new.size() < 21) {
        for (Case theCase:trigger.new) {
            if(theCase.Origin == 'Web') {
                Database.DMLOptions dmo = new Database.DMLOptions();
                dmo.emailHeader.triggerUserEmail = true;
                dmo.assignmentRuleHeader.useDefaultRule = true;
                theCase.setOptions(dmo);
                Database.update(theCase);    
            }      
        }
    }
}

Message Edited by jduhls on 04-23-2009 02:26 PM
Message Edited by jduhls on 05-14-2009 08:21 AM
Message Edited by jduhls on 05-14-2009 09:56 AM

All Answers

werewolfwerewolf
I'm not sure it'll work in the context of a trigger, because by the time the trigger is run that assignment rule option is already set.  What's creating the case here?  You should turn on the assignment rules at the time of creation.
jduhlsjduhls

these are incoming from a synchronization with a local database that serves our customer portal where the cases are created.

 

I thought a "before/after insert" trigger was at the time of creation?  How much more "at the time of creation" can we get?

werewolfwerewolf
If it's coming from a sync with a local server then you're creating the cases via the API, so set the AssignmentRuleHeader when you're doing the API transaction instead.
jduhlsjduhls

Hm...the "replicator" is Relational Junction which currently uses Api 13.0. After speaking with Sesame, it can be config'd to trigger assignment rules but only globally and for ALL actions, not just new but updated too.  A disaster.

 

I am speaking with them now about doing an update to their software. I have also had to stop answering my deskphone as this has created a bit of a mess with our customer support department as cases are currently not getting routed.

 

I still don't understand why the trigger won't work.  Triggers fire for other items from the replication, just not this one...why wouldn't DMLOptions work in a trigger?

jduhlsjduhls

Updated trigger code (still not working):

 

trigger WebCaseAssign on Case (before insert) {
    string ruleId = [Select Name, Id From AssignmentRule  where name='Case Assignment Rule'].id;
    for (Case theCase:trigger.new) {
        if(theCase.Origin == 'Web') {
            Database.DMLOptions dmo = new Database.DMLOptions();
            dmo.emailHeader.triggerUserEmail = true;
            dmo.assignmentRuleHeader.useDefaultRule = false;
            dmo.AssignmentRuleHeader.assignmentRuleId = ruleId;
            theCase.setOptions(dmo);   
        }      
    }
}

werewolfwerewolf

Well, certainly one weird thing about your trigger is that you're not actually doing anything to the case itself -- you're just setting DMLOptions, but they only really apply after you make a DML statement (insert, update, etc.).

 

That said, you can't do an update on a case in a before insert.  You might be able to do it on after insert, or you could move this little block to an @future method and do your update there (a trivial update, maybe just update some bogus custom field, just so you can get your DML in).

 

Don't forget that you can't actually pass the cases themselves to an @future method, you have to pass their IDs instead.

jduhlsjduhls

Here's the magical code that works (from the friendly folks at Salesforce Support).  The key was "theCase.setOptions(dmo);"  and only "after insert" apparently:

 

trigger WebCaseAssign on Case (after insert) {
    if(trigger.new.size() < 21) {
        for (Case theCase:trigger.new) {
            if(theCase.Origin == 'Web') {
                Database.DMLOptions dmo = new Database.DMLOptions();
                dmo.emailHeader.triggerUserEmail = true;
                dmo.assignmentRuleHeader.useDefaultRule = true;
                theCase.setOptions(dmo);
                Database.update(theCase);    
            }      
        }
    }
}

Message Edited by jduhls on 04-23-2009 02:26 PM
Message Edited by jduhls on 05-14-2009 08:21 AM
Message Edited by jduhls on 05-14-2009 09:56 AM
This was selected as the best answer
ryanhcaryanhca

Thanks, it's good to see confirmation. BTW, you don't need to check for the trigger batch size if you manage your inserts better:

 

trigger WebCaseAssign on Case (after insert) { // curious why this only works 'after insert' List<Case> cases = new List<Case>(); for (Case theCase:trigger.new) { if(theCase.Origin == 'Web') { Database.DMLOptions dmo = new Database.DMLOptions(); dmo.emailHeader.triggerUserEmail = true; dmo.assignmentRuleHeader.useDefaultRule = true; theCase.setOptions(dmo); cases.add(theCase); } } update cases; }

 

 

 

OldDeadBugOldDeadBug

I get an error.

 

Here's the Code:

trigger LeadOwnerChange on Lead (after insert, before update, after update) { if (trigger.isAfter) { if (trigger.isInsert) { Database.DMLOptions dmo = new Database.DMLOptions(); dmo.assignmentRuleHeader.useDefaultRule= true; Lead[] Leads = new Lead[]{}; for (Lead L :trigger.new) { L.setOptions(dmo); Leads.add(L); } update Leads; }

}

...

}

 

and here's the error when creating a new lead:

 

"... execution of AfterInsert caused by: System.SObjectException: DML statment cannot operate on trigger.new or trigger.old: "

 

That's pretty straightforward - no DML statements on records in the trigger list, so I'm a little bit surprised that Salesforce would allow an exception to this in the case of Leads or Cases with setOptions values.

 

Is this process working for everyone else? Did I miss something in my own version? Are Cases different from Leads with regard to their 'sObjectivity' that they can be updated while they are being inserted?  Questions, questions, questions...

Also, the before and after triggers process before the Assignment Rules, so it would seem that setting the option during the before insert trigger should work. Alas.

 

ODB

ShrutiShruti

I am also geeting the error:

"execution of AfterInsert caused by: System.SObjectException: DML statment cannot operate on trigger.new or trigger.old:"

 

Can any one please tell how to resolve this error???

Its very urgent...

 

thanks,

Shruti

Ritesh AswaneyRitesh Aswaney

This should do it. Select and update rather than update the references in trigger.new

 

trigger test on Case (after insert) {
  
List<Id> caseIds = new List<Id>{};

        for (Case theCase:trigger.new) 
            caseIds.add(theCase.Id);
        
        List<Case> cases = new List<Case>{}; 
        for(Case c : [Select Id from Case where Id in :caseIds])
        {
            Database.DMLOptions dmo = new Database.DMLOptions();
 
            dmo.assignmentRuleHeader.useDefaultRule = true;
            c.setOptions(dmo);
            
            cases.add(c);
        }

        Database.upsert(cases);
}

sgorbatovsgorbatov

Hi

 

I resolve this error.

Write trigger where we check cases and collect its IDs.

If them present more than zerro call future method DMLOpt.autoAssign

trigger caseAssign4Apex on Case (after insert) {
  List<string> cids = new List<string>();
  if(trigger.new.size() < 21) {
    for (Case theCase:trigger.new)
      if(theCase.Origin == 'Web')
        cids.add(theCase.id);
  }
  if(cids.size()>0) DMLOpt.autoAssign(cids);
}

 This is our class with future method:

public with sharing class DMLOpt {
 @future public static void autoAssign(List<String> lst){
    List<Case> cases = [select id from Case where id IN :lst];
    if(cases!=null && cases.size()>0){
      Database.DMLOptions dmo = new Database.DMLOptions();
      dmo.assignmentRuleHeader.useDefaultRule = true;
      for(Case cs : cases){
        //dmo.emailHeader.triggerUserEmail = true;
        cs.setOptions(dmo);
      }
      update cases;
    }
  }
}

 It's really work.

gliustonecobragliustonecobra

It looks like this only works for inserts, though, eh? I've been trying for updates (trigger on after update), but the assignment rule I specify simply refuses to fire. The flow I'm expecting is:

 

1 - case is updated

2 - trigger after updated fires, catches that case and only does something if type changed (for example, this will prevent loops, and I only need it when type changes)

3 - from #2, if type changed, set DMO options to specify an assignment rule, and update the Case again

4 - It should loop back to #2, then it will move on, since type has not changed in step 2-3

5 - assignment rule I specified in #3 should still apply. It doesn't, though

Jeff MayJeff May

I got this to work on Lead Updates!!!!

 

There are a couple of pieces to making it work.

 

1) You need to protect against recursive triggers. (This step is stolen (borrowed) from other previous posts by people smarter than me)

 

Create a TriggerControl apex class to be used in the trigger:

 

public class TriggerControl {
    public static set<string> triggersInUse = new set<string>();

    @isTest
    static void testTriggerControl(){
        TriggerControl.triggersInUse.add('ThisIsATest');
    }
}

 

 

 

2) Create an after update trigger (maybe not, but that's where I got it to work)

 

trigger LeadAssignmentRulesOnUpdate on Lead (after update) {

    if (TriggerControl.triggersInUse.contains('LeadAssignRUpInUse')) {
    	return;
    } else {
    	TriggerControl.triggersInUse.add('LeadAssignRUpInUse');
    }
    
    Set<Id> lid = new Set<Id>();
    
    List<Lead> lup = new List<Lead>();
    
    for (Lead l : trigger.new){
    	lid.add(l.id);
    }
    
   Database.DMLOptions dmo = new Database.DMLOptions();
    dmo.assignmentRuleHeader.useDefaultRule = true;
                
                    
    for (Lead l : [select id from Lead where Id in :lid]){
    	l.setOptions(dmo);
        lup.add(l);    	
    }
    
    if (lup.size() > 0){
    	database.update(lup);
    }
}

 

I leave the test class for this as an exercise for the reader.

DanSaiDanSai

Jeff - That's a neat trigger, and this kind of fits in with some process requirements that I've been working on. I've dropped this in a sandbox and it works like a dream, but idealy would want the trigger to fire when a checkbox is updated on a certain lead type.

 

I'm pretty green with Apex code and have tried making some changes, but to no avail. Do you have any suggestions on how to direct the trigger to work on selective updates?

 

Thanks!

Jeff MayJeff May

welcome to the world of triggers.  With great power, comes great responsibility (yeah, I stole that from somewhere too)

 

in your trigger loop, you can compare a field value:

 

for (Lead l : trigger.new){

 

   if (l.Field__c == true) {

       // I care about this one

   }

}

 

or check to see if a field has changed value:

 

for (Lead l : trigger.new){

 

   if (l.Field__c != trigger.oldMap.get(l.Id).Field__c) {

       // I care about this one

   }

}

 

Sajjan Gupta 14Sajjan Gupta 14
Make sure you use Database.update method when you use  Database.DMLOptions dmo = new Database.DMLOptions();