Ir al contenido

Auditoría técnica de módulos Odoo antes de producción

Checklist de calidad, seguridad y rendimiento para un release seguro
7 de octubre de 2026 por
Auditoría técnica de módulos Odoo antes de producción
ATEMI, Aitor Atencia

Odoo · Calidad · Release

El módulo funciona en local, los tests están en verde y el cliente tiene prisa. Es exactamente el momento en que se cuelan los problemas caros: un modelo sin permisos, una ruta pública que escribe datos, un cr.execute con un f-string o una migración que nunca se probó sobre una base de datos con años de historia. Esta es la lista que repasamos antes de cada release.

Logo Odoo
Cuatro bloques: seguridad, datos, rendimiento y actualización.

1 · Seguridad

Permisos, sudo() y rutas HTTP

Empieza por lo que más duele si falla. Cada modelo nuevo necesita su línea en ir.model.access.csv (Odoo avisa en el log al cargar un modelo sin reglas de acceso), cada sudo() necesita una justificación y cada ruta con auth='public' o auth='none' debe revisarse a mano. Unos grep bastan para hacer el inventario:

# Todos los sudo() del módulo, para revisarlos uno a uno
grep -rn "sudo()" mi_modulo/ --include=*.py

# Rutas públicas o sin sesión
grep -rn "auth=.\(public\|none\)" mi_modulo/controllers/

# SQL crudo: ¿está parametrizado?
grep -rn "cr.execute" mi_modulo/ --include=*.py

Para el SQL crudo, la regla es no construir nunca la query con formato de cadenas. Desde Odoo 17 el envoltorio SQL de odoo.tools compone identificadores y parámetros de forma segura:

from odoo.tools import SQL

# ❌ Inyección SQL si 'state' viene del usuario
self.env.cr.execute(f"SELECT id FROM fleet_trip WHERE state = '{state}'")

# ✅ Parámetros separados de la query
self.env.cr.execute(SQL("SELECT id FROM fleet_trip WHERE state = %s", state))

Para el detalle de cada punto: ACL y record rules, sudo() y seguridad en controladores.

2 · Datos

Prueba como un usuario sin privilegios

La mayoría de los tests se ejecutan como superusuario, que se salta las record rules. Un módulo puede tener la suite entera en verde y aun así mostrar a un comercial los registros de otro. Añade al menos un test por rol que use with_user() con un usuario real del grupo:

def test_salesman_only_sees_own_trips(self):
    salesman = self.env["res.users"].create({
        "name": "Salesman",
        "login": "salesman_trip",
        "group_ids": [(6, 0, [self.env.ref("mi_modulo.group_fleet_trip_user").id])],
    })
    other_trip = self.env["fleet.trip"].create({"name": "Ajeno"})
    visible = self.env["fleet.trip"].with_user(salesman).search([])
    self.assertNotIn(other_trip, visible)

3 · Rendimiento

Las consultas que crecen con los datos

Con veinte registros de demo nada es lento. Revisa los campos calculados almacenados con dependencias amplias, los search dentro de bucles y los campos por los que se filtra en vistas de búsqueda sin índice. Un assertQueryCount en el flujo crítico evita que una regresión de rendimiento llegue a producción sin que nadie lo note (ver performance en Odoo).

def test_close_trips_query_count(self):
    trips = self.env["fleet.trip"].create([{"name": f"T{i}"} for i in range(50)])
    with self.assertQueryCount(__system__=12):
        trips.action_close()   # no debe crecer con el número de viajes

4 · Actualización

Ensaya el -u sobre una copia de producción

Instalar en una base de datos vacía y actualizar sobre una con datos reales son dos operaciones distintas. Un campo nuevo con required=True, un cambio de tipo o un registro noupdate que esperabas que cambiase solo fallan en la segunda. El ensayo es barato: restaura el último backup, neutralízalo y lanza la actualización buscando avisos en el log.

odoo-bin neutralize -c odoo.conf -d copia_prod
odoo-bin -c odoo.conf -d copia_prod -u mi_modulo --stop-after-init 2>&1 \
  | grep -E "WARNING|ERROR"
BloqueComprobación¿OK?
SeguridadTodos los modelos tienen ACL; ningún aviso de acceso en el log☐
SeguridadCada sudo() y cada ruta pública está justificada☐
SeguridadNingún cr.execute construido con f-strings☐
DatosUn test por rol con with_user()☐
RendimientoSin search en bucles; índices en campos filtrados☐
Calidadpylint-odoo y suite de tests en verde en CI☐
ActualizaciónVersión del manifest subida y scripts de migración probados☐
Actualización-u sobre copia neutralizada sin WARNING ni ERROR☐
i18n.pot regenerado y es.po completo☐

Resumen

Antes de un release, inventaría los sudo(), las rutas públicas y el SQL crudo; prueba con usuarios sin privilegios; vigila las consultas que crecen con los datos; y ensaya la actualización sobre una copia neutralizada de producción. Una tarde de auditoría cuesta mucho menos que un rollback.

en Odoo
Diseño de módulos Odoo escalables: estructura y convenciones
Organizar models, views, data, security y tests para que el módulo aguante años