File: workflow.rst

package info (click to toggle)
tryton-server 7.0.40-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,748 kB
  • sloc: python: 53,502; xml: 5,194; sh: 803; sql: 217; makefile: 28
file content (166 lines) | stat: -rw-r--r-- 4,773 bytes parent folder | download | duplicates (3)
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>`.