“The requirement is to add an extra task to a set of tasks defined in a graphical workflow once it is running AND have a way of specifying the order AND have the new tasks start automatically when its predecessor completes and start the next one when it completes. In the days of execution/delivery plans I had done this using a rule which set the new predecessor/successor entries but can anyone advise how to approach it with workflow?”
This isn’t an easy problem to solve. I still don’t know if I’ve found a really good way to approach it yet, but here are a couple of things I’ve done in the past (that are currently being used in production systems) that may give you some ideas. Take it for what it’s worth. These probably aren’t perfect solutions, but it’s better than nothing I suppose! I’d love to hear of any feedback you have as you try these out (if you do) because this is an issue that I’m probably going to have to tackle in another month or so again.
Option #1:
Using execution plans and a custom business rule to allow insertion and re-ordering of tasks after the execution plan has initially been attached.–
What I’ve found is that execution plans are still more forgiving when it comes to ad-hoc tasks than workflow is. Because of this, if you have a need for ad-hoc tasks in Change requests, I think it makes a lot of sense to split the workflow processing and use Graphical workflow for approvals, and use Execution plans for task generation. If there’s no need for ad-hoc tasks, then I think Graphical workflow works best.
The challenge with ad-hoc tasks is the sequencing of those tasks. This business rule basically re-orders the entire list of associated change tasks in the execution plan each time the order on a change task changes. This includes when a new task is inserted.
-Table: Change task
-Order: 100
-Runs after insert/update
-Condition: current.order.changes()
function syncTaskOrder(){
//Query for all tasks that are associated to the same parent
var rec = new GlideRecord('change_task');
rec.addQuery('parent', current.parent);
rec.orderBy('order');
rec.query();
while(rec.next()){
//Delete all of the existing successor/predecessor records for the task
var rec1 = new GlideRecord('execution_plan_local');
var qc = rec1.addQuery('predecessor', rec.sys_id);
qc.addOrCondition('successor', rec.sys_id);
rec1.query();
while(rec1.next()){
rec1.deleteRecord();
}
}
//Query for all tasks that are associated to the same parent
var tsk = new GlideRecord(current.sys_class_name);
tsk.addQuery('parent', current.parent);
tsk.orderBy('order');
tsk.query();
var lastID = '';
var lastOrder;
var myIDArray=new Array();
while(tsk.next()){
if(tsk.order > lastOrder){
//Iterate through the sys_id array and create a new successor/predecessor record for each item
for(x in myIDArray){
//Initialize the creation of a new Task Sequencing record
var tsk1 = new GlideRecord('execution_plan_local');
tsk1.initialize();
tsk1.predecessor = myIDArray[x];
tsk1.successor = tsk.sys_id;
tsk1.insert();
}
//Empty the existing array
myIDArray.length = 0;
//Populate the current task sys_id into the array
myIDArray[0] = tsk.sys_id.toString();
}
else if((tsk.order == lastOrder) || !lastOrder){
var myIDArrayLength = myIDArray.length;
if(myIDArrayLength > 0){
//Get the last item in the array
var arrayIDVal = myIDArray[myIDArrayLength - 1];
//Query the Task Sequencing table for that item
var ps = new GlideRecord('execution_plan_local');
ps.addQuery('successor', arrayIDVal);
ps.query();
while(ps.next()){
//Create a new successor/predecessor record for the current task
var ps1 = new GlideRecord('execution_plan_local');
ps1.initialize();
ps1.predecessor = ps.predecessor;
ps1.successor = tsk.sys_id;
ps1.insert();
}
}
//Populate the current task sys_id into the array
myIDArray[myIDArrayLength] = tsk.sys_id.toString();
}
else{
}
lastOrder = Math.round(tsk.order);
lastID = tsk.sys_id.toString();
}
}
I also had to include this business rule to get the orders on the tasks to sync with execution plan task orders initially (although this may have been a bug that has since been fixed so this one may be optional).
-Table: Change task
-Order: 1,100 – This is important!
-Runs before insert
-Condition: current.delivery_task != ”
I suppose the above method could also be used with Service requests, but I haven’t tried it.
Option #2:
I’ve seen this option used for service requests in ServiceNow. You can set up ‘dummy’ service request items that can be added to a service request with a defined number and ordering of tasks. This method could be used with either Graphical workflow or Execution plans.
The client that used this method (like most other customers) didn’t have a defined Service catalog, but wanted to use our Service catalog anyway. Basically what they ended up doing was having a single generic Service catalog item with a couple of steps that got added to any service request. Then they allowed their technicians to add other pre-defined request items to the request on the back end using the ‘Add new item’ button on the Request form. They defined as much as they could, but they also had pre-defined items that had tasks associated with them like ‘One task item’, ‘Three task item’, etc.
The nice thing about this method is that you can still attach your workflow or execution plan directly to those items. The person who needs to add more tasks can simply add another item to the request and it already has its workflow associated with it.
Option #3:
Create sub-tasks that can be manually added to the generated change or catalog tasks. Here is an example of how you could handle this for change requests. Let’s say that all of your change requests have to go through the same 3 steps. However, in certain cases, one or more ad-hoc tasks are needed. You could set up an execution plan (or workflow) to create 3 change tasks, and then create another table called ‘subtask’ that could be manually added to each auto-generated change task as necessary like this…
*Change Request
*Change task 1 –> FROM EXECUTION PLAN
*Subtask 1 –> MANUALLY ADDED
*Subtask 2 –> MANUALLY ADDED
*Change task 2 –> FROM EXECUTION PLAN
*Change task 3 –> FROM EXECUTION PLAN
So Subtasks 1 and 2 are children of Change task 1, and change task 1 does not get closed until subtasks 1 and 2 are closed.
Option #4:
Use a script in a ‘Wait for’ activity in your workflow to query for associated tasks and wait for their completion before moving on. This approach doesn’t deal with the ordering directly, but it does allow you to control tasks that are added outside of the workflow scope. The full solution is documented here in the Service-now wiki.
Hi Mark,
Thanks so much for this. Option 1 is the the way I have done it when using an execution (delivery) plan but it is good to know what we have done is the same as the guru’s!
I think your option 3 sounds the most promising as it looks as though it could co-exist quite nicely with the graphical workflow and we will investigate that.
Do you reckon it could be worthwhile asking for an enhancement in this area (e.g. an option to allow the order field on a task to be used to control this)?
Thanks, Ruth
It’s never a bad idea to request an enhancement to the product. In some cases (like this) the enhancement has already been requested, but the more people ask for it, the more likely it is to get the attention of Service-now developers.
Hi.
For our Change module, we needed a workflow that would be completely open when it came to change tasks. So we built buttons for moving changes from Stage to Stage. Also some custom BRs at ‘implementation’ step to check for any pending change tasks and start the first one(s) by switching their state to Open instead of Pending. When each change task is closed, it checks to see if there’s another child of the same parent that needs to be switched on and switches on the one(s) with the smallest order number. At the moment we have no restrictions on when change tasks are allowed to be closed (you can have a task that involves prep work as opposed to implementation), but it’s easy enough to build a check into a UI action to query the open tasks and make a decision based on that. Anyway, none of our change tasks appear in our Change workflow, it’s all done with BRs. It’s up to the humans to build all the tasks and set their orders appropriately, of course. No auto reordering or anything. But we do allow list edit on this field, so it’s simple to reorder stuff in the related list shown at the bottom of the change record. So far it seems to be working, though I have a few pending requests to tweak it. 🙂
Hi mark,
Nice article.
With my experience, I see the Option 3 and Option 4 are “better” when compared to others(again,its my opinion)
Option 4 : might create some confusion(like attaching two subworkflows) if you have two wait-for’s for the activity which creates a sub flow 🙂
Hi Mark, I am looking at option 1 for a mix of execution plans and workflow. We originially set up with workflows but it just doesn’t manage the ad hoc tasks well. I love how easy it is to work with ad hoc task with the execution plan. Our pre-set tasks include pending final approval and pending verification. As we hit the different stages of the change throught the progression of the tasks, how do I change the state of the ticket? I know I can easily change the state within the workflow but how can the workflow know where we are in the execution of the tasks?
Basically the question is how do I get both methods to talk to each other through the progression of the ticket?
Hi. You’ve basically got 2 options. The first is to use a ‘Script’ or ‘Wait for’ activity to query for the tasks and pull back the necessary information. That way might be a bit more difficult but it’s the best way to integrate with your workflow. The second way is to set some attribute on your parent record from the workflow and then have a business rule that updates the corresponding tickets. Either way, it’s a challenge unless you’ve got some consistent attributes on your tasks (perhaps forced by a template or order).
Looking at option 3 now and not sure what you mean by manually added to each auto-generated task. I am using a workflow to create the required tasks and can create a table called chg_subtask.
Option 3 assumes you create a sub-task table that sits under ‘change_task’ or something similar. Your workflow wouldn’t be involved with the sub-task records at all. Users would simply associate the sub-task records to change task records as needed via a related list or something similar.