- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday
I'm am trying to populate the Collaborators field in a Demand using a script include and business rule, but it doesn't get populated and I don't know why.
For those who may not use Demands, the Collaborators field is a list of users who are collaborating with the demand. The field is defined as a glide_list; it's the same as a watch list or additional assignee field on incident or any table extended from Task.
I created a script include that uses specific criteria to get a list of users who should be collaborators. At the end of the script include, I remove the duplicate entries and return the unique array to the business rule that calls the script include and is supposed to populate the users to the collaborators field. I'm getting good data back from the script include, but the collaborators field never gets populated and I don't know why and could use some help.
Script include snippet (that returns the correct data):
Can anyone spot what is missing?
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday
Hi @gjz ,
Thanks for sharing the complete code. With the additional information, I think we can narrow this down.
The behavior you saw in your PDI with the quotes is expected JavaScript behavior.
This is valid:
var userList = ['be82abf03710200044e0bfc8bcbe5d1c', 'af2a757b1b74c4500750fc87cc4bcbc1'];
The sys_ids have to be quoted because they are JavaScript strings.
This is not valid:
var userList = [be82abf03710200044e0bfc8bcbe5d1c, af2a757b1b74c4500750fc87cc4bcbc1];
Without quotes, JavaScript treats those values as variable names.
However, you do NOT need to add quotes around every sys_id before setting the Collaborators field. A Glide List field can be populated with a comma-separated string of sys_ids.
I would remove ArrayUtil and the GlideElement concatenation from this implementation and use the actual stored values from the Glide List fields.
Replace your getCollaborators method with this:
getCollaborators: function(dept) {
var collaborators = [];
var uniqueUsers = {};
if (gs.nil(dept)) {
return '';
}
var divisionGR = new GlideRecord('u_cmn_divisions');
divisionGR.addQuery('u_department', dept);
divisionGR.query();
while (divisionGR.next()) {
var liaisons = divisionGR.getValue('u_liaisons') || '';
var fundingLiaisons = divisionGR.getValue('u_funding_approvers') || '';
var users = [];
if (liaisons) {
users = users.concat(liaisons.split(','));
}
if (fundingLiaisons) {
users = users.concat(fundingLiaisons.split(','));
}
for (var i = 0; i < users.length; i++) {
var userSysId = users[i] + '';
if (!userSysId) {
continue;
}
if (!uniqueUsers[userSysId]) {
uniqueUsers[userSysId] = true;
collaborators.push(userSysId);
}
}
}
var collaboratorString = collaborators.join(',');
gs.info('@@@ getCollaborators result: ' + collaboratorString);
return collaboratorString;},
Then configure the Demand Business Rule as follows:
When: Before
Insert: Checked, if this should also populate during insert
Update: Checked
Advanced: Checked
For testing, leave the condition empty so that it runs on every update.
Most importantly, make sure this is a BEFORE Business Rule, not After or Async.
Use this exact Business Rule script:
(function executeRule(current, previous) {
var dept = current.getValue('u_department');
gs.info('@@@ Department: ' + dept);
if (gs.nil(dept)) {
current.setValue('collaborators', '');
return;
}
var ppm = new XXX_PPM_Utils();
var liaisonList = ppm.getCollaborators(dept);
gs.info('@@@ Collaborators returned: ' + liaisonList);
current.setValue('collaborators', liaisonList || '');
gs.info('@@@ Collaborators on current before save: ' + current.getValue('collaborators'));})(current, previous);
Do not use current.update() inside this Business Rule.
The important part is that the Business Rule runs Before the Demand is written to the database. The value assigned to current.collaborators will then be included in the same database update.
For example, assume your divisions contain:
Division 1
u_liaisons:
userA,userB
u_funding_approvers:
userC
Division 2
u_liaisons:
userA,userD
u_funding_approvers:
userC
The method will return:
userA,userB,userC,userD
Duplicates are removed before the value is returned.
There is also one thing I noticed in your description that I would verify.
You referred to the second field as "Funding Liaisons", but your script is querying:
u_funding_approvers
Please confirm that u_funding_approvers is really the backend field name of that Glide List field.
If the actual field is something like:
u_funding_liaisons
then change this line:
var fundingLiaisons = divisionGR.getValue('u_funding_approvers') || '';
to use the actual backend field name.
I would test this exact version first.
After updating the Demand, check the logs for:
@@@ getCollaborators result:
@@@ Collaborators returned:
@@@ Collaborators on current before save:
All three should contain the same comma-separated sys_ids.
If "@@@ Collaborators on current before save" contains the correct sys_ids but the Collaborators field is empty after the record is saved, then the Script Include is no longer the issue. That would indicate another Business Rule, Flow, or other server-side process is clearing or replacing Collaborators later in the transaction.
But with the Business Rule running Before and the correct backend field names, the above code should populate the Glide List directly without needing quotes around individual sys_ids.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday
Hi @gjz ,
I can spot one issue directly in the code you shared.
Your Script Include appears to be returning the correct sys_ids, but in the Business Rule you are doing:
current.setValue('collaborators', liaisonList.join(''));
The problem is:
join('')
This concatenates all sys_ids together without any separator.
For example, instead of storing:
sys_id_1,sys_id_2,sys_id_3
you are effectively creating:
sys_id_1sys_id_2sys_id_3
A List / Glide List field expects a comma-separated list of sys_ids.
So change:
current.setValue('collaborators', liaisonList.join(''));
to:
current.setValue('collaborators', liaisonList.join(','));
Corrected Business Rule:
var dept = current.u_department;
var ppm = new XXX_PPM_Utils();
var liaisonList = ppm.getCollaborators(dept);
gs.info(
'Collaborators returned: ' +
liaisonList
);
if (liaisonList && liaisonList.length > 0) {
current.setValue(
'collaborators',
liaisonList.join(',')
);
} else {
current.setValue(
'collaborators',
''
);
}
There is also an important reason why your log may have looked correct.
When you log an Array like:
gs.info(liaisonList);
JavaScript displays the array values separated by commas.
Therefore the log may look like:
sys_id_1,sys_id_2,sys_id_3
even though the code:
liaisonList.join('')
actually removes those commas before setting the Collaborators field.
This can make the debugging output misleading.
I would also simplify the Script Include.
Currently you have logic similar to:
var listArray = liaison_list
? liaison_list.split(',')
: [];
var uniqueArray =
new ArrayUtil().unique(listArray);
var cleanString =
uniqueArray.join(',');
return uniqueArray;
Notice that you create:
cleanString
but never return it.
You can choose either of these approaches.
OPTION 1 - Return the Array
Script Include:
var listArray = liaison_list
? liaison_list.split(',')
: [];
var uniqueArray =
new ArrayUtil().unique(listArray);
return uniqueArray;
Business Rule:
var liaisonList =
ppm.getCollaborators(dept);
current.setValue(
'collaborators',
liaisonList.join(',')
);
This is perfectly fine.
OPTION 2 - Recommended for this use case
Since the Script Include is ultimately preparing a value for a Glide List field, return the final comma-separated string directly.
Script Include:
var listArray = liaison_list
? liaison_list.split(',')
: [];
var uniqueArray =
new ArrayUtil().unique(listArray);
return uniqueArray.join(',');
Then your Business Rule becomes simpler:
var dept = current.u_department;
var ppm = new XXX_PPM_Utils();
var collaborators =
ppm.getCollaborators(dept);
gs.info(
'Collaborators: ' +
collaborators
);
current.setValue(
'collaborators',
collaborators
);
I would personally use Option 2 because the Script Include returns exactly the format required by the Collaborators field.
There is one more important thing to verify:
Check WHEN your Business Rule executes.
If you are changing a field on the same Demand record using:
current.setValue()
the Business Rule should normally be:
When:
Before
Insert:
As required
Update:
As required
For example:
Before Update
Condition:
Department changes
or whatever condition matches your requirement.
ServiceNow recommends Before Business Rules when setting values on the current record because those changes are included automatically in the database update.
If your Business Rule is:
After
then:
current.setValue('collaborators', ...);
changes the in-memory current record after the original database update has already occurred, so the new Collaborators value will not normally be persisted.
Do NOT solve that by adding:
current.update();
inside an After Business Rule.
That can retrigger Business Rules and introduce recursion/performance problems.
Instead, change the rule to Before when the requirement is simply:
Department changes
-> Calculate Collaborators
-> Save Collaborators on the same Demand
Recommended final design:
Demand Department changes
-> Before Business Rule
-> Call Script Include
-> Build unique collaborator sys_ids
-> Return comma-separated sys_ids
-> Set current.collaborators
-> Normal Demand update saves the value
So I would check these two items in this order:
1. Fix:
liaisonList.join('')
to:
liaisonList.join(',')
2. Verify the Business Rule is Before, not After/Async.
Based on the code shown in your screenshot, the missing comma in join() is definitely an issue and should be corrected first.
Official ServiceNow reference:
Business Rules and Script Includes:
https://www.servicenow.com/docs/r/application-development/business-rules-and-script-includes.html
Related ServiceNow Community solution for populating Glide List fields:
Hope this helps!
If this response helped, please mark it as Helpful.
If it resolves your issue, please Accept it as Solution.
Kind Regards,
Abhishek Pal
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday
@Abhishek Pal - Well, shoot, it still doesn't work. But, you appear to know a lot more about arrays than I do, so do you mind helping me some more?
We have a custom division table and in this table are two fields that are glide_list, Liaisons and Funding Liaisons. Each field must have at least one user, most have multiple users. What I need to do is get the users in these fields for the specific dept they belong to (department to division = 1:many). Because the same user can be in both fields and there may be multiple divisions for the department, there will be duplicates in the list.
I switched to your option 2 since I agree, it does seem simpler, but it still doesn't work. For now, I just have the business rule firing always on an update until I can get it to work. I'll change it to the correct criteria once it does work.
Here is the complete code for the script include:
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
yesterday
Hi @gjz ,
Thanks for sharing the complete code. With the additional information, I think we can narrow this down.
The behavior you saw in your PDI with the quotes is expected JavaScript behavior.
This is valid:
var userList = ['be82abf03710200044e0bfc8bcbe5d1c', 'af2a757b1b74c4500750fc87cc4bcbc1'];
The sys_ids have to be quoted because they are JavaScript strings.
This is not valid:
var userList = [be82abf03710200044e0bfc8bcbe5d1c, af2a757b1b74c4500750fc87cc4bcbc1];
Without quotes, JavaScript treats those values as variable names.
However, you do NOT need to add quotes around every sys_id before setting the Collaborators field. A Glide List field can be populated with a comma-separated string of sys_ids.
I would remove ArrayUtil and the GlideElement concatenation from this implementation and use the actual stored values from the Glide List fields.
Replace your getCollaborators method with this:
getCollaborators: function(dept) {
var collaborators = [];
var uniqueUsers = {};
if (gs.nil(dept)) {
return '';
}
var divisionGR = new GlideRecord('u_cmn_divisions');
divisionGR.addQuery('u_department', dept);
divisionGR.query();
while (divisionGR.next()) {
var liaisons = divisionGR.getValue('u_liaisons') || '';
var fundingLiaisons = divisionGR.getValue('u_funding_approvers') || '';
var users = [];
if (liaisons) {
users = users.concat(liaisons.split(','));
}
if (fundingLiaisons) {
users = users.concat(fundingLiaisons.split(','));
}
for (var i = 0; i < users.length; i++) {
var userSysId = users[i] + '';
if (!userSysId) {
continue;
}
if (!uniqueUsers[userSysId]) {
uniqueUsers[userSysId] = true;
collaborators.push(userSysId);
}
}
}
var collaboratorString = collaborators.join(',');
gs.info('@@@ getCollaborators result: ' + collaboratorString);
return collaboratorString;},
Then configure the Demand Business Rule as follows:
When: Before
Insert: Checked, if this should also populate during insert
Update: Checked
Advanced: Checked
For testing, leave the condition empty so that it runs on every update.
Most importantly, make sure this is a BEFORE Business Rule, not After or Async.
Use this exact Business Rule script:
(function executeRule(current, previous) {
var dept = current.getValue('u_department');
gs.info('@@@ Department: ' + dept);
if (gs.nil(dept)) {
current.setValue('collaborators', '');
return;
}
var ppm = new XXX_PPM_Utils();
var liaisonList = ppm.getCollaborators(dept);
gs.info('@@@ Collaborators returned: ' + liaisonList);
current.setValue('collaborators', liaisonList || '');
gs.info('@@@ Collaborators on current before save: ' + current.getValue('collaborators'));})(current, previous);
Do not use current.update() inside this Business Rule.
The important part is that the Business Rule runs Before the Demand is written to the database. The value assigned to current.collaborators will then be included in the same database update.
For example, assume your divisions contain:
Division 1
u_liaisons:
userA,userB
u_funding_approvers:
userC
Division 2
u_liaisons:
userA,userD
u_funding_approvers:
userC
The method will return:
userA,userB,userC,userD
Duplicates are removed before the value is returned.
There is also one thing I noticed in your description that I would verify.
You referred to the second field as "Funding Liaisons", but your script is querying:
u_funding_approvers
Please confirm that u_funding_approvers is really the backend field name of that Glide List field.
If the actual field is something like:
u_funding_liaisons
then change this line:
var fundingLiaisons = divisionGR.getValue('u_funding_approvers') || '';
to use the actual backend field name.
I would test this exact version first.
After updating the Demand, check the logs for:
@@@ getCollaborators result:
@@@ Collaborators returned:
@@@ Collaborators on current before save:
All three should contain the same comma-separated sys_ids.
If "@@@ Collaborators on current before save" contains the correct sys_ids but the Collaborators field is empty after the record is saved, then the Script Include is no longer the issue. That would indicate another Business Rule, Flow, or other server-side process is clearing or replacing Collaborators later in the transaction.
But with the Business Rule running Before and the correct backend field names, the above code should populate the Glide List directly without needing quotes around individual sys_ids.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
3 hours ago
@Abhishek Pal - Thank you so much, this worked!
Interestingly, it's close to code that was already written in a script include for finding the correct liaisons for request approvals. I started with that code but I couldn't get it to work, which is why I went down the path I chose originally.
Again - thanks!