You need to sign in to do that
Don't have an account?

Urgent: Lead Assignment Rule Trigger recursively updates itself and fails
I need a way to apply lead assignment rules whenever the lead score goes above 99 and the lead review status is complete.
Here is my trigger code:
trigger ApplyLeadAssignmentRules on Lead (before update, before insert) {
for (Lead l:trigger.new) {
Decimal score = l.Score__c;
String review_status = l.Review_Status__c;
if(score == null) score = 0.0;
if(review_status == null) review_status = '';
if( (score >= 100) && (review_status.equalsIgnoreCase('Complete')) ){
Database.DMLOptions dmo = new Database.DMLOptions();
dmo.assignmentRuleHeader.useDefaultRule = true;
l.setOptions(dmo);
Database.update(l);
}
}
}
This throws the following exception:
Apex script unhandled trigger exception by user/organization:
Source organization:
ApplyLeadAssignmentRules: execution of BeforeUpdate
caused by: System.DmlException: Update failed. First exception on row 0 with id ; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = 00QQ0000001Q2av) is currently in trigger ApplyLeadAssignmentRules, therefore it cannot recursively update itself: []
How do I fix this? Any solution?
This post has what you need:
http://community.salesforce.com/sforce/board/message?board.id=apex&message.id=26391#M26391
Code from that post:
public global class AssignLeads{
public static Boolean assignAlreadyCalled=FALSE;
public static boolean assignAlreadyCalled(){
return assignAlreadyCalled;
}
@future
public static void assign(List<Id> lIds){
assignAlreadyCalled=TRUE;
List<Lead> leads=[SELECT Id FROM Lead WHERE Id IN: lIds];
For (lead l:leads){
Database.DMLOptions dmo = new Database.DMLOptions();
dmo.assignmentRuleHeader.useDefaultRule= true;
l.setOptions(dmo);
}
update(leads);
}
}
All Answers
Heya,
I'm not so familiar with assignment rules, but in this case it seems you should try to use after update trigger?
If you can't, use trigger.new and trigger.old (can't be used in insert trigger) to compare wheter the value ie. sorce or review status has been changed.
Also in before insert, you should never try to call update on object that isn't yet inserted to database.
Check this how trigger has been built: http://wiki.developerforce.com/index.php/Apex_Day_Lab_Exercises
This post has what you need:
http://community.salesforce.com/sforce/board/message?board.id=apex&message.id=26391#M26391
Code from that post:
public global class AssignLeads{
public static Boolean assignAlreadyCalled=FALSE;
public static boolean assignAlreadyCalled(){
return assignAlreadyCalled;
}
@future
public static void assign(List<Id> lIds){
assignAlreadyCalled=TRUE;
List<Lead> leads=[SELECT Id FROM Lead WHERE Id IN: lIds];
For (lead l:leads){
Database.DMLOptions dmo = new Database.DMLOptions();
dmo.assignmentRuleHeader.useDefaultRule= true;
l.setOptions(dmo);
}
update(leads);
}
}
Thanks for the code and sending me the link to the post!
The code works in Sandbox.
We have Marketo configured in Salesforce Prod and Marketo sets the lead score based on various parameters.
Do you know if that may have any impact on the code in Prod? As per the post, the solution works for clean orgs...
The only potential conflicts are around other methods that execute asynchronously (@future).
Marketo uses the API to update records, not asynch apex, so you should be good to go.
Awesome!
Thanks John!
Hey Guys,
We have nearly the same challenge. That is, to have existing Leads go through the Assignment Rules when they reach a certain status in Marketo. From this post and replies, it seems that a simple Update Trigger is not sufficient to force the Leads through the Assignment Rules.
Do I really need a trigger that calls the code to run the Assignment Rules or are there simpler alternatives?
Thanks....
You need code. You could push Marketo to add it to their integration as they can build it once for all customers.
The non code methods aren't a good fit - exporting the leads & then importing in SFDC using the wizards.
This is the only automated way of applying lead assignment rules with in salesforce. The trigger is simple and works with Marketo - you may want to go through the link posted above. If you need help building your trigger, let me know.
The only issue with @future is: when you click update on the lead (no need to select apply assignment rules checkbox) - the update is done but the page refresh does not happen. So, for example if your lead assignment rules assign John Smith as the lead owner, you won't see him as the lead owner unless you refresh the page.
Other option is to use APEX data loader but again it's a manual process. You can build the same process outside salesforce using APEX Data Loader or using WS calls... If you are already touching the APEX governance limit then you may want to use Salesforce WS.
If you are thinking about using workflow to activate assignment rules, nope that can't be done.
Thanks John.
Yes, I plan to suggest or promote an enhancement to Marketo to allow us to specify that Assignment Rules be run, even for exiisting SFDC Leads. Currently, it will fire Assignment Rules when a Lead is uploaded from Marketo to SFDC, but only for net new Leads.
I was just looking for clarification on the post where a trigger calls code defined in an Apex class, versus simply creating a trigger that fires the Assignment Rules for me.
Does adding the method call via the Class object add deployment difficulties above and beyond a simple trigger?
Thanks!
Jeff
You need to write a test class for the trigger before you can deploy. No difficulties!
You can also try out the outboud/inbound change set feature.
Thanks for the confirmation K-squared!
I'm playing around with the trigger logic now and it appears to be working. I'd like to narrow down the instances where the Assignment Rules actually fire so I don't take the chance of assigning Leads accidentally to the Fallout Queue. I'll let you know if I run into some challenges for which your experience would be helpful.
Right now, I'm just working in our Sandbox and I've not actually deployed any code like this to our production environment before. Any quick advice on that process would be appreciated too.
Thanks!
Jeff
King Kong,
Using the IDE, I now have the Lead Trigger and the Class coded, as described in this post. Also, I put a test method in the Class file and am now attempting to deply just the Class to our Production org. Basically, the test method creates a new Dummy Lead record, then calls the same AssignLead method that is called from the Trigger code.
Unfortunately, the deployment to production is being blocked because it says I have only 72% code coverage with my test. Not sure how to write the test method in any other way that might increase this percentage. Also, how do I deploy trigger code if I cannot write a test method for it?
Here is the code for the AssignLead class, including/starting with the test method (should look familiar:...smileywink) Any idea why the test method only gets credit for executing 72% of the lines of code?
TIA, Jeff
Start by commenting out your system.debug lines as they aren't called from tests.
Thanks John. Yes, I should have known to do that....I recall reading it. I've now commented/removed the debug lines, but I'm still at 72% coverage and not allowed to complete the deployment to Production.
Thanks...Jeff
just 2 steps:
1. Insert a new lead (you are already doing it).
2. Update an existing lead
That should take you to 78%. To deploy it from IDE, select the trigger and class (both) and then deploy it.
Let me know if you get into any issues.
Jeff,
My bad - didn't fully go through your test class when I responded earlier.
I think you are missing the insert piece. (You just add lead object to the list collection).
If adding insert does not take you to 75+ the add another line to update some lead. That will surely take you over 75+.
Thanks!
King Kong and John,
Thanks for your advice. I was able to deploy the Class and the Trigger via the IDE to our production environment. I appreciate your help!
Jeff
I'm trying to implement something similar to address reassigning leads that already exist in the system. Specifically in our case, we have old leads that are assigned to Sales reps that are no longer active. Instead of reassiging all of the old leads to the new reps (and have them wade through the junk) we want the leads to be reassigned after the lead becomes "re-engaged" with us (i.e. engages in some Marketing activity synched from Marketo).
I've pretty much used your code exactly, except I perform one additional query to get the list of Owner Ids from the User object WHERE IsActive = false.
One a one-by-one basis, it works great, but if I try to update more than 10 leads at once (e.g. change lead status from a list view), I get a "too many future calls" exception. I do not have any other future calls being made in other triggers/classes.
Here's my code for the trigger:
Here's the code for the class:
Thoughts?
Thanks,
-jl
I think there is a limit for @future. You can have like 10 calls at the most. I think you need to modify the code. But, wait for someone to confirm this or check the documentation.
If you can't find solution or can't wait try using Batch APEX. Here is the link: http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#StartTopic=Content/apex_batch.htm
The limit for @future calls is 200 * # users per 24 hours so if you have 20 users in your org, you get 4,000 per day.
If the @future method is called via lead trigger, that means 1 call is "used" every time 1 or more leads are created or updated.
You may be able to file a ticket with support to request an increase. I can't guarantee that will work though.
John -
That's the organization wide limit, but per trigger you are only allowed to make 10 per invocation, correct?
What I thought originally was that the other triggers I had on leads (one for before update and another after update) may not have been properly "bulkified" and therefore causing the exception to be thrown. However, in my Sandbox I was testing this using a custom VF page I wrote to change lead statuses (we have a validation rule that requires reps to enter in a Reason Rejected value if they choose "Rejected" as a value). It appears that the problem is here (even though I'm using the standard controller for Leads). I retested by using the default "Change Status" button and everything worked (even with the other triggers being active) - meaning that the @future method processed all the records in a single call since I had properly "bulkified" the trigger. I guess the default "Change Status" button has some additional mojo to handle records in bulk better??
I *did* decide to go ahead and write a Batch Apex class and got it to work beautifully. Here's the code for that:
And here's the Scheduler class:
I just put the LIMIT 200 in for testing, I'll probably increase that...
The only unfortunate thing using the Batch is that I can run it once per day (or, I suppose I could create a number of scheduled jobs to run each day), which means that we lose the near-real-time response to the prospect by the rep.
I guess one possible solution is to create a custom list controller for my custom Change Lead Status VF page and override the Save button to "bulkify" the processing...but not sure I've got the time to tackle that right now.
Anyhow, interesting exercise and glad I got a chance to play with Batch Apex. Thanks for all of the feedback!
Just a drive-by comment....
Instead of using @Future to do this, couldn't you just do it in the Trigger itself and use a Static variable to control the recursion? I think there's an example in the Cookbook somewhere, search for "recursion". Then I think you could just do the update right there and not worry about @future limits.
Best, Steve
I tried not making this an asynchronous (@future) call, but when I tried it would not fire the assignment rules. Maybe I did something wrong though...?
For assignment rules, @future is the only way in a trigger... You can google lead assignment rule @future salesforce to see all discussions on this topic.
I haven't tried this myself as I haven't had the need yet, and perhaps I'm missing something, but how does this correlate with this thread:
http://community.salesforce.com/t5/Apex-Code-Development/Trigger-to-Fire-Lead-Assignment-Rules-Does-not-Fire-them/m-p/150720
It seems to me that as long as you make sure that recursion doesn't happen for the Lead Updates, the DMO options should work. I don't see anything in the documentation that leads me to believe that there is a restriction on Assignment Rules being kicked off with a trigger? (Perhaps I've missed it?) Obviously the proof is in the pudding, and I assume you guys have tried coding it up, it just seems weird to me.
Best, Steve.
It seems to me that as long as you make sure that recursion doesn't happen for the Lead Updates...
This is the only concern and @future is the only solution that prevents this from happening. The other solution (avoding recusrion not using @future) did not work for me - probably there were other triggers tied to the Lead object and I was not able to modify the Lead object to add a flag because there were like over 600K leads in the system and if I had added a new field the Marketo sync process would have killed the system for at least 11 days. If you have been using Marketo for some time you'd know that.
The recursion problem is that you can't update a lead in an update trigger, even with DML options. I'm pretty sure I've used DMLOptions update in a before insert trigger, but update in an update trigger definitely doesn't work (which is what most people want to do).
That means you have to use @future for re-assignment to avoid the problem.
I've updated the linked to post where I incorrectly stated you could use Database.Update in an update trigger. Sorry for the confusion.
I hate to disagree with you all, and perhaps I'm really missing something, but this seems to work for me:
Note: On Lead, I've created a field called "RunReassignmentRule__c as a boolean which I set when I want the assignment rules applied.
If adding a field is a problem using the third party app, you could instead overload a field. For example, if "Company" is set to "XXXX_CompanyName", then apply the assignment rule and reset the Company name back to it's proper form.
I'm sure you get the idea. Best, Steve.
Trigger:
Utility class for Recursion:
Test Code:
Of course this isn't perfect, especially as far as the recursion control goes. Critical sections would be nice; you might want to create a use-specific version of the Utility class, and perhaps even a user-specific check within the class to further restrict the possibilities for conflict.
Please post your test code.
Something like this should cover it:
1) Create lead
2) Change owner to something else (create a user in the test if you don't have a 2nd one)
3) test.start(); then call this method, then test.stop()
4) Assert that owner has reverted to the original intended user
Hi Jon,
Thank for the reply.. Still am struggling on that.... No Luck..
Here is my code still i developed:
with this I got 66% coverage. Lines 19 to 23 are could not cover.
Here is the trigger for the above class.
Here is code I developed for above trigger.
In this also getting errors.
Can you show me the way, how to cover test coverage for the trigger and class?
Thanks in advance...!!
Two things that might help:
1) After your lead insert, change the owner & call an update
2) Do that update between test.startTest(); and test.stopTest()
#2 is key to make sure the @future call finishes during the test method:
Also, you should have asserts in there to make sure it actually works like:
Note that system.debug lines never get used in a test, so comment them out to up your %.
Actually you must focus on the base condition. Follow these steps and your issue will be fixed:
1) Create lead
2) Change owner to something else (create a user in the test if you don't have a 2nd one)
3) test.start(); then call this method, then test.stop()
4) Assert that owner has reverted to the original intended user
Thanks! For posting question, for more technical info, you can refer this Website (https://trendinghacker.com/)