.. _tutorial-module-wizard:

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 :ref:`wizard <topics-wizard>` is the preferred
solution.
A wizard is a kind of `state machine`_ where states can be a form view, an
action or transition.

.. _state machine: https://en.wikipedia.org/wiki/Finite-state_machine

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

First we define a :class:`~trytond.model.ModelView` class in
:file:`opportunity.py`:

.. code-block:: python

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

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

And we register it in the :class:`~trytond.pool.Pool` in :file:`__init__.py`:

.. code-block:: python

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

Then the form view record in :file:`opportunity.xml`:

.. code-block:: 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 :file:`view/opportunity_convert_start_form.xml`:

.. code-block:: 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 :class:`~trytond.wizard.Wizard` with a ``start``
:class:`~trytond.wizard.StateView` for the form and a ``convert``
:class:`~trytond.wizard.StateTransition` in :file:`opportunity.py`:

.. code-block:: python

    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 :class:`~trytond.pool.Pool` as type ``wizard`` in
:file:`__init__.py`:

.. code-block:: python

    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 :file:`opportunity.xml`:

.. code-block:: 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 :class:`~trytond.wizard.Wizard` with the
:class:`~trytond.model.Model`.

``name``
   The string that is shown on the menu.
``wiz_name``
   The name of the :class:`~trytond.wizard.Wizard`.
``model``
   The name of the :class:`~trytond.model.Model`.

And the ``ir.action.keyword`` makes the :class:`~trytond.wizard.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:

.. code-block:: console

   $ trytond-admin -d test --all

And restart the server and reconnect with the client to test the wizard:

.. code-block:: console

   $ trytond

Let's create a :ref:`a report to print opportunities <tutorial-module-report>`.