MultiInstance Tasks

BPMN Model

We’ll be using the following files from spiff-example-cli.

Loop Task

Suppose we want our customer to be able to select more than one product.

We’ll run our ‘Select and Customize Product’ Call Activity as a Loop Task.

First we’ll update the Call Activity’s model to ask the customer if they would like to continue shopping.

../_images/call_activity_multi.png

Selecting more than one product

We’ve also added a postScript to the user task. Spiffworkflow provides extensions that allow scripts to be run before and after tasks. It is often the case that data needs to be manipulated before and after a task. We could add regular Script Tasks before and after, but diagrams quickly become cluttered with scripts, and these extensions are intended to alleviate that.

We use a postScript to add the current product to a list of products.

products.append({
'product_name': product_name,
'product_quantity': product_quantity,
'product_color': product_color,
'product_size': product_size,
'product_style': product_style,
'product_price': product_price,
})

We’ll use a preScript on the first User Task (Select Product and Quantity) to initialize these variables to None each time we execute the task.

Loop Tasks run either a specified number of times or until a completion condition is met. Since we can’t know in advance how many products the customer will select, we’ll add continue_shopping == 'Y' as a completion condition. We’ll re-run this Call Activity as long as the customer indicates they want to choose another product. We’ll also set up the list of products that we plan on appending to.

We also added a postscript to this activity to delete the customization values so that we won’t have to look at them for the remainder of the workflow.

../_images/loop_task.png

Call Activity with Loop

We also needed to update our Script Task and the Instructions of the Review Order Task to handle an array of products rather than a single product.

Here is our new script

order_total = sum([ p['product_quantity'] * p['product_price'] for p in products ]) + shipping_cost

And our order summary

Order Summary
{% for product in products %}
{{ product['product_name'] }}
Quantity: {{ product['product_quantity'] }}
Price: {{ product['product_price'] }}
{% endfor %}
Shipping Cost: {{ shipping_cost }}
Order Total: {{ order_total }}

Parallel MultiInstance

We’ll also update our ‘Retrieve Product’ Task and ‘Product Not Available’ flows to accommodate multiple products. We can use a Parallel MultiInstance for this, since it does not matter what order our Employee retrieves the products in.

../_images/multiinstance_task_configuration1.png

MultiInstance Task configuration

We’ve specified products as our Input Collection and product as our Input Item. The Input Collection should be an existing collection. We’ll create a task instance for each element of the collection, and copy the value into the Input Item; this is how we’ll access the data of the element.

We also specified availability as our Output Collection. Since this variable does not exist, SpiffWorkflow will automatically create it. You can use an existing variable as an Output Collection; in this case, its contents will be updated with new values. The Output Item will be copied out of the child task into the Output Collection.

The ‘Retrieve Product’ task creates product_available from the form input.

Since our input is a list, our output will also be a list. It is possible to generate different output types if you create the output collections before referring to them.

We have to update our gateway condition to handle the list:

../_images/availability_flow.png

Gateway Condition

Sequential MultiInstance

SpiffWorkflow also supports Sequential MultiInstance Tasks for collections, or if the loopCardinality is known in advance, although we have not added an example of this to our workflow. Their configuraiton is almost idenitcal to the configuration for Parallel MultiInstance Tasks.

Running The Model

If you have set up our example repository, this model can be run with the following command:

./spiff-bpmn-runner.py -p order_product \
   -d bpmn/tutorial/product_prices.dmn bpmn/tutorial/shipping_costs.dmn \
   -b bpmn/tutorial/top_level_multi.bpmn bpmn/tutorial/call_activity_multi.bpmn