# Odoo custom module development, from a plain-English brief to an addon you can install

Canonical: https://erpfly.com/odoo-module-development/
Last updated: September 15, 2026

Sooner or later every Odoo company needs something the Apps store doesn't sell, or sells for a version two releases behind yours. erpfly writes that addon from your description, with the manifest, models, views and access rules a reviewer expects, and hands it over as a Git branch.

### Deliverables

- **A standard addon layout.** __manifest__.py with a real version string and a complete depends list, then models, views, security and data folders. Nothing an experienced Odoo developer would need to rearrange.
- **Models that follow the ORM.** _name for new models, _inherit for extending existing ones, stored computed fields with @api.depends, constraints, and mail.thread where a chatter earns its place.
- **Views written for your version.** Form, list, kanban and search views using inline invisible and readonly expressions, and list instead of tree on Odoo 18 and 19.
- **Access rules that aren't wide open.** ir.model.access.csv lines per group, plus record rules for multi-company databases, instead of giving every internal user full rights on every model.
- **Tests you can run in CI.** TransactionCase tests for the business rules, tagged so they run with --test-tags on Odoo.sh builds or your own pipeline.

### Process

1. **Tell us version and edition.** Odoo 17, 18 or 19, Community or Enterprise, Odoo.sh or self-hosted. These decide which modules you can depend on and what the XML is allowed to look like.
2. **Describe the process.** Write it the way you'd explain it to a new production planner. erpfly asks follow-up questions where the rules are ambiguous, like who can mark a gauge as passed.
3. **Try it on a throwaway database.** The addon is installed on a fresh database with the same apps you run. Click through, create test records and ask for changes in the chat.
4. **Push to a branch.** The code lands in your GitHub repository. On Odoo.sh a development branch builds automatically, and you promote it to staging and production when you're happy.

### What's inside an Odoo addon

An Odoo module is a Python package with a manifest. That's it. The rest is convention, and following the convention is what makes a module easy for the next developer to pick up. Here's the layout erpfly generates for the calibration prompt at the top of this page:

```text
gauge_calibration/
├── __init__.py
├── __manifest__.py
├── models/
│   ├── __init__.py
│   ├── calibration_gauge.py
│   └── mrp_workorder.py        # _inherit, blocks overdue gauges
├── security/
│   ├── security.xml            # groups and record rules
│   └── ir.model.access.csv
├── data/
│   └── ir_cron.xml             # daily due-date check
├── views/
│   ├── calibration_gauge_views.xml
│   └── menus.xml
└── tests/
    └── test_calibration.py
```

A few details decide whether an addon installs cleanly or throws errors on a real database. The `data` list in `__manifest__.py` is loaded in order, so security groups come before the CSV that references them, and actions come before the menus that open them. Every new model gets a line in `ir.model.access.csv`. Without one, recent Odoo versions warn at install and ordinary users can't open the model at all, which is a confusing first day for everyone. And `depends` lists what the code really uses, `mrp` and `mail` here, not every app that happened to be installed.

If you'd rather build one by hand first, our walkthrough on [creating a custom Odoo module step by step](https://erpfly.com/blog/create-custom-module-odoo/) covers the same structure line by line.

### One codebase per Odoo version

Odoo changes enough between major versions that "compatible with 17, 18 and 19" usually means three branches. The differences that bite most often:

- **Odoo 17** removed `attrs` and `states` from views. Visibility is now a Python-like expression, as in `invisible="state != 'draft'"`.
- **Odoo 17** also replaced `name_get` with `_compute_display_name`, which you'll see at the bottom of the model on this page.
- **Odoo 18** renamed `<tree>` views to `<list>`, in view definitions and in action view modes alike. Anything still using the old name has to be updated.
- The web client is built on **OWL** throughout, so any custom widget from the old widget system has to be rewritten, not ported.

erpfly asks for your version before writing a line, and the manifest version string follows the usual `18.0.1.0.0` pattern so the major version is visible at a glance.

### Community, Enterprise and what you can depend on

This decision shapes the module more than people expect. Enterprise is Community plus a set of extra addons. If your module inherits from an Enterprise model, say `quality.check` or anything in Helpdesk, it only installs where those addons exist.

On Community, the same feature is often built on top of standard stock and manufacturing models, or on modules from the Odoo Community Association. It starts from a different place, and it isn't a worse module for it. What we won't do is copy Enterprise code into a Community addon. It's licensed differently, and that shortcut ends in a very awkward email.

### Deploying on Odoo.sh or your own server

**Odoo.sh** deploys from GitHub branches. Push the addon to a development branch and Odoo.sh builds it on a fresh database and runs the tests. Merge into a staging branch and it runs against a neutralized copy of production. Merge into production and the module updates on the live database. Odoo.sh needs an Enterprise subscription, which is worth knowing before you plan around it.

**Self-hosted** is the familiar route: add the addon's folder to `addons_path`, restart, then install from Apps or with `-i gauge_calibration`. Updates run with `-u`, and the generated tests run with `--test-enable` and a test tag for the module, so a broken rule shows up before users find it. Test on a copy of production first. Most failed installs aren't bugs in the code, they're existing records that don't satisfy a new constraint.

### Example: calibration in a machine shop

Picture a shop making precision parts, with a couple of hundred gauges, micrometers and bore gauges tracked in a spreadsheet that one quality engineer updates when she remembers. An auditor asks which gauge measured a batch from last March and the spreadsheet can't answer.

Before writing anything, erpfly asks two questions a good consultant would. Can a gauge be used while its calibration is at an outside lab? And does overdue mean blocked immediately, or after a grace period? The answers change the model, so they're worth the thirty seconds.

The module from the prompt fixes the part that matters. Gauges get a record with a stored next due date. A daily `ir.cron` marks them due and schedules an activity for the quality team. Work orders `_inherit` a check that refuses to start if a linked gauge is overdue. The certificate PDF sits on the gauge record, and the chatter keeps the history the auditor was asking for. If your quality process runs wider than calibration, look at the [quality management module](https://erpfly.com/modules/quality-management/) and how it ties into [manufacturing](https://erpfly.com/modules/manufacturing/).

### When a new module is the wrong answer

- **You only need a few fields on an existing screen.** That's inheritance on an existing model, and our [Odoo customization](https://erpfly.com/odoo-customization/) page covers it. A whole addon for three fields is still fine, but don't call it a module project.
- **A maintained OCA module already does it.** Install it, read the code, contribute a fix if needed. Cheaper than owning a copy.
- **You're on Odoo Online and don't want to move.** You can't install custom Python there. Decide on Odoo.sh or self-hosting first, or read our view on [Studio versus a custom module](https://erpfly.com/blog/odoo-studio-vs-custom-module/).

And if the choice of platform is still open, our [ERPNext vs Odoo comparison](https://erpfly.com/compare/erpnext-vs-odoo/) is blunt about where each one fits.

### FAQ

### Which Odoo versions do you generate code for?

Odoo 17, 18 and 19. The addon is written for the version you pick, because the view syntax and a few ORM methods changed between them. Odoo 16 left standard support in September 2025, and Odoo's own schedule ends standard support for 17 in September 2026. If you're on 16 or older, we'd rather plan the upgrade with you than write new code for it, and if you're on 17, plan the move to 18 or 19 soon.

### Will the module work on Odoo Online?

No, and nobody's module will. Odoo Online doesn't let you install your own Python addons. You need Odoo.sh or your own server. On Odoo Online the only option is Studio, which covers fields and simple views but not real business logic.

### Can the addon depend on Enterprise apps?

Only if you have an Enterprise subscription. An addon that lists quality_control or helpdesk in its depends won't install on Community. erpfly checks your edition first and, on Community, builds on standard Community modules or OCA ones instead.

### Who maintains the module when Odoo releases a new version?

Odoo's upgrade service migrates the standard database for Enterprise customers. Custom addons are only covered if you pay Odoo for maintenance of customizations; otherwise porting them is on you. erpfly can regenerate or port the addon to the next version, and the tests show you what changed behaviour.

### Do we own the code?

Yes. It's a normal Odoo addon in your repository, licensed however you choose (we default to LGPL-3 to match Community). Stop using erpfly and the module keeps working.

### Sources

- [Branches, Odoo.sh documentation](https://www.odoo.com/documentation/19.0/administration/odoo_sh/getting_started/branches.html)
- [Odoo pricing, Odoo S.A.](https://www.odoo.com/pricing)
- [Odoo editions comparison, Odoo S.A.](https://www.odoo.com/page/editions)
- [Chapter 6: Basic Views, Odoo 18 developer tutorial](https://www.odoo.com/documentation/18.0/developer/tutorials/server_framework_101/06_basicviews.html)
- [Upgrade, Odoo 19 documentation](https://www.odoo.com/documentation/19.0/administration/upgrade.html)