summaryrefslogtreecommitdiff
path: root/doc/reference
diff options
context:
space:
mode:
authorVictor Feyens <vfe@odoo.com>2021-04-30 13:24:57 +0000
committerAntoine Vandevenne (anv) <anv@odoo.com>2021-05-04 08:09:20 +0000
commit27bad8c3e32049ec69a588df0558e57a0b928309 (patch)
treeb60e430163d486c8c1fd052738c9854aeb8300f5 /doc/reference
parent75a122bbae3a32bfa79da3506179fc63a8bced5d (diff)
[MOV] doc: move documentation to a dedicated repository
In odoo/documentation-user#945, the developer documentation is merged with the user documentation in a single repository and build config. This commit then removes source files for the developer documentation from the odoo/odoo repository. DOC PR: https://github.com/odoo/documentation-user/pull/945 task-2351938 task-2352371 task-2205684 task-2352544 Co-authored-by: Victor Feyens <vfe@odoo.com> Co-authored-by: Antoine Vandevenne <anv@odoo.com>
Diffstat (limited to 'doc/reference')
-rw-r--r--doc/reference/actions.rst445
-rw-r--r--doc/reference/cmdline.rst745
-rw-r--r--doc/reference/data.rst301
-rw-r--r--doc/reference/guidelines.rst1158
-rw-r--r--doc/reference/http.rst87
-rw-r--r--doc/reference/images/mobile_contact_create.pngbin7218 -> 0 bytes
-rw-r--r--doc/reference/images/mobile_notification.pngbin7714 -> 0 bytes
-rw-r--r--doc/reference/images/mobile_switch_account.pngbin19702 -> 0 bytes
-rw-r--r--doc/reference/images/mobile_working.jpgbin21719 -> 0 bytes
-rw-r--r--doc/reference/images/odoo_mobile_api.pngbin13125 -> 0 bytes
-rw-r--r--doc/reference/images/runner.pngbin2864 -> 0 bytes
-rw-r--r--doc/reference/images/runner2.pngbin4000 -> 0 bytes
-rw-r--r--doc/reference/images/snackbar.pngbin4111 -> 0 bytes
-rw-r--r--doc/reference/images/tests.pngbin110669 -> 0 bytes
-rw-r--r--doc/reference/images/tests2.pngbin10081 -> 0 bytes
-rw-r--r--doc/reference/images/tests3.pngbin10394 -> 0 bytes
-rw-r--r--doc/reference/images/toast.pngbin2986 -> 0 bytes
-rw-r--r--doc/reference/iot.rst195
-rw-r--r--doc/reference/javascript_cheatsheet.rst545
-rw-r--r--doc/reference/javascript_reference.rst2425
-rw-r--r--doc/reference/mixins.rst1076
-rw-r--r--doc/reference/mobile.rst213
-rw-r--r--doc/reference/module.rst148
-rw-r--r--doc/reference/orm.rst1092
-rw-r--r--doc/reference/qweb.rst686
-rw-r--r--doc/reference/reports.rst326
-rw-r--r--doc/reference/security.rst389
-rw-r--r--doc/reference/static/res.country.state.csv15
-rw-r--r--doc/reference/testing.rst638
-rw-r--r--doc/reference/translations.rst214
-rw-r--r--doc/reference/translations/po-export.pngbin11576 -> 0 bytes
-rw-r--r--doc/reference/views.rst2139
32 files changed, 0 insertions, 12837 deletions
diff --git a/doc/reference/actions.rst b/doc/reference/actions.rst
deleted file mode 100644
index fef80a12737..00000000000
--- a/doc/reference/actions.rst
+++ /dev/null
@@ -1,445 +0,0 @@
-:banner: banners/actions.jpg
-
-.. _reference/actions:
-
-=======
-Actions
-=======
-
-Actions define the behavior of the system in response to user actions: login,
-action button, selection of an invoice, ...
-
-Actions can be stored in the database or returned directly as dictionaries in
-e.g. button methods. All actions share two mandatory attributes:
-
-``type``
- the category of the current action, determines which fields may be
- used and how the action is interpreted
-``name``
- short user-readable description of the action, may be displayed in the
- client's interface
-
-A client can get actions in 4 forms:
-
-* ``False``
- if any action dialog is currently open, close it
-* A string
- if a :ref:`client action <reference/actions/client>` matches, interpret as
- a client action's tag, otherwise treat as a number
-* A number
- read the corresponding action record from the database, may be a database
- identifier or an :term:`external id`
-* A dictionary
- treat as a client action descriptor and execute
-
-.. _reference/bindings:
-
-Bindings
-========
-
-Aside from their two mandatory attributes, all actions also share *optional*
-attributes used to present an action in an arbitrary model's contextual menu:
-
-``binding_model_id``
- specifies which model the action is bound to
-
- .. note:: For Server Actions, use ``model_id``.
-
-``binding_type``
- specifies the type of binding, which is mostly which contextual menu the
- action will appear under
-
- ``action`` (default)
- Specifies that the action will appear in the :menuselection:`Action`
- contextual menu of the bound model.
- ``report``
- Specifies that the action will appear in the :menuselection:`Print`
- contextual menu of the bound model.
-``binding_view_types``
- a comma-separated list of view types for which the action appears in the
- contextual menu, mostly "list" and / or "form". Defaults to ``list,form``
- (both list and form )
-
-.. _reference/actions/window:
-
-Window Actions (``ir.actions.act_window``)
-==========================================
-
-The most common action type, used to present visualisations of a model through
-:ref:`views <reference/views>`: a window action defines a set of view types
-(and possibly specific views) for a model (and possibly specific record of the
-model).
-
-Its fields are:
-
-``res_model``
- model to present views for
-``views``
- a list of ``(view_id, view_type)`` pairs. The second element of each pair
- is the category of the view (tree, form, graph, ...) and the first is
- an optional database id (or ``False``). If no id is provided, the client
- should fetch the default view of the specified type for the requested
- model (this is automatically done by
- :meth:`~odoo.models.Model.fields_view_get`). The first type of the
- list is the default view type and will be open by default when the action
- is executed. Each view type should be present at most once in the list
-``res_id`` (optional)
- if the default view is ``form``, specifies the record to load (otherwise
- a new record should be created)
-``search_view_id`` (optional)
- ``(id, name)`` pair, ``id`` is the database identifier of a specific
- search view to load for the action. Defaults to fetching the default
- search view for the model
-``target`` (optional)
- whether the views should be open in the main content area (``current``),
- in full screen mode (``fullscreen``) or in a dialog/popup (``new``). Use
- ``main`` instead of ``current`` to clear the breadcrumbs. Defaults to
- ``current``.
-``context`` (optional)
- additional context data to pass to the views
-``domain`` (optional)
- filtering domain to implicitly add to all view search queries
-``limit`` (optional)
- number of records to display in lists by default. Defaults to 80 in the
- web client
-
-For instance, to open customers (partner with the ``customer`` flag set) with
-list and form views::
-
- {
- "type": "ir.actions.act_window",
- "res_model": "res.partner",
- "views": [[False, "tree"], [False, "form"]],
- "domain": [["customer", "=", true]],
- }
-
-Or to open the form view of a specific product (obtained separately) in a new
-dialog::
-
- {
- "type": "ir.actions.act_window",
- "res_model": "product.product",
- "views": [[False, "form"]],
- "res_id": a_product_id,
- "target": "new",
- }
-
-In-database window actions have a few different fields which should be ignored
-by clients, mostly to use in composing the ``views`` list:
-
-``view_mode`` (default= ``tree,form`` )
- comma-separated list of view types as a string (/!\\ No spaces /!\\). All of these types will be
- present in the generated ``views`` list (with at least a ``False`` view_id)
-``view_ids``
- M2M\ [#notquitem2m]_ to view objects, defines the initial content of
- ``views``
-
- .. note:: Act_window views can also be defined cleanly through ``ir.actions.act_window.view``.
-
- If you plan to allow multiple views for your model, prefer using
- ir.actions.act_window.view instead of the action ``view_ids``
-
- .. code-block:: xml
-
- <record model="ir.actions.act_window.view" id="test_action_tree">
- <field name="sequence" eval="1"/>
- <field name="view_mode">tree</field>
- <field name="view_id" ref="view_test_tree"/>
- <field name="act_window_id" ref="test_action"/>
- </record>
-
-``view_id``
- specific view added to the ``views`` list in case its type is part of the
- ``view_mode`` list and not already filled by one of the views in
- ``view_ids``
-
-These are mostly used when defining actions from :ref:`reference/data`:
-
-.. code-block:: xml
-
- <record model="ir.actions.act_window" id="test_action">
- <field name="name">A Test Action</field>
- <field name="res_model">some.model</field>
- <field name="view_mode">graph</field>
- <field name="view_id" ref="my_specific_view"/>
- </record>
-
-will use the "my_specific_view" view even if that's not the default view for
-the model.
-
-The server-side composition of the ``views`` sequence is the following:
-
-* get each ``(id, type)`` from ``view_ids`` (ordered by ``sequence``)
-* if ``view_id`` is defined and its type isn't already filled, append its
- ``(id, type)``
-* for each unfilled type in ``view_mode``, append ``(False, type)``
-
-.. todo::
-
- * ``usage``?
- * ``groups_id``?
- * ``filter``?
-
-.. [#notquitem2m] technically not an M2M: adds a sequence field and may be
- composed of just a view type, without a view id.
-
-.. _reference/actions/url:
-
-URL Actions (``ir.actions.act_url``)
-====================================
-
-Allow opening a URL (website/web page) via an Odoo action. Can be customized
-via two fields:
-
-``url``
- the address to open when activating the action
-``target``
- opens the address in a new window/page if ``new``, replaces
- the current content with the page if ``self``. Defaults to ``new``
-
-::
-
- {
- "type": "ir.actions.act_url",
- "url": "https://odoo.com",
- "target": "self",
- }
-
-will replace the current content section by the Odoo home page.
-
-.. _reference/actions/server:
-
-Server Actions (``ir.actions.server``)
-======================================
-
-.. autoclass:: odoo.addons.base.models.ir_actions.IrActionsServer
-
-Allow triggering complex server code from any valid action location. Only
-two fields are relevant to clients:
-
-``id``
- the in-database identifier of the server action to run
-``context`` (optional)
- context data to use when running the server action
-
-In-database records are significantly richer and can perform a number of
-specific or generic actions based on their ``state``. Some fields (and
-corresponding behaviors) are shared between states:
-
-``model_id``
- Odoo model linked to the action.
-
-``state``
-
-* ``code``: Executes python code given through the ``code`` argument.
-
-* ``object_create``: Creates a new record of model ``crud_model_id`` following ``fields_lines`` specifications.
-
-* ``object_write``: Updates the current record(s) following ``fields_lines`` specifications
-
-* ``multi``: Executes serveral actions given through the ``child_ids`` argument.
-
-State fields
-------------
-
-Depending on its state, the behavior is defined through different fields.
-The concerned state is given after each field.
-
-``code`` (code)
- Specify a piece of Python code to execute when the action is called
-
- .. code-block:: xml
-
- <record model="ir.actions.server" id="print_instance">
- <field name="name">Res Partner Server Action</field>
- <field name="model_id" ref="model_res_partner"/>
- <field name="state">code</field>
- <field name="code">
- raise Warning(record.name)
- </field>
- </record>
-
- .. note::
-
- The code segment can define a variable called ``action``, which will be
- returned to the client as the next action to execute:
-
- .. code-block:: xml
-
- <record model="ir.actions.server" id="print_instance">
- <field name="name">Res Partner Server Action</field>
- <field name="model_id" ref="model_res_partner"/>
- <field name="state">code</field>
- <field name="code">
- if record.some_condition():
- action = {
- "type": "ir.actions.act_window",
- "view_mode": "form",
- "res_model": record._name,
- "res_id": record.id,
- }
- </field>
- </record>
-
- will ask the client to open a form for the record if it fulfills some
- condition
-
- .. This tends to be the only action type created from :ref:`data files
- <reference/data>`, other types aside from
- :ref:`reference/actions/server/multi` are simpler than Python code to define
- from the UI, but not from :ref:`data files <reference/data>`.
-
-``crud_model_id`` (create)(required)
- model in which to create a new record
-``link_field_id`` (create)
- many2one to ``ir.model.fields``, specifies the current record's m2o field
- on which the newly created record should be set (models should match)
-
-``fields_lines`` (create/write)
- fields to override when creating or copying the record.
- :class:`~odoo.fields.One2many` with the fields:
-
- ``col1``
- ``ir.model.fields`` to set in the concerned model
- (``crud_model_id`` for creates, ``model_id`` for updates)
- ``value``
- value for the field, interpreted via ``type``
- ``type`` (value|reference|equation)
- If ``value``, the ``value`` field is interpreted as a literal value
- (possibly converted), if ``equation`` the ``value`` field is
- interpreted as a Python expression and evaluated
-
-``child_ids`` (multi)
- Specify the multiple sub-actions (``ir.actions.server``) to enact in state multi.
- If sub-actions themselves return actions, the last
- one will be returned to the client as the multi's own next action
-
-.. _reference/actions/server/context:
-
-Evaluation context
-------------------
-
-A number of keys are available in the evaluation context of or surrounding
-server actions:
-
-* ``model`` model object linked to the action via ``model_id``
-* ``record``/``records`` record/recorset on which the action is triggered, can be void.
-* ``env`` Odoo Environment
-* ``datetime``, ``dateutil``, ``time``, ``timezone`` corresponding Python modules
-* ``log: log(message, level='info')`` logging function to record debug information in ir.logging table
-* ``Warning`` constructor for the ``Warning`` exception
-
-.. _reference/actions/report:
-
-Report Actions (``ir.actions.report``)
-======================================
-
-Triggers the printing of a report.
-
-If you define your report through a `<record>` instead of a `<report>` tag and
-want the action to show up in the Print menu of the model's views, you will
-also need to specify ``binding_model_id`` from :ref:`reference/bindings`. It's
-not necessary to set ``binding_type`` to ``report``, since
-``ir.actions.report`` will implicitly default to that.
-
-
-``name`` (mandatory)
- used as the file name if ``print_report_name`` is not specified.
- Otherwise, only useful as a mnemonic/description of the report
- when looking for one in a list of some sort
-``model`` (mandatory)
- the model your report will be about
-``report_type`` (default=qweb-pdf)
- either ``qweb-pdf`` for PDF reports or ``qweb-html`` for HTML
-``report_name`` (mandatory)
- the name (:term:`external id`) of the qweb template used to render the report
-``print_report_name``
- python expression defining the name of the report.
-``groups_id``
- :class:`~odoo.fields.Many2many` field to the groups allowed to view/use
- the current report
-``multi``
- if set to ``True``, the action will not be displayed on a form view.
-``paperformat_id``
- :class:`~odoo.fields.Many2one` field to the paper format you wish to
- use for this report (if not specified, the company format will be used)
-``attachment_use``
- if set to ``True``, the report is only generated once the first time it is
- requested, and re-printed from the stored report afterwards instead of
- being re-generated every time.
-
- Can be used for reports which must only be generated once (e.g. for legal
- reasons)
-``attachment``
- python expression that defines the name of the report; the record is
- accessible as the variable ``object``
-
-.. _reference/actions/client:
-
-Client Actions (``ir.actions.client``)
-======================================
-
-Triggers an action implemented entirely in the client.
-
-``tag``
- the client-side identifier of the action, an arbitrary string which
- the client should know how to react to
-``params`` (optional)
- a Python dictionary of additional data to send to the client, alongside
- the client action tag
-``target`` (optional)
- whether the client action should be open in the main content area
- (``current``), in full screen mode (``fullscreen``) or in a dialog/popup
- (``new``). Use ``main`` instead of ``current`` to clear the breadcrumbs.
- Defaults to ``current``.
-
-::
-
- {
- "type": "ir.actions.client",
- "tag": "pos.ui"
- }
-
-tells the client to start the Point of Sale interface, the server has no idea
-how the POS interface works.
-
-.. seealso::
- - :ref:`Tutorial: Client Actions <howtos/web/client_actions>`
-
-.. _reference/actions/cron:
-
-Automated Actions (``ir.cron``)
-======================================
-
-Actions triggered automatically on a predefined frequency.
-
-``name``
- Name of the automated action (Mainly used in log display)
-
-``interval_number``
- Number of *interval_type* uom between two executions of the action
-
-``interval_type``
- Unit of measure of frequency interval (``minutes``, ``hours``, ``days``, ``weeks``, ``months``,
-
-``numbercall``
- Number of times this action has to be run.
- If the action is expected to run indefinitely, set to ``-1``.
-
-``doall``
- Boolean precising whether the missed actions have to be executed in case of
- server restarts.
-
-``model_id``
- Model on which this action will be called
-
-``code``
- Code content of the action.
- Can be a simple call to the model's method :
-
- .. code-block:: python
-
- model.<method_name>()
-
-``nextcall``
- Next planned execution date of this action (date/time format)
diff --git a/doc/reference/cmdline.rst b/doc/reference/cmdline.rst
deleted file mode 100644
index 7936a32b888..00000000000
--- a/doc/reference/cmdline.rst
+++ /dev/null
@@ -1,745 +0,0 @@
-:banner: banners/cmdline.jpg
-
-.. _reference/cmdline:
-
-================================
-Command-line interface: odoo-bin
-================================
-
-.. _reference/cmdline/server:
-
-Running the server
-==================
-
-.. program:: odoo-bin
-
-.. option:: -d <database>, --database <database>
-
- database(s) used when installing or updating modules.
- Providing a comma-separated list restrict access to databases provided in
- list.
-
- For advanced database options, take a look :ref:`below <reference/cmdline/server/database>`.
-
-.. option:: -i <modules>, --init <modules>
-
- comma-separated list of modules to install before running the server
- (requires :option:`-d`).
-
-.. option:: -u <modules>, --update <modules>
-
- comma-separated list of modules to update before running the server
- (requires :option:`-d`).
-
-.. option:: --addons-path <directories>
-
- comma-separated list of directories in which modules are stored. These
- directories are scanned for modules.
-
- .. (nb: when and why?)
-
-.. option:: -c <config>, --config <config>
-
- provide an alternate :ref:`configuration file <reference/cmdline/config>`
-
-.. option:: -s, --save
-
- saves the server configuration to the current configuration file
- (:file:`{$HOME}/.odoorc` by default, and can be overridden using
- :option:`-c`).
-
-.. option:: --without-demo
-
- disables demo data loading for modules installed
- comma-separated, use ``all`` for all modules.
-
-.. option:: --test-enable
-
- runs tests after installing modules
-
-.. option:: --test-tags 'tag_1,tag_2,...,-tag_n'
-
- select the tests to run by using tags.
-
-.. option:: --screenshots
-
- Specify directory where to write screenshots when an HttpCase.browser_js test
- fails. It defaults to :file:`/tmp/odoo_tests/{db_name}/screenshots`
-
-.. option:: --screencasts
-
- Enable screencasts and specify directory where to write screencasts files.
- The ``ffmpeg`` utility needs to be installed to encode frames into a video
- file. Otherwise frames will be kept instead of the video file.
-
-.. _reference/cmdline/server/database:
-
-Database
---------
-
-.. option:: -r <user>, --db_user <user>
-
- database username, used to connect to PostgreSQL.
-
-.. option:: -w <password>, --db_password <password>
-
- database password, if using `password authentication`_.
-
-.. option:: --db_host <hostname>
-
- host for the database server
-
- * ``localhost`` on Windows
- * UNIX socket otherwise
-
-.. option:: --db_port <port>
-
- port the database listens on, defaults to 5432
-
-.. option:: --db-filter <filter>
-
- hides databases that do not match ``<filter>``. The filter is a
- `regular expression`_, with the additions that:
-
- - ``%h`` is replaced by the whole hostname the request is made on.
- - ``%d`` is replaced by the subdomain the request is made on, with the
- exception of ``www`` (so domain ``odoo.com`` and ``www.odoo.com`` both
- match the database ``odoo``).
-
- These operations are case sensitive. Add option ``(?i)`` to match all
- databases (so domain ``odoo.com`` using ``(?i)%d`` matches the database
- ``Odoo``).
-
- Since version 11, it's also possible to restrict access to a given database
- listen by using the --database parameter and specifying a comma-separated
- list of databases
-
- When combining the two parameters, db-filter supersedes the comma-separated
- database list for restricting database list, while the comma-separated list
- is used for performing requested operations like upgrade of modules.
-
- .. code-block:: bash
-
- $ odoo-bin --db-filter ^11.*$
-
- Restrict access to databases whose name starts with 11
-
- .. code-block:: bash
-
- $ odoo-bin --database 11firstdatabase,11seconddatabase
-
- Restrict access to only two databases, 11firstdatabase and 11seconddatabase
-
- .. code-block:: bash
-
- $ odoo-bin --database 11firstdatabase,11seconddatabase -u base
-
- Restrict access to only two databases, 11firstdatabase and 11seconddatabase,
- and update base module on one database: 11firstdatabase.
- If database 11seconddatabase doesn't exist, the database is created and base modules
- is installed
-
- .. code-block:: bash
-
- $ odoo-bin --db-filter ^11.*$ --database 11firstdatabase,11seconddatabase -u base
-
- Restrict access to databases whose name starts with 11,
- and update base module on one database: 11firstdatabase.
- If database 11seconddatabase doesn't exist, the database is created and base modules
- is installed
-
-.. option:: --db-template <template>
-
- when creating new databases from the database-management screens, use the
- specified `template database`_. Defaults to ``template0``.
-
-.. option:: --pg_path </path/to/postgresql/binaries>
-
- Path to the PostgreSQL binaries that are used by the database manager to
- dump and restore databases. You have to specify this option only if these
- binaries are located in a non-standard directory.
-
-.. option:: --no-database-list
-
- Suppresses the ability to list databases available on the system
-
-.. option:: --db_sslmode
-
- Control the SSL security of the connection between Odoo and PostgreSQL.
- Value should bve one of 'disable', 'allow', 'prefer', 'require',
- 'verify-ca' or 'verify-full'
- Default value is 'prefer'
-
-.. _reference/cmdline/server/emails:
-
-Emails
-------
-
-.. option:: --email-from <address>
-
- Email address used as <FROM> when Odoo needs to send mails
-
-.. option:: --smtp <server>
-
- Address of the SMTP server to connect to in order to send mails
-
-.. option:: --smtp-port <port>
-
-.. option:: --smtp-ssl
-
- If set, odoo should use SSL/STARTSSL SMTP connections
-
-.. option:: --smtp-user <name>
-
- Username to connect to the SMTP server
-
-.. option:: --smtp-password <password>
-
- Password to connect to the SMTP server
-
-.. _reference/cmdline/server/internationalisation:
-
-Internationalisation
---------------------
-
-Use these options to translate Odoo to another language. See i18n section of
-the user manual. Option '-d' is mandatory. Option '-l' is mandatory in case
-of importation
-
-.. option:: --load-language <languages>
-
- specifies the languages (separated by commas) for the translations you
- want to be loaded
-
-.. option:: -l, --language <language>
-
- specify the language of the translation file. Use it with --i18n-export
- or --i18n-import
-
-.. option:: --i18n-export <filename>
-
- export all sentences to be translated to a CSV file, a PO file or a TGZ
- archive and exit.
-
-.. option:: --i18n-import <filename>
-
- import a CSV or a PO file with translations and exit. The '-l' option is
- required.
-
-.. option:: --i18n-overwrite
-
- overwrites existing translation terms on updating a module or importing
- a CSV or a PO file.
-
-.. option:: --modules
-
- specify modules to export. Use in combination with --i18n-export
-
-.. _reference/cmdline/advanced:
-
-Advanced Options
-----------------
-
-.. _reference/cmdline/dev:
-
-Developer features
-''''''''''''''''''
-
-.. option:: --dev <feature,feature,...,feature>
-
- * ``all``: all the features below are activated
-
- * ``xml``: read template qweb from xml file directly instead of database.
- Once a template has been modified in database, it will be not be read from
- the xml file until the next update/init.
-
- * ``reload``: restart server when python file are updated (may not be detected
- depending on the text editor used)
-
- * ``qweb``: break in the evaluation of qweb template when a node contains ``t-debug='debugger'``
-
- * ``(i)p(u)db``: start the chosen python debugger in the code when an
- unexpected error is raised before logging and returning the error.
-
-
-.. _reference/cmdline/server/http:
-
-HTTP
-''''
-
-.. option:: --no-http
-
- do not start the HTTP or long-polling workers (may still start :ref:`cron <reference/actions/cron>`
- workers)
-
- .. warning:: has no effect if :option:`--test-enable` is set, as tests
- require an accessible HTTP server
-
-.. option:: --http-interface <interface>
-
- TCP/IP address on which the HTTP server listens, defaults to ``0.0.0.0``
- (all addresses)
-
-.. option:: --http-port <port>
-
- Port on which the HTTP server listens, defaults to 8069.
-
-.. option:: --longpolling-port <port>
-
- TCP port for long-polling connections in multiprocessing or gevent mode,
- defaults to 8072. Not used in default (threaded) mode.
-
-.. option:: --proxy-mode
-
- enables the use of ``X-Forwarded-*`` headers through `Werkzeug's proxy
- support`_.
-
- .. warning:: proxy mode *must not* be enabled outside of a reverse proxy
- scenario
-
-.. _reference/cmdline/server/logging:
-
-Logging
-'''''''
-
-By default, Odoo displays all logging of level_ ``info`` except for workflow
-logging (``warning`` only), and log output is sent to ``stdout``. Various
-options are available to redirect logging to other destinations and to
-customize the amount of logging output.
-
-.. option:: --logfile <file>
-
- sends logging output to the specified file instead of stdout. On Unix, the
- file `can be managed by external log rotation programs
- <https://docs.python.org/3/library/logging.handlers.html#watchedfilehandler>`_
- and will automatically be reopened when replaced
-
-.. option:: --syslog
-
- logs to the system's event logger: `syslog on unices <https://docs.python.org/3/library/logging.handlers.html#sysloghandler>`_
- and `the Event Log on Windows <https://docs.python.org/3/library/logging.handlers.html#nteventloghandler>`_.
-
- Neither is configurable
-
-.. option:: --log-db <dbname>
-
- logs to the ``ir.logging`` model (``ir_logging`` table) of the specified
- database. The database can be the name of a database in the "current"
- PostgreSQL, or `a PostgreSQL URI`_ for e.g. log aggregation.
-
-.. option:: --log-handler <handler-spec>
-
- :samp:`{LOGGER}:{LEVEL}`, enables ``LOGGER`` at the provided ``LEVEL``
- e.g. ``odoo.models:DEBUG`` will enable all logging messages at or above
- ``DEBUG`` level in the models.
-
- * The colon ``:`` is mandatory
- * The logger can be omitted to configure the root (default) handler
- * If the level is omitted, the logger is set to ``INFO``
-
- The option can be repeated to configure multiple loggers e.g.
-
- .. code-block:: console
-
- $ odoo-bin --log-handler :DEBUG --log-handler werkzeug:CRITICAL --log-handler odoo.fields:WARNING
-
-.. option:: --log-request
-
- enable DEBUG logging for RPC requests, equivalent to
- ``--log-handler=odoo.http.rpc.request:DEBUG``
-
-.. option:: --log-response
-
- enable DEBUG logging for RPC responses, equivalent to
- ``--log-handler=odoo.http.rpc.response:DEBUG``
-
-.. option:: --log-web
-
- enables DEBUG logging of HTTP requests and responses, equivalent to
- ``--log-handler=odoo.http:DEBUG``
-
-.. option:: --log-sql
-
- enables DEBUG logging of SQL querying, equivalent to
- ``--log-handler=odoo.sql_db:DEBUG``
-
-.. option:: --log-level <level>
-
- Shortcut to more easily set predefined levels on specific loggers. "real"
- levels (``critical``, ``error``, ``warn``, ``debug``) are set on the
- ``odoo`` and ``werkzeug`` loggers (except for ``debug`` which is only
- set on ``odoo``).
-
- Odoo also provides debugging pseudo-levels which apply to different sets
- of loggers:
-
- ``debug_sql``
- sets the SQL logger to ``debug``
-
- equivalent to ``--log-sql``
- ``debug_rpc``
- sets the ``odoo`` and HTTP request loggers to ``debug``
-
- equivalent to ``--log-level debug --log-request``
- ``debug_rpc_answer``
- sets the ``odoo`` and HTTP request and response loggers to
- ``debug``
-
- equivalent to ``--log-level debug --log-request --log-response``
-
- .. note::
-
- In case of conflict between :option:`--log-level` and
- :option:`--log-handler`, the latter is used
-
-.. _reference/cdmline/workers:
-
-Multiprocessing
-'''''''''''''''
-
-.. option:: --workers <count>
-
- if ``count`` is not 0 (the default), enables multiprocessing and sets up
- the specified number of HTTP workers (sub-processes processing HTTP
- and RPC requests).
-
- .. note:: multiprocessing mode is only available on Unix-based systems
-
- A number of options allow limiting and recycling workers:
-
- .. option:: --limit-request <limit>
-
- Number of requests a worker will process before being recycled and
- restarted.
-
- Defaults to *8196*.
-
- .. option:: --limit-memory-soft <limit>
-
- Maximum allowed virtual memory per worker. If the limit is exceeded,
- the worker is killed and recycled at the end of the current request.
-
- Defaults to *2048MiB*.
-
- .. option:: --limit-memory-hard <limit>
-
- Hard limit on virtual memory, any worker exceeding the limit will be
- immediately killed without waiting for the end of the current request
- processing.
-
- Defaults to *2560MiB*.
-
- .. option:: --limit-time-cpu <limit>
-
- Prevents the worker from using more than <limit> CPU seconds for each
- request. If the limit is exceeded, the worker is killed.
-
- Defaults to *60*.
-
- .. option:: --limit-time-real <limit>
-
- Prevents the worker from taking longer than <limit> seconds to process
- a request. If the limit is exceeded, the worker is killed.
-
- Differs from :option:`--limit-time-cpu` in that this is a "wall time"
- limit including e.g. SQL queries.
-
- Defaults to *120*.
-
-.. option:: --max-cron-threads <count>
-
- number of workers dedicated to :ref:`cron <reference/actions/cron>` jobs. Defaults to *2*.
- The workers are threads in multi-threading mode and processes in multi-processing mode.
-
- For multi-processing mode, this is in addition to the HTTP worker processes.
-
-.. _reference/cmdline/config:
-
-Configuration file
-==================
-
-.. program:: odoo-bin
-
-Most of the command-line options can also be specified via a configuration
-file. Most of the time, they use similar names with the prefix ``-`` removed
-and other ``-`` are replaced by ``_`` e.g. :option:`--db-template` becomes
-``db_template``.
-
-Some conversions don't match the pattern:
-
-* :option:`--db-filter` becomes ``dbfilter``
-* :option:`--no-http` corresponds to the ``http_enable`` boolean
-* logging presets (all options starting with ``--log-`` except for
- :option:`--log-handler` and :option:`--log-db`) just add content to
- ``log_handler``, use that directly in the configuration file
-* :option:`--smtp` is stored as ``smtp_server``
-* :option:`--database` is stored as ``db_name``
-* :option:`--i18n-import` and :option:`--i18n-export` aren't available at all
- from configuration files
-
-The default configuration file is :file:`{$HOME}/.odoorc` which
-can be overridden using :option:`--config <odoo-bin -c>`. Specifying
-:option:`--save <odoo-bin -s>` will save the current configuration state back
-to that file. The configuration items relative to the command-line are to be
-specified in the section ``[options]``.
-
-Here is a sample file:
-
-.. code-block:: ini
-
- [options]
- db_user=odoo
- dbfilter=odoo
-
-.. _jinja2: http://jinja.pocoo.org
-.. _regular expression: https://docs.python.org/3/library/re.html
-.. _password authentication:
- https://www.postgresql.org/docs/9.3/static/auth-methods.html#AUTH-PASSWORD
-.. _template database:
- https://www.postgresql.org/docs/9.3/static/manage-ag-templatedbs.html
-.. _level:
- https://docs.python.org/3/library/logging.html#logging.Logger.setLevel
-.. _a PostgreSQL URI:
- https://www.postgresql.org/docs/9.2/static/libpq-connect.html#AEN38208
-.. _Werkzeug's proxy support:
- http://werkzeug.pocoo.org/docs/contrib/fixers/#werkzeug.contrib.fixers.ProxyFix
-.. _pyinotify: https://github.com/seb-m/pyinotify/wiki
-
-
-Shell
-=====
-
-Odoo command-line also allows to launch odoo as a python console environment.
-This enables direct interaction with the :ref:`orm <reference/orm>` and its functionalities.
-
-
-.. code-block:: console
-
- $ odoo_bin shell
-
-.. option:: --shell-interface (ipython|ptpython|bpython|python)
-
- Specify a preferred REPL to use in shell mode.
-
-
-.. _reference/cmdline/scaffold:
-
-Scaffolding
-===========
-
-.. program:: odoo-bin scaffold
-
-Scaffolding is the automated creation of a skeleton structure to simplify
-bootstrapping (of new modules, in the case of Odoo). While not necessary it
-avoids the tedium of setting up basic structures and looking up what all
-starting requirements are.
-
-Scaffolding is available via the :command:`odoo-bin scaffold` subcommand.
-
-.. code-block:: console
-
- $ odoo_bin scaffold my_module /addons/
-
-.. option:: name (required)
-
- the name of the module to create, may munged in various manners to
- generate programmatic names (e.g. module directory name, model names, …)
-
-.. option:: destination (default=current directory)
-
- directory in which to create the new module, defaults to the current
- directory
-
-.. option:: -t <template>
-
- a template directory, files are passed through jinja2_ then copied to
- the ``destination`` directory
-
-
-This will create module *my_module* in directory */addons/*.
-
-.. _reference/cmdline/populate:
-
-Database Population
-===================
-
-.. program:: odoo-bin populate
-
-Odoo CLI supports database population features. If the feature is
-:ref:`implemented on a given model <reference/testing/populate/methods>`, it allows automatic data
-generation of the model's records to test your modules in databases containing non-trivial amounts of records.
-
-.. code-block:: console
-
- $ odoo_bin populate
-
-.. option:: --models
-
- list of models for which the database should be filled
-
-.. option:: --size (small|medium|large)
-
- population size, the actual records number depends on the model's `_populate_sizes` attribute.
- The generated records content is specified by the :meth:`~odoo.models._populate_factories` method
- of a given model (cf. the :file:`populate` folder of modules for further details).
-
-.. seealso::
-
- :ref:`reference/testing/populate`
-
-
-Cloc
-====
-
-.. program:: odoo-bin cloc
-
-Odoo Cloc is a tool to count the number of relevant lines written in
-Python, Javascript or XML. This can be used as a rough metric for pricing
-maintenance of extra modules.
-
-Command-line options
---------------------
-.. option:: -d <database>, --database <database>
-
-| Process the code of all extra modules installed on the provided database,
- and of all server actions and computed fields manually created in the provided
- database.
-| The :option:`--addons-path` option is required to specify the path(s) to the
- module folder(s).
-| If combined with :option:`--path`, the count will be that of the sum of both
- options' results (with possible overlaps). At least one of these two options is
- required to specify which code to process.
-
-.. code-block:: console
-
- $ odoo-bin cloc --addons-path=addons -d my_database
-
-.. seealso::
- - :ref:`reference/cmdline/cloc/database-option`
-
-
-.. option:: -p <path>, --path <path>
-
-| Process the files in the provided path.
-| If combined with :option:`--database`, the count will be that of the sum of both
- options' results (with possible overlaps). At least one of these two options is
- required to specify which code to process.
-
-.. code-block:: console
-
- $ odoo-bin cloc -p addons/account
-
-
-Multiple paths can be provided by repeating the option.
-
-.. code-block:: console
-
- $ odoo-bin cloc -p addons/account -p addons/sale
-
-.. seealso::
- - :ref:`reference/cmdline/cloc/path-option`
-
-
-.. option:: --addons-path <directories>
-
-| Comma-separated list of directories in which modules are stored. These directories
- are scanned for modules.
-| Required if the :option:`--database` option is used.
-
-
-.. option:: -c <directories>
-
-Specify a configuration file to use in place of the :option:`--addons-path` option.
-
-.. code-block:: console
-
- $ odoo-bin cloc -c config.conf -d my_database
-
-
-.. option:: -v, --verbose
-
-Show the details of lines counted for each file.
-
-
-Processed files
----------------
-
-.. _reference/cmdline/cloc/database-option:
-
-With the :option:`--database` option
-''''''''''''''''''''''''''''''''''''
-
-Odoo Cloc counts the lines in each file of extra installed modules in a
-given database. In addition, it counts the Python lines of server actions and
-custom computed fields that have been directly created in the database or
-imported.
-
-Some files are excluded from the count by default:
-
-- The manifest (:file:`__manifest__.py` or :file:`__openerp__.py`)
-- The contents of the folder :file:`static/lib`
-- The tests defined in the folder :file:`tests` and :file:`static/tests`
-- The migrations scripts defined in the folder :file:`migrations`
-- The XML files declared in the ``demo`` or ``demo_xml`` sections of the manifest
-
-For special cases, a list of files that should be ignored by Odoo Cloc can be defined
-per module. This is specified by the ``cloc_exclude`` entry of the manifest:
-
-.. code-block:: python
-
- "cloc_exclude": [
- "lib/common.py", # exclude a single file
- "data/*.xml", # exclude all XML files in a specific folder
- "example/**/*", # exclude all files in a folder hierarchy recursively
- ]
-
-| The pattern ``**/*`` can be used to ignore an entire module. This can be useful
- to exclude a module from maintenance service costs.
-| For more information about the pattern syntax, see `glob
- <https://docs.python.org/3/library/pathlib.html#pathlib.Path.glob>`_.
-
-
-
-.. _reference/cmdline/cloc/path-option:
-
-With the :option:`--path` option
-''''''''''''''''''''''''''''''''
-
-This method works the same as with the :ref:`--database option
-<reference/cmdline/cloc/database-option>` if a manifest file is present in the given
-folder. Otherwise, it counts all files.
-
-
-Identifying Extra Modules
--------------------------
-
-To distinguish between standard and extra modules, Odoo Cloc uses the following heuristic:
-modules that are located (real file system path, after following symbolic links)
-in the same parent directory as the ``base``, ``web`` or ``web_enterprise``
-standard modules are considered standard. Other modules are treated as extra modules.
-
-
-Error Handling
---------------
-
-Some file cannot be counted by Odoo Cloc.
-Those file are reported at the end of the output.
-
-Max file size exceeded
-''''''''''''''''''''''
-
-Odoo Cloc rejects any file larger than 25MB. Usually, source files are smaller
-than 1 MB. If a file is rejected, it may be:
-
-- A generated XML file that contains lots of data. It should be excluded in the manifest.
-- A JavaScript library that should be placed in the :file:`static/lib` folder.
-
-Syntax Error
-''''''''''''
-
-Odoo Cloc cannot count the lines of code of a Python file with a syntax problem.
-If an extra module contains such files, they should be fixed to allow the module to
-load. If the module works despite the presence of those files, they are probably
-not loaded and should therefore be removed from the module, or at least excluded
-in the manifest via ``cloc_exclude``.
-
diff --git a/doc/reference/data.rst b/doc/reference/data.rst
deleted file mode 100644
index 6dd0d7ccd9b..00000000000
--- a/doc/reference/data.rst
+++ /dev/null
@@ -1,301 +0,0 @@
-:banner: banners/data_files.jpg
-
-.. _reference/data:
-
-==========
-Data Files
-==========
-
-Odoo is greatly data-driven, and a big part of modules definition is thus
-the definition of the various records it manages: UI (menus and views),
-security (access rights and access rules), reports and plain data are all
-defined via records.
-
-Structure
-=========
-
-The main way to define data in Odoo is via XML data files: The broad structure
-of an XML data file is the following:
-
-* Any number of operation elements within the root element ``odoo``
-
-.. code-block:: xml
-
- <!-- the root elements of the data file -->
- <?xml version="1.0" encoding="UTF-8"?>
- <odoo>
- <operation/>
- ...
- </odoo>
-
-Data files are executed sequentially, operations can only refer to the result
-of operations defined previously
-
-.. note::
-
- If the content of the data file is expected to be applied only once, you
- can specify the odoo flag ``noupdate`` set to 1. If part of
- the data in the file is expected to be applied once, you can place this part
- of the file in a <data noupdate="1"> domain.
-
- .. code-block:: xml
-
- <odoo>
- <data noupdate="1">
- <!-- Only loaded when installing the module (odoo-bin -i module) -->
- <operation/>
- </data>
-
- <!-- (Re)Loaded at install and update (odoo-bin -i/-u) -->
- <operation/>
- </odoo>
-
-Core operations
-===============
-
-.. _reference/data/record:
-
-``record``
-----------
-
-``record`` appropriately defines or updates a database record, it has the
-following attributes:
-
-``model`` (required)
- name of the model to create (or update)
-``id``
- the :term:`external identifier` for this record. It is strongly
- recommended to provide one
-
- * for record creation, allows subsequent definitions to either modify or
- refer to this record
- * for record modification, the record to modify
-``context``
- context to use when creating the record
-``forcecreate``
- in update mode whether the record should be created if it doesn't exist
-
- Requires an :term:`external id`, defaults to ``True``.
-
-``field``
-----------
-
-Each record can be composed of ``field`` tags, defining values to set when
-creating the record. A ``record`` with no ``field`` will use all default
-values (creation) or do nothing (update).
-
-A ``field`` has a mandatory ``name`` attribute, the name of the field to set,
-and various methods to define the value itself:
-
-Nothing
- if no value is provided for the field, an implicit ``False`` will be set
- on the field. Can be used to clear a field, or avoid using a default value
- for the field.
-``search``
- for :ref:`relational fields <reference/fields/relational>`, should be
- a :ref:`domain <reference/orm/domains>` on the field's model.
-
- Will evaluate the domain, search the field's model using it and set the
- search's result as the field's value. Will only use the first result if
- the field is a :class:`~odoo.fields.Many2one`
-``ref``
- if a ``ref`` attribute is provided, its value must be a valid
- :term:`external id`, which will be looked up and set as the field's value.
-
- Mostly for :class:`~odoo.fields.Many2one` and
- :class:`~odoo.fields.Reference` fields
-``type``
- if a ``type`` attribute is provided, it is used to interpret and convert
- the field's content. The field's content can be provided through an
- external file using the ``file`` attribute, or through the node's body.
-
- Available types are:
-
- ``xml``, ``html``
- extracts the ``field``'s children as a single document, evaluates
- any :term:`external id` specified with the form ``%(external_id)s``.
- ``%%`` can be used to output actual *%* signs.
- ``file``
- ensures that the field content is a valid file path in the current
- model, saves the pair :samp:`{module},{path}` as the field value
- ``char``
- sets the field content directly as the field's value without
- alterations
- ``base64``
- base64_-encodes the field's content, useful combined with the ``file``
- *attribute* to load e.g. image data into attachments
- ``int``
- converts the field's content to an integer and sets it as the field's
- value
- ``float``
- converts the field's content to a float and sets it as the field's
- value
- ``list``, ``tuple``
- should contain any number of ``value`` elements with the same
- properties as ``field``, each element resolves to an item of a
- generated tuple or list, and the generated collection is set as the
- field's value
-``eval``
- for cases where the previous methods are unsuitable, the ``eval``
- attributes simply evaluates whatever Python expression it is provided and
- sets the result as the field's value.
-
- The evaluation context contains various modules (``time``, ``datetime``,
- ``timedelta``, ``relativedelta``), a function to resolve :term:`external
- identifiers` (``ref``) and the model object for the current field if
- applicable (``obj``)
-
-``delete``
-----------
-
-The ``delete`` tag can remove any number of records previously defined. It
-has the following attributes:
-
-``model`` (required)
- the model in which a specified record should be deleted
-``id``
- the :term:`external id` of a record to remove
-``search``
- a :ref:`domain <reference/orm/domains>` to find records of the model to
- remove
-
-``id`` and ``search`` are exclusive
-
-``function``
-------------
-
-The ``function`` tag calls a method on a model, with provided parameters.
-It has two mandatory parameters ``model`` and ``name`` specifying respectively
-the model and the name of the method to call.
-
-Parameters can be provided using ``eval`` (should evaluate to a sequence of
-parameters to call the method with) or ``value`` elements (see ``list``
-values).
-
-.. code-block:: xml
-
- <odoo>
- <data noupdate="1">
- <record name="partner_1" model="res.partner">
- <field name="name">Odude</field>
- </record>
-
- <function model="res.partner" name="send_inscription_notice"
- eval="[[ref('partner_1'), ref('partner_2')]]"/>
-
- <function model="res.users" name="send_vip_inscription_notice">
- <function eval="[[('vip','=',True)]]" model="res.partner" name="search"/>
- </function>
- </data>
-
- <record id="model_form_view" model="ir.ui.view">
-
- </record>
- </odoo>
-
-.. ignored assert
-
-.. _reference/data/shortcuts:
-
-Shortcuts
-=========
-
-Because some important structural models of Odoo are complex and involved,
-data files provide shorter alternatives to defining them using
-:ref:`record tags <reference/data/record>`:
-
-``menuitem``
-------------
-
-Defines an ``ir.ui.menu`` record with a number of defaults and fallbacks:
-
-``parent``
- * If a ``parent`` attribute is set, it should be the :term:`external id`
- of an other menu item, used as the new item's parent
- * If no ``parent`` is provided, tries to interpret the ``name`` attribute
- as a ``/``-separated sequence of menu names and find a place in the menu
- hierarchy. In that interpretation, intermediate menus are automatically
- created
- * Otherwise the menu is defined as a "top-level" menu item (*not* a menu
- with no parent)
-``name``
- If no ``name`` attribute is specified, tries to get the menu name from
- a linked action if any. Otherwise uses the record's ``id``
-``groups``
- A ``groups`` attribute is interpreted as a comma-separated sequence of
- :term:`external identifiers` for ``res.groups`` models. If an
- :term:`external identifier` is prefixed with a minus (``-``), the group
- is *removed* from the menu's groups
-``action``
- if specified, the ``action`` attribute should be the :term:`external id`
- of an action to execute when the menu is open
-``id``
- the menu item's :term:`external id`
-
-.. _reference/data/template:
-
-``template``
-------------
-
-Creates a :ref:`QWeb view <reference/views/qweb>` requiring only the ``arch``
-section of the view, and allowing a few *optional* attributes:
-
-``id``
- the view's :term:`external identifier`
-``name``, ``inherit_id``, ``priority``
- same as the corresponding field on ``ir.ui.view`` (nb: ``inherit_id``
- should be an :term:`external identifier`)
-``primary``
- if set to ``True`` and combined with a ``inherit_id``, defines the view
- as a primary
-``groups``
- comma-separated list of group :term:`external identifiers`
-``page``
- if set to ``"True"``, the template is a website page (linkable to,
- deletable)
-``optional``
- ``enabled`` or ``disabled``, whether the view can be disabled (in the
- website interface) and its default status. If unset, the view is always
- enabled.
-
-.. deprecated act_window & report
-.. ignored url, act_window and ir_set
-
-CSV data files
-==============
-
-XML data files are flexible and self-descriptive, but very verbose when
-creating a number of simple records of the same model in bulk.
-
-For this case, data files can also use csv_, this is often the case for
-:ref:`access rights <reference/security/acl>`:
-
-* the file name is :file:`{model_name}.csv`
-* the first row lists the fields to write, with the special field ``id``
- for :term:`external identifiers` (used for creation or update)
-* each row thereafter creates a new record
-
-Here's the first lines of the data file defining US states
-``res.country.state.csv``
-
-.. literalinclude:: static/res.country.state.csv
- :language: text
-
-rendered in a more readable format:
-
-.. csv-table::
- :file: static/res.country.state.csv
- :header-rows: 1
- :class: table-striped table-hover table-condensed
-
-For each row (record):
-
-* the first column is the :term:`external id` of the record to create or
- update
-* the second column is the :term:`external id` of the country object to link
- to (country objects must have been defined beforehand)
-* the third column is the ``name`` field for ``res.country.state``
-* the fourth column is the ``code`` field for ``res.country.state``
-
-.. _base64: https://tools.ietf.org/html/rfc3548.html#section-3
-.. _csv: https://en.wikipedia.org/wiki/Comma-separated_values
diff --git a/doc/reference/guidelines.rst b/doc/reference/guidelines.rst
deleted file mode 100644
index 09d8fbe1fd8..00000000000
--- a/doc/reference/guidelines.rst
+++ /dev/null
@@ -1,1158 +0,0 @@
-:banner: banners/odoo_guideline.jpg
-
-.. highlight:: python
-
-.. _reference/guidelines:
-
-===============
-Odoo Guidelines
-===============
-
-This page introduces the Odoo Coding Guidelines. Those aim to improve the
-quality of Odoo Apps code. Indeed proper code improves readability, eases
-maintenance, helps debugging, lowers complexity and promotes reliability.
-These guidelines should be applied to every new module and to all new development.
-
-.. warning::
-
- When modifying existing files in **stable version** the original file style
- strictly supersedes any other style guidelines. In other words please never
- modify existing files in order to apply these guidelines. It avoids disrupting
- the revision history of code lines. Diff should be kept minimal. For more
- details, see our `pull request guide <https://odoo.com/submit-pr>`_.
-
-.. warning::
-
- When modifying existing files in **master (development) version** apply those
- guidelines to existing code only for modified code or if most of the file is
- under revision. In other words modify existing files structure only if it is
- going under major changes. In that case first do a **move** commit then apply
- the changes related to the feature.
-
-Module structure
-================
-
-Directories
------------
-A module is organized in important directories. Those contain the business logic;
-having a look at them should make you understand the purpose of the module.
-
-- *data/* : demo and data xml
-- *models/* : models definition
-- *controllers/* : contains controllers (HTTP routes)
-- *views/* : contains the views and templates
-- *static/* : contains the web assets, separated into *css/, js/, img/, lib/, ...*
-
-Other optional directories compose the module.
-
-- *wizard/* : regroups the transient models (``models.TransientModel``) and their views
-- *report/* : contains the printable reports and models based on SQL views. Python objects and XML views are included in this directory
-- *tests/* : contains the Python tests
-
-
-File naming
------------
-
-File naming is important to quickly find information through all odoo addons.
-This section explains how to name files in a standard odoo module. As an
-example we use a `plant nursery <https://github.com/tivisse/odoodays-2018/tree/master/plant_nursery>`_ application.
-It holds two main models *plant.nursery* and *plant.order*.
-
-Concerning *models*, split the business logic by sets of models belonging to
-a same main model. Each set lies in a given file named based on its main model.
-If there is only one model, its name is the same as the module name. Each
-inherited model should be in its own file to help understanding of impacted
-models.
-
-.. code-block:: text
-
- addons/plant_nursery/
- |-- models/
- | |-- plant_nursery.py (first main model)
- | |-- plant_order.py (another main model)
- | |-- res_partner.py (inherited Odoo model)
-
-Concerning *security* and access rights and rules two main files should be used.
-First one is the definition of access rights done in a ``ir.model.access.csv``
-file. User groups are defined in ``<module>_groups.xml``. Access rules are
-defined in ``<model>_security.xml``.
-
-.. code-block:: text
-
- addons/plant_nursery/
- |-- security/
- | |-- ir.model.access.csv
- | |-- plant_nursery_groups.xml
- | |-- plant_nursery_security.xml
- | |-- plant_order_security.xml
-
-Concerning *views*, backend views should be split like models and suffixed
-by ``_views.xml``. Backend views are list, form, kanban, activity, graph, pivot, ..
-views. To ease split by model in views main menus not linked to specific actions
-may be extracted into an optional ``<module>_menus.xml`` file. Templates (QWeb
-pages used notably for portal / website display) and bundles (import of JS and
-CSS assets) are put in separate files. Those are respectively
-``<model>_templates.xml`` and ``assets.xml`` files.
-
-.. code-block:: text
-
- addons/plant_nursery/
- |-- views/
- | | -- assets.xml (import of JS / CSS)
- | | -- plant_nursery_menus.xml (optional definition of main menus)
- | | -- plant_nursery_views.xml (backend views)
- | | -- plant_nursery_templates.xml (portal templates)
- | | -- plant_order_views.xml
- | | -- plant_order_templates.xml
- | | -- res_partner_views.xml
-
-Concerning *data*, split them by purpose (demo or data) and main model. Filenames
-will be the main_model name suffixed by ``_demo.xml`` or ``_data.xml``. For instance
-for an application having demo and data for its main model as well as subtypes,
-activities and mail templates all related to mail module:
-
-.. code-block:: text
-
- addons/plant_nursery/
- |-- data/
- | |-- plant_nursery_data.xml
- | |-- plant_nursery_demo.xml
- | |-- mail_data.xml
-
-Concerning *controllers*, generally all controllers belong to a single controller
-contained in a file named ``<module_name>.py``. An old convention in Odoo is to
-name this file ``main.py`` but it is considered as outdated. If you need to inherit
-an existing controller from another module do it in ``<inherited_module_name>.py``.
-For example adding portal controller in an application is done in ``portal.py``.
-
-.. code-block:: text
-
- addons/plant_nursery/
- |-- controllers/
- | |-- plant_nursery.py
- | |-- portal.py (inheriting portal/controllers/portal.py)
- | |-- main.py (deprecated, replaced by plant_nursery.py)
-
-Concerning *static files*, Javascript files follow globally the same logic as
-python models. Each component should be in its own file with a meaningful name.
-For instance, the activity widgets are located in ``activity.js`` of mail module.
-Subdirectories can also be created to structure the 'package' (see web module
-for more details). The same logic should be applied for the templates of JS
-widgets (static XML files) and for their styles (scss files). Don't link
-data (image, libraries) outside Odoo: do not use an URL to an image but copy
-it in the codebase instead.
-
-Concerning *wizards*, naming convention is the same of for python models:
-``<transient>.py`` and ``<transient>_views.xml``. Both are put in the wizard
-directory. This naming comes from old odoo applications using the wizard
-keyword for transient models.
-
-.. code-block:: text
-
- addons/plant_nursery/
- |-- wizard/
- | |-- make_plant_order.py
- | |-- make_plant_order_views.xml
-
-Concerning *statistics reports* done with python / SQL views and classic views
-naming is the following :
-
-.. code-block:: text
-
- addons/plant_nursery/
- |-- report/
- | |-- plant_order_report.py
- | |-- plant_order_report_views.xml
-
-Concerning *printable reports* which contain mainly data preparation and Qweb
-templates naming is the following :
-
-.. code-block:: text
-
- addons/plant_nursery/
- |-- report/
- | |-- plant_order_reports.xml (report actions, paperformat, ...)
- | |-- plant_order_templates.xml (xml report templates)
-
-The complete tree of our Odoo module therefore looks like
-
-.. code-block:: text
-
- addons/plant_nursery/
- |-- __init__.py
- |-- __manifest__.py
- |-- controllers/
- | |-- __init__.py
- | |-- plant_nursery.py
- | |-- portal.py
- |-- data/
- | |-- plant_nursery_data.xml
- | |-- plant_nursery_demo.xml
- | |-- mail_data.xml
- |-- models/
- | |-- __init__.py
- | |-- plant_nursery.py
- | |-- plant_order.py
- | |-- res_partner.py
- |-- report/
- | |-- __init__.py
- | |-- plant_order_report.py
- | |-- plant_order_report_views.xml
- | |-- plant_order_reports.xml (report actions, paperformat, ...)
- | |-- plant_order_templates.xml (xml report templates)
- |-- security/
- | |-- ir.model.access.csv
- | |-- plant_nursery_groups.xml
- | |-- plant_nursery_security.xml
- | |-- plant_order_security.xml
- |-- static/
- | |-- img/
- | | |-- my_little_kitten.png
- | | |-- troll.jpg
- | |-- lib/
- | | |-- external_lib/
- | |-- src/
- | | |-- js/
- | | | |-- widget_a.js
- | | | |-- widget_b.js
- | | |-- scss/
- | | | |-- widget_a.scss
- | | | |-- widget_b.scss
- | | |-- xml/
- | | | |-- widget_a.xml
- | | | |-- widget_a.xml
- |-- views/
- | |-- assets.xml
- | |-- plant_nursery_menus.xml
- | |-- plant_nursery_views.xml
- | |-- plant_nursery_templates.xml
- | |-- plant_order_views.xml
- | |-- plant_order_templates.xml
- | |-- res_partner_views.xml
- |-- wizard/
- | |--make_plant_order.py
- | |--make_plant_order_views.xml
-
-.. note:: File names should only contain ``[a-z0-9_]`` (lowercase
- alphanumerics and ``_``)
-
-.. warning:: Use correct file permissions : folder 755 and file 644.
-
-.. _reference/guidelines/xml:
-
-XML files
-=========
-
-Format
-------
-To declare a record in XML, the **record** notation (using *<record>*) is recommended:
-
-- Place ``id`` attribute before ``model``
-- For field declaration, ``name`` attribute is first. Then place the
- *value* either in the ``field`` tag, either in the ``eval``
- attribute, and finally other attributes (widget, options, ...)
- ordered by importance.
-
-- Try to group the record by model. In case of dependencies between
- action/menu/views, this convention may not be applicable.
-- Use naming convention defined at the next point
-- The tag *<data>* is only used to set not-updatable data with ``noupdate=1``.
- If there is only not-updatable data in the file, the ``noupdate=1`` can be
- set on the ``<odoo>`` tag and do not set a ``<data>`` tag.
-
-.. code-block:: xml
-
- <record id="view_id" model="ir.ui.view">
- <field name="name">view.name</field>
- <field name="model">object_name</field>
- <field name="priority" eval="16"/>
- <field name="arch" type="xml">
- <tree>
- <field name="my_field_1"/>
- <field name="my_field_2" string="My Label" widget="statusbar" statusbar_visible="draft,sent,progress,done" />
- </tree>
- </field>
- </record>
-
-Odoo supports custom tags acting as syntactic sugar:
-
-- menuitem: use it as a shortcut to declare a ``ir.ui.menu``
-- template: use it to declare a QWeb View requiring only the ``arch`` section of the view.
-- report: use to declare a :ref:`report action <reference/actions/report>`
-- act_window: use it if the record notation can't do what you want
-
-The 4 first tags are preferred over the *record* notation.
-
-
-XML IDs and naming
-------------------
-
-Security, View and Action
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Use the following pattern :
-
-* For a menu: :samp:`{<model_name>}_menu`, or :samp:`{<model_name>}_menu_{do_stuff}` for submenus.
-* For a view: :samp:`{<model_name>}_view_{<view_type>}`, where *view_type* is
- ``kanban``, ``form``, ``tree``, ``search``, ...
-* For an action: the main action respects :samp:`{<model_name>}_action`.
- Others are suffixed with :samp:`_{<detail>}`, where *detail* is a
- lowercase string briefly explaining the action. This is used only if
- multiple actions are declared for the model.
-* For window actions: suffix the action name by the specific view information
- like :samp:`{<model_name>}_action_view_{<view_type>}`.
-* For a group: :samp:`{<module_name>}_group_{<group_name>}` where *group_name*
- is the name of the group, generally 'user', 'manager', ...
-* For a rule: :samp:`{<model_name>}_rule_{<concerned_group>}` where
- *concerned_group* is the short name of the concerned group ('user'
- for the 'model_name_group_user', 'public' for public user, 'company'
- for multi-company rules, ...).
-
-Name should be identical to xml id with dots replacing underscores. Actions
-should have a real naming as it is used as display name.
-
-.. code-block:: xml
-
- <!-- views -->
- <record id="model_name_view_form" model="ir.ui.view">
- <field name="name">model.name.view.form</field>
- ...
- </record>
-
- <record id="model_name_view_kanban" model="ir.ui.view">
- <field name="name">model.name.view.kanban</field>
- ...
- </record>
-
- <!-- actions -->
- <record id="model_name_action" model="ir.act.window">
- <field name="name">Model Main Action</field>
- ...
- </record>
-
- <record id="model_name_action_child_list" model="ir.actions.act_window">
- <field name="name">Model Access Childs</field>
- </record>
-
- <!-- menus and sub-menus -->
- <menuitem
- id="model_name_menu_root"
- name="Main Menu"
- sequence="5"
- />
- <menuitem
- id="model_name_menu_action"
- name="Sub Menu 1"
- parent="module_name.module_name_menu_root"
- action="model_name_action"
- sequence="10"
- />
-
- <!-- security -->
- <record id="module_name_group_user" model="res.groups">
- ...
- </record>
-
- <record id="model_name_rule_public" model="ir.rule">
- ...
- </record>
-
- <record id="model_name_rule_company" model="ir.rule">
- ...
- </record>
-
-Inheriting XML
-~~~~~~~~~~~~~~
-
-Xml Ids of inheriting views should use the same ID as the original record.
-It helps finding all inheritance at a glance. As final Xml Ids are prefixed
-by the module that creates them there is no overlap.
-
-Naming should contain an ``.inherit.{details}`` suffix to ease understanding
-the override purpose when looking at its name.
-
-.. code-block:: xml
-
- <record id="model_view_form" model="ir.ui.view">
- <field name="name">model.view.form.inherit.module2</field>
- <field name="inherit_id" ref="module1.model_view_form"/>
- ...
- </record>
-
-New primary views do not require the inherit suffix as those are new records
-based upon the first one.
-
-.. code-block:: xml
-
- <record id="module2.model_view_form" model="ir.ui.view">
- <field name="name">model.view.form.module2</field>
- <field name="inherit_id" ref="module1.model_view_form"/>
- <field name="mode">primary</field>
- ...
- </record>
-
-.. _reference/guidelines/python:
-
-Python
-======
-
-.. warning::
-
- Do not forget to read the :ref:`Security Pitfalls <reference/security/pitfalls>`
- section as well to write secure code.
-
-PEP8 options
-------------
-
-Using a linter can help show syntax and semantic warnings or errors. Odoo
-source code tries to respect Python standard, but some of them can be ignored.
-
-- E501: line too long
-- E301: expected 1 blank line, found 0
-- E302: expected 2 blank lines, found 1
-
-Imports
--------
-The imports are ordered as
-
-#. External libraries (one per line sorted and split in python stdlib)
-#. Imports of ``odoo``
-#. Imports from Odoo modules (rarely, and only if necessary)
-
-Inside these 3 groups, the imported lines are alphabetically sorted.
-
-.. code-block:: python
-
- # 1 : imports of python lib
- import base64
- import re
- import time
- from datetime import datetime
- # 2 : imports of odoo
- import odoo
- from odoo import api, fields, models, _ # alphabetically ordered
- from odoo.tools.safe_eval import safe_eval as eval
- # 3 : imports from odoo addons
- from odoo.addons.website.models.website import slug
- from odoo.addons.web.controllers.main import login_redirect
-
-Idiomatics of Programming (Python)
-----------------------------------
-
-- Each python file should have ``# -*- coding: utf-8 -*-`` as first line.
-- Always favor *readability* over *conciseness* or using the language features or idioms.
-- Don't use ``.clone()``
-
-.. code-block:: python
-
- # bad
- new_dict = my_dict.clone()
- new_list = old_list.clone()
- # good
- new_dict = dict(my_dict)
- new_list = list(old_list)
-
-- Python dictionary : creation and update
-
-.. code-block:: python
-
- # -- creation empty dict
- my_dict = {}
- my_dict2 = dict()
-
- # -- creation with values
- # bad
- my_dict = {}
- my_dict['foo'] = 3
- my_dict['bar'] = 4
- # good
- my_dict = {'foo': 3, 'bar': 4}
-
- # -- update dict
- # bad
- my_dict['foo'] = 3
- my_dict['bar'] = 4
- my_dict['baz'] = 5
- # good
- my_dict.update(foo=3, bar=4, baz=5)
- my_dict = dict(my_dict, **my_dict2)
-
-- Use meaningful variable/class/method names
-- Useless variable : Temporary variables can make the code clearer by giving
- names to objects, but that doesn't mean you should create temporary variables
- all the time:
-
-.. code-block:: python
-
- # pointless
- schema = kw['schema']
- params = {'schema': schema}
- # simpler
- params = {'schema': kw['schema']}
-
-- Multiple return points are OK, when they're simpler
-
-.. code-block:: python
-
- # a bit complex and with a redundant temp variable
- def axes(self, axis):
- axes = []
- if type(axis) == type([]):
- axes.extend(axis)
- else:
- axes.append(axis)
- return axes
-
- # clearer
- def axes(self, axis):
- if type(axis) == type([]):
- return list(axis) # clone the axis
- else:
- return [axis] # single-element list
-
-- Know your builtins : You should at least have a basic understanding of all
- the Python builtins (http://docs.python.org/library/functions.html)
-
-.. code-block:: python
-
- value = my_dict.get('key', None) # very very redundant
- value = my_dict.get('key') # good
-
-Also, ``if 'key' in my_dict`` and ``if my_dict.get('key')`` have very different
-meaning, be sure that you're using the right one.
-
-- Learn list comprehensions : Use list comprehension, dict comprehension, and
- basic manipulation using ``map``, ``filter``, ``sum``, ... They make the code
- easier to read.
-
-.. code-block:: python
-
- # not very good
- cube = []
- for i in res:
- cube.append((i['id'],i['name']))
- # better
- cube = [(i['id'], i['name']) for i in res]
-
-- Collections are booleans too : In python, many objects have "boolean-ish" value
- when evaluated in a boolean context (such as an if). Among these are collections
- (lists, dicts, sets, ...) which are "falsy" when empty and "truthy" when containing
- items:
-
-.. code-block:: python
-
- bool([]) is False
- bool([1]) is True
- bool([False]) is True
-
-So, you can write ``if some_collection:`` instead of ``if len(some_collection):``.
-
-
-- Iterate on iterables
-
-.. code-block:: python
-
- # creates a temporary list and looks bar
- for key in my_dict.keys():
- "do something..."
- # better
- for key in my_dict:
- "do something..."
- # accessing the key,value pair
- for key, value in my_dict.items():
- "do something..."
-
-- Use dict.setdefault
-
-.. code-block:: python
-
- # longer.. harder to read
- values = {}
- for element in iterable:
- if element not in values:
- values[element] = []
- values[element].append(other_value)
-
- # better.. use dict.setdefault method
- values = {}
- for element in iterable:
- values.setdefault(element, []).append(other_value)
-
-- As a good developer, document your code (docstring on methods, simple
- comments for tricky part of code)
-- In additions to these guidelines, you may also find the following link
- interesting: http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html
- (a little bit outdated, but quite relevant)
-
-Programming in Odoo
--------------------
-
-- Avoid to create generators and decorators: only use the ones provided by
- the Odoo API.
-- As in python, use ``filtered``, ``mapped``, ``sorted``, ... methods to
- ease code reading and performance.
-
-
-Make your method work in batch
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-When adding a function, make sure it can process multiple records by iterating
-on self to treat each record.
-
-.. code-block:: python
-
- def my_method(self)
- for record in self:
- record.do_cool_stuff()
-
-For performance issue, when developing a 'stat button' (for instance), do not
-perform a ``search`` or a ``search_count`` in a loop. It
-is recommended to use ``read_group`` method, to compute all value in only one request.
-
-.. code-block:: python
-
- def _compute_equipment_count(self):
- """ Count the number of equipment per category """
- equipment_data = self.env['hr.equipment'].read_group([('category_id', 'in', self.ids)], ['category_id'], ['category_id'])
- mapped_data = dict([(m['category_id'][0], m['category_id_count']) for m in equipment_data])
- for category in self:
- category.equipment_count = mapped_data.get(category.id, 0)
-
-
-Propagate the context
-~~~~~~~~~~~~~~~~~~~~~
-The context is a ``frozendict`` that cannot be modified. To call a method with
-a different context, the ``with_context`` method should be used :
-
-.. code-block:: python
-
- records.with_context(new_context).do_stuff() # all the context is replaced
- records.with_context(**additionnal_context).do_other_stuff() # additionnal_context values override native context ones
-
-.. warning::
- Passing parameter in context can have dangerous side-effects.
-
- Since the values are propagated automatically, some unexpected behavior may appear.
- Calling ``create()`` method of a model with *default_my_field* key in context
- will set the default value of *my_field* for the concerned model.
- But if during this creation, other objects (such as sale.order.line, on sale.order creation)
- having a field name *my_field* are created, their default value will be set too.
-
-If you need to create a key context influencing the behavior of some object,
-choice a good name, and eventually prefix it by the name of the module to
-isolate its impact. A good example are the keys of ``mail`` module :
-*mail_create_nosubscribe*, *mail_notrack*, *mail_notify_user_signature*, ...
-
-
-Think extendable
-~~~~~~~~~~~~~~~~
-
-Functions and methods should not contain too much logic: having a lot of small
-and simple methods is more advisable than having few large and complex methods.
-A good rule of thumb is to split a method as soon as it has more than one
-responsibility (see http://en.wikipedia.org/wiki/Single_responsibility_principle).
-
-Hardcoding a business logic in a method should be avoided as it prevents to be
-easily extended by a submodule.
-
-.. code-block:: python
-
- # do not do this
- # modifying the domain or criteria implies overriding whole method
- def action(self):
- ... # long method
- partners = self.env['res.partner'].search(complex_domain)
- emails = partners.filtered(lambda r: arbitrary_criteria).mapped('email')
-
- # better but do not do this either
- # modifying the logic forces to duplicate some parts of the code
- def action(self):
- ...
- partners = self._get_partners()
- emails = partners._get_emails()
-
- # better
- # minimum override
- def action(self):
- ...
- partners = self.env['res.partner'].search(self._get_partner_domain())
- emails = partners.filtered(lambda r: r._filter_partners()).mapped('email')
-
-The above code is over extendable for the sake of example but the readability
-must be taken into account and a tradeoff must be made.
-
-Also, name your functions accordingly: small and properly named functions are
-the starting point of readable/maintainable code and tighter documentation.
-
-This recommendation is also relevant for classes, files, modules and packages.
-(See also http://en.wikipedia.org/wiki/Cyclomatic_complexity)
-
-
-Never commit the transaction
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The Odoo framework is in charge of providing the transactional context for
-all RPC calls. The principle is that a new database cursor is opened at the
-beginning of each RPC call, and committed when the call has returned, just
-before transmitting the answer to the RPC client, approximately like this:
-
-.. code-block:: python
-
- def execute(self, db_name, uid, obj, method, *args, **kw):
- db, pool = pooler.get_db_and_pool(db_name)
- # create transaction cursor
- cr = db.cursor()
- try:
- res = pool.execute_cr(cr, uid, obj, method, *args, **kw)
- cr.commit() # all good, we commit
- except Exception:
- cr.rollback() # error, rollback everything atomically
- raise
- finally:
- cr.close() # always close cursor opened manually
- return res
-
-If any error occurs during the execution of the RPC call, the transaction is
-rolled back atomically, preserving the state of the system.
-
-Similarly, the system also provides a dedicated transaction during the execution
-of tests suites, so it can be rolled back or not depending on the server
-startup options.
-
-The consequence is that if you manually call ``cr.commit()`` anywhere there is
-a very high chance that you will break the system in various ways, because you
-will cause partial commits, and thus partial and unclean rollbacks, causing
-among others:
-
-#. inconsistent business data, usually data loss
-#. workflow desynchronization, documents stuck permanently
-#. tests that can't be rolled back cleanly, and will start polluting the
- database, and triggering error (this is true even if no error occurs
- during the transaction)
-
-Here is the very simple rule:
- You should **NEVER** call ``cr.commit()`` yourself, **UNLESS** you have
- created your own database cursor explicitly! And the situations where you
- need to do that are exceptional!
-
- And by the way if you did create your own cursor, then you need to handle
- error cases and proper rollback, as well as properly close the cursor when
- you're done with it.
-
-And contrary to popular belief, you do not even need to call ``cr.commit()``
-in the following situations:
-- in the ``_auto_init()`` method of an *models.Model* object: this is taken
-care of by the addons initialization method, or by the ORM transaction when
-creating custom models
-- in reports: the ``commit()`` is handled by the framework too, so you can
-update the database even from within a report
-- within *models.Transient* methods: these methods are called exactly like
-regular *models.Model* ones, within a transaction and with the corresponding
-``cr.commit()/rollback()`` at the end
-- etc. (see general rule above if you have in doubt!)
-
-All ``cr.commit()`` calls outside of the server framework from now on must
-have an **explicit comment** explaining why they are absolutely necessary, why
-they are indeed correct, and why they do not break the transactions. Otherwise
-they can and will be removed !
-
-
-Use translation method correctly
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Odoo uses a GetText-like method named "underscore" ``_( )`` to indicate that
-a static string used in the code needs to be translated at runtime using the
-language of the context. This pseudo-method is accessed within your code by
-importing as follows:
-
-.. code-block:: python
-
- from odoo import _
-
-A few very important rules must be followed when using it, in order for it to
-work and to avoid filling the translations with useless junk.
-
-Basically, this method should only be used for static strings written manually
-in the code, it will not work to translate field values, such as Product names,
-etc. This must be done instead using the translate flag on the corresponding
-field.
-
-The method accepts optional positional or named parameter
-The rule is very simple: calls to the underscore method should always be in
-the form ``_('literal string')`` and nothing else:
-
-.. code-block:: python
-
- # good: plain strings
- error = _('This record is locked!')
-
- # good: strings with formatting patterns included
- error = _('Record %s cannot be modified!', record)
-
- # ok too: multi-line literal strings
- error = _("""This is a bad multiline example
- about record %s!""", record)
- error = _('Record %s cannot be modified' \
- 'after being validated!', record)
-
- # bad: tries to translate after string formatting
- # (pay attention to brackets!)
- # This does NOT work and messes up the translations!
- error = _('Record %s cannot be modified!' % record)
-
- # bad: formatting outside of translation
- # This won't benefit from fallback mechanism in case of bad translation
- error = _('Record %s cannot be modified!') % record
-
- # bad: dynamic string, string concatenation, etc are forbidden!
- # This does NOT work and messes up the translations!
- error = _("'" + que_rec['question'] + "' \n")
-
- # bad: field values are automatically translated by the framework
- # This is useless and will not work the way you think:
- error = _("Product %s is out of stock!") % _(product.name)
- # and the following will of course not work as already explained:
- error = _("Product %s is out of stock!" % product.name)
-
- # Instead you can do the following and everything will be translated,
- # including the product name if its field definition has the
- # translate flag properly set:
- error = _("Product %s is not available!", product.name)
-
-
-Also, keep in mind that translators will have to work with the literal values
-that are passed to the underscore function, so please try to make them easy to
-understand and keep spurious characters and formatting to a minimum. Translators
-must be aware that formatting patterns such as ``%s`` or ``%d``, newlines, etc.
-need to be preserved, but it's important to use these in a sensible and obvious
-manner:
-
-.. code-block:: python
-
- # Bad: makes the translations hard to work with
- error = "'" + question + _("' \nPlease enter an integer value ")
-
- # Ok (pay attention to position of the brackets too!)
- error = _("Answer to question %s is not valid.\n" \
- "Please enter an integer value.", question)
-
- # Better
- error = _("Answer to question %(title)s is not valid.\n" \
- "Please enter an integer value.", title=question)
-
-In general in Odoo, when manipulating strings, prefer ``%`` over ``.format()``
-(when only one variable to replace in a string), and prefer ``%(varname)`` instead
-of position (when multiple variables have to be replaced). This makes the
-translation easier for the community translators.
-
-
-Symbols and Conventions
------------------------
-
-- Model name (using the dot notation, prefix by the module name) :
- - When defining an Odoo Model : use singular form of the name (*res.partner*
- and *sale.order* instead of *res.partnerS* and *saleS.orderS*)
- - When defining an Odoo Transient (wizard) : use ``<related_base_model>.<action>``
- where *related_base_model* is the base model (defined in *models/*) related
- to the transient, and *action* is the short name of what the transient do. Avoid the *wizard* word.
- For instance : ``account.invoice.make``, ``project.task.delegate.batch``, ...
- - When defining *report* model (SQL views e.i.) : use
- ``<related_base_model>.report.<action>``, based on the Transient convention.
-
-- Odoo Python Class : use camelcase (Object-oriented style).
-
-
-.. code-block:: python
-
- class AccountInvoice(models.Model):
- ...
-
-- Variable name :
- - use camelcase for model variable
- - use underscore lowercase notation for common variable.
- - suffix your variable name with *_id* or *_ids* if it contains a record id or list of id. Don't use ``partner_id`` to contain a record of res.partner
-
-.. code-block:: python
-
- Partner = self.env['res.partner']
- partners = Partner.browse(ids)
- partner_id = partners[0].id
-
-- ``One2Many`` and ``Many2Many`` fields should always have *_ids* as suffix (example: sale_order_line_ids)
-- ``Many2One`` fields should have *_id* as suffix (example : partner_id, user_id, ...)
-- Method conventions
- - Compute Field : the compute method pattern is *_compute_<field_name>*
- - Search method : the search method pattern is *_search_<field_name>*
- - Default method : the default method pattern is *_default_<field_name>*
- - Selection method: the selection method pattern is *_selection_<field_name>*
- - Onchange method : the onchange method pattern is *_onchange_<field_name>*
- - Constraint method : the constraint method pattern is *_check_<constraint_name>*
- - Action method : an object action method is prefix with *action_*.
- Since it uses only one record, add ``self.ensure_one()``
- at the beginning of the method.
-
-- In a Model attribute order should be
- #. Private attributes (``_name``, ``_description``, ``_inherit``, ...)
- #. Default method and ``_default_get``
- #. Field declarations
- #. Compute, inverse and search methods in the same order as field declaration
- #. Selection method (methods used to return computed values for selection fields)
- #. Constrains methods (``@api.constrains``) and onchange methods (``@api.onchange``)
- #. CRUD methods (ORM overrides)
- #. Action methods
- #. And finally, other business methods.
-
-.. code-block:: python
-
- class Event(models.Model):
- # Private attributes
- _name = 'event.event'
- _description = 'Event'
-
- # Default methods
- def _default_name(self):
- ...
-
- # Fields declaration
- name = fields.Char(string='Name', default=_default_name)
- seats_reserved = fields.Integer(oldname='register_current', string='Reserved Seats',
- store=True, readonly=True, compute='_compute_seats')
- seats_available = fields.Integer(oldname='register_avail', string='Available Seats',
- store=True, readonly=True, compute='_compute_seats')
- price = fields.Integer(string='Price')
- event_type = fields.Selection(string="Type", selection='_selection_type')
-
- # compute and search fields, in the same order of fields declaration
- @api.depends('seats_max', 'registration_ids.state', 'registration_ids.nb_register')
- def _compute_seats(self):
- ...
-
- @api.model
- def _selection_type(self):
- return []
-
- # Constraints and onchanges
- @api.constrains('seats_max', 'seats_available')
- def _check_seats_limit(self):
- ...
-
- @api.onchange('date_begin')
- def _onchange_date_begin(self):
- ...
-
- # CRUD methods (and name_get, name_search, ...) overrides
- def create(self, values):
- ...
-
- # Action methods
- def action_validate(self):
- self.ensure_one()
- ...
-
- # Business methods
- def mail_user_confirm(self):
- ...
-
-.. _reference/guidelines/js:
-
-Javascript and CSS
-==================
-
-Static files organization
---------------------------
-
-Odoo addons have some conventions on how to structure various files. We explain
-here in more details how web assets are supposed to be organized.
-
-The first thing to know is that the Odoo server will serve (statically) all files
-located in a *static/* folder, but prefixed with the addon name. So, for example,
-if a file is located in *addons/web/static/src/js/some_file.js*, then it will be
-statically available at the url *your-odoo-server.com/web/static/src/js/some_file.js*
-
-The convention is to organize the code according to the following structure:
-
-- *static*: all static files in general
-
- - *static/lib*: this is the place where js libs should be located, in a sub folder.
- So, for example, all files from the *jquery* library are in *addons/web/static/lib/jquery*
- - *static/src*: the generic static source code folder
-
- - *static/src/css*: all css files
- - *static/src/fonts*
- - *static/src/img*
- - *static/src/js*
-
- - *static/src/js/tours*: end user tour files (tutorials, not tests)
-
- - *static/src/scss*: scss files
- - *static/src/xml*: all qweb templates that will be rendered in JS
-
- - *static/tests*: this is where we put all test related files.
-
- - *static/tests/tours*: this is where we put all tour test files (not tutorials).
-
-Javascript coding guidelines
-----------------------------
-
-- ``use strict;`` is recommended for all javascript files
-- Use a linter (jshint, ...)
-- Never add minified Javascript Libraries
-- Use camelcase for class declaration
-
-More precise JS guidelines are detailed in the `github wiki <https://github.com/odoo/odoo/wiki/Javascript-coding-guidelines>`_.
-You may also have a look at existing API in Javascript by looking Javascript
-References.
-
-CSS coding guidelines
----------------------
-
-- Prefix all your classes with *o_<module_name>* where *module_name* is the
- technical name of the module ('sale', 'im_chat', ...) or the main route
- reserved by the module (for website module mainly, i.e. : 'o_forum' for
- *website_forum* module). The only exception for this rule is the
- webclient: it simply uses *o_* prefix.
-- Avoid using *id* tag
-- Use Bootstrap native classes
-- Use underscore lowercase notation to name class
-
-.. _reference/guidelines/git:
-
-Git
-===
-
-Configure your git
-------------------
-
-Based on ancestral experience and oral tradition, the following things go a long
-way towards making your commits more helpful:
-
-- Be sure to define both the user.email and user.name in your local git config
-
- .. code-block:: text
-
- git config --global <var> <value>
-
-- Be sure to add your full name to your Github profile here. Please feel fancy
- and add your team, avatar, your favorite quote, and whatnot ;-)
-
-Commit message structure
-------------------------
-
-Commit message has four parts: tag, module, short description and full
-description. Try to follow the preferred structure for your commit messages
-
-.. code-block:: text
-
- [TAG] module: describe your change in a short sentence (ideally < 50 chars)
-
- Long version of the change description, including the rationale for the change,
- or a summary of the feature being introduced.
-
- Please spend a lot more time describing WHY the change is being done rather
- than WHAT is being changed. This is usually easy to grasp by actually reading
- the diff. WHAT should be explained only if there are technical choices
- or decision involved. In that case explain WHY this decision was taken.
-
- End the message with references, such as task or bug numbers, PR numbers, and
- OPW tickets, following the suggested format:
- task-123 (related to task)
- Fixes #123 (close related issue on Github)
- Closes #123 (close related PR on Github)
- opw-123 (related to ticket)
-
-Tag and module name
--------------------
-
-Tags are used to prefix your commit. They should be one of the following
-
-- **[FIX]** for bug fixes: mostly used in stable version but also valid if you
- are fixing a recent bug in development version;
-- **[REF]** for refactoring: when a feature is heavily rewritten;
-- **[ADD]** for adding new modules;
-- **[REM]** for removing resources: removing dead code, removing views,
- removing modules, ...;
-- **[REV]** for reverting commits: if a commit causes issues or is not wanted
- reverting it is done using this tag;
-- **[MOV]** for moving files: use git move and do not change content of moved file
- otherwise Git may loose track and history of the file; also used when moving
- code from one file to another;
-- **[REL]** for release commits: new major or minor stable versions;
-- **[IMP]** for improvements: most of the changes done in development version
- are incremental improvements not related to another tag;
-- **[MERGE]** for merge commits: used in forward port of bug fixes but also as
- main commit for feature involving several separated commits;
-- **[CLA]** for signing the Odoo Individual Contributor License;
-- **[I18N]** for changes in translation files;
-
-After tag comes the modified module name. Use the technical name as functional
-name may change with time. If several modules are modified, list them or use
-various to tell it is cross-modules. Unless really required or easier avoid
-modifying code across several modules in the same commit. Understanding module
-history may become difficult.
-
-Commit message header
----------------------
-
-After tag and module name comes a meaningful commit message header. It should be
-self explanatory and include the reason behind the change. Do not use single words
-like "bugfix" or "improvements". Try to limit the header length to about 50 characters
-for readability.
-
-Commit message header should make a valid sentence once concatenated with
-``if applied, this commit will <header>``. For example ``[IMP] base: prevent to
-archive users linked to active partners`` is correct as it makes a valid sentence
-``if applied, this commit will prevent users to archive...``.
-
-Commit message full description
--------------------------------
-
-In the message description specify the part of the code impacted by your changes
-(module name, lib, transversal object, ...) and a description of the changes.
-
-First explain WHY you are modifying code. What is important if someone goes back
-to your commit in about 4 decades (or 3 days) is why you did it. It is the
-purpose of the change.
-
-What you did can be found in the commit itself. If there was some technical choices
-involved it is a good idea to explain it also in the commit message after the why.
-For Odoo R&D developers "PO team asked me to do it" is not a valid why, by the way.
-
-Please avoid commits which simultaneously impact multiple modules. Try to split
-into different commits where impacted modules are different. It will be helpful
-if we need to revert changes in a given module separately.
-
-Don't hesitate to be a bit verbose. Most people will only see your commit message
-and judge everything you did in your life just based on those few sentences.
-No pressure at all.
-
-**You spend several hours, days or weeks working on meaningful features. Take
-some time to calm down and write clear and understandable commit messages.**
-
-If you are an Odoo R&D developer the WHY should be the purpose of the task you
-are working on. Full specifications make the core of the commit message.
-**If you are working on a task that lacks purpose and specifications please
-consider making them clear before continuing.**
-
-Finally here are some examples of correct commit messages :
-
-.. code-block:: text
-
- [REF] models: use `parent_path` to implement parent_store
-
- This replaces the former modified preorder tree traversal (MPTT) with the
- fields `parent_left`/`parent_right`[...]
-
- [FIX] account: remove frenglish
-
- [...]
-
- Closes #22793
- Fixes #22769
-
- [FIX] website: remove unused alert div, fixes look of input-group-btn
-
- Bootstrap's CSS depends on the input-group-btn
- element being the first/last child of its parent.
- This was not the case because of the invisible
- and useless alert.
-
-.. note:: Use the long description to explain the *why* not the
- *what*, the *what* can be seen in the diff
diff --git a/doc/reference/http.rst b/doc/reference/http.rst
deleted file mode 100644
index 0675847aaa0..00000000000
--- a/doc/reference/http.rst
+++ /dev/null
@@ -1,87 +0,0 @@
-:banner: banners/web_controllers.jpg
-
-.. _reference/controllers:
-
-===============
-Web Controllers
-===============
-
-Controllers
-===========
-
-Controllers need to provide extensibility, much like
-:class:`~odoo.models.Model`, but can't use the same mechanism as the
-pre-requisites (a database with loaded modules) may not be available yet (e.g.
-no database created, or no database selected).
-
-Controllers thus provide their own extension mechanism, separate from that of
-models:
-
-Controllers are created by :ref:`inheriting <python:tut-inheritance>` from :class:`~odoo.http.Controller`.
-Routes are defined through methods decorated with :func:`~odoo.http.route`::
-
- class MyController(odoo.http.Controller):
- @route('/some_url', auth='public')
- def handler(self):
- return stuff()
-
-To *override* a controller, :ref:`inherit <python:tut-inheritance>` from its
-class and override relevant methods, re-exposing them if necessary::
-
- class Extension(MyController):
- @route()
- def handler(self):
- do_before()
- return super(Extension, self).handler()
-
-* decorating with :func:`~odoo.http.route` is necessary to keep the method
- (and route) visible: if the method is redefined without decorating, it
- will be "unpublished"
-* the decorators of all methods are combined, if the overriding method's
- decorator has no argument all previous ones will be kept, any provided
- argument will override previously defined ones e.g.::
-
- class Restrict(MyController):
- @route(auth='user')
- def handler(self):
- return super(Restrict, self).handler()
-
- will change ``/some_url`` from public authentication to user (requiring a
- log-in)
-
-API
-===
-
-.. _reference/http/routing:
-
-Routing
--------
-
-.. autofunction:: odoo.http.route
-
-.. _reference/http/request:
-
-Request
--------
-
-The request object is automatically set on :data:`odoo.http.request` at
-the start of the request
-
-.. autoclass:: odoo.http.WebRequest
- :members:
- :member-order: bysource
-.. autoclass:: odoo.http.HttpRequest
- :members:
-.. autoclass:: odoo.http.JsonRequest
- :members:
-
-Response
---------
-
-.. autoclass:: odoo.http.Response
- :members:
- :member-order: bysource
-
- .. maybe set this to document all the fine methods on Werkzeug's Response
- object? (it works)
- :inherited-members:
diff --git a/doc/reference/images/mobile_contact_create.png b/doc/reference/images/mobile_contact_create.png
deleted file mode 100644
index 53ca9e4998f..00000000000
--- a/doc/reference/images/mobile_contact_create.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/mobile_notification.png b/doc/reference/images/mobile_notification.png
deleted file mode 100644
index ea72d7a6d24..00000000000
--- a/doc/reference/images/mobile_notification.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/mobile_switch_account.png b/doc/reference/images/mobile_switch_account.png
deleted file mode 100644
index 3c7da3e2c31..00000000000
--- a/doc/reference/images/mobile_switch_account.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/mobile_working.jpg b/doc/reference/images/mobile_working.jpg
deleted file mode 100644
index 8243530f715..00000000000
--- a/doc/reference/images/mobile_working.jpg
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/odoo_mobile_api.png b/doc/reference/images/odoo_mobile_api.png
deleted file mode 100644
index 8fdf37942f4..00000000000
--- a/doc/reference/images/odoo_mobile_api.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/runner.png b/doc/reference/images/runner.png
deleted file mode 100644
index fba0121caf5..00000000000
--- a/doc/reference/images/runner.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/runner2.png b/doc/reference/images/runner2.png
deleted file mode 100644
index 45bb5c5585b..00000000000
--- a/doc/reference/images/runner2.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/snackbar.png b/doc/reference/images/snackbar.png
deleted file mode 100644
index 76ad0f1a9ad..00000000000
--- a/doc/reference/images/snackbar.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/tests.png b/doc/reference/images/tests.png
deleted file mode 100644
index 11552c0539d..00000000000
--- a/doc/reference/images/tests.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/tests2.png b/doc/reference/images/tests2.png
deleted file mode 100644
index b51da6b9e72..00000000000
--- a/doc/reference/images/tests2.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/tests3.png b/doc/reference/images/tests3.png
deleted file mode 100644
index e89da505f52..00000000000
--- a/doc/reference/images/tests3.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/images/toast.png b/doc/reference/images/toast.png
deleted file mode 100644
index d6f5298a722..00000000000
--- a/doc/reference/images/toast.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/iot.rst b/doc/reference/iot.rst
deleted file mode 100644
index 2a7e8d66eb1..00000000000
--- a/doc/reference/iot.rst
+++ /dev/null
@@ -1,195 +0,0 @@
-:banner: banners/iot.jpg
-
-==================
-Internet of Things
-==================
-
-IoT Drivers allow any Odoo module to communicate in real-time with any device
-connected to the IoT Box. Communication with the IoT Box goes both ways, so the
-Odoo client can send commands to and receive information from any of the
-supported devices.
-
-To add support for a device, all we need is:
-
-- an `Interface`, to detect connected devices of a specific type
-- a `Driver`, to communicate with an individual device
-
-At each boot, the IoT Box will load all of the Interfaces and Drivers that can
-be located on the connected Odoo instance. Each module can contain an
-`iot_handlers` directory that will be copied to the IoT Box. The structure of
-this directory is the following
-
-.. code-block:: text
-
- your_module
- ├── ...
- └── iot_handlers
- ├── drivers
- │ ├── DriverName.py
- │ └── ...
- │
- └── interfaces
- ├── InterfaceName.py
- └── ...
-
-Detect Devices
-==============
-
-Devices connected to the IoT Box are detected through `Interfaces`. There is an
-Interface for each supported connection type (USB, Bluetooth, Video,
-Printers, Serial, etc.). The interface maintains a list of detected devices
-and associates them with the right Driver.
-
-Supported devices will appear both on the IoT Box Homepage that you can access
-through its IP address and in the IoT module of the connected Odoo instance.
-
-Interface
----------
-
-The role of the Interface is to maintain a list of devices connected through a
-determined connection type. Creating a new interface requires
-
-- Extending the `Interface` class
-- Setting the `connection_type` class attribute
-- Implementing the `get_devices` method, that should return a dictionary
- containing data about each detected device. This data will be given as
- argument to the constructors and `supported` method of the Drivers.
-
-.. note::
- Setting the `_loop_delay` attribute will modify the interval between calls
- to `get_devices`. By default, this interval is set to 3 seconds.
-
-.. code-block:: python
-
- from odoo.addons.hw_drivers.interface import Interface
-
- class InterfaceName(Interface):
- connection_type = 'ConnectionType'
-
- def get_devices(self):
- return {
- 'device_identifier_1': {...},
- ...
- }
-
-Driver
-------
-
-Once the interface has retrieved the list of detected devices, it will loop
-through all of the Drivers that have the same `connection_type` attribute and
-test their respective `supported` method on all detected devices. If the
-supported method of a Driver returns `True`, an instance of this Driver will be
-created for the corresponding device.
-
-.. note::
- `supported` methods of drivers are given a priority order. The `supported`
- method of a child class will always be tested before the one of its parent.
- This priority can be adjusted by modifying the `priority` attribute of the
- Driver.
-
-Creating a new Driver requires:
-
-- Extending `Driver`
-- Setting the `connection_type` class attribute.
-- Setting the `device_type`, `device_connection` and `device_name` attributes.
-- Defining the `supported` method
-
-.. code-block:: python
-
- from odoo.addons.hw_drivers.driver import Driver
-
- class DriverName(Driver):
- connection_type = 'ConnectionType'
-
- def __init__(self, identifier, device):
- super(NewDriver, self).__init__(identifier, device)
- self.device_type = 'DeviceType'
- self.device_connection = 'DeviceConnection'
- self.device_name = 'DeviceName'
-
- @classmethod
- def supported(cls, device):
- ...
-
-Communicate With Devices
-========================
-
-Once your new device is detected and appears in the IoT module, the next step
-is to communicate with it. Since the box only has a local IP address, it can
-only be reached from the same local network. Communication, therefore, needs to
-happen on the browser-side, in JavaScript.
-
-The process depends on the direction of the communication:
-- From the browser to the box, through `Actions`_
-- From the box to the browser, through `Longpolling`_
-
-Both channels are accessed from the same JS object, the `DeviceProxy`, which is
-instantiated using the IP of the IoT Box and the device identifier.
-
-.. code-block:: javascript
-
- var DeviceProxy = require('iot.DeviceProxy');
-
- var iot_device = new DeviceProxy({
- iot_ip: iot_ip,
- identifier: device_identifier
- });
-
-Actions
--------
-
-Actions are used to tell a selected device to execute a specific action,
-such as taking a picture, printing a receipt, etc.
-
-.. note::
- It must be noted that no “answer” will be sent by the box on this route,
- only the request status. The answer to the action, if any, has to be
- retrieved via the longpolling.
-
-An action can be performed on the DeviceProxy Object.
-
-.. code-block:: javascript
-
- iot_device.action(data);
-
-In your driver, define an `action` method that will be executed when called
-from an Odoo module. It takes the data given during the call as argument.
-
-.. code-block:: python
-
- def action(self, data):
- ...
-
-Longpolling
------------
-
-When any module in Odoo wants to read data from a specific device, it creates a
-listener identified by the IP/domain of the box and the device identifier and
-passes it a callback function to be called every time the device status
-changes. The callback is called with the new data as argument.
-
-.. code-block:: javascript
-
- iot_device.add_listener(this._onValueChange.bind(this));
-
- _onValueChange: function (result) {
- ...
- }
-
-In the Driver, an event is released by calling the `device_changed` function
-from the `event_manager`. All callbacks set on the listener will then be called
-with `self.data` as argument.
-
-.. code-block:: python
-
- from odoo.addons.hw_drivers.event_manager import event_manager
-
- class DriverName(Driver):
- connection_type = 'ConnectionType'
-
- def methodName(self):
- self.data = {
- 'value': 0.5,
- ...
- }
- event_manager.device_changed(self)
diff --git a/doc/reference/javascript_cheatsheet.rst b/doc/reference/javascript_cheatsheet.rst
deleted file mode 100644
index 1dfa1937d00..00000000000
--- a/doc/reference/javascript_cheatsheet.rst
+++ /dev/null
@@ -1,545 +0,0 @@
-:banner: banners/javascript.jpg
-
-.. _reference/jscs:
-
-=====================
-Javascript Cheatsheet
-=====================
-
-There are many ways to solve a problem in JavaScript, and in Odoo. However, the
-Odoo framework was designed to be extensible (this is a pretty big constraint),
-and some common problems have a nice standard solution. The standard solution
-has probably the advantage of being easy to understand for an odoo developers,
-and will probably keep working when Odoo is modified.
-
-This document tries to explain the way one could solve some of these issues.
-Note that this is not a reference. This is just a random collection of recipes,
-or explanations on how to proceed in some cases.
-
-
-First of all, remember that the first rule of customizing odoo with JS is:
-*try to do it in python*. This may seem strange, but the python framework is
-quite extensible, and many behaviours can be done simply with a touch of xml or
-python. This has usually a lower cost of maintenance than working with JS:
-
-- the JS framework tends to change more, so JS code needs to be more frequently
- updated
-- it is often more difficult to implement a customized behaviour if it needs to
- communicate with the server and properly integrate with the javascript framework.
- There are many small details taken care by the framework that customized code
- needs to replicate. For example, responsiveness, or updating the url, or
- displaying data without flickering.
-
-
-.. note:: This document does not really explain any concepts. This is more a
- cookbook. For more details, please consult the javascript reference
- page (see :doc:`javascript_reference`)
-
-Creating a new field widget
-===========================
-
-This is probably a really common usecase: we want to display some information in
-a form view in a really specific (maybe business dependent) way. For example,
-assume that we want to change the text color depending on some business condition.
-
-This can be done in three steps: creating a new widget, registering it in the
-field registry, then adding the widget to the field in the form view
-
-- creating a new widget:
- This can be done by extending a widget:
-
- .. code-block:: javascript
-
- var FieldChar = require('web.basic_fields').FieldChar;
-
- var CustomFieldChar = FieldChar.extend({
- _renderReadonly: function () {
- // implement some custom logic here
- },
- });
-
-- registering it in the field registry:
- The web client needs to know the mapping between a widget name and its
- actual class. This is done by a registry:
-
- .. code-block:: javascript
-
- var fieldRegistry = require('web.field_registry');
-
- fieldRegistry.add('my-custom-field', CustomFieldChar);
-
-- adding the widget in the form view
- .. code-block:: xml
-
- <field name="somefield" widget="my-custom-field"/>
-
- Note that only the form, list and kanban views use this field widgets registry.
- These views are tightly integrated, because the list and kanban views can
- appear inside a form view).
-
-Modifying an existing field widget
-==================================
-
-Another use case is that we want to modify an existing field widget. For
-example, the voip addon in odoo need to modify the FieldPhone widget to add the
-possibility to easily call the given number on voip. This is done by *including*
-the FieldPhone widget, so there is no need to change any existing form view.
-
-Field Widgets (instances of (subclass of) AbstractField) are like every other
-widgets, so they can be monkey patched. This looks like this:
-
-.. code-block:: javascript
-
- var basic_fields = require('web.basic_fields');
- var Phone = basic_fields.FieldPhone;
-
- Phone.include({
- events: _.extend({}, Phone.prototype.events, {
- 'click': '_onClick',
- }),
-
- _onClick: function (e) {
- if (this.mode === 'readonly') {
- e.preventDefault();
- var phoneNumber = this.value;
- // call the number on voip...
- }
- },
- });
-
-Note that there is no need to add the widget to the registry, since it is already
-registered.
-
-Modifying a main widget from the interface
-==========================================
-
-Another common usecase is the need to customize some elements from the user
-interface. For example, adding a message in the home menu. The usual process
-in this case is again to *include* the widget. This is the only way to do it,
-since there are no registries for those widgets.
-
-This is usually done with code looking like this:
-
-.. code-block:: javascript
-
- var HomeMenu = require('web_enterprise.HomeMenu');
-
- HomeMenu.include({
- render: function () {
- this._super();
- // do something else here...
- },
- });
-
-
-
-Creating a new view (from scratch)
-==================================
-
-Creating a new view is a more advanced topic. This cheatsheet will only
-highlight the steps that will probably need to be done (in no particular order):
-
-- adding a new view type to the field ``type`` of ``ir.ui.view``::
-
- class View(models.Model):
- _inherit = 'ir.ui.view'
-
- type = fields.Selection(selection_add=[('map', "Map")])
-
-- adding the new view type to the field ``view_mode`` of ``ir.actions.act_window.view``::
-
- class ActWindowView(models.Model):
- _inherit = 'ir.actions.act_window.view'
-
- view_mode = fields.Selection(selection_add=[('map', "Map")])
-
-
-- creating the four main pieces which makes a view (in JavaScript):
- we need a view (a subclass of ``AbstractView``, this is the factory), a
- renderer (from ``AbstractRenderer``), a controller (from ``AbstractController``)
- and a model (from ``AbstractModel``). I suggest starting by simply
- extending the superclasses:
-
- .. code-block:: javascript
-
- var AbstractController = require('web.AbstractController');
- var AbstractModel = require('web.AbstractModel');
- var AbstractRenderer = require('web.AbstractRenderer');
- var AbstractView = require('web.AbstractView');
-
- var MapController = AbstractController.extend({});
- var MapRenderer = AbstractRenderer.extend({});
- var MapModel = AbstractModel.extend({});
-
- var MapView = AbstractView.extend({
- config: {
- Model: MapModel,
- Controller: MapController,
- Renderer: MapRenderer,
- },
- });
-
-- adding the view to the registry:
- As usual, the mapping between a view type and the actual class needs to be
- updated:
-
- .. code-block:: javascript
-
- var viewRegistry = require('web.view_registry');
-
- viewRegistry.add('map', MapView);
-
-- implementing the four main classes:
- The ``View`` class needs to parse the ``arch`` field and setup the other
- three classes. The ``Renderer`` is in charge of representing the data in
- the user interface, the ``Model`` is supposed to talk to the server, to
- load data and process it. And the ``Controller`` is there to coordinate,
- to talk to the web client, ...
-
-- creating some views in the database:
- .. code-block:: xml
-
- <record id="customer_map_view" model="ir.ui.view">
- <field name="name">customer.map.view</field>
- <field name="model">res.partner</field>
- <field name="arch" type="xml">
- <map latitude="partner_latitude" longitude="partner_longitude">
- <field name="name"/>
- </map>
- </field>
- </record>
-
-
-Customizing an existing view
-============================
-
-Assume we need to create a custom version of a generic view. For example, a
-kanban view with some extra *ribbon-like* widget on top (to display some
-specific custom information). In that case, this can be done with 3 steps:
-extend the kanban view (which also probably mean extending controllers/renderers
-and/or models), then registering the view in the view registry, and finally,
-using the view in the kanban arch (a specific example is the helpdesk dashboard).
-
-- extending a view:
- Here is what it could look like:
-
- .. code-block:: javascript
-
- var HelpdeskDashboardRenderer = KanbanRenderer.extend({
- ...
- });
-
- var HelpdeskDashboardModel = KanbanModel.extend({
- ...
- });
-
- var HelpdeskDashboardController = KanbanController.extend({
- ...
- });
-
- var HelpdeskDashboardView = KanbanView.extend({
- config: _.extend({}, KanbanView.prototype.config, {
- Model: HelpdeskDashboardModel,
- Renderer: HelpdeskDashboardRenderer,
- Controller: HelpdeskDashboardController,
- }),
- });
-
-- adding it to the view registry:
- as usual, we need to inform the web client of the mapping between the name
- of the views and the actual class.
-
- .. code-block:: javascript
-
- var viewRegistry = require('web.view_registry');
- viewRegistry.add('helpdesk_dashboard', HelpdeskDashboardView);
-
-- using it in an actual view:
- we now need to inform the web client that a specific ``ir.ui.view`` needs to
- use our new class. Note that this is a web client specific concern. From
- the point of view of the server, we still have a kanban view. The proper
- way to do this is by using a special attribute ``js_class`` (which will be
- renamed someday into ``widget``, because this is really not a good name) on
- the root node of the arch:
-
- .. code-block:: xml
-
- <record id="helpdesk_team_view_kanban" model="ir.ui.view" >
- ...
- <field name="arch" type="xml">
- <kanban js_class="helpdesk_dashboard">
- ...
- </kanban>
- </field>
- </record>
-
-.. note::
-
- Note: you can change the way the view interprets the arch structure. However,
- from the server point of view, this is still a view of the same base type,
- subjected to the same rules (rng validation, for example). So, your views still
- need to have a valid arch field.
-
-Promises and asynchronous code
-===============================
-
-For a very good and complete introduction to promises, please read this excellent article https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/async%20%26%20performance/ch3.md
-
-Creating new Promises
------------------------
-- turn a constant into a promise
- There are 2 static functions on Promise that create a resolved or rejected promise based on a constant:
-
- .. code-block:: javascript
-
- var p = Promise.resolve({blabla: '1'}); // creates a resolved promise
- p.then(function (result) {
- console.log(result); // --> {blabla: '1'};
- });
-
-
- var p2 = Promise.reject({error: 'error message'}); // creates a rejected promise
- p2.catch(function (reason) {
- console.log(reason); // --> {error: 'error message');
- });
-
-
- .. note:: Note that even if the promises are created already resolved or rejected, the `then` or `catch` handlers will still be called asynchronously.
-
-
-- based on an already asynchronous code
- Suppose that in a function you must do a rpc, and when it is completed set the result on this.
- The `this._rpc` is a function that returns a `Promise`.
-
- .. code-block:: javascript
-
- function callRpc() {
- var self = this;
- return this._rpc(...).then(function (result) {
- self.myValueFromRpc = result;
- });
- }
-
-- for callback based function
- Suppose that you were using a function `this.close` that takes as parameter a callback that is called when the closing is finished.
- Now suppose that you are doing that in a method that must send a promise that is resolved when the closing is finished.
-
- .. code-block:: javascript
- :linenos:
-
- function waitForClose() {
- var self = this;
- return new Promise (function(resolve, reject) {
- self.close(resolve);
- });
- }
-
- * line 2: we save the `this` into a variable so that in an inner function, we can access the scope of our component
- * line 3: we create and return a new promise. The constructor of a promise takes a function as parameter. This function itself has 2 parameters that we called here `resolve` and `reject`
- - `resolve` is a function that, when called, puts the promise in the resolved state.
- - `reject` is a function that, when called, puts the promise in the rejected state. We do not use reject here and it can be omitted.
- * line 4: we are calling the function close on our object. It takes a function as parameter (the callback) and it happens that resolve is already a function, so we can pass it directly. To be clearer, we could have written:
-
- .. code-block:: javascript
-
- return new Promise (function (resolve) {
- self.close(function () {
- resolve();
- });
- });
-
-
-
-- creating a promise generator (calling one promise after the other *in sequence* and waiting for the last one)
- Suppose that you need to loop over an array, do an operation *in sequence* and resolve a promise when the last operation is done.
-
- .. code-block:: javascript
-
- function doStuffOnArray(arr) {
- var done = Promise.resolve();
- arr.forEach(function (item) {
- done = done.then(function () {
- return item.doSomethingAsynchronous();
- });
- });
- return done;
- }
-
- This way, the promise you return is effectively the last promise.
-- creating a promise, then resolving it outside the scope of its definition (anti-pattern)
- .. note:: we do not recommend using this, but sometimes it is useful. Think carefully for alternatives first...
-
- .. code-block:: javascript
-
- ...
- var resolver, rejecter;
- var prom = new Promise(function (resolve, reject){
- resolver = resolve;
- rejecter = reject;
- });
- ...
-
- resolver("done"); // will resolve the promise prom with the result "done"
- rejecter("error"); // will reject the promise prom with the reason "error"
-
-Waiting for Promises
---------------------
-- waiting for a number of Promises
- if you have multiple promises that all need to be waited, you can convert them into a single promise that will be resolved when all the promises are resolved using Promise.all(arrayOfPromises).
-
- .. code-block:: javascript
-
- var prom1 = doSomethingThatReturnsAPromise();
- var prom2 = Promise.resolve(true);
- var constant = true;
-
- var all = Promise.all([prom1, prom2, constant]); // all is a promise
- // results is an array, the individual results correspond to the index of their
- // promise as called in Promise.all()
- all.then(function (results) {
- var prom1Result = results[0];
- var prom2Result = results[1];
- var constantResult = results[2];
- });
- return all;
-
-
-- waiting for a part of a promise chain, but not another part
- If you have an asynchronous process that you want to wait to do something, but you also want to return to the caller before that something is done.
-
- .. code-block:: javascript
-
- function returnAsSoonAsAsyncProcessIsDone() {
- var prom = AsyncProcess();
- prom.then(function (resultOfAsyncProcess) {
- return doSomething();
- });
- /* returns prom which will only wait for AsyncProcess(),
- and when it will be resolved, the result will be the one of AsyncProcess */
- return prom;
- }
-
-Error handling
---------------
-
-- in general in promises
- The general idea is that a promise should not be rejected for control flow, but should only be rejected for errors.
- When that is the case, you would have multiple resolutions of your promise with, for instance status codes that you would have to check in the `then` handlers and a single `catch` handler at the end of the promise chain.
-
- .. code-block:: javascript
-
- function a() {
- x.y(); // <-- this is an error: x is undefined
- return Promise.resolve(1);
- }
- function b() {
- return Promise.reject(2);
- }
-
- a().catch(console.log); // will log the error in a
- a().then(b).catch(console.log); // will log the error in a, the then is not executed
- b().catch(console.log); // will log the rejected reason of b (2)
- Promise.resolve(1)
- .then(b) // the then is executed, it executes b
- .then(...) // this then is not executed
- .catch(console.log); // will log the rejected reason of b (2)
-
-
-
-- in Odoo specifically
- In Odoo, it happens that we use promise rejection for control flow, like in mutexes and other concurrency primitives defined in module `web.concurrency`
- We also want to execute the catch for *business* reasons, but not when there is a coding error in the definition of the promise or of the handlers.
- For this, we have introduced the concept of `guardedCatch`. It is called like `catch` but not when the rejected reason is an error
-
- .. code-block:: javascript
-
- function blabla() {
- if (someCondition) {
- return Promise.reject("someCondition is truthy");
- }
- return Promise.resolve();
- }
-
- // ...
-
- var promise = blabla();
- promise.then(function (result) { console.log("everything went fine"); })
- // this will be called if blabla returns a rejected promise, but not if it has an error
- promise.guardedCatch(function (reason) { console.log(reason); });
-
- // ...
-
- var anotherPromise =
- blabla().then(function () { console.log("everything went fine"); })
- // this will be called if blabla returns a rejected promise,
- // but not if it has an error
- .guardedCatch(console.log);
-
-
- .. code-block:: javascript
-
- var promiseWithError = Promise.resolve().then(function () {
- x.y(); // <-- this is an error: x is undefined
- });
- promiseWithError.guardedCatch(function (reason) {console.log(reason);}); // will not be called
- promiseWithError.catch(function (reason) {console.log(reason);}); // will be called
-
-
-
-Testing asynchronous code
---------------------------
-
-- using promises in tests
- In the tests code, we support the latest version of Javascript, including primitives like `async` and `await`. This makes using and waiting for promises very easy.
- Most helper methods also return a promise (either by being marked `async` or by returning a promise directly.
-
- .. code-block:: javascript
-
- var testUtils = require('web.test_utils');
- QUnit.test("My test", async function (assert) {
- // making the function async has 2 advantages:
- // 1) it always returns a promise so you don't need to define `var done = assert.async()`
- // 2) it allows you to use the `await`
- assert.expect(1);
-
- var form = await testUtils.createView({ ... });
- await testUtils.form.clickEdit(form);
- await testUtils.form.click('jquery selector');
- assert.containsOnce('jquery selector');
- form.destroy();
- });
-
- QUnit.test("My test - no async - no done", function (assert) {
- // this function is not async, but it returns a promise.
- // QUnit will wait for for this promise to be resolved.
- assert.expect(1);
-
- return testUtils.createView({ ... }).then(function (form) {
- return testUtils.form.clickEdit(form).then(function () {
- return testUtils.form.click('jquery selector').then(function () {
- assert.containsOnce('jquery selector');
- form.destroy();
- });
- });
- });
- });
-
-
- QUnit.test("My test - no async", function (assert) {
- // this function is not async and does not return a promise.
- // we have to use the done function to signal QUnit that the test is async and will be finished inside an async callback
- assert.expect(1);
- var done = assert.async();
-
- testUtils.createView({ ... }).then(function (form) {
- testUtils.form.clickEdit(form).then(function () {
- testUtils.form.click('jquery selector').then(function () {
- assert.containsOnce('jquery selector');
- form.destroy();
- done();
- });
- });
- });
- });
-
- as you can see, the nicer form is to use `async/await` as it is clearer and shorter to write.
diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst
deleted file mode 100644
index 9e6e208648e..00000000000
--- a/doc/reference/javascript_reference.rst
+++ /dev/null
@@ -1,2425 +0,0 @@
-:banner: banners/javascript.jpg
-
-.. highlight:: javascript
-
-.. default-domain:: js
-
-=====================
-Javascript Reference
-=====================
-
-This document presents the Odoo Javascript framework. This
-framework is not a large application in term of lines of code, but it is quite
-generic, because it is basically a machine to turn a declarative interface
-description into a live application, able to interact with every model and
-records in the database. It is even possible to use the web client to modify
-the interface of the web client.
-
-Overview
-=========
-
-The Javascript framework is designed to work with three main use cases:
-
-- the *web client*: this is the private web application, where one can view and
- edit business data. This is a single page application (the page is never
- reloaded, only the new data is fetched from the server whenever it is needed)
-- the *website*: this is the public part of Odoo. It allows an unidentified
- user to browse some content, to shop or to perform many actions, as a client.
- This is a classical website: various routes with controllers and some
- javascript to make it work.
-- the *point of sale*: this is the interface for the point of sale. It is a
- specialized single page application.
-
-Some javascript code is common to these three use cases, and is bundled together
-(see below in the assets section). This document will focus mostly on the web
-client design.
-
-Web client
-==========
-
-Single Page Application
------------------------
-
-In short, the *webClient*, instance of *WebClient* is the root component of the
-whole user interface. Its responsibility is to orchestrate all various
-subcomponents, and to provide services, such as rpcs, local storage and more.
-
-In runtime, the web client is a single page application. It does not need to
-request a full page from the server each time the user perform an action. Instead,
-it only requests what it needs, and then replaces/updates the view. Also, it
-manages the url: it is kept in sync with the web client state.
-
-It means that while a user is working on Odoo, the web client class (and the
-action manager) actually creates and destroys many sub components. The state is
-highly dynamic, and each widget could be destroyed at any time.
-
-Overview of web client JS code
--------------------------------------
-
-Here, we give a very quick overview on the web client code, in
-the *web/static/src/js* addon. Note that it is deliberately not exhaustive.
-We only cover the most important files/folders.
-
-- *boot.js*: this is the file that defines the module system. It needs to be
- loaded first.
-- *core/*: this is a collection of lower level building blocks. Notably, it
- contains the class system, the widget system, concurrency utilities, and many
- other class/functions.
-- *chrome/*: in this folder, we have most large widgets which make up most of
- the user interface.
-- *chrome/abstract_web_client.js* and *chrome/web_client.js*: together, these
- files define the WebClient widget, which is the root widget for the web client.
-- *chrome/action_manager.js*: this is the code that will convert an action into
- a widget (for example a kanban or a form view)
-- *chrome/search_X.js* all these files define the search view (it is not a view
- in the point of view of the web client, only from the server point of view)
-- *fields*: all main view field widgets are defined here
-- *views*: this is where the views are located
-
-Assets Management
-=================
-
-Managing assets in Odoo is not as straightforward as it is in some other apps.
-One of the reason is that we have a variety of situations where some, but not all
-the assets are required. For example, the needs of the web client, the point of
-sale, the website or even the mobile application are different. Also, some
-assets may be large, but are seldom needed. In that case, we sometimes want them
-to be loaded lazily.
-
-The main idea is that we define a set of *bundles* in xml. A bundle is here defined as
-a collection of files (javascript, css, scss). In Odoo, the most important
-bundles are defined in the file *addons/web/views/webclient_templates.xml*. It looks
-like this:
-
-.. code-block:: xml
-
- <template id="web.assets_common" name="Common Assets (used in backend interface and website)">
- <link rel="stylesheet" type="text/css" href="/web/static/lib/jquery.ui/jquery-ui.css"/>
- ...
- <script type="text/javascript" src="/web/static/src/js/boot.js"></script>
- ...
- </template>
-
-
-The files in a bundle can then be inserted into a template by using the *t-call-assets*
-directive:
-
-.. code-block:: xml
-
- <t t-call-assets="web.assets_common" t-js="false"/>
- <t t-call-assets="web.assets_common" t-css="false"/>
-
-Here is what happens when a template is rendered by the server with these directives:
-
-- all the *scss* files described in the bundle are compiled into css files. A file
- named *file.scss* will be compiled in a file named *file.scss.css*.
-
-- if we are in *debug=assets* mode
-
- - the *t-call-assets* directive with the *t-js* attribute set to false will
- be replaced by a list of stylesheet tags pointing to the css files
-
- - the *t-call-assets* directive with the *t-css* attribute set to false will
- be replaced by a list of script tags pointing to the js files
-
-- if we are not in *debug=assets* mode
-
- - the css files will be concatenated and minified, then a stylesheet tag is
- generated
-
- - the js files are concatenated and minified, then a script tag is generated
-
-Note that the assets files are cached, so in theory, a browser should only load
-them once.
-
-Main bundles
-------------
-When the Odoo server is started, it checks the timestamp of each file in a bundle,
-and if necessary, will create/recreate the corresponding bundles.
-
-Here are some important bundles that most developers will need to know:
-
-- *web.assets_common*: this bundle contains most assets which are common to the
- web client, the website, and also the point of sale. This is supposed to contain
- lower level building blocks for the odoo framework. Note that it contains the
- *boot.js* file, which defines the odoo module system.
-
-- *web.assets_backend*: this bundle contains the code specific to the web client
- (notably the web client/action manager/views)
-
-- *web.assets_frontend*: this bundle is about all that is specific to the public
- website: ecommerce, forum, blog, event management, ...
-
-
-Adding files in an asset bundle
--------------------------------
-
-The proper way to add a file located in *addons/web* to a bundle is simple:
-it is just enough to add a *script* or a *stylesheet* tag to the bundle in the
-file *webclient_templates.xml*. But when we work in a different addon, we need
-to add a file from that addon. In that case, it should be done in three steps:
-
-1. add a *assets.xml* file in the *views/* folder
-2. add the string 'views/assets.xml' in the 'data' key in the manifest file
-3. create an inherited view of the desired bundle, and add the file(s) with an
- xpath expression. For example,
-
-.. code-block:: xml
-
- <template id="assets_backend" name="helpdesk assets" inherit_id="web.assets_backend">
- <xpath expr="//script[last()]" position="after">
- <link rel="stylesheet" type="text/scss" href="/helpdesk/static/src/scss/helpdesk.scss"/>
- <script type="text/javascript" src="/helpdesk/static/src/js/helpdesk_dashboard.js"></script>
- </xpath>
- </template>
-
-
-.. note ::
-
- Note that the files in a bundle are all loaded immediately when the user loads the
- odoo web client. This means that the files are transferred through the network
- everytime (except when the browser cache is active). In some cases, it may be
- better to lazyload some assets. For example, if a widget requires a large
- library, and that widget is not a core part of the experience, then it may be
- a good idea to only load the library when the widget is actually created. The
- widget class has actually builtin support just for this use case. (see section
- :ref:`reference/javascript_reference/qweb`)
-
-What to do if a file is not loaded/updated
-------------------------------------------
-
-There are many different reasons why a file may not be properly loaded. Here
-are a few things you can try to solve the issue:
-
-- once the server is started, it does not know if an asset file has been
- modified. So, you can simply restart the server to regenerate the assets.
-- check the console (in the dev tools, usually opened with F12) to make sure
- there are no obvious errors
-- try to add a console.log at the beginning of your file (before any module
- definition), so you can see if a file has been loaded or not
-- in the user interface, in debug mode (INSERT LINK HERE TO DEBUG MODE), there
- is an option to force the server to update its assets files.
-- use the *debug=assets* mode. This will actually bypass the asset bundles (note
- that it does not actually solve the issue. The server still uses outdated bundles)
-- finally, the most convenient way to do it, for a developer, is to start the
- server with the *--dev=all* option. This activates the file watcher options,
- which will automatically invalidate assets when necessary. Note that it does
- not work very well if the OS is Windows.
-- remember to refresh your page!
-- or maybe to save your code file...
-
-.. note::
- Once an asset file has been recreated, you need to refresh the page, to reload
- the proper files (if that does not work, the files may be cached).
-
-
-Javascript Module System
-========================
-
-Once we are able to load our javascript files into the browser, we need to make
-sure they are loaded in the correct order. In order to do that, Odoo has defined
-a small module system (located in the file *addons/web/static/src/js/boot.js*,
-which needs to be loaded first).
-
-The Odoo module system, inspired by AMD, works by defining the function *define*
-on the global odoo object. We then define each javascript module by calling that
-function. In the Odoo framework, a module is a piece of code that will be executed
-as soon as possible. It has a name and potentially some dependencies. When its
-dependencies are loaded, a module will then be loaded as well. The value of the
-module is then the return value of the function defining the module.
-
-
-As an example, it may look like this:
-
-
-.. code-block:: javascript
-
- // in file a.js
- odoo.define('module.A', function (require) {
- "use strict";
-
- var A = ...;
-
- return A;
- });
-
- // in file b.js
- odoo.define('module.B', function (require) {
- "use strict";
-
- var A = require('module.A');
-
- var B = ...; // something that involves A
-
- return B;
- });
-
-An alternative way to define a module is to give explicitly a list of dependencies
-in the second argument.
-
-.. code-block:: javascript
-
- odoo.define('module.Something', ['module.A', 'module.B'], function (require) {
- "use strict";
-
- var A = require('module.A');
- var B = require('module.B');
-
- // some code
- });
-
-
-If some dependencies are missing/non ready, then the module will simply not be
-loaded. There will be a warning in the console after a few seconds.
-
-Note that circular dependencies are not supported. It makes sense, but it means that one
-needs to be careful.
-
-Defining a module
------------------
-
-The *odoo.define* method is given three arguments:
-
-- *moduleName*: the name of the javascript module. It should be a unique string.
- The convention is to have the name of the odoo addon followed by a specific
- description. For example, 'web.Widget' describes a module defined in the *web*
- addon, which exports a *Widget* class (because the first letter is capitalized)
-
- If the name is not unique, an exception will be thrown and displayed in the
- console.
-
-- *dependencies*: the second argument is optional. If given, it should be a list
- of strings, each corresponding to a javascript module. This describes the
- dependencies that are required to be loaded before the module is executed. If
- the dependencies are not explicitly given here, then the module system will
- extract them from the function by calling toString on it, then using a regexp
- to find all *require* statements.
-
-.. code-block:: javascript
-
- odoo.define('module.Something', ['web.ajax'], function (require) {
- "use strict";
-
- var ajax = require('web.ajax');
-
- // some code here
- return something;
- });
-
-- finally, the last argument is a function which defines the module. Its return
- value is the value of the module, which may be passed to other modules requiring
- it. Note that there is a small exception for asynchronous modules, see the
- next section.
-
-If an error happens, it will be logged (in debug mode) in the console:
-
-* ``Missing dependencies``:
- These modules do not appear in the page. It is possible that the JavaScript
- file is not in the page or that the module name is wrong
-* ``Failed modules``:
- A javascript error is detected
-* ``Rejected modules``:
- The module returns a rejected Promise. It (and its dependent modules) is not
- loaded.
-* ``Rejected linked modules``:
- Modules who depend on a rejected module
-* ``Non loaded modules``:
- Modules who depend on a missing or a failed module
-
-
-
-Asynchronous modules
----------------------
-
-It can happen that a module needs to perform some work before it is ready. For
-example, it could do a rpc to load some data. In that case, the module can
-simply return a promise. In that case, the module system will simply
-wait for the promise to complete before registering the module.
-
-.. code-block:: javascript
-
- odoo.define('module.Something', function (require) {
- "use strict";
-
- var ajax = require('web.ajax');
-
- return ajax.rpc(...).then(function (result) {
- // some code here
- return something;
- });
- });
-
-
-Best practices
-----------------
-
-- remember the convention for a module name: *addon name* suffixed with *module
- name*.
-- declare all your dependencies at the top of the module. Also, they should be
- sorted alphabetically by module name. This makes it easier to understand your module.
-- declare all exported values at the end
-- try to avoid exporting too many things from one module. It is usually better
- to simply export one thing in one (small/smallish) module.
-- asynchronous modules can be used to simplify some use cases. For example,
- the *web.dom_ready* module returns a promise which will be resolved when the
- dom is actually ready. So, another module that needs the DOM could simply have
- a `require('web.dom_ready')` statement somewhere, and the code will only be
- executed when the DOM is ready.
-- try to avoid defining more than one module in one file. It may be convenient
- in the short term, but this is actually harder to maintain.
-
-
-Class System
-============
-
-Odoo was developed before ECMAScript 6 classes were available. In Ecmascript 5,
-the standard way to define a class is to define a function and to add methods
-on its prototype object. This is fine, but it is slightly complex when we want
-to use inheritance, mixins.
-
-For these reasons, Odoo decided to use its own class system, inspired by John
-Resig. The base Class is located in *web.Class*, in the file *class.js*.
-
-Creating a subclass
--------------------
-
-Let us discuss how classes are created. The main mechanism is to use the
-*extend* method (this is more or less the equivalent of *extend* in ES6 classes).
-
-.. code-block:: javascript
-
- var Class = require('web.Class');
-
- var Animal = Class.extend({
- init: function () {
- this.x = 0;
- this.hunger = 0;
- },
- move: function () {
- this.x = this.x + 1;
- this.hunger = this.hunger + 1;
- },
- eat: function () {
- this.hunger = 0;
- },
- });
-
-
-In this example, the *init* function is the constructor. It will be called when
-an instance is created. Making an instance is done by using the *new* keyword.
-
-Inheritance
------------
-
-It is convenient to be able to inherit an existing class. This is simply done
-by using the *extend* method on the superclass. When a method is called, the
-framework will secretly rebind a special method: *_super* to the currently
-called method. This allows us to use *this._super* whenever we need to call a
-parent method.
-
-
-.. code-block:: javascript
-
- var Animal = require('web.Animal');
-
- var Dog = Animal.extend({
- move: function () {
- this.bark();
- this._super.apply(this, arguments);
- },
- bark: function () {
- console.log('woof');
- },
- });
-
- var dog = new Dog();
- dog.move()
-
-Mixins
-------
-
-The odoo Class system does not support multiple inheritance, but for those cases
-when we need to share some behaviour, we have a mixin system: the *extend*
-method can actually take an arbitrary number of arguments, and will combine all
-of them in the new class.
-
-.. code-block:: javascript
-
- var Animal = require('web.Animal');
- var DanceMixin = {
- dance: function () {
- console.log('dancing...');
- },
- };
-
- var Hamster = Animal.extend(DanceMixin, {
- sleep: function () {
- console.log('sleeping');
- },
- });
-
-In this example, the *Hamster* class is a subclass of Animal, but it also mix
-the DanceMixin in.
-
-
-Patching an existing class
---------------------------
-
-It is not common, but we sometimes need to modify another class *in place*. The
-goal is to have a mechanism to change a class and all future/present instances.
-This is done by using the *include* method:
-
-.. code-block:: javascript
-
- var Hamster = require('web.Hamster');
-
- Hamster.include({
- sleep: function () {
- this._super.apply(this, arguments);
- console.log('zzzz');
- },
- });
-
-
-This is obviously a dangerous operation and should be done with care. But with
-the way Odoo is structured, it is sometimes necessary in one addon to modify
-the behavior of a widget/class defined in another addon. Note that it will
-modify all instances of the class, even if they have already been created.
-
-
-Widgets
-=======
-
-The *Widget* class is really an important building block of the user interface.
-Pretty much everything in the user interface is under the control of a widget.
-The Widget class is defined in the module *web.Widget*, in *widget.js*.
-
-In short, the features provided by the Widget class include:
-
-* parent/child relationships between widgets (*PropertiesMixin*)
-* extensive lifecycle management with safety features (e.g. automatically
- destroying children widgets during the destruction of a parent)
-* automatic rendering with :ref:`qweb <reference/qweb>`
-* various utility functions to help interacting with the outside environment.
-
-Here is an example of a basic counter widget:
-
-.. code-block:: javascript
-
- var Widget = require('web.Widget');
-
- var Counter = Widget.extend({
- template: 'some.template',
- events: {
- 'click button': '_onClick',
- },
- init: function (parent, value) {
- this._super(parent);
- this.count = value;
- },
- _onClick: function () {
- this.count++;
- this.$('.val').text(this.count);
- },
- });
-
-For this example, assume that the template *some.template* (and is properly
-loaded: the template is in a file, which is properly defined in the *qweb* key
-in the module manifest) is given by:
-
-.. code-block:: xml
-
- <div t-name="some.template">
- <span class="val"><t t-esc="widget.count"/></span>
- <button>Increment</button>
- </div>
-
-This example widget can be used in the following manner:
-
-.. code-block:: javascript
-
- // Create the instance
- var counter = new Counter(this, 4);
- // Render and insert into DOM
- counter.appendTo(".some-div");
-
-This example illustrates a few of the features of the *Widget* class, including
-the event system, the template system, the constructor with the initial *parent* argument.
-
-Widget Lifecycle
-----------------
-
-Like many component systems, the widget class has a well defined lifecycle. The
-usual lifecycle is the following: *init* is called, then *willStart*, then the
-rendering takes place, then *start* and finally *destroy*.
-
-.. function:: Widget.init(parent)
-
- this is the constructor. The init method is supposed to initialize the
- base state of the widget. It is synchronous and can be overridden to
- take more parameters from the widget's creator/parent
-
- :param parent: the new widget's parent, used to handle automatic
- destruction and event propagation. Can be ``null`` for
- the widget to have no parent.
- :type parent: :class:`~Widget`
-
-.. function:: Widget.willStart()
-
- this method will be called once by the framework when a widget is created
- and in the process of being appended to the DOM. The *willStart* method is a
- hook that should return a promise. The JS framework will wait for this promise
- to complete before moving on to the rendering step. Note that at this point,
- the widget does not have a DOM root element. The *willStart* hook is mostly
- useful to perform some asynchronous work, such as fetching data from the server
-
-.. function:: [Rendering]
-
- This step is automatically done by the framework. What happens is
- that the framework checks if a template key is defined on the widget. If that is
- the case, then it will render that template with the *widget* key bound to the
- widget in the rendering context (see the example above: we use *widget.count*
- in the QWeb template to read the value from the widget). If no template is
- defined, we read the *tagName* key and create a corresponding DOM element.
- When the rendering is done, we set the result as the $el property of the widget.
- After this, we automatically bind all events in the events and custom_events
- keys.
-
-.. function:: Widget.start()
-
- when the rendering is complete, the framework will automatically call
- the *start* method. This is useful to perform some specialized post-rendering
- work. For example, setting up a library.
-
- Must return a promise to indicate when its work is done.
-
- :returns: promise
-
-.. function:: Widget.destroy()
-
- This is always the final step in the life of a widget. When a
- widget is destroyed, we basically perform all necessary cleanup operations:
- removing the widget from the component tree, unbinding all events, ...
-
- Automatically called when the widget's parent is destroyed,
- must be called explicitly if the widget has no parent or if it is
- removed but its parent remains.
-
-Note that the willStart and start method are not necessarily called. A widget
-can be created (the *init* method will be called) and then destroyed (*destroy*
-method) without ever having been appended to the DOM. If that is the case, the
-willStart and start will not even be called.
-
-Widget API
-----------
-
-.. attribute:: Widget.tagName
-
- Used if the widget has no template defined. Defaults to ``div``,
- will be used as the tag name to create the DOM element to set as
- the widget's DOM root. It is possible to further customize this
- generated DOM root with the following attributes:
-
-
-.. attribute:: Widget.id
-
- Used to generate an ``id`` attribute on the generated DOM
- root. Note that this is rarely needed, and is probably not a good idea
- if a widget can be used more than once.
-
-.. attribute:: Widget.className
-
- Used to generate a ``class`` attribute on the generated DOM root. Note
- that it can actually contain more than one css class:
- *'some-class other-class'*
-
-.. attribute:: Widget.attributes
-
- Mapping (object literal) of attribute names to attribute
- values. Each of these k:v pairs will be set as a DOM attribute
- on the generated DOM root.
-
-.. attribute:: Widget.el
-
- raw DOM element set as root to the widget (only available after the start
- lifecycle method)
-
-.. attribute:: Widget.$el
-
- jQuery wrapper around :attr:`~Widget.el`. (only available after the start
- lifecycle method)
-
-.. attribute:: Widget.template
-
- Should be set to the name of a :ref:`QWeb template <reference/qweb>`.
- If set, the template will be rendered after the widget has been
- initialized but before it has been started. The root element generated by
- the template will be set as the DOM root of the widget.
-
-.. attribute:: Widget.xmlDependencies
-
- List of paths to xml files that need to be loaded before the
- widget can be rendered. This will not induce loading anything that has already
- been loaded. This is useful when you want to load your templates lazily,
- or if you want to share a widget between the website and the web client
- interface.
-
- .. code-block:: javascript
-
- var EditorMenuBar = Widget.extend({
- xmlDependencies: ['/web_editor/static/src/xml/editor.xml'],
- ...
-
-.. attribute:: Widget.events
-
- Events are a mapping of an event selector (an event name and an optional
- CSS selector separated by a space) to a callback. The callback can
- be the name of a widget's method or a function object. In either case, the
- ``this`` will be set to the widget:
-
- .. code-block:: javascript
-
- events: {
- 'click p.oe_some_class a': 'some_method',
- 'change input': function (e) {
- e.stopPropagation();
- }
- },
-
- The selector is used for jQuery's event delegation, the
- callback will only be triggered for descendants of the DOM root
- matching the selector. If the selector is left out
- (only an event name is specified), the event will be set directly on the
- widget's DOM root.
-
- Note: the use of an inline function is discouraged, and will probably be
- removed sometimes in the future.
-
-.. attribute:: Widget.custom_events
-
- this is almost the same as the *events* attribute, but the keys
- are arbitrary strings. They represent business events triggered by
- some sub widgets. When an event is triggered, it will 'bubble up' the widget
- tree (see the section on component communication for more details).
-
-.. function:: Widget.isDestroyed()
-
- :returns: ``true`` if the widget is being or has been destroyed, ``false``
- otherwise
-
-.. function:: Widget.$(selector)
-
- Applies the CSS selector specified as parameter to the widget's
- DOM root:
-
- .. code-block:: javascript
-
- this.$(selector);
-
- is functionally identical to:
-
- .. code-block:: javascript
-
- this.$el.find(selector);
-
- :param String selector: CSS selector
- :returns: jQuery object
-
- .. note:: this helper method is similar to ``Backbone.View.$``
-
-.. function:: Widget.setElement(element)
-
- Re-sets the widget's DOM root to the provided element, also
- handles re-setting the various aliases of the DOM root as well as
- unsetting and re-setting delegated events.
-
- :param Element element: a DOM element or jQuery object to set as
- the widget's DOM root
-
-
-Inserting a widget in the DOM
------------------------------
-
-.. function:: Widget.appendTo(element)
-
- Renders the widget and inserts it as the last child of the target, uses
- `.appendTo()`_
-
-.. function:: Widget.prependTo(element)
-
- Renders the widget and inserts it as the first child of the target, uses
- `.prependTo()`_
-
-.. function:: Widget.insertAfter(element)
-
- Renders the widget and inserts it as the preceding sibling of the target,
- uses `.insertAfter()`_
-
-.. function:: Widget.insertBefore(element)
-
- Renders the widget and inserts it as the following sibling of the target,
- uses `.insertBefore()`_
-
-All of these methods accept whatever the corresponding jQuery method accepts
-(CSS selectors, DOM nodes or jQuery objects). They all return a promise
-and are charged with three tasks:
-
-* rendering the widget's root element via :func:`~Widget.renderElement`
-* inserting the widget's root element in the DOM using whichever jQuery
- method they match
-* starting the widget, and returning the result of starting it
-
-Widget Guidelines
-----------------------
-
-* Identifiers (``id`` attribute) should be avoided. In generic applications
- and modules, ``id`` limits the re-usability of components and tends to make
- code more brittle. Most of the time, they can be replaced with nothing,
- classes or keeping a reference to a DOM node or jQuery element.
-
- If an ``id`` is absolutely necessary (because a third-party library requires
- one), the id should be partially generated using ``_.uniqueId()`` e.g.:
-
- .. code-block:: javascript
-
- this.id = _.uniqueId('my-widget-');
-
-* Avoid predictable/common CSS class names. Class names such as "content" or
- "navigation" might match the desired meaning/semantics, but it is likely an
- other developer will have the same need, creating a naming conflict and
- unintended behavior. Generic class names should be prefixed with e.g. the
- name of the component they belong to (creating "informal" namespaces, much
- as in C or Objective-C).
-
-* Global selectors should be avoided. Because a component may be used several
- times in a single page (an example in Odoo is dashboards), queries should be
- restricted to a given component's scope. Unfiltered selections such as
- ``$(selector)`` or ``document.querySelectorAll(selector)`` will generally
- lead to unintended or incorrect behavior. Odoo Web's
- :class:`~Widget` has an attribute providing its DOM root
- (:attr:`~Widget.$el`), and a shortcut to select nodes directly
- (:func:`~Widget.$`).
-
-* More generally, never assume your components own or controls anything beyond
- its own personal :attr:`~Widget.$el` (so, avoid using a reference to the
- parent widget)
-
-* Html templating/rendering should use QWeb unless absolutely trivial.
-
-* All interactive components (components displaying information to the screen
- or intercepting DOM events) must inherit from :class:`~Widget`
- and correctly implement and use its API and life cycle.
-
-* Make sure to wait for start to be finished before using $el e.g.:
-
- .. code-block:: javascript
-
- var Widget = require('web.Widget');
-
- var AlmostCorrectWidget = Widget.extend({
- start: function () {
- this.$el.hasClass(....) // in theory, $el is already set, but you don't know what the parent will do with it, better call super first
- return this._super.apply(arguments);
- },
- });
-
- var IncorrectWidget = Widget.extend({
- start: function () {
- this._super.apply(arguments); // the parent promise is lost, nobody will wait for the start of this widget
- this.$el.hasClass(....)
- },
- });
-
- var CorrectWidget = Widget.extend({
- start: function () {
- var self = this;
- return this._super.apply(arguments).then(function() {
- self.$el.hasClass(....) // this works, no promise is lost and the code executes in a controlled order: first super, then our code.
- });
- },
- });
-
-.. _reference/javascript_reference/qweb:
-
-QWeb Template Engine
-====================
-
-The web client uses the :doc:`qweb` template engine to render widgets (unless they
-override the *renderElement* method to do something else).
-The Qweb JS template engine is based on XML, and is mostly compatible with the
-python implementation.
-
-Now, let us explain how the templates are loaded. Whenever the web client
-starts, a rpc is made to the */web/webclient/qweb* route. The server will then
-return a list of all templates defined in data files for each installed modules.
-The correct files are listed in the *qweb* entry in each module manifest.
-
-The web client will wait for that list of template to be loaded, before starting
-its first widget.
-
-This mechanism works quite well for our needs, but sometimes, we want to lazy
-load a template. For example, imagine that we have a widget which is rarely
-used. In that case, maybe we prefer to not load its template in the main file,
-in order to make the web client slightly lighter. In that case, we can use the
-*xmlDependencies* key of the Widget:
-
-.. code-block:: javascript
-
- var Widget = require('web.Widget');
-
- var Counter = Widget.extend({
- template: 'some.template',
- xmlDependencies: ['/myaddon/path/to/my/file.xml'],
-
- ...
-
- });
-
-With this, the *Counter* widget will load the xmlDependencies files in its
-*willStart* method, so the template will be ready when the rendering is performed.
-
-
-Event system
-============
-
-There are currently two event systems supported by Odoo: a simple system which
-allows adding listeners and triggering events, and a more complete system that
-also makes events 'bubble up'.
-
-Both of these event systems are implemented in the *EventDispatcherMixin*, in
-the file *mixins.js*. This mixin is included in the *Widget* class.
-
-Base Event system
------------------
-
-This event system was historically the first. It implements a simple bus
-pattern. We have 4 main methods:
-
-- *on*: this is used to register a listener on an event.
-- *off*: useful to remove events listener.
-- *once*: this is used to register a listener that will only be called once.
-- *trigger*: trigger an event. This will cause each listeners to be called.
-
-Here is an example on how this event system could be used:
-
-.. code-block:: javascript
-
- var Widget = require('web.Widget');
- var Counter = require('myModule.Counter');
-
- var MyWidget = Widget.extend({
- start: function () {
- this.counter = new Counter(this);
- this.counter.on('valuechange', this, this._onValueChange);
- var def = this.counter.appendTo(this.$el);
- return Promise.all([def, this._super.apply(this, arguments)]);
- },
- _onValueChange: function (val) {
- // do something with val
- },
- });
-
- // in Counter widget, we need to call the trigger method:
-
- ... this.trigger('valuechange', someValue);
-
-
-.. warning::
- the use of this event system is discouraged, we plan to replace each
- *trigger* method by the *trigger_up* method from the extended event system
-
-Extended Event System
----------------------
-
-The custom event widgets is a more advanced system, which mimic the DOM events
-API. Whenever an event is triggered, it will 'bubble up' the component tree,
-until it reaches the root widget, or is stopped.
-
-- *trigger_up*: this is the method that will create a small *OdooEvent* and
- dispatch it in the component tree. Note that it will start with the component
- that triggered the event
-- *custom_events*: this is the equivalent of the *event* dictionary, but for
- odoo events.
-
-The OdooEvent class is very simple. It has three public attributes: *target*
-(the widget that triggered the event), *name* (the event name) and *data* (the
-payload). It also has 2 methods: *stopPropagation* and *is_stopped*.
-
-The previous example can be updated to use the custom event system:
-
-.. code-block:: javascript
-
- var Widget = require('web.Widget');
- var Counter = require('myModule.Counter');
-
- var MyWidget = Widget.extend({
- custom_events: {
- valuechange: '_onValueChange'
- },
- start: function () {
- this.counter = new Counter(this);
- var def = this.counter.appendTo(this.$el);
- return Promise.all([def, this._super.apply(this, arguments)]);
- },
- _onValueChange: function(event) {
- // do something with event.data.val
- },
- });
-
- // in Counter widget, we need to call the trigger_up method:
-
- ... this.trigger_up('valuechange', {value: someValue});
-
-
-Registries
-===========
-
-A common need in the Odoo ecosystem is to extend/change the behaviour of the
-base system from the outside (by installing an application, i.e. a different
-module). For example, one may need to add a new widget type in some views. In
-that case, and many others, the usual process is to create the desired component,
-then add it to a registry (registering step), to make the rest of the web client
-aware of its existence.
-
-There are a few registries available in the system:
-
-field registry (exported by :js:data:`web.field_registry`)
- The field registry contains all field widgets known to the web client.
- Whenever a view (typically form or list/kanban) needs a field widget, this
- is where it will look. A typical use case look like this:
-
- .. code-block:: javascript
-
- var fieldRegistry = require('web.field_registry');
-
- var FieldPad = ...;
-
- fieldRegistry.add('pad', FieldPad);
-
- Note that each value should be a subclass of *AbstractField*
-
-view registry
- This registry contains all JS views known to the web client
- (and in particular, the view manager). Each value of this registry should
- be a subclass of *AbstractView*.
-
-action registry
- We keep track of all client actions in this registry. This
- is where the action manager looks up whenever it needs to create a client
- action. In version 11, each value should simply be a subclass of *Widget*.
- However, in version 12, the values are required to be *AbstractAction*.
-
-
-Communication between widgets
-=============================
-
-There are many ways to communicate between components.
-
-From a parent to its child
- This is a simple case. The parent widget can simply call a method on its
- child:
-
- .. code-block:: javascript
-
- this.someWidget.update(someInfo);
-
-From a widget to its parent/some ancestor
- In this case, the widget's job is simply to notify its environment that
- something happened. Since we do not want the widget to have a reference to
- its parent (this would couple the widget with its parent's implementation),
- the best way to proceed is usually to trigger an event, which will bubble up
- the component tree, by using the ``trigger_up`` method:
-
- .. code-block:: javascript
-
- this.trigger_up('open_record', { record: record, id: id});
-
- This event will be triggered on the widget, then will bubble up and be
- eventually caught by some upstream widget:
-
- .. code-block:: javascript
-
- var SomeAncestor = Widget.extend({
- custom_events: {
- 'open_record': '_onOpenRecord',
- },
- _onOpenRecord: function (event) {
- var record = event.data.record;
- var id = event.data.id;
- // do something with the event.
- },
- });
-
-Cross component
- Cross component communication can be achieved by using a bus. This is not
- the preferred form of communication, because it has the disadvantage of
- making the code harder to maintain. However, it has the advantage of
- decoupling the components. In that case, this is simply done by triggering
- and listening to events on a bus. For example:
-
- .. code-block:: javascript
-
- // in WidgetA
- var core = require('web.core');
-
- var WidgetA = Widget.extend({
- ...
- start: function () {
- core.bus.on('barcode_scanned', this, this._onBarcodeScanned);
- },
- });
-
- // in WidgetB
- var WidgetB = Widget.extend({
- ...
- someFunction: function (barcode) {
- core.bus.trigger('barcode_scanned', barcode);
- },
- });
-
-
- In this example, we use the bus exported by *web.core*, but this is not
- required. A bus could be created for a specific purpose.
-
-Services
-========
-
-In version 11.0, we introduced the notion of *service*. The main idea is to
-give to sub components a controlled way to access their environment, in a way
-that allow the framework enough control, and which is testable.
-
-The service system is organized around three ideas: services, service providers
-and widgets. The way it works is that widgets trigger (with *trigger_up*)
-events, these events bubble up to a service provider, which will ask a service
-to perform a task, then maybe return an answer.
-
-Service
---------
-
-A service is an instance of the *AbstractService* class. It basically only has
-a name and a few methods. Its job is to perform some work, typically something
-depending on the environment.
-
-For example, we have the *ajax* service (job is to perform a rpc), the
-*localStorage* (interact with the browser local storage) and many others.
-
-Here is a simplified example on how the ajax service is implemented:
-
-.. code-block:: javascript
-
- var AbstractService = require('web.AbstractService');
-
- var AjaxService = AbstractService.extend({
- name: 'ajax',
- rpc: function (...) {
- return ...;
- },
- });
-
-This service is named 'ajax' and define one method, *rpc*.
-
-Service Provider
-----------------
-
-For services to work, it is necessary that we have a service provider ready to
-dispatch the custom events. In the *backend* (web client), this is done by the
-main web client instance. Note that the code for the service provider comes from
-the *ServiceProviderMixin*.
-
-
-Widget
-------
-
-The widget is the part that requests a service. In order to do that, it simply
-triggers an event *call_service* (typically by using the helper function *call*).
-This event will bubble up and communicate the intent to the rest of the system.
-
-In practice, some functions are so frequently called that we have some helpers
-functions to make them easier to use. For example, the *_rpc* method is a helper
-that helps making a rpc.
-
-.. code-block:: javascript
-
- var SomeWidget = Widget.extend({
- _getActivityModelViewID: function (model) {
- return this._rpc({
- model: model,
- method: 'get_activity_view_id'
- });
- },
- });
-
-.. warning::
- If a widget is destroyed, it will be detached from the main component tree
- and will not have a parent. In that case, the events will not bubble up, which
- means that the work will not be done. This is usually exactly what we want from
- a destroyed widget.
-
-RPCs
-----
-
-The rpc functionality is supplied by the ajax service. But most people will
-probably only interact with the *_rpc* helpers.
-
-There are typically two usecases when working on Odoo: one may need to call a
-method on a (python) model (this goes through a controller *call_kw*), or one
-may need to directly call a controller (available on some route).
-
-* Calling a method on a python model:
-
-.. code-block:: javascript
-
- return this._rpc({
- model: 'some.model',
- method: 'some_method',
- args: [some, args],
- });
-
-* Directly calling a controller:
-
-.. code-block:: javascript
-
- return this._rpc({
- route: '/some/route/',
- params: { some: kwargs},
- });
-
-Notifications
-==============
-
-The Odoo framework has a standard way to communicate various information to the
-user: notifications, which are displayed on the top right of the user interface.
-
-There are two types of notifications:
-
-- *notification*: useful to display some feedback. For example, whenever a user
- unsubscribed to a channel.
-
-- *warning*: useful to display some important/urgent information. Typically
- most kind of (recoverable) errors in the system.
-
-Also, notifications can be used to ask a question to the user without disturbing
-its workflow. Imagine a phone call received through VOIP: a sticky notification
-could be displayed with two buttons *Accept* and *Decline*.
-
-Notification system
--------------------
-
-The notification system in Odoo is designed with the following components:
-
-- a *Notification* widget: this is a simple widget that is meant to be created
- and displayed with the desired information
-
-- a *NotificationService*: a service whose responsibility is to create and
- destroy notifications whenever a request is done (with a custom_event). Note
- that the web client is a service provider.
-
-- a client action *display_notification*: this allows to trigger the display
- of a notification from python (e.g. in the method called when the user
- clicked on a button of type object).
-
-- two helper functions in *ServiceMixin*: *do_notify* and *do_warn*
-
-
-Displaying a notification
--------------------------
-The most common way to display a notification is by using two methods that come
-from the *ServiceMixin*:
-
-- *do_notify(title, message, sticky, className)*:
- Display a notification of type *notification*.
-
- - *title*: string. This will be displayed on the top as a title
-
- - *message*: string, the content of the notification
-
- - *sticky*: boolean, optional. If true, the notification will stay until the
- user dismisses it. Otherwise, the notification will be automatically
- closed after a short delay.
-
- - *className*: string, optional. This is a css class name that will be
- automatically added to the notification. This could be useful for styling
- purpose, even though its use is discouraged.
-
-- *do_warn(title, message, sticky, className)*:
- Display a notification of type *warning*.
-
- - *title*: string. This will be displayed on the top as a title
-
- - *message*: string, the content of the notification
-
- - *sticky*: boolean, optional. If true, the notification will stay until the
- user dismisses it. Otherwise, the notification will be automatically
- closed after a short delay.
-
- - *className*: string, optional. This is a css class name that will be
- automatically added to the notification. This could be useful for styling
- purpose, even though its use is discouraged.
-
-Here are two examples on how to use these methods:
-
-.. code-block:: javascript
-
- // note that we call _t on the text to make sure it is properly translated.
- this.do_notify(_t("Success"), _t("Your signature request has been sent."));
-
- this.do_warn(_t("Error"), _t("Filter name is required."));
-
-Here an example in python:
-
-.. code-block:: python
-
- # note that we call _(string) on the text to make sure it is properly translated.
- def show_notification(self):
- return {
- 'type': 'ir.actions.client',
- 'tag': 'display_notification',
- 'params': {
- 'title': _('Success'),
- 'message': _('Your signature request has been sent.'),
- 'sticky': False,
- }
- }
-
-Systray
-=======
-
-The Systray is the right part of the menu bar in the interface, where the web
-client displays a few widgets, such as a messaging menu.
-
-When the SystrayMenu is created by the menu, it will look for all registered
-widgets and add them as a sub widget at the proper place.
-
-There is currently no specific API for systray widgets. They are supposed to
-be simple widgets, and can communicate with their environment just like other
-widgets with the *trigger_up* method.
-
-Adding a new Systray Item
--------------------------
-
-There is no systray registry. The proper way to add a widget is to add it to
-the class variable SystrayMenu.items.
-
-.. code-block:: javascript
-
- var SystrayMenu = require('web.SystrayMenu');
-
- var MySystrayWidget = Widget.extend({
- ...
- });
-
- SystrayMenu.Items.push(MySystrayWidget);
-
-
-Ordering
---------
-
-Before adding the widget to himself, the Systray Menu will sort the items by
-a sequence property. If that property is not present on the prototype, it will
-use 50 instead. So, to position a systray item to be on the right, one can
-set a very high sequence number (and conversely, a low number to put it on the
-left).
-
-.. code-block:: javascript
-
- MySystrayWidget.prototype.sequence = 100;
-
-
-Translation management
-======================
-
-Some translations are made on the server side (basically all text strings rendered or
-processed by the server), but there are strings in the static files that need
-to be translated. The way it currently works is the following:
-
-- each translatable string is tagged with the special function *_t* (available in
- the JS module *web.core*
-- these strings are used by the server to generate the proper PO files
-- whenever the web client is loaded, it will call the route */web/webclient/translations*,
- which returns a list of all translatable terms
-- in runtime, whenever the function *_t* is called, it will look up in this list
- in order to find a translation, and return it or the original string if none
- is found.
-
-Note that translations are explained in more details, from the server point of
-view, in the document :doc:`translations`.
-
-There are two important functions for the translations in javascript: *_t* and
-*_lt*. The difference is that *_lt* is lazily evaluated.
-
-.. code-block:: javascript
-
- var core = require('web.core');
-
- var _t = core._t;
- var _lt = core._lt;
-
- var SomeWidget = Widget.extend({
- exampleString: _lt('this should be translated'),
- ...
- someMethod: function () {
- var str = _t('some text');
- ...
- },
- });
-
-In this example, the *_lt* is necessary because the translations are not ready
-when the module is loaded.
-
-Note that translation functions need some care. The string given in argument
-should not be dynamic.
-
-Session
-=======
-
-There is a specific module provided by the web client which contains some
-information specific to the user current *session*. Some notable keys are
-
-- uid: the current user ID (its ID as a *res.users*)
-- user_name: the user name, as a string
-- the user context (user ID, language and timezone)
-- partner_id: the ID of the partner associated to the current user
-- db: the name of the database currently being in use
-
-Adding information to the session
----------------------------------
-
-When the /web route is loaded, the server will inject some session information
-in the template a script tag. The information will be read from the method
-*session_info* of the model *ir.http*. So, if one wants to add a specific
-information, it can be done by overriding the session_info method and adding it
-to the dictionary.
-
-.. code-block:: python
-
- from odoo import models
- from odoo.http import request
-
-
- class IrHttp(models.AbstractModel):
- _inherit = 'ir.http'
-
- def session_info(self):
- result = super(IrHttp, self).session_info()
- result['some_key'] = get_some_value_from_db()
- return result
-
-Now, the value can be obtained in javascript by reading it in the session:
-
-.. code-block:: javascript
-
- var session = require('web.session');
- var myValue = session.some_key;
- ...
-
-Note that this mechanism is designed to reduce the amount of communication
-needed by the web client to be ready. It is more appropriate for data which is
-cheap to compute (a slow session_info call will delay the loading for the web
-client for everyone), and for data which is required early in the initialization
-process.
-
-Views
-======
-
-The word 'view' has more than one meaning. This section is about the design of
-the javascript code of the views, not the structure of the *arch* or anything
-else.
-
-In 2017, Odoo replaced the previous view code with a new architecture. The
-main need was to separate the rendering logic from the model logic.
-
-
-Views (in a generic sense) are now described with 4 pieces: a View, a
-Controller, a Renderer and a Model. The API of these 4 pieces is described in
-the AbstractView, AbstractController, AbstractRenderer and AbstractModel classes.
-
-.. raw:: html
-
- <svg width="550" height="173">
- <!-- Created with Method Draw - https://github.com/duopixel/Method-Draw/ -->
- <path id="svg_1" d="m147.42498,79.79206c0.09944,-8.18859 -0.06363,-16.38812 0.81774,-24.5623c21.65679,2.68895 43.05815,7.08874 64.35,11.04543c1.14304,-4.01519 0.60504,-7.34585 1.59817,-11.05817c13.67878,7.81176 27.23421,15.73476 40.23409,24.03505c-12.47212,9.41539 -26.77809,17.592 -40.82272,25.96494c-0.4548,-3.89916 -0.90967,-7.79828 -1.36448,-11.69744c-20.69972,3.77225 -42.59036,7.6724 -63.42391,11.12096c-1.41678,-7.95741 -1.37514,-16.62327 -1.38888,-24.84846z" stroke-width="1.5" stroke="#000" fill="#fff"/>
- <rect id="svg_3" height="41" width="110" y="57.5" x="7" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="#fff"/>
- <rect stroke="#000" id="svg_5" height="41" width="135" y="20.5" x="328" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" fill="#fff"/>
- <rect stroke="#000" id="svg_6" height="41" width="128" y="102.5" x="262" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" fill="#fff"/>
- <rect stroke="#000" id="svg_7" height="41" width="119" y="100.5" x="417" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" fill="#fff"/>
- <line stroke-linecap="null" stroke-linejoin="null" id="svg_8" y2="96.5" x2="317" y1="65.5" x1="364" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="none"/>
- <line stroke-linecap="null" stroke-linejoin="null" id="svg_9" y2="96.5" x2="467" y1="63.5" x1="425" fill-opacity="null" stroke-opacity="null" stroke-width="1.5" stroke="#000" fill="none"/>
- <text xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="24" id="svg_10" y="83.5" x="38" fill-opacity="null" stroke-opacity="null" stroke-width="0" stroke="#000" fill="#000000">View</text>
- <text xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="24" id="svg_11" y="44.5" x="346" fill-opacity="null" stroke-opacity="null" stroke-width="0" stroke="#000" fill="#000000">Controller</text>
- <text xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="24" id="svg_12" y="128.5" x="276" fill-opacity="null" stroke-opacity="null" stroke-width="0" stroke="#000" fill="#000000">Renderer</text>
- <text xml:space="preserve" text-anchor="start" font-family="Helvetica, Arial, sans-serif" font-size="24" id="svg_13" y="127.5" x="442" fill-opacity="null" stroke-opacity="null" stroke-width="0" stroke="#000" fill="#000000">Model</text>
- </svg>
-
-- the View is the factory. Its job is to get a set of fields, arch, context and
- some other parameters, then to construct a Controller/Renderer/Model triplet.
-
- The view's role is to properly setup each piece of the MVC pattern, with the correct
- information. Usually, it has to process the arch string and extract the
- data necessary for each other parts of the view.
-
- Note that the view is a class, not a widget. Once its job has been done, it
- can be discarded.
-
-- the Renderer has one job: representing the data being viewed in a DOM element.
- Each view can render the data in a different way. Also, it should listen on
- appropriate user actions and notify its parent (the Controller) if necessary.
-
- The Renderer is the V in the MVC pattern.
-
-- the Model: its job is to fetch and hold the state of the view. Usually, it
- represents in some way a set of records in the database. The Model is the
- owner of the 'business data'. It is the M in the MVC pattern.
-
-- the Controller: its job is to coordinate the renderer and the model. Also, it
- is the main entry point for the rest of the web client. For example, when
- the user changes something in the search view, the *update* method of the
- controller will be called with the appropriate information.
-
- It is the C in the MVC pattern.
-
-.. note::
- The JS code for the views has been designed to be usable outside of the
- context of a view manager/action manager. They could be used in a client action,
- or, they could be displayed in the public website (with some work on the assets).
-
-.. _reference/js/widgets:
-
-Field Widgets
-=============
-
-A good part of the web client experience is about editing and creating data. Most
-of that work is done with the help of field widgets, which are aware of the field
-type and of the specific details on how a value should be displayed and edited.
-
-AbstractField
--------------
-
-The *AbstractField* class is the base class for all widgets in a view, for all
-views that support them (currently: Form, List, Kanban).
-
-There are many differences between the v11 field widgets and the previous versions.
-Let us mention the most important ones:
-
-- the widgets are shared between all views (well, Form/List/Kanban). No need to
- duplicate the implementation anymore. Note that it is possible to have a
- specialized version of a widget for a view, by prefixing it with the view name
- in the view registry: *list.many2one* will be chosen in priority over *many2one*.
-- the widgets are no longer the owner of the field value. They only represent
- the data and communicate with the rest of the view.
-- the widgets do no longer need to be able to switch between edit and readonly
- mode. Now, when such a change is necessary, the widget will be destroyed and
- rerendered again. It is not a problem, since they do not own their value
- anyway
-- the field widgets can be used outside of a view. Their API is slightly
- awkward, but they are designed to be standalone.
-
-Decorations
------------
-
-Like the list view, field widgets have a simple support for decorations. The
-goal of decorations is to have a simple way to specify a text color depending on
-the record current state. For example,
-
-.. code-block:: xml
-
- <field name="state" decoration-danger="amount &lt; 10000"/>
-
-The valid decoration names are:
-
-- decoration-bf
-- decoration-it
-- decoration-danger
-- decoration-info
-- decoration-muted
-- decoration-primary
-- decoration-success
-- decoration-warning
-
-Each decoration *decoration-X* will be mapped to a css class *text-X*, which is
-a standard bootstrap css class (except for *text-it* and *text-bf*, which are
-handled by odoo and correspond to italic and bold, respectively). Note that the
-value of the decoration attribute should be a valid python expression, which
-will be evaluated with the record as evaluation context.
-
-Non relational fields
----------------------
-
-We document here all non relational fields available by default, in no particular
-order.
-
-integer (FieldInteger)
- This is the default field type for fields of type *integer*.
-
- - Supported field types: *integer*
-
- Options:
-
- - type: setting the input type (*text* by default, can be set on *number*)
-
- On edit mode, the field is rendered as an input with the HTML attribute type
- setted on *number* (so user can benefit the native support, especially on
- mobile). In this case, the default formatting is disabled to avoid incompability.
-
- .. code-block:: xml
-
- <field name="int_value" options='{"type": "number"}'/>
-
- - step: set the step to the value up and down when the user click on buttons
- (only for input of type number, 1 by default)
-
- .. code-block:: xml
-
- <field name="int_value" options='{"type": "number", "step": 100}'/>
-
- - format: should the number be formatted. (true by default)
-
- By default, numbers are formatted according to locale parameters.
- This option will prevent the field's value from being formatted.
-
- .. code-block:: xml
-
- <field name="int_value" options='{"format": false}'/>
-
-float (FieldFloat)
- This is the default field type for fields of type *float*.
-
- - Supported field types: *float*
-
- Attributes:
-
- - digits: displayed precision
-
- .. code-block:: xml
-
- <field name="factor" digits="[42,5]"/>
-
- Options:
-
- - type: setting the input type (*text* by default, can be set on *number*)
-
- On edit mode, the field is rendered as an input with the HTML attribute type
- setted on *number* (so user can benefit the native support, especially on
- mobile). In this case, the default formatting is disabled to avoid incompability.
-
- .. code-block:: xml
-
- <field name="int_value" options='{"type": "number"}'/>
-
- - step: set the step to the value up and down when the user click on buttons
- (only for input of type number, 1 by default)
-
- .. code-block:: xml
-
- <field name="int_value" options='{"type": "number", "step": 0.1}'/>
-
- - format: should the number be formatted. (true by default)
-
- By default, numbers are formatted according to locale parameters.
- This option will prevent the field's value from being formatted.
-
- .. code-block:: xml
-
- <field name="int_value" options='{"format": false}'/>
-
-float_time (FieldFloatTime)
- The goal of this widget is to display properly a float value that represents
- a time interval (in hours). So, for example, 0.5 should be formatted as 0:30,
- or 4.75 correspond to 4:45.
-
- - Supported field types: *float*
-
-float_factor (FieldFloatFactor)
- This widget aims to display properly a float value that converted using a factor
- given in its options. So, for example, the value saved in database is 0.5 and the
- factor is 3, the widget value should be formatted as 1.5.
-
- - Supported field types: *float*
-
-float_toggle (FieldFloatToggle)
- The goal of this widget is to replace the input field by a button containing a
- range of possible values (given in the options). Each click allows the user to loop
- in the range. The purpose here is to restrict the field value to a predefined selection.
- Also, the widget support the factor conversion as the *float_factor* widget (Range values
- should be the result of the conversion).
-
- - Supported field types: *float*
-
- .. code-block:: xml
-
- <field name="days_to_close" widget="float_toggle" options='{"factor": 2, "range": [0, 4, 8]}'/>
-
-boolean (FieldBoolean)
- This is the default field type for fields of type *boolean*.
-
- - Supported field types: *boolean*
-
-char (FieldChar)
- This is the default field type for fields of type *char*.
-
- - Supported field types: *char*
-
-date (FieldDate)
- This is the default field type for fields of type *date*. Note that it also
- works with datetime fields. It uses the session timezone when formatting
- dates.
-
- - Supported field types: *date*, *datetime*
-
- Options:
-
- - datepicker: extra settings for the datepicker_ widget.
-
- .. code-block:: xml
-
- <field name="datefield" options='{"datepicker": {"daysOfWeekDisabled": [0, 6]}}'/>
-
-datetime (FieldDateTime)
- This is the default field type for fields of type *datetime*.
-
- - Supported field types: *date*, *datetime*
-
- Options:
-
- - datepicker: extra settings for the datepicker_ widget.
-
- .. code-block:: xml
-
- <field name="datetimefield" options='{"datepicker": {"daysOfWeekDisabled": [0, 6]}}'/>
-
-daterange (FieldDateRange)
- This widget allows the user to select start and end date into a single picker.
-
- - Supported field types: *date*, *datetime*
-
- Options:
-
- - related_start_date: apply on end date field to get start date value which
- is used to display range in the picker.
- - related_end_date: apply on start date field to get end date value which
- is used to display range in the picker.
- - picker_options: extra settings for picker.
-
- .. code-block:: xml
-
- <field name="start_date" widget="daterange" options='{"related_end_date": "end_date"}'/>
-
-remaining_days (RemainingDays)
- This widget can be used on date and datetime fields. In readonly, it displays
- the delta (in days) between the value of the field and today. The widget is
- intended to be used for informative purpose: therefore the value cannot be
- modified in edit mode.
-
- - Supported field types: *date*, *datetime*
-
-monetary (FieldMonetary)
- This is the default field type for fields of type 'monetary'. It is used to
- display a currency. If there is a currency fields given in option, it will
- use that, otherwise it will fall back to the default currency (in the session)
-
- - Supported field types: *monetary*, *float*
-
- Options:
-
- - currency_field: another field name which should be a many2one on currency.
-
- .. code-block:: xml
-
- <field name="value" widget="monetary" options="{'currency_field': 'currency_id'}"/>
-
-text (FieldText)
- This is the default field type for fields of type *text*.
-
- - Supported field types: *text*
-
-
-handle (HandleWidget)
- This field's job is to be displayed as a *handle*, and allows reordering the
- various records by drag and dropping them.
-
- .. warning:: It has to be specified on the field by which records are sorted.
- .. warning:: Having more than one field with a handle widget on the same list is not supported.
-
- - Supported field types: *integer*
-
-
-email (FieldEmail)
- This field displays email address. The main reason to use it is that it
- is rendered as an anchor tag with the proper href, in readonly mode.
-
- - Supported field types: *char*
-
-phone (FieldPhone)
- This field displays a phone number. The main reason to use it is that it
- is rendered as an anchor tag with the proper href, in readonly mode, but
- only in some cases: we only want to make it clickable if the device can
- call this particular number.
-
- - Supported field types: *char*
-
-url (UrlWidget)
- This field displays an url (in readonly mode). The main reason to use it is
- that it is rendered as an anchor tag with the proper css classes and href.
-
- Also, the text of the anchor tag can be customized with the *text* attribute
- (it won't change the href value).
-
- .. code-block:: xml
-
- <field name="foo" widget="url" text="Some URL"/>
-
- Options:
-
- - website_path: (default:false) by default, the widget forces (if not already
- the case) the href value to begin with http:// except if this option is set
- to true, thus allowing redirections to the database's own website.
-
- - Supported field types: *char*
-
-domain (FieldDomain)
- The "Domain" field allows the user to construct a technical-prefix domain
- thanks to a tree-like interface and see the selected records in real time.
- In debug mode, an input is also there to be able to enter the prefix char
- domain directly (or to build advanced domains the tree-like interface does
- not allow to).
-
- Note that this is limited to 'static' domain (no dynamic expression, or access
- to context variable).
-
- - Supported field types: *char*
-
-link_button (LinkButton)
- The LinkButton widget actually simply displays a span with an icon and the
- text value as content. The link is clickable and will open a new browser
- window with its value as url.
-
- - Supported field types: *char*
-
-image (FieldBinaryImage)
- This widget is used to represent a binary value as an image. In some cases,
- the server returns a 'bin_size' instead of the real image (a bin_size is a
- string representing a file size, such as 6.5kb). In that case, the widget
- will make an image with a source attribute corresponding to an image on the
- server.
-
- - Supported field types: *binary*
-
- Options:
-
- - preview_image: if the image is only loaded as a 'bin_size', then this
- option is useful to inform the web client that the default field name is
- not the name of the current field, but the name of another field.
-
- - accepted_file_extensions: the file extension the user can pick from the file input dialog box (default value is `image/\*`)
- (cf: ``accept`` attribute on <input type="file"/>)
-
- .. code-block:: xml
-
- <field name="image" widget='image' options='{"preview_image":"image_128"}'/>
-
-binary (FieldBinaryFile)
- Generic widget to allow saving/downloading a binary file.
-
- - Supported field types: *binary*
-
- Options:
-
- - accepted_file_extensions: the file extension the user can pick from the file input dialog box
- (cf: ``accept`` attribute on <input type="file"/>)
-
- Attribute:
-
- - filename: saving a binary file will lose its file name, since it only
- saves the binary value. The filename can be saved in another field. To do
- that, an attribute filename should be set to a field present in the view.
-
- .. code-block:: xml
-
- <field name="datas" filename="datas_fname"/>
-
-priority (PriorityWidget)
- This widget is rendered as a set of stars, allowing the user to click on it
- to select a value or not. This is useful for example to mark a task as high
- priority.
-
- Note that this widget also works in 'readonly' mode, which is unusual.
-
- - Supported field types: *selection*
-
-attachment_image (AttachmentImage)
- Image widget for many2one fields. If the field is set, this widget will be
- rendered as an image with the proper src url. This widget does not have a
- different behaviour in edit or readonly mode, it is only useful to view an
- image.
-
- - Supported field types: *many2one*
-
- .. code-block:: xml
-
- <field name="displayed_image_id" widget="attachment_image"/>
-
-image_selection (ImageSelection)
- Allow the user to select a value by clicking on an image.
-
- - Supported field types: *selection*
-
- Options: a dictionary with a mapping from a selection value to an object with
- the url for an image (*image_link*) and a preview image (*preview_link*).
-
- Note that this option is not optional!
-
- .. code-block:: xml
-
- <field name="external_report_layout" widget="image_selection" options="{
- 'background': {
- 'image_link': '/base/static/img/preview_background.png',
- 'preview_link': '/base/static/pdf/preview_background.pdf'
- },
- 'standard': {
- 'image_link': '/base/static/img/preview_standard.png',
- 'preview_link': '/base/static/pdf/preview_standard.pdf'
- }
- }"/>
-
-label_selection (LabelSelection)
- This widget renders a simple non-editable label. This is only useful to
- display some information, not to edit it.
-
- - Supported field types: *selection*
-
- Options:
-
- - classes: a mapping from a selection value to a css class
-
- .. code-block:: xml
-
- <field name="state" widget="label_selection" options="{
- 'classes': {'draft': 'default', 'cancel': 'default', 'none': 'danger'}
- }"/>
-
-state_selection (StateSelectionWidget)
- This is a specialized selection widget. It assumes that the record has some
- hardcoded fields, present in the view: *stage_id*, *legend_normal*,
- *legend_blocked*, *legend_done*. This is mostly used to display and change
- the state of a task in a project, with additional information displayed in
- the dropdown.
-
- - Supported field types: *selection*
-
- .. code-block:: xml
-
- <field name="kanban_state" widget="state_selection"/>
-
-kanban_state_selection (StateSelectionWidget)
- This is exactly the same widget as state_selection
-
- - Supported field types: *selection*
-
-boolean_favorite (FavoriteWidget)
- This widget is displayed as an empty (or not) star, depending on a boolean
- value. Note that it also can be edited in readonly mode.
-
- - Supported field types: *boolean*
-
-boolean_button (FieldBooleanButton)
- The Boolean Button widget is meant to be used in a stat button in a form view.
- The goal is to display a nice button with the current state of a boolean
- field (for example, 'Active'), and allow the user to change that field when
- clicking on it.
-
- Note that it also can be edited in readonly mode.
-
- - Supported field types: *boolean*
-
- Options:
-
- - terminology: it can be either 'active', 'archive', 'close' or a customized
- mapping with the keys *string_true*, *string_false*, *hover_true*, *hover_false*
-
- .. code-block:: xml
-
- <field name="active" widget="boolean_button" options='{"terminology": "archive"}'/>
-
-boolean_toggle (BooleanToggle)
- Displays a toggle switch to represent a boolean. This is a subfield of
- FieldBoolean, mostly used to have a different look.
-
-statinfo (StatInfo)
- This widget is meant to represent statistical information in a *stat button*.
- It is basically just a label with a number.
-
- - Supported field types: *integer, float*
-
- Options:
-
- - label_field: if given, the widget will use the value of the label_field as
- text.
-
- .. code-block:: xml
-
- <button name="%(act_payslip_lines)d"
- icon="fa-money"
- type="action">
- <field name="payslip_count" widget="statinfo"
- string="Payslip"
- options="{'label_field': 'label_tasks'}"/>
- </button>
-
-percentpie (FieldPercentPie)
- This widget is meant to represent statistical information in a *stat button*.
- This is similar to a statinfo widget, but the information is represented in
- a *pie* chart (empty to full). Note that the value is interpreted as a
- percentage (a number between 0 and 100).
-
- - Supported field types: *integer, float*
-
- .. code-block:: xml
-
- <field name="replied_ratio" string="Replied" widget="percentpie"/>
-
-progressbar (FieldProgressBar)
- Represent a value as a progress bar (from 0 to some value)
-
- - Supported field types: *integer, float*
-
- Options:
-
- - editable: boolean if value is editable
- - current_value: get the current_value from the field that must be present in the view
- - max_value: get the max_value from the field that must be present in the view
- - edit_max_value: boolean if the max_value is editable
- - title: title of the bar, displayed on top of the bar --> not translated,
- use parameter (not option) "title" instead
-
- .. code-block:: xml
-
- <field name="absence_of_today" widget="progressbar"
- options="{'current_value': 'absence_of_today', 'max_value': 'total_employee', 'editable': false}"/>
-
-toggle_button (FieldToggleBoolean)
- This widget is intended to be used on boolean fields. It toggles a button
- switching between a green bullet / gray bullet. It also set up a tooltip,
- depending on the value and some options.
-
- - Supported field types: *boolean*
-
- Options:
-
- - active: the string for the tooltip that should be set when boolean is true
- - inactive: the tooltip that should be set when boolean is false
-
- .. code-block:: xml
-
- <field name="payslip_status" widget="toggle_button"
- options='{"active": "Reported in last payslips", "inactive": "To Report in Payslip"}'
- />
-
-dashboard_graph (JournalDashboardGraph)
- This is a more specialized widget, useful to display a graph representing a
- set of data. For example, it is used in the accounting dashboard kanban view.
-
- It assumes that the field is a JSON serialization of a set of data.
-
- - Supported field types: *char*
-
- Attribute
-
- - graph_type: string, can be either 'line' or 'bar'
-
- .. code-block:: xml
-
- <field name="dashboard_graph_data"
- widget="dashboard_graph"
- graph_type="line"/>
-
-ace (AceEditor)
- This widget is intended to be used on Text fields. It provides Ace Editor
- for editing XML and Python.
-
- - Supported field types: *char, text*
-
-- badge (FieldBadge)
- Displays the value inside a bootstrap badge pill.
-
- - Supported field types: *char*, *selection*, *many2one*
-
- By default, the badge has a lightgrey background, but it can be customized
- by using the decoration-X mechanism. For instance, to display a red badge
- under a given condition:
-
- .. code-block:: xml
-
- <field name="foo" widget"badge" decoration-danger="state == 'cancel'"/>
-
-Relational fields
------------------
-
-.. class:: web.relational_fields.FieldSelection
-
- Supported field types: *selection*
-
- .. attribute:: placeholder
-
- a string which is used to display some info when no value is selected
-
- .. code-block:: xml
-
- <field name="tax_id" widget="selection" placeholder="Select a tax"/>
-
-radio (FieldRadio)
- This is a subfield of FielSelection, but specialized to display all the
- valid choices as radio buttons.
-
- Note that if used on a many2one records, then more rpcs will be done to fetch
- the name_gets of the related records.
-
- - Supported field types: *selection, many2one*
-
- Options:
-
- - horizontal: if true, radio buttons will be displayed horizontally.
-
- .. code-block:: xml
-
- <field name="recommended_activity_type_id" widget="radio"
- options="{'horizontal':true}"/>
-
-selection_badge (FieldSelectionBadge)
- This is a subfield of FieldSelection, but specialized to display all the
- valid choices as rectangular badges.
-
- - Supported field types: *selection, many2one*
-
- .. code-block:: xml
-
- <field name="recommended_activity_type_id" widget="selection_badge"/>
-
-many2one (FieldMany2One)
- Default widget for many2one fields.
-
- - Supported field types: *many2one*
-
- Attributes:
-
- - can_create: allow the creation of related records (take precedence over no_create
- option)
- - can_write: allow the editing of related records (default: true)
-
- Options:
-
- - no_create: prevent the creation of related records
- - quick_create: allow the quick creation of related records (default: true)
- - no_quick_create: prevent the quick creation of related records (don't ask me)
- - no_create_edit: same as no_create, maybe...
- - create_name_field: when creating a related record, if this option is set, the value of the *create_name_field* will be filled with the value of the input (default: *name*)
- - always_reload: boolean, default to false. If true, the widget will always
- do an additional name_get to fetch its name value. This is used for the
- situations where the name_get method is overridden (please do not do that)
- - no_open: boolean, default to false. If set to true, the many2one will not
- redirect on the record when clicking on it (in readonly mode)
-
- .. code-block:: xml
-
- <field name="currency_id" options="{'no_create': True, 'no_open': True}"/>
-
-list.many2one (ListFieldMany2One)
- Default widget for many2one fields (in list view).
-
- Specialization of many2one field for list views. The main reason is that we
- need to render many2one fields (in readonly mode) as a text, which does not
- allow opening the related records.
-
- - Supported field types: *many2one*
-
-many2one_barcode (FieldMany2OneBarcode)
- Widget for many2one fields allows to open the camera from a mobile device (Android/iOS) to scan a barcode.
-
- Specialization of many2one field where the user is allowed to use the native camera to scan a barcode.
- Then it uses name_search to search this value.
-
- If this widget is set and user is not using the mobile application,
- it will fallback to regular many2one (FieldMany2One)
-
- - Supported field types: *many2one*
-
-many2one_avatar (Many2OneAvatar)
- This widget is only supported on many2one fields pointing to a model which
- inherits from 'image.mixin'. In readonly, it displays the image of the
- related record next to its display_name. Note that the display_name isn't a
- clickable link in this case. In edit, it behaves exactly like the regular
- many2one.
-
- - Supported field types: *many2one*
-
-many2one_avatar_user (Many2OneAvatarUser)
- This widget is a specialization of the Many2OneAvatar. When the avatar is
- clicked, we open a chat window with the corresponding user. This widget can
- only be set on many2one fields pointing to the 'res.users' model.
-
- - Supported field types: *many2one* (pointing to 'res.users')
-
-many2one_avatar_employee (Many2OneAvatarEmployee)
- Same as Many2OneAvatarUser, but for many2one fields pointing to 'hr.employee'.
-
- - Supported field types: *many2one* (pointing to 'hr.employee')
-
-kanban.many2one (KanbanFieldMany2One)
- Default widget for many2one fields (in kanban view). We need to disable all
- editing in kanban views.
-
- - Supported field types: *many2one*
-
-many2many (FieldMany2Many)
- Default widget for many2many fields.
-
- - Supported field types: *many2many*
-
- Attributes:
-
- - mode: string, default view to display
- - domain: restrict the data to a specific domain
-
- Options:
-
- - create_text: allow the customization of the text displayed when adding a
- new record
- - link: domain determining whether or not records can be added to the relation (default: True).
- - unlink: domain determining whether or not records can be removed from the relation (default: True).
-
-many2many_binary (FieldMany2ManyBinaryMultiFiles)
- This widget helps the user to upload or delete one or more files at the same
- time.
-
- Note that this widget is specific to the model 'ir.attachment'.
-
- - Supported field types: *many2many*
-
- Options:
-
- - accepted_file_extensions: the file extension the user can pick from the file input dialog box
- (cf: ``accept`` attribute on <input type="file"/>)
-
-many2many_tags (FieldMany2ManyTags)
- Display many2many as a list of tags.
-
- - Supported field types: *many2many*
-
- Options:
-
- - create: domain determining whether or not new tags can be created (default: True).
-
- .. code-block:: xml
-
- <field name="category_id" widget="many2many_tags" options="{'create': [['some_other_field', '>', 24]]}"/>
-
- - color_field: the name of a numeric field, which should be present in the
- view. A color will be chosen depending on its value.
-
- .. code-block:: xml
-
- <field name="category_id" widget="many2many_tags" options="{'color_field': 'color'}"/>
-
- - no_edit_color: set to True to remove the possibility to change the color of the tags (default: False).
-
- .. code-block:: xml
-
- <field name="category_id" widget="many2many_tags" options="{'color_field': 'color', 'no_edit_color': True}"/>
-
-form.many2many_tags (FormFieldMany2ManyTags)
- Specialization of many2many_tags widget for form views. It has some extra
- code to allow editing the color of a tag.
-
- - Supported field types: *many2many*
-
-kanban.many2many_tags (KanbanFieldMany2ManyTags)
- Specialization of many2many_tags widget for kanban views.
-
- - Supported field types: *many2many*
-
-many2many_checkboxes (FieldMany2ManyCheckBoxes)
- This field displays a list of checkboxes and allows the user to select a
- subset of the choices. Note that the number of displayed values is limited to
- 100. This limit isn't customizable. It simply allows to handle extreme cases
- where this widget is wrongly set on a field with a huge comodel. In those
- cases, a list view is more adequate as it allows pagination and filtering.
-
- - Supported field types: *many2many*
-
-one2many (FieldOne2Many)
- Default widget for one2many fields.
-
- It usually displays data in a sub list view, or a sub kanban view.
-
- - Supported field types: *one2many*
-
- Options:
-
- - create: domain determining whether or not related records can be created (default: True).
-
- - delete: domain determining whether or not related records can be deleted (default: True).
-
- .. code-block:: xml
-
- <field name="turtles" options="{'create': [['some_other_field', '>', 24]]}"/>
-
- - create_text: a string that is used to customize the 'Add' label/text.
-
- .. code-block:: xml
-
- <field name="turtles" options="{\'create_text\': \'Add turtle\'}">
-
-statusbar (FieldStatus)
- This is a really specialized widget for the form views. It is the bar on top
- of many forms which represent a flow, and allow selecting a specific state.
-
- - Supported field types: *selection, many2one*
-
-reference (FieldReference)
- The FieldReference is a combination of a select (for the model) and a
- FieldMany2One (for its value). It allows the selection of a record on an
- arbitrary model.
-
- - Supported field types: *char, reference*
-
-
-Client actions
-==============
-
-The idea of a client action is a customized widget that is integrated in the
-web client interface, just like a *act_window_action*. This is useful when
-you need a component that is not closely linked to an existing view or a
-specific model. For example, the Discuss application is actually a client
-action.
-
-A client action is a term that has various meanings, depending on the context:
-
-- from the perspective of the server, it is a record of the model *ir_action*,
- with a field *tag* of type char
-- from the perspective of the web client, it is a widget, which inherit from
- the class AbstractAction, and is supposed to be registered in the
- action registry under the corresponding key (from the field char)
-
-Whenever a menu item is associated to a client action, opening it will simply
-fetch the action definition from the server, then lookup into its action
-registry to get the Widget definition at the appropriate key, and finally, it
-will instantiate and append the widget to the proper place in the DOM.
-
-Adding a client action
-----------------------
-
-A client action is a widget which will control the part of the screen below the
-menu bar. It can have a control panel, if necessary. Defining a client action
-can be done in two steps: implementing a new widget, and registering the widget
-in the action registry.
-
-Implementing a new client action.
- This is done by creating a widget:
-
- .. code-block:: javascript
-
- var AbstractAction = require('web.AbstractAction');
-
- var ClientAction = AbstractAction.extend({
- hasControlPanel: true,
- ...
- });
-
-Registering the client action:
- As usual, we need to make the web client aware of the mapping between
- client actions and the actual class:
-
- .. code-block:: javascript
-
- var core = require('web.core');
-
- core.action_registry.add('my-custom-action', ClientAction);
-
-
- Then, to use the client action in the web client, we need to create a client
- action record (a record of the model ``ir.actions.client``) with the proper
- ``tag`` attribute:
-
- .. code-block:: xml
-
- <record id="my_client_action" model="ir.actions.client">
- <field name="name">Some Name</field>
- <field name="tag">my-custom-action</field>
- </record>
-
-
-Using the control panel
------------------------
-
-By default, the client action does not display a control panel. In order to
-do that, several steps should be done.
-
-- Set the *hasControlPanel* to *true*.
- In the widget code:
-
- .. code-block:: javascript
-
- var MyClientAction = AbstractAction.extend({
- hasControlPanel: true,
- loadControlPanel: true, // default: false
- ...
- });
-
- .. warning::
- when the ``loadControlPanel`` is set to true, the client action will automatically get the content of a search view or a control panel view.
- In this case, a model name should be specified like this:
-
- .. code-block:: javascript
-
- init: function (parent, action, options) {
- ...
- this.controlPanelParams.modelName = 'model.name';
- ...
- }
-
-- Call the method *updateControlPanel* whenever we need to update the control panel.
- For example:
-
- .. code-block:: javascript
-
- var SomeClientAction = Widget.extend({
- hasControlPanel: true,
- ...
- start: function () {
- this._renderButtons();
- this._update_control_panel();
- ...
- },
- do_show: function () {
- ...
- this._update_control_panel();
- },
- _renderButtons: function () {
- this.$buttons = $(QWeb.render('SomeTemplate.Buttons'));
- this.$buttons.on('click', ...);
- },
- _update_control_panel: function () {
- this.updateControlPanel({
- cp_content: {
- $buttons: this.$buttons,
- },
- });
- }
-
-The ``updateControlPanel`` is the main method to customize the content in controlpanel.
-For more information, look into the `control_panel_renderer.js <https://github.com/odoo/odoo/blob/13.0/addons/web/static/src/js/views/control_panel/control_panel_renderer.js#L130>`_ file.
-
-.. _.appendTo():
- https://api.jquery.com/appendTo/
-
-.. _.prependTo():
- https://api.jquery.com/prependTo/
-
-.. _.insertAfter():
- https://api.jquery.com/insertAfter/
-
-.. _.insertBefore():
- https://api.jquery.com/insertBefore/
-
-.. _event delegation:
- https://api.jquery.com/delegate/
-
-.. _datepicker: https://github.com/Eonasdan/bootstrap-datetimepicker
diff --git a/doc/reference/mixins.rst b/doc/reference/mixins.rst
deleted file mode 100644
index 953b604ffa3..00000000000
--- a/doc/reference/mixins.rst
+++ /dev/null
@@ -1,1076 +0,0 @@
-:banner: banners/mixins.jpg
-
-.. _reference/mixins:
-
-=========================
-Mixins and Useful Classes
-=========================
-
-Odoo implements some useful classes and mixins that make it easy for you to add
-often-used behaviours on your objects. This guide will details most of them, with
-examples and use cases.
-
-.. _reference/mixins/mail:
-
-Messaging features
-==================
-
-.. _reference/mixins/mail/chatter:
-
-Messaging integration
----------------------
-
-Basic messaging system
-''''''''''''''''''''''
-
-Integrating messaging features to your model is extremely easy. Simply inheriting
-the ``mail.thread`` model and adding the messaging fields (and their appropriate
-widgets) to your form view will get you up and running in no time.
-
-.. admonition:: Example
-
- Let's create a simplistic model representing a business trip. Since organizing
- this kind of trip usually involves a lot of people and a lot of discussion, let's
- add support for message exchange on the model.
-
- .. code-block:: python
-
- class BusinessTrip(models.Model):
- _name = 'business.trip'
- _inherit = ['mail.thread']
- _description = 'Business Trip'
-
- name = fields.Char()
- partner_id = fields.Many2one('res.partner', 'Responsible')
- guest_ids = fields.Many2many('res.partner', 'Participants')
-
- In the form view:
-
- .. code-block:: xml
-
- <record id="businness_trip_form" model="ir.ui.view">
- <field name="name">business.trip.form</field>
- <field name="model">business.trip</field>
- <field name="arch" type="xml">
- <form string="Business Trip">
- <!-- Your usual form view goes here
- ...
- Then comes chatter integration -->
- <div class="oe_chatter">
- <field name="message_follower_ids" widget="mail_followers"/>
- <field name="message_ids" widget="mail_thread"/>
- </div>
- </form>
- </field>
- </record>
-
-Once you've added chatter support on your model, users can easily add messages
-or internal notes on any record of your model; every one of those will send a
-notification (to all followers for messages, to employee (*base.group_user*)
-users for internal notes). If your mail gateway and catchall address are correctly
-configured, these notifications will be sent by e-mail and can be replied-to directly
-from your mail client; the automatic routing system will route the answer to the
-correct thread.
-
-Server-side, some helper functions are there to help you easily send messages and
-to manage followers on your record:
-
-.. rubric:: Posting messages
-
-.. method:: message_post(self, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, **kwargs)
-
- Post a new message in an existing thread, returning the new
- mail.message ID.
-
- :param str body: body of the message, usually raw HTML that will
- be sanitized
- :param str message_type: see mail_message.message_type field
- :param int parent_id: handle reply to a previous message by adding the
- parent partners to the message in case of private discussion
- :param list(tuple(str,str)) attachments: list of attachment tuples in the form
- ``(name,content)``, where content is NOT base64 encoded
- :param `\**kwargs`: extra keyword arguments will be used as default column values for the
- new mail.message record
- :return: ID of newly created mail.message
- :rtype: int
-
-.. method:: message_post_with_view(views_or_xmlid, **kwargs):
-
- Helper method to send a mail / post a message using a view_id to
- render using the ir.qweb engine. This method is stand alone, because
- there is nothing in template and composer that allows to handle
- views in batch. This method will probably disappear when templates
- handle ir ui views.
-
- :param str or ``ir.ui.view`` record: external id or record of the view that
- should be sent
-
-.. method:: message_post_with_template(template_id, **kwargs)
-
- Helper method to send a mail with a template
-
- :param template_id: the id of the template to render to create the body of the message
- :param `\**kwargs`: parameter to create a mail.compose.message wizzard (which inherit from mail.message)
-
-.. rubric:: Receiving messages
-
-These methods are called when a new e-mail is processed by the mail gateway. These
-e-mails can either be new thread (if they arrive via an :ref:`alias <reference/mixins/mail/alias>`)
-or simply replies from an existing thread. Overriding them allows you to set values
-on the thread's record depending on some values from the email itself (i.e. update
-a date or an e-mail address, add CC's addresses as followers, etc.).
-
-.. method:: message_new(msg_dict, custom_values=None)
-
- Called by ``message_process`` when a new message is received
- for a given thread model, if the message did not belong to
- an existing thread.
-
- The default behavior is to create a new record of the corresponding
- model (based on some very basic info extracted from the message).
- Additional behavior may be implemented by overriding this method.
-
- :param dict msg_dict: a map containing the email details and
- attachments. See ``message_process`` and ``mail.message.parse`` for details
- :param dict custom_values: optional dictionary of additional
- field values to pass to create() when creating the new thread record;
- be careful, these values may override any other values coming from
- the message
- :rtype: int
- :return: the id of the newly created thread object
-
-.. method:: message_update(msg_dict, update_vals=None)
-
- Called by ``message_process`` when a new message is received
- for an existing thread. The default behavior is to update the record
- with ``update_vals`` taken from the incoming email.
-
- Additional behavior may be implemented by overriding this
- method.
-
- :param dict msg_dict: a map containing the email details and attachments;
- see ``message_process`` and ``mail.message.parse()`` for details.
- :param dict update_vals: a dict containing values to update records given
- their ids; if the dict is None or is void, no write operation is performed.
- :return: True
-
-.. rubric:: Followers management
-
-.. method:: message_subscribe(partner_ids=None, channel_ids=None, subtype_ids=None, force=True)
-
- Add partners to the records followers.
-
- :param list(int) partner_ids: IDs of the partners that will be subscribed
- to the record
- :param list(int) channel_ids: IDs of the channels that will be subscribed
- to the record
- :param list(int) subtype_ids: IDs of the subtypes that the channels/partners
- will be subscribed to (defaults to the default subtypes if ``None``)
- :param force: if True, delete existing followers before creating new one
- using the subtypes given in the parameters
- :return: Success/Failure
- :rtype: bool
-
-
-.. method:: message_unsubscribe(partner_ids=None, channel_ids=None)
-
- Remove partners from the record's followers.
-
- :param list(int) partner_ids: IDs of the partners that will be subscribed
- to the record
- :param list(int) channel_ids: IDs of the channels that will be subscribed
- to the record
- :return: True
- :rtype: bool
-
-
-.. method:: message_unsubscribe_users(user_ids=None)
-
- Wrapper on message_subscribe, using users.
-
- :param list(int) user_ids: IDs of the users that will be unsubscribed
- to the record; if None, unsubscribe the current user instead.
- :return: True
- :rtype: bool
-
-
-Logging changes
-'''''''''''''''
-
-The ``mail`` module adds a powerful tracking system on fields, allowing you
-to log changes to specific fields in the record's chatter. To add tracking
-to a field, simple set the tracking attribute to True.
-
-.. admonition:: Example
-
- Let's track changes on the name and responsible of our business trips:
-
- .. code-block:: python
-
- class BusinessTrip(models.Model):
- _name = 'business.trip'
- _inherit = ['mail.thread']
- _description = 'Business Trip'
-
- name = fields.Char(tracking=True)
- partner_id = fields.Many2one('res.partner', 'Responsible',
- tracking=True)
- guest_ids = fields.Many2many('res.partner', 'Participants')
-
- From now on, every change to a trip's name or responsible will log a note
- on the record. The ``name`` field will be displayed in the notification as
- well to give more context about the notification (even if the name did not
- change).
-
-
-Subtypes
-''''''''
-
-Subtypes give you more granular control over messages. Subtypes act as a classification
-system for notifications, allowing subscribers to a document to customize the
-subtype of notifications they wish to receive.
-
-Subtypes are created as data in your module; the model has the following fields:
-
-``name`` (mandatory) - :class:`~odoo.fields.Char`
- name of the subtype, will be displayed in the notification customization
- popup
-``description`` - :class:`~odoo.fields.Char`
- description that will be added in the message posted for this
- subtype. If void, the name will be added instead
-``internal`` - :class:`~odoo.fields.Boolean`
- messages with internal subtypes will be visible only by employees,
- aka members of the ``base.group_user`` group
-``parent_id`` - :class:`~odoo.fields.Many2one`
- link subtypes for automatic subscription; for example project subtypes are
- linked to task subtypes through this link. When someone is subscribed to
- a project, he will be subscribed to all tasks of this project with
- subtypes found using the parent subtype
-``relation_field`` - :class:`~odoo.fields.Char`
- as an example, when linking project and tasks subtypes, the relation
- field is the project_id field of tasks
-``res_model`` - :class:`~odoo.fields.Char`
- model the subtype applies to; if False, this subtype applies to all models
-``default`` - :class:`~odoo.fields.Boolean`
- wether the subtype is activated by default when subscribing
-``sequence`` - :class:`~odoo.fields.Integer`
- used to order subtypes in the notification customization popup
-``hidden`` - :class:`~odoo.fields.Boolean`
- wether the subtype is hidden in the notification customization popup
-
-
-Interfacing subtypes with field tracking allows to subscribe to different kind
-of notifications depending on what might interest users. To do this, you
-can override the ``_track_subtype()`` function:
-
-.. method:: _track_subtype(init_values)
-
- Give the subtype triggered by the changes on the record according
- to values that have been updated.
-
- :param dict init_values: the original values of the record; only modified fields
- are present in the dict
- :returns: a subtype's full external id or False if no subtype is triggered
-
-
-.. admonition:: Example
-
- Let's add a ``state`` field on our example class and trigger a notification
- with a specific subtype when this field change values.
-
- First, let's define our subtype:
-
- .. code-block:: xml
-
- <record id="mt_state_change" model="mail.message.subtype">
- <field name="name">Trip confirmed</field>
- <field name="res_model">business.trip</field>
- <field name="default" eval="True"/>
- <field name="description">Business Trip confirmed!</field>
- </record>
-
-
- Then, we need to override the ``track_subtype()`` function. This function
- is called by the tracking system to know which subtype should be used depending
- on the change currently being applied. In our case, we want to use our shiny new
- subtype when the ``state`` field changes from *draft* to *confirmed*:
-
- .. code-block:: python
-
- class BusinessTrip(models.Model):
- _name = 'business.trip'
- _inherit = ['mail.thread']
- _description = 'Business Trip'
-
- name = fields.Char(tracking=True)
- partner_id = fields.Many2one('res.partner', 'Responsible',
- tracking=True)
- guest_ids = fields.Many2many('res.partner', 'Participants')
- state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
- tracking=True)
-
- def _track_subtype(self, init_values):
- # init_values contains the modified fields' values before the changes
- #
- # the applied values can be accessed on the record as they are already
- # in cache
- self.ensure_one()
- if 'state' in init_values and self.state == 'confirmed':
- return self.env.ref('my_module.mt_state_change')
- return super(BusinessTrip, self)._track_subtype(init_values)
-
-
-Customizing notifications
-'''''''''''''''''''''''''
-
-When sending notifications to followers, it can be quite useful to add buttons in
-the template to allow quick actions directly from the e-mail. Even a simple button
-to link directly to the record's form view can be useful; however in most cases
-you don't want to display these buttons to portal users.
-
-The notification system allows customizing notification templates in the following
-ways:
-
-- Display *Access Buttons*: these buttons are visible at the top of the notification
- e-mail and allow the recipient to directly access the form view of the record
-- Display *Follow Buttons*: these buttons allow the recipient to
- directly quickly subscribe from the record
-- Display *Unfollow Buttons*: these buttons allow the recipient to
- directly quickly unsubscribe from the record
-- Display *Custom Action Buttons*: these buttons are calls to specific routes
- and allow you to make some useful actions directly available from the e-mail (i.e.
- converting a lead to an opportunity, validating an expense sheet for an
- Expense Manager, etc.)
-
-These buttons settings can be applied to different groups that you can define
-yourself by overriding the function ``_notification_recipients``.
-
-.. method:: _notification_recipients(message, groups)
-
- Give the subtype triggered by the changes on the record according
- to values that have been updated.
-
- :param ``record`` message: ``mail.message`` record currently being sent
- :param list(tuple) groups: list of tuple of the form (group_name, group_func,group_data) where:
-
- group_name
- is an identifier used only to be able to override and manipulate
- groups. Default groups are ``user`` (recipients linked to an employee user),
- ``portal`` (recipients linked to a portal user) and ``customer`` (recipients not
- linked to any user). An example of override use would be to add a group
- linked to a res.groups like Hr Officers to set specific action buttons to
- them.
- group_func
- is a function pointer taking a partner record as parameter. This
- method will be applied on recipients to know whether they belong to a given
- group or not. Only first matching group is kept. Evaluation order is the
- list order.
- group_data
- is a dict containing parameters for the notification email with the following
- possible keys - values:
-
- - has_button_access
- whether to display Access <Document> in email. True by default for
- new groups, False for portal / customer.
- - button_access
- dict with url and title of the button
- - has_button_follow
- whether to display Follow in email (if recipient is not currently
- following the thread). True by default for new groups, False for
- portal / customer.
- - button_follow
- dict with url adn title of the button
- - has_button_unfollow
- whether to display Unfollow in email (if recipient is currently following the thread).
- True by default for new groups, False for portal / customer.
- - button_unfollow
- dict with url and title of the button
- - actions
- list of action buttons to display in the notification email.
- Each action is a dict containing url and title of the button.
-
- :returns: a subtype's full external id or False if no subtype is triggered
-
-
-The urls in the actions list can be generated automatically by calling the
-``_notification_link_helper()`` function:
-
-
-.. method:: _notification_link_helper(self, link_type, **kwargs)
-
- Generate a link for the given type on the current record (or on a specific
- record if the kwargs ``model`` and ``res_id`` are set).
-
- :param str link_type: link type to be generated; can be any of these values:
-
- ``view``
- link to form view of the record
- ``assign``
- assign the logged user to the ``user_id`` field of
- the record (if it exists)
- ``follow``
- self-explanatory
- ``unfollow``
- self-explanatory
- ``method``
- call a method on the record; the method's name should be
- provided as the kwarg ``method``
- ``new``
- open an empty form view for a new record; you can specify
- a specific action by providing its id (database id or fully resolved
- external id) in the kwarg ``action_id``
-
- :returns: link of the type selected for the record
- :rtype: str
-
-.. admonition:: Example
-
- Let's add a custom button to the Business Trip state change notification;
- this button will reset the state to Draft and will be only visible to a member
- of the (imaginary) group Travel Manager (``business.group_trip_manager``)
-
- .. code-block:: python
-
- class BusinessTrip(models.Model):
- _name = 'business.trip'
- _inherit = ['mail.thread', 'mail.alias.mixin']
- _description = 'Business Trip'
-
- # Pevious code goes here
-
- def action_cancel(self):
- self.write({'state': 'draft'})
-
- def _notification_recipients(self, message, groups):
- """ Handle Trip Manager recipients that can cancel the trip at the last
- minute and kill all the fun. """
- groups = super(BusinessTrip, self)._notification_recipients(message, groups)
-
- self.ensure_one()
- if self.state == 'confirmed':
- app_action = self._notification_link_helper('method',
- method='action_cancel')
- trip_actions = [{'url': app_action, 'title': _('Cancel')}]
-
- new_group = (
- 'group_trip_manager',
- lambda partner: bool(partner.user_ids) and
- any(user.has_group('business.group_trip_manager')
- for user in partner.user_ids),
- {
- 'actions': trip_actions,
- })
-
- return [new_group] + groups
-
-
- Note that that I could have defined my evaluation function outside of this
- method and define a global function to do it instead of a lambda, but for
- the sake of being more brief and less verbose in these documentation files
- that can sometimes be boring, I choose the former instead of the latter.
-
-Overriding defaults
-'''''''''''''''''''
-
-There are several ways you can customize the behaviour of ``mail.thread`` models,
-including (but not limited to):
-
-``_mail_post_access`` - :class:`~odoo.models.Model` attribute
- the required access rights to be able to post a message on the model; by
- default a ``write`` access is needed, can be set to ``read`` as well
-
-Context keys:
- These context keys can be used to somewhat control ``mail.thread`` features
- like auto-subscription or field tracking during calls to ``create()`` or
- ``write()`` (or any other method where it may be useful).
-
- - ``mail_create_nosubscribe``: at create or message_post, do not subscribe
- the current user to the record thread
- - ``mail_create_nolog``: at create, do not log the automatic '<Document>
- created' message
- - ``mail_notrack``: at create and write, do not perform the value tracking
- creating messages
- - ``tracking_disable``: at create and write, perform no MailThread features
- (auto subscription, tracking, post, ...)
- - ``mail_auto_delete``: auto delete mail notifications; True by default
- - ``mail_notify_force_send``: if less than 50 email notifications to send,
- send them directly instead of using the queue; True by default
- - ``mail_notify_user_signature``: add the current user signature in
- email notifications; True by default
-
-
-.. _reference/mixins/mail/alias:
-
-Mail alias
-----------
-
-Aliases are configurable email addresses that are linked to a specific record
-(which usually inherits the ``mail.alias.mixin`` model) that will create new records when
-contacted via e-mail. They are an easy way to make your system accessible from
-the outside, allowing users or customers to quickly create records in your
-database without needing to connect to Odoo directly.
-
-Aliases vs. Incoming Mail Gateway
-'''''''''''''''''''''''''''''''''
-
-Some people use the Incoming Mail Gateway for this same purpose. You still need
-a correctly configured mail gateway to use aliases, however a single
-catchall domain will be sufficient since all routing will be done inside Odoo.
-Aliases have several advantages over Mail Gateways:
-
-* Easier to configure
- * A single incoming gateway can be used by many aliases; this avoids having
- to configure multiple emails on your domain name (all configuration is done
- inside Odoo)
- * No need for System access rights to configure aliases
-* More coherent
- * Configurable on the related record, not in a Settings submenu
-* Easier to override server-side
- * Mixin model is built to be extended from the start, allowing you to
- extract useful data from incoming e-mails more easily than with a mail
- gateway.
-
-
-Alias support integration
-'''''''''''''''''''''''''
-
-Aliases are usually configured on a parent model which will then create specific
-record when contacted by e-mail. For example, Project have aliases to create tasks
-or issues, Sales Team have aliases to generate Leads.
-
-.. note:: The model that will be created by the alias **must** inherit the
- ``mail_thread`` model.
-
-Alias support is added by inheriting ``mail.alias.mixin``; this mixin will
-generate a new ``mail.alias`` record for each record of the parent class that
-gets created (for example, every ``project.project`` record having its ``mail.alias``
-record initialized on creation).
-
-.. note:: Aliases can also be created manually and supported by a simple
- :class:`~odoo.fields.Many2one` field. This guide assumes you wish a
- more complete integration with automatic creation of the alias, record-specific
- default values, etc.
-
-Unlike ``mail.thread`` inheritance, the ``mail.alias.mixin`` **requires** some
-specific overrides to work correctly. These overrides will specify the values
-of the created alias, like the kind of record it must create and possibly
-some default values these records may have depending on the parent object:
-
-.. method:: _get_alias_model_name(vals)
-
- Return the model name for the alias. Incoming emails that are not
- replies to existing records will cause the creation of a new record
- of this alias model. The value may depend on ``vals``, the dict of
- values passed to ``create`` when a record of this model is created.
-
- :param vals dict: values of the newly created record that will holding
- the alias
- :return: model name
- :rtype: str
-
-.. method:: _get_alias_values()
-
- Return values to create an alias, or to write on the alias after its
- creation. While not completely mandatory, it is usually required to make
- sure that newly created records will be linked to the alias' parent (i.e.
- tasks getting created in the right project) by setting a dictionary of
- default values in the alias' ``alias_defaults`` field.
-
- :return: dictionnary of values that will be written to the new alias
- :rtype: dict
-
-The ``_get_alias_values()`` override is particularly interesting as it allows you
-to modify the behaviour of your aliases easily. Among the fields that can be set
-on the alias, the following are of particular interest:
-
-``alias_name`` - :class:`~odoo.fields.Char`
- name of the email alias, e.g. 'jobs' if you want to catch emails for
- <jobs@example.odoo.com>
-``alias_user_id`` - :class:`~odoo.fields.Many2one` (``res.users``)
- owner of records created upon receiving emails on this alias;
- if this field is not set the system will attempt to find the right owner
- based on the sender (From) address, or will use the Administrator account
- if no system user is found for that address
-``alias_defaults`` - :class:`~odoo.fields.Text`
- Python dictionary that will be evaluated to provide
- default values when creating new records for this alias
-``alias_force_thread_id`` - :class:`~odoo.fields.Integer`
- optional ID of a thread (record) to which all incoming messages will be
- attached, even if they did not reply to it; if set, this will disable the
- creation of new records completely
-``alias_contact`` - :class:`~odoo.fields.Selection`
- Policy to post a message on the document using the mailgateway
-
- - *everyone*: everyone can post
- - *partners*: only authenticated partners
- - *followers*: only followers of the related document or members of following channels
-
-Note that aliases make use of :ref:`delegation inheritance <reference/orm/inheritance>`,
-which means that while the alias is stored in another table, you have
-access to all these fields directly from your parent object. This allows
-you to make your alias easily configurable from the record's form view.
-
-.. admonition:: Example
-
- Let's add aliases on our business trip class to create expenses on the fly via
- e-mail.
-
- .. code-block:: python
-
- class BusinessTrip(models.Model):
- _name = 'business.trip'
- _inherit = ['mail.thread', 'mail.alias.mixin']
- _description = 'Business Trip'
-
- name = fields.Char(tracking=True)
- partner_id = fields.Many2one('res.partner', 'Responsible',
- tracking=True)
- guest_ids = fields.Many2many('res.partner', 'Participants')
- state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
- tracking=True)
- expense_ids = fields.One2many('business.expense', 'trip_id', 'Expenses')
- alias_id = fields.Many2one('mail.alias', string='Alias', ondelete="restrict",
- required=True)
-
- def _get_alias_model_name(self, vals):
- """ Specify the model that will get created when the alias receives a message """
- return 'business.expense'
-
- def _get_alias_values(self):
- """ Specify some default values that will be set in the alias at its creation """
- values = super(BusinessTrip, self)._get_alias_values()
- # alias_defaults holds a dictionnary that will be written
- # to all records created by this alias
- #
- # in this case, we want all expense records sent to a trip alias
- # to be linked to the corresponding business trip
- values['alias_defaults'] = {'trip_id': self.id}
- # we only want followers of the trip to be able to post expenses
- # by default
- values['alias_contact'] = 'followers'
- return values
-
- class BusinessExpense(models.Model):
- _name = 'business.expense'
- _inherit = ['mail.thread']
- _description = 'Business Expense'
-
- name = fields.Char()
- amount = fields.Float('Amount')
- trip_id = fields.Many2one('business.trip', 'Business Trip')
- partner_id = fields.Many2one('res.partner', 'Created by')
-
- We would like our alias to be easily configurable from the form view of our
- business trips, so let's add the following to our form view:
-
- .. code-block:: xml
-
- <page string="Emails">
- <group name="group_alias">
- <label for="alias_name" string="Email Alias"/>
- <div name="alias_def">
- <!-- display a link while in view mode and a configurable field
- while in edit mode -->
- <field name="alias_id" class="oe_read_only oe_inline"
- string="Email Alias" required="0"/>
- <div class="oe_edit_only oe_inline" name="edit_alias"
- style="display: inline;" >
- <field name="alias_name" class="oe_inline"/>
- @
- <field name="alias_domain" class="oe_inline" readonly="1"/>
- </div>
- </div>
- <field name="alias_contact" class="oe_inline"
- string="Accept Emails From"/>
- </group>
- </page>
-
- Now we can change the alias address directly from the form view and change
- who can send e-mails to the alias.
-
- We can then override ``message_new()`` on our expense model to fetch the values
- from our email when the expense will be created:
-
- .. code-block:: python
-
- class BusinessExpense(models.Model):
- # Previous code goes here
- # ...
-
- def message_new(self, msg, custom_values=None):
- """ Override to set values according to the email.
-
- In this simple example, we simply use the email title as the name
- of the expense, try to find a partner with this email address and
- do a regex match to find the amount of the expense."""
- name = msg_dict.get('subject', 'New Expense')
- # Match the last occurence of a float in the string
- # Example: '50.3 bar 34.5' becomes '34.5'. This is potentially the price
- # to encode on the expense. If not, take 1.0 instead
- amount_pattern = '(\d+(\.\d*)?|\.\d+)'
- expense_price = re.findall(amount_pattern, name)
- price = expense_price and float(expense_price[-1][0]) or 1.0
- # find the partner by looking for it's email
- partner = self.env['res.partner'].search([('email', 'ilike', email_address)],
- limit=1)
- defaults = {
- 'name': name,
- 'amount': price,
- 'partner_id': partner.id
- }
- defaults.update(custom_values or {})
- res = super(BusinessExpense, self).message_new(msg, custom_values=defaults)
- return res
-
-.. _reference/mixins/mail/activities:
-
-Activities tracking
--------------------
-
-Activities are actions users have to take on a document like making a phone call
-or organizing a meeting. Activities come with the mail module as they are
-integrated in the Chatter but are *not bundled with mail.thread*. Activities
-are records of the ``mail.activity`` class, which have a type (``mail.activity.type``),
-name, description, scheduled time (among others). Pending activities are visible
-above the message history in the chatter widget.
-
-You can integrate activities using the ``mail.activity.mixin`` class on your object
-and the specific widgets to display them (via the field ``activity_ids``) in the form
-view and kanban view of your records (``mail_activity`` and ``kanban_activity``
-widgets, respectively).
-
-.. admonition:: Example
-
- Organizing a business trip is a tedious process and tracking needed activities
- like ordering plane tickets or a cab for the airport could be useful. To do so,
- we will add the activities mixin on our model and display the next planned activities
- in the message history of our trip.
-
- .. code-block:: python
-
- class BusinessTrip(models.Model):
- _name = 'business.trip'
- _inherit = ['mail.thread', 'mail.activity.mixin']
- _description = 'Business Trip'
-
- name = fields.Char()
- # [...]
-
- We modify the form view of our trips to display their next activites:
-
- .. code-block:: xml
-
- <record id="businness_trip_form" model="ir.ui.view">
- <field name="name">business.trip.form</field>
- <field name="model">business.trip</field>
- <field name="arch" type="xml">
- <form string="Business Trip">
- <!-- Your usual form view goes here -->
- <div class="oe_chatter">
- <field name="message_follower_ids" widget="mail_followers"/>
- <field name="activity_ids" widget="mail_activity"/>
- <field name="message_ids" widget="mail_thread"/>
- </div>
- </form>
- </field>
- </record>
-
-You can find concrete examples of integration in the following models:
-
-* ``crm.lead`` in the CRM (*crm*) Application
-* ``sale.order`` in the Sales (*sale*) Application
-* ``project.task`` in the Project (*poject*) Application
-
-
-.. _reference/mixins/website:
-
-Website features
-================
-
-.. _reference/mixins/website/utm:
-
-Visitor tracking
-----------------
-
-The ``utm.mixin`` class can be used to track online marketing/communication
-campaigns through arguments in links to specified resources. The mixin adds
-3 fields to your model:
-
-* ``campaign_id``: :class:`~odoo.fields.Many2one` field to a ``utm.campaign``
- object (i.e. Christmas_Special, Fall_Collection, etc.)
-* ``source_id``: :class:`~odoo.fields.Many2one` field to a ``utm.source``
- object (i.e. Search Engine, mailing list, etc.)
-* ``medium_id``: :class:`~odoo.fields.Many2one` field to a ``utm.medium``
- object (i.e. Snail Mail, e-Mail, social network update, etc.)
-
-These models have a single field ``name`` (i.e. they are simply there to
-distinguish campaigns but don't have any specific behaviour).
-
-Once a customer visits your website with these parameters set in the url
-(i.e. https://www.odoo.com/?campaign_id=mixin_talk&source_id=www.odoo.com&medium_id=website),
-three cookies are set in the visitor's website for these parameters.
-Once a object that inherits the utm.mixin is created from the website (i.e. lead
-form, job application, etc.), the utm.mixin code kicks in and fetches the values
-from the cookies to set them in the new record. Once this is done, you can then
-use the campaign/source/medium fields as any other field when defining reports
-and views (group by, etc.).
-
-To extend this behaviour, simply add a relational field to a simple model (the
-model should support the *quick create* (i.e. call to ``create()`` with a single
-``name`` value) and extend the function ``tracking_fields()``:
-
-.. code-block:: python
-
- class UtmMyTrack(models.Model):
- _name = 'my_module.my_track'
- _description = 'My Tracking Object'
-
- name = fields.Char(string='Name', required=True)
-
-
- class MyModel(models.Models):
- _name = 'my_module.my_model'
- _inherit = ['utm.mixin']
- _description = 'My Tracked Object'
-
- my_field = fields.Many2one('my_module.my_track', 'My Field')
-
- @api.model
- def tracking_fields(self):
- result = super(MyModel, self).tracking_fields()
- result.append([
- # ("URL_PARAMETER", "FIELD_NAME_MIXIN", "NAME_IN_COOKIES")
- ('my_field', 'my_field', 'odoo_utm_my_field')
- ])
- return result
-
-This will tell the system to create a cookie named *odoo_utm_my_field* with the
-value found in the url parameter ``my_field``; once a new record of this model is
-created by a call from a website form, the generic override of the ``create()``
-method of ``utm.mixin`` will fetch the default values for this field from the
-cookie (and the ``my_module.my_track`` record will be creatwed on the fly if it
-does not exist yet).
-
-You can find concrete examples of integration in the following models:
-
-* ``crm.lead`` in the CRM (*crm*) Application
-* ``hr.applicant`` in the Recruitment Process (*hr_recruitment*) Application
-* ``helpdesk.ticket`` in the Helpdesk (*helpdesk* - Odoo Enterprise only) Application
-
-.. _reference/mixins/website/published:
-
-Website visibility
-------------------
-
-You can quite easily add a website visibility toggle on any of your record. While
-this mixin is quite easy to implement manually, it is the most often-used after
-the ``mail.thread`` inheritance; a testament to its usefulness. The typical use
-case for this mixin is any object that has a frontend-page; being able to control
-the visibility of the page allows you to take your time while editing the page
-and only publish it when you're satisfied.
-
-To include the functionnality, you only need to inherit ``website.published.mixin``:
-
-.. code-block:: python
-
- class BlogPost(models.Model):
- _name = "blog.post"
- _description = "Blog Post"
- _inherit = ['website.published.mixin']
-
-This mixin adds 2 fields on your model:
-
-* ``website_published``: :class:`~odoo.fields.Boolean` field which represents
- the status of the publication
-* ``website_url``: :class:`~odoo.fields.Char` field which represents
- the URL through which the object is accessed
-
-Note that this last field is a computed field and must be implemented for your class:
-
-.. code-block:: python
-
- def _compute_website_url(self):
- for blog_post in self:
- blog_post.website_url = "/blog/%s" % (log_post.blog_id)
-
-Once the mechanism is in place, you just have to adapt your frontend and backend
-views to make it accessible. In the backend, adding a button in the button box is
-usually the way to go:
-
-.. code-block:: xml
-
- <button class="oe_stat_button" name="website_publish_button"
- type="object" icon="fa-globe">
- <field name="website_published" widget="website_button"/>
- </button>
-
-In the frontend, some security checks are needed to avoid showing 'Editing'
-buttons to website visitors:
-
-.. code-block:: xml
-
- <div id="website_published_button" class="float-right"
- groups="base.group_website_publisher"> <!-- or any other meaningful group -->
- <t t-call="website.publish_management">
- <t t-set="object" t-value="blog_post"/>
- <t t-set="publish_edit" t-value="True"/>
- <t t-set="action" t-value="'blog.blog_post_action'"/>
- </t>
- </div>
-
-Note that you must pass your object as the variable ``object`` to the template;
-in this example, the ``blog.post`` record was passed as the ``blog_post`` variable
-to the ``qweb`` rendering engine, it is necessary to specify this to the publish
-management template. The ``publish_edit`` variable allow the frontend
-button to link to the backend (allowing you to switch from frontend to backend
-and vice-versa easily); if set, you must specify the full external id of the action
-you want to call in the backend in the ``action`` variable (note that a Form View
-must exist for the model).
-
-The action ``website_publish_button`` is defined in the mixin and adapts its
-behaviour to your object: if the class has a valid ``website_url`` compute function,
-the user is redirected to the frontend when he clicks on the button; the user
-can then publish the page directly from the frontend. This ensures
-that no online publication can happen by accident. If there is not compute function,
-the boolean ``website_published`` is simply triggered.
-
-.. _reference/mixins/website/seo:
-
-Website metadata
-----------------
-
-This simple mixin simply allows you to easily inject metadata in your frontend
-pages.
-
-.. code-block:: python
-
- class BlogPost(models.Model):
- _name = "blog.post"
- _description = "Blog Post"
- _inherit = ['website.seo.metadata', 'website.published.mixin']
-
-This mixin adds 3 fields on your model:
-
-* ``website_meta_title``: :class:`~odoo.fields.Char` field that allow you to set
- an additional title to your page
-* ``website_meta_description``: :class:`~odoo.fields.Char` field that contains a
- short description of the page (sometimes used in search engines results)
-* ``website_meta_keywords``: :class:`~odoo.fields.Char` field that contains some
- keywords to help your page to be classified more precisely by search engines; the
- "Promote" tool will help you select lexically-related keywords easily
-
-These fields are editable in the frontend using the "Promote" tool from the Editor
-toolbar. Setting these fields can help search engines to better index your pages.
-Note that search engines do not base their results only on these metadata; the
-best SEO practice should still be to get referenced by reliable sources.
-
-.. _reference/mixins/misc:
-
-Others
-======
-
-.. _reference/mixins/misc/rating:
-
-Customer Rating
----------------
-
-The rating mixin allows sending email to ask for customer rating, automatic
-transitioning in a kanban processes and aggregating statistics on your ratings.
-
-Adding rating on your model
-'''''''''''''''''''''''''''
-
-To add rating support, simply inherit the ``rating.mixin`` model:
-
-.. code-block:: python
-
- class MyModel(models.Models):
- _name = 'my_module.my_model'
- _inherit = ['rating.mixin', 'mail.thread']
-
- user_id = fields.Many2one('res.users', 'Responsible')
- partner_id = fields.Many2one('res.partner', 'Customer')
-
-The behaviour of the mixin adapts to your model:
-
-* The ``rating.rating`` record will be linked to the ``partner_id`` field of your
- model (if the field is present).
-
- - this behaviour can be overridden with the function ``rating_get_partner_id()``
- if you use another field than ``partner_id``
-
-* The ``rating.rating`` record will be linked to the partner of the ``user_id``
- field of your model (if the field is present) (i.e. the partner who is rated)
-
- - this behaviour can be overridden with the function ``rating_get_rated_partner_id()``
- if you use another field than ``user_id`` (note that the function must return a
- ``res.partner``, for ``user_id`` the system automatically fetches the partner
- of the user)
-
-* The chatter history will display the rating event (if your model inherits from
- ``mail.thread``)
-
-Send rating requests by e-mail
-''''''''''''''''''''''''''''''
-
-If you wish to send emails to request a rating, simply generate an e-mail with
-links to the rating object. A very basic email template could look like this:
-
-.. code-block:: xml
-
- <record id="rating_my_model_email_template" model="mail.template">
- <field name="name">My Model: Rating Request</field>
- <field name="email_from">${object.rating_get_rated_partner_id().email or '' | safe}</field>
- <field name="subject">Service Rating Request</field>
- <field name="model_id" ref="my_module.model_my_model"/>
- <field name="partner_to" >${object.rating_get_partner_id().id}</field>
- <field name="auto_delete" eval="True"/>
- <field name="body_html"><![CDATA[
- % set access_token = object.rating_get_access_token()
- <p>Hi,</p>
- <p>How satsified are you?</p>
- <ul>
- <li><a href="/rate/${access_token}/5">Satisfied</a></li>
- <li><a href="/rate/${access_token}/3">Not satisfied</a></li>
- <li><a href="/rate/${access_token}/1">Very unsatisfied</a></li>
- </ul>
- ]]></field>
- </record>
-
-Your customer will then receive an e-mail with links to a simple webpage allowing
-them to provide a feedback on their interaction with your users (including a free-text
-feedback message).
-
-You can then quite easily integrate your ratings with your form view by defining
-an action for the ratings:
-
-.. code-block:: xml
-
- <record id="rating_rating_action_my_model" model="ir.actions.act_window">
- <field name="name">Customer Ratings</field>
- <field name="res_model">rating.rating</field>
- <field name="view_mode">kanban,pivot,graph</field>
- <field name="domain">[('res_model', '=', 'my_module.my_model'), ('res_id', '=', active_id), ('consumed', '=', True)]</field>
- </record>
-
- <record id="my_module_my_model_view_form_inherit_rating" model="ir.ui.view">
- <field name="name">my_module.my_model.view.form.inherit.rating</field>
- <field name="model">my_module.my_model</field>
- <field name="inherit_id" ref="my_module.my_model_view_form"/>
- <field name="arch" type="xml">
- <xpath expr="//div[@name='button_box']" position="inside">
- <button name="%(rating_rating_action_my_model)d" type="action"
- class="oe_stat_button" icon="fa-smile-o">
- <field name="rating_count" string="Rating" widget="statinfo"/>
- </button>
- </xpath>
- </field>
- </record>
-
-Note that there are default views (kanban,pivot,graph) for ratings which allow
-you a quick bird's eye view of your customer ratings.
-
-You can find concrete examples of integration in the following models:
-
-* ``project.task`` in the Project (*rating_project*) Application
-* ``helpdesk.ticket`` in the Helpdesk (*helpdesk* - Odoo Enterprise only) Application
diff --git a/doc/reference/mobile.rst b/doc/reference/mobile.rst
deleted file mode 100644
index 355164e82c9..00000000000
--- a/doc/reference/mobile.rst
+++ /dev/null
@@ -1,213 +0,0 @@
-:banner: banners/mobile.jpg
-:types: api
-
-.. _reference/mobile:
-
-==================
-Mobile JavaScript
-==================
-
-Introduction
-============
-
-In Odoo 10.0 we released a mobile app which allows you to access all **Odoo apps**
-(even your customized modules).
-
-The application is a combination of **Odoo Web** and **Native Mobile
-components**. In other words it is a Odoo Web instance loaded inside a native, mobile, WebView container.
-
-This page documents how you can access mobile native components like Camera,
-Vibration, Notification and Toast through Odoo Web (via JavaScript). For this, you
-do not need to be a mobile developer, if you know Odoo JavaScript API you can
-access all available mobile features.
-
-.. warning:: These features work with **Odoo Enterprise 10.0+** only
-
-How does it work?
-=================
-
-Internal workings of the mobile application:
-
-.. image:: images/mobile_working.jpg
-
-Of course, it is a web page that loads on a Mobile Native Web container. But it
-is integrated in such a way that you can access native resources from your web
-JavaScript.
-
-WebPages (Odoo Web) is on the top of each layer, where the second layer is a Bridge
-between Odoo Web (JS) and the native mobile components.
-
-When any call from JavaScript is triggered it passes through Bridge and Bridge
-passes it to the native invoker to perform that action.
-
-When the native component has done its work, it is passed to the Bridge again and
-you get the output in JavaScript.
-
-Process time taken by the Native component depends on what you are requesting
-from the Native resources. For example the Camera or GPS Location.
-
-How to use it?
-==============
-
-Just like the Odoo Web Framework, the Mobile API can be used anywhere by getting the object from
-**web_mobile.rpc**
-
-.. image:: images/odoo_mobile_api.png
-
-The mobile RPC object provides a list of methods that are available (this only works with the mobile
-app).
-
-Check if the method is available and then execute it.
-
-Methods
--------
-
-.. note:: Each of the methods returns a JQuery Deffered object which returns
- a data JSON dictionary
-
-Show Toast in device
-.....................
-
-.. js:function:: showToast
-
- :param object args: **message** text to display
-
-A toast provides simple feedback about an operation in a small popup. It only
-fills the amount of space required for the message and the current activity
-remains visible and interactive.
-
-.. code-block:: javascript
-
- mobile.methods.showToast({'message': 'Message sent'});
-
-.. image:: images/toast.png
-
-
-Vibrating device
-................
-
-
-.. js:function:: vibrate
-
- :param object args: Vibrates constantly for the specified period of time
- (in milliseconds).
-
-Vibrate mobile device with given duration.
-
-.. code-block:: javascript
-
- mobile.methods.vibrate({'duration': 100});
-
-Show snackbar with action
-.........................
-
-.. js:function:: showSnackBar
-
- :param object args: (*required*) **Message** to show in snackbar and action **button label** in Snackbar (optional)
- :returns: ``True`` if the user clicks on the Action button, ``False`` if SnackBar auto dismissed after some time.
-
-Snackbars provide lightweight feedback about an operation. They show a brief
-message at the bottom of the screen on mobile or in the lower left corner on larger devices.
-Snackbars appear above all the other elements on the screen and only one can be
-displayed at a time.
-
-.. code-block:: javascript
-
- mobile.methods.showSnackBar({'message': 'Message is deleted', 'btn_text': 'Undo'}).then(function(result){
- if(result){
- // Do undo operation
- }else{
- // Snack Bar dismissed
- }
- });
-
-.. image:: images/snackbar.png
-
-Showing notification
-.....................
-
-.. js:function:: showNotification
-
- :param object args: **title** (first row) of the notification, **message** (second row) of the notification, in a standard notification.
-
-A notification is a message you can display to the user outside of your
-application's normal UI. When you tell the system to issue a notification, it
-first appears as an icon in the notification area. To see the details of the
-notification, the user opens the notification drawer. Both the notification
-area and the notification drawer are system-controlled areas that the user can
-view at any time.
-
-.. code-block:: javascript
-
- mobile.showNotification({'title': 'Simple Notification', 'message': 'This is a test for a simple notification'})
-
-.. image:: images/mobile_notification.png
-
-
-Create contact in device
-.........................
-
-.. js:function:: addContact
-
- :param object args: Dictionary with contact details. Possible keys (name, mobile, phone, fax, email, website, street, street2, country_id, state_id, city, zip, parent_id, function and image)
-
-Create a new device contact with the given contact details.
-
-.. code-block:: javascript
-
- var contact = {
- 'name': 'Michel Fletcher',
- 'mobile': '9999999999',
- 'phone': '7954856587',
- 'fax': '765898745',
- 'email': 'michel.fletcher@agrolait.example.com',
- 'website': 'http://www.agrolait.com',
- 'street': '69 rue de Namur',
- 'street2': false,
- 'country_id': [21, 'Belgium'],
- 'state_id': false,
- 'city': 'Wavre',
- 'zip': '1300',
- 'parent_id': [8, 'Agrolait'],
- 'function': 'Analyst',
- 'image': '<<BASE 64 Image Data>>'
- }
-
- mobile.methods.addContact(contact);
-
-.. image:: images/mobile_contact_create.png
-
-Scanning barcodes
-..................
-
-.. js:function:: scanBarcode
-
- :returns: Scanned ``code`` from any barcode
-
-The barcode API detects barcodes in real-time, on the device, in any orientation.
-
-The barcode API can read the following barcode formats:
-
-* 1D barcodes: EAN-13, EAN-8, UPC-A, UPC-E, Code-39, Code-93, Code-128, ITF, Codabar
-* 2D barcodes: QR Code, Data Matrix, PDF-417, AZTEC
-
-.. code-block:: javascript
-
- mobile.methods.scanBarcode().then(function(code){
- if(code){
- // Perform operation with the scanned code
- }
- });
-
-Switching account in device
-...........................
-
-.. js:function:: switchAccount
-
-Use switchAccount to switch from one account to another on the device.
-
-.. code-block:: javascript
-
- mobile.methods.switchAccount();
-
-.. image:: images/mobile_switch_account.png
diff --git a/doc/reference/module.rst b/doc/reference/module.rst
deleted file mode 100644
index 2c91e4f6d6e..00000000000
--- a/doc/reference/module.rst
+++ /dev/null
@@ -1,148 +0,0 @@
-:banner: banners/module.jpg
-
-================
-Module Manifests
-================
-
-
-
-.. _reference/module/manifest:
-
-Manifest
-========
-
-The manifest file serves to declare a python package as an Odoo module
-and to specify module metadata.
-
-It is a file called ``__manifest__.py`` and contains a single Python
-dictionary, where each key specifies module metadatum.
-
-::
-
- {
- 'name': "A Module",
- 'version': '1.0',
- 'depends': ['base'],
- 'author': "Author Name",
- 'category': 'Category',
- 'description': """
- Description text
- """,
- # data files always loaded at installation
- 'data': [
- 'views/mymodule_view.xml',
- ],
- # data files containing optionally loaded demonstration data
- 'demo': [
- 'demo/demo_data.xml',
- ],
- }
-
-Available manifest fields are:
-
-``name`` (``str``, required)
- the human-readable name of the module
-``version`` (``str``)
- this module's version, should follow `semantic versioning`_ rules
-``description`` (``str``)
- extended description for the module, in reStructuredText
-``author`` (``str``)
- name of the module author
-``website`` (``str``)
- website URL for the module author
-``license`` (``str``, defaults: ``LGPL-3``)
- distribution license for the module.
- Possible values:
-
- * `GPL-2`
- * `GPL-2 or any later version`
- * `GPL-3`
- * `GPL-3 or any later version`
- * `AGPL-3`
- * `LGPL-3`
- * `Other OSI approved licence`
- * `OEEL-1` (Odoo Enterprise Edition License v1.0)
- * `OPL-1` (Odoo Proprietary License v1.0)
- * `Other proprietary`
-
-``category`` (``str``, default: ``Uncategorized``)
- classification category within Odoo, rough business domain for the module.
-
- Although using `existing categories`_ is recommended, the field is
- freeform and unknown categories are created on-the-fly. Category
- hierarchies can be created using the separator ``/`` e.g. ``Foo / Bar``
- will create a category ``Foo``, a category ``Bar`` as child category of
- ``Foo``, and will set ``Bar`` as the module's category.
-``depends`` (``list(str)``)
- Odoo modules which must be loaded before this one, either because this
- module uses features they create or because it alters resources they
- define.
-
- When a module is installed, all of its dependencies are installed before
- it. Likewise dependencies are loaded before a module is loaded.
-``data`` (``list(str)``)
- List of data files which must always be installed or updated with the
- module. A list of paths from the module root directory
-``demo`` (``list(str)``)
- List of data files which are only installed or updated in *demonstration
- mode*
-``auto_install`` (``bool``, default: ``False``)
- If ``True``, this module will automatically be installed if all of its
- dependencies are installed.
-
- It is generally used for "link modules" implementing synergic integration
- between two otherwise independent modules.
-
- For instance ``sale_crm`` depends on both ``sale`` and ``crm`` and is set
- to ``auto_install``. When both ``sale`` and ``crm`` are installed, it
- automatically adds CRM campaigns tracking to sale orders without either
- ``sale`` or ``crm`` being aware of one another
-``external_dependencies`` (``dict(key=list(str))``)
- A dictionary containing python and/or binary dependencies.
-
- For python dependencies, the ``python`` key must be defined for this
- dictionary and a list of python modules to be imported should be assigned
- to it.
-
- For binary dependencies, the ``bin`` key must be defined for this
- dictionary and a list of binary executable names should be assigned to it.
-
- The module won't be installed if either the python module is not installed
- in the host machine or the binary executable is not found within the
- host machine's PATH environment variable.
-``application`` (``bool``, default: ``False``)
- Whether the module should be considered as a fully-fledged application
- (``True``) or is just a technical module (``False``) that provides some
- extra functionality to an existing application module.
-``css`` (``list(str)``)
- Specify css files with custom rules to be imported, these files should be
- located in ``static/src/css`` inside the module.
-``images`` (``list(str)``)
- Specify image files to be used by the module.
-``installable`` (``bool`` default: ``True``)
- Whether a user should be able to install the module from the Web UI or not.
-``maintainer`` (``str``)
- Person or entity in charge of the maintenance of this module, by default
- it is assumed that the author is the maintainer.
-``{pre_init, post_init, uninstall}_hook`` (``str``)
- Hooks for module installation/uninstallation, their value should be a
- string representing the name of a function defined inside the module's
- ``__init__.py``.
-
- ``pre_init_hook`` takes a cursor as its only argument, this function is
- executed prior to the module's installation.
-
- ``post_init_hook`` takes a cursor and a registry as its arguments, this
- function is executed right after the module's installation.
-
- ``uninstall_hook`` takes a cursor and a registry as its arguments, this
- function is executed after the module's uninstallation.
-
- These hooks should only be used when setup/cleanup required for this module
- is either extremely difficult or impossible through the api.
-``active`` (``bool``)
- This indicates whether this module must install automatically or not.
-
-.. _semantic versioning: https://semver.org
-.. _existing categories:
- https://github.com/odoo/odoo/blob/14.0/odoo/addons/base/data/ir_module_category_data.xml
diff --git a/doc/reference/orm.rst b/doc/reference/orm.rst
deleted file mode 100644
index 3ca49846fb6..00000000000
--- a/doc/reference/orm.rst
+++ /dev/null
@@ -1,1092 +0,0 @@
-:banner: banners/orm_api.jpg
-
-.. _reference/orm:
-
-=======
-ORM API
-=======
-
-.. automodule:: odoo.models
-
-.. _reference/orm/models:
-.. _reference/orm/model:
-
-Models
-======
-
-Model fields are defined as attributes on the model itself::
-
- from odoo import models, fields
- class AModel(models.Model):
- _name = 'a.model.name'
-
- field1 = fields.Char()
-
-.. warning:: this means you cannot define a field and a method with the same
- name, the last one will silently overwrite the former ones.
-
-By default, the field's label (user-visible name) is a capitalized version of
-the field name, this can be overridden with the ``string`` parameter. ::
-
- field2 = fields.Integer(string="Field Label")
-
-For the list of field types and parameters, see :ref:`the fields reference
-<reference/fields>`.
-
-Default values are defined as parameters on fields, either as a value::
-
- name = fields.Char(default="a value")
-
-or as a function called to compute the default value, which should return that
-value::
-
- def _default_name(self):
- return self.get_value()
-
- name = fields.Char(default=lambda self: self._default_name())
-
-.. rubric:: API
-
-.. autoclass:: odoo.models.BaseModel()
-
- .. autoattribute:: _auto
- .. attribute:: _log_access
-
- Whether the ORM should automatically generate and update the
- :ref:`reference/fields/automatic/log_access`.
-
- Defaults to whatever value was set for :attr:`~._auto`.
-
- .. autoattribute:: _table
- .. autoattribute:: _sequence
- .. autoattribute:: _sql_constraints
-
- .. autoattribute:: _register
- .. autoattribute:: _abstract
- .. autoattribute:: _transient
-
- .. autoattribute:: _name
- .. autoattribute:: _description
-
- .. autoattribute:: _inherit
- .. autoattribute:: _inherits
-
- .. autoattribute:: _rec_name
- .. autoattribute:: _order
-
- .. autoattribute:: _check_company_auto
-
- .. autoattribute:: _parent_name
- .. autoattribute:: _parent_store
-
- .. autoattribute:: _date_name
- .. autoattribute:: _fold_name
-
-AbstractModel
--------------
-
-.. autoclass:: odoo.models.AbstractModel()
-
-Model
------
-
-.. autoclass:: odoo.models.Model()
-
- .. autoattribute:: _auto
- .. autoattribute:: _abstract
- .. autoattribute:: _transient
-
-TransientModel
---------------
-
-.. autoclass:: odoo.models.TransientModel()
-
- .. autoattribute:: _auto
- .. autoattribute:: _abstract
- .. autoattribute:: _transient
-
-.. _reference/fields:
-.. _reference/orm/fields:
-
-Fields
-======
-
-.. currentmodule:: odoo.fields
-
-.. autoclass:: Field()
-
-.. .. autoattribute:: Field._slots
- :annotation:
-
-.. _reference/fields/basic:
-
-Basic Fields
-------------
-
-.. autoclass:: Boolean()
-
-.. autoclass:: Char()
-
-.. autoclass:: Float()
-
-.. autoclass:: Integer()
-
-.. _reference/fields/advanced:
-
-Advanced Fields
----------------
-
-.. autoclass:: Binary()
-
-.. autoclass:: Html()
-
-.. autoclass:: Image()
-
-.. autoclass:: Monetary()
-
-.. autoclass:: Selection()
-
-.. autoclass:: Text()
-
-.. _reference/fields/date:
-
-Date(time) Fields
-'''''''''''''''''
-
-:class:`Dates <odoo.fields.Date>` and :class:`Datetimes <odoo.fields.Datetime>`
-are very important fields in any kind of business application.
-Their misuse can create invisible yet painful bugs, this section
-aims to provide Odoo developers with the knowledge required
-to avoid misusing these fields.
-
-When assigning a value to a Date/Datetime field, the following options are valid:
-
-* A `date` or `datetime` object.
-* A string in the proper server format:
-
- * ``YYYY-MM-DD`` for :class:`~odoo.fields.Date` fields,
- * ``YYYY-MM-DD HH:MM:SS`` for :class:`~odoo.fields.Datetime` fields.
-
-* `False` or `None`.
-
-The Date and Datetime fields class have helper methods to attempt conversion
-into a compatible type:
-
-* :func:`~odoo.fields.Date.to_date` will convert to a :class:`datetime.date`
-* :func:`~odoo.fields.Datetime.to_datetime` will convert to a :class:`datetime.datetime`.
-
-.. admonition:: Example
-
- To parse date/datetimes coming from external sources::
-
- fields.Date.to_date(self._context.get('date_from'))
-
-Date / Datetime comparison best practices:
-
-* Date fields can **only** be compared to date objects.
-* Datetime fields can **only** be compared to datetime objects.
-
-.. warning:: Strings representing dates and datetimes can be compared
- between each other, however the result may not be the expected
- result, as a datetime string will always be greater than a
- date string, therefore this practice is **heavily**
- discouraged.
-
-Common operations with dates and datetimes such as addition, substraction or
-fetching the start/end of a period are exposed through both
-:class:`~odoo.fields.Date` and :class:`~odoo.fields.Datetime`.
-These helpers are also available by importing `odoo.tools.date_utils`.
-
-.. note:: Timezones
-
- Datetime fields are stored as `timestamp without timezone` columns in the database and are stored
- in the UTC timezone. This is by design, as it makes the Odoo database independent from the timezone
- of the hosting server system. Timezone conversion is managed entirely by the client side.
-
-.. autoclass:: Date()
- :members: today, context_today, to_date, to_string, start_of, end_of, add, subtract
-
-.. autoclass:: Datetime()
- :members: now, today, context_timestamp, to_datetime, to_string, start_of, end_of, add, subtract
-
-.. _reference/fields/relational:
-
-Relational Fields
-'''''''''''''''''
-
-.. autoclass:: Many2one()
-
-.. autoclass:: One2many()
-
-.. autoclass:: Many2many()
-
-Pseudo-relational fields
-''''''''''''''''''''''''
-
-.. autoclass:: Reference()
-
-.. autoclass:: Many2oneReference()
-
-.. _reference/fields/compute:
-
-Computed Fields
-'''''''''''''''
-
-Fields can be computed (instead of read straight from the database) using the
-``compute`` parameter. **It must assign the computed value to the field**. If
-it uses the values of other *fields*, it should specify those fields using
-:func:`~odoo.api.depends`. ::
-
- from odoo import api
- total = fields.Float(compute='_compute_total')
-
- @api.depends('value', 'tax')
- def _compute_total(self):
- for record in self:
- record.total = record.value + record.value * record.tax
-
-* dependencies can be dotted paths when using sub-fields::
-
- @api.depends('line_ids.value')
- def _compute_total(self):
- for record in self:
- record.total = sum(line.value for line in record.line_ids)
-
-* computed fields are not stored by default, they are computed and
- returned when requested. Setting ``store=True`` will store them in the
- database and automatically enable searching.
-* searching on a computed field can also be enabled by setting the ``search``
- parameter. The value is a method name returning a
- :ref:`reference/orm/domains`. ::
-
- upper_name = field.Char(compute='_compute_upper', search='_search_upper')
-
- def _search_upper(self, operator, value):
- if operator == 'like':
- operator = 'ilike'
- return [('name', operator, value)]
-
- The search method is invoked when processing domains before doing an
- actual search on the model. It must return a domain equivalent to the
- condition: ``field operator value``.
-
-.. TODO and/or by setting the store to True for search domains ?
-
-* Computed fields are readonly by default. To allow *setting* values on a computed field, use the ``inverse``
- parameter. It is the name of a function reversing the computation and
- setting the relevant fields::
-
- document = fields.Char(compute='_get_document', inverse='_set_document')
-
- def _get_document(self):
- for record in self:
- with open(record.get_document_path) as f:
- record.document = f.read()
- def _set_document(self):
- for record in self:
- if not record.document: continue
- with open(record.get_document_path()) as f:
- f.write(record.document)
-
-* multiple fields can be computed at the same time by the same method, just
- use the same method on all fields and set all of them::
-
- discount_value = fields.Float(compute='_apply_discount')
- total = fields.Float(compute='_apply_discount')
-
- @api.depends('value', 'discount')
- def _apply_discount(self):
- for record in self:
- # compute actual discount from discount percentage
- discount = record.value * record.discount
- record.discount_value = discount
- record.total = record.value - discount
-
-.. warning::
-
- While it is possible to use the same compute method for multiple
- fields, it is not recommended to do the same for the inverse
- method.
-
- During the computation of the inverse, **all** fields that use
- said inverse are protected, meaning that they can't be computed,
- even if their value is not in the cache.
-
- If any of those fields is accessed and its value is not in cache,
- the ORM will simply return a default value of `False` for these fields.
- This means that the value of the inverse fields (other than the one
- triggering the inverse method) may not give their correct value and
- this will probably break the expected behavior of the inverse method.
-
-.. _reference/fields/related:
-
-Related fields
-''''''''''''''
-
-A special case of computed fields are *related* (proxy) fields, which provide
-the value of a sub-field on the current record. They are defined by setting
-the ``related`` parameter and like regular computed fields they can be
-stored::
-
- nickname = fields.Char(related='user_id.partner_id.name', store=True)
-
-The value of a related field is given by following a sequence of
-relational fields and reading a field on the reached model. The complete
-sequence of fields to traverse is specified by the ``related`` attribute.
-
-Some field attributes are automatically copied from the source field if
-they are not redefined: ``string``, ``help``, ``readonly``, ``required`` (only
-if all fields in the sequence are required), ``groups``, ``digits``, ``size``,
-``translate``, ``sanitize``, ``selection``, ``comodel_name``, ``domain``,
-``context``. All semantic-free attributes are copied from the source
-field.
-
-By default, the values of related fields are not stored to the database.
-Add the attribute ``store=True`` to make it stored, just like computed
-fields. Related fields are automatically recomputed when their
-dependencies are modified.
-
-.. note:: The related fields are computed in sudo mode.
-
-.. warning::
-
- You cannot chain :class:`~odoo.fields.Many2many` or :class:`~odoo.fields.One2many` fields in ``related`` fields dependencies.
-
- ``related`` can be used to refer to a :class:`~odoo.fields.One2many` or
- :class:`~odoo.fields.Many2many` field on another model on the
- condition that it's done through a ``Many2one`` relation on the current model.
- ``One2many`` and ``Many2many`` are not supported and the results will not be
- aggregated correctly::
-
- m2o_id = fields.Many2one()
- m2m_ids = fields.Many2many()
- o2m_ids = fields.One2many()
-
- # Supported
- d_ids = fields.Many2many(related="m2o_id.m2m_ids")
- e_ids = fields.One2many(related="m2o_id.o2m_ids")
-
- # Won't work: use a custom Many2many computed field instead
- f_ids = fields.Many2many(related="m2m_ids.m2m_ids")
- g_ids = fields.One2many(related="o2m_ids.o2m_ids")
-
-.. _reference/fields/automatic:
-
-Automatic fields
-----------------
-
-.. attribute:: id
-
- Identifier :class:`field <odoo.fields.Field>`
-
- If length of current recordset is 1, return id of unique record in it.
-
- Raise an Error otherwise.
-
-.. _reference/fields/automatic/log_access:
-
-Access Log fields
-'''''''''''''''''
-
-These fields are automatically set and updated if
-:attr:`~odoo.models.BaseModel._log_access` is enabled. It can be
-disabled to avoid creating or updating those fields on tables for which they are
-not useful.
-
-By default, :attr:`~odoo.models.BaseModel._log_access` is set to the same value
-as :attr:`~odoo.models.BaseModel._auto`
-
-.. attribute:: create_date
-
- Stores when the record was created, :class:`~odoo.fields.Datetime`
-
-.. attribute:: create_uid
-
- Stores *who* created the record, :class:`~odoo.fields.Many2one` to a
- ``res.users``.
-
-.. attribute:: write_date
-
- Stores when the record was last updated, :class:`~odoo.fields.Datetime`
-
-.. attribute:: write_uid
-
- Stores who last updated the record, :class:`~odoo.fields.Many2one` to a
- ``res.users``.
-
-.. warning:: :attr:`~odoo.models.BaseModel._log_access` *must* be enabled on
- :class:`~odoo.models.TransientModel`.
-
-.. _reference/orm/fields/reserved:
-
-Reserved Field names
---------------------
-
-A few field names are reserved for pre-defined behaviors beyond that of
-automated fields. They should be defined on a model when the related
-behavior is desired:
-
-.. attribute:: name
-
- default value for :attr:`~odoo.models.BaseModel._rec_name`, used to
- display records in context where a representative "naming" is
- necessary.
-
- :class:`~odoo.fields.Char`
-
-.. attribute:: active
-
- toggles the global visibility of the record, if ``active`` is set to
- ``False`` the record is invisible in most searches and listing.
-
- :class:`~odoo.fields.Boolean`
-
-.. .. attribute:: sequence
-..
-.. Alterable ordering criteria, allows drag-and-drop reordering of models
-.. in list views.
-..
-.. :class:`~odoo.fields.Integer`
-
-.. attribute:: state
-
- lifecycle stages of the object, used by the ``states`` attribute on
- :class:`fields <odoo.fields.Field>`.
-
- :class:`~odoo.fields.Selection`
-
-.. attribute:: parent_id
-
- default_value of :attr:`~._parent_name`, used to organize
- records in a tree structure and enables the ``child_of``
- and ``parent_of`` operators in domains.
-
- :class:`~odoo.fields.Many2one`
-
-.. attribute:: parent_path
-
- When :attr:`~._parent_store` is set to True, used to store a value reflecting
- the tree structure of :attr:`~._parent_name`, and to optimize the operators
- ``child_of`` and ``parent_of`` in search domains.
- It must be declared with ``index=True`` for proper operation.
-
- :class:`~odoo.fields.Char`
-
-.. attribute:: company_id
-
- Main field name used for Odoo multi-company behavior.
-
- Used by `:meth:~odoo.models._check_company` to check multi company consistency.
- Defines whether a record is shared between companies (no value) or only
- accessible by the users of a given company.
-
- :class:`~odoo.fields.Many2one`
- :type: :class:`~odoo.addons.base.models.res_company`
-
-Recordsets
-==========
-
-Interactions with models and records are performed through recordsets, an ordered
-collection of records of the same model.
-
-.. warning:: Contrary to what the name implies, it is currently possible for
- recordsets to contain duplicates. This may change in the future.
-
-Methods defined on a model are executed on a recordset, and their ``self`` is
-a recordset::
-
- class AModel(models.Model):
- _name = 'a.model'
- def a_method(self):
- # self can be anything between 0 records and all records in the
- # database
- self.do_operation()
-
-Iterating on a recordset will yield new sets of *a single record*
-("singletons"), much like iterating on a Python string yields strings of a
-single characters::
-
- def do_operation(self):
- print(self) # => a.model(1, 2, 3, 4, 5)
- for record in self:
- print(record) # => a.model(1), then a.model(2), then a.model(3), ...
-
-Field access
-------------
-
-Recordsets provide an "Active Record" interface: model fields can be read and
-written directly from the record as attributes.
-
-.. note::
-
- When accessing non-relational fields on a recordset of potentially multiple
- records, use :meth:`~odoo.models.BaseModel.mapped`::
-
- total_qty = sum(self.mapped('qty'))
-
-Field values can also be accessed like dict items, which is more elegant and
-safer than ``getattr()`` for dynamic field names.
-Setting a field's value triggers an update to the database::
-
- >>> record.name
- Example Name
- >>> record.company_id.name
- Company Name
- >>> record.name = "Bob"
- >>> field = "name"
- >>> record[field]
- Bob
-
-.. warning::
-
- Trying to read a field on multiple records will raise an error for non relational
- fields.
-
-Accessing a relational field (:class:`~odoo.fields.Many2one`,
-:class:`~odoo.fields.One2many`, :class:`~odoo.fields.Many2many`)
-*always* returns a recordset, empty if the field is not set.
-
-Record cache and prefetching
-----------------------------
-
-Odoo maintains a cache for the fields of the records, so that not every field
-access issues a database request, which would be terrible for performance. The
-following example queries the database only for the first statement::
-
- record.name # first access reads value from database
- record.name # second access gets value from cache
-
-To avoid reading one field on one record at a time, Odoo *prefetches* records
-and fields following some heuristics to get good performance. Once a field must
-be read on a given record, the ORM actually reads that field on a larger
-recordset, and stores the returned values in cache for later use. The prefetched
-recordset is usually the recordset from which the record comes by iteration.
-Moreover, all simple stored fields (boolean, integer, float, char, text, date,
-datetime, selection, many2one) are fetched altogether; they correspond to the
-columns of the model's table, and are fetched efficiently in the same query.
-
-Consider the following example, where ``partners`` is a recordset of 1000
-records. Without prefetching, the loop would make 2000 queries to the database.
-With prefetching, only one query is made::
-
- for partner in partners:
- print partner.name # first pass prefetches 'name' and 'lang'
- # (and other fields) on all 'partners'
- print partner.lang
-
-The prefetching also works on *secondary records*: when relational fields are
-read, their values (which are records) are subscribed for future prefetching.
-Accessing one of those secondary records prefetches all secondary records from
-the same model. This makes the following example generate only two queries, one
-for partners and one for countries::
-
- countries = set()
- for partner in partners:
- country = partner.country_id # first pass prefetches all partners
- countries.add(country.name) # first pass prefetches all countries
-
-
-.. _reference/api/decorators:
-
-Method decorators
-=================
-
-.. automodule:: odoo.api
- :members: depends, depends_context, constrains, onchange, returns, autovacuum, model, model_create_multi
-
-.. .. currentmodule:: odoo.api
-
-.. .. autodata:: model
-.. .. autodata:: depends
-.. .. autodata:: constrains
-.. .. autodata:: onchange
-.. .. autodata:: returns
-.. .. autodata:: autovacuum
-
-.. todo:: With sphinx 2.0 : autodecorator
-
-.. todo:: Add in Views reference
- * It is possible to suppress the trigger from a specific field by adding
- ``on_change="0"`` in a view::
-
- <field name="name" on_change="0"/>
-
- will not trigger any interface update when the field is edited by the user,
- even if there are function fields or explicit onchange depending on that
- field.
-
-.. _reference/orm/environment:
-
-Environment
-===========
-
-The :class:`~odoo.api.Environment` stores various contextual data used by
-the ORM: the database cursor (for database queries), the current user
-(for access rights checking) and the current context (storing arbitrary
-metadata). The environment also stores caches.
-
-All recordsets have an environment, which is immutable, can be accessed
-using :attr:`~odoo.models.Model.env` and gives access to:
-
-* the current user (:attr:`~odoo.api.Environment.user`)
-* the cursor (:attr:`~odoo.api.Environment.cr`)
-* the superuser flag (:attr:`~odoo.api.Environment.su`)
-* or the context (:attr:`~odoo.api.Environment.context`)
-
-.. code-block:: bash
-
- >>> records.env
- <Environment object ...>
- >>> records.env.user
- res.user(3)
- >>> records.env.cr
- <Cursor object ...)
-
-When creating a recordset from an other recordset, the environment is
-inherited. The environment can be used to get an empty recordset in an
-other model, and query that model::
-
- >>> self.env['res.partner']
- res.partner()
- >>> self.env['res.partner'].search([['is_company', '=', True], ['customer', '=', True]])
- res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74)
-
-.. currentmodule:: odoo.api
-
-.. automethod:: Environment.ref
-
-.. autoattribute:: Environment.lang
-
-.. autoattribute:: Environment.user
-
-.. autoattribute:: Environment.company
-
-.. autoattribute:: Environment.companies
-
-.. TODO cr, uid but not @property or methods of Environment class...
-
-Altering the environment
-------------------------
-
-.. currentmodule:: odoo.models
-
-.. automethod:: Model.with_context
-
-.. automethod:: Model.with_user
-
-.. automethod:: Model.with_company
-
-.. automethod:: Model.with_env
-
-.. automethod:: Model.sudo
-
-.. _reference/orm/sql:
-
-SQL Execution
--------------
-
-The :attr:`~odoo.api.Environment.cr` attribute on environments is the
-cursor for the current database transaction and allows executing SQL directly,
-either for queries which are difficult to express using the ORM (e.g. complex
-joins) or for performance reasons::
-
- self.env.cr.execute("some_sql", param1, param2, param3)
-
-Because models use the same cursor and the :class:`~odoo.api.Environment`
-holds various caches, these caches must be invalidated when *altering* the
-database in raw SQL, or further uses of models may become incoherent. It is
-necessary to clear caches when using ``CREATE``, ``UPDATE`` or ``DELETE`` in
-SQL, but not ``SELECT`` (which simply reads the database).
-
-.. note::
- Clearing caches can be performed using the
- :meth:`~odoo.models.Model.invalidate_cache` method.
-
-.. automethod:: Model.invalidate_cache
-
-.. warning::
- Executing raw SQL bypasses the ORM, and by consequent, Odoo security rules.
- Please make sure your queries are sanitized when using user input and prefer using
- ORM utilities if you don't really need to use SQL queries.
-
-
-.. _reference/orm/models/crud:
-
-Common ORM methods
-==================
-
-.. currentmodule:: odoo.models
-
-Create/update
--------------
-
-.. todo:: api.model_create_multi information
-
-.. automethod:: Model.create
-
-.. automethod:: Model.copy
-
-.. automethod:: Model.default_get
-
-.. automethod:: Model.name_create
-
-.. automethod:: Model.write
-
-.. automethod:: Model.flush
-
-Search/Read
------------
-
-.. automethod:: Model.browse
-
-.. automethod:: Model.search
-
-.. automethod:: Model.search_count
-
-.. automethod:: Model.name_search
-
-.. automethod:: Model.read
-
-.. automethod:: Model.read_group
-
-Fields/Views
-''''''''''''
-
-.. automethod:: Model.fields_get
-
-.. automethod:: Model.fields_view_get
-
-.. _reference/orm/domains:
-
-Search domains
-''''''''''''''
-
-A domain is a list of criteria, each criterion being a triple (either a
-``list`` or a ``tuple``) of ``(field_name, operator, value)`` where:
-
-* ``field_name`` (``str``)
- a field name of the current model, or a relationship traversal through
- a :class:`~odoo.fields.Many2one` using dot-notation e.g. ``'street'``
- or ``'partner_id.country'``
-
-* ``operator`` (``str``)
- an operator used to compare the ``field_name`` with the ``value``. Valid
- operators are:
-
- ``=``
- equals to
- ``!=``
- not equals to
- ``>``
- greater than
- ``>=``
- greater than or equal to
- ``<``
- less than
- ``<=``
- less than or equal to
- ``=?``
- unset or equals to (returns true if ``value`` is either ``None`` or
- ``False``, otherwise behaves like ``=``)
- ``=like``
- matches ``field_name`` against the ``value`` pattern. An underscore
- ``_`` in the pattern stands for (matches) any single character; a
- percent sign ``%`` matches any string of zero or more characters.
- ``like``
- matches ``field_name`` against the ``%value%`` pattern. Similar to
- ``=like`` but wraps ``value`` with '%' before matching
- ``not like``
- doesn't match against the ``%value%`` pattern
- ``ilike``
- case insensitive ``like``
- ``not ilike``
- case insensitive ``not like``
- ``=ilike``
- case insensitive ``=like``
- ``in``
- is equal to any of the items from ``value``, ``value`` should be a
- list of items
- ``not in``
- is unequal to all of the items from ``value``
- ``child_of``
- is a child (descendant) of a ``value`` record (value can be either
- one item or a list of items).
-
- Takes the semantics of the model into account (i.e following the
- relationship field named by
- :attr:`~odoo.models.Model._parent_name`).
- ``parent_of``
- is a parent (ascendant) of a ``value`` record (value can be either
- one item or a list of items).
-
- Takes the semantics of the model into account (i.e following the
- relationship field named by
- :attr:`~odoo.models.Model._parent_name`).
-
-* ``value``
- variable type, must be comparable (through ``operator``) to the named
- field.
-
-Domain criteria can be combined using logical operators in *prefix* form:
-
-``'&'``
- logical *AND*, default operation to combine criteria following one
- another. Arity 2 (uses the next 2 criteria or combinations).
-``'|'``
- logical *OR*, arity 2.
-``'!'``
- logical *NOT*, arity 1.
-
- .. note:: Mostly to negate combinations of criteria
- Individual criterion generally have a negative form (e.g. ``=`` ->
- ``!=``, ``<`` -> ``>=``) which is simpler than negating the positive.
-
-.. admonition:: Example
-
- To search for partners named *ABC*, from belgium or germany, whose language
- is not english::
-
- [('name','=','ABC'),
- ('language.code','!=','en_US'),
- '|',('country_id.code','=','be'),
- ('country_id.code','=','de')]
-
- This domain is interpreted as:
-
- .. code-block:: text
-
- (name is 'ABC')
- AND (language is NOT english)
- AND (country is Belgium OR Germany)
-
-Unlink
-------
-
-.. automethod:: Model.unlink
-
-.. _reference/orm/records/info:
-
-Record(set) information
------------------------
-
-.. autoattribute:: Model.ids
-
-.. attribute:: env
-
- Returns the environment of the given recordset.
-
- :type: :class:`~odoo.api.Environment`
-
-.. todo:: Environment documentation
-
-.. automethod:: Model.exists
-
-.. automethod:: Model.ensure_one
-
-.. automethod:: Model.name_get
-
-.. automethod:: Model.get_metadata
-
-.. _reference/orm/records/operations:
-
-Operations
-----------
-
-Recordsets are immutable, but sets of the same model can be combined using
-various set operations, returning new recordsets.
-
-.. addition preserves order but can introduce duplicates
-
-* ``record in set`` returns whether ``record`` (which must be a 1-element
- recordset) is present in ``set``. ``record not in set`` is the inverse
- operation
-* ``set1 <= set2`` and ``set1 < set2`` return whether ``set1`` is a subset
- of ``set2`` (resp. strict)
-* ``set1 >= set2`` and ``set1 > set2`` return whether ``set1`` is a superset
- of ``set2`` (resp. strict)
-* ``set1 | set2`` returns the union of the two recordsets, a new recordset
- containing all records present in either source
-* ``set1 & set2`` returns the intersection of two recordsets, a new recordset
- containing only records present in both sources
-* ``set1 - set2`` returns a new recordset containing only records of ``set1``
- which are *not* in ``set2``
-
-Recordsets are iterable so the usual Python tools are available for
-transformation (:func:`python:map`, :func:`python:sorted`,
-:func:`~python:itertools.ifilter`, ...) however these return either a
-:class:`python:list` or an :term:`python:iterator`, removing the ability to
-call methods on their result, or to use set operations.
-
-Recordsets therefore provide the following operations returning recordsets themselves
-(when possible):
-
-Filter
-''''''
-
-.. automethod:: Model.filtered
-
-.. automethod:: Model.filtered_domain
-
-Map
-'''
-
-.. automethod:: Model.mapped
-
-.. note::
-
- Since V13, multi-relational field access is supported and works like a mapped call:
-
- .. code-block:: python3
-
- records.partner_id # == records.mapped('partner_id')
- records.partner_id.bank_ids # == records.mapped('partner_id.bank_ids')
- records.partner_id.mapped('name') # == records.mapped('partner_id.name')
-
-Sort
-''''
-
-.. automethod:: Model.sorted
-
-.. _reference/orm/inheritance:
-
-Inheritance and extension
-=========================
-
-Odoo provides three different mechanisms to extend models in a modular way:
-
-* creating a new model from an existing one, adding new information to the
- copy but leaving the original module as-is
-* extending models defined in other modules in-place, replacing the previous
- version
-* delegating some of the model's fields to records it contains
-
-.. image:: ../images/inheritance_methods.png
- :align: center
-
-Classical inheritance
----------------------
-
-When using the :attr:`~odoo.models.Model._inherit` and
-:attr:`~odoo.models.Model._name` attributes together, Odoo creates a new
-model using the existing one (provided via
-:attr:`~odoo.models.Model._inherit`) as a base. The new model gets all the
-fields, methods and meta-information (defaults & al) from its base.
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/inheritance.py
- :language: python
- :lines: 6-
-
-and using them:
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/tests/test_inheritance.py
- :language: python
- :lines: 10,11,14,19
-
-will yield:
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/tests/test_inheritance.py
- :language: text
- :lines: 16,21
-
-the second model has inherited from the first model's ``check`` method and its
-``name`` field, but overridden the ``call`` method, as when using standard
-:ref:`Python inheritance <python:tut-inheritance>`.
-
-Extension
----------
-
-When using :attr:`~odoo.models.Model._inherit` but leaving out
-:attr:`~odoo.models.Model._name`, the new model replaces the existing one,
-essentially extending it in-place. This is useful to add new fields or methods
-to existing models (created in other modules), or to customize or reconfigure
-them (e.g. to change their default sort order):
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/extension.py
- :language: python
- :lines: 6-
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/tests/test_extension.py
- :language: python
- :lines: 10,15
-
-will yield:
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/tests/test_extension.py
- :language: text
- :lines: 13
-
-.. note::
-
- It will also yield the various :ref:`automatic fields
- <reference/fields/automatic>` unless they've been disabled
-
-Delegation
-----------
-
-The third inheritance mechanism provides more flexibility (it can be altered
-at runtime) but less power: using the :attr:`~odoo.models.Model._inherits`
-a model *delegates* the lookup of any field not found on the current model
-to "children" models. The delegation is performed via
-:class:`~odoo.fields.Reference` fields automatically set up on the parent
-model.
-
-The main difference is in the meaning. When using Delegation, the model
-**has one** instead of **is one**, turning the relationship in a composition
-instead of inheritance:
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/delegation.py
- :language: python
- :lines: 5-
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/tests/test_delegation.py
- :language: python
- :lines: 11-14,23,28
-
-will result in:
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/tests/test_delegation.py
- :language: text
- :lines: 25,30
-
-and it's possible to write directly on the delegated field:
-
-.. literalinclude:: ../../odoo/addons/test_documentation_examples/tests/test_delegation.py
- :language: python
- :lines: 45
-
-.. warning:: when using delegation inheritance, methods are *not* inherited,
- only fields
-
-.. warning::
-
- * `_inherits` is more or less implemented, avoid it if you can;
- * chained `_inherits` is essentially not implemented, we cannot guarantee anything on the final behavior.
-
-
-Fields Incremental Definition
------------------------------
-
-A field is defined as class attribute on a model class. If the model
-is extended, one can also extend the field definition by redefining
-a field with the same name and same type on the subclass.
-In that case, the attributes of the field are taken from the parent class
-and overridden by the ones given in subclasses.
-
-For instance, the second class below only adds a tooltip on the field
-``state``::
-
- class First(models.Model):
- _name = 'foo'
- state = fields.Selection([...], required=True)
-
- class Second(models.Model):
- _inherit = 'foo'
- state = fields.Selection(help="Blah blah blah")
-
-.. _reference/exceptions:
-
-Error management
-================
-
-.. automodule:: odoo.exceptions
- :members: AccessDenied, AccessError, CacheMiss, MissingError, RedirectWarning, UserError, ValidationError
diff --git a/doc/reference/qweb.rst b/doc/reference/qweb.rst
deleted file mode 100644
index b224f46f2d4..00000000000
--- a/doc/reference/qweb.rst
+++ /dev/null
@@ -1,686 +0,0 @@
-:banner: banners/qweb.jpg
-
-.. highlight:: xml
-
-.. _reference/qweb:
-
-====
-QWeb
-====
-
-QWeb is the primary templating_ engine used by Odoo\ [#othertemplates]_. It
-is an XML templating engine\ [#genshif]_ and used mostly to generate HTML_
-fragments and pages.
-
-Template directives are specified as XML attributes prefixed with ``t-``,
-for instance ``t-if`` for :ref:`reference/qweb/conditionals`, with elements
-and other attributes being rendered directly.
-
-To avoid element rendering, a placeholder element ``<t>`` is also available,
-which executes its directive but doesn't generate any output in and of
-itself::
-
- <t t-if="condition">
- <p>Test</p>
- </t>
-
-will result in::
-
- <p>Test</p>
-
-if ``condition`` is true, but::
-
- <div t-if="condition">
- <p>Test</p>
- </div>
-
-will result in::
-
- <div>
- <p>Test</p>
- </div>
-
-.. _reference/qweb/output:
-
-Data output
-===========
-
-QWeb has a primary output directive which automatically HTML-escape its
-content limiting XSS_ risks when displaying user-provided content: ``esc``.
-
-``esc`` takes an expression, evaluates it and prints the content::
-
- <p><t t-esc="value"/></p>
-
-rendered with the value ``value`` set to ``42`` yields::
-
- <p>42</p>
-
-There is one other output directive ``raw`` which behaves the same as
-respectively ``esc`` but *does not HTML-escape its output*. It can be useful
-to display separately constructed markup (e.g. from functions) or already
-sanitized user-provided markup.
-
-.. _reference/qweb/conditionals:
-
-Conditionals
-============
-
-QWeb has a conditional directive ``if``, which evaluates an expression given
-as attribute value::
-
- <div>
- <t t-if="condition">
- <p>ok</p>
- </t>
- </div>
-
-The element is rendered if the condition is true::
-
- <div>
- <p>ok</p>
- </div>
-
-but if the condition is false it is removed from the result::
-
- <div>
- </div>
-
-The conditional rendering applies to the bearer of the directive, which does
-not have to be ``<t>``::
-
- <div>
- <p t-if="condition">ok</p>
- </div>
-
-will give the same results as the previous example.
-
-Extra conditional branching directives ``t-elif`` and ``t-else`` are also
-available::
-
- <div>
- <p t-if="user.birthday == today()">Happy birthday!</p>
- <p t-elif="user.login == 'root'">Welcome master!</p>
- <p t-else="">Welcome!</p>
- </div>
-
-
-.. _reference/qweb/loops:
-
-Loops
-=====
-
-QWeb has an iteration directive ``foreach`` which take an expression returning
-the collection to iterate on, and a second parameter ``t-as`` providing the
-name to use for the "current item" of the iteration::
-
- <t t-foreach="[1, 2, 3]" t-as="i">
- <p><t t-esc="i"/></p>
- </t>
-
-will be rendered as::
-
- <p>1</p>
- <p>2</p>
- <p>3</p>
-
-Like conditions, ``foreach`` applies to the element bearing the directive's
-attribute, and
-
-::
-
- <p t-foreach="[1, 2, 3]" t-as="i">
- <t t-esc="i"/>
- </p>
-
-is equivalent to the previous example.
-
-``foreach`` can iterate on an array (the current item will be the current
-value) or a mapping (the current item will be the current key). Iterating on an
-integer (equivalent to iterating on an array between 0 inclusive and the
-provided integer exclusive) is still supported but deprecated.
-
-In addition to the name passed via ``t-as``, ``foreach`` provides a few other
-variables for various data points:
-
-.. warning:: ``$as`` will be replaced by the name passed to ``t-as``
-
-:samp:`{$as}_all` (deprecated)
- the object being iterated over
-
- .. note:: This variable is only available on JavaScript QWeb, not Python.
-
-:samp:`{$as}_value`
- the current iteration value, identical to ``$as`` for lists and integers,
- but for mappings it provides the value (where ``$as`` provides the key)
-:samp:`{$as}_index`
- the current iteration index (the first item of the iteration has index 0)
-:samp:`{$as}_size`
- the size of the collection if it is available
-:samp:`{$as}_first`
- whether the current item is the first of the iteration (equivalent to
- :samp:`{$as}_index == 0`)
-:samp:`{$as}_last`
- whether the current item is the last of the iteration (equivalent to
- :samp:`{$as}_index + 1 == {$as}_size`), requires the iteratee's size be
- available
-:samp:`{$as}_parity` (deprecated)
- either ``"even"`` or ``"odd"``, the parity of the current iteration round
-:samp:`{$as}_even` (deprecated)
- a boolean flag indicating that the current iteration round is on an even
- index
-:samp:`{$as}_odd` (deprecated)
- a boolean flag indicating that the current iteration round is on an odd
- index
-
-These extra variables provided and all new variables created into the
-``foreach`` are only available in the scope of the``foreach``. If the
-variable exists outside the context of the ``foreach``, the value is copied
-at the end of the foreach into the global context.
-
-::
-
- <t t-set="existing_variable" t-value="False"/>
- <!-- existing_variable now False -->
-
- <p t-foreach="[1, 2, 3]" t-as="i">
- <t t-set="existing_variable" t-value="True"/>
- <t t-set="new_variable" t-value="True"/>
- <!-- existing_variable and new_variable now True -->
- </p>
-
- <!-- existing_variable always True -->
- <!-- new_variable undefined -->
-
-.. _reference/qweb/attributes:
-
-attributes
-==========
-
-QWeb can compute attributes on-the-fly and set the result of the computation
-on the output node. This is done via the ``t-att`` (attribute) directive which
-exists in 3 different forms:
-
-:samp:`t-att-{$name}`
- an attribute called ``$name`` is created, the attribute value is evaluated
- and the result is set as the attribute's value::
-
- <div t-att-a="42"/>
-
- will be rendered as::
-
- <div a="42"></div>
-:samp:`t-attf-{$name}`
- same as previous, but the parameter is a :term:`format string`
- instead of just an expression, often useful to mix literal and non-literal
- string (e.g. classes)::
-
- <t t-foreach="[1, 2, 3]" t-as="item">
- <li t-attf-class="row {{ (item_index % 2 === 0) ? 'even' : 'odd' }}">
- <t t-esc="item"/>
- </li>
- </t>
-
- will be rendered as::
-
- <li class="row even">1</li>
- <li class="row odd">2</li>
- <li class="row even">3</li>
-:samp:`t-att=mapping`
- if the parameter is a mapping, each (key, value) pair generates a new
- attribute and its value::
-
- <div t-att="{'a': 1, 'b': 2}"/>
-
- will be rendered as::
-
- <div a="1" b="2"></div>
-:samp:`t-att=pair`
- if the parameter is a pair (tuple or array of 2 element), the first
- item of the pair is the name of the attribute and the second item is the
- value::
-
- <div t-att="['a', 'b']"/>
-
- will be rendered as::
-
- <div a="b"></div>
-
-setting variables
-=================
-
-QWeb allows creating variables from within the template, to memoize a
-computation (to use it multiple times), give a piece of data a clearer name,
-...
-
-This is done via the ``set`` directive, which takes the name of the variable
-to create. The value to set can be provided in two ways:
-
-* a ``t-value`` attribute containing an expression, and the result of its
- evaluation will be set::
-
- <t t-set="foo" t-value="2 + 1"/>
- <t t-esc="foo"/>
-
- will print ``3``
-* if there is no ``t-value`` attribute, the node's body is rendered and set
- as the variable's value::
-
- <t t-set="foo">
- <li>ok</li>
- </t>
- <t t-esc="foo"/>
-
- will generate ``&lt;li&gt;ok&lt;/li&gt;`` (the content is escaped as we
- used the ``esc`` directive)
-
- .. note:: using the result of this operation is a significant use-case for
- the ``raw`` directive.
-
-calling sub-templates
-=====================
-
-QWeb templates can be used for top-level rendering, but they can also be used
-from within another template (to avoid duplication or give names to parts of
-templates) using the ``t-call`` directive::
-
- <t t-call="other-template"/>
-
-This calls the named template with the execution context of the parent, if
-``other_template`` is defined as::
-
- <p><t t-value="var"/></p>
-
-the call above will be rendered as ``<p/>`` (no content), but::
-
- <t t-set="var" t-value="1"/>
- <t t-call="other-template"/>
-
-will be rendered as ``<p>1</p>``.
-
-However this has the problem of being visible from outside the ``t-call``.
-Alternatively, content set in the body of the ``call`` directive will be
-evaluated *before* calling the sub-template, and can alter a local context::
-
- <t t-call="other-template">
- <t t-set="var" t-value="1"/>
- </t>
- <!-- "var" does not exist here -->
-
-The body of the ``call`` directive can be arbitrarily complex (not just
-``set`` directives), and its rendered form will be available within the called
-template as a magical ``0`` variable::
-
- <div>
- This template was called with content:
- <t t-raw="0"/>
- </div>
-
-being called thus::
-
- <t t-call="other-template">
- <em>content</em>
- </t>
-
-will result in::
-
- <div>
- This template was called with content:
- <em>content</em>
- </div>
-
-Python
-======
-
-Exclusive directives
---------------------
-
-Asset bundles
-'''''''''''''
-
-.. todo:: have fme write these up because I've no idea how they work
-
-"smart records" fields formatting
-'''''''''''''''''''''''''''''''''
-
-The ``t-field`` directive can only be used when performing field access
-(``a.b``) on a "smart" record (result of the ``browse`` method). It is able
-to automatically format based on field type, and is integrated in the
-website's rich text editing.
-
-``t-options`` can be used to customize fields, the most common option
-is ``widget``, other options are field- or widget-dependent.
-
-Debugging
----------
-
-``t-debug``
- invokes a debugger using PDB's ``set_trace`` API. The parameter should
- be the name of a module, on which a ``set_trace`` method is called::
-
- <t t-debug="pdb"/>
-
- is equivalent to ``importlib.import_module("pdb").set_trace()``
-
-Helpers
--------
-
-Request-based
-'''''''''''''
-
-Most Python-side uses of QWeb are in controllers (and during HTTP requests),
-in which case templates stored in the database (as
-:ref:`views <reference/views/qweb>`) can be trivially rendered by calling
-:meth:`odoo.http.HttpRequest.render`:
-
-.. code-block:: python
-
- response = http.request.render('my-template', {
- 'context_value': 42
- })
-
-This automatically creates a :class:`~odoo.http.Response` object which can
-be returned from the controller (or further customized to suit).
-
-View-based
-''''''''''
-
-At a deeper level than the previous helper is the ``render`` method on
-``ir.ui.view``:
-
-.. py:method:: render(cr, uid, id[, values][, engine='ir.qweb][, context])
-
- Renders a QWeb view/template by database id or :term:`external id`.
- Templates are automatically loaded from ``ir.ui.view`` records.
-
- Sets up a number of default values in the rendering context:
-
- ``request``
- the current :class:`~odoo.http.WebRequest` object, if any
- ``debug``
- whether the current request (if any) is in ``debug`` mode
- :func:`quote_plus <werkzeug.urls.url_quote_plus>`
- url-encoding utility function
- :mod:`json`
- the corresponding standard library module
- :mod:`time`
- the corresponding standard library module
- :mod:`datetime`
- the corresponding standard library module
- `relativedelta <https://labix.org/python-dateutil#head-ba5ffd4df8111d1b83fc194b97ebecf837add454>`_
- see module
- ``keep_query``
- the ``keep_query`` helper function
-
- :param values: context values to pass to QWeb for rendering
- :param str engine: name of the Odoo model to use for rendering, can be
- used to expand or customize QWeb locally (by creating
- a "new" qweb based on ``ir.qweb`` with alterations)
-
-.. _reference/qweb/javascript:
-
-.. todo:: the members below are no longer relevant, section to rewrite
-
-.. API
-.. ---
-
-.. It is also possible to use the ``ir.qweb`` model directly (and extend it, and
-.. inherit from it):
-
-.. .. automodule:: odoo.addons.base.ir.ir_qweb
-.. :members: QWeb, QWebContext, FieldConverter, QwebWidget
-
-Javascript
-==========
-
-Exclusive directives
---------------------
-
-Defining templates
-''''''''''''''''''
-
-The ``t-name`` directive can only be placed at the top-level of a template
-file (direct children to the document root)::
-
- <templates>
- <t t-name="template-name">
- <!-- template code -->
- </t>
- </templates>
-
-It takes no other parameter, but can be used with a ``<t>`` element or any
-other. With a ``<t>`` element, the ``<t>`` should have a single child.
-
-The template name is an arbitrary string, although when multiple templates
-are related (e.g. called sub-templates) it is customary to use dot-separated
-names to indicate hierarchical relationships.
-
-Template inheritance
-''''''''''''''''''''
-
-Template inheritance is used to either:
- - Alter existing templates in-place, e.g. to add information to templates
-created by other modules.
- - Create a new template from a given parent template
-
-Template inheritance is performed via the use of two directives:
- - ``t-inherit`` which is the name of the template to inherit from,
- - ``t-inherit-mode`` which is the behaviour of the inheritance: it can either be
- set to ``primary`` to create a new child template from the parented one or
- to ``extension`` to alter the parent template in place.
-
-An optional ``t-name`` directive can also be specified. It will be the name of
-the newly created template if used in primary mode, else it will be added as a
-comment on the transformed template to help retrace inheritances.
-
-For the inheritance itself, the changes are done using xpaths directives.
-See the XPATH_ documentation for the complete set of available instructions.
-
-Primary inheritance (child template)::
-
- <t t-name="child.template" t-inherit="base.template" t-inherit-mode="primary">
- <xpath expr="//ul" position="inside">
- <li>new element</li>
- </xpath>
- </t>
-
-Extension inheritance (in-place transformation)::
-
- <t t-inherit="base.template" t-inherit-mode="extension">
- <xpath expr="//tr[1]" position="after">
- <tr><td>new cell</td></tr>
- </xpath>
- </t>
-
-Old inheritance mechanism (deprecated)
-''''''''''''''''''''''''''''''''''''''
-
-Template inheritance is performed via the ``t-extend`` directive which takes
-the name of the template to alter as parameter.
-
-The directive ``t-extend`` will act as a primary inheritance when combined with
-``t-name`` and as an extension one when used alone.
-
-In both cases the alteration is then performed with any number of ``t-jquery``
-sub-directives::
-
- <t t-extend="base.template">
- <t t-jquery="ul" t-operation="append">
- <li>new element</li>
- </t>
- </t>
-
-The ``t-jquery`` directives takes a `CSS selector`_. This selector is used
-on the extended template to select *context nodes* to which the specified
-``t-operation`` is applied:
-
-``append``
- the node's body is appended at the end of the context node (after the
- context node's last child)
-``prepend``
- the node's body is prepended to the context node (inserted before the
- context node's first child)
-``before``
- the node's body is inserted right before the context node
-``after``
- the node's body is inserted right after the context node
-``inner``
- the node's body replaces the context node's children
-``replace``
- the node's body is used to replace the context node itself
-``attributes``
- the nodes's body should be any number of ``attribute`` elements,
- each with a ``name`` attribute and some textual content, the named
- attribute of the context node will be set to the specified value
- (either replaced if it already existed or added if not)
-No operation
- if no ``t-operation`` is specified, the template body is interpreted as
- javascript code and executed with the context node as ``this``
-
- .. warning:: while much more powerful than other operations, this mode is
- also much harder to debug and maintain, it is recommended to
- avoid it
-
-debugging
----------
-
-The javascript QWeb implementation provides a few debugging hooks:
-
-``t-log``
- takes an expression parameter, evaluates the expression during rendering
- and logs its result with ``console.log``::
-
- <t t-set="foo" t-value="42"/>
- <t t-log="foo"/>
-
- will print ``42`` to the console
-``t-debug``
- triggers a debugger breakpoint during template rendering::
-
- <t t-if="a_test">
- <t t-debug="">
- </t>
-
- will stop execution if debugging is active (exact condition depend on the
- browser and its development tools)
-``t-js``
- the node's body is javascript code executed during template rendering.
- Takes a ``context`` parameter, which is the name under which the rendering
- context will be available in the ``t-js``'s body::
-
- <t t-set="foo" t-value="42"/>
- <t t-js="ctx">
- console.log("Foo is", ctx.foo);
- </t>
-
-Helpers
--------
-
-.. js:attribute:: core.qweb
-
- (core is the ``web.core`` module) An instance of :js:class:`QWeb2.Engine` with all module-defined template
- files loaded, and references to standard helper objects ``_``
- (underscore), ``_t`` (translation function) and JSON_.
-
- :js:func:`core.qweb.render <QWeb2.Engine.render>` can be used to
- easily render basic module templates
-
-.. _reference/qweb/api:
-
-API
----
-
-.. js:class:: QWeb2.Engine
-
- The QWeb "renderer", handles most of QWeb's logic (loading,
- parsing, compiling and rendering templates).
-
- Odoo Web instantiates one for the user in the core module, and
- exports it to ``core.qweb``. It also loads all the template files
- of the various modules into that QWeb instance.
-
- A :js:class:`QWeb2.Engine` also serves as a "template namespace".
-
- .. js:function:: QWeb2.Engine.render(template[, context])
-
- Renders a previously loaded template to a String, using
- ``context`` (if provided) to find the variables accessed
- during template rendering (e.g. strings to display).
-
- :param String template: the name of the template to render
- :param Object context: the basic namespace to use for template
- rendering
- :returns: String
-
- The engine exposes an other method which may be useful in some
- cases (e.g. if you need a separate template namespace with, in
- Odoo Web, Kanban views get their own :js:class:`QWeb2.Engine`
- instance so their templates don't collide with more general
- "module" templates):
-
- .. js:function:: QWeb2.Engine.add_template(templates)
-
- Loads a template file (a collection of templates) in the QWeb
- instance. The templates can be specified as:
-
- An XML string
- QWeb will attempt to parse it to an XML document then load
- it.
-
- A URL
- QWeb will attempt to download the URL content, then load
- the resulting XML string.
-
- A ``Document`` or ``Node``
- QWeb will traverse the first level of the document (the
- child nodes of the provided root) and load any named
- template or template override.
-
- :type templates: String | Document | Node
-
- A :js:class:`QWeb2.Engine` also exposes various attributes for
- behavior customization:
-
- .. js:attribute:: QWeb2.Engine.prefix
-
- Prefix used to recognize directives during parsing. A string. By
- default, ``t``.
-
- .. js:attribute:: QWeb2.Engine.debug
-
- Boolean flag putting the engine in "debug mode". Normally,
- QWeb intercepts any error raised during template execution. In
- debug mode, it leaves all exceptions go through without
- intercepting them.
-
- .. js:attribute:: QWeb2.Engine.jQuery
-
- The jQuery instance used during template inheritance processing.
- Defaults to ``window.jQuery``.
-
- .. js:attribute:: QWeb2.Engine.preprocess_node
-
- A ``Function``. If present, called before compiling each DOM
- node to template code. In Odoo Web, this is used to
- automatically translate text content and some attributes in
- templates. Defaults to ``null``.
-
-.. [#genshif] it is similar in that to Genshi_, although it does not use (and
- has no support for) `XML namespaces`_
-
-.. [#othertemplates] although it uses a few others, either for historical
- reasons or because they remain better fits for the
- use case. Odoo 9.0 still depends on Jinja_ and Mako_.
-
-.. _templating:
- https://en.wikipedia.org/wiki/Template_processor
-
-.. _Jinja: http://jinja.pocoo.org
-.. _Mako: https://www.makotemplates.org
-.. _Genshi: https://genshi.edgewall.org
-.. _XML namespaces: https://en.wikipedia.org/wiki/XML_namespace
-.. _HTML: https://en.wikipedia.org/wiki/HTML
-.. _XSS: https://en.wikipedia.org/wiki/Cross-site_scripting
-.. _JSON: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
-.. _CSS selector: https://api.jquery.com/category/selectors/
-.. _XPATH: https://developer.mozilla.org/en-US/docs/Web/XPath
diff --git a/doc/reference/reports.rst b/doc/reference/reports.rst
deleted file mode 100644
index 140f91024c1..00000000000
--- a/doc/reference/reports.rst
+++ /dev/null
@@ -1,326 +0,0 @@
-:banner: banners/reports.jpg
-
-.. highlight:: xml
-
-.. _reference/reports:
-
-.. _reference/reports/report:
-
-============
-QWeb Reports
-============
-
-Reports are written in HTML/QWeb, like website views in Odoo. You can use
-the usual :ref:`QWeb control flow tools <reference/qweb>`. The PDF rendering
-itself is performed by wkhtmltopdf_.
-
-Reports are declared using a :ref:`report action <reference/actions/report>`,
-and a :ref:`reference/reports/templates` for the action to use.
-
-If useful or necessary, it is possible to specify a
-:ref:`reference/reports/paper_formats` for the report report.
-
-.. _reference/reports/templates:
-
-Report template
-===============
-
-Report templates will always provide the following variables:
-
-``time``
- a reference to :mod:`python:time` from the Python standard library
-``user``
- ``res.user`` record for the user printing the report
-``res_company``
- record for the current ``user``'s company
-``website``
- the current website object, if any (this item can be present but ``None``)
-``web_base_url``
- the base url for the webserver
-``context_timestamp``
- a function taking :class:`python:datetime.datetime` in UTC\ [#unzoned]_ and
- converting it to the timezone of the user printing the report
-
-Minimal viable template
------------------------
-
-A minimal template would look like::
-
- <template id="report_invoice">
- <t t-call="web.html_container">
- <t t-foreach="docs" t-as="o">
- <t t-call="web.external_layout">
- <div class="page">
- <h2>Report title</h2>
- <p>This object's name is <span t-field="o.name"/></p>
- </div>
- </t>
- </t>
- </t>
- </template>
-
-Calling ``external_layout`` will add the default header and footer on your
-report. The PDF body will be the content inside the ``<div
-class="page">``. The template's ``id`` must be the name specified in the
-report declaration; for example ``account.report_invoice`` for the above
-report. Since this is a QWeb template, you can access all the fields of the
-``docs`` objects received by the template.
-
-By default, the rendering context will also expose the following items:
-
-``docs``
- records for the current report
-``doc_ids``
- list of ids for the ``docs`` records
-``doc_model``
- model for the ``docs`` records
-
-If you wish to access other records/models in the template, you will need
-:ref:`a custom report <reference/reports/custom_reports>`, however in that case
-you will have to provide the items above if you need them.
-
-Translatable Templates
-----------------------
-
-If you wish to translate reports (to the language of a partner, for example),
-you need to define two templates:
-
-* The main report template
-* The translatable document
-
-You can then call the translatable document from your main template with the attribute
-``t-lang`` set to a language code (for example ``fr`` or ``en_US``) or to a record field.
-You will also need to re-browse the related records with the proper context if you use
-fields that are translatable (like country names, sales conditions, etc.)
-
-.. warning::
-
- If your report template does not use translatable record fields, re-browsing the record
- in another language is *not* necessary and will impact performances.
-
-For example, let's look at the Sale Order report from the Sale module::
-
- <!-- Main template -->
- <template id="report_saleorder">
- <t t-call="web.html_container">
- <t t-foreach="docs" t-as="doc">
- <t t-call="sale.report_saleorder_document" t-lang="doc.partner_id.lang"/>
- </t>
- </t>
- </template>
-
- <!-- Translatable template -->
- <template id="report_saleorder_document">
- <!-- Re-browse of the record with the partner lang -->
- <t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)" />
- <t t-call="web.external_layout">
- <div class="page">
- <div class="oe_structure"/>
- <div class="row">
- <div class="col-6">
- <strong t-if="doc.partner_shipping_id == doc.partner_invoice_id">Invoice and shipping address:</strong>
- <strong t-if="doc.partner_shipping_id != doc.partner_invoice_id">Invoice address:</strong>
- <div t-field="doc.partner_invoice_id" t-options="{&quot;no_marker&quot;: True}"/>
- <...>
- <div class="oe_structure"/>
- </div>
- </t>
- </template>
-
-
-The main template calls the translatable template with ``doc.partner_id.lang`` as a
-``t-lang`` parameter, so it will be rendered in the language of the partner. This way,
-each Sale Order will be printed in the language of the corresponding customer. If you wish
-to translate only the body of the document, but keep the header and footer in a default
-language, you could call the report's external layout this way::
-
- <t t-call="web.external_layout" t-lang="en_US">
-
-.. tip::
-
- Please take note that this works only when calling external templates, you will not be
- able to translate part of a document by setting a ``t-lang`` attribute on an xml node other
- than ``t-call``. If you wish to translate part of a template, you can create an external
- template with this partial template and call it from the main one with the ``t-lang``
- attribute.
-
-
-Barcodes
---------
-
-Barcodes are images returned by a controller and can easily be embedded in
-reports thanks to the QWeb syntax (e.g. see :ref:`reference/qweb/attributes`):
-
-.. code-block:: html
-
- <img t-att-src="'/report/barcode/QR/%s' % 'My text in qr code'"/>
-
-More parameters can be passed as a query string
-
-.. code-block:: html
-
- <img t-att-src="'/report/barcode/?
- type=%s&amp;value=%s&amp;width=%s&amp;height=%s'%('QR', 'text', 200, 200)"/>
-
-
-Useful Remarks
---------------
-* Twitter Bootstrap and FontAwesome classes can be used in your report
- template
-* Local CSS can be put directly in the template
-* Global CSS can be inserted in the main report layout by inheriting its
- template and inserting your CSS::
-
- <template id="report_saleorder_style" inherit_id="report.style">
- <xpath expr=".">
- <t>
- .example-css-class {
- background-color: red;
- }
- </t>
- </xpath>
- </template>
-
-* If it appears that your PDF report is missing the styles, please check
- :ref:`these instructions <reference/backend/reporting/printed-reports/pdf-without-styles>`.
-
-.. _reference/reports/paper_formats:
-
-Paper Format
-============
-
-Paper formats are records of ``report.paperformat`` and can contain the
-following attributes:
-
-``name`` (mandatory)
- only useful as a mnemonic/description of the report when looking for one
- in a list of some sort
-``description``
- a small description of your format
-``format``
- either a predefined format (A0 to A9, B0 to B10, Legal, Letter,
- Tabloid,...) or ``custom``; A4 by default. You cannot use a non-custom
- format if you define the page dimensions.
-``dpi``
- output DPI; 90 by default
-``margin_top``, ``margin_bottom``, ``margin_left``, ``margin_right``
- margin sizes in mm
-``page_height``, ``page_width``
- page dimensions in mm
-``orientation``
- Landscape or Portrait
-``header_line``
- boolean to display a header line
-``header_spacing``
- header spacing in mm
-
-Example::
-
- <record id="paperformat_frenchcheck" model="report.paperformat">
- <field name="name">French Bank Check</field>
- <field name="default" eval="True"/>
- <field name="format">custom</field>
- <field name="page_height">80</field>
- <field name="page_width">175</field>
- <field name="orientation">Portrait</field>
- <field name="margin_top">3</field>
- <field name="margin_bottom">3</field>
- <field name="margin_left">3</field>
- <field name="margin_right">3</field>
- <field name="header_line" eval="False"/>
- <field name="header_spacing">3</field>
- <field name="dpi">80</field>
- </record>
-
-.. _reference/reports/custom_reports:
-
-Custom Reports
-==============
-
-By default, the reporting system builds rendering values based on the target
-model specified through the ``model`` field.
-
-However, it will first look for a model named
-:samp:`report.{module.report_name}` and call that model's
-``_get_report_values(doc_ids, data)`` in order to prepare the rendering data for
-the template.
-
-This can be used to include arbitrary items to use or display while rendering
-the template, such as data from additional models:
-
-.. code-block:: python
-
- from odoo import api, models
-
- class ParticularReport(models.AbstractModel):
- _name = 'report.module.report_name'
-
- def _get_report_values(self, docids, data=None):
- # get the report action back as we will need its data
- report = self.env['ir.actions.report']._get_report_from_name('module.report_name')
- # get the records selected for this rendering of the report
- obj = self.env[report.model].browse(docids)
- # return a custom rendering context
- return {
- 'lines': docids.get_lines()
- }
-
-.. warning::
-
- When using a custom report, the "default" document-related items
- (``doc_ids``, ``doc_model`` and ``docs``) will *not* be included. If you
- want them, you will need to include them yourself.
-
- In the example above, the rendering context will contain the "global" values
- as well as the ``lines`` we put in there but nothing else.
-
-.. _reference/reports/custom_fonts:
-
-Custom fonts
-============
-If you want to use custom fonts you will need to add your custom font and the related less/CSS to the ``web.reports_assets_common`` assets bundle.
-Adding your custom font(s) to ``web.assets_common`` or ``web.assets_backend`` will not make your font available in QWeb reports.
-
-Example::
-
- <template id="report_assets_common_custom_fonts" name="Custom QWeb fonts" inherit_id="web.report_assets_common">
- <xpath expr="." position="inside">
- <link href="/your_module/static/src/less/fonts.less" rel="stylesheet" type="text/less"/>
- </xpath>
- </template>
-
-You will need to define your ``@font-face`` within this less file, even if you've used in another assets bundle (other than ``web.reports_assets_common``).
-
-Example::
-
- @font-face {
- font-family: 'MonixBold';
- src: local('MonixBold'), local('MonixBold'), url(/your_module/static/src/fonts/MonixBold-Regular.otf) format('opentype');
- }
-
- .h1-title-big {
- font-family: MonixBold;
- font-size: 60px;
- color: #3399cc;
- }
-
-After you've added the less into your assets bundle you can use the classes - in this example ``h1-title-big`` - in your custom QWeb report.
-
-Reports are web pages
-=====================
-
-Reports are dynamically generated by the report module and can be accessed
-directly via URL:
-
-For example, you can access a Sale Order report in html mode by going to
-\http://<server-address>/report/html/sale.report_saleorder/38
-
-Or you can access the pdf version at
-\http://<server-address>/report/pdf/sale.report_saleorder/38
-
-.. [#unzoned] it does not matter what timezone the :class:`python:datetime`
- object is actually in (including no timezone), its timezone will
- unconditionally be *set* to UTC before being adjusted to the
- user's
-
-.. _wkhtmltopdf: https://wkhtmltopdf.org
diff --git a/doc/reference/security.rst b/doc/reference/security.rst
deleted file mode 100644
index 7c235b08338..00000000000
--- a/doc/reference/security.rst
+++ /dev/null
@@ -1,389 +0,0 @@
-:banner: banners/security.jpg
-
-.. _reference/security:
-
-================
-Security in Odoo
-================
-
-Aside from manually managing access using custom code, Odoo provides two main
-data-driven mechanisms to manage or restrict access to data.
-
-Both mechanisms are linked to specific users through *groups*: a user belongs
-to any number of groups, and security mechanisms are associated to groups,
-thus applying security mechamisms to users.
-
-.. _reference/security/acl:
-
-Access Control
-==============
-
-Managed by the ``ir.model.access`` records, defines access to a whole model.
-
-Each access control has a model to which it grants permissions, the
-permissions it grants and optionally a group.
-
-Access controls are additive, for a given model a user has access all
-permissions granted to any of its groups: if the user belongs to one group
-which allows writing and another which allows deleting, they can both write
-and delete.
-
-If no group is specified, the access control applies to all users, otherwise
-it only applies to the members of the given group.
-
-Available permissions are creation (``perm_create``), searching and reading
-(``perm_read``), updating existing records (``perm_write``) and deleting
-existing records (``perm_unlink``)
-
-.. _reference/security/rules:
-
-Record Rules
-============
-
-Record rules are conditions that records must satisfy for an operation
-(create, read, update or delete) to be allowed. It is applied record-by-record
-after access control has been applied.
-
-A record rule has:
-
-* a model on which it applies
-* a set of permissions to which it applies (e.g. if ``perm_read`` is set, the
- rule will only be checked when reading a record)
-* a set of user groups to which the rule applies, if no group is specified
- the rule is *global*
-* a :ref:`domain <reference/orm/domains>` used to check whether a given record
- matches the rule (and is accessible) or does not (and is not accessible).
- The domain is evaluated with two variables in context: ``user`` is the
- current user's record and ``time`` is the `time module`_
-
-Global rules and group rules (rules restricted to specific groups versus
-groups applying to all users) are used quite differently:
-
-* Global rules are subtractive, they *must all* be matched for a record to be
- accessible
-* Group rules are additive, if *any* of them matches (and all global rules
- match) then the record is accessible
-
-This means the first *group rule* restricts access, but any further
-*group rule* expands it, while *global rules* can only ever restrict access
-(or have no effect).
-
-.. warning:: record rules do not apply to the Superuser account
- :class: aphorism
-
-.. _reference/security/fields:
-
-Field Access
-============
-
-An ORM :class:`~odoo.fields.Field` can have a ``groups`` attribute
-providing a list of groups (as a comma-separated string of
-:term:`external identifiers`).
-
-If the current user is not in one of the listed groups, he will not have
-access to the field:
-
-* restricted fields are automatically removed from requested views
-* restricted fields are removed from :meth:`~odoo.models.Model.fields_get`
- responses
-* attempts to (explicitly) read from or write to restricted fields results in
- an access error
-
-.. todo::
-
- field access groups apply to the Superuser in fields_get but not in
- read/write...
-
-.. _time module: https://docs.python.org/3/library/time.html
-
-
-.. _reference/security/pitfalls:
-
-Security Pitfalls
-=================
-
-As a developer, it is important to understand the security mechanisms and avoid
-common mistakes leading to insecure code.
-
-Unsafe Public Methods
----------------------
-
-Any public method can be executed via a :ref:`RPC call
-<webservices/odoo/calling_methods>` with the chosen parameters. The methods
-starting with a ``_`` are not callable from an action button or external API.
-
-On public methods, the record on which a method is executed and the parameters
-can not be trusted, ACL being only verified during CRUD operations.
-
-.. code-block:: python
-
- # this method is public and its arguments can not be trusted
- def action_done(self):
- if self.state == "draft" and self.user_has_groups('base.manager'):
- self._set_state("done")
-
- # this method is private and can only be called from other python methods
- def _set_state(self, new_state):
- self.sudo().write({"state": new_state})
-
-Making a method private is obviously not enough and care must be taken to use it
-properly.
-
-Bypassing the ORM
------------------
-You should never use the database cursor directly when the ORM can do the same
-thing! By doing so you are bypassing all the ORM features, possibly the
-automated behaviours like translations, invalidation of fields, ``active``,
-access rights and so on.
-
-And chances are that you are also making the code harder to read and probably
-less secure.
-
-.. code-block:: python
-
- # very very wrong
- self.env.cr.execute('SELECT id FROM auction_lots WHERE auction_id in (' + ','.join(map(str, ids))+') AND state=%s AND obj_price > 0', ('draft',))
- auction_lots_ids = [x[0] for x in self.env.cr.fetchall()]
-
- # no injection, but still wrong
- self.env.cr.execute('SELECT id FROM auction_lots WHERE auction_id in %s '\
- 'AND state=%s AND obj_price > 0', (tuple(ids), 'draft',))
- auction_lots_ids = [x[0] for x in self.env.cr.fetchall()]
-
- # better
- auction_lots_ids = self.search([('auction_id','in',ids), ('state','=','draft'), ('obj_price','>',0)])
-
-
-SQL injections
-~~~~~~~~~~~~~~
-Care must be taken not to introduce SQL injections vulnerabilities when using
-manual SQL queries. The vulnerability is present when user input is either
-incorrectly filtered or badly quoted, allowing an attacker to introduce
-undesirable clauses to a SQL query (such as circumventing filters or
-executing ``UPDATE`` or ``DELETE`` commands).
-
-The best way to be safe is to never, NEVER use Python string concatenation (+)
-or string parameters interpolation (%) to pass variables to a SQL query string.
-
-The second reason, which is almost as important, is that it is the job of the
-database abstraction layer (psycopg2) to decide how to format query parameters,
-not your job! For example psycopg2 knows that when you pass a list of values
-it needs to format them as a comma-separated list, enclosed in parentheses !
-
-.. code-block:: python
-
- # the following is very bad:
- # - it's a SQL injection vulnerability
- # - it's unreadable
- # - it's not your job to format the list of ids
- self.env.cr.execute('SELECT distinct child_id FROM account_account_consol_rel ' +
- 'WHERE parent_id IN ('+','.join(map(str, ids))+')')
-
- # better
- self.env.cr.execute('SELECT DISTINCT child_id '\
- 'FROM account_account_consol_rel '\
- 'WHERE parent_id IN %s',
- (tuple(ids),))
-
-This is very important, so please be careful also when refactoring, and most
-importantly do not copy these patterns!
-
-Here is a memorable example to help you remember what the issue is about (but
-do not copy the code there). Before continuing, please be sure to read the
-online documentation of pyscopg2 to learn of to use it properly:
-
-- `The problem with query parameters <http://initd.org/psycopg/docs/usage.html#the-problem-with-the-query-parameters>`_
-- `How to pass parameters with psycopg2 <http://initd.org/psycopg/docs/usage.html#passing-parameters-to-sql-queries>`_
-- `Advanced parameter types <http://initd.org/psycopg/docs/usage.html#adaptation-of-python-values-to-sql-types>`_
-- `Psycopg documentation <https://www.psycopg.org/docs/sql.html>`_
-
-Unescaped field content
------------------------
-
-When rendering content using JavaScript and XML, one may be tempted to use
-a ``t-raw`` to display rich-text content. This should be avoided as a frequent
-`XSS <https://en.wikipedia.org/wiki/Cross-site_scripting>`_ vector.
-
-It is very hard to control the integrity of the data from the computation until
-the final integration in the browser DOM. A ``t-raw`` that is correctly escaped
-at the time of introduction may no longer be safe at the next bugfix or
-refactoring.
-
-.. code-block:: javascript
-
- QWeb.render('insecure_template', {
- info_message: "You have an <strong>important</strong> notification",
- })
-
-.. code-block:: xml
-
- <div t-name="insecure_template">
- <div id="information-bar"><t t-raw="info_message" /></div>
- </div>
-
-The above code may feel safe as the message content is controlled but is a bad
-practice that may lead to unexpected security vulnerabilities once this code
-evolves in the future.
-
-.. code-block:: javascript
-
- // XSS possible with unescaped user provided content !
- QWeb.render('insecure_template', {
- info_message: "You have an <strong>important</strong> notification on " \
- + "the product <strong>" + product.name + "</strong>",
- })
-
-While formatting the template differently would prevent such vulnerabilities.
-
-.. code-block:: javascript
-
- QWeb.render('secure_template', {
- message: "You have an important notification on the product:",
- subject: product.name
- })
-
-.. code-block:: xml
-
- <div t-name="secure_template">
- <div id="information-bar">
- <div class="info"><t t-esc="message" /></div>
- <div class="subject"><t t-esc="subject" /></div>
- </div>
- </div>
-
-.. code-block:: css
-
- .subject {
- font-weight: bold;
- }
-
-Escaping vs Sanitizing
-----------------------
-
-.. important::
-
- Escaping is always 100% mandatory when you mix data and code, no matter how
- safe the data
-
-**Escaping** converts *TEXT* to *CODE*. It is absolutely mandatory to do it
-every time you mix *DATA/TEXT* with *CODE* (e.g. generating HTML or python code
-to be evaluated inside a `safe_eval`), because *CODE* always requires *TEXT* to
-be encoded. It is critical for security, but it's also a question of
-correctness. Even when there is no security risk (because the text is 100%
-guarantee to be safe or trusted), it is still required (e.g. to avoid breaking
-the layout in generated HTML).
-
-Escaping will never break any feature, as long as the developer identifies which
-variable contains *TEXT* and which contains *CODE*.
-
-.. code-block:: python
-
- >>> from odoo.tools import html_escape, html_sanitize
- >>> data = "<R&D>" # `data` is some TEXT coming from somewhere
-
- # Escaping turns it into CODE, good!
- >>> code = html_escape(data)
- >>> code
- '&lt;R&amp;D&gt;'
-
- # Now you can mix it with other code...
- >>> self.message_post(body="<strong>%s</strong>" % code)
-
-**Sanitizing** converts *CODE* to *SAFER CODE* (but not necessary *safe* code).
-It does not work on *TEXT*. Sanitizing is only necessary when *CODE* is
-untrusted, because it comes in full or in part from some user-provided data. If
-the user-provided data is in the form of *TEXT* (e.g. the content from a form
-filled by a user), and if that data was correctly escaped before putting it in
-*CODE*, then sanitizing is useless (but can still be done). If however, the
-user-provided data was **not escaped**, then sanitizing will **not** work as
-expected.
-
-.. code-block:: python
-
- # Sanitizing without escaping is BROKEN: data is corrupted!
- >>> html_sanitize(data)
- ''
-
- # Sanitizing *after* escaping is OK!
- >>> html_sanitize(code)
- '<p>&lt;R&amp;D&gt;</p>'
-
-Sanitizing can break features, depending on whether the *CODE* is expected to
-contain patterns that are not safe. That's why `fields.Html` and
-`tools.html_sanitize()` have options to fine-tune the level of sanitization for
-styles, etc. Those options have to be carefully considered depending on where
-the data comes from, and the desired features. The sanitization safety is
-balanced against sanitization breakages: the safer the sanitisation the more
-likely it is to break things.
-
-.. code-block:: python
-
- >>code = "<p class='text-warning'>Important Information</p>"
- # this will remove the style, which may break features
- # but is necessary if the source is untrusted
- >> html_sanitize(code, strip_classes=True)
- '<p>Important Information</p>'
-
-Evaluating content
-------------------
-Some may want to ``eval`` to parse user provided content. Using ``eval`` should
-be avoided at all cost. A safer, sandboxed, method :class:`~odoo.tools.safe_eval`
-can be used instead but still gives tremendous capabilities to the user running
-it and must be reserved for trusted privileged users only as it breaks the
-barrier between code and data.
-
-.. code-block:: python
-
- # very bad
- domain = eval(self.filter_domain)
- return self.search(domain)
-
- # better but still not recommended
- from odoo.tools import safe_eval
- domain = safe_eval(self.filter_domain)
- return self.search(domain)
-
- # good
- from ast import literal_eval
- domain = literal_eval(self.filter_domain)
- return self.search(domain)
-
-Parsing content does not need ``eval``
-
-========== ================== ================================
-Language Data type Suitable parser
-========== ================== ================================
-Python int, float, etc. int(), float()
-Javascript int, float, etc. parseInt(), parseFloat()
-Python dict json.loads(), ast.literal_eval()
-Javascript object, list, etc. JSON.parse()
-========== ================== ================================
-
-Accessing object attributes
----------------------------
-
-If the values of a record needs to be retrieved or modified dynamically, one may
-want to use the ``getattr`` and ``setattr`` methods.
-
-.. code-block:: python
-
- # unsafe retrieval of a field value
- def _get_state_value(self, res_id, state_field):
- record = self.sudo().browse(res_id)
- return getattr(record, state_field, False)
-
-This code is however not safe as it allows to access any property of the record,
-including private attributes or methods.
-
-The ``__getitem__`` of a recordset has been defined and accessing a dynamic
-field value can be easily achieved safely:
-
-.. code-block:: python
-
- # better retrieval of a field value
- def _get_state_value(self, res_id, state_field):
- record = self.sudo().browse(res_id)
- return record[state_field]
-
-The above method is obviously still too optimistic and additional verifications
-on the record id and field value must be done.
diff --git a/doc/reference/static/res.country.state.csv b/doc/reference/static/res.country.state.csv
deleted file mode 100644
index b1960d9a4c5..00000000000
--- a/doc/reference/static/res.country.state.csv
+++ /dev/null
@@ -1,15 +0,0 @@
-"id","country_id:id","name","code"
-state_au_1,au,"Australian Capital Territory","ACT"
-state_au_2,au,"New South Wales","NSW"
-state_au_3,au,"Northern Territory","NT"
-state_au_4,au,"Queensland","QLD"
-state_au_5,au,"South Australia","SA"
-state_au_6,au,"Tasmania","TAS"
-state_au_7,au,"Victoria","VIC"
-state_au_8,au,"Western Australia","WA"
-state_us_1,us,"Alabama","AL"
-state_us_2,us,"Alaska","AK"
-state_us_3,us,"Arizona","AZ"
-state_us_4,us,"Arkansas","AR"
-state_us_5,us,"California","CA"
-state_us_6,us,"Colorado","CO"
diff --git a/doc/reference/testing.rst b/doc/reference/testing.rst
deleted file mode 100644
index dc0b9e1823a..00000000000
--- a/doc/reference/testing.rst
+++ /dev/null
@@ -1,638 +0,0 @@
-:banner: banners/testing_modules.jpg
-
-.. _reference/testing:
-
-
-===============
-Testing Odoo
-===============
-
-There are many ways to test an application. In Odoo, we have three kinds of
-tests
-
-- Python unit tests (see `Testing Python code`_): useful for testing model business logic
-- JS unit tests (see `Testing JS code`_): useful to test the javascript code in isolation
-- Tours (see `Integration Testing`_): tours simulate a real situation. They ensures that the
- python and the javascript parts properly talk to each other.
-
-Testing Python code
-===================
-
-Odoo provides support for testing modules using unittest.
-
-To write tests, simply define a ``tests`` sub-package in your module, it will
-be automatically inspected for test modules. Test modules should have a name
-starting with ``test_`` and should be imported from ``tests/__init__.py``,
-e.g.
-
-.. code-block:: text
-
- your_module
- |-- ...
- `-- tests
- |-- __init__.py
- |-- test_bar.py
- `-- test_foo.py
-
-and ``__init__.py`` contains::
-
- from . import test_foo, test_bar
-
-.. warning::
-
- test modules which are not imported from ``tests/__init__.py`` will not be
- run
-
-The test runner will simply run any test case, as described in the official
-`unittest documentation`_, but Odoo provides a number of utilities and helpers
-related to testing Odoo content (modules, mainly):
-
-.. autoclass:: odoo.tests.common.TransactionCase
- :members: browse_ref, ref
-
-.. autoclass:: odoo.tests.common.SingleTransactionCase
- :members: browse_ref, ref
-
-.. autoclass:: odoo.tests.common.SavepointCase
-
-.. autoclass:: odoo.tests.common.HttpCase
- :members: browse_ref, ref, url_open, browser_js
-
-.. autofunction:: odoo.tests.common.tagged
-
-By default, tests are run once right after the corresponding module has been
-installed. Test cases can also be configured to run after all modules have
-been installed, and not run right after the module installation::
-
- # coding: utf-8
- from odoo.tests import HttpCase, tagged
-
- # This test should only be executed after all modules have been installed.
- @tagged('-at_install', 'post_install')
- class WebsiteVisitorTests(HttpCase):
- def test_create_visitor_on_tracked_page(self):
- Page = self.env['website.page']
-
-The most common situation is to use
-:class:`~odoo.tests.common.TransactionCase` and test a property of a model
-in each method::
-
- class TestModelA(common.TransactionCase):
- def test_some_action(self):
- record = self.env['model.a'].create({'field': 'value'})
- record.some_action()
- self.assertEqual(
- record.field,
- expected_field_value)
-
- # other tests...
-
-.. note::
-
- Test methods must start with ``test_``
-
-.. autoclass:: odoo.tests.common.Form
- :members:
-
-.. autoclass:: odoo.tests.common.M2MProxy
- :members: add, remove, clear
-
-.. autoclass:: odoo.tests.common.O2MProxy
- :members: new, edit, remove
-
-Running tests
--------------
-
-Tests are automatically run when installing or updating modules if
-:option:`--test-enable <odoo-bin --test-enable>` was enabled when starting the
-Odoo server.
-
-.. _unittest documentation: https://docs.python.org/3/library/unittest.html
-
-Test selection
---------------
-
-In Odoo, Python tests can be tagged to facilitate the test selection when
-running tests.
-
-Subclasses of :class:`odoo.tests.common.BaseCase` (usually through
-:class:`~odoo.tests.common.TransactionCase`,
-:class:`~odoo.tests.common.SavepointCase` or
-:class:`~odoo.tests.common.HttpCase`) are automatically tagged with
-``standard``, ``at_install`` and their source module's name by default.
-
-Invocation
-^^^^^^^^^^
-
-:option:`--test-tags <odoo-bin --test-tags>` can be used to select/filter tests
-to run on the command-line.
-
-This option defaults to ``+standard`` meaning tests tagged ``standard``
-(explicitly or implicitly) will be run by default when starting Odoo
-with :option:`--test-enable <odoo-bin --test-enable>`.
-
-When writing tests, the :func:`~odoo.tests.common.tagged` decorator can be
-used on **test classes** to add or remove tags.
-
-The decorator's arguments are tag names, as strings.
-
-.. danger:: :func:`~odoo.tests.common.tagged` is a class decorator, it has no
- effect on functions or methods
-
-Tags can be prefixed with the minus (``-``) sign, to *remove* them instead of
-add or select them e.g. if you don't want your test to be executed by
-default you can remove the ``standard`` tag:
-
-.. code-block:: python
-
- from odoo.tests import TransactionCase, tagged
-
- @tagged('-standard', 'nice')
- class NiceTest(TransactionCase):
- ...
-
-This test will not be selected by default, to run it the relevant tag will
-have to be selected explicitely:
-
-.. code-block:: console
-
- $ odoo-bin --test-enable --test-tags nice
-
-Note that only the tests tagged ``nice`` are going to be executed. To run
-*both* ``nice`` and ``standard`` tests, provide multiple values to
-:option:`--test-tags <odoo-bin --test-tags>`: on the command-line, values
-are *additive* (you're selecting all tests with *any* of the specified tags)
-
-.. code-block:: console
-
- $ odoo-bin --test-enable --test-tags nice,standard
-
-The config switch parameter also accepts the ``+`` and ``-`` prefixes. The
-``+`` prefix is implied and therefore, totaly optional. The ``-`` (minus)
-prefix is made to deselect tests tagged with the prefixed tags, even if they
-are selected by other specified tags e.g. if there are ``standard`` tests which
-are also tagged as ``slow`` you can run all standard tests *except* the slow
-ones:
-
-.. code-block:: console
-
- $ odoo-bin --test-enable --test-tags 'standard,-slow'
-
-When you write a test that does not inherit from the
-:class:`~odoo.tests.common.BaseCase`, this test will not have the default tags,
-you have to add them explicitely to have the test included in the default test
-suite. This is a common issue when using a simple ``unittest.TestCase`` as
-they're not going to get run:
-
-.. code-block:: python
-
- import unittest
- from odoo.tests import tagged
-
- @tagged('standard', 'at_install')
- class SmallTest(unittest.TestCase):
- ...
-
-Special tags
-^^^^^^^^^^^^
-
-- ``standard``: All Odoo tests that inherit from
- :class:`~odoo.tests.common.BaseCase` are implicitely tagged standard.
- :option:`--test-tags <odoo-bin --test-tags>` also defaults to ``standard``.
-
- That means untagged test will be executed by default when tests are enabled.
-- ``at_install``: Means that the test will be executed right after the module
- installation and before other modules are installed. This is a default
- implicit tag.
-- ``post_install``: Means that the test will be executed after all the modules
- are installed. This is what you want for HttpCase tests most of the time.
-
- Note that this is *not exclusive* with ``at_install``, however since you
- will generally not want both ``post_install`` is usually paired with
- ``-at_install`` when tagging a test class.
-- *module_name*: Odoo tests classes extending
- :class:`~odoo.tests.common.BaseCase` are implicitely tagged with the
- technical name of their module. This allows easily selecting or excluding
- specific modules when testing e.g. if you want to only run tests from
- ``stock_account``:
-
- .. code-block:: console
-
- $ odoo-bin --test-enable --test-tags stock_account
-
-Examples
-^^^^^^^^
-
-.. important::
-
- Tests will be executed only in the installed or updated modules. So
- modules have to be selected with the :option:`-u <odoo-bin -u>` or
- :option:`-i <odoo-bin -i>` switches. For simplicity, those switches are
- not specified in the examples below.
-
-Run only the tests from the sale module:
-
-.. code-block:: console
-
- $ odoo-bin --test-enable --test-tags sale
-
-Run the tests from the sale module but not the ones tagged as slow:
-
-.. code-block:: console
-
- $ odoo-bin --test-enable --test-tags 'sale,-slow'
-
-Run only the tests from stock or tagged as slow:
-
-.. code-block:: console
-
- $ odoo-bin --test-enable --test-tags '-standard, slow, stock'
-
-.. note:: ``-standard`` is implicit (not required), and present for clarity
-
-Testing JS code
-===============
-
-Testing a complex system is an important safeguard to prevent regressions and to
-guarantee that some basic functionality still works. Since Odoo has a non trivial
-codebase in Javascript, it is necessary to test it. In this section, we will
-discuss the practice of testing JS code in isolation: these tests stay in the
-browser, and are not supposed to reach the server.
-
-Qunit test suite
-----------------
-
-The Odoo framework uses the QUnit_ library testing framework as a test runner.
-QUnit defines the concepts of *tests* and *modules* (a set of related tests),
-and gives us a web based interface to execute the tests.
-
-For example, here is what a pyUtils test could look like:
-
-.. code-block:: javascript
-
- QUnit.module('py_utils');
-
- QUnit.test('simple arithmetic', function (assert) {
- assert.expect(2);
-
- var result = pyUtils.py_eval("1 + 2");
- assert.strictEqual(result, 3, "should properly evaluate sum");
- result = pyUtils.py_eval("42 % 5");
- assert.strictEqual(result, 2, "should properly evaluate modulo operator");
- });
-
-The main way to run the test suite is to have a running Odoo server, then
-navigate a web browser to ``/web/tests``. The test suite will then be executed
-by the web browser Javascript engine.
-
-.. image:: ./images/tests.png
- :align: center
-
-The web UI has many useful features: it can run only some submodules, or
-filter tests that match a string. It can show every assertions, failed or passed,
-rerun specific tests, ...
-
-.. warning::
-
- While the test suite is running, make sure that:
-
- - your browser window is focused,
- - it is not zoomed in/out. It needs to have exactly 100% zoom level.
-
- If this is not the case, some tests will fail, without a proper explanation.
-
-Testing Infrastructure
-----------------------
-
-Here is a high level overview of the most important parts of the testing
-infrastructure:
-
-- there is an asset bundle named `web.qunit_suite`_. This bundle contains
- the main code (assets common + assets backend), some libraries, the QUnit test
- runner and the test bundles listed below.
-
-- a bundle named `web.tests_assets`_ includes most of the assets and utils required
- by the test suite: custom QUnit asserts, test helpers, lazy loaded assets, etc.
-
-- another asset bundle, `web.qunit_suite_tests`_, contains all the test scripts.
- This is typically where the test files are added to the suite.
-
-- there is a `controller`_ in web, mapped to the route */web/tests*. This controller
- simply renders the *web.qunit_suite* template.
-
-- to execute the tests, one can simply point its browser to the route */web/tests*.
- In that case, the browser will download all assets, and QUnit will take over.
-
-- there is some code in `qunit_config.js`_ which logs in the console some
- information when a test passes or fails.
-
-- we want the runbot to also run these tests, so there is a test (in `test_js.py`_)
- which simply spawns a browser and points it to the *web/tests* url. Note that
- the browser_js method spawns a Chrome headless instance.
-
-
-Modularity and testing
-----------------------
-
-With the way Odoo is designed, any addon can modify the behaviour of other parts
-of the system. For example, the *voip* addon can modify the *FieldPhone* widget
-to use extra features. This is not really good from the perspective of the
-testing system, since this means that a test in the addon web will fail whenever
-the voip addon is installed (note that the runbot runs the tests with all addons
-installed).
-
-At the same time, our testing sytem is good, because it can detect whenever
-another module breaks some core functionality. There is no complete solution to
-this issue. For now, we solve this on a case by case basis.
-
-Usually, it is not a good idea to modify some other behaviour. For our voip
-example, it is certainly cleaner to add a new *FieldVOIPPhone* widget and
-modify the few views that needs it. This way, the *FieldPhone* widget is not
-impacted, and both can be tested.
-
-Adding a new test case
-----------------------
-
-Let us assume that we are maintaining an addon *my_addon*, and that we
-want to add a test for some javascript code (for example, some utility function
-myFunction, located in *my_addon.utils*). The process to add a new test case is
-the following:
-
-1. create a new file *my_addon/static/tests/utils_tests.js*. This file contains the basic code to
- add a QUnit module *my_addon > utils*.
-
- .. code-block:: javascript
-
- odoo.define('my_addon.utils_tests', function (require) {
- "use strict";
-
- var utils = require('my_addon.utils');
-
- QUnit.module('my_addon', {}, function () {
-
- QUnit.module('utils');
-
- });
- });
-
-
-2. In *my_addon/assets.xml*, add the file to the main test assets:
-
- .. code-block:: xml
-
- <?xml version="1.0" encoding="utf-8"?>
- <odoo>
- <template id="qunit_suite_tests" name="my addon tests" inherit_id="web.qunit_suite_tests">
- <xpath expr="//script[last()]" position="after">
- <script type="text/javascript" src="/my_addon/static/tests/utils_tests.js"/>
- </xpath>
- </template>
- </odoo>
-
-3. Restart the server and update *my_addon*, or do it from the interface (to
- make sure the new test file is loaded)
-
-4. Add a test case after the definition of the *utils* sub test suite:
-
- .. code-block:: javascript
-
- QUnit.test("some test case that we want to test", function (assert) {
- assert.expect(1);
-
- var result = utils.myFunction(someArgument);
- assert.strictEqual(result, expectedResult);
- });
-
-5. Visit */web/tests/* to make sure the test is executed
-
-Helper functions and specialized assertions
--------------------------------------------
-
-Without help, it is quite difficult to test some parts of Odoo. In particular,
-views are tricky, because they communicate with the server and may perform many
-rpcs, which needs to be mocked. This is why we developed some specialized
-helper functions, located in `test_utils.js`_.
-
-- Mock test functions: these functions help setting up a test environment. The
- most important use case is mocking the answers given by the Odoo server. These
- functions use a `mock server`_. This is a javascript class that simulates
- answers to the most common model methods: read, search_read, nameget, ...
-
-- DOM helpers: useful to simulate events/actions on some specific target. For
- example, testUtils.dom.click performs a click on a target. Note that it is
- safer than doing it manually, because it also checks that the target exists,
- and is visible.
-
-- create helpers: they are probably the most important functions exported by
- `test_utils.js`_. These helpers are useful to create a widget, with a mock
- environment, and a lot of small detail to simulate as much as possible the
- real conditions. The most important is certainly `createView`_.
-
-- `qunit assertions`_: QUnit can be extended with specialized assertions. For
- Odoo, we frequently test some DOM properties. This is why we made some
- assertions to help with that. For example, the *containsOnce* assertion takes
- a widget/jQuery/HtmlElement and a selector, then checks if the target contains
- exactly one match for the css selector.
-
-For example, with these helpers, here is what a simple form test could look like:
-
-.. code-block:: javascript
-
- QUnit.test('simple group rendering', function (assert) {
- assert.expect(1);
-
- var form = testUtils.createView({
- View: FormView,
- model: 'partner',
- data: this.data,
- arch: '<form string="Partners">' +
- '<group>' +
- '<field name="foo"/>' +
- '</group>' +
- '</form>',
- res_id: 1,
- });
-
- assert.containsOnce(form, 'table.o_inner_group');
-
- form.destroy();
- });
-
-Notice the use of the testUtils.createView helper and of the containsOnce
-assertion. Also, the form controller was properly destroyed at the end of
-the test.
-
-Best Practices
---------------
-
-In no particular order:
-
-- all test files should be added in *some_addon/static/tests/*
-- for bug fixes, make sure that the test fails without the bug fix, and passes
- with it. This ensures that it actually works.
-- try to have the minimal amount of code necessary for the test to work.
-- usually, two small tests are better than one large test. A smaller test is
- easier to understand and to fix.
-- always cleanup after a test. For example, if your test instantiates a widget,
- it should destroy it at the end.
-- no need to have full and complete code coverage. But adding a few tests helps
- a lot: it makes sure that your code is not completely broken, and whenever a
- bug is fixed, it is really much easier to add a test to an existing test suite.
-- if you want to check some negative assertion (for example, that a HtmlElement
- does not have a specific css class), then try to add the positive assertion in
- the same test (for example, by doing an action that changes the state). This
- will help avoid the test to become dead in the future (for example, if the css
- class is changed).
-
-Tips
-----
-
-- running only one test: you can (temporarily!) change the *QUnit.test(...)*
- definition into *QUnit.only(...)*. This is useful to make sure that QUnit
- only runs this specific test.
-- debug flag: most create utility functions have a debug mode (activated by the
- debug: true parameter). In that case, the target widget will be put in the DOM
- instead of the hidden qunit specific fixture, and more information will be
- logged. For example, all mocked network communications will be available in the
- console.
-- when working on a failing test, it is common to add the debug flag, then
- comment the end of the test (in particular, the destroy call). With this, it
- is possible to see the state of the widget directly, and even better, to
- manipulate the widget by clicking/interacting with it.
-
-
-Integration Testing
-===================
-
-Testing Python code and JS code separately is very useful, but it does not prove
-that the web client and the server work together. In order to do that, we can
-write another kind of test: tours. A tour is a mini scenario of some interesting
-business flow. It explains a sequence of steps that should be followed. The
-test runner will then create a Chrome headless browser, point it to the proper url
-and simulate the click and inputs, according to the scenario.
-
-Screenshots and screencasts during browser_js tests
----------------------------------------------------
-
-When running tests that use HttpCase.browser_js from the command line, the Chrome
-browser is used in headless mode. By default, if a test fails, a PNG screenshot is
-taken at the moment of the failure and written in
-
-.. code-block:: console
-
- '/tmp/odoo_tests/{db_name}/screenshots/'
-
-Two new command line arguments were added since Odoo 13.0 to control this behavior:
-:option:`--screenshots <odoo-bin --screenshots>` and :option:`--screencasts <odoo-bin --screencasts>`
-
-
-Performance Testing
-===================
-
-Query counts
-------------
-
-One of the ways to test performance is to measure database queries. Manually, this can be tested with the
-`--log-sql` CLI parameter. If you want to establish the maximum number of queries for an operation,
-you can use the :meth:`~odoo.tests.common.BaseCase.assertQueryCount` method, integrated in Odoo test classes.
-
-.. code-block:: python
-
- with self.assertQueryCount(11):
- do_something()
-
-.. _reference/testing/populate:
-
-Database population
--------------------
-
-Odoo CLI offers a :ref:`database population<reference/cmdline/populate>` feature.
-
-.. code-block:: console
-
- odoo-bin populate
-
-Instead of the tedious manual, or programmatic, specification of test data,
-one can use this feature to fill a database on demand with the desired number of test data.
-This can be used to detect diverse bugs or performance issues in tested flows.
-
-.. _reference/testing/populate/methods:
-
-To specify this feature for a given model, the following methods and attributes can be defined.
-
-.. currentmodule:: odoo.models
-
-.. autoattribute:: Model._populate_sizes
-.. autoattribute:: Model._populate_dependencies
-.. automethod:: Model._populate
-.. automethod:: Model._populate_factories
-
-.. note::
-
- You have to define at least :meth:`~odoo.models.Model._populate` or :meth:`~odoo.models.Model._populate_factories`
- on the model to enable database population.
-
-Example model
-^^^^^^^^^^^^^
-
-.. code-block:: python
-
- from odoo.tools import populate
-
- class CustomModel(models.Model)
- _inherit = "custom.some_model"
- _populate_sizes = {"small": 100, "medium": 2000, "large": 10000}
- _populate_dependencies = ["custom.some_other_model"]
-
- def _populate_factories(self):
- # Record ids of previously populated models are accessible in the registry
- some_other_ids = self.env.registry.populated_models["custom.some_other_model"]
-
- def get_some_field(values=None, random=None, **kwargs):
- """ Choose a value for some_field depending on other fields values.
-
- :param dict values:
- :param random: seeded :class:`random.Random` object
- """
- field_1 = values['field_1']
- if field_1 in [value2, value3]:
- return random.choice(some_field_values)
- return False
-
- return [
- ("field_1", populate.randomize([value1, value2, value3])),
- ("field_2", populate.randomize([value_a, value_b], [0.5, 0.5])),
- ("some_other_id", populate.randomize(some_other_ids)),
- ("some_field", populate.compute(get_some_field, seed="some_field")),
- ('active', populate.cartesian([True, False])),
- ]
-
- def _populate(self, size):
- records = super()._populate(size)
-
- # If you want to update the generated records
- # E.g setting the parent-child relationships
- records.do_something()
-
- return records
-
-Population tools
-^^^^^^^^^^^^^^^^
-
-Multiple population tools are available to easily create
-the needed data generators.
-
-.. automodule:: odoo.tools.populate
- :members: cartesian, compute, constant, iterate, randint, randomize
-
-.. _qunit: https://qunitjs.com/
-.. _qunit_config.js: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/qunit_config.js#L49
-.. _web.tests_assets: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L594
-.. _web.qunit_suite: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L660
-.. _web.qunit_suite_tests: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L680
-.. _controller: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/controllers/main.py#L637
-.. _test_js.py: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/tests/test_js.py#L13
-.. _test_utils.js: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/test_utils.js
-.. _mock server: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/mock_server.js
-.. _qunit assertions: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/qunit_asserts.js
-.. _createView: https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/test_utils_create.js#L267
diff --git a/doc/reference/translations.rst b/doc/reference/translations.rst
deleted file mode 100644
index cc6c92cbb5d..00000000000
--- a/doc/reference/translations.rst
+++ /dev/null
@@ -1,214 +0,0 @@
-:banner: banners/translate.jpg
-
-.. _reference/translations:
-
-
-===================
-Translating Modules
-===================
-
-This section explains how to provide translation abilities to your module.
-
-.. note:: If you want to contribute to the translation of Odoo itself, please refer to the
- `Odoo Wiki page <https://github.com/odoo/odoo/wiki/Translations>`_.
-
-Exporting translatable term
-===========================
-
-A number of terms in your modules are "implicitly translatable" as a result,
-even if you haven't done any specific work towards translation you can export
-your module's translatable terms and may find content to work with.
-
-.. todo:: needs technical features
-
-Translations export is performed via the administration interface by logging into
-the backend interface and opening :menuselection:`Settings --> Translations
---> Import / Export --> Export Translations`
-
-* leave the language to the default (new language/empty template)
-* select the `PO File`_ format
-* select your module
-* click :guilabel:`Export` and download the file
-
-.. image:: translations/po-export.*
- :align: center
- :width: 75%
-
-This gives you a file called :file:`{yourmodule}.pot` which should be moved to
-the :file:`{yourmodule}/i18n/` directory. The file is a *PO Template* which
-simply lists translatable strings and from which actual translations (PO files)
-can be created. PO files can be created using msginit_, with a dedicated
-translation tool like POEdit_ or by simply copying the template to a new file
-called :file:`{language}.po`. Translation files should be put in
-:file:`{yourmodule}/i18n/`, next to :file:`{yourmodule}.pot`, and will be
-automatically loaded by Odoo when the corresponding language is installed (via
-:menuselection:`Settings --> Translations --> Languages`)
-
-.. note:: translations for all loaded languages are also installed or updated
- when installing or updating a module
-
-Implicit exports
-================
-
-Odoo automatically exports translatable strings from "data"-type content:
-
-* in non-QWeb views, all text nodes are exported as well as the content of
- the ``string``, ``help``, ``sum``, ``confirm`` and ``placeholder``
- attributes
-* QWeb templates (both server-side and client-side), all text nodes are
- exported except inside ``t-translation="off"`` blocks, the content of the
- ``title``, ``alt``, ``label`` and ``placeholder`` attributes are also
- exported
-* for :class:`~odoo.fields.Field`, unless their model is marked with
- ``_translate = False``:
-
- * their ``string`` and ``help`` attributes are exported
- * if ``selection`` is present and a list (or tuple), it's exported
- * if their ``translate`` attribute is set to ``True``, all of their existing
- values (across all records) are exported
-* help/error messages of :attr:`~odoo.models.Model._constraints` and
- :attr:`~odoo.models.Model._sql_constraints` are exported
-
-Explicit exports
-================
-
-When it comes to more "imperative" situations in Python code or Javascript
-code, Odoo cannot automatically export translatable terms so they
-must be marked explicitly for export. This is done by wrapping a literal
-string in a function call.
-
-In Python, the wrapping function is :func:`odoo._`::
-
- title = _("Bank Accounts")
-
-In JavaScript, the wrapping function is generally :js:func:`odoo.web._t`:
-
-.. code-block:: javascript
-
- title = _t("Bank Accounts");
-
-.. warning::
-
- Only literal strings can be marked for exports, not expressions or
- variables. For situations where strings are formatted, this means the
- format string must be marked, not the formatted string
-
-The lazy version of `_` and `_t` is :func:`odoo._lt` in python and
-:js:func:`odoo.web._lt` in javascript. The translation lookup is executed only
-at rendering and can be used to declare translatable properties in class methods
-of global variables.
-
-Variables
-^^^^^^^^^
-**Don't** the extract may work but it will not translate the text correctly::
-
- _("Scheduled meeting with %s" % invitee.name)
-
-**Do** set the dynamic variables as a parameter of the translation lookup (this
-will fallback on source in case of missing placeholder in the translation)::
-
- _("Scheduled meeting with %s", invitee.name)
-
-
-Blocks
-^^^^^^
-**Don't** split your translation in several blocks or multiples lines::
-
- # bad, trailing spaces, blocks out of context
- _("You have ") + len(invoices) + _(" invoices waiting")
- _t("You have ") + invoices.length + _t(" invoices waiting");
-
- # bad, multiple small translations
- _("Reference of the document that generated ") + \
- _("this sales order request.")
-
-**Do** keep in one block, giving the full context to translators::
-
- # good, allow to change position of the number in the translation
- _("You have %s invoices wainting") % len(invoices)
- _.str.sprintf(_t("You have %s invoices wainting"), invoices.length);
-
- # good, full sentence is understandable
- _("Reference of the document that generated " + \
- "this sales order request.")
-
-Plural
-^^^^^^
-**Don't** pluralize terms the English-way::
-
- msg = _("You have %(count)s invoice", count=invoice_count)
- if invoice_count > 1:
- msg += _("s")
-
-**Do** keep in mind every language has different plural forms::
-
- if invoice_count > 1:
- msg = _("You have %(count)s invoices", count=invoice_count)
- else:
- msg = _("You have one invoice")
-
-Read vs Run Time
-^^^^^^^^^^^^^^^^
-
-**Don't** invoke translation lookup at server launch::
-
- ERROR_MESSAGE = {
- # bad, evaluated at server launch with no user language
- 'access_error': _('Access Error'),
- 'missing_error': _('Missing Record'),
- }
-
- class Record(models.Model):
-
- def _raise_error(self, code):
- raise UserError(ERROR_MESSAGE[code])
-
-**Don't** invoke translation lookup when the javascript file is read::
-
- # bad, js _t is evaluated too early
- var core = require('web.core');
- var _t = core._t;
- var map_title = {
- access_error: _t('Access Error'),
- missing_error: _t('Missing Record'),
- };
-
-
-**Do** use lazy translation lookup method::
-
- ERROR_MESSAGE = {
- 'access_error': _lt('Access Error'),
- 'missing_error': _lt('Missing Record'),
- }
-
- class Record(models.Model):
-
- def _raise_error(self, code):
- # translation lookup executed at error rendering
- raise UserError(ERROR_MESSAGE[code])
-
-
-or **do** evaluate dynamically the translatable content::
-
- # good, evaluated at run time
- def _get_error_message(self):
- return {
- access_error: _('Access Error'),
- missing_error: _('Missing Record'),
- }
-
-**Do** in the case where the translation lookup is done when the JS file is
-*read*, use `_lt` instead of `_t` to translate the term when it is *used*::
-
- # good, js _lt is evaluated lazily
- var core = require('web.core');
- var _lt = core._lt;
- var map_title = {
- access_error: _lt('Access Error'),
- missing_error: _lt('Missing Record'),
- };
-
-
-.. _PO File: https://en.wikipedia.org/wiki/Gettext#Translating
-.. _msginit: https://www.gnu.org/software/gettext/manual/gettext.html#Creating
-.. _POEdit: https://poedit.net/
diff --git a/doc/reference/translations/po-export.png b/doc/reference/translations/po-export.png
deleted file mode 100644
index 91574b40ee8..00000000000
--- a/doc/reference/translations/po-export.png
+++ /dev/null
Binary files differ
diff --git a/doc/reference/views.rst b/doc/reference/views.rst
deleted file mode 100644
index a8ad929c6a2..00000000000
--- a/doc/reference/views.rst
+++ /dev/null
@@ -1,2139 +0,0 @@
-:banner: banners/views.jpg
-
-.. _reference/views:
-
-=====
-Views
-=====
-
-Views are what define how records should be displayed to end-users. They are specified in XML which
-means that they can be edited independently from the models that they represent. They are flexible
-and allow a high level of customization of the screens that they control. There exist various types
-of views. Each of them represents a mode of visualization: *form*, *list*, *kanban*, etc.
-
-.. todo:: Build doc of ir_ui_view.py ?
-
-.. _reference/views/structure:
-
-Generic structure
-=================
-
-Basic views generally share the common structure defined below. Placeholders are denoted in all
-caps.
-
-.. code-block:: xml
-
- <record id="MODEL_view_TYPE" model="ir.ui.view">
- <field name="name">NAME</field>
- <field name="model">MODEL</field>
- <field name="arch" type="xml">
- <VIEW_TYPE>
- <VIEW_SPECIFICATIONS/>
- </VIEW_TYPE>
- </field>
- </record>
-
-Fields
-======
-
-View objects expose a number of fields. They are optional unless specified otherwise.
-
-* ``name`` (mandatory) :class:`~odoo.fields.Char`
-
- Only useful as a mnemonic/description of the view when looking for one in a list of some sort.
-
-* ``model`` :class:`~odoo.fields.Char`
-
- The model linked to the view, if applicable.
-
-* ``priority`` :class:`~odoo.fields.Integer`
-
- When a view is requested by ``(model, type)``, the view matching the model and
- the type, with the lowest priority will be returned (it is the default view).
-
- It also defines the order of views application during :ref:`view
- inheritance <reference/views/inheritance>`.
-
-* ``groups_id`` :class:`~odoo.fields.Many2many` -> :class:`odoo.addons.base.models.res_users.Groups`
-
- The groups allowed to use/access the current view.
-
- If the view extends an existing view, the extension will only be applied
- for a given user if the user has access to the provided ``groups_id``.
-
-* ``arch`` :class:`~odoo.fields.Text`
-
- The description of the view layout.
-
-Attributes
-==========
-
-.. todo::
-
- view attributes & view element attributes
- attrs & states attributes are missing generic information
-
-The different view types have a wide variety of attributes allowing customizations of
-the generic behaviors. Some main attributes will be explained here. They do not all have
-an impact on all view types.
-
-.. note:: The current context and user access rights may also impact the view abilities.
-
-.. todo:: info on create/... in the context ?
-
-* ``create``
-
- Disable/enable record creation on the view.
-
-* ``edit`` (``form`` & ``list`` & ``gantt``)
-
- Disable/enable record editing on the view.
-
-* ``delete`` (``form`` & ``list``)
-
- Disable/enable record deletion on the view through the **Action** dropdown.
-
-* ``duplicate`` (``form`` & ``list``)
-
- Disable/enable record duplication on the view through the **Action** dropdown.
-
-* ``decoration-{$name}`` (``list`` & ``gantt``)
-
- Define a conditional display of a record in the style of a row's text based on the corresponding
- record's attributes.
-
- Values are Python expressions. For each record, the expression is evaluated
- with the record's attributes as context values and if ``true``, the
- corresponding style is applied to the row. Here are some of the other values
- available in the context:
-
- * ``uid``: the id of the current user,
- * ``today``: the current local date as a string of the form ``YYYY-MM-DD``,
- * ``now``: same as ``today`` with the addition of the current time.
- This value is formatted as ``YYYY-MM-DD hh:mm:ss``.
-
- .. code-block:: xml
-
- <tree decoration-info="state == 'draft'"
- decoration-danger="state == 'help_needed'"
- decoration-bf="state='busy'">
- <TREE_VIEW_CONTENT>
- </tree>
-
- .. warning::
- Supported values differ for the two view types. The Gantt view only supports ``success``,
- ``info``, ``warning``, ``danger`` and ``secondary`` displays. The list view supports ``bf``,
- ``it``, ``success``, ``info``, ``warning``, ``danger``, ``muted`` and ``primary`` displays.
-
-* ``sample`` (``kanban`` & ``list`` & ``gantt`` & ``graph`` & ``pivot`` & ``cohort`` & ``dashboard``)
-
- Populate the view with a set of sample records if none are found for the current model.
- This attribute is false by default.
-
- These fake records will have heuristics for certain field names/models. For example,
- a field 'display_name' on the model 'res.users' will be populated with sample people names
- while an 'email' field will be in the form 'firstname.lastname@sample.demo'.
-
- The user will not be able to interact with these data and they will be discarded as soon as
- an action is performed (record created, column added, etc.)
-
-* ``banner_route``
- a route address to be fetched and prepended to the view.
-
- If this attribute is set, the
- :ref:`controller route url<reference/controllers>` will be fetched and
- displayed above the view. The json response from the controller should
- contain an "html" key.
-
- If the html contains a stylesheet <link> tag, it will be
- removed and appended to <head>.
-
- To interact with the backend you can use <a type="action"> tags. Please take
- a look at the documentation of the _onActionClicked method of
- AbstractController (*addons/web/static/src/js/views/abstract_controller.js*)
- for more details.
-
- Only views extending AbstractView and AbstractController can use this
- attribute, like :ref:`reference/views/form`, :ref:`reference/views/kanban`,
- :ref:`reference/views/list`, ...
-
- Example:
-
- .. code-block:: xml
-
- <tree banner_route="/module_name/hello" />
-
- .. code-block:: python
-
- class MyController(odoo.http.Controller):
- @http.route('/module_name/hello', auth='user', type='json')
- def hello(self):
- return {
- 'html': """
- <div>
- <link href="/module_name/static/src/css/banner.css"
- rel="stylesheet">
- <h1>hello, world</h1>
- </div> """
- }
-
-.. todo:: Views main content section, with field, group & separator ?
-
-.. _reference/views/inheritance:
-
-Inheritance
-===========
-
-Inheritance fields
-------------------
-
-The two following :class:`~odoo.addons.base.ir_ui_view.View` fields are used to specify
-inherited views.
-
-* ``inherit_id`` :class:`~odoo.fields.Many2one`
-
- the current view's parent view, unset by default. Specify the parent using
- the `ref` attribute:
-
- .. code-block:: xml
-
- <field name="inherit_id" ref="library.view_book_form"/>
-
-* ``mode`` :class:`~odoo.fields.Selection`: `extension / primary`
-
- inheritance mode, ``extension`` by default if ``inherit_id`` is set,
- ``primary`` otherwise.
-
- An example of where you would want to override ``mode`` while using
- ``inherit_id`` is delegation inheritance.
- In that case your derived model will be separate from its parent and views
- matching with one won't match with the other. Suppose you inherit from a view
- associated with the parent model and want to customize the derived view to
- show data from the derived model. The ``mode`` of the derived view needs to
- be set to ``primary``, because it's the base (and maybe only) view for that
- derived model. Otherwise the :ref:`view matching <reference/views/inheritance/view-matching>`
- rules won't apply.
-
-.. _reference/views/inheritance/view-matching:
-
-View matching
--------------
-
-* if a view is requested by ``(model, type)``, the view with the right model
- and type, ``mode=primary`` and the lowest priority is matched.
-* when a view is requested by ``id``, if its mode is not ``primary`` its
- *closest* parent with mode ``primary`` is matched.
-
-View resolution
----------------
-
-Resolution generates the final ``arch`` for a requested/matched ``primary``
-view:
-
-#. if the view has a parent, the parent is fully resolved then the current
- view's inheritance specs are applied
-#. if the view has no parent, its ``arch`` is used as-is
-#. the current view's children with mode ``extension`` are looked up and their
- inheritance specs are applied depth-first (a child view is applied, then
- its children, then its siblings)
-
-The result of applying children views yields the final ``arch``
-
-.. todo:: NOTE on fields_view_get and link to ORM ?
-
-Inheritance specs
------------------
-
-Inheritance specs are comprised of an element locator, to match
-the inherited element in the parent view, and children element that
-will be used to modify the inherited element.
-
-There are three types of element locators for matching a target element:
-
-* An ``xpath`` element with an ``expr`` attribute. ``expr`` is an XPath_
- expression\ [#hasclass]_ applied to the current ``arch``, the first node
- it finds is the match
-* a ``field`` element with a ``name`` attribute, matches the first ``field``
- with the same ``name``. All other attributes are ignored during matching
-* any other element: the first element with the same name and identical
- attributes (ignoring ``position`` and ``version`` attributes) is matched
-
-.. code-block:: xml
-
- <xpath expr="page[@name='pg']/group[@name='gp']/field" position="inside">
- <field name="description"/>
- </xpath>
-
- <field name="res_id" position="after"/>
-
- <div name="name" position="replace">
- <div name="name2">
- <field name="name2"/>
- </div>
- </div>
-
-The inheritance spec may have an optional ``position`` attribute specifying
-how the matched node should be altered:
-
-``inside`` (default)
- the content of the inheritance spec is appended to the matched node
-``replace``
- the content of the inheritance spec replaces the matched node.
- Any text node containing only ``$0`` within the contents of the spec will
- be replaced by a complete copy of the matched node, effectively wrapping
- the matched node.
-``after``
- the content of the inheritance spec is added to the matched node's
- parent, after the matched node
-``before``
- the content of the inheritance spec is added to the matched node's
- parent, before the matched node
-``attributes``
- the content of the inheritance spec should be ``attribute`` elements
- with a ``name`` attribute and an optional body:
-
- * if the ``attribute`` element has a body, a new attributed named
- after its ``name`` is created on the matched node with the
- ``attribute`` element's text as value
- * if the ``attribute`` element has no body, the attribute named after
- its ``name`` is removed from the matched node. If no such attribute
- exists, an error is raised
-
- .. code-block:: xml
-
- <field name="sale_information" position="attributes">
- <attribute name="invisible">0</attribute>
- <attribute name="attrs">
- {'invisible': [('sale_ok', '=', False)], 'readonly': [('editable', '=', False)]}
- </attribute>
- </field>
-
-``move``
- can be used as a direct child of a inheritance spec
- with a ``inside``, ``replace``, ``after`` or ``before`` ``position`` attribute
- to move a node.
-
- .. code-block:: xml
-
- <xpath expr="//@target" position="after">
- <xpath expr="//@node" position="move"/>
- </xpath>
-
- <field name="target_field" position="after">
- <field name="my_field" position="move"/>
- </field>
-
-
-A view's specs are applied sequentially.
-
-.. [#hasclass] an extension function is added for simpler matching in QWeb
- views: ``hasclass(*classes)`` matches if the context node has
- all the specified classes
-
-.. _reference/views/types:
-
-View types
-==========
-
-.. _reference/views/activity:
-
-Activity
---------
-
-The Activity view is used to display the activities linked to the records. The
-data are displayed in a chart with the records forming the rows and the activity
-types the columns. The first cell of each row displays a (customizable, see
-``templates``, quite similarly to :ref:`reference/views/kanban`) card representing
-the corresponding record. When clicking on others cells, a detailed description
-of all activities of the same type for the record is displayed.
-
-.. warning::
-
- The Activity view is only available when the ``mail`` module is installed,
- and for the models that inherit from the ``mail.activity.mixin``.
-
-The root element of the Activity view is ``<activity>``, it accepts the following
-attributes:
-
-- ``string`` (mandatory)
- A title, which should describe the view
-
-Possible children of the view element are:
-
-``field``
- declares fields to use in activity *logic*. If the field is simply displayed
- in the activity view, it does not need to be pre-declared.
-
- Possible attributes are:
-
- ``name`` (required)
- the name of the field to fetch
-
-``templates``
- defines the :ref:`reference/qweb` templates. Cards definition may be
- split into multiple templates for clarity, but activity views *must* define at
- least one root template ``activity-box``, which will be rendered once for each
- record.
-
- The activity view uses mostly-standard :ref:`javascript qweb
- <reference/qweb/javascript>` and provides the following context variables
- (see :ref:`reference/views/kanban` for more details):
-
- ``widget``
- the current :js:class:`ActivityRecord`, can be used to fetch some
- meta-information. These methods are also available directly in the
- template context and don't need to be accessed via ``widget``
- ``record``
- an object with all the requested fields as its attributes. Each field has
- two attributes ``value`` and ``raw_value``
-
-.. _reference/views/calendar:
-
-Calendar
---------
-
-Calendar views display records as events in a daily, weekly, monthly or yearly
-calendar. Their root element is ``<calendar>``. Available attributes on the
-calendar view are:
-
-``date_start`` (required)
- name of the record's field holding the start date for the event
-``date_stop``
- name of the record's field holding the end date for the event, if
- ``date_stop`` is provided records become movable (via drag and drop)
- directly in the calendar
-``date_delay``
- alternative to ``date_stop``, provides the duration of the event instead of
- its end date (unit: day)
-``color``
- name of a record field to use for *color segmentation*. Records in the
- same color segment are allocated the same highlight color in the calendar,
- colors are allocated semi-randomly.
- Displayed the display_name/avatar of the visible record in the sidebar
-``form_view_id``
- view to open when the user create or edit an event. Note that if this attribute
- is not set, the calendar view will fall back to the id of the form view in the
- current action, if any.
-``event_open_popup``
- If the option 'event_open_popup' is set to true, then the calendar view will
- open events (or records) in a FormViewDialog. Otherwise, it will open events
- in a new form view (with a do_action)
-``quick_add``
- enables quick-event creation on click: only asks the user for a ``name``
- and tries to create a new event with just that and the clicked event
- time. Falls back to a full form dialog if the quick creation fails
-``all_day``
- name of a boolean field on the record indicating whether the corresponding
- event is flagged as day-long (and duration is irrelevant)
-``mode``
- Default display mode when loading the calendar.
- Possible attributes are: ``day``, ``week``, ``month``, ``year``
-``scales``
- Comma-separated list of scales to provide. By default, all scales are
- available. See mode for possible scale values.
-
-``<field>``
- declares fields to aggregate or to use in kanban *logic*. If the field is
- simply displayed in the calendar cards.
-
- Fields can have additional attributes:
-
- * ``invisible``
- use "True" to hide the value in the cards
- * ``avatar_field``
- only for x2many field, to display the avatar instead of the display_name
- in the cards
- * ``write_model`` and ``write_field``
-
- you can add a filter and save the result in the defined model, the
- filter is added in the sidebar
- * ``filter`` and ``color``
-
- use "True" to add this field in filter in the sidebar. You can specify
- a ``color`` field used to colorize the checkbox.
-
-.. _reference/views/cohort:
-
-Cohort
-------
-
-.. raw:: html
-
- <span class="badge" style="background-color:#AD5E99">Enterprise feature</span>
-
-The cohort view is used to display and understand the way some data changes over
-a period of time. For example, imagine that for a given business, clients can
-subscribe to some service. The cohort view can then display the total number
-of subscriptions each month, and study the rate at which client leave the service
-(churn). When clicking on a cell, the cohort view will redirect you to a new action
-in which you will only see the records contained in the cell's time interval;
-this action contains a list view and a form view.
-
-.. note:: By default the cohort view will use the same list and form views as those
- defined on the action. You can pass a list view and a form view
- to the context of the action in order to set/override the views that will be
- used (the context keys to use being `form_view_id` and `list_view_id`)
-
-For example, here is a very simple cohort view:
-
-.. code-block:: xml
-
- <cohort string="Subscription" date_start="date_start" date_stop="date" interval="month"/>
-
-The root element of the Cohort view is <cohort>, it accepts the following
-attributes:
-
-
-- ``string`` (mandatory)
- A title, which should describe the view
-
-- ``date_start`` (mandatory)
- A valid date or datetime field. This field is understood by the view as the
- beginning date of a record
-
-- ``date_stop`` (mandatory)
- A valid date or datetime field. This field is understood by the view as the
- end date of a record. This is the field that will determine the churn.
-
-- ``mode`` (optional)
- A string to describe the mode. It should be either 'churn' or
- 'retention' (default). Churn mode will start at 0% and accumulate over time
- whereas retention will start at 100% and decrease over time.
-
-- ``timeline`` (optional)
- A string to describe the timeline. It should be either 'backward' or 'forward' (default).
- Forward timeline will display data from date_start to date_stop, whereas backward timeline
- will display data from date_stop to date_start (when the date_start is in future / greater
- than date_stop).
-
-- ``interval`` (optional)
- A string to describe a time interval. It should be 'day', 'week', 'month''
- (default) or 'year'.
-
-- ``measure`` (optional)
- A field that can be aggregated. This field will be used to compute the values
- for each cell. If not set, the cohort view will count the number of occurrences.
-
-``<field>`` (optional)
- allows to specify a particular field in order to manage it from the available measures, it's
- main use is for hiding a field from the selectable measures:
-
-- ``name`` (required)
- the name of the field to use in the view.
-- ``string`` (optional)
- the name that would be used to display the field in the cohort view, overrides the
- default python String attribute of the field.
-- ``invisible`` (optional)
- if true, the field will not appear either in the active measures nor in the selectable
- measures (useful for fields that do not make sense aggregated, such as fields in different
- units, e.g. € and $).
-
-.. _reference/views/dashboard:
-
-Dashboard
----------
-
-.. raw:: html
-
- <span class="badge" style="background-color:#AD5E99">Enterprise feature</span>
-
-Like pivot and graph view, The dashboard view is used to display aggregate data.
-However, the dashboard can embed sub views, which makes it possible to have a
-more complete and interesting look on a given dataset.
-
-The dashboard view can display sub views, aggregates for some fields (over a
-domain), or even *formulas* (expressions which involves one or more aggregates).
-For example, here is a very simple dashboard:
-
-.. code-block:: xml
-
- <dashboard>
- <view type="graph" ref="sale_report.view_order_product_graph"/>
- <group string="Sale">
- <aggregate name="price_total" field="price_total" widget="monetary"/>
- <aggregate name="order_id" field="order_id" string="Orders"/>
- <formula name="price_average" string="Price Average"
- value="record.price_total / record.order_id" widget="percentage"/>
- </group>
- <view type="pivot" ref="sale_report.view_order_product_pivot"/>
- </dashboard>
-
-The root element of the Dashboard view is <dashboard>, it does not accept any
-attributes.
-
-There are 5 possible type of tags in a dashboard view:
-
-``view``
- declares a sub view.
-
- Admissible attributes are:
-
- - ``type`` (mandatory)
- The type of the sub view. For example, *graph* or *pivot*.
-
- - ``ref`` (optional)
- An xml id for a view. If not given, the default view for the model will
- be used.
-
- - ``name`` (optional)
- A string which identifies this element. It is mostly
- useful to be used as a target for an xpath.
-
-``group``
- defines a column layout. This is actually very similar to the group element
- in a form view.
-
- Admissible attributes are:
-
- - ``string`` (optional)
- A description which will be displayed as a group title.
-
- - ``colspan`` (optional)
- The number of subcolumns in this group tag. By default, 6.
-
- - ``col`` (optional)
- The number of columns spanned by this group tag (only makes sense inside
- another group). By default, 6.
-
-
-``aggregate``
- declares an aggregate. This is the value of an aggregate for a given field
- over the current domain.
-
- Note that aggregates are supposed to be used inside a group tag (otherwise
- the style will not be properly applied).
-
- Admissible attributes are:
-
- - ``field`` (mandatory)
- The field name to use for computing the aggregate. Possible field types
- are:
-
- - ``integer`` (default group operator is sum)
- - ``float`` (default group operator is sum)
- - ``many2one`` (default group operator is count distinct)
-
- - ``name`` (mandatory)
- A string to identify this aggregate (useful for formulas)
-
- - ``string`` (optional)
- A short description that will be displayed above the value. If not
- given, it will fall back to the field string.
-
- - ``domain`` (optional)
- An additional restriction on the set of records that we want to aggregate.
- This domain will be combined with the current domain.
-
- - ``domain_label`` (optional)
- When the user clicks on an aggregate with a domain, it will be added to
- the search view as a facet. The string displayed for this facet can
- be customized with this attribute.
-
- - ``group_operator`` (optional)
- A valid postgreSQL aggregate function identifier to use when aggregating
- values (see https://www.postgresql.org/docs/9.5/static/functions-aggregate.html).
- If not provided, By default, the group_operator from the field definition is used.
- Note that no aggregation of field values is achieved if the group_operator value is "".
-
- .. note:: The special aggregate function ``count_distinct`` (defined in odoo) can also be used here
-
- .. code-block:: xml
-
- <aggregate name="price_total_max" field="price_total" group_operator="max"/>
-
-
-
- - ``col`` (optional)
- The number of columns spanned by this tag (only makes sense inside a
- group). By default, 1.
-
- - ``widget`` (optional)
- A widget to format the value (like the widget attribute for fields).
- For example, monetary.
-
- - ``help`` (optional)
- A help message to dipslay in a tooltip (equivalent of help for a field in python)
-
- - ``measure`` (optional)
- This attribute is the name of a field describing the measure that has to be used
- in the graph and pivot views when clicking on the aggregate.
- The special value __count__ can be used to use the count measure.
-
- .. code-block:: xml
-
- <aggregate name="total_ojects" string="Total Objects" field="id" group_operator="count" measure="__count__"/>
-
- - ``clickable`` (optional)
- A boolean indicating if this aggregate should be clickable or not (default to true).
- Clicking on a clickable aggregate will change the measures used by the subviews
- and add the value of the domain attribute (if any) to the search view.
-
- - ``value_label`` (optional)
- A string put on the right of the aggregate value.
- For example, it can be useful to indicate the unit of measure
- of the aggregate value.
-
-``formula``
- declares a derived value. Formulas are values computed from aggregates.
-
- Note that like aggregates, formulas are supposed to be used inside a group
- tag (otherwise the style will not be properly applied).
-
- Admissible attributes are:
-
- - ``value`` (mandatory)
- A string expression that will be evaluated, with the builtin python
- evaluator (in the web client). Every aggregate can be used in the
- context, in the ``record`` variable. For example,
- ``record.price_total / record.order_id``.
-
- - ``name`` (optional)
- A string to identify this formula
-
- - ``string`` (optional)
- A short description that will be displayed above the formula.
-
- - ``col`` (optional)
- The number of columns spanned by this tag (only makes sense inside a
- group). By default, 1.
-
- - ``widget`` (optional)
- A widget to format the value (like the widget attribute for fields).
- For example, monetary. By default, it is 'float'.
-
- - ``help`` (optional)
- A help message to dipslay in a tooltip (equivalent of help for a field in python)
-
- - ``value_label`` (optional)
- A string put on the right of the formula value.
- For example, it can be useful to indicate the unit of measure
- of the formula value.
-
-``widget``
- Declares a specialized widget to be used to display the information. This is
- a mechanism similar to the widgets in the form view.
-
- Admissible attributes are:
-
- - ``name`` (mandatory)
- A string to identify which widget should be instantiated. The view will
- look into the ``widget_registry`` to get the proper class.
-
- - ``col`` (optional)
- The number of columns spanned by this tag (only makes sense inside a
- group). By default, 1.
-
-.. _reference/views/form:
-
-Form
-----
-
-Form views are used to display the data from a single record. Their root
-element is ``<form>``. They are composed of regular HTML_ with additional
-structural and semantic components.
-
-Structural components
-~~~~~~~~~~~~~~~~~~~~~
-
-Structural components provide structure or "visual" features with little
-logic. They are used as elements or sets of elements in form views.
-
-``notebook``
- defines a tabbed section. Each tab is defined through a ``page`` child
- element. Pages can have the following attributes:
-
- * ``string`` (required)
- the title of the tab
- * ``accesskey``
- an HTML accesskey_
- * ``attrs``
- standard dynamic attributes based on record values
-
- .. note:: Note that ``notebook`` should not be placed within ``group``
-
-``group``
- used to define column layouts in forms. By default, groups define 2 columns
- and most direct children of groups take a single column. ``field`` direct
- children of groups display a label by default, and the label and the field
- itself have a colspan of 1 each.
-
- The number of columns in a ``group`` can be customized using the ``col``
- attribute, the number of columns taken by an element can be customized using
- ``colspan``.
-
- Children are laid out horizontally (tries to fill the next column before
- changing row).
-
- Groups can have a ``string`` attribute, which is displayed as the group's
- title
-``newline``
- only useful within ``group`` elements, ends the current row early and
- immediately switches to a new row (without filling any remaining column
- beforehand)
-``separator``
- small horizontal spacing, with a ``string`` attribute behaves as a section
- title
-``sheet``
- can be used as a direct child to ``form`` for a narrower and more responsive
- form layout
-``header``
- combined with ``sheet``, provides a full-width location above the sheet
- itself, generally used to display workflow buttons and status widgets
-
-Semantic components
-~~~~~~~~~~~~~~~~~~~
-
-Semantic components tie into and allow interaction with the Odoo
-system. Available semantic components are:
-
-``button``
- call into the Odoo system, similar to :ref:`list view buttons
- <reference/views/list/button>`. In addition, the following attribute can be
- specified:
-
- ``special``
- for form views opened in dialogs: ``save`` to save the record and close the
- dialog, ``cancel`` to close the dialog without saving.
- ``confirm``
- confirmation message to display (and for the user to accept) before
- performing the button's Odoo call (also works in Kanban views).
-
-``field``
- renders (and allow editing of, possibly) a single field of the current
- record. Using several times a field in a form view is supported and the fields
- can receive different values for modifiers 'invisible' and 'readonly'. However,
- the behavior is not guaranteed when several fields exist with different values
- for modifier 'required'. Possible attributes of the field node are:
-
- ``name`` (mandatory)
- the name of the field to render
- ``id``
- the node id. Useful when there are several occurrences of the same field in
- the view (see ``label`` component below). Default is the field name.
- ``widget``
- fields have a default rendering based on their type
- (e.g. :class:`~odoo.fields.Char`,
- :class:`~odoo.fields.Many2one`). The ``widget`` attributes allows using
- a different rendering method and context.
-
- .. todo:: list of widgets
-
- & options & specific attributes (e.g. widget=statusbar
- statusbar_visible clickable)
- ``options``
- JSON object specifying configuration option for the field's widget
- (including default widgets)
- ``class``
- HTML class to set on the generated element, common field classes are:
-
- ``oe_inline``
- prevent the usual line break following fields
- ``oe_left``, ``oe_right``
- floats_ the field to the corresponding direction
- ``oe_read_only``, ``oe_edit_only``
- only displays the field in the corresponding form mode
- ``oe_avatar``
- for image fields, displays images as "avatar" (square, 90x90 maximum
- size, some image decorations)
- ``groups``
- only displays the field for specific users
- ``on_change``
- calls the specified method when this field's value is edited, can generate
- update other fields or display warnings for the user
-
- .. deprecated:: 8.0
-
- Use :func:`odoo.api.onchange` on the model
-
- ``attrs``
- dynamic meta-parameters based on record values
- ``domain``
- for relational fields only, filters to apply when displaying existing
- records for selection
- ``context``
- for relational fields only, context to pass when fetching possible values
- ``readonly``
- display the field in both readonly and edit mode, but never make it
- editable
- ``required``
- generates an error and prevents saving the record if the field doesn't
- have a value
- ``nolabel``
- don't automatically display the field's label, only makes sense if the
- field is a direct child of a ``group`` element
- ``placeholder``
- help message to display in *empty* fields. Can replace field labels in
- complex forms. *Should not* be an example of data as users are liable to
- confuse placeholder text with filled fields
- ``mode``
- for :class:`~odoo.fields.One2many`, display mode (view type) to use for
- the field's linked records. One of ``tree``, ``form``, ``kanban`` or
- ``graph``. The default is ``tree`` (a list display)
- ``help``
- tooltip displayed for users when hovering the field or its label
- ``filename``
- for binary fields, name of the related field providing the name of the
- file
- ``password``
- indicates that a :class:`~odoo.fields.Char` field stores a password and
- that its data shouldn't be displayed
- ``kanban_view_ref``
- for opening specific kanban view when selecting records from m2o/m2m in mobile
- environment
-
-``label``
- when a ``field`` component isn't placed directly inside a ``group``, or when
- its ``nolabel`` attribute is set, the field's label isn't automatically
- displayed alongside its value. The ``label`` component is the manual alternative
- of displaying the label of a field. Possible attributes are:
-
- ``for`` (mandatory)
- the reference to the field associated with the label. Can be either the name
- of a field, or its id (``id`` attribute set on the ``field``). When there are
- several occurrences of the same field in the view, and there are several
- ``label`` components associated with these ``field`` nodes, those labels
- must have unique ``for`` attributes (in this case referencing the ``id``
- attribute of the corresponding ``field`` nodes).
- ``string``
- the label to display. Display the field's label (coming from the field
- definition in the model) by default.
- ``class``
- same as for ``field`` component.
- ``attrs``
- same as for ``field`` component.
-
-
-Generic structure
-~~~~~~~~~~~~~~~~~
-
-.. code-block:: xml
-
- <form>
- <header>
- <field name="state" widget="statusbar"/>
- </header>
- <sheet>
- <div class="oe_button_box">
- <BUTTONS/>
- </div>
- <group>
- <group>
- <field name="fname"/>
- </group>
- </group>
- <notebook>
- <page string="Page1">
- <group>
- <CONTENT/>
- </group>
- </page>
- <page string="Page2">
- <group>
- <CONTENT/>
- </group>
- </page>
- </notebook>
- </sheet>
- </form>
-
-.. todo:: classes for forms
-
-.. todo:: widgets?
-
-.. _reference/views/gantt:
-
-Gantt
------
-
-.. raw:: html
-
- <span class="badge" style="background-color:#AD5E99">Enterprise feature</span>
-
-Gantt views appropriately display Gantt charts (for scheduling).
-
-The root element of gantt views is ``<gantt/>``, it has no children but can
-take the following attributes:
-
-``date_start`` (required)
- name of the field providing the start datetime of the event for each
- record.
-``date_stop`` (required)
- name of the field providing the end duration of the event for each
- record.
-``color``
- name of the field used to color the pills according to its value
-``decoration-{$name}``
- allow changing the style of a row's text based on the corresponding
- record's attributes.
-
- Values are Python expressions. For each record, the expression is evaluated
- with the record's attributes as context values and if ``true``, the
- corresponding style is applied to the row. Here are some of the other values
- available in the context:
-
- * ``uid``: the id of the current user,
- * ``today``: the current local date as a string of the form ``YYYY-MM-DD``,
- * ``now``: same as ``today`` with the addition of the current time.
- This value is formatted as ``YYYY-MM-DD hh:mm:ss``.
-
- ``{$name}`` can be one of the following `bootstrap contextual color`_ (``danger``,
- ``info``, ``secondary``, ``success`` or ``warning``).
-``default_group_by``
- name of a field to group tasks by
-``consolidation``
- field name to display consolidation value in record cell
-``consolidation_max``
- dictionnary with the "group by" field as key and the maximum consolidation
- value that can be reached before displaying the cell in red
- (e.g. ``{"user_id": 100}``)
-``consolidation_exclude``
- name of the field that describes if the task has to be excluded
- from the consolidation
- if set to true it displays a striped zone in the consolidation line
-``create``, ``cell_create``, ``edit``, ``delete``, ``plan``
- allows *dis*\ abling the corresponding action in the view by setting the
- corresponding attribute to ``false`` (default: ``true``).
-
- * ``create``: If enabled, an ``Add`` button will be available in the control
- panel to create records.
- * ``cell_create``: If enabled and ``create`` enabled, a "**+**" button will be
- displayed while hovering on a time slot cell to create a new record on that slot.
- * ``edit``: If enabled, the opened records will be in edit mode (thus editable).
- * ``plan``: If enabled and ``edit`` enabled, a "magnifying glass" button will be displayed
- on time slots to plan unassigned records into that time slot.
-
- .. admonition:: Example
-
- When you do not want to create records on the gantt view and the beginning and end
- dates are required on the model, the planning feature should be disabled
- because no record will ever be found.
-``offset``
- Depending on the scale, the number of units to add to today to compute the
- default period. Examples: An offset of +1 in default_scale week will open the
- gantt view for next week, and an offset of -2 in default_scale month will open
- the gantt view of 2 months ago.
-``progress``
- name of a field providing the completion percentage for the record's event,
- between 0 and 100
-``string``
- title of the gantt view
-``precision``
- JSON object specifying snapping precisions for the pills in each scale.
-
- * Possible values for scale ``day`` are (default: ``hour``):
-
- ``hour``: records times snap to full hours (ex: 7:12 becomes 8:00)
-
- ``hour:half``: records times snap to half hours (ex: 7:12 becomes 7:30)
-
- ``hour:quarter``: records times snap to half hours (ex: 7:12 becomes 7:15)
-
- * Possible values for scale ``week`` are (default: ``day:half``):
-
- ``day``: records times snap to full days (ex: 7:28 AM becomes 11:59:59 PM of the previous day, 10:32 PM becomes 12:00 PM of the current day)
-
- ``day:half``: records times snap to half hours (ex: 7:28 AM becomes 12:00 PM)
-
- * Possible values for scale ``month`` are (default: ``day:half``):
-
- ``day``: records times snap to full days (ex: 7:28 AM becomes 11:59:59 PM of the previous day, 10:32 PM becomes 12:00 PM of the current day)
-
- ``day:half``: records times snap to half hours (ex: 7:28 AM becomes 12:00 PM)
-
- * Scale ``year`` always snap to full day.
-
- Example of precision attribute: ``{"day": "hour:quarter", "week": "day:half", "month": "day"}``
-``total_row``
- boolean to control whether the row containing the total count of records should
- be displayed. (default: ``false``)
-``collapse_first_level``
- boolean to control whether it is possible to collapse each row if grouped by
- one field. (default: ``false``, the collapse starts when grouping by two fields)
-``display_unavailability``
- boolean to mark the dates returned by the ``gantt_unavailability`` function of
- the model as available inside the gantt view. Records can still be scheduled
- in them, but their unavailability is visually displayed. (default: ``false``)
-``default_scale``
- default scale when rendering the view. Possible values are (default: ``month``):
-
- * ``day``
- * ``week``
- * ``month``
- * ``year``
-
-``scales``
- comma-separated list of allowed scales for this view. By default, all scales
- are allowed. For possible scale values to use in this list, see ``default_scale``.
-
-``templates``
- defines the :ref:`reference/qweb` template ``gantt-popover`` which is used
- when the user hovers over one of the records in the gantt view.
-
- The gantt view uses mostly-standard :ref:`javascript qweb
- <reference/qweb/javascript>` and provides the following context variables:
-
- ``widget``
- the current :js:class:`GanttRow`, can be used to fetch some
- meta-information. The ``getColor`` method to convert in a color integer is
- also available directly in the template context without using ``widget``.
-
- ``on_create``
- If specified when clicking the add button on the view, instead of opening a generic dialog, launch a client action.
- this should hold the xmlid of the action (eg: ``on_create="%(my_module.my_wizard)d"``
-
-``form_view_id``
- view to open when the user create or edit a record. Note that if this attribute
- is not set, the gantt view will fall back to the id of the form view in the
- current action, if any.
-
-``dynamic_range``
- if set to true, the gantt view will start at the first record,
- instead of starting at the beginning of the year/month/day.
-
-``thumbnails``
- This allows to display a thumbnail next to groups name if the group is a relationnal field.
- This expects a python dict which keys are the name of the field on the active model.
- Values are the names of the field holding the thumbnail on the related model.
-
- Example: tasks have a field user_id that reference res.users. The res.users model has a field image that holds the avatar,
- then:
-
- .. code-block:: xml
-
- <gantt
- date_start="date_start"
- date_stop="date_stop"
- thumbnails="{'user_id': 'image_128'}"
- >
- </gantt>
-
- will display the users avatars next to their names when grouped by user_id.
-
-.. _reference/views/graph:
-
-Graph
------
-
-The graph view is used to visualize aggregations over a number of records or
-record groups. Its root element is ``<graph>`` which can take the following
-attributes:
-
-``type``
- one of ``bar`` (default), ``pie`` and ``line``, the type of graph to use
-``stacked``
- only used for ``bar`` charts. If present and set to ``True``, stacks bars
- within a group
-``disable_linking``
- set to ``True`` to prevent from redirecting clicks on graph to list view
-``order``
- if set, x-axis values will be sorted by default according their measure with
- respect to the given order (``asc`` or ``desc``). Only used for ``bar`` and
- ``pie`` charts.
-
-The only allowed element within a graph view is ``field`` which can have the
-following attributes:
-
-``name`` (required)
- the name of a field to use in the view. If used for grouping (rather
- than aggregating)
-
-``title`` (optional)
- string displayed on the top of the graph.
-
-``invisible`` (optional)
- if true, the field will not appear either in the active measures nor in the
- selectable measures.
-
-``type``
- indicates whether the field should be used as a grouping criteria or as an
- aggregated value within a group. Possible values are:
-
- ``row`` (default)
- groups by the specified field. All graph types support at least one level
- of grouping, some may support more.
- ``col``
- authorized in graph views but only used by pivot tables
- ``measure``
- field to aggregate within a group
-
-``interval``
- on date and datetime fields, groups by the specified interval (``day``,
- ``week``, ``month``, ``quarter`` or ``year``) instead of grouping on the
- specific datetime (fixed second resolution) or date (fixed day resolution).
-
-The measures are automatically generated from the model fields; only the
-aggregatable fields are used. Those measures are also alphabetically
-sorted on the string of the field.
-
-.. warning::
-
- graph view aggregations are performed on database content, non-stored
- function fields can not be used in graph views
-
-.. _reference/views/kanban:
-
-Kanban
-------
-
-The kanban view is a `kanban board`_ visualisation: it displays records as
-"cards", halfway between a :ref:`list view <reference/views/list>` and a
-non-editable :ref:`form view <reference/views/form>`. Records may be grouped
-in columns for use in workflow visualisation or manipulation (e.g. tasks or
-work-progress management), or ungrouped (used simply to visualize records).
-
-.. note:: The kanban view will load and display a maximum of ten columns.
- Any column after that will be closed (but can still be opened by
- the user).
-
-The root element of the Kanban view is ``<kanban>``, it can use the following
-attributes:
-
-``default_group_by``
- whether the kanban view should be grouped if no grouping is specified via
- the action or the current search. Should be the name of the field to group
- by when no grouping is otherwise specified
-``default_order``
- cards sorting order used if the user has not already sorted the records (via
- the list view)
-``class``
- adds HTML classes to the root HTML element of the Kanban view
-``examples``
- if set to a key in the `KanbanExamplesRegistry`_, examples on column setups will be available in the grouped kanban view. `Here <https://github.com/odoo/odoo/blob/99821fdcf89aa66ac9561a972c6823135ebf65c0/addons/project/static/src/js/project_task_kanban_examples.js#L27>`_ is an example of how to define those setups.
-``group_create``
- whether the "Add a new column" bar is visible or not. Default: true.
-``group_delete``
- whether groups can be deleted via the context menu. Default: true.
-``group_edit``
- whether groups can be edited via the context menu. Default: true.
-``archivable``
- whether records belonging to a column can be archived / restored if an
- ``active`` field is defined on the model. Default: true.
-``quick_create``
- whether it should be possible to create records without switching to the
- form view. By default, ``quick_create`` is enabled when the Kanban view is
- grouped by many2one, selection, char or boolean fields, and disabled when not.
-``quick_create_view``
- ``form`` view reference, specifying the view used for records quick creation.
-``records_draggable``
- whether it should be possible to drag records when kanban is grouped. Default: true.
-
- Set to ``true`` to always enable it, and to ``false`` to always disable it.
-
-.. todo:: VFE missing information on on_create attribute of kanban views.
-
-Possible children of the view element are:
-
-``field``
- declares fields to use in kanban *logic*. If the field is simply displayed in
- the kanban view, it does not need to be pre-declared.
-
- Possible attributes are:
-
- ``name`` (required)
- the name of the field to fetch
-
-``progressbar``
- declares a progressbar element to put on top of kanban columns.
-
- Possible attributes are:
-
- ``field`` (required)
- the name of the field whose values are used to subgroup column's records in
- the progressbar
-
- ``colors`` (required)
- JSON mapping the above field values to either "danger", "warning", "success"
- or "muted" colors
-
- ``sum_field`` (optional)
- the name of the field whose column's records' values will be summed and
- displayed next to the progressbar (if omitted, displays the total number of
- records)
-
-``templates``
- defines a list of :ref:`reference/qweb` templates. Cards definition may be
- split into multiple templates for clarity, but kanban views *must* define at
- least one root template ``kanban-box``, which will be rendered once for each
- record.
-
- The kanban view uses mostly-standard :ref:`javascript qweb
- <reference/qweb/javascript>` and provides the following context variables:
-
- ``widget``
- the current :js:class:`KanbanRecord`, can be used to fetch some
- meta-information. These methods are also available directly in the
- template context and don't need to be accessed via ``widget``
- ``record``
- an object with all the requested fields as its attributes. Each field has
- two attributes ``value`` and ``raw_value``, the former is formatted
- according to current user parameters, the latter is the direct value from
- a :meth:`~odoo.models.Model.read` (except for date and datetime fields
- that are `formatted according to user's locale
- <https://github.com/odoo/odoo/blob/a678bd4e/addons/web_kanban/static/src/js/kanban_record.js#L102>`_)
- ``context``
- the current context, coming from the action, and the one2many or many2many
- field in the case of a Kanban view embedded in a Form view
- ``user_context``
- self-explanatory
- ``read_only_mode``
- self-explanatory
- ``selection_mode``
- set to true when kanban view is opened in mobile environment from m2o/m2m field
- for selecting records.
-
- .. note:: clicking on m2o/m2m field in mobile environment opens kanban view
-
-
- .. rubric:: buttons and fields
-
- While most of the Kanban templates are standard :ref:`reference/qweb`, the
- Kanban view processes ``field``, ``button`` and ``a`` elements specially:
-
- * by default fields are replaced by their formatted value, unless the
- ``widget`` attribute is specified, in which case their rendering and
- behavior depends on the corresponding widget. Possible values are (among
- others):
-
- ``handle``
- for ``sequence`` (or ``integer``) fields by which records are
- sorted, allows to drag&drop records to reorder them.
-
- .. todo:: list widgets?
-
- * buttons and links with a ``type`` attribute become perform Odoo-related
- operations rather than their standard HTML function. Possible types are:
-
- ``action``, ``object``
- standard behavior for :ref:`Odoo buttons
- <reference/views/list/button>`, most attributes relevant to standard
- Odoo buttons can be used.
- ``open``
- opens the card's record in the form view in read-only mode
- ``edit``
- opens the card's record in the form view in editable mode
- ``delete``
- deletes the card's record and removes the card
-
- .. todo::
-
- * kanban-specific CSS
- * kanban structures/widgets (vignette, details, ...)
-
-If you need to extend the Kanban view, see :js:class::`the JS API <KanbanRecord>`.
-
-Calendar
-~~~~~~~~
-
-Calendar views display records as events in a daily, weekly or monthly
-calendar. Their root element is ``<calendar>``. Available attributes on the
-calendar view are:
-
-``date_start`` (required)
- name of the record's field holding the start date for the event
-``date_stop``
- name of the record's field holding the end date for the event, if
- ``date_stop`` is provided records become movable (via drag and drop)
- directly in the calendar
-``date_delay``
- alternative to ``date_stop``, provides the duration of the event instead of
- its end date (unit: day)
-``color``
- name of a record field to use for *color segmentation*. Records in the
- same color segment are allocated the same highlight color in the calendar,
- colors are allocated semi-randomly.
- Displayed the display_name/avatar of the visible record in the sidebar
-``form_view_id``
- view to open when the user create or edit an event. Note that if this attribute
- is not set, the calendar view will fall back to the id of the form view in the
- current action, if any.
-``event_open_popup``
- If the option 'event_open_popup' is set to true, then the calendar view will
- open events (or records) in a FormViewDialog. Otherwise, it will open events
- in a new form view (with a do_action)
-``quick_add``
- enables quick-event creation on click: only asks the user for a ``name``
- and tries to create a new event with just that and the clicked event
- time. Falls back to a full form dialog if the quick creation fails
-``all_day``
- name of a boolean field on the record indicating whether the corresponding
- event is flagged as day-long (and duration is irrelevant)
-``mode``
- Default display mode when loading the calendar.
- Possible attributes are: ``day``, ``week``, ``month``
-``create``, ``delete``
- allows *dis*\ abling the corresponding action in the view by setting the
- corresponding attribute to ``false``
-``<field>``
- declares fields to aggregate or to use in kanban *logic*. If the field is
- simply displayed in the calendar cards.
-
-.. _reference/views/list:
-
-List
-----
-
-The root element of list views is ``<tree>``\ [#treehistory]_. The list view's
-root can have the following attributes:
-
-``editable``
- by default, selecting a list view's row opens the corresponding
- :ref:`form view <reference/views/form>`. The ``editable`` attributes makes
- the list view itself editable in-place.
-
- Valid values are ``top`` and ``bottom``, making *new* records appear
- respectively at the top or bottom of the list.
-
- The architecture for the inline :ref:`form view <reference/views/form>` is
- derived from the list view. Most attributes valid on a :ref:`form view
- <reference/views/form>`'s fields and buttons are thus accepted by list
- views although they may not have any meaning if the list view is
- non-editable
-
- .. note:: if the ``edit`` attribute is set to ``false``, the ``editable`` option will be ignored.
-
-``multi_edit``
- editable or not editable list can activate the multi-editing feature by defining
- the `multi_edit=1`
-
-``default_order``
- overrides the ordering of the view, replacing the model's order (:attr:`~odoo.models.BaseModel._order` model attribute).
- The value is a comma-separated list of fields, postfixed by ``desc`` to
- sort in reverse order:
-
- .. code-block:: xml
-
- <tree default_order="sequence,name desc">
-
-``decoration-{$name}``
- allow changing the style of a row's text based on the corresponding
- record's attributes.
-
- ``{$name}`` can be ``bf`` (``font-weight: bold``), ``it``
- (``font-style: italic``), or any `bootstrap contextual color`_ (``danger``,
- ``info``, ``muted``, ``primary``, ``success`` or ``warning``).
-``create``, ``edit``, ``delete``, ``duplicate``, ``import``, ``export_xlsx``
- allows *dis*\ abling the corresponding action in the view by setting the
- corresponding attribute to ``false``
-``limit``
- the default size of a page. It must be a positive integer
-``groups_limit``
- when the list view is grouped, the default number of groups of a page. It
- must be a position integer
-``expand``
- when the list view is grouped, automatically open the first level of groups
- if set to true (default: false)
-
-Possible children elements of the list view are:
-
-.. _reference/views/list/button:
-
-``button``
- displays a button in a list cell
-
- ``icon``
- icon to use to display the button
- ``string``
- * if there is no ``icon``, the button's text
- * if there is an ``icon``, ``alt`` text for the icon
- ``type``
- type of button, indicates how it clicking it affects Odoo:
-
- ``object``
- call a method on the list's model. The button's ``name`` is the
- method, which is called with the current row's record id and the
- current context.
-
- .. web client also supports a @args, which allows providing
- additional arguments as JSON. Should that be documented? Does
- not seem to be used anywhere
-
- ``action``
- load an execute an ``ir.actions``, the button's ``name`` is the
- database id of the action. The context is expanded with the list's
- model (as ``active_model``), the current row's record
- (``active_id``) and all the records currently loaded in the list
- (``active_ids``, may be just a subset of the database records
- matching the current search)
- ``name``
- see ``type``
- ``args``
- see ``type``
- ``attrs``
- dynamic attributes based on record values.
-
- A mapping of attributes to domains, domains are evaluated in the
- context of the current row's record, if ``True`` the corresponding
- attribute is set on the cell.
-
- Possible attribute is ``invisible`` (hides the button).
- ``states``
- shorthand for ``invisible`` ``attrs``: a list of states, comma separated,
- requires that the model has a ``state`` field and that it is
- used in the view.
-
- Makes the button ``invisible`` if the record is *not* in one of the
- listed states
-
- .. danger::
-
- Using ``states`` in combination with ``attrs`` may lead to
- unexpected results as domains are combined with a logical AND.
- ``context``
- merged into the view's context when performing the button's Odoo call
-
- .. todo:: declared but unused: help
-
-``field``
- defines a column where the corresponding field should be displayed for
- each record. Can use the following attributes:
-
- ``name``
- the name of the field to display in the current model. A given name
- can only be used once per view
- ``string``
- the title of the field's column (by default, uses the ``string`` of
- the model's field)
- ``invisible``
- fetches and stores the field, but doesn't display the column in the
- table. Necessary for fields which shouldn't be displayed but are
- used by e.g. ``@colors``
- ``groups``
- lists the groups which should be able to see the field
- ``widget``
- alternate representations for a field's display. Possible list view
- values are (among others):
-
- ``progressbar``
- displays ``float`` fields as a progress bar.
- ``handle``
- for ``sequence`` (or ``integer``) fields by which records are
- sorted, instead of displaying the field's value just displays a
- drag&drop icon to reorder records.
- ``sum``, ``avg``
- displays the corresponding aggregate at the bottom of the column. The
- aggregation is only computed on *currently displayed* records. The
- aggregation operation must match the corresponding field's
- ``group_operator``
- ``attrs``
- dynamic attributes based on record values. Only effects the current
- field, so e.g. ``invisible`` will hide the field but leave the same
- field of other records visible, it will not hide the column itself
- ``width`` (for ``editable``)
- when there is no data in the list, the width of a column can be forced
- by setting this attribute. The value can be an absolute width (e.g.
- '100px'), or a relative weight (e.g. '3', meaning that this column will
- be 3 times larger than the others). Note that when there are records in
- the list, we let the browser automatically adapt the column's widths
- according to their content, and this attribute is thus ignored.
- ``decoration-{$name}``
- allow changing the style of a cell's text based on the corresponding
- record's attributes.
-
- ``{$name}`` can be ``bf`` (``font-weight: bold``), ``it``
- (``font-style: italic``), or any `bootstrap contextual color`_ (``danger``,
- ``info``, ``muted``, ``primary``, ``success`` or ``warning``).
- ``nolabel``
- if set to "1", the column header will remain empty. Also, the column
- won't be sortable.
-
- .. note::
-
- if the list view is ``editable``, any field attribute from the
- :ref:`form view <reference/views/form>` is also valid and will
- be used when setting up the inline form view.
-
- .. note::
-
- In case of list sub-views (One2many/Many2many display in a form view),
- The attribute ``column_invisible`` can be useful to hide a column
- depending on the parent object.
-
- .. code-block:: xml
-
- <field name="product_is_late" attrs="{'column_invisible': [('parent.has_late_products', '=', False)]}"/>
-
- .. note:: When a list view is grouped, numeric fields are aggregated and
- displayed for each group. Also, if there are too many records in
- a group, a pager will appear on the right of the group row. For
- this reason, it is not a good practice to have a numeric field in
- the last column, when the list view is in a situation where it can
- be grouped (it is however fine for x2manys field in a form view:
- they cannot be grouped).
-
-``groupby``
- defines custom headers (with buttons) for the current view when grouping
- records on many2one fields. It is also possible to add `field`, inside the
- `groupby` which can be used for modifiers. These fields thus belong on the
- many2one comodel. These extra fields will be fetched in batch.
-
- ``name``
- the name of a many2one field (on the current model). Custom header will be
- displayed when grouping the view on this field name.
-
- .. code-block:: xml
-
- <groupby name="partner_id">
- <field name="name"/> <!-- name of partner_id -->
- <button type="edit" name"edit" string="Edit/>
- <button type="object" name="my_method" string="Button1"
- attrs="{'invisible': [('name', '=', 'Georges')]}"/>
- </groupby>
-
- A special button (`type="edit"`) can be defined to open the many2one form view.
-
-``control``
- defines custom controls for the current view.
-
- This makes sense if the parent ``tree`` view is inside a One2many field.
-
- Does not support any attribute, but can have children:
-
- ``create``
- adds a button to create a new element on the current list.
-
- .. note:: If any ``create`` is defined, it will overwrite the default
- "add a line" button.
-
- The following attributes are supported:
-
- ``string`` (required)
- The text displayed on the button.
-
- ``context``
- This context will be merged into the existing context
- when retrieving the default value of the new record.
-
- For example it can be used to override default values.
-
-
- The following example will override the default "add a line" button
- by replacing it with 3 new buttons:
- "Add a product", "Add a section" and "Add a note".
-
- "Add a product" will set the field 'display_type' to its default value.
-
- The two other buttons will set the field 'display_type'
- to be respectively 'line_section' and 'line_note'.
-
- .. code-block:: xml
-
- <control>
- <create
- string="Add a product"
- />
- <create
- string="Add a section"
- context="{'default_display_type': 'line_section'}"
- />
- <create
- string="Add a note"
- context="{'default_display_type': 'line_note'}"
- />
- </control>
-
-.. [#treehistory] for historical reasons, it has its origin in tree-type views
- later repurposed to a more table/list-type display
-
-.. _reference/views/map:
-
-Map
----
-
-.. raw:: html
-
- <span class="badge" style="background-color:#AD5E99">Enterprise feature</span>
-
-This view is able to display records on a map and the routes between them. The record are represented by pins. It also allows the visualization of fields from the model in a popup tied to the record's pin.
-
-.. note::
-
- The model on which the view is applied should contains a res.partner many2one since the view relies on the res.partner's address and coordinates fields to localize the records.
-
-.. _reference/views/map/api:
-
-Api
-~~~
-
-The view uses location data platforms' api to fetch the tiles (the map's background), do the geoforwarding (converting addresses to a set of coordinates) and fetch the routes.
-The view implements two api, the default one, openstreet map is able to fetch `tiles`_ and do `geoforwarding`_. This api does not require a token.
-As soon as a valid `MapBox`_ token is provided in the general settings the view switches to the Mapbox api. This api is faster and allows the computation of routes. The token are available by `signing up`_ to MapBox
-
-.. _reference/views/structural components:
-
-Structural components
-~~~~~~~~~~~~~~~~~~~~~
-
-The view's root element is ``<map>`` multiple attributes are allowed
-
-``res_partner``
- Contains the res.partner many2one. If not provided the view will resort to create an empty map.
-``default_order``
- If a field is provided the view will override the model's default order. The field must be part of the model on which the view is applied not from res.partner
-``routing``
- if ``true`` the routes between the records will be shown. The view still needs a valid MapBox token and at least two located records. (i.e the records has a res.partner many2one and the partner has a address or valid coordinates)
-``hide_name``
- if ``true`` hide a name from the marker's popup (default: false)
-``hide_address``
- if ``true`` hide a address from the marker's popup (default: false)
-
-The ``<map>`` element can contain multiple ``<field>`` elements. Each ``<field>`` element will be interpreted as a line in the marker's popup. The field's attributes are the following:
-
-``name``
- The field to display.
-``string``
- This string will be displayed before the field's content. It Can be used as a description.
-``limit``
- The size of a page (default: 80). It must be a positive integer.
-
-No attribute or element is mandatory but as stated above if no res.partner many2one is provided the view won't be able to locate records.
-
-For example here is a map:
- .. code-block:: xml
-
- <map res_partner="partner_id" default_order="date_begin" routing="true" hide_name="true">
- <field name="partner_id" string="Customer Name"/>
- </map>
-
-.. _reference/views/pivot:
-
-Pivot
------
-
-The pivot view is used to visualize aggregations as a `pivot table`_. Its root
-element is ``<pivot>`` which can take the following attributes:
-
-``disable_linking``
- Set to ``True`` to remove table cell's links to list view.
-``display_quantity``
- Set to ``true`` to display the Quantity column by default.
-``default_order``
- The name of the measure and the order (asc or desc) to use as default order
- in the view.
-
- .. code-block:: xml
-
- <pivot default_order="foo asc">
- <field name="foo" type="measure"/>
- </pivot>
-
-The only allowed element within a pivot view is ``field`` which can have the
-following attributes:
-
-``name`` (required)
- the name of a field to use in the view. If used for grouping (rather
- than aggregating)
-
-``string``
- the name that will be used to display the field in the pivot view,
- overrides the default python String attribute of the field.
-
-``type``
- indicates whether the field should be used as a grouping criteria or as an
- aggregated value within a group. Possible values are:
-
- ``row`` (default)
- groups by the specified field, each group gets its own row.
- ``col``
- creates column-wise groups
- ``measure``
- field to aggregate within a group
- ``interval``
- on date and datetime fields, groups by the specified interval (``day``,
- ``week``, ``month``, ``quarter`` or ``year``) instead of grouping on the
- specific datetime (fixed second resolution) or date (fixed day resolution).
-
-``invisible``
- if true, the field will not appear either in the active measures nor in the
- selectable measures (useful for fields that do not make sense aggregated,
- such as fields in different units, e.g. € and $).
-
-The measures are automatically generated from the model fields; only the
-aggregatable fields are used. Those measures are also alphabetically
-sorted on the string of the field.
-
-.. warning::
-
- like the graph view, the pivot aggregates data on database content
- which means that non-stored function fields can not be used in pivot views
-
-
-In Pivot view a ``field`` can have a ``widget`` attribute to dictate its format.
-The widget should be a field formatter, of which the most interesting are
-``date``, ``datetime``, ``float_time``, and ``monetary``.
-
-For instance a timesheet pivot view could be defined as::
-
- <pivot string="Timesheet">
- <field name="employee_id" type="row"/>
- <field name="date" interval="month" type="col"/>
- <field name="unit_amount" type="measure" widget="float_time"/>
- </pivot>
-
-.. _reference/views/qweb:
-
-QWeb
-----
-
-QWeb views are standard :ref:`reference/qweb` templates inside a view's
-``arch``. They don't have a specific root element. Because QWeb views don't
-have a specific root element, their type must be specified explicitly (it can
-not be inferred from the root element of the ``arch`` field).
-
-QWeb views have two use cases:
-
-* they can be used as frontend templates, in which case
- :ref:`reference/data/template` should be used as a shortcut.
-* they can be used as actual qweb views (opened inside an action), in which
- case they should be defined as regular view with an explicit ``type`` (it
- can not be inferred) and a model.
-
-The main additions of qweb-as-view to the basic qweb-as-template are:
-
-* qweb-as-view has a special case for a ``<nav>`` element bearing the CSS
- class ``o_qweb_cp_buttons``: its contents should be buttons and will be
- extracted and moved to the control panel's button area, the ``<nav>`` itself
- will be removed, this is a work-around to control panel views not existing
- yet
-* qweb-as-view rendering adds several items to the standard qweb rendering
- context:
-
- ``model``
- the model to which the qweb view is bound
- ``domain``
- the domain provided by the search view
- ``context``
- the context provided by the search view
- ``records``
- a lazy proxy to ``model.search(domain)``, this can be used if you just
- want to iterate the records and not perform more complex operations
- (e.g. grouping)
-* qweb-as-view also provides additional rendering hooks:
-
- - ``_qweb_prepare_context(view_id, domain)`` prepares the rendering context
- specific to qweb-as-view
- - ``qweb_render_view(view_id, domain)`` is the method called by the client
- and will call the context-preparation methods and ultimately
- ``env['ir.qweb'].render()``.
-
-.. _reference/views/search:
-
-Search
-------
-
-Search views are a break from previous view types in that they don't display
-*content*: although they apply to a specific model, they are used to filter
-other view's content (generally aggregated views
-e.g. :ref:`reference/views/list` or :ref:`reference/views/graph`). Beyond that
-difference in use case, they are defined the same way.
-
-The root element of search views is ``<search>``. It takes no attributes.
-
-.. @string is not displayed anywhere, should be removed
-
-Possible children elements of the search view are:
-
-``field``
- fields define domains or contexts with user-provided values. When search
- domains are generated, field domains are composed with one another and
- with filters using **AND**.
-
- Fields can have the following attributes:
-
- ``name``
- the name of the field to filter on
- ``string``
- the field's label
- ``operator``
- by default, fields generate domains of the form :samp:`[({name},
- {operator}, {provided_value})]` where ``name`` is the field's name and
- ``provided_value`` is the value provided by the user, possibly
- filtered or transformed (e.g. a user is expected to provide the
- *label* of a selection field's value, not the value itself).
-
- The ``operator`` attribute allows overriding the default operator,
- which depends on the field's type (e.g. ``=`` for float fields but
- ``ilike`` for char fields)
- ``filter_domain``
- complete domain to use as the field's search domain, can use a
- ``self`` variable to inject the provided value in the custom
- domain. Can be used to generate significantly more flexible domains
- than ``operator`` alone (e.g. searches on multiple fields at once)
-
- If both ``operator`` and ``filter_domain`` are provided,
- ``filter_domain`` takes precedence.
- ``context``
- allows adding context keys, including the user-provided values (which
- as for ``domain`` are available as a ``self`` variable, an array of
- values e.g. ``[id_1, id_2]`` for a :class:`~odoo.fields.Many2one` field).
- By default, fields don't generate domains.
-
- .. note:: the domain and context are inclusive and both are generated
- if a ``context`` is specified. To only generate context
- values, set ``filter_domain`` to an empty list:
- ``filter_domain="[]"``
- ``groups``
- make the field only available to specific users
- ``domain``
- if the field can provide an auto-completion
- (e.g. :class:`~odoo.fields.Many2one`), filters the possible
- completion results.
-
-``filter``
- a filter is a predefined toggle in the search view, it can only be enabled
- or disabled. Its main purposes are to add data to the search context (the
- context passed to the data view for searching/filtering), or to append new
- sections to the search filter.
-
- Filters can have the following attributes:
-
- ``string`` (required)
- the label of the filter
- ``domain`` (optional)
- an Odoo :ref:`domain <reference/orm/domains>`, will be appended to the
- action's domain as part of the search domain.
- ``date`` (optional)
- the name of a field of type ``date`` or ``datetime``.
- Using this attribute has the effect to create
- a set of filters available in a submenu
- of the filters menu. The filters proposed are time dependent
- but not dynamic in the sense that their domains are evaluated
- at the time of the control panel instantiation.
-
- Example:
-
- .. code-block:: xml
-
- <filter name="filter_create_date" date="create_date" string="Creation Date"/>
-
- The example above allows to easily search for records with creation date field
- values in one of the periods below (if the current month is August 2019).
-
- .. code-block:: text
-
- Create Date >
- August
- July
- June
- Q4
- Q3
- Q2
- Q1
- --------------
- 2019
- 2018
- 2017
-
- Muti selection of options is allowed.
-
- ``default_period`` (optional)
- only makes sense for a filter with non empty ``date`` attribute.
- determines which period is activated if the filter is in the
- default set of filters activated at the view initialization. If not provided,
- 'this_month' is used by default.
-
- To choose among the following options:
- today, this_week, this_month, last_month, antepenultimate_month,
- fourth_quarter, third_quarter, second_quarter, first_quarter,
- this_year, last_year, antepenultimate_year.
-
- Example:
-
- .. code-block:: xml
-
- <filter name="filter_create_date" date="create_date" string="Creation Date" default_period="this_week"/>
-
- ``context``
- a Python dictionary, merged into the action's domain to generate the
- search domain
-
- The key ``group_by`` can be used to define a groupby available in the
- 'Group By' menu. The 'group_by' value can be a valid field name.
-
- .. code-block:: xml
-
- <filter name="groupby_category" string="Category" context = {'group_by': 'category_id'}/>
-
- The groupby defined above allows to group data by category.
-
- When the field is of type ``date`` or ``datetime``, the filter generates a submenu of the Group By
- menu in which the following interval options are available: day, week, month, quarter, year.
-
- In case the filter is in the default set of filters activated at the view initialization,
- the records are grouped by month by default. This can be changed by using the syntax
- 'date_field:interval' as in the following example.
-
- Example:
-
- .. code-block:: xml
-
- <filter name="groupby_create_date" string="Creation Date" context = {'group_by': 'create_date:week'}/>
-
- .. note::
- The results of read_groups grouped on a field may be influenced by its group_expand attribute,
- allowing to display empty groups when needed. For more information, please refer to
- :class:`~odoo.fields.Field` attributes documentation.
-
- ``name``
- logical name for the filter, can be used to :ref:`enable it by default
- <reference/views/search/defaults>`, can also be used as
- :ref:`inheritance hook <reference/views/inheritance>`
- ``help``
- a longer explanatory text for the filter, may be displayed as a
- tooltip
- ``groups``
- makes a filter only available to specific users
-
- .. tip::
-
- .. versionadded:: 7.0
-
- Sequences of filters (without non-filters separating them) are treated
- as inclusively composited: they will be composed with ``OR`` rather
- than the usual ``AND``, e.g.
-
- ::
-
- <filter domain="[('state', '=', 'draft')]"/>
- <filter domain="[('state', '=', 'done')]"/>
-
- if both filters are selected, will select the records whose ``state``
- is ``draft`` or ``done``, but
-
- ::
-
- <filter domain="[('state', '=', 'draft')]"/>
- <separator/>
- <filter domain="[('delay', '<', 15)]"/>
-
- if both filters are selected, will select the records whose ``state``
- is ``draft`` **and** ``delay`` is below 15.
-
-``separator``
- can be used to separates groups of filters in simple search views
-
-``group``
- can be used to separate groups of filters, more readable than
- ``separator`` in complex search views
-
-``searchpanel``
- allows to display a search panel on the left of any multi records view.
- By default, the list and kanban views have the searchpanel enabled.
- The search panel can be activated on other views with the attribute:
-
- * ``view_types`` a comma separated list of view types on which to enable the search panel
- default: 'tree,kanban'
-
- This tool allows to quickly filter data on the basis of given fields. The fields
- are specified as direct children of the ``searchpanel`` with tag name ``field``,
- and the following attributes:
-
- * ``name`` (mandatory) the name of the field to filter on
-
- * ``select`` determines the behavior and display. Possible values are
-
- * ``one`` (default) at most one value can be selected. Supported field types are
- many2one and selection.
-
- * ``multi`` several values can be selected (checkboxes). Supported field
- types are many2one, many2many and selection.
-
- * ``groups``: restricts to specific users
-
- * ``string``: determines the label to display
-
- * ``icon``: specifies which icon is used
-
- * ``color``: determines the icon color
-
- * ``enable_counters``: default is false. If set to true the record counters will be computed and
- displayed if non-zero.
-
- This feature has been implemented in case performances would be too bad.
-
- Another way to solve performance issues is to properly override the
- ``search_panel_select_range`` and ``search_panel_select_multi_range`` methods.
-
- * ``expand``: default is false. If set to false categories or filters with 0 records will be hidden.
-
- * ``limit``: default is 200. Integer determining the maximal number of values to fetch for the field.
- If the limit is reached, no values will be displayed in the search panel and an error message will
- appear instead because we consider that is useless / bad performance-wise. All values will be
- fetched if set to 0.
-
- Additional optional attributes are available according to the chosen case:
-
- For the ``one`` case:
-
- * ``hierarchize``: (only available for many2one fields) default is true. Handles the display style of categories :
-
- If set to true child categories will appear under their related parent.
- If not, all categories will be displayed on the same level.
-
- For the ``multi`` case:
-
- * ``domain``: determines conditions that the comodel records have to satisfy.
-
- A domain might be used to express a dependency on another field (with select="one")
- of the search panel. Consider
- /!\ This attribute is incompatible with a select="one" with enabled counters; if a select="multi"
- has a `domain` attribute, all select="one" will have their counters disabled.
-
- .. code-block:: xml
-
- <searchpanel>
- <field name="department_id"/>
- <field name="manager_id" select="multi" domain="[('department_id', '=', department_id)]"/>
- <searchpanel/>
-
- In the above example, the range of values for manager_id (manager names) available at screen
- will depend on the value currently selected for the field ``department_id``.
-
- * ``groupby``: field name of the comodel (only available for many2one and many2many fields). Values will be grouped by that field.
-
-
-.. _reference/views/search/defaults:
-
-Search defaults
-~~~~~~~~~~~~~~~
-
-Search fields and filters can be configured through the action's ``context``
-using :samp:`search_default_{name}` keys. For fields, the value should be the
-value to set in the field, for filters it's a boolean value or a number. For instance,
-assuming ``foo`` is a field and ``bar`` is a filter an action context of:
-
-.. code-block:: python
-
- {
- 'search_default_foo': 'acro',
- 'search_default_bar': 1
- }
-
-will automatically enable the ``bar`` filter and search the ``foo`` field for
-*acro*.
-
-A numeric value (between 1 and 99) can be used to describe the order of default groupbys.
-For instance if ``foo`` and ``bar`` refer to two groupbys
-
-.. code-block:: python
-
- {
- 'search_default_foo': 2,
- 'search_default_bar': 1
- }
-
-has the effect to activate first ``bar`` then ``foo``.
-
-.. todo:: View Grid
-
-.. _bootstrap contextual color: https://getbootstrap.com/docs/3.3/components/#available-variations
-.. _geoforwarding: https://nominatim.org/release-docs/develop/
-.. _tiles: https://wiki.openstreetmap.org/wiki/Tile_data_server
-.. _MapBox: https://docs.mapbox.com/api/
-.. _signing up: https://account.mapbox.com/auth/signup/
-.. _accesskey: https://www.w3.org/TR/html5/editing.html#the-accesskey-attribute
-.. _floats: https://developer.mozilla.org/en-US/docs/Web/CSS/float
-.. _HTML: https://en.wikipedia.org/wiki/HTML
-.. _kanban board: https://en.wikipedia.org/wiki/Kanban_board
-.. _pivot table: https://en.wikipedia.org/wiki/Pivot_table
-.. _XPath: https://en.wikipedia.org/wiki/XPath
-.. _KanbanExamplesRegistry: https://github.com/odoo/odoo/blob/99821fdcf89aa66ac9561a972c6823135ebf65c0/addons/web/static/src/js/views/kanban/kanban_examples_registry.js