Brad Bowman
Kilo Patron
Kilo Patron

I often encounter two distinct but related scenarios where I either need to change a reference qualifier on a variable in a single-row variable set, but only for a certain catalog item, or I cannot set a reference qualifier to what I want (what I really, really want) in a multi-row variable set variable that relies on the value of a catalog item variable.  This article presents two examples, then details surprisingly simple solutions to both. 

 

Scene 1: "A Change Will Do You Good"

A popular variable is part of an established single-row variable set that has been added to many catalog items over time.  This variable is a reference with a qualifier, controlling what can be seen - and not seen - in a list of records.  A new catalog item comes along and challenges the status quo.  It includes the variable set for efficiency, but wants the reference variable to change its ways.  The reference variable reluctantly accepts that its qualifier needs to change - but only for this one item (so far).  How can this be done? Read on!

 

Starting Point: Common workarounds to this scenario are to either not use the variable set, necessitating the re-creation of all the variables for this rogue catalog item (and any that follow with a similar use case), or to create one variable with the new reference qualifier in the catalog item or within the variable set, then hide the original variable on the new catalog item.

 

Conflict: The biggest downside common to both workarounds, aside from creating one or more extra variables, is that to maintain downstream usage and reporting on the variable across catalog items, the value from the new variable needs to be copied to the old one, then kept in sync or hidden...

 

Our Hero: A simple solution is to always set the reference qualifier from a script include.

 

Here's an example. We have a reference variable named v_configuration_item that is a reference to the cmdb_ci table in a single-row variable set that has been included in a number of catalog items. The simple reference qualifier for this variable, as seen when switching it to advanced, is:

sys_class_name=cmdb_ci_ip_router^ORsys_class_name=cmdb_ci_ip_switch

Example 1.1: This qualifier will only show routers and switches in the list of CI records.

 

In developing a new catalog item, we want to use this variable set, but for this CI variable we also want to show VPN (cmdb_ci_vpn) records. To meet this requirement, complete these steps:

 

1. Change the advanced Reference qualifier on the variable to something like:

javascript: new refQualUtils().getRefQual(current.cat_item,'sr.ref.vs.ci.cat_items','sr.ref_qual.ci.orig','sr.ref_qual.ci.new');

Example 1.2: Use any compliant names for a script include, function, and 3 system property names.

 

This reference qualifier will pass the catalog item sys_id and 3 system property names to a script include.  Creating 3 system properties will allow us to re-use the script include function for other similar use cases, if the original and new reference qualifiers are simple enough to be stated in an encoded query string as in this example.  For more advanced reference qualifiers, create only the first, or no, system properties, then use similar logic in the script include to ultimately return one of two complete reference qualifiers based on the catalog item sys_id. 

 

2. Create a system property with the Value = the sys_id(s) of the catalog item(s) that will use the new reference qualifier.  Separate sys_ids with a comma, a space, or your favorite emoticon {;-{) 

BradBowman_0-1728324985304.png

3. Create a system property with the Value = the original reference qualifier.

BradBowman_1-1728325090328.png

4. Create a system property with the Value = the new reference qualifier.

BradBowman_2-1728325186801.png

5. Create a script include which will return the complete reference qualifier from one of the system properties, determined by the catalog item being included, or not, in the exception list system property:

var refQualUtils = Class.create();
refQualUtils.prototype = {

    initialize: function() {

    },    

    getRefQual: function(catItemID, propNameCatItem, propNameRQOrig, propNameRQNew) {
        var answer = '';
        if (gs.getProperty(propNameCatItem).indexOf(catItemID) != -1) { //catalog item found in the system property
            answer = gs.getProperty(propNameRQNew); //use the new reference qualifier
        } else {
            answer = gs.getProperty(propNameRQOrig); //use the original reference qualifier
        }
        return answer;
    },

    type: 'refQualUtils'

};

Now you can re-use a variable set, but change a reference qualifier for one or more catalog items, without adversely affecting the other catalog items using the variable set!

 

 

Scene 2: "You Complete Me"

An impressionable reference variable in the mysterious, beautiful world of the Multi-Row Variable Set has stars in its eyes over something beyond its grasp - an "Influencer" in the catalog item world.  This Influencer is a variable with a valuable value that the reference variable in the MRVS world needs to make it work.  Two worlds collide as the MRVS variable wants to "like and subscribe" to the catalog item variable's influential prowess.  How can we bridge this great divide?  "It can't be done." everyone says... Or can it?  Will a heroic solution save the day once again, allowing the Influencer to build a following in the MRVS world?

 

Starting Point: A common workaround in this scenario is to create another variable in the MRVS.  Then, in an onLoad script of the MRVS, populate this other variable on every row with the value of the catalog item variable to use in the reference qualifier (...current.variables.var_name).  

 

Conflict: The downside to this workaround is creating and populating an unnecessary variable that can only be hidden in the Add/Edit Row dialog window, as all MRVS variables are always shown when a MRVS is displayed.

 

Our Hero: A simple solution is to use session data to complete the reference qualifier.

 

Here's an example. We have a catalog item reference variable named v_manager that is a reference to the sys_user table. In the MRVS we have a reference variable named v_employee that is a reference to the sys_user table. When adding or editing rows on the MRVS, before submitting the request and after, we should always only be able to select user records that are active, and the Manager is the user selected in the catalog item variable.

 

If the Employee variable was just another variable in the catalog item, the reference qualifier would easily be:

javascript: 'active=true^manager=' + current.variables.v_manager;

In a MRVS, since 'current' refers to the MRVS, and there is not (yet?) a similar method for reference qualifiers to access catalog item variables, we need to create a way.  To meet this requirement, complete these simple steps:

 

1. Set the advanced reference qualifier on the MRVS variable to something like:

javascript:'active=true^manager=' + session.getClientData('v_manager');

Example 2.1: Use your catalog item variable name.

 

2. Create a catalog client script that applies to the Variable Set:

function onLoad() {
var catItemVarName = 'v_manager'; //catalog item variable name
var catItemVarValue = g_service_catalog.parent.getValue(catItemVarName);
var ga = new GlideAjax('refQualUtils'); //client callable script include name
ga.addParam('sysparm_name', 'setSessionData'); //function in script include
ga.addParam('sysparm_cat_item_var_name', catItemVarName);
ga.addParam('sysparm_cat_item_var_value', catItemVarValue);
ga.getXMLAnswer(getResponse);
}

function getResponse(response) {
//do nothing
}

Example 2.2: Update the script as needed with your catalog item variable name, script include name, and a new function name. 

 

3. Create a Client callable script include to retrieve and set session data from the client script. 

var refQualUtils = Class.create();
refQualUtils.prototype = Object.extendsObject(AbstractAjaxProcessor, {

setSessionData: function() {
var catItemVarName = this.getParameter('sysparm_cat_item_var_name');
var catItemVarValue = this.getParameter('sysparm_cat_item_var_value');
var session = gs.getSession().putClientData(catItemVarName, catItemVarValue);
return;
},
type: 'refQualUtils'
});

Example 2.3: Since the catalog client script and script include use parameters, they can be re-used in every instance of this solution.

 

When the Add or Edit Rows MRVS window opens, the value of the catalog item variable is stored in the session data with the variable name, then the reference qualifier can retrieve the session data.  See the ServiceNow Docs on GlideSession for more information about this API.

 

Now you can include values from catalog item variables in MRVS variable reference qualifiers!

 

FINAL NOTES:

  • These solutions work in both the Native UI and Service Portal, and for catalog items that are standalone or are included in Order Guides!
  • Reference type variables are used in both examples, but the same should work for List, List Collector, Lookup Select Box... any field or variable type that has a reference qualifier!

  • Everything I am using in these examples is in the global scope.  If you have catalog items, scripts, or fields in different applications, adjust the calls accordingly.
  • If you haven't seen this before, this editor has a bad habit of messing up javascript followed by the colon character, so everywhere you see 'javascript:', replace that withBradBowman_0-1728566541473.png 
  • Please comment with your ideas on how to enhance this solution, in the spirit of continuous learning and improvement for all.
Comments
rafaelalves4337
Tera Contributor

@Brad Bowman , great article!!!

 

But I need your help with a similar sittuation.

 

I have 2 variable sets in my form.

Variable set 1 is a single row, and contain a reference variable "Requested_for"

Variable set 2 is a MRVS and contains a reference variable "Computer_name"

 

What I need? to show in my Computer_name variable only the computers assigned_to the user that was selected in requested_for (variable set 1 single row).

 

I have tried different things, nothing worked, any Ideas?

 

Brad Bowman
Kilo Patron
Kilo Patron

@rafaelalves4337 you can treat the requested_for variable the same as if it were not in a single row variable set, so following the second example in this article, you would just want to make sure requested_for is populated before a MRVS is row is added.  Your reference qualifier on the MRVS computer_name variable will be something like 'assigned_to=' + session.getClientData('requested_for')

Stephen Anderso
Tera Expert

So I am testing this solution and the session data from the main catalog string variable 'filter' is passing to the reference qualifier javascript: "u_active=true"+session.getClientData('filter'); configured on the MRVS variable company but what I have noticed is if I change the filter on the main form it takes 2 tries of the Add button on the MRVS for the session data and reference qualifier to update to the value of filter. Is there a recommendation on clearing the session data so it can repopulate every time the onLoad MRVS client script runs?

sagarchilak
Tera Explorer

Hi Brad,

 

I am in the same situation as Scene 1, but here we have a variable set with 4 variables (single row). The requirement is to change the reference qualifier of a variable 'requested for' ( which is one of the variable in variable set ) based on the normal catalog variable (choice with 2 options). For choice 'some one else' the reference variable 'requested for' should include all the active and in active users and for the other choice No change. This should not impact existing configurations or the other catalog items this variable set being used.

 

Your help much Appreciated!!

Brad Bowman
Kilo Patron
Kilo Patron

@Stephen Anderso I am not seeing this behavior in my environment.  My example is a working use case.  When we select a manager, then Add on the Employee MRVS the list of employees for that manager are displayed.  If I cancel or add a row, then change the manager and click Add again (or edit an existing row), now the correct list of employees is displayed, the first time.  Are you using the native UI or Service Portal/ESC? The onLoad Catalog Client Script runs each time the MRVS Add/Edit row dialog launches, so I'm not sure what would be causing the delay, unless the server call is slow, so it's not done running the Script Include when the reference search is executed to apply the qualifier.  Does it work the first time if you change the value of the variable on the main form, then wait a bit after clicking the Add button before checking the MRVS reference values?

Stephen Anderso
Tera Expert

@sagerchilak what I did is to build the qualifier on a hidden string variable on the main form and that is the value that I pass into the session data. This allowed me to show the variable while I was building and confirm the qualifier string is correct. The only issue for me is that it takes 2 clicks of the MRVS Add button to update the reference qualifier based on the Session Data containing my 'filter' variable.

Stephen Anderso
Tera Expert

@Brad Bowman  I will add a debug alert to make sure the client script is firing every time onLoad() Here is my client script applied to the Catalog Item View. I am setting a MRVS division_mvrs as well from the main page.

function onLoad() {
    //set the division_mvrs from main page
      var mainDivisionValue = g_service_catalog.parent.getValue('division');
      g_form.setValue('division_mvr', mainDivisionValue);
   
    // set the filter from the main page into session data for ref qualifier use by company and profit centers
    var catItemVarName = 'filter'; //catalog item variable name
    var catItemVarValue = g_service_catalog.parent.getValue(catItemVarName);
    var ga = new GlideAjax('refQualUtils'); //client callable script include name
    ga.addParam('sysparm_name', 'setSessionData'); //function in script include
    ga.addParam('sysparm_cat_item_var_name', catItemVarName);
    ga.addParam('sysparm_cat_item_var_value', catItemVarValue);
    ga.getXMLAnswer(getResponse);
}

function getResponse(response) {
    //do nothing
}
 
I will keep digging as this may be a miss on my part
Arun Priya
Tera Contributor

Hi @Brad Bowman   I have similar scenario but in change request . The requirement was to filter the field configuration item(cmdb_ci) to display only the CI whose environment is "Production" & Empty, when the environment in the change request form is selected as "'Production".
I tried using script include and client script but it didn't work due to reference Qualifier and dictionary override already existing.
Could you please suggest me a method to implement it.

Brad Bowman
Kilo Patron
Kilo Patron

@Arun Priya This sounds like a different scenario as you want to change the reference qualifier based on the selection of another field, and this is on the change_request table, not Catalog Items.  What is your existing dictionary override for the change_request table?

Arun Priya
Tera Contributor

Hi @Brad Bowman  
Thanks for your reply. 
In dictionary override, the below script is given.
javascript: new TaskUtils().getConfigurationItemFilter(current);

Brad Bowman
Kilo Patron
Kilo Patron

@Arun Priya assuming you are using the out of box CI field and values on the "environment" field, and you have a custom field named "u_environment" on the change_request table with text or choice values that exactly match (case-sensitive) the CI field values - 'Production', 'Test'... this is one way to modify the dictionary override:

First change the actual override by specifying a different function name such as:

 

javascript:new TaskUtils().getChangeConfigurationItemFilter(current);

 

Then copy the old function from the TaskUtilsSNC Script Include into the existing override script named TaskUtils.  Use the function name that you used in the override, then add an if block for your condition.  Given the assumptions I mentioned, the Script Include will look like this:

 

var TaskUtils = Class.create();
TaskUtils.prototype = Object.extendsObject(TaskUtilsSNC, {

    /***************Custom changes****************/
	getChangeConfigurationItemFilter: function(current) {
		if (!current) {
			gs.error("[TaskUtilsSNC.getConfigurationItemFilter] : Invalid parameter");
			return;
		}

		var configItemFilter = '';
		var serviceOfferingFilter = 'sys_class_name!=service_offering';

		configItemFilter += serviceOfferingFilter;

		if (current.u_environment=='Production') {
			configItemFilter += '^environment=Production^ORenvironment=""';
		}

		if (this.CONSTANTS.OPS_FILTER_CLASSNAMES.indexOf(current.sys_class_name + '') > -1)
			configItemFilter += "^operational_statusNOT IN" + new OpsStatusFilter('cmdb_ci').by('CreateTask').join();

		var principalClassFilter = this.getPCFilterEvaluated(current.sys_class_name + '');
		if (principalClassFilter.length > 0)
			configItemFilter += '^' + principalClassFilter;

		return configItemFilter;
	},

    type: 'TaskUtils'
});

The second if block is all that I added.

 

 

 

Arun Priya
Tera Contributor

Hi @Brad Bowman  
It still filters all the values. Could you please check and let me know were is the error?

ArunPriya_0-1753516295657.png

ArunPriya_1-1753516324160.png

 

Brad Bowman
Kilo Patron
Kilo Patron

@Arun Priya I can't see your attachments, but if you are seeing all values, or none, the qualifier is not interpreted correctly.  In my example I created a field on the change_request table named u_environment and assigned it a value of 'Production' on a change ticket.  It is only showing me the CIs that it would have anyway (Operational, and whatever the other criteria of the out of box Script Include are) that also have the 'environment' field blank or = 'Production'.  If you are using a different custom field, CI field, or values you need to adjust this accordingly.

Arun Priya
Tera Contributor

Hi @Brad Bowman  

It did work. Thanks for your support.

sorry  for responding late.

Version history
Last update:
‎10-11-2024 08:46 AM
Updated by:
Contributors