← Back to home
Specialism · Odoo development

A platform we've been deep in,for nearly a decade.

Six platforms in production across Odoo 11 through 19. Custom modules running from eleven on the small end to ninety-five on the large. Mainline modules we've backported, mainline modules we've forward-ported, and a handful of mixins that get inherited everywhere we work.

Versions touched
11 → 19
Platforms in production
6
Largest module count
~95 (DISH BSS)
Status
Open for Q3 2026
Overview

Odoo is the spine of the workshop. When a client comes in with a business that's mostly shaped already — customers, contracts, accounting, CRM, HR, billing — Odoo is where we put the business logic and let the rest of the team do its actual work. What's left to build is the part that's actually theirs.

We've been writing custom Odoo since 2016, in production every year since. Six platforms in our hands today, between eleven and forty-five modules each on the small end, ninety-five on the large. Versions eleven through nineteen. We've migrated platforms forward (Odoo 8 → 16 via a custom ETL), backported features from later versions (Odoo 19 Peppol back to 16), and run two-environment deploy policies that haven't produced a destructive incident in four years.

The interesting parts of an Odoo engagement are rarely where the marketing decks promise. They sit in the seams — the place where a Belgian Peppol receiver and an Odoo 16 module disagree about which EAS to put on a partner; the place where an OCR-filled bill silently loses its 'importable' flag because a write happened without the right context; the place where `requests` gets monkey-patched by a Knock-SDK transitive and breaks payment-provider exceptions until you reverse the patch at import. We name those places, we fix them, and we write down what we learned so the next engineer doesn't have to relive it.

What this practice covers

The scope, without the shopping list.

Custom modules, deep01

Eleven to ninety-five modules per platform. Mixins, security rules, computed fields, OWL components, wizards, migration scripts versioned per module. The shape that survives upgrades.

Multi-version mastery02

From Odoo 11 (Wood, legacy stewardship) through Odoo 19 (Rebound, live in production). Comfortable backporting features from a newer version, forward-porting platforms across major versions, and reading the source when the docs run thin.

Integrations & EDI03

Peppol e-invoicing (Belgian compliance, backported 19 → 16), Vonage telephony (ported off Salesforce), RabbitMQ event bus, FastAPI gateway, UPS shipment tracking, Brevo contact sync, Knock + Slack notifications, Microsoft Calendar, Mapbox.

Architectural mixins04

Abstract Odoo models — flag.mixin, audit.model, knock.model, AvataqDocumentS3Sync — that every business module inherits. Pay the cost once; every new module collects the dividends.

Hybrid stacks05

Odoo + Django on one Postgres (Avataq, MySpecialist); Odoo + FastAPI gateway and RabbitMQ bus (DISH BSS); Odoo + AWS Lambdas + GCP Cloud Run (Procell). The right stack for each layer, with the seams owned.

Migration & rescue06

Odoo 8 → 16 via custom ETL, schema by schema (MySpecialist). Symfony rebuilt as a clean Django app alongside Odoo (also MySpecialist). Salesforce → Odoo, ninety-plus modules, multi-million-euro licence retired (DISH BSS).

Test automation07

Playwright suites driving live Odoo screens. A nightly regression at 2 AM UTC (Rebound) that tells us about failures before a user does. End-to-end pipelines covering quote → invoice → payment → feedback (MySpecialist).

Deploy policies that hold08

Two-environment workflows, staging-first, prod-on-explicit-ask. Module upgrades via XML-RPC. Four years of MySpecialist production without a destructive incident.

Signature engineering

A handful of solves we still remember.

01

Peppol backport, Odoo 19 → 16

account_peppol re-implemented against the older send wizard (account.invoice.send vs Odoo 19's account.move.send). XML regenerated before re-send because the stored attachment can drift. Belgian EAS overridden so partners actually post — core ships COUNTRY_EAS['BE'] = 9925, but receivers use 0208. Stuck invoices get a revalidate button instead of a silent archive.

02

Mainline patch fights

requests.models.complexjson reversed at module import (knockapi pulls simplejson, which monkey-patches it and breaks Odoo's payment exceptions). is_manually_modified honoured via context so OCR-filled bills stay importable. _import_retrieve_and_fill_partner_bank_details searches by sanitized_acc_number so 'BE55 7350' doesn't hit the unique constraint. Each mainline gotcha documented next to the fix.

03

OWL & view-layer surprises

Always-invisible domain fields, OWL list-button templates, is_manually_modified on account.move, t-foreach keyed by app.name, fieldDependencies defaulting to readonly: true. Twenty-odd Odoo 19 footguns catalogued in the module's CLAUDE.md so the next person doesn't relive them.

04

Architectural mixins, not copy-paste

Three abstract Odoo models — flag.mixin (any record flaggable with a reason), audit.model (HR-blind audit fields), knock.model (state transitions auto-fire Knock workflows). The 45 business modules in Rebound inherit them; new modules collect the dividends for free.

05

Hybrid stacks on one Postgres

Avataq's Django document explorer and Odoo share a single database. Sixty-second presigned S3 URLs keep large binaries out of memory. An AvataqDocumentS3Sync mixin makes a new uploader a one-line inherit. Odoo writes; Django indexes and reads; nobody crosses.

06

Migration-per-version, scripted

Every module follows 19.0.X.Y.Z versioning. When data needs to move, we bump the version and drop migrations/19.0.X.Y.Z/pre-migrate.py or post-migrate.py with a migrate(cr, version) function. The pipeline picks it up; the data moves with the code; nothing manual on production.

Where we've done it

The platforms that carry this work.

2022 · MYSPECIALIST
B2B marketplace · Odoo platform
MySpecialist

A Belgian SaaS marketplace that pairs customers with vetted independent specialists, across Belgium and Switzerland. Inherited in 2022 as a Symfony tier on Odoo 8 — rebuilt as clean Django on Odoo 16, and run by the same three engineers ever since.

Odoo 16PeppolBE · CH
Read the case study
2024 · REBOUND
Odoo 19 platform · live in production
Rebound Technologies

An Odoo 19 platform that runs the company end to end — sales, CRM, HR, projects, treatment, cashflow, GAAP audit. Forty-odd custom modules, a nightly Playwright regression on AWS, and the first project where the three of us hold distinct seats: Dan on architecture and DevOps, Dina on Odoo modules, Hasina on test automation.

Odoo 19PlaywrightAWS
Read the case study
2023 · PROCELL
Multi-cloud platform · Odoo 18 + AWS + GCP
Procell Therapies

A multi-stack platform for a US aesthetics and treatments company — an Odoo 18 ERP, a Next.js website on AWS, Lambdas for UPS shipment tracking and link verification, GCP Cloud Run services for OpenAI verification pipelines. We own the seams: the AWS Terraform estate, the serverless integrations, and the QA load testing.

AWS LambdaTerraformOdoo 18
Read the case study
2023 · DISH-BSS
Salesforce → Odoo migration
DISH Digital Solutions

A multi-million-euro Salesforce stack rebuilt on Odoo 16 for METRO's restaurant-tech arm — the commercial backend that powers DISH for tens of thousands of independent restaurants across Europe. We led the feature recreation: FastAPI gateway, RabbitMQ event bus, Vonage telephony, sales flow and product catalogue.

Odoo 16FastAPIRabbitMQ
Read the case study
2023 · AVATAQ
Scholarship platform · Odoo 16 + Django
Avataq

A hybrid Odoo 16 + Django platform managing Indigenous student scholarships, enrollment, billing and document handling. Two stacks on one Postgres database, with documents in S3 behind short-lived presigned download URLs.

Odoo 16DjangoAWS S3
Read the case study
2021 · WOOD
Odoo 11 platform · metadata configurator
Wood

A French Odoo 11 platform for Wood, a pool-equipment supplier — built around a metadata-driven product configurator that lets customers spec a pool through a dynamic questionnaire whose entire shape is rows in the database.

Odoo 11ConfiguratorLong-running
Read the case study

Have a project that deserves this kind of care?

Start a conversation