This commit is contained in:
gsinghpal
2026-04-28 19:39:37 -04:00
parent 2d42b33d68
commit 13e300d90e
103 changed files with 4959 additions and 331 deletions

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
"""19.0.12.1.0 — Convert every free-text UoM column to the curated
selection keys defined in models/_fp_uom_selection.py.
Runs after fusion_plating's tables have been re-described (so the
columns are now Selection-typed at the ORM level), but before users
hit the new views. Idempotent — re-running maps already-converted
values to themselves and leaves them in place.
"""
import logging
from odoo.api import Environment
from odoo.addons.fusion_plating.models._fp_uom_selection import (
fp_migrate_uom_column,
)
_logger = logging.getLogger(__name__)
def migrate(cr, version):
env = Environment(cr, 1, {}) # SUPERUSER
targets = [
# core
('fusion_plating_bath_parameter', 'uom', 'bath parameter'),
('fusion_plating_process_node_input', 'uom', 'process node input'),
('fusion_plating_process_node_input', 'target_unit', 'process node target'),
('fp_step_template_input', 'target_unit', 'step template input target'),
# compliance (only migrated when the module is installed — the
# helper is no-op when the table doesn't exist)
('fusion_plating_discharge_limit', 'uom', 'discharge limit'),
('fusion_plating_discharge_sample_line', 'uom', 'discharge sample line'),
('fusion_plating_waste_manifest', 'uom', 'waste manifest'),
('fusion_plating_waste_stream', 'generation_uom', 'waste stream'),
('fusion_plating_spill_register', 'uom', 'spill register'),
# safety
('fusion_plating_chemical', 'container_uom', 'chemical container'),
('fusion_plating_exposure_monitoring', 'uom', 'exposure monitoring'),
]
total_rewritten = total_cleared = 0
for table, column, label in targets:
rewritten, cleared = fp_migrate_uom_column(env, table, column, label)
total_rewritten += rewritten
total_cleared += cleared
_logger.info(
'Fusion Plating 19.0.12.1.0 — UoM migration complete: '
'%s rewritten, %s cleared (across %s columns).',
total_rewritten, total_cleared, len(targets),
)

View File

@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
"""19.0.12.4.0 — Step-library polish + Policy B Contract Review backfill.
post_init_hook only fires on fresh install. Existing DBs upgrading
from pre-Policy-B versions need this migration to:
1. Add the missing 'Contract Review' library template (the
_seed_step_library_if_empty seeder skipped it because their
library was already populated when 19.0.12.3.0 landed).
2. Backfill default_kind on existing library entries that landed
without a kind because the original seeder used a brittle
case-sensitive lookup that missed common name variations
("E-Nickel Plating" vs "E-Nickel Plate", "DeRacking" vs
"De-Racking", "Ready for X" gating prefixes, etc.). The new
`fp_resolve_step_kind` helper is hyphen / case / -ing tolerant.
3. Add canonical missing entries (Soak Clean, Rinse, Etch, Acid Dip,
Drying, Inspection, Shipping, Water Break Test, Desmut, Zincate)
that ENP-ALUM-BASIC's seed didn't include — these are the names
a fresh estimator would expect to find when they open the library
from scratch. Without them, an empty recipe has no obvious starting
templates for cleaning / rinsing / standard inspection.
All three steps are idempotent — re-running on an already-fixed DB
is a no-op.
"""
import logging
from odoo.api import Environment
from odoo.addons.fusion_plating import fp_resolve_step_kind
_logger = logging.getLogger(__name__)
CANONICAL_MISSING = [
('Soak Clean', 'cleaning'),
('Electroclean', 'cleaning'),
('Rinse', 'rinse'),
('Etch', 'etch'),
('Desmut', 'etch'),
('Zincate', 'etch'),
('Acid Dip', 'etch'),
('HCl Activation', 'etch'),
('Water Break Test', 'wbf_test'),
('Drying', 'dry'),
('Inspection', 'inspect'),
('Final Inspection', 'final_inspect'),
('Shipping', 'ship'),
('Contract Review', 'contract_review'),
]
def migrate(cr, version):
env = Environment(cr, 1, {}) # SUPERUSER
Tpl = env['fp.step.template']
# ---- 1. Backfill default_kind on existing library entries -----------
blank_kind = Tpl.search([('default_kind', '=', False)])
fixed = 0
for tpl in blank_kind:
kind = fp_resolve_step_kind(tpl.name)
if kind:
tpl.default_kind = kind
tpl.action_seed_default_inputs()
fixed += 1
_logger.info(
'Fusion Plating 19.0.12.4.0: backfilled default_kind on %s/%s '
'library entries via fp_resolve_step_kind.',
fixed, len(blank_kind),
)
# ---- 2. Add canonical missing entries -------------------------------
existing_names_lower = {
(n.strip().lower()) for n in Tpl.search([]).mapped('name') if n
}
added = 0
for name, kind in CANONICAL_MISSING:
if name.lower() in existing_names_lower:
continue
tpl = Tpl.create({
'name': name,
'default_kind': kind,
})
tpl.action_seed_default_inputs()
added += 1
_logger.info(
'Fusion Plating 19.0.12.4.0: added %s canonical missing library '
'entries (Soak Clean, Rinse, Etch, etc.).', added,
)

View File

@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
"""19.0.12.5.0 — Backfill default_kind on existing recipe nodes.
The Page-2 audit (2026-04-28) showed that pre-Sub-12a recipe nodes
have NULL `default_kind` because the field was added later. The
recipe-side soft-gates (Sub 8 racking, Policy B contract review) fall
back to name-matching when the kind is missing, which means a
renamed step ("Hang on Bar" instead of "Racking") silently bypasses
the gate.
This migration walks `fusion.plating.process.node` rows with NULL
default_kind, resolves a sensible kind via the central
`fp_resolve_step_kind()` helper, and sets it.
It also walks `fp.job.step` rows whose `kind` is the legacy 'other'
placeholder and re-derives `kind` from `recipe_node_id.default_kind`
(after the node-side backfill above sets it). Non-other kinds are
left alone — operator may have set them deliberately.
Idempotent.
"""
import logging
from odoo.api import Environment
from odoo.addons.fusion_plating import fp_resolve_step_kind
_logger = logging.getLogger(__name__)
# Same mapping as in fp_job.py — keep them in sync.
_NODE_KIND_TO_STEP_KIND = {
'cleaning': 'wet',
'etch': 'wet',
'rinse': 'wet',
'plate': 'wet',
'dry': 'wet',
'wbf_test': 'wet',
'bake': 'bake',
'mask': 'mask',
'demask': 'mask',
'racking': 'rack',
'derack': 'rack',
'inspect': 'inspect',
'final_inspect': 'inspect',
'contract_review': 'other',
'gating': 'other',
'ship': 'other',
}
def migrate(cr, version):
env = Environment(cr, 1, {})
# ---- 1. Backfill default_kind on recipe nodes -----------------------
Node = env['fusion.plating.process.node']
blank = Node.search([
('default_kind', '=', False),
('node_type', 'in', ('operation', 'step')),
])
fixed = 0
for n in blank:
kind = fp_resolve_step_kind(n.name)
if kind:
n.default_kind = kind
fixed += 1
_logger.info(
'19.0.12.5.0: backfilled default_kind on %s/%s recipe nodes via '
'fp_resolve_step_kind.', fixed, len(blank),
)
# ---- 2. Re-derive fp.job.step.kind from recipe node default_kind ----
Step = env['fp.job.step']
other_steps = Step.search([
('kind', '=', 'other'),
('recipe_node_id', '!=', False),
('state', 'not in', ('done', 'cancelled')),
])
rederived = 0
for s in other_steps:
node_kind = (
s.recipe_node_id.default_kind
if 'default_kind' in s.recipe_node_id._fields else None
)
new_kind = _NODE_KIND_TO_STEP_KIND.get(node_kind) if node_kind else None
if new_kind and new_kind != 'other':
s.kind = new_kind
rederived += 1
_logger.info(
'19.0.12.5.0: re-derived kind on %s/%s in-flight job steps from '
'recipe node default_kind.', rederived, len(other_steps),
)