Skip to content

[FIX] kw_api_custom_endpoint: apply the endpoint domain when change() resolves by id - #4

Open
brmk wants to merge 1 commit into
kitworks-systems:19.0from
Uplab:fix/change-honours-endpoint-domain
Open

[FIX] kw_api_custom_endpoint: apply the endpoint domain when change() resolves by id#4
brmk wants to merge 1 commit into
kitworks-systems:19.0from
Uplab:fix/change-honours-endpoint-domain

Conversation

@brmk

@brmk brmk commented Sep 5, 2026

Copy link
Copy Markdown

Fixes #2.

The configured domain is applied on the LIST branch and ignored by the by-id branches.
change() resolved its target with a bare
search([(self.model_id_field, '=', obj_id)], limit=1), and Odoo only skips the implicit
active = True when a domain mentions active. An archived record was therefore never
found, write() on the empty recordset was a silent no-op, and data_response serialised
it as {"content": [], "code": "200"} — the caller told that a write succeeded when nothing
had been written.

So an endpoint configured with [("active", "in", [True, False])], which declares archived
records to be in scope, still could not write to them. The same gap let a POST update a
record the endpoint's domain was meant to exclude.

change() now resolves through a small api_get_obj_domain() helper that narrows the id
lookup by the endpoint's own domain — the same thing the list branch has always done.
No context is touched: a domain mentioning active makes Odoo skip active_test on its
own.

Behaviour change

An id that resolves to nothing now answers 400: Wrong ID — the shape response() already
uses — instead of a 200 claiming a write happened. For an endpoint with no domain, an
archived id moves from a silent fake success to that honest 400.

Verified on Odoo 19.0

Against a product.template endpoint with name changeable:

endpoint domain target before after
[("active","in",[True,False])] active 200, echoed, written unchanged
[("active","in",[True,False])] archived 200, content: [], not written 200, echoed, written
none active 200, echoed, written unchanged
none archived 200, content: [], not written 400 Wrong ID
any unknown id 200, content: [] 400 Wrong ID

A note for reviewers

Our first attempt at this fixed it with

super(CustomEndpoint, self.with_context(active_test=False)).change(...)

That put active_test=False into the whole request environment, and kw_api's translation
helper does self.env['res.lang'].sudo().search([]). res.lang is archivable, so it began
returning inactive languages, and reading a translation for a language Odoo has not loaded
raised KeyError: '<model>.<field>' from the ORM field cache — after a successful write,
inside the response serialiser. Every write broke, not just archived ones. It reached our
production and was reverted 23 minutes later.

Applying the domain avoids that entire class of problem, which is why this PR takes that
route rather than the context one.

Left alone deliberately

response() (GET by id) and delete() share the same bare search. I did not touch them:
response() at least answers 400 honestly today, and widening what delete() can reach
seems like a maintainer's call rather than a bug fix. Both are noted in #2.

… resolves by id

The configured domain was applied on the list branch and ignored by the by-id
branches. change() resolved its target with a bare

    m.search([(self.model_id_field, '=', obj_id)], limit=1)

and Odoo only skips the implicit active = True when a domain mentions active,
which that one does not. An archived record was therefore never found, write()
on the empty recordset was a silent no-op, and data_response serialised it as

    {"content": [], "code": "200"}

so the caller was told a write succeeded that never happened. An endpoint
configured with [("active", "in", [True, False])] - which declares archived
records to be in scope - still could not write to them. The same gap let a POST
update a record the endpoint's domain was meant to exclude.

change() now resolves through api_get_obj_domain(), which narrows the id lookup
by the endpoint's own domain. No context is touched: a domain that mentions
active makes Odoo skip active_test by itself.

Behaviour change worth calling out: an id that resolves to nothing now answers
400: Wrong ID - the same shape response() already uses - instead of a 200 that
claims a write occurred.

Verified on Odoo 19.0 against a product.template endpoint:

  domain [("active","in",[True,False])]  active   -> 200, record echoed, written
  domain [("active","in",[True,False])]  archived -> 200, record echoed, written
                                                     (was: 200, content: [], no write)
  no domain                              active   -> 200, record echoed, written
                                                     (unchanged)
  no domain                              archived -> 400 Wrong ID
                                                     (was: 200, content: [], no write)
  any                                    unknown id -> 400 Wrong ID

Closes kitworks-systems#2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant