August 31, 2026 / Dipak Vyas
Upgrading an ERP is never “just” a version bump — especially when your business runs on years of custom modules and configuration. We recently completed a full migration of our own Odoo environment from version 18 to version 19, covering both the database and our custom codebase, end to end on Odoo.sh.
This post is for two kinds of readers. If you’re evaluating whether it’s time to move your business to Odoo 19, you’ll get a clear picture of what changes and what to expect. If you’re an Odoo developer planning your own migration, we’ve included the specific technical issues we hit — including one change in Odoo 19 that broke a heavily customized module and forced us to rebuild it from the ground up.
Odoo 19 touches nearly every major module, not just the UI. A few highlights that made the upgrade worthwhile:
AI woven into daily workflows. CRM lead assignment is now rules-based and AI-assisted, with leads auto-generated from business card scans and live chat conversations.
A reworked Inventory and Barcode experience — batch transfers, multi-step flows, and cycle counts during live operations. Website and eCommerce upgrades — SEO-optimized product pages, tighter Google Merchant Center integration, and new shop page layouts.
Stronger accounting tools — simplified OCR-based bank reconciliation, WhatsApp payment reminders, and expanded ISO20022 support.
A new ESG/sustainability layer — built-in CO₂ footprint reporting aligned with the GHG Protocol and Bilan Carbone standards.
Odoo’s release cadence means staying multiple versions behind eventually costs you security patches and vendor support. We wanted to move on our own schedule rather than under pressure — which is exactly what we did.
Here’s the actual sequence we followed, running on Odoo.sh:
This was the most disruptive change we encountered, and it’s worth calling out on its own.
In Odoo 18, the Expense Report (hr.expense.sheet) was a core model — expenses were grouped into a report, and that report went through submission and approval. We had built substantial customizations on top of this model to match our internal approval workflow.
In Odoo 19, Odoo removed the Expense Report model entirely. The stated reasoning is that most expense reports in practice only ever contained a single expense, so Odoo simplified the flow: expenses are now submitted, approved, and posted individually (or in bulk from the list view), with hr.expense.sheet’s remaining logic folded directly into hr.expense. If no expense manager is configured, expenses can even auto-approve on submission.
That’s a reasonable simplification for a default install. It is not compatible with a heavily customized expense workflow built around the old report-based model. When we migrated, our customizations had nothing left to attach to — the model they extended no longer existed.
How we solved it: 1. We first rebuilt the standard Odoo 18-style Expense Report behavior as a module on top of Odoo 19’s base Expenses app — effectively reintroducing the reporting layer Odoo had removed. 2. Once that base layer was working and stable, we re applied our original customizations on top of it.
This let us keep the approval workflow our business actually depends on, while still running on the current Odoo 19 core. It was easily the most time-consuming single piece of the entire migration, and it’s the kind of change that a purely “port the code over” approach to migration will not catch until testing — the module doesn’t fail to install, it just has nothing left to extend.
If you’re planning your own 18 → 19 migration and have customized Expenses in any way, budget real time for this specifically.
A few smaller but still meaningful changes we ran into along the way:
Fields removed outright. Beyond Expenses, we hit cases where fields we relied on in custom code were removed in Odoo 19 rather than renamed, meaning a simple mapping wasn’t enough — the underlying logic had to be re-thought, not just re-pointed. XML ID changes in base modules. When Odoo renames or removes an XML ID, anything referencing it via env.ref() breaks — sometimes silently, sometimes at install time. Worth grepping your codebase for env.ref( before you start.
After working through the branch conversion, the module rewrites, the migration scripts, the expense report rebuild, and a fair number of Odoo.sh build cycles, we’re now running fully on Odoo 19 with our custom workflows intact — including the expense approval process our business actually needs, not just the simplified default.
The overall lesson: a version upgrade like this is rarely about the database migration itself. The database step is mechanical and well supported. The real work — and the real risk — is in the custom code that sits on top of it, especially where Odoo has removed or restructured a model your business depended on.
If you’re running custom modules on Odoo 18 — particularly around Expenses, or anything built on a model that Odoo has since restructured — it’s worth having that code audited before you commit to an upgrade timeline. We went through this exact process recently and know where the sharp edges are.
Get in touch with our team if you’d like a second set of eyes on your migration plan.

Dipak is an experienced industry veteran and trusted Odoo ERP Consultant, serving as the co-founder of Eternal Web Pvt Ltd. He specializes in delivering tailored ERP solutions that drive digital transformation for SMEs and enterprises. With extensive expertise in Odoo customization, integration, and deployment, Dipak has consistently helped businesses streamline operations, enhance productivity, and achieve sustainable growth.
Have queries about your project idea or concept? Please drop in your project details to discuss with our AWS Global Cloud Infrastructure service specialists and consultants.