Last active
September 3, 2018 21:47
-
-
Save tjsummerford/e5ea0e856d47175edc655504505439d4 to your computer and use it in GitHub Desktop.
Adapting Round Robin Record Assignment from Salesforce Labs to assign leads.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The Round Robin Record Assignment app from Salesforce Labs (https://appexchange.salesforce.com/listingDetail?listingId=a0N3000000178fsEAA) is quite helpful when you need to manage round robin assignment of objects to users. However the app only provides funcitionality for assigning cases. | |
In one of the reviews of the app on the AppExchange, Greg Burris (https://success.salesforce.com/profile?u=00530000001tvh3AAA) provided some instructions on how adapt the app for managing leads: | |
----------------------------- | |
It's pretty straightforward to adapt this to work with Leads. Just did it today in about 30 minutes. | |
1. add a field to the Leads object "TempOwnerID" (text 18 char). | |
2. Copy the code for the trigger "caseRoundRobin" | |
3. Create a new Lead trigger and paste the code copied in Step 2 | |
4. In the lead trigger, change all occurrences of the string "case" to "lead". | |
5. Save the new trigger. | |
6. In the object "Assignment Group" edit the picklist "Type" and add the value "Leads" to the list. | |
I *THINK* that is it. I'll see what I can do to get this package updated, although I am NOT its author. Please note that just like the original Salesforce Labs package, the modifications I described above are not core Salesforce functionality, and are therefore not supported by our Support organization. I will try to answer any questions you may have, though. Good luck, all. | |
Oh, and you need to still follow the rest of the configuration and setup rules described in the package documentation, including setting up your Lead Assignment Rules to fire the trigger according to your own criteria. I set up my demo to fire based on the Last Name of the lead = "Walkin". You can however use whatever assignment rules you want. | |
------------------ | |
I followed his suggestions and wanted to share my results in case they are helpful to anyone else. Following are 3 files: | |
1. AssignmentGroup_LeadRoundRobin.cls | |
This is my adaption of caseRoundRobin.trigger which is part of the original package. I encapsulate the code in a class so that I can execute it from a master Lead trigger. | |
2. Test_LeadRoundRobin.cls | |
A test class which should provide more thatn 80% coverage for AssignmentGroup_LeadRoundRobin.cls. This is adapted from TestMethodCls3.cls included in the original package. | |
3. MasterLeadTrigger.trigger | |
A simplified master Lead trigger which implmenents AssignmentGroup_LeadRoundRobin.cls. | |
------------------------- | |
Update - 2016-10-25 | |
After using this code in production for a month or so, NullPointerException errors starting to appear. At the same time, new leads were not being created consistently through an associated Pardot connector. In the comments from the app on the AppExchange, Ryan Schwartz offered some code to fix these problems in 2012 - http://dl.dropbox.com/u/53527196/sfdc/leadRoundRobin.trigger.txt. I incorporated his updates in to the code above on Oct 25, 2016. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class AssignmentGroup_LeadRoundRobin { | |
public static void AssignLead(boolean IsUpdate, List<Lead> Leads, Map<Id, Lead> oldMap_Leads){ | |
// | |
//Check if assignment owner has changed | |
// | |
Map<Integer,Id> queueIds = new Map<Integer,Id>(); //Trigger index --> Queue ID | |
Integer idx = 0; | |
for (lead l : Leads) | |
{ | |
if(IsUpdate) { | |
if(l.OwnerId <> oldMap_Leads.get(l.id).OwnerId) { | |
if (l.TempOwnerId__c == 'SKIP') { | |
Leads[idx].TempOwnerId__c = ''; | |
} else { | |
queueIds.put(idx, l.OwnerId); | |
} | |
} | |
}else { | |
queueIds.put(idx, l.OwnerId); | |
} | |
idx++; | |
} | |
System.debug('>>>>>queueIds: '+queueIds); | |
if (queueIds.isEmpty()) return; | |
// | |
//Find active Assignment Group for Queue | |
// | |
Map<Integer,Id> asgnGroupNameIds = new Map<Integer,Id>(); //Trigger index --> Assignment_Group_Name ID | |
Map<Id,Assignment_Group_Queues__c> asgnGroupQueues = new Map<Id,Assignment_Group_Queues__c>(); //Queue ID --> Assignment Group Queues | |
for(Assignment_Group_Queues__c[] agq : [SELECT Assignment_Group_Name__c, QueueId__c | |
FROM Assignment_Group_Queues__c | |
WHERE QueueId__c in :queueIds.values() | |
AND Active__c = 'True']) | |
{ | |
for (Integer i = 0; i < agq.size() ; i++) { | |
asgnGroupQueues.put(agq[i].QueueId__c, agq[i]); | |
} | |
} | |
System.debug('>>>>>asgnGroupQueues: '+asgnGroupQueues); | |
if (asgnGroupQueues.isEmpty()) return; | |
for (Integer i : queueIds.keySet()) { | |
Assignment_Group_Queues__c agq = asgnGroupQueues.get(queueIds.get(i)); | |
if (agq <> null) { | |
asgnGroupNameIds.put(i, agq.Assignment_Group_Name__c); | |
} | |
//else no active assignment group queue error | |
} | |
System.debug('>>>>>asgnGroupNameIds: '+asgnGroupNameIds); | |
if (asgnGroupNameIds.isEmpty()) return; | |
// | |
//Determine next valid user in Queue/Assignment Group for round robin | |
//User with earliest last assignment date wins. | |
// | |
Map<Id,Assignment_Groups__c[]> asgnGroups = new Map<Id,Assignment_Groups__c[]>(); // Assignment Group Name ID --> User ID | |
for(Assignment_Groups__c[] ags : [SELECT Group_Name__c, User__c, Last_Assignment__c, Millisecond__c | |
FROM Assignment_Groups__c | |
WHERE Group_Name__c in :asgnGroupNameIds.values() | |
AND Active__c = 'True' AND User_Active__c = 'True' | |
ORDER BY Last_Assignment__c, Millisecond__c]) | |
{ | |
/* | |
Start Ryan Schwartz (RS) Edit | |
source: https://dl.dropboxusercontent.com/u/53527196/sfdc/leadRoundRobin.trigger.txt | |
This edit helps resolve some of the NullPointerException errors. | |
*/ | |
/* | |
if (ags.size()>0) { | |
asgnGroups.put(ags[0].Group_Name__c, ags); | |
} | |
*/ | |
if (ags.size()>0) { | |
Assignment_Groups__c[] tmpAssignGroups = new List<Assignment_Groups__c>(); | |
String tmpGroupName = ags[0].Group_Name__c; | |
for (Integer i = 0; i < ags.size() ; i++) { | |
// If the group has changed reset the list | |
if(ags[i].Group_Name__c <> tmpGroupName){ | |
tmpGroupName = ags[i].Group_Name__c; | |
tmpAssignGroups = new List<Assignment_Groups__c>(); | |
} | |
// Always add members who belong to the current group name to a temp array | |
tmpAssignGroups.add(ags[i]); | |
asgnGroups.put(ags[i].Group_Name__c, tmpAssignGroups); | |
} | |
} | |
/* End RS Edit */ | |
} | |
System.debug('>>>>>asgnGroups: '+asgnGroups); | |
if (asgnGroups.isEmpty()) return; | |
Map<Id,Assignment_Groups__c> updateAssignmentGroups = new Map<Id,Assignment_Groups__c>(); | |
Map<Id, datetime> latestAGDateTime = new Map<Id,datetime>(); | |
idx = 0; | |
for (Integer i : queueIds.keySet()) | |
{ | |
Assignment_Groups__c[] ags = asgnGroups.get(asgnGroupNameIds.get(i)); | |
if(ags != null) //This edit helps resolve some of the NullPointerException errors. | |
{ | |
if (ags.size()>0) | |
{ | |
//Choose next user in line if user ID has already been used but not committed in this trigger batch | |
Assignment_Groups__c ag = ags[math.mod(idx, ags.size())]; | |
//Assign User to lead as the new owner | |
System.debug('>>>>>Owner changed for lead ' + Leads[i].Id + ' from '+Leads[i].OwnerId+' to '+ ag.User__c); | |
Leads[i].OwnerId = ag.User__c; | |
Leads[i].TempOwnerId__c = ag.User__c; | |
//Set last assignment datetime | |
datetime now = datetime.now(); | |
ag.Last_Assignment__c = now; | |
ag.Millisecond__c = now.millisecondGMT(); | |
//update only latest Assignment Groups per ID | |
if (latestAGDateTime.containsKey(ag.id)) { | |
if(latestAGDateTime.get(ag.id) < now) { | |
updateAssignmentGroups.put(ag.id, ag); | |
latestAGDateTime.put(ag.id, now); | |
} | |
} else { | |
updateAssignmentGroups.put(ag.id, ag); | |
latestAGDateTime.put(ag.id,now); | |
} | |
idx++; | |
} | |
} | |
} | |
//Map --> List/Array for DML update | |
List<Assignment_Groups__c> updateAG = new List<Assignment_Groups__c>(); | |
for (Id agId : updateAssignmentGroups.keySet()) { | |
updateAG.add(updateAssignmentGroups.get(agId)); | |
} | |
System.debug('>>>>>Update Assignment Groups: '+updateAG); | |
// | |
//Update last assignment for Assignment Group in batch | |
// | |
if (updateAG.size()>0) { | |
try { | |
update updateAG; | |
} catch (Exception e){ | |
for (Integer i : queueIds.keySet()) | |
{ | |
Trigger.new[i].addError('ERROR: Could not update Assignment Group records ' + ' DETAIL: '+e.getMessage()); | |
} | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trigger MasterLeadTrigger on Lead ( | |
before insert, after insert, | |
before update, after update, | |
before delete, after delete) { | |
if (Trigger.isBefore) { | |
if (Trigger.isInsert) { | |
AssignmentGroup_LeadRoundRobin.AssignLead(false, Trigger.new, Trigger.oldMap); | |
} | |
if (Trigger.isUpdate) { | |
AssignmentGroup_LeadRoundRobin.AssignLead(true, Trigger.new, Trigger.oldMap); | |
} | |
if (Trigger.isDelete) { } | |
} | |
if (Trigger.IsAfter) { | |
if (Trigger.isInsert) { } | |
if (Trigger.isUpdate) { } | |
if (Trigger.isDelete) { } | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@isTest() | |
private class Test_LeadRoundRobin { | |
static testMethod void myTest2() { | |
// This code runs as the system user | |
User u1; | |
try{ | |
u1 = [select Id from User WHERE IsActive=True AND Profile.Name = 'System Administrator' LIMIT 1]; | |
} catch (QueryException qe){ | |
List<User> users = [SELECT Id, Profile.PermissionsModifyAllData FROM User WHERE IsActive = true LIMIT 1000]; | |
for(User u : users){ | |
if(u.Profile.PermissionsModifyAllData = true){ | |
u1 = u; | |
break; | |
} | |
} | |
} | |
System.debug(u1); | |
//*****Create Queue | |
Group testGroup = new Group (); | |
testGroup.Name = 'TestQueue'; | |
testGroup.Type = 'Queue'; | |
insert testGroup; | |
QueueSObject testQueue = new QueueSObject(); | |
testQueue.QueueId = testGroup.id; | |
testQueue.SObjectType = 'Lead'; | |
insert testQueue; | |
// Second Queue | |
Group testGroup2 = new Group (); | |
testGroup2.Name = 'TestQueue2'; | |
testGroup2.Type = 'Queue'; | |
insert testGroup2; | |
QueueSObject testQueue2 = new QueueSObject(); | |
testQueue2.QueueId = testGroup2.id; | |
testQueue2.SObjectType = 'Lead'; | |
insert testQueue2; | |
System.runAs ( u1 ) { | |
test.starttest(); | |
u1.Assignment_Group_Active__c = true; | |
update u1; | |
//Run test | |
//Create Assignment Group | |
Assignment_Group_Name__c ag1 = new Assignment_Group_Name__c (Name='TestAG', Type__c = 'Lead'); | |
insert ag1; | |
//Add Good Queue to Assignment Group | |
Assignment_Group_Queues__c agq1 = new Assignment_Group_Queues__c(name=testGroup.Name ,Assignment_Group_Name__c = ag1.id ); | |
insert agq1; | |
//Add User to Assignment Groups Users | |
Assignment_Groups__c agu1 = new Assignment_Groups__c (User__c = u1.id, Active__c='True', Group_Name__c = ag1.id, Last_Assignment__c = datetime.valueOf('2009-01-01 21:13:24') ); | |
insert agu1; | |
Lead c2 = new Lead (lastName='testLead1', company='self', tempOwnerID__c=testGroup2.id , OwnerID=testGroup.id); //Set owner ID to Queue | |
insert c2; | |
update c2; | |
Lead c3 = new Lead (lastName='testLead2', company='self', tempOwnerID__c=testGroup2.id , OwnerID=testGroup.id); //Set owner ID to Queue | |
insert c3; | |
update c3; | |
test.stoptest(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment