Use the real Odoo 19 external API (JSON-2), and batch calls

The previous commit moved to /jsonrpc, which the v19 docs list as deprecated
alongside /xmlrpc — both go away in Odoo 22. The actual v19 external API is
JSON-2: POST /json/2/<model>/<method>, API key as a bearer token.

Three consequences, all of which change calling code:
  - no uid and no password; UID now comes from res.users/context_get
  - every argument is named, positional args do not exist, so x() becomes
    x(model, method, ids=None, **kwargs) and callers pass domain=/vals=/fields=
  - one call is one transaction and nothing chains, which is the mechanical
    reason state changes must go through a button method

Batching: no multi-call envelope exists, so batching means widening a call, not
bundling calls. Added get_or_create_many() (one search_read + one create for a
whole set) and put build_demo.py's product, customer, vehicle, invoice and
headroom loops through set-based calls.

test_odoo_connect.py checks the call shape and the batching offline, with a
faked urlopen — none of this can be exercised without a live instance otherwise.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
glenn schrooyen
2026-08-09 02:53:03 +02:00
co-authored by Claude Opus 5
parent 3fd2c51a3d
commit 93edf9296e
6 changed files with 295 additions and 129 deletions
+9 -3
View File
@@ -1,6 +1,6 @@
# Odoo 19 (Enterprise) — field names & footguns
Hard-won notes from a real v19 demo build over JSON-RPC. Check these before you waste a build cycle. Always confirm the major version first: `version()["server_version"]`.
Hard-won notes from a real v19 demo build. Check these before you waste a build cycle. Always confirm the major version first: `version()["version"]`.
## Renamed core fields (these break v17/v18 code)
| Concept | v17/18 | **v19** |
@@ -43,8 +43,14 @@ A custom module that sets `category_id` on `res.groups` or `groups_id` on `res.u
- `stock.picking.button_validate` can return a wizard dict (`stock.backorder.confirmation`, immediate transfer) instead of `True` — create the wizard from `res_model`/`context` and call its `process` button.
## RPC helper footgun
- `execute_kw(..., method, args, kwargs)`: a **context dict must go in kwargs**, never as a positional arg. `search(domain, {"active_test":False})` sends the dict as `offset``psycopg2 can't adapt type 'dict'`. Pass `context=` as a keyword (see `x()` in `odoo_connect.py`).
- v19's API is **JSON-2** (`POST /json/2/<model>/<method>`, `Authorization: bearer <api key>`). `/xmlrpc`, `/xmlrpc/2` and `/jsonrpc` still work but are deprecated and go away in Odoo 22.
- **No positional arguments exist.** ids go in the `ids` body key, everything else under the ORM parameter's own name: `domain`, `fields`, `vals` (write), `vals_list` (create), `context`, `limit`. Wrong name = `TypeError` from the server, not a silent misread — but guessing still costs a round trip. Every model+method signature for YOUR database is published at `<ODOO_URL>/doc`.
- The old execute_kw footgun (a context dict passed positionally landing in `offset``psycopg2 can't adapt type 'dict'`) is gone with the endpoint. If you see it, you are on legacy code.
- **One call, one transaction.** Committed on success, discarded on error, and nothing chains across calls. Prefer a single method that does the whole job (`search_read`, `action_confirm`) over a sequence you orchestrate client-side.
- API keys expire — three months maximum, one day if you generated it interactively. Check the expiry before demo day.
- Odoo Online exposes the external API on **Custom** plans only (not One App Free, not Standard).
- **Batch by widening calls, not by bundling them.** JSON-2 has no multi-call envelope, but every ORM method is set-based: `create(vals_list=[...])` takes a list and returns ids in order, `write`/`read`/`action_post` take an id list, `search_read` replaces search+read in one transaction. `get_or_create_many()` in `odoo_connect.py` does the batched get-or-create. A batched `create` fails as a unit — keep the loop where you need per-record error isolation.
## Module install over RPC
- `x("ir.module.module","button_immediate_install",[ids])`. Heavy — let it run minutes (the JSON-RPC connector uses a 600s timeout for this reason). On a failed XML-data load it rolls back cleanly (module stays uninstalled); fix and retry.
- `x("ir.module.module","button_immediate_install", ids)`. Heavy — let it run minutes (`x()` defaults to a 600s timeout for this reason). On a failed XML-data load it rolls back cleanly (module stays uninstalled); fix and retry.
- After deploying a custom module to git (Odoo.sh), wait for the rebuild, then `x("ir.module.module","update_list")` and search for it before installing.