This post is written in response to a question I received from a reader about how to handle ad-hoc tasks when you’re using graphical workflow. I’m always open to suggestions on how to improve the site and its content. If you have any ideas, questions, or suggestions for the site just use the Contact link to submit them. Thanks Ruth!

“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()

syncTaskOrder();

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 != ”

current.order = current.delivery_task.order;

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.