Saltar al contenido
NUEVOGuía práctica con 5 agentes Python para Odoo, verificada contra Odoo 20 →
Focuz/academy

casos practicos

Odoo 20 sin ir.rule: migra tus módulos con upgrade_code

Odoo 20 reemplaza ir.model.access e ir.rule por un único modelo, ir.access. Migramos un módulo real con el comando upgrade_code de Odoo, lo instalamos en Odoo 20 y te contamos qué falló en el camino.

Focuz Academy · 29 de setiembre de 2026 · 5 min

Odoo 20 se presentó en Odoo Experience 2026, del 24 al 26 de septiembre en Bruselas. Las notas de versión traen muchas funciones nuevas, pero hay una línea que todo desarrollador de módulos debe leer: "Access rights have been simplified by removing record rules and adding a domain directly at the access right level".

En la práctica, ir.model.access e ir.rule dejan de existir. Si tu módulo trae un ir.model.access.csv o reglas de registro, no se instalará en Odoo 20 tal como está. La buena noticia es que Odoo incluye una herramienta para migrar ese código. La probamos.

El nuevo modelo: ir.access

En la rama 20.0 de github.com/odoo/odoo, el modelo ir.access (en odoo/addons/base/models/ir_access.py) une los permisos y las reglas en un solo registro:

  • model_id y group_id (el grupo es opcional).
  • operation: un subconjunto de crud (por ejemplo, cru o r).
  • domain: los registros a los que aplica.
  • kind (calculado): permission si tiene grupo y restriction si no lo tiene.

Otros cambios que te afectan de 19.0 a 20.0

El changelog del ORM registra cuatro versiones de Odoo Online entre 19.0 y 20.0 (19.1 a 19.4). Si saltas de 19.0 on-premise a 20.0, heredas todos sus cambios. Según el changelog del ORM:

  • Campos binarios: su tipo pasa a ser BinaryValue y ya no se codifica en base64 en todo el flujo (19.3). En el código fuente, BinaryValue ofrece content, open() y to_base64(). Revisa cada base64.b64decode(record.campo).
  • request sale del código de los modelos y se agrega env.website (19.4).
  • Se elimina Model._table_query (20.0).
  • Nueva API para ir.config_parameter y Field.compute_sql (19.1).
  • Nuevo script populate para generar datos de prueba (20.0).

Dos datos más del código: odoo/release.py exige Python 3.12 a 3.14, y el cliente web incluye OWL 3 en web/static/lib/owl/owl.js (en el commit que usamos, la versión 3.0.0-alpha.49).

La herramienta: upgrade_code

odoo/cli/upgrade_code.py reescribe tu código fuente con los scripts de odoo/upgrade_code/, cuyos nombres siguen el formato {versión}-{nombre}.py. Opciones: --script (un solo script), --from/--to (un rango de versiones), --glob, --addons-path y --dry-run. Su propia documentación advierte que los scripts hacen un esfuerzo razonable, pero que "no son balas de plata".

Para pasar de 19.0 a 20.0 nos interesan, entre otros, 19.4-00-ir-access.py, 19.4-00-ormcache-on-transaction.py, 19.5-00-tuple-rec_names_search.py y owl3-migration.py (para JavaScript y plantillas OWL).

Caso práctico: el módulo focuz_garantia

Creamos un módulo al estilo de Odoo 19 con lo que suele tener un módulo real: un ir.model.access.csv con dos permisos, una regla multiempresa en XML y este modelo:

class FocuzGarantia(models.Model):
    _name = 'focuz.garantia'
    _description = 'Garantía de producto'
    _rec_names_search = ['name', 'serie']

    name = fields.Char(required=True)
    serie = fields.Char()
    company_id = fields.Many2one('res.company', default=lambda self: self.env.company)

    def write(self, vals):
        res = super().write(vals)
        if 'serie' in vals:
            self.env.registry.clear_cache()
        return res

Desde la raíz de un clon de la rama 20.0 (en nuestro caso, el commit del 28 de septiembre), ejecutamos los scripts uno por uno. Primero, en modo de prueba:

PYTHONPATH=. python odoo/cli/upgrade_code.py --script ir-access --addons-path odoo/addons,../mis_addons --dry-run
updated: .../focuz_garantia/__manifest__.py
deleted: .../focuz_garantia/security/garantia_security.xml
updated: .../focuz_garantia/security/ir.access.csv
deleted: .../focuz_garantia/security/ir.model.access.csv

Luego, sin --dry-run, con ir-access, ormcache-on-transaction y tuple-rec_names_search. El resultado de seguridad fue este security/ir.access.csv:

id,name,model_id,group_id/id,operation,domain
access_focuz_garantia_user,focuz.garantia.user,focuz.garantia,base.group_user,cru,
access_focuz_garantia_manager,focuz.garantia.manager,focuz.garantia,base.group_system,crud,
focuz_garantia_company_rule,Garantías: multiempresa,focuz.garantia,,crud,"[('company_id', 'in', company_ids)]"

El script también actualizó el manifiesto, cambió _rec_names_search a una tupla y reemplazó self.env.registry.clear_cache() por self.env.transaction.invalidate_ormcache().

Después instalamos el módulo en Odoo 20 sobre una base de datos limpia y lo revisamos desde odoo shell:

focuz.garantia.manager | Role / Administrator | crud | permission |
focuz.garantia.user | Role / User | cru | permission |
Garantías: multiempresa | - | crud | restriction | [('company_id', 'in', company_ids)]

La regla global se convirtió en una restriction sin grupo, y 'ir.model.access' in env devolvió False.

Lo que aprendimos (y no está en las notas)

  1. Ejecuta los scripts de uno en uno con --script. En nuestra prueba (commit del 28 de septiembre), --from 19.0 con los addons del núcleo en la ruta se detuvo con un KeyError dentro de 19.3-00-account-groups.py, un script de contabilidad que nuestro módulo ni siquiera usa. Ejecutar solo los scripts que necesitas evita el problema.
  2. Incluye odoo/addons en --addons-path. Sin el módulo base, el script ir-access falla con KeyError: 'base.group_user' porque necesita las definiciones de grupos.
  3. La versión del manifiesto no se actualiza sola. Con 'version': '19.0.1.0.0', Odoo 20 marcó el módulo como versión incompatible (installable=False). Cámbiala a 20.0.x.y.z.
  4. Revisa el CSV generado con alguien de negocio. La conversión es mecánica: confirma que cada restriction refleja la regla que querías.

Ejercicio

  1. Clona la rama 20.0 y crea una rama de migración en tu repositorio de módulos.
  2. Ejecuta --dry-run con ir-access y lee la lista de archivos que cambiarían.
  3. Aplica los scripts de uno en uno, haciendo un commit después de cada uno para revisar los diffs.
  4. Si tienes JavaScript propio, ejecuta --script owl3-migration en otro commit.
  5. Busca b64decode y b64encode en el código que lee campos binarios.
  6. Sube la versión del manifiesto, instala en una base limpia de Odoo 20 y ejecuta tus tests.

Fuentes: Notas de versión de Odoo 20, Changelog del ORM (Odoo 20.0), upgrade_code.py en odoo/odoo 20.0, Script 19.4-00-ir-access.py, Odoo Experience 2026.

Guía gratuita: 5 agentes Python para Odoo

Casos de Junior a Senior, con código verificado contra Odoo 20.

Descargar →

¿Empiezas desde cero? Lee qué es el agent loop y sigue las lecciones gratuitas.

Sigue leyendo