Sunday 26 May 2013

Programmatically vote for a workflow

While workflow voting in Innovator is pretty simple there are times when you might need to vote via a method to get your desired level of automation.

I did this recently for two different clients with two very different needs.

One had some Item that we needed to "park" for a while with no-one being really responsible for them and then via a scheduled server method vote them back into active management and the other  wanted a very clean way of recording receipt of some returned goods and at the same time voting for the advancement of the workflow.

What follows is a sample server method (c#) for achieving the vote:

// Programmatic voting for an activity to facilitate some// additional automation
// called as a generic method.
// Limitations: This method expects that there is only one
// assignee for the vote.
// Passed in is the id of the Item that we need to vote for:
// expecting: <itemId>id</itemId>
// <itemType>type</itemType>
// <votingPath>path</votingPath>

Innovator inn = this.getInnovator();
string itemID = this.getProperty("itemId","");
string itemType = this.getProperty("itemType","");
string votePath = this.getProperty("votingPath","");
if ( itemID == "" ){
    return inn.newError("No Item ID supplied");
}
if ( itemType == ""){
    return inn.newError("No Item type supplied"); 
}
if ( votePath == "" ){
   return inn.newError("No voting path supplied"); 
}
// retrieve the "Active" workflow activity:
Item wfcItem = inn.newItem(itemType,"get"); // work flow controlled Item wfcItem
wfcItem.setID(itemID);
Item workflow = inn.newItem("Workflow","get");
Item workflowProcess = inn.newItem("Workflow Process","get");
Item wfpa = inn.newItem("Workflow Process Activity","get");
Item activity = inn.newItem("Activity","get");
activity.setProperty("state","Active");
Item activityAssign = inn.newItem("Activity Assignment","get");
Item wpp = inn.newItem("Workflow Process Path","get");
wpp.setProperty("name",votePath);
activity.addRelationship(activityAssign);
activity.addRelationship(wpp);
wfpa.setRelatedItem(activity);
workflowProcess.addRelationship(wfpa);
workflow.setRelatedItem(workflowProcess);
wfcItem.addRelationship(workflow);
wfcItem = wfcItem.apply();

if ( wfcItem.isError() ){
    return inn.newError("Error retrieving Workflow Process Item: " + wfcItem.getErrorString()); 
}
Item wfPaths = wfcItem.getItemsByXPath("//Item[@type='Workflow Process Path']");
if ( wfPaths.getItemCount() != 1 ){
    return inn.newError("Unable to get voting path: " + votePath);
}
string submitPathId = wfPaths.getItemByIndex(0).getID();

Item act = wfcItem.getItemsByXPath("//Item[@type='Workflow Process Activity']/related_id/Item[@type='Activity']");
if ( act.getItemCount() != 1 ){
    return inn.newError("Unable to get activity"); 
}
string actId = act.getID();
string vote = votePath;
string comment = "";
string assignId = "";
Item actAss = wfcItem.getItemsByXPath("//Item[@type='Activity Assignment']");
if ( actAss.getItemCount() != 1){
    return inn.newError("Unable to get activity assignment"); 
}
assignId = actAss.getID();
// Build the voting request
StringBuilder voteXml = new StringBuilder("");
voteXml.Append("<Item type=\"Activity\" action=\"EvaluateActivity\">");
voteXml.Append(" <Activity>{0}</Activity>");
voteXml.Append(" <ActivityAssignment>{1}</ActivityAssignment>");
voteXml.Append(" <Paths>");
voteXml.Append(" <Path id=\"{2}\">{3}</Path>");
voteXml.Append(" </Paths>");
voteXml.Append(" <DelegateTo>0</DelegateTo>");
voteXml.Append(" <Tasks />");
voteXml.Append(" <Variables />");
voteXml.Append(" <Authentication mode=\"\" />");
voteXml.Append(" <Comments>{4}</Comments>");
voteXml.Append(" <Complete>1</Complete>");
voteXml.Append("</Item>");

// Submit the vote
Item res = inn.newItem();
res.loadAML(String.Format(voteXml.ToString(),actId,assignId,submitPathId,vote,comment));
res = res.apply();

return res;


This method is called from a client method:

// vote for the RMA to move down the "Receive" path
var body = "<itemId>" + myItem.getID() + "</itemId><itemType>MyItemType</itemType>votingPath>MyPath</votingPath>";
var voteRes = inn.applyMethod("FT Programmatic Vote", body);
if (voteRes.isError()) {
    alert("Error voting for activity: " + myItem.getProperty("item_number") + ": " + voteRes.getErrorString());
}