The CreatorCon Call for Content is officially open! Get started here.

Automatic allocation method

Julien GOUPIL
Tera Contributor

Hello everyone,

 

I imagine that this subject has already been discussed, but I haven't found a solution to my problem.

In our department, our employees manage RITMs (for requests) and MSSs (for incidents). We currently allocate tickets manually, aiming for a balance in quantity.

 

However, we'd like to set up an automatic allocation of tickets between all the people in the same group using the following criteria:

 

- Always taking quantity into account.
- More equitable sharing between RITMs and MSSs.
- Distribution that also takes into account the status of existing tickets.
For example: one person has 15 tickets and the other has 20, but the person with 20 only has tickets waiting to be returned (on hold), so it's this person who should receive the ticket.

 

I was able to find an article that came close to this by calculating a score:

 

https://docs.servicenow.com/fr-FR/bundle/vancouver-service-management-for-the-enterprise/page/produc...

 

But in the end, I doubt it will meet my needs.

Do you have any ideas?

 

Best regards,

4 REPLIES 4

Julien GOUPIL
Tera Contributor

Hello,

 

Can anyone help me ?
Would you like more details ? Perhaps I'm not being clear enough ?

Best regards,

PaulRittaler
Kilo Contributor

Did you find the Answer Julien GOUPIL

as I need to do same thing

 

Yes ! Give me time to remove the personal information from the script and I'll send it to you in the morning 🙂

Business rule: Requested item table

Option:
- When: Async
- Type: Insert

I put my filter conditions.


Some explanation:

 

I had to impersonate the script so it would run with sufficiently elevated rights, as by default, the business rule only uses the rights of the person triggering it.

This corresponds to the part at the beginning: PLACEHOLDER_USER WITH GOOD RIGHTS.

 

Next, I added new fields to the user table, which are referenced in the script:
- u_requests: This field allows a user to opt in or out of the ticket rotation autonomously, simply by checking or unchecking a box in their profile (e.g., for training or vacation purposes).
- last_assigned_to_ritm: To ensure a fair ticket distribution, this field records the date of the last ticket assigned to the user’s profile. The script uses this information to assign the ticket to the person with the oldest date.

 

I also applied the same logic to the MSS table. This makes it possible, for example, to assign only incidents to certain people and only requests to others.
Alternatively, you can set up an equitable rotation where both types of tickets are distributed, while also considering the number of RITMs or MSS already assigned to each user.


The distribution method has changed compared to my first message. I no longer balance it to ensure everyone has the same amount. Now, it’s a round-robin distribution, assigning tickets one at a time in turn.

Why ? The previous method slowed down the processing pace and penalized high performers.
With this new method, it’s easier to identify those who are struggling, while also rewarding those who perform well by giving them more free time for training, tech monitoring, etc.

Regarding the ticket status, I wasn’t able to integrate it into the system.

That said, this system has been in place for months now, and it works seamlessly! 😊


Then the script:

(function executeRule(current, previous /*null when async*/) {
try {
var originalUser = new GlideImpersonate().impersonate(new GlideRecord("sys_user").get("user_name", "PLACEHOLDER_USER WITH GOOD RIGHTS").sys_id);
gs.info("User impersonnated");
gs.info(originalUser.user_name);

// Declaration and initialization of variables
var groupMembers = []; // An array to store group members
var groupMemberGr = new GlideRecord('sys_user_grmember'); // GlideRecord object to access group members
groupMemberGr.addQuery('user.active', true);
groupMemberGr.addQuery('user.u_requests', true);
groupMemberGr.addQuery('group.name', 'PLACEHOLDER_GROUP OF THE TEAM'); // Replace with the desired group name
groupMemberGr.query();
// Loop to retrieve group members
while (groupMemberGr.next()) {
groupMembers.push(String(groupMemberGr.user)); // Storing members in the array
}
// Checking if there are members in the group
if (groupMembers.length > 0) {
var lastAssignedDates = [];
var userGr = new GlideRecord('sys_user');
// Creating an array with 'last_assigned_to' based on the members array
for (var i = 0; i < groupMembers.length; i++) {
userGr.get(groupMembers[i]);
lastAssignedDates.push(String(userGr.u_last_assigned_to_ritm)); // Storing all 'last_assigned_to' dates in an array
}
gs.info('Array of sysIDs: ' + groupMembers);
gs.info('Array of dates: ' + lastAssignedDates);


// Finding the oldest date
var oldestDateIndex = lastAssignedDates.reduce(function (oldestIndex, currentDate, currentIndex, arr) {
var oldestDate = new GlideDateTime(arr[oldestIndex]);
var currentParsedDate = new GlideDateTime(currentDate);
return currentParsedDate.before(oldestDate) ? currentIndex : oldestIndex;
}, 0);

var oldestAssignedUserId = groupMembers[oldestDateIndex]; // Retrieving the user ID with the oldest assignment date
// Retrieving details of this same user
if (userGr.get(oldestAssignedUserId)) {
gs.info('Name: ' + userGr.user_name); // Displaying the user’s name in the console
oldestAssignedUser = userGr.sys_id;
gs.info(userGr.u_last_assigned_to_ritm);
// Assigning tasks based on conditions
{
current.assignment_group = 'PLACEHOLDER_SYS ID GROUP OF THE TEAM'; // Replace with the appropriate group
current.assigned_to = oldestAssignedUser;
// Updating the 'last_assigned_to' date to the current date and time
var userToUpdate = new GlideRecord('sys_user');
if (userToUpdate.get(oldestAssignedUserId)) {
userToUpdate.u_last_assigned_to_ritm = new GlideDateTime();
userToUpdate.update();
gs.info('Updated the last assigned field for ' + userToUpdate.user_name + ' to the date: ' + userToUpdate.u_last_assigned_to_ritm);
} else {
gs.error('Unable to find the user with ID: ' + oldestAssignedUserId);
}
current.update();
}
// Updating the record
} else {
gs.error('User not found.');
}
} else {
gs.error('No members found in the group.');
}
} catch (ex) {
gs.error('An error occurred: ' + ex.message); // Error handling
}
new GlideImpersonate().impersonate(originalUser); // Revert back to original user.
})(current, previous);