# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # Part of the Fusion Plating product family. from . import controllers from . import models from . import wizard def _backfill_currency(env): """Fill missing currency_id on existing money-holding records. Older demo data and manually-created rows were persisted before the `required=True` was added, so some records sit with currency_id=NULL and Monetary fields render without a $ symbol. This runs on module install/upgrade and pins them to the company's currency. """ company_currency = env.company.currency_id.id if not company_currency: return for model_name in ( 'fp.pricing.rule', 'fp.quote.configurator', ): Model = env.get(model_name) if Model is None: continue Model.search([('currency_id', '=', False)]).write( {'currency_id': company_currency} ) def _backfill_cloned_process_names(env): """Append " — Rev " to every existing part- cloned process ROOT whose name doesn't already carry the suffix. Feedback on 2026-04-23: the Process tab on the part form was showing a bare template name ("General Processing"), so users couldn't tell at a glance that the clone belonged to THIS part. The clone logic now adds the suffix automatically; this backfill brings older clones up to the same format without forcing users to re-compose (which would wipe their edits). Idempotent: checks for a literal " — " separator before rewriting. """ Node = env['fusion.plating.process.node'] roots = Node.search([ ('node_type', '=', 'recipe'), ('part_catalog_id', '!=', False), ('parent_id', '=', False), ]) renamed = 0 for root in roots: part = root.part_catalog_id if not part: continue if ' — ' in (root.name or ''): continue # Already has a suffix — leave alone. suffix_bits = [] if part.part_number: suffix_bits.append(part.part_number) if part.revision: # `revision` sometimes already carries a "Rev " prefix # (e.g. "Rev 2") — don't double up. rev = part.revision.strip() if not rev.lower().startswith('rev'): rev = 'Rev %s' % rev suffix_bits.append(rev) if not suffix_bits: continue root.name = '%s — %s' % (root.name or '', ' '.join(suffix_bits)) renamed += 1 def _backfill_part_material_id(env): """Pin existing parts AND quote configurators to a row in the shared material library. Pre-Sub-12d, both models only had a `substrate_material` Selection. This sets `material_id` on every record that doesn't yet have one, matching by substrate_material → seed material XML id. Idempotent. """ Part = env['fp.part.catalog'] Material = env['fp.part.material'] if Part is None or Material is None: return # Map legacy Selection key → seed XML id (the generic per-category entry). xmlid_by_key = { 'aluminium': 'fusion_plating_configurator.fp_material_aluminium', 'steel': 'fusion_plating_configurator.fp_material_steel', 'stainless': 'fusion_plating_configurator.fp_material_stainless', 'copper': 'fusion_plating_configurator.fp_material_copper', 'titanium': 'fusion_plating_configurator.fp_material_titanium', 'other': 'fusion_plating_configurator.fp_material_other', } cache = {} for key, xmlid in xmlid_by_key.items(): rec = env.ref(xmlid, raise_if_not_found=False) if rec: cache[key] = rec.id if not cache: return # Parts for part in Part.search([('material_id', '=', False)]): mid = cache.get(part.substrate_material) if mid: part.material_id = mid # Quote configurators (same Selection key → same library) Quote = env['fp.quote.configurator'] if Quote is not None: for q in Quote.search([('material_id', '=', False)]): mid = cache.get(q.substrate_material) if mid: q.material_id = mid def post_init_hook(env): _backfill_currency(env) _backfill_cloned_process_names(env) _backfill_part_material_id(env) def post_upgrade_hook(env): _backfill_currency(env) _backfill_cloned_process_names(env) _backfill_part_material_id(env)