# 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.