I haven’t been sleeping for about 36 hours by now, so please bear with me.
The following solution is inspired by this post.
Use Case Link to heading
Users wants to dynamically assign a user to a queue automatically, at the same time, they want to dynamically submit for approval to the queue that’s related to opp owner’s department when condition meets.
Break the Problem Link to heading
One of the things I learned from algorithm is to break down the problem, divide and conquer.
In our use case, there are 2 main problems:
- Dynamically assign users to a queue
- Dynamically submit the opp to a queue
Once it is broken down into those 2, it is clear how to resolve it.
- Dynamically assign users to a queue: use a trigger to dynamically assign/remove users
- Dynamically submit the opp to a queue : flow to submit for approval depending on certain situations
Hands-On Link to heading
What we need:
- Custom Object: Approval Matrix
- This is used to map opp record and which queue it should submit to
- Utilize the Owner field on this object as the Approver, this will help us solve 2 problems:
- Opportunity Queue (This function will come in 2022 release, if I remembered right)
- Lookup to queue custom field doesn’t exist
- Approval process
- Flow-trigger: to create “Approval Matrix” record when an Opp record is created, and to update “Approval Matrix” record owner when an Opp is updated (this is probably easier to do in trigger code instead of flow)
- Flow-trigger: to submit for approval when certain conditions meet, in this case, I just made it easy, when Opp Stage = Needs Analysis, then submit for approval
- Custom Metadata Type : I only used this to map the department and the queue name, but you can add in more complicated conditions, for example, if a user belong to certain account, then they should be put into certain queue. You can put that logic here as well.
- Trigger on User: add/remove user to the proper queue (you can also use flow, just for me, it is easier to do trigger here, sometimes, I don’t like drag and drop….)
Please also note, I didn’t bulkify my flow at all… you should always do that, I just need a proof of concept here.
Custom Object Link to heading
This is just a simple record map opp and approver queue
Notice the Owner field here, this is the field will be used as approvers. In my process, when a new opp record is created, the owner field on Approval Matrix record is automatically assigned to “Empty Queue”, this is a queue without any users. When you are doing your process, you should optimize it to auto assign to a correct queue, unless no proper queue can be found. I am just being lazy here.
Approval Process Link to heading
Your approval process would be set up like the following
Approval Process
Notice the “Assigned Approver” is set to “Related User: Owner”
The Email Template will be a VF Email Template where you can query proper records
Example of Email Template
Flow-trigger: to create “Approval Matrix” record Link to heading
Snapshot of the flow I built
Like what I said before, you should optimize the following:
- Bulkify
- Set the queue when you create a record too –> this will improve user experience
Flow-trigger: to submit for approval Link to heading
This will submit for an approval when the stage is changed to “Needs Analysis”
Snap of flow
- Trigger on User:
Custom Metadata Type Link to heading
Here is my setup for Custom Metadata
Setup of CMDT
All we need, at least in my case, is map the department and queue here. You can make it as complicated as you can. For example, if a user belong to certain account, then they should be put into certain queue. You can put that logic here as well.
Here is the record:
Example record CMDT
Trigger on User Link to heading
Add/remove user to the proper queue (you can also use flow, just for me, it is easier to do trigger here, sometimes, I don’t like drag and drop….)
Here my code:
Trigger
trigger UserTrigger on User (after insert, after update, before insert, before update) {
if(trigger.isBefore && (trigger.isUpdate || trigger.isInsert) ) {
UserTriggerHandler.addUserToQueue(Trigger.New,Trigger.Old);
}
}
Handler
public class UserTriggerHandler {
public static void addUserToQueue(List<User> TriggerNew, List<User> TriggerOld) {
map<String, String> manager_dep_map = new map<String, String>();
set<String> keySet_dep = new set<String>();
list<GroupMember> gmLists = new list<GroupMember>();
List<String> depNames = new List<String>();
for(User_Assignment_Queue__mdt cmdt_User :[select Id,Queue__c, Department__c
from User_Assignment_Queue__mdt
]){
manager_dep_map.put(cmdt_User.Department__c, cmdt_User.Queue__c);
}
for(User usrO : TriggerOld){
removeUserHelper(usrO);
}
system.debug('depNames = '+depNames);
for(User usr: TriggerNew){
if(usr.Manager__c && usr.Department <> NULL) {
if(manager_dep_map.get(usr.Department) <> NULL){
Id qId = [select Id from Group where Type =: 'Queue' AND Name =: manager_dep_map.get(usr.Department) ].Id;
GroupMember gm = new GroupMember(GroupId = qId, UserOrGroupId = usr.Id);
gmLists.add(gm);
}
else{
Group g = new Group(Type='Queue',
Name = usr.Department + 'Team Queue');
insert g;
GroupMember gm = new GroupMember(GroupId = g.Id, UserOrGroupId = usr.Id);
gmLists.add(gm);
usr.addError('This is a new department, please setup the queue and conditions accordingly');
}
}
}
system.debug('gmLists = '+ gmLists);
depNames.clear();
insert gmLists;
}
public static void removeUserHelper(User UserOld){
list<GroupMember> DeleteGroupMemberRecord = New list<GroupMember>();
list<Id> qId = new list<Id>();
list<Id> oldUId = new list<Id>();
List<String> depNames = new List<String>();
map<String, String> manager_dep_map = new map<String, String>();
for(User_Assignment_Queue__mdt cmdt_User :[select Id,Queue__c, Department__c
from User_Assignment_Queue__mdt
]){
manager_dep_map.put(cmdt_User.Department__c, cmdt_User.Queue__c);
}
String groupName = manager_dep_map.get(UserOld.Department);
system.debug('groupName = '+groupName);
if(UserOld.Manager__c){
for(GroupMember gm:[
select Id, Group.Name, UserOrGroupId from GroupMember where UserOrGroupId =: UserOld.Id AND Group.Name =: groupName
]) {
DeleteGroupMemberRecord.add(gm);
}
}
else{
for(GroupMember gm:[
select Id, Group.Name, UserOrGroupId from GroupMember where UserOrGroupId =: UserOld.Id
]) {
DeleteGroupMemberRecord.add(gm);
}
}
System.debug('DeleteGroupMemberRecord = '+DeleteGroupMemberRecord);
if(!DeleteGroupMemberRecord.isEmpty()) {
delete DeleteGroupMemberRecord;
}
}
}
Full disclosure on the Queue setup:
Queue
Moment of Truth! Link to heading
First let’s test the user part
We have this user John Doe here, I added another field called “Manager” as a checkbox, the reason is, in this use case, there are multiple managers belong to one department. This way, it solves the problem where one user can only have 1 manager, because in this use case, there can be multiple managers. (I know, a little odd)
On user, you set the department field as how you want, in my case, I am setting it to Sales with the Manager checkbox checked, after you save, you will see this user is assigned to a queue.
John Doe
manager checkbox
Successfully assigned queue
Create a new opp, then a new Approval Matrix record should be created too
new opp
Approval Matrix record created
Now I am going to update Opp stage to “Needs Analysis”
stage updated
Now if you navigate back to the Approval Matrix record, you should see the owner has been updated to Sales Team Queue accordingly
Approval Matrix updated
If you go to the “Related” tab, you should see an approval process has been submitted to the queue (owner of this record)
Approval submitted
YAY it worked!!!!!
Conclusion Link to heading
The logic here could get really complicated based on your own business process.
Once break down the problem, things are not clearer and simpler in a way.
OK. I am officially have been up for 44 hours straight now. I am going to take a nap.
Please let me know if you have any questions. I am looking for feedbacks as well. Thank you!!














