1 .. queue:: backend/series
7 Start/Stop the Odoo server
8 ==========================
10 Odoo uses a client/server architecture in which clients are web browsers
11 accessing the odoo server via RPC.
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.
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
25 The server is stopped by hitting ``Ctrl-C`` twice from the terminal, or by
26 killing the corresponding OS process.
31 Both server and client extensions are packaged as *modules* which are
32 optionally loaded in a *database*.
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.
39 Everything in Odoo thus starts and ends with modules.
41 Composition of a module
42 -----------------------
44 An Odoo module can contain a number of elements:
47 declared as Python classes, these resources are automatically persisted
48 by Odoo based on their configuration
51 XML or CSV files declaring metadata (views or workflows), configuration
52 data (modules parameterization), demonstration data and more
55 Handle requests from web browsers
58 Images, CSS or javascript files used by the web interface or website
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>`
70 most command-line options can also be set using :ref:`a configuration
71 file <reference/cmdline/config>`
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, …
78 The manifest's general structure is::
84 'author': "Author Name",
85 'category': 'Category',
89 # data files always loaded at installation
93 # data files containing optionally loaded demonstration data
100 `Python package <http://docs.python.org/2/tutorial/modules.html#packages>`_
101 with a ``__init__.py`` file, containing import instructions for various Python
104 For instance, if the module has a single ``mymodule.py`` file ``__init__.py``
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:
115 odoo.py scaffold <module name> <where to put it>
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.
121 .. exercise:: Module creation
123 Use the command line above to create an empty module Open Academy, and
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.
134 Object-Relational Mapping
135 -------------------------
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]_.
141 Business objects are declared as Python classes extending
142 :class:`~openerp.models.Model` which integrates them into the automated
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
151 from openerp import models
152 class MinimalModel(models.Model):
158 Fields are used to define what the model can store and where. Fields are
159 defined as attributes on the model class::
161 from openerp import models, fields
163 class LessMinimalModel(models.Model):
164 _name = 'test.model2'
171 Much like the model itself, its fields can be configured, by passing
172 configuration attributes as parameters::
174 name = field.Char(required=True)
176 Some attributes are available on all fields, here are the most common ones:
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-formm, 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
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).
195 Example of simple fields are :class:`~openerp.fields.Boolean`,
196 :class:`~openerp.fields.Date`, :class:`~openerp.fields.Char`.
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
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
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`.
223 .. exercise:: Define a model
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.
230 Edit the file ``openacademy/models.py`` to include a *Course* class.
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.
240 .. tip:: some modules exist solely to add data into Odoo
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
251 <record model="{model name}" id="{record identifier}">
252 <field name="{a field name}">{a value}</field>
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.
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).
267 .. exercise:: Define demonstration data
269 Create demonstration data filling the *Courses* model with a few
270 demonstration courses.
274 Edit the file ``openacademy/demo.xml`` to include some data.
281 Actions and menus are regular records in database, usually declared through
282 data files. Actions can be triggered in three ways:
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
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
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>
299 <menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10"
300 action="action_list_ideas"/>
305 The action must be declared before its corresponding menu in the XML file.
307 Data files are executed sequentially, the action's ``id`` must be present
308 in the database before the menu can be created.
310 .. exercise:: Define new menu entries
312 Define new menu entries to access courses and sessions under the
313 OpenAcademy menu entry. A user should be able to
315 - display a list of all the courses
316 - create/modify courses
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``
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
337 :ref:`View inheritance <reference/views/inheritance>` allows altering views
338 declared elsewhere (adding or removing content).
340 Generic view declaration
341 ------------------------
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:
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>, ... -->
357 .. danger:: The view's content is XML.
360 The ``arch`` field must thus be declared as ``type="xml"`` to be parsed
366 Tree views, also called list views, display records in a tabular form.
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):
373 <tree string="Idea list">
375 <field name="inventor_id"/>
381 Forms are used to create and edit single records.
384 Their root element is ``<form>``. They composed of high-level structure
385 elements (groups, notebooks) and interactive elements (buttons and fields):
389 <form string="Idea form">
391 <group colspan="2" col="2">
392 <separator string="General stuff" colspan="2"/>
394 <field name="inventor_id"/>
397 <group colspan="2" col="2">
398 <separator string="Dates" colspan="2"/>
399 <field name="active"/>
400 <field name="invent_date" readonly="1"/>
403 <notebook colspan="4">
404 <page string="Description">
405 <field name="description" nolabel="1"/>
409 <field name="state"/>
413 .. exercise:: Customise form view using XML
415 Create your own form view for the Course object. Data displayed should be:
416 the name and the description of the course.
422 .. exercise:: Notebooks
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
430 Modify the Course form view as follows:
434 Form views can also use plain HTML for more flexible layouts:
438 <form string="Idea Form">
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"/>
449 <div class="oe_title">
450 <label for="name" class="oe_edit_only" string="Idea Name" />
451 <h1><field name="name" /></h1>
453 <separator string="General" colspan="2" />
454 <group colspan="2" col="2">
455 <field name="description" placeholder="Idea description..." />
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:
471 <field name="inventor_id"/>
474 If no search view exists for the model, Odoo generates one which only allows
475 searching on the ``name`` field.
477 .. exercise:: Search courses
479 Allow searching for courses based on their title or their description.
485 Relations between models
486 ========================
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.
492 .. exercise:: Create a session model
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.
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.
503 #. Create the class *Session* in ``openacademy/models.py``.
504 #. Add access to the session object in ``openacademy/view/openacademy.xml``.
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
516 Relational fields link records, either of the same model (hierarchies) or
517 between different models.
519 Relational field types are:
521 :class:`Many2one(other_model, ondelete='set null') <openerp.fields.Many2one>`
522 A simple link to an other object::
524 print foo.other_id.name
526 .. seealso:: `foreign keys <http://www.postgresql.org/docs/9.3/static/tutorial-fk.html>`_
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::
533 for other in foo.other_ids:
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}`
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::
547 for other in foo.other_ids:
550 .. exercise:: Many2one relations
552 Using a many2one, modify the *Course* and *Session* models to reflect their
553 relation with other models:
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.
565 #. Add the relevant ``Many2one`` fields to the models, and
566 #. add them in the views.
570 .. exercise:: Inverse one2many relations
572 Using the inverse relational field one2many, modify the models to reflect
573 the relation between courses and sessions.
577 #. Modify the ``Course`` class, and
578 #. add the field in the course form view.
582 .. exercise:: Multiple many2many relations
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.
591 #. Modify the ``Session`` class, and
592 #. add the field in the form view.
602 Odoo provides two *inheritance* mechanisms to extend an existing model in a
605 The first inheritance mechanism allows a module to modify the behavior of a
606 model defined in another module:
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.
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.
618 .. image:: backend/inheritance_methods.png
623 * :attr:`~openerp.models.Model._inherit`
624 * :attr:`~openerp.models.Model._inherits`
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.
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:
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"/>
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
657 Operation to apply to the matched element:
660 appends ``xpath``'s body at the end of the matched element
662 replaces the matched element by the ``xpath``'s body
664 inserts the ``xpath``'s body as a sibling before the matched element
666 inserts the ``xpaths``'s body as a sibling after the matched element
668 alters the attributes of the matched element using special
669 ``attribute`` elements in the ``xpath``'s body
671 .. exercise:: Alter existing content
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
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
686 #. Create a file ``openacademy/partner.py`` and import it in
688 #. Create a file ``openacademy/views/partner.xml`` and add it to
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.
700 For instance, when used on the *Product* model the following domain selects
701 all *services* with a unit price over *1000*::
703 [('product_type', '=', 'service'), ('unit_price', '>', 1000)]
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"::
712 ('product_type', '=', 'service'),
714 ('unit_price', '>=', 1000),
715 ('unit_price', '<', 2000)]
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.
720 .. exercise:: Domains on relational fields
722 When selecting the instructor for a *Session*, only instructors (partners
723 with ``instructor`` set to ``True``) should be visible.
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
736 .. exercise:: More complex domains
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
744 #. Modify the *Session* model's domain
745 #. Modify ``openacademy/view/partner.xml`` to get access to
746 *Partner categories*:
750 Computed fields and default values
751 ==================================
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
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
763 .. danger:: ``self`` is a collection
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 +
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``.
775 .. code-block:: python
778 from openerp import models, fields
780 class ComputedModel(models.Model):
781 _name = 'test.computed'
783 name = fields.Char(compute='_compute_name')
785 def _compute_name(self):
787 record.name = str(random.randint(1, 1e6))
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::
794 def _compute_name(self):
795 self.name = str(random.randint(1, 1e6))
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::
806 from openerp import models, fields, api
808 class ComputedModel(models.Model):
809 _name = 'test.computed'
811 name = fields.Char(compute='_compute_name')
812 value = fields.Integer()
815 @api.depends('value')
816 def _compute_name(self):
817 self.name = "Record with value %s" % self.value
819 .. exercise:: Computed fields
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
827 #. Add a computed field to *Session*
828 #. Show the field in the *Session* view:
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::
839 name = fields.Char(default="Unknown")
840 user_id = fields.Many2one('res.users', default=lambda self: self.env.user)
844 The object ``self.env`` gives access to request parameters and other useful
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
855 .. exercise:: Active objects – Default values
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
868 Odoo has built-in rules making fields with an ``active`` field set
869 to ``False`` invisible.
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
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.
887 <!-- content of form view -->
888 <field name="amount"/>
889 <field name="unit_price"/>
890 <field name="price" readonly="1"/>
892 .. code-block:: python
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
902 'title': "Something bad happened",
903 'message': "It was very bad indeed",
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.
911 .. exercise:: Warning
913 Add an explicit onchange to warn about invalid values, like a negative
914 number of seats, or more participants than seats.
923 Odoo provides two ways to set up automatically verified invariants:
924 :func:`Python constraints <openerp.api.constrains>` and
925 :attr:`SQL constaints <openerp.models.Model._sql_constraints>`.
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::
933 from openerp.exceptions import ValidationError
935 @api.constrains('age')
936 def _check_something(self):
939 raise ValidationError("Your record is too old: %s" % record.age)
940 # all records passed the test, don't return anything
942 .. exercise:: Add Python constraints
944 Add a constraint that checks that the instructor is not present in the
945 attendees of his/her own session.
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.
957 .. exercise:: Add SQL constraints
959 With the help of `PostgreSQL's documentation`_ , add the following
962 #. CHECK that the course description and the course title are different
963 #. Make the Course's name UNIQUE
969 .. exercise:: Exercise 6 - Add a duplicate option
971 Since we added a constraint for the Course name uniqueness, it is not
972 possible to use the "duplicate" function anymore (:menuselection:`Form -->
975 Re-implement your own "copy" method which allows to duplicate the Course
976 object, changing the original name into "Copy of [original name]".
988 Tree views can take supplementary attributes to further customize their
992 mappings of colors to conditions. If the condition evaluates to ``True``,
993 the corresponding color is applied to the row:
997 <tree string="Idea Categories" colors="blue:state=='draft';red:state=='trashed'">
999 <field name="state"/>
1002 Clauses are separated by ``;``, the color and condition are separated by
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.
1010 .. exercise:: List coloring
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
1018 Modify the session tree view:
1025 Displays records as calendar events. Their root element is ``<calendar>`` and
1026 their most common attributes are:
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.
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
1038 field (to define the label for each calendar event)
1042 <calendar string="Ideas" date_start="invent_date" color="inventor_id">
1043 <field name="name"/>
1046 .. exercise:: Calendar view
1048 Add a Calendar view to the *Session* model enabling the user to view the
1049 events associated to the Open Academy.
1053 #. Add an ``end_date`` field computed from ``start_date`` and
1056 .. tip:: the inverse function makes the field writable, and allows
1057 moving the sessions (via drag and drop) in the calendar view
1059 #. Add a calendar view to the *Session* model
1060 #. And add the calendar view to the *Session* model's actions
1067 Search view fields can take custom operators or :ref:`reference/orm/domains`
1068 for more flexible matching of results.
1070 Search views can also contain *filters* which act as toggles for predefined
1071 searches (defined using :ref:`reference/orm/domains`):
1075 <search string="Ideas">
1076 <filter name="my_ideas" domain="[('inventor_id','=',uid)]"
1077 string="My Ideas" icon="terp-partner"/>
1078 <field name="name"/>
1079 <field name="description"/>
1080 <field name="inventor_id"/>
1081 <field name="country_id" widget="selection"/>
1084 To use a non-default search view in an action, it should be linked using the
1085 ``search_view_id`` field of the action record.
1087 The action can also set default values for search fields through its
1088 ``context`` field: context keys of the form
1089 :samp:`search_default_{field_name}` will initialize *field_name* with the
1090 provided value. Search filters must have an optional ``@name`` to have a
1091 default and behave as booleans (they can only be enabled by default).
1093 .. exercise:: Search views
1095 Add a button to filter the courses for which the current user is the
1096 responsible in the course search view. Make it selected by default.
1105 Horizontal bar charts typically used to show project planning and advancement,
1106 their root element is ``<gantt>``.
1110 <gantt string="Ideas" date_start="invent_date" color="inventor_id">
1111 <level object="idea.idea" link="id" domain="[]">
1112 <field name="inventor_id"/>
1116 .. exercise:: Gantt charts
1118 Add a Gantt Chart enabling the user to view the sessions scheduling linked
1119 to the Open Academy module. The sessions should be grouped by instructor.
1123 #. Create a computed field expressing the session's duration in hours
1124 #. Add the gantt view's definition, and add the gantt view to the
1125 *Session* model's action
1132 Graph views allow aggregated overview and analysis of models, their root
1133 element is ``<graph>``.
1135 Graph views have 4 display modes, the default mode is selected using the
1136 ``@type`` attribute.
1139 a multidimensional table, allows the selection of filers and dimensions
1140 to get the right aggregated dataset before moving to a more graphical
1143 a bar chart, the first dimension is used to define groups on the
1144 horizontal axis, other dimensions define aggregated bars within each group.
1146 By default bars are side-by-side, they can be stacked by using
1147 ``@stacked="True"`` on the ``<graph>``
1149 2-dimensional line chart
1153 Graph views contain ``<field>`` with a mandatory ``@type`` attribute taking
1157 the field should be aggregated by default
1159 the field should be aggregated rather than grouped on
1163 <graph string="Total idea score by Inventor">
1164 <field name="inventor_id"/>
1165 <field name="score" type="measure"/>
1170 Graph views perform aggregations on database values, they do not work
1171 with non-stored computed fields.
1173 .. exercise:: Graph view
1175 Add a Graph view in the Session object that displays, for each course, the
1176 number of attendees under the form of a bar chart.
1180 #. Add the number of attendees as a stored computed field
1181 #. Then add the relevant view
1188 Used to organize tasks, production processes, etc… their root element is
1191 A kanban view shows a set of cards possibly grouped in columns. Each card
1192 represents a record, and each column the values of an aggregation field.
1194 For instance, project tasks may be organized by stage (each column is a
1195 stage), or by responsible (each column is a user), and so on.
1197 Kanban views define the structure of each card as a mix of form elements
1198 (including basic HTML) and :ref:`reference/qweb`.
1200 .. exercise:: Kanban view
1202 Add a Kanban view that displays sessions grouped by course (columns are
1207 #. Add an integer ``color`` field to the *Session* model
1208 #. Add the kanban view and update the action
1215 Workflows are models associated to business objects describing their dynamics.
1216 Workflows are also used to track processes that evolve over time.
1218 .. exercise:: Almost a workflow
1220 Add a ``state`` field to the *Session* model. It will be used to define
1223 A sesion can have three possible states: Draft (default), Confirmed and
1226 In the session form, add a (read-only) field to
1227 visualize the state, and buttons to change it. The valid transitions are:
1236 #. Add a new ``state`` field
1237 #. Add state-transitioning methods, those can be called from view
1238 buttons to change the record's state
1239 #. And add the relevant buttons to the session's form view
1243 Workflows may be associated with any object in Odoo, and are entirely
1244 customizable. Workflows are used to structure and manage the lifecycles of
1245 business objects and documents, and define transitions, triggers, etc. with
1246 graphical tools. Workflows, activities (nodes or actions) and transitions
1247 (conditions) are declared as XML records, as usual. The tokens that navigate
1248 in workflows are called workitems.
1250 .. exercise:: Workflow
1252 Replace the ad-hoc *Session* workflow by a real workflow. Transform the
1253 *Session* form view so its buttons call the workflow instead of the
1262 A workflow associated with a model is only created when the
1263 model's records are created. Thus there is no workflow instance
1264 associated with session instances created before the workflow's
1269 In order to check if instances of the workflow are correctly
1270 created alongside sessions, go to :menuselection:`Settings -->
1271 Technical --> Workflows --> Instances`
1275 .. exercise:: Automatic transitions
1277 Automatically transition sessions from *Draft* to *Confirmed* when more
1278 than half the session's seats are reserved.
1284 .. exercise:: Server actions
1286 Replace the Python methods for synchronizing session state by
1289 Both the workflow and the server actions could have been created entirely
1299 Access control mechanisms must be configured to achieve a coherent security
1302 Group-based access control mechanisms
1303 -------------------------------------
1305 Groups are created as normal records on the model ``res.groups``, and granted
1306 menu access via menu definitions. However even without a menu, objects may
1307 still be accessible indirectly, so actual object-level permissions (read,
1308 write, create, unlink) must be defined for groups. They are usually inserted
1309 via CSV files inside modules. It is also possible to restrict access to
1310 specific fields on a view or object using the field's groups attribute.
1315 Access rights are defined as records of the model ``ir.model.access``. Each
1316 access right is associated to a model, a group (or no group for global
1317 access), and a set of permissions: read, write, create, unlink. Such access
1318 rights are usually created by a CSV file named after its model:
1319 ``ir.model.access.csv``.
1321 .. code-block:: text
1323 id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
1324 access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0
1325 access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0
1327 .. exercise:: Add access control through the OpenERP interface
1329 Create a new user "John Smith". Then create a group
1330 "OpenAcademy / Session Read" with read access to the *Session* model.
1334 #. Create a new user *John Smith* through
1335 :menuselection:`Settings --> Users --> Users`
1336 #. Create a new group ``session_read`` through
1337 :menuselection:`Settings --> Users --> Groups`, it should have
1338 read access on the *Session* model
1339 #. Edit *John Smith* to make them a member of ``session_read``
1340 #. Log in as *John Smith* to check the access rights are correct
1342 .. exercise:: Add access control through data files in your module
1346 * Create a group *OpenAcademy / Manager* with full access to all
1348 * Make *Session* and *Course* readable by all users
1352 #. Create a new file ``openacademy/security/security.xml`` to
1353 hold the OpenAcademy Manager group
1354 #. Edit the file ``openacademy/security/ir.model.access.csv`` with
1355 the access rights to the models
1356 #. Finally update ``openacademy/__openerp__.py`` to add the new data
1364 A record rule restricts the access rights to a subset of records of the given
1365 model. A rule is a record of the model ``ir.rule``, and is associated to a
1366 model, a number of groups (many2many field), permissions to which the
1367 restriction applies, and a domain. The domain specifies to which records the
1368 access rights are limited.
1370 Here is an example of a rule that prevents the deletion of leads that are not
1371 in state ``cancel``. Notice that the value of the field ``groups`` must follow
1372 the same convention as the method ``write`` of the ORM.
1376 <record id="delete_cancelled_only" model="ir.rule">
1377 <field name="name">Only cancelled leads may be deleted</field>
1378 <field name="model_id" ref="crm.model_crm_lead"/>
1379 <field name="groups" eval="[(4, ref('base.group_sale_manager'))]"/>
1380 <field name="perm_read" eval="0"/>
1381 <field name="perm_write" eval="0"/>
1382 <field name="perm_create" eval="0"/>
1383 <field name="perm_unlink" eval="1" />
1384 <field name="domain_force">[('state','=','cancel')]</field>
1387 .. exercise:: Record rule
1389 Add a record rule for the model Course and the group
1390 "OpenAcademy / Manager", that restricts ``write`` and ``unlink`` accesses
1391 to the responsible of a course. If a course has no responsible, all users
1392 of the group must be able to modify it.
1396 Create a new rule in ``openacademy/security/security.xml``:
1403 Wizards describe interactive sessions with the user (or dialog boxes) through
1404 dynamic forms. A wizard is simply a model that extends the class
1405 :class:`~openerp.models.TransientModel` instead of
1406 :class:`~openerp.models.Model`. The class
1407 :class:`~openerp.models.TransientModel` extends :class:`~openerp.models.Model`
1408 and reuse all its existing mechanisms, with the following particularities:
1410 - Wizard records are not meant to be persistent; they are automatically deleted
1411 from the database after a certain time. This is why they are called
1413 - Wizard models do not require explicit access rights: users have all
1414 permissions on wizard records.
1415 - Wizard records may refer to regular records or wizard records through many2one
1416 fields, but regular records *cannot* refer to wizard records through a
1419 We want to create a wizard that allow users to create attendees for a particular
1420 session, or for a list of sessions at once.
1422 .. exercise:: Define the wizard
1424 Create a wizard model with a many2one relationship with the *Session*
1425 model and a many2many relationship with the *Partner* model.
1429 Add a new file ``openacademy/wizard.py``:
1436 Wizards are launched by ``ir.actions.act_window`` records, with the field
1437 ``target`` set to the value ``new``. The latter opens the wizard view into a
1438 popup window. The action may be triggered by a menu item.
1440 There is another way to launch the wizard: using an ``ir.actions.act_window``
1441 record like above, but with an extra field ``src_model`` that specifies in the
1442 context of which model the action is available. The wizard will appear in the
1443 contextual actions of the model, above the main view. Because of some internal
1444 hooks in the ORM, such an action is declared in XML with the tag ``act_window``.
1448 <act_window id="launch_the_wizard"
1449 name="Launch the Wizard"
1450 src_model="context_model_name"
1451 res_model="wizard_model_name"
1454 key2="client_action_multi"/>
1456 Wizards use regular views and their buttons may use the attribute
1457 ``special="cancel"`` to close the wizard window without saving.
1459 .. exercise:: Launch the wizard
1461 #. Define a form view for the wizard.
1462 #. Add the action to launch it in the context of the *Session* model.
1463 #. Define a default value for the session field in the wizard; use the
1464 context parameter ``self._context`` to retrieve the current session.
1470 .. exercise:: Register attendees
1472 Add buttons to the wizard, and implement the corresponding method for adding
1473 the attendees to the given session.
1479 .. exercise:: Register attendees to multiple sessions
1481 Modify the wizard model so that attendees can be registered to multiple
1488 Internationalization
1489 ====================
1491 Each module can provide its own translations within the i18n directory, by
1492 having files named LANG.po where LANG is the locale code for the language, or
1493 the language and country combination when they differ (e.g. pt.po or
1494 pt_BR.po). Translations will be loaded automatically by Odoo for all
1495 enabled languages. Developers always use English when creating a module, then
1496 export the module terms using Odoo's gettext POT export feature
1497 (:menuselection:`Settings --> Translations --> Import/Export --> Export
1498 Translation` without specifying a language), to create the module template POT
1499 file, and then derive the translated PO files. Many IDE's have plugins or modes
1500 for editing and merging PO/POT files.
1502 .. tip:: The GNU gettext format (Portable Object) used by Odoo is
1503 integrated into LaunchPad, making it an online collaborative
1504 translation platform.
1506 .. code-block:: text
1508 |- idea/ # The module directory
1509 |- i18n/ # Translation files
1510 | - idea.pot # Translation Template (exported from Odoo)
1511 | - fr.po # French translation
1512 | - pt_BR.po # Brazilian Portuguese translation
1517 By default Odoo's POT export only extracts labels inside XML files or
1518 inside field definitions in Python code, but any Python string can be
1519 translated this way by surrounding it with the function :func:`openerp._`
1520 (e.g. ``_("Label")``)
1522 .. exercise:: Translate a module
1524 Choose a second language for your Odoo installation. Translate your
1525 module using the facilities provided by Odoo.
1529 #. Create a directory ``openacademy/i18n/``
1530 #. Install whichever language you want (
1531 :menuselection:`Administration --> Translations --> Load an
1532 Official Translation`)
1533 #. Synchronize translatable terms (:menuselection:`Administration -->
1534 Translations --> Application Terms --> Synchronize Translations`)
1535 #. Create a template translation file by exporting (
1536 :menuselection:`Administration --> Translations -> Import/Export
1537 --> Export Translation`) without specifying a language, save in
1538 ``openacademy/i18n/``
1539 #. Create a translation file by exporting (
1540 :menuselection:`Administration --> Translations --> Import/Export
1541 --> Export Translation`) and specifying a language. Save it in
1542 ``openacademy/i18n/``
1543 #. Open the exported translation file (with a basic text editor or a
1544 dedicated PO-file editor e.g. POEdit_ and translate the missing
1547 #. Add ``from openerp import _`` to ``course.py`` and
1548 mark missing strings as translatable
1554 .. todo:: do we never reload translations?
1563 Odoo 8.0 comes with a new report engine based on :ref:`reference/qweb`,
1564 `Twitter Bootstrap`_ and Wkhtmltopdf_.
1566 A report is a combination two elements:
1568 * an ``ir.actions.report.xml``, for which a ``<report>`` shortcut element is
1569 provided, it sets up various basic parameters for the report (default
1570 type, whether the report should be saved to the database after generation,…)
1576 id="account_invoices"
1577 model="account.invoice"
1579 report_type="qweb-pdf"
1580 name="account.report_invoice"
1581 file="account.report_invoice"
1582 attachment_use="True"
1583 attachment="(object.state in ('open','paid')) and
1584 ('INV'+(object.number or '').replace('/','')+'.pdf')"
1587 * A standard :ref:`QWeb view <reference/views/qweb>` for the actual report:
1591 <t t-call="report.html_container">
1592 <t t-foreach="docs" t-as="o">
1593 <t t-call="report.external_layout">
1595 <h2>Report title</h2>
1601 the standard rendering context provides a number of elements, the most
1605 the records for which the report is printed
1607 the user printing the report
1609 Because reports are standard web pages, they are available through a URL and
1610 output parameters can be manipulated through this URL, for instance the HTML
1611 version of the *Invoice* report is available through
1612 http://localhost:8069/report/html/account.report_invoice/1 (if ``account`` is
1613 installed) and the PDF version through
1614 http://localhost:8069/report/pdf/account.report_invoice/1.
1616 .. exercise:: Create a report for the Session model
1618 For each session, it should display session's name, its start and end,
1619 and list the session's attendees.
1628 .. exercise:: Define a Dashboard
1630 Define a dashboard containing the graph view you created, the sessions
1631 calendar view and a list view of the courses (switchable to a form
1632 view). This dashboard should be available through a menuitem in the menu,
1633 and automatically displayed in the web client when the OpenAcademy main
1638 #. Create a file ``openacademy/views/session_board.xml``. It should contain
1639 the board view, the actions referenced in that view, an action to
1640 open the dashboard and a re-definition of the main menu item to add
1641 the dashboard action
1643 .. note:: Available dashboard styles are ``1``, ``1-1``, ``1-2``,
1644 ``2-1`` and ``1-1-1``
1646 #. Update ``openacademy/__openerp__.py`` to reference the new data
1654 The web-service module offer a common interface for all web-services :
1659 Business objects can also be accessed via the distributed object
1660 mechanism. They can all be modified via the client interface with contextual
1663 Odoo is accessible through XML-RPC/JSON-RPC interfaces, for which libraries
1664 exist in many languages.
1669 The following example is a Python program that interacts with an Odoo
1670 server with the library xmlrpclib.
1676 root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
1678 uid = xmlrpclib.ServerProxy(root + 'common').login(db, username, password)
1679 print "Logged in as %s (uid: %d)" % (USER, uid)
1682 sock = xmlrpclib.ServerProxy(root + 'object')
1684 'name' : 'Another idea',
1685 'description' : 'This is another idea of mine',
1688 idea_id = sock.execute(db, uid, password, 'idea.idea', 'create', args)
1690 .. exercise:: Add a new service to the client
1692 Write a Python program able to send XML-RPC requests to a PC running
1693 Odoo (yours, or your instructor's). This program should display all
1694 the sessions, and their corresponding number of seats. It should also
1695 create a new session for one of the courses.
1699 .. code-block:: python
1708 ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)
1711 uid = xmlrpclib.ServerProxy(ROOT + 'common').login(DB,USER,PASS)
1712 print "Logged in as %s (uid:%d)" % (USER,uid)
1714 call = functools.partial(
1715 xmlcprlib.ServerProxy(ROOT + 'object').execute,
1718 # 2. Read the sessions
1719 sessions = call('openacademy.session','search_read', [], ['name','seats'])
1720 for session in sessions :
1721 print "Session %s (%s seats)" % (session['name'], session['seats'])
1722 # 3.create a new session
1723 session_id = call('openacademy.session', 'create', {
1724 'name' : 'My session',
1728 Instead of using a hard-coded course id, the code can look up a course
1731 # 3.create a new session for the "Functional" course
1732 course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]
1733 session_id = call('openacademy.session', 'create', {
1734 'name' : 'My session',
1735 'course_id' : course_id,
1738 .. note:: there are also a number of high-level APIs in various languages to
1739 access Odoo systems without *explicitly* going through XML-RPC e.g.
1741 * https://github.com/akretion/ooor
1742 * https://github.com/syleam/openobject-library
1743 * https://github.com/nicolas-van/openerp-client-lib
1744 * https://pypi.python.org/pypi/oersted/
1746 .. [#autofields] it is possible to :attr:`disable the automatic creation of some
1747 fields <openerp.models.Model._log_access>`
1748 .. [#rawsql] writing raw SQL queries is possible, but requires care as it
1749 bypasses all Odoo authentication and security mechanisms.
1752 http://use-the-index-luke.com/sql/preface
1754 .. _POEdit: http://poedit.net
1756 .. _PostgreSQL's documentation:
1757 .. _table_constraint:
1758 http://www.postgresql.org/docs/9.3/static/ddl-constraints.html
1760 .. _python: http://python.org
1762 .. _XPath: http://w3.org/TR/xpath
1764 .. _twitter bootstrap: http://getbootstrap.com
1766 .. _wkhtmltopdf: http://wkhtmltopdf.org