diff --git a/content/developer/reference/backend/views.rst b/content/developer/reference/backend/views.rst index f60fd97f1..8c7957d85 100644 --- a/content/developer/reference/backend/views.rst +++ b/content/developer/reference/backend/views.rst @@ -124,7 +124,7 @@ an impact on all view types. ``info``, ``warning``, ``danger`` and ``secondary`` displays. The list view supports ``bf``, ``it``, ``success``, ``info``, ``warning``, ``danger``, ``muted`` and ``primary`` displays. -* ``sample`` (``kanban`` & ``list`` & ``gantt`` & ``graph`` & ``pivot`` & ``cohort`` & ``dashboard``) +* ``sample`` (``kanban`` & ``list`` & ``gantt`` & ``graph`` & ``pivot`` & ``cohort``) Populate the view with a set of sample records if none are found for the current model. This attribute is false by default. @@ -577,213 +577,6 @@ attributes: measures (useful for fields that do not make sense aggregated, such as fields in different units, e.g. € and $). -.. _reference/views/dashboard: - -Dashboard ---------- - -.. raw:: html - - Enterprise feature - -Like pivot and graph view, The dashboard view is used to display aggregate data. -However, the dashboard can embed sub views, which makes it possible to have a -more complete and interesting look on a given dataset. - -The dashboard view can display sub views, aggregates for some fields (over a -domain), or even *formulas* (expressions which involves one or more aggregates). -For example, here is a very simple dashboard: - -.. code-block:: xml - - - - - - - - - - - -The root element of the Dashboard view is , it does not accept any -attributes. - -There are 5 possible type of tags in a dashboard view: - -.. rst-class:: o-definition-list - -``view`` - declares a sub view. - - Admissible attributes are: - - .. rst-class:: o-definition-list - - ``type`` (mandatory) - The type of the sub view. For example, *graph* or *pivot*. - - ``ref`` (optional) - An xml id for a view. If not given, the default view for the model will - be used. - - ``name`` (optional) - A string which identifies this element. It is mostly - useful to be used as a target for an xpath. - -``group`` - defines a column layout. This is actually very similar to the group element - in a form view. - - Admissible attributes are: - - .. rst-class:: o-definition-list - - ``string`` (optional) - A description which will be displayed as a group title. - - ``colspan`` (optional) - The number of subcolumns in this group tag. By default, 6. - - ``col`` (optional) - The number of columns spanned by this group tag (only makes sense inside - another group). By default, 6. - - -``aggregate`` - declares an aggregate. This is the value of an aggregate for a given field - over the current domain. - - Note that aggregates are supposed to be used inside a group tag (otherwise - the style will not be properly applied). - - Admissible attributes are: - - .. rst-class:: o-definition-list - - ``field`` (mandatory) - The field name to use for computing the aggregate. Possible field types - are: - - - ``integer`` (default group operator is sum) - - ``float`` (default group operator is sum) - - ``many2one`` (default group operator is count distinct) - - ``name`` (mandatory) - A string to identify this aggregate (useful for formulas) - - ``string`` (optional) - A short description that will be displayed above the value. If not - given, it will fall back to the field string. - - ``domain`` (optional) - An additional restriction on the set of records that we want to aggregate. - This domain will be combined with the current domain. - - ``domain_label`` (optional) - When the user clicks on an aggregate with a domain, it will be added to - the search view as a facet. The string displayed for this facet can - be customized with this attribute. - - ``group_operator`` (optional) - A valid postgreSQL aggregate function identifier to use when aggregating - values (see https://www.postgresql.org/docs/12/static/functions-aggregate.html). - If not provided, By default, the group_operator from the field definition is used. - Note that no aggregation of field values is achieved if the group_operator value is "". - - .. note:: The special aggregate function ``count_distinct`` (defined in odoo) can also be used here - - .. code-block:: xml - - - - - - ``col`` (optional) - The number of columns spanned by this tag (only makes sense inside a - group). By default, 1. - - ``widget`` (optional) - A widget to format the value (like the widget attribute for fields). - For example, monetary. - - ``help`` (optional) - A help message to dipslay in a tooltip (equivalent of help for a field in python) - - ``measure`` (optional) - This attribute is the name of a field describing the measure that has to be used - in the graph and pivot views when clicking on the aggregate. - The special value __count__ can be used to use the count measure. - - .. code-block:: xml - - - - ``clickable`` (optional) - A boolean indicating if this aggregate should be clickable or not (default to true). - Clicking on a clickable aggregate will change the measures used by the subviews - and add the value of the domain attribute (if any) to the search view. - - ``value_label`` (optional) - A string put on the right of the aggregate value. - For example, it can be useful to indicate the unit of measure - of the aggregate value. - -``formula`` - declares a derived value. Formulas are values computed from aggregates. - - Note that like aggregates, formulas are supposed to be used inside a group - tag (otherwise the style will not be properly applied). - - Admissible attributes are: - - .. rst-class:: o-definition-list - - ``value`` (mandatory) - A string expression that will be evaluated, with the builtin python - evaluator (in the web client). Every aggregate can be used in the - context, in the ``record`` variable. For example, - ``record.price_total / record.order_id``. - - ``name`` (optional) - A string to identify this formula - - ``string`` (optional) - A short description that will be displayed above the formula. - - ``col`` (optional) - The number of columns spanned by this tag (only makes sense inside a - group). By default, 1. - - ``widget`` (optional) - A widget to format the value (like the widget attribute for fields). - For example, monetary. By default, it is 'float'. - - ``help`` (optional) - A help message to dipslay in a tooltip (equivalent of help for a field in python) - - ``value_label`` (optional) - A string put on the right of the formula value. - For example, it can be useful to indicate the unit of measure - of the formula value. - -``widget`` - Declares a specialized widget to be used to display the information. This is - a mechanism similar to the widgets in the form view. - - Admissible attributes are: - - .. rst-class:: o-definition-list - - ``name`` (mandatory) - A string to identify which widget should be instantiated. The view will - look into the ``widget_registry`` to get the proper class. - - ``col`` (optional) - The number of columns spanned by this tag (only makes sense inside a - group). By default, 1. - .. _reference/views/form: Form diff --git a/content/developer/tutorials.rst b/content/developer/tutorials.rst index 1a2d3088d..3aea79e78 100644 --- a/content/developer/tutorials.rst +++ b/content/developer/tutorials.rst @@ -15,7 +15,6 @@ Tutorials tutorials/unit_tests tutorials/mixins tutorials/pdf_reports - tutorials/dashboards .. cards:: @@ -66,9 +65,3 @@ Tutorials :target: tutorials/pdf_reports Use QWeb, Odoo's powerful templating engine, to create custom PDF reports for your documents. - - .. card:: Visualize data in dashboards - :target: tutorials/dashboards - - Create data visualization dashboards using the enterprise edition "Dashboard" view and - so-called "SQL views". diff --git a/content/developer/tutorials/dashboards.rst b/content/developer/tutorials/dashboards.rst deleted file mode 100644 index 861585f5c..000000000 --- a/content/developer/tutorials/dashboards.rst +++ /dev/null @@ -1,370 +0,0 @@ -============================ -Visualize data in dashboards -============================ - -.. important:: - This tutorial is an extension of the :doc:`getting_started` tutorial. Make sure you have - completed it and use the `estate` module you have built as a base for the exercises in this - tutorial. Fetch the branch `{BRANCH}-core` from the `technical-training-solutions - `_ repository if you - want to start from a clean base. - -The term "Dashboard" is used in Odoo for objects that display data, but involves different -implementations. This tutorial will only focus on the Enterprise view that is used to provide -aggregated data visualization. They can be added as a ``view_mode`` to an existing model (i.e. a -view you can switch to via the view buttons in the top right of a view), but they are also often -used as a view for to a special model customized for data visualization. You may hear these -special views referred to as SQL views. - -It is useful to note there is a "Dashboard" app in Odoo Community. This app allows users to create -their own customized view of data, but the customization is only visible to each user and can -only be viewed within the "Dashboard" app. Technically it is possible to make global dashboards -using this ``board`` module, but it is much easier to do as an Enterprise view. Plus it looks nicer -and has extra features not available in ``board``. Some other dashboards within Odoo also exist, -but they are custom made and are beyond the scope of this tutorial. - -The documentation related to this topic can be found in :ref:`reference/views/dashboard`. - -File Structure -============== - -You probably have already guessed that since dashboard views are an Enterprise view, they must have -a dependency on an Enterprise module. The Enterprise module is ``web_dashboard``. Don't forget to -add it to your manifest file! It is standard to add dashboards intended to be used as a -``view_mode`` for one of your module's models (in the ``model`` folder) to the views directory -(i.e. the same file that contains the other views for the same model). - -It is standard to create a separate Enterprise module to add extra Enterprise views and features to -a Community module. This is done in a similar manner as the module link technique covered within -:ref:`tutorials/getting_started/14_other_module`. The difference is that instead of linking 2 -different modules, we are extending our `estate` module. We do this by creating a new module and -adding both the Community module and its necessary Enterprise module dependencies to its manifest. -You will commonly see "enterprise" in the module's directory name. To keep this tutorial simple, we -will add dashboards to our existing ``estate`` module. - -SQL Views have 2 parts: their xml file (don't forget to add it to your manifest file) and their -Python file (don't forget to add it to the appropriate ``__init.py__`` files). The former is the -same format as the ``view_mode`` xml while the latter contains a custom model and SQL code to -populate its fields. It is standard to add SQL view files to the ``report/`` directory. It -is also common to include "report" in the name of the SQL view's files. You may be -wondering why do we put the files in a report directory? We saw earlier that the dashboard is -for data visualization, therefore it is not editable. You can think of dashboards as interactive -reports where you can click on statistics, graphs, and charts to see the specific data contributing -to them. Note it is also standard to store the xml code for :doc:`PDF report templates -` in the report directory. - -It is expected that your work tree will look something like: - -.. code-block:: bash - - estate - ├── models - │ ├── *.py - │ └── __init__.py - ├── report - │ ├── __init__.py - │ ├── estate_report.py - │ └── estate_report_views.xml - ├── security - │ └── ir.model.access.csv - ├── views - │ ├── *.xml - │ └── estate_property_views.xml - ├── __init__.py - └── __manifest__.py - -Dashboard View -============== - -.. note:: - - **Goal**: at the end of this section, we will have a new dashboard view that displays - different property statistics. - - .. image:: dashboards/simple_dashboard.png - :align: center - :alt: Basic Dashboard view - -Dashboards can display data in different ways, including: - -* showing an ``aggregate`` of a field -* using aggregated fields in a ``formula`` -* using a ``widget`` -* using another ``view`` as a subview - -There are many useful statistics and visuals we can provide for our real estate example using -these options. A full example to reference while doing the exercises in this section is -`viewable here `__ -(restricted github repository link). - -Data ----- - -To fully enjoy our dashboard view, we will need good test data to populate it. Test data will -allow us to check that the resulting look and statistics are correct. It is a good idea to test -with data that will cover most or all of your expected use cases, but is also easy to verify with -that your statistics are correct. In our goal's case we are looking at count, sum, average, -minimum, and maximum statistics, therefore a good representation set for our dashboard is: - -* At least 3 properties with different property types, expected prices, and average living area. -* At least 1 sold property and at least 1 canceled property - -If you don't have a set of data like this already, you can either: - -* Complete :doc:`define_module_data` (if you haven't done so already) and add the extra cases to - your demo data (you may need to create a new database to load in the demo data). -* Manually create the data in your database. -* Copy this `data file - `_ - into a new directory called ``data`` in your estate module and copy `these lines - `_ - into your __manifest__.py file (you may need to create a new database to load in the demo data). - -Click through your database data and make sure it is what you expect. Of course you can add the -data after you write your dashboard code and then test that your view is working as expected. - -Aggregations ------------- - -Building a dashboard view is very similar to what you have previously done in -:ref:`tutorials/getting_started/07_basicviews`. For the dashboard view, we use the `dashboard` root -element and choose from its possible tags (see all the possibilities and their attributes in the -:ref:`reference/views/dashboard` documentation). So a simple dashboard example is: - -.. code-block:: xml - - - - - - - -In this example, ```` adds styling and ```` declares an aggregation. We -indicate which ``field`` we want to aggregate, what ``string`` to display with the value, and -how to aggregate it with the `group_operator` attribute. The `group_operator` can use any valid -PostgreSQL aggregate function plus the special Odoo defined ``count_distinct``. - -Hopefully you remember how to add views to a window action `view_mode` (hint, it was -covered in :ref:`tutorials/getting_started/06_firstui`). Now let's make some dashboards! - -.. exercise:: Make a dashboard view. - - - Create a dashboard of aggregated values for the ``estate.property`` model. You can - look at the **Goal** of this section for some inspiration. Remember to check that your - statistics are calculating as you expect and note that the calculated values take into - consideration any applied view filters! - - - Bonus: Add in some aggregations that need a `domain` to make sense (remember domains were - also covered in :ref:`tutorials/getting_started/07_basicviews`). - -Pie Charts ----------- - -Adding pie charts to dashboards is a piece of cake using the `` element. An example is: - -.. code-block:: xml - - - - - - - -In this example, we indicate that we're using the `pie_chart` widget with the `name` attribute and -the ``title`` for the pie chart, and that we're grouping it by property type. - -.. exercise:: Add some pie charts. - - - Add the pie charts from the **Goal** of this section to your dashboard. Hint: you will need - to add `'measure': selling_price` to your pie chart `attrs` if you want to show selling - prices grouped by property type. - - - Hover over and click on the pie charts to check your charts counts values and don't forget - that filters will also apply to the charts. - - - Bonus: Add a domain to your selling price pie chart to only include "sold" properties (i.e. - not "offer_accepted" ones). Note that the `'` will need to be escaped since it is declared - as part of the `attrs`. - -Subviews --------- - -Similar to how we can use the list view within the form view (we saw this automatically happen for -One2many relationships in :ref:`tutorials/getting_started/08_relations`), we can add other views -within our dashboard view. The most commonly added are the pivot and graph views, but the cohort -view is also an option. A dashboard with only subviews is: - -.. code-block:: xml - - - - - - -The `ref` attribute can be added to `` elements to use a specific XML id for that view. If -no XML id is provided for a graph or pivot view, then the default view will be used. -The cohort view will not work in the dashboard without a specific XML id. If you have already -created some of these views, then you are welcome to add them to your dashboard! Sample graph and -pivot views are included in the `solution code -`_ -that you are welcome to use as well. - -.. exercise:: Add subviews. - - - Add in a graph and a pivot view to your dashboard. Try playing around with the layout of - your subviews in relation to your pie charts and aggregated values and refer to the **Goal** - of this section for an often used layout. Remember to check that your subviews are - displaying your data as expected (and yes, they are also affected by the filters!). - -SQL Views -========= - -.. warning:: - - This section expects you to have a basic knowledge of SQL. If you have little to no SQL - knowledge then `this is a good tutorial to start with `__ - and these `exercises `__ are good for those who need - a refresher or extra practice. - -.. note:: - - **Goal**: at the end of this section, we will have a new SQL view that displays different - property statistics. - - .. image:: dashboards/report_dashboard.png - :align: center - :alt: SQL view - - -Occasionally we want to show data that goes beyond what our model already has in it. We could add -a lot of stored computed or related fields (non-stored fields cannot be aggregated -or displayed in pie charts), but it would be impractical to store a bunch of fields only for this -purpose. We can instead add a custom SQL view to minimize the computational load and keep our -model clean of unnecessary fields. - -Model ------ - -We will start with the more difficult part: our special report model. This file starts the same as -any other model, except that we add 2 attributes ``_auto`` and ``_rec_name``:: - - from odoo import fields, models, tools - - - class EstateReport(models.Model): - _name = 'estate.report' - _description = "Stock Report" - _rec_name = 'id' - _auto = False - -``_auto = False`` indicates that we do not want to store the model in the database, and we will -create a custom table by overriding the ``BaseModel.init()`` method. ``_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). In this case, I left it as 'id' because -our property offers don't have a name. We will need the `tools` import later (i.e. -``odoo/odoo/tools``, which is full of all sorts of useful helper methods you will probably use in -the future). Note that it is standard to include ``report`` in the model's name. - -Remember, your new model will need to be added to your security file, as you learned in -:ref:`tutorials/getting_started/05_securityintro`! - -Then we define the fields we need for our dashboard the same way as any other model (like you -learned in :ref:`tutorials/getting_started/04_basicmodel`), except that every field is -``readonly=True``. After all, our model is for read-only purposes only. - -Now we override the ``BaseModel.init()`` method mentioned earlier:: - - 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())) - -We use ``tools.drop_view_if_exists`` to ensure that we don't create a conflicting view and then -execute the SQL query. 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 you will often see at minimum ``_select`` and ``_from`` methods [or something -similar], and of course all these methods will return strings. The columns from the SELECT -will populate our model's fields, so ensure that your column names match your field names -or use alias names that match. - -.. exercise:: Create report model. - - - Create a report model with the following fields: - - ========================= ========================= ========================= - Field Type Note - ========================= ========================= ========================= - id Integer Corresponds to ``id`` of ``estate.property.offer`` - offer_state Selection Equals ``state`` choices of ``estate.property.offer`` - property_id Many2one ``estate.property`` - property_state Selection Equals ``state`` choices of ``estate.property`` - property_type_id Many2one ``estate.property.type`` - ========================= ========================= ========================= - - and write the SQL query necessary to populate the fields (hint, you will need 2 JOINs). - - You won't be able to check if your model is correct until we create a view for it, but you are - welcome to check your query directly in your database to see if the results are as you expect. - If you struggle with this exercise, then - `here is an example `__ - to reference. - -View ----- - -Now that we have our model, we can make its dashboard view. There is no difference in how it's made, -except that its file is in the ``report`` folder. Since it is a new model not linked to -any other model, we will also have to add a new menuitem to view our dashboard. Typically, SQL views -are added under a first-level menu called *Reporting* (because it's a report, surprise!). Do you -remember how to add a ``menuitem``? If not, revisit :ref:`tutorials/getting_started/06_firstui`) again. - -.. exercise:: Create report view. - - - Recreate the dashboard in the **Goal** of this section. Hint: it uses the ``formula`` element, - which we did not need for our previous dashboard. - - - Bonus: Create ``list`` and ``form`` views for your new report model so we don't have to see the ugly - defaults when you click on your pie charts. - -Extra Tips ----------- - -**Tip 1** A common mistake in SQL views is not considering the duplication of certain data -due to table JOINs. For example, in our **Goal**, we have a pie chart of the offers' property types. -We may be tempted to add a similar pie chart with a domain to only include canceled properties, -so we think we are only counting the number of canceled properties by property type. In reality, we -are still looking at all the offers per property, so any property with more than 1 offer will be -counted per offer. This example is easily double-checked by clicking on the pie chart to see its -list view: - - .. image:: dashboards/report_list_detail.png - :align: center - :alt: Pie chart list view - -But for cases such as average aggregations or using a subview such as the pivot view, it is easy to -miss this mistake. It is also easy to miss this mistake when you have insufficient test data. -To add a number of properties canceled by property type pie chart to this -report, we would either have to do a hack (too advanced for this tutorial) or simply exclude it -from this report. - -**Tip 2** If you have a field that you do not want as a measure (i.e., in your pivot or -graph views), then you can add ``store=False`` to it, and it will not show. - -**Tip 3** If you have a SQL View that depends on context, then instead of overriding -``BaseModel.init()`` set the ``_table_query`` property:: - - @property - def _table_query(self): - return 'SELECT %s FROM %s' % (self._select(), self._from()) - -The *select* and *from* methods remain the same. - -`Here is an example <{GITHUB_PATH}/addons/account/report/account_invoice_report.py>`__ -of a report that depends on the currently selected companies (in a multi-company environment) context to -determine the currency exchange rates to use for accurately displaying amounts when the selected companies -have different currencies. diff --git a/content/developer/tutorials/dashboards/report_dashboard.png b/content/developer/tutorials/dashboards/report_dashboard.png deleted file mode 100644 index a95028623..000000000 Binary files a/content/developer/tutorials/dashboards/report_dashboard.png and /dev/null differ diff --git a/content/developer/tutorials/dashboards/report_list_detail.png b/content/developer/tutorials/dashboards/report_list_detail.png deleted file mode 100644 index 62d44e92c..000000000 Binary files a/content/developer/tutorials/dashboards/report_list_detail.png and /dev/null differ diff --git a/content/developer/tutorials/dashboards/simple_dashboard.png b/content/developer/tutorials/dashboards/simple_dashboard.png deleted file mode 100644 index 9a25d81a9..000000000 Binary files a/content/developer/tutorials/dashboards/simple_dashboard.png and /dev/null differ diff --git a/content/developer/tutorials/pdf_reports.rst b/content/developer/tutorials/pdf_reports.rst index acdeea0d6..391ed847e 100644 --- a/content/developer/tutorials/pdf_reports.rst +++ b/content/developer/tutorials/pdf_reports.rst @@ -50,11 +50,6 @@ Therefore, it is expected that your work tree will look something like this: ├── __init__.py └── __manifest__.py -Note that you will often see other non-QWeb and non-XML files containing "report" in their name also within -the report folder. These are unrelated to the reports covered in this tutorial and are covered in -the :doc:`dashboards` tutorial. For now you can think of them as customized views that use direct -SQL queries (sometimes referred to as SQL Views). - Don't forget to add whatever files your template and action view will be into to your ``__manifest__.py``. In this case, you will want to add the files to the ``data`` list and remember that the files listed in a manifest are loaded sequentially!