Skip to content

Instantly share code, notes, and snippets.

@tjsummerford
Last active September 3, 2018 21:47
Show Gist options
  • Save tjsummerford/e5ea0e856d47175edc655504505439d4 to your computer and use it in GitHub Desktop.
Save tjsummerford/e5ea0e856d47175edc655504505439d4 to your computer and use it in GitHub Desktop.
Adapting Round Robin Record Assignment from Salesforce Labs to assign leads.
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.
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());
}
}
}
}
}
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) { }
}
}
@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