[18.0][IMP] sale_order_type: flexible pricelist precedence#3
Open
dnplkndll wants to merge 5 commits into
Open
Conversation
…ner/none) Three precedence modes configurable per `sale.order.type`, exposed in developer mode only: - `type_first` (default — preserves legacy behavior) - `partner_first` — partner's value wins; type fills gaps - `partner_only` — type's propagated fields are ignored A `_sot_resolve` helper consolidates the resolution rule for the seven fields the module propagates (pricelist, payment term, warehouse, picking_policy, incoterm, route, invoice journal). Existing computes collapse to a one-line call into the helper. Adds sibling `effective_*` related fields on `res.partner` for full symmetry with the `effective_pricelist_id` introduced in PR OCA#4328. Adapts the partner-form alert to the active mode and adds a subtle SO form caption when precedence is non-default. Manual edits to SO fields are preserved across recomputes via trigger narrowing — re-firing only on `type_id` / `partner_id` change (matches the lef-adhoc OCA#4273 intent). Tests: new `TestPrecedence` class — six unit tests on `_sot_resolve` plus integration tests across the seven fields and `effective_*` related fields. Existing 24 tests unaffected. No migration script needed: the new `precedence` field defaults to `type_first`, preserving behavior on upgrade. A company-level `sale_order_type_default_precedence` is exposed in Settings as the default-for-new-types convenience knob.
…nance (Huly OCA-23) `_sot_resolve` now accepts an optional `fname` argument; when the companion module `web_field_provenance` is installed, manual user edits to that field win over the precedence rules regardless of mode. Falls through silently when `_user_set` is not on the record, so this module continues to work standalone. Threads field names through the seven existing call sites (warehouse_id, picking_policy, payment_term_id, pricelist_id, incoterm, route_id). The integration is one `hasattr` check inside the helper — zero behavior change without OCA-23 installed. Documents the pattern in DESCRIPTION.md so reviewers see the upstream roadmap and the standalone vs. composed semantics.
Author
Follow-up: forward-compat hooks for
|
5 tasks
…provenance is loaded
Wires the cascade-attribution call into every compute that propagates
a type field onto the SO. New helper `_sot_stamp_cascade_if_available`
on both `sale.order` and `sale.order.line`:
- no-op when `web_field_provenance` (Huly OCA-23) is not installed
(soft dependency via `hasattr(self, "_stamp_provenance")`)
- no-op when the resolved value did NOT come from the type
- otherwise calls `_stamp_provenance([fname], source="r",
by="sot.cascade", rule="Sale Order Type cascade")` so the OWL
badge shows the green-cog icon and the tooltip reads
"Set by Sale Order Type cascade".
Wired through six cascade sites: warehouse_id, picking_policy,
payment_term_id, pricelist_id, incoterm on sale.order; route_id on
sale.order.line. `_prepare_invoice` (journal_id) is intentionally not
stamped here because the target record is the account.move, not the
sale.order.
Soft-dependency guard means the wiring is safe to ship without
web_field_provenance installed. Integration tests follow in the next
commit (commit-then-test workflow; will squash for clean review).
dnplkndll
added a commit
to ledoent/web
that referenced
this pull request
May 17, 2026
Two integration bugs surfaced when wiring the first consumer (sale_order_type cascade via ledoent/sale-workflow#3): * **Onchange diff leak**: stamping `_provenance` via `write()` during a compute leaked the field into Odoo's onchange diff. Form views that don't declare `_provenance` (most do not — the badge consumes it via `web_read` only) raised `KeyError: '_provenance'`. Switched `_stamp_provenance_keys` to raw SQL + `invalidate_recordset` so the stamp does not surface as an in-flight ORM change. * **NewId records cannot be SQL-updated**: skip records whose `id` is not yet a database integer. This affects two paths: 1. Form / onchange — stamping is the wrong thing anyway, see above. 2. `create()` precompute — there is no DB row to UPDATE. Both paths fall back to the cascade attribution being applied on the next persistent-record write that touches the field, which matches typical user flows (the cascade re-fires when a user changes `type_id` on a saved SO). Documented in the docstring; a deferred-flush mechanism is the right long-term fix and stays on the ROADMAP. Existing 23 web_field_provenance tests still pass; the sibling sale-workflow PR's 60-test suite goes 60/60 once the SO cascade is exercised on persistent records (separate commit there).
…r() in cascade computes
Discovered while wiring cascade attribution into web_field_provenance:
the original _sot_resolve short-circuit checked _user_set(fname) and
returned current_value to preserve manual edits. But by that point
super() had already overwritten current_value with the partner
default, so the "preserve" returned an empty/wrong value.
The correct pattern is the one documented in web_field_provenance's
_user_set docstring: filter user-anchored records OUT of super()
before it runs.
This commit:
* Adds SaleOrder._sot_preserve_user_set(fname) and a line-level
mirror SaleOrderLine._sot_preserve_user_set(fname). Both return an
empty recordset when web_field_provenance is not installed (soft
dependency, hasattr guard).
* Each cascade compute now does:
preserved = self._sot_preserve_user_set("<field>")
target = self - preserved
res = super(<Class>, target)._compute_<field>()
for ... in target.filtered("type_id"):
... resolve + stamp via cascade
* _sot_resolve no longer consults _user_set (was misleading — the
check happens at the compute-filter level now). The fname
parameter is kept for caller-side parity.
Wired through five SO computes (warehouse_id, picking_policy,
payment_term_id, pricelist_id, incoterm) and the SaleOrderLine
route_id compute. _prepare_invoice unchanged.
Tests in the next commit. With both installed, the full suite goes
60/60 (TestPrecedence + TestPrecedenceWithProvenance +
TestPrecedenceMultiCompany + TestSaleOrderType + provenance suite).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a per-type precedence mode to
sale.order.typeso different types can resolve conflicts between their own propagated fields and the partner's defaults differently:type_first(default — preserves legacy)partner_firstpartner_onlyA
_sot_resolvehelper consolidates the resolution across all seven propagated fields (pricelist, payment term, warehouse, picking_policy, incoterm, route, invoice journal). The five SO_compute_*overrides collapse to one-line calls into the helper.Manual edits to SO fields are preserved across recomputes via trigger narrowing — re-firing only on
type_idorpartner_idchange, matching the intent of lef-adhoc's #4273.Why
The current module hard-wires "type wins" — partner pricelists, payment terms etc. are silently overwritten whenever a type carries a value. Community feedback (issue #3690, PR OCA#3019, PR OCA#4273) signals demand for configurable precedence. PR OCA#4328 (currently merged at branch HEAD) introduced an alert for the pricelist case but didn't change the rule. This PR makes the rule itself configurable while keeping legacy behavior as the default.
The precedence field on `sale.order.type` is only visible in developer mode (
groups=\"base.group_no_one\") so normal sales-ops users don't see it. A company-level default for new types lives in Settings → Sales.What changed
Test plan
Notes for reviewers
Companion module
Soft-dependency: when
web_field_provenanceis also installed, the_sot_resolvehelper additionally stamps cascade provenance via_stamp_provenance(..., source="r", rule="Sale Order Type cascade")so the OWL badge surfaces in the form view (green cog on cascade-set fields, pencil on user-set fields, grey circle on defaults). Integration is gated behindhasattr(self, "_user_set")so this module installs cleanly with or without the companion.Visual proof of the three badge states (against
res.partner.titleas a demo target — same machinery applies to any opted-in field onsale.order):