Skip to content
Snippets Groups Projects
README.rst 14.3 KiB
Newer Older
Analytic Structure
==================

Brendan Masson's avatar
Brendan Masson committed
What is it ?
------------

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

- Dimensions, represented by ``analytic.dimension``
- Structure, represented by ``analytic.structure``
- Codes, 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``.
Brendan Masson's avatar
Brendan Masson committed

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

Schematic::
Brendan Masson's avatar
Brendan Masson committed

Brendan Masson's avatar
Brendan Masson committed
 | 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.
Brendan Masson's avatar
Brendan Masson committed
The goal of this constraint is to ensure the integrity of your analyses.
Brendan Masson's avatar
Brendan Masson committed

.. _ConfigureAnalyticFields:

Configure your OpenERP server for analytic fields
-------------------------------------------------

In your OpenERP 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).

Brendan Masson's avatar
Brendan Masson committed
.. _MetaAnalytic:

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:

.. code:: python

 __metaclass__ = MetaAnalytic

Brendan Masson's avatar
Brendan Masson committed
.. _AnalyticFields:
This metaclass adds a method able to return the technical names of all the
analytic fields of the inheriting class:
.. get_analytic_field_names

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:

.. code:: python

 _analytic = True


Use analytic fields associated with another model:

.. code:: python

 _analytic = 'account_move_line'


Use several analytic field structures, associated with different prefixes:

.. code:: python

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

Add analytic fields to a view
-----------------------------

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

.. code:: xml

 <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.


You can also use a field named 'analytic_dimensions' to insert every analytic field within a given structure
(defined by its prefix) that wasn't explicitly placed in the view. This field is automatically generated when
you call the Metaclass

.. code:: xml

 <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.


Warning: analytic fields should generally not be used inside nested sub-views.
If possible, create a separate record and use the context to specify the view:

.. code:: xml

 <field name="order_line" colspan="4" nolabel="1" context="{
     'form_view_ref' : 'module.view_id',
     'tree_view_ref' : 'module.view_id'
 }"/>


Brendan Masson's avatar
Brendan Masson committed
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)
Brendan Masson's avatar
Brendan Masson committed
``type`` a field class, the field type to use
Brendan Masson's avatar
Brendan Masson committed
``default`` default value for the fields
Brendan Masson's avatar
Brendan Masson committed
``args`` list of arguments to inject in ``type`` constructor
Brendan Masson's avatar
Brendan Masson committed
``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``, ...

.. code:: python

 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),
Brendan Masson's avatar
Brendan Masson committed
 }

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:

.. code::python

 def validate_analytic_fields(self, analytic):

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()``.
Brendan Masson's avatar
Brendan Masson committed
.. _AnalyticDimensions:

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:

.. code:: python

 _dimension = True


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

.. code:: python

 _dimension = 'Funding Source'


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

.. code:: python

 _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,
     'use_code_name_methods': False,
     'code_name': 'name',
     'code_description': 'description',
     'parent_column': 'analytic_code_id',
     'automatic': True,
 }


key (default value): description

Brendan Masson's avatar
Brendan Masson committed
``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.

Brendan Masson's avatar
Brendan Masson committed
``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.

Brendan Masson's avatar
Brendan Masson committed
``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.

Brendan Masson's avatar
Brendan Masson committed
``sync_parent`` (``False``): Controls the synchronization of the codes' parent-child
hierarchy with that of the model. When using an inherited, renamed parent field,
Brendan Masson's avatar
Brendan Masson committed
you must give the parent field name rather than simply ``True``.
Brendan Masson's avatar
Brendan Masson committed
``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.
Brendan Masson's avatar
Brendan Masson committed
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``.
Brendan Masson's avatar
Brendan Masson committed
``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.
Brendan Masson's avatar
Brendan Masson committed
If given ``True``, the default field label 'Active' will also be used.
Brendan Masson's avatar
Brendan Masson committed
``rel_description`` (``False``): Same as rel_active for the code field 'description'.
If given a string, the default field name 'description' will be used.
Brendan Masson's avatar
Brendan Masson committed
If given ``True``, the default field label 'Description' will also be used.
Brendan Masson's avatar
Brendan Masson committed
``use_code_name_methods`` (``False``): Set to ``True`` in order to override the methods
name_get and name_search, using those of analytic code.
This allows the analytic code's description to be displayed (and searched)
along with the entry's name in many2one fields targeting the model.

``code_name`` (``name``): Name of the record field, which will define the name of the 
created analytic code. The default value is 'name'. The parameter can be a
class, it must be defined as in section 'Name and description generation'.
``code_description`` (``description``): Name of the record field, which will define the 
description of the created analytic code. The default value is 'description'.
The parameter can be a class, it must be defined as in section 'Name and
description generation'.

``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.

``automatic`` (``False``): Whether the automatic creation of a bound analytic code to
each record must be deactivated on record creation or modification.
Set to ``True``, which is the default value, if analytic code creation and update must
work as defined by default on create or write operation.

Name and description generation
-------------------------------

The analytic code name and description can be computed by a class. Field names
can still be used.

The class must be defined under this format, with a field named ``fields``, 
listing the field names implied in the name or description computation and 
a method ``generate`` with two parameters (the model class and the field value
dictionary) for the computation:

class Example(object):

    fields = ["xxx", "yyy" ...]  # The list of field names implied in the
                                 # name or description computation. 

    # This method must always be called ``generate``. It receives two
    # parameters, which can be named differently.
    # The first parameter ``model_obj`` is the Odoo class of the model from
    # which the analytic code is generated.
    # The second parameter ``vals`` is the field value dictionary. The
    # MetaAnalytic metaclass works in such a way, that the field names defined
    # in ``fields`` will always be present in ``vals``.
    
    def generate(model_obj, vals):

        # Computation code.
        # Example:
        result = model_obj.xxx_method(vals["xxx"], vals["yyy"])

        return result  # The method returns the name or description computed
                       # from the values read in ``vals`` and corresponding to
                       # the keys defined in ``fields``. ``model_obj`` can be
                       # called in this code, to use specific methods defined
                       # in this class.


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
- Disabled per company: Determines whether an analytic code is disabled for the