Como extender el ORM de Odoo

Loggeando cambios a los productos
22 de julio de 2023 por
Gustavo Orrillo
| Sin comentarios aún
 

Uno de los patrones que se usan con frecuencia en Odoo es la extensión del ORM. Basicamente tomamos un método del ORM (por ejemplo unlink para el borrado) y lo extendemos para implementar la funcionalidad que necesitamos. Supongamos que necesitamos evitar que se borren los registros de un modelo. Para eso debemos extender el método unlink

def unlink(self):
​raise ValidationError('No se puede borrar el registro')

En este caso cada vez que se intente borrar el registro, mostrará el mensaje de error. Ahora supongamos que deseamos borrar el registro solo de los registros que se encuentren archivados, se debería hacer lo siguiente

def unlink(self):
​active_records = self.filetered(lambda l: l.active == True)
​if active_records:
​raise ValidationError('No se pueden borrar registros activos')
​return super(MyModel, self).unlink()

En este caso, si alguno de los registros que se quiere borrar no esta archivado, se mostrará el ensaje de error. Caso contrario se continua on la ejecución del ORM.

Desarrollando un logger de cambios

Apliquemos estos conceptos con un ejemplo práctico. Queremos loggear los cambios a los productos hechos por cada usuario. En este caso para simplificar el ejemplo vamos a loggear lso cambios a un campo: el costo del producto (standard_price)

Para ello desarrollamos un módulo llamado logger_products. Dicho módulo, agrega un modelo, product.logging. Este modelo consta de tres campos para loggear el campo que se modifica, su valor previo y valor posterior. Y consta de un campo tipo many2one en el que hace referencia al product.template. Esta es la definición del modelo:

class ProductLogging(models.Model):
_name = 'product.logging'
_description = 'product.logging'

product_tmpl_id = fields.Many2one('product.template','Producto')
field_name = fields.Char('Campo')
old_val = fields.Float('Viejo valor')
new_val = fields.Float('Nuevo valor')

Y en el modelo product.tempalte tenemos un campo tipo one2many llamado logging_ids para relacionarlo con el modelo product.logging

    logging_ids = fields.One2many(
comodel_name='product.logging',
inverse_name='product_tmpl_id',
string='Logging')

Este modelo se muestra dentro del tab "Logging" del producto. Para eso extendemos la vista del product.template y le agregamos el tab

    <record id="product_template_logging_form" model="ir.ui.view">
<field name="name">product.template.logging.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"></field>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page string="Logging">
<br />
<group>
<field name="logging_ids" readonly="1">
<tree>
<field name="field_name" />
<field name="old_val" />
<field name="new_val" />
<field name="create_date" />
<field name="create_uid" />
</tree>
</field>
</group>
</page>
</xpath>
</field>
</record>

Por último, debemos extender el método write para loggear los cambios cuando lo necesitamos. Eso lo hacemos de esta manera:

    def write(self, vals):
if 'standard_price' in vals:
logging_values = {
'product_tmpl_id': self.id,
'field_name': 'standard_price',
'old_val': self.standard_price,
'new_val': vals.get('standard_price')
}
logging_id = self.env['product.logging'].create(logging_values)
return super(ProductTemplate, self).write(vals)

En este método chequeamos si en los valores a escribirse (variable vals) se encuentra el campo standard_price. Si es así insertamos un registro en el modelo product.logging con los nuevos valores. Y luego dejamos que el ORM escriba el producto como lo hace habitualmente. A continuación podemos apreciar  un screenshot de lo que acabamos de ilustrar


El código de lo que acabamos de hacer se encuentra en nuestro repositorio, es un muy buen ejemplo sobre como extender el ORM. La técnica que se describe en la que extendemos el método write, es una técnica que usamos casi a diario como desarrolladores de Odoo.

Gustavo Orrillo 22 de julio de 2023
Compartir
Categorías
Archivar
Identificarse dejar un comentario