From 31bd8d1e564a87b56fde8891ee6a1a99cf26aa6d Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Sun, 19 Apr 2026 21:07:48 -0400 Subject: [PATCH] =?UTF-8?q?feat(configurator):=20C3=20=E2=80=94=20link=20d?= =?UTF-8?q?irect-order=20line=20to=20a=20prior=20quote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds quote_id (Many2one fp.quote.configurator) on the wizard line with a domain scoped to the wizard's customer + quote states (sent / accepted / won). Onchange auto-fills part, coating, and unit price (final = estimator_override_price or calculated_price, per-part). Mirrors x_fc_quote_id on sale.order.line for the audit trail. Surfaced as a togglable column on the SO line tree and under "Qty & Price" on the wizard line drill-in form. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../models/sale_order_line.py | 5 +++++ .../views/sale_order_views.xml | 1 + .../wizard/fp_direct_order_line.py | 22 +++++++++++++++++++ .../wizard/fp_direct_order_wizard.py | 1 + .../wizard/fp_direct_order_wizard_views.xml | 2 ++ 5 files changed, 31 insertions(+) diff --git a/fusion_plating/fusion_plating_configurator/models/sale_order_line.py b/fusion_plating/fusion_plating_configurator/models/sale_order_line.py index 81225030..c6e923ee 100644 --- a/fusion_plating/fusion_plating_configurator/models/sale_order_line.py +++ b/fusion_plating/fusion_plating_configurator/models/sale_order_line.py @@ -41,3 +41,8 @@ class SaleOrderLine(models.Model): help='Flag for prototype / non-catalog parts that should not be ' 'reused after this order.', ) + x_fc_quote_id = fields.Many2one( + 'fp.quote.configurator', + string='Linked Quote', + help='Quote that seeded this line. Links back for audit trail.', + ) diff --git a/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml b/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml index 9430010e..59d01e26 100644 --- a/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml +++ b/fusion_plating/fusion_plating_configurator/views/sale_order_views.xml @@ -103,6 +103,7 @@ + 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 a37edd9b..e0d7a8a3 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 @@ -108,6 +108,13 @@ class FpDirectOrderLine(models.TransientModel): help='After submit, write this line\'s coating + additional ' 'treatments back onto the part catalog as its new defaults.', ) + quote_id = fields.Many2one( + 'fp.quote.configurator', + string='Linked Quote', + domain="[('partner_id', '=', parent.partner_id), ('state', 'in', ['sent','accepted','won'])]", + help='Optional: link this line to a prior quote. The unit price ' + 'auto-fills from the quote\'s final price (or override).', + ) # ---- Description ---- description_template_id = fields.Many2one( @@ -143,6 +150,21 @@ class FpDirectOrderLine(models.TransientModel): ) # ---- Onchange ---- + @api.onchange('quote_id') + def _onchange_quote_id(self): + """Auto-fill part, coating, and unit price from the linked quote.""" + if not self.quote_id: + return + q = self.quote_id + if q.part_catalog_id and not self.part_catalog_id: + self.part_catalog_id = q.part_catalog_id + if q.coating_config_id and not self.coating_config_id: + self.coating_config_id = q.coating_config_id + if not self.unit_price: + final = q.estimator_override_price or q.calculated_price + if final and q.quantity: + self.unit_price = final / q.quantity + @api.onchange('part_catalog_id') def _onchange_part_defaults(self): """When a part is picked, seed coating + treatments from its catalog defaults.""" diff --git a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py index c1bb5204..939d05c9 100644 --- a/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py +++ b/fusion_plating/fusion_plating_configurator/wizard/fp_direct_order_wizard.py @@ -246,6 +246,7 @@ class FpDirectOrderWizard(models.TransientModel): 'x_fc_part_wo_description': line.part_wo_description or False, 'x_fc_start_at_node_id': line.start_at_node_id.id or False, 'x_fc_is_one_off': line.is_one_off, + 'x_fc_quote_id': line.quote_id.id or False, })) # 5. Create + confirm 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 d79f4929..3f22533e 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 @@ -118,6 +118,8 @@ widget="many2many_tags"/> +