What Changed in Odoo 18
Odoo 18 introduced several significant changes that affect custom modules. Understanding these changes before migration prevents most upgrade failures.
Breaking Change 1: <tree> → <list>
The most visible change: all <tree> tags in XML views must be renamed to <list>.
<!-- Odoo 17 -->
<tree decoration-danger="state == 'overdue'">
<field name="name"/>
<field name="state"/>
</tree>
<!-- Odoo 18 -->
<list decoration-danger="state == 'overdue'">
<field name="name"/>
<field name="state"/>
</list>Migration: Find and replace all <tree with <list and </tree> with </list> in your XML view files. Also update xpath expressions that reference tree.
# Command to find all affected files:
grep -rl '<tree' --include='*.xml' your_module/
# Command to replace:
find your_module/ -name '*.xml' -exec sed -i 's/<tree/<list/g; s/<\/tree/<\/list/g' {} +Breaking Change 2: Hoot Testing Framework
Odoo 18 replaces QUnit with Hoot for JavaScript testing. If you have JS tests:
// Odoo 17 (QUnit)
QUnit.module('My Module', function () {
QUnit.test('my test', async function (assert) {
assert.ok(true);
});
});
// Odoo 18 (Hoot)
import { describe, test, expect } from '@odoo/hoot';
describe('My Module', () => {
test('my test', async () => {
expect(true).toBe(true);
});
});Python tests (TransactionCase, HttpCase) are unchanged.
Breaking Change 3: @api.readonly
Odoo 18 introduces the @api.readonly decorator for methods that only read data. This is used for read-replica routing — methods decorated with @api.readonly can be routed to a read-only database replica.
@api.readonly
def get_statistics(self):
"""This method only reads data — safe for read replica."""
return self.env['sale.order'].read_group(...)Not required for migration, but recommended for performance optimization if you use read replicas.
Other Changes
Bootstrap 5.3.3
CSS framework updated. If your custom templates use Bootstrap classes, verify compatibility. Most changes are minor.
aggregator Replaces group_operator
# Odoo 17
amount = fields.Float(group_operator='sum')
# Odoo 18
amount = fields.Float(aggregator='sum')Asset Bundle Changes
Some asset bundles were reorganized. If you extend web assets, verify your manifest entries.
Step-by-Step Migration Process
- Backup — Full database + filestore backup before any changes
- Create staging — Deploy a staging Odoo 18 instance on DeployMonkey
- Update XML views — Replace all
<tree>with<list> - Update JS tests — Migrate from QUnit to Hoot (if applicable)
- Update group_operator — Replace with
aggregator - Test on staging — Install modules, run tests, verify views
- Fix issues — Address any remaining compatibility problems
- Deploy to production — After successful staging validation
AI-Assisted Migration
Claude Code can automate most of the migration work:
# Prompt:
"Migrate my Odoo module from v17 to v18.
Change all <tree> to <list>,
replace group_operator with aggregator,
and update any QUnit tests to Hoot."Claude Code reads all files, identifies the changes needed, and applies them across the entire module. A migration that takes a developer 2-3 days takes Claude Code 30 minutes.
Getting Started
Deploy an Odoo 18 staging instance on DeployMonkey (free plan). Test your module migration there before applying to production. The AI agent helps diagnose any upgrade issues.