1 .. queue:: backend/series
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 Both server and client extensions are packaged as *modules* which are
18 optionally loaded in a *database*.
20 Odoo modules can either add brand new business logic to an Odoo system, or
21 alter and extend existing business logic: a module can be created to add your
22 country's accounting rules to Odoo's generic accounting support, while the
23 next module adds support for real-time visualisation of a bus fleet.
25 Everything in Odoo thus starts and ends with modules.
27 Composition of a module
28 -----------------------
30 An Odoo module can contain a number of elements:
33 declared as Python classes, these resources are automatically persisted
34 by Odoo based on their configuration
37 XML or CSV files declaring metadata (views or workflows), configuration
38 data (modules parameterization), demonstration data and more
41 Handle requests from web browsers
44 Images, CSS or javascript files used by the web interface or website
49 Each module is a directory within a *module directory*. Module directories
50 are specified by using the :option:`--addons-path <odoo.py --addons-path>`
56 most command-line options can also be set using :ref:`a configuration
57 file <reference/cmdline/config>`
59 An Odoo module is declared by its :ref:`manifest <reference/module/manifest>`. It
60 is mandatory and contains a single python dictionary declaring various
61 metadata for the module: the module's name and description, list of Odoo
62 modules required for this one to work properly, references to data files, …
64 The manifest's general structure is::
70 'author': "Author Name",
71 'category': 'Category',
75 # data files always loaded at installation
79 # data files containing optionally loaded demonstration data
86 `Python package <http://docs.python.org/2/tutorial/modules.html#packages>`_
87 with a ``__init__.py`` file, containing import instructions for various Python
90 For instance, if the module has a single ``mymodule.py`` file ``__init__.py``
95 .. exercise:: Module creation
97 Create an empty module Open Academy, install it in Odoo.
101 #. Create a new folder ``openacademy``
102 #. Create an empty ``openacademy/__init__.py`` file
103 #. Create an ``openacademy/__openerp__.py`` file
107 Object-Relational Mapping
108 -------------------------
110 A key component of Odoo is the :abbr:`ORM (Object-Relational Mapping)` layer.
111 This layer avoids having to write most :abbr:`SQL (Structured Query Language)`
112 by hand and provides extensibility and security services\ [#rawsql]_.
114 Business objects are declared as Python classes extending
115 :class:`~openerp.models.Model` which integrates them into the automated
118 Models can be configured by setting a number of attributes at their
119 definition. The most important attribute is
120 :attr:`~openerp.models.Model._name` which is required and defines the name for
121 the model in the Odoo system. Here is a minimally complete definition of a
124 from openerp import models
125 class MinimalModel(models.Model):
131 Fields are used to define what the model can store and where. Fields are
132 defined as attributes on the model class::
134 from openerp import models, fields
136 class LessMinimalModel(models.Model):
137 _name = 'test.model2'
144 Much like the model itself, its fields can be configured, by passing
145 configuration attributes as parameters::
147 name = field.Char(required=True)
149 Some attributes are available on all fields, here are the most common ones:
151 :attr:`~openerp.fields.Field.string` (``unicode``, default: field's name)
152 The label of the field in UI (visible by users).
153 :attr:`~openerp.fields.Field.required` (``bool``, default: ``False``)
154 If ``True``, the field can not be empty, it must either have a default
155 value or always be given a value when creating a record.
156 :attr:`~openerp.fields.Field.help` (``unicode``, default: ``''``)
157 Long-formm, provides a help tooltip to users in the UI.
158 :attr:`~openerp.fields.Field.index` (``bool``, default: ``False``)
159 Requests that Odoo create a `database index`_ on the column
164 There are two broad categories of fields: "simple" fields which are atomic
165 values stored directly in the model's table and "relational" fields linking
166 records (of the same model or of different models).
168 Example of simple fields are :class:`~openerp.fields.Boolean`,
169 :class:`~openerp.fields.Date`, :class:`~openerp.fields.Char`.
174 Odoo creates a few fields in all models\ [#autofields]_. These fields are
175 managed by the system and shouldn't be written to. They can be read if
178 :attr:`~openerp.fields.Model.id` (:class:`~openerp.fields.Id`)
179 the unique identifier for a record in its model
180 :attr:`~openerp.fields.Model.create_date` (:class:`~openerp.fields.Datetime`)
181 creation date of the record
182 :attr:`~openerp.fields.Model.create_uid` (:class:`~openerp.fields.Many2one`)
183 user who created the record
184 :attr:`~openerp.fields.Model.write_date` (:class:`~openerp.fields.Datetime`)
185 last modification date of the record
186 :attr:`~openerp.fields.Model.write_uid` (:class:`~openerp.fields.Many2one`)
187 user who last modified the record
192 By default, Odoo also requires a ``name`` field on all models for various
193 display and search behaviors. The field used for these purposes can be
194 overridden by setting :attr:`~openerp.models.Model._rec_name`.
196 .. exercise:: Define a model
198 Define a new data model *Course* in the *openacademy* module. A course
199 has a title and a description. Courses must have a title.
203 #. Create a new file ``openacademy/course.py``
204 #. Edit ``openacademy/__init__.py`` to import it
211 Odoo is a highly data driven system. Although behavior is customized using
212 Python_ code part of a module's value is in the data it sets up when loaded.
214 .. tip:: some modules exist solely to add data into Odoo
217 Module data is declared via :ref:`data files <reference/data>`, XML files with
218 ``<record>`` elements. Each ``<record>`` element creates or updates a database
225 <record model="{model name}" id="{record identifier}">
226 <field name="{a field name}">{a value}</field>
231 * ``model`` is the name of the Odoo model for the record
232 * ``id`` is an :term:`external identifier`, it allows referring to the record
233 (without having to know its in-database identifier)
234 * ``<field>`` elements have a ``name`` which is the name of the field in the
235 model (e.g. ``description``). Their body is the field's value.
237 Data files have to be declared in the manifest file to be loaded, they can
238 be declared in the ``'data'`` list (always loaded) or in the ``'demo'`` list
239 (only loaded in demonstration mode).
241 .. exercise:: Define demonstration data
243 Create demonstration data filling the *Courses* model with a few
244 demonstration courses.
248 #. Create a new file ``openacademy/demo.xml``
249 #. Add the file to the ``'demo'`` list of your ``__openerp__.py``
256 Actions and menus are regular records in database, usually declared through
257 data files. Actions can be triggered in three ways:
259 #. by clicking on menu items (linked to specific actions)
260 #. by clicking on buttons in views (if these are connected to actions)
261 #. as contextual actions on object
263 Because menus are somewhat complex to declare there is a ``<menuitem>``
264 shortcut to declare an ``ir.ui.menu`` and connect it to the corresponding
269 <record model="ir.actions.act_window" id="action_list_ideas">
270 <field name="name">Ideas</field>
271 <field name="res_model">idea.idea</field>
272 <field name="view_mode">tree,form</field>
274 <menuitem id="menu_ideas" parent="menu_root" name="Ideas" sequence="10"
275 action="action_list_ideas"/>
280 The action must be declared before its corresponding menu in the XML file.
282 Data files are executed sequentially, the action's ``id`` must be present
283 in the database before the menu can be created.
285 .. exercise:: Define new menu entries
287 Define new menu entries to access courses and sessions under the
288 OpenAcademy menu entry. A user should be able to
290 - display a list of all the courses
291 - create/modify courses
295 #. Create ``openacademy/views/openacademy.xml`` with an action and
296 the menus triggering the action
297 #. Add it to the ``data`` list of ``openacademy/__openerp__.py``
304 Views define the way the records of a model are displayed. Each type of view
305 represents a mode of visualization (a list of records, a graph of their
306 aggregation, …). Views can either be requested generically via their type
307 (e.g. *a list of partners*) or specifically via their id. For generic
308 requests, the view with the correct type and the lowest priority will be
309 used (so the lowest-priority view of each type is the default view for that
312 :ref:`View inheritance <reference/views/inheritance>` allows altering views
313 declared elsewhere (adding or removing content).
315 Generic view declaration
316 ------------------------
318 A view is declared as a record of the model ``ir.ui.view``. The view type
319 is implied by the root element of the ``arch`` field:
323 <record model="ir.ui.view" id="view_id">
324 <field name="name">view.name</field>
325 <field name="model">object_name</field>
326 <field name="priority" eval="16"/>
327 <field name="arch" type="xml">
328 <!-- view content: <form>, <tree>, <graph>, ... -->
332 .. danger:: The view's content is XML.
335 The ``arch`` field must thus be declared as ``type="xml"`` to be parsed
341 Tree views, also called list views, display records in a tabular form.
343 Their root element is ``<tree>``. The simplest form of the tree view simply
344 lists all the fields to display in the table (each field as a column):
348 <tree string="Idea list">
350 <field name="inventor_id"/>
356 Forms are used to create and edit single records.
359 Their root element is ``<form>``. They composed of high-level structure
360 elements (groups, notebooks) and interactive elements (buttons and fields):
364 <form string="Idea form">
366 <group colspan="2" col="2">
367 <separator string="General stuff" colspan="2"/>
369 <field name="inventor_id"/>
372 <group colspan="2" col="2">
373 <separator string="Dates" colspan="2"/>
374 <field name="active"/>
375 <field name="invent_date" readonly="1"/>
378 <notebook colspan="4">
379 <page string="Description">
380 <field name="description" nolabel="1"/>
384 <field name="state"/>
388 .. exercise:: Customise form view using XML
390 Create your own form view for the Course object. Data displayed should be:
391 the name and the description of the course.
397 .. exercise:: Notebooks
399 In the Course form view, put the description field under a tab, such that
400 it will be easier to add other tabs later, containing additional
405 Modify the Course form view as follows:
409 Form views can also use plain HTML for more flexible layouts:
413 <form string="Idea Form">
415 <button string="Confirm" type="object" name="action_confirm"
416 states="draft" class="oe_highlight" />
417 <button string="Mark as done" type="object" name="action_done"
418 states="confirmed" class="oe_highlight"/>
419 <button string="Reset to draft" type="object" name="action_draft"
420 states="confirmed,done" />
421 <field name="state" widget="statusbar"/>
424 <div class="oe_title">
425 <label for="name" class="oe_edit_only" string="Idea Name" />
426 <h1><field name="name" /></h1>
428 <separator string="General" colspan="2" />
429 <group colspan="2" col="2">
430 <field name="description" placeholder="Idea description..." />
438 Search views customize the search field associated with the list view (and
439 other aggregated views). Their root element is ``<search>`` and they're
440 composed of fields defining which fields can be searched on:
446 <field name="inventor_id"/>
449 If no search view exists for the model, Odoo generates one which only allows
450 searching on the ``name`` field.
452 .. exercise:: Search courses
454 Allow searching for courses based on their title or their description.
460 Relations between models
461 ========================
463 A record from a model may be related to a record from another model. For
464 instance, a sale order record is related to a client record that contains the
465 client data; it is also related to its sale order line records.
467 .. exercise:: Create a session model
469 For the module Open Academy, we consider a model for *sessions*: a session
470 is an occurrence of a course taught at a given time for a given audience.
472 Create a model for *sessions*. A session has a name, a start date, a
473 duration and a number of seats. Add an action and a menu item to display
478 Create class *Session*:
482 .. note:: ``digits=(6, 2)`` specifies the precision of a float number:
483 6 is the total number of digits, while 2 is the number of
484 digits after the comma. Note that it results in the number
485 digits before the comma is a maximum 4
490 Relational fields link records, either of the same model (hierarchies) or
491 between different models.
493 Relational field types are:
495 :class:`Many2one(other_model, ondelete='set null') <openerp.fields.Many2one>`
496 A simple link to an other object::
498 print foo.other_id.name
500 .. seealso:: `foreign keys <http://www.postgresql.org/docs/9.3/static/tutorial-fk.html>`_
502 :class:`One2many(other_model, related_field) <openerp.fields.One2many>`
503 A virtual relationship, inverse of a :class:`~openerp.fields.Many2one`.
504 A :class:`~openerp.fields.One2many` behaves as a container of records,
505 accessing it results in a (possibly empty) set of records::
507 for other in foo.other_ids:
512 Because a :class:`~openerp.fields.One2many` is a virtual relationship,
513 there *must* be a :class:`~openerp.fields.Many2one` field in the
514 :samp:`{other_model}`, and its name *must* be :samp:`{related_field}`
516 :class:`Many2many(other_model) <openerp.fields.Many2many>`
517 Bidirectional multiple relationship, any record on one side can be related
518 to any number of records on the other side. Behaves as a container of
519 records, accessing it also results in a possibly empty set of records::
521 for other in foo.other_ids:
524 .. exercise:: Many2one relations
526 Using a many2one, modify the *Course* and *Session* models to reflect their
527 relation with other models:
529 - A course has a *responsible* user; the value of that field is a record of
530 the built-in model ``res.users``.
531 - A session has an *instructor*; the value of that field is a record of the
532 built-in model ``res.partner``.
533 - A session is related to a *course*; the value of that field is a record
534 of the model ``openacademy.course`` and is required.
538 #. Add the relevant ``Many2one`` fields to the models, and
539 #. add access to the session object in
540 ``openacademy/view/openacademy.xml``.
544 .. exercise:: Inverse one2many relations
546 Using the inverse relational field one2many, modify the models to reflect
547 the relation between courses and sessions.
551 Modify the ``Course`` class as follows:
555 .. exercise:: Multiple many2many relations
557 Using the relational field many2many, modify the *Session* model to relate
558 every session to a set of *attendees*. Attendees will be represented by
559 partner records, so we will relate to the built-in model ``res.partner``.
563 Modify the ``Session`` class as follows:
567 .. exercise:: Views modification
569 For the *Course* model,
571 * the name and instructor for the course should be displayed in the tree
573 * the form view should display the course name and responsible at
574 the top, followed by the course description in a tab and the course
575 sessions in a second tab
577 For the *Session* model,
579 * the name of the session and the session course should be displayed in
581 * the form view should display all the session's fields
583 Try to lay out the form views so that they're clear and readable.
595 Odoo provides two *inheritance* mechanisms to extend an existing model in a
598 The first inheritance mechanism allows a module to modify the behavior of a
599 model defined in another module:
601 - add fields to a model,
602 - override the definition of fields on a model,
603 - add constraints to a model,
604 - add methods to a model,
605 - override existing methods on a model.
607 The second inheritance mechanism (delegation) allows to link every record of a
608 model to a record in a parent model, and provides transparent access to the
609 fields of the parent record.
611 .. image:: backend/inheritance_methods.png
616 * :attr:`~openerp.models.Model._inherit`
617 * :attr:`~openerp.models.Model._inherits`
622 Instead of modifying existing views in place (by overwriting them), Odoo
623 provides view inheritance where children "extension" views are applied on top of
624 root views, and can add or remove content from their parent.
626 An extension view references its parent using the ``inherit_id`` field, and
627 instead of a single view its ``arch`` field is composed of any number of
628 ``xpath`` elements selecting and altering the content of their parent view:
632 <!-- improved idea categories list -->
633 <record id="idea_category_list2" model="ir.ui.view">
634 <field name="name">id.category.list2</field>
635 <field name="model">ir.ui.view</field>
636 <field name="inherit_id" ref="id_category_list"/>
637 <field name="arch" type="xml">
638 <!-- find field description inside tree, and add the field
639 idea_ids after it -->
640 <xpath expr="/tree/field[@name='description']" position="after">
641 <field name="idea_ids" string="Number of ideas"/>
647 An XPath_ expression selecting a single element in the parent view.
648 Raises an error if it matches no element or more than one
650 Operation to apply to the matched element:
653 appends ``xpath``'s body at the end of the matched element
655 replaces the matched element by the ``xpath``'s body
657 inserts the ``xpath``'s body as a sibling before the matched element
659 inserts the ``xpaths``'s body as a sibling after the matched element
661 alters the attributes of the matched element using special
662 ``attribute`` elements in the ``xpath``'s body
664 .. exercise:: Alter existing content
666 * Using model inheritance, modify the existing *Partner* model to add an
667 ``instructor`` boolean field
668 * Using view inheritance, display this fields in the partner form view
674 This is the opportunity to introduce the developer mode to
675 inspect the view, find its external ID and the place to put the
678 #. Create a ``openacademy/partner.py`` and import it in
680 #. Create an ``openacademy/views/partner.xml`` and add it to
688 In Odoo, :ref:`reference/orm/domains` are values that encode conditions on
689 records. A domain is a list of criteria used to select a subset of a model's
690 records. Each criteria is a triple with a field name, an operator and a value.
692 For instance, when used on the *Product* model the following domain selects
693 all *services* with a unit price over *1000*::
695 [('product_type', '=', 'service'), ('unit_price', '>', 1000)]
697 By default criteria are combined with an implicit AND. The logical operators
698 ``&`` (AND), ``|`` (OR) and ``!`` (NOT) can be used to explicitly combine
699 criteria. They are used in prefix position (the operator is inserted before
700 its arguments rather than between). For instance to select products "which are
701 services *OR* have a unit price which is *NOT* between 1000 and 2000"::
704 ('product_type', '=', 'service'),
706 ('unit_price', '>=', 1000),
707 ('unit_price', '<', 2000)]
709 A ``domain`` parameter can be added to relational fields to limit valid
710 records for the relation when trying to select records in the client interface.
712 .. exercise:: Domains on relational fields
714 When selecting the instructor for a *Session*, only instructors (partners
715 with ``instructor`` set to ``True``) should be visible.
723 A domain declared as a literal list is evaluated server-side and
724 can't refer to dynamic values on the right-hand side, a domain
725 declared as a string is evaluated client-side and allows
726 field names on the right-hand side
728 .. exercise:: More complex domains
730 Create new partner categories *Teacher / Level 1* and *Teacher / Level 2*.
731 The instructor for a session can be either an instructor or a teacher
736 #. Modify the *Session* model's domain
737 #. Modify ``openacademy/view/partner.xml`` to get access to
738 *Partner categories*:
742 Computed fields and default values
743 ==================================
745 So far fields have been stored directly in and retrieved directly from the
746 database. Fields can also be *computed*. In that case, the field's value is not
747 retrieved from the database but computed on-the-fly by calling a method of the
750 To create a computed field, create a field and set its attribute
751 :attr:`~openerp.fields.Field.compute` to the name of a method. The computation
752 method should simply set the value of the field to compute on every record in
755 .. danger:: ``self`` is a collection
758 The object ``self`` is a *recordset*, i.e., an ordered collection of
759 records. It supports the standard Python operations on collections, like
760 ``len(self)`` and ``iter(self)``, plus extra set operations like ``recs1 +
763 Iterating over ``self`` gives the records one by one, where each record is
764 itself a collection of size 1. You can access/assign fields on single
765 records by using the dot notation, like ``record.name``.
767 .. code-block:: python
770 from openerp import models, fields
772 class ComputedModel(models.Model):
773 _name = 'test.computed'
775 name = fields.Char(compute='_compute_name')
777 def _compute_name(self):
779 record.name = str(random.randint(1, 1e6))
781 Our compute method is very simple: it loops over ``self`` and performs the same
782 operation on every record. We can make it slightly simpler by using the
783 decorator :func:`~openerp.api.one` to automatically loop on the collection::
786 def _compute_name(self):
787 self.name = str(random.randint(1, 1e6))
792 The value of a computed field usually depends on the values of other fields on
793 the computed record. The ORM expects the developer to specify those dependencies
794 on the compute method with the decorator :func:`~openerp.api.depends`.
795 The given dependencies are used by the ORM to trigger the recomputation of the
796 field whenever some of its dependencies have been modified::
798 from openerp import models, fields, api
800 class ComputedModel(models.Model):
801 _name = 'test.computed'
803 name = fields.Char(compute='_compute_name')
804 value = fields.Integer()
807 @api.depends('value')
808 def _compute_name(self):
809 self.name = "Record with value %s" % self.value
811 .. exercise:: Computed fields
813 * Add the percentage of taken seats to the *Session* model
814 * Display that field in the tree and form views
815 * Display the field as a progress bar
819 #. Add a computed field to *Session*
820 #. Show the field in the *Session* view:
827 Any field can be given a default value. In the field definition, add the option
828 ``default=X`` where ``X`` is either a Python literal value (boolean, integer,
829 float, string), or a function taking a recordset and returning a value::
831 name = fields.Char(default="Unknown")
832 user_id = fields.Many2one('res.users', default=lambda self: self.env.user)
834 .. exercise:: Active objects – Default values
836 * Define the start_date default value as today (see
837 :class:`~openerp.fields.Date`).
838 * Add a field ``active`` in the class Session, and set sessions as active by
847 Odoo has built-in rules making fields with an ``active`` field set
848 to ``False`` invisible.
853 The "onchange" mechanism provides a way for the client interface to update a
854 form whenever the user has filled in a value in a field, without saving anything
857 For instance, suppose a model has three fields ``amount``, ``unit_price`` and
858 ``price``, and you want to update the price on the form when any of the other
859 fields is modified. To achieve this, define a method where ``self`` represents
860 the record in the form view, and decorate it with :func:`~openerp.api.onchange`
861 to specify on which field it has to be triggered. Any change you make on
862 ``self`` will be reflected on the form.
866 <!-- content of form view -->
867 <field name="amount"/>
868 <field name="unit_price"/>
869 <field name="price" readonly="1"/>
871 .. code-block:: python
874 @api.onchange('amount', 'unit_price')
875 def _onchange_price(self):
876 # set auto-changing field
877 self.price = self.amount * self.unit_price
878 # Can optionally return a warning and domains
881 'title': "Something bad happened",
882 'message': "It was very bad indeed",
886 For computed fields, valued ``onchange`` behavior is built-in as can be seen by
887 playing with the *Session* form: change the number of seats or participants, and
888 the ``taken_seats`` progressbar is automatically updated.
890 .. exercise:: Warning
892 Add an explicit onchange to warn about invalid values, like a negative
893 number of seats, or more participants than seats.
902 Odoo provides two ways to set up automatically verified invariants:
903 :func:`Python constraints <openerp.api.constrains>` and
904 :attr:`SQL constaints <openerp.models.Model._sql_constraints>`.
906 A Python constraint is defined as a method decorated with
907 :func:`~openerp.api.constrains`, and invoked on a recordset. The decorator
908 specifies which fields are involved in the constraint, so that the constraint is
909 automatically evaluated when one of them is modified. The method is expected to
910 raise an exception if its invariant is not satisfied::
912 from openerp.exceptions import ValidationError
914 @api.constrains('age')
915 def _check_something(self):
918 raise ValidationError("Your record is too old: %s" % record.age)
919 # all records passed the test, don't return anything
921 .. exercise:: Add Python constraints
923 Add a constraint that checks that the instructor is not present in the
924 attendees of his/her own session.
930 SQL constraints are defined through the model attribute
931 :attr:`~openerp.models.Model._sql_constraints`. The latter is assigned to a list
932 of triples of strings ``(name, sql_definition, message)``, where ``name`` is a
933 valid SQL constraint name, ``sql_definition`` is a table_constraint_ expression,
934 and ``message`` is the error message.
936 .. exercise:: Add SQL constraints
938 With the help of `PostgreSQL's documentation`_ , add the following
941 #. CHECK that the course description and the course title are different
942 #. Make the Course's name UNIQUE
948 .. exercise:: Exercise 6 - Add a duplicate option
950 Since we added a constraint for the Course name uniqueness, it is not
951 possible to use the "duplicate" function anymore (:menuselection:`Form -->
954 Re-implement your own "copy" method which allows to duplicate the Course
955 object, changing the original name into "Copy of [original name]".
967 Tree views can take supplementary attributes to further customize their
971 mappings of colors to conditions. If the condition evaluates to ``True``,
972 the corresponding color is applied to the row:
976 <tree string="Idea Categories" colors="blue:state=='draft';red:state=='trashed'">
978 <field name="state"/>
981 Clauses are separated by ``;``, the color and condition are separated by
985 Either ``"top"`` or ``"bottom"``. Makes the tree view editable in-place
986 (rather than having to go through the form view), the value is the
987 position where new rows appear.
989 .. exercise:: List coloring
991 Modify the Session tree view in such a way that sessions lasting less than
992 5 days are colored blue, and the ones lasting more than 15 days are
997 Modify the session tree view:
1004 Displays records as calendar events. Their root element is ``<calendar>`` and
1005 their most common attributes are:
1008 The name of the field used for *color segmentation*. Colors are
1009 automatically distributed to events, but events in the same color segment
1010 (records which have the same value for their ``@color`` field) will be
1011 given the same color.
1013 record's field holding the start date/time for the event
1014 ``date_stop`` (optional)
1015 record's field holding the end date/time for the event
1017 field (to define the label for each calendar event)
1021 <calendar string="Ideas" date_start="invent_date" color="inventor_id">
1022 <field name="name"/>
1025 .. exercise:: Calendar view
1027 Add a Calendar view to the *Session* model enabling the user to view the
1028 events associated to the Open Academy.
1032 #. Add an ``end_date`` field computed from ``start_date`` and
1035 .. tip:: the inverse function makes the field writable, and allows
1036 moving the sessions (via drag and drop) in the calendar view
1038 #. Add a calendar view to the *Session* model
1039 #. And add the calendar view to the *Session* model's actions
1046 Search view fields can take custom operators or :ref:`reference/orm/domains`
1047 for more flexible matching of results.
1049 Search views can also contain *filters* which act as toggles for predefined
1050 searches (defined using :ref:`reference/orm/domains`):
1054 <search string="Ideas">
1055 <filter name="my_ideas" domain="[('inventor_id','=',uid)]"
1056 string="My Ideas" icon="terp-partner"/>
1057 <field name="name"/>
1058 <field name="description"/>
1059 <field name="inventor_id"/>
1060 <field name="country_id" widget="selection"/>
1063 To use a non-default search view in an action, it should be linked using the
1064 ``search_view_id`` field of the action record.
1066 The action can also set default values for search fields through its
1067 ``context`` field: context keys of the form
1068 :samp:`search_default_{field_name}` will initialize *field_name* with the
1069 provided value. Search filters must have an optional ``@name`` to have a
1070 default and behave as booleans (they can only be enabled by default).
1072 .. exercise:: Search views
1074 Add a button to filter the courses for which the current user is the
1075 responsible in the course search view. Make it selected by default.
1084 Horizontal bar charts typically used to show project planning and advancement,
1085 their root element is ``<gantt>``.
1089 <gantt string="Ideas" date_start="invent_date" color="inventor_id">
1090 <level object="idea.idea" link="id" domain="[]">
1091 <field name="inventor_id"/>
1095 .. exercise:: Gantt charts
1097 Add a Gantt Chart enabling the user to view the sessions scheduling linked
1098 to the Open Academy module. The sessions should be grouped by instructor.
1102 #. Create a computed field expressing the session's duration in hours
1103 #. Add the gantt view's definition, and add the gantt view to the
1104 *Session* model's action
1111 Graph views allow aggregated overview and analysis of models, their root
1112 element is ``<graph>``.
1114 Graph views have 4 display modes, the default mode is selected using the
1115 ``@type`` attribute.
1118 a multidimensional table, allows the selection of filers and dimensions
1119 to get the right aggregated dataset before moving to a more graphical
1122 a bar chart, the first dimension is used to define groups on the
1123 horizontal axis, other dimensions define aggregated bars within each group.
1125 By default bars are side-by-side, they can be stacked by using
1126 ``@stacked="True"`` on the ``<graph>``
1128 2-dimensional line chart
1132 Graph views contain ``<field>`` with a mandatory ``@type`` attribute taking
1136 the field should be aggregated by default
1138 the field should be aggregated rather than grouped on
1142 <graph string="Total idea score by Inventor">
1143 <field name="inventor_id"/>
1144 <field name="score" type="measure"/>
1149 Graph views perform aggregations on database values, they do not work
1150 with non-stored computed fields.
1152 .. exercise:: Graph view
1154 Add a Graph view in the Session object that displays, for each course, the
1155 number of attendees under the form of a bar chart.
1159 #. Add the number of attendees as a stored computed field
1160 #. Then add the relevant view
1167 Used to organize tasks, production processes, etc… their root element is
1170 A kanban view shows a set of cards possibly grouped in columns. Each card
1171 represents a record, and each column the values of an aggregation field.
1173 For instance, project tasks may be organized by stage (each column is a
1174 stage), or by responsible (each column is a user), and so on.
1176 Kanban views define the structure of each card as a mix of form elements
1177 (including basic HTML) and :ref:`reference/qweb`.
1179 .. exercise:: Kanban view
1181 Add a Kanban view that displays sessions grouped by course (columns are
1186 #. Add an integer ``color`` field to the *Session* model
1187 #. Add the kanban view and update the action
1194 Workflows are models associated to business objects describing their dynamics.
1195 Workflows are also used to track processes that evolve over time.
1197 .. exercise:: Almost a workflow
1199 Add a ``state`` field to the *Session* model. It will be used to define
1202 A sesion can have three possible states: Draft (default), Confirmed and
1205 In the session form, add a (read-only) field to
1206 visualize the state, and buttons to change it. The valid transitions are:
1215 #. Add a new ``state`` field
1216 #. Add state-transitioning methods, those can be called from view
1217 buttons to change the record's state
1218 #. And add the relevant buttons to the session's form view
1222 Workflows may be associated with any object in Odoo, and are entirely
1223 customizable. Workflows are used to structure and manage the lifecycles of
1224 business objects and documents, and define transitions, triggers, etc. with
1225 graphical tools. Workflows, activities (nodes or actions) and transitions
1226 (conditions) are declared as XML records, as usual. The tokens that navigate
1227 in workflows are called workitems.
1229 .. exercise:: Workflow
1231 Replace the ad-hoc *Session* workflow by a real workflow. Transform the
1232 *Session* form view so its buttons call the workflow instead of the
1241 A workflow associated with a model is only created when the
1242 model's records are created. Thus there is no workflow instance
1243 associated with session instances created before the workflow's
1248 In order to check if instances of the workflow are correctly
1249 created alongside sessions, go to :menuselection:`Settings -->
1250 Technical --> Workflows --> Instances`
1254 .. exercise:: Automatic transitions
1256 Automatically transition sessions from *Draft* to *Confirmed* when more
1257 than half the session's seats are reserved.
1263 .. exercise:: Server actions
1265 Replace the Python methods for synchronizing session state by
1268 Both the workflow and the server actions could have been created entirely
1278 Access control mechanisms must be configured to achieve a coherent security
1281 Group-based access control mechanisms
1282 -------------------------------------
1284 Groups are created as normal records on the model “res.groups”, and granted
1285 menu access via menu definitions. However even without a menu, objects may
1286 still be accessible indirectly, so actual object-level permissions (read,
1287 write, create, unlink) must be defined for groups. They are usually inserted
1288 via CSV files inside modules. It is also possible to restrict access to
1289 specific fields on a view or object using the field's groups attribute.
1294 Access rights are defined as records of the model “ir.model.access”. Each
1295 access right is associated to a model, a group (or no group for global
1296 access), and a set of permissions: read, write, create, unlink. Such access
1297 rights are usually created by a CSV file named after its model:
1298 ``ir.model.access.csv``.
1300 .. code-block:: text
1302 id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
1303 access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0
1304 access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0
1306 .. exercise:: Add access control through the OpenERP interface
1308 Create a new user "John Smith". Then create a group
1309 "OpenAcademy / Session Read" with read access to the *Session* model.
1313 #. Create a new user *John Smith* through
1314 :menuselection:`Settings --> Users --> Users`
1315 #. Create a new group ``session_read`` through
1316 :menuselection:`Settings --> Users --> Groups`, it should have
1317 read access on the *Session* model
1318 #. Edit *John Smith* to make them a member of ``session_read``
1319 #. Log in as *John Smith* to check the access rights are correct
1321 .. exercise:: Add access control through data files in your module
1325 * Create a group *OpenAcademy / Manager* with full access to all
1327 * Make *Session* and *Course* readable by all users
1331 #. Create a new file ``openacademy/security/security.xml`` to
1332 hold the OpenAcademy Manager group
1333 #. Create a new file ``openacademy/security/ir.model.access.csv`` with
1334 the access rights to the models
1335 #. finally update ``openacademy/__openerp__.py`` to add the new data
1343 A record rule restricts the access rights to a subset of records of the given
1344 model. A rule is a record of the model “ir.rule”, and is associated to a
1345 model, a number of groups (many2many field), permissions to which the
1346 restriction applies, and a domain. The domain specifies to which records the
1347 access rights are limited.
1349 Here is an example of a rule that prevents the deletion of leads that are not
1350 in state “cancel”. Notice that the value of the field “groups” must follow
1351 the same convention as the method “write” of the ORM.
1355 <record id="delete_cancelled_only" model="ir.rule">
1356 <field name="name">Only cancelled leads may be deleted</field>
1357 <field name="model_id" ref="crm.model_crm_lead"/>
1358 <field name="groups" eval="[(4, ref('base.group_sale_manager'))]"/>
1359 <field name="perm_read" eval="0"/>
1360 <field name="perm_write" eval="0"/>
1361 <field name="perm_create" eval="0"/>
1362 <field name="perm_unlink" eval="1" />
1363 <field name="domain_force">[('state','=','cancel')]</field>
1366 .. exercise:: Record rule
1368 Add a record rule for the model Course and the group
1369 "OpenAcademy / Manager", that restricts ``write`` and ``unlink`` accesses
1370 to the responsible of a course. If a course has no responsible, all users
1371 of the group must be able to modify it.
1375 Create a new rule in ``openacademy/security/security.xml``:
1379 Internationalization
1380 ====================
1382 Each module can provide its own translations within the i18n directory, by
1383 having files named LANG.po where LANG is the locale code for the language, or
1384 the language and country combination when they differ (e.g. pt.po or
1385 pt_BR.po). Translations will be loaded automatically by Odoo for all
1386 enabled languages. Developers always use English when creating a module, then
1387 export the module terms using Odoo's gettext POT export feature
1388 (Settings>Translations>Export a Translation File without specifying a
1389 language), to create the module template POT file, and then derive the
1390 translated PO files. Many IDE's have plugins or modes for editing and merging
1393 .. tip:: The GNU gettext format (Portable Object) used by Odoo is
1394 integrated into LaunchPad, making it an online collaborative
1395 translation platform.
1397 .. code-block:: text
1399 |- idea/ # The module directory
1400 |- i18n/ # Translation files
1401 | - idea.pot # Translation Template (exported from Odoo)
1402 | - fr.po # French translation
1403 | - pt_BR.po # Brazilian Portuguese translation
1408 By default Odoo's POT export only extracts labels inside XML files or
1409 inside field definitions in Python code, but any Python string can be
1410 translated this way by surrounding it with the tools.translate._ method
1413 .. exercise:: Translate a module
1415 Choose a second language for your Odoo installation. Translate your
1416 module using the facilities provided by Odoo.
1420 #. Create a directory ``openacademy/i18n/``
1421 #. Install whichever language you want (
1422 :menuselection:`Administration --> Translations --> Load an
1423 Official Translation`)
1424 #. Synchronize translatable terms (:menuselection`Administration -->
1425 Translations --> Application termsn --> Synchronize Translations`)
1426 #. Create a template translation file by exporting (
1427 :menuselection:`Administration --> Translations -> Import/Export
1428 --> Export Translation`) without specifying a language, save in
1429 ``openacademy/i18n/``
1430 #. Create a translation file by exporting (
1431 :menuselection:`Administration --> Translations --> Import/Export
1432 --> Export Translation`) and specifying a language. Save it in
1433 ``openacademy/i18n/``
1434 #. Open the exported translation file (with a basic text editor or a
1435 dedicated PO-file editor e.g. POEdit_ and translate the missing
1440 By default, Odoo's export only extracts labels inside XML
1441 records or Python field definitions, but arbitrary Python
1442 strings can be marked as translatable by calling
1443 :func:`openerp._` with them e.g. ``_("Label")``)
1445 #. Add ``from openerp import _`` to ``course.py`` and
1446 mark missing strings as translatable
1448 .. todo:: there isn't any!
1452 .. todo:: do we never reload translations?
1461 Odoo v8 comes with a new report engine based on :ref:`reference/qweb`,
1462 `Twitter Bootstrap`_ and Wkhtmltopdf_.
1464 A report is a combination two elements:
1466 * an ``ir.actions.report.xml``, for which a ``<report>`` shortcut element is
1467 provided, it sets up various basic parameters for the report (default
1468 type, whether the report should be saved to the database after generation,…)
1474 id="account_invoices"
1475 model="account.invoice"
1477 report_type="qweb-pdf"
1478 name="account.report_invoice"
1479 file="account.report_invoice"
1480 attachment_use="True"
1481 attachment="(object.state in ('open','paid')) and
1482 ('INV'+(object.number or '').replace('/','')+'.pdf')"
1485 * A standard :ref:`QWeb view <reference/views/qweb>` for the actual report:
1489 <t t-call="report.html_container">
1490 <t t-foreach="docs" t-as="o">
1491 <t t-call="report.external_layout">
1493 <h2>Report title</h2>
1499 the standard rendering context provides a number of elements, the most
1503 the records for which the report is printed
1505 the user printing the report
1507 Because reports are standard web pages, they are available through a URL and
1508 output parameters can be manipulated through this URL, for instance the HTML
1509 version of the *Invoice* report is available through
1510 http://localhost:8069/report/html/account.report_invoice/1 (if ``account`` is
1511 installed) and the PDF version through
1512 http://localhost:8069/report/pdf/account.report_invoice/1.
1514 .. exercise:: Create a report for the Session model
1516 For each session, it should display session's name, its start and end,
1517 and list the session's attendees.
1526 .. exercise:: Define a Dashboard
1528 Define a dashboard containing the graph view you created, the sessions
1529 calendar view and a list view of the courses (switchable to a form
1530 view). This dashboard should be available through a menuitem in the menu,
1531 and automatically displayed in the web client when the OpenAcademy main
1536 #. Create a ``openacademy/views/session_board.xml``. It should contain
1537 the board view, the actions referenced in that view, an action to
1538 open the dashboard and a re-definition of the main menu item to add
1539 the dashboard action
1541 .. note:: Available dashboard styles are ``1``, ``1-1``, ``1-2``,
1542 ``2-1`` and ``1-1-1``
1544 #. Update ``openacademy/__openerp__.py`` to reference the new data
1552 The web-service module offer a common interface for all web-services :
1558 Business objects can also be accessed via the distributed object
1559 mechanism. They can all be modified via the client interface with contextual
1562 Odoo is accessible through XML-RPC interfaces, for which libraries exist in
1568 The following example is a Python program that interacts with an Odoo
1569 server with the library xmlrpclib.
1575 root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)
1577 uid = xmlrpclib.ServerProxy(root + 'common').login(db, username, password)
1578 print "Logged in as %s (uid: %d)" % (USER, uid)
1581 sock = xmlrpclib.ServerProxy(root + 'object')
1583 'name' : 'Another idea',
1584 'description' : 'This is another idea of mine',
1587 idea_id = sock.execute(db, uid, password, 'idea.idea', 'create', args)
1589 .. exercise:: Add a new service to the client
1591 Write a Python program able to send XML-RPC requests to a PC running
1592 Odoo (yours, or your instructor's). This program should display all
1593 the sessions, and their corresponding number of seats. It should also
1594 create a new session for one of the courses.
1598 .. code-block:: python
1607 ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)
1610 uid = xmlrpclib.ServerProxy(ROOT + 'common').login(DB,USER,PASS)
1611 print "Logged in as %s (uid:%d)" % (USER,uid)
1613 call = functools.partial(
1614 xmlcprlib.ServerProxy(ROOT + 'object').execute,
1617 # 2. Read the sessions
1618 sessions = call('openacademy.session','search_read', [], ['name','seats'])
1619 for session in sessions :
1620 print "Session %s (%s seats)" % (session['name'], session['seats'])
1621 # 3.create a new session
1622 session_id = call('openacademy.session', 'create', {
1623 'name' : 'My session',
1627 Instead of using a hard-coded course id, the code can look up a course
1630 # 3.create a new session for the "Functional" course
1631 course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]
1632 session_id = call('openacademy.session', 'create', {
1633 'name' : 'My session',
1634 'course_id' : course_id,
1637 .. note:: there are also a number of high-level APIs in various languages to
1638 access Odoo systems without *explicitly* going through XML-RPC e.g.
1640 * https://github.com/akretion/ooor
1641 * https://github.com/syleam/openobject-library
1642 * https://github.com/nicolas-van/openerp-client-lib
1643 * https://pypi.python.org/pypi/oersted/
1645 .. [#autofields] it is possible to :attr:`disable the creation of some
1646 <openerp.models.Model._log_access>`
1647 .. [#rawsql] writing raw SQL queries is possible, but requires care as it
1648 bypasses all Odoo authentication and security mechanisms.
1651 http://use-the-index-luke.com/sql/preface
1653 .. _POEdit: http://poedit.net
1655 .. _PostgreSQL's documentation:
1656 .. _table_constraint:
1657 http://www.postgresql.org/docs/9.3/static/ddl-constraints.html
1659 .. _python: http://python.org
1661 .. _XPath: http://w3.org/TR/xpath
1663 .. _twitter bootstrap: http://getbootstrap.com
1665 .. _wkhtmltopdf: http://wkhtmltopdf.org