2
.. i18n: ===============================
3
.. i18n: Creating Wizard - (The Process)
4
.. i18n: ===============================
7
===============================
8
Creating Wizard - (The Process)
9
===============================
13
.. i18n: Wizards describe interaction sequences between the client and the server.
18
Wizards describe interaction sequences between the client and the server.
20
.. i18n: Here is, as an example, a typical process for a wizard:
23
Here is, as an example, a typical process for a wizard:
25
.. i18n: 1. A window is sent to the client (a form to be completed)
26
.. i18n: 2. The client sends back the data from the fields which were filled in
27
.. i18n: 3. The server gets the result, usually execute a function and possibly sends another window/form to the client
30
1. A window is sent to the client (a form to be completed)
31
2. The client sends back the data from the fields which were filled in
32
3. The server gets the result, usually execute a function and possibly sends another window/form to the client
34
.. i18n: .. image:: images/Wizard.png
37
.. image:: images/Wizard.png
39
.. i18n: Here is a screenshot of the wizard used to reconcile transactions (when you click on the gear icon in an account chart):
42
Here is a screenshot of the wizard used to reconcile transactions (when you click on the gear icon in an account chart):
44
.. i18n: .. image:: images/Wizard_screenshot.png
48
.. image:: images/Wizard_screenshot.png
51
.. i18n: Wizards - Principles
52
.. i18n: ====================
53
.. i18n: A wizard is a succession of steps. A step is composed of several actions;
58
A wizard is a succession of steps. A step is composed of several actions;
60
.. i18n: #. send a form to the client and some buttons
61
.. i18n: #. get the form result and the button pressed from the client
62
.. i18n: #. execute some actions
63
.. i18n: #. send a new action to the client (form, print, ...)
66
#. send a form to the client and some buttons
67
#. get the form result and the button pressed from the client
68
#. execute some actions
69
#. send a new action to the client (form, print, ...)
71
.. 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.
72
.. i18n: A full example of a simple wizard can be found at http://www.openobject.com/forum/post43900.html#43900
75
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.
76
A full example of a simple wizard can be found at http://www.openobject.com/forum/post43900.html#43900
78
.. i18n: Here is an example of such a class:
81
Here is an example of such a class:
83
.. i18n: .. code-block:: python
85
.. i18n: class wiz_reconcile(wizard.interface):
88
.. i18n: 'actions': [_trans_rec_get],
89
.. i18n: 'result': {'type': 'form',
90
.. i18n: 'arch': _transaction_form,
91
.. i18n: 'fields': _transaction_fields,
92
.. i18n: 'state':[('reconcile','Reconcile'),('end','Cancel')]}
94
.. i18n: 'reconcile': {
95
.. i18n: 'actions': [_trans_rec_reconcile],
96
.. i18n: 'result': {'type': 'state', 'state':'end'}
99
.. i18n: wiz_reconcile('account.move.line.reconcile');
102
.. code-block:: python
104
class wiz_reconcile(wizard.interface):
107
'actions': [_trans_rec_get],
108
'result': {'type': 'form',
109
'arch': _transaction_form,
110
'fields': _transaction_fields,
111
'state':[('reconcile','Reconcile'),('end','Cancel')]}
114
'actions': [_trans_rec_reconcile],
115
'result': {'type': 'state', 'state':'end'}
118
wiz_reconcile('account.move.line.reconcile');
120
.. 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.
123
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.
125
.. i18n: A wizard always starts in the **init** state and ends in the **end** state.
128
A wizard always starts in the **init** state and ends in the **end** state.
130
.. i18n: A state define two things:
133
A state define two things:
135
.. i18n: #. a list of actions
142
.. i18n: The list of actions
143
.. i18n: -------------------
144
.. 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.
149
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.
151
.. i18n: The function (actions) must have the following signatures:
154
The function (actions) must have the following signatures:
156
.. i18n: .. code-block:: python
158
.. i18n: def _trans_rec_get(self, uid, data, res_get=False):
161
.. code-block:: python
163
def _trans_rec_get(self, uid, data, res_get=False):
170
.. i18n: * **self** is the pointer to the wizard object
171
.. i18n: * **uid** is the user ID of the user which is executing the wizard
172
.. i18n: * **data** is a dictionary containing the following data:
173
.. i18n: * **ids**: the list of ids of resources selected when the user executed the wizard
174
.. i18n: * **id**: the id highlighted when the user executed the wizard
175
.. 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.
178
* **self** is the pointer to the wizard object
179
* **uid** is the user ID of the user which is executing the wizard
180
* **data** is a dictionary containing the following data:
181
* **ids**: the list of ids of resources selected when the user executed the wizard
182
* **id**: the id highlighted when the user executed the wizard
183
* **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.
185
.. i18n: Each action function must return a dictionary. Any entries in this dictionary
186
.. i18n: will be merged with the data that is passed to the form when it's displayed.
189
Each action function must return a dictionary. Any entries in this dictionary
190
will be merged with the data that is passed to the form when it's displayed.
199
.. i18n: Here are some result examples:
202
Here are some result examples:
204
.. i18n: Result: next step
209
.. i18n: .. code-block:: python
211
.. i18n: 'result': {'type': 'state',
212
.. i18n: 'state':'end'}
215
.. code-block:: python
217
'result': {'type': 'state',
220
.. i18n: Indicate that the wizard has to continue to the next state: 'end'. If this is the 'end' state, the wizard stops.
223
Indicate that the wizard has to continue to the next state: 'end'. If this is the 'end' state, the wizard stops.
225
.. i18n: Result: new dialog for the client
228
Result: new dialog for the client
230
.. i18n: .. code-block:: python
232
.. i18n: 'result': {'type': 'form',
233
.. i18n: 'arch': _form,
234
.. i18n: 'fields': _fields,
235
.. i18n: 'state':[('reconcile','Reconcile'),('end','Cancel')]}
238
.. code-block:: python
240
'result': {'type': 'form',
243
'state':[('reconcile','Reconcile'),('end','Cancel')]}
245
.. i18n: The type=form indicate that this step is a dialog to the client. The dialog is composed of:
248
The type=form indicate that this step is a dialog to the client. The dialog is composed of:
250
.. i18n: #. a form : with fields description and a form description
251
.. i18n: #. some buttons : on which the user press after completing the form
254
#. a form : with fields description and a form description
255
#. some buttons : on which the user press after completing the form
257
.. i18n: The form description (arch) is like in the views objects. Here is an example of form:
260
The form description (arch) is like in the views objects. Here is an example of form:
262
.. i18n: .. code-block:: xml
264
.. i18n: _form = """<?xml version="1.0"?>
265
.. i18n: <form title="Reconciliation">
266
.. i18n: <separator string="Reconciliation transactions" colspan="4"/>
267
.. i18n: <field name="trans_nbr"/>
269
.. i18n: <field name="credit"/>
270
.. i18n: <field name="debit"/>
271
.. i18n: <field name="state"/>
272
.. i18n: <separator string="Write-Off" colspan="4"/>
273
.. i18n: <field name="writeoff"/>
275
.. i18n: <field name="writeoff_acc_id" colspan="3"/>
282
_form = """<?xml version="1.0"?>
283
<form title="Reconciliation">
284
<separator string="Reconciliation transactions" colspan="4"/>
285
<field name="trans_nbr"/>
287
<field name="credit"/>
288
<field name="debit"/>
289
<field name="state"/>
290
<separator string="Write-Off" colspan="4"/>
291
<field name="writeoff"/>
293
<field name="writeoff_acc_id" colspan="3"/>
297
.. i18n: The fields description is similar to the fields described in the python ORM objects. Example:
300
The fields description is similar to the fields described in the python ORM objects. Example:
302
.. i18n: .. code-block:: python
304
.. i18n: _transaction_fields = {
305
.. i18n: 'trans_nbr': {'string':'# of Transaction', 'type':'integer', 'readonly':True},
306
.. i18n: 'credit': {'string':'Credit amount', 'type':'float', 'readonly':True},
307
.. i18n: 'debit': {'string':'Debit amount', 'type':'float', 'readonly':True},
309
.. i18n: 'string':"Date/Period Filter",
310
.. i18n: 'type':'selection',
311
.. i18n: 'selection':[('bydate','By Date'),
312
.. i18n: ('byperiod','By Period'),
313
.. i18n: ('all','By Date and Period'),
314
.. i18n: ('none','No Filter')],
315
.. i18n: 'default': lambda *a:'none'
317
.. i18n: 'writeoff': {'string':'Write-Off amount', 'type':'float', 'readonly':True},
318
.. i18n: 'writeoff_acc_id': {'string':'Write-Off account',
319
.. i18n: 'type':'many2one',
320
.. i18n: 'relation':'account.account'
325
.. code-block:: python
327
_transaction_fields = {
328
'trans_nbr': {'string':'# of Transaction', 'type':'integer', 'readonly':True},
329
'credit': {'string':'Credit amount', 'type':'float', 'readonly':True},
330
'debit': {'string':'Debit amount', 'type':'float', 'readonly':True},
332
'string':"Date/Period Filter",
334
'selection':[('bydate','By Date'),
335
('byperiod','By Period'),
336
('all','By Date and Period'),
337
('none','No Filter')],
338
'default': lambda *a:'none'
340
'writeoff': {'string':'Write-Off amount', 'type':'float', 'readonly':True},
341
'writeoff_acc_id': {'string':'Write-Off account',
343
'relation':'account.account'
347
.. 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.
350
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.
352
.. i18n: For example:
357
.. i18n: .. code-block:: python
359
.. i18n: 'state':[('end', 'Cancel', 'gtk-cancel'), ('reconcile', 'Reconcile', '', True)]
362
.. code-block:: python
364
'state':[('end', 'Cancel', 'gtk-cancel'), ('reconcile', 'Reconcile', '', True)]
366
.. i18n: #. the next step name (determine which state will be next)
367
.. i18n: #. the button string (to display for the client)
368
.. i18n: #. the gtk stock item without the stock prefix (since 4.2)
369
.. i18n: #. a boolean, if true the button is set as the default action (since 4.2)
372
#. the next step name (determine which state will be next)
373
#. the button string (to display for the client)
374
#. the gtk stock item without the stock prefix (since 4.2)
375
#. a boolean, if true the button is set as the default action (since 4.2)
377
.. i18n: Here is a screenshot of this form:
380
Here is a screenshot of this form:
382
.. i18n: .. image:: images/Wizard_screenshot1.png
383
.. i18n: :width: 100%
386
.. image:: images/Wizard_screenshot1.png
389
.. i18n: Result: call a method to determine which state is next
392
Result: call a method to determine which state is next
394
.. i18n: .. code-block:: python
396
.. i18n: def _check_refund(self, cr, uid, data, context):
398
.. i18n: return datas['form']['refund_id'] and 'wait_invoice' or 'end'
402
.. i18n: 'result': {'type':'choice', 'next_state':_check_refund}
405
.. code-block:: python
407
def _check_refund(self, cr, uid, data, context):
409
return datas['form']['refund_id'] and 'wait_invoice' or 'end'
413
'result': {'type':'choice', 'next_state':_check_refund}
415
.. i18n: Result: print a report
418
Result: print a report
420
.. i18n: .. code-block:: python
422
.. i18n: def _get_invoice_id(self, uid, datas):
424
.. i18n: return {'ids': [...]}
428
.. i18n: 'actions': [_get_invoice_id],
429
.. i18n: 'result': {'type':'print',
430
.. i18n: 'report':'account.invoice',
431
.. i18n: 'get_id_from_action': True,
432
.. i18n: 'state':'check_refund'}
435
.. code-block:: python
437
def _get_invoice_id(self, uid, datas):
439
return {'ids': [...]}
443
'actions': [_get_invoice_id],
444
'result': {'type':'print',
445
'report':'account.invoice',
446
'get_id_from_action': True,
447
'state':'check_refund'}
449
.. i18n: Result: client run an action
452
Result: client run an action
454
.. i18n: .. code-block:: python
456
.. i18n: def _makeInvoices(self, cr, uid, data, context):
459
.. i18n: 'domain': "[('id','in', ["+','.join(map(str,newinv))+"])]",
460
.. i18n: 'name': 'Invoices',
461
.. i18n: 'view_type': 'form',
462
.. i18n: 'view_mode': 'tree,form',
463
.. i18n: 'res_model': 'account.invoice',
464
.. i18n: 'view_id': False,
465
.. i18n: 'context': "{'type':'out_refund'}",
466
.. i18n: 'type': 'ir.actions.act_window'
471
.. i18n: 'result': {'type': 'action',
472
.. i18n: 'action': _makeInvoices,
473
.. i18n: 'state': 'end'}
476
.. code-block:: python
478
def _makeInvoices(self, cr, uid, data, context):
481
'domain': "[('id','in', ["+','.join(map(str,newinv))+"])]",
484
'view_mode': 'tree,form',
485
'res_model': 'account.invoice',
487
'context': "{'type':'out_refund'}",
488
'type': 'ir.actions.act_window'
493
'result': {'type': 'action',
494
'action': _makeInvoices,
497
.. 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.
500
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.
502
.. i18n: It is recommended to use the result of a read on the ir.actions object like this:
505
It is recommended to use the result of a read on the ir.actions object like this:
507
.. i18n: .. code-block:: python
509
.. i18n: def _account_chart_open_window(self, cr, uid, data, context):
510
.. i18n: mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
511
.. i18n: act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window')
513
.. i18n: result = mod_obj._get_id(cr, uid, 'account', 'action_account_tree')
514
.. i18n: id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
515
.. i18n: result = act_obj.read(cr, uid, [id])[0]
516
.. i18n: result['context'] = str({'fiscalyear': data['form']['fiscalyear']})
517
.. i18n: return result
521
.. i18n: 'result': {'type': 'action',
522
.. i18n: 'action': _account_chart_open_window,
523
.. i18n: 'state':'end'}
526
.. code-block:: python
528
def _account_chart_open_window(self, cr, uid, data, context):
529
mod_obj = pooler.get_pool(cr.dbname).get('ir.model.data')
530
act_obj = pooler.get_pool(cr.dbname).get('ir.actions.act_window')
532
result = mod_obj._get_id(cr, uid, 'account', 'action_account_tree')
533
id = mod_obj.read(cr, uid, [result], ['res_id'])[0]['res_id']
534
result = act_obj.read(cr, uid, [id])[0]
535
result['context'] = str({'fiscalyear': data['form']['fiscalyear']})
540
'result': {'type': 'action',
541
'action': _account_chart_open_window,
544
.. i18n: Specification
545
.. i18n: =============
558
.. i18n: .. code-block:: xml
560
.. i18n: _form = '''<?xml version="1.0"?>
561
.. i18n: <form string="Your String">
562
.. i18n: <field name="Field 1"/>
564
.. i18n: <field name="Field 2"/>
570
_form = '''<?xml version="1.0"?>
571
<form string="Your String">
572
<field name="Field 1"/>
574
<field name="Field 2"/>
591
.. i18n: .. code-block:: python
593
.. i18n: Field type: char, integer, boolean, float, date, datetime
596
.. i18n: 'str_field': {'string':'product name', 'type':'char', 'readonly':True},
600
.. code-block:: python
602
Field type: char, integer, boolean, float, date, datetime
605
'str_field': {'string':'product name', 'type':'char', 'readonly':True},
608
.. i18n: * **string**: Field label (required)
609
.. i18n: * **type**: (required)
610
.. i18n: * **readonly**: (optional)
613
* **string**: Field label (required)
614
* **type**: (required)
615
* **readonly**: (optional)
624
.. i18n: .. code-block:: python
626
.. i18n: Field type: one2one,many2one,one2many,many2many
629
.. i18n: 'field_id': {'string':'Write-Off account', 'type':'many2one', 'relation':'account.account'}
633
.. code-block:: python
635
Field type: one2one,many2one,one2many,many2many
638
'field_id': {'string':'Write-Off account', 'type':'many2one', 'relation':'account.account'}
641
.. i18n: * **string**: Field label (required)
642
.. i18n: * **type**: (required)
643
.. i18n: * **relation**: name of the relation object
646
* **string**: Field label (required)
647
* **type**: (required)
648
* **relation**: name of the relation object
657
.. i18n: .. code-block:: python
659
.. i18n: Field type: selection
662
.. i18n: 'field_id': {
663
.. i18n: 'string':"Date/Period Filter",
664
.. i18n: 'type':'selection',
665
.. i18n: 'selection':[('bydate','By Date'),
666
.. i18n: ('byperiod','By Period'),
667
.. i18n: ('all','By Date and Period'),
668
.. i18n: ('none','No Filter')],
669
.. i18n: 'default': lambda *a:'none'
673
.. code-block:: python
675
Field type: selection
679
'string':"Date/Period Filter",
681
'selection':[('bydate','By Date'),
682
('byperiod','By Period'),
683
('all','By Date and Period'),
684
('none','No Filter')],
685
'default': lambda *a:'none'
688
.. i18n: * **string**: Field label (required)
689
.. i18n: * **type**: (required)
690
.. i18n: * **selection**: key and values for the selection field
693
* **string**: Field label (required)
694
* **type**: (required)
695
* **selection**: key and values for the selection field
697
.. i18n: Add A New Wizard
698
.. i18n: ================
704
.. i18n: To create a new wizard, you must:
707
To create a new wizard, you must:
709
.. i18n: * create the wizard definition in a .py file
710
.. i18n: * wizards are usually defined in the wizard subdirectory of their module as in server/bin/addons/module_name/wizard/your_wizard_name.py
711
.. i18n: * add your wizard to the list of import statements in the __init__.py file of your module's wizard subdirectory.
712
.. i18n: * declare your wizard in the database
715
* create the wizard definition in a .py file
716
* wizards are usually defined in the wizard subdirectory of their module as in server/bin/addons/module_name/wizard/your_wizard_name.py
717
* add your wizard to the list of import statements in the __init__.py file of your module's wizard subdirectory.
718
* declare your wizard in the database
720
.. 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.
723
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.
725
.. i18n: Here is an example of the account_wizard.xml file;
728
Here is an example of the account_wizard.xml file;
730
.. i18n: .. code-block:: python
732
.. i18n: <?xml version="1.0"?>
735
.. i18n: <delete model="ir.actions.wizard" search="[('wiz_name','like','account.')]" />
736
.. i18n: <wizard string="Reconcile Transactions" model="account.move.line"
737
.. i18n: name="account.move.line.reconcile" />
738
.. i18n: <wizard string="Verify Transac steptions" model="account.move.line"
739
.. i18n: name="account.move.line.check" keyword="tree_but_action" />
740
.. i18n: <wizard string="Verify Transactions" model="account.move.line"
741
.. i18n: name="account.move.line.check" />
742
.. i18n: <wizard string="Print Journal" model="account.account"
743
.. i18n: name="account.journal" />
744
.. i18n: <wizard string="Split Invoice" model="account.invoice"
745
.. i18n: name="account.invoice.split" />
746
.. i18n: <wizard string="Refund Invoice" model="account.invoice"
747
.. i18n: name="account.invoice.refund" />
752
.. code-block:: python
754
<?xml version="1.0"?>
757
<delete model="ir.actions.wizard" search="[('wiz_name','like','account.')]" />
758
<wizard string="Reconcile Transactions" model="account.move.line"
759
name="account.move.line.reconcile" />
760
<wizard string="Verify Transac steptions" model="account.move.line"
761
name="account.move.line.check" keyword="tree_but_action" />
762
<wizard string="Verify Transactions" model="account.move.line"
763
name="account.move.line.check" />
764
<wizard string="Print Journal" model="account.account"
765
name="account.journal" />
766
<wizard string="Split Invoice" model="account.invoice"
767
name="account.invoice.split" />
768
<wizard string="Refund Invoice" model="account.invoice"
769
name="account.invoice.refund" />
773
.. i18n: Attributes for the wizard tag:
776
Attributes for the wizard tag:
778
.. i18n: * **id**: Unique identifier for this wizard.
779
.. 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).
780
.. i18n: * **model**: The name of the **model** where the data needed by the wizard is.
781
.. i18n: * **name**: The name of the wizard. It is used internally and should be unique.
782
.. i18n: * **replace** (optional): Whether or not the wizard should override **all** existing wizards for this model. Default value: False.
783
.. 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.
784
.. i18n: * **keyword** (optional): Bind the wizard to another action (print icon, gear icon, ...). Possible values for the keyword attribute are:
785
.. i18n: * **client_print_multi**: the print icon in a form
786
.. i18n: * **client_action_multi**: the 'gears' icon in a form
787
.. i18n: * **tree_but_action**: the 'gears' icon in a tree view (with the shortcuts on the left)
788
.. 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.
791
* **id**: Unique identifier for this wizard.
792
* **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).
793
* **model**: The name of the **model** where the data needed by the wizard is.
794
* **name**: The name of the wizard. It is used internally and should be unique.
795
* **replace** (optional): Whether or not the wizard should override **all** existing wizards for this model. Default value: False.
796
* **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.
797
* **keyword** (optional): Bind the wizard to another action (print icon, gear icon, ...). Possible values for the keyword attribute are:
798
* **client_print_multi**: the print icon in a form
799
* **client_action_multi**: the 'gears' icon in a form
800
* **tree_but_action**: the 'gears' icon in a tree view (with the shortcuts on the left)
801
* **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.
803
.. i18n: **__terp__.py**
808
.. 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 __terp__.py file of the module.
811
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 __terp__.py file of the module.
813
.. i18n: Here is, for example, the **__terp__.py** file for the account module.
816
Here is, for example, the **__terp__.py** file for the account module.
818
.. i18n: .. code-block:: python
821
.. i18n: "name": OpenERP Accounting",
822
.. i18n: "version": "0.1",
823
.. i18n: "depends": ["base"],
824
.. i18n: "init_xml": ["account_workflow.xml", "account_data.xml"],
825
.. i18n: "update_xml": ["account_view.xml","account_report.xml", "account_wizard.xml"],
829
.. code-block:: python
832
"name": OpenERP Accounting",
835
"init_xml": ["account_workflow.xml", "account_data.xml"],
836
"update_xml": ["account_view.xml","account_report.xml", "account_wizard.xml"],
839
.. i18n: osv_memory Wizard System
840
.. i18n: ========================
841
.. 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.
844
osv_memory Wizard System
845
========================
846
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.
848
.. 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.
849
.. 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.
850
.. 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 correspondance is not exactly 1:1.
853
That's all, nothing more than just changing the inherit. These wizards can be defined at any location unlike addons/modulename/modulename_wizard.py.
854
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.
855
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 correspondance is not exactly 1:1.
857
.. i18n: So what makes them looks like 'old' wizards?
860
So what makes them looks like 'old' wizards?
862
.. i18n: * In the action that opens the object, you can put
865
* In the action that opens the object, you can put
867
.. i18n: .. code-block:: python
869
.. i18n: <field name="target">new</field>
872
.. code-block:: python
874
<field name="target">new</field>
876
.. i18n: It means the object will open in a new window instead of the current one.
879
It means the object will open in a new window instead of the current one.
881
.. i18n: * On a button, you can use <button special="cancel" .../> to close the window.
884
* On a button, you can use <button special="cancel" .../> to close the window.
886
.. i18n: Example : In project.py file.
889
Example : In project.py file.
891
.. i18n: .. code-block:: python
893
.. i18n: class config_compute_remaining(osv.osv_memory):
894
.. i18n: _name='config.compute.remaining'
895
.. i18n: def _get_remaining(self,cr, uid, ctx):
896
.. i18n: if 'active_id' in ctx:
897
.. i18n: return self.pool.get('project.task').browse(cr,uid,ctx['active_id']).remaining_hours
898
.. i18n: return False
899
.. i18n: _columns = {
900
.. i18n: 'remaining_hours' : fields.float('Remaining Hours', digits=(16,2),),
902
.. i18n: _defaults = {
903
.. i18n: 'remaining_hours': _get_remaining
905
.. i18n: def compute_hours(self, cr, uid, ids, context=None):
906
.. i18n: if 'active_id' in context:
907
.. i18n: remaining_hrs=self.browse(cr,uid,ids)[0].remaining_hours
908
.. i18n: self.pool.get('project.task').write(cr,uid,context['active_id'],
909
.. i18n: {'remaining_hours' : remaining_hrs})
911
.. i18n: 'type': 'ir.actions.act_window_close',
913
.. i18n: config_compute_remaining()
916
.. code-block:: python
918
class config_compute_remaining(osv.osv_memory):
919
_name='config.compute.remaining'
920
def _get_remaining(self,cr, uid, ctx):
921
if 'active_id' in ctx:
922
return self.pool.get('project.task').browse(cr,uid,ctx['active_id']).remaining_hours
925
'remaining_hours' : fields.float('Remaining Hours', digits=(16,2),),
928
'remaining_hours': _get_remaining
930
def compute_hours(self, cr, uid, ids, context=None):
931
if 'active_id' in context:
932
remaining_hrs=self.browse(cr,uid,ids)[0].remaining_hours
933
self.pool.get('project.task').write(cr,uid,context['active_id'],
934
{'remaining_hours' : remaining_hrs})
936
'type': 'ir.actions.act_window_close',
938
config_compute_remaining()
940
.. i18n: * View is same as normal view (Note buttons).
943
* View is same as normal view (Note buttons).
950
.. i18n: .. code-block:: xml
952
.. i18n: <record id="view_config_compute_remaining" model="ir.ui.view">
953
.. i18n: <field name="name">Compute Remaining Hours </field>
954
.. i18n: <field name="model">config.compute.remaining</field>
955
.. i18n: <field name="type">form</field>
956
.. i18n: <field name="arch" type="xml">
957
.. i18n: <form string="Remaining Hours">
958
.. i18n: <separator colspan="4" string="Change Remaining Hours"/>
960
.. i18n: <field name="remaining_hours" widget="float_time"/>
961
.. i18n: <group col="4" colspan="4">
962
.. i18n: <button icon="gtk-cancel" special="cancel" string="Cancel"/>
963
.. i18n: <button icon="gtk-ok" name="compute_hours" string="Update" type="object"/>
972
<record id="view_config_compute_remaining" model="ir.ui.view">
973
<field name="name">Compute Remaining Hours </field>
974
<field name="model">config.compute.remaining</field>
975
<field name="type">form</field>
976
<field name="arch" type="xml">
977
<form string="Remaining Hours">
978
<separator colspan="4" string="Change Remaining Hours"/>
980
<field name="remaining_hours" widget="float_time"/>
981
<group col="4" colspan="4">
982
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
983
<button icon="gtk-ok" name="compute_hours" string="Update" type="object"/>
989
.. i18n: * Action is also same as normal action (don't forget to add target attribute)
992
* Action is also same as normal action (don't forget to add target attribute)
999
.. i18n: .. code-block:: xml
1001
.. i18n: <record id="action_config_compute_remaining" model="ir.actions.act_window">
1002
.. i18n: <field name="name">Compute Remaining Hours</field>
1003
.. i18n: <field name="type">ir.actions.act_window</field>
1004
.. i18n: <field name="res_model">config.compute.remaining</field>
1005
.. i18n: <field name="view_type">form</field>
1006
.. i18n: <field name="view_mode">form</field>
1007
.. i18n: <field name="target">new</field>
1013
<record id="action_config_compute_remaining" model="ir.actions.act_window">
1014
<field name="name">Compute Remaining Hours</field>
1015
<field name="type">ir.actions.act_window</field>
1016
<field name="res_model">config.compute.remaining</field>
1017
<field name="view_type">form</field>
1018
<field name="view_mode">form</field>
1019
<field name="target">new</field>
1022
.. i18n: osv_memory configuration item
1023
.. i18n: =============================
1026
osv_memory configuration item
1027
=============================
1029
.. i18n: Sometimes, your addon can't do with configurable defaults and needs
1030
.. i18n: upfront configuration settings to work correctly. In these cases, you
1031
.. i18n: want to provide a configuration wizard right after installation, and
1032
.. i18n: potentially one which can be re-run later if needed.
1035
Sometimes, your addon can't do with configurable defaults and needs
1036
upfront configuration settings to work correctly. In these cases, you
1037
want to provide a configuration wizard right after installation, and
1038
potentially one which can be re-run later if needed.
1040
.. i18n: Up until 5.0, OpenERP had such a facility but it was hardly documented
1041
.. i18n: and a very manual, arduous process. A simpler, more straightforward
1042
.. i18n: solution has been implemented for those needs.
1045
Up until 5.0, OpenERP had such a facility but it was hardly documented
1046
and a very manual, arduous process. A simpler, more straightforward
1047
solution has been implemented for those needs.
1049
.. i18n: The basic concepts
1050
.. i18n: ------------------
1056
.. i18n: The new implementation provides a base behavior ``osv_memory`` object
1057
.. i18n: from which you need to inherit. This behavior handles the flow between
1058
.. i18n: the configuration items of the various extensions, and inheriting from
1059
.. i18n: it is therefore mandatory.
1062
The new implementation provides a base behavior ``osv_memory`` object
1063
from which you need to inherit. This behavior handles the flow between
1064
the configuration items of the various extensions, and inheriting from
1065
it is therefore mandatory.
1067
.. i18n: There is also an inheritable view which provides a basic canvas,
1068
.. i18n: through mechanisms which will be explained later it's highly
1069
.. i18n: customizable. It's therefore strongly suggested that you should
1070
.. i18n: inherit from that view from yours as well.
1073
There is also an inheritable view which provides a basic canvas,
1074
through mechanisms which will be explained later it's highly
1075
customizable. It's therefore strongly suggested that you should
1076
inherit from that view from yours as well.
1078
.. i18n: Creating a basic configuration item
1079
.. i18n: -----------------------------------
1082
Creating a basic configuration item
1083
-----------------------------------
1085
.. i18n: Your configuration model
1086
.. i18n: ++++++++++++++++++++++++
1089
Your configuration model
1090
++++++++++++++++++++++++
1092
.. i18n: First comes the creation of the configuration item itself. This is a
1093
.. i18n: normal ``osv_memory`` object with a few constraints:
1096
First comes the creation of the configuration item itself. This is a
1097
normal ``osv_memory`` object with a few constraints:
1099
.. i18n: * it has to inherit from ``res.config``, which provides the basic
1100
.. i18n: configuration behaviors as well as the base event handlers and
1101
.. i18n: extension points
1103
.. i18n: * it has to provide an ``execute`` method.[#]_ This method will be called
1104
.. i18n: when validating the configuration form and contains the validation
1105
.. i18n: logic. It shouldn't return anything.
1108
* it has to inherit from ``res.config``, which provides the basic
1109
configuration behaviors as well as the base event handlers and
1112
* it has to provide an ``execute`` method.[#]_ This method will be called
1113
when validating the configuration form and contains the validation
1114
logic. It shouldn't return anything.
1116
.. i18n: .. code-block:: python
1118
.. i18n: class my_item_config(osv.osv_memory):
1119
.. i18n: _name = 'my.model.config'
1120
.. i18n: _inherit = 'res.config' # mandatory
1122
.. i18n: _columns = {
1123
.. i18n: 'my_field': fields.char('Field', size=64, required=True),
1126
.. i18n: def execute(self, cr, uid, ids, context=None):
1127
.. i18n: 'do whatever configuration work you need here'
1128
.. i18n: my_item_config()
1131
.. code-block:: python
1133
class my_item_config(osv.osv_memory):
1134
_name = 'my.model.config'
1135
_inherit = 'res.config' # mandatory
1138
'my_field': fields.char('Field', size=64, required=True),
1141
def execute(self, cr, uid, ids, context=None):
1142
'do whatever configuration work you need here'
1145
.. i18n: Your configuration view
1146
.. i18n: +++++++++++++++++++++++
1149
Your configuration view
1150
+++++++++++++++++++++++
1152
.. i18n: Then comes the configuration form. OpenERP provides a base view which
1153
.. i18n: you can inherit so you don't have to deal with creating buttons and
1154
.. i18n: handling the progress bar (which should be displayed at the bottom
1155
.. i18n: left of all initial configuration dialogs). It's very strongly
1156
.. i18n: recommended that you use this base view.
1159
Then comes the configuration form. OpenERP provides a base view which
1160
you can inherit so you don't have to deal with creating buttons and
1161
handling the progress bar (which should be displayed at the bottom
1162
left of all initial configuration dialogs). It's very strongly
1163
recommended that you use this base view.
1165
.. i18n: Simply add an ``inherit_id`` field to a regular ``ir.ui.view`` and
1166
.. i18n: set its value to ``res_config_view_base``:
1169
Simply add an ``inherit_id`` field to a regular ``ir.ui.view`` and
1170
set its value to ``res_config_view_base``:
1172
.. i18n: .. code-block:: xml
1174
.. i18n: <record id="my_config_view_form" model="ir.ui.view">
1175
.. i18n: <field name="name">my.item.config.view</field>
1176
.. i18n: <!-- this is the model defined above -->
1177
.. i18n: <field name="model">my.model.config</field>
1178
.. i18n: <field name="type">form</field>
1179
.. i18n: <field name="inherit_id" ref="base.res_config_view_base"/>
1186
<record id="my_config_view_form" model="ir.ui.view">
1187
<field name="name">my.item.config.view</field>
1188
<!-- this is the model defined above -->
1189
<field name="model">my.model.config</field>
1190
<field name="type">form</field>
1191
<field name="inherit_id" ref="base.res_config_view_base"/>
1195
.. i18n: While this could be used as-is, it would display an empty dialog with
1196
.. i18n: a progress bar and two buttons which isn't of much
1197
.. i18n: interest. ``res_config_view_base`` has a special group hook which you
1198
.. i18n: should replace with your own content like so:
1201
While this could be used as-is, it would display an empty dialog with
1202
a progress bar and two buttons which isn't of much
1203
interest. ``res_config_view_base`` has a special group hook which you
1204
should replace with your own content like so:
1206
.. i18n: .. code-block:: xml
1208
.. i18n: <field name="arch" type="xml">
1209
.. i18n: <group string="res_config_contents" position="replace">
1210
.. i18n: <!-- your content should be inserted within this, the string
1211
.. i18n: attribute of the previous group is used to easily find
1212
.. i18n: it for replacement -->
1213
.. i18n: <label colspan="4" align="0.0" string="
1214
.. i18n: Configure this item by defining its field"/>
1215
.. i18n: <field colspan="2" name="my_field"/>
1222
<field name="arch" type="xml">
1223
<group string="res_config_contents" position="replace">
1224
<!-- your content should be inserted within this, the string
1225
attribute of the previous group is used to easily find
1226
it for replacement -->
1227
<label colspan="4" align="0.0" string="
1228
Configure this item by defining its field"/>
1229
<field colspan="2" name="my_field"/>
1233
.. i18n: Opening your window
1234
.. i18n: +++++++++++++++++++
1240
.. i18n: The next step is to create the ``act_window`` which links to the
1241
.. i18n: configuration model and the view:
1244
The next step is to create the ``act_window`` which links to the
1245
configuration model and the view:
1247
.. i18n: .. code-block:: xml
1249
.. i18n: <record id="my_config_window" model="ir.actions.act_window">
1250
.. i18n: <field name="name">My config window</field>
1251
.. i18n: <field name="type">ir.actions.act_window</field>
1252
.. i18n: <field name="res_model">my.model.config</field>
1253
.. i18n: <field name="view_type">form</field>
1254
.. i18n: <field name="view_id" ref="my_config_view_form"/>
1255
.. i18n: <field name="view_mode">form</field>
1256
.. i18n: <field name="target">new</field>
1262
<record id="my_config_window" model="ir.actions.act_window">
1263
<field name="name">My config window</field>
1264
<field name="type">ir.actions.act_window</field>
1265
<field name="res_model">my.model.config</field>
1266
<field name="view_type">form</field>
1267
<field name="view_id" ref="my_config_view_form"/>
1268
<field name="view_mode">form</field>
1269
<field name="target">new</field>
1272
.. i18n: Note that the ``name`` field of this ``act_window`` will be displayed
1273
.. i18n: when listing the various configuration items in the Config Wizard
1274
.. i18n: Steps submenu (in Administration > Configuration > Configuration
1278
Note that the ``name`` field of this ``act_window`` will be displayed
1279
when listing the various configuration items in the Config Wizard
1280
Steps submenu (in Administration > Configuration > Configuration
1283
.. i18n: Registering your action
1284
.. i18n: +++++++++++++++++++++++
1287
Registering your action
1288
+++++++++++++++++++++++
1290
.. i18n: Finally comes actually registering the configuration item with
1291
.. i18n: OpenERP. This is done with an ``ir.actions.todo`` object, which
1292
.. i18n: mandates a single ``action_id`` field referencing the ``act_window``
1293
.. i18n: created previously:
1296
Finally comes actually registering the configuration item with
1297
OpenERP. This is done with an ``ir.actions.todo`` object, which
1298
mandates a single ``action_id`` field referencing the ``act_window``
1301
.. i18n: .. code-block:: xml
1303
.. i18n: <record id="my_config_step" model="ir.actions.todo">
1304
.. i18n: <field name="action_id" ref="my_config_window"/>
1310
<record id="my_config_step" model="ir.actions.todo">
1311
<field name="action_id" ref="my_config_window"/>
1314
.. i18n: ``ir.actions.todo`` also has 3 optional fields:
1317
``ir.actions.todo`` also has 3 optional fields:
1319
.. i18n: ``sequence`` (default: ``10``)
1320
.. i18n: The order in which the different steps are to be
1321
.. i18n: executed, lowest first.
1324
``sequence`` (default: ``10``)
1325
The order in which the different steps are to be
1326
executed, lowest first.
1328
.. i18n: ``active`` (default: ``True``)
1329
.. i18n: An inactive step will not be executed on the next round of
1330
.. i18n: configuration.
1333
``active`` (default: ``True``)
1334
An inactive step will not be executed on the next round of
1337
.. i18n: ``state`` (default: ``'open'``)
1338
.. i18n: The current state for the configuration step, mostly used to
1339
.. i18n: register what happened during its execution. The possible
1340
.. i18n: values are ``'open'``, ``'done'``, ``'skip'`` and
1341
.. i18n: ``'cancel'``.
1344
``state`` (default: ``'open'``)
1345
The current state for the configuration step, mostly used to
1346
register what happened during its execution. The possible
1347
values are ``'open'``, ``'done'``, ``'skip'`` and
1350
.. i18n: The result at this point is the following:
1353
The result at this point is the following:
1355
.. i18n: .. image:: images/config_wizard_base.png
1356
.. i18n: :width: 100%
1359
.. image:: images/config_wizard_base.png
1362
.. i18n: Customizing your configuration item
1363
.. i18n: -----------------------------------
1366
Customizing your configuration item
1367
-----------------------------------
1369
.. i18n: While your current knowledge is certainly enough to configure your
1370
.. i18n: addon, a bit of good customization can be the difference between a
1371
.. i18n: good user experience and a great user experience.
1374
While your current knowledge is certainly enough to configure your
1375
addon, a bit of good customization can be the difference between a
1376
good user experience and a great user experience.
1378
.. i18n: More extensive view customization
1379
.. i18n: +++++++++++++++++++++++++++++++++
1382
More extensive view customization
1383
+++++++++++++++++++++++++++++++++
1385
.. i18n: As you might have noticed from the previous screen shot, by default
1386
.. i18n: your configuration window doesn't have a *title*, which isn't a
1387
.. i18n: problem but doesn't look very good either.
1390
As you might have noticed from the previous screen shot, by default
1391
your configuration window doesn't have a *title*, which isn't a
1392
problem but doesn't look very good either.
1394
.. i18n: Before setting a title, a small modification to the existing view is
1395
.. i18n: needed though: the existing ``group`` needs to be wrapped in a
1396
.. i18n: ``data`` element so it's possible to customize more than one item of
1397
.. i18n: the parent view:
1400
Before setting a title, a small modification to the existing view is
1401
needed though: the existing ``group`` needs to be wrapped in a
1402
``data`` element so it's possible to customize more than one item of
1405
.. i18n: .. code-block:: xml
1407
.. i18n: <record id="my_config_view_form" model="ir.ui.view">
1408
.. i18n: <field name="name">my.item.config.view</field>
1409
.. i18n: <!-- this is the model defined above -->
1410
.. i18n: <field name="model">my.model.config</field>
1411
.. i18n: <field name="type">form</field>
1412
.. i18n: <field name="inherit_id">res_config_view_base</field>
1413
.. i18n: <field name="arch" type="xml">
1415
.. i18n: <group string="res_config_contents" position="replace">
1416
.. i18n: <!-- your content should be inserted within this, the
1417
.. i18n: string attribute of the previous group is used to
1418
.. i18n: easily find it for replacement
1420
.. i18n: <label colspan="4" align="0.0" string="
1421
.. i18n: Configure this item by defining its field
1423
.. i18n: <field colspan="2" name="my_field"/>
1432
<record id="my_config_view_form" model="ir.ui.view">
1433
<field name="name">my.item.config.view</field>
1434
<!-- this is the model defined above -->
1435
<field name="model">my.model.config</field>
1436
<field name="type">form</field>
1437
<field name="inherit_id">res_config_view_base</field>
1438
<field name="arch" type="xml">
1440
<group string="res_config_contents" position="replace">
1441
<!-- your content should be inserted within this, the
1442
string attribute of the previous group is used to
1443
easily find it for replacement
1445
<label colspan="4" align="0.0" string="
1446
Configure this item by defining its field
1448
<field colspan="2" name="my_field"/>
1454
.. i18n: Then it becomes possible to alter the ``string`` attribute of the
1455
.. i18n: original ``form`` by adding the following code within the ``data``
1456
.. i18n: element (in this case, probably before ``group``):
1459
Then it becomes possible to alter the ``string`` attribute of the
1460
original ``form`` by adding the following code within the ``data``
1461
element (in this case, probably before ``group``):
1463
.. i18n: .. code-block:: xml
1465
.. i18n: <!-- position=attributes is new and is used to alter the
1466
.. i18n: element's attributes, instead of its content -->
1467
.. i18n: <form position="attributes">
1468
.. i18n: <!-- set the value of the 'string' attribute -->
1469
.. i18n: <attribute name="string">Set item field</attribute>
1475
<!-- position=attributes is new and is used to alter the
1476
element's attributes, instead of its content -->
1477
<form position="attributes">
1478
<!-- set the value of the 'string' attribute -->
1479
<attribute name="string">Set item field</attribute>
1482
.. i18n: .. warning:: Comments in view overload
1484
.. i18n: At this point (December 2009) OpenERP cannot handle comments at the
1485
.. i18n: toplevel of the view element overload. When testing or reusing
1486
.. i18n: these examples, remember to strip out the comments or you will get
1487
.. i18n: runtime errors when testing the addon.
1490
.. warning:: Comments in view overload
1492
At this point (December 2009) OpenERP cannot handle comments at the
1493
toplevel of the view element overload. When testing or reusing
1494
these examples, remember to strip out the comments or you will get
1495
runtime errors when testing the addon.
1497
.. i18n: With this, the configuration form gets a nice title:
1500
With this, the configuration form gets a nice title:
1502
.. i18n: .. image:: images/config_wizard_title.png
1503
.. i18n: :width: 100%
1506
.. image:: images/config_wizard_title.png
1509
.. i18n: More interesting customizations might be to alter the buttons provided
1510
.. i18n: by ``res_config_view_base`` at the bottom of the dialog: remove a
1511
.. i18n: button (if the configuration action shouldn't be skippable), change
1512
.. i18n: the button labels, ...
1515
More interesting customizations might be to alter the buttons provided
1516
by ``res_config_view_base`` at the bottom of the dialog: remove a
1517
button (if the configuration action shouldn't be skippable), change
1518
the button labels, ...
1520
.. i18n: Since no specific hooks are provided for these alterations, they
1521
.. i18n: require the use of xpath selectors (using the ``xpath`` element).
1524
Since no specific hooks are provided for these alterations, they
1525
require the use of xpath selectors (using the ``xpath`` element).
1527
.. i18n: Removing the Skip button and changing the label of the Record button
1528
.. i18n: to Set, for instance, would be done by adding the following after the
1529
.. i18n: ``group`` element:
1532
Removing the Skip button and changing the label of the Record button
1533
to Set, for instance, would be done by adding the following after the
1536
.. i18n: .. code-block:: xml
1538
.. i18n: <!-- select the button 'action_skip' of the original template
1539
.. i18n: and replace it by nothing, removing it -->
1540
.. i18n: <xpath expr="//button[@name='action_skip']"
1541
.. i18n: position="replace"/>
1546
<!-- select the button 'action_skip' of the original template
1547
and replace it by nothing, removing it -->
1548
<xpath expr="//button[@name='action_skip']"
1549
position="replace"/>
1551
.. i18n: .. code-block:: xml
1553
.. i18n: <!-- select the button 'action_next' -->
1554
.. i18n: <xpath expr="//button[@name='action_next']"
1555
.. i18n: position="attributes">
1556
.. i18n: <!-- and change the attribute 'string' to 'Set' -->
1557
.. i18n: <attribute name="string">Set</attribute>
1563
<!-- select the button 'action_next' -->
1564
<xpath expr="//button[@name='action_next']"
1565
position="attributes">
1566
<!-- and change the attribute 'string' to 'Set' -->
1567
<attribute name="string">Set</attribute>
1575
.. i18n: .. image:: images/config_wizard_buttons.png
1576
.. i18n: :width: 100%
1579
.. image:: images/config_wizard_buttons.png
1582
.. i18n: It is also possible to use this method to change the name of the
1583
.. i18n: button, and thus the method invoked on the object (though that isn't
1584
.. i18n: necessarily recommended).
1587
It is also possible to use this method to change the name of the
1588
button, and thus the method invoked on the object (though that isn't
1589
necessarily recommended).
1591
.. i18n: Model customization
1592
.. i18n: +++++++++++++++++++
1598
.. i18n: Though most of the requirements should be easy to fulfill using the
1599
.. i18n: provided ``execute`` method hook, some addon-specific requirements
1600
.. i18n: are a bit more complex. ``res.config`` should be able to provide all
1601
.. i18n: the hooks necessary for more complex behaviors.
1604
Though most of the requirements should be easy to fulfill using the
1605
provided ``execute`` method hook, some addon-specific requirements
1606
are a bit more complex. ``res.config`` should be able to provide all
1607
the hooks necessary for more complex behaviors.
1609
.. i18n: Ignoring the next step
1610
.. i18n: ~~~~~~~~~~~~~~~~~~~~~~
1613
Ignoring the next step
1614
~~~~~~~~~~~~~~~~~~~~~~
1616
.. i18n: Ultimately, the switch to the next configuration item is done by
1617
.. i18n: calling the ``self.next`` method of ``res.config`` [#]_. This is the
1618
.. i18n: last thing the base implementations of ``action_next`` and
1619
.. i18n: ``action_skip`` do. But in some cases, looping on the current view or
1620
.. i18n: implementing a workflow-like behavior is needed. In these cases, you
1621
.. i18n: can simply return a dictionary from ``execute``, and ``res.config``
1622
.. i18n: will jump to that view instead of the one returned by ``self.next``.
1625
Ultimately, the switch to the next configuration item is done by
1626
calling the ``self.next`` method of ``res.config`` [#]_. This is the
1627
last thing the base implementations of ``action_next`` and
1628
``action_skip`` do. But in some cases, looping on the current view or
1629
implementing a workflow-like behavior is needed. In these cases, you
1630
can simply return a dictionary from ``execute``, and ``res.config``
1631
will jump to that view instead of the one returned by ``self.next``.
1633
.. i18n: This is what the user creation item does, for instance, to let the
1634
.. i18n: user create several new users in a row.
1637
This is what the user creation item does, for instance, to let the
1638
user create several new users in a row.
1640
.. i18n: Performing an action on skipping
1641
.. i18n: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1644
Performing an action on skipping
1645
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1647
.. i18n: As opposed to ``action_next`` which requires that ``execute`` be
1648
.. i18n: implemented by the children classes, ``action_skip`` comes fully
1649
.. i18n: implemented in ``res.config``. But in the case where the child model
1650
.. i18n: needs to perform an action upon skipping discovery, it also provides a
1651
.. i18n: hook method called ``cancel`` which you can overload in a way similar
1652
.. i18n: to ``execute``. Its behavior is identical to ``execute``'s: not only
1653
.. i18n: is ``next`` called automatically at the end of ``cancel`` but it also
1654
.. i18n: gives the possibility of `ignoring the next step`_.
1657
As opposed to ``action_next`` which requires that ``execute`` be
1658
implemented by the children classes, ``action_skip`` comes fully
1659
implemented in ``res.config``. But in the case where the child model
1660
needs to perform an action upon skipping discovery, it also provides a
1661
hook method called ``cancel`` which you can overload in a way similar
1662
to ``execute``. Its behavior is identical to ``execute``'s: not only
1663
is ``next`` called automatically at the end of ``cancel`` but it also
1664
gives the possibility of `ignoring the next step`_.
1666
.. i18n: Alternative actions
1667
.. i18n: ~~~~~~~~~~~~~~~~~~~
1673
.. i18n: It's also possible to either overload ``action_next`` and
1674
.. i18n: ``action_skip`` or, more useful, to implement more actions than these
1675
.. i18n: two, if more than two buttons are needed for instance.
1678
It's also possible to either overload ``action_next`` and
1679
``action_skip`` or, more useful, to implement more actions than these
1680
two, if more than two buttons are needed for instance.
1682
.. i18n: In this case, please remember that you should always provide a way to
1683
.. i18n: reach ``self.next`` to the user, in order for him to be able to
1684
.. i18n: configure the rest of his addons.
1687
In this case, please remember that you should always provide a way to
1688
reach ``self.next`` to the user, in order for him to be able to
1689
configure the rest of his addons.
1691
.. i18n: ``res.config``'s public API
1692
.. i18n: ---------------------------
1695
``res.config``'s public API
1696
---------------------------
1698
.. i18n: All of the public API methods take the standard OpenERP set of
1699
.. i18n: arguments: ``self``, ``cr``, ``uid``, ``ids`` and ``context``.
1702
All of the public API methods take the standard OpenERP set of
1703
arguments: ``self``, ``cr``, ``uid``, ``ids`` and ``context``.
1705
.. i18n: ``execute``
1706
.. i18n: +++++++++++
1712
.. i18n: Hook method called in case the ``action_next`` button
1713
.. i18n: (default label: Record) is clicked. Should not return *anything*
1714
.. i18n: unless you want to display another view than the next configuration
1715
.. i18n: item. Returning anything other than a view dictionary will lead to
1716
.. i18n: undefined behaviors.
1719
Hook method called in case the ``action_next`` button
1720
(default label: Record) is clicked. Should not return *anything*
1721
unless you want to display another view than the next configuration
1722
item. Returning anything other than a view dictionary will lead to
1723
undefined behaviors.
1725
.. i18n: It is mandatory to overload it. Failure to do so will result in a
1726
.. i18n: ``NotImplementedError`` being raised at runtime.
1729
It is mandatory to overload it. Failure to do so will result in a
1730
``NotImplementedError`` being raised at runtime.
1732
.. i18n: The default ``res.config`` implementation should not be called in the
1733
.. i18n: overload (don't use ``super``).
1736
The default ``res.config`` implementation should not be called in the
1737
overload (don't use ``super``).
1746
.. i18n: Hook method called in case the ``action_skip`` button
1747
.. i18n: (default label: Skip) is clicked. Its behavior is the same as
1748
.. i18n: `execute`_'s, except it's not mandatory to overload it.
1751
Hook method called in case the ``action_skip`` button
1752
(default label: Skip) is clicked. Its behavior is the same as
1753
`execute`_'s, except it's not mandatory to overload it.
1762
.. i18n: Method called to fetch the todo (and the corresponding action) for the
1763
.. i18n: next configuration item. It can be overloaded if the configuration
1764
.. i18n: item needs custom behavior common to all events.
1767
Method called to fetch the todo (and the corresponding action) for the
1768
next configuration item. It can be overloaded if the configuration
1769
item needs custom behavior common to all events.
1771
.. i18n: If overloaded, the default ``res.config`` implementation must be
1772
.. i18n: called and its result returned in order to get and execute the next
1773
.. i18n: configuration item.
1776
If overloaded, the default ``res.config`` implementation must be
1777
called and its result returned in order to get and execute the next
1780
.. i18n: ``action_next`` and ``action_skip``
1781
.. i18n: +++++++++++++++++++++++++++++++++++
1784
``action_next`` and ``action_skip``
1785
+++++++++++++++++++++++++++++++++++
1787
.. i18n: Event handler for the buttons of the base view, overloading them
1788
.. i18n: should never be necessary but in case it's needed the default
1789
.. i18n: ``res.config`` implementation should be called (via ``super``) and its
1790
.. i18n: result returned.
1793
Event handler for the buttons of the base view, overloading them
1794
should never be necessary but in case it's needed the default
1795
``res.config`` implementation should be called (via ``super``) and its
1798
.. i18n: .. [#] This isn't completely true, as you will see when `Customizing
1799
.. i18n: your configuration item`_
1802
.. [#] This isn't completely true, as you will see when `Customizing
1803
your configuration item`_
1805
.. i18n: .. [#] this method is part of the official API and you're free to
1806
.. i18n: overload it if needed, but you should always call
1807
.. i18n: ``res.config``'s through ``super`` when your work is
1808
.. i18n: done. Overloading ``next`` is also probably overkill in most
1809
.. i18n: situations.
1811
.. i18n: ======================================================================
1812
.. i18n: Guidelines on how to convert old-style wizard to new osv_memory style
1813
.. i18n: ======================================================================
1816
.. [#] this method is part of the official API and you're free to
1817
overload it if needed, but you should always call
1818
``res.config``'s through ``super`` when your work is
1819
done. Overloading ``next`` is also probably overkill in most
1822
======================================================================
1823
Guidelines on how to convert old-style wizard to new osv_memory style
1824
======================================================================
1826
.. i18n: OSV Memory Wizard
1827
.. i18n: =================
1828
.. i18n: provide important advantages over the pre-5.0 wizard system, with support features that were difficult to implement in wizards previously, such as:
1833
provide important advantages over the pre-5.0 wizard system, with support features that were difficult to implement in wizards previously, such as:
1835
.. i18n: #. inheritance
1836
.. i18n: #. workflows
1837
.. i18n: #. complex relation fields
1838
.. i18n: #. computed fields
1839
.. i18n: #. all kind of views (lists, graphs, ...)
1844
#. complex relation fields
1846
#. all kind of views (lists, graphs, ...)
1848
.. 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.
1851
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.
1853
.. i18n: This section will highlight the main steps usually required when porting a classical wizard to the new osv_memory wizard system.
1854
.. i18n: For more details about the osv_memory wizard see also section XXX.
1857
This section will highlight the main steps usually required when porting a classical wizard to the new osv_memory wizard system.
1858
For more details about the osv_memory wizard see also section XXX.
1860
.. 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.
1863
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.
1865
.. i18n: If the wizard is complex, you could even define a workflow on the wizard object (see section XXX for details about workflows)
1868
If the wizard is complex, you could even define a workflow on the wizard object (see section XXX for details about workflows)
1870
.. i18n: Using a very simple wizard as an example, here is a step-by-step conversion to the new osv_memory system:
1873
Using a very simple wizard as an example, here is a step-by-step conversion to the new osv_memory system:
1882
.. i18n: 1. Create a new object that extends osv_memory, including the required fields and methods:
1885
1. Create a new object that extends osv_memory, including the required fields and methods:
1887
.. i18n: .. image:: images/wizard_window.png
1890
.. image:: images/wizard_window.png
1892
.. i18n: .. code-block:: python
1894
.. i18n: def _action_open_window(self, cr, uid, data, context):
1898
.. i18n: class product_margins(wizard.interface):
1899
.. i18n: form1 = '''<?xml version="1.0"?>
1900
.. i18n: <form string="View Stock of Products">
1901
.. i18n: <separator string="Select " colspan="4"/>
1902
.. i18n: <field name="from_date"/>
1903
.. i18n: <field name="to_date"/>
1904
.. i18n: <field name="invoice_state"/>
1907
.. i18n: form1_fields = {
1908
.. i18n: 'from_date': {
1909
.. i18n: 'string': 'From',
1910
.. i18n: 'type': 'date',
1911
.. i18n: 'default': lambda *a:time.strftime('%Y-01-01'),
1914
.. i18n: 'to_date': {
1915
.. i18n: 'string': 'To',
1916
.. i18n: 'type': 'date',
1917
.. i18n: 'default': lambda *a:time.strftime('%Y-12-31'),
1920
.. i18n: 'invoice_state': {
1921
.. i18n: 'string': 'Invoice State',
1922
.. i18n: 'type': 'selection',
1923
.. i18n: 'selection': [('paid','Paid'),('open_paid','Open and Paid'),('draft_open_paid','Draft, Open and Paid'),],
1924
.. i18n: 'required': True,
1925
.. i18n: 'default': lambda *a:"open_paid",
1931
.. i18n: 'actions': [],
1932
.. i18n: 'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, 'state': [('end', 'Cancel','gtk-cancel'),('open', 'Open Margins','gtk-ok')]}
1935
.. i18n: 'actions': [],
1936
.. i18n: 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
1939
.. i18n: product_margins('product.margins')
1942
.. code-block:: python
1944
def _action_open_window(self, cr, uid, data, context):
1948
class product_margins(wizard.interface):
1949
form1 = '''<?xml version="1.0"?>
1950
<form string="View Stock of Products">
1951
<separator string="Select " colspan="4"/>
1952
<field name="from_date"/>
1953
<field name="to_date"/>
1954
<field name="invoice_state"/>
1961
'default': lambda *a:time.strftime('%Y-01-01'),
1967
'default': lambda *a:time.strftime('%Y-12-31'),
1971
'string': 'Invoice State',
1972
'type': 'selection',
1973
'selection': [('paid','Paid'),('open_paid','Open and Paid'),('draft_open_paid','Draft, Open and Paid'),],
1975
'default': lambda *a:"open_paid",
1982
'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, 'state': [('end', 'Cancel','gtk-cancel'),('open', 'Open Margins','gtk-ok')]}
1986
'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
1989
product_margins('product.margins')
1991
.. i18n: New Wizard File : <<module_name>>_<<filename>>.py
1992
.. i18n: =================================================
1995
New Wizard File : <<module_name>>_<<filename>>.py
1996
=================================================
1998
.. i18n: .. code-block:: python
2000
.. i18n: class product_margin(osv.osv_memory):
2002
.. i18n: Product Margin
2004
.. i18n: _name = 'product.margin'
2005
.. i18n: _description = 'Product Margin'
2007
.. i18n: def _action_open_window(self, cr, uid, ids, context):
2012
.. i18n: _columns = {
2013
.. i18n: #TODO : import time required to get currect date
2014
.. i18n: 'from_date': fields.date('From'),
2015
.. i18n: #TODO : import time required to get currect date
2016
.. i18n: 'to_date': fields.date('To'),
2017
.. i18n: 'invoice_state':fields.selection([
2018
.. i18n: ('paid','Paid'),
2019
.. i18n: ('open_paid','Open and Paid'),
2020
.. i18n: ('draft_open_paid','Draft, Open and Paid'),
2021
.. i18n: ],'Invoice State', select=True, required=True),
2023
.. i18n: _defaults = {
2024
.. i18n: 'from_date': lambda *a:time.strftime('%Y-01-01'),
2025
.. i18n: 'to_date': lambda *a:time.strftime('%Y-01-01'),
2026
.. i18n: 'invoice_state': lambda *a:"open_paid",
2028
.. i18n: product_margin()
2031
.. code-block:: python
2033
class product_margin(osv.osv_memory):
2037
_name = 'product.margin'
2038
_description = 'Product Margin'
2040
def _action_open_window(self, cr, uid, ids, context):
2046
#TODO : import time required to get currect date
2047
'from_date': fields.date('From'),
2048
#TODO : import time required to get currect date
2049
'to_date': fields.date('To'),
2050
'invoice_state':fields.selection([
2052
('open_paid','Open and Paid'),
2053
('draft_open_paid','Draft, Open and Paid'),
2054
],'Invoice State', select=True, required=True),
2057
'from_date': lambda *a:time.strftime('%Y-01-01'),
2058
'to_date': lambda *a:time.strftime('%Y-01-01'),
2059
'invoice_state': lambda *a:"open_paid",
2063
.. i18n: Convert the views into real view records defined on the model of your wizard:
2066
Convert the views into real view records defined on the model of your wizard:
2068
.. i18n: Old Wizard File : wizard_product_margin.py
2069
.. i18n: ==========================================
2072
Old Wizard File : wizard_product_margin.py
2073
==========================================
2075
.. i18n: .. code-block:: python
2077
.. i18n: form1 = '''<?xml version="1.0"?>
2078
.. i18n: <form string="View Stock of Products">
2079
.. i18n: <separator string="Select " colspan="4"/>
2080
.. i18n: <field name="date"/>
2081
.. i18n: <field name="invoice_state"/>
2085
.. code-block:: python
2087
form1 = '''<?xml version="1.0"?>
2088
<form string="View Stock of Products">
2089
<separator string="Select " colspan="4"/>
2090
<field name="date"/>
2091
<field name="invoice_state"/>
2094
.. i18n: New Wizard File : wizard/<<module_name>>_<<filename>>_view.xml
2095
.. i18n: ==============================================================
2098
New Wizard File : wizard/<<module_name>>_<<filename>>_view.xml
2099
==============================================================
2101
.. i18n: .. code-block:: xml
2103
.. i18n: <record id="product_margin_form_view" model="ir.ui.view">
2104
.. i18n: <field name="name">product.margin.form</field>
2105
.. i18n: <field name="model">product.margin</field>
2106
.. i18n: <field name="type">form</field>
2107
.. i18n: <field name="arch" type="xml">
2108
.. i18n: <form string="Properties categories">
2109
.. i18n: <separator colspan="4" string="General Information"/>
2110
.. i18n: <field name="from_date" />
2111
.. i18n: <field name="to_date" />
2112
.. i18n: <field name="invoice_state" />
2113
.. i18n: <group col="4" colspan="2">
2114
.. i18n: <button special="cancel" string="Cancel" type="object"/>
2115
.. i18n: <button name="_action_open_window" string="Open Margins" type="object" default_focus=”1”/>
2124
<record id="product_margin_form_view" model="ir.ui.view">
2125
<field name="name">product.margin.form</field>
2126
<field name="model">product.margin</field>
2127
<field name="type">form</field>
2128
<field name="arch" type="xml">
2129
<form string="Properties categories">
2130
<separator colspan="4" string="General Information"/>
2131
<field name="from_date" />
2132
<field name="to_date" />
2133
<field name="invoice_state" />
2134
<group col="4" colspan="2">
2135
<button special="cancel" string="Cancel" type="object"/>
2136
<button name="_action_open_window" string="Open Margins" type="object" default_focus=”1”/>
2142
.. i18n: Default_focus attribute :
2143
.. i18n: =========================
2146
Default_focus attribute :
2147
=========================
2149
.. i18n: .. code-block:: xml
2151
.. i18n: <button name="_action_open_window" string="Open Margins" type="object" default_focus=”1”/>
2156
<button name="_action_open_window" string="Open Margins" type="object" default_focus=”1”/>
2158
.. i18n: **defaule_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.
2161
**defaule_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.
2163
.. i18n: Note: For all states in the old wizard, we need to create buttons in new approach.
2166
Note: For all states in the old wizard, we need to create buttons in new approach.
2168
.. 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:
2171
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:
2173
.. i18n: .. code-block:: xml
2175
.. i18n: <act_window name="Open Margin"
2176
.. i18n: res_model="product.margin"
2177
.. i18n: src_model="product.product"
2178
.. i18n: view_mode="form"
2179
.. i18n: target="new"
2180
.. i18n: key2="client_action_multi"
2181
.. i18n: id="product_margin_act_window"/>
2186
<act_window name="Open Margin"
2187
res_model="product.margin"
2188
src_model="product.product"
2191
key2="client_action_multi"
2192
id="product_margin_act_window"/>
2194
.. i18n: key2="client_action_multi" : While using it in the act_window, wizard will be added in the
2197
key2="client_action_multi" : While using it in the act_window, wizard will be added in the
2204
.. i18n: .. image:: images/wizard_button.png
2207
.. image:: images/wizard_button.png
2214
.. i18n: .. image:: images/wizard_panel.png
2217
.. image:: images/wizard_panel.png
2219
.. i18n: If key2 is ommited then it will be displayed only in sidebar.
2222
If key2 is ommited then it will be displayed only in sidebar.
2224
.. i18n: Note: The "src_model" attribute is only required if you want to put the
2225
.. i18n: wizard in the side bar of an object, you can leave it out, for example
2226
.. i18n: if you define an action to open the second view of a wizard.
2229
Note: The "src_model" attribute is only required if you want to put the
2230
wizard in the side bar of an object, you can leave it out, for example
2231
if you define an action to open the second view of a wizard.
2233
.. 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:
2236
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:
2238
.. i18n: In Menu Item:
2239
.. i18n: =============
2245
.. 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.
2248
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.
2250
.. i18n: .. code-block:: xml
2252
.. i18n: <menuitem id="main" name="OSV Memory Wizard Test"/>
2254
.. i18n: action="product_margin_act_window"
2255
.. i18n: id="menu_product_act"
2256
.. i18n: parent="main" />
2261
<menuitem id="main" name="OSV Memory Wizard Test"/>
2263
action="product_margin_act_window"
2264
id="menu_product_act"
2267
.. 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:
2270
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:
2272
.. i18n: .. code-block:: xml
2274
.. i18n: <button name="%(product_margin.product_margin_act_window)d"
2275
.. i18n: string="Test Wizard" type="action" states="draft"/>
2280
<button name="%(product_margin.product_margin_act_window)d"
2281
string="Test Wizard" type="action" states="draft"/>
2283
.. 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 __terp__.py file.
2286
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 __terp__.py file.