Zenithonsoft

Odoo 20 Deprecated Record Rules: What the Unified Access Rights Change Means for Your Upgrade

In the official Odoo 20 Release Notes, tucked quietly inside the General updates, sits what may be the single most significant architectural change for developers and system architects:

“Simplified access rights: Access rights have been simplified by removing record rules and adding a domain directly at the access right level to determine which records the access right applies to.”

For over a decade—from OpenERP 7 through Odoo 19—security in Odoo was governed by a two-layer architecture: Model Access Rights (ir.model.access.csv) and Record Rules (ir.rule).

In Odoo 20, that separation is gone. Record rules as an independent framework have been retired, replaced by unified domain-based access rights.

If your business relies on custom modules with proprietary permission logic, multi-branch security filters, or custom record rules, this change will be one of your primary upgrade considerations.

Here is what changed, why Odoo made this architectural leap, and how to audit your custom codebase before upgrading to v20.


The Legacy Problem: Why Odoo Retired ir.rule

To understand the impact of Odoo 20’s new security framework, we must look at the pain points of the legacy system:

Legacy Security Architecture (Odoo 8 – 19)
┌────────────────────────────────────────────────────────┐
│         Layer 1: Model Access (ir.model.access)        │
│  Can Group X read/write/create/unlink table Y? (CRUD) │
└───────────────────────────┬────────────────────────────┘
                            │ (If Allowed)
                            ▼
┌────────────────────────────────────────────────────────┐
│             Layer 2: Record Rules (ir.rule)            │
│  Evaluates dynamic domain filters in SQL WHERE clause: │
│  ['|', ('user_id', '=', user.id), ('company_id', ...)] │
└────────────────────────────────────────────────────────┘

While functional, this two-layer model created chronic headaches for enterprise deployments:

  1. Complex Evaluation Logic: Combining global rules (joined via AND) and group-specific rules (joined via OR) frequently caused subtle permission leaks or unexpected AccessError exceptions that were notoriously difficult to debug.
  2. PostgreSQL Query Overhead: On high-volume tables with hundreds of thousands of records (e.g. account.move, stock.move, crm.lead), evaluating nested record rule domains injected complex subqueries into every single database read, hurting overall ERP responsiveness.
  3. Fragmented Code Maintenance: Permissions for a single custom module were split across CSV files (security/ir.model.access.csv) and verbose XML records (security/ir_rule.xml).

The Odoo 20 Unified Access Rights Model

In Odoo 20, access permissions and domain constraints are consolidated into a single unified access model:

Odoo 20 Unified Access Rights
┌────────────────────────────────────────────────────────┐
│                  Unified Access Right                  │
│  • Target Model: e.g. sale.order                       │
│  • Target Group: e.g. sales_team.group_sale_salesman   │
│  • Operations: read, write, create, unlink             │
│  • Filter Domain: [('user_id', '=', user.id)]          │
└────────────────────────────────────────────────────────┘

Instead of requiring an ir.model.access record to grant table-level access followed by an ir.rule XML record to restrict rows, the access right definition itself carries the operational domain.

Key Architectural Benefits

  • Single Source of Truth: A developer can inspect exactly what a user group can see and do on a model in one place.
  • Streamlined ORM Query Compilation: Because access domains are bound directly to permission models, Odoo’s SQL query builder can optimize filter clauses and index paths more efficiently during high-concurrency operations.
  • Simplified Access Rights UI: Administrators can manage operational access domains directly from a modernized settings interface without navigating between two disconnected menus.

Why This Breaks Custom Modules During Upgrades

If you are planning an upgrade to Odoo 20, any custom module containing legacy record rules will face immediate roadblocks:

1. Obsolete ir.rule XML Data Records

Existing module definitions like this:

<!-- This legacy XML structure is deprecated in Odoo 20 -->
<record id="rule_clinic_patient_doctor" model="ir.rule">
    <field name="name">Clinic Patient: Doctor Only</field>
    <field name="model_id" ref="model_clinic_patient"/>
    <field name="domain_force">[('doctor_id.user_id', '=', user.id)]</field>
    <field name="groups" eval="[(4, ref('group_clinic_doctor'))]"/>
    <field name="perm_read" eval="1"/>
    <field name="perm_write" eval="1"/>
</record>

…will fail validation during module installation or upgrade (odoo-bin -u all), throwing XML parsing and schema errors.

2. Deprecated ORM Helper Methods

Custom Python code that programmatically searched, modified, or bypassed record rules using legacy methods:

  • env['ir.rule']._apply_ir_rules(...)
  • Overrides to _apply_ir_rules()
  • Custom multi-company domain generation hooks

…will encounter AttributeError or unexpected behavior in Odoo 20, as the underlying rule engine has been refactored.

🔒 Worried Your Custom Security Rules Will Break in Odoo 20?
Don’t spend hours grepping and manually testing access rights in a broken staging database. Upload your module to Zenithonsoft’s automated review pipeline for a comprehensive AST scan that flags deprecated security declarations in 24 hours under our 18-point NDA:
👉 Request Your Free 24h Odoo Security & Code Review →


Pre-Upgrade Security Audit Checklist

Before initiating migration testing for Odoo 20, execute this 4-step security audit on your custom codebase:

Step 1: Inventory All Custom ir.rule XML Definitions

Run a recursive search across your custom addons directory to locate every XML file declaring model="ir.rule":

grep -rn "model=\"ir.rule\"" custom_addons/

Document each rule’s target model, operational domain, assigned user groups, and whether it was configured as a global rule (global=True).

Step 2: Audit Multi-Company & Multi-Branch Logic

Multi-company separation was one of the most common use cases for ir.rule. In Odoo 20, review how your organization’s branch and legal entity separation will be represented under the new domain-based access rights framework.

Step 3: Review Programmatic sudo() and Domain Overrides

Inspect custom controllers and models where developers historically used sudo() to bypass restrictive record rules:

  • In Odoo 20, verify whether sudo() is still necessary or if proper access domain scoping can eliminate dangerous unauthenticated privileges.

Step 4: Prepare Migration Transformation Scripts

When migrating your database, prepare delta scripts to translate legacy ir.rule table rows into the new Odoo 20 unified access schema, ensuring that security boundaries remain intact from day one.


Summary: A Cleaner, Faster Security Engine

While retiring ir.rule requires updating custom addon code, the long-term payoff is substantial: faster database queries, less debugging friction, and a security model that is significantly easier to audit.


Audit Your Custom Odoo Security with Zenithonsoft

Navigating breaking ORM changes doesn’t have to stall your version upgrade: