Create wizard

Sometime you want to add functionalities to a model that do not suite the use of a button. For this kind of use case the wizard is the preferred solution. A wizard is a kind of state machine where states can be a form view, an action or transition.

Let’s create a wizard that converts the opportunities by asking for the end date.

First we define a ModelView class in opportunity.py:

class ConvertStart(ModelView):
    "Convert Opportunities"
    __name__ = 'training.opportunity.convert.start'

    end_date = fields.Date("End Date", required=True)

And we register it in the Pool in __init__.py:

def register():
    Pool.register(
        ...,
        opportunity.ConvertStart,
        module='opportunity', type_='model')

Then the form view record in opportunity.xml:

<tryton>
   <data>
      ...

      <record model="ir.ui.view" id="opportunity_convert_start_view_form">
         <field name="model">training.opportunity.convert.start</field>
         <field name="type">form</field>
         <field name="name">opportunity_convert_start_form</field>
      </record>
   </data>
</tryton>

And the view in view/opportunity_convert_start_form.xml:

<form col="2">
   <label string="Convert Opportunities?" id="convert_opportunities" colspan="2" xalign="0"/>
   <label name="end_date"/>
   <field name="end_date"/>
</form>

Now we can define the Wizard with a start StateView for the form and a convert StateTransition in opportunity.py:

from trytond.wizard import Wizard, StateView, StateTransition, Button
...
class Opportunity(...):
    ...
    @classmethod
    @Workflow.transition('converted')
    def convert(cls, opportunities, end_date=None):
        pool = Pool()
        Date = pool.get('ir.date')
        cls.write(opportunities, {
            'end_date': end_date or Date.today(),
            })
...
class Convert(Wizard):
    "Convert Opportunities"
    __name__ = 'training.opportunity.convert'

    start = StateView(
        'training.opportunity.convert.start',
        'opportunity.opportunity_convert_start_view_form', [
            Button("Cancel", 'end', 'tryton-cancel'),
            Button("Convert", 'convert', 'tryton-ok', default=True),
            ])
    convert = StateTransition()

    def transition_convert(self):
        self.model.convert(self.records, self.start.end_date)
        return 'end'

Note

We added an optional end_date to the convert method.

And we register it in the Pool as type wizard in __init__.py:

def register():
    ...
    Pool.register(
        opportunity.Convert,
        module='opportunity', type_='wizard')

Finally we just need to create a ir.action.wizard and ir.action.keyword in opportunity.xml:

<tryton>
   <data>
      ...
      <record model="ir.action.wizard" id="act_convert_opportunities">
         <field name="name">Convert Opportunities</field>
         <field name="wiz_name">training.opportunity.convert</field>
         <field name="model">training.opportunity</field>
      </record>
      <record model="ir.action.keyword" id="act_convert_opportunities_keyword">
         <field name="keyword">form_action</field>
         <field name="model">training.opportunity,-1</field>
         <field name="action" ref="act_convert_opportunities"/>
      </record>
   </data>
</tryton>

The ir.action.wizard links the Wizard with the Model.

name

The string that is shown on the menu.

wiz_name

The name of the Wizard.

model

The name of the Model.

And the ir.action.keyword makes the Wizard available as action to any training.opportunity.

keyword

The type of keyword <topics-actions>.

model

The model or record for which the action must be displayed. Use -1 as id for any record.

action

The link to the action.

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 wizard:

$ trytond

Let’s create a a report to print opportunities.