Skip to main content
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 version 12.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

  1. 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.
  2. Port the addon behind that contract. Only the Python code that turns API calls into Odoo records changes.
  3. 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)

  1. Tax localization for Odoo 18. The addon depends on dgii, dgii_pos and dgii_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_order and create_picking_job behaviour (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 to marcos_stock, marcos_pos_ui, pos_backend_sync and simple_menu in __manifest__.py.
  2. Payments model change. Odoo 12 stores POS payments as bank statement lines (statement_ids). Assumption: Odoo 13+ uses pos.payment, and session cash control no longer uses account.bank.statement cashboxes. We use statement_ids 64 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.
  3. Invoices become account.move. Assumption: account.invoice merged into account.move in 13.0. We use account.invoice 17 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.
  4. 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_sale and 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.
  5. Hub transport. The hub calls Odoo’s generic execute_kw with a password. Assumption: /jsonrpc and /xmlrpc/2 still work in 18 but are deprecated in later versions. The floor-plan read touches core restaurant.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) in lib/payments_compat.py and invoice access in lib/invoice_compat.py. The port then changes a few functions, not 64 call sites.
  • Wrap request.uid / request._env and odoo.registry() in one helper each.
  • Move the hub off core models. Add /pos-hub/floor-plan and /pos-hub/order-snapshot routes in the addon, and point the hub at them. Delete the dead http adapter mode.
  • Freeze new direct dependencies on dgii_* and marcos_* models. Add them only behind helpers.

Open questions for the owner

  1. Localization vendor. Who maintains dgii, dgii_pos, dgii_encf and the marcos_* modules? Do they have Odoo 18 versions, and at what cost? If not, which Dominican localization do we use?
  2. 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?
  3. Edition and hosting. Community or Enterprise? Self-hosted or Odoo.sh? This affects OpenUpgrade and the Python/PostgreSQL versions.
  4. Freeze window. When can we stop changes to the Odoo 12 addon, and which low-traffic weeks allow cutover?
  5. 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.
  6. 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.py is the only reader of request.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.py is 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.