Creando consultas en Odoo

13 de julio de 2023 por
Gustavo Orrillo
| Sin comentarios aún
 

Odoo brinda buenas herramientas para el análisis de datos. Entre ellas estan las vistas tipo lista (con la posibilidad de filtrar y agrupar registros, esto no es menor) junto con las vistas tipo pivot, brindan al usuario funcionalidades para consultar datos out-of-the-box. Estas funcionalidades brindan posibilidades similares a las que brindaban un datawarehouse o los datamarts veinticinco años atras (no hablemos de sumarizaciones, ese es otro problema).



Peroestas vistas tienen sus limitaciones. Primero sus columnas están limitadas a las columnas que definió el desarrollador en la vista. Pero por sobre todo, no permite relacionar la información de dos modelos (por ejemplo proveedores con el modelo res.partner junto con pedidos de compra con el modelo purchase.order).  

La otra gran limitación es la performance. Las vistas pivot son muy lindas, ahora sus cubos se resuelven todos en la memoria del navegador de internet. Esto implica que si uno realiza una consulta contra un modelo que tiene miles de lineas (como tranquilamente puede ser un año de ventas); la misma no se va a poder resolver debido a los límites de procesamiento que tiene el navegador de internet.

Creando una vista en Odoo que refleja una vista SQL

Como se pueden resolver estas limitaciones? Lo primero que se me viene a la mente Es desarrollar módulos que implementen modelos con las consultas que necesiten en forma frecuente los usuarios. Dichos modelos se mapearán a vistas de SQL (para lo que necesitamos saber utilizar SQL). Esta técnica es muy popular en Odoo. Muchos informes que hay en el core crean vistas de SQL que luego se analizan mediante vistas tipo lista o pivot. Otro buen ejemplo de esta técnica lo van a encontrar en el módulo account_debt_management, donde se mapea los asientos contables a una vista que permite navegar la cuenta corriente de un cliente/proveedor.

Para ilustrar esta idea vamos a crear una consulta. Supongamos que necesitamos conocer que productos se venden por provincia y ciudad. Esa es una consulta no provista por default por Odoo; entonces como hacemos?

Primero crear un nuevo módulo, supongamos que se va a llamar a2_query_sales

mkdir a2_query_sales
cd a2_query_sales

Luego con nuestro editor favorito, vamos a crear el archivo de manifiesto con los siguientes contenidos:

{
"name": "a2 query sales",
"version": "15.0.1.0.0",
"license": "AGPL-3",
"depends": ["sale","sales_team"],
"category": "Sale",
"data": [
'a2_sales.xml',
'security/ir.model.access.csv',
]
}


Como pueden ver el archivo de manifiesto no tiene grandes secretos. Solo depende de dos módulos (sale y sale_team), e indica el uso de un solo archivo donde declararemos las vistas, a2_sales.xml. Tambien declara el archivo desde donde importamos las definiciones de seguridad (ir.model.access.csv). El sigueinte paso es crear archivo para inicializar el módulo (__init__.py)

from . import models

Donde le indicamos a Odoo que debe cargar el archivo de Python models. En este archivo crearemos el nuevo modelo llamado a2.sales (es un tutorial... no necesitamos nombres sofisticados) y este archivo va a tener el siguiente contenido:

from odoo import tools, models, fields, api, _

class A2Sales(models.Model):
​_name = "a2.sales"
​_description = "A2 Sales"
​_auto = False

​partner_id = fields.Many2one('res.partner','Cliente')
​city = fields.Char('Ciudad')
​state_id = fields.Many2one('res.country.state','Provincia')
​product_id = fields.Many2one('product.product','Producto')
​product_uom_qty = fields.Float('Cantidad')

Estas líneas primero crean una nueva clase e inmediatamente setean su atributo _auto a False (así no se crea la tabla en la base de datos al instalarse o actualizar el módulo). 

El paso siguiente, tenemos que hacer la consulta SQL con la que crearemos la vista. La consulta a resolver es que productos se venden por cliente, provincia y ciudad. Esta consulta se puede resolver con el siguiente query de SQL

select sol.id,so.partner_id,pa.city,pa.state_id,sol.product_id,sol.product_uom_qty 
from sale_order_line sol 
inner join sale_order so on so.id = sol.order_id 
inner join res_partner pa on pa.id = so.partner_id

Aquí podemos ver por cada columna del query se mapea un atributo del modelo. Se respetan los tipos de campo de los modelos originales, así en las vistas tienen el comportamiento correcto. Al principio de la consulta hay que indicar que hay una columna extra al principio con un ID, en donde indicamos una columna única de ID de la consulta. Si esta columna de ID no se agrega, Odoo no va a poder mostrar los resultados de las consultas en sus vistas.

Depsues debemos definir el método que inicializa el modelo. En este método se crea la vista de SQL si la vista no se encuentra creada:

def init(self):
tools.drop_view_if_exists(self._cr, self._table)
query = """
select sol.id,so.partner_id,pa.city,pa.state_id,sol.product_id,sol.product_uom_qty
from sale_order_line sol
inner join sale_order so on so.id = sol.order_id
inner join res_partner pa on pa.id = so.partner_id
"""
self.env.cr.execute("""CREATE or REPLACE VIEW %s as (%s)""" % (self._table, query))


Aca podemos ver que cada vez que el modelo se inicializa (o sea cada vez que Odoo se reinicia) la vista SQL es recreada en la base de datos. Al hacerlo Odoo automaticamente mapea cada una de las columnas de la vista al modelo definido previamente.

El paso siguiente es declarar sus permisos de seguridad así lo pueden usar los usuarios. Para ello crearemos el archivo security/ir.model.access.csv con el siguiente contenido

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_a2_sales,a2.sales,model_a2_sales,sales_team.group_sale_salesman,1,1,1,1

Aca se indica que el modelo creado puede ser accedido por todos los vendedores.

Ahora tenemos que crear un menú, una acción y dos vistas (una de tipo tree y otra del tipo pivot) para mostrar los resultados del modelo. Para ello necesitamos un nuevo archivo a2_sales.xml donde crearemos el menuitem, el action y las dos views.

<?xml version="1.0" encoding="utf-8"?>
<odoo>

<record id="a2_sales_view_pivot" model="ir.ui.view">
<field name="name">a2.sales.view.pivot</field>
<field name="model">a2.sales</field>
<field name="arch" type="xml">
<tree string="A2 Sales">
<field name="partner_id" type="row"/>
<field name="state_id" type="row"/>
<field name="city" type="row"/>
<field name="product_id" type="col"/>
<field name="product_uom_qty" type="measure"/>
</tree>
</field>
</record>

<record id="a2_sales_view_tree" model="ir.ui.view">
<field name="name">a2.sales.view.tree</field>
<field name="model">a2.sales</field>
<field name="arch" type="xml">
<tree string="A2 Sales">
<field name="partner_id" />
<field name="state_id" />
<field name="city" />
<field name="product_id" />
<field name="product_uom_qty" />
</tree>
</field>
</record>

<record id="action_a2_sales" model="ir.actions.act_window">
<field name="name">A2 Sales</field>
<field name="res_model">a2.sales</field>
<field name="view_mode">tree,pivot</field>
</record>

<menuitem id="menu_a2_sales"
name="A2 Sales"
parent="sale.menu_sale_report"
sequence="10"
action="action_a2_sales"
groups="sales_team.group_sale_salesman"/>
</odoo>

Aca primero definimos el menuitem que va a pender del menu "Reporting" en el modulo sale. Después creamos la acción en donde indicamos que va a poder ver las vistas en dos modos: tree y pivot. Y seguidamente declaramos las dos vistas.

Bueno, finalmente instalamos el módulo y refrescamos el navegador. Podremos apreciar que en el menu Reporting de Ventas hay un nuevo menu A2 Sales.


Si lo seleccionamos podremos apreciar la vista tipo lista con los contenidos de la consulta


Y la vista tipo pivot.


Bueno, eso es lo que hay que hacer para crear una consulta que muestre los resultados de una vista de SQL. El código lo pueden ver en el repositorio a2_query_sales


Gustavo Orrillo 13 de julio de 2023
Compartir
Archivar
Identificarse dejar un comentario