Define workflow¶
Often records follow a workflow to change their state.
For example the opportunity can be converted or lost.
Tryton has a Workflow
class that provides the tooling
to follow a workflow based on the field defined in
_transition_state
which is by default
state
.
First we need to inherit from Workflow
and add a
Selection
field to store the state of the
record:
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
_transitions
set with tuples using the
__setup__()
method:
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
transition()
method.
For example when the opportunity is converted we fill the end_date
field
with today:
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 _buttons
dictionary and decorate the transition method with the
button()
to be callable from the client:
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 access right).
We must add to the opportunity.xml
file:
<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:
<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:
$ trytond-admin -d test --all
And restart the server and reconnect with the client to test the workflow:
$ trytond
Exercise¶
As exercise we let you add a transition between lost
and draft
which
will clear the end_date
.
Let’s continue with adding more reaction with dynamic state.