From 381a750902e7a26fb6f90f583bfc6c771f1e5159 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Thu, 23 Apr 2026 09:20:40 -0400 Subject: [PATCH] feat(configurator): direct-order line shows effective Process alongside Treatment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User asked why the direct-order wizard only showed "Primary Treatment" and not Process — aren't they the same? They're not, but the distinction was invisible on the order line until now. Mental model (preserved here to keep future decisions aligned with the user's question): * Primary Treatment (fp.coating.config) = "WHAT coating" (process type, thickness range, spec reference — the contractual deliverable). * Process (fusion.plating.process.node tree) = "HOW we make it" (the floor-level sequence of operations and steps that WO generation turns into work). Each coating carries a default process (recipe_id). Parts can override that via the Process Composer, storing a part-scoped clone (default_process_id on fp.part.catalog). Resolution: part's composed process wins; coating default is the fallback. Added a computed read-only `effective_process_id` field on fp.direct.order.line that displays exactly what process will drive WO generation for the line, plus a one-line `effective_process_source` showing whether it came from the part ("customised") or the coating ("default"). Both surfaced on the wizard list and form so the estimator can verify before confirming the order. No behaviour change — this is pure visibility. WO generation still uses the same resolution chain it did before. fusion_plating_configurator → 19.0.13.2.0 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../__manifest__.py | 2 +- .../wizard/fp_direct_order_line.py | 41 +++++++++++++++++++ .../wizard/fp_direct_order_wizard_views.xml | 15 +++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/fusion_plating/fusion_plating_configurator/__manifest__.py b/fusion_plating/fusion_plating_configurator/__manifest__.py index 71dffecc..3bb59aaf 100644 --- a/fusion_plating/fusion_plating_configurator/__manifest__.py +++ b/fusion_plating/fusion_plating_configurator/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Fusion Plating — Configurator', - 'version': '19.0.13.1.0', + 'version': '19.0.13.2.0', 'category': 'Manufacturing/Plating', 'summary': 'Quotation configurator with part catalog, coating configs, and formula-based pricing engine.', 'description': """ diff --git a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_line.py b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_line.py index 2876e019..1233e0ae 100644 --- a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_line.py +++ b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_line.py @@ -59,6 +59,47 @@ class FpDirectOrderLine(models.TransientModel): string='Additional Treatments', help='Extra pre/post treatments applied to this line.', ) + # Read-only preview of the process tree that WILL drive WO generation + # for this line. Resolution priority: + # 1. Part's composed process (fp.part.catalog.default_process_id) + # — a part-scoped customisation set via the Process Composer. + # 2. Primary Treatment's default recipe (fp.coating.config.recipe_id) + # — the shared template used if the part has no override. + # Shown so operators can see *what will run* before confirming the + # order. Treatment answers the "what coating"; process answers the + # "how" — they're distinct but coupled via the resolution chain. + effective_process_id = fields.Many2one( + 'fusion.plating.process.node', + string='Process', + compute='_compute_effective_process', + help='Process tree that will generate work orders for this line. ' + 'Uses the part-composed process if one exists, otherwise the ' + "primary treatment's default recipe.", + ) + effective_process_source = fields.Char( + compute='_compute_effective_process', + help='Tells the estimator whether the process comes from the ' + 'part (customised) or the coating (shared default).', + ) + + @api.depends('part_catalog_id.default_process_id', + 'coating_config_id.recipe_id') + def _compute_effective_process(self): + for rec in self: + part_proc = (rec.part_catalog_id.default_process_id + if rec.part_catalog_id else False) + if part_proc: + rec.effective_process_id = part_proc + rec.effective_process_source = 'Part (customised)' + continue + cc_proc = (rec.coating_config_id.recipe_id + if rec.coating_config_id else False) + if cc_proc: + rec.effective_process_id = cc_proc + rec.effective_process_source = 'Coating default' + continue + rec.effective_process_id = False + rec.effective_process_source = False # ---- Qty / price ---- quantity = fields.Integer(string='Qty', default=1, required=True) diff --git a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml index b9884089..8b6da084 100644 --- a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml +++ b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard_views.xml @@ -106,6 +106,14 @@ + + + +