From def1f897860000f66c399a332a4399acce8fad29 Mon Sep 17 00:00:00 2001 From: "Tiffany Chang (tic)" Date: Tue, 4 Apr 2023 14:50:43 +0000 Subject: [PATCH] [REF] developer: rewrite sql view as a howto SQL view was formerly part of the dashboard tutorial which is now obsolete. Since creating a SQL view as a tutorial does not make sense anymore, it has been converted into a simple howto. closes odoo/documentation#4033 Task: 3226581 X-original-commit: 7124542ed9fcaa474d552cca5a9aff2f5a3c7c08 Signed-off-by: Tiffany Chang --- content/developer/howtos.rst | 6 + content/developer/howtos/create_reports.rst | 119 ++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 content/developer/howtos/create_reports.rst diff --git a/content/developer/howtos.rst b/content/developer/howtos.rst index 9db5f6d46..adcf15d6d 100644 --- a/content/developer/howtos.rst +++ b/content/developer/howtos.rst @@ -14,6 +14,7 @@ How-to guides howtos/javascript_client_action howtos/web_services howtos/company + howtos/create_reports howtos/accounting_localization howtos/translations howtos/provide_iap_services @@ -52,6 +53,11 @@ How-to guides Learn how to manage multiple companies and deal with the records-related specificities of a multi-company environment. + .. card:: Create customized reports + :target: howtos/create_reports + + Learn how to create customized reports with SQL Views. + .. card:: Accounting localization :target: howtos/accounting_localization diff --git a/content/developer/howtos/create_reports.rst b/content/developer/howtos/create_reports.rst new file mode 100644 index 000000000..d2a8a1254 --- /dev/null +++ b/content/developer/howtos/create_reports.rst @@ -0,0 +1,119 @@ +========================= +Create customized reports +========================= + +SQL views are a technique for creating customized reports to show data that cannot be +shown with existing models' fields and views. In other words, this technique helps avoid +unnecessary creation and calculation of additional fields solely for data analysis +purposes. + +Create a model +============== + +A SQL view is created in a similar manner as a standard model: + +.. code-block:: python + + from odoo import fields, models + + + class ModuleReport(models.Model): + _name = 'module.report' + _description = "Module Report" + _rec_name = 'module_field' + _auto = False + +Where the attributes: + +- `_auto = False` indicates that we do not want to store the model in the database +- `_rec_name` indicates which of the model's fields represents a record's name (i.e. the + name that will be used in the navigation breadcrumb when opening a record's form view) + +and its fields are defined in the same way as a standard model, except every field is +marked as `readonly=True`. + +.. note:: + Don't forget to add your new model to your security file. + +Populate the model +================== + +There are 2 ways to populate a SQL view's table: + +- override the `BaseModel.init()` method, +- set the `_table_query` property. + +Regardless of which way is used, a SQL query will be executed to populate the model. +Therefore, any SQL commands can be used to collect and/or calculate the data needed +and you are expected to keep in mind that you are bypassing the ORM (i.e. it is a +good idea to read through :ref:`reference/security` if you haven't already). The columns +returned from the `SELECT` will populate the model's fields, so ensure that your column +names match your field names, or use alias names that match. + +.. tabs:: + + .. tab:: Overriding `BaseModel.init()` + + In most cases, overriding the `BaseModel.init()` method is the standard and better option to + use. It requires the import of `tools` and is typically written as follows: + + .. code-block:: python + + def init(self): + tools.drop_view_if_exists(self.env.cr, self._table) + self.env.cr.execute("""CREATE or REPLACE VIEW %s as ( + SELECT + %s + FROM + %s + )""" % (self._table, self._select(), self._from())) + + `tools.drop_view_if_exists` ensures that a conflicting view is not created when the + SQL query is executed. It is standard to separate the different parts of the query to + allow for easier model extension. Exactly how the query is split up across methods is not + standardized, but at minimum, the `_select` and `_from` methods are common, and of course, + all these methods will return strings. + + .. seealso:: + `Example: a SQL view using an override of BaseModel.init() + <{GITHUB_PATH}/addons/project/report/project_report.py>`_ + + .. tab:: Using `_table_query` + + The ``_table_query`` property is used when the view depends on the context. It is typically + written as follows: + + .. code-block:: python + + @property + def _table_query(self): + return 'SELECT %s FROM %s' % (self._select(), self._from()) + + and follows the same `_select` and `_from` methods standards as `BaseModel.init()`. + + An example of when the property should be used instead of overriding `BaseModel.init()` + is in a multi-company and multi-currency environment where currency related amounts need + to be converted using currency exchange rates when the user switches between companies. + + .. seealso:: + `Example: a SQL view using _table_query + <{GITHUB_PATH}/addons/account/report/account_invoice_report.py>`_ + +Use the model +============= + +Views and menu items for your SQL views are created and used in the same way as any +other Odoo model. You are all set to start using your SQL view. Have fun! + +Extra tips +========== + +.. tip:: + A common mistake in SQL views is not considering the duplication of certain data + due to table JOINs. This can lead to miscounting when using a field's `group_operator` + and/or the pivot view. It is best to test your SQL view with sufficient data to ensure the + resulting field values are as you expect. + +.. tip:: + If you have a field that you do not want as a measure (i.e., in your pivot or graph views), add + `store=False` to it, and it will not show.