2
.. i18n: ===============================
3
.. i18n: Creating Wizard - (The Process)
4
.. i18n: ===============================
7
===============================
8
Creating Wizard - (The Process)
9
===============================
18
.. i18n: Wizards describe interaction sequences between the client and the server.
21
Wizards describe interaction sequences between the client and the server.
23
.. i18n: Here is, as an example, a typical process for a wizard:
26
Here is, as an example, a typical process for a wizard:
28
.. i18n: 1. A window is sent to the client (a form to be completed)
29
.. i18n: 2. The client sends back the data from the fields which were filled in
30
.. i18n: 3. The server gets the result, usually execute a function and possibly sends another window/form to the client
33
1. A window is sent to the client (a form to be completed)
34
2. The client sends back the data from the fields which were filled in
35
3. The server gets the result, usually execute a function and possibly sends another window/form to the client
37
.. i18n: .. image:: images/Wizard.png
40
.. image:: images/Wizard.png
42
.. i18n: Here is a screenshot of the wizard used to reconcile transactions (when you click on the gear icon in an account chart):
45
Here is a screenshot of the wizard used to reconcile transactions (when you click on the gear icon in an account chart):
47
.. i18n: .. image:: images/Wizard_screenshot.png
51
.. image:: images/Wizard_screenshot.png
54
.. i18n: Wizards - Principles
55
.. i18n: ====================
61
.. i18n: A wizard is a succession of steps. A step is composed of several actions;
64
A wizard is a succession of steps. A step is composed of several actions;
66
.. i18n: #. send a form to the client and some buttons
67
.. i18n: #. get the form result and the button pressed from the client
68
.. i18n: #. execute some actions
69
.. i18n: #. send a new action to the client (form, print, ...)
72
#. send a form to the client and some buttons
73
#. get the form result and the button pressed from the client
74
#. execute some actions
75
#. send a new action to the client (form, print, ...)
77
.. i18n: To define a wizard, you have to create a class inheriting from **wizard.interface** and instantiate it. Each wizard must have a unique name, which can be chosen arbitrarily except for the fact it has to start with the module name (for example: account.move.line.reconcile). The wizard must define a dictionary named **states** which defines all its steps.
78
.. i18n: A full example of a simple wizard can be found at http://www.openobject.com/forum/post43900.html#43900
81
To define a wizard, you have to create a class inheriting from **wizard.interface** and instantiate it. Each wizard must have a unique name, which can be chosen arbitrarily except for the fact it has to start with the module name (for example: account.move.line.reconcile). The wizard must define a dictionary named **states** which defines all its steps.
82
A full example of a simple wizard can be found at http://www.openobject.com/forum/post43900.html#43900
84
.. i18n: Here is an example of such a class:
87
Here is an example of such a class:
89
.. i18n: .. code-block:: python
91
.. i18n: class wiz_reconcile(wizard.interface):
94
.. i18n: 'actions': [_trans_rec_get],
95
.. i18n: 'result': {'type': 'form',
96
.. i18n: 'arch': _transaction_form,
97
.. i18n: 'fields': _transaction_fields,
98
.. i18n: 'state':[('reconcile','Reconcile'),('end','Cancel')]}
100
.. i18n: 'reconcile': {
101
.. i18n: 'actions': [_trans_rec_reconcile],
102
.. i18n: 'result': {'type': 'state', 'state':'end'}
105
.. i18n: wiz_reconcile('account.move.line.reconcile');
108
.. code-block:: python
110
class wiz_reconcile(wizard.interface):
113
'actions': [_trans_rec_get],
114
'result': {'type': 'form',
115
'arch': _transaction_form,
116
'fields': _transaction_fields,
117
'state':[('reconcile','Reconcile'),('end','Cancel')]}
120
'actions': [_trans_rec_reconcile],
121
'result': {'type': 'state', 'state':'end'}
124
wiz_reconcile('account.move.line.reconcile');
126
.. i18n: The 'states' dictionary define all the states of the wizard. In this example; **init** and **reconcile**. There is another state which is named end which is implicit.
129
The 'states' dictionary define all the states of the wizard. In this example; **init** and **reconcile**. There is another state which is named end which is implicit.
131
.. i18n: A wizard always starts in the **init** state and ends in the **end** state.
134
A wizard always starts in the **init** state and ends in the **end** state.
136
.. i18n: A state define two things:
139
A state define two things:
141
.. i18n: #. a list of actions
148
.. i18n: The list of actions
149
.. i18n: -------------------
150
.. i18n: Each step/state of a wizard defines a list of actions which are executed when the wizard enters the state. This list can be empty.
155
Each step/state of a wizard defines a list of actions which are executed when the wizard enters the state. This list can be empty.
157
.. i18n: The function (actions) must have the following signatures:
160
The function (actions) must have the following signatures:
162
.. i18n: .. code-block:: python
164
.. i18n: def _trans_rec_get(self, uid, data, res_get=False):
167
.. code-block:: python
169
def _trans_rec_get(self, uid, data, res_get=False):
176
.. i18n: * **self** is the pointer to the wizard object
177
.. i18n: * **uid** is the user ID of the user which is executing the wizard
178
.. i18n: * **data** is a dictionary containing the following data:
179
.. i18n: * **ids**: the list of ids of resources selected when the user executed the wizard
180
.. i18n: * **id**: the id highlighted when the user executed the wizard
181
.. i18n: * **form**: a dictionary containing all the values the user completed in the preceding forms. If you change values in this dictionary, the following forms will be pre-completed.
184
* **self** is the pointer to the wizard object
185
* **uid** is the user ID of the user which is executing the wizard
186
* **data** is a dictionary containing the following data:
187
* **ids**: the list of ids of resources selected when the user executed the wizard
188
* **id**: the id highlighted when the user executed the wizard
189
* **form**: a dictionary containing all the values the user completed in the preceding forms. If you change values in this dictionary, the following forms will be pre-completed.
191
.. i18n: Each action function must return a dictionary. Any entries in this dictionary
192
.. i18n: will be merged with the data that is passed to the form when it's displayed.
195
Each action function must return a dictionary. Any entries in this dictionary
196
will be merged with the data that is passed to the form when it's displayed.
205
.. i18n: Here are some result examples:
208
Here are some result examples:
210
.. i18n: Result: next step
215
.. i18n: .. code-block:: python
217
.. i18n: 'result': {'type': 'state',
218
.. i18n: 'state':'end'}
221
.. code-block:: python
223
'result': {'type': 'state',
226
.. i18n: Indicate that the wizard has to continue to the next state: 'end'. If this is the 'end' state, the wizard stops.
229
Indicate that the wizard has to continue to the next state: 'end'. If this is the 'end' state, the wizard stops.
231
.. i18n: Result: new dialog for the client
234
Result: new dialog for the client
236
.. i18n: .. code-block:: python
238
.. i18n: 'result': {'type': 'form',
239
.. i18n: 'arch': _form,
240
.. i18n: 'fields': _fields,
241
.. i18n: 'state':[('reconcile','Reconcile'),('end','Cancel')]}
244
.. code-block:: python
246
'result': {'type': 'form',
249
'state':[('reconcile','Reconcile'),('end','Cancel')]}
251
.. i18n: The type=form indicate that this step is a dialog to the client. The dialog is composed of:
254
The type=form indicate that this step is a dialog to the client. The dialog is composed of:
256
.. i18n: #. a form : with fields description and a form description
257
.. i18n: #. some buttons : on which the user press after completing the form
260
#. a form : with fields description and a form description
261
#. some buttons : on which the user press after completing the form
263
.. i18n: The form description (arch) is like in the views objects. Here is an example of form:
266
The form description (arch) is like in the views objects. Here is an example of form:
268
.. i18n: .. code-block:: xml
270
.. i18n: _form = """<?xml version="1.0"?>
271
.. i18n: <form title="Reconciliation">
272
.. i18n: <separator string="Reconciliation transactions" colspan="4"/>
273
.. i18n: <field name="trans_nbr"/>
275
.. i18n: <field name="credit"/>
276
.. i18n: <field name="debit"/>
277
.. i18n: <field name="state"/>
278
.. i18n: <separator string="Write-Off" colspan="4"/>
279
.. i18n: <field name="writeoff"/>
281
.. i18n: <field name="writeoff_acc_id" colspan="3"/>
288
_form = """<?xml version="1.0"?>
289
<form title="Reconciliation">
290
<separator string="Reconciliation transactions" colspan="4"/>
291
<field name="trans_nbr"/>
293
<field name="credit"/>
294
<field name="debit"/>
295
<field name="state"/>
296
<separator string="Write-Off" colspan="4"/>
297
<field name="writeoff"/>
299
<field name="writeoff_acc_id" colspan="3"/>
303
.. i18n: The fields description is similar to the fields described in the python ORM objects. Example:
306
The fields description is similar to the fields described in the python ORM objects. Example:
308
.. i18n: .. code-block:: python
310
.. i18n: _transaction_fields = {
311
.. i18n: 'trans_nbr': {'string':'# of Transaction', 'type':'integer', 'readonly':True},
312
.. i18n: 'credit': {'string':'Credit amount', 'type':'float', 'readonly':True},
313
.. i18n: 'debit': {'string':'Debit amount', 'type':'float', 'readonly':True},
315
.. i18n: 'string':"Date/Period Filter",
316
.. i18n: 'type':'selection',
317
.. i18n: 'selection':[('bydate','By Date'),
318
.. i18n: ('byperiod','By Period'),
319
.. i18n: ('all','By Date and Period'),
320
.. i18n: ('none','No Filter')],
321
.. i18n: 'default': lambda *a:'none'
323
.. i18n: 'writeoff': {'string':'Write-Off amount', 'type':'float', 'readonly':True},
324
.. i18n: 'writeoff_acc_id': {'string':'Write-Off account',
325
.. i18n: 'type':'many2one',
326
.. i18n: 'relation':'account.account'
331
.. code-block:: python
333
_transaction_fields = {
334
'trans_nbr': {'string':'# of Transaction', 'type':'integer', 'readonly':True},
335
'credit': {'string':'Credit amount', 'type':'float', 'readonly':True},
336
'debit': {'string':'Debit amount', 'type':'float', 'readonly':True},
338
'string':"Date/Period Filter",
340
'selection':[('bydate','By Date'),
341
('byperiod','By Period'),
342
('all','By Date and Period'),
343
('none','No Filter')],
344
'default': lambda *a:'none'
346
'writeoff': {'string':'Write-Off amount', 'type':'float', 'readonly':True},
347
'writeoff_acc_id': {'string':'Write-Off account',
349
'relation':'account.account'
353
.. i18n: Each step/state of a wizard can have several buttons. Those are located on the bottom right of the dialog box. The list of buttons for each step of the wizard is declared in the state key of its result dictionary.
356
Each step/state of a wizard can have several buttons. Those are located on the bottom right of the dialog box. The list of buttons for each step of the wizard is declared in the state key of its result dictionary.
358
.. i18n: For example:
363
.. i18n: .. code-block:: python
365
.. i18n: 'state':[('end', 'Cancel', 'gtk-cancel'), ('reconcile', 'Reconcile', '', True)]
368
.. code-block:: python
370
'state':[('end', 'Cancel', 'gtk-cancel'), ('reconcile', 'Reconcile', '', True)]
372
.. i18n: #. the next step name (determine which state will be next)
373
.. i18n: #. the button string (to display for the client)
374
.. i18n: #. the gtk stock item without the stock prefix (since 4.2)
375
.. i18n: #. a boolean, if true the button is set as the default action (since 4.2)
378
#. the next step name (determine which state will be next)
379
#. the button string (to display for the client)
380
#. the gtk stock item without the stock prefix (since 4.2)
381
#. a boolean, if true the button is set as the default action (since 4.2)
383
.. i18n: Here is a screenshot of this form:
386
Here is a screenshot of this form:
388
.. i18n: .. image:: images/Wizard_screenshot1.png
389
.. i18n: :width: 100%
392
.. image:: images/Wizard_screenshot1.png
395
.. i18n: Result: call a method to determine which state is next
398
Result: call a method to determine which state is next
400
.. i18n: .. code-block:: python
402
.. i18n: def _check_refund(self, cr, uid, data, context):
404
.. i18n: return datas['form']['refund_id'] and 'wait_invoice' or 'end'
408
.. i18n: 'result': {'type':'choice', 'next_state':_check_refund}
411
.. code-block:: python
413
def _check_refund(self, cr, uid, data, context):
415
return datas['form']['refund_id'] and 'wait_invoice' or 'end'
419
'result': {'type':'choice', 'next_state':_check_refund}
421
.. i18n: Result: print a report
424
Result: print a report
426
.. i18n: .. code-block:: python
428
.. i18n: def _get_invoice_id(self, uid, datas):
430
.. i18n: return {'ids': [...]}
434
.. i18n: 'actions': [_get_invoice_id],
435
.. i18n: 'result': {'type':'print',
436
.. i18n: 'report':'account.invoice',
437
.. i18n: 'get_id_from_action': True,
438
.. i18n: 'state':'check_refund'}
441
.. code-block:: python
443
def _get_invoice_id(self, uid, datas):
445
return {'ids': [...]}
449
'actions': [_get_invoice_id],
450
'result': {'type':'print',
451
'report':'account.invoice',
452
'get_id_from_action': True,
453
'state':'check_refund'}
455
.. i18n: Result: client run an action
458
Result: client run an action
460
.. i18n: .. code-block:: python
462
.. i18n: def _makeInvoices(self, cr, uid, data, context):
465
.. i18n: 'domain': "[('id','in', ["+','.join(map(str,newinv))+"])]",
466
.. i18n: 'name': 'Invoices',
467
.. i18n: 'view_type': 'form',
468
.. i18n: 'view_mode': 'tree,form',
469
.. i18n: 'res_model': 'account.invoice',
470
.. i18n: 'view_id': False,
471
.. i18n: 'context': "{'type':'out_refund'}",
472
.. i18n: 'type': 'ir.actions.act_window'
477
.. i18n: 'result': {'type': 'action',
478
.. i18n: 'action': _makeInvoices,
479
.. i18n: 'state': 'end'}
482
.. code-block:: python
484
def _makeInvoices(self, cr, uid, data, context):
487
'domain': "[('id','in', ["+','.join(map(str,newinv))+"])]",
490
'view_mode': 'tree,form',
491
'res_model': 'account.invoice',
493
'context': "{'type':'out_refund'}",
494
'type': 'ir.actions.act_window'
499
'result': {'type': 'action',
500
'action': _makeInvoices,
503
.. i18n: The result of the function must be an all the fields of an ir.actions.* Here it is an ir.action.act_window, so the client will open an new tab for the objects account.invoice For more information about the fields used click here.
506
The result of the function must be an all the fields of an ir.actions.* Here it is an ir.action.act_window, so the client will open an new tab for the objects account.invoice For more information about the fields used click here.
508
.. i18n: It is recommended to use the result of a read on the ir.actions object like this:
511
It is recommended to use the result of a read on the ir.actions object like this:
513
.. i18n: .. code-block:: python
515
.. i18n: def _account_chart_open_window(self, cr, uid, data, context):
516
.. i18n: mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
517
.. i18n: act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window')
519
.. i18n: result = mod_obj._get_id(cr, uid, 'account', 'action_account_tree')
520
.. i18n: id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
521
.. i18n: result = act_obj.read(cr, uid, [id])[0]
522
.. i18n: result['context'] = str({'fiscalyear': data['form']['fiscalyear']})
523
.. i18n: return result
527
.. i18n: 'result': {'type': 'action',
528
.. i18n: 'action': _account_chart_open_window,
529
.. i18n: 'state':'end'}
532
.. code-block:: python
534
def _account_chart_open_window(self, cr, uid, data, context):
535
mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
536
act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window')
538
result = mod_obj._get_id(cr, uid, 'account', 'action_account_tree')
539
id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
540
result = act_obj.read(cr, uid, [id])[0]
541
result['context'] = str({'fiscalyear': data['form']['fiscalyear']})
546
'result': {'type': 'action',
547
'action': _account_chart_open_window,
550
.. i18n: Specification
551
.. i18n: =============
564
.. i18n: .. code-block:: xml
566
.. i18n: _form = '''<?xml version="1.0"?>
567
.. i18n: <form string="Your String">
568
.. i18n: <field name="Field 1"/>
570
.. i18n: <field name="Field 2"/>
576
_form = '''<?xml version="1.0"?>
577
<form string="Your String">
578
<field name="Field 1"/>
580
<field name="Field 2"/>
597
.. i18n: .. code-block:: python
599
.. i18n: Field type: char, integer, boolean, float, date, datetime
602
.. i18n: 'str_field': {'string':'product name', 'type':'char', 'readonly':True},
606
.. code-block:: python
608
Field type: char, integer, boolean, float, date, datetime
611
'str_field': {'string':'product name', 'type':'char', 'readonly':True},
614
.. i18n: * **string**: Field label (required)
615
.. i18n: * **type**: (required)
616
.. i18n: * **readonly**: (optional)
619
* **string**: Field label (required)
620
* **type**: (required)
621
* **readonly**: (optional)
630
.. i18n: .. code-block:: python
632
.. i18n: Field type: one2one,many2one,one2many,many2many
635
.. i18n: 'field_id': {'string':'Write-Off account', 'type':'many2one', 'relation':'account.account'}
639
.. code-block:: python
641
Field type: one2one,many2one,one2many,many2many
644
'field_id': {'string':'Write-Off account', 'type':'many2one', 'relation':'account.account'}
647
.. i18n: * **string**: Field label (required)
648
.. i18n: * **type**: (required)
649
.. i18n: * **relation**: name of the relation object
652
* **string**: Field label (required)
653
* **type**: (required)
654
* **relation**: name of the relation object
663
.. i18n: .. code-block:: python
665
.. i18n: Field type: selection
668
.. i18n: 'field_id': {
669
.. i18n: 'string':"Date/Period Filter",
670
.. i18n: 'type':'selection',
671
.. i18n: 'selection':[('bydate','By Date'),
672
.. i18n: ('byperiod','By Period'),
673
.. i18n: ('all','By Date and Period'),
674
.. i18n: ('none','No Filter')],
675
.. i18n: 'default': lambda *a:'none'
679
.. code-block:: python
681
Field type: selection
685
'string':"Date/Period Filter",
687
'selection':[('bydate','By Date'),
688
('byperiod','By Period'),
689
('all','By Date and Period'),
690
('none','No Filter')],
691
'default': lambda *a:'none'
694
.. i18n: * **string**: Field label (required)
695
.. i18n: * **type**: (required)
696
.. i18n: * **selection**: key and values for the selection field
699
* **string**: Field label (required)
700
* **type**: (required)
701
* **selection**: key and values for the selection field
703
.. i18n: Add A New Wizard
704
.. i18n: ================
710
.. i18n: To create a new wizard, you must:
713
To create a new wizard, you must:
715
.. i18n: * create the wizard definition in a .py file
716
.. i18n: * wizards are usually defined in the wizard subdirectory of their module as in server/bin/addons/module_name/wizard/your_wizard_name.py
717
.. i18n: * add your wizard to the list of import statements in the __init__.py file of your module's wizard subdirectory.
718
.. i18n: * declare your wizard in the database
721
* create the wizard definition in a .py file
722
* wizards are usually defined in the wizard subdirectory of their module as in server/bin/addons/module_name/wizard/your_wizard_name.py
723
* add your wizard to the list of import statements in the __init__.py file of your module's wizard subdirectory.
724
* declare your wizard in the database
726
.. i18n: The declaration is needed to map the wizard with a key of the client; when to launch which client. To declare a new wizard, you need to add it to the module_name_wizard.xml file, which contains all the wizard declarations for the module. If that file does not exist, you need to create it first.
729
The declaration is needed to map the wizard with a key of the client; when to launch which client. To declare a new wizard, you need to add it to the module_name_wizard.xml file, which contains all the wizard declarations for the module. If that file does not exist, you need to create it first.
731
.. i18n: Here is an example of the account_wizard.xml file;
734
Here is an example of the account_wizard.xml file;
736
.. i18n: .. code-block:: python
738
.. i18n: <?xml version="1.0"?>
741
.. i18n: <delete model="ir.actions.wizard" search="[('wiz_name','like','account.')]" />
742
.. i18n: <wizard string="Reconcile Transactions" model="account.move.line"
743
.. i18n: name="account.move.line.reconcile" />
744
.. i18n: <wizard string="Verify Transac steptions" model="account.move.line"
745
.. i18n: name="account.move.line.check" keyword="tree_but_action" />
746
.. i18n: <wizard string="Verify Transactions" model="account.move.line"
747
.. i18n: name="account.move.line.check" />
748
.. i18n: <wizard string="Print Journal" model="account.account"
749
.. i18n: name="account.journal" />
750
.. i18n: <wizard string="Split Invoice" model="account.invoice"
751
.. i18n: name="account.invoice.split" />
752
.. i18n: <wizard string="Refund Invoice" model="account.invoice"
753
.. i18n: name="account.invoice.refund" />
758
.. code-block:: python
760
<?xml version="1.0"?>
763
<delete model="ir.actions.wizard" search="[('wiz_name','like','account.')]" />
764
<wizard string="Reconcile Transactions" model="account.move.line"
765
name="account.move.line.reconcile" />
766
<wizard string="Verify Transac steptions" model="account.move.line"
767
name="account.move.line.check" keyword="tree_but_action" />
768
<wizard string="Verify Transactions" model="account.move.line"
769
name="account.move.line.check" />
770
<wizard string="Print Journal" model="account.account"
771
name="account.journal" />
772
<wizard string="Split Invoice" model="account.invoice"
773
name="account.invoice.split" />
774
<wizard string="Refund Invoice" model="account.invoice"
775
name="account.invoice.refund" />
779
.. i18n: Attributes for the wizard tag:
782
Attributes for the wizard tag:
784
.. i18n: * **id**: Unique identifier for this wizard.
785
.. i18n: * **string**: The string which will be displayed if there are several wizards for one resource. (The user will be presented a list with the wizards' names).
786
.. i18n: * **model**: The name of the **model** where the data needed by the wizard is.
787
.. i18n: * **name**: The name of the wizard. It is used internally and should be unique.
788
.. i18n: * **replace** (optional): Whether or not the wizard should override **all** existing wizards for this model. Default value: False.
789
.. i18n: * **menu** (optional): Whether or not (True|False) to link the wizard with the 'gears' button (i.e. show the button or not). Default value: True.
790
.. i18n: * **keyword** (optional): Bind the wizard to another action (print icon, gear icon, ...). Possible values for the keyword attribute are:
791
.. i18n: * **client_print_multi**: the print icon in a form
792
.. i18n: * **client_action_multi**: the 'gears' icon in a form
793
.. i18n: * **tree_but_action**: the 'gears' icon in a tree view (with the shortcuts on the left)
794
.. i18n: * **tree_but_open**: the double click on a branch of a tree (with the shortcuts on the left). For example, this is used, to bind wizards in the menu.
797
* **id**: Unique identifier for this wizard.
798
* **string**: The string which will be displayed if there are several wizards for one resource. (The user will be presented a list with the wizards' names).
799
* **model**: The name of the **model** where the data needed by the wizard is.
800
* **name**: The name of the wizard. It is used internally and should be unique.
801
* **replace** (optional): Whether or not the wizard should override **all** existing wizards for this model. Default value: False.
802
* **menu** (optional): Whether or not (True|False) to link the wizard with the 'gears' button (i.e. show the button or not). Default value: True.
803
* **keyword** (optional): Bind the wizard to another action (print icon, gear icon, ...). Possible values for the keyword attribute are:
804
* **client_print_multi**: the print icon in a form
805
* **client_action_multi**: the 'gears' icon in a form
806
* **tree_but_action**: the 'gears' icon in a tree view (with the shortcuts on the left)
807
* **tree_but_open**: the double click on a branch of a tree (with the shortcuts on the left). For example, this is used, to bind wizards in the menu.
809
.. i18n: **__openerp__.py**
814
.. i18n: If the wizard you created is the first one of its module, you probably had to create the modulename_wizard.xml file yourself. In that case, it should be added to the update_xml field of the __openerp__.py file of the module.
817
If the wizard you created is the first one of its module, you probably had to create the modulename_wizard.xml file yourself. In that case, it should be added to the update_xml field of the __openerp__.py file of the module.
819
.. i18n: Here is, for example, the **__openerp__.py** file for the account module.
822
Here is, for example, the **__openerp__.py** file for the account module.
824
.. i18n: .. code-block:: python
827
.. i18n: "name": OpenERP Accounting",
828
.. i18n: "version": "0.1",
829
.. i18n: "depends": ["base"],
830
.. i18n: "init_xml": ["account_workflow.xml", "account_data.xml"],
831
.. i18n: "update_xml": ["account_view.xml","account_report.xml", "account_wizard.xml"],
835
.. code-block:: python
838
"name": OpenERP Accounting",
841
"init_xml": ["account_workflow.xml", "account_data.xml"],
842
"update_xml": ["account_view.xml","account_report.xml", "account_wizard.xml"],
845
.. i18n: osv_memory Wizard System
846
.. i18n: ========================
847
.. i18n: To develop osv_memory wizard, just create a normal object, But instead of inheriting from osv.osv, Inherit from osv.osv_memory. Methods of "wizard" are in object and if the wizard is complex, You can define workflow on object. osv_memory object is managed in memory instead of storing in postgresql.
850
osv_memory Wizard System
851
========================
852
To develop osv_memory wizard, just create a normal object, But instead of inheriting from osv.osv, Inherit from osv.osv_memory. Methods of "wizard" are in object and if the wizard is complex, You can define workflow on object. osv_memory object is managed in memory instead of storing in postgresql.
854
.. i18n: That's all, nothing more than just changing the inherit. These wizards can be defined at any location unlike addons/modulename/modulename_wizard.py.
855
.. i18n: Historically, the _wizard prefix is for actual (old-style) wizards, so there might be a connotation there, the "new-style" osv_memory based "wizards" are perfectly normal objects (just used to emulate the old wizards, so they don't really match the old separations.
856
.. i18n: Furthermore, osv_memory based "wizards" tend to need more than one object (e.g. one osv_memory object for each state of the original wizard) so the correspondence is not exactly 1:1.
859
That's all, nothing more than just changing the inherit. These wizards can be defined at any location unlike addons/modulename/modulename_wizard.py.
860
Historically, the _wizard prefix is for actual (old-style) wizards, so there might be a connotation there, the "new-style" osv_memory based "wizards" are perfectly normal objects (just used to emulate the old wizards, so they don't really match the old separations.
861
Furthermore, osv_memory based "wizards" tend to need more than one object (e.g. one osv_memory object for each state of the original wizard) so the correspondence is not exactly 1:1.
863
.. i18n: So what makes them looks like 'old' wizards?
866
So what makes them looks like 'old' wizards?
868
.. i18n: * In the action that opens the object, you can put
871
* In the action that opens the object, you can put
873
.. i18n: .. code-block:: python
875
.. i18n: <field name="target">new</field>
878
.. code-block:: python
880
<field name="target">new</field>
882
.. i18n: It means the object will open in a new window instead of the current one.
885
It means the object will open in a new window instead of the current one.
887
.. i18n: * On a button, you can use <button special="cancel" .../> to close the window.
890
* On a button, you can use <button special="cancel" .../> to close the window.
892
.. i18n: Example : In project.py file.
895
Example : In project.py file.
897
.. i18n: .. code-block:: python
899
.. i18n: class config_compute_remaining(osv.osv_memory):
900
.. i18n: _name='config.compute.remaining'
901
.. i18n: def _get_remaining(self,cr, uid, ctx):
902
.. i18n: if 'active_id' in ctx:
903
.. i18n: return self.pool.get('project.task').browse(cr,uid,ctx['active_id']).remaining_hours
904
.. i18n: return False
905
.. i18n: _columns = {
906
.. i18n: 'remaining_hours' : fields.float('Remaining Hours', digits=(16,2),),
908
.. i18n: _defaults = {
909
.. i18n: 'remaining_hours': _get_remaining
911
.. i18n: def compute_hours(self, cr, uid, ids, context=None):
912
.. i18n: if 'active_id' in context:
913
.. i18n: remaining_hrs=self.browse(cr,uid,ids)[0].remaining_hours
914
.. i18n: self.pool.get('project.task').write(cr,uid,context['active_id'],
915
.. i18n: {'remaining_hours' : remaining_hrs})
917
.. i18n: 'type': 'ir.actions.act_window_close',
919
.. i18n: config_compute_remaining()
922
.. code-block:: python
924
class config_compute_remaining(osv.osv_memory):
925
_name='config.compute.remaining'
926
def _get_remaining(self,cr, uid, ctx):
927
if 'active_id' in ctx:
928
return self.pool.get('project.task').browse(cr,uid,ctx['active_id']).remaining_hours
931
'remaining_hours' : fields.float('Remaining Hours', digits=(16,2),),
934
'remaining_hours': _get_remaining
936
def compute_hours(self, cr, uid, ids, context=None):
937
if 'active_id' in context:
938
remaining_hrs=self.browse(cr,uid,ids)[0].remaining_hours
939
self.pool.get('project.task').write(cr,uid,context['active_id'],
940
{'remaining_hours' : remaining_hrs})
942
'type': 'ir.actions.act_window_close',
944
config_compute_remaining()
946
.. i18n: * View is same as normal view (Note buttons).
949
* View is same as normal view (Note buttons).
956
.. i18n: .. code-block:: xml
958
.. i18n: <record id="view_config_compute_remaining" model="ir.ui.view">
959
.. i18n: <field name="name">Compute Remaining Hours </field>
960
.. i18n: <field name="model">config.compute.remaining</field>
961
.. i18n: <field name="type">form</field>
962
.. i18n: <field name="arch" type="xml">
963
.. i18n: <form string="Remaining Hours">
964
.. i18n: <separator colspan="4" string="Change Remaining Hours"/>
966
.. i18n: <field name="remaining_hours" widget="float_time"/>
967
.. i18n: <group col="4" colspan="4">
968
.. i18n: <button icon="gtk-cancel" special="cancel" string="Cancel"/>
969
.. i18n: <button icon="gtk-ok" name="compute_hours" string="Update" type="object"/>
978
<record id="view_config_compute_remaining" model="ir.ui.view">
979
<field name="name">Compute Remaining Hours </field>
980
<field name="model">config.compute.remaining</field>
981
<field name="type">form</field>
982
<field name="arch" type="xml">
983
<form string="Remaining Hours">
984
<separator colspan="4" string="Change Remaining Hours"/>
986
<field name="remaining_hours" widget="float_time"/>
987
<group col="4" colspan="4">
988
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
989
<button icon="gtk-ok" name="compute_hours" string="Update" type="object"/>
995
.. i18n: * Action is also same as normal action (don't forget to add target attribute)
998
* Action is also same as normal action (don't forget to add target attribute)
1005
.. i18n: .. code-block:: xml
1007
.. i18n: <record id="action_config_compute_remaining" model="ir.actions.act_window">
1008
.. i18n: <field name="name">Compute Remaining Hours</field>
1009
.. i18n: <field name="type">ir.actions.act_window</field>
1010
.. i18n: <field name="res_model">config.compute.remaining</field>
1011
.. i18n: <field name="view_type">form</field>
1012
.. i18n: <field name="view_mode">form</field>
1013
.. i18n: <field name="target">new</field>
1019
<record id="action_config_compute_remaining" model="ir.actions.act_window">
1020
<field name="name">Compute Remaining Hours</field>
1021
<field name="type">ir.actions.act_window</field>
1022
<field name="res_model">config.compute.remaining</field>
1023
<field name="view_type">form</field>
1024
<field name="view_mode">form</field>
1025
<field name="target">new</field>
1028
.. i18n: osv_memory configuration item
1029
.. i18n: =============================
1032
osv_memory configuration item
1033
=============================
1035
.. i18n: Sometimes, your addon can't do with configurable defaults and needs
1036
.. i18n: upfront configuration settings to work correctly. In these cases, you
1037
.. i18n: want to provide a configuration wizard right after installation, and
1038
.. i18n: potentially one which can be re-run later if needed.
1041
Sometimes, your addon can't do with configurable defaults and needs
1042
upfront configuration settings to work correctly. In these cases, you
1043
want to provide a configuration wizard right after installation, and
1044
potentially one which can be re-run later if needed.
1046
.. i18n: Up until 5.0, OpenERP had such a facility but it was hardly documented
1047
.. i18n: and a very manual, arduous process. A simpler, more straightforward
1048
.. i18n: solution has been implemented for those needs.
1051
Up until 5.0, OpenERP had such a facility but it was hardly documented
1052
and a very manual, arduous process. A simpler, more straightforward
1053
solution has been implemented for those needs.
1055
.. i18n: The basic concepts
1056
.. i18n: ------------------
1062
.. i18n: The new implementation provides a base behavior ``osv_memory`` object
1063
.. i18n: from which you need to inherit. This behavior handles the flow between
1064
.. i18n: the configuration items of the various extensions, and inheriting from
1065
.. i18n: it is therefore mandatory.
1068
The new implementation provides a base behavior ``osv_memory`` object
1069
from which you need to inherit. This behavior handles the flow between
1070
the configuration items of the various extensions, and inheriting from
1071
it is therefore mandatory.
1073
.. i18n: There is also an inheritable view which provides a basic canvas,
1074
.. i18n: through mechanisms which will be explained later it's highly
1075
.. i18n: customizable. It's therefore strongly suggested that you should
1076
.. i18n: inherit from that view from yours as well.
1079
There is also an inheritable view which provides a basic canvas,
1080
through mechanisms which will be explained later it's highly
1081
customizable. It's therefore strongly suggested that you should
1082
inherit from that view from yours as well.
1084
.. i18n: Creating a basic configuration item
1085
.. i18n: -----------------------------------
1088
Creating a basic configuration item
1089
-----------------------------------
1091
.. i18n: Your configuration model
1092
.. i18n: ++++++++++++++++++++++++
1095
Your configuration model
1096
++++++++++++++++++++++++
1098
.. i18n: First comes the creation of the configuration item itself. This is a
1099
.. i18n: normal ``osv_memory`` object with a few constraints:
1102
First comes the creation of the configuration item itself. This is a
1103
normal ``osv_memory`` object with a few constraints:
1105
.. i18n: * it has to inherit from ``res.config``, which provides the basic
1106
.. i18n: configuration behaviors as well as the base event handlers and
1107
.. i18n: extension points
1109
.. i18n: * it has to provide an ``execute`` method.[#]_ This method will be called
1110
.. i18n: when validating the configuration form and contains the validation
1111
.. i18n: logic. It shouldn't return anything.
1114
* it has to inherit from ``res.config``, which provides the basic
1115
configuration behaviors as well as the base event handlers and
1118
* it has to provide an ``execute`` method.[#]_ This method will be called
1119
when validating the configuration form and contains the validation
1120
logic. It shouldn't return anything.
1122
.. i18n: .. code-block:: python
1124
.. i18n: class my_item_config(osv.osv_memory):
1125
.. i18n: _name = 'my.model.config'
1126
.. i18n: _inherit = 'res.config' # mandatory
1128
.. i18n: _columns = {
1129
.. i18n: 'my_field': fields.char('Field', size=64, required=True),
1132
.. i18n: def execute(self, cr, uid, ids, context=None):
1133
.. i18n: 'do whatever configuration work you need here'
1134
.. i18n: my_item_config()
1137
.. code-block:: python
1139
class my_item_config(osv.osv_memory):
1140
_name = 'my.model.config'
1141
_inherit = 'res.config' # mandatory
1144
'my_field': fields.char('Field', size=64, required=True),
1147
def execute(self, cr, uid, ids, context=None):
1148
'do whatever configuration work you need here'
1151
.. i18n: Your configuration view
1152
.. i18n: +++++++++++++++++++++++
1155
Your configuration view
1156
+++++++++++++++++++++++
1158
.. i18n: Then comes the configuration form. OpenERP provides a base view which
1159
.. i18n: you can inherit so you don't have to deal with creating buttons and
1160
.. i18n: handling the progress bar (which should be displayed at the bottom
1161
.. i18n: left of all initial configuration dialogs). It's very strongly
1162
.. i18n: recommended that you use this base view.
1165
Then comes the configuration form. OpenERP provides a base view which
1166
you can inherit so you don't have to deal with creating buttons and
1167
handling the progress bar (which should be displayed at the bottom
1168
left of all initial configuration dialogs). It's very strongly
1169
recommended that you use this base view.
1171
.. i18n: Simply add an ``inherit_id`` field to a regular ``ir.ui.view`` and
1172
.. i18n: set its value to ``res_config_view_base``:
1175
Simply add an ``inherit_id`` field to a regular ``ir.ui.view`` and
1176
set its value to ``res_config_view_base``:
1178
.. i18n: .. code-block:: xml
1180
.. i18n: <record id="my_config_view_form" model="ir.ui.view">
1181
.. i18n: <field name="name">my.item.config.view</field>
1182
.. i18n: <!-- this is the model defined above -->
1183
.. i18n: <field name="model">my.model.config</field>
1184
.. i18n: <field name="type">form</field>
1185
.. i18n: <field name="inherit_id" ref="base.res_config_view_base"/>
1192
<record id="my_config_view_form" model="ir.ui.view">
1193
<field name="name">my.item.config.view</field>
1194
<!-- this is the model defined above -->
1195
<field name="model">my.model.config</field>
1196
<field name="type">form</field>
1197
<field name="inherit_id" ref="base.res_config_view_base"/>
1201
.. i18n: While this could be used as-is, it would display an empty dialog with
1202
.. i18n: a progress bar and two buttons which isn't of much
1203
.. i18n: interest. ``res_config_view_base`` has a special group hook which you
1204
.. i18n: should replace with your own content like so:
1207
While this could be used as-is, it would display an empty dialog with
1208
a progress bar and two buttons which isn't of much
1209
interest. ``res_config_view_base`` has a special group hook which you
1210
should replace with your own content like so:
1212
.. i18n: .. code-block:: xml
1214
.. i18n: <field name="arch" type="xml">
1215
.. i18n: <group string="res_config_contents" position="replace">
1216
.. i18n: <!-- your content should be inserted within this, the string
1217
.. i18n: attribute of the previous group is used to easily find
1218
.. i18n: it for replacement -->
1219
.. i18n: <label colspan="4" align="0.0" string="
1220
.. i18n: Configure this item by defining its field"/>
1221
.. i18n: <field colspan="2" name="my_field"/>
1228
<field name="arch" type="xml">
1229
<group string="res_config_contents" position="replace">
1230
<!-- your content should be inserted within this, the string
1231
attribute of the previous group is used to easily find
1232
it for replacement -->
1233
<label colspan="4" align="0.0" string="
1234
Configure this item by defining its field"/>
1235
<field colspan="2" name="my_field"/>
1239
.. i18n: Opening your window
1240
.. i18n: +++++++++++++++++++
1246
.. i18n: The next step is to create the ``act_window`` which links to the
1247
.. i18n: configuration model and the view:
1250
The next step is to create the ``act_window`` which links to the
1251
configuration model and the view:
1253
.. i18n: .. code-block:: xml
1255
.. i18n: <record id="my_config_window" model="ir.actions.act_window">
1256
.. i18n: <field name="name">My config window</field>
1257
.. i18n: <field name="type">ir.actions.act_window</field>
1258
.. i18n: <field name="res_model">my.model.config</field>
1259
.. i18n: <field name="view_type">form</field>
1260
.. i18n: <field name="view_id" ref="my_config_view_form"/>
1261
.. i18n: <field name="view_mode">form</field>
1262
.. i18n: <field name="target">new</field>
1268
<record id="my_config_window" model="ir.actions.act_window">
1269
<field name="name">My config window</field>
1270
<field name="type">ir.actions.act_window</field>
1271
<field name="res_model">my.model.config</field>
1272
<field name="view_type">form</field>
1273
<field name="view_id" ref="my_config_view_form"/>
1274
<field name="view_mode">form</field>
1275
<field name="target">new</field>
1278
.. i18n: Note that the ``name`` field of this ``act_window`` will be displayed
1279
.. i18n: when listing the various configuration items in the Config Wizard
1280
.. i18n: Steps submenu (in Administration > Configuration > Configuration
1284
Note that the ``name`` field of this ``act_window`` will be displayed
1285
when listing the various configuration items in the Config Wizard
1286
Steps submenu (in Administration > Configuration > Configuration
1289
.. i18n: Registering your action
1290
.. i18n: +++++++++++++++++++++++
1293
Registering your action
1294
+++++++++++++++++++++++
1296
.. i18n: Finally comes actually registering the configuration item with
1297
.. i18n: OpenERP. This is done with an ``ir.actions.todo`` object, which
1298
.. i18n: mandates a single ``action_id`` field referencing the ``act_window``
1299
.. i18n: created previously:
1302
Finally comes actually registering the configuration item with
1303
OpenERP. This is done with an ``ir.actions.todo`` object, which
1304
mandates a single ``action_id`` field referencing the ``act_window``
1307
.. i18n: .. code-block:: xml
1309
.. i18n: <record id="my_config_step" model="ir.actions.todo">
1310
.. i18n: <field name="action_id" ref="my_config_window"/>
1316
<record id="my_config_step" model="ir.actions.todo">
1317
<field name="action_id" ref="my_config_window"/>
1320
.. i18n: ``ir.actions.todo`` also has 3 optional fields:
1323
``ir.actions.todo`` also has 3 optional fields:
1325
.. i18n: ``sequence`` (default: ``10``)
1326
.. i18n: The order in which the different steps are to be
1327
.. i18n: executed, lowest first.
1330
``sequence`` (default: ``10``)
1331
The order in which the different steps are to be
1332
executed, lowest first.
1334
.. i18n: ``active`` (default: ``True``)
1335
.. i18n: An inactive step will not be executed on the next round of
1336
.. i18n: configuration.
1339
``active`` (default: ``True``)
1340
An inactive step will not be executed on the next round of
1343
.. i18n: ``state`` (default: ``'open'``)
1344
.. i18n: The current state for the configuration step, mostly used to
1345
.. i18n: register what happened during its execution. The possible
1346
.. i18n: values are ``'open'``, ``'done'``, ``'skip'`` and
1347
.. i18n: ``'cancel'``.
1350
``state`` (default: ``'open'``)
1351
The current state for the configuration step, mostly used to
1352
register what happened during its execution. The possible
1353
values are ``'open'``, ``'done'``, ``'skip'`` and
1356
.. i18n: The result at this point is the following:
1359
The result at this point is the following:
1361
.. i18n: .. image:: images/config_wizard_base.png
1362
.. i18n: :width: 100%
1365
.. image:: images/config_wizard_base.png
1368
.. i18n: Customizing your configuration item
1369
.. i18n: -----------------------------------
1372
Customizing your configuration item
1373
-----------------------------------
1375
.. i18n: While your current knowledge is certainly enough to configure your
1376
.. i18n: addon, a bit of good customization can be the difference between a
1377
.. i18n: good user experience and a great user experience.
1380
While your current knowledge is certainly enough to configure your
1381
addon, a bit of good customization can be the difference between a
1382
good user experience and a great user experience.
1384
.. i18n: More extensive view customization
1385
.. i18n: +++++++++++++++++++++++++++++++++
1388
More extensive view customization
1389
+++++++++++++++++++++++++++++++++
1391
.. i18n: As you might have noticed from the previous screen shot, by default
1392
.. i18n: your configuration window doesn't have a *title*, which isn't a
1393
.. i18n: problem but doesn't look very good either.
1396
As you might have noticed from the previous screen shot, by default
1397
your configuration window doesn't have a *title*, which isn't a
1398
problem but doesn't look very good either.
1400
.. i18n: Before setting a title, a small modification to the existing view is
1401
.. i18n: needed though: the existing ``group`` needs to be wrapped in a
1402
.. i18n: ``data`` element so it's possible to customize more than one item of
1403
.. i18n: the parent view:
1406
Before setting a title, a small modification to the existing view is
1407
needed though: the existing ``group`` needs to be wrapped in a
1408
``data`` element so it's possible to customize more than one item of
1411
.. i18n: .. code-block:: xml
1413
.. i18n: <record id="my_config_view_form" model="ir.ui.view">
1414
.. i18n: <field name="name">my.item.config.view</field>
1415
.. i18n: <!-- this is the model defined above -->
1416
.. i18n: <field name="model">my.model.config</field>
1417
.. i18n: <field name="type">form</field>
1418
.. i18n: <field name="inherit_id">res_config_view_base</field>
1419
.. i18n: <field name="arch" type="xml">
1421
.. i18n: <group string="res_config_contents" position="replace">
1422
.. i18n: <!-- your content should be inserted within this, the
1423
.. i18n: string attribute of the previous group is used to
1424
.. i18n: easily find it for replacement
1426
.. i18n: <label colspan="4" align="0.0" string="
1427
.. i18n: Configure this item by defining its field
1429
.. i18n: <field colspan="2" name="my_field"/>
1438
<record id="my_config_view_form" model="ir.ui.view">
1439
<field name="name">my.item.config.view</field>
1440
<!-- this is the model defined above -->
1441
<field name="model">my.model.config</field>
1442
<field name="type">form</field>
1443
<field name="inherit_id">res_config_view_base</field>
1444
<field name="arch" type="xml">
1446
<group string="res_config_contents" position="replace">
1447
<!-- your content should be inserted within this, the
1448
string attribute of the previous group is used to
1449
easily find it for replacement
1451
<label colspan="4" align="0.0" string="
1452
Configure this item by defining its field
1454
<field colspan="2" name="my_field"/>
1460
.. i18n: Then it becomes possible to alter the ``string`` attribute of the
1461
.. i18n: original ``form`` by adding the following code within the ``data``
1462
.. i18n: element (in this case, probably before ``group``):
1465
Then it becomes possible to alter the ``string`` attribute of the
1466
original ``form`` by adding the following code within the ``data``
1467
element (in this case, probably before ``group``):
1469
.. i18n: .. code-block:: xml
1471
.. i18n: <!-- position=attributes is new and is used to alter the
1472
.. i18n: element's attributes, instead of its content -->
1473
.. i18n: <form position="attributes">
1474
.. i18n: <!-- set the value of the 'string' attribute -->
1475
.. i18n: <attribute name="string">Set item field</attribute>
1481
<!-- position=attributes is new and is used to alter the
1482
element's attributes, instead of its content -->
1483
<form position="attributes">
1484
<!-- set the value of the 'string' attribute -->
1485
<attribute name="string">Set item field</attribute>
1488
.. i18n: .. warning:: Comments in view overload
1490
.. i18n: At this point (December 2009) OpenERP cannot handle comments at the
1491
.. i18n: toplevel of the view element overload. When testing or reusing
1492
.. i18n: these examples, remember to strip out the comments or you will get
1493
.. i18n: runtime errors when testing the addon.
1496
.. warning:: Comments in view overload
1498
At this point (December 2009) OpenERP cannot handle comments at the
1499
toplevel of the view element overload. When testing or reusing
1500
these examples, remember to strip out the comments or you will get
1501
runtime errors when testing the addon.
1503
.. i18n: With this, the configuration form gets a nice title:
1506
With this, the configuration form gets a nice title:
1508
.. i18n: .. image:: images/config_wizard_title.png
1509
.. i18n: :width: 100%
1512
.. image:: images/config_wizard_title.png
1515
.. i18n: More interesting customizations might be to alter the buttons provided
1516
.. i18n: by ``res_config_view_base`` at the bottom of the dialog: remove a
1517
.. i18n: button (if the configuration action shouldn't be skipped), change
1518
.. i18n: the button labels, ...
1521
More interesting customizations might be to alter the buttons provided
1522
by ``res_config_view_base`` at the bottom of the dialog: remove a
1523
button (if the configuration action shouldn't be skipped), change
1524
the button labels, ...
1526
.. i18n: Since no specific hooks are provided for these alterations, they
1527
.. i18n: require the use of xpath selectors (using the ``xpath`` element).
1530
Since no specific hooks are provided for these alterations, they
1531
require the use of xpath selectors (using the ``xpath`` element).
1533
.. i18n: Removing the Skip button and changing the label of the Record button
1534
.. i18n: to Set, for instance, would be done by adding the following after the
1535
.. i18n: ``group`` element:
1538
Removing the Skip button and changing the label of the Record button
1539
to Set, for instance, would be done by adding the following after the
1542
.. i18n: .. code-block:: xml
1544
.. i18n: <!-- select the button 'action_skip' of the original template
1545
.. i18n: and replace it by nothing, removing it -->
1546
.. i18n: <xpath expr="//button[@name='action_skip']"
1547
.. i18n: position="replace"/>
1552
<!-- select the button 'action_skip' of the original template
1553
and replace it by nothing, removing it -->
1554
<xpath expr="//button[@name='action_skip']"
1555
position="replace"/>
1557
.. i18n: .. code-block:: xml
1559
.. i18n: <!-- select the button 'action_next' -->
1560
.. i18n: <xpath expr="//button[@name='action_next']"
1561
.. i18n: position="attributes">
1562
.. i18n: <!-- and change the attribute 'string' to 'Set' -->
1563
.. i18n: <attribute name="string">Set</attribute>
1569
<!-- select the button 'action_next' -->
1570
<xpath expr="//button[@name='action_next']"
1571
position="attributes">
1572
<!-- and change the attribute 'string' to 'Set' -->
1573
<attribute name="string">Set</attribute>
1581
.. i18n: .. image:: images/config_wizard_buttons.png
1582
.. i18n: :width: 100%
1585
.. image:: images/config_wizard_buttons.png
1588
.. i18n: It is also possible to use this method to change the name of the
1589
.. i18n: button, and thus the method invoked on the object (though that isn't
1590
.. i18n: necessarily recommended).
1593
It is also possible to use this method to change the name of the
1594
button, and thus the method invoked on the object (though that isn't
1595
necessarily recommended).
1597
.. i18n: Model customization
1598
.. i18n: +++++++++++++++++++
1604
.. i18n: Though most of the requirements should be easy to fulfill using the
1605
.. i18n: provided ``execute`` method hook, some addon-specific requirements
1606
.. i18n: are a bit more complex. ``res.config`` should be able to provide all
1607
.. i18n: the hooks necessary for more complex behaviors.
1610
Though most of the requirements should be easy to fulfill using the
1611
provided ``execute`` method hook, some addon-specific requirements
1612
are a bit more complex. ``res.config`` should be able to provide all
1613
the hooks necessary for more complex behaviors.
1615
.. i18n: Ignoring the next step
1616
.. i18n: ~~~~~~~~~~~~~~~~~~~~~~
1619
Ignoring the next step
1620
~~~~~~~~~~~~~~~~~~~~~~
1622
.. i18n: Ultimately, the switch to the next configuration item is done by
1623
.. i18n: calling the ``self.next`` method of ``res.config`` [#]_. This is the
1624
.. i18n: last thing the base implementations of ``action_next`` and
1625
.. i18n: ``action_skip`` do. But in some cases, looping on the current view or
1626
.. i18n: implementing a workflow-like behavior is needed. In these cases, you
1627
.. i18n: can simply return a dictionary from ``execute``, and ``res.config``
1628
.. i18n: will jump to that view instead of the one returned by ``self.next``.
1631
Ultimately, the switch to the next configuration item is done by
1632
calling the ``self.next`` method of ``res.config`` [#]_. This is the
1633
last thing the base implementations of ``action_next`` and
1634
``action_skip`` do. But in some cases, looping on the current view or
1635
implementing a workflow-like behavior is needed. In these cases, you
1636
can simply return a dictionary from ``execute``, and ``res.config``
1637
will jump to that view instead of the one returned by ``self.next``.
1639
.. i18n: This is what the user creation item does, for instance, to let the
1640
.. i18n: user create several new users in a row.
1643
This is what the user creation item does, for instance, to let the
1644
user create several new users in a row.
1646
.. i18n: Performing an action on skipping
1647
.. i18n: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1650
Performing an action on skipping
1651
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1653
.. i18n: As opposed to ``action_next`` which requires that ``execute`` be
1654
.. i18n: implemented by the children classes, ``action_skip`` comes fully
1655
.. i18n: implemented in ``res.config``. But in the case where the child model
1656
.. i18n: needs to perform an action upon skipping discovery, it also provides a
1657
.. i18n: hook method called ``cancel`` which you can overload in a way similar
1658
.. i18n: to ``execute``. Its behavior is identical to ``execute``'s: not only
1659
.. i18n: is ``next`` called automatically at the end of ``cancel`` but it also
1660
.. i18n: gives the possibility of `ignoring the next step`_.
1663
As opposed to ``action_next`` which requires that ``execute`` be
1664
implemented by the children classes, ``action_skip`` comes fully
1665
implemented in ``res.config``. But in the case where the child model
1666
needs to perform an action upon skipping discovery, it also provides a
1667
hook method called ``cancel`` which you can overload in a way similar
1668
to ``execute``. Its behavior is identical to ``execute``'s: not only
1669
is ``next`` called automatically at the end of ``cancel`` but it also
1670
gives the possibility of `ignoring the next step`_.
1672
.. i18n: Alternative actions
1673
.. i18n: ~~~~~~~~~~~~~~~~~~~
1679
.. i18n: It's also possible to either overload ``action_next`` and
1680
.. i18n: ``action_skip`` or, more useful, to implement more actions than these
1681
.. i18n: two, if more than two buttons are needed for instance.
1684
It's also possible to either overload ``action_next`` and
1685
``action_skip`` or, more useful, to implement more actions than these
1686
two, if more than two buttons are needed for instance.
1688
.. i18n: In this case, please remember that you should always provide a way to
1689
.. i18n: reach ``self.next`` to the user, in order for him to be able to
1690
.. i18n: configure the rest of his addons.
1693
In this case, please remember that you should always provide a way to
1694
reach ``self.next`` to the user, in order for him to be able to
1695
configure the rest of his addons.
1697
.. i18n: ``res.config``'s public API
1698
.. i18n: ---------------------------
1701
``res.config``'s public API
1702
---------------------------
1704
.. i18n: All of the public API methods take the standard OpenERP set of
1705
.. i18n: arguments: ``self``, ``cr``, ``uid``, ``ids`` and ``context``.
1708
All of the public API methods take the standard OpenERP set of
1709
arguments: ``self``, ``cr``, ``uid``, ``ids`` and ``context``.
1711
.. i18n: ``execute``
1712
.. i18n: +++++++++++
1718
.. i18n: Hook method called in case the ``action_next`` button
1719
.. i18n: (default label: Record) is clicked. Should not return *anything*
1720
.. i18n: unless you want to display another view than the next configuration
1721
.. i18n: item. Returning anything other than a view dictionary will lead to
1722
.. i18n: undefined behaviors.
1725
Hook method called in case the ``action_next`` button
1726
(default label: Record) is clicked. Should not return *anything*
1727
unless you want to display another view than the next configuration
1728
item. Returning anything other than a view dictionary will lead to
1729
undefined behaviors.
1731
.. i18n: It is mandatory to overload it. Failure to do so will result in a
1732
.. i18n: ``NotImplementedError`` being raised at runtime.
1735
It is mandatory to overload it. Failure to do so will result in a
1736
``NotImplementedError`` being raised at runtime.
1738
.. i18n: The default ``res.config`` implementation should not be called in the
1739
.. i18n: overload (don't use ``super``).
1742
The default ``res.config`` implementation should not be called in the
1743
overload (don't use ``super``).
1752
.. i18n: Hook method called in case the ``action_skip`` button
1753
.. i18n: (default label: Skip) is clicked. Its behavior is the same as
1754
.. i18n: `execute`_'s, except it's not mandatory to overload it.
1757
Hook method called in case the ``action_skip`` button
1758
(default label: Skip) is clicked. Its behavior is the same as
1759
`execute`_'s, except it's not mandatory to overload it.
1768
.. i18n: Method called to fetch the todo (and the corresponding action) for the
1769
.. i18n: next configuration item. It can be overloaded if the configuration
1770
.. i18n: item needs custom behavior common to all events.
1773
Method called to fetch the todo (and the corresponding action) for the
1774
next configuration item. It can be overloaded if the configuration
1775
item needs custom behavior common to all events.
1777
.. i18n: If overloaded, the default ``res.config`` implementation must be
1778
.. i18n: called and its result returned in order to get and execute the next
1779
.. i18n: configuration item.
1782
If overloaded, the default ``res.config`` implementation must be
1783
called and its result returned in order to get and execute the next
1786
.. i18n: ``action_next`` and ``action_skip``
1787
.. i18n: +++++++++++++++++++++++++++++++++++
1790
``action_next`` and ``action_skip``
1791
+++++++++++++++++++++++++++++++++++
1793
.. i18n: Event handler for the buttons of the base view, overloading them
1794
.. i18n: should never be necessary but in case it's needed the default
1795
.. i18n: ``res.config`` implementation should be called (via ``super``) and its
1796
.. i18n: result returned.
1799
Event handler for the buttons of the base view, overloading them
1800
should never be necessary but in case it's needed the default
1801
``res.config`` implementation should be called (via ``super``) and its
1804
.. i18n: .. [#] This isn't completely true, as you will see when `Customizing
1805
.. i18n: your configuration item`_
1808
.. [#] This isn't completely true, as you will see when `Customizing
1809
your configuration item`_
1811
.. i18n: .. [#] this method is part of the official API and you're free to
1812
.. i18n: overload it if needed, but you should always call
1813
.. i18n: ``res.config``'s through ``super`` when your work is
1814
.. i18n: done. Overloading ``next`` is also probably overkill in most
1815
.. i18n: situations.
1817
.. i18n: Guidelines on how to convert old-style wizard to new osv_memory style
1818
.. i18n: ======================================================================
1821
.. [#] this method is part of the official API and you're free to
1822
overload it if needed, but you should always call
1823
``res.config``'s through ``super`` when your work is
1824
done. Overloading ``next`` is also probably overkill in most
1827
Guidelines on how to convert old-style wizard to new osv_memory style
1828
======================================================================
1830
.. i18n: OSV Memory Wizard
1831
.. i18n: -----------------
1832
.. i18n: provide important advantages over the pre-5.0 wizard system, with support features that were difficult to implement in wizards previously, such as:
1837
provide important advantages over the pre-5.0 wizard system, with support features that were difficult to implement in wizards previously, such as:
1839
.. i18n: #. inheritance
1840
.. i18n: #. workflows
1841
.. i18n: #. complex relation fields
1842
.. i18n: #. computed fields
1843
.. i18n: #. all kind of views (lists, graphs, ...)
1848
#. complex relation fields
1850
#. all kind of views (lists, graphs, ...)
1852
.. i18n: The new wizards are also easier and more intuitive to write as they make use of the same syntax as other osv objects and views.
1855
The new wizards are also easier and more intuitive to write as they make use of the same syntax as other osv objects and views.
1857
.. i18n: This section will highlight the main steps usually required when porting a classical wizard to the new osv_memory wizard system.
1858
.. i18n: For more details about the osv_memory wizard see also section XXX.
1861
This section will highlight the main steps usually required when porting a classical wizard to the new osv_memory wizard system.
1862
For more details about the osv_memory wizard see also section XXX.
1864
.. i18n: Basically the idea is to create a regular osv object to hold the data structures and the logic of the wizard, but instead of inheriting from osv.osv, you inherit from osv.osv_memory. The methods of the old-style wizard will be moved as methods of the osv_memory object, and the various views changed into real views defined on the model of the wizard.
1867
Basically the idea is to create a regular osv object to hold the data structures and the logic of the wizard, but instead of inheriting from osv.osv, you inherit from osv.osv_memory. The methods of the old-style wizard will be moved as methods of the osv_memory object, and the various views changed into real views defined on the model of the wizard.
1869
.. i18n: If the wizard is complex, you could even define a workflow on the wizard object (see section XXX for details about workflows)
1872
If the wizard is complex, you could even define a workflow on the wizard object (see section XXX for details about workflows)
1874
.. i18n: Using a very simple wizard as an example, here is a step-by-step conversion to the new osv_memory system:
1877
Using a very simple wizard as an example, here is a step-by-step conversion to the new osv_memory system:
1886
.. i18n: 1. Create a new object that extends osv_memory, including the required fields and methods:
1889
1. Create a new object that extends osv_memory, including the required fields and methods:
1891
.. i18n: .. image:: images/wizard_window.png
1894
.. image:: images/wizard_window.png
1896
.. i18n: .. code-block:: python
1898
.. i18n: def _action_open_window(self, cr, uid, data, context):
1902
.. i18n: class product_margins(wizard.interface):
1903
.. i18n: form1 = '''<?xml version="1.0"?>
1904
.. i18n: <form string="View Stock of Products">
1905
.. i18n: <separator string="Select " colspan="4"/>
1906
.. i18n: <field name="from_date"/>
1907
.. i18n: <field name="to_date"/>
1908
.. i18n: <field name="invoice_state"/>
1911
.. i18n: form1_fields = {
1912
.. i18n: 'from_date': {
1913
.. i18n: 'string': 'From',
1914
.. i18n: 'type': 'date',
1915
.. i18n: 'default': lambda *a:time.strftime('%Y-01-01'),
1918
.. i18n: 'to_date': {
1919
.. i18n: 'string': 'To',
1920
.. i18n: 'type': 'date',
1921
.. i18n: 'default': lambda *a:time.strftime('%Y-12-31'),
1924
.. i18n: 'invoice_state': {
1925
.. i18n: 'string': 'Invoice State',
1926
.. i18n: 'type': 'selection',
1927
.. i18n: 'selection': [('paid','Paid'),('open_paid','Open and Paid'),('draft_open_paid','Draft, Open and Paid'),],
1928
.. i18n: 'required': True,
1929
.. i18n: 'default': lambda *a:"open_paid",
1935
.. i18n: 'actions': [],
1936
.. i18n: 'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, 'state': [('end', 'Cancel','gtk-cancel'),('open', 'Open Margins','gtk-ok')]}
1939
.. i18n: 'actions': [],
1940
.. i18n: 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
1943
.. i18n: product_margins('product.margins')
1946
.. code-block:: python
1948
def _action_open_window(self, cr, uid, data, context):
1952
class product_margins(wizard.interface):
1953
form1 = '''<?xml version="1.0"?>
1954
<form string="View Stock of Products">
1955
<separator string="Select " colspan="4"/>
1956
<field name="from_date"/>
1957
<field name="to_date"/>
1958
<field name="invoice_state"/>
1965
'default': lambda *a:time.strftime('%Y-01-01'),
1971
'default': lambda *a:time.strftime('%Y-12-31'),
1975
'string': 'Invoice State',
1976
'type': 'selection',
1977
'selection': [('paid','Paid'),('open_paid','Open and Paid'),('draft_open_paid','Draft, Open and Paid'),],
1979
'default': lambda *a:"open_paid",
1986
'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, 'state': [('end', 'Cancel','gtk-cancel'),('open', 'Open Margins','gtk-ok')]}
1990
'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
1993
product_margins('product.margins')
1995
.. i18n: New Wizard File : <<module_name>>_<<filename>>.py
1996
.. i18n: -------------------------------------------------
1999
New Wizard File : <<module_name>>_<<filename>>.py
2000
-------------------------------------------------
2002
.. i18n: .. code-block:: python
2004
.. i18n: class product_margin(osv.osv_memory):
2006
.. i18n: Product Margin
2008
.. i18n: _name = 'product.margin'
2009
.. i18n: _description = 'Product Margin'
2011
.. i18n: def _action_open_window(self, cr, uid, ids, context):
2016
.. i18n: _columns = {
2017
.. i18n: #TODO : import time required to get correct date
2018
.. i18n: 'from_date': fields.date('From'),
2019
.. i18n: #TODO : import time required to get correct date
2020
.. i18n: 'to_date': fields.date('To'),
2021
.. i18n: 'invoice_state':fields.selection([
2022
.. i18n: ('paid','Paid'),
2023
.. i18n: ('open_paid','Open and Paid'),
2024
.. i18n: ('draft_open_paid','Draft, Open and Paid'),
2025
.. i18n: ],'Invoice State', select=True, required=True),
2027
.. i18n: _defaults = {
2028
.. i18n: 'from_date': lambda *a:time.strftime('%Y-01-01'),
2029
.. i18n: 'to_date': lambda *a:time.strftime('%Y-01-01'),
2030
.. i18n: 'invoice_state': lambda *a:"open_paid",
2032
.. i18n: product_margin()
2035
.. code-block:: python
2037
class product_margin(osv.osv_memory):
2041
_name = 'product.margin'
2042
_description = 'Product Margin'
2044
def _action_open_window(self, cr, uid, ids, context):
2050
#TODO : import time required to get correct date
2051
'from_date': fields.date('From'),
2052
#TODO : import time required to get correct date
2053
'to_date': fields.date('To'),
2054
'invoice_state':fields.selection([
2056
('open_paid','Open and Paid'),
2057
('draft_open_paid','Draft, Open and Paid'),
2058
],'Invoice State', select=True, required=True),
2061
'from_date': lambda *a:time.strftime('%Y-01-01'),
2062
'to_date': lambda *a:time.strftime('%Y-01-01'),
2063
'invoice_state': lambda *a:"open_paid",
2067
.. i18n: Convert the views into real view records defined on the model of your wizard:
2070
Convert the views into real view records defined on the model of your wizard:
2072
.. i18n: Old Wizard File : wizard_product_margin.py
2073
.. i18n: ------------------------------------------
2076
Old Wizard File : wizard_product_margin.py
2077
------------------------------------------
2079
.. i18n: .. code-block:: python
2081
.. i18n: form1 = '''<?xml version="1.0"?>
2082
.. i18n: <form string="View Stock of Products">
2083
.. i18n: <separator string="Select " colspan="4"/>
2084
.. i18n: <field name="date"/>
2085
.. i18n: <field name="invoice_state"/>
2089
.. code-block:: python
2091
form1 = '''<?xml version="1.0"?>
2092
<form string="View Stock of Products">
2093
<separator string="Select " colspan="4"/>
2094
<field name="date"/>
2095
<field name="invoice_state"/>
2098
.. i18n: New Wizard File : wizard/<<module_name>>_<<filename>>_view.xml
2099
.. i18n: --------------------------------------------------------------
2102
New Wizard File : wizard/<<module_name>>_<<filename>>_view.xml
2103
--------------------------------------------------------------
2105
.. i18n: .. code-block:: xml
2107
.. i18n: <record id="product_margin_form_view" model="ir.ui.view">
2108
.. i18n: <field name="name">product.margin.form</field>
2109
.. i18n: <field name="model">product.margin</field>
2110
.. i18n: <field name="type">form</field>
2111
.. i18n: <field name="arch" type="xml">
2112
.. i18n: <form string="Properties categories">
2113
.. i18n: <separator colspan="4" string="General Information"/>
2114
.. i18n: <field name="from_date" />
2115
.. i18n: <field name="to_date" />
2116
.. i18n: <field name="invoice_state" />
2117
.. i18n: <group col="4" colspan="2">
2118
.. i18n: <button special="cancel" string="Cancel" type="object"/>
2119
.. i18n: <button name="_action_open_window" string="Open Margins" type="object" default_focus=”1”/>
2128
<record id="product_margin_form_view" model="ir.ui.view">
2129
<field name="name">product.margin.form</field>
2130
<field name="model">product.margin</field>
2131
<field name="type">form</field>
2132
<field name="arch" type="xml">
2133
<form string="Properties categories">
2134
<separator colspan="4" string="General Information"/>
2135
<field name="from_date" />
2136
<field name="to_date" />
2137
<field name="invoice_state" />
2138
<group col="4" colspan="2">
2139
<button special="cancel" string="Cancel" type="object"/>
2140
<button name="_action_open_window" string="Open Margins" type="object" default_focus=”1”/>
2146
.. i18n: Default_focus attribute
2147
.. i18n: -----------------------
2150
Default_focus attribute
2151
-----------------------
2153
.. i18n: .. code-block:: xml
2155
.. i18n: <button name="_action_open_window" string="Open Margins" type="object" default_focus=”1”/>
2160
<button name="_action_open_window" string="Open Margins" type="object" default_focus=”1”/>
2162
.. i18n: **default_focus="1"** is a new attribute added in 5.2. While opening wizard default control will be on the widget having this attribute. There must be only one widget on a view having this attribute = 1 otherwise it will raise exception.
2165
**default_focus="1"** is a new attribute added in 5.2. While opening wizard default control will be on the widget having this attribute. There must be only one widget on a view having this attribute = 1 otherwise it will raise exception.
2167
.. i18n: Note: For all states in the old wizard, we need to create buttons in new approach.
2170
Note: For all states in the old wizard, we need to create buttons in new approach.
2172
.. i18n: 2. To open the new wizard, you need to register an action that opens the first view on your wizard object. You will need to do the same for each view if your wizard contains several views. To make the view open in a pop-up window you can add a special target='new' field in the action:
2175
2. To open the new wizard, you need to register an action that opens the first view on your wizard object. You will need to do the same for each view if your wizard contains several views. To make the view open in a pop-up window you can add a special target='new' field in the action:
2177
.. i18n: .. code-block:: xml
2179
.. i18n: <act_window name="Open Margin"
2180
.. i18n: res_model="product.margin"
2181
.. i18n: src_model="product.product"
2182
.. i18n: view_mode="form"
2183
.. i18n: target="new"
2184
.. i18n: key2="client_action_multi"
2185
.. i18n: id="product_margin_act_window"/>
2190
<act_window name="Open Margin"
2191
res_model="product.margin"
2192
src_model="product.product"
2195
key2="client_action_multi"
2196
id="product_margin_act_window"/>
2198
.. i18n: key2="client_action_multi" : While using it in the act_window, wizard will be added in the
2201
key2="client_action_multi" : While using it in the act_window, wizard will be added in the
2208
.. i18n: .. image:: images/wizard_button.png
2211
.. image:: images/wizard_button.png
2218
.. i18n: .. image:: images/wizard_panel.png
2221
.. image:: images/wizard_panel.png
2223
.. i18n: If key2 is omitted then it will be displayed only in sidebar.
2226
If key2 is omitted then it will be displayed only in sidebar.
2228
.. i18n: Note: The "src_model" attribute is only required if you want to put the
2229
.. i18n: wizard in the side bar of an object, you can leave it out, for example
2230
.. i18n: if you define an action to open the second view of a wizard.
2233
Note: The "src_model" attribute is only required if you want to put the
2234
wizard in the side bar of an object, you can leave it out, for example
2235
if you define an action to open the second view of a wizard.
2237
.. i18n: 3. You can register this new action as a menuitem or in the context bar of any object by using a <menuitem> or <act_window> record instead of the old <wizard> tag that can be removed:
2240
3. You can register this new action as a menuitem or in the context bar of any object by using a <menuitem> or <act_window> record instead of the old <wizard> tag that can be removed:
2242
.. i18n: In Menu Item
2243
.. i18n: ------------
2249
.. i18n: To open a wizard view via a menuitem you can use the following syntax for the menu, using the XML id of the corresponding act_window.
2252
To open a wizard view via a menuitem you can use the following syntax for the menu, using the XML id of the corresponding act_window.
2254
.. i18n: .. code-block:: xml
2256
.. i18n: <menuitem id="main" name="OSV Memory Wizard Test"/>
2258
.. i18n: action="product_margin_act_window"
2259
.. i18n: id="menu_product_act"
2260
.. i18n: parent="main" />
2265
<menuitem id="main" name="OSV Memory Wizard Test"/>
2267
action="product_margin_act_window"
2268
id="menu_product_act"
2271
.. i18n: 4. To open a wizard view via a button in another form you can use the following syntax for the button, using the XML id of the corresponding act_window. This can be used to have multiple steps in your wizard:
2274
4. To open a wizard view via a button in another form you can use the following syntax for the button, using the XML id of the corresponding act_window. This can be used to have multiple steps in your wizard:
2276
.. i18n: .. code-block:: xml
2278
.. i18n: <button name="%(product_margin.product_margin_act_window)d"
2279
.. i18n: string="Test Wizard" type="action" states="draft"/>
2284
<button name="%(product_margin.product_margin_act_window)d"
2285
string="Test Wizard" type="action" states="draft"/>
2287
.. i18n: 5. Finally, you need to cleanup the module, update the python __init__.py files if you have changed the python file name for the wizard, and add your new XML files in the update_xml list in the __openerp__.py file.
2290
5. Finally, you need to cleanup the module, update the python __init__.py files if you have changed the python file name for the wizard, and add your new XML files in the update_xml list in the __openerp__.py file.