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

@@ -75,11 +75,55 @@ def _backfill_cloned_process_names(env):
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)