One2many tracking

In this article, we'll dive into how to implement tracking for a One2many (1:N) relationship in Odoo,


One2many tracking example


Here is the code snippet for tracking the One2many purchase order line field from the purchase order:


from odoo import models, fields


class PurchaseOrderLineTracking(models.Model):
_name = "purchase.order.line"
_inherit = ["purchase.order.line", "mail.thread", "mail.activity.mixin"]

product_id = fields.Many2one(tracking=True)
product_qty = fields.Float(tracking=True)
product_uom_qty = fields.Float(tracking=True)
price_unit = fields.Float(tracking=True)

def _message_log(self, body='',
subject=False,
message_type='notification', **kwargs):
self.order_id._message_log(
body=f"{self.name} / {self.sequence}: \n{body}",
subject=subject, message_type=message_type,
**kwargs
)



The code snippet defines a class PurchaseOrderLineTracking, which is a model in the Odoo framework. Let's break down its components:


Model Definition:


class PurchaseOrderLineTracking(models.Model):
_name = "purchase.order.line"
_inherit = ["purchase.order.line", "mail.thread", "mail.activity.mixin"]


_name: This is the technical name of the model. In this case, it's purchase.order.line.
_inherit: This attribute is used to extend an existing model. Here, it extends purchase.order.line model and also adds functionality from mail.thread and  mail.activity.mixin for tracking and logging purposes.


Field Definitions with Tracking:


    product_id = fields.Many2one(tracking=True)
product_qty = fields.Float(tracking=True)
product_uom_qty = fields.Float(tracking=True)
price_unit = fields.Float(tracking=True)


Each field (like product_id, product_qty, etc.) is defined with a type (Many2one, Float) and an additional tracking=True parameter. This parameter enables tracking changes to these fields. It adds the tracking info to the existing inherited field.


Message Logging Method:


def _message_log(self, body='',
subject=False,
message_type='notification', **kwargs):
self.order_id._message_log(
body=f"{self.name} / {self.sequence}: \n{body}",
subject=subject, message_type=message_type,
**kwargs
)



This method is a custom function for logging messages. It formats the message with the line name and sequence before logging it into the parent order (purchase.order).

How Tracking Works in this Code


  • Enabling Field Tracking:
    By setting tracking=True on model fields, Odoo automatically tracks changes to these fields. This is crucial for audit trails and history tracking in business applications.
  • Integration with Mail Thread:
    The inheritance from mail.thread and mail.activity.mixin integrates the model with Odoo's mail system. This allows for automatic generation of messages or logs when tracked fields are modified.
  • Custom Message Logging:
    The _message_log method enhances this functionality by enabling custom messages to be logged. This is mandatory for writing the changes at the parent level too.


Practical Implications in Odoo


It is important to choose the fields you want to track wisely. In this example both  product_uom_qty and product_qty are track which will generate most of the time the same information twice. It might be more interesting to log the product_uom instead. 

It will also generate more messages so if you follow the record, a lot of emails can be generated.

To reduce the number of message, it could be wise to merge the logs together with the parent.

Conclusion


The code snippet effectively demonstrates how Odoo’s ORM (Object Relational Mapping) framework can be used to add tracking to a model. This is a key feature for any ERP system, as it ensures transparency and accountability in business processes. Understanding these concepts allows developers and system architects to build robust and user-friendly applications on top of the Odoo platform.e...

Odoo & Postgres