L
ately I’ve been doing some service catalog work for a couple of clients and I’ve come across a requirement that I really haven’t had to address before. The requirement deals with order guides…specifically with the ability to control the execution order of individual items within an order guide. You’re probably aware that ServiceNow provides complete control over the ordering of tasks within a catalog item, but what do you do with an order guide that needs to have one item complete before the next 2 items can start? There’s not a simple way to control this behavior by default so I came up with a way and I’ll share it here.
Initial setup
All of this depends on having the ability to identify whether or not an order guide is associated with an item that has been ordered, and which order guide that is. As such, your first step will be to make sure that you are recording the order guide used against the parent request record. This setup is not provided by default but you can add it by following the instructions in this article.
NOTE: This is recorded for you automatically in the Helsinki release and beyond! The instructions below reflect these changes as of the Helsinki release.
Establishing an execution order
Once you’ve validated that the order guide is being populated correctly you’re ready to set up the execution order for your items within that order guide. The solution is actually pretty simple and only requires 2 pieces, both of which are implemented in the workflow for the respective items within an order guide.
Example:
In order to illustrate this setup, I’ll use a simple example. Let’s say that you have an order guide for new hires that includes 2 items; ‘Computer Workstation’ and ‘Computer Software’. Both of these items need to be ordered and delivered as part of the new hire process, but they need to be delivered in a specific order. The ‘Computer Software’ item cannot be started until the ‘Computer Workstation’ item has finished because we need to have a workstation to be able to install the software.
The first step is to modify the graphical workflow associated with item 1 (the ‘Computer Workstation’ catalog item). The workflow should run through the same process whether or not the item is ordered as part of an order guide, but at some point in the workflow (probably right before the end) we need to check if the item is associated with an order guide and then tell the next item to start. The next item in this case is ‘Computer Software’.
We don’t have direct access to the next item, but we can easily set up a ‘Run script’ workflow activity to query for that item and set a value there telling the item that it can start. I’ve found that the simplest way of doing this is to set the ‘State’ field on the next request item record to ‘Work in Progress’. The script below can be used in your ‘Run script’ activity in your workflow. Just replace the name of the ‘Computer Software’ item below with the name of the item in your system.
Name: Start next item
Script:
if(!current.order_guide.nil()){ //Change to 'current.request.u_order_guide' for pre-Helsinki
//Start the 'Computer Software' item next
itemStart('Computer Software');
}
function itemStart(nextItemName){
//Query for the item that should start next
var item = new GlideRecord('sc_req_item');
item.addQuery('request', current.request);
item.addQuery('cat_item.name', nextItemName);
item.query();
while(item.next()){
//Set the item to 'Work in Progress' to initiate the item workflow (requires 'Wait for' activity in item workflow)
item.state = 2;
item.update();
}
}
This script works great, but it’s only part of the puzzle! Unless you tell item 2 (Computer Software) to wait for item 1 (Computer Workstation) then your script isn’t going to work the way you need it to. So, the second configuration change you’ll need to make is to add a ‘Wait for’ activity to the graphical workflow associated with item 2 (Computer Software). The ‘Wait for’ activity placement should probably be the first activity in the item 2 workflow, but will depend on your specific workflow. It should only apply if an order guide is associated with the parent request and the state is ‘Work in Progress.
If you’ve followed the instructions above correctly, you should now be able to order your order guide item and execute the items in the order you’ve specified in the workflows. You can handle any order guide execution order scenario by repeating the same steps in the workflow for each item. If you have multiple items that need to start when item 1 is finished, then simply add those items to the script from item 1 and add the wait for condition to items 2, 3, 4, etc…
Bonus! Copying variable values from one item to another
Because your items are associated under a single order guide, it might also be necessary to pass variable values from one item to another. In order to do this, you’ll need to make sure that each item sharing variable values this way has an identically-named variable to place the value in. Once you’ve got that you could execute the following script inside a workflow ‘Run script’ activity.
function copyVariables(){
//Copy the matching variables from this item to the other item(s) in this request
//Query the item option table for item variables
var rec = new GlideRecord('sc_item_option_mtom');
rec.addQuery('request_item', current.sys_id);
rec.query();
while(rec.next()){
//Query for the same variable associated with the parent Request's items
var itemVars = new GlideRecord('sc_item_option_mtom');
itemVars.addQuery('request_item', '!=', current.sys_id);
itemVars.addQuery('request_item.request', current.request);
itemVars.addQuery('sc_item_option.item_option_new.name', rec.sc_item_option.item_option_new.name.toString());
itemVars.query();
//If we find a matching variable in another item update its value
while(itemVars.next()){
//Get the variable value record
var itemVar = new GlideRecord('sc_item_option');
itemVar.get(itemVars.sc_item_option);
itemVar.value = rec.sc_item_option.value.toString();
itemVar.update();
}
}
}
I had a similar requirement in the past to be able to choose an existing requested item and copy all the variables into a new request. I used JSON to build a string with all the name/value pairs and then set the form with the client script.
Script Include (findItemVariables)
var findItemVariables = Class.create();
findItemVariables.prototype = Object.extendsObject(AbstractAjaxProcessor, {
findVariables: function() {
var item = this.getParameter('sysparm_request_item');
var set = new Packages.com.glideapp.servicecatalog.variables.VariablePoolQuestionSet();
set.setRequestID(item.toString());
set.load();
var vs = set.getFlatQuestions();
var find = {};
for (var i = 0; i < vs.size(); i++) {
if (vs.get(i).getDisplayValue() != "") {
eval("find." + vs.get(i).getName() + "=" + '"' + vs.get(i).getDisplayValue() + '"');
}
}
var json = new JSON();
var text = json.encode(find);
return text;
}
});
Catalog Client Script
//calls the findItemVariables script include
var ga = new GlideAjax('findItemVariables');
ga.addParam('sysparm_name','findVariables');
ga.addParam('sysparm_request_item', g_form.getValue('request_item'));
ga.getXMLWait();
var answer = ga.getAnswer();
answer = eval("(" + answer + ")");
for(x in answer){
if(typeof(answer[x]) == "string"){
eval('g_form.setValue(' + '"' + x + '"' + ',' + '"' + answer[x] + '"' + ');');
}
}
}
Awesome. Thanks for sharing this. If I needed to populate the variables in a client script I would definitely use something like this. If I needed to populate the variables in a workflow or business rule I could use a script like mine above.
This is great. I’m doing something similar, but I’m opening the flood gates after the User Creation RITM is complete on a New Hire. We have to wait for the user to be created in LDAP, sync’d to ServiceNow and update the “Requested For” so it becomes accurate on the Desktop, Software, Phone requested items, etc.
ritm.addQuery('request', current.request.sys_id);
ritm.addQuery('sys_id', '', current.sys_id);
ritm.query();
while(ritm.next()) {
ritm.state = 2;
ritm.update();
}
Hi Mark,
I implemented this and works perfectly. However, given that the “next items” are waiting for the first one to complete IF approved (in my case), how do I get to cancel the workflows for these “next items” if the first one is not approved, as these others depend on this approval?
Thanks in advance for your input.
Cheers,
Luis
You’ll have to query for the other items and close them (which should cancel the workflows). If it doesn’t then you can force the workflow cancellation as shown here…
https://servicenowguru.wpengine.com/scripting/business-rules-scripting/canceling-executing-workflows-task-closure/
Hi Mark,
This is fantastic, thank you. Any ideas on how to set the variable in Step 2 in the latest Eureka release? request.order guide isn’t a variable I can choose… I can plug in parent.tasktype and use that as a Request, but that assumes that every REQ is going to be part of an order guide… bit messy?
Cheers,
-Adam
Have you followed the initial setup listed above? In order to get the ‘request.u_order_guide’ field you have to create it by following the instructions included in the article I link to above.
Of course not! I’m a user and only half-read everything 🙂 I’ll give it a crack tonight and see how I go, thanks for being polite about it!
Cheers,
-Adam
Hi Mark,
Do you know if Geneva or Helsinki has any sort of OOB functionality for this, or are you still using your custom approach with clients?
Thanks!
There’s nothing new in Helsinki or Geneva to do this as far as I know.
Just to validate, do all of the request items still get created at the very beginning? Does this just control when certain request item’s states gets set to “work in progress” as dependent items are completed?
All of the requested item records will always get created right at the beginning regardless of what you do. This helps to control the workflow so that they only get opened and put into people’s work queue when they’re supposed to.
Hi Mark,
I’m a bit confused on the second step. Based on the provided example, it looks like it will wait on the “wait activity” even when the request isn’t made from an Order Guide.
Are you saying there should be a “conditional if activity” before the “wait activity” checking if the Order Guide is set on the request?
Thanks,
-Tyson
Yes, the example shown above would really only apply if the second item was always part of the order guide. A conditional activity in your second item would allow the flexibility of handling multiple types of flows.
Hi Mark,
The script to pass variables between various catalog item should be written in which workflow..I tried putting it in the 1st Requested item but it didnt work… The other scripts work as expceted.