Status: draft for the owner to review. Written 2026-09-29 from branch
claude/hub-edges-odoo-exit.
Every count below comes from grep over this repo, with tests excluded unless the row says otherwise.
Anything marked Assumption is about Odoo 18 or third-party modules and is not verified from this repo.Why and target
- We run the backend addon
nu_restaurant_pos(manifest version12.0.1.35.0) on Odoo 12. - Odoo 12 gets no security fixes. Assumption: Odoo supports only the three latest major versions, so Odoo 12 lost support around late 2021.
- CI tests the addon on Python 3.8 (
.github/workflows/backend.yml:17). Assumption: Odoo 18 needs Python 3.10 or newer. - Target: Odoo 18, Community or Enterprise (see open questions).
Strategy
- Keep the
/pos-api/v1/*contract as it is. We own it. The POS app and the hub talk to it, not to Odoo models. If the routes, request shapes and response shapes stay the same, the app and the hub do not need a release for the migration. - Port the addon behind that contract. Only the Python code that turns API calls into Odoo records changes.
- The Odoo POS JavaScript rewrite does not affect us. Odoo moved its POS screen to OWL. Our POS screen is our own React app, so we skip the largest part of a normal POS upgrade. The addon has one small backend widget (
static/src/js/pos_colorpicker.js) to port.
Who calls Odoo today
The addon registers 64 routes: 61 in
controllers/api_v1.py (55 type="json", 6 type="http") and 3 in controllers/api_hub.py.
Blockers and risks (ranked)
- Tax localization for Odoo 18. The addon depends on
dgii,dgii_posanddgii_encf(NCF and e-CF). They are not in this repo. It calls their models directly:pos.order.ncf.seq.get_ncf(controllers/api_orders.py:465),ncf.sequence(models/pos_bootstrap_data.py:190), and it works around their_process_orderandcreate_picking_jobbehaviour (models/pos_order.py:280,:303). Open question: does the vendor ship these for Odoo 18? If not, we must choose another Dominican localization and rewrite the NCF flow. This decides whether the project is possible on our timeline. The same applies tomarcos_stock,marcos_pos_ui,pos_backend_syncandsimple_menuin__manifest__.py. - Payments model change. Odoo 12 stores POS payments as bank statement lines (
statement_ids). Assumption: Odoo 13+ usespos.payment, and session cash control no longer usesaccount.bank.statementcashboxes. We usestatement_ids64 times in 5 Python files, and build cashboxes by hand for session open and close (controllers/api.py:366, 39 hits in that file). Payments, credit, reservation advances and session close all need a rewrite. - Invoices become
account.move. Assumption:account.invoicemerged intoaccount.movein 13.0. We useaccount.invoice17 times in 6 files, including the credit-note flow (account.invoice.refund,models/pos_order.py:1035) and the picking-sale invoice wizard (wizards/picking_sale_invoice_wizard.py:152). NCF numbers live on invoices, so this is tied to risk 1. - Historic data. Two paths:
- OpenUpgrade 12 → 13 → 14 → 15 → 16 → 17 → 18. Six hops. Every non-core module needs a migration script per hop. Assumption: OpenUpgrade coverage for
point_of_saleand the Dominican modules at each hop is unknown. - Fresh Odoo 18 database with master data (products, partners, taxes, floors) and opening balances. Old orders stay read-only in the Odoo 12 database. Much less work. Needs the owner to accept that DGII reports for past periods come from the old system.
- OpenUpgrade 12 → 13 → 14 → 15 → 16 → 17 → 18. Six hops. Every non-core module needs a migration script per hop. Assumption: OpenUpgrade coverage for
- Hub transport. The hub calls Odoo’s generic
execute_kwwith a password. Assumption:/jsonrpcand/xmlrpc/2still work in 18 but are deprecated in later versions. The floor-plan read touches corerestaurant.table.name. Assumption: that field changed in 18. Fix: move both behind our own/pos-hub/*routes before the port.
Inventory of Odoo 12-only code
Scope:nu_restaurant_pos/ (about 19,500 lines of non-test Python, 27 test files with about 8,700 lines). Constructs that are not present are left out (no @api.one, track_visibility or oldname).
Every row above sits behind the
/pos-api/v1 contract, so none of it is visible to the app.
Schedule
Assumptions: one developer full time on this; the tax localization exists for 18; start on 2026-10-05. Ranges are developer-weeks (dw) of work, then calendar time.
Total: about 16–35 dw, or 4–8 months for one developer. The low end needs a fresh database. The high end is OpenUpgrade. A no-go at Phase 1 stops the plan until the localization question is solved.
Phase 0: what we can do in Odoo 12 now
Odoo 12 still needs@api.multi, account.invoice, statement_ids and attrs, so we cannot remove them yet. We can make the port smaller and safer:
- Contract snapshot tests for
/pos-api/v1. Record the request and response shape of each route (bootstrap, orders create/update, payments, sessions open/close, credit note, reserve NCF). Run the same tests on 18 in Phase 2. This is our definition of “done”. - Small helpers like
http_compat.py: put payment reads/writes (statement_ids,add_payment, cashbox) inlib/payments_compat.pyand invoice access inlib/invoice_compat.py. The port then changes a few functions, not 64 call sites. - Wrap
request.uid/request._envandodoo.registry()in one helper each. - Move the hub off core models. Add
/pos-hub/floor-planand/pos-hub/order-snapshotroutes in the addon, and point the hub at them. Delete the deadhttpadapter mode. - Freeze new direct dependencies on
dgii_*andmarcos_*models. Add them only behind helpers.
Open questions for the owner
- Localization vendor. Who maintains
dgii,dgii_pos,dgii_encfand themarcos_*modules? Do they have Odoo 18 versions, and at what cost? If not, which Dominican localization do we use? - Historic data. Do we need past POS orders and invoices inside the new Odoo, or is a read-only Odoo 12 archive enough for audits and DGII reports?
- Edition and hosting. Community or Enterprise? Self-hosted or Odoo.sh? This affects OpenUpgrade and the Python/PostgreSQL versions.
- Freeze window. When can we stop changes to the Odoo 12 addon, and which low-traffic weeks allow cutover?
- Version choice. Assumption: Odoo 19 is already out, so 18 has a shorter support window than a newer version. Confirm 18 is still the target.
- Other addons.
nu_recipe_management(12.0.1.2.0) is also an Odoo 12 addon in this repo. Is it in scope?
Done already
lib/http_compat.pyis the only reader ofrequest.jsonrequest(5 reads, all in that file). The port changes_raw_json_body()and leaves the seven controllers that use it alone.lib/order_line_codec.pyis the single wire → Odoo line mapper, so order-line field changes happen in one place.- The app already talks only to
/pos-api/v1/*. No app change is needed if the contract holds.