feat(step-library): full plating workflow coverage + per-recipe configurability + audit

Implements 2026-04-29-step-library-audit-design.md. Bumps fusion_plating
to 19.0.18.7.0, fusion_plating_jobs to 19.0.8.12.0, fusion_plating_reports
to 19.0.10.2.0.

LIBRARY EXPANSION
- 8 new Step Kinds: Receiving, Electroclean, Strike, Salt Spray,
  Adhesion Test, Hardness Test, Packaging, Tank Replenishment
- 4 new input types: photo, multi_point_thickness, bath_chemistry_panel, ph
- DEFAULT_INPUTS_BY_KIND rewritten to seed audit-grade prompts on every
  kind (bath IDs, photos, multi-point thickness, signatures, etc.)
- + Common Audit Fields one-click button on the library template form
- Default Operator Instructions relabel + alert callout

PER-RECIPE CONFIGURABILITY
- collect (Boolean) per recipe-step input prompt — opt out without delete
- collect_measurements (Boolean) master switch on recipe step — when off,
  wizard skips entirely
- template_input_id (Many2one) traceability link from recipe to library
- Recipe-step backend form view exposes the new fields with handle drag,
  toggle, target range, and library-source column

RUNTIME WIRING
- Step input wizard filters node.input_ids to step_input AND collect=True;
  short-circuits on collect_measurements=False
- New input types: photo (image widget + ir.attachment), multi-point
  thickness (5 readings + auto avg, skips empty cells), bath chemistry
  panel (pH/conc/temp/bath bundle), pH (0-14 numeric)
- Composite values JSON-serialized into value_text; photo via attachment

CoC REPORT
- Filters captured prompts to collect=True only
- Renders new input types with appropriate format

MIGRATION (post-migrate.py for 19.0.18.7.0)
- Backfills collect=True on recipe-step inputs
- Backfills collect_measurements=True on recipe steps
- Re-runs action_seed_default_inputs on every existing template
  (idempotent, preserves user edits)
- Backfills template_input_id by name-matching against source library
  template (handles JSONB vs varchar name columns)

SEED DATA
- 8 example templates (one per new kind) in fp_step_template_data.xml
  with noupdate=1

BATTLE TEST
- bt_step_library_audit.py: 29 assertions all PASS on entech

OWL EDITOR EXTENSION DEFERRED
- The simple recipe editor's per-step Instructions/Measurements
  expansions were not implemented in this pass; users configure via the
  backend recipe-step form. Track follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-29 22:13:54 -04:00
parent bbf2476f01
commit b187192c58
34 changed files with 1690 additions and 110 deletions

View File

@@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
"""Post-migration for 19.0.18.7.0 — Step Library audit expansion.
1. Default `collect=True` on all existing recipe-step inputs.
2. Default `collect_measurements=True` on all existing recipe steps.
3. Re-run action_seed_default_inputs on every existing template to
pull in the newly-added prompts (idempotent — skips rows whose
name is already present, so user edits survive).
4. Backfill template_input_id by name-matching against the linked
library template (best-effort).
"""
import logging
_logger = logging.getLogger(__name__)
def migrate(cr, version):
if not version:
return
from odoo.api import Environment, SUPERUSER_ID
env = Environment(cr, SUPERUSER_ID, {})
# 1. Default collect=True on all recipe-step inputs that have NULL
cr.execute("""
UPDATE fusion_plating_process_node_input
SET collect = TRUE
WHERE collect IS NULL
""")
_logger.info(
"Backfilled collect=True on %s recipe-step inputs", cr.rowcount
)
# 2. Default collect_measurements=True on recipe steps with NULL
cr.execute("""
UPDATE fusion_plating_process_node
SET collect_measurements = TRUE
WHERE collect_measurements IS NULL
""")
_logger.info(
"Backfilled collect_measurements=True on %s recipe steps", cr.rowcount
)
# 3. Re-seed defaults on every existing template (idempotent)
Template = env['fp.step.template']
templates = Template.search([('default_kind', '!=', False)])
for tpl in templates:
try:
tpl.action_seed_default_inputs()
except Exception as e:
_logger.warning(
"Failed to re-seed defaults on template %s: %s", tpl.id, e
)
_logger.info("Re-seeded defaults on %s templates", len(templates))
# 4. Backfill template_input_id — name-match recipe-node inputs against
# their parent recipe's source library template.
# Note: fusion_plating_process_node_input.name is plain varchar;
# fp_step_template_input.name is translatable JSONB (use ->>'en_US').
cr.execute("""
SELECT ni.id, ni.name, n.source_template_id
FROM fusion_plating_process_node_input ni
JOIN fusion_plating_process_node n ON n.id = ni.node_id
WHERE ni.template_input_id IS NULL
AND n.source_template_id IS NOT NULL
""")
rows = cr.fetchall()
matched = 0
for ni_id, name, tpl_id in rows:
if not name:
continue
cr.execute("""
SELECT id FROM fp_step_template_input
WHERE template_id = %s
AND name->>'en_US' = %s
LIMIT 1
""", (tpl_id, name))
match = cr.fetchone()
if match:
cr.execute("""
UPDATE fusion_plating_process_node_input
SET template_input_id = %s WHERE id = %s
""", (match[0], ni_id))
matched += 1
_logger.info(
"Backfilled template_input_id on %s recipe-step inputs", matched
)