diff --git a/fusion_plating/fusion_plating_configurator/__manifest__.py b/fusion_plating/fusion_plating_configurator/__manifest__.py index ae1be85c..0ce6d31e 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.22.0.0', + 'version': '19.0.22.1.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/migrations/19.0.22.1.0/__init__.py b/fusion_plating/fusion_plating_configurator/migrations/19.0.22.1.0/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fusion_plating/fusion_plating_configurator/migrations/19.0.22.1.0/pre-migrate.py b/fusion_plating/fusion_plating_configurator/migrations/19.0.22.1.0/pre-migrate.py new file mode 100644 index 00000000..43cc3b79 --- /dev/null +++ b/fusion_plating/fusion_plating_configurator/migrations/19.0.22.1.0/pre-migrate.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +"""Pre-migration for 19.0.22.1.0 — material_process Char → Many2One. + +The material_process field on fp.direct.order.wizard was originally a +free-text Char tag for shop-level metadata (e.g. "ENP-STEEL-HP-ADVANCED"). +Per 2026-05-27 customer feedback, it should instead link directly to a +recipe (fusion.plating.process.node, node_type='recipe') so that picking +a tag auto-applies the recipe to every order line. + +Drop the old VARCHAR column so Odoo can recreate it as an INTEGER FK +when the new field declaration loads. Per the Express Orders spec +section 12 (dev-stage, ignore past orders), losing the old Char values +is acceptable. + +Idempotent — IF EXISTS guards mean a re-run is safe. +""" + + +def migrate(cr, version): + # Same model needs the same pre-migration on sale.order too + for table, column in ( + ('fp_direct_order_wizard', 'material_process'), + ('sale_order', 'x_fc_material_process'), + ): + cr.execute(""" + SELECT data_type FROM information_schema.columns + WHERE table_name = %s AND column_name = %s + """, (table, column)) + row = cr.fetchone() + if not row: + continue + data_type = row[0] + # Only drop if the existing column is the old Char shape + if data_type in ('character varying', 'text'): + cr.execute( + f"ALTER TABLE {table} DROP COLUMN IF EXISTS {column}" + ) diff --git a/fusion_plating/fusion_plating_configurator/models/fp_part_catalog.py b/fusion_plating/fusion_plating_configurator/models/fp_part_catalog.py index 85733956..6ac7c051 100644 --- a/fusion_plating/fusion_plating_configurator/models/fp_part_catalog.py +++ b/fusion_plating/fusion_plating_configurator/models/fp_part_catalog.py @@ -543,6 +543,7 @@ class FpPartCatalog(models.Model): part.description_template_count = len(part.description_template_ids) @api.depends('part_number', 'revision', 'name') + @api.depends_context('fp_express_part_picker') def _compute_display_name(self): """Display = 'PART-NUMBER (Rev X) — Optional Name'. @@ -552,8 +553,19 @@ class FpPartCatalog(models.Model): Defensive: some legacy rows stored revision as "Rev 1" (with the prefix baked in). Strip any leading "rev " so the wrapper doesn't render "(Rev Rev 1)". + + Express Orders override (2026-05-27): when called with the + `fp_express_part_picker=True` context, return JUST part_number. + The FpExpressPartCell OWL widget shows revision and part name on + their own rows, so the picker display should be just the bare + part number to avoid 'PART (Rev A) — NAME' duplicating with the + widget's separate rev / name rows. """ + express = self.env.context.get('fp_express_part_picker') for rec in self: + if express and rec.part_number: + rec.display_name = rec.part_number + continue if rec.part_number: core = f"{rec.part_number}" if rec.revision: diff --git a/fusion_plating/fusion_plating_configurator/models/sale_order.py b/fusion_plating/fusion_plating_configurator/models/sale_order.py index 670d3446..9ca54c22 100644 --- a/fusion_plating/fusion_plating_configurator/models/sale_order.py +++ b/fusion_plating/fusion_plating_configurator/models/sale_order.py @@ -123,10 +123,14 @@ class SaleOrder(models.Model): ) # ---- Express Orders header-level (2026-05-26) ---- - x_fc_material_process = fields.Char( + # 2026-05-27: changed from Char to Many2One — Material/Process Tag + # IS the order's recipe. Auto-applies to every line at confirm time. + x_fc_material_process = fields.Many2one( + 'fusion.plating.process.node', string='Material / Process Tag', - help='Free-text order-level shop tag (e.g. ENP-STEEL-HP-ADVANCED). ' - 'Informational; not used by the workflow.', + domain="[('node_type', '=', 'recipe')]", + help='Order-level recipe — auto-applies to every line. Individual ' + 'lines can still override via x_fc_process_variant_id.', ) x_fc_internal_notes = fields.Text( string='Order-Level Internal Notes', diff --git a/fusion_plating/fusion_plating_configurator/views/fp_express_order_views.xml b/fusion_plating/fusion_plating_configurator/views/fp_express_order_views.xml index 7d1882d5..afbdbe3c 100644 --- a/fusion_plating/fusion_plating_configurator/views/fp_express_order_views.xml +++ b/fusion_plating/fusion_plating_configurator/views/fp_express_order_views.xml @@ -140,9 +140,10 @@
- + + options="{'no_create_edit': True}" + placeholder="Pick a recipe..."/>
@@ -239,7 +240,7 @@ string="Part Number" widget="fp_express_part_cell" width="230px" - context="{'default_partner_id': parent.partner_id, 'default_revision': 'A'}" + context="{'default_partner_id': parent.partner_id, 'default_revision': 'A', 'fp_express_part_picker': True}" domain="[('partner_id', '=', parent.partner_id), ('is_latest_revision', '=', True)]" options="{'no_quick_create': True}"/>