Brad Tilton
ServiceNow Employee

In my last post, we created a multi-row variable set, added variables, then showed it in action by filling out the variables and checking out. In this post we'll use some of the new methods described here to access and set values. For the purposes of this post we're going to be using background scripts (which received some new functionality in London) in the global scope. 

The first thing we'll look at is the JSON object returned by referencing the MRVS. In the following script we're using the sys_id of therequest item ordered in the last post to get the GlideRecord object for that request item. Then we're populating the mrvs variable with the access_list variable set.

 

var mrvs;
var itemID = '70506da8db002300e69dfbef2996194a';
var ritmGR = new GlideRecord('sc_req_item');
if (ritmGR.get(itemID)) {
    mrvs = ritmGR.variables.access_list;
}
gs.print(mrvs);

 

Returns:

 

*** Script: [ {
  "application" : "829e953a0ad3370200af63483498b1ea",
  "access_level" : "read"
}, {
  "application" : "2811a2efc0a8000b0069bb464f215ff5",
  "access_level" : "full",
  "business_justification" : "This field is mandatory because I selected full access"
}, {
  "application" : "28110ea1c0a8000b003abee48ecbc3fa",
  "access_level" : "read_write"
} ]

 

The first thing I noticed was that it appears that only variables with values are included in the json object as the first and third elements don't have a business_justification. From here, we can get more granular.

NOTE: In order to save space the following scripts will assume we have the variable mrvs from the first script above that represents the multi-row variable set. 

 

//get all the values in a single column
gs.print(mrvs.application);
//get the number of rows in the multi-row variable set
gs.print(mrvs.getRowCount());

 

Returns:

 

*** Script: [829e953a0ad3370200af63483498b1ea, 2811a2efc0a8000b0069bb464f215ff5, 28110ea1c0a8000b003abee48ecbc3fa]
*** Script: 3

 

Based on this we see that we can use the variableset.variablename will give us all of the values from the variables column in an array. This could be useful if we wanted to do some sort of check against the applications before looking at the level of access needed. We also see that we have the familiar getRowCount() to use to see how many rows are in the MRVS. We can also usevariableset = <JSON Object> and variableset.variablename = <Array of values> to set values or the whole variable set or all rows in a column.

Now we'll use getRowCount and getRow to iterate through the rows, and then getRow to print the values of our application and access_level variables:

 

var rowCount = mrvs.getRowCount();
for (var i = 0; i < rowCount; i++) {
	var row = mrvs.getRow(i);
	var app = row.application;
	var accessLevel = row.access_level;
	gs.print(app + ' ' + accessLevel);
}

 

Returns:

 

*** Script: 829e953a0ad3370200af63483498b1ea read
*** Script: 2811a2efc0a8000b0069bb464f215ff5 full
*** Script: 28110ea1c0a8000b003abee48ecbc3fa read_write

 

Now we're going to use addRow to add a row and populate the variables in the row using setCellValue and setting it directly using the row objects property for the variable:

 

var newRow = mrvs.addRow();
//set the value using row.setCellValue('<var_name>',value)
newRow.setCellValue('application', '829e953a0ad3370200af63483498b1ea');
//set the value using row.<var_name> = value
newRow.access_level = 'read_write';
gs.print(mrvs);

 

Returns:

 

*** Script: [ {
  "application" : "829e953a0ad3370200af63483498b1ea",
  "access_level" : "read"
}, {
  "application" : "2811a2efc0a8000b0069bb464f215ff5",
  "access_level" : "full",
  "business_justification" : "This field is mandatory because I selected full access"
}, {
  "application" : "28110ea1c0a8000b003abee48ecbc3fa",
  "access_level" : "read_write"
}, {
  "application" : "829e953a0ad3370200af63483498b1ea",
  "access_level" : "read_write"
} ]

 

We now have a fourth row in the catalog table variable with the values we set in the script. Notice that both methods worked to set the values. There's also a method for deleting a row, but I'm usually against deleting anything in ServiceNow so I'm not going to use it here. 

Lastly, I wanted to post the notes and limitations from the docs article here as they're important to know when working with the MRVS:

  1. You can only set a variable in a before business rule. Variables set in an after rule are not written to the database.
  2. There is nothing in place to prevent namespace collision with variables. Creating two variables named computer_speed would result in only one of them showing up; the second one would overwrite the first one.
  3. Date/time variables use the same time zone formatting and storage rules as all other dates in the system. They are stored internally in GMT, but translated into the user's local time zone and format for display.

I hope this post was helpful as you're exploring how to use the values from London's new Multi-row variable set in your workflows, scheduled jobs, and business rules.

 

151 Comments
shail_phillip
Tera Contributor

Hi @Brad Tilton ,

 

I have an Business rule which runs on "insert" ,  take all the requested item variables & value  and store in description of sc task.

i wanted to make generic for mvrs

 

here is my code

 

function getmrvsvariable() {

//_checkMVR();

var ritmGR = new GlideRecord('sc_req_item');
if (ritmGR.get(itemID)) {
var mrvsName = _checkMVR(ritmGR.cat_item);

gs.log(" mrvsName " + mrvsName, "shail");

if (mrvsName != "") {
mrvs = ritmGR.variables.mrvsName;
gs.log(" ritmGR.variables.mrvsName " + ritmGR.variables.mrvsName, "shail");
return mrvs;
}
}

}

function _checkMVR(item) {
gs.log(" inside checkmvr " + item, "shail");
var internalName = "";

var gr = new GlideRecord("io_set_item");
gr.addEncodedQuery("sc_cat_item=" + item + "^variable_set.type=one_to_many");
gr.query();
if (gr.next()) {
internalName = gr.variable_set.internal_name;
gs.log(" internalName " + internalName, "shail");

}

return internalName;
}

 

All is working fine but below logs is giving undefined value

gs.log(" ritmGR.variables.mrvsName " + ritmGR.variables.mrvsName, "shail");

 

Could you please help on this .

 

Thanks

kevclark
Tera Contributor

The ritmGR.variables object will contain variable and MRVS objects identified by their internal names - usually in snake_case, I wouldn't expect an camelCase object to be correct, so mrvsName is probably not a valid Variable Set internal name - It looks like you're trying to retrieve the ACTUAL internal name in your function but trying to mix and match javascript variables (e.g. mrvsName) with the objects found in the real ritmGR.variables object.

 

Is the internal name of your multi row variable on the sc_req_item with the sys_ID defined in itemID actually called "mrvsName"?  

 

If not perhaps you meant to pass it the string contained in that var as an expression using Bracket Notation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors)?    Maybe try the syntax ritmGR.variables[mrvsName] instead of dotwalking to an object explicitly called mrvsName.

Suvedha
Tera Expert

Hi Brad,

 

Thank you for the useful post.

 

In my case, I couldn't get display value for the sysid.

In need to display the value, I am not getting it.

 

I used getDisplayValue();

 

Please help me out.

 

Thanks & Regards,

Suvedha

shail_phillip
Tera Contributor

Hello @Brad Tilton ,

 

I am sure application is a reference field ,Is there way to get Display value in place of sys id of reference field?.

 

Thanks

Shane J
Tera Guru

Below is the portion of a massive mail script we use that is specific to variables and MRVs, including what I think you're looking for @shail_phillip 

 

        // INSERT REQUEST VARIABLES
        if (current.sys_class_name == 'sc_req_item' || current.sys_class_name == 'sc_task' || current.sysapproval.getRefRecord().getTableName() == 'sc_req_item') { // || current.sysapproval.getRefRecord().getTableName() == 'sc_task') { //Start all Variables

            if (current.sys_class_name == 'sc_req_item' || current.sys_class_name == 'sc_task') { //Requested Items only
                var rec = current.sys_id;
                var recV = current.variables;
                var variables = current.variables.getElements();
            }
            if (current.sysapproval.getRefRecord().getTableName() == 'sc_req_item' || current.sysapproval.getRefRecord().getTableName() == 'sc_task') { //Approvals for Requested items only
                rec = current.sysapproval.sys_id;
                recV = current.sysapproval.variables;
                variables = current.sysapproval.variables.getElements();
            }
            if (current.sys_class_name == 'sc_task') {
                rec = current.request_item.sys_id;
            }
            if (current.sysapproval.getRefRecord().getTableName() == 'sc_task') {
                rec = current.sysapproval.request_item.sys_id;
            }

            if (variables.length > 0) {
                template.print('<p><strong>Request Details</strong></p>');
                //Create the Variables table
                template.print('<div class="col-xs-10 col-md-9 col-lg-8 form-field input_controls"><p><table style="border-collapse: collapse;border-right:1px solid silver;border-bottom:1px solid silver;width:96%"><tr>');
                template.print('<td style="padding:10px !important;border-left:1px solid silver;border-top:1px solid silver;background-color:WhiteSmoke;overflow-wrap:normal;width:50%">Question</td>');
                template.print('<td style="padding:10px !important;border-left:1px solid silver;border-top:1px solid silver;background-color:WhiteSmoke;overflow-wrap:normal;width:50%">Answer</td>');
                template.print('</tr>');

                var relatedVarseQuery = 'request_item=' + rec + '^sc_item_option.item_option_new.active=true^sc_item_option.item_option_new.hidden=false^sc_item_option.item_option_new.question_textISNOTEMPTY^sc_item_option.valueISNOTEMPTY^sc_item_option.item_option_new.u_remove_from_notifications=false^sc_item_option.item_option_new.type!=25';
                var relatedVars = new GlideRecord('sc_item_option_mtom');
                relatedVars.addEncodedQuery(relatedVarseQuery);
                relatedVars.orderBy('sc_item_option.order');
                relatedVars.query();
                while (relatedVars.next()) {
                    var relatedVarsValue = '';
                    if (relatedVars.sc_item_option.u_display_value != '') {
                        relatedVarsValue = relatedVars.sc_item_option.u_display_value;
                    } else {
                        relatedVarsValue = relatedVars.sc_item_option.value;
                    }
                    if (relatedVarsValue != 'false' && relatedVarsValue != '') {
                        //template.print(relatedVars.sc_item_option.order.toString() + ' ' + relatedVars.sc_item_option.item_option_new.question_text + ' ' + relatedVarsValue + '<p>');

                        template.print('<tr><td style="padding:10px !important;border-left:1px solid silver;border-top:1px solid silver;overflow-wrap:normal;width:50%">' + relatedVars.sc_item_option.item_option_new.question_text + '</td><td style="padding:10px !important;border-left:1px solid silver;border-top:1px solid silver;word-break:break-all;width:50%">' + relatedVarsValue + '</td></tr>');
                    }
                }
                template.print('</table></div>');
            } 

           //Start MRVs
            var allMRVS = [];
            //loop through the variables looking for multi-row variable sets
            for (var eachVar in recV) {
                //found one!
                if (recV[eachVar].isMultiRow()) {

                    var grMRV = new GlideRecord('sc_req_item');
                    if (grMRV.get(rec))

                        //////////////////////////////////////////
                        //get Multi-Row Variable Set structure
                        var mrvsDefintion = {},
                            title = '';
                    var mrvsStructure = new GlideRecord('item_option_new');
                    mrvsStructure.addEncodedQuery('active=true^variable_setISNOTEMPTY^variable_set.internal_name=' + eachVar);
                    mrvsStructure.orderBy('order');
                    mrvsStructure.query();

                    while (mrvsStructure.next()) {
                        //What is the title of this MRVS?
                        if (title == '') title = mrvsStructure.variable_set.title.toString();
                        //What about each of the variables
                        mrvsDefintion[mrvsStructure.name.toString()] = {
                            "name": mrvsStructure.name.toString(),
                            "question": mrvsStructure.question_text.toString(),
                            "sys_id": mrvsStructure.sys_id.toString(),
                            "type": mrvsStructure.type.getDisplayValue(),
                            "table": mrvsStructure.type.getDisplayValue() == "Reference" ? mrvsStructure.reference.getValue() : mrvsStructure.type.getDisplayValue() == "List Collector" ? mrvsStructure.list_table.getValue() : "",
                            "order": mrvsStructure.order.toString(),
                            "row": "",
                            "value": ""
                        };
                    }

                    //get the Multi-Row Variable Set values
                    var mrvsValue = [];
                    var mrvsAnswers = new GlideRecord('sc_multi_row_question_answer');
                    mrvsAnswers.addEncodedQuery('parent_id=' + rec + '^variable_set.internal_name=' + eachVar);
                    mrvsAnswers.orderBy('row_index');
                    mrvsAnswers.query();

                    while (mrvsAnswers.next()) {
                        var thisValue = '';
                        var thisVariable = mrvsAnswers.item_option_new.name.toString();
                        if (mrvsDefintion[thisVariable].type == 'List Collector' && mrvsDefintion[thisVariable].table != '') {
                            var list_c = mrvsAnswers.value.toString();
                            if (list_c != '') {
                                var myString1Split = list_c.split(',');
                                var list_str = '';
                                for (j = 0; j < myString1Split.length; j++) {
                                    var mylst = new GlideRecord(mrvsDefintion[thisVariable].table);
                                    mylst.addQuery('sys_id', myString1Split[j]);
                                    mylst.query();
                                    if (myString1Split.length > 1) {
                                        while (mylst.next()) {
                                            list_str += mylst.getDisplayValue() + ', ';
                                        }
                                    } else {
                                        while (mylst.next()) {
                                            list_str += mylst.getDisplayValue();
                                        }
                                    }
                                }
                                thisValue = list_str;
                            }
                        }
                        if (mrvsDefintion.hasOwnProperty(thisVariable)) {
                            //Get value 
                            thisValue = mrvsAnswers.value.toString();
                            //if this is a reference field get the display value
                            if (mrvsDefintion[thisVariable].type == 'Reference' && mrvsDefintion[thisVariable].table != '') {
                                var getDisplayVal = new GlideRecord(mrvsDefintion[thisVariable].table);
                                if (getDisplayVal.get(thisValue)) {
                                    thisValue = getDisplayVal.getDisplayValue();
                                }
                            }

                            //If this is a select box with choices, get the question_choice (display value)
                            if (mrvsDefintion[thisVariable].type == 'Select Box') {
                                var getQuestionChoice = new GlideRecord('question_choice');
                                getQuestionChoice.addEncodedQuery('question=' + mrvsDefintion[thisVariable].sys_id + '^value=' + thisValue);
                                getQuestionChoice.query();
                                if (getQuestionChoice.next()) {
                                    thisValue = getQuestionChoice.text.toString();
                                }
                            }
                            mrvsDefintion[thisVariable].value = thisValue;
                            mrvsDefintion[thisVariable].row = mrvsAnswers.row_index.toString();
                            mrvsValue.push(JSON.parse(JSON.stringify(mrvsDefintion[thisVariable]))); //push in a clean object
                        }
                    }
                    allMRVS.push({
                        "name": title,
                        "details": mrvsValue
                    });
                }
            }

            var results = JSON.stringify(allMRVS);
            var allMRVS_b = JSON.parse(results);
            allMRVS_b.forEach(function(mvrs) {
                if (mvrs.details.length != 0) {
                    //First, sort results by row then by order
                    mvrs.details.sort(function(a, b) {
                        if (a.row === b.row) {
                            return a.order > b.order ? 1 : -1;
                        }
                        return a.row > b.row ? 1 : -1;
                    });

                    template.print('Pop-up Table Submitted fields (' + title + ')\n');
                    template.print('<div class="col-xs-10 col-md-9 col-lg-8 form-field input_controls"><p><table style="border-collapse: collapse;border-right:1px solid silver;border-bottom:1px solid silver;width:96%"><tr>');
                    template.print('<td style="padding:10px !important;border-left:1px solid silver;border-top:1px solid silver;background-color:WhiteSmoke;overflow-wrap:normal;width:50%">Question</td>');
                    template.print('<td style="padding:10px !important;border-left:1px solid silver;border-top:1px solid silver;background-color:WhiteSmoke;overflow-wrap:normal;width:50%">Answer</td>');
                    template.print('</tr>');
                    var x = 0;
                    //gs.info(thisEntry.row);
                    mvrs.details.forEach(function(thisEntry, index) {
                        if (mvrs.details[index - 1].row != mvrs.details[index].row) { //Used to get Row number
                            x++;
                        }

                        template.print('<tr><td style="padding:10px !important;border-left:1px solid silver;border-top:1px solid silver;overflow-wrap:normal;width:50%">' + x + '-' + thisEntry.question + '</td><td style="padding:10px !important;border-left:1px solid silver;border-top:1px solid silver;overflow-wrap:normal;word-break:break-all;width:50%">' + thisEntry.value + '</td></tr>');
                    });
                    template.print('</table></div>');
                }
            });

 

shail_phillip
Tera Contributor

Hello @Shane J ,

 

Thank you for your reply and it helpful .

there is an issue i am facing , when i do test from fix script for catalog task it is working perfectly but same script i trying in Business rule (before or after) it is not giving the output

this is not giving the desired output  if (recV[eachVar].isMultiRow()) 
 
I tried many ways to do but not working 
Shane J
Tera Guru

Can you provide the latest version of your script?

shail_phillip
Tera Contributor

Hey @Shane J 

 

Here is my Script:

 

So i have Business rule to run this script on insertion of catalog task, where all requested item's variable data should be copy on catalog task description 

 

   var final_result = "";
    var recV = current.variables;
    
    rec = current.request_item.sys_id;


    //Start MRVs
    var allMRVS = [];
    //loop through the variables looking for multi-row variable sets
    for (var eachVar in recV) {
        //found one!
        
        if (recV[eachVar].isMultiRow()) {
            
            var grMRV = new GlideRecord('sc_req_item');
            if (grMRV.get(rec))

                //////////////////////////////////////////
                //get Multi-Row Variable Set structure
                var mrvsDefintion = {},
                    title = '';
            var mrvsStructure = new GlideRecord('item_option_new');
            mrvsStructure.addEncodedQuery('active=true^variable_setISNOTEMPTY^variable_set.internal_name=' + eachVar);
            mrvsStructure.orderBy('order');
            mrvsStructure.query();

            while (mrvsStructure.next()) {
                //What is the title of this MRVS?
                if (title == '') title = mrvsStructure.variable_set.title.toString();
                //What about each of the variables
                mrvsDefintion[mrvsStructure.name.toString()] = {
                    "name": mrvsStructure.name.toString(),
                    "question": mrvsStructure.question_text.toString(),
                    "sys_id": mrvsStructure.sys_id.toString(),
                    "type": mrvsStructure.type.getDisplayValue(),
                    "table": mrvsStructure.type.getDisplayValue() == "Reference" ? mrvsStructure.reference.getValue() : mrvsStructure.type.getDisplayValue() == "List Collector" ? mrvsStructure.list_table.getValue() : "",
                    "order": mrvsStructure.order.toString(),
                    "row""",
                    "value"""
                };
            }

            //get the Multi-Row Variable Set values
            var mrvsValue = [];
            var mrvsAnswers = new GlideRecord('sc_multi_row_question_answer');
            mrvsAnswers.addEncodedQuery('parent_id=' + rec + '^variable_set.internal_name=' + eachVar);
            mrvsAnswers.orderBy('row_index');
            mrvsAnswers.query();

            while (mrvsAnswers.next()) {
                var thisValue = '';
                var thisVariable = mrvsAnswers.item_option_new.name.toString();
                if (mrvsDefintion[thisVariable].type == 'List Collector' && mrvsDefintion[thisVariable].table != '') {
                    var list_c = mrvsAnswers.value.toString();
                    if (list_c != '') {
                        var myString1Split = list_c.split(',');
                        var list_str = '';
                        for (j = 0; j < myString1Split.length; j++) {
                            var mylst = new GlideRecord(mrvsDefintion[thisVariable].table);
                            mylst.addQuery('sys_id', myString1Split[j]);
                            mylst.query();
                            if (myString1Split.length > 1) {
                                while (mylst.next()) {
                                    list_str += mylst.getDisplayValue() + ', ';
                                }
                            } else {
                                while (mylst.next()) {
                                    list_str += mylst.getDisplayValue();
                                }
                            }
                        }
                        thisValue = list_str;
                    }
                }
                if (mrvsDefintion.hasOwnProperty(thisVariable)) {
                    //Get value 
                    thisValue = mrvsAnswers.value.toString();
                    //if this is a reference field get the display value
                    if (mrvsDefintion[thisVariable].type == 'Reference' && mrvsDefintion[thisVariable].table != '') {
                        var getDisplayVal = new GlideRecord(mrvsDefintion[thisVariable].table);
                        if (getDisplayVal.get(thisValue)) {
                            thisValue = getDisplayVal.getDisplayValue();
                        }
                    }

                    //If this is a select box with choices, get the question_choice (display value)
                    if (mrvsDefintion[thisVariable].type == 'Select Box') {
                        var getQuestionChoice = new GlideRecord('question_choice');
                        getQuestionChoice.addEncodedQuery('question=' + mrvsDefintion[thisVariable].sys_id + '^value=' + thisValue);
                        getQuestionChoice.query();
                        if (getQuestionChoice.next()) {
                            thisValue = getQuestionChoice.text.toString();
                        }
                    }
                    mrvsDefintion[thisVariable].value = thisValue;
                    mrvsDefintion[thisVariable].row = mrvsAnswers.row_index.toString();
                    mrvsValue.push(JSON.parse(JSON.stringify(mrvsDefintion[thisVariable]))); //push in a clean object
                }
            }
            allMRVS.push({
                "name": title,
                "details": mrvsValue
            });
        }
    }

    var results = JSON.stringify(allMRVS);
    var allMRVS_b = JSON.parse(results);
    allMRVS_b.forEach(function(mvrs) {
        if (mvrs.details.length != 0) {
            //First, sort results by row then by order
            mvrs.details.sort(function(a, b) {
                if (a.row === b.row) {
                    return a.order > b.order ? 1 : -1;
                }
                return a.row > b.row ? 1 : -1;
            });


            var x = 0;
            //gs.info(thisEntry.row);
            mvrs.details.forEach(function(thisEntry, index) {
                if (mvrs.details[index - 1].row != mvrs.details[index].row) { //Used to get Row number
                    x++;
                    final_result  += "\n\n";
                }

                final_result += thisEntry.question + ' : ' + thisEntry.value + "\n";
            });

        }
    });
    
    return final_result

}
 
Thanks
Shane J
Tera Guru

@shail_phillip - I was thinking about this and if it works fine via Fix Script it could be a timing issue, in that the variable records don't exist yet when your BR runs.

 

Maybe try to add a delay (3-5 sec I think would do it, that's what I'd do in a workflow), such as the Solution in this post:  https://www.servicenow.com/community/developer-forum/gs-sleep-in-business-rule/m-p/1627305

Nick43
Tera Contributor

Brad, 

thank you for the Blog post. I would also like to provide this util that will provide all information on MRVS variables: global.GlobalServiceCatalogUtil()

 

Thanks