1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
.. _tutorial-module-workflow:
Define workflow
===============
Often records follow a workflow to change their state.
For example the opportunity can be converted or lost.
Tryton has a :class:`~trytond.model.Workflow` class that provides the tooling
to follow a workflow based on the field defined in
:attr:`~trytond.model.Workflow._transition_state` which is by default
``state``.
First we need to inherit from :class:`~trytond.model.Workflow` and add a
:class:`~trytond.model.fields.Selection` field to store the state of the
record:
.. code-block:: python
from trytond.model import Workflow
...
class Opportunity(Workflow, ModelSQL, ModelView):
...
state = fields.Selection([
('draft', "Draft"),
('converted', "Converted"),
('lost', "Lost"),
], "State",
required=True, readonly=True, sort=False)
@classmethod
def default_state(cls):
return 'draft'
We must define the allowed transitions between states by filling the
:attr:`~trytond.model.Workflow._transitions` set with tuples using the
:meth:`~trytond.model.Model.__setup__` method:
.. code-block:: python
class Opportunity(Workflow, ModelSQL, ModelView):
...
@classmethod
def __setup__(cls):
super().__setup__()
cls._transitions.update({
('draft', 'converted'),
('draft', 'lost'),
})
For each target state, we must define a
:meth:`~trytond.model.Workflow.transition` method.
For example when the opportunity is converted we fill the ``end_date`` field
with today:
.. code-block:: python
class Opportunity(Workflow, ModelSQL, ModelView):
...
@classmethod
@Workflow.transition('converted')
def convert(cls, opportunities):
pool = Pool()
Date = pool.get('ir.date')
cls.write(opportunities, {
'end_date': Date.today(),
})
.. note::
We let you define the transition method for lost.
Now we need to add a button for each transition so the user can trigger them.
We must declare the button in the :attr:`~trytond.model.ModelView._buttons`
dictionary and decorate the transition method with the
:meth:`~trytond.model.ModelView.button` to be callable from the client:
.. code-block:: python
class Opportunity(Workflow, ModelSQL, ModelView):
...
@classmethod
def __setup__(cls):
...
cls._buttons.update({
'convert': {},
'lost': {},
})
@classmethod
@ModelView.button
@Workflow.transition('converted')
def convert(cls, opportunities):
...
@classmethod
@ModelView.button
@Workflow.transition('lost')
def lost(cls, opportunities):
...
Every button must also be recorded in ``ir.model.button`` to define its label
(and also the :ref:`access right <topics-access_rights>`).
We must add to the ``opportunity.xml`` file:
.. code-block:: xml
<tryton>
<data>
...
<record model="ir.model.button" id="opportunity_convert_button">
<field name="name">convert</field>
<field name="string">Convert</field>
<field name="model" search="[('model', '=', 'training.opportunity')]"/>
</record>
<record model="ir.model.button" id="opportunity_lost_button">
<field name="name">lost</field>
<field name="string">Lost</field>
<field name="model" search="[('model', '=', 'training.opportunity')]"/>
</record>
</data>
</tryton>
Now we can add the ``state`` field and the buttons in the form view.
The buttons can be grouped under a ``group`` tag.
This is how the ``view/opportunity_form.xml`` must be adapted:
.. code-block:: xml
<form>
...
<label name="state"/>
<field name="state"/>
<group col="2" colspan="2" id="button">
<button name="lost" icon="tryton-cancel"/>
<button name="convert" icon="tryton-forward"/>
</group>
</form>
.. note::
We let you add the ``state`` field on the list view.
Update database
---------------
As we have defined new fields and XML records, we need to update the database
with:
.. code-block:: shell
$ trytond-admin -d test --all
And restart the server and reconnect with the client to test the workflow:
.. code-block:: shell
$ trytond
Exercise
---------
As exercise we let you add a transition between ``lost`` and ``draft`` which
will clear the ``end_date``.
Let's continue with :ref:`adding more reaction with dynamic state
<tutorial-module-states>`.
|