This how-to is work in progress.
Written mostly by inexperienced developer, needs review.
You've been warned.
What are interaction workflows
Normal workflows are used to implement effects of user interaction - validating of objects, ordering, planning and confirming of orders, etc.
By interaction workflows it is possible to define interactions on objects - in other words catching some methods invocations and defining actions, which shall happen on object. You can think of it as something similar to a "trigger" in relation database, a "hook", or a "side effect" of other actions. In other words, you can add some behaviour to an object without altering the object (class) itself.
For example, you may want to set some values on object, when user edits any property on this object. To gain such possibility you shall use interaction workflow.
Examples of interaction workflow are in business templates - very interesting usage is in packing_list_interaction_workflow.
Example case of interaction workflow
We want to set title on Purchase Order and Sale Order objects.
Go to Contents tab of workflow tool, create custom_title_setter_sale_order_interaction_workflow, choose interaction workflow.
Now go to Workflows tab of workflow tool and associate custom_title_seter_sale_order_interaction_workflow with Purchase Order and Sale Order portal types, then return to defining of interaction workflow.
Create scripts: PurchaseOrder_titleSetter and SaleOrder_titleSetter.
1 ## Script (Python) "PurchaseOrder_titleSetter"
2 ##bind container=container
3 ##bind context=context
4 ##bind namespace=
5 ##bind script=script
6 ##bind subpath=traverse_subpath
7 ##parameters=state_change
8 ##title=
9 ##
10 purchase_order = state_change['object']
11
12 generated_title = 'PO-%s'%(purchase_order.getId(),)
13
14 if purchase_order.getTitle() != generated_title:
15 # we do not want to set same title again and again
16 purchase_order.setTitle(generated_title)
1 ## Script (Python) "SaleOrder_titleSetter"
2 ##bind container=container
3 ##bind context=context
4 ##bind namespace=
5 ##bind script=script
6 ##bind subpath=traverse_subpath
7 ##parameters=state_change
8 ##title=
9 ##
10 sale_order = state_change['object']
11
12 generated_title = 'SO-%s'%(sale_order.getId(),)
13
14 if sale_order.getTitle() != generated_title:
15 # we do not want to set same title again and again
16 sale_order.setTitle(generated_title)
So now we have to define interaction for our portal types. Go to Interactions tab, and create PurchaseOrder_titleSetInteraction. Edit it, and for set fields to:
Filter : Purchase Order
Trigger Method Id(s) : edit _edit _setDestinationValue _setDestinationSectionValue _setSourceValue _setSourceSectionValue order plan confirm cancel
Script (after) : PurchaseOrder_titleSetter
Create interaction SaleOrder_titleSetInteraction and set fields properly. As exercise base definitions of above values.
Now create Purchase or Sale order and watch what happens. Set some values, invoke another workflow actions, edit event Title.
Most important part in interaction definition is Trigger Method Id(s). To catch all typical invocations edit and _edit would be enough. But to catch order_workflow transitions we have to add order, plan, confirm and cancel. And last, but not least, to catch relating order with source/destination section we have to add _setDestinationValue, _setDestinationSectionValue, _setSourceValue, _setSourceSectionValue.
Reference documentation
Creating interaction workflows
To create interaction workflow navigate to workflow tool Contents tab add new workflow, type its Id and choose Type interaction_workflow.
Proposed Id grammar is someprefix[_portal_or_meta_type][_description_]interaction_workflow, eg:
- custom_packing_list_interaction_workflow
- custom_order_line_title_setter_interaction_workflow
Interaction workflows can be organised in various ways - per portal type (one interaction workflow for each portal type which needs it), or per function (aspect), or something else. For example, some developers find it convenient to create one security_update_interaction_workflow which is then used for every portal type which requires a security update upon certain actions.
Properties
Same as in normal workflows.
Interactions
List of possible interactions, which every can have additional constraints:
Filter - interaction will be invoked only on chosen portal types ((!) this list will be filled after association interaction workflows with portal types)
Trigger type :
Automatic - TODO
Initiated by WorkflowMethod - TODO
Trigger Method Id(s) - list of methods, which will trigger workflows. You can catch many things:
edit and _edit (though think twice, this would be triggered very often)
normal workflow actions (like stop, cancel, confirm)
low level methods (manage_afterClone)
category setters (_setResourceValue,_setDestinationValue)
property setters (use _set[PropertyName].* to catch all invocations of the setter)
Script (before), Script (after) and Activities (later) - list of script which will be invoked before action happens, after action happens and which will be invoked in activities.
Experiment with other scripts which are invoked by those actions. Guard - which guards applies to this interaction, same as in normal workflows
It is quite good idea to define methods (scripts) before defining interactions.
Variables
TODO
Scripts
Every script in you interaction workflows definition shall have argument called (by convention) state_change. To access changed object use:
1 changed_object = state_change['object']
Don't do state_change.object - it usually works but sometimes causes security problems
Scripts can be invoked:
before - ran immediately; if triggered by a setter uses the old value of a property
after - ran immediately; if triggered by a setter uses the new value of a property
later - executed a bit later, usually a few seconds but can be much longer
QUESTION: what to do if an interaction has to compare the old value to the new one? Is there a way to access them both at the same time (other then request lookup)?
List of usable Trigger methods
When editing using the standard ERP5 interface, edit method will be called. Then for each modified property, when you have for example a field "my_foo", the corresponding accessor _setFoo will be called. If _setFoo does not exists, setFoo will be called instead. If setFoo does not exist either, setProperty('foo', value) will be called. There is an exception for RelationFields. When a RelationField is modified, either the field defines a "Relation Update Method" that will be called, either the default _set${base_category}Value (or _set${base_category}ValueList when setting a multiple relation) accessor will be called, where ${base_category} is the capitalized name of the base category used for this accessor. Here is a summary of methods that can be used:
this method will be triggered on when relation defined on resource category will be changed.
Such change will NOT be triggered by edit method
- you may use regular expressions to trigger on more then one method - it is quite usefull in case of relation setting methods
- trigger on while adding object
trigger on while pasting copied or cut object. Also note that you can use _afterClone TypeBasedMethod.
- trigger on before deleting object
Questions and Answers
- How can I redirect from an interaction workflow script?
You can't.