Skip to content
Snippets Groups Projects
Name Last commit Last update
.badges
demo
doc
i18n
models
security
static/description
util
views
.editorconfig
.flake8
.gitlab-ci.yml
.hgignore
.hgtags
.prettierrc.yml
.yamllint.yaml
LICENSE
MetaAnalytic.py
NEWS.rst
README.rst
__init__.py
__manifest__.py
post_load_hooks.py
pyproject.toml

Analytic Structure

Coverage report pylint score Alpha License: AGPL-3 Black Prettier

Dynamic relationships between objects to build analytic matrices.

What Is It?

The module provides a way of defining dynamic relationships between objects. We use the following concepts:

  • Dimension, represented by analytic.dimension
  • Structure, represented by analytic.structure
  • Code, represented by analytic.code

These objects can be seen in Technical >> Analytics under Settings.

Dimensions act as labels for data points that can be used to perform analyses. Structures define mappings between models and dimensions, they can be configured through the Settings menu. Codes are the objects that are bound to instances of dimension models, they allow us to define dynamic relationships.

Example: Your company has several product lines and you would like to analyse expenses related to these product lines in your accounting system. You would then define a product line dimension and some structures that bind the product line dimension to product.product, invoice.line, account.move.line models.

How Does It Work?

Analytic Structure provides the MetaAnalytic metaclass (see MetaAnalytic) that injects new behaviors into Odoo models. Dimension models and models that define analytic fields must use MetaAnalytic and define various class attributes.

A dimension model is declared with the _dimension attribute (see AnalyticDimensions). The metaclass automatically creates analytic.dimension records for each dimension model. Once a model is declared as a dimension, every new instance will automatically create a new analytic.code record that points to the relevant analytic.dimension record.

A model that is declared with the _analytic attribute (see AnalyticFields) can reference dimension objects. The MetaAnalytic will automatically create a number of M2O fields that point to analytic codes. The number of fields that will be added depends on the configuration (See ConfigureAnalyticFields). They are named with a predefined prefix and a number or slot eg. a1_id, a2_id, a3_id, ...

These analytic fields will be displayed in views with the names of the dimensions they point to thanks to view manipulation magic.

Schematic:

| AnalyticModel     Code        DimensionModel     Dimension
| -----             ----        --------------     ---------
| an_id ----------> id,name <-- analytic_code_id
|
|                   n_id    ---------------------> id,name

The relationship between a model and a dimension is configured by analytic structures. Structure define how models point to dimensions and what analytic field to use.

Example: You have a dimension D you wish to bind to model A. You would create an analytic.structure record for A that references D through the Analysis 1 slot. This would allow you to use the a1_id field (assuming the default prefix is used) to reference D records.

Integrity of Analytic Codes

You cannot delete analytic codes that are referenced by objects. The goal of this constraint is to ensure the integrity of your analyses.

Server Configuration

In Odoo server's configuration file, you can set several optional parameters related to the analytic module.

[analytic]
key = value ...

Those options must be grouped under the [analytic] category. If the category doesn't exist, add it to your configuration file.

key (default value): description

analytic_size (5): define the maximum number of analytic dimensions that can be associated with a model.

translate (False): enable or disable the translation of field values on analytic dimensions (name) and codes (name and description).

How to Add the MetaAnalytic Metaclass to a Model

At the beginning of the source file, import the MetaAnalytic metaclass:

from openerp.addons.analytic_structure.MetaAnalytic import MetaAnalytic

Inside your Model class, define MetaAnalytic to be used as metaclass:

__metaclass__ = MetaAnalytic

How to Add Analytic Fields to a Model

First of all, make sure you are using the MetaAnalytic metaclass. Then, add the _analytic attribute to your class, using the following syntax.

Use the analytic fields associated with the model:

_analytic = True

The analytic name used will be the same as the name of the model’s table. For example, for the model res.users, the name will be res_users.

Use analytic fields associated with another model:

_analytic = "account_move_line"

Use several analytic field structures, associated with different prefixes:

_analytic = {
    "a": "account_asset_asset",
    "t": "account_move_line",
}

How to Add Analytic Fields to a View

Analytic fields can be added to the view individually, like any other field:

<field name="a1_id" />

'a' is the prefix associated with the structure. By default, it is 'a'. '1' is the dimension's ordering as defined by the analytic structure.

However, you would usually instead add the automatic analytic_dimensions field, which gets replaced at runtime by analytic fields specified within a given structure (defined by its prefix):

<field name="analytic_dimensions" required="1" prefix="t" />

The prefix can be omitted for a structure that uses the default prefix 'a'. Any other attribute will be propagated to the analytic fields.

Although not optimal, analytic field replacement works fine in nested sub-views (eg sale.order.line views within sale.order views). To handle them, use the _analytic_view_subfields class attribute, which is a tuple. Example for sale.order:

_analytic_view_subfields = ("order_line",)

Advanced: Para-analytic fields

Para-analytic fields are a more advanced feature of analytic_structure. They differ from ordinary analytics fields in two ways:

  • They are entirely configurable, meaning that you decide their type and parameters
  • They don't have predefined behaviors

Para-analytic fields are defined in with the _para_analytic attribute. For each entry in _para_analytic the MetaAnalytic metaclass will create a number fields. The number of fields depend on analytic_size in the configuration file (see ConfigureAnalyticFields).

Each entry is key-value pair of a dict where the key is a (prefix, suffix) tuple and the value a dict containing the following:

model the name of the referenced dimension model (doesn't do anything special)

type a field class, the field type to use

default default value for the fields

args list of arguments to inject in type constructor

kwargs dict of keyword arguments to inject in type constructor.

Here is declaration that will create fields with the names a1_b, a2_b, a3_b, ...

from openerp import fields

# ...

# Inside a class
_para_analytic = {
   ('a', 'b'): {
       'model': 'account_move_line',
       'type': fields.Boolean,
       'default': True,
       'args': ("field is optional"),
       'kwargs': dict(required=True),
   }
}

Validation Hook for Analytic Fields

Models that define the _analytic attribute can override the _validate_analytic_fields to perform validation on analytic fields. The method is called every time the model's create and write methods are called.

Odoo 8.0 Method signature:

where analytic is a dict containing in the same information given in the _analytic class attribute, in the expanded form.

The method signals failure by raising an exception, just like methods decorated with api.constrains().

Bind an Analytic Dimension to a Model

First of all, make sure you are using the MetaAnalytic metaclass. Then, add the _dimension attribute to your class, using the following syntax.

Bind the model to an analytic dimension named after the model, using default values:

_dimension = True

Bind the model to an analytic dimension with a specified name, using default values:

_dimension = 'Funding Source'

Bind the model to an analytic dimension, using either custom or default values:

_dimension = {
    'name': 'School',
    'column': 'analytic_code_id',
    'ref_id': 'school_analytic_dimension',
    'ref_module': 'my_module',
    'sync_parent': False,
    'rel_description': True,
    'rel_active': (u"Active", 'active_code'),
    'use_inherits': False,
    'code_name': 'name',
    'code_description': 'description',
    'parent_column': 'analytic_code_id',
    'required': True,
}

key (default value): description

name (= _description or _name): The name of the analytic dimension. This name is only used when creating the dimension in the database.

column (analytic_id): The field that links each record to an analytic code.

ref_id (= _name + analytic_dimension_id): The external ID that will be used by the analytic dimension. By setting this value, you can allow two models to use the same dimension, or a model to use an already existing one.

ref_module (empty string): The name of the module associated with the dimension record. Change this value in order to use a dimension defined in a data file.

sync_parent (False): Controls the synchronization of the codes' parent-child hierarchy with that of the model. When using an inherited, renamed parent field, you must give the parent field name rather than simply True.

use_inherits (special): Determines whether the analytic codes should be bound to the records by inheritance, or through a simple many2one field. Inheritance allows for better synchronization, but can only be used if there are no duplicate fields between the two objects. The default value is True if the model has no 'name' and 'code_parent_id' field as well as no inheritance of any kind, and False otherwise. If the object has inheritances that do not cause conflicts, you can set it to True.

rel_active (False): Create a related field in the model, targeting the analytic code field 'active' and with an appropriate store parameter. This is useful when the model doesn't inherit analytic_code and/or when it already has a field named 'active'. Can take a pair of string values: (field label, field name). If given a string, the default field name 'active' will be used. If given True, the default field label 'Active' will also be used.

rel_description (False): Same as rel_active for the code field 'description'. If given a string, the default field name 'description' will be used. If given True, the default field label 'Description' will also be used.

code_name (name): Name of the record field, which will define the name of the created analytic code. The default value is 'name'.

code_description (description): Name of the record field, which will define the description of the created analytic code. The default value is 'description'.

parent_column (column): Name of the parent record (sync_parent) field, which contains the parent code of the analytic code to create. The default value is given by column.

required (True): Whether the new field should be marked as required. Set to False when you expect data without bound analytics.

Bound Dimension: ORM Methods

The following methods are available within a model which has bound dimensions:

  • get_bound_dimension(raise_if_not_found=True): Return the analytic.dimension record bound to this model.
  • get_bound_analytics(model: str) -> dict: Get the analytic code bound to this model, ready to be fed into the specified target model, when the latter contains a corresponding dimension.

Analytic Tools

Tools to factor out common operations when playing with analytics:

  • get_defined_analytics(source, source_model, target_model) -> dict: Get analytics ready to be propagated from source to target. But only keep those that have an actual value set.
  • merge_analytics(master_dict, *analytic_dicts) -> dict: Merge provided dictionaries into a single one. Make sure each new dict does not override a previous one (so the master dict should be first).

Active / View type / Disabled in my company

Differences between the various "active" fields:

  • Active: Determines whether an analytic code is in the referential.
  • View type: Determines whether an analytic code is not selectable (but still in the referential).
  • Disabled per company: Determines whether an analytic code is disabled for the current company.