b72540cf2d386d978dc63115105b180bf730ea3e
[odoo/odoo.git] / doc / howtos / backend.rst
1 .. queue:: backend/series
2
3 =================
4 Building a Module
5 =================
6
7 Start/Stop the Odoo server
8 ==========================
9
10 Odoo uses a client/server architecture in which clients are web browsers
11 accessing the odoo server via RPC.
12
13 Business logic and extension is generally performed on the server side,
14 although supporting client features (e.g. new data representation such as
15 interactive maps) can be added to the client.
16
17 In order to start the server, simply invoke the command :ref:`odoo.py
18 <reference/cmdline>` in the shell, adding the full path to the file if
19 necessary:
20
21 .. code:: bash
22
23     odoo.py
24
25 The server is stopped by hitting ``Ctrl-C`` twice from the terminal, or by
26 killing the corresponding OS process.
27
28 Build an Odoo module
29 ====================
30
31 Both server and client extensions are packaged as *modules* which are
32 optionally loaded in a *database*.
33
34 Odoo modules can either add brand new business logic to an Odoo system, or
35 alter and extend existing business logic: a module can be created to add your
36 country's accounting rules to Odoo's generic accounting support, while the
37 next module adds support for real-time visualisation of a bus fleet.
38
39 Everything in Odoo thus starts and ends with modules.
40
41 Composition of a module
42 -----------------------
43
44 An Odoo module can contain a number of elements:
45
46 Business objects
47     declared as Python classes, these resources are automatically persisted
48     by Odoo based on their configuration
49
50 Data files
51     XML or CSV files declaring metadata (views or workflows), configuration
52     data (modules parameterization), demonstration data and more
53
54 Web controllers
55     Handle requests from web browsers
56
57 Static web data
58     Images, CSS or javascript files used by the web interface or website
59
60 Module structure
61 ----------------
62
63 Each module is a directory within a *module directory*. Module directories
64 are specified by using the :option:`--addons-path <odoo.py --addons-path>`
65 option.
66
67 .. tip::
68     :class: aphorism
69
70     most command-line options can also be set using :ref:`a configuration
71     file <reference/cmdline/config>`
72
73 An Odoo module is declared by its :ref:`manifest <reference/module/manifest>`. It
74 is mandatory and contains a single python dictionary declaring various
75 metadata for the module: the module's name and description, list of Odoo
76 modules required for this one to work properly, references to data files, …
77
78 The manifest's general structure is::
79
80     {
81         'name': "MyModule",
82         'version': '1.0',
83         'depends': ['base'],
84         'author': "Author Name",
85         'category': 'Category',
86         'description': """
87         Description text
88         """,
89         # data files always loaded at installation
90         'data': [
91             'mymodule_view.xml',
92         ],
93         # data files containing optionally loaded demonstration data
94         'demo': [
95             'demo_data.xml',
96         ],
97     }
98
99 A module is also a
100 `Python package <http://docs.python.org/2/tutorial/modules.html#packages>`_
101 with a ``__init__.py`` file, containing import instructions for various Python
102 files in the module.
103
104 For instance, if the module has a single ``mymodule.py`` file ``__init__.py``
105 might contain::
106
107     import mymodule
108
109 Fortunately, there is a mechanism to help you set up an module. The command
110 ``odoo.py`` has a subcommand :ref:`scaffold <reference/cmdline/scaffold>` to
111 create an empty module:
112
113 .. code:: bash
114
115     odoo.py scaffold <module name> <where to put it>
116
117 The command creates a subdirectory for your module, and automatically creates a
118 bunch of standard files for a module. Most of them simply contain commented code
119 or XML. The usage of most of those files will be explained along this tutorial.
120
121 .. exercise:: Module creation
122
123     Use the command line above to  create an empty module Open Academy, and
124     install it in Odoo.
125
126     .. only:: solutions
127
128         #. Invoke the command ``odoo.py scaffold openacademy addons``.
129         #. Adapt the manifest file to your module.
130         #. Don't bother about the other files.
131
132         .. patch::
133
134 Object-Relational Mapping
135 -------------------------
136
137 A key component of Odoo is the :abbr:`ORM (Object-Relational Mapping)` layer.
138 This layer avoids having to write most :abbr:`SQL (Structured Query Language)`
139 by hand and provides extensibility and security services\ [#rawsql]_.
140
141 Business objects are declared as Python classes extending
142 :class:`~openerp.models.Model` which integrates them into the automated
143 persistence system.
144
145 Models can be configured by setting a number of attributes at their
146 definition. The most important attribute is
147 :attr:`~openerp.models.Model._name` which is required and defines the name for
148 the model in the Odoo system. Here is a minimally complete definition of a
149 model::
150
151     from openerp import models
152     class MinimalModel(models.Model):
153         _name = 'test.model'
154
155 Model fields
156 ------------
157
158 Fields are used to define what the model can store and where. Fields are
159 defined as attributes on the model class::
160
161     from openerp import models, fields
162
163     class LessMinimalModel(models.Model):
164         _name = 'test.model2'
165
166         name = fields.Char()
167
168 Common Attributes
169 #################
170
171 Much like the model itself, its fields can be configured, by passing
172 configuration attributes as parameters::
173
174     name = field.Char(required=True)
175
176 Some attributes are available on all fields, here are the most common ones:
177
178 :attr:`~openerp.fields.Field.string` (``unicode``, default: field's name)
179     The label of the field in UI (visible by users).
180 :attr:`~openerp.fields.Field.required` (``bool``, default: ``False``)
181     If ``True``, the field can not be empty, it must either have a default
182     value or always be given a value when creating a record.
183 :attr:`~openerp.fields.Field.help` (``unicode``, default: ``''``)
184     Long-form, provides a help tooltip to users in the UI.
185 :attr:`~openerp.fields.Field.index` (``bool``, default: ``False``)
186     Requests that Odoo create a `database index`_ on the column
187
188 Simple fields
189 #############
190
191 There are two broad categories of fields: "simple" fields which are atomic
192 values stored directly in the model's table and "relational" fields linking
193 records (of the same model or of different models).
194
195 Example of simple fields are :class:`~openerp.fields.Boolean`,
196 :class:`~openerp.fields.Date`, :class:`~openerp.fields.Char`.
197
198 Reserved fields
199 ###############
200
201 Odoo creates a few fields in all models\ [#autofields]_. These fields are
202 managed by the system and shouldn't be written to. They can be read if
203 useful or necessary:
204
205 :attr:`~openerp.fields.Model.id` (:class:`~openerp.fields.Id`)
206     the unique identifier for a record in its model
207 :attr:`~openerp.fields.Model.create_date` (:class:`~openerp.fields.Datetime`)
208     creation date of the record
209 :attr:`~openerp.fields.Model.create_uid` (:class:`~openerp.fields.Many2one`)
210     user who created the record
211 :attr:`~openerp.fields.Model.write_date` (:class:`~openerp.fields.Datetime`)
212     last modification date of the record
213 :attr:`~openerp.fields.Model.write_uid` (:class:`~openerp.fields.Many2one`)
214     user who last modified the record
215
216 Special fields
217 ##############
218
219 By default, Odoo also requires a ``name`` field on all models for various
220 display and search behaviors. The field used for these purposes can be
221 overridden by setting :attr:`~openerp.models.Model._rec_name`.
222
223 .. exercise:: Define a model
224
225     Define a new data model *Course* in the *openacademy* module. A course
226     has a title and a description. Courses must have a title.
227
228     .. only:: solutions
229
230         Edit the file ``openacademy/models.py`` to include a *Course* class.
231
232         .. patch::
233
234 Data files
235 ----------
236
237 Odoo is a highly data driven system. Although behavior is customized using
238 Python_ code part of a module's value is in the data it sets up when loaded.
239
240 .. tip:: some modules exist solely to add data into Odoo
241     :class: aphorism
242
243 Module data is declared via :ref:`data files <reference/data>`, XML files with
244 ``<record>`` elements. Each ``<record>`` element creates or updates a database
245 record.
246
247 .. code-block:: xml
248
249     <openerp>
250         <data>
251             <record model="{model name}" id="{record identifier}">
252                 <field name="{a field name}">{a value}</field>
253             </record>
254         </data>
255     <openerp>
256
257 * ``model`` is the name of the Odoo model for the record
258 * ``id`` is an :term:`external identifier`, it allows referring to the record
259   (without having to know its in-database identifier)
260 * ``<field>`` elements have a ``name`` which is the name of the field in the
261   model (e.g. ``description``). Their body is the field's value.
262
263 Data files have to be declared in the manifest file to be loaded, they can
264 be declared in the ``'data'`` list (always loaded) or in the ``'demo'`` list
265 (only loaded in demonstration mode).
266
267 .. exercise:: Define demonstration data
268
269     Create demonstration data filling the *Courses* model with a few
270     demonstration courses.
271
272     .. only:: solutions
273
274         Edit the file ``openacademy/demo.xml`` to include some data.
275
276         .. patch::
277
278 Actions and Menus
279 -----------------
280
281 Actions and menus are regular records in database, usually declared through
282 data files. Actions can be triggered in three ways:
283
284 #. by clicking on menu items (linked to specific actions)
285 #. by clicking on buttons in views (if these are connected to actions)
286 #. as contextual actions on object
287
288 Because menus are somewhat complex to declare there is a ``<menuitem>``
289 shortcut to declare an ``ir.ui.menu`` and connect it to the corresponding
290 action more easily.
291
292 .. code-block:: xml
293
294     <record model="ir.actions.act_window" id="action_list_ideas">
295         <field name="name">Ideas</field>
296         <field name="res_model">idea.idea</field>
297         <field name="view_mode">tree,form</field>
298     </record>
299     <menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10"
300               action="action_list_ideas"/>
301
302 .. danger::
303     :class: aphorism
304
305     The action must be declared before its corresponding menu in the XML file.
306
307     Data files are executed sequentially, the action's ``id`` must be present
308     in the database before the menu can be created.
309
310 .. exercise:: Define new menu entries
311
312     Define new menu entries to access courses and sessions under the
313     OpenAcademy menu entry. A user should be able to
314
315     - display a list of all the courses
316     - create/modify courses
317
318     .. only:: solutions
319
320         #. Create ``openacademy/views/openacademy.xml`` with an action and
321            the menus triggering the action
322         #. Add it to the ``data`` list of ``openacademy/__openerp__.py``
323
324         .. patch::
325
326 Basic views
327 ===========
328
329 Views define the way the records of a model are displayed. Each type of view
330 represents a mode of visualization (a list of records, a graph of their
331 aggregation, …). Views can either be requested generically via their type
332 (e.g. *a list of partners*) or specifically via their id. For generic
333 requests, the view with the correct type and the lowest priority will be
334 used (so the lowest-priority view of each type is the default view for that
335 type).
336
337 :ref:`View inheritance <reference/views/inheritance>` allows altering views
338 declared elsewhere (adding or removing content).
339
340 Generic view declaration
341 ------------------------
342
343 A view is declared as a record of the model ``ir.ui.view``. The view type
344 is implied by the root element of the ``arch`` field:
345
346 .. code-block:: xml
347
348     <record model="ir.ui.view" id="view_id">
349         <field name="name">view.name</field>
350         <field name="model">object_name</field>
351         <field name="priority" eval="16"/>
352         <field name="arch" type="xml">
353             <!-- view content: <form>, <tree>, <graph>, ... -->
354         </field>
355     </record>
356
357 .. danger:: The view's content is XML.
358     :class: aphorism
359
360     The ``arch`` field must thus be declared as ``type="xml"`` to be parsed
361     correctly.
362
363 Tree views
364 ----------
365
366 Tree views, also called list views, display records in a tabular form.
367
368 Their root element is ``<tree>``. The simplest form of the tree view simply
369 lists all the fields to display in the table (each field as a column):
370
371 .. code-block:: xml
372
373     <tree string="Idea list">
374         <field name="name"/>
375         <field name="inventor_id"/>
376     </tree>
377
378 Form views
379 ----------
380
381 Forms are used to create and edit single records.
382
383
384 Their root element is ``<form>``. They composed of high-level structure
385 elements (groups, notebooks) and interactive elements (buttons and fields):
386
387 .. code-block:: xml
388
389     <form string="Idea form">
390         <group colspan="4">
391             <group colspan="2" col="2">
392                 <separator string="General stuff" colspan="2"/>
393                 <field name="name"/>
394                 <field name="inventor_id"/>
395             </group>
396
397             <group colspan="2" col="2">
398                 <separator string="Dates" colspan="2"/>
399                 <field name="active"/>
400                 <field name="invent_date" readonly="1"/>
401             </group>
402
403             <notebook colspan="4">
404                 <page string="Description">
405                     <field name="description" nolabel="1"/>
406                 </page>
407             </notebook>
408
409             <field name="state"/>
410         </group>
411     </form>
412
413 .. exercise:: Customise form view using XML
414
415     Create your own form view for the Course object. Data displayed should be:
416     the name and the description of the course.
417
418     .. only:: solutions
419
420         .. patch::
421
422 .. exercise:: Notebooks
423
424     In the Course form view, put the description field under a tab, such that
425     it will be easier to add other tabs later, containing additional
426     information.
427
428     .. only:: solutions
429
430         Modify the Course form view as follows:
431
432         .. patch::
433
434 Form views can also use plain HTML for more flexible layouts:
435
436 .. code-block:: xml
437
438     <form string="Idea Form">
439         <header>
440             <button string="Confirm" type="object" name="action_confirm"
441                     states="draft" class="oe_highlight" />
442             <button string="Mark as done" type="object" name="action_done"
443                     states="confirmed" class="oe_highlight"/>
444             <button string="Reset to draft" type="object" name="action_draft"
445                     states="confirmed,done" />
446             <field name="state" widget="statusbar"/>
447         </header>
448         <sheet>
449             <div class="oe_title">
450                 <label for="name" class="oe_edit_only" string="Idea Name" />
451                 <h1><field name="name" /></h1>
452             </div>
453             <separator string="General" colspan="2" />
454             <group colspan="2" col="2">
455                 <field name="description" placeholder="Idea description..." />
456             </group>
457         </sheet>
458     </form>
459
460 Search views
461 ------------
462
463 Search views customize the search field associated with the list view (and
464 other aggregated views). Their root element is ``<search>`` and they're
465 composed of fields defining which fields can be searched on:
466
467 .. code-block:: xml
468
469     <search>
470         <field name="name"/>
471         <field name="inventor_id"/>
472     </search>
473
474 If no search view exists for the model, Odoo generates one which only allows
475 searching on the ``name`` field.
476
477 .. exercise:: Search courses
478
479     Allow searching for courses based on their title or their description.
480
481     .. only:: solutions
482
483         .. patch::
484
485 Relations between models
486 ========================
487
488 A record from a model may be related to a record from another model. For
489 instance, a sale order record is related to a client record that contains the
490 client data; it is also related to its sale order line records.
491
492 .. exercise:: Create a session model
493
494     For the module Open Academy, we consider a model for *sessions*: a session
495     is an occurrence of a course taught at a given time for a given audience.
496
497     Create a model for *sessions*. A session has a name, a start date, a
498     duration and a number of seats. Add an action and a menu item to display
499     them. Make the new model visible via a menu item.
500
501     .. only:: solutions
502
503         #. Create the class *Session* in ``openacademy/models.py``.
504         #. Add access to the session object in ``openacademy/view/openacademy.xml``.
505
506         .. patch::
507
508         .. note:: ``digits=(6, 2)`` specifies the precision of a float number:
509                   6 is the total number of digits, while 2 is the number of
510                   digits after the comma. Note that it results in the number
511                   digits before the comma is a maximum 4
512
513 Relational fields
514 -----------------
515
516 Relational fields link records, either of the same model (hierarchies) or
517 between different models.
518
519 Relational field types are:
520
521 :class:`Many2one(other_model, ondelete='set null') <openerp.fields.Many2one>`
522     A simple link to an other object::
523
524         print foo.other_id.name
525
526     .. seealso:: `foreign keys <http://www.postgresql.org/docs/9.3/static/tutorial-fk.html>`_
527
528 :class:`One2many(other_model, related_field) <openerp.fields.One2many>`
529     A virtual relationship, inverse of a :class:`~openerp.fields.Many2one`.
530     A :class:`~openerp.fields.One2many` behaves as a container of records,
531     accessing it results in a (possibly empty) set of records::
532
533         for other in foo.other_ids:
534             print other.name
535
536     .. danger::
537
538         Because a :class:`~openerp.fields.One2many` is a virtual relationship,
539         there *must* be a :class:`~openerp.fields.Many2one` field in the
540         :samp:`{other_model}`, and its name *must* be :samp:`{related_field}`
541
542 :class:`Many2many(other_model) <openerp.fields.Many2many>`
543     Bidirectional multiple relationship, any record on one side can be related
544     to any number of records on the other side. Behaves as a container of
545     records, accessing it also results in a possibly empty set of records::
546
547         for other in foo.other_ids:
548             print other.name
549
550 .. exercise:: Many2one relations
551
552     Using a many2one, modify the *Course* and *Session* models to reflect their
553     relation with other models:
554
555     - A course has a *responsible* user; the value of that field is a record of
556       the built-in model ``res.users``.
557     - A session has an *instructor*; the value of that field is a record of the
558       built-in model ``res.partner``.
559     - A session is related to a *course*; the value of that field is a record
560       of the model ``openacademy.course`` and is required.
561     - Adapt the views.
562
563     .. only:: solutions
564
565         #. Add the relevant ``Many2one`` fields to the models, and
566         #. add them in the views.
567
568         .. patch::
569
570 .. exercise:: Inverse one2many relations
571
572     Using the inverse relational field one2many, modify the models to reflect
573     the relation between courses and sessions.
574
575     .. only:: solutions
576
577         #. Modify the ``Course`` class, and
578         #. add the field in the course form view.
579
580         .. patch::
581
582 .. exercise:: Multiple many2many relations
583
584     Using the relational field many2many, modify the *Session* model to relate
585     every session to a set of *attendees*. Attendees will be represented by
586     partner records, so we will relate to the built-in model ``res.partner``.
587     Adapt the views accordingly.
588
589     .. only:: solutions
590
591         #. Modify the ``Session`` class, and
592         #. add the field in the form view.
593
594         .. patch::
595
596 Inheritance
597 ===========
598
599 Model inheritance
600 -----------------
601
602 Odoo provides two *inheritance* mechanisms to extend an existing model in a
603 modular way.
604
605 The first inheritance mechanism allows a module to modify the behavior of a
606 model defined in another module:
607
608 - add fields to a model,
609 - override the definition of fields on a model,
610 - add constraints to a model,
611 - add methods to a model,
612 - override existing methods on a model.
613
614 The second inheritance mechanism (delegation) allows to link every record of a
615 model to a record in a parent model, and provides transparent access to the
616 fields of the parent record.
617
618 .. image:: ../images/inheritance_methods.png
619     :align: center
620
621 .. seealso::
622
623     * :attr:`~openerp.models.Model._inherit`
624     * :attr:`~openerp.models.Model._inherits`
625
626 View inheritance
627 ----------------
628
629 Instead of modifying existing views in place (by overwriting them), Odoo
630 provides view inheritance where children "extension" views are applied on top of
631 root views, and can add or remove content from their parent.
632
633 An extension view references its parent using the ``inherit_id`` field, and
634 instead of a single view its ``arch`` field is composed of any number of
635 ``xpath`` elements selecting and altering the content of their parent view:
636
637 .. code-block:: xml
638
639     <!-- improved idea categories list -->
640     <record id="idea_category_list2" model="ir.ui.view">
641         <field name="name">id.category.list2</field>
642         <field name="model">ir.ui.view</field>
643         <field name="inherit_id" ref="id_category_list"/>
644         <field name="arch" type="xml">
645             <!-- find field description inside tree, and add the field
646                  idea_ids after it -->
647             <xpath expr="/tree/field[@name='description']" position="after">
648               <field name="idea_ids" string="Number of ideas"/>
649             </xpath>
650         </field>
651     </record>
652
653 ``expr``
654     An XPath_ expression selecting a single element in the parent view.
655     Raises an error if it matches no element or more than one
656 ``position``
657     Operation to apply to the matched element:
658
659     ``inside``
660         appends ``xpath``'s body at the end of the matched element
661     ``replace``
662         replaces the matched element by the ``xpath``'s body
663     ``before``
664         inserts the ``xpath``'s body as a sibling before the matched element
665     ``after``
666         inserts the ``xpaths``'s body as a sibling after the matched element
667     ``attributes``
668         alters the attributes of the matched element using special
669         ``attribute`` elements in the ``xpath``'s body
670
671 .. exercise:: Alter existing content
672
673     * Using model inheritance, modify the existing *Partner* model to add an
674       ``instructor`` boolean field, and a many2many field that corresponds to
675       the session-partner relation
676     * Using view inheritance, display this fields in the partner form view
677
678     .. only:: solutions
679
680        .. note::
681
682            This is the opportunity to introduce the developer mode to
683            inspect the view, find its external ID and the place to put the
684            new field.
685
686        #. Create a file ``openacademy/partner.py`` and import it in
687           ``__init__.py``
688        #. Create a file ``openacademy/views/partner.xml`` and add it to
689           ``__openerp__.py``
690
691        .. patch::
692
693 Domains
694 #######
695
696 In Odoo, :ref:`reference/orm/domains` are values that encode conditions on
697 records. A domain is a  list of criteria used to select a subset of a model's
698 records. Each criteria is a triple with a field name, an operator and a value.
699
700 For instance, when used on the *Product* model the following domain selects
701 all *services* with a unit price over *1000*::
702
703     [('product_type', '=', 'service'), ('unit_price', '>', 1000)]
704
705 By default criteria are combined with an implicit AND. The logical operators
706 ``&`` (AND), ``|`` (OR) and ``!`` (NOT) can be used to explicitly combine
707 criteria. They are used in prefix position (the operator is inserted before
708 its arguments rather than between). For instance to select products "which are
709 services *OR* have a unit price which is *NOT* between 1000 and 2000"::
710
711     ['|',
712         ('product_type', '=', 'service'),
713         '!', '&',
714             ('unit_price', '>=', 1000),
715             ('unit_price', '<', 2000)]
716
717 A ``domain`` parameter can be added to relational fields to limit valid
718 records for the relation when trying to select records in the client interface.
719
720 .. exercise:: Domains on relational fields
721
722     When selecting the instructor for a *Session*, only instructors (partners
723     with ``instructor`` set to ``True``) should be visible.
724
725     .. only:: solutions
726
727         .. patch::
728
729         .. note::
730
731             A domain declared as a literal list is evaluated server-side and
732             can't refer to dynamic values on the right-hand side, a domain
733             declared as a string is evaluated client-side and allows
734             field names on the right-hand side
735
736 .. exercise:: More complex domains
737
738     Create new partner categories *Teacher / Level 1* and *Teacher / Level 2*.
739     The instructor for a session can be either an instructor or a teacher
740     (of any level).
741
742     .. only:: solutions
743
744         #. Modify the *Session* model's domain
745         #. Modify ``openacademy/view/partner.xml`` to get access to
746            *Partner categories*:
747
748         .. patch::
749
750 Computed fields and default values
751 ==================================
752
753 So far fields have been stored directly in and retrieved directly from the
754 database. Fields can also be *computed*. In that case, the field's value is not
755 retrieved from the database but computed on-the-fly by calling a method of the
756 model.
757
758 To create a computed field, create a field and set its attribute
759 :attr:`~openerp.fields.Field.compute` to the name of a method. The computation
760 method should simply set the value of the field to compute on every record in
761 ``self``.
762
763 .. danger:: ``self`` is a collection
764     :class: aphorism
765
766     The object ``self`` is a *recordset*, i.e., an ordered collection of
767     records. It supports the standard Python operations on collections, like
768     ``len(self)`` and ``iter(self)``, plus extra set operations like ``recs1 +
769     recs2``.
770
771     Iterating over ``self`` gives the records one by one, where each record is
772     itself a collection of size 1. You can access/assign fields on single
773     records by using the dot notation, like ``record.name``.
774
775 .. code-block:: python
776
777     import random
778     from openerp import models, fields
779
780     class ComputedModel(models.Model):
781         _name = 'test.computed'
782
783         name = fields.Char(compute='_compute_name')
784
785         def _compute_name(self):
786             for record in self:
787                 record.name = str(random.randint(1, 1e6))
788
789 Our compute method is very simple: it loops over ``self`` and performs the same
790 operation on every record. We can make it slightly simpler by using the
791 decorator :func:`~openerp.api.one` to automatically loop on the collection::
792
793         @api.one
794         def _compute_name(self):
795             self.name = str(random.randint(1, 1e6))
796
797 Dependencies
798 ------------
799
800 The value of a computed field usually depends on the values of other fields on
801 the computed record. The ORM expects the developer to specify those dependencies
802 on the compute method with the decorator :func:`~openerp.api.depends`.
803 The given dependencies are used by the ORM to trigger the recomputation of the
804 field whenever some of its dependencies have been modified::
805
806     from openerp import models, fields, api
807
808     class ComputedModel(models.Model):
809         _name = 'test.computed'
810
811         name = fields.Char(compute='_compute_name')
812         value = fields.Integer()
813
814         @api.one
815         @api.depends('value')
816         def _compute_name(self):
817             self.name = "Record with value %s" % self.value
818
819 .. exercise:: Computed fields
820
821     * Add the percentage of taken seats to the *Session* model
822     * Display that field in the tree and form views
823     * Display the field as a progress bar
824
825     .. only:: solutions
826
827         #. Add a computed field to *Session*
828         #. Show the field in the *Session* view:
829
830         .. patch::
831
832 Default values
833 --------------
834
835 Any field can be given a default value. In the field definition, add the option
836 ``default=X`` where ``X`` is either a Python literal value (boolean, integer,
837 float, string), or a function taking a recordset and returning a value::
838
839     name = fields.Char(default="Unknown")
840     user_id = fields.Many2one('res.users', default=lambda self: self.env.user)
841
842 .. note::
843
844     The object ``self.env`` gives access to request parameters and other useful
845     things:
846
847     - ``self.env.cr`` or ``self._cr`` is the database *cursor* object; it is
848       used for querying the database
849     - ``self.env.uid`` or ``self._uid`` is the current user's database id
850     - ``self.env.user`` is the current user's record
851     - ``self.env.context`` or ``self._context`` is the context dictionary
852     - ``self.env.ref(xml_id)`` returns the record corresponding to an XML id
853     - ``self.env[model_name]`` returns an instance of the given model
854
855 .. exercise:: Active objects – Default values
856
857     * Define the start_date default value as today (see
858       :class:`~openerp.fields.Date`).
859     * Add a field ``active`` in the class Session, and set sessions as active by
860       default.
861
862     .. only:: solutions
863
864         .. patch::
865
866         .. note::
867
868             Odoo has built-in rules making fields with an ``active`` field set
869             to ``False`` invisible.
870
871 Onchange
872 ========
873
874 The "onchange" mechanism provides a way for the client interface to update a
875 form whenever the user has filled in a value in a field, without saving anything
876 to the database.
877
878 For instance, suppose a model has three fields ``amount``, ``unit_price`` and
879 ``price``, and you want to update the price on the form when any of the other
880 fields is modified. To achieve this, define a method where ``self`` represents
881 the record in the form view, and decorate it with :func:`~openerp.api.onchange`
882 to specify on which field it has to be triggered. Any change you make on
883 ``self`` will be reflected on the form.
884
885 .. code-block:: xml
886
887     <!-- content of form view -->
888     <field name="amount"/>
889     <field name="unit_price"/>
890     <field name="price" readonly="1"/>
891
892 .. code-block:: python
893
894     # onchange handler
895     @api.onchange('amount', 'unit_price')
896     def _onchange_price(self):
897         # set auto-changing field
898         self.price = self.amount * self.unit_price
899         # Can optionally return a warning and domains
900         return {
901             'warning': {
902                 'title': "Something bad happened",
903                 'message': "It was very bad indeed",
904             }
905         }
906
907 For computed fields, valued ``onchange`` behavior is built-in as can be seen by
908 playing with the *Session* form: change the number of seats or participants, and
909 the ``taken_seats`` progressbar is automatically updated.
910
911 .. exercise:: Warning
912
913     Add an explicit onchange to warn about invalid values, like a negative
914     number of seats, or more participants than seats.
915
916     .. only:: solutions
917
918         .. patch::
919
920 Model constraints
921 =================
922
923 Odoo provides two ways to set up automatically verified invariants:
924 :func:`Python constraints <openerp.api.constrains>` and
925 :attr:`SQL constraints <openerp.models.Model._sql_constraints>`.
926
927 A Python constraint is defined as a method decorated with
928 :func:`~openerp.api.constrains`, and invoked on a recordset. The decorator
929 specifies which fields are involved in the constraint, so that the constraint is
930 automatically evaluated when one of them is modified. The method is expected to
931 raise an exception if its invariant is not satisfied::
932
933     from openerp.exceptions import ValidationError
934
935     @api.constrains('age')
936     def _check_something(self):
937         for record in self:
938             if record.age > 20:
939                 raise ValidationError("Your record is too old: %s" % record.age)
940         # all records passed the test, don't return anything
941
942 .. exercise:: Add Python constraints
943
944     Add a constraint that checks that the instructor is not present in the
945     attendees of his/her own session.
946
947     .. only:: solutions
948
949         .. patch::
950
951 SQL constraints are defined through the model attribute
952 :attr:`~openerp.models.Model._sql_constraints`. The latter is assigned to a list
953 of triples of strings ``(name, sql_definition, message)``, where ``name`` is a
954 valid SQL constraint name, ``sql_definition`` is a table_constraint_ expression,
955 and ``message`` is the error message.
956
957 .. exercise:: Add SQL constraints
958
959     With the help of `PostgreSQL's documentation`_ , add the following
960     constraints:
961
962     #. CHECK that the course description and the course title are different
963     #. Make the Course's name UNIQUE
964
965     .. only:: solutions
966
967         .. patch::
968
969 .. exercise:: Exercise 6 - Add a duplicate option
970
971     Since we added a constraint for the Course name uniqueness, it is not
972     possible to use the "duplicate" function anymore (:menuselection:`Form -->
973     Duplicate`).
974
975     Re-implement your own "copy" method which allows to duplicate the Course
976     object, changing the original name into "Copy of [original name]".
977
978     .. only:: solutions
979
980         .. patch::
981
982 Advanced Views
983 ==============
984
985 Tree views
986 ----------
987
988 Tree views can take supplementary attributes to further customize their
989 behavior:
990
991 ``colors``
992     mappings of colors to conditions. If the condition evaluates to ``True``,
993     the corresponding color is applied to the row:
994
995     .. code-block:: xml
996
997         <tree string="Idea Categories" colors="blue:state=='draft';red:state=='trashed'">
998             <field name="name"/>
999             <field name="state"/>
1000         </tree>
1001
1002     Clauses are separated by ``;``, the color and condition are separated by
1003     ``:``.
1004
1005 ``editable``
1006     Either ``"top"`` or ``"bottom"``. Makes the tree view editable in-place
1007     (rather than having to go through the form view), the value is the
1008     position where new rows appear.
1009
1010 .. exercise:: List coloring
1011
1012     Modify the Session tree view in such a way that sessions lasting less than
1013     5 days are colored blue, and the ones lasting more than 15 days are
1014     colored red.
1015
1016     .. only:: solutions
1017
1018         Modify the session tree view:
1019
1020         .. patch::
1021
1022 Calendars
1023 ---------
1024
1025 Displays records as calendar events. Their root element is ``<calendar>`` and
1026 their most common attributes are:
1027
1028 ``color``
1029     The name of the field used for *color segmentation*. Colors are
1030     automatically distributed to events, but events in the same color segment
1031     (records which have the same value for their ``@color`` field) will be
1032     given the same color.
1033 ``date_start``
1034     record's field holding the start date/time for the event
1035 ``date_stop`` (optional)
1036     record's field holding the end date/time for the event
1037
1038 field (to define the label for each calendar event)
1039
1040 .. code-block:: xml
1041
1042     <calendar string="Ideas" date_start="invent_date" color="inventor_id">
1043         <field name="name"/>
1044     </calendar>
1045
1046 .. exercise:: Calendar view
1047
1048     Add a Calendar view to the *Session* model enabling the user to view the
1049     events associated to the Open Academy.
1050
1051     .. only:: solutions
1052
1053         #. Add an ``end_date`` field computed from ``start_date`` and
1054            ``duration``
1055
1056            .. tip:: the inverse function makes the field writable, and allows
1057                     moving the sessions (via drag and drop) in the calendar view
1058
1059         #. Add a calendar view to the *Session* model
1060         #. And add the calendar view to the *Session* model's actions
1061
1062         .. patch::
1063
1064 Search views
1065 ------------
1066
1067 Search view ``<field>`` elements can have a ``@filter_domain`` that overrides
1068 the domain generated for searching on the given field. In the given domain,
1069 ``self`` represents the value entered by the user. In the example below, it is
1070 used to search on both fields ``name`` and ``description``.
1071
1072 Search views can also contain ``<filter>`` elements, which act as toggles for
1073 predefined searches. Filters must have one of the following attributes:
1074
1075 ``domain``
1076     add the given domain to the current search
1077 ``context``
1078     add some context to the current search; use the key ``group_by`` to group
1079     results on the given field name
1080
1081 .. code-block:: xml
1082
1083     <search string="Ideas">
1084         <field name="name"/>
1085         <field name="description" string="Name and description"
1086                filter_domain="['|', ('name', 'ilike', self), ('description', 'ilike', self)]"/>
1087         <field name="inventor_id"/>
1088         <field name="country_id" widget="selection"/>
1089
1090         <filter name="my_ideas" string="My Ideas"
1091                 domain="[('inventor_id', '=', uid)]"/>
1092         <group string="Group By">
1093             <filter name="group_by_inventor" string="Inventor"
1094                     context="{'group_by': 'inventor'}"/>
1095         </group>
1096     </search>
1097
1098 To use a non-default search view in an action, it should be linked using the
1099 ``search_view_id`` field of the action record.
1100
1101 The action can also set default values for search fields through its
1102 ``context`` field: context keys of the form
1103 :samp:`search_default_{field_name}` will initialize *field_name* with the
1104 provided value. Search filters must have an optional ``@name`` to have a
1105 default and behave as booleans (they can only be enabled by default).
1106
1107 .. exercise:: Search views
1108
1109     #. Add a button to filter the courses for which the current user is the
1110        responsible in the course search view. Make it selected by default.
1111     #. Add a button to group courses by responsible user.
1112
1113     .. only:: solutions
1114
1115         .. patch::
1116
1117 Gantt
1118 -----
1119
1120 Horizontal bar charts typically used to show project planning and advancement,
1121 their root element is ``<gantt>``.
1122
1123 .. code-block:: xml
1124
1125     <gantt string="Ideas" date_start="invent_date" color="inventor_id">
1126         <level object="idea.idea" link="id" domain="[]">
1127             <field name="inventor_id"/>
1128         </level>
1129     </gantt>
1130
1131 .. exercise:: Gantt charts
1132
1133     Add a Gantt Chart enabling the user to view the sessions scheduling linked
1134     to the Open Academy module. The sessions should be grouped by instructor.
1135
1136     .. only:: solutions
1137
1138         #. Create a computed field expressing the session's duration in hours
1139         #. Add the gantt view's definition, and add the gantt view to the
1140            *Session* model's action
1141
1142         .. patch::
1143
1144 Graph views
1145 -----------
1146
1147 Graph views allow aggregated overview and analysis of models, their root
1148 element is ``<graph>``.
1149
1150 Graph views have 4 display modes, the default mode is selected using the
1151 ``@type`` attribute.
1152
1153 Pivot
1154     a multidimensional table, allows the selection of filers and dimensions
1155     to get the right aggregated dataset before moving to a more graphical
1156     overview
1157 Bar (default)
1158     a bar chart, the first dimension is used to define groups on the
1159     horizontal axis, other dimensions define aggregated bars within each group.
1160
1161     By default bars are side-by-side, they can be stacked by using
1162     ``@stacked="True"`` on the ``<graph>``
1163 Line
1164     2-dimensional line chart
1165 Pie
1166     2-dimensional pie
1167
1168 Graph views contain ``<field>`` with a mandatory ``@type`` attribute taking
1169 the values:
1170
1171 ``row`` (default)
1172     the field should be aggregated by default
1173 ``measure``
1174     the field should be aggregated rather than grouped on
1175
1176 .. code-block:: xml
1177
1178     <graph string="Total idea score by Inventor">
1179         <field name="inventor_id"/>
1180         <field name="score" type="measure"/>
1181     </graph>
1182
1183 .. warning::
1184
1185     Graph views perform aggregations on database values, they do not work
1186     with non-stored computed fields.
1187
1188 .. exercise:: Graph view
1189
1190     Add a Graph view in the Session object that displays, for each course, the
1191     number of attendees under the form of a bar chart.
1192
1193     .. only:: solutions
1194
1195         #. Add the number of attendees as a stored computed field
1196         #. Then add the relevant view
1197
1198         .. patch::
1199
1200 Kanban
1201 ------
1202
1203 Used to organize tasks, production processes, etc… their root element is
1204 ``<kanban>``.
1205
1206 A kanban view shows a set of cards possibly grouped in columns. Each card
1207 represents a record, and each column the values of an aggregation field.
1208
1209 For instance, project tasks may be organized by stage (each column is a
1210 stage), or by responsible (each column is a user), and so on.
1211
1212 Kanban views define the structure of each card as a mix of form elements
1213 (including basic HTML) and :ref:`reference/qweb`.
1214
1215 .. exercise:: Kanban view
1216
1217     Add a Kanban view that displays sessions grouped by course (columns are
1218     thus courses).
1219
1220     .. only:: solutions
1221
1222         #. Add an integer ``color`` field to the *Session* model
1223         #. Add the kanban view and update the action
1224
1225         .. patch::
1226
1227 Workflows
1228 =========
1229
1230 Workflows are models associated to business objects describing their dynamics.
1231 Workflows are also used to track processes that evolve over time.
1232
1233 .. exercise:: Almost a workflow
1234
1235     Add a ``state`` field to the *Session* model. It will be used to define
1236     a workflow-ish.
1237
1238     A sesion can have three possible states: Draft (default), Confirmed and
1239     Done.
1240
1241     In the session form, add a (read-only) field to
1242     visualize the state, and buttons to change it. The valid transitions are:
1243
1244     * Draft -> Confirmed
1245     * Confirmed -> Draft
1246     * Confirmed -> Done
1247     * Done -> Draft
1248
1249     .. only:: solutions
1250
1251         #. Add a new ``state`` field
1252         #. Add state-transitioning methods, those can be called from view
1253            buttons to change the record's state
1254         #. And add the relevant buttons to the session's form view
1255
1256         .. patch::
1257
1258 Workflows may be associated with any object in Odoo, and are entirely
1259 customizable. Workflows are used to structure and manage the lifecycles of
1260 business objects and documents, and define transitions, triggers, etc. with
1261 graphical tools. Workflows, activities (nodes or actions) and transitions
1262 (conditions) are declared as XML records, as usual. The tokens that navigate
1263 in workflows are called workitems.
1264
1265 .. warning::
1266
1267     A workflow associated with a model is only created when the
1268     model's records are created. Thus there is no workflow instance
1269     associated with session instances created before the workflow's
1270     definition
1271
1272 .. exercise:: Workflow
1273
1274     Replace the ad-hoc *Session* workflow by a real workflow. Transform the
1275     *Session* form view so its buttons call the workflow instead of the
1276     model's methods.
1277
1278     .. only:: solutions
1279
1280         .. patch::
1281
1282         .. tip::
1283
1284             In order to check if instances of the workflow are correctly
1285             created alongside sessions, go to :menuselection:`Settings -->
1286             Technical --> Workflows --> Instances`
1287
1288
1289
1290 .. exercise:: Automatic transitions
1291
1292     Automatically transition sessions from *Draft* to *Confirmed* when more
1293     than half the session's seats are reserved.
1294
1295     .. only:: solutions
1296
1297         .. patch::
1298
1299 .. exercise:: Server actions
1300
1301     Replace the Python methods for synchronizing session state by
1302     server actions.
1303
1304     Both the workflow and the server actions could have been created entirely
1305     from the UI.
1306
1307     .. only:: solutions
1308
1309         .. patch::
1310
1311 Security
1312 ========
1313
1314 Access control mechanisms must be configured to achieve a coherent security
1315 policy.
1316
1317 Group-based access control mechanisms
1318 -------------------------------------
1319
1320 Groups are created as normal records on the model ``res.groups``, and granted
1321 menu access via menu definitions. However even without a menu, objects may
1322 still be accessible indirectly, so actual object-level permissions (read,
1323 write, create, unlink) must be defined for groups. They are usually inserted
1324 via CSV files inside modules. It is also possible to restrict access to
1325 specific fields on a view or object using the field's groups attribute.
1326
1327 Access rights
1328 -------------
1329
1330 Access rights are defined as records of the model ``ir.model.access``. Each
1331 access right is associated to a model, a group (or no group for global
1332 access), and a set of permissions: read, write, create, unlink. Such access
1333 rights are usually created by a CSV file named after its model:
1334 ``ir.model.access.csv``.
1335
1336 .. code-block:: text
1337
1338     id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
1339     access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0
1340     access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0
1341
1342 .. exercise:: Add access control through the OpenERP interface
1343
1344     Create a new user "John Smith". Then create a group
1345     "OpenAcademy / Session Read" with read access to the *Session* model.
1346
1347     .. only:: solutions
1348
1349         #. Create a new user *John Smith* through
1350            :menuselection:`Settings --> Users --> Users`
1351         #. Create a new group ``session_read`` through
1352            :menuselection:`Settings --> Users --> Groups`, it should have
1353            read access on the *Session* model
1354         #. Edit *John Smith* to make them a member of ``session_read``
1355         #. Log in as *John Smith* to check the access rights are correct
1356
1357 .. exercise:: Add access control through data files in your module
1358
1359     Using data files,
1360
1361     * Create a group *OpenAcademy / Manager* with full access to all
1362       OpenAcademy models
1363     * Make *Session* and *Course* readable by all users
1364
1365     .. only:: solutions
1366
1367         #. Create a new file ``openacademy/security/security.xml`` to
1368            hold the OpenAcademy Manager group
1369         #. Edit the file ``openacademy/security/ir.model.access.csv`` with
1370            the access rights to the models
1371         #. Finally update ``openacademy/__openerp__.py`` to add the new data
1372            files to it
1373
1374         .. patch::
1375
1376 Record rules
1377 ------------
1378
1379 A record rule restricts the access rights to a subset of records of the given
1380 model. A rule is a record of the model ``ir.rule``, and is associated to a
1381 model, a number of groups (many2many field), permissions to which the
1382 restriction applies, and a domain. The domain specifies to which records the
1383 access rights are limited.
1384
1385 Here is an example of a rule that prevents the deletion of leads that are not
1386 in state ``cancel``. Notice that the value of the field ``groups`` must follow
1387 the same convention as the method :meth:`~openerp.models.Model.write` of the ORM.
1388
1389 .. code-block:: xml
1390
1391     <record id="delete_cancelled_only" model="ir.rule">
1392         <field name="name">Only cancelled leads may be deleted</field>
1393         <field name="model_id" ref="crm.model_crm_lead"/>
1394         <field name="groups" eval="[(4, ref('base.group_sale_manager'))]"/>
1395         <field name="perm_read" eval="0"/>
1396         <field name="perm_write" eval="0"/>
1397         <field name="perm_create" eval="0"/>
1398         <field name="perm_unlink" eval="1" />
1399         <field name="domain_force">[('state','=','cancel')]</field>
1400     </record>
1401
1402 .. exercise:: Record rule
1403
1404     Add a record rule for the model Course and the group
1405     "OpenAcademy / Manager", that restricts ``write`` and ``unlink`` accesses
1406     to the responsible of a course. If a course has no responsible, all users
1407     of the group must be able to modify it.
1408
1409     .. only:: solutions
1410
1411         Create a new rule in ``openacademy/security/security.xml``:
1412
1413         .. patch::
1414
1415 Wizards
1416 =======
1417
1418 Wizards describe interactive sessions with the user (or dialog boxes) through
1419 dynamic forms. A wizard is simply a model that extends the class
1420 :class:`~openerp.models.TransientModel` instead of
1421 :class:`~openerp.models.Model`. The class
1422 :class:`~openerp.models.TransientModel` extends :class:`~openerp.models.Model`
1423 and reuse all its existing mechanisms, with the following particularities:
1424
1425 - Wizard records are not meant to be persistent; they are automatically deleted
1426   from the database after a certain time. This is why they are called
1427   *transient*.
1428 - Wizard models do not require explicit access rights: users have all
1429   permissions on wizard records.
1430 - Wizard records may refer to regular records or wizard records through many2one
1431   fields, but regular records *cannot* refer to wizard records through a
1432   many2one field.
1433
1434 We want to create a wizard that allow users to create attendees for a particular
1435 session, or for a list of sessions at once.
1436
1437 .. exercise:: Define the wizard
1438
1439     Create a wizard model with a many2one relationship with the *Session*
1440     model and a many2many relationship with the *Partner* model.
1441
1442     .. only:: solutions
1443
1444         Add a new file ``openacademy/wizard.py``:
1445
1446         .. patch::
1447
1448 Launching wizards
1449 -----------------
1450
1451 Wizards are launched by ``ir.actions.act_window`` records, with the field
1452 ``target`` set to the value ``new``. The latter opens the wizard view into a
1453 popup window. The action may be triggered by a menu item.
1454
1455 There is another way to launch the wizard: using an ``ir.actions.act_window``
1456 record like above, but with an extra field ``src_model`` that specifies in the
1457 context of which model the action is available. The wizard will appear in the
1458 contextual actions of the model, above the main view. Because of some internal
1459 hooks in the ORM, such an action is declared in XML with the tag ``act_window``.
1460
1461 .. code:: xml
1462
1463     <act_window id="launch_the_wizard"
1464                 name="Launch the Wizard"
1465                 src_model="context_model_name"
1466                 res_model="wizard_model_name"
1467                 view_mode="form"
1468                 target="new"
1469                 key2="client_action_multi"/>
1470
1471 Wizards use regular views and their buttons may use the attribute
1472 ``special="cancel"`` to close the wizard window without saving.
1473
1474 .. exercise:: Launch the wizard
1475
1476     #. Define a form view for the wizard.
1477     #. Add the action to launch it in the context of the *Session* model.
1478     #. Define a default value for the session field in the wizard; use the
1479        context parameter ``self._context`` to retrieve the current session.
1480
1481     .. only:: solutions
1482
1483         .. patch::
1484
1485 .. exercise:: Register attendees
1486
1487     Add buttons to the wizard, and implement the corresponding method for adding
1488     the attendees to the given session.
1489
1490     .. only:: solutions
1491
1492         .. patch::
1493
1494 .. exercise:: Register attendees to multiple sessions
1495
1496     Modify the wizard model so that attendees can be registered to multiple
1497     sessions.
1498
1499     .. only:: solutions
1500
1501         .. patch::
1502
1503 Internationalization
1504 ====================
1505
1506 Each module can provide its own translations within the i18n directory, by
1507 having files named LANG.po where LANG is the locale code for the language, or
1508 the language and country combination when they differ (e.g. pt.po or
1509 pt_BR.po). Translations will be loaded automatically by Odoo for all
1510 enabled languages. Developers always use English when creating a module, then
1511 export the module terms using Odoo's gettext POT export feature
1512 (:menuselection:`Settings --> Translations --> Import/Export --> Export
1513 Translation` without specifying a language), to create the module template POT
1514 file, and then derive the translated PO files. Many IDE's have plugins or modes
1515 for editing and merging PO/POT files.
1516
1517 .. tip:: The GNU gettext format (Portable Object) used by Odoo is
1518          integrated into LaunchPad, making it an online collaborative
1519          translation platform.
1520
1521 .. code-block:: text
1522
1523    |- idea/ # The module directory
1524       |- i18n/ # Translation files
1525          | - idea.pot # Translation Template (exported from Odoo)
1526          | - fr.po # French translation
1527          | - pt_BR.po # Brazilian Portuguese translation
1528          | (...)
1529
1530 .. tip:: 
1531
1532    By default Odoo's POT export only extracts labels inside XML files or
1533    inside field definitions in Python code, but any Python string can be
1534    translated this way by surrounding it with the function :func:`openerp._`
1535    (e.g. ``_("Label")``)
1536
1537 .. exercise:: Translate a module
1538
1539    Choose a second language for your Odoo installation. Translate your
1540    module using the facilities provided by Odoo.
1541
1542    .. only:: solutions
1543
1544         #. Create a directory ``openacademy/i18n/``
1545         #. Install whichever language you want (
1546            :menuselection:`Administration --> Translations --> Load an
1547            Official Translation`)
1548         #. Synchronize translatable terms (:menuselection:`Administration -->
1549            Translations --> Application Terms --> Synchronize Translations`)
1550         #. Create a template translation file by exporting (
1551            :menuselection:`Administration --> Translations -> Import/Export
1552            --> Export Translation`) without specifying a language, save in
1553            ``openacademy/i18n/``
1554         #. Create a translation file by exporting (
1555            :menuselection:`Administration --> Translations --> Import/Export
1556            --> Export Translation`) and specifying a language. Save it in
1557            ``openacademy/i18n/``
1558         #. Open the exported translation file (with a basic text editor or a
1559            dedicated PO-file editor e.g. POEdit_ and translate the missing
1560            terms
1561
1562         #. In ``models.py``, add an import statement for the function
1563            ``openerp._`` and mark missing strings as translatable
1564
1565         #. Repeat steps 3-6
1566
1567         .. patch::
1568
1569         .. todo:: do we never reload translations?
1570
1571
1572 Reporting
1573 =========
1574
1575 Printed reports
1576 ---------------
1577
1578 Odoo 8.0 comes with a new report engine based on :ref:`reference/qweb`,
1579 `Twitter Bootstrap`_ and Wkhtmltopdf_. 
1580
1581 A report is a combination two elements:
1582
1583 * an ``ir.actions.report.xml``, for which a ``<report>`` shortcut element is
1584   provided, it sets up various basic parameters for the report (default
1585   type, whether the report should be saved to the database after generation,…)
1586
1587
1588   .. code-block:: xml
1589
1590       <report
1591           id="account_invoices"
1592           model="account.invoice"
1593           string="Invoices"
1594           report_type="qweb-pdf"
1595           name="account.report_invoice"
1596           file="account.report_invoice"
1597           attachment_use="True"
1598           attachment="(object.state in ('open','paid')) and
1599               ('INV'+(object.number or '').replace('/','')+'.pdf')"
1600       />
1601
1602 * A standard :ref:`QWeb view <reference/views/qweb>` for the actual report:
1603
1604   .. code-block:: xml
1605
1606     <t t-call="report.html_container">
1607         <t t-foreach="docs" t-as="o">
1608             <t t-call="report.external_layout">
1609                 <div class="page">
1610                     <h2>Report title</h2>
1611                 </div>
1612             </t>
1613         </t>
1614     </t>
1615
1616     the standard rendering context provides a number of elements, the most
1617     important being:
1618
1619     ``docs``
1620         the records for which the report is printed
1621     ``user``
1622         the user printing the report
1623
1624 Because reports are standard web pages, they are available through a URL and
1625 output parameters can be manipulated through this URL, for instance the HTML
1626 version of the *Invoice* report is available through
1627 http://localhost:8069/report/html/account.report_invoice/1 (if ``account`` is
1628 installed) and the PDF version through
1629 http://localhost:8069/report/pdf/account.report_invoice/1.
1630
1631 .. exercise:: Create a report for the Session model
1632
1633    For each session, it should display session's name, its start and end,
1634    and list the session's attendees.
1635
1636    .. only:: solutions
1637
1638         .. patch::
1639
1640 Dashboards
1641 ----------
1642
1643 .. exercise:: Define a Dashboard
1644
1645    Define a dashboard containing the graph view you created, the sessions
1646    calendar view and a list view of the courses (switchable to a form
1647    view). This dashboard should be available through a menuitem in the menu,
1648    and automatically displayed in the web client when the OpenAcademy main
1649    menu is selected.
1650
1651    .. only:: solutions
1652
1653         #. Create a file ``openacademy/views/session_board.xml``. It should contain
1654            the board view, the actions referenced in that view, an action to
1655            open the dashboard and a re-definition of the main menu item to add
1656            the dashboard action
1657
1658            .. note:: Available dashboard styles are ``1``, ``1-1``, ``1-2``,
1659                      ``2-1`` and ``1-1-1``
1660
1661         #. Update ``openacademy/__openerp__.py`` to reference the new data
1662            file
1663
1664         .. patch::
1665
1666 WebServices
1667 ===========
1668
1669 The web-service module offer a common interface for all web-services :
1670
1671 - XML-RPC
1672 - JSON-RPC
1673
1674 Business objects can also be accessed via the distributed object
1675 mechanism. They can all be modified via the client interface with contextual
1676 views.
1677
1678 Odoo is accessible through XML-RPC/JSON-RPC interfaces, for which libraries
1679 exist in many languages.
1680
1681 XML-RPC Library
1682 ---------------
1683
1684 The following example is a Python program that interacts with an Odoo
1685 server with the library ``xmlrpclib``::
1686
1687    import xmlrpclib
1688
1689    root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
1690
1691    uid = xmlrpclib.ServerProxy(root + 'common').login(DB, USER, PASS)
1692    print "Logged in as %s (uid: %d)" % (USER, uid)
1693
1694    # Create a new idea
1695    sock = xmlrpclib.ServerProxy(root + 'object')
1696    args = {
1697        'name' : 'Another idea',
1698        'description' : 'This is another idea of mine',
1699        'inventor_id': uid,
1700    }
1701    idea_id = sock.execute(DB, uid, PASS, 'idea.idea', 'create', args)
1702
1703 .. exercise:: Add a new service to the client
1704
1705    Write a Python program able to send XML-RPC requests to a PC running
1706    Odoo (yours, or your instructor's). This program should display all
1707    the sessions, and their corresponding number of seats. It should also
1708    create a new session for one of the courses.
1709
1710    .. only:: solutions
1711
1712         .. code-block:: python
1713
1714             import functools
1715             import xmlrpclib
1716             HOST = 'localhost'
1717             PORT = 8069
1718             DB = 'openacademy'
1719             USER = 'admin'
1720             PASS = 'admin'
1721             ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)
1722
1723             # 1. Login
1724             uid = xmlrpclib.ServerProxy(ROOT + 'common').login(DB,USER,PASS)
1725             print "Logged in as %s (uid:%d)" % (USER,uid)
1726
1727             call = functools.partial(
1728                 xmlrpclib.ServerProxy(ROOT + 'object').execute,
1729                 DB, uid, PASS)
1730
1731             # 2. Read the sessions
1732             sessions = call('openacademy.session','search_read', [], ['name','seats'])
1733             for session in sessions:
1734                 print "Session %s (%s seats)" % (session['name'], session['seats'])
1735             # 3.create a new session
1736             session_id = call('openacademy.session', 'create', {
1737                 'name' : 'My session',
1738                 'course_id' : 2,
1739             })
1740
1741         Instead of using a hard-coded course id, the code can look up a course
1742         by name::
1743
1744             # 3.create a new session for the "Functional" course
1745             course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]
1746             session_id = call('openacademy.session', 'create', {
1747                 'name' : 'My session',
1748                 'course_id' : course_id,
1749             })
1750
1751 JSON-RPC Library
1752 ----------------
1753
1754 The following example is a Python program that interacts with an Odoo server
1755 with the standard Python libraries ``urllib2`` and ``json``::
1756
1757     import json
1758     import random
1759     import urllib2
1760
1761     def json_rpc(url, method, params):
1762         data = {
1763             "jsonrpc": "2.0",
1764             "method": method,
1765             "params": params,
1766             "id": random.randint(0, 1000000000),
1767         }
1768         req = urllib2.Request(url=url, data=json.dumps(data), headers={
1769             "Content-Type":"application/json",
1770         })
1771         reply = json.load(urllib2.urlopen(req))
1772         if reply.get("error"):
1773             raise Exception(reply["error"])
1774         return reply["result"]
1775
1776     def call(url, service, method, *args):
1777         return json_rpc(url, "call", {"service": service, "method": method, "args": args})
1778
1779     # log in the given database
1780     url = "http://%s:%s/jsonrpc" % (HOST, PORT)
1781     uid = call(url, "common", "login", DB, USER, PASS)
1782
1783     # create a new idea
1784     args = {
1785         'name' : 'Another idea',
1786         'description' : 'This is another idea of mine',
1787         'inventor_id': uid,
1788     }
1789     idea_id = call(url, "object", "execute", DB, uid, PASS, 'idea.idea', 'create', args)
1790
1791 Here is the same program, using the library
1792 `jsonrpclib <https://pypi.python.org/pypi/jsonrpclib>`::
1793
1794     import jsonrpclib
1795
1796     # server proxy object
1797     url = "http://%s:%s/jsonrpc" % (HOST, PORT)
1798     server = jsonrpclib.Server(url)
1799
1800     # log in the given database
1801     uid = server.call(service="common", method="login", args=[DB, USER, PASS])
1802
1803     # helper function for invoking model methods
1804     def invoke(model, method, *args):
1805         args = [DB, uid, PASS, model, method] + list(args)
1806         return server.call(service="object", method="execute", args=args)
1807
1808     # create a new idea
1809     args = {
1810         'name' : 'Another idea',
1811         'description' : 'This is another idea of mine',
1812         'inventor_id': uid,
1813     }
1814     idea_id = invoke('idea.idea', 'create', args)
1815
1816 Examples can be easily adapted from XML-RPC to JSON-RPC.
1817
1818 .. note::
1819
1820     There are a number of high-level APIs in various languages to access Odoo
1821     systems without *explicitly* going through XML-RPC or JSON-RPC, such as:
1822
1823     * https://github.com/akretion/ooor
1824     * https://github.com/syleam/openobject-library
1825     * https://github.com/nicolas-van/openerp-client-lib
1826     * https://pypi.python.org/pypi/oersted/
1827
1828 .. [#autofields] it is possible to :attr:`disable the automatic creation of some
1829                  fields <openerp.models.Model._log_access>`
1830 .. [#rawsql] writing raw SQL queries is possible, but requires care as it
1831              bypasses all Odoo authentication and security mechanisms.
1832
1833 .. _database index:
1834     http://use-the-index-luke.com/sql/preface
1835
1836 .. _POEdit: http://poedit.net
1837
1838 .. _PostgreSQL's documentation:
1839 .. _table_constraint:
1840     http://www.postgresql.org/docs/9.3/static/ddl-constraints.html
1841
1842 .. _python: http://python.org
1843
1844 .. _XPath: http://w3.org/TR/xpath
1845
1846 .. _twitter bootstrap: http://getbootstrap.com
1847
1848 .. _wkhtmltopdf: http://wkhtmltopdf.org