[REF][MOV] documentation apocalypse
Prior to this commit, the Odoo documentation was mainly split between
two repositories: odoo/odoo/doc and odoo/documentation-user. Some bits
of documentation were also hosted elsewhere (e.g., wiki, upgrade, ...).
This was causing several problems among which:
- The theme, config, Makefile, and similar technical resources had to
be duplicated. This resulted in inconsistent layout, features, and
build environments from one documentation to another.
- Some pages did not fit either documentation as they were relevant
for both users and developers. Some were relevant to neither of the
two (e.g., DB management).
- Cross-doc references had to be absolute links and they broke often.
- Merging large image files in the developer documentation would bloat
the odoo/odoo repository. Some contributions had to be lightened to
avoid merging too many images (e.g., Odoo development tutorials).
- Long-time contributors to the user documentation were chilly about
going through the merging process of the developer documentation
because of the runbot, mergebot, `odoo-dev` repository, etc.
- Some contributors would look for the developer documentation in the
`odoo/documentation-user` repository.
- Community issues about the user documentation were submitted on the
`odoo/odoo` repository and vice-versa.
Merging all documentations in one repository will allow us to have one
place, one theme, one work process, and one set of tools (build
environment, ...) for all of the Odoo docs.
As this is a good opportunity to revamp the layout of the documentation,
a brand new theme replaces the old one. It features a new way to
navigate the documentation, centered on the idea of always letting the
reader know what is the context (enclosing section, child pages, page
structure ...) of the page they are reading. The previous theme would
quickly confuse readers as they navigated the documentation and followed
cross-application links.
The chance is also taken to get rid of all the technical dangling parts,
performance issues, and left-overs. Except for some page-specific JS
scripts, the Odoo theme Sphinx extension is re-written from scratch
based on the latest Sphinx release to benefit from the improvements and
ease future contributions.
task-2351938
task-2352371
task-2205684
task-2352544
Closes #945
@@ -0,0 +1,444 @@
|
||||
|
||||
.. _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.
|
||||
|
||||
.. _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
|
||||
``auto_search`` (optional)
|
||||
whether a search should be performed immediately after loading the default
|
||||
view. Defaults to ``True``
|
||||
|
||||
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::
|
||||
|
||||
* ``src_model``, ``multi`` seem linked to "sidebar" actions?
|
||||
* ``auto_refresh`` looks ignored/deprecated
|
||||
* ``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)
|
||||
@@ -0,0 +1,706 @@
|
||||
|
||||
.. _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.
|
||||
|
||||
.. _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:: --logrotate
|
||||
|
||||
enables `log rotation <https://docs.python.org/3/library/logging.handlers.html#timedrotatingfilehandler>`_
|
||||
daily, keeping 30 backups. Log rotation frequency and number of backups is
|
||||
not configurable.
|
||||
|
||||
.. danger::
|
||||
|
||||
Built-in log rotation is not reliable in multi-workers scenarios
|
||||
and may incur significant data loss. It is *strongly recommended* to
|
||||
use an external log rotation utility or use system loggers (--syslog)
|
||||
instead.
|
||||
|
||||
.. 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.
|
||||
|
||||
.. _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.
|
||||
|
||||
.. 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
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ odoo_bin scaffold my_module /addons/
|
||||
|
||||
This will create module *my_module* in directory */addons/*.
|
||||
|
||||
|
||||
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``.
|
||||
@@ -0,0 +1,316 @@
|
||||
|
||||
.. _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/orm/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.
|
||||
|
||||
``report``
|
||||
----------
|
||||
|
||||
Creates a :ref:`IrActionsReport <reference/actions/report>` record with a few default values.
|
||||
|
||||
Mostly just proxies attributes to the corresponding fields on
|
||||
``ir.actions.report``, but also automatically creates the item in the
|
||||
:guilabel:`More` menu of the report's ``model``.
|
||||
|
||||
.. note::
|
||||
|
||||
You might expect the ``name`` of the ``report`` tag to become the ``ir.actions.report`` name,
|
||||
but the value is used as ``report_name`` field value. To specify the ``name`` field in ``ir.actions.report``,
|
||||
you should use the ``string`` attribute of the ``report`` tag.
|
||||
|
||||
The detailed attributes and values supported can be found :ref:`here <reference/reports/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-sm
|
||||
|
||||
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
|
||||
@@ -0,0 +1,86 @@
|
||||
|
||||
.. _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:
|
||||
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,139 @@
|
||||
|
||||
==================
|
||||
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 a `Driver`.
|
||||
|
||||
At each boot, the IoT Box will load all of the Drivers that can
|
||||
be located on the connected Odoo instance. Each module can contain a
|
||||
`drivers` directory, whose content will be copied to the IoT Box.
|
||||
|
||||
Detect Devices
|
||||
==============
|
||||
|
||||
The `addons/hw_drivers/controllers/driver.py` file contains a Manager that is
|
||||
in charge of the devices. The Manager maintains a list of connected 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.
|
||||
|
||||
Driver
|
||||
------
|
||||
|
||||
Once the Manager has retrieved the list of detected devices, it will loop
|
||||
through all of the Drivers that have the same connection type 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.
|
||||
|
||||
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.controllers.driver import Driver
|
||||
|
||||
class DriverName(Driver):
|
||||
connection_type = 'ConnectionType'
|
||||
|
||||
def __init__(self, device):
|
||||
super(NewDriver, self).__init__(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.widgets').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.controllers.driver import event_manager
|
||||
|
||||
class DriverName(Driver):
|
||||
connection_type = 'ConnectionType'
|
||||
|
||||
def methodName(self):
|
||||
self.data = {
|
||||
'value': 0.5,
|
||||
...
|
||||
}
|
||||
event_manager.device_changed(self)
|
||||
@@ -0,0 +1,280 @@
|
||||
|
||||
.. _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.
|
||||
@@ -0,0 +1,212 @@
|
||||
: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
|
||||
@@ -0,0 +1,145 @@
|
||||
|
||||
================
|
||||
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.
|
||||
|
||||
.. _semantic versioning: https://semver.org
|
||||
.. _existing categories:
|
||||
https://github.com/odoo/odoo/blob/12.0/odoo/addons/base/data/ir_module_category_data.xml
|
||||
@@ -0,0 +1,650 @@
|
||||
|
||||
.. 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), a mapping (the current item will be the current key) or an integer
|
||||
(equivalent to iterating on an array between 0 inclusive and the provided
|
||||
integer exclusive).
|
||||
|
||||
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`
|
||||
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`
|
||||
either ``"even"`` or ``"odd"``, the parity of the current iteration round
|
||||
:samp:`{$as}_even`
|
||||
a boolean flag indicating that the current iteration round is on an even
|
||||
index
|
||||
:samp:`{$as}_odd`
|
||||
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_parity }}"><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 ``<li>ok</li>`` (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 edition.
|
||||
|
||||
``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 alter existing templates in-place, e.g. to
|
||||
add information to templates created by other modules.
|
||||
|
||||
Template inheritance is performed via the ``t-extend`` directive which takes
|
||||
the name of the template to alter as parameter.
|
||||
|
||||
When ``t-extend`` is combined with ``t-name`` a new template with the given name
|
||||
is created. In this case the extended template is not altered, instead the
|
||||
directives define how to create the new template.
|
||||
|
||||
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/
|
||||
@@ -0,0 +1,353 @@
|
||||
|
||||
.. highlight:: xml
|
||||
|
||||
.. _reference/reports:
|
||||
|
||||
============
|
||||
QWeb Reports
|
||||
============
|
||||
|
||||
Reports are written in HTML/QWeb, like all regular views in Odoo. You can use
|
||||
the usual :ref:`QWeb control flow tools <reference/qweb>`. The PDF rendering
|
||||
itself is performed by wkhtmltopdf_.
|
||||
|
||||
If you want to create a report on a certain model, you will need to define
|
||||
this :ref:`reference/reports/report` and the
|
||||
:ref:`reference/reports/templates` it will use. If you wish, you can also
|
||||
specify a specific :ref:`reference/reports/paper_formats` for this
|
||||
report. Finally, if you need access to more than your model, you can define a
|
||||
:ref:`reference/reports/custom_reports` class that gives you access to more
|
||||
models and records in the template.
|
||||
|
||||
.. _reference/reports/report:
|
||||
|
||||
Report
|
||||
======
|
||||
|
||||
Every report must be declared by a :ref:`report action
|
||||
<reference/actions/report>`.
|
||||
|
||||
For simplicity, a shortcut ``<report>`` element is available to define a
|
||||
report, rather than have to set up :ref:`the action
|
||||
<reference/actions/report>` and its surroundings manually. That ``<report>``
|
||||
can take the following attributes:
|
||||
|
||||
``id``
|
||||
the generated record's :term:`external id`
|
||||
``string`` (mapping to ``IrActionsReport.name`` field)
|
||||
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
|
||||
``name`` (mandatory) (mapping to ``IrActionsReport.report_name`` field)
|
||||
the name of the template used to render the report
|
||||
``model`` (mandatory)
|
||||
the model your report will be about
|
||||
``report_type`` (mandatory)
|
||||
either ``qweb-pdf`` for PDF reports or ``qweb-html`` for HTML
|
||||
``print_report_name``
|
||||
python expression defining the name of the report.
|
||||
``groups``
|
||||
:class:`~odoo.fields.Many2many` field to the groups allowed to view/use
|
||||
the current report
|
||||
``attachment_use``
|
||||
if set to True, the report will be stored as an attachment of the record
|
||||
using the name generated by the ``attachment`` expression; you can use
|
||||
this if you need your report to be generated only once (for legal reasons,
|
||||
for example)
|
||||
``attachment``
|
||||
python expression that defines the name of the report; the record is
|
||||
acessible as the variable ``object``
|
||||
``paperformat``
|
||||
external id of the paperformat you wish to use (defaults to the company's
|
||||
paperformat if not specified)
|
||||
|
||||
Example::
|
||||
|
||||
<report
|
||||
id="account_invoices"
|
||||
model="account.invoice"
|
||||
string="Invoices"
|
||||
name="account.report_invoice"
|
||||
report_type="qweb-pdf"
|
||||
print_report_name="object._get_report_filename()"
|
||||
attachment_use="True"
|
||||
attachment="(object.state in ('open','paid')) and
|
||||
('INV'+(object.number or '').replace('/','')+'.pdf')"
|
||||
/>
|
||||
|
||||
.. _reference/reports/templates:
|
||||
|
||||
Report template
|
||||
===============
|
||||
|
||||
|
||||
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.
|
||||
|
||||
There are some specific variables accessible in reports, mainly:
|
||||
|
||||
``docs``
|
||||
records for the current report
|
||||
``doc_ids``
|
||||
list of ids for the ``docs`` records
|
||||
``doc_model``
|
||||
model for the ``docs`` records
|
||||
``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
|
||||
|
||||
If you wish to access other records/models in the template, you will need
|
||||
:ref:`a custom report <reference/reports/custom_reports>`.
|
||||
|
||||
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="{"no_marker": 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&value=%s&width=%s&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
|
||||
==============
|
||||
|
||||
The report model has a default ``get_html`` function that looks for a model
|
||||
named :samp:`report.{module.report_name}`. If it exists, it will use it to
|
||||
call the QWeb engine; otherwise a generic function will be used. If you wish
|
||||
to customize your reports by including more things in the template (like
|
||||
records of others models, for example), you can define this model, overwrite
|
||||
the function ``_get_report_values`` and pass objects in the ``docargs`` dictionary:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
class ParticularReport(models.AbstractModel):
|
||||
_name = 'report.module.report_name'
|
||||
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
report_obj = self.env['ir.actions.report']
|
||||
report = report_obj._get_report_from_name('module.report_name')
|
||||
docargs = {
|
||||
'doc_ids': docids,
|
||||
'doc_model': report.model,
|
||||
'docs': self,
|
||||
}
|
||||
return docargs
|
||||
|
||||
.. _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
|
||||
|
||||
.. _wkhtmltopdf: https://wkhtmltopdf.org
|
||||
@@ -0,0 +1,99 @@
|
||||
|
||||
.. _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 Administrator user
|
||||
:class: aphorism
|
||||
|
||||
.. _reference/security/fields:
|
||||
|
||||
Field Access
|
||||
============
|
||||
|
||||
.. versionadded:: 7.0
|
||||
|
||||
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 administrator in fields_get but not in
|
||||
read/write...
|
||||
|
||||
.. _foo: http://google.com
|
||||
.. _time module: https://docs.python.org/2/library/time.html
|
||||
@@ -0,0 +1,15 @@
|
||||
"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"
|
||||
|
@@ -0,0 +1,289 @@
|
||||
|
||||
.. _reference/testing:
|
||||
|
||||
|
||||
===============
|
||||
Testing Odoo
|
||||
===============
|
||||
|
||||
There are many ways to test an application. In Odoo, we have three kinds of
|
||||
tests
|
||||
|
||||
- python unit tests: useful for testing model business logic
|
||||
- js unit tests: this is necessary to test the javascript code in isolation
|
||||
- tours: this is a form of integration testing. The tours ensure 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, phantom_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:
|
||||
|
||||
.. autofunction:: odoo.tests.common.at_install
|
||||
|
||||
.. autofunction:: odoo.tests.common.post_install
|
||||
|
||||
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
|
||||
===============
|
||||
|
||||
Qunit test suite
|
||||
----------------
|
||||
|
||||
Odoo Web includes means to unit-test both the core code of
|
||||
Odoo Web and your own javascript modules. On the javascript side,
|
||||
unit-testing is based on QUnit_ with a number of helpers and
|
||||
extensions for better integration with Odoo.
|
||||
|
||||
To see what the runner looks like, find (or start) an Odoo server
|
||||
with the web client enabled, and navigate to ``/web/tests``
|
||||
This will show the runner selector, which lists all modules with javascript
|
||||
unit tests, and allows starting any of them (or all javascript tests in all
|
||||
modules at once).
|
||||
|
||||
.. image:: ./images/runner.png
|
||||
:align: center
|
||||
|
||||
Clicking any runner button will launch the corresponding tests in the
|
||||
bundled QUnit_ runner:
|
||||
|
||||
.. image:: ./images/tests.png
|
||||
:align: center
|
||||
|
||||
Writing a test case
|
||||
-------------------
|
||||
|
||||
This section will be updated as soon as possible.
|
||||
|
||||
.. _qunit: https://qunitjs.com/
|
||||
|
||||
|
||||
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 phantom_js browser, point it to the proper url
|
||||
and simulate the click and inputs, according to the scenario.
|
||||
@@ -0,0 +1,193 @@
|
||||
|
||||
.. _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 --> Load a Translation`)
|
||||
|
||||
.. 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
|
||||
|
||||
|
||||
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 outside of the translation lookup::
|
||||
|
||||
_("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 %s invoice") % 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 %s invoices") % invoice_count
|
||||
else:
|
||||
msg = _("You have %s invoice") % invoice_count
|
||||
|
||||
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** evaluate dynamically the translatable content::
|
||||
|
||||
# good, evaluated at run time
|
||||
def _get_error_message():
|
||||
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/
|
||||
|
After Width: | Height: | Size: 11 KiB |