--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.38.0 (20140413.2041)
+ -->
+<!-- Title: order Pages: 1 -->
+<svg width="260pt" height="291pt"
+ viewBox="0.00 0.00 259.79 291.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 287)">
+<title>order</title>
+<polygon fill="white" stroke="none" points="-4,4 -4,-287 255.792,-287 255.792,4 -4,4"/>
+<!-- Draft -->
+<g id="node1" class="node"><title>Draft</title>
+<ellipse fill="#73fa79" stroke="black" cx="85.3968" cy="-265" rx="30.5947" ry="18"/>
+<text text-anchor="middle" x="85.3968" y="-261.3" font-family="Times,serif" font-size="14.00">Draft</text>
+</g>
+<!-- Confirmed -->
+<g id="node4" class="node"><title>Confirmed</title>
+<ellipse fill="none" stroke="black" cx="85.3968" cy="-91" rx="51.9908" ry="18"/>
+<text text-anchor="middle" x="85.3968" y="-87.3" font-family="Times,serif" font-size="14.00">Confirmed</text>
+</g>
+<!-- Draft->Confirmed -->
+<g id="edge1" class="edge"><title>Draft->Confirmed</title>
+<path fill="none" stroke="black" d="M72.5816,-248.399C62.7132,-235.312 49.8641,-215.656 44.3968,-196 40.1092,-180.585 40.1092,-175.415 44.3968,-160 48.7071,-144.503 57.6054,-129.007 66.0142,-116.73"/>
+<polygon fill="black" stroke="black" points="68.9995,-118.572 71.9779,-108.405 63.3089,-114.496 68.9995,-118.572"/>
+<text text-anchor="middle" x="94.3968" y="-174.3" font-family="Times,serif" font-size="14.00">discount <= 15%</text>
+</g>
+<!-- Validation -->
+<g id="node5" class="node"><title>Validation</title>
+<ellipse fill="none" stroke="black" cx="202.397" cy="-178" rx="49.2915" ry="18"/>
+<text text-anchor="middle" x="202.397" y="-174.3" font-family="Times,serif" font-size="14.00">Validation</text>
+</g>
+<!-- Draft->Validation -->
+<g id="edge2" class="edge"><title>Draft->Validation</title>
+<path fill="none" stroke="black" d="M103.936,-250.531C122.48,-237.059 151.286,-216.131 172.959,-200.386"/>
+<polygon fill="black" stroke="black" points="175.139,-203.129 181.172,-194.42 171.024,-197.466 175.139,-203.129"/>
+<text text-anchor="middle" x="195.897" y="-217.8" font-family="Times,serif" font-size="14.00">discount > 15%</text>
+</g>
+<!-- Closed -->
+<g id="node2" class="node"><title>Closed</title>
+<ellipse fill="#98c7df" stroke="black" cx="36.3968" cy="-18" rx="36.2938" ry="18"/>
+<text text-anchor="middle" x="36.3968" y="-14.3" font-family="Times,serif" font-size="14.00">Closed</text>
+</g>
+<!-- Canceled -->
+<g id="node3" class="node"><title>Canceled</title>
+<ellipse fill="#98c7df" stroke="black" cx="135.397" cy="-18" rx="44.6926" ry="18"/>
+<text text-anchor="middle" x="135.397" y="-14.3" font-family="Times,serif" font-size="14.00">Canceled</text>
+</g>
+<!-- Confirmed->Closed -->
+<g id="edge4" class="edge"><title>Confirmed->Closed</title>
+<path fill="none" stroke="black" d="M73.7845,-73.174C67.7175,-64.3831 60.1766,-53.4564 53.4596,-43.7236"/>
+<polygon fill="black" stroke="black" points="56.1491,-41.4588 47.5884,-35.2165 50.3879,-45.4348 56.1491,-41.4588"/>
+</g>
+<!-- Confirmed->Canceled -->
+<g id="edge5" class="edge"><title>Confirmed->Canceled</title>
+<path fill="none" stroke="black" d="M97.2461,-73.174C103.348,-64.5087 110.912,-53.7682 117.692,-44.1415"/>
+<polygon fill="black" stroke="black" points="120.736,-45.8965 123.633,-35.7052 115.013,-41.8661 120.736,-45.8965"/>
+</g>
+<!-- Validation->Confirmed -->
+<g id="edge3" class="edge"><title>Validation->Confirmed</title>
+<path fill="none" stroke="black" d="M181.209,-161.607C162.688,-148.152 135.604,-128.475 114.953,-113.472"/>
+<polygon fill="black" stroke="black" points="116.97,-110.612 106.822,-107.566 112.856,-116.275 116.97,-110.612"/>
+<text text-anchor="middle" x="170.397" y="-130.8" font-family="Times,serif" font-size="14.00">Accept</text>
+</g>
+</g>
+</svg>
Workflows
=========
-In OpenERP, a workflow is a technical artefact to manage a set of "things to
-do" associated to the records of some data model. The workflow provides a
-higher- level way to organize the things to do on a record.
+In Odoo, a workflow is a technical artefact to manage a set of "things to
+do" associated to the records of a model. The workflow provides a higher-level
+way to organize tasks to perform with or on a record.
More specifically, a workflow is a directed graph where the nodes are called
"activities" and the arcs are called "transitions".
-- Activities define work that should be done within the OpenERP server, such
+- Activities define work that should be done within the Odoo server, such
as changing the state of some records, or sending emails.
- Transitions control how the workflow progresses from activity to activity.
actions (such as clicking on a button), changes to records, or arbitrary
Python code.
+All in all, Odoo's workflow system provides:
+
+* a description of the evolution of a record (document) over time
+* automatic actions based on various and flexible conditions
+* management of company roles and validation steps
+* management of interactions between objects
+* a visual representation of document flows through their lifecycle
+
+For instance, a basic order could have the following flow:
+
+.. sphinx.ext.graphviz would be nice, but it requires ``dot`` on any machine
+.. where the doc is compiled... otoh this is a pain in the ass because you
+.. need 2 compilation steps (dot -> image and rst -> html) every time
+
+.. image:: workflow/order_0.*
+ :align: center
+
+Orders start in the *Draft* state, can be *Confirmed* by a user, and then
+either shipped (*Closed*) or *Canceled*.
+
+A company using Odoo may want to add discount support to orders, where sales
+staff has discretionary discounting powers up to 15%, but manager validation
+is required for discounts beyond 15%. The workflow can be altered online to
+add the relevant steps without editing Python or XML files:
+
+.. image:: workflow/order_1.*
+ :align: center
+
+Because Activities can perform arbitrary actions, the *Validation* can
+automatically send a validation request to the relevant employee.
+
+.. note:: the order view needs to be modified to add an *Accept Discount*
+ button for managers
+
Basics
------
to "b".
The first activity has its attribute ``flow_start`` set to ``True`` so that
-OpenERP knows where to start the workflow traversal after it is instanciated.
+Odoo knows where to start the workflow traversal after it is instanciated.
Because ``on_create`` is set to True on the workflow record, the workflow is
instanciated for each newly created record. (Otherwise, the workflow should be
instanciated by other means, such as from some module Python code.)
that the workflow instance immediately goes from "a" to "b" after "a" has been
processed, and thus also processes activity "b".
-Transitions
------------
-
-Transitions provide the control structures to orchestrate a workflow. When an
-activity is completed, the workflow engine tries to get across transitions
-departing from the completed activity, towards the next activities. In their
-simplest form (as in the example above), they link activities sequentially:
-activities are processed as soon as the activities preceding them are
-completed.
-
-Instead of running all activities in one fell swoop, it is also possible to
-wait on transitions, going through them only when some criteria are met. The
-criteria are the conditions, the signals, and the triggers. They are detailed
-in the following sections.
-
-Conditions
-''''''''''
-
-When an activity has been completed, its outgoing transitions are inspected to
-determine whether it is possible for the workflow instance to proceed through
-them and reach the next activities. When only a condition is defined (i.e., no
-signal or trigger is defined), the condition is evaluated by OpenERP, and if
-it evaluates to ``True``, the worklfow instance progresses through the
-transition. If the condition is not met, it will be reevaluated every time
-the associated record is modified, or by an explicit method call to do it.
-
-By default, the attribute ``condition`` (i.e., the expression to be evaluated)
-is just "True", which trivially evaluates to ``True``. Note that the condition
-may be several lines long; in that case, the value of the last one determines
-whether the transition can be taken.
-
-In the condition evaluation environment, several symbols are conveniently
-defined (in addition to the OpenERP ``safe_eval`` environment):
-
-- all the model column names, and
-- all the browse record's attributes.
-
-Signals
-'''''''
-
-In addition to a condition, a transition can specify a signal name. When such
-a signal name is present, the transition is not taken directly, even if the
-condition evaluates to ``True``. Instead the transition blocks, waiting to be
-woken up.
-
-In order to wake up a transition with a defined signal name, the signal must
-be sent to the workflow instance. A common way to send a signal is to use a
-button in the user interface, using the element ``<button/>`` with the signal
-name as the attribute ``name`` of the button. Once the button is clicked, the
-signal is sent to the workflow instance of the current record.
-
-.. note:: The condition is still evaluated when the signal is sent to the
- workflow instance.
-
-Triggers
-''''''''
-
-With conditions that evaluate to ``False``, transitions are not taken (and
-thus the activity it leads to is not processed immediately). Still, the
-workflow instance can get new chances to progress across that transition by
-providing so-called triggers. The idea is that when the condition is not
-satisfied, triggers are recorded in database. Later, it is possible to wake up
-specifically the workflow instances that installed those triggers, offering
-them to reevaluate their transition conditions. This mechanism makes it
-cheaper to wake up workflow instances by targetting just a few of them (those
-that have installed the triggers) instead of all of them.
-
-Triggers are recorded in database as record IDs (together with the model name)
-and refer to the workflow instance waiting for those records. The transition
-definition provides a model name (attribute ``trigger_model``) and a Python
-expression (attribute ``trigger_expression``) that evaluates to a list of
-record IDs in the given model. Any of those records can wake up the workflow
-instance they are associated with.
-
-.. note:: triggers are not re-installed whenever the transition is re-tried.
-
-Splitting and joining transitions
-'''''''''''''''''''''''''''''''''
-
-When multiple transitions leave the same activity, or lead to the same
-activity, OpenERP provides some control over which transitions are actually
-taken, or how the reached activity will be processed. The attributes
-``split_mode`` and ``join_mode`` on the activity are used for such
-control. The possible values of those attributes are explained below.
-
Activities
----------
The attribute ``flow_start`` is a boolean value specifying whether the activity
is processed when the workflow is instanciated. Multiple activities can have
their attribute ``flow_start`` set to ``True``. When instanciating a workflow
-for a record, OpenERP simply processes all of them, and evaluate all their
+for a record, Odoo simply processes all of them, and evaluate all their
outgoing transitions afterwards.
The attribute ``flow_stop`` is a boolean value specifying whether the activity
all its activities with the attribute ``flow_stop`` set to ``True`` are
completed.
-It is important for OpenERP to know when a workflow instance is completed. A
+It is important for Odoo to know when a workflow instance is completed. A
workflow can have an activity that is actually another workflow (called a
subflow); that activity is completed when the subflow is completed.
When a workflow is embedded in an activity (as a subflow) of a workflow, the
sublow can send a signal from its own activities to the parent workflow by
-giving a signal name in the attribute ``signal_send``. OpenERP processes those
+giving a signal name in the attribute ``signal_send``. Odoo processes those
activities by sending the value of ``signal_send`` prefixed by "subflow." to
the parent workflow instance.
Split mode
''''''''''
-After an activity has been processed, its outgoing transitions are evaluated.
-Normally, if a transition can be taken, OpenERP traverses it and proceed to
-the activity the transition leads to.
+After an activity has been processed, Odoo evaluates its transition to reach
+the next activity in the flow.
-Actually, when more than a single transition is leaving an activity, OpenERP
-may proceed or not, depending on the other transitions. That is, the
-conditions on the transitions can be combined together, and the combined
-result instructs OpenERP to traverse zero, one, or all the transitions. The
-way they are combined is controlled by the attribute ``split_mode``.
+However if an activity has more than one transition, Odoo must decide which
+activity or activities to follow.
-There are three possible split modes: ``XOR``, ``OR`` and ``AND``.
+.. image:: workflow/split.*
+ :align: center
-``XOR``
- When the transitions are combined with a ``XOR`` split mode, as soon as a
- transition has a satisfied condition, the transition is traversed and the
- others are skipped.
+This choice is controlled by the ``split_mode`` attribute:
+
+``XOR`` (default)
+ By default, Odoo will use the first transition (in ``sequence`` order)
+ whose condition is satisfied. All other transitions are ignored.
``OR``
- With the ``OR`` mode, all the transitions with a satisfied condition are
- traversed. The remaining transitions will not be evaluated later.
+ In ``OR`` mode, all transitions with a satisfied condition are traversed
+ simultanously. Transitions not yet valid will be ignored, even if they
+ become valid later.
``AND``
- With the ``AND`` mode, OpenERP will wait for all outgoing transition
- conditions to be satisfied, then traverse all of them at once.
+ In ``AND`` mode, Odoo will wait until *all* transitions are satisfied, and
+ will traverse all of them (much like the ``OR`` mode).
+
+Both ``OR`` and ``AND`` mode will lead to activities being active in the same
+workflow.
Join mode
'''''''''
Just like outgoing transition conditions can be combined together to decide
whether they can be traversed or not, incoming transitions can be combined
-together to decide if and when an activity may be processed. The attribute
-``join_mode`` controls that behavior.
+together to decide if and when an activity may be processed.
-There are two possible join modes: ``XOR`` and ``AND``.
+.. image:: workflow/join.*
+ :align: center
-``XOR``
- With the ``XOR`` mode, an incoming transition with a satisfied condition
- is traversed immediately, and enables the processing of the activity.
+The ``join_mode`` attribute controls that behavior:
+``XOR`` (default)
+ Any incoming transition enables the activity and starts its processing.
``AND``
- With the ``AND`` mode, OpenERP will wait until all incoming transitions
- have been traversed before enabling the processing of the activity.
+ The activity is enabled and processed only once *all* incoming transitions
+ have been traversed.
Kinds
'''''
-Activities can be of different kinds: ``dummy``, ``function``, ``subflow``, or
-``stopall``. The kind defines what type of work an activity can do.
-
-Dummy
- The ``dummy`` kind is for activities that do nothing, or for activities
- that only call a server action. Activities that do nothing can be used as
- hubs to gather/dispatch transitions.
-Function
- The ``function`` kind is for activities that only need to run some Python
- code, and possibly a server action.
-Stop all
- The ``stopall`` kind is for activities that will completely stop the
- workflow instance and mark it as completed. In addition they can also run
- some Python code.
-Subflow
- When the kind of the activity is ``subflow``, the activity embeds another
- workflow instance. When the subflow is completed, the activity is also
- considered completed.
+An activity's kind defines the type of work an activity can perform.
+
+Dummy (``dummy``, default)
+ Do nothing at all, or call a server action. Often used as dispatch or
+ gather "hubs" for transitions.
+Function (``function``)
+ Run some python code, execute a server action.
+Stop all (``stopall``)
+ Completely stops the workflow instance and marks it as completed.
+Subflow (``subflow``)
+ Starts executing an other workflow, once that workflow is completed the
+ activity is done processing.
By default, the subflow is instanciated for the same record as the parent
workflow. It is possible to change that behavior by providing Python code
that returns a record ID (of the same data model as the subflow). The
embedded subflow instance is then the one of the given record.
+
+
+Transitions
+-----------
+
+Transitions provide the control structures to orchestrate a workflow. When an
+activity is completed, the workflow engine tries to get across transitions
+departing from the completed activity, towards the next activities. In their
+simplest form (as in the example above), they link activities sequentially:
+activities are processed as soon as the activities preceding them are
+completed.
+
+Instead of running all activities in one fell swoop, it is also possible to
+wait on transitions, going through them only when some criteria are met. The
+criteria are the conditions, the signals, and the triggers. They are detailed
+in the following sections.
+
+Conditions
+''''''''''
+
+When an activity has been completed, its outgoing transitions are inspected to
+determine whether it is possible for the workflow instance to proceed through
+them and reach the next activities. When only a condition is defined (i.e., no
+signal or trigger is defined), the condition is evaluated by Odoo, and if
+it evaluates to ``True``, the worklfow instance progresses through the
+transition. If the condition is not met, it will be reevaluated every time
+the associated record is modified, or by an explicit method call to do it.
+
+By default, the attribute ``condition`` (i.e., the expression to be evaluated)
+is just "True", which trivially evaluates to ``True``. Note that the condition
+may be several lines long; in that case, the value of the last one determines
+whether the transition can be taken.
+
+In the condition evaluation environment, several symbols are conveniently
+defined (in addition to the Odoo ``safe_eval`` environment):
+
+- all the model column names, and
+- all the browse record's attributes.
+
+Signals
+'''''''
+
+In addition to a condition, a transition can specify a signal name. When such
+a signal name is present, the transition is not taken directly, even if the
+condition evaluates to ``True``. Instead the transition blocks, waiting to be
+woken up.
+
+In order to wake up a transition with a defined signal name, the signal must
+be sent to the workflow instance. A common way to send a signal is to use a
+button in the user interface, using the element ``<button/>`` with the signal
+name as the attribute ``name`` of the button. Once the button is clicked, the
+signal is sent to the workflow instance of the current record.
+
+.. note:: The condition is still evaluated when the signal is sent to the
+ workflow instance.
+
+Triggers
+''''''''
+
+With conditions that evaluate to ``False``, transitions are not taken (and
+thus the activity it leads to is not processed immediately). Still, the
+workflow instance can get new chances to progress across that transition by
+providing so-called triggers. The idea is that when the condition is not
+satisfied, triggers are recorded in database. Later, it is possible to wake up
+specifically the workflow instances that installed those triggers, offering
+them to reevaluate their transition conditions. This mechanism makes it
+cheaper to wake up workflow instances by targetting just a few of them (those
+that have installed the triggers) instead of all of them.
+
+Triggers are recorded in database as record IDs (together with the model name)
+and refer to the workflow instance waiting for those records. The transition
+definition provides a model name (attribute ``trigger_model``) and a Python
+expression (attribute ``trigger_expression``) that evaluates to a list of
+record IDs in the given model. Any of those records can wake up the workflow
+instance they are associated with.
+
+.. note:: triggers are not re-installed whenever the transition is re-tried.