Compare commits

..

18 Commits

Author SHA1 Message Date
Simon Genin (ges)
1fca236842 wip 2021-10-15 17:12:45 +02:00
Olivier Dony
a8c58815f3 [FIX] mail_plugins: fix outlook plugin url
The outlook plugin (v2.0.0) for v15 is not compatible with
older databases, so we need both version to be hosted
separately, and therefore a new URL.
2021-10-13 19:39:43 +02:00
Valentin Chevalier (vcr)
a4aa310b85 [FIX] payment_acquirers/paypal: broken link
Users couldn't click on Paypal and saw the HTTP link instead of only 'Paypal'

closes odoo/documentation#1174

Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-10-12 14:36:49 +00:00
Xavier
4331177bb6 [IMP] mail plugins: improve the naming consistency of plugins
The plugins were named "Odoo-Gmail Plugin" / "Gmail Plugin" and "Odoo-Outlook Plugin" /
"Outlook Plugin". I chose the later version for both plugins consistently.
I also added a reference to both the Gmail and Outlook docs in the main Mail Plugins doc.

closes odoo/documentation#1170

Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-10-11 09:39:43 +00:00
Xavier
debe8cb393 [IMP] mail plugins: add gmail plugin + update and move outlook plugin
Task ID: 2662764

closes odoo/documentation#1167

Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-10-07 18:23:02 +00:00
Demesmaeker
3a2bbc028b [IMP] payment_acquirers: revamp online payment providers' documentation
task-2654702

closes odoo/documentation#1061

Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-10-05 17:24:43 +00:00
Antoine Vandevenne (anv)
b28e34b357 [REL] supported_versions: update list of versions and add end of support
closes odoo/documentation#1162

X-original-commit: e0c70eb5ff
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-10-05 15:17:53 +00:00
Antoine Vandevenne (anv)
c107d76086 [IMP] contributing: adapt the GitHub workflow to the new doc theme
closes odoo/documentation#1159

X-original-commit: 8043d92b24
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-10-05 14:23:56 +00:00
Antoine Vandevenne (anv)
b118669860 [REL] freeze 15.0 branch 2021-10-05 13:50:03 +02:00
Denis Ledoux
3218d318fe [FIX] maintain: upgrade command line link
closes odoo/documentation#1150

X-original-commit: 7276929071
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
2021-09-22 10:37:59 +00:00
Tiffany Chang (tic)
bb3996b936 [IMP] developer/{howtos,reference}: update training to match v15 changes
- Reference v15 training solutions
- Refer to Command namespace instead of triplets (e.g.
  Command.create(values) instead of (0, 0, values)
- Add notice about OWL transition
- Add reference to @api.ondelete (instead of override unlink())
- Remove references to SavepointCase (now only TransactionCase) from
  unit test topic

- Also add missing reference documentation for ondelete

closes odoo/documentation#1146

Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-09-20 11:47:42 +00:00
Tiffany Chang (tic)
b3845ae0d8 [IMP] developer/howtos: proofread adv topics
- Cleaned up the language a tiny bit
- Fixed small errors/typos
- Add some missing info for dashboard topic
- Updated unit test exercise to match what's in the solutions repo

Part-of: odoo/documentation#1146
2021-09-20 11:47:42 +00:00
Tiffany Chang (tic)
85410bd8dc [IMP] developer/howtos: encourage linters more
There have been complaints of newbies not setting up their linters
therefore let's emphasize their use/setup more in the tutorial and hope
for the best.

Part-of: odoo/documentation#1146
2021-09-20 11:47:42 +00:00
Jonathan Castillo (jcs)
248135a48e [IMP] maintain: update test doc link in versions upgrade
closes odoo/documentation#1141

X-original-commit: 0842b682fc
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-09-14 16:23:07 +00:00
Raf Geens
6833379ad3 [FIX] app/accounting: Fix outdated Colombian test server url
The test server url for Carvajal T&S was out of date and has been
updated with the correct one. Carvajal has also split into 2
different companies, with new customers using the new company (CSC)
by default but the older company (CTS) still being available for
existing customers. See task 2508208. So the documentation was
updated to include both of them.

CTS (Carvajal T&S) was being used to refer to Carvajal in general,
so to avoid confusion those mentions have been changed to just
"Carvajal" since it can refer to either CTS or CSC.

closes odoo/documentation#1138

X-original-commit: 396df2abdd
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-09-14 16:18:51 +00:00
Jonathan Castillo (jcs)
00046cb690 [FIX] subscriptions: wrong Odoo Tutorials link
task-2636416

closes odoo/documentation#1136

X-original-commit: ebf216cc8e
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2021-09-14 12:31:13 +00:00
Antoine Vandevenne (anv)
ac40d2008c [IMP] conf: hide saas branches from the version switcher 2021-09-09 17:22:27 +02:00
Antoine Vandevenne (anv)
5cef86f181 [REL] freeze saas-14.5 branch 2021-09-09 16:25:46 +02:00
121 changed files with 1721 additions and 6107 deletions

View File

@@ -24,7 +24,7 @@ SOURCE_DIR = content
HTML_BUILD_DIR = $(BUILD_DIR)/html
ifdef VERSIONS
HTML_BUILD_DIR := $(HTML_BUILD_DIR)/master
HTML_BUILD_DIR := $(HTML_BUILD_DIR)/15.0
endif
ifneq ($(CURRENT_LANG),en)
HTML_BUILD_DIR := $(HTML_BUILD_DIR)/$(CURRENT_LANG)
@@ -69,7 +69,7 @@ gettext:
$(HTML_BUILD_DIR)/_static/style.css: extensions/odoo_theme/static/style.scss extensions/odoo_theme/static/scss/*.scss
@echo "Compiling stylesheets..."
mkdir -p $(HTML_BUILD_DIR)/_static
python3 -m pysassc extensions/odoo_theme/static/style.scss $(HTML_BUILD_DIR)/_static/style.css
pysassc extensions/odoo_theme/static/style.scss $(HTML_BUILD_DIR)/_static/style.css
@echo "Compilation finished."
#=== Development and debugging rules ===#

View File

@@ -4,11 +4,11 @@
### Requirements
- [Git](https://www.odoo.com/documentation/master/contributing/documentation.html#install-git)
- [Python 3.7 or 3.8](https://www.odoo.com/documentation/master/contributing/documentation.html#python)
- Python dependencies listed in the file [`requirements.txt`](https://github.com/odoo/documentation/tree/master/requirements.txt).
- [Make](https://www.odoo.com/documentation/master/contributing/documentation.html#make)
- A local copy of the [odoo/odoo repository in master](https://github.com/odoo/odoo/tree/master) (Optional)
- [Git](https://www.odoo.com/documentation/15.0/contributing/documentation.html#install-git)
- [Python 3.6, 3.7, or 3.8](https://www.odoo.com/documentation/15.0/contributing/documentation.html#python)
- Python dependencies listed in the file [`requirements.txt`](https://github.com/odoo/documentation/tree/15.0/requirements.txt).
- [Make](https://www.odoo.com/documentation/15.0/contributing/documentation.html#make)
- A local copy of the [odoo/odoo repository in 15.0](https://github.com/odoo/odoo/tree/15.0) (Optional)
### Instructions
@@ -23,7 +23,7 @@
2. Open the file `documentation/_build/html/index.html` in your web browser to display the render.
3. See [this guide](https://www.odoo.com/documentation/master/contributing/documentation.html#preview-your-changes)
3. See [this guide](https://www.odoo.com/documentation/15.0/contributing/documentation.html#preview-your-changes)
for more detailed instructions.
Optional: to fully build the developer documentation with inline docstrings for documented Python
@@ -34,7 +34,7 @@ be shown.
## Contribute to the documentation
For contributions to the content of the documentation, please refer to the
[Introduction Guide](https://www.odoo.com/documentation/master/contributing/documentation.html).
[Introduction Guide](https://www.odoo.com/documentation/15.0/contributing/documentation.html).
To **report a content issue**, **request new content** or **ask a question**, use the
[repository's issue tracker](https://github.com/odoo/documentation-user/issues) as usual.

17
conf.py
View File

@@ -19,7 +19,7 @@ copyright = 'Odoo S.A.'
# `version` if the version info for the project being documented, acts as replacement for |version|,
# also used in various other places throughout the built documents.
# `release` is the full version, including alpha/beta/rc tags. Acts as replacement for |release|.
version = release = 'master'
version = release = '15.0'
# The minimal Sphinx version required to build the documentation.
needs_sphinx = '3.0.0'
@@ -70,23 +70,20 @@ if not odoo_dir.is_dir():
f"Could not find Odoo sources directory at {odoo_dir.absolute()}.\n"
f"The 'Developer' documentation will be built but autodoc directives will be skipped.\n"
f"In order to fully build the 'Developer' documentation, clone the repository with "
f"`git clone https://github.com/odoo/odoo` or create a symbolink link."
f"`git clone https://github.com/odoo/odoo` or create a symbolic link."
)
else:
sys.path.insert(0, str(odoo_dir.absolute()))
if sys.version_info < (3, 7) and sys.version_info > (3, 6):
# running odoo needs python 3.7 min but monkey patch version_info to be
# able to build the doc in python 3.6
sys.version_info = (3, 7, 0)
from odoo import release as odoo_release # Don't collide with Sphinx's 'release' config option
odoo_version = odoo_release.version if 'alpha' not in odoo_release.version else 'master'
odoo_version = odoo_release.version.replace('~', '-') \
if 'alpha' not in odoo_release.version else 'master'
if release != odoo_version:
_logger.warning(
f"Found Odoo sources directory but with version '{odoo_version}' incompatible with "
f"documentation version '{version}'.\n"
f"The 'Developer' documentation will be built but autodoc directives will be skipped.\n"
f"In order to fully build the 'Developer' documentation, checkout the matching branch"
f" with `cd odoo && git checkout {version}`."
f"In order to fully build the 'Developer' documentation, checkout the matching branch "
f"with `cd odoo && git checkout {version}`."
)
else:
_logger.info(f"Found Odoo sources directory matching documentation version {release}.")
@@ -184,7 +181,7 @@ html_permalinks = True # Sphinx >= 3.5
# Additional JS & CSS files that can be imported with the 'custom-js' and 'custom-css' metadata.
# Lists are empty because the files are specified in extensions/themes.
html_js_files = []
html_css_files = ["css/js.css"]
html_css_files = []
# PHP lexer option to not require <?php
highlight_options = {

View File

@@ -18,5 +18,4 @@ These guides provide instructions on how to install, maintain and upgrade Odoo d
administration/install
administration/maintain
administration/upgrade
administration/odoo_sh

View File

@@ -302,13 +302,13 @@ Prepare
Python
^^^^^^
Odoo requires Python 3.7 or later to run. Visit `Python's download page <https://www.python.org/downloads/windows/>`_
Odoo requires Python 3.6 or later to run. Visit `Python's download page <https://www.python.org/downloads/windows/>`_
to download and install the latest version of Python 3 on your machine.
During installation, check **Add Python 3 to PATH**, then click **Customize Installation** and make
sure that **pip** is checked.
.. note:: If Python 3 is already installed, make sure that the version is 3.7 or above, as previous
.. note:: If Python 3 is already installed, make sure that the version is 3.6 or above, as previous
versions are not compatible with Odoo.
.. code-block:: doscon
@@ -466,10 +466,10 @@ Prepare
Python
^^^^^^
Odoo requires Python 3.7 or later to run. Use your package manager to download and install Python 3
Odoo requires Python 3.6 or later to run. Use your package manager to download and install Python 3
on your machine if it is not already done.
.. note:: If Python 3 is already installed, make sure that the version is 3.7 or above, as previous
.. note:: If Python 3 is already installed, make sure that the version is 3.6 or above, as previous
versions are not compatible with Odoo.
.. code-block:: console
@@ -635,10 +635,10 @@ Prepare
Python
^^^^^^
Odoo requires Python 3.7 or later to run. Use your preferred package manager (homebrew_, macports_)
Odoo requires Python 3.6 or later to run. Use your preferred package manager (homebrew_, macports_)
to download and install Python 3 on your machine if it is not already done.
.. note:: If Python 3 is already installed, make sure that the version is 3.7 or above, as previous
.. note:: If Python 3 is already installed, make sure that the version is 3.6 or above, as previous
versions are not compatible with Odoo.
.. code-block:: console

View File

@@ -7,8 +7,8 @@ Maintain
.. toctree::
maintain/update
maintain/db_upgrade
maintain/enterprise
maintain/hosting_changes
maintain/online
maintain/on_premise
maintain/db_premise
maintain/supported_versions

View File

@@ -1,6 +1,9 @@
==============================
On-premise database management
==============================
.. _db_premise:
===============================
On-premises Database management
===============================
Register a database
===================
@@ -18,7 +21,7 @@ Registration Error Message
If you are unable to register your database, you will likely encounter this
message:
.. image:: on_premise/error_message_sub_code.png
.. image:: media/error_message_sub_code.png
:align: center
:alt: Something went wrong while registering your database,
you can try again or contact Odoo Help
@@ -41,14 +44,14 @@ Solutions
* You can unlink the old database yourself on your `Odoo Contract
<https://accounts.odoo.com/my/subscription>`__ with the button "Unlink database"
.. image:: on_premise/unlink_single_db.png
.. image:: media/unlink_single_db.png
:align: center
A confirmation message will appear; make sure this is the correct database as
it will be deactivated shortly:
.. image:: on_premise/unlink_confirm_enterprise_edition.png
.. image:: media/unlink_confirm_enterprise_edition.png
:align: center
@@ -62,7 +65,7 @@ Solutions
<https://accounts.odoo.com/my/subscription>`__, a short message will appear
specifying which database is problematic:
.. image:: on_premise/unlink_db_name_collision.png
.. image:: media/unlink_db_name_collision.png
:align: center
@@ -93,7 +96,7 @@ Error message due to too many users
If you have more users in your local database than provisionned in your
Odoo Enterprise subscription, you may encounter this message:
.. image:: on_premise/add_more_users.png
.. image:: media/add_more_users.png
:align: center
:alt: This database will expire in X days, you
have more users than your subscription allows
@@ -122,7 +125,7 @@ Database expired error message
If your database reaches its expiration date before your renew your subscription,
you will encounter this message:
.. image:: on_premise/database_expired.png
.. image:: media/database_expired.png
:align: center
:alt: This database has expired.
@@ -154,7 +157,7 @@ You can duplicate your database by accessing the database manager on your
server (<odoo-server>/web/database/manager). In this page, you can easily
duplicate your database (among other things).
.. image:: on_premise/db_manager.gif
.. image:: media/db_manager.gif
:align: center
@@ -172,5 +175,5 @@ System Parameters`, we advise you to use a `uuid generator <https://www.uuidtool
use the unix command ``uuidgen`` to generate a new uuid. You can then simply replace it like any
other record by clicking on it and using the edit button.
.. image:: on_premise/db_uuid.png
.. image:: media/db_uuid.png
:align: center

View File

@@ -0,0 +1,413 @@
.. |assistance-contact| replace::
If you need Odoo assistance on this matter, please contact your Odoo Account Manager or contact
our `Sales department`_.
.. _Sales department: mailto:sales@odoo.com
.. _db-upgrade:
================
Versions upgrade
================
.. _db-upgrade/overview:
Overview
========
.. _db-upgrade/process:
The upgrade process
-------------------
This documentation is for our *On-Premise* (self-hosted) and *Odoo.sh* customers.
.. _db-upgrade/definition:
Definition
~~~~~~~~~~
An upgrade is switching to a newer version of Odoo (e.g., Odoo 13.0 to Odoo 14.0)
An upgrade does not cover:
* changing :ref:`Editions <db-upgrade/faq/editions-change>` (i.e., Community to Enterprise edition)
* switching :ref:`hosting type <db-upgrade/faq/hosting-types-switch>` (i.e., On-Premise to Online or
Odoo.sh)
* migration from another ERP to Odoo
.. note:: |assistance-contact|
.. _db-upgrade/process-workflow:
Process workflow
~~~~~~~~~~~~~~~~
The upgrade process in a nutshell:
#. You create a test upgrade request.
#. | The request is processed by Odoo:
| This happens via an automated process that runs the database through an upgrade script and
takes between 20 and 120 minutes. Only if an issue(s) arises will we have to intervene
manually and adjust the script specifically to your database until the upgrade succeeds.
#. Odoo delivers a test database.
#. You test your database for possible discrepancies (see :ref:`db-upgrade/test-guidance`)
#. If there are any discrepancies, you report them to the Upgrade support team via the
:ref:`Help portal <db-upgrade/test-assistance>`.
#. We will fix the issues and send you a new test database.
#. Once you completed the testing and are happy with the result, you decide on a date and time when
you stop users from accessing Odoo, freeze all data entries and create an upgrade request for the
production upgrade.
#. Odoo delivers the production database through the automated process.
#. You restore it in your Production environment a few short hours later and continue working on the
newly upgraded database.
.. _db-upgrade/service-level:
Service Level Agreement
-----------------------
What is covered by the Enterprise Licence?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Databases hosted on Odoos Cloud platforms (Saas and Odoo.sh) or On-Premise (Self-Hosting) enjoy the
following service at all times.
The upgrade of:
* standard applications
* Studio customization (as long as the Studio app is still active)
* customizations done by our consulting and developer services *if* they are covered by a
Maintenance of Customisations subscription
The Upgrade Service is limited to the technical conversion and adaptation of your database (standard
modules and data) to make it compatible with the targeted version.
What upgrading does NOT cover
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* The cleaning of pre-existing data & configuration while upgrading
* Any new developments and/or upgrade of your own :ref:`custom modules
<db-upgrade/faq/custom-modules>`
* `Training <https://www.odoo.com/learn>`_ on the new version
You can get more information about your Enterprise Licence on our :ref:`Odoo Enterprise Subscription
Agreement <upgrade>` page.
.. note:: |assistance-contact|
.. _db-upgrade/get-started:
Get started
===========
The upgrade process varies depending on where your database is hosted.
.. _db-upgrade/online:
Online (SaaS)
-------------
If you are hosted Online, please check your `database manager <https://www.odoo.com/my/databases>`_.
.. _db-upgrade/odoo-sh:
Odoo.sh
-------
If you are Odoo.sh hosted, check our :doc:`specific instructions to be able to upgrade
<../odoo_sh/advanced/upgrade_your_database>`.
.. _db-upgrade/on-premise:
On-Premise
----------
There are two possibilities:
#. Via the interface of our `website form <https://upgrade.odoo.com>`_
#. | For technically-advanced users and partners, via the following command line (to be used on the
machine where your database is hosted):
| ``python <(curl -s https://upgrade.odoo.com/upgrade) test -d <your db name> -t 14.0``
What does it do?
~~~~~~~~~~~~~~~~
The above command will dump your database to a file, then send it to the upgrade platform for
upgrade, displaying you the live logs, then restore the upgraded database back on your server as a
duplicate test database.
.. _db-upgrade/steps:
Steps
=====
.. _db-upgrade/steps-test:
The testing phase
-----------------
.. _db-upgrade/test-process:
Test process
~~~~~~~~~~~~
Also referred to as the pre-production phase, the test phase allows you to review an upgraded
version of your database without affecting your production database in any way.
We suggest that you run the test upgrade process at least once, but you can do it as often as you
want (one at a time).
Once you receive your upgraded test database, you should check that all data, processes and
functionality are still correct and working as expected.
If you do find discrepancies, you'll be able to:
* | :ref:`Report your issues <db-upgrade/test-assistance>`
| and/or
* Ask for a new :ref:`test request <db-upgrade/test-db-request>` after the reported issues have
been fixed in the upgrade script.
When you do not find any discrepancies, you'll be able to:
* Move on to the upgrade of your :ref:`production database <db-upgrade/production-live>`.
.. _db-upgrade/test-db-request:
Request a test database
~~~~~~~~~~~~~~~~~~~~~~~
When filling the `website form <https://upgrade.odoo.com>`_, select *Testing* purpose.
.. image:: media/db-upgrade-test-purpose.png
:align: center
:alt: Selection of the "Testing" purpose in the upgrade form on Odoo
.. _db-upgrade/test-guidance:
Test guidance
~~~~~~~~~~~~~
Every business and organization has its own operational needs and will have to test its specific
Odoo instance respectively. However, we recommend you look at `the test scenario
<https://docs.google.com/document/d/1ypNs7JKPOsjNbKpdiKFH7Al6g6whZ9jr7f7duAQ5E1w/>`_ we created, a
high-level idea of what you should test and look out for.
.. todo:: change link "test scenario" once the related doc is published
.. _db-upgrade/test-assistance:
Assistance
~~~~~~~~~~
If you encounter issues or problems in the **test database**, please contact the Odoo Upgrade
Support:
#. Connect to our `Odoo Support page <https://www.odoo.com/help>`_.
#. Under the *Ticket Description* section, select *An issue related to my upgrade* ticket type.
.. image:: media/db-upgrade-test-assistance.png
:align: center
:alt: Selection of "An issue related to my upgrade" as Ticket Type in the support form on Odoo
.. warning::
If you choose another *Ticket Description* type, the request will be redirected to another
team than the upgrade one and will slow down the processing and response time.
#. Please provide as much detail as you can. Where applicable, illustrate the current and previous
flows with videos and/or screenshots. This will avoid clarifying questions and speed up the
resolution process significantly.
.. image:: media/db-upgrade-test-assistance-details.png
:align: center
:alt: "Detailed Description" field in the support form on Odoo
.. note::
* The purpose of the test phase is not to correct existing data or configurations in your
database.
* |assistance-contact|
.. _db-upgrade/steps-production:
The production launch
---------------------
.. _db-upgrade/production-live:
Production goes live
~~~~~~~~~~~~~~~~~~~~
The production upgrade request is when you decide to upgrade your current database with all your
production data (invoices, VAT returns, inventories, current orders) to a new version of your choice.
After your :ref:`tests <db-upgrade/steps-test>` are completed to your satisfaction, submit the
request to upgrade your production database via our `website form <https://upgrade.odoo.com>`_.
Select *Production* purpose.
.. image:: media/db-upgrade-production-purpose.png
:align: center
:alt: Selection of the "Production" purpose in the upgrade form on Odoo
.. danger::
Going into production without first testing may lead to:
- business interruptions (e.g. no longer having the possibility to validate an action)
- poor customer experiences (e.g. an eCommerce website that does not work correctly)
.. _db-upgrade/production-assistance:
Assistance
~~~~~~~~~~
If you encounter issues or problems in the **production database**, please contact the **Odoo
Support**:
#. Connect to our `Odoo Support page <https://www.odoo.com/help>`_.
#. Under the *Ticket Description* section, select the appropriate type related to your issue but
**do not select** the option *An issue related to my upgrade*.
.. note::
After upgrading to production, the support will be provided by the Support team instead of the
Upgrade team.
#. Please provide as much detail as you can. Where applicable, illustrate the current and previous
flows with videos and/or screenshots. This will avoid clarifying questions and speed up the
resolution process significantly.
.. image:: media/db-upgrade-production-assistance-details.png
:align: center
:alt: "Detailed Description" field in the support form on Odoo
.. warning::
If you choose *An issue related to my upgrade* as ticket type, the request will be redirected
to another team than the support one and will slow down the processing and response time.
.. _db-upgrade/faq:
FAQ
===
.. _db-upgrade/faq/why:
Why upgrade?
------------
* You benefit from the latest features of the :ref:`new major version
<db-upgrade/faq/release-notes>` released by Odoo.
* If you are in an :ref:`unsupported version <db-upgrade/supported-versions>`, you get a new version
with support.
.. _db-upgrade/faq/when:
When to upgrade?
----------------
Whenever you want. You can make your upgrade request as soon as a new version is released on our
`website form <https://upgrade.odoo.com>`_.
.. _db-upgrade/faq/availability:
Availability of the new version
-------------------------------
Please kindly note that as soon as we announce the release of a new major version (usually at the
end of year), the Upgrade Service team needs to adapt the upgrade scripts to it, which is why the
new version is not immediately available for existing databases.
.. _db-upgrade/faq/finalization:
Finalization of the upgrade (:abbr:`ETA (Estimated Time of Arrival)`)
---------------------------------------------------------------------
Unfortunately, it is impossible to give time estimates for every upgrade request. Odoo offers so
many possibilities (e.g. branding, workflows, customization, etc) that it can get tricky to upgrade,
and translate to the new structure. If you use multiple apps managing sensitive data (e.g.,
Accounting, Inventory, etc.), some cases may still require a human intervention, making the process
slower.
This is especially true during the first months following the release of a new major version, which
can significantly lengthen the upgrade delay.
In general, the smaller the database, the quickest the upgrade. A single-user database that uses
only CRM will be processed faster than a multi-company, multi-user database that uses Accounting,
Sales, Purchase, and Manufacturing.
So, in a nutshell, what can impact your upgrade lead time?
* Source & targeted versions
* Installed apps
* Volume of data
* Amount of customization (models, fields, methods, workflows, reports, website, etc.)
* Installation of new apps or configuration changes after the start of the test phase
Usually, the delays experienced during the first request (waiting time between the time you
submitted your first request for a test upgrade) can generally give you an idea of the time to wait
for the production request.
.. _db-upgrade/faq/custom-modules:
Upgrade of the custom modules
-----------------------------
As stated in our :doc:`/legal/terms/enterprise`, section :ref:`charges_standard`, this optional
service is subject to additional fees.
If you have a custom code, you can choose to have it upgraded by our services, by one of our
`partners <https://www.odoo.com/partners>`_ or you can do it yourself.
.. note:: |assistance-contact|
.. _db-upgrade/faq/editions-change:
Editions change (from Community to Enterprise)
----------------------------------------------
An upgrade does not cover a change of `Editions <https://www.odoo.com/page/editions>`_
.. note:: |assistance-contact|
.. _db-upgrade/faq/hosting-types-switch:
Switching the hosting types (Self-hosted vs Online vs Odoo.sh)
--------------------------------------------------------------
An upgrade does not cover a change of `Hosting types <https://www.odoo.com/page/hosting-types>`_.
Open the following link to get :doc:`more information about how to change your hosting type
<hosting_changes>`.
.. note:: |assistance-contact|
.. _db-upgrade/faq/release-notes:
Release Notes by version
------------------------
Open our `Release Note <https://www.odoo.com/page/release-notes>`_ page to get a summary of the new
features and improvements made in each version.
.. _db-upgrade/assistance:
Assistance
==========
.. _db-upgrade/contact:
Contact our Upgrade service support
-----------------------------------
Should you have any more questions about the upgrade, do not hesitate to send a message to `Odoo
Upgrade Team <mailto:upgrade@odoo.com>`_. We will be very pleased to answer it as soon as possible.
.. _db-upgrade/supported-versions:
Supported versions
------------------
Please note that Odoo provides support and bug fixing only for the three last major versions of Odoo.
This is a factor to take into consideration before upgrading. If you are on an older version, we
suggest you to prefer the most recent version to benefit from a longer support (before having to
upgrade again).
You can get more information about our :doc:`supported versions <supported_versions>`.

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,143 +0,0 @@
=================================
Online (SaaS) database management
=================================
To manage a database, sign in to https://www.odoo.com and access the `database management page
<https://www.odoo.com/my/databases>`_ by clicking on the user icon, then on *My Databases*.
.. image:: online/my-databases.png
:align: center
:alt: Clicking on the user icon opens a drop-down menu. "My databases" button is highlighted.
.. note::
Make sure you are connected as the administrator of the database you want to manage.
.. image:: online/dropdown-menu.png
:align: right
:alt: Clicking on the gear icon opens the drop-down menu.
Open the drop-down menu next to the database you want to manage by clicking on the gear icon.
Several actions are available:
- :ref:`online/upgrade`
- :ref:`online/duplicate`
- :ref:`online/rename`
- :ref:`online/download`
- :ref:`online/domains`
- :ref:`online/tags`
- :ref:`online/delete`
- :ref:`online/contact-support`
- :ref:`online/users`
.. _online/upgrade:
Upgrade
=======
Upgrade the database to the latest Odoo version.
.. warning::
Upgrading a database to a newer version of Odoo is a complex operation that requires time and
caution. It is essential to test the upgrade before upgrading the production database.
.. seealso::
- :doc:`../upgrade/process`
.. _online/duplicate:
Duplicate
=========
Make an exact copy of the database to be able to perform testing without compromising the daily
operations.
.. important::
- By checking *For testing purposes*, all external communication (emails, payments, delivery
orders, etc.) are disabled by default on the duplicated database.
- Duplicate databases expire automatically after 15 days.
.. _online/rename:
Rename
======
Rename the database and its URL.
.. _online/download:
Download
========
Download instantly a ZIP file with a backup of the database.
.. note::
Databases are backed up daily according to the `Odoo Cloud SLA
<https://www.odoo.com/cloud-sla>`_.
.. _online/domains:
Domains
=======
Configure custom domains to access the database via another URL.
.. seealso::
- :doc:`/applications/websites/website/publish/domain_name`
.. _online/tags:
Tags
====
Add tags to sort your databases out. You can search the tags in the search bar.
.. _online/delete:
Delete
======
Delete a database instantly.
.. danger::
Deleting a database means that all data is permanently lost. The deletion is instant and for all
users. It is recommended to create a backup of the database before deleting it.
Read carefully the warning message that pops up and proceed only if you fully understand the
implications of deleting a database:
.. image:: online/delete.png
:align: center
:alt: A warning message is prompted before deleting a database.
.. note::
- Only an administrator can delete a database.
- The database name is immediately available for a new database.
- It is not possible to delete a database if it is expired or linked to a subscription. If
needed, please get in touch with `Odoo Support <https://www.odoo.com/help>`_.
- To delete your account, please get in touch with `Odoo Support <https://www.odoo.com/help>`_.
.. _online/contact-support:
Contact Support
===============
Access the Odoo `support page <https://www.odoo.com/help>`_ with your database's details already
pre-filled.
.. _online/users:
Invite / Remove Users
=====================
To invite users, fill out the email address of the new user and click on *Invite*. To add multiple
users, click on *Add more users*.
.. image:: online/invite-users.png
:align: center
:alt: Clicking on "Add more users" adds additional email fields.
To remove users, select the users to remove and click on *Remove*.
.. seealso::
- :doc:`/applications/general/users/manage_users`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

View File

@@ -9,4 +9,5 @@ Advanced
advanced/containers
advanced/submodules
advanced/upgrade_your_database
advanced/frequent_technical_questions

View File

@@ -1,7 +1,9 @@
=======================
Odoo.sh version upgrade
=======================
=====================
Upgrade your database
=====================
.. _odoosh-advanced-upgrade_your_database:
Download and Upload your database
=================================

View File

@@ -184,4 +184,4 @@ as you can only have one database linked per subscription.
If you plan to make it your production,
unlink your former database from the subscription, and register the newly imported database.
Read the :doc:`database registration documentation <../../maintain/on_premise>` for instructions.
Read the :ref:`database registration documentation <db_premise>` for instructions.

View File

@@ -1,12 +0,0 @@
:nosearch:
=======
Upgrade
=======
.. toctree::
upgrade/process
upgrade/odoo_sh
upgrade/service_level
upgrade/faq

View File

@@ -1,171 +0,0 @@
.. |assistance-contact| replace::
If you need Odoo assistance on this matter, please get in touch with your Odoo Account Manager or
our `Sales department`_.
.. _Sales department: mailto:sales@odoo.com
===
FAQ
===
.. _upgrade-faq/why:
Why upgrade
===========
* You benefit from the latest features of the :ref:`new major version
<upgrade-faq/release-notes>` released by Odoo.
* If you are in an :ref:`unsupported version <upgrade/supported-versions>`, you get a new version
with support.
.. _upgrade-faq/when:
When to upgrade
===============
Whenever you want. You can make your upgrade request as soon as a new version is released or when
your version turns unsupported, and you still wish to enjoy support.
.. _upgrade-faq/availability:
Availability of the new version
===============================
As soon as Odoo announces the release of a new major version, you can create a test upgrade request
to try the latest version. Please note that at this point, the upgrade scripts will only have been
tested with demo data. Please report any issue you might encounter while testing via the `Odoo
Support page <https://www.odoo.com/help>`_ and make sure to be happy with your test version before
requesting the upgrade of your database in production.
.. _upgrade-faq/duration:
Duration of the upgrade
=======================
It is impossible to give time estimates for every upgrade request.
In general, the "smaller" the database, the quickest the upgrade request is completed. A single-user
database that uses only CRM will be processed faster than a multi-company, multi-user database that
uses Accounting, Sales, Purchase, and Manufacturing.
You can expect the time it takes for the platform to upgrade the test database will be similar to
the production upgrade.
.. _upgrade-faq/project:
The upgrade project
===================
It depends on the user involvement (the time spent on testing, reporting problems, etc.) and the
issues encountered that might need to be addressed by our technical team.
So, in a nutshell, what can impact your upgrade lead time?
* Source & targeted versions
* Installed apps
* Volume of data
* Amount of customization (models, fields, methods, workflows, reports, website, etc.)
* Installation of new apps or configuration changes after the start of the test phase
* User commitment
.. _upgrade-faq/custom-modules:
Upgrade of the custom modules
=============================
As stated in our :doc:`/legal/terms/enterprise`, section :ref:`charges_standard`, this optional
service is subject to additional fees.
Depending on your situation, the custom code could be upgraded by our services, by one of our
partners, or you can do it yourself.
.. note:: |assistance-contact|
.. _upgrade-faq/editions-change:
Editions change (from Community to Enterprise)
==============================================
The upgrade always returns an Enterprise edition of Odoo, whether the database you sent was a
community or enterprise edition. It is required to have an enterprise subscription to upgrade.
.. note:: |assistance-contact|
.. seealso::
- `Editions <https://www.odoo.com/page/editions>`_
.. _upgrade-faq/hosting-types-switch:
Switching the hosting types (Self-Hosting vs. Online Hosting - SaaS vs. Cloud Platform - Odoo.sh)
=================================================================================================
An upgrade does not cover a change of `Hosting types <https://www.odoo.com/page/hosting-types>`_.
Open the following link to get :doc:`more information about how to change your hosting type
<../maintain/hosting_changes>`.
.. note:: |assistance-contact|
.. _upgrade-faq/release-notes:
Release Notes by version
========================
Open our `Release Note <https://www.odoo.com/page/release-notes>`_ page to get a summary of the new
features and improvements made in each version.
How long is my test available for
---------------------------------
An Odoo Online (SaaS) test database is available for one month by default. We can extend this trial
period upon request. For Odoo.sh or on-premise, there is no restriction.
How many tests to perform before upgrading to production?
---------------------------------------------------------
As many as needed. When you are comfortable with the database, run a last test upgrade 48 hours
before requesting your production upgrade and test your workflows one last time.
How to/Where to report upgrade issues?
--------------------------------------
If you encounter issues during the upgrade process, please contact the Odoo Support through the
`Odoo Support page <https://www.odoo.com/help>`_
- To report an issue discovered during the testing phase, please select **An issue related to my
upgrade (test phase)**.
- To report an issue discovered post-upgrade, please select **An issue related to my upgrade
(production)**
Upgrading to production
-----------------------
Once you have completed testing and are happy with the result, you decide on a date and time when
you stop users from accessing Odoo, freeze all data entries, and create an upgrade request for the
production upgrade.
How is my data handled in the Upgrade Platform?
-----------------------------------------------
The Odoo Upgrade platform uses the same Privacy Policy as the rest of Odoo.com services.
Your data is hosted on servers that follow our security guidelines, namely:
- SSL - All web connections to client instances are protected with 256-bit SSL encryption
(HTTPS with a 2048-bit modulus SSL certificate), and running behind Grade A SSL stacks. All our
certificate chains are using SHA-2 already.
- Safe System - Our servers are running recent Linux distribution with up-to-date security patches,
with firewall and intrusion countermeasures (not disclosed for obvious reasons).
Servers are located at the same locations as our Cloud providers with the following services:
- Restricted perimeter, physically accessed by authorized data center employees only
- Physical access control with security badges or biometrical security
- Security cameras monitoring the data center locations 24/7
- Security personnel on-site 24/7
The uploaded and migrated databases uploaded to the Upgrade platform are kept for up to 3 months and
are permanently deleted following that period.
You can learn more about privacy and data handling at Odoo by visiting our `General Data Protection
Regulation page <https://www.odoo.com/gdpr>`_.

View File

@@ -1,256 +0,0 @@
.. |assistance-contact| replace::
If you need Odoo assistance on this matter, please get in touch with your Odoo Account Manager or
our `Sales department`_.
.. _Sales department: mailto:sales@odoo.com
===============
Upgrade process
===============
.. _upgrade/overview:
Overview
========
An upgrade is switching to a newer version of Odoo (e.g., Odoo 14.0 to Odoo 15.0).
An upgrade does not cover:
* Changing :ref:`editions <upgrade-faq/editions-change>` (i.e., Community to Enterprise edition)
* Switching :ref:`hosting type <upgrade-faq/hosting-types-switch>` (i.e., On-Premise to Online or
Odoo.sh)
* Migration from another ERP to Odoo
.. note:: |assistance-contact|
.. _upgrade/process-workflow:
Process workflow
----------------
The upgrade process in a nutshell:
#. You create a test upgrade request.
#. | Odoo processes the request:
| This happens via an automated process that runs the database through an upgrade script and
takes between 20 and 120 minutes. Only if an issue(s) arises will we have to intervene
manually and adjust the script specifically to your database until the upgrade succeeds.
#. Odoo delivers a test database.
#. You test your database for possible discrepancies (see :ref:`upgrade/test-guidance`)
#. If there are any discrepancies, you report them to the Upgrade support team via the help portal
(see :ref:`upgrade/test-assistance`).
#. We will fix the issues and send you a new test database.
#. Once you have completed the testing and are happy with the result, you decide on a date and time
when you stop users from accessing Odoo, freeze all data entries, and create an upgrade request
for the production upgrade.
#. Odoo delivers the production database through the automated process.
#. You restore it in your Production environment a few short hours later and continue working on the
newly upgraded database (this is done automatically on SaaS).
.. _upgrade/get-started:
Get started
===========
The upgrade process varies depending on where your database is hosted.
.. _upgrade/online:
Online Hosting (SaaS)
---------------------
The upgrade request is made via your `database manager <https://www.odoo.com/my/databases>`_.
.. image:: process/online-access-databases.png
:align: center
:alt: Click on the profile button then on "My Databases"
.. image:: process/online-upgrade-button.png
:align: center
:alt: Click on the settings button next to your database, then on "Upgrade"
.. _upgrade/odoo-sh:
Cloud Platform (Odoo.sh)
------------------------
:doc:`odoo_sh`
.. _upgrade/on-premise:
Self-Hosting (On-Premise)
-------------------------
There are two possibilities:
#. Via `Odoo Upgrade service <https://upgrade.odoo.com>`_
#. | For technically advanced users and partners, via the following command line on the machine
where your database is hosted:
| ``python <(curl -s https://upgrade.odoo.com/upgrade) test -d <your db name> -t <target
version>``
The above command will dump your database to a file, send it to the upgrade platform for an upgrade,
display you the live logs, and restore the upgraded database back on your server as a duplicate test
database.
.. _upgrade/testing-phase:
Testing Phase (pre-production phase)
====================================
This phase allows you to review an upgraded version of your database without affecting your
production database in any way.
We suggest that you run the test upgrade process at least once, but you can do it as many times as
you need (one at a time).
Once you receive your upgraded test database, check that all data, processes, and functionality are
still correct and working as expected.
If you do find discrepancies, report your issues (see :ref:`upgrade/test-assistance`) and request
a new test database (see :ref:`upgrade/test-db-request`) when the reported issues are fixed in
the upgrade script.
If you do not find any discrepancies, you'll be able to move on to the upgrade of your
:ref:`production database <upgrade/production-live>`.
.. _upgrade/test-db-request:
Request a test database
-----------------------
When filling the `website form <https://upgrade.odoo.com>`_, select *Testing* purpose.
.. image:: process/test-purpose.png
:align: center
:alt: Selection of the "Testing" purpose in the upgrade form on Odoo
.. _upgrade/test-guidance:
Test guidance
-------------
Every business and organization has its own operational needs and has to test its specific Odoo
instance respectively. We recommend you look at `the test scenario
<https://docs.google.com/document/d/1ypNs7JKPOsjNbKpdiKFH7Al6g6whZ9jr7f7duAQ5E1w/>` for further
information.
.. todo:: change link "test scenario" once the related doc is published
.. _upgrade/test-assistance:
Assistance
----------
If you encounter an issue in the **test database**, please get in touch with Odoo Upgrade Support
via the `Odoo Support page <https://www.odoo.com/help>`_.
Under the *Ticket Description* section, select *An issue related to my upgrade* ticket type.
.. image:: process/test-assistance.png
:align: center
:alt: Selection of "An issue related to my upgrade" as Ticket Type in the support form on Odoo
.. warning::
If you choose another *Ticket Description* type, the request will be redirected to another
team. This will slow down the processing and response time.
Please provide as much detail as you can (i.e., videos and screenshots to illustrate your issue).
This will avoid clarifying questions and speed up the resolution process significantly.
.. image:: process/test-assistance-details.png
:align: center
:alt: "Detailed Description" field in the support form on Odoo
.. note::
* The purpose of the test phase is not to correct existing data or configurations in your
database.
* |assistance-contact|
.. _upgrade/steps-production:
The production launch
---------------------
.. _upgrade/production-live:
Production goes live
~~~~~~~~~~~~~~~~~~~~
The production upgrade request is when you decide to upgrade your current database with all your
production data (invoices, VAT returns, inventories, current orders) to a new version of your
choice.
After your :ref:`tests <upgrade/testing-phase>` are completed to your satisfaction, submit the
request to upgrade your production database via our `website form <https://upgrade.odoo.com>`_.
Select *Production* purpose.
.. image:: process/production-purpose.png
:align: center
:alt: Selection of the "Production" purpose in the upgrade form on Odoo
.. danger::
Going into production without first testing may lead to:
- business interruptions (e.g., no longer having the possibility to validate an action)
- poor customer experiences (e.g., an eCommerce website that does not work correctly)
.. _upgrade/production-assistance:
Assistance
~~~~~~~~~~
If you encounter issues or problems in the **production database**, please get in touch with **Odoo
Support**:
#. Connect to our `Odoo Support page <https://www.odoo.com/help>`_.
#. Under the *Ticket Description* section, select the appropriate type related to your issue but
**do not select** the option *An issue related to my upgrade*.
.. note::
After upgrading to production, the support will be provided by the Support team instead of the
Upgrade team.
#. Please provide as much detail as you can (i.e., videos and screenshots to illustrate your issue).
This will avoid clarifying questions and speed up the resolution process significantly.
.. image:: process/production-assistance-details.png
:align: center
:alt: "Detailed Description" field in the support form on Odoo
.. warning::
If you choose *An issue related to my upgrade* as ticket type, the request will be redirected
to another team than the support one and will slow down the processing and response time.
.. _upgrade/assistance:
Assistance
==========
.. _upgrade/contact:
Contact our Upgrade service support
-----------------------------------
Should you have any more questions about the upgrade, do not hesitate to send a message to `Odoo
Upgrade Team <mailto:upgrade@odoo.com>`_. We will be happy to answer it as soon as possible.
.. _upgrade/supported-versions:
Supported versions
------------------
Please note that Odoo provides support and bug fixing only for the three last major versions of
Odoo.
This is a factor to take into consideration before upgrading. If you are on an older version, we
suggest you to prefer the most recent version to benefit from longer support (before having to
upgrade again).
You can get more information about our :doc:`supported versions <../maintain/supported_versions>`.
.. seealso::
- :doc:`faq`
- :doc:`odoo_sh`
- :doc:`service_level`
- :doc:`../maintain/supported_versions`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,37 +0,0 @@
.. |assistance-contact| replace::
If you need Odoo assistance on this matter, please get in touch with your Odoo Account Manager or
our `Sales department`_.
.. _Sales department: mailto:sales@odoo.com
=======================
Service Level Agreement
=======================
What is covered by the Enterprise Licence?
==========================================
Databases hosted on Odoos Cloud platforms (Saas and Odoo.sh) or On-Premise (Self-Hosting) enjoy the
following service at all times.
The upgrade of:
* standard applications
* Studio customization (as long as the Studio app is still active)
* customizations done by our consulting and developer services *if* they are covered by a
Maintenance of Customisations subscription
The Upgrade Service is limited to your database's technical conversion and adaptation (standard
modules and data) to make it compatible with the targeted version.
What upgrading does NOT cover
=============================
* The cleaning of pre-existing data & configuration while upgrading
* Any new developments and/or upgrades of your own :ref:`custom modules
<upgrade-faq/custom-modules>`
* `Training <https://www.odoo.com/learn>`_ on the latest version
You can get more information about your Enterprise Licence on our :ref:`Odoo Enterprise Subscription
Agreement <upgrade>` page.
.. note:: |assistance-contact|

View File

@@ -22,6 +22,10 @@ selected at the creation of the database.
To install a new package, go to :menuselection:`Accounting --> Configuration --> Fiscal
Localization`, click on **Install More Packages**, and install your country's module.
.. image:: media/fiscal_localization_packages_modules.png
:align: center
:alt: Install the appropriate module as fiscal localization package in Odoo Accounting.
Once done, select your country's package, and click on *Save*.
.. image:: media/fiscal_localization_packages_selection.png

View File

@@ -6,6 +6,10 @@ Odoo Accounting can be used in many countries out of the box by installing the a
Here is a list of all :doc:`Fiscal Localization Packages <fiscal_localization_packages>` that are
available on Odoo.
.. image:: media/fiscal_localization_packages_modules.png
:align: center
:alt: Odoo Accounting.
Fiscal Localization Packages available
======================================

View File

@@ -43,52 +43,6 @@ In Odoo
and the zip code). Go to :menuselection:`Settings --> Users & Companies --> Companies`
to open and edit your Company record.
Automatically post taxes to the correct Tax Payable account
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* For each one of your companies that uses TaxCloud, it might be necessary to create a
**User-Defined Default** so that the new taxes generated by the TaxCloud integration are created
with the correct Tax Payable account:
.. warning::
A User-Defined Default impacts all records at creation. It means that **every** new tax will be
set up to record income in the specified Tax Payable account, unless the tax is manually edited
to specify a different income account (or there exists another User-Defined Default that takes
precedence).
* In :menuselection:`Accounting --> Configuration --> Chart of Accounts`, select the Tax Payable
account for the company. Take note of the account's ``id`` in the URL string.
.. image:: taxcloud/user-default-find-account-id.png
:alt: The account's ID can be found in the URL string as 'id=...'.
:align: center
* Activate the :ref:`developer mode <developer-mode>`, then go to
:menuselection:`Settings --> Technical --> Actions --> User-Defined Defaults`, and
click on *Create*.
* Click on *Field*, then, in the drop-down menu, on *Search More*.
.. image:: taxcloud/user-default-search-field.png
:alt: Click on 'Search More' in the 'Field' drop-down menu.
:align: center
* In the pop-up's search box, filter on the model ``tax.repartition.line`` and the field ``account``.
Select the ``account`` field of the ``tax.repartition.line`` model.
.. image:: taxcloud/user-default-select-field.png
:alt: Select the 'account' field of the 'tax.repartition.line' model.
:align: center
* In the **Default Value** field, enter the ID of the company's Tax Payable account.
Select the company for which this configuration should apply in the *Company* field.
Click *Save*.
.. image:: taxcloud/user-default-enter-default-account-id.png
:alt: Enter the ID of the company's Tax Payable account.
:align: center
How it works
============

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -33,45 +33,23 @@ Activate through a browser extension
:align: center
:alt: View of odoos debug icon in a chromes toolbar
Activate through the command palette
====================================
The command palette tool has a command to activate the debug mode: open it with
the keyboard shortcut `ctrl+k`, then type `debug`: a command will show up to
activate the debug mode.
.. image:: developer_mode/command_palette.png
:align: center
:alt: Command palette with debug command
Activate through the URL
========================
In the URL, add ``?debug=1`` or ``?debug=true`` after *web*. To deactivate the
debug mode, add `?debug=0` instead.
In the URL add ``?debug=1`` or ``?debug=true`` after *web*.
.. image:: url.png
:align: center
:alt: Overview of an url with the debug mode command added in Odoo
.. tip::
Additional modes are available for developers: `?debug=assets` enables the
:ref:`assets mode <frontend/framework/assets_debug_mode>`, and `?debug=tests` enables
the :ref:`tests mode <frontend/framework/tests_debug_mode>`.
Developers: type ``?debug=assets`` and activate the mode with assets.
Locate the mode tools
=====================
The Developer mode tools can be accessed from the *Open Developer Tools* button,
located on the header of your pages. This menu contains additional tools that
are useful to understand or edit technical data, such as the views or the actions.
It contains some useful menu items such as:
- edit action
- manage filters
- edit the current view
- see the `fields view get`
- and much more.
The Developer mode tools can be accessed from the *Open Developer Tools* button, located on the
header of your pages.
.. image:: button_location.png
:align: center

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -40,7 +40,7 @@ Install the Outlook Plugin
:align: center
:alt: Custom add-ins in Outlook
#. Enter the following URL `https://download.odoo.com/plugins/outlook/manifest.xml` and press
#. Enter the following URL `https://download.odoo.com/plugins/v15/outlook/manifest.xml` and press
*OK*.
.. image:: outlook/enter-add-in-url.png
@@ -119,4 +119,4 @@ time, it's possible to add it next to the other default actions.
.. image:: outlook/odoo-outlook-shortcut.png
:align: center
:alt: Odoo for Outlook customized action
:alt: Odoo for Outlook customized action

View File

@@ -9,5 +9,3 @@ Overview
overview/getting_started
overview/register
overview/https
overview/epos_ssc

View File

@@ -1,87 +0,0 @@
=========================================
Self-signed certificate for ePOS printers
=========================================
ePos printers are designed specifically to work with your Point of Sale system, which sends the
tickets directly to the printer.
Some models don't require an IoT box, but the connection between your web browser and the printer
may require a :doc:`secure connection with the HTTPS protocol <https>`. If so, a self-signed
certificate is necessary to use your printer.
.. note::
Please check the following list of compatible `Epson ePOS printers
<https://c4b.epson-biz.com/modules/community/index.php?content_id=91>`_. This list includes the
following models:
- TM-H6000IV-DT (Receipt printer only)
- TM-T70II-DT
- TM-T88V-DT
- TM-L90-i
- TM-T20II-i
- TM-T70-i
- TM-T82II-i
- TM-T83II-i
- TM-T88V-i
- TM-U220-i
- TM-m10
- TM-m30
- TM-P20 (Wi-Fi® model)
- TM-P60II (Receipt: Wi-Fi® model)
- TM-P60II (Peeler: Wi-Fi® model)
- TM-P80 (Wi-Fi® model)
Generate a Self-signed certificate
==================================
Access your ePOS printer's settings with your web browser by navigating to its IP address, for
example, `http://192.168.1.25`.
.. note::
- The printer automatically prints the IP address during startup.
- We recommend assigning a **fixed IP address** to the printer from the network router.
Go to :menuselection:`Authentication --> Certificate List` and create a new **Self-Signed
Certificate**.
- **Common Name**: the IP address of the ePos Printer, for example, `192.168.1.25`
- **Validity Period**: `10`
Create and reboot the printer, go to :menuselection:`Security --> SSL/TLS`, and check if
**Selfsigned Certificate** is selected.
Export the Self-signed certificate
==================================
To avoid having to accept the self-signed certificate several times, you can export it and then
import it to your web browser or mobile device.
To do so, access your ePOS printer's settings with your web browser by navigating to its IP address,
for example, `https://192.168.1.25`. Then, accept the self-signed certificate.
.. note::
Note that the protocol is now **HTTPS**.
Click on :menuselection:`Connection is not secure --> Certificate is not valid`.
.. image:: epos_ssc/browser-warning.png
:align: center
:alt: The web browser indicates that the connection to the printer is not secure.
Go to the *Details* tab and click on **Copy to file**.
Select X.509 in base 64 and save it.
Import the Self-signed certificate to Windows (Using Chrome)
============================================================
In your Chrome browser, go to :menuselection:`Settings --> Privacy and security --> Security -->
Manage certificates`
Go to the *Trusted Root Certification Authorities* tab and click on **Import** and select
your previous file. Accept all warnings and restart your browser.
Import the Self-signed certificate to your Android device
=========================================================
On your Android device, open the settings and search for *certificate*. Then, click on **Certificate
AC** (Install from device storage), and select the certificate.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,22 +0,0 @@
=========================
Secure connection (HTTPS)
=========================
If **Direct Devices** is enabled in a Point of Sale settings (for example, if you use an ePos
printer), HTTP becomes the default protocol.
Force your Point of Sale to use a secure connection (HTTPS)
===========================================================
Add a new **key** in the **System Parameters** to force your Point of Sale to use a secure
connection with the HTTPS protocol.
To do so, activate the :ref:`developer mode <developer-mode>`, go to :menuselection:`Settings -->
Technical --> Parameters --> System Parameters`, then create a new parameter, add the following
values and click on *Save*.
- **Key**: `point_of_sale.enforce_https`
- **Value**: `True`
.. seealso::
- :doc:`epos_ssc`

View File

@@ -9,4 +9,3 @@ Manage your products
products/import
products/variants
products/product_images

View File

@@ -1,131 +0,0 @@
===================================================
Automatically get product images with Google Images
===================================================
The product images are very useful in Odoo, for example, to quickly find a product or check if you
scanned the right one, but it can be a bit painful to set up especially if you have a lot of
products. **Google Custom Search** allows finding images automatically for your product, based on
their barcode, keeping your focus on what matters in your business.
.. _product_images/configuration:
Configuration
=============
This functionnality requires configuration both on Google and on Odoo.
With a free Google account, you can get up to 100 free images per day. If you need a higher rate,
you'll have to upgrade to a billing account.
.. _product_images/google-api-dashboard:
Google API dashboard
--------------------
#. Go to the `Google Cloud Platform API & Services <https://console.developers.google.com/>`_ page
to generate Google Custom Search API credentials. Log in with your Google account.
#. Select or create an API project to store the credentials. Give it an explicit name
(e.g. Odoo Images).
#. In the credentials section, click on **Create Credentials** and select **API Keys**.
.. image:: product_images/gcp-api-services.png
:align: center
:alt: API & Services page on Google Cloud Platform
#. Save your **API Key**. You'll need it for the next step in Odoo!
#. Use the search bar to look for **Google Custom Search API** and select it.
.. image:: product_images/gcp-search.png
:align: center
:alt: Search bar containing "Custom Search API" on Google Cloud Platform
#. Enable the API.
.. image:: product_images/gcp-custom-search-api.png
:align: center
:alt: "Custom Search API" tile with Enable button highlighted on Google Cloud Platform
.. _product_images/google-pse-dashboard:
Google Programmable Search dashboard
------------------------------------
#. Go to `Google Programmable Search Engine <https://programmablesearchengine.google.com/>`_ and
click on **Get Started**. Log in with your Google account.
.. image:: product_images/google-pse.png
:align: center
:alt: Google Programmable Search Engine page with the **Get Started** button on the up-right
of the page
#. Fill the language and the name of the search engine. Give it an explicit name
(e.g. Odoo Images).
.. note::
Google doesn't allow to create a search engine without having entered at least one specific
site to search on. You can put any website (e.g. www.google.com) for this step, we will
remove it later.
#. Validate the form by clicking on **Create**. Then, go to the edition mode of the search engine
that you created (either by clicking on **Control Panel** on the confirmation page or by
clicking on the name of your Search Engine on the Home page).
#. In the **basics** tab, make sure to enable **Image search**, **SafeSearch** and
**Search the entire web**.
.. note::
Once **Search the entire web** is enabled, you can safely delete the site that you put at the
previous step.
#. Save your **Search Engine Id**. Youll need it for the next step in Odoo!
.. _product_images/setup-in-odoo:
Odoo
----
#. Go to :menuselection:`Settings --> General Settings --> Integrations`,
activate **Google Images** and save.
#. Go back to :menuselection:`Settings --> General Settings--> Integrations`, enter your **API Key**
and **Search Engine ID** in **Google Images** settings and save again.
.. _product_images/get-product-images:
Automatically get your product images in Odoo
=============================================
The action to automatically get your product images in Odoo appears in any Products or Product
Variants list view. Here is a step-by-step guide from the Inventory app.
#. Go to the Products menu (:menuselection:`Products --> Products` or :menuselection:`Products -->
Product Variants`) from any application that uses products like Inventory or Sales.
#. On the list view, select the products that needs an image.
.. important::
Only the 10,000 first selected products or product variants will be processed.
.. note::
- Only the products or product variants with a barcode and without an image will be processed.
- If you select a product that has one or more variants from the Products view, each variant
matching the previous criteria will be processed.
#. In the action menu, select **Get Pictures from Google Images** and validate by clicking on
**Get picture**.
#. You should see your images appearing incrementally.
.. note::
- Only the 10 first images are fetched immediatly. If you selected more than 10, the rest will
be fetched as a background job.
- The background job process about 100 images in a minute. If you reach the quota authorized
by Google (either with a free or a paid plan), the background job will put itself on hold
for 24 hours and continue where it stopped the day before.
.. seealso::
- `Create, modify, or close your Google Cloud Billing account
<https://cloud.google.com/billing/docs/how-to/manage-billing-account>`_

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -77,7 +77,7 @@ We can now apply the redirection from your domain name's manager account:
.. note:: Here are some specific guidelines to create a CNAME record:
- `GoDaddy <https://www.godaddy.com/help/add-a-cname-record-19236>`__
- `GoDaddy <https://be.godaddy.com/fr/help/add-a-cname-record-19236>`__
- `Namecheap <https://www.namecheap.com/support/knowledgebase/article.aspx/9646/10/how-can-i-set-up-a-cname-record-for-my-domain>`__
- `OVH <https://www.ovh.co.uk/g1519.exchange_20132016_how_to_add_a_cname_record>`__

View File

@@ -220,7 +220,7 @@ Python comes with its own package manager: `pip
a single command.
#. Download and install the recommended release (`see README file
<https://github.com/odoo/documentation/tree/master/README.md>`_) of **Python 3** on your machine.
<https://github.com/odoo/documentation/tree/15.0/README.md>`_) of **Python 3** on your machine.
#. Make sure to have **pip** installed on your machine (on Windows, you can install pip alongside
Python).
#. Execute the following commands in a terminal to verify that both installations finished

View File

@@ -36,8 +36,8 @@ A reference to the rendered :file:`prices.html` and :file:`variants.html` could
#. Absolute:
- ``https://odoo.com/documentation/master/applications/sales/sales/products_prices/prices.html``
- ``https://odoo.com/documentation/master/applications/sales/sales/products_prices/products/variants.html``
- ``https://odoo.com/documentation/15.0/applications/sales/sales/products_prices/prices.html``
- ``https://odoo.com/documentation/15.0/applications/sales/sales/products_prices/products/variants.html``
#. Relative:

View File

@@ -131,7 +131,7 @@ to make sure we start on the same page during the development of our new applica
https://en.wikipedia.org/wiki/Multitier_architecture
.. _Python tutorial:
https://docs.python.org/3.7/tutorial/
https://docs.python.org/3.6/tutorial/
.. _two versions:
https://www.odoo.com/page/editions

View File

@@ -161,7 +161,7 @@ Install the dependencies
Python
------
Odoo requires Python 3.7 or later, if your computer is up-to-date you should already be at this
Odoo requires Python 3.6 or later, if your computer is up-to-date you should already be at this
version or higher.
You can check your Python version with:

View File

@@ -9,4 +9,3 @@ API
api/odoo
api/iap
api/extract_api

View File

@@ -1,555 +0,0 @@
:code-column:
===============
Extract API
===============
Odoo provides a service allowing you to automate the processing of your invoices. The service scans your document using an Optical
Character Recognition (OCR) engine and then uses AI-based algorithms to extract the fields of interest such as the total, the due date, or
the invoice lines. More functional information can be found on the `demo page <https://www.odoo.com/page/invoice-automation>`_.
This service is a paid service. Each invoice processing will cost you one credit. Three different sized packs can be bought on `iap.odoo.com <https://iap.odoo.com/iap/in-app-services/259?sortby=date>`_.
You can either use this service directly in the Odoo Accounting App or through the API. The Extract API which is detailed in the next section
allows you to integrate our service directly into your own projects.
Invoices
========
The extract API use the JSON-RPC2_ protocol. The diffent routes are located at the following address: **https://iap-extract.odoo.com**.
Expected successful flow
------------------------
1. Call :ref:`webservices/extract_api/invoice_parse` to submit your invoices (one call for each invoice). On success, you receive a `document_id` in the response.
2. You then have to regularly poll :ref:`webservices/extract_api/invoice_get_results` to get the document's parsing status.
3. Once the result received, you can validate it by calling :ref:`webservices/extract_api/invoice_validate` and sending the expected values.
This step is optional but greatly helps the system to improve.
These 3 routes are detailed in this :ref:`section <webservices/extract_api/routes>`. The HTTP POST method should be used for all of them. A python implementation of the full flow
can be found :download:`here <extract_api/implementation.py>` and a token for integration testing is provided in the :ref:`integration testing section <webservices/extract_api/integration_testing>`.
.. _webservices/extract_api/routes:
Routes
------
.. _webservices/extract_api/invoice_parse:
``/iap/invoice_extract/parse``
''''''''''''''''''''''''''''''
Description
^^^^^^^^^^^
Request a processing of the document from the OCR. The route will return a `document_id` you can use to obtain the result of your request.
Request Body
^^^^^^^^^^^^
``jsonrpc`` (required)
Must be exactly “2.0”.
``method`` (required)
Must be “call”.
``id`` (required)
An identifier established by the client. It allows the client to keep track of which response goes with which request. This makes asynchronous calls easier.
``params``
``account_token`` (required)
The token of the account from which credits will be taken. Each successful call costs one token.
``version`` (optional)
The version will determine the format of your requests and the format of the server response. Some results can be unavailable in older versions. For the current
version 1.2.0, send 120. If not specified, the latest version will be used.
``documents`` (required)
The invoice must be provided as a string in the ASCII encoding. The list should contain only one string. If multiple strings are provided only the first string
corresponding to a pdf will be processed. If no pdf is found, the first string will be processed. This field is a list only for legacy reasons. The supported extensions
are *pdf*, *png*, *jpg* and *bmp*.
``user_infos`` (required)
Information concerning the person to whom the invoice is intended. This information is not required in order for the service to work but it greatly improves the quality of the result.
``user_company_vat`` (optional)
VAT number of the client.
``user_company_name`` (optional)
Name of the clients company.
``user_company_country_code`` (optional)
Country code of the client. Format: `ISO3166 alpha-2 <https://www.iban.com/country-codes>`_.
``user_lang`` (optional)
The client language. Format: *language_code + _ + locale* (ex: fr_FR, en_US).
``user_email`` (optional)
The client email.
.. rst-class:: setup doc-aside
.. switcher::
.. code-block:: text
{
"jsonrpc": string,
"method": string,
"params": {
"account_token": string (hex),
"version": int,
"documents": [string],
"user_infos": {
"user_company_vat": string,
"user_company_name": string,
"user_company_country_code": string,
"user_lang": string,
"user_email": string,
},
},
"id": string (hex),
}
Response
^^^^^^^^
``jsonrpc``
A string specifying the version of the JSON-RPC protocol. It will be “2.0”.
``id``
The identifier you set in the request body.
``result``
``status_code``
|STATUS_CODE|
``status_msg``
|STATUS_MSG|
``document_id``
Only present if the request is successful.
.. note:: The API does not actually use the JSON-RPC error scheme. Instead the API has its own error scheme bundled inside a successful JSON-RPC result.
============= ==============================================================
status_code status_msg
============= ==============================================================
0 Success
2 An error occurred
3 You don't have enough credit
6 Unsupported file format
9 Server is currently under maintenance. Please try again later.
============= ==============================================================
.. rst-class:: setup doc-aside
.. switcher::
.. code-block:: text
{
"jsonrpc": string,
"id": string,
"result": {
"status_code": int,
"status_msg": string,
"document_id": int,
}
}
.. _webservices/extract_api/invoice_get_results:
``/iap/invoice_extract/get_results``
''''''''''''''''''''''''''''''''''''
Description
^^^^^^^^^^^
Request the results of the documents ids obtained with the :ref:`/parse <webservices/extract_api/invoice_parse>` route. Can either return the results or a "request pending" message.
Request Body
^^^^^^^^^^^^
``jsonrpc`` (required)
|SAME_AS_PARSE|
``method`` (required)
|SAME_AS_PARSE|
``id`` (required)
|SAME_AS_PARSE|
``params``:
``version`` (required)
|SAME_AS_PARSE|
``documents_ids`` (required)
The list of ``document_id`` for which you want to get the current parsing status.
.. rst-class:: setup doc-aside
.. switcher::
.. code-block:: text
{
"jsonrpc": string,
"method": string,
"params": {
"version": int,
"documents_ids": [int]
},
"id": string (hex),
}
Response
^^^^^^^^
``jsonrpc``
|SAME_AS_PARSE|
``id``
|SAME_AS_PARSE|
``result``
Dictionary where each key is a document_id. For each ``document_id``:
``status_code``
|STATUS_CODE|
``status_msg``
|STATUS_MSG|
``results``
Only present if the request is successful.
.. warning:: result keys are strings despite the fact that the document_ids given in the request body are integers.
============= ==============================================================
status_code status_msg
============= ==============================================================
0 Success
1 Not ready
2 An error occurred
9 Server is currently under maintenance. Please try again later.
============= ==============================================================
.. rst-class:: setup doc-aside
.. switcher::
.. code-block:: text
{
"jsonrpc": string,
"id": string,
"result": {
"document_id_1": {
"status_code": int,
"status_msg": str,
"results": [{"feature_1_name": feature_1_result,
"feature_2_name": feature_2_result,
}]
},
"document_id_2": {
"status_code": int,
"status_msg": str,
"results": [{"feature_1_name": feature_1_result,
"feature_2_name": feature_2_result,
}]
},
...
}
}
.. _webservices/extract_api/invoice_get_results/feature_result:
``feature_result``
''''''''''''''''''
Each field of interest we want to extract from the invoice such as the total or the due date are also called features. An exhaustive list of all the extracted features can be found in the table below.
For each feature, we return a list of candidates and we spotlight the candidate our model predicts to be the best fit for the feature.
``selected_value``
The best candidate for this feature.
``words``
List of all the candidates for this feature ordered by decreasing score.
.. container:: doc-aside
.. switcher::
.. code-block:: text
{
"selected_value": candidate_12,
"words": [candidate_12, candidate_3, candidate_4,...]
}
``candidate``
'''''''''''''
For each candidate we give its representation and position in the document. Candidates are sorted by decreasing order of suitability.
``content``
Representation of the candidate.
``coords``
``[center_x, center_y, width, height, rotation_angle]``. The position and dimensions are relative to the size of the page and are therefore between 0 and 1.
The angle is a clockwise rotation measured in degrees.
``page``
Page of the original document on which the candidate is located (starts at 0).
.. container:: doc-aside
.. switcher::
.. code-block:: text
{
"content": string|float,
"coords": [float, float, float, float, float],
"page": int
}
+-------------------------+------------------------------------------------------------------------------------+
| Feature name | Specifities |
+=========================+====================================================================================+
| ``SWIFT_code`` | **content** is a dictionary encoded as a string. |
| | |
| | It contains information about the detected SWIFT code |
| | (or `BIC <https://www.iso9362.org/isobic/overview.html>`_). |
| | |
| | Keys: |
| | |
| | ``bic`` |
| | detected BIC (string). |
| | ``name`` (optional) |
| | bank name (string). |
| | ``country_code`` |
| | ISO3166 alpha-2 country code of the bank (string). |
| | ``city`` (optional) |
| | city of the bank (string). |
| | ``verified_bic`` |
| | True if the BIC has been found in our DB (bool). |
| | |
| | Name and city are present only if verified_bic is true. |
+-------------------------+------------------------------------------------------------------------------------+
| ``VAT_Number`` | **content** is a string |
+-------------------------+------------------------------------------------------------------------------------+
| ``country`` | **content** is a string |
+-------------------------+------------------------------------------------------------------------------------+
| ``currency`` | **content** is a string |
+-------------------------+------------------------------------------------------------------------------------+
| ``date`` | **content** is a string |
| | |
| | Format : *YYYY-MM-DD HH:MM:SS* |
+-------------------------+------------------------------------------------------------------------------------+
| ``due_date`` | Same as for ``date`` |
+-------------------------+------------------------------------------------------------------------------------+
| ``global_taxes`` | **content** is a float |
| | |
| | **candidate** has an additional field ``amount_type``. Its value is always percent.|
| | |
| | **selected_values** is a list of candidates. |
+-------------------------+------------------------------------------------------------------------------------+
| ``global_taxes_amount`` | **content** is a float |
+-------------------------+------------------------------------------------------------------------------------+
| ``invoice_id`` | **content** is a string |
+-------------------------+------------------------------------------------------------------------------------+
| ``subtotal`` | **content** is a float |
+-------------------------+------------------------------------------------------------------------------------+
| ``total`` | **content** is a float |
+-------------------------+------------------------------------------------------------------------------------+
| ``supplier`` | **content** is a string |
+-------------------------+------------------------------------------------------------------------------------+
``feature_result`` for the ``invoice_lines`` feature
''''''''''''''''''''''''''''''''''''''''''''''''''''
It follows a more specific structure. It is basically a list of dictionaries where each dictionary represents an invoice line. Each value follows
a :ref:`webservices/extract_api/invoice_get_results/feature_result` structure.
.. container:: doc-aside
.. switcher::
.. code-block:: text
[
{
"description": feature_result,
"discount": feature_result,
"product": feature_result,
"quantity": feature_result,
"subtotal": feature_result,
"total": feature_result,
"taxes": feature_result,
"total": feature_result,
"unit": feature_result,
"unit_price": feature_result
},
...
]
.. _webservices/extract_api/invoice_validate:
``/iap/invoice_extract/validate``
'''''''''''''''''''''''''''''''''
Description
^^^^^^^^^^^
Route that validates the different features of an invoice. The validation step is an optional step but is strongly recommended. By telling the system if it were right or wrong for each
feature you give an important feedback. It has no direct impact but it helps the system to greatly improve its prediction accuracy for the invoices you will send in the future.
Request Body
^^^^^^^^^^^^
``jsonrpc`` (required)
|SAME_AS_PARSE|
``method`` (required)
|SAME_AS_PARSE|
``params``
``documents_id`` (required)
Id of the document for which you want to validate the result.
``values``
Contains the validation for each feature. The field ``merged_line`` indicates if the ``invoice_lines`` have been merged or not.
.. note:: You don't have to validate all the features in order for the validation to succeed. However :ref:`/validate <webservices/extract_api/invoice_validate>` can't be called multiple times for a same invoice.
Therefore you should validate all the features you want to validate at once.
.. rst-class:: setup doc-aside
.. switcher::
.. code-block:: text
{
"jsonrpc": string,
"method": string,
"params": {
"document_id": int,
"values": {
"merged_lines": bool
"feature_name_1": validation_1,
"feature_name_2": validation_2,
...
}
},
"id": string (hex),
}
``validation``
''''''''''''''
A **validation** for a given feature is a dictionary containing the textual representation of the expected value for this given feature.
This format apply for all the features except for ``global_taxes`` and ``invoice_lines`` which have more complex validation format.
.. rst-class:: setup doc-aside
.. switcher::
.. code-block:: text
{ "content": string|float }
validation for ``global_taxes``
'''''''''''''''''''''''''''''''
**content** is a list of dictionaries. Each dictionary represents a tax:
``amount``
Amount on which the tax is applied.
``tax_amount``
Amount of the tax.
``tax_amount_type``
Indicates if the ``tax_amount`` is a percentage or a fixed value. The type must be specified using the literal string "fixed" or "percent".
``tax_price_include``
Indicates if ``amount`` already contains the tax or not.
.. rst-class:: setup doc-aside
.. switcher::
.. code-block:: text
{"content": [
{
"amount": float,
"tax_amount": float,
"tax_amount_type": "fixed"|"percent",
"tax_price_include": bool
},
...
]}
validation for ``invoice_lines``
''''''''''''''''''''''''''''''''
**lines** is a list of dictionaries. Each dictionary represents an invoice line. The dictionary keys speak for themselves.
.. rst-class:: setup doc-aside
.. switcher::
.. code-block:: text
{"lines": [
{
"description": string,
"quantity": float,
"unit_price": float,
"product": string,
"taxes_amount": float,
"taxes": [
{
"amount": float,
"type": "fixed"|"percent",
"price_include": bool
},
...
],
"subtotal": float,
"total": float
},
...
]}
Response
--------
``jsonrpc``
|SAME_AS_PARSE|
``id``
|SAME_AS_PARSE|
``result``
``status_code``
|STATUS_CODE|
``status_msg``
|STATUS_MSG|
============= ==========================================================
status_code status_msg
============= ==========================================================
0 Success
12 Validation format is incorrect
============= ==========================================================
.. rst-class:: setup doc-aside
.. switcher::
.. code-block:: text
{
"jsonrpc": string,
"id": string,
"result": {
"status_code": int,
"status_msg": string,
}
}
.. _webservices/extract_api/integration_testing:
Integration Testing
===================
You can test your integration by using *integration_token* as ``account_token`` in the :ref:`/parse <webservices/extract_api/invoice_parse>` request.
Using this token put you in test mode and allows you to simulate the entire flow without really parsing a document and without being billed one credit for each successful invoice parsing.
The only technical differences in test mode is that the document you send is not parsed by the system and that the response you get from :ref:`/get_results <webservices/extract_api/invoice_get_results>`
is a hard-coded one.
A python implementation of the full flow can be found :download:`here <extract_api/implementation.py>`.
.. _JSON-RPC2: https://www.jsonrpc.org/specification
.. |SAME_AS_PARSE| replace:: Same as for :ref:`/parse <webservices/extract_api/invoice_parse>`.
.. |STATUS_CODE| replace:: The code indicating the status of the request. ``status_code`` is 0 in case of success. Other ``status_code`` are detailed in the table below.
.. |STATUS_MSG| replace:: A string giving verbose details about the request status.

View File

@@ -1,100 +0,0 @@
import base64
import time
import sys
import json
import requests
account_token = "integration_token" # Use your token
domain_name = "https://iap-extract.odoo.com"
path_to_pdf = "/path/to/invoice_file"
API_VERSION = 120 # Do not change
SUCCESS = 0
NOT_READY = 1
def jsonrpc(path, params):
payload = {
'jsonrpc': '2.0',
'method': 'call',
'params': params,
'id': 0,
}
req = requests.post(domain_name+path, json=payload, timeout=10)
req.raise_for_status()
resp = req.json()
return resp
with open(path_to_pdf, "rb") as file:
params = {
'account_token': account_token,
'version': API_VERSION,
'documents': [base64.b64encode(file.read()).decode('ascii')],
}
response = jsonrpc("/iap/invoice_extract/parse", params)
print("/parse call status: ", response['result']['status_msg'])
if response['result']['status_code'] != SUCCESS:
sys.exit(1)
# You received an id that you can use to poll the server to get the result of the ocr when it will be ready
document_id = response['result']['document_id']
params = {
'version': API_VERSION,
'document_ids': [document_id], # you can request the results of multiple documents at once if wanted
}
response = jsonrpc("/iap/invoice_extract/get_results", params)
document_id = str(document_id) # /get_results expects a string despite the fact that the returned document_id is a int
while response['result'][document_id]['status_code'] == NOT_READY: # 1 is the status code indicating that the server is still processing the document
print("Still processing... Retrying in 5 seconds")
time.sleep(5)
response = jsonrpc("/iap/invoice_extract/get_results", params)
with open('results.txt', 'w') as outfile:
json.dump(response, outfile, indent=2)
print("\nResult saved in results.txt")
if response['result'][document_id]['status_code'] != SUCCESS:
print(response['result'][document_id]['status_msg']) # if it isn't a success, print the error message
sys.exit(1)
document_results = response['result'][document_id]['results'][0]
print("\nTotal:", document_results['total']['selected_value']['content'])
print("Subtotal:", document_results['subtotal']['selected_value']['content'])
print("Invoice id:", document_results['invoice_id']['selected_value']['content'])
print("Date:", document_results['date']['selected_value']['content'])
print("...\n")
params = {
'document_id': document_id,
'values': {
'total': {'content': 100.0},
'subtotal': {'content': 100.0},
'global_taxes': {'content': []},
'global_taxes_amount': {'content': 0.0},
'date': {'content': '2020-09-25'},
'due_date': {'content': '2020-09-25'},
'invoice_id': {'content': document_results['invoice_id']['selected_value']['content']},
'partner': {'content': 'twinnta'},
'VAT_Number': {'content': 'BE23252248420'},
'currency': {'content': 'USD'},
'merged_lines': False,
'invoice_lines': {'lines': [{'description': 'Total TVA ',
'quantity': 1.0,
'unit_price': 100.0,
'product': False,
'taxes_amount': 0.0,
'taxes': [],
'subtotal': 100.0,
'total': 100.0}]
}
}
}
response = jsonrpc("/iap/invoice_extract/validate", params)
if response['result']['status_code'] == SUCCESS:
print("/validate call status: Success")
else:
print("/validate call status: wrong format")

View File

@@ -1,12 +1,12 @@
:nosearch:
:types: reference
=========
Reference
=========
================
Reference Guides
================
.. toctree::
:titlesonly:
reference/backend
reference/frontend
reference/addons
reference/javascript

View File

@@ -0,0 +1,19 @@
:nosearch:
=============
Server Addons
=============
.. toctree::
:titlesonly:
addons/orm
addons/data
addons/actions
addons/views
addons/module
addons/reports
addons/security
addons/testing
addons/http
addons/mixins

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -118,10 +118,11 @@ Available manifest fields are:
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.
``assets`` (``dict``)
A definition of how all static files are loaded in various assets bundles.
See the :ref:`assets <reference/assets>` page for more details on how to
describe bundles.
``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``)

View File

@@ -120,15 +120,13 @@ 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`` and ``at_install`` by default.
``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. It implies :option:`--test-enable <odoo-bin --test-enable>`,
so it's not necessary to specify :option:`--test-enable <odoo-bin --test-enable>`
when using :option:`--test-tags <odoo-bin --test-tags>`.
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
@@ -159,7 +157,7 @@ have to be selected explicitly:
.. code-block:: console
$ odoo-bin --test-tags nice
$ 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
@@ -168,10 +166,10 @@ are *additive* (you're selecting all tests with *any* of the specified tags)
.. code-block:: console
$ odoo-bin --test-tags nice,standard
$ odoo-bin --test-enable --test-tags nice,standard
The config switch parameter also accepts the ``+`` and ``-`` prefixes. The
``+`` prefix is implied and therefore, totally optional. The ``-`` (minus)
``+`` 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
@@ -179,7 +177,7 @@ ones:
.. code-block:: console
$ odoo-bin --test-tags 'standard,-slow'
$ 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,
@@ -196,36 +194,6 @@ they're not going to get run:
class SmallTest(unittest.TestCase):
...
Besides tags you can also specify specific modules, classes or functions to
test. The full syntax of the format accepted by :option:`--test-tags <odoo-bin --test-tags>`
is:
.. code-block::
[-][tag][/module][:class][.method]
So if you want to test the `stock_account` module, you can use:
.. code-block:: console
$ odoo-bin --test-tags /stock_account
If you want to test a specific function with a unique name, it can be specified
directly:
.. code-block:: console
$ odoo-bin --test-tags .test_supplier_invoice_forwarded_by_internal_user_without_supplier
This is equivalent to
.. code-block:: console
$ odoo-bin --test-tags /account:TestAccountIncomingSupplierInvoice.test_supplier_invoice_forwarded_by_internal_user_without_supplier
if the name of the test is unambiguous. Multiple modules, classes and functions
can be specified at once separated by a `,` like with regular tags.
.. _reference/testing/tags:
Special tags
@@ -245,36 +213,43 @@ Special tags
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 implicitly 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 installed modules. If you're starting from
a clean database, you'll need to install the modules with the
:option:`-i <odoo-bin -i>` switch at least once. After that it's no longer
needed, unless you need to upgrade the module, in which case
:option:`-u <odoo-bin -u>` can be used. For simplicity, those switches are
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-tags /sale
$ 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-tags '/sale,-slow'
$ odoo-bin --test-enable --test-tags 'sale,-slow'
Run only the tests from stock or tagged as slow:
.. code-block:: console
$ odoo-bin --test-tags '-standard, slow, /stock'
$ odoo-bin --test-enable --test-tags '-standard, slow, stock'
.. note:: ``-standard`` is implicit (not required), and present for clarity

View File

@@ -1925,56 +1925,54 @@ Map
<span class="badge" style="background-color:#AD5E99">Enterprise feature</span>
This view is able to display records on a map and the routes between them. The records are represented by pins. It also allows the visualization of fields from the model in a popup tied to the record's pin.
This view is able to display records on a map and the routes between them. The record are represented by pins. It also allows the visualization of fields from the model in a popup tied to the record's pin.
.. note::
The model on which the view is applied should contain a `res.partner` many2one since the view relies on the `res.partner`'s address and coordinates fields to localize the records.
The model on which the view is applied should contains a res.partner many2one since the view relies on the res.partner's address and coordinates fields to localize the records.
.. _reference/views/map/api:
API
Api
~~~
The view uses location data platforms' API to fetch the tiles (the map's background), do the geoforwarding (converting addresses to a set of coordinates) and fetch the routes.
The view implements two API, OpenStreetMap and MapBox. OpenStreetMap is used by default and is able to fetch `tiles`_ and do `geoforwarding`_. This API does not require a token.
As soon as a valid `MapBox`_ token is provided in the general settings the view switches to the MapBox API. This API is faster and allows the computation of routes. A token can be obtained by `signing up`_ to MapBox.
The view uses location data platforms' api to fetch the tiles (the map's background), do the geoforwarding (converting addresses to a set of coordinates) and fetch the routes.
The view implements two api, the default one, openstreet map is able to fetch `tiles`_ and do `geoforwarding`_. This api does not require a token.
As soon as a valid `MapBox`_ token is provided in the general settings the view switches to the Mapbox api. This api is faster and allows the computation of routes. The token are available by `signing up`_ to MapBox
.. _reference/views/structural components:
Structural components
~~~~~~~~~~~~~~~~~~~~~
The view's root element is ``<map>``. It can have the following attributes:
The view's root element is ``<map>`` multiple attributes are allowed
``res_partner``
Contains the `res.partner` many2one. If not provided the view resorts to create an empty map.
Contains the res.partner many2one. If not provided the view will resort to create an empty map.
``default_order``
If a field is provided the view overrides the model's default order. The field must be part of the model on which the view is applied, not from `res.partner`.
If a field is provided the view will override the model's default order. The field must be part of the model on which the view is applied not from res.partner
``routing``
if ``1`` display the routes between the records. The view needs a valid MapBox token and at least two located records (i.e the records have a `res.partner` many2one and the partner has an address or valid coordinates).
if ``true`` the routes between the records will be shown. The view still needs a valid MapBox token and at least two located records. (i.e the records has a res.partner many2one and the partner has a address or valid coordinates)
``hide_name``
if ``1`` hide the name from the pin's popup (default: ``0``).
if ``true`` hide a name from the marker's popup (default: false)
``hide_address``
if ``1`` hide the address from the pin's popup (default: ``0``).
``hide_title``
if ``1`` hide the title from the pin list (default: ``0``).
``panel_title``
String to display as title of the pin list. If not provided, the title is the action's name or "Items" if the view is not in an action.
``limit``
Maximum number of records to fetch (default: 80). It must be a positive integer.
if ``true`` hide a address from the marker's popup (default: false)
The ``<map>`` element can contain multiple ``<field>`` elements. Each ``<field>`` element is interpreted as a line in the pin's popup. The field's attributes are the following:
The ``<map>`` element can contain multiple ``<field>`` elements. Each ``<field>`` element will be interpreted as a line in the marker's popup. The field's attributes are the following:
``name``
The field to display.
``string``
String to display before the field's content. It can be used as a description.
This string will be displayed before the field's content. It Can be used as a description.
``limit``
The size of a page (default: 80). It must be a positive integer.
No attribute or element is mandatory but as stated above if no res.partner many2one is provided the view won't be able to locate records.
For example here is a map:
.. code-block:: xml
<map res_partner="partner_id" default_order="date_begin" routing="1" hide_name="1">
<map res_partner="partner_id" default_order="date_begin" routing="true" hide_name="true">
<field name="partner_id" string="Customer Name"/>
</map>

View File

@@ -1,19 +0,0 @@
:nosearch:
=======
Backend
=======
.. toctree::
:titlesonly:
backend/orm
backend/data
backend/actions
backend/views
backend/module
backend/reports
backend/security
backend/testing
backend/http
backend/mixins

View File

@@ -1,22 +0,0 @@
:nosearch:
========
Frontend
========
.. toctree::
:titlesonly:
frontend/framework_overview
frontend/assets
frontend/javascript_modules
frontend/owl_components
frontend/registries
frontend/services
frontend/hooks
frontend/fields
frontend/patching_code
frontend/javascript_cheatsheet
frontend/javascript_reference
frontend/mobile
frontend/qweb

View File

@@ -1,366 +0,0 @@
.. _reference/assets:
======
Assets
======
Managing assets in Odoo is not as straightforward as it is in some other apps.
One of the reasons is that we have a variety of situations where some, but not all
of the assets are required. For example, the needs of the web client, the point of
sale app, the website or even the mobile application are different. Also, some
assets may be large, but are seldom needed: in that case we may want them
to be :ref:`loaded lazily (on demand) <frontend/assets/lazy_loading>`.
Asset types
===========
There are three different asset types: code (`js` files), style (`css` or `scss`
files) and templates (`xml` files).
Code
Odoo supports :ref:`three different kinds of javascript files<frontend/js_modules>`.
All these files are then processed (native JS modules are transformed into odoo
modules), then minified (if not in `debug=assets` :ref:`mode <frontend/framework/assets_debug_mode>`)
and concatenated. The result is then saved as a file attachment. These file
attachments are usually loaded via a `<script>` tag in the `<head>` part of
the page (as a static file).
Style
Styling can be done with either `css` or `scss <https://sass-lang.com/>`_. Like
the javascript files, these files are processed (`scss` files are converted into
`css`), then minified (again, if not in `debug=assets` :ref:`mode <frontend/framework/assets_debug_mode>`)
and concatenated. The result is then saved as a file attachment. They are
then usually loaded via a `<link>` tag in the `<head>` part of the page (as
a static file).
Template
Templates (static `xml` files) are handled in a different way: they are simply
read from the file system whenever they are needed, and concatenated.
Whenever the browser loads odoo, it calls the `/web/webclient/qweb/` controller
to fetch the :ref:`templates <reference/qweb>`.
It is useful to know that in most cases, a browser only performs a request the
first time it loads a page. This is because each of these assets are
associated with a checksum, which is injected into the page source. The checksum
is then added to the url, which means that it is possible to safely set the cache
headers to a long period.
Bundles
=======
Odoo assets are grouped by *bundles*. Each bundle (a *list of file paths*
of specific types: `xml`, `js`, `css` or `scss`) is listed in the
:ref:`module manifest <reference/module/manifest>`. Files can be declared using
`glob <https://en.wikipedia.org/wiki/Glob_(programming)>`_ syntax, meaning that
you can declare several asset files using a single line.
The bundles are defined in each module's :file:`__manifest__.py`,
with a dedicated `assets` key which contains a dictionary. The dictionary maps
bundle names (keys) to the list of files they contain (values). It looks
like this:
.. code-block:: py
'assets': {
'web.assets_backend': [
'web/static/src/xml/**/*',
],
'web.assets_common': [
'web/static/lib/bootstrap/**/*',
'web/static/src/js/boot.js',
'web/static/src/js/webclient.js',
],
'web.qunit_suite_tests': [
'web/static/src/js/webclient_tests.js',
],
},
Here is a list of some important bundles that most odoo developers will need to
know:
- `web.assets_common`: this bundle contains most assets which are common to the
web client, the website and also the point of sale. This is supposed to contain
lower level building blocks for the odoo framework. Note that it contains the
:file:`boot.js` file, which defines the odoo module system.
- `web.assets_backend`: this bundle contains the code specific to the web client
(notably the web client/action manager/views)
- `web.assets_frontend`: this bundle is about all that is specific to the public
website: ecommerce, portal, forum, blog, ...
- `web.assets_qweb`: all static XML templates used in the backend environment
and in the point of sale.
- `web.qunit_suite_tests`: all javascript qunit testing code (tests, helpers, mocks)
- `web.qunit_mobile_suite_tests`: mobile specific qunit testing code
Operations
----------
Typically, handling assets is simple: you just need to add some new files
to a frequently used bundle like `assets_common` or `assets_backend`. But there are other operations
available to cover some more specific use cases.
Note that all directives targeting a certain asset file (i.e. `before`, `after`,
`replace` and `remove`) need that file to be declared beforehand, either
in manifests higher up in the hierarchy or in ``ir.asset`` records with a lower
sequence.
`append`
~~~~~~~~
This operation adds one or multiple file(s). Since it is the most common
operation, it can be done by simply using the file name:
.. code-block:: python
'web.assets_common': [
'my_addon/static/src/js/**/*',
],
By default, adding a simple string to a bundle will append the files matching the
glob pattern at the end of the bundle. Obviously, the pattern may also be directly
a single file path.
`prepend`
~~~~~~~~~
Add one or multiple file(s) at the beginning of the bundle.
Useful when you need to put a certain file before the others in a bundle (for
example with css files). The `prepend` operation is invoked with the following
syntax: `('prepend', <path>)`.
.. code-block:: python
'web.assets_common': [
('prepend', 'my_addon/static/src/css/bootstrap_overridden.scss'),
],
`before`
~~~~~~~~
Add one or multiple file(s) before a specific file.
Prepending a file at the beginning of a bundle might not be precise enough. The
`before` directive can be used to add the given file(s) right *before* the target
file. It is declared by replacing the normal path with a 3-element tuple
`('before', <target>, <path>)`.
.. code-block:: python
'web.assets_common': [
('before', 'web/static/src/css/bootstrap_overridden.scss', 'my_addon/static/src/css/bootstrap_overridden.scss'),
],
`after`
~~~~~~~
Add one or multiple file(s) after a specific file.
Same as `before`, with the matching file(s) appended right *after* the target file.
It is declared by replacing the normal path with a 3-element tuple
`('after', <target>, <path>)`.
.. code-block:: python
'web.assets_common': [
('after', 'web/static/src/css/list_view.scss', 'my_addon/static/src/css/list_view.scss'),
],
`include`
~~~~~~~~~
Use nested bundles.
The `include` directive is a way to use a bundle in other bundles to minimize
the size of your manifest. In Odoo we use sub bundles (prefixed with an underscore
by convention) to batch files used in multiple other bundles. You can then
specify the sub bundle as a pair `('include', <bundle>)` like this:
.. code-block:: python
'web.assets_common': [
('include', 'web._primary_variables'),
],
`remove`
~~~~~~~~
Remove one or multiple file(s).
In some cases, you may want to remove one or multiple files from a bundle. This
can be done using the `remove` directive by specifying a pair
`('remove', <target>)`:
.. code-block:: python
'web.assets_common': [
('remove', 'web/static/src/js/boot.js'),
],
`replace`
~~~~~~~~~
Replace an asset file with one or multiple file(s).
Let us say that an asset needs not only to be removed, but you also want to insert
your new version of that asset at the same exact position. This can be done with
the `replace` directive, using a 3-element tuple `('replace', <target>, <path>)`:
.. code-block:: python
'web.assets_common': [
('replace', 'web/static/src/js/boot.js', 'my_addon/static/src/js/boot.js'),
],
Loading order
-------------
The order in which assets are loaded is sometimes critical and must be deterministic,
mostly for stylesheets priorities and setup scripts. Assets in Odoo are processed
as follows:
#. When an asset bundle is called (e.g. `t-call-assets="web.assets_common"`), an empty
list of assets is generated
#. All records of type `ir.asset` matching the bundle are fetched and sorted
by sequence number. Then all records with a sequence strictly less than 16 are
processed and applied to the current list of assets.
#. All modules declaring assets for said bundle in their manifest apply their
assets operations to this list. This is done following the order of modules dependencies
(e.g. `web` assets is processed before `website`). If a directive tries to add
a file already present in the list, nothing is done for that file. In other word,
only the first occurrence of a file is kept in the list.
#. The remaining `ir.asset` records (those with a sequence greater than or equal
to 16) are then processed and applied as well.
Assets declared in the manifest may need to be loaded in a particular order, for
example :file:`jquery.js` must be loaded before all other jquery scripts when loading the
lib folder. One solution would be to create an :ref:`ir.asset <frontend/assets/ir_asset>`
record with a lower sequence or a 'prepend' directive, but there is another simpler
way to do so.
Since the unicity of each file path in the list of assets is guaranteed, you can
mention any specific file before a glob that includes it. That file will thus appear
in the list before all the others included in the glob.
.. code-block:: python
'web.assets_common': [
'my_addon/static/lib/jquery/jquery.js',
'my_addon/static/lib/jquery/**/*',
],
.. note::
A module *b* removing/replacing the assets declared in a module *a* will have
to depend on it. Trying to operate on assets that have yet to be declared will
result in an error.
.. _frontend/assets/lazy_loading:
Lazy loading
============
It is sometimes useful to load files and/or asset bundles dynamically, for
example to only load a library once it is needed. To do that, the Odoo framework
provides a few helper functions, located in :file:`@web/core/assets`.
.. code-block:: javascript
await loadAssets({
jsLibs: ["/web/static/lib/stacktracejs/stacktrace.js"],
});
.. js:function:: loadAssets(assets)
:param Object assets: a description of various assets that should be loaded
:returns: Promise<void>
Load the assets described py the `assets` parameter. It is an object that
may contain the following keys:
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Key
- Type
- Description
* - `jsLibs`
- `string[]`
- a list of urls of javascript files
* - `cssLibs`
- `string[]`
- a list of urls of css files
.. js:function:: useAssets(assets)
:param Object assets: a description of various assets that should be loaded
This hook is useful when components need to load some assets in their
`onWillStart` method. It internally calls `loadAssets`.
.. _frontend/assets/ir_asset:
The asset model (`ir.asset`)
============================
In most cases the assets declared in the manifest will largely suffice. Yet for
more flexibility, the framework also supports dynamic assets declared in the
database.
This is done by creating `ir.asset` records. Those will be processed as if they
were found in a module manifest, and they give the same expressive power as their
manifest counterparts.
.. autoclass:: odoo.addons.base.models.ir_asset.IrAsset
`name`
Name of the asset record (for identification purpose).
`bundle`
Bundle in which the asset will be applied.
`directive` (default= `append`)
This field determines how the `path` (and `target` if needed) will be interpreted.
Here is the list of available directives along with their required arguments:
- **append**: `path`
- **prepend**: `path`
- **before**: `target`, `path`
- **after**: `target`, `path`
- **include**: `path` (interpreted as a **bundle name**)
- **remove**: `path` (interpreted as a **target asset** to remove)
- **replace**: `target`, `path`
`path`
A string defining one of the following:
- a **relative path** to an asset file in the addons file system;
- a **glob pattern** to a set of asset files in the addons file system;
- a **URL** to an attachment or external asset file;
- a **bundle name**, when using the `include` directive.
`target`
Target file to specify a position in the bundle. Can only be used with the
directives `replace`, `before` and `after`.
`active` (default= `True`)
Whether the record is active
`sequence` (default= `16`)
Loading order of the asset records (ascending). A sequence lower than 16 means
that the asset will be processed *before* the ones declared in the manifest.

View File

@@ -1,693 +0,0 @@
======
Fields
======
In Odoo, the word *field* is mostly used to denote a part of a model, usually
represented by a column in a database. Fields are represented by components in
the user interface (mostly in the form, kanban and list view). In this page, we document
how these field components work and how to use them.
How to define a field
=====================
Fields are simply owl components registered in the fields registry.
.. code-block:: javascript
import { registry } from "@web/core/registry";
const { Component } = owl;
const { xml } = owl.tags;
class CounterField extends Component {
increment() {
if (!this.props.readonly) {
this.props.update(this.props.value + 1);
}
}
}
CounterField.template = xml`
<div t-on-click="increment">
<t t-esc="props.value">
</div>
`;
registry.category("fields").add("counter", CounterField);
The component of a field receives several props:
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Name
- Type
- Description
* - `archs`
- `object?`
- description...
* - `attrs`
- `object`
- description...
* - `id`
- `string?`
- description...
* - `name`
- `string`
- The field's name
* - `options`
- `object`
- description...
* - `readonly`
- `boolean`
- description...
* - `required`
- `boolean`
- description...
* - `record`
- `DataPoint`
- description...
* - `type`
- `string`
- The field's base type.
* - `update`
- `function`
- description...
* - `value`
- `any`
- description...
How to use a field
==================
.. code-block:: xml
<Field name="'my_field'" record="getRecord()" type="'counter'" readonly="true" required="false" />
Reference List
==============
.. list-table::
:widths: 15 20 20 45
:header-rows: 1
* - Name
- Technical name
- Type
- Short Description
* - :ref:`BinaryField <frontend/fields/binary_field>`
- `binary`
- `binary`
- transfer files in the client
* - :ref:`BooleanFavoriteField <frontend/fields/boolean_favorite_field>`
- `boolean_favorite`
- `boolean`
- description...
* - :ref:`BooleanField <frontend/fields/boolean_field>`
- `boolean`
- `boolean`
- Displays a checkbox
* - :ref:`BooleanToggleField <frontend/fields/boolean_toggle_field>`
- `boolean_toggle`
- `boolean`
- description...
* - :ref:`CharField <frontend/fields/char_field>`
- `char`
- `char`
- description...
* - :ref:`ColorField <frontend/fields/color_field>`
- `color`
- `char`
- description...
* - :ref:`ColorPickerField <frontend/fields/color_picker_field>`
- `color_picker`
- `integer`
- description...
* - :ref:`CopyClipboardCharField <frontend/fields/copy_clipboard_char_field>`
- `CopyClipboardChar`
- `char`
- button to copy a `char` value to the clipboard
* - :ref:`CopyClipboardTextField <frontend/fields/copy_clipboard_text_field>`
- `CopyClipboardText`
- `char`
- button to copy a text to the clipboard
* - :ref:`CopyClipboardURLField <frontend/fields/copy_clipboard_url_field>`
- `CopyClipboardURL`
- `char`
- button to copy a url to the clipboard
* - :ref:`DateField <frontend/fields/date_field>`
- `date`
- `date`, `datetime`
- description...
* - :ref:`DateTimeField <frontend/fields/datetime_field>`
- `datetime`
- `datetime`
- description...
* - :ref:`EmailField <frontend/fields/email_field>`
- `text`
- `char`
- display email addresses
* - :ref:`HandleField <frontend/fields/handle_field>`
- `handle`
- `integer`
- description...
* - :ref:`ImageField <frontend/fields/image_field>`
- `image`
- `binary`
- description...
* - :ref:`Many2ManyCheckboxesField <frontend/fields/many2many_checkboxes_field>`
- `many2many_checkboxes`
- `many2many`
- description...
* - :ref:`Many2ManyTagsField <frontend/fields/many2many_tags_field>`
- `many2many_tags`
- `many2many`
- description...
* - :ref:`Many2oneField <frontend/fields/many2one_field>`
- `many2one`
- `many2one`
- description...
* - :ref:`PdfViewerField <frontend/fields/pdf_viewer_field>`
- `pdf_viewer`
- `binary`
- display a progress bar
* - :ref:`PercentageField <frontend/fields/percentage_field>`
- `text`
- `integer`, `float`
- display percentages
* - :ref:`PercentPieField <frontend/fields/percent_pie_field>`
- `text`
- `integer`, `float`
- display a progress using a pie
* - :ref:`PhoneField <frontend/fields/phone_field>`
- `text`
- `char`, `integer`
- display phone numbers
* - :ref:`PriorityField <frontend/fields/priority_field>`
- `priority`
- `selection`
- description...
* - :ref:`ProgressBarField <frontend/fields/progress_bar_field>`
- `priority`
- `integer`, `float`
- display a progress bar
* - :ref:`RadioField <frontend/fields/radio_field>`
- `radio`
- `many2one`, `selection`
- description...
* - :ref:`RemainingDaysField <frontend/fields/remaining_days_field>`
- `remaining_days`
- `date`, `datetime`
- description...
* - :ref:`SelectionField <frontend/fields/selection_field>`
- `selection`
- `selection`
- description...
* - :ref:`StatInfoField <frontend/fields/stat_info_field>`
- `statinfo`
- `float`, `integer`
- description...
* - :ref:`StatusBarField <frontend/fields/statusbar_field>`
- `statusbar`
- `many2one`, `selection`
- description...
* - :ref:`TextField <frontend/fields/text_field>`
- `text`
- `html`, `text`
- description...
* - :ref:`UrlField <frontend/fields/url_field>`
- `text`
- `char`
- display URLs
.. _frontend/fields/binary_field:
BinaryField
-----------
- Location: `@web/fields/image_field`
- Technical name: `image`
- Supported types: `binary`
The purpose of this component is to upload files to the web client. In readonly
mode, you are able to download the file if one is available from the field. Otherwise,
the field indicates that no file is available.
When a file has been uploaded, the field let you replace the file by clicking on the
field or its dedicated `edit` button. A button lets you delete the uploaded file.
A `filename` attribute can be added, to display a name in the field. It can be the name of
a value from the record, or any value given as a `string`.
It supports the following options:
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Name
- Type
- Description
* - `accepted_file_extensions`
- `string`
- optional. list of files accepted by the input.
.. code-block:: xml
<field name="my_field" widget="binary" filename="Your document" options="{'accepted_file_extensions': '.dat,.bin'}" />
.. _frontend/fields/boolean_favorite_field:
BooleanFavoriteField
--------------------
- Location: `@web/fields/boolean_favorite_field`
- Technical name: `boolean_favorite`
- Supported types: `boolean`
.. code-block:: xml
<field name="my_field" widget="boolean_favorite" />
.. _frontend/fields/boolean_field:
BooleanField
------------
- Location: `@web/fields/boolean_field`
- Technical name: `boolean`
- Supported types: `boolean`
The `BooleanField` component represents a boolean value. It is the default field
for all fields of type `boolean`.
.. code-block:: xml
<field name="my_field" widget="boolean" />
.. _frontend/fields/boolean_toggle_field:
BooleanToggleField
------------------
- Location: `@web/fields/boolean_toggle_field`
- Technical name: `boolean_toggle`
- Supported types: `boolean`
.. _frontend/fields/char_field:
CharField
---------
- Location: `@web/fields/char_field`
- Technical name: `char`
- Supported types: `char`
.. _frontend/fields/color_field:
ColorField
----------
- Location: `@web/fields/color_field`
- Technical name: `color`
- Supported types: `char`
.. _frontend/fields/color_picker_field:
ColorPickerField
----------------
- Location: `@web/fields/color_picker_field`
- Technical name: `color_picker`
- Supported types: `integer`
.. _frontend/fields/copy_clipboard_char_field:
CopyClipboardCharField
----------------------
- Location: `@web/fields/copy_clipboard_char_field`
- Technical name: `CopyClipboardText`
- Supported types: `char`
This component represents a field that allows a user to copy a `char` value to its clipboard
by clicking on the copy button present in the field. The value is also displayed inline on
the left.
.. _frontend/fields/copy_clipboard_text_field:
CopyClipboardTextField
----------------------
- Location: `@web/fields/copy_clipboard_text_field`
- Technical name: `CopyClipboardChar`
- Supported types: `char`
This component represents a field that allows a user to copy a text block to its clipboard
by clicking on the copy button present in the field. The text is also displayed on the left
and can have multiple lines.
.. _frontend/fields/copy_clipboard_url_field:
CopyClipboardURLField
---------------------
- Location: `@web/fields/copy_clipboard_url_field`
- Technical name: `CopyClipboardURL`
- Supported types: `char`
This component represents a field that allows a user to copy a URL link to its clipboard
by clicking on the copy button present in the field. The value is also displayed on the left
and the user can click on it to open the link in a new tab.
.. _frontend/fields/date_field:
DateField
---------
- Location: `@web/fields/date_field`
- Technical name: `date`
- Supported types: `date`, `datetime`
.. _frontend/fields/datetime_field:
DateTimeField
-------------
- Location: `@web/fields/datetime_field`
- Technical name: `datetime`
- Supported types: `datetime`
.. _frontend/fields/email_field:
EmailField
----------
- Location: `@web/fields/email_field`
- Technical name: `email`
- Supported types: `char`
The `EmailField` component represents a textual value containing an email address. The field
is an input with the `email` type in edit mode, and a link with an `href` in readonly mode with
the `mailto:` prefix. It opens an email application if available whenever a click is made by the user.
.. code-block:: xml
<field name="my_field" widget="email" />
.. _frontend/fields/handle_field:
HandleField
-----------
- Location: `@web/fields/handle_field`
- Technical name: `handle`
- Supported types: `integer`
.. _frontend/fields/image_field:
ImageField
----------
- Location: `@web/fields/image_field`
- Technical name: `image`
- Supported types: `binary`
.. _frontend/fields/many2many_checkboxes_field:
Many2ManyCheckboxesField
------------------------
- Location: `@web/fields/many2many_checkboxes_field`
- Technical name: `many2many_checkboxes`
- Supported types: `many2many`
.. _frontend/fields/many2many_tags_field:
Many2ManyTagsField
------------------
- Location: `@web/fields/many2many_tags_field`
- Technical name: `many2many_tags`
- Supported types: `many2many`
.. _frontend/fields/many2one_field:
Many2OneField
-------------
- Location: `@web/fields/many2one_field`
- Technical name: `many2one`
- Supported types: `many2one`
.. _frontend/fields/pdf_viewer_field:
PdfViewerField
--------------
- Location: `@web/fields/pdf_viewer_field`
- Technical name: `pdf_viewer`
- Supported types: `binary`
The PdfViewerField allows a user to upload a file in edit mode. If a file is loaded, the PDF is
visible inside the field. From the preview, the user can navigate between pages or download
the file. You can specify to which page the preview is loaded by using an other field in the
same record. To do so, the field must have the same name, followed by `_page`.
The following example will display the third page by default once the field is shown:
.. code-block:: xml
<record>
<field name="my_pdf_page">3</field>
<field name="my_pdf" widget="pdf_viewer" filename="Your PDF" />
</record>
.. _frontend/fields/percentage_field:
PercentageField
---------------
- Location: `@web/fields/percentage_field`
- Technical name: `percentage`
- Supported types: `integer`, `float`
The `PercentageField` component represents a percentage. To use the field, you must give a
float value. Then, the field will format and display the value to a percentage, using a single
decimal (e.g. `0.5671` would be converted to `56.7%`). When the user enters the edit mode, the
value is still visible as a percentage, but the inner value is not rounded this time. In the
end, the value is always saved as a float value.
.. code-block:: xml
<field name="my_field" widget="percentage" />
.. _frontend/fields/percent_pie_field:
PercentPieField
---------------
- Location: `@web/fields/percent_pie_field`
- Technical name: `percentpie`
- Supported types: `integer`, `float`
The `PercentPieField` component represents a progress using a percentage associated with a
pie. To use this field, you provide the percentage directly to the field. The PercentPie
is not editable directly. To do so, you must update the value used by the field.
.. code-block:: xml
<field name="my_field" widget="percentpie" />
.. _frontend/fields/phone_field:
PhoneField
----------
- Location: `@web/fields/phone_field`
- Technical name: `phone`
- Supported types: `char`, `integer`
The `PhoneField` component represents a phone number. This field is used as
an input with the `phone` type in edit mode, and a link with an `href` in readonly mode.
The link contains the `tel:` prefix which means that it starts a call to the given number
whenever a user clicks on it.
.. code-block:: xml
<field name="my_field" widget="phone" />
.. _frontend/fields/priority_field:
PriorityField
-------------
- Location: `@web/fields/priority_field`
- Technical name: `priority`
- Supported types: `selection`
.. _frontend/fields/progress_bar_field:
ProgressBarField
----------------
- Location: `@web/fields/progress_bar_field`
- Technical name: `priority`
- Supported types: `integer`, `float`
The `ProgressBarField` component indicates a progress with a bar. It is a more visual way to
indicate a form of progression. The progress value can be displayed in two ways, depending
if a maximum value is set explicitly or not. In the first case, a ratio is shown. Otherwise,
a percentage is shown instead.
The main entry point to edit the value is the `editable` option. If you use a percentage,
you will edit the value and see the changes directly on the left progress bar.
But if you use a ratio (with a maximum value), you will edit the values following the
specific options that were given. By default, you will only edit the current value. But if
any of the specific options `edit_max_value` and/or the `edit_current_value` are set, then
you will only be able to edit those fields.
It supports the following options:
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Name
- Type
- Description
* - `editable`
- `boolean`
- optional. set if the value is editable
* - `edit_current_value`
- `boolean`
- optional. set if the current value is editable
* - `edit_max_value`
- `boolean`
- optional. set if the maximum value is editable
* - `current_value`
- `integer`, `float`
- optional. this is the current value of the progress. It can be the name of any field
that is present in the view, or any numerical value set directly.
* - `max_value`
- `integer`, `float`
- optional. this value is used to set a maximum value. It can be the name of any field
that is present in the view, or any numerical value set directly.
.. code-block:: xml
<field name="my_field" widget="progressbar" options="{'editable': true, 'current_value': 'quantity', 'max_value': 'available_stock'}" />
.. _frontend/fields/radio_field:
RadioField
----------
- Location: `@web/fields/radio_field`
- Technical name: `radio`
- Supported types: `many2one`, `selection`
.. _frontend/fields/remaining_days_field:
RemainingDaysField
------------------
- Location: `@web/fields/remaining_days_field`
- Technical name: `remaining_days`
- Supported types: `date`, `datetime`
.. _frontend/fields/selection_field:
SelectionField
--------------
- Location: `@web/fields/selection_field`
- Technical name: `selection`
- Supported types: `selection`
.. _frontend/fields/stat_info_field:
StatInfoField
-------------
- Location: `@web/fields/stat_info_field`
- Technical name: `statinfo`
- Supported types: `float`, `integer`
.. _frontend/fields/statusbar_field:
StatusBarField
--------------
- Location: `@web/fields/statusbar_field`
- Technical name: `statusbar`
- Supported types: `many2one`, `selection`
.. _frontend/fields/text_field:
TextField
---------
- Location: `@web/fields/text_field`
- Technical name: `text`
- Supported types: `html`, `text`
.. _frontend/fields/url_field:
UrlField
--------
- Location: `@web/fields/url_field`
- Technical name: `url`
- Supported types: `char`
The `UrlField` component represents a URL. That field
has a text input in edit mode, and a link with an `href` to the given value. By default,
the URL value is displayed when the view is readonly, but if an other value is given as
the `text` attribute, the link will display the given value instead.
It supports the following options:
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Name
- Type
- Description
* - `website_path`
- `boolean`
- optional. if `true`, the href will be the exact given value. No prefix will be added to format the URL
.. code-block:: xml
<field name="my_field" widget="url" options="{'website_path': true}" />

View File

@@ -1,649 +0,0 @@
==================
Framework Overview
==================
Introduction
============
The Odoo Javascript framework is a set of features/building blocks provided by
the ``web/`` addon to help build odoo applications running in the browser. At
the same time, the Odoo Javascript framework is a single page application,
usually known as the *web client* (available at the url ``/web``).
The web client started as an application made with a custom class and widget
system, but it is now transitioning to using native javascript classes instead,
and Owl as a component system. This explains why both systems are currently in
use in the codebase.
From a high-level perspective, the web client is a single-page application: it
does not need to request a full page from the server each time the user performs
an action. Instead, it only requests what it needs and then replaces/updates the
current screen accordingly. Also, it manages the url to keep it in sync with
the current state.
The javascript framework (all or some parts) is also used in other situations,
such as the Odoo website or the point of sale. This reference is mostly focused
on the web client.
.. note::
It is common in the Odoo ecosystem to see the words *frontend* and *backend*
as synonyms for the odoo website (public) and the web client, respectively.
This terminology is not to be confused with the more common use of
browser-code (frontend) and server (backend).
.. note::
In this documentation, the word *component* always refers to new Owl
components, and *widget* refers to old Odoo widgets.
.. note::
All new development should be done in Owl, if possible!
Code structure
==============
The ``web/static/src`` folder contains all the ``web/`` javascript (and css and
templates) codebase. Here is a list of the most important folders:
- ``core/`` most of the low level features
- ``fields/`` all field components
- ``views/`` all javascript views components (``form``, ``list``, ...)
- ``search/`` control panel, search bar, search panel, ...
- ``webclient/`` the web client specific code: navbar, user menu, action service, ...
The ``web/static/src`` is the root folder. Everything inside can simply be
imported by using the ``@web`` prefix. For example, here is how one can import
the ``memoize`` function located in ``web/static/src/core/utils/functions``:
.. code-block:: javascript
import { memoize } from "@web/core/utils/functions";
WebClient Architecture
======================
As mentioned above, the web client is an owl application. Here is a slightly
simplified version of its template:
.. code-block:: xml
<t t-name="web.WebClient" owl="1">
<body class="o_web_client">
<NavBar/>
<ActionContainer/>
<MainComponentsContainer/>
</body>
</t>
As we can see, it basically is a wrapper for a navbar, the current action and
some additional components. The ``ActionContainer`` is a higher order component
that will display the current action controller (so, a client action, or a
specific view in the case of actions of type ``act_window``). Managing actions
is a huge part of its work: the action service keeps in memory a stack of
all active actions (represented in the breadcrumbs), and coordinates each
change.
Another interesting thing to note is the ``MainComponentsContainer``: it is
simply a component that displays all components registered in the
``main_components`` registry. This is how other parts of the system can extend
the web client.
.. _frontend/framework/environment:
Environment
===========
As an Owl application, the Odoo web client defines its own environment (components
can access it using ``this.env``). Here is a description of what Odoo adds to
the shared ``env`` object:
.. list-table::
:widths: 25 75
:header-rows: 1
* - Key
- Value
* - `qweb`
- required by Owl (contains all templates)
* - `bus`
- :ref:`main bus <frontend/framework/bus>`, used to coordinate some generic events
* - `services`
- all deployed :ref:`services <frontend/services>` (should usually be accessed
with the `useService` hook)
* - `debug`
- string. If non empty, the web client is in :ref:`debug mode <frontend/framework/debug_mode>`
* - `_t`
- translation function
* - `isSmall`
- boolean. If true, the web client is currently in mobile mode (screen width <= 767px)
So, for example, to translate a string in a component (note: templates are
automatically translated, so no specific action is required in that case), one
can do this:
.. code-block:: javascript
const someString = this.env._t('some text');
.. note::
Having a reference to the environment is quite powerful, because it provides
access to all services. This is useful in many cases: for example,
user menu items are mostly defined as a string, and a function taking the `env`
as unique argument. This is enough to express all user menu needs.
Building Blocks
===============
Most of the web client is built with a few types of abstractions: registries,
services, components and hooks.
Registries
----------
:ref:`Registries <frontend/registries>` are basically a simple key/value mapping
that stores some specific kind of objects. They are an important part of the
extensibility of the UI: once some object is registered, the rest of the web
client can use it. For example, the field registry contains all field components
(or widgets) that can be used in views.
.. code-block:: javascript
import { registry } from "./core/registry";
class MyFieldChar extends owl.Component {
// some code
}
registry.category("fields").add("my_field_char", MyFieldChar);
Note that we import the main registry from ``@web/core/registry`` then open the
sub registry ``fields``.
Services
--------
:ref:`Services <frontend/services>` are long lived pieces of code that provide a
feature. They may be imported by components (with ``useService``) or by other
services. Also, they can declare a set of dependencies. In that sense, services
are basically a DI (dependency injection) system. For example, the ``notification``
service provides a way to display a notification, or the ``rpc`` service is the
proper way to perform a request to the Odoo server.
The following example registers a simple service that displays a notification
every 5 second:
.. code-block:: javascript
import { registry } from "./core/registry";
const myService = {
dependencies: ["notification"],
start(env, { notification }) {
let counter = 1;
setInterval(() => {
notification.add(`Tick Tock ${counter++}`);
}, 5000);
}
};
serviceRegistry.add("myService", myService);
Components and Hooks
--------------------
:ref:`Components <frontend/components>` and :ref:`hooks <frontend/hooks>` are ideas coming from the
`Owl component system <https://github.com/odoo/owl/blob/master/doc/readme.md>`_.
Odoo components are simply owl components that are part of the web client.
`Hooks <https://github.com/odoo/owl/blob/master/doc/reference/hooks.md>`_ are a
way to factorize code, even if it depends on lifecycle. This is a
composable/functional way to inject a feature in a component. They can be seen
as a kind of mixin.
.. code-block:: javascript
function useCurrentTime() {
const state = useState({ now: new Date() });
const update = () => state.now = new Date();
let timer;
onWillStart(() => timer = setInterval(update, 1000));
onWillUnmount(() => clearInterval(timer));
return state;
}
Context
=======
An important concept in the Odoo javascript is the *context*: it provides a way
for code to give more context to a function call or a rpc, so other parts of the
system can properly react to that information. In some way, it is like a bag of
information that is propagated everywhere. It is useful in some situations, such
as letting the Odoo server know that a model rpc comes from a specific form view,
or activating/disabling some features in a component.
There are two different contexts in the Odoo web client: the *user context* and
the *action context* (so, we should be careful when using the word *context*: it
could mean a different thing depending on the situation).
.. note::
The `context` object may be useful in many cases, but one should be careful
not to overuse it! Many problems can be solved in a standard way without
modifying the context.
.. _frontend/framework/user_context:
User Context
------------
The *user context* is a small object containing various informations related to
the current user. It is available through the `user` service:
.. code-block:: javascript
class MyComponent extends Component {
setup() {
const user = useService("user");
console.log(user.context);
}
}
It contains the following information:
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Name
- Type
- Description
* - `allowed_company_ids`
- `number[]`
- the list of active company ids for the user
* - `lang`
- `string`
- the user language code (such as "en_us")
* - `tz`
- `string`
- the user current timezone (for example "Europe/Brussels")
In practice, the `orm` service automatically adds the user context to each of
its requests. This is why it is usually not necessary to import it directly in
most cases.
.. note::
The first element of the `allowed_company_ids` is the main company of the user.
Action Context
--------------
The :ref:`ir.actions.act_window<reference/actions/window>` and
:ref:`ir.actions.client<reference/actions/client>` support an optional `context` field.
This field is a `char` that represents an object. Whenever the corresponding
action is loaded in the web client, this context field will be evaluated as an
object and given to the component that corresponds to the action.
.. code-block:: xml
<field name="context">{'search_default_customer': 1}</field>
It can be used in many different ways. For example, the views add the
action context to every requests made to the server. Another important use is to
activate some search filter by default (see example above).
Sometimes, when we execute new actions manually (so, programmatically, in javascript),
it is useful to be able to extend the action context. This can be done with the
`additional_context` argument.
.. code-block:: javascript
// in setup
let actionService = useService("action");
// in some event handler
actionService.doAction("addon_name.something", {
additional_context:{
default_period_id: defaultPeriodId
}
});
In this example, the action with xml_id `addon_name.something` will be loaded,
and its context will be extended with the `default_period_id` value. This is a
very important usecase that lets developers combine actions together by providing
some information to the next action.
.. _frontend/framework/pyjs:
Python Interpreter
==================
The Odoo framework features a built-in small python interpreter. Its purpose
is to evaluate small python expressions. This is important, because views in
Odoo have modifiers written in python, but they need to be evaluated by the
browser.
Example:
.. code-block:: js
import { evaluateExpr } from "@web/core/py_js/py";
evaluateExpr("1 + 2*{'a': 1}.get('b', 54) + v", { v: 33 }); // returns 142
The ``py`` javascript code exports 5 functions:
.. js:function:: tokenize(expr)
:param string expr: the expression to tokenize
:returns: Token[] a list of token
.. js:function:: parse(tokens)
:param Token[] tokens: a list of tokens
:returns: AST an abstract syntax tree structure representing the expression
.. js:function:: parseExpr(expr)
:param string expr: a string representing a valid python expression
:returns: AST an abstract syntax tree structure representing the expression
.. js:function:: evaluate(ast[, context])
:param AST ast: a AST structure that represents an expression
:param Object context: an object that provides an additional evaluation context
:returns: any the resulting value of the expression, with respect to the context
.. js:function:: evaluateExpr(expr[, context])
:param string expr: a string representing a valid python expression
:param Object context: an object that provides an additional evaluation context
:returns: any the resulting value of the expression, with respect to the context
.. _frontend/framework/domains:
Domains
=======
Broadly speaking, domains in Odoo represent a set of records that matches some
specified conditions. In javascript, they are usually represented either as a
list of conditions (or of operators: `|`, `&` or `!` in prefix notation), or as string
expressions. They don't have to be normalized (the `&` operator is implied if
necessary). For example:
.. code-block:: javascript
// list of conditions
[]
[["a", "=", 3]]
[["a", "=", 1], ["b", "=", 2], ["c", "=", 3]]
["&", "&", ["a", "=", 1], ["b", "=", 2], ["c", "=", 3]]
["&", "!", ["a", "=", 1], "|", ["a", "=", 2], ["a", "=", 3]]
// string expressions
"[('some_file', '>', a)]"
"[('date','>=', (context_today() - datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]"
"[('date', '!=', False)]"
String expressions are more powerful than list expressions: they can contain
python expressions and unevaluated values, that depends on some evaluation context.
However, manipulating string expressions is more difficult.
Since domains are quite important in the web client, Odoo provides a `Domain`
class:
.. code-block:: javascript
new Domain([["a", "=", 3]]).contains({ a: 3 }) // true
const domain = new Domain(["&", "&", ["a", "=", 1], ["b", "=", 2], ["c", "=", 3]]);
domain.contains({ a: 1, b: 2, c: 3 }); // true
domain.contains({ a: -1, b: 2, c: 3 }); // false
// next expression returns ["|", ("a", "=", 1), ("b", "<=", 3)]
Domain.or([[["a", "=", 1]], "[('b', '<=', 3)]"]).toString();
Here is the `Domain` class description:
.. js:class:: Domain([descr])
:param descr: a domain description
:type descr: string | any[] | Domain
.. js:method:: contains(record)
:param Object record: a record object
:returns: boolean
Returns true if the record matches all the condition specified by the domain
.. js:method:: toString()
:returns: string
Returns a string description for the domain
.. js:method:: toList([context])
:param Object context: evaluation context
:returns: any[]
Returns a list description for the domain. Note that this method takes an
optional `context` object that will be used to replace all free variables.
.. code-block:: javascript
new Domain(`[('a', '>', b)]`).toList({ b:3 }); // [['a', '>', 3]]
The `Domain` class also provides 4 useful static methods to combine domains:
.. code-block:: javascript
// ["&", ("a", "=", 1), ("uid", "<=", uid)]
Domain.and([[["a", "=", 1]], "[('uid', '<=', uid)]"]).toString();
// ["|", ("a", "=", 1), ("uid", "<=", uid)]
Domain.or([[["a", "=", 1]], "[('uid', '<=', uid)]"]).toString();
// ["!", ("a", "=", 1)]
Domain.not([["a", "=", 1]]).toString();
// ["&", ("a", "=", 1), ("uid", "<=", uid)]
Domain.combine([[["a", "=", 1]], "[('uid', '<=', uid)]"], "AND").toString();
.. staticmethod:: Domain.and(domains)
:param domains: a list of domain representations
:type domains: string[] | any[][] | Domain[]
:returns: Domain
Returns a domain representing the intersection of all domains.
.. staticmethod:: Domain.or(domains)
:param domains: a list of domain representations
:type domains: string[] | any[][] | Domain[]
:returns: Domain
Returns a domain representing the union of all domains.
.. staticmethod:: Domain.not(domain)
:param domain: a domain representation
:type domain: string | any[] | Domain
:returns: Domain
Returns a domain representing the negation of the domain argument
.. staticmethod:: Domain.combine(domains, operator)
:param domains: a list of domain representations
:type domains: string[] | any[][] | Domain[]
:param operator: an operator
:type operator: 'AND' or 'OR'
:returns: Domain
Returns a domain representing either the intersection or the union of all the
domains, depending on the value of the operator argument.
.. _frontend/framework/bus:
Bus
===
The web client :ref:`environment <frontend/framework/environment>` object contains an event
bus, named `bus`. Its purpose is to allow various parts of the system to properly
coordinate themselves, without coupling them. The `env.bus` is an owl
`EventBus <https://github.com/odoo/owl/blob/master/doc/reference/event_bus.md>`_,
that should be used for global events of interest.
.. code-block:: javascript
// for example, in some service code:
env.bus.on("WEB_CLIENT_READY", null, doSomething);
Here is a list of the events that can be triggered on this bus:
.. list-table::
:header-rows: 1
* - Message
- Payload
- Trigger
* - ``ACTION_MANAGER:UI-UPDATED``
- a mode indicating what part of the ui has been updated ('current', 'new' or 'fullscreen')
- the rendering of the action requested to the action manager is done
* - ``ACTION_MANAGER:UPDATE``
- next rendering info
- the action manager has finished computing the next interface
* - ``MENUS:APP-CHANGED``
- none
- the menu service's current app has changed
* - ``ROUTE_CHANGE``
- none
- the url hash was changed
* - ``RPC:REQUEST``
- rpc id
- a rpc request has just started
* - ``RPC:RESPONSE``
- rpc id
- a rpc request is completed
* - ``WEB_CLIENT_READY``
- none
- the web client has been mounted
* - ``FOCUS-VIEW``
- none
- the main view should focus itself
* - ``CLEAR-CACHES``
- none
- all internal caches should be cleared
* - ``CLEAR-UNCOMMITTED-CHANGES``
- list of functions
- all views with uncommitted changes should clear them, and push a callback in the list
Browser Object
==============
The javascript framework also provides a special object ``browser`` that
provides access to many browser APIs, like ``location``, ``localStorage``
or ``setTimeout``. For example, here is how one could use the
``browser.setTimeout`` function:
.. code-block:: javascript
import { browser } from "@web/core/browser/browser";
// somewhere in code
browser.setTimeout(someFunction, 1000);
It is mostly interesting for testing purposes: all code using the browser object
can be tested easily by mocking the relevant functions for the duration of the
test.
It contains the following content:
.. list-table::
* - `addEventListener`
- `cancelAnimationFrame`
- `clearInterval`
* - `clearTimeout`
- `console`
- `Date`
* - `fetch`
- `history`
- `localStorage`
* - `location`
- `navigator`
- `open`
* - `random`
- `removeEventListener`
- `requestAnimationFrame`
* - `sessionStorage`
- `setInterval`
- `setTimeout`
* - `XMLHttpRequest`
-
-
.. _frontend/framework/debug_mode:
Debug mode
==========
Odoo can sometimes operate in a special mode called the `debug` mode. It is used
for two main purposes:
- display additional information/fields for some particular screens,
- provide some additional tools to help developer debug the Odoo interface.
The `debug` mode is described by a string. An empty string means that the `debug`
mode is not active. Otherwise, it is active. If the string contains `assets` or
`tests`, then the corresponding specific sub modes are activated (see below). Both
modes can be active at the same time, for example with the string `assets,tests`.
The `debug` mode current value can be read in the :ref:`environment<frontend/framework/environment>`:
`env.debug`.
.. tip::
To show menus, fields or view elements only in debug mode, you should target
the group `base.group_no_one`:
.. code-block:: xml
<field name="fname" groups="base.group_no_one"/>
.. seealso::
- :ref:`Activate the debug mode <developer-mode>`
.. _frontend/framework/assets_debug_mode:
Assets mode
-----------
The `debug=assets` sub mode is useful to debug javascript code: once activated,
the :ref:`assets<reference/assets>` bundles are no longer minified, and source-maps
are generated as well. This makes it useful to debug all kind of javascript code.
.. _frontend/framework/tests_debug_mode:
Tests mode
----------
There is another sub mode named `tests`: if enabled, the server injects the
bundle `web.assets_tests` in the page. This bundle contains mostly test tours
(tours whose purpose is to test a feature, not to show something interesting to
users). The `tests` mode is then useful to be able to run these tours.
.. seealso::
- `Owl Repository <https://github.com/odoo/owl>`_

View File

@@ -1,203 +0,0 @@
.. _frontend/hooks:
=====
Hooks
=====
`Owl hooks <https://github.com/odoo/owl/blob/master/doc/reference/hooks.md>`_ are a
way to factorize code, even if it depends on some component lifecycle. Most hooks
provided by Owl are related to the lifecycle of a component, but some of them (such as
`useComponent <https://github.com/odoo/owl/blob/master/doc/reference/hooks.md#usecomponent>`_)
provide a way to build specific hooks.
Using these hooks, it is possible to build many customized hooks that help solve
a specific problem, or make some common tasks easier. The rest of this page
documents the list of hooks provided by the Odoo web framework.
.. list-table::
:widths: 30 70
:header-rows: 1
* - Name
- Short Description
* - :ref:`useAssets <frontend/hooks/useassets>`
- load assets
* - :ref:`useBus <frontend/hooks/usebus>`
- subscribe and unsubscribe to a bus
* - :ref:`usePager <frontend/hooks/usepager>`
- Display the pager of the control panel of a view.
* - :ref:`usePosition <frontend/hooks/useposition>`
- position an element relative to a target
.. _frontend/hooks/useassets:
useAssets
=========
Location
--------
`@web/core/assets`
Description
-----------
See the section on :ref:`lazy loading assets <frontend/assets/lazy_loading>` for
more details.
.. _frontend/hooks/usebus:
useBus
======
Location
--------
`@web/core/utils/hooks`
Description
-----------
Add and clear an event listener to a bus. This hook ensures that
the listener is properly cleared when the component is unmounted.
.. code-block:: javascript
import { useBus } from "@web/core/utils/hooks";
class MyComponent {
setup() {
useBus(this.env.bus, "some-event", event => {
console.log(event);
});
}
}
API
---
.. js:function:: useBus(bus, eventName, callback)
:param EventBus bus: the target event bus
:param string eventName: the name of the event that we want to listen to
:param function callback: listener callback
.. _frontend/hooks/usepager:
usePager
========
Location
--------
`@web/search/pager_hook`
Description
-----------
Display the :ref:`Pager <frontend/pager>` of the control panel of a view. This hooks correctly sets `env.config` to provide the props to the pager.
.. code-block:: javascript
import { usePager } from "@web/search/pager_hook";
class CustomView {
setup() {
const state = owl.hooks.useState({
offset: 0,
limit: 80,
total: 50,
});
usePager(() => {
return {
offset: this.state.offset,
limit: this.state.limit,
total: this.state.total,
onUpdate: (newState) => {
Object.assign(this.state, newState);
},
};
});
}
}
API
---
.. js:function:: usePager(getPagerProps)
:param function getPagerProps: function that returns the pager props.
.. _frontend/hooks/useposition:
usePosition
===========
Location
--------
`@web/core/position/position_hook`
Description
-----------
Helps positioning a component (or a specific HTMLElement) relatively to a target
HTMLElement. This hook ensures the positioning is updated when the window is
resized/scrolled.
.. code-block:: javascript
import { usePosition } from "@web/core/position/position_hook";
class MyPopover {
setup() {
// Here, the target is an HTMLElement
usePosition(this.props.target);
}
}
MyPopover.template = owl.tags.xml`<div>I am positioned through a wonderful hook!</div>`
.. note::
The following CSS classes can be used to style the target HTMLElement:
- `o-popper-position`
- `o-popper-position--{D}{V}` where `{D}` and `{V}` are replaced by the first letter of the corresponding Direction and Variant (see Options table below for valid directions and variants). E.g.: for position `bottom-end`, the class name will be `o-popper-position--be`.
API
---
.. js:function:: usePosition(reference[, options])
:param reference: the target HTMLElement to be positioned from
:type reference: HTMLElement or ()=>HTMLElement
:param Options options: the positioning options (see table below)
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Option
- Type
- Description
* - `popper`
- string | undefined
- this is the element that will get positioned. You can provide here a
`useRef reference <https://github.com/odoo/owl/blob/master/doc/reference/hooks.md#useref>`_.
If not provided, `this.el` is used (default: `undefined`).
* - `container`
- HTMLElement
- the container from which the popper is expected not to overflow. If
overflowing occurs, other popper positions are tried until a not
overflowing one is found. (default: the `<html/>` node)
* - `margin`
- number
- added margin between popper and reference elements (default: `0`)
* - `position`
- string
- the desired position. It is a string composed of one direction and one
variant separated by a dash character. Valid directions are: `top`,
`bottom`, `right`, `left`. Valid variants are: `start`,
`middle`, `end`. The variant can be omitted (default variant is
`middle`). Examples of valid positions: `right-end`, `top-start`,
`left-middle`, `left`. (default position: `bottom`)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

View File

@@ -1,392 +0,0 @@
.. _frontend/js_modules:
==================
Javascript Modules
==================
Odoo supports three different kinds of javascript files:
- :ref:`plain javascript files <frontend/modules/plain_js>` (no module system),
- :ref:`native javascript module <frontend/modules/native_js>`.
- :ref:`Odoo modules <frontend/modules/odoo_module>` (using a custom module system),
As described in the :ref:`assets management page <reference/assets>`,
all javascript files are bundled together and served to the browser.
Note that native javascript files are processed by the Odoo server and transformed into Odoo custom modules.
Let us briefly explain the purpose behind each kind of javascript file. Plain
javascript files should be reserved only for external libraries and some small
specific low level purposes. All new javascript files should be created in the
native javascript module system. The custom module system is only useful for old,
not yet converted files.
.. _frontend/modules/plain_js:
Plain Javascript files
======================
Plain javascript files can contain arbitrary content. It is advised to use the
*iife* :dfn:`immediately invoked function execution` style when writing such a file:
.. code-block:: javascript
(function () {
// some code here
let a = 1;
console.log(a);
})();
The advantages of such files is that we avoid leaking local variables to the
global scope.
Clearly, plain javascript files do not offer the benefits of a module system, so
one needs to be careful about the order in the bundle (since the browser will
execute them precisely in that order).
.. note::
In Odoo, all external libraries are loaded as plain javascript files.
.. _frontend/modules/native_js:
Native Javascript Modules
=========================
Most new Odoo javascript code should use the native javascript module system. This
is simpler, and brings the benefits of a better developer experience with a better
integration with the IDE.
There is a very important point to know: Odoo needs to know which files
should be translated into :ref:`Odoo modules <frontend/modules/odoo_module>` and which
files should not be translated. This is an opt-in system: Odoo will look at the
first line of a JS file and check if it contains the string *@odoo-module*. If so, it will
automatically be converted to an Odoo module.
For example, let us consider the following module, located in :file:`web/static/src/file_a.js`:
.. code-block:: javascript
/** @odoo-module **/
import { someFunction } from './file_b';
export function otherFunction(val) {
return someFunction(val + 3);
}
Note the comment in the first line: it describes that this file should be converted.
Any file without this comment will be kept as-is (which will most likely be an
error). This file will then be translated into an Odoo module that look like this:
.. code-block:: javascript
odoo.define('@web/file_a', function (require) {
'use strict';
let __exports = {};
const { someFunction } = require("@web/file_b");
__exports.otherFunction = function otherFunction(val) {
return someFunction(val + 3);
};
return __exports;
)};
So, as you can see, the transformation is basically adding `odoo.define` on top,
and updating the import/export statements.
Another important point is that the translated module has an official name:
*@web/file_a*. This is the actual name of the module. Every relative imports
will be converted as well. Every file located in an Odoo addon
:file:`some_addon/static/src/path/to/file.js` will be assigned a name prefixed by the
addon name like this: *@some_addon/path/to/file*.
Relative imports work, but only if the modules are in the same Odoo addon. So, imagine that we have
the following file structure:
::
addons/
web/
static/
src/
file_a.js
file_b.js
stock/
static/
src/
file_c.js
The file :file:`file_b` can import :file:`file_a` like this:
.. code-block:: javascript
/** @odoo-module **/
import {something} from `./file_a`
But :file:`file_c` need to use the full name:
.. code-block:: javascript
/** @odoo-module **/
import {something} from `@web/file_a`
Aliased modules
---------------
Because :ref:`Odoo modules <frontend/modules/odoo_module>` follow a different module naming pattern, a system exists to allow a smooth
transition towards the new system. Currently, if a file is converted to a module (and therefore
follow the new naming convention), other files not yet converted to ES6-like syntax in the project
won't be able to require the module. Aliases are here to map old names with new ones by creating a
small proxy function. The module can then be called by its new *and* old name.
To add such alias, the comment tag on top of the file should look like this:
.. code-block:: javascript
/** @odoo-module alias=web.someName**/
import { someFunction } from './file_b';
export default function otherFunction(val) {
return someFunction(val + 3);
}
Then, the translated module will also create an alias with the requested name:
.. code-block:: javascript
odoo.define(`web.someName`, function(require) {
return require('@web/file_a')[Symbol.for("default")];
});
The default behaviour of aliases is to re-export the ``default`` value of the
module they alias. This is because "classic" modules generally export a single
value which would be used directly, roughly matching the semantics of default
exports.
However it is also possible to delegate more directly, and follow the exact
behaviour of the aliased module:
.. code-block:: javascript
/** @odoo-module alias=web.someName default=0**/
import { someFunction } from './file_b';
export function otherFunction(val) {
return someFunction(val + 3);
}
In that case, this will define an alias with exactly the values exported by the
original module:
.. code-block:: javascript
odoo.define(`web.someName`, function(require) {
return require('@web/file_a');
});
.. note::
Only one alias can be defined using this method. If you were to need another one to have, for
example, three names to call the same module, you would have to add a proxy manually.
This is not good practice and should be avoided unless there is no other options.
Limitations
-----------
For performance reasons, Odoo does not use a full javascript
parser to transform native modules. There are, therefore, a number of limitations including but not
limited to:
- an `import` or `export` keyword cannot be preceded by a non-space character,
- a multiline comment or string cannot have a line starting by `import` or `export`
.. code-block:: javascript
// supported
import X from "xxx";
export X;
export default X;
import X from "xxx";
/*
* import X ...
*/
/*
* export X
*/
// not supported
var a= 1;import X from "xxx";
/*
import X ...
*/
- when you export an object, it can't contain a comment
.. code-block:: javascript
// supported
export {
a as b,
c,
d,
}
export {
a
} from "./file_a"
// not supported
export {
a as b, // this is a comment
c,
d,
}
export {
a /* this is a comment */
} from "./file_a"
- Odoo needs a way to determine if a module is described by a path (like :file:`./views/form_view`)
or a name (like `web.FormView`). It has to use a heuristic to do just that: if there is a `/` in
the name, it is considered a path. This means that Odoo does not really support module names with
a `/` anymore.
As "classic" modules are not deprecated and there is currently no plan to remove them, you can and should keep using
them if you encounter issues with, or are constrained by the limitations of, native modules. Both styles can coexist
within the same Odoo addon.
.. _frontend/modules/odoo_module:
Odoo Module System
===================
Odoo has defined a small module system (located in the file
:file:`addons/web/static/src/js/boot.js`, which needs to be loaded first). The Odoo
module system, inspired by AMD, works by defining the function `define`
on the global odoo object. We then define each javascript module by calling that
function. In the Odoo framework, a module is a piece of code that will be executed
as soon as possible. It has a name and potentially some dependencies. When its
dependencies are loaded, a module will then be loaded as well. The value of the
module is then the return value of the function defining the module.
As an example, it may look like this:
.. code-block:: javascript
// in file a.js
odoo.define('module.A', function (require) {
"use strict";
var A = ...;
return A;
});
// in file b.js
odoo.define('module.B', function (require) {
"use strict";
var A = require('module.A');
var B = ...; // something that involves A
return B;
});
An alternative way to define a module is to give explicitly a list of dependencies
in the second argument.
.. code-block:: javascript
odoo.define('module.Something', ['module.A', 'module.B'], function (require) {
"use strict";
var A = require('module.A');
var B = require('module.B');
// some code
});
If some dependencies are missing/non ready, then the module will simply not be
loaded. There will be a warning in the console after a few seconds.
Note that circular dependencies are not supported. It makes sense, but it means that one
needs to be careful.
Defining a module
-----------------
The `odoo.define` method is given three arguments:
- `moduleName`: the name of the javascript module. It should be a unique string.
The convention is to have the name of the odoo addon followed by a specific
description. For example, `web.Widget` describes a module defined in the `web`
addon, which exports a `Widget` class (because the first letter is capitalized)
If the name is not unique, an exception will be thrown and displayed in the
console.
- `dependencies`: the second argument is optional. If given, it should be a list
of strings, each corresponding to a javascript module. This describes the
dependencies that are required to be loaded before the module is executed. If
the dependencies are not explicitly given here, then the module system will
extract them from the function by calling toString on it, then using a regexp
to find all the `require` statements.
.. code-block:: javascript
odoo.define('module.Something', ['web.ajax'], function (require) {
"use strict";
var ajax = require('web.ajax');
// some code here
return something;
});
- finally, the last argument is a function which defines the module. Its return
value is the value of the module, which may be passed to other modules requiring
it. Note that there is a small exception for asynchronous modules, see the
next section.
If an error happens, it will be logged (in debug mode) in the console:
* `Missing dependencies`:
These modules do not appear in the page. It is possible that the JavaScript
file is not in the page or that the module name is wrong
* `Failed modules`:
A javascript error is detected
* `Rejected modules`:
The module returns a rejected Promise. It (and its dependent modules) is not
loaded.
* `Rejected linked modules`:
Modules who depend on a rejected module
* `Non loaded modules`:
Modules who depend on a missing or a failed module
Asynchronous modules
---------------------
It can happen that a module needs to perform some work before it is ready. For
example, it could do an rpc to load some data. In that case, the module can
simply return a promise. The module system will simply
wait for the promise to complete before registering the module.
.. code-block:: javascript
odoo.define('module.Something', function (require) {
"use strict";
var ajax = require('web.ajax');
return ajax.rpc(...).then(function (result) {
// some code here
return something;
});
});

View File

@@ -1,522 +0,0 @@
.. _frontend/components:
==============
Owl Components
==============
The Odoo Javascript framework uses a custom component framework called Owl. It
is a declarative component system, loosely inspired by Vue and React. Components
are defined using :doc:`QWeb templates <qweb>`, enriched with some Owl
specific directives. The official
`Owl documentation <https://github.com/odoo/owl/blob/master/doc/readme.md>`_
contains a complete reference and a tutorial.
.. important::
Although the code can be found in the `web` module, it is maintained from a
separate GitHub repository. Any modification to Owl should therefore be made
through a pull request on https://github.com/odoo/owl.
.. note::
Currently, all Odoo versions (starting in version 14) share the same Owl version.
Using Owl components
====================
The `Owl documentation`_ already documents in detail the Owl framework, so this
page will only provide Odoo specific information. But first, let us see how we
can make a simple component in Odoo.
.. code-block:: javascript
const { useState } = owl.hooks;
const { xml } = owl.tags;
class MyComponent extends Component {
setup() {
this.state = useState({ value: 1 });
}
increment() {
this.state.value++;
}
}
MyComponent.template = xml
`<div t-on-click="increment">
<t t-esc="state.value">
</div>`;
This example shows that Owl is available as a library in the global namespace as
``owl``: it can simply be used like most libraries in Odoo. Note that we
defined here the template as a static property, but without using the `static`
keyword, which is not available in some browsers (Odoo javascript code should
be Ecmascript 2019 compliant).
We define here the template in the javascript code, with the help of the ``xml``
helper. However, it is only useful to get started. In practice, templates in
Odoo should be defined in an xml file, so they can be translated. In that case,
the component should only define the template name.
In practice, most components should define 2 or 3 files, located at the same
place: a javascript file (``my_component.js``), a template file (``my_component.xml``)
and optionally a scss (or css) file (``my_component.scss``). These files should
then be added to some assets bundle. The web framework will take care of
loading the javascript/css files, and loading the templates into Owl.
Here is how the component above should be defined:
.. code-block:: javascript
const { useState } = owl.hooks;
class MyComponent extends Component {
...
}
MyComponent.template = 'myaddon.MyComponent';
And the template is now located in the corresponding xml file:
.. code-block:: xml
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="myaddon.MyComponent" owl="1">
<div t-on-click="increment">
<t t-esc="state.value"/>
</div>
</t>
</templates>
Odoo code is not yet completely made in Owl, so it needs a way to tell the
difference between Owl templates (new code) and old templates (for components). To
do that in a backward-compatible way, all new templates should be defined with
the ``owl`` attribute set to 1.
.. note::
Do not forget to set ``owl="1"`` in your Owl templates!
.. note::
Template names should follow the convention `addon_name.ComponentName`.
.. seealso::
- `Owl Repository <https://github.com/odoo/owl>`_
.. _frontend/owl/best_practices:
Best practices
==============
First of all, components are classes, so they have a constructor. But constructors
are special methods in javascript that are not overridable in any way. Since this
is an occasionally useful pattern in Odoo, we need to make sure that no component
in Odoo directly uses the constructor method. Instead, components should use the
`setup` method:
.. code-block:: javascript
// correct:
class MyComponent extends Component {
setup() {
// initialize component here
}
}
// incorrect. Do not do that!
class IncorrectComponent extends Component {
constructor(parent, props) {
// initialize component here
}
}
Another good practice is to use a consistent convention for template names:
`addon_name.ComponentName`. This prevents name collision between odoo addons.
Reference List
==============
The Odoo web client is built with `Owl <https://github.com/odoo/owl>`_ components.
To make it easier, the Odoo javascript framework provides a suite of generic
components that can be reused in some common situations, such as dropdowns,
checkboxes or datepickers. This page explains how to use these generic components.
.. list-table::
:widths: 30 70
:header-rows: 1
* - Technical Name
- Short Description
* - :ref:`CheckBox <frontend/owl/checkbox>`
- a simple checkbox component with a label next to it
* - :ref:`Dropdown <frontend/owl/dropdown>`
- full-featured dropdown
* - :ref:`Pager <frontend/pager>`
- a small component to handle pagination
.. _frontend/owl/checkbox:
CheckBox
--------
Location
~~~~~~~~
`@web/core/checkbox/checkbox`
Description
~~~~~~~~~~~
This is a simple checkbox component with a label next to it. The checkbox is
linked to the label: the checkbox is toggled whenever the label is clicked.
.. code-block:: xml
<CheckBox value="boolean" disabled="boolean" t-on-change="onValueChange">
Some Text
</CheckBox>
Props
~~~~~
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Name
- Type
- Description
* - `value`
- `boolean`
- if true, the checkbox is checked, otherwise it is unchecked
* - `disabled`
- `boolean`
- if true, the checkbox is disabled, otherwise it is enabled
.. _frontend/owl/dropdown:
Dropdown
--------
Location
~~~~~~~~
`@web/core/dropdown/dropdown` and `@web/core/dropdown/dropdown_item`
Description
~~~~~~~~~~~
Dropdowns are surprisingly complicated components. They need to provide many
features such as:
- Toggle the item list on click
- Direct siblings dropdowns: when one is open, toggle others on hover
- Close on outside click
- Optionally close the item list when an item is selected
- Emit an event to inform which list item is clicked
- Support sub dropdowns, up to any level
- SIY: style it yourself
- Configurable hotkey to open/close a dropdown or select a dropdown item
- Keyboard navigation (arrows, tab, shift+tab, home, end, enter and escape)
- Reposition itself whenever the page scrolls or is resized
- Smartly chose the direction it should open (right-to-left direction is automatically handled).
To solve these issues once and for all, the Odoo framework provides a set of two
components: a `Dropdown` component (the actual dropdown), and `DropdownItem`,
for each element in the item list.
.. code-block:: xml
<Dropdown>
<t t-set-slot="toggler">
<!-- "toggler" slot content is rendered inside a button -->
Click me to toggle the dropdown menu !
</t>
<!-- "default" slot content is rendered inside a div -->
<DropdownItem t-on-dropdown-item-selected="selectItem1">Menu Item 1</DropdownItem>
<DropdownItem t-on-dropdown-item-selected="selectItem2">Menu Item 2</DropdownItem>
</Dropdown>
Props
~~~~~
A `<Dropdown/>` component is simply a `<div class="dropdown"/>` having a
`<button class="dropdown-toggle"/>` next to menu div
(`<div class="dropdown-menu"/>`). The button is responsible for the menu
being present in the DOM or not.
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Dropdown
- Type
- Description
* - ``startOpen``
- boolean
- initial dropdown open state (defaults to `false`)
* - ``menuClass``
- string
- additional css class applied to the dropdown menu ``<div class="dropdown-menu"/>``
* - ``togglerClass``
- string
- additional css class applied to the toggler ``<button class="dropdown-toggle"/>``
* - ``hotkey``
- string
- hotkey to toggle the opening through keyboard
* - ``beforeOpen``
- function
- hook to execute logic just before opening. May be asynchronous.
* - ``manualOnly``
- boolean
- if true, only toggle the dropdown when the button is clicked on (defaults to `false`)
* - ``title``
- string
- title attribute content for the ``<button class="dropdown-toggle"/>`` (default: none)
* - ``position``
- string
- defines the desired menu opening position. RTL direction is automatically applied. Should be a valid :ref:`usePosition <frontend/hooks/useposition>` hook position. (default: ``bottom-start``)
* - ``toggler``
- ``"parent"`` or ``undefined``
- when set to ``"parent"`` the ``<button class="dropdown-toggle"/>`` is not
rendered (thus ``toggler`` slot is ignored) and the toggling feature is handled by the parent node (e.g. use
case: pivot cells). (default: ``undefined``)
A `<DropdownItem/>` is simply a span (`<span class="dropdown-item"/>`).
When a `<DropdownItem/>` is selected, it emits a custom `dropdown-item-selected`
event containing its payload. (see
`OWL Business Events <https://github.com/odoo/owl/blob/master/doc/reference/event_handling.md#business-dom-events>`_).
So, to react to such an event, one needs to define an event listener on the
`dropdown-item-selected` event.
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - DropdownItem
- Type
- Description
* - ``payload``
- Object
- payload that will be added to the `dropdown-item-selected` event (default to null)
* - `parentClosingMode`
- `none` | `closest` | `all`
- when the item is selected, control which parent dropdown will get closed:
none, closest or all (default = `all`)
* - ``hotkey``
- string
- optional hotkey to select the item
* - ``href``
- string
- if provided the DropdownItem will become an ``<a href="value" class="dropdown-item"/>`` instead of a ``<span class="dropdown-item"/>``. (default: not provided)
* - ``title``
- string
- optional title attribute which will be passed to the root node of the DropdownItem. (default: not provided)
Technical notes
~~~~~~~~~~~~~~~
The rendered DOM is structured like this:
.. code-block:: html
<div class="dropdown">
<button class="dropdown-toggle">Click me !</button>
<!-- following <div/> will or won't appear in the DOM depending on the state controlled by the preceding button -->
<div class="dropdown-menu">
<span class="dropdown-item">Menu Item 1</span>
<span class="dropdown-item">Menu Item 2</span>
</div>
</div>
To properly use a `<Dropdown/>` component, you need to populate two
`OWL slots <https://github.com/odoo/owl/blob/master/doc/reference/slots.md>`_ :
- `toggler` slot: it contains the *toggler* elements of your dropdown and is
rendered inside the dropdown `button` (unless the `toggler` prop is set to `parent`),
- `default` slot: it contains the *elements* of the dropdown menu itself and is
rendered inside the ``<div class="dropdown-menu"/>``. Although it is not mandatory, there is usually at least one
`DropdownItem` inside the `menu` slot.
When several dropdowns share the same parent element in the DOM, then they are
considered part of a group, and will notify each other about their state changes.
This means that when one of these dropdowns is open, the others will automatically
open themselves on mouse hover, without the need for a click.
Example: Direct Siblings Dropdown
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When one dropdown toggler is clicked (**File** , **Edit** or **About**), the
others will open themselves on hover.
.. code-block:: xml
<div t-on-dropdown-item-selected="onItemSelected">
<Dropdown>
<t t-set-slot="toggler">File</t>
<DropdownItem payload="'file-open'">Open</DropdownItem>
<DropdownItem payload="'file-new-document'">New Document</DropdownItem>
<DropdownItem payload="'file-new-spreadsheet'">New Spreadsheet</DropdownItem>
</Dropdown>
<Dropdown>
<t t-set-slot="toggler">Edit</t>
<DropdownItem payload="'edit-undo'">Undo</DropdownItem>
<DropdownItem payload="'edit-redo'">Redo</DropdownItem>
<DropdownItem payload="'edit-find'">Search</DropdownItem>
</Dropdown>
<Dropdown>
<t t-set-slot="toggler">About</t>
<DropdownItem payload="'about-help'">Help</DropdownItem>
<DropdownItem payload="'about-update'">Check update</DropdownItem>
</Dropdown>
</div>
Example: Multi-level Dropdown (with `t-call`)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This example shows how one could make a `File` dropdown menu, with submenus for
the `New` and `Save as...` sub elements.
.. code-block:: xml
<t t-name="addon.Dropdown.File" owl="1">
<Dropdown t-on-dropdown-item-selected="onItemSelected">
<t t-set-slot="toggler">File</t>
<DropdownItem payload="'file-open'">Open</DropdownItem>
<t t-call="addon.Dropdown.File.New"/>
<DropdownItem payload="'file-save'">Save</DropdownItem>
<t t-call="addon.Dropdown.File.Save.As"/>
</Dropdown>
</t>
<t t-name="addon.Dropdown.File.New" owl="1">
<Dropdown>
<t t-set-slot="toggler">New</t>
<DropdownItem payload="'file-new-document'">Document</DropdownItem>
<DropdownItem payload="'file-new-spreadsheet'">Spreadsheet</DropdownItem>
</Dropdown>
</t>
<t t-name="addon.Dropdown.File.Save.As" owl="1">
<Dropdown>
<t t-set-slot="toggler">Save as...</t>
<DropdownItem payload="'file-save-as-csv'">CSV</DropdownItem>
<DropdownItem payload="'file-save-as-pdf'">PDF</DropdownItem>
</Dropdown>
</t>
Example: Multi-level Dropdown (nested)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: xml
<Dropdown t-on-dropdown-item-selected="onItemSelected">
<t t-set-slot="toggler">File</t>
<DropdownItem payload="'file-open'">Open</DropdownItem>
<Dropdown>
<t t-set-slot="toggler">New</t>
<DropdownItem payload="'file-new-document'">Document</DropdownItem>
<DropdownItem payload="'file-new-spreadsheet'">Spreadsheet</DropdownItem>
</Dropdown>
<DropdownItem payload="'file-save'">Save</DropdownItem>
<Dropdown>
<t t-set-slot="toggler">Save as...</t>
<DropdownItem payload="'file-save-as-csv'">CSV</DropdownItem>
<DropdownItem payload="'file-save-as-pdf'">PDF</DropdownItem>
</Dropdown>
</Dropdown>
Example: Recursive Multi-level Dropdown
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In this example, we recursively call a template to display a tree-like structure.
.. code-block:: xml
<t t-name="addon.MainTemplate" owl="1">
<div t-on-dropdown-item-selected="onItemSelected">
<t t-call="addon.RecursiveDropdown">
<t t-set="name" t-value="'Main Menu'" />
<t t-set="items" t-value="state.menuItems" />
</t>
</div>
</t>
<t t-name="addon.RecursiveDropdown" owl="1">
<Dropdown>
<t t-set-slot="toggler"><t t-esc="name"/></t>
<t t-foreach="items" t-as="item" t-key="item.id">
<!-- If this item has no child: make it a <DropdownItem/> -->
<t t-if="!item.childrenTree.length">
<DropdownItem payload="item" t-esc="item.name"/>
</t>
<!-- Else: recursively call the current dropdown template. -->
<t t-else="" t-call="addon.RecursiveDropdown">
<t t-set="name" t-value="item.name" />
<t t-set="items" t-value="item.childrenTree" />
</t>
</t>
</t>
</Dropdown>
</t>
.. _frontend/pager:
Pager
-----
Location
~~~~~~~~
`@web/core/pager/pager`
Description
~~~~~~~~~~~
The Pager is a small component to handle pagination. A page is defined by an `offset` and a `limit` (the size of the page). It displays the current page and the `total` number of elements, for instance, "9-12 / 20". In the previous example, `offset` is 8, `limit` is 4 and `total` is 20. It has two buttons ("Previous" and "Next") to navigate between pages.
.. note::
The pager can be used anywhere but its main use is in the control panel. See the :ref:`usePager <frontend/hooks/usepager>` hook in order to manipulate the pager of the control panel.
.. code-block:: xml
<Pager offset="0" limit="80" total="50" onUpdate="doSomething" />
Props
~~~~~
.. list-table::
:widths: 20 20 60
:header-rows: 1
* - Name
- Type
- Description
* - `offset`
- `number`
- Index of the first element of the page. It starts with 0 but the pager displays `offset + 1`.
* - `limit`
- `number`
- Size of the page. The sum of `offset` and `limit` corresponds to the index of the last element of the page.
* - `total`
- `number`
- Total number of elements the page can reach.
* - `onUpdate`
- `function`
- Function that is called when page is modified by the pager. This function can be async, the pager cannot be edited while this function is executing.
* - `isEditable`
- `boolean`
- Allows to click on the current page to edit it (`true` by default).
* - `withAccessKey`
- `boolean`
- Binds access key `p` on the previous page button and `n` on the next page one (`true` by default).

View File

@@ -1,196 +0,0 @@
=============
Patching code
=============
Sometimes, we need to customize the way the UI works. Many common needs are
covered by some supported API. For example, all registries are good extension
points: the field registry allows adding/removing specialized field components,
or the main component registry allows adding components that should be displayed
all the time.
However, there are situations for which it is not sufficient. In those cases, we
may need to modify an object or a class in place. To achieve that, Odoo
provides the utility function `patch`. It is mostly useful to override/update
the behavior of some other component/piece of code that one does not control.
Description
===========
The patch function is located in `@web/core/utils/patch`:
.. js:function:: patch(obj, patchName, patchValue, options)
:param Object obj: object that should be patched
:param string patchName: unique string describing the patch
:param Object patchValue: an object mapping each key to a patchValue
:param Object options: option object (see below)
The `patch` function modifies in place the `obj` object (or class) and
applies all key/value described in the `patchValue` object. This operation
is registered under the `patchName` name, so it can be unpatched later if
necessary.
Most patch operations provide access to the parent value by using the
`_super` property (see below in the examples). To do that, the `patch` method
wraps each pair key/value in a getter that dynamically binds `_super`.
The only option is `pure (boolean)`. If set to `true`, the patch operation
does not bind the `_super` property.
Patching a simple object
========================
Here is a simple example of how an object can be patched:
.. code-block:: javascript
import { patch } from "@web/core/utils/patch";
const object = {
field: "a field",
fn() {
// do something
},
};
patch(object, "patch name", {
fn() {
// do things
},
});
When patching functions, we usually want to be able to access the ``parent``
function. Since we are working with patch objects, not ES6 classes, we cannot
use the native ``super`` keyword. So, Odoo provides a special method to simulate
this behaviour: ``this._super``:
.. code-block:: javascript
patch(object, "_super patch", {
fn() {
this._super(...arguments);
// do other things
},
});
.. warning::
``this._super`` is reassigned after each patched function is called.
This means that if you use an asynchronous function in the patch then you
cannot call ``this._super`` after an ``await``, because it may or may not be
the function that you expect. The correct way to do that is to keep a reference
to the initial ``_super`` method:
.. code-block:: javascript
patch(object, "async _super patch", {
async myAsyncFn() {
const _super = this._super.bind(this);
await Promise.resolve();
await _super(...arguments);
// await this._super(...arguments); // this._super is undefined.
},
});
Getters and setters are supported too:
.. code-block:: javascript
patch(object, "getter/setter patch", {
get number() {
return this._super() / 2;
},
set number(value) {
this._super(value * 2);
},
});
Patching a javascript class
===========================
The ``patch`` function is designed to work with anything: object or ES6 class.
However, since javascript classes work with the prototypal inheritance, when
one wishes to patch a standard method from a class, then we actually need to patch
the `prototype`:
.. code-block:: javascript
class MyClass {
static myStaticFn() {...}
myPrototypeFn() {...}
}
// this will patch static properties!!!
patch(MyClass, "static patch", {
myStaticFn() {...},
});
// this is probably the usual case: patching a class method
patch(MyClass.prototype, "prototype patch", {
myPrototypeFn() {...},
});
Also, Javascript handles the constructor in a special native way which makes it
impossible to be patched. The only workaround is to call a method in the original
constructor and patch that method instead:
.. code-block:: javascript
class MyClass {
constructor() {
this.setup();
}
setup() {
this.number = 1;
}
}
patch(MyClass.prototype, "constructor", {
setup() {
this._super(...arguments);
this.doubleNumber = this.number * 2;
},
});
.. warning::
It is impossible to patch directly the `constructor` of a class!
Patching a component
====================
Components are defined by javascript classes, so all the information above still
holds. For these reasons, Owl components should use the `setup` method, so they
can easily be patched as well (see the section on :ref:`best practices<frontend/owl/best_practices>`.
.. code-block:: javascript
patch(MyComponent.prototype, "my patch", {
setup() {
useMyHook();
},
});
Removing a patch
================
The `patch` function has a counterpart, `unpatch`, also located in `@web/core/utils/patch`.
.. js:function:: unpatch(obj, patchName)
:param Object obj: object that should be unpatched
:param string patchName: string describing the patch that should be removed
Removes an existing patch from an object `obj`. This is mostly useful for
testing purposes, when we patch something at the beginning of a test, and
unpatch it at the end.
.. code-block:: javascript
patch(object, "patch name", { ... });
// test stuff here
unpatch(object, "patch name");

View File

@@ -1,304 +0,0 @@
.. _frontend/registries:
==========
Registries
==========
Registries are (ordered) key/value maps. They are the main web client extension
points: many features provided by the Odoo javascript framework simply look up
into a registry whenever it needs a definition for some object (such as fields,
views, client actions or services). Customizing the web client is then simply
done by adding specific values in the correct registry.
.. code-block:: javascript
import { Registry } from "@web/core/registry";
const myRegistry = new Registry();
registry.add("hello", "odoo");
console.log(registry.get("hello"));
A useful feature of registries is that they maintain a set of sub registries,
obtained by the `category` method. If the sub registry does not exist yet, it
is created on the fly. All registries used by the web client are obtained
in such a way from one root registry, exported in `@web/core/registry`.
.. code-block:: javascript
import { registry } from "@web/core/registry";
const fieldRegistry = registry.category("fields");
const serviceRegistry = registry.category("services");
const viewRegistry = registry.category("views");
Registry API
============
.. js:class:: Registry()
Creates a new registry. Note that a registry is an event bus, so one can
listen to the `UPDATE` event if necessary. Registries are ordered: the
:js:meth:`getAll <Registry.getAll>` method returns a list of
values ordered according to their sequence number.
.. js:method:: add(key, value[, options])
:param string key: key for the new entry
:param any value: value for the new entry
:param Object options: options
:param boolean [options.force]: do not throw if key already exists
:param number [options.sequence]: sequence number (useful to order entries)
:returns: Registry
Inserts a value at a specific key. If the key is already used, this method
throws an error (unless the option `force` is set to true). The option
`sequence` is useful to insert the value at a specific position. This method
also triggers an `UPDATE` event.
Returns the same registry, so `add` method calls can be chained.
.. js:method:: get(key[, defaultValue])
:param string key: key for the entry
:param defaultValue any: return value if no entry for key exists
Returns the value corresponding to the `key` argument. If the registry does
not contain that key, this method returns `defaultValue` if given, or throws
an error otherwise.
.. js:method:: contains(key)
:param string key: key for the entry
:returns: boolean
Returns `true` if `key` is present in the registry
.. js:method:: getAll()
:returns: any[]
Returns the list of all elements in the registry. It is ordered
according to the sequence numbers.
.. js:method:: remove(key)
:param string key: the key for the entry that should be removed
Removes a key/value pair from the registry. This operation triggers an
`UPDATE` event.
.. js:method:: category(subcategory)
:param string subcategory: the name for the sub category
:returns: Registry
Returns the sub registry associated with the `subcategory`. If it does not
exist yet, the sub registry is created on the fly.
Reference List
==============
.. list-table::
:widths: 30 70
:header-rows: 1
* - Category
- Content
* - :ref:`effects <frontend/registries/effects>`
- implementation for all available effects
* - :ref:`formatters <frontend/registries/formatters>`
- utility functions to format values (mostly used for field values)
* - :ref:`main_components <frontend/registries/main_components>`
- top level components
* - :ref:`parsers <frontend/registries/parsers>`
- utility functions to parse values (mostly used for field values)
* - :ref:`services <frontend/registries/services>`
- all services that should be activated
* - :ref:`systray <frontend/registries/systray>`
- components displayed in the systray zone in the navbar
* - :ref:`user_menuitems <frontend/registries/usermenu>`
- menu items displayed in the user menu (top right of navbar)
.. _frontend/registries/effects:
Effect registry
---------------
The `effects` registry contains the implementations of all available effects.
See the section on the :ref:`effect service <frontend/services/effect_registry>`
for more details.
.. _frontend/registries/formatters:
Formatter registry
------------------
The `formatters` registry contains functions to format values. Each formatter
has the following API:
.. js:function:: format(value[, options])
:param value: a value of a specific type, or `false` if no value is given
:type value: T | false
:param Object options: various options
:returns: string
Formats a value and returns a string
.. seealso::
- :ref:`Parsers registry <frontend/registries/parsers>`
.. _frontend/registries/main_components:
Main components registry
------------------------
The main component registry (`main_components`) is useful for adding top level
components in the web client. The webclient has a `MainComponentsContainer` as
direct child. This component is basically a live representation of the ordered
list of components registered in the main components registry.
API
.. code-block::
interface {
Component: Owl Component class
props?: any
}
For example, the `LoadingIndicator` component can be added in the registry like
this:
.. code-block:: javascript
registry.category("main_components").add("LoadingIndicator", {
Component: LoadingIndicator,
});
.. _frontend/registries/parsers:
Parser registry
---------------
The `parsers` registry contains functions to parse values. Each parser
has the following API:
.. js:function:: parse(value[, options])
:noindex:
:param value: a string representing a value
:type value: string
:param Object options: various options (parser specific)
:returns: T a valid value
Parses a string and returns a value. If the string does not represent a valid
value, parsers can fail and throw errors.
.. seealso::
- :ref:`Formatters registry <frontend/registries/formatters>`
.. _frontend/registries/services:
Service registry
----------------
The service registry (category: `services`) contains all
:ref:`services <frontend/services>` that should be activated by the Odoo
framework.
.. code-block:: javascript
import { registry } from "@web/core/registry";
const myService = {
dependencies: [...],
start(env, deps) {
// some code here
}
};
registry.category("services").add("myService", myService);
.. _frontend/registries/systray:
Systray registry
----------------
The systray is the zone on the right of the navbar that contains various small
components, that usually display some sort of information (like the number of
unread messages), notifications and/or let the user interact with them.
The `systray` registry contains a description of these systray items, as objects
with the following three keys:
- `Component`: the component class that represents the item. Its root element
should be a `<li>` tag, otherwise it might not be styled properly.
- `props (optional)`: props that should be given to the component
- `isDisplayed (optional)`: a function that takes the :ref:`env <frontend/framework/environment>`
and returns a boolean. If true, the systray item is displayed. Otherwise it is
removed.
For example:
.. code-block:: js
import { registry } from "@web/core/registry";
class MySystrayItem extends Component {
// some component ...
}
registry.category("systray").add("myAddon.myItem", {
Component: MySystrayItem,
});
The systray registry is an ordered registry (with the `sequence` number):
.. code-block:: js
const item = {
Component: MySystrayItem
};
registry.category("systray").add("myaddon.some_description", item, { sequence: 43 });
The sequence number defaults to 50. If given, this number will be used
to order the items. The lowest sequence is on the right and the highest sequence
is on the left in the systray menu.
.. _frontend/registries/usermenu:
Usermenu registry
-----------------
The user menu registry (category: `user_menuitems`) contains all menu items that
are shown when opening the user menu (the navbar element with the user name, on
the top right).
User menu items are defined by a function taking the :ref:`env <frontend/framework/environment>`
and returning a plain object, containing the following information:
* `description` : the menu item text,
* `href` : (optional) if given (and truthy), the item text is put in a `a` tag with given attribute href,
* `callback` : callback to call when the item is selected,
* `hide`: (optional) indicates if the item should be hidden (default: `false`),
* `sequence`: (optional) determines the rank of the item among the other dropwdown items (default: 100).
The user menu calls all the functions defining items every time it is opened.
Example:
.. code-block:: js
import { registry } from "@web/core/registry";
registry.category("user_menuitems").add("my item", (env) => {
return {
description: env._t("Technical Settings"),
callback: () => { env.services.action_manager.doAction(3); };
hide: (Math.random() < 0.5),
};
}

View File

@@ -1,919 +0,0 @@
.. _frontend/services:
========
Services
========
Services are long lived pieces of code that provide a feature. They may be
imported by components (with ``useService``) or by other services. Also, they
can declare a set of dependencies. In that sense, services are basically a
DI :dfn:`dependency injection` system. For example, the ``notification`` service
provides a way to display a notification, or the ``rpc`` service is the proper
way to perform a request to the Odoo server.
The following example registers a simple service that displays a notification
every 5 seconds:
.. code-block:: javascript
import { registry } from "@web/core/registry";
const myService = {
dependencies: ["notification"],
start(env, { notification }) {
let counter = 1;
setInterval(() => {
notification.add(`Tick Tock ${counter++}`);
}, 5000);
}
};
registry.category("services").add("myService", myService);
At startup, the web client starts all services present in the `services`
registry. Note that the name used in the registry is the name of the service.
.. note::
Most code that is not a component should be *packaged* in a service, in
particular if it performs some side effect. This is very useful for testing
purposes: tests can choose which services are active, so there are less chance
for unwanted side effects interfering with the code being tested.
Defining a service
==================
A service needs to implement the following interface:
.. js:data:: dependencies
Optional list of strings. It is the list of all dependencies (other services)
that this service needs
.. js:function:: start(env, deps)
:param Environment env: the application environment
:param Object deps: all requested dependencies
:returns: value of service or Promise<value of service>
This is the main definition for the service. It can return either a value or
a promise. In that case, the service loader simply waits for the promise to
resolve to a value, which is then the value of the service.
Some services do not export any value. They may just do their work without a
need to be directly called by other code. In that case, their value will be
set to ``null`` in ``env.services``.
.. js:data:: async
Optional value. If given, it should be `true` or a list of strings.
Some services need to provide an asynchronous API. For example, the `rpc`
service is an asynchronous function, or the `orm` service provides a set of
functions to call the Odoo server.
In that case, it is possible for components that use a service to be
destroyed before the end of an asynchronous function call. Most of the time,
the asynchronous function call needs to be ignored. Doing otherwise is
potentially very risky, because the underlying component is no longer active.
The `async` flag is a way to do just that: it signals to the service creator
that all asynchronous calls coming from components should be left pending if
the component is destroyed.
Using a service
===============
A service that depends on other services and has properly declared its
``dependencies`` simply receives a reference to the corresponding services
in the second argument of the ``start`` method.
The ``useService`` hook is the proper way to use a service in a component. It
simply returns a reference to the service value, that can then be used by the
component later. For example:
.. code-block:: javascript
import { useService } from "@web/core/utils/hooks";
class MyComponent extends Component {
setup() {
const rpc = useService("rpc");
onWillStart(async () => {
this.someValue = await rpc(...);
});
}
}
Reference List
==============
.. list-table::
:widths: 25 75
:header-rows: 1
* - Technical Name
- Short Description
* - :ref:`cookie <frontend/services/cookie>`
- read or modify cookies
* - :ref:`effect <frontend/services/effect>`
- display graphical effects
* - :ref:`http <frontend/services/http>`
- perform low level http calls
* - :ref:`notification <frontend/services/notification>`
- display notifications
* - :ref:`router <frontend/services/router>`
- manage the browser url
* - :ref:`rpc <frontend/services/rpc>`
- send requests to the server
* - :ref:`scroller <frontend/services/scroller>`
- handle clicks on anchors elements
* - :ref:`title <frontend/services/title>`
- read or modify the window title
* - :ref:`user <frontend/services/user>`
- provides some information related to the current user
.. _frontend/services/cookie:
Cookie service
--------------
Overview
~~~~~~~~
- Technical name: `cookie`
- Dependencies: none
Provides a way to manipulate cookies. For example:
.. code-block:: javascript
cookieService.setCookie("hello", "odoo");
API
~~~
.. js:data:: current
Object representing each cookie and its value if any (or empty string)
.. js:function:: setCookie(name[, value, ttl])
:param string name: the name of the cookie that should be set
:param any value: optional. If given, the cookie will be set to that value
:param number ttl: optional. the time in seconds before the cookie will be deleted (default=1 year)
Sets the cookie `name` to the value `value` with a max age of `ttl`
.. js:function:: deleteCookie(name)
:param string name: name of the cookie
Deletes the cookie `name`.
.. _frontend/services/effect:
Effect service
--------------
Overview
~~~~~~~~
* Technical name: `effect`
* Dependencies: None
Effects are graphical elements that can be temporarily displayed on top of the page, usually to provide feedback to the user that something interesting happened.
A good example would be the rainbow man:
.. image:: images/rainbow_man.png
:alt: The rainbow man effect
:width: 600
:align: center
Here's how this can be displayed:
.. code-block:: javascript
const effectService = useService("effect");
effectService.add({
type: "rainbow_man", // can be omitted, default type is already "rainbow_man"
message: "Boom! Team record for the past 30 days.",
});
.. warning ::
The hook `useEffect` is not related to the effect service.
API
~~~
.. js:function:: effectService.add(options)
:param object options: the options for the effect. They will get passed along to the underlying effect component.
Display an effect.
The options are defined by:
.. code-block:: ts
interface EffectOptions {
// The name of the desired effect
type?: string;
[paramName: string]: any;
}
Available effects
~~~~~~~~~~~~~~~~~
Currently, the only effect is the rainbow man.
RainbowMan
**********
.. code-block:: javascript
effectService.add({ type: "rainbow_man" });
.. list-table::
:widths: 20 40 40
:header-rows: 1
* - Name
- Type
- Description
* - `params.Component`
- `owl.Component?`
- Component class to instantiate inside the RainbowMan (will replace the message).
* - `params.props`
- `object?={}`
- If params.Component is given, its props can be passed with this argument.
* - `params.message`
- `string?="Well Done!"`
- Message is the notice the rainbowman holds.
If effects are disabled for the user, the rainbowman won't appear and a simple notification
will get displayed as a fallback.
If effects are enabled and params.Component is given, params.message is not used.
The message is a simple string or a string representing html
(prefer using params.Component if you want interactions in the DOM).
* - `params.messageIsHtml`
- `boolean?=false`
- Set to true if the message represents html, s.t. it will be correctly inserted into the DOM.
* - `params.img_url`
- `string?=/web/static/img/smile.svg`
- The url of the image to display inside the rainbow.
* - `params.fadeout`
- `("slow"|"medium"|"fast"|"no")?="medium"`
- Delay for rainbowman to disappear.
`"fast"` will make rainbowman dissapear quickly.
`"medium"` and `"slow"` will wait little longer before disappearing (can be used when `params.message` is longer).
`"no"` will keep rainbowman on screen until user clicks anywhere outside rainbowman.
How to add an effect
~~~~~~~~~~~~~~~~~~~~
.. _frontend/services/effect_registry:
The effects are stored in a registry called `effects`.
You can add new effects by providing a name and a function.
.. code-block:: javascript
const effectRegistry = registry.category("effects");
effectRegistry.add("rainbow_man", rainbowManEffectFunction);
The function must follow this API:
.. js:function:: <newEffectFunction>(env, params)
:param Env env: the environment received by the service
:param object params: the params received from the add function on the service.
:returns: `({Component, props} | void)` A component and its props or nothing.
This function must create a component and return it. This component is mounted inside the
effect component container.
Example
~~~~~~~
Let's say we want to add an effect that add a sepia look at the page.
.. code-block:: javascript
/** @odoo-module **/
import { registry } from "@web/core/registry";
const { Component, tags } = owl;
class SepiaEffect extends Component {}
SepiaEffect.template = tags.xml`
<div style="
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
pointer-events: none;
background: rgba(124,87,0, 0.4);
"></div>
`;
export function sepiaEffectProvider(env, params = {}) {
return {
Component: SepiaEffect,
};
}
const effectRegistry = registry.category("effects");
effectRegistry.add("sepia", sepiaEffectProvider);
And then, call it somewhere you want and you will see the result.
Here, it is called in webclient.js to make it visible everywhere for the example.
.. code-block:: javascript
const effectService = useService("effect");
effectService.add({ type: "sepia" });
.. image:: images/odoo_sepia.png
:alt: Odoo in sepia
:width: 600
:align: center
.. _frontend/services/http:
Http Service
------------
Overview
~~~~~~~~
* Technical name: `http`
* Dependencies: None
While most interactions between the client and the server in odoo are `RPCs` (`XMLHTTPRequest`), lower level
control on requests may sometimes be required.
This service provides a way to send `get` and `post` `http requests <https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods>`_.
API
~~~
.. js:function:: async get(route[,readMethod = "json"])
:param string route: the url to send the request to
:param string readMethod: the response content type. Can be "text", "json", "formData", "blob", "arrayBuffer".
:returns: the result of the request with the format defined by the readMethod argument.
Sends a get request.
.. js:function:: async post(route [,params = {}, readMethod = "json"])
:param string route: the url to send the request to
:param object params: key value data to be set in the form data part of the request
:param string readMethod: the response content type. Can be "text", "json", "formData", "blob", "arrayBuffer".
:returns: the result of the request with the format defined by the readMethod argument.
Sends a post request.
Example
~~~~~~~
.. code-block:: javascript
const httpService = useService("http");
const data = await httpService.get("https://something.com/posts/1");
// ...
await httpService.post("https://something.com/posts/1", { title: "new title", content: "new content" });
.. _frontend/services/notification:
Notification service
--------------------
Overview
~~~~~~~~
* Technical name: `notification`
* Dependencies: None
The `notification` service allows to display notifications on the screen.
.. code-block:: javascript
const notificationService = useService("notification");
notificationService.add("I'm a very simple notification");
API
~~~
.. js:function:: add(message[, options])
:param string message: the notification message to display
:param object options: the options of the notification
:returns: a function to close the notification
Shows a notification.
The options are defined by:
.. list-table::
:widths: 15 30 55
:header-rows: 1
* - Name
- Type
- Description
* - `title`
- string
- Add a title to the notification
* - `type`
- `warning` | `danger` | `success` | `info`
- Changes the background color according to the type
* - `sticky`
- boolean
- Whether or not the notification should stay until dismissed
* - `className`
- string
- additional css class that will be added to the notification
* - `onClose`
- function
- callback to be executed when the notification closes
* - `buttons`
- button[] (see below)
- list of button to display in the notification
The buttons are defined by:
.. list-table::
:widths: 15 30 55
:header-rows: 1
* - Name
- Type
- Description
* - `name`
- string
- The button text
* - `onClick`
- function
- callback to execute when the button is clicked
* - `primary`
- boolean
- whether the button should be styled as a primary button
Examples
~~~~~~~~
A notification for when a sale deal is made with a button to go some kind of commission page.
.. code-block:: javascript
// in setup
this.notificationService = useService("notification");
this.actionService = useService("actionService");
// later
this.notificationService.add("You closed a deal!", {
title: "Congrats",
type: "success",
buttons: [
{
name: "See your Commission",
onClick: () => {
this.actionService.doAction("commission_action");
},
},
],
});
.. image:: images/notification_service.png
:width: 600 px
:alt: Example of notification
:align: center
A notification that closes after a second:
.. code-block:: javascript
const notificationService = useService("notification");
const close = notificationService.add("I will be quickly closed");
setTimeout(close, 1000);
.. _frontend/services/router:
Router Service
--------------
Overview
~~~~~~~~
- Technical name: `router`
- Dependencies: none
The `router` service provides three features:
* information about the current route
* a way for the application to update the url, depending on its state
* listens to every hash change, and notifies the rest of the application
API
~~~
.. js:data:: current
:noindex:
The current route can be accessed with the ``current`` key. It is an object
with the following information:
* `pathname (string)`: the path for the current location (most likely `/web` )
* `search (object)`: a dictionary mapping each search keyword (the querystring)
from the url to its value. An empty string is the value if no value was
explicitely given
* `hash (object)`: same as above, but for values described in the hash.
For example:
.. code-block:: javascript
// url = /web?debug=assets#action=123&owl&menu_id=174
const { pathname, search, hash } = env.services.router.current;
console.log(pathname); // /web
console.log(search); // { debug="assets" }
console.log(hash); // { action:123, owl: "", menu_id: 174 }
Updating the URL is done with the `pushState` method:
.. js:function:: pushState(hash: object[, replace?: boolean])
:param Object hash: object containing a mapping from some keys to some values
:param boolean replace: if true, the url will be replaced, otherwise only
key/value pairs from the `hash` will be updated.
Updates the URL with each key/value pair from the `hash` object. If a value is
set to an empty string, the key is added to the url without any corresponding
value.
If true, the `replace` argument tells the router that the url hash should be
completely replaced (so values not present in the `hash` object will be removed).
This method call does not reload the page. It also does not trigger a
`hashchange` event, nor a `ROUTE_CHANGE` in the :ref:`main bus <frontend/framework/bus>`.
This is because this method is intended to only updates the url. The code calling
this method has the responsibility to make sure that the screen is updated as
well.
For example:
.. code-block:: javascript
// url = /web#action_id=123
routerService.pushState({ menu_id: 321 });
// url is now /web#action_id=123&menu_id=321
routerService.pushState({ yipyip: "" }, replace: true);
// url is now /web#yipyip
Finally, the `redirect` method will redirect the browser to a specified url:
.. js:function:: redirect(url[, wait])
:param string url: a valid url
:param boolean wait: if true, wait for the server to be ready, and redirect after
Redirect the browser to `url`. This method reloads the page. The `wait`
argument is rarely used: it is useful in some cases where we know that the
server will be unavailable for a short duration, typically just after an addon
update or install operation.
.. note::
The router service emits a `ROUTE_CHANGE` event on the :ref:`main bus <frontend/framework/bus>`
whenever the current route has changed.
.. _frontend/services/rpc:
RPC service
-----------
Overview
~~~~~~~~
- Technical name: `rpc`
- Dependencies: none
The `rpc` service provides a single asynchronous function to send requests to
the server. Calling a controller is very simple: the route should be the first
argument and optionally, a ``params`` object can be given as a second argument.
.. code-block:: javascript
// in setup
this.rpc = useService("rpc");
// somewhere else, in an async function:
const result = await this.rpc("/my/route", { some: "value" });
.. note::
Note that the ``rpc`` service is considered a low-level service. It should
only be used to interact with Odoo controllers. To work with models (which
is by far the most important usecase), one should use the ``orm`` service
instead.
API
~~~
.. js:function:: rpc(route, params, settings)
:param string route: route targeted by the request
:param Object params: parameters sent to the server
:param Object settings (optional): request settings (see below)
The ``settings`` object can contain:
- ``xhr``, which should be a ``XMLHTTPRequest`` object. In that case,
the ``rpc`` method will simply use it instead of creating a new one. This
is useful when one accesses advanced features of the `XMLHTTPRequest` API.
- ``silent (boolean)`` If set to ``true``, the web client will not provide
a feedback that there is a pending rpc.
The ``rpc`` service communicates with the server by using a ``XMLHTTPRequest``
object, configured to work with the ``application/json`` content type. So clearly
the content of the request should be JSON serializable. Each request done by
this service uses the ``POST`` http method.
Server errors actually return the response with an http code 200. But the ``rpc``
service will treat them as error.
Error Handling
~~~~~~~~~~~~~~
An rpc can fail for two main reasons:
* either the odoo server returns an error (so, we call this a ``server`` error).
In that case the http request will return with an http code 200 BUT with a
response object containing an ``error`` key.
* or there is some other kind of network error
When a rpc fails, then:
* the promise representing the rpc is rejected, so the calling code will crash,
unless it handles the situation
*
an event ``RPC_ERROR`` is triggered on the main application bus. The event payload
contains a description of the cause of the error:
If it is a server error (the server code threw an exception). In that case
the event payload will be an object with the following keys:
* ``type = 'server'``
* ``message(string)``
*
``code(number)``
*
``name(string)`` (optional, used by the error service to look for an appropriate
dialog to use when handling the error)
* ``subType(string)`` (optional, often used to determine the dialog title)
* ``data(object)`` (optional object that can contain various keys among which
``debug`` : the main debug information, with the call stack)
If it is a network error, then the error description is simply an object
``{type: 'network'}``.
When a network error occurs, a :ref:`notification <frontend/services/notification>` is
displayed and the server is regularly contacted until it responds. The
notification is closed as soon as the server responds.
.. _frontend/services/scroller:
Scroller service
----------------
Overview
~~~~~~~~
- Technical name: `scroller`
- Dependencies: none
Whenever the user clicks on an anchor in the web client, this service automatically scrolls
to the target (if appropriate).
The service adds an event listener to get `click`'s on the document. The service checks
if the selector contained in its href attribute is valid to distinguish anchors and Odoo
actions (e.g. `<a href="#target_element"></a>`). It does nothing if it is not the case.
An event `SCROLLER:ANCHOR_LINK_CLICKED` is triggered on the main application bus if the click seems to be
targeted at an element. The event contains a custom event containing the `element` matching and its `id` as a reference.
It may allow other parts to handle a behavior relative to anchors themselves. The original event is also
given as it might need to be prevented. If the event is not prevented, then the user interface will
scroll to the target element.
API
~~~
The following values are contained in the `anchor-link-clicked` custom event explained above.
.. list-table::
:widths: 25 25 50
:header-rows: 1
* - Name
- Type
- Description
* - `element`
- `HTMLElement | null`
- The anchor element targeted by the href
* - `id`
- `string`
- The id contained in the href
* - `originalEv`
- `Event`
- The original click event
.. note::
The scroller service emits a `SCROLLER:ANCHOR_LINK_CLICKED` event on the :ref:`main bus <frontend/framework/bus>`.
To avoid the default scroll behavior of the scroller service, you must use `preventDefault()` on the event given
to the listener so that you can implement your own behavior correctly from the listener.
.. _frontend/services/title:
Title Service
-------------
Overview
~~~~~~~~
- Technical name: `title`
- Dependencies: none
The `title` service offers a simple API that allows to read/modify the document
title. For example, if the current document title is "Odoo", we can change it
to "Odoo 15 - Apple" by using the following command:
.. code-block:: javascript
// in some component setup method
const titleService = useService("title");
titleService.setParts({ odoo: "Odoo 15", fruit: "Apple" });
API
~~~
The ``title`` service manipulates the following interface:
.. code-block:: ts
interface Parts {
[key: string]: string | null;
}
Each key represents the identity of a part of the title, and each value is the
string that is displayed, or `null` if it has been removed.
Its API is:
.. js:data:: current
:noindex:
This is a string representing the current title. It is structured in the
following way: ``value_1 - ... - value_n`` where each `value_i` is a (non null)
value found in the `Parts` object (returned by the `getParts` function)
.. js:function:: getParts
:returns: Parts the current `Parts` object maintained by the title service
.. js:function:: setParts(parts)
:param Parts parts: object representing the required change
The ``setParts`` method allows to add/replace/delete several parts of the title.
Delete a part (a value) is done by setting the associated key value to `null`.
Note that one can only modify a single part without affecting the other
parts. For example, if the title is composed of the following parts:
.. code-block:: javascript
{ odoo: "Odoo", action: "Import" }
with ``current`` value being ``Odoo - Import`` , then
.. code-block:: javascript
setParts({
action: null,
});
will change the title to ``Odoo``.
.. _frontend/services/user:
User service
------------
Overview
~~~~~~~~
* Technical name: `user`
* Dependencies: `rpc`
The `user` service provides a bunch of data and a few helper functions concerning
the connected user.
API
~~~
.. list-table::
:widths: 25 25 50
:header-rows: 1
* - Name
- Type
- Description
* - ``context``
- ``Object``
- The :ref:`user context<frontend/framework/user_context>`
* - ``db``
- ``Object``
- Info about the database
* - ``home_action_id``
- ``(number | false)``
- Id of the action used as home for the user
* - ``isAdmin``
- ``boolean``
- Whether the user is an admin (group `base.group_erp_manager` or superuser)
* - ``isSystem``
- ``boolean``
- Whether the user is part of the system group (`base.group_system`)
* - ``lang``
- ``string``
- language used
* - ``name``
- ``string``
- Name of the user
* - ``partnerId``
- ``number``
- Id of the partner instance of the user
* - ``tz``
- ``string``
- The timezone of the user
* - ``userId``
- ``number``
- Id of the user
* - ``userName``
- ``string``
- Alternative nick name of the user
.. js:function:: updateContext(update)
:param object update: the object to update the context with
update the :ref:`user context<frontend/framework/user_context>` with the given object.
.. code-block:: javascript
userService.updateContext({ isFriend: true })
.. js:function:: removeFromContext(key)
:param string key: the key of the targeted attribute
remove the value with the given key from the :ref:`user context<frontend/framework/user_context>`
.. code-block:: js
userService.removeFromContext("isFriend")
.. js:function:: hasGroup(group)
:param string group: the xml_id of the group to look for
:returns: `Promise<boolean>` is the user in the group
check if the user is part of a group
.. code-block:: js
const isInSalesGroup = await userService.hasGroup("sale.group_sales")

View File

@@ -0,0 +1,19 @@
:nosearch:
==================
Javascript Modules
==================
.. toctree::
:titlesonly:
javascript/hooks
javascript/registries
javascript/services
javascript/command_palette
javascript/javascript_cheatsheet
javascript/javascript_reference
javascript/mobile
javascript/qweb

View File

@@ -0,0 +1,301 @@
=========================
Command & Command Palette
=========================
Overview
========
A command is a link between an action (piece of code) and a possible hotkey.
It can be registered and then called when its hotkey is pressed.
Commands also appear in the Command Palette, a convenient tool to quickly access and trigger things in Odoo.
Commands without hotkey can only be triggered with the Command Palette.
Command
=======
A command is defined as:
.. code-block:: ts
type Command = {
name: string,
action: () => (void | CommandConfiguration),
category?: string,
hotkey?: string
}
.. code-block:: js
// example of a command
const sayHelloCommand = {
"Say Hello Command",
() => console.log("hello"),
"useless category",
"ctrl-h"
};
The most important part of the command is the action, which is a function to execute when the command
is triggered. If the action doesn't return anything, the command palette will close after the execution.
If it returns a ``CommandPaletteConfig``, it updates the palette content based on this config. More on that
later.
Command Palette
===============
Commands also appear in the Command Palette.
Commands without hotkey can only be triggered with the Command Palette.
.. image:: images/command_palette.png
:align: center
:alt: the command palette
Fundamentally, the Command Palette exposes a list of command items. A simple component that
holds a command.
It can be opened by pressing the hotkey ``Control+K``.
By default, it displays:
* the commands you registered in the dedicated service
* any visible elements in the ``ui.activeElement`` that are accessible through a ``[data-hotkey]`` attribute
.. _command Service:
Command Service
===============
.. list-table::
:header-rows: 1
* - Technical name
- Dependencies
* - ``command``
- ``dialog`` , ``hotkey`` , ``ui``
The ``command`` service offers a way to register new commands.
API
---
.. code-block:: ts
add(name: string, action: () => void, options: Object?): () => void;
Register a command to the command palette. It returns a cleanup function that can be called to
unregister the command.
+-----------------------+------------------------------+---------------------------------------------------------+
| key | type | description |
+=======================+==============================+=========================================================+
| name | ``string`` | Command name |
+-----------------------+------------------------------+---------------------------------------------------------+
| action | ``Function`` | Action to execute on command triggering |
+-----------------------+------------------------------+---------------------------------------------------------+
| options.category | ``string?`` | Define the category of the command |
+-----------------------+------------------------------+---------------------------------------------------------+
| options.hokey | ``string?`` | Hotkey to trigger the command |
+-----------------------+------------------------------+---------------------------------------------------------+
| options.activeElement | ``HtmlElement?`` | HTMLElement from which commands and hotkey are fetched |
+-----------------------+------------------------------+---------------------------------------------------------+
| options.global | ``boolean?`` | Ignore the active element and make it always accessible |
+-----------------------+------------------------------+---------------------------------------------------------+
.. note::
A command is scoped to a DOM element. This is why there is this idea of activeElement.
.. code-block:: ts
getCommands(activeElement): Command[]
Get all the commands scoped to the activeElement.
+----------------+---------------+---------------------------------------------------------+
| key | type | description |
+================+===============+=========================================================+
| activeElement | HtmlElement | HTMLElement from which commands are scoped and fetched |
+----------------+---------------+---------------------------------------------------------+
.. code-block:: js
const commandService = useService("command");
const commandCleanUp = commandService.add({ "My Command 1", () => { /* do stuff */ } });
commandCleanUp(); // If you want to unregister it
.. code-block:: ts
openPalette(config: Object): void
Opens a command palette with the given configuration. See Later.
+-----------------------------------+------------------------------+-----------------------------------------------------------------+
| key | type | description |
+===================================+==============================+=================================================================+
| config.providers | ``() => (void | config)`` | Command item providers |
+-----------------------------------+------------------------------+-----------------------------------------------------------------+
| config.placeholder | ``string?`` | Search placeholder |
+-----------------------------------+------------------------------+-----------------------------------------------------------------+
| config.namespace | ``string?`` | Add a starting namespace in the search |
+-----------------------------------+------------------------------+-----------------------------------------------------------------+
| config.categoriesByNamespace | ``{[namespace]: string[]}?`` | Map between the namespaces and the categories |
+-----------------------------------+------------------------------+-----------------------------------------------------------------+
| config.emptyMessageByNamespace | ``{[namespace]: string}?`` | Map between the namespaces and their message for empty content |
+-----------------------------------+------------------------------+-----------------------------------------------------------------+
| config.footerTemplate | ``string?`` | Owl template identifier for the footer |
+-----------------------------------+------------------------------+-----------------------------------------------------------------+
.. _useCommand hook:
Hook: useCommand
================
A hook that ensures your registration exists only when your component is mounted by registering it
and unregistering it at the right lifecycle time.
.. note::
As long as you only need to add commands to the default command palette and you're inside an Owl Component,
you should use this.
If you wish to add more complex behavior like namespace symbols or actions opening other palettes, you should go
to the advanced section.
API
---
.. code-block:: js
useCommand(command: Command): void
Register a command.
Usage
-----
.. code-block:: js
class MyComponent extends Component {
setup() {
useCommand({
name: "My Command 1",
action: () => {
// code when command 1 is executed
}
});
useCommand({
name: "My Super Command",
hotkey: "shift-home",
action: () => {
// code when super command is executed
// note that the super command can also get executed with the hotkey "shift-home"
}
});
}
}
.. _commandCategory Registry:
Registry: Command Category
==========================
The ``command_categories`` gathers the command categories.
The keys in this registry can be used in two differents ways in order to organize the command palette:
*
when registering a new command: ``useCommand({ category: "key", ... })``.
*
applied as an attribute in the document: ``[data-command-category="key"]``.
N.B.: if an element should appear in the command palette
(e.g. it has a ``[data-hotkey]`` attribute), the closest parent (including itself)
having a ``[data-command-category]`` will provide the category key to seek for in the registry.
API
---
!! does it still exist ? !!
!! there seem to be a namespace !!
Usage
------
.. code-block:: js
import { registry } from "@web/core/registry";
registry.category("command_categories").add("new_category")
registry.category("command_categories").add("another_new_category", { label: "Name of the category" })
Available Categories
--------------------
.. list-table::
:header-rows: 1
* - Key
- Sequence
- Description
* - ``main``
- 10
- Main Commands
* - ``app``
- 20
- Current App Commands
* - ``actions``
- 30
- More Actions
* - ``navbar``
- 40
- NavBar
* - ``default``
- 100
- Other commands
.. note::
The sequence is a registry option, to define an order.
.. _commandProvider Registry:
Registry: Command Provider
==========================
API
---
Usage
-----
.. _commandEmptyList Registry:
Registry: Command Empty List
============================
API
---
Usage
-----
Advanced Topics
===============
The Command Palette therefore requires a configuration to be opened. However, there exists a default configuration.
This `default` palette can be opened by . The ``CommandPaletteConfig`` part is left for
the advanced section.
A ``CommandPaletteConfig`` is defined as
.. code-block:: js
type CommandPaletteConfig = {
categoriesByNamespace?: {[namespace]: string[]};
namespace?: string;
emptyMessageByNamespace?: {[namespace]: string};
footerTemplate?: string;
placeholder?: string;
providers: Provider[];
}

View File

@@ -0,0 +1,6 @@
Hooks
=====
What is a hook ?
:ref:`useCommand<useCommand hook>`

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Some files were not shown because too many files have changed in this diff Show More