Files
Odoo-Modules/fusion_plating/docs/superpowers/specs/2026-04-29-step-library-audit-design.md
gsinghpal 9401afb21d spec(step-library): full plating workflow coverage + per-recipe configurability + audit
Adds 8 new Step Kinds (Receiving, Electroclean, Strike, Salt Spray,
Adhesion Test, Hardness Test, Packaging, Replenishment) with industry-
standard default measurements. Adds 4 new input types (photo,
multi_point_thickness, bath_chemistry_panel, ph). Beefs up existing
kinds (cleaning, etch, plate, bake, ship, etc.) with bath ID, photos,
multi-point thickness, signatures.

Per-recipe configurability: each recipe step can disable, rename,
retarget, reorder prompts; add custom prompts; toggle entire-step
data collection. Library is the smart default; recipe is final say.

Office-to-operator instructions: relabel as Default Operator
Instructions in the library; per-recipe override surfaced in the
simple recipe editor; falls back to library default at runtime when
recipe override is empty.

Battle test plan covers 18 assertions end-to-end on entech.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:48:12 -04:00

21 KiB
Raw Blame History

Step Library Expansion + Per-Recipe Configurability + Audit Coverage

Date: 2026-04-29 Modules: fusion_plating (core), fusion_plating_jobs, fusion_plating_reports Status: Approved by user, ready for implementation plan

Problem

The plating-shop step library (fp.step.template) covers ~60% of a real plating workflow but is missing several common steps (Receiving, Electroclean, Strike, Salt Spray, Adhesion Test, Hardness Test, Packaging, Tank Replenishment). Existing kinds have light default measurement seeding — many industry-standard fields (Bath ID, current density, photo evidence, multi-point thickness) aren't authored by default, so audit-required values often get skipped.

The recipe author also can't customise step measurements at the recipe level — they can only edit the library template, which affects every recipe that step appears in. There's no per-recipe override for prompts, target ranges, or instructions.

The office team writes per-step instructions that operators read at runtime; this channel exists but is hidden in a tab and isn't surfaced in the simple recipe editor for inline editing.

Goals

  1. Cover the full plating workflow with appropriate Step Kinds and audit-grade default measurements.
  2. Add specialised input types (photo, multi_point_thickness, bath_chemistry_panel, ph) that match real shop instrumentation.
  3. Make every prompt per-recipe configurable — recipe authors can disable, rename, retarget, reorder, or add custom prompts on each recipe step without touching the library.
  4. Make office→operator instructions visible and editable directly in the simple recipe editor, with library default + per-recipe override.
  5. Wire all of the above through to runtime (fp.job.step input wizard, tablet QC checklist, CoC chronological report) so recorded values are captured for audit.
  6. Battle-test end-to-end after deploy.

Non-Goals

  • IoT/sensor auto-fill of measurements (read pH probe / bath chemistry panel directly from instruments) — separate project.
  • Customer-portal display of recorded measurements — separate.
  • Step Kinds beyond the 8 added here (deburring, anodize seal, abrasive blasting) — same pattern, easy to add later.
  • Replacing the existing tablet OWL QC checklist — extend only.
  • Per-customer or per-part override of step measurements — separate concern (parts already carry their own recipe variant).

Architecture Map

fp.step.template (library — office authors here)
  ├── description (Html, "Default Operator Instructions")
  ├── default_kind (Selection, drives DEFAULT_INPUTS_BY_KIND)
  ├── input_template_ids → fp.step.template.input
  └── transition_input_ids → fp.step.template.transition.input
       ↓ [snapshot copy on drag into recipe]
fusion.plating.process.node (recipe step — recipe authors override here)
  ├── description (Html, per-recipe override; empty = fall back to library)
  ├── collect_measurements (Boolean, master switch — NEW)
  └── input_ids → fusion.plating.process.node.input
       ├── collect (Boolean, per-prompt opt-out — NEW)
       ├── template_input_id (m2o — NEW, traceability for "reset to defaults")
       └── (existing fields: name, input_type, target_min/max, target_unit, required, hint, sequence, kind)
       ↓ [MO/job creation copies recipe nodes]
fp.job.step (work order step — operator records here)
       ↓ [Mark Done → input wizard fires, FILTERED to collect=True only]
fp.job.step.input.wizard.line
       ↓ [operator types values → commit]
fp.job.step.move (transfer_type='step', json input data)
       ↓ [QWeb render]
report_coc_chronological.xml (audit document — only collect=True inputs)

New Step Kinds

Added to fp.step.template.default_kind Selection and DEFAULT_INPUTS_BY_KIND:

Kind code Label Default Operation Measurements
receiving Receiving / Incoming Inspection Qty Received, Qty Rejected, Customer PO# Verified (boolean), Packing Slip #, Condition Notes, Damage Photo (photo, optional), Inspector Initials (signature)
electroclean Electroclean Time, Temperature, Amperage (A), Voltage (V), Current Density (ASF), Polarity (selection: anodic/cathodic/periodic), Bath ID
strike Strike (Wood's Nickel / Activation) Time, Temperature, Amperage (A), Voltage (V), Current Density (ASF), Bath ID
salt_spray Salt Spray / Corrosion Test Test Duration (hr), Result (pass_fail), Red Rust %, White Corrosion %, Lab Report (photo)
adhesion_test Adhesion Test Test Method (selection: bend/tape/burnish/file), Result (pass_fail), Photo of Coupon (photo)
hardness_test Hardness Test (HV / HK / HRC) Test Load (gf), Reading 1/2/3 (multi_point_thickness), Average (number), Equipment ID, Last Calibration Date
packaging Packaging / Pre-Ship Packaging Type (selection: VCI bag/bubble wrap/separator paper/custom crate), Qty Per Package, Package Count, Cert Package Included (boolean), Customer-Supplied Packaging (boolean)
replenishment Tank Replenishment Bath ID, Chemistry Added (text), pH Before (ph), pH After (ph), Concentration Before, Concentration After, Operator Initials (signature)

Beefed-Up Defaults on Existing Kinds

Idempotently appended via action_seed_default_inputs (skips if name already present):

Existing Kind New seeded inputs
racking Rack ID, Masking Applied (boolean), Photo of Racked Load (photo)
derack Mask Removal Method (selection), Residue Check (pass_fail)
mask Mask Material (selection: Microshield/latex tape/vinyl plugs/wax), Photo (photo)
demask Residue Check (pass_fail), Surface Condition (selection: clean/marks/needs rework)
cleaning Bath ID, Ultrasonic On (boolean), Titration Done (boolean)
etch Acid Concentration (% or g/L), Bath ID, HE Risk Flag (boolean)
rinse Rinse Type (selection: cascade/spray/DI/city), Conductivity µS/cm, Time
plate Bath ID, pH (ph), Bath Concentration (g/L), Current Density ASF (electroplate), Multi-Point Thickness (multi_point_thickness)
bake Oven ID, Chart Recorder File (photo)
inspect Defect Type (selection), Thickness Sample, Photo, Inspector Signature
final_inspect Defect Categorization (selection), Dimensional Verification (pass_fail), Surface Finish Ra, Inspector Signature
wbf_test Retest Count, Photo on FAIL (photo)
dry Dry Method (selection), Time, Temperature
ship Carrier (selection), Tracking #, BoL #, Photo of Sealed Shipment

New Input Types

Added to fp.step.template.input.input_type, fusion.plating.process.node.input.input_type (the recipe-step input model), and fp.job.step.input.wizard.line.input_type (runtime wizard):

Type code Label Renders as
photo Photo Binary widget with image rendering — capture or upload one image
multi_point_thickness Multi-Point Thickness (avg) Five number inputs (R1R5) + auto-computed average; min/max bounds applied to average
bath_chemistry_panel Bath Chemistry Panel Bundle: pH + concentration + temperature + bath ID — saves clicks for plate steps
ph pH Number input clamped to 014, two-decimal precision

signature already exists; will be auto-seeded on more kinds via the new defaults.

Per-Recipe Configurability — Model Changes

Field Lives on Type Default Purpose
collect fusion.plating.process.node.input_ids row Boolean True Per-prompt opt-out. Wizard / report filter to collect=True only
template_input_id same Many2one to fp.step.template.input (snapshot link) Traceability — "this prompt was seeded from library template X". Powers "reset to library defaults"
collect_measurements fusion.plating.process.node Boolean True Master switch. When False, runtime skips the input wizard entirely for this step

Existing fields on fusion.plating.process.node.input_ids (name, input_type, target_min, target_max, target_unit, required, hint, sequence, selection_options) are already editable — recipe author can rename / retarget / reorder freely without touching the library.

Per-Recipe Configurability — Recipe Author Capabilities

Each recipe step exposes:

  1. Disable a prompt — toggle collect off (preserves history; not deleted)
  2. Override target range — edit target_min / target_max per recipe
  3. Rename a prompt — edit name
  4. Reorder prompts — drag handle on sequence
  5. Mark required per recipe — flip required independently of library
  6. Add custom prompt — new row with template_input_id = False
  7. Disable entire step's data collection — master collect_measurements = False on the recipe node
  8. Reset to library defaults — re-sync input_ids + description from linked library template, preserve custom rows where template_input_id = False

Operator Instructions (Office → Floor)

fp.step.template.description (Html) — relabel UI to "Default Operator Instructions" with tooltip "Standing instructions the office gives operators for this step. Snapshot-copied onto every recipe that uses this step. Recipe authors can override per recipe."

fusion.plating.process.node.description (existing, already snapshot-copied) is the per-recipe override. Empty recipe-node description falls back to library at runtime so blank means "use library default", not "show nothing".

Runtime visibility — three places:

  1. Tablet shop-floor card — instructions panel above input prompts (expandable)
  2. Backend Mark-Done wizard — same panel at top of input form
  3. Printed traveller / WO sheet — instructions section under each step heading

Simple Recipe Editor — UI Changes

Each step card gains two expansions:

┌─ Plate ENP ───────────────────────────────────────┐
│ Plate ENP @ Tank #3 · 30 min · 180-200°F           │
│                                                    │
│ ▸ 📋 Instructions  (using library default)         │
│ ▸ ⚙ Measurements   (5/5 collected)                 │
└────────────────────────────────────────────────────┘

Instructions expansion

  • Source toggle: "Library default" vs "Custom for this recipe"
  • Rich-text editor showing current text (library default if recipe override empty)
  • [Use library default] / [Save override] buttons
  • Badge on step card: "📋 default" vs "📋 custom"

Measurements expansion

  • Master toggle: "Collect measurements at this step"
  • Editable list of prompts with: drag handle, collect checkbox, name, input type, target range, unit, required toggle
  • [+ Add custom prompt] to append a recipe-only prompt
  • [Reset to library defaults] button
  • Badge on step card: "5/5 collected" (green), "3/5 collected" (amber), "No measurements" (grey)

"Add Common Audit Fields" One-Click Action

New button on fp.step.template form: action_add_common_audit_fields. Idempotently appends:

  • Operator Initials (signature, required)
  • Bath ID (text)
  • Photo on Failure (photo, optional, hint="upload only if failure observed")
  • Equipment ID (text)

Skip rows whose name already exists. Logs to chatter.

Runtime Wiring

Component Change
fp.job.step.input.wizard default_get Filter recipe_node.input_ids to kind == 'step_input' AND collect == True. If recipe_node.collect_measurements == False, return empty line_ids.
fp.job.step.input.wizard.line.input_type Mirror the 4 new input types so the wizard form can render per-type widgets
fp.job.step.input.wizard_views.xml Conditional widgets per input type (photo → image binary; multi_point_thickness → 5-cell row + avg; bath_chemistry_panel → 4-cell group; ph → number with [0,14])
fusion_plating_shopfloor tablet QC checklist OWL Same per-type rendering on tablet
fp.job.step.move No schema change — JSON input_data field already accepts arrays/dicts; just confirm encoding round-trips for the new types
Snapshot-copy logic (library → recipe node) Verify template_input_id link is set; verify all fields including new types copy verbatim
_fp_recipe_to_job (MO/job creation) Verify collect, collect_measurements, template_input_id carry through to fp.job.step.recipe_node_id chain

CoC / Audit Report Rendering

fusion_plating_reports/views/report_coc_chronological.xml:

  1. Filter rendered inputs to collect == True only.
  2. Render branches for new input types:
    • photo → thumbnail <img> from ir.attachment, fallback to [photo: filename] if attachment missing
    • multi_point_thicknessR1: x, R2: y, R3: z, R4: a, R5: b → avg M
    • bath_chemistry_panel → 4 labelled rows (pH, conc, temp, bath ID)
    • phpH X.XX
  3. Skip prompts where the recipe author opted out (collect=False) — don't render them as "(not collected)" since auditors only care about what WAS measured.

Migration

Single migration script: fusion_plating/migrations/19.0.18.7.0/post-migrate.py

  1. Default collect=True on all existing fusion.plating.process.node.input_ids rows.
  2. Default collect_measurements=True on all existing fusion.plating.process.node rows.
  3. For each existing fp.step.template, re-run action_seed_default_inputs (idempotent — adds new defaults without clobbering manual edits).
  4. Backfill template_input_id on recipe-node inputs by name-matching against the linked library template's inputs (best-effort; rows that don't match stay as False and become "custom prompts" for purposes of reset-to-defaults).

Seed data file: fusion_plating/data/fp_step_template_data.xml (noupdate="1") — one example template per new Step Kind so users see populated entries on upgrade.

Edge Cases

  1. Recipe node with empty description — runtime renders the linked library template's description (fall-through). If both empty, instructions section is hidden in the operator wizard.
  2. Recipe author opts out of all prompts but leaves collect_measurements=True — wizard fires with zero rows; UX gives "no inputs to record, mark done?" confirmation. Same as today's behaviour for kinds with no defaults (rinse, dry, gating).
  3. Photo input on operation measurement (new) vs transition input (existing) — both kinds always store the image as an ir.attachment linked to the fp.job.step.move row (never as inline base64 in the input_data JSON, to keep the payload small and indexable). Distinguish operation vs transition by kind field on the prompt row. CoC report iterates both kinds and renders thumbnails.
  4. Multi-point thickness validation — at least one reading required when required=True; average computed over non-empty cells only (empty = "didn't measure that point"); when the computed average falls outside [target_min, target_max] the wizard shows a non-blocking warning banner before commit (operator can override and proceed, but the out-of-band condition is recorded on the move for QC review).
  5. Bath chemistry panel — partial fill — auditor wants to know which fields were captured; all four fields stored in the JSON payload as separate keys. Empty values render as "—" in the CoC.
  6. Reset to library defaults — preserves rows where template_input_id = False (recipe-author-added custom prompts). Re-enables collect=True on rows where template_input_id is set. Logs to chatter with diff summary.
  7. Library template renamed — recipe nodes already snapshotted; template_input_id link survives the rename. "Reset to defaults" pulls the renamed prompt back.
  8. Library prompt deleted — recipe nodes' template_input_id becomes a dangling reference; reset-to-defaults treats those as "deleted upstream, leave as recipe custom" (don't drop them — recipe author may still want them).
  9. Existing recipes upgrading — the migration sets collect=True everywhere, so behaviour is unchanged for existing data. Only newly-disabled prompts (post-upgrade) suppress at runtime.

Battle Test Plan

Build fusion_plating/scripts/bt_step_library_audit.py exercising every change. Same bt_s*.py pattern as fusion_plating_quality/scripts/. Runs in odoo-shell against the live entech DB after deploy. Reports PASS / FAIL / SKIP per assertion, summary counts at the end.

# Assertion
1 Every new Step Kind has at least 1 seed template loaded
2 Every Step Kind (new + existing) yields the expected default inputs after action_seed_default_inputs runs on a fresh template
3 Library → recipe drag snapshot-copies every input field including the 4 new types
4 template_input_id link is set on snapshot-copied inputs
5 Recipe → job step inheritance preserves collect, collect_measurements, custom prompts
6 Operator wizard at runtime filters to collect=True only
7 Master collect_measurements=False skips the wizard entirely
8 Adding a custom prompt to a recipe node (no template_input_id) survives reset-to-defaults
9 Reset-to-defaults re-enables opted-out library prompts and pulls in newly-added library prompts
10 Each new input type stores and round-trips through fp.job.step.move.input_data (JSON)
11 Multi-point thickness average computed correctly (incl. edge case of 1 reading)
12 Photo attachment lands in ir.attachment and links to the move row
13 CoC chronological report renders each new input type without errors
14 CoC excludes collect=False rows
15 Operator instructions render at runtime — recipe override beats library default; empty override falls back to library
16 "Add Common Audit Fields" one-click is idempotent (run twice, no duplicates)
17 action_seed_default_inputs is idempotent after manual edits (user-edited rows survive re-seed)
18 description on recipe node clears correctly via "Use library default" toggle

Files Touched

File Change
fusion_plating/models/fp_step_template.py Extend default_kind Selection, extend DEFAULT_INPUTS_BY_KIND, add action_add_common_audit_fields
fusion_plating/models/fp_step_template_input.py Add 4 input types to selection
fusion_plating/models/fp_process_node.py Add collect_measurements on node; add collect + template_input_id on inline input model; mirror new input types
fusion_plating/models/fp_process_node_inherit.py If applicable for filtering helpers
fusion_plating/views/fp_step_template_views.xml Add "Add Common Audit Fields" button; relabel description to "Default Operator Instructions"
fusion_plating/data/fp_step_template_data.xml NEW — seed templates for the 8 new kinds
fusion_plating/migrations/19.0.18.7.0/post-migrate.py NEW — backfill collect, collect_measurements, template_input_id; re-run action_seed_default_inputs
fusion_plating/static/src/js/simple_recipe_editor.js Render Instructions + Measurements expansions; collect badge; reset-to-defaults action
fusion_plating/static/src/xml/simple_recipe_editor.xml Templates for new affordances
fusion_plating/static/src/scss/simple_recipe_editor.scss Styles for expansions and badges
fusion_plating/controllers/simple_recipe_controller.py New endpoints: /fp/simple_recipe/step/toggle_collect, /fp/simple_recipe/step/edit_input, /fp/simple_recipe/step/edit_instructions, /fp/simple_recipe/step/reset_to_library
fusion_plating_jobs/wizards/fp_job_step_input_wizard.py Filter to collect=True only; mirror new input types
fusion_plating_jobs/wizards/fp_job_step_input_wizard_views.xml Conditional widgets per input type
fusion_plating_shopfloor/static/src/... (tablet QC checklist OWL) Per-type rendering on tablet
fusion_plating_reports/views/report_coc_chronological.xml Render branches for new types; filter to collect=True
fusion_plating/scripts/bt_step_library_audit.py NEW — battle-test script
fusion_plating/__manifest__.py Bump version to 19.0.18.7.0
fusion_plating_jobs/__manifest__.py Bump version
fusion_plating_reports/__manifest__.py Bump version

Effort Estimate

68 working days. Roughly 60% backend (models, migrations, wizard filtering, CoC, battle test) and 40% frontend (OWL editor expansions, per-input-type widgets in wizard + tablet).

Out of Scope (Defer)

  • IoT/sensor auto-fill of measurements
  • Customer portal display of recorded values
  • Step Kinds beyond the 8 listed (deburring, anodize seal, abrasive blasting)
  • Replacing the existing tablet QC checklist OWL component
  • Per-customer or per-part override of step measurements
  • Notification/email when audit-required values are missing post-completion