Files
Odoo-Modules/fusion_plating/docs/superpowers/specs/2026-04-27-sub12-simple-recipe-editor-design.md
gsinghpal 4e4ca2c9da docs(sub12): simple recipe editor + library + tablet move/rack + reports
Three-part design (12a/12b/12c) for adding a flat drag-drop recipe
editor alongside the existing tree editor, with a reusable step
library, Steelhead-style Move Parts/Rack/Stop-Timer dialogs, and
recipe-order + chronological CoC PDF reports.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:23:25 -04:00

53 KiB
Raw Blame History

Sub 12 — Simple Recipe Editor + Step Library + Tablet Move/Rack + Reports

Status: design ready for implementation planning Date: 2026-04-27 Sliced into 3 sequential sub-projects: 12a → 12b → 12c Companion file: Steelhead screen inventory — 24 screenshots logged with field-by-field notes that drove these decisions.


1. Why this sub-project exists

Two facts forced the design:

  1. The customer has two operator personas with opposite preferences. "Tree-loving" engineers like our existing OWL tree editor (fusion.plating.process.node, hierarchical recipe → sub-process → operation → step, drag-drop tree). "Simple-loving" foremen find the tree intimidating and want a flat ordered drag-drop list with a step library on the side. Forcing one persona to use the other's tool blocks adoption.

  2. The customer is migrating off Steelhead and brought 24 screenshots showing what their team is used to: a flat step library + a 2-column drag-drop recipe builder + Move-Parts / Move-Rack / Stop-Timer dialogs at the tablet + a chronological CoC traveller PDF. Their authoring UX is simpler than ours; their runtime UX is roughly equivalent but has gaps we can improve on (every blocker should have an inline resolution button).

Plus: the existing tree editor + 205+ live fp.job records + 1800+ fp.job.step records + the entire shopfloor runtime + the S14 predecessor lock + the S19 Fischerscope merge + the Sub 11 MRP cutout — all already shipped, all working — must keep working unchanged.

The design satisfies both personas without forking the data model. Same recipe data, two editor views, same tablet runtime + reports.


2. Locked decisions (Q1Q8 from the brainstorming session)

Q Decision
Q1 — Editor strategy Hybrid. Keep the existing OWL tree editor, build the simple editor alongside. Both edit the same recipes.
Q2 — Data model fork No fork. Both editors operate on the same fusion.plating.process.node records. Edits in either editor update the same recipe.
Q3 — Step library New model fp.step.template as a dedicated reusable step library. Surfaced under Plating → Configuration → Step Library.
Q4 — Library import semantics Snapshot copy. Dragging a library step into a recipe creates a fresh fusion.plating.process.node with the template's fields copied in. Editing the library template later does not mutate recipes already built. source_template_id carries the trace.
Q5 — Recipe templates ("starter recipes") is_template Boolean on the existing recipe node. A recipe flagged as a template appears in the simple editor's "Import starter from template" dropdown. Importing snapshot-copies all child nodes.
Q6 — What's on a step template Mirror Steelhead screen 1 + Advanced expander with high-value extras. Visible by default: Title, Stations, Operation Measurements, Instructions, Require QA. Advanced: icon, time/temp targets, voltage, viscosity, material callout, predecessor lock, rack/transition flags, transition inputs.
Q7 — Editor toggle Per-recipe preferred_editor (tree/simple/auto) + company-level default_recipe_editor setting + header buttons "Open in Tree Editor" / "Open in Simple Editor". Authoring lead can build in tree, hand off to a foreman who edits in simple.
Q8 — Slicing strategy Three sequential sub-projects (12a → 12b → 12c). Each independently shippable, each closes with a smoke test on entech.

3. Architecture overview

                                                                    ┌────────────────────┐
              Tree Editor (existing OWL)        ─────reads/writes──▶│                    │
                                                                    │   Recipe data:     │
              Simple Editor (12a, new OWL)      ─────reads/writes──▶│  fusion.plating.   │
                                                                    │   process.node     │
              Step Library (12a, new model)     ─────snapshots─────▶│   (hierarchical,   │
                                                                    │   _parent_store)   │
                                                                    │                    │
              Recipe Template (12a, is_template)─────snapshots─────▶│  + new fields:     │
                                                                    │  is_template       │
                                                                    │  source_template_id│
                                                                    │  tank_ids          │
                                                                    │  target ranges,    │
                                                                    │  units, kind,      │
                                                                    │  transition flags  │
                                                                    └─────────┬──────────┘
                                                                              │
                                                                              ▼
                                                              Same job-creation flow
                                                              (no runtime change)
                                                                              │
                                                                              ▼
                                                                    ┌────────────────────┐
              Tablet (existing OWL)             ─────operates on───▶│   fp.job.step      │
                  + Move Parts / Move Rack (12b)                    │   + new fields:    │
                  + Rack Parts sub-dialog (12b)                     │    current_rack_id │
                  + Stop Timer dialog (12b)                         │    is_racked       │
                  + Soft/Hard block UX (12b)                        │    qty_at_step_*   │
                                                                    └─────────┬──────────┘
                                                                              │
                                                                              ▼
                                                                    ┌────────────────────┐
                                                                    │  fp.job.step.move  │
                                                                    │  (12b, NEW)        │
                                                                    │  fp.rack (12b, NEW)│
                                                                    │  fp.labor.timer    │
                                                                    │  (12b, NEW)        │
                                                                    └─────────┬──────────┘
                                                                              │
                                                                              ▼
              Operator Traveller PDF (12c)      ◀─renders from ────  recipe-order step list
              Customer CoC PDF (12c)            ◀─renders from ────  chronological move log
              Labor History screen (12c)        ◀─lists ───────────  fp.labor.timer

Hard rule preserved: every change is additive at the data layer. No FK drops, no model deletions, no ACL relaxations. Tree editor + every existing battle-test scenario + the Sub 11 MRP cutout + the Sub 12 (Quality / RMA) work all keep working unchanged.


4. Sub 12a — Simple Recipe Editor + Step Library

4.1 Scope

Recipe authoring only. No runtime/tablet/report changes. Estimated 4 days.

Customer outcome: authors can build / edit / clone recipes via a flat drag-drop editor with a step library on the side. They can flag any recipe as a starter template and import its full step list into a new recipe (snapshot copy). Tree editor untouched.

4.2 Data model

New model: fp.step.template (the reusable step library)

Field Type Notes
name Char, required, translate "Solvent Clean"
code Char optional short code, auto-uppercased
description Html rich-text instructions / WI reference
icon Selection reuses the 24-icon list from fusion.plating.process.node
tank_ids M2M fusion.plating.tank allowed stations ("Stations" column in screen 1)
process_type_id M2O fusion.plating.process.type bath / chemistry tie
material_callout Char "MID PHOS" — short string for traveller print; defaults to process_type_id.name
time_min_target Float lower bound (time_unit-aware)
time_max_target Float upper bound
time_unit Selection (sec / min / hr) default min
temp_min_target Float lower bound (temp_unit-aware)
temp_max_target Float upper bound
temp_unit Selection (F / C) default F
voltage_target Float (optional) electrolytic
viscosity_target Float (optional) bath quality
requires_signoff Boolean "Require QA"
requires_predecessor_done Boolean S14 lock support
requires_rack_assignment Boolean step-type flag → triggers Rack Parts sub-dialog at runtime
requires_transition_form Boolean step-type flag → opens transition form before Mark Done
input_template_ids O2M fp.step.template.input operation measurements
transition_input_ids O2M fp.step.template.transition.input compliance fields collected at move-time
default_kind Selection cleaning / etch / rinse / plate / bake / inspect / racking / derack / mask / demask / dry / wbf_test / final_inspect / ship / gating — drives sane-defaults seeding
active, sequence, company_id (standard)

New model: fp.step.template.input — operation measurements (recorded during a step)

Field Type Notes
name Char, required e.g. "Soak Clean Time"
template_id M2O fp.step.template parent
input_type Selection text / number / boolean / selection / date / signature / time_hms / time_seconds / temperature / thickness / pass_fail (typed inputs auto-format on the report)
target_min Float structured target lower bound
target_max Float structured target upper bound
target_unit Char "min" / "ºF" / "A" / "FT2" / "in"
required Boolean hard-block sign-off if blank
hint Char inline help
selection_options Text comma-separated when input_type='selection'
sequence Integer render order

New model: fp.step.template.transition.input — compliance fields collected when leaving a step

Field Type Notes
name Char, required "Customer WO #" / "Photo Evidence" / "Scrap Reason"
template_id M2O fp.step.template parent
input_type Selection text / number / boolean / selection / date / signature / photo / location_picker / customer_wo
required Boolean hard-block move if blank
hint Char
selection_options Text
sequence Integer
compliance_tag Selection none / as9100 / nadcap / cgp / nuclear — drives audit report filter

Changes to existing fusion.plating.process.node (all additive — zero impact on tree editor / runtime)

Field Type Notes
is_template Boolean, default False marks a recipe (when node_type='recipe') as a starter template
source_template_id M2O fp.step.template, optional, indexed snapshot trace; set when a node is created by dragging a library step in
tank_ids M2M fusion.plating.tank mirrors what library steps carry
material_callout Char mirrors library
time_min_target, time_max_target, time_unit (mirrors library)
temp_min_target, temp_max_target, temp_unit (mirrors library)
voltage_target, viscosity_target (mirrors library)
requires_rack_assignment, requires_transition_form Boolean mirrors library
default_kind Selection mirrors library; auto-set when imported
transition_input_ids O2M to existing fusion.plating.process.node.input (filtered by new kind field) one model, two roles
preferred_editor Selection (tree / simple / auto) per-recipe editor choice (only meaningful when node_type='recipe')

Changes to fusion.plating.process.node.input:

  • Add kind Selection (step_input / transition_input), default step_input (existing rows backfill via post_init_hook).
  • Add the same target-range fields as fp.step.template.input.

New: res.config.settings.default_recipe_editor — Selection (tree / simple), default tree. Drives "New Recipe" button's editor choice.

4.3 Simple Recipe Editor UI (OWL client action)

Registered as fp_simple_recipe_editor in web.client_actions. Single-page, full-screen, tablet-friendly.

┌──────────────────────────────────────────────────────────────────────┐
│  ← Back   Recipe: ENP-ALUM-BASIC      [Tree Editor]  [Save]  [More ▾]│
├──────────────────────────────────────────────────────────────────────┤
│  Title           [ ENP-ALUM-BASIC                                  ] │
│  Code            [ ENP_ALUM                                        ] │
│  Coating Config  [ Electroless Nickel Mid-Phos    ▾ ]                │
│  Part            [ — none —                       ▾ ]                │
│  ☐ Use as starter template                                           │
│                                                                      │
│  Import starter from template:  [ Select template ▾ ]   [Import]     │
├──────────────────────────────────────────────────────────────────────┤
│ ┌─ Selected (drag to reorder) ─────────┐ ┌─ Step Library ──────────┐ │
│ │ ⠿ 1. Part Verification      [Edit ▾] │ │  🔍 [Search…]            │ │
│ │ ⠿ 2. Solvent Clean   SP-1   [Edit ▾] │ │  Acid Dip                │ │
│ │ ⠿ 3. Soak Clean      SP-1   [Edit ▾] │ │  Bake                    │ │
│ │ ⠿ 4. Rinse           SP-2   [Edit ▾] │ │  E-Nickel Plate          │ │
│ │ ...                                  │ │  ...                     │ │
│ │ [+ Add Inline Step]                  │ │  [+ New Library Step]    │ │
│ └──────────────────────────────────────┘ └──────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘

Behavior:

  • Drag from Library → Selected: snapshot-copies the library step into the recipe as a new fusion.plating.process.node (node_type=step, source_template_id set, all author-defined fields copied, sane-default input_template_ids copied, all transition_input_ids copied).
  • Drag within Selected: reorders by updating sequence.
  • Drag out / X button: removes the step from the recipe (unlinks the node).
  • Station picker per Selected row: dropdown of the step's tank_ids M2M.
  • Edit ▾ per row: expands inline panel with Title, Stations, Operation Measurements, Instructions, Require QA + an Advanced expander (icon, time/temp targets, voltage, viscosity, material callout, requires_predecessor_done, requires_rack_assignment, requires_transition_form, transition inputs).
  • + Add Inline Step: creates a one-off step in the recipe without touching the library.
  • + New Library Step: side panel to author a new fp.step.template.
  • Import starter from template: dropdown of fusion.plating.process.node where is_template=True AND node_type='recipe'. Snapshot-copies all child steps preserving sequence. Confirms before replacing existing steps.
  • [Tree Editor] button: switches to the existing tree editor on the same recipe.
  • Auto-save every 5s when dirty + explicit Save.
  • Mobile/tablet responsive: <900px width stacks columns.

Search/filter: case-insensitive substring match against name + code + description (plain-text).

Drag-drop: HTML5 native dragstart/dragend, reuses helpers from existing tree editor.

Soft-validation for predecessor lock: a Selected row with requires_predecessor_done=True placed before its predecessor highlights amber with a tooltip. Doesn't block save (S14 enforces at runtime), informs the author.

4.4 Backend controller endpoints

New file: fusion_plating/controllers/simple_recipe_controller.py. JSONRPC routes:

Route Purpose
POST /fp/simple_recipe/load recipe header + ordered step list
POST /fp/simple_recipe/library/list all fp.step.template (search-filtered, company-scoped)
POST /fp/simple_recipe/library/create new template
POST /fp/simple_recipe/library/write update
POST /fp/simple_recipe/library/delete unlink (soft if any node references it via source_template_id, hard otherwise)
POST /fp/simple_recipe/step/insert insert (from library or blank) at position N
POST /fp/simple_recipe/step/write inline edit
POST /fp/simple_recipe/step/remove unlink from recipe
POST /fp/simple_recipe/step/reorder bulk sequence update after drag-drop
POST /fp/simple_recipe/template/list recipes where is_template=True (for Import dropdown)
POST /fp/simple_recipe/template/import snapshot-copy all child nodes of a template recipe

All endpoints honor company multi-tenancy + ACL (group_fusion_plating_supervisor for write, _operator for read).

4.5 Recipe form integration

On fusion.plating.process.node form view (when node_type='recipe'):

  • Header buttons: Open in Simple Editor + Open in Tree Editor.
  • New "Editor Preference" Selection field (tree / simple / auto).
  • Clicking a recipe in the menu list routes through preferred_editor (falls back to company default_recipe_editor if auto).
  • "Use as starter template" checkbox surfaces is_template (visible to supervisors only).

Menu integration:

  • Plating → Operations → Process Recipes — existing list view; clicks route through preferred editor.
  • Plating → Configuration → Step Library — NEW; CRUD on fp.step.template.

4.6 Sane-default input seeding per default_kind

default_kind Suggested input_template_ids
cleaning Actual Time (time_seconds, "sec") + Actual Temperature (temperature, "°F")
etch Actual Time + Actual Temperature
rinse (none — sign-off only)
plate Actual Time (time_hms, "min") + Actual Temperature + Plating Thickness (thickness, "in")
bake Time In (text "HH:MM") + Time Out (text "HH:MM") + Actual Temperature
racking Actual Qty (number, "each")
derack Actual Qty
inspect PASS/FAIL (pass_fail)
final_inspect Outgoing Part Count Verified (boolean) + Qty Accepted (number, "each") + Qty Rejected (number, "each") + Actual Coating Thickness (thickness, "in") + Pass/Fail
wbf_test PASS/FAIL
mask Actual Qty
demask (none)
dry (none)
ship Outgoing Qty (number, "each")
gating (none)

Implemented via server method fp.step.template._seed_default_inputs(self), idempotent, exposed as a "Seed Defaults" button on the library form.

4.7 Migration / install

Module: extend fusion_plating core (no new module). Bump to 19.0.10.0.0.

post_init_hook for 12a:

  • Backfills kind='step_input' on all existing fusion.plating.process.node.input rows.
  • Seeds fp.step.template with 18 starter templates copied from the existing ENP-ALUM-BASIC recipe's child steps (Soak Clean, Rinse, Etch, Desmut, Zincate, Strip Zincate, Electroclean, Acid Dip, Water Break Test, Issue Panels, Racking, E-Nickel Plate, Hot Rinse, Drying, De-rack, Inspection, Final Inspection, Shipping). Each gets default_kind set + sane-default inputs seeded. Idempotent — won't re-seed if any fp.step.template rows already exist.

4.8 Verification (smoke test on entech staging)

  1. Install module → step library auto-seeds 18 templates.
  2. Plating → Configuration → Step Library — confirm 18 entries with sane-default inputs.
  3. Plating → Operations → Process Recipes → New Recipe → Simple Editor.
  4. Drag 5 library steps in, reorder via drag-drop, pick stations.
  5. Save, close, reopen — data persists, sequence correct.
  6. Click [Tree Editor] — same recipe opens in tree editor with all 5 steps under root. Edit step 3's name in tree editor, save, return to Simple Editor — change visible.
  7. Mark a recipe as template, build a new recipe, "Import starter from template" — all steps copy in, snapshot.
  8. Edit the original library step's name → previously-imported recipe steps DO NOT change (snapshot decoupling).
  9. Run bt_s2_* battle tests on a job built from a Simple-Editor recipe — confirm runtime unaffected.

5. Sub 12b — Move Parts / Move Rack Dialogs + Tablet Transition Capture

5.1 Scope

Tablet UX + transition-time data capture. Uses 12a's authored data; no new recipe-authoring UI. Estimated 34 days.

Customer outcome: operators on the tablet get the Steelhead-style Move Parts / Move Rack flow with author-defined compliance prompts, station picker, photo evidence, and the soft/hard block UX — but with our improvement: every blocker has a clickable resolution button.

5.2 Data model

New: fp.rack — physical rack registry

Field Type Notes
name Char, required "Rack 3"
code Char, required "R-03"
qr_code Char scannable; defaults to FP-RACK:<code>
state Selection empty / loading / loaded / in_use / awaiting_unrack / out_of_service
current_part_count Integer (compute) sum of part-batches currently on rack
current_job_step_id M2O fp.job.step (compute) current location
current_tank_id M2O fusion.plating.tank (compute) derived from current step
tag_ids M2M fp.rack.tag rack labels
facility_id, work_center_id M2O location
material Selection (steel / titanium / polypro / pvc / plastic / other) construction
capacity_count Integer max parts (soft warn)
notes Text maintenance / damage notes
active, company_id (standard)

New: fp.rack.tag — rack labels (M2M tag registry)

Field Type Notes
name Char, required "Rush" / "Customer-Amphenol" / "Hold-for-QC"
color Integer kanban color

New: fp.job.step.move — chain-of-custody transition log (one row per move)

Field Type Notes
name Char, sequence FP/MOVE/YYYY/NNNN
job_id M2O fp.job
from_step_id M2O fp.job.step source
to_step_id M2O fp.job.step destination
from_tank_id M2O fusion.plating.tank derived
to_tank_id M2O fusion.plating.tank operator's choice on multi-station node
transfer_type Selection step / hold / scrap / rework / split / return
qty_moved Integer partial-qty supported
qty_available_at_move Integer snapshot
to_location Selection (global / quarantine / staging_a / staging_b / shipping_dock / scrap_bin)
photo_evidence_id M2O ir.attachment inline-captured photo
customer_wo_count Integer optional
rack_id M2O fp.rack populated on rack-aware moves
unrack_after_move Boolean for derack steps
moved_by_user_id M2O res.users
move_datetime Datetime
transition_input_value_ids O2M fp.job.step.move.input.value compliance values captured
chatter mail.thread yes

New: fp.job.step.move.input.value — recorded transition-input values

Field Type Notes
move_id M2O fp.job.step.move parent
template_input_id M2O fp.step.template.transition.input what was asked (template-level)
node_input_id M2O fusion.plating.process.node.input snapshot of the authored prompt at job-creation time
value_text Char for text/selection
value_number Float for number
value_boolean Boolean for boolean
value_date Datetime for date
value_attachment_id M2O ir.attachment for photo/signature

New: fp.labor.timer — persistent labor timer (lifted from screens 9, 10)

Field Type Notes
name Char, sequence FP/TIMER/YYYY/NNNN
user_id M2O res.users operator
job_id M2O fp.job
step_id M2O fp.job.step step at start
state Selection running / paused / stopped / reconciled
started_at, last_paused_at, stopped_at Datetime
total_paused_duration Float (compute) sum of pauses
accrued_seconds Integer (compute) live for running, frozen otherwise
billed_hrs / billed_min / billed_sec Integer reconciled, default = accrued, editable on stop
billed_pct Float (compute) billed / accrued
product_id M2O product.product optional split-target product
notes Text
chatter mail.thread yes

Lifecycle: running → paused → running → stopped → reconciled. Stop Timer dialog (screen 10) opens on stop and lets the operator reconcile billed hrs/min/sec + optional product split (creating sibling timer rows).

Changes to existing fp.job.step:

Field Type Notes
move_ids O2M fp.job.step.move (inverse from_step_id) history of moves out of this step
current_rack_id M2O fp.rack snapshot of rack on this step (when racked)
is_racked Boolean (compute, stored) current_rack_id != False
qty_at_step_start Integer sum of incoming move qty
qty_at_step_finish Integer sum of outgoing move qty

Changes to fp.job:

Field Type Notes
qty_received Integer from screen 16 traveller header
qty_visual_inspection_rejects Integer
qty_rework Integer
special_requirements Text from customer spec
active_timer_ids O2M fp.labor.timer (inverse job_id, filtered by state) for live displays

5.3 Move Parts dialog

Trigger: operator taps Move Parts on a part-batch row in the tablet, OR scans a part QR while at a step.

┌────────────────────────────────────────────────────────┐
│  Move Parts                                            │
├────────────────────────────────────────────────────────┤
│  Part Count    [ 49 ]  Available: 49                   │
│  Part Number   TEST225451 (link)                       │
│  From Node     Bake (link)                             │
│  From Station  Bake (link)         ← shown if applicable│
│  Transfer Type [ Step ▾ ]                              │
│  To Node       Adhesion Testing (link)                 │
│  To Station    [ Adhesion Testing ▾ ] ← shown if multi │
│  To Location   [ Global ▾ ]    📷 ← inline camera       │
│                                                        │
│  ── Compliance Prompts (author-defined) ──             │
│  • Customer WO #     [ 731830           ]              │
│  • Photo Evidence    [ Attach 📷         ]              │
│  • Scrap Reason      [ none ▾ ]                        │
│                                                        │
│  ─── Blockers ──────────────────────────────────       │
│  ⚠ Additional Spec Measurements required.              │
│      [ RECORD MEASUREMENTS ]   ← OUR IMPROVEMENT       │
│                                                        │
│  Billed Labor   ⏱   [Reset All Edits]                  │
│   Kris Pathinather  Timer: 7s (100% billed)            │
│   WO #4521 PN: TEST225451 Qty: 49   0 hrs 0 min 7 sec │
│                                                        │
│   [ Cancel ]                          [ MOVE (49) ]    │
└────────────────────────────────────────────────────────┘

Behavior:

  • System-derived top section: Part Count (editable, max=available), Part Number, From Node, From Station (only if source step has a tank), Transfer Type, To Node, To Station (only if destination step's tank_ids count > 1), To Location.
  • Camera button next to To Location: launches getUserMedia on tablet → captures photo → uploads as ir.attachment → links to the move.
  • Compliance Prompts section: renders the destination step's transition_input_ids (snapshot from authored template). required=True prompts hard-block MOVE.
  • Blockers section (NEW pattern, our improvement over Steelhead): a list of resolvable issues, each with an inline action button:
    • "Spec measurements required" → opens spec input dialog inline → re-evaluates → clears.
    • "Parts not racked" → opens Rack Parts sub-dialog (5.4).
    • "Predecessor not done" (S14) → opens predecessor step's checklist.
    • "Operation measurements missing" → opens operation input form on source step.
  • MOVE button state: enabled only when all hard-blockers cleared + all required transition inputs filled. Disabled state shows tooltip listing blockers (improvement over Steelhead's silent disabled state).
  • Billed Labor section: surfaces the operator's active fp.labor.timer for this WO with editable hrs/min/sec.
  • MOVE click: creates fp.job.step.move, copies transition-input values, advances the part-batch, stops the active timer, advances qty_done on source step + qty_at_step_start on dest step.

5.4 Rack Parts sub-dialog

Trigger: from Move Parts when destination has requires_rack_assignment=True and operator hasn't picked a rack → "RACK PARTS" button.

┌────────────────────────────────────────────┐
│  Rack Parts                       [QR scan]│
├────────────────────────────────────────────┤
│  To Rack    [ Search Racks…           ▾ ]  │
│                                            │
│  Part Number    Unit       Amount          │
│  TEST225451     [ Count ▾] [ 49 ] Count    │
│   on WO 4521 (49)                          │
│                                            │
│  Billed Labor   ⏱                          │
│                                            │
│   [Cancel]   [Save]   [Save + Print]       │
└────────────────────────────────────────────┘
  • Rack picker filters to state='empty' by default; "show all" toggle bypasses.
  • QR scan button: tablet camera + parses FP-RACK:<code> → auto-fills "To Rack".
  • Save: marks rack state='loaded', sets current_job_step_id, returns to Move Parts dialog with rack populated, blocker cleared.
  • Save + Print: same + prints rack travel ticket.
  • Unit + Amount: defaults Count + Move Parts' Part Count. Editable for partial racking.

5.5 Move Rack dialog

Trigger: operator taps MOVE RACK on a rack row in the tablet.

┌────────────────────────────────────────────────────────┐
│  Move Rack: tyut                                       │
├────────────────────────────────────────────────────────┤
│  Rack Labels  [ Rush ✕ ] [ + ]                         │
│  Parts                                                 │
│   • 49 TEST225451 Parts on WO 4521                     │
│   • 1 TEST225451 Parts on WO 4521                      │
│                                                        │
│  Type         [ Step ▾ ]                               │
│  To Node      [ Soak Clean (SP-1)  ▾ greyed ]          │
│  To Station   [ Soak Clean (SP-1)  ▾ ]                 │
│                                                        │
│  Billed Labor   ⏱   [Reset All Edits]                  │
│   Kris Pathinather  Timer: 6s (100% billed)            │
│   WO #4521 PN: TEST225451 Qty: 49   0 hrs 0 min 6 sec │
│   WO #4521 PN: TEST225451 Qty: 1    0 hrs 0 min 0 sec │
│                                                        │
│   [Cancel]                            [Save]           │
└────────────────────────────────────────────────────────┘
  • Rack name in title from fp.rack.name.
  • Parts list read-only.
  • To Node auto-derived (greyed); To Station shown only if multi-station.
  • Per-batch billed-labor split for each batch.
  • Save atomically creates one fp.job.step.move per batch, all linked by the same rack_id.

5.6 Stop User Labor Timer dialog

Trigger: operator taps the timer pause icon on the tablet without moving parts.

Mirror screen 10. Reconcile billed hrs/min/sec + optional product split. Footer: Cancel / Save / Save & Start New Timer.

5.7 Tablet runtime guards

Per-batch row in tablet station view:

  • current_rack_id set → MOVE PARTS button disabled / greyed with tooltip "Racked — use Move Rack instead."
  • current_rack_id empty → MOVE PARTS enabled.

Plant overview gets two panes (mirror screen 12):

  • Top: Racks with MOVE RACK per row + bulk UNRACK MULTIPLE.
  • Bottom: Parts with MOVE PARTS per row (greyed when racked) + + ADD NEW PARTS + filters + search.

Soft/hard block protocol (our protocol — improves on Steelhead):

  • Amber banner + button enabled = soft (proceed with audit).
  • Amber banner + button disabled + tooltip listing blockers = hard.
  • Every blocker carries an inline resolution button that opens a form to resolve without leaving the dialog.
  • Dialog re-evaluates blockers reactively after each resolution.

5.8 Backend controller endpoints

Extend fusion_plating_jobs/controllers/tablet_controller.py:

Route Purpose
POST /fp/tablet/move_parts/preview dialog payload (system fields + author prompts + blockers)
POST /fp/tablet/move_parts/commit creates fp.job.step.move, advances qty, handles timer
POST /fp/tablet/move_rack/preview multi-batch dialog payload
POST /fp/tablet/move_rack/commit atomic multi-batch move tied to a rack
POST /fp/tablet/rack_parts/commit assigns parts to a rack
POST /fp/tablet/rack_parts/print prints rack travel ticket
POST /fp/tablet/labor_timer/start start
POST /fp/tablet/labor_timer/pause pause
POST /fp/tablet/labor_timer/resume resume
POST /fp/tablet/labor_timer/stop stop and open reconciliation
POST /fp/tablet/labor_timer/reconcile save reconciled billed time + optional product split

ACL: group_fusion_plating_operator minimum.

5.9 Migration / install

Same module: extend fusion_plating core. Bump to 19.0.10.1.0.

post_init_hook for 12b:

  • Seeds fp.rack.tag with 4 starter tags: "Rush", "Hold for QC", "Damaged", "Customer Sample".
  • Backfills fp.job.step.qty_at_step_start from existing qty_done chain (idempotent).

No data destruction. No FK drops.

5.10 Verification (smoke test on entech staging)

  1. Open tablet, scan a part QR → tablet shows the part-batch row at its current step.
  2. Tap Move Parts → dialog opens with system fields populated, dest step's authored transition prompts rendered.
  3. Try MOVE with required prompt blank → button disabled, tooltip lists the blank prompt.
  4. Fill prompt → MOVE re-enables.
  5. Move to a step with requires_rack_assignment=True → amber blocker + RACK PARTS button.
  6. Click RACK PARTS → sub-dialog → pick rack → save → blocker clears, MOVE re-enables.
  7. Complete MOVE → fp.job.step.move row created, part-batch advanced, rack state updated.
  8. Confirm tablet now shows MOVE PARTS button greyed out + MOVE RACK shown on the rack row.
  9. Tap MOVE RACK → dialog with all batches → save → all advance atomically.
  10. Tap timer pause → Stop Timer dialog → reconcile billed time → save → state moves to reconciled.
  11. Run bt_s1_* through bt_s17_* battle tests — confirm no regressions on existing flows.

6. Sub 12c — Reports + Persistent Labor Audit

6.1 Scope

Two PDF templates + a labor history screen. No new model surface — uses 12a + 12b data. Estimated 34 days.

Customer outcome:

  • Operator Traveller PDF: recipe-order, paper-style A4 landscape. Equivalent to Amphenol paper sheets (screens 1618).
  • Customer CoC Traveller PDF: chronological audit, branded, Nadcap stamped. Equivalent to Steelhead's CoC output (screens 1924).
  • Labor History screen: surfaces fp.labor.timer for billing audit, payroll reconciliation, and "who-was-on-what-when" forensics.

6.2 Operator Traveller Report

File: fusion_plating_jobs/report/report_fp_job_traveller_v2.xml Layout: A4 landscape, multi-page, table-driven. Replaces: existing report_fp_job_traveller.xml (S5/S18 minimal portrait — kept as fallback).

Page structure mirrors Amphenol paper sheets:

  • Header (every page): logo, WO# + barcode (Code 128), Date In, Due Date, Type, Order #, P.O. #, Customer + address.
  • Item Information (page 1): Part #, Rev., Mat., Catg., S/N, Item-Name / Process Description, Qty Rec., Vis Insp, Rework, Special Requirements, Stamp/Date.
  • Process-Sheet Header (page 1): recipe name, sub-process name, category, special req.
  • Step Table (continues page-to-page): Step | Tank | Operation + Actual | Instruction | Unit | Material | Voltage | Viscosity | Time(min) | Temp | Stamp | Date.
  • Footer (every page): WO# + Page n of total.

Data source: walks fp.job.step ordered by sequence (recipe order). Each row pulls from the step's authored fields (target ranges, units, material callout) + leaves blank lines for the operator to pencil in actuals.

ir.actions.report: action_report_fp_job_traveller_v2, paperformat A4 landscape. Smart button on fp.job form: "Print Operator Traveller".

6.3 Customer CoC Traveller Report

File: fusion_plating_certificates/report/report_fp_certificate_coc_v2.xml Layout: A4 portrait, multi-page, table-driven. Extends: existing fp.certificate flow (S18/S19) — replaces minimal CoC body with chronological audit body.

Page structure mirrors Steelhead's CoC (screens 1924):

  • Header (page 1): Part Number, Description, Quantity, WO#, PO#, Packing List, Date, Specification(s).
  • Body: chronological list of step transitions, each rendered as:
    • Step heading: <step.name> (<tank.code>).
    • "Part Number / Moved By / Time" line.
    • If the step has captured input values, a 5-column table: Name | Description | Target | Actual | Recorded By.
  • Last page: 2-column sign-off block (left = signed image + name, right = cert statement + comments). Footer: Nadcap logo + ENTECH logo + "Cert Created At: " + page n/total.

Critical improvements over Steelhead:

  1. Target column is its own column (Steelhead embeds "(5-10 min.)" in the input name).
  2. Out-of-range Actual values colour-coded red, in-range green.
  3. Heading-only steps (Ready For X, gating) render as compact 1-line transitions — no empty tables.
  4. Multi-day jobs: each transition carries its own datetime; the report walks chronologically.
  5. Signature image from fp.certificate.signoff_user_id.x_fc_signature.
  6. Configurable per-company: Nadcap logo + brand logo as res.company.x_fc_nadcap_logo + x_fc_company_brand_logo (fall back to Fusion Plating logo).

Data source: walks fp.job.step.move records ordered by move_datetime, NOT fp.job.step records ordered by sequence. Chain-of-custody view auditors expect.

For each move:

  • Render heading: <from_step.name> (<from_tank.code>) or <to_step.name> (<to_tank.code>).
  • Render "Part Number / Moved By / Time".
  • If destination step had input_template_ids filled at move time, render the measurement table.

ir.actions.report: action_report_fp_certificate_coc_v2, paperformat A4 portrait. Smart button on fp.certificate form: "Generate Customer CoC PDF".

Existing CoC merge with Fischerscope thickness PDF (S19) is preserved — _fp_render_and_attach_pdf keeps appending the Fischerscope page.

6.4 Labor History Screen

Menu: Plating → Operations → Labor History.

View: list view on fp.labor.timer, grouped by user_id then job_id.

Columns: Operator | Job | Step | State (badge) | Started | Stopped | Accrued (HH:MM:SS) | Billed | Billed % (bar) | Product.

Filters: My timers / Today / This week / Reconciled / Pending reconciliation / By operator / By customer.

Group-by: Operator / Job / Customer / Date.

Form view: read-only timer history with chatter for operator notes. Manager-only fields:

  • billed_hrs / billed_min / billed_sec editable (audit-logged).
  • "Re-open for re-reconciliation" button — moves a reconciled timer back to stopped.

ACL:

  • Operator: read own timers, write own running/paused/stopped timers.
  • Supervisor: read all team timers, write reconciliations.
  • Manager: full edit including re-open.

6.5 Backend support

Extend fusion_plating_certificates/models/fp_certificate.py:

  • New method _fp_build_chronological_payload(self) returns the ordered list of moves + measurement values for the QWeb template.
  • Existing _fp_render_and_attach_pdf calls it and assembles the multi-page PDF.
  • Existing Fischerscope merge logic (S19) untouched.

Extend fusion_plating_jobs/models/fp_job.py:

  • Property traveller_v2_step_payload returns ordered step list with target ranges + author-defined inputs for the operator traveller QWeb template.

No new endpoints.

6.6 Migration / install

Bumps:

  • fusion_plating19.0.10.2.0
  • fusion_plating_jobs19.0.7.0.0
  • fusion_plating_certificates19.0.6.0.0

post_init_hook for 12c:

  • Creates paperformat.fp_a4_landscape_traveller.
  • Sets fp.certificate.report_template_id = action_report_fp_certificate_coc_v2 so existing certs auto-use the new layout. Old report_fp_certificate_coc.xml stays in place as fallback.
  • Bumps fp.certificate.version field on existing rows (drives "regenerate PDF" prompt).
  • Adds res.company.x_fc_nadcap_logo + x_fc_company_brand_logo placeholders.

6.7 Verification (smoke test on entech staging)

  1. Open an in-flight job. Click Print Operator Traveller → A4 landscape PDF renders with header, item info, process-sheet header, step table with target ranges + blank actual lines + tank codes.
  2. Verify Code 128 barcode on header reads correctly with a phone scanner.
  3. Take a completed job (ENP-ALUM-BASIC, ~25 transitions). Open its fp.certificate. Click Generate Customer CoC PDF → portrait PDF renders chronologically with WO header, transitions in time order, per-step measurement tables with Target + Actual columns, sign-off block at end with signature + Nadcap + ENTECH logos.
  4. For a step where actual was out-of-spec (soak clean ran 12 min, target 4-6) → confirm Actual cell renders red.
  5. Run bt_s19_fischer_merge.py → confirm Fischerscope PDF appends as page N+1.
  6. Plating → Operations → Labor History → see all timers from smoke-test jobs. Group by Operator → expand → see hrs/min/sec breakdown. Verify reconciliation form opens for a stopped timer.
  7. Run all existing battle tests (bt_s1 through bt_s17) — no regressions.

6.8 Things to NOT do in 12c

  • Don't replace report_fp_certificate_coc.xml (legacy) — keep as fallback.
  • Don't touch fp.certificate.action_issue flow — only the rendering layer changes.
  • Don't add new model fields — 12a + 12b shipped everything we need.
  • Don't try to merge Operator Traveller and Customer CoC into one report — different audiences, different layouts.
  • Don't bake the cert statement into the CoC template — read from fp.certificate.cert_statement so it stays per-customer configurable.

7. Cross-cutting concerns

7.1 Manager-bypass context flags (preserved)

The existing manager-bypass context-flag protocol stays as-is. New flags added by 12b:

Flag Skips
fp_skip_transition_form=True required transition input check on Move Parts
fp_skip_rack_assignment=True rack-assignment check on requires_rack_assignment steps

All bypasses post to chatter with user name for audit (consistent with existing flags).

7.2 ACL changes

  • group_fusion_plating_supervisor gets write on fp.step.template + fp.rack + fp.rack.tag.
  • group_fusion_plating_operator gets read on fp.step.template + fp.rack, write on fp.job.step.move + fp.labor.timer (own only).
  • group_fusion_plating_manager gets full access on all new models, plus the Re-open Reconciled Timer action.

7.3 Multi-company

All new models carry company_id. All new controllers honor request.env.company. Step library is company-scoped by default — a multi-shop customer authoring a recipe in Shop A doesn't see Shop B's library. A "shared library" cross-company toggle is out of scope (YAGNI).

7.4 Performance

  • fp.step.template is a small table (< 1000 rows expected).
  • fp.rack is small (< 100 rows expected).
  • fp.job.step.move will be the biggest new table (~510 rows per job × 1000s of jobs/year = 50k rows/year). All key queries index on (job_id, move_datetime).
  • fp.labor.timer similar volume. Index on (user_id, state, started_at).
  • Simple Editor's library/list endpoint paginates server-side at 50 rows + supports search (no client-side filter on full library).

7.5 Backwards compatibility

  • Tree editor: 100% unchanged.
  • Existing ENP-ALUM-BASIC recipe: keeps working, retroactively gets is_template=True if customer wants (via post-install patch).
  • Existing battle tests S1S17 + S18/S19 cert flow: all keep working.
  • Existing CoC report: stays as fallback. New CoC report is opt-in per customer.
  • Existing tablet flows: keep working. New Move dialogs are opt-in via requires_transition_form flag on step templates (default False = legacy one-tap behavior).

8. Build order (single-session executable checklist)

  1. Read this design + the screen inventory.
  2. Confirm Sub 11 (MRP cutout) + Sub 12 quality-native work + Subs 110 fine-tuning all shipped. Check fusion_plating version ≥ 19.0.9.3.0 (after the tank state-control work shipped this session).
  3. Sub 12a (recipe authoring):
    1. Bump fusion_plating/__manifest__.py to 19.0.10.0.0.
    2. Add models: fp.step.template + fp.step.template.input + fp.step.template.transition.input.
    3. Extend fusion.plating.process.node with the additive fields.
    4. Extend fusion.plating.process.node.input with kind + target-range fields.
    5. Build OWL fp_simple_recipe_editor client action + SCSS + XML template.
    6. Build simple_recipe_controller.py JSONRPC endpoints.
    7. Recipe form: header buttons + preferred_editor field + is_template checkbox.
    8. Menu: Plating → Configuration → Step Library.
    9. res.config.settings.default_recipe_editor field.
    10. post_init_hook: backfill kind='step_input' + seed 18 starter library templates from ENP-ALUM-BASIC.
    11. Smoke test → deploy → verify on entech.
  4. Sub 12b (tablet move/rack/timer):
    1. Bump fusion_plating/__manifest__.py to 19.0.10.1.0.
    2. Add models: fp.rack + fp.rack.tag + fp.job.step.move + fp.job.step.move.input.value + fp.labor.timer.
    3. Extend fp.job.step + fp.job with the new fields.
    4. Build OWL Move Parts dialog + Move Rack dialog + Rack Parts sub-dialog + Stop Timer dialog (extend tablet OWL).
    5. Extend tablet plant-overview pane: Racks section + Parts section.
    6. Build tablet_controller.py extension: 11 new endpoints.
    7. Implement runtime guards (rack-vs-parts duality, soft/hard block).
    8. post_init_hook: seed 4 starter rack tags + backfill qty_at_step_start.
    9. Smoke test → deploy → verify on entech.
    10. Run bt_s1_* through bt_s17_* — confirm no regressions.
  5. Sub 12c (reports + labor history):
    1. Bump fusion_plating19.0.10.2.0, fusion_plating_jobs19.0.7.0.0, fusion_plating_certificates19.0.6.0.0.
    2. Build report_fp_job_traveller_v2.xml + ir.actions.report + paperformat.fp_a4_landscape_traveller + smart button on fp.job.
    3. Build report_fp_certificate_coc_v2.xml + extend _fp_render_and_attach_pdf to use chronological payload + extend _fp_build_chronological_payload.
    4. Build Labor History screen: list + form + filters + group-by + ACL.
    5. Add res.company.x_fc_nadcap_logo + x_fc_company_brand_logo placeholders.
    6. post_init_hook: paperformat creation + cert template wiring.
    7. Smoke test → deploy → verify on entech.
    8. Run bt_s19_fischer_merge.py — confirm Fischerscope PDF appends.

Each sub-project deploys independently with its own version bump and -u command. If 12b reveals issues, 12a stays shipped. If 12c reveals issues, 12a and 12b stay shipped.


9. Things to NOT do (cross-cutting)

  • Don't touch the existing tree editor, its OWL file, or its 7 endpoints. Sub 12a's simple editor lives alongside, not on top.
  • Don't introduce a new module. All work extends existing modules: fusion_plating, fusion_plating_jobs, fusion_plating_certificates.
  • Don't fork the recipe data model. Both editors operate on the same fusion.plating.process.node records.
  • Don't make library imports live references. Every drag-drop creates an independent snapshot. Editing the library never mutates an in-flight recipe.
  • Don't break the S14 predecessor lock, S15 bake gate, S17 scrap auto-hold, S18 cert flow, or S19 Fischerscope merge. All of these continue to fire on jobs created from Simple Editor recipes.
  • Don't auto-install requires_rack_assignment=True or requires_transition_form=True on existing step templates. New flags default to False so existing tablet flows are unaffected. Customer opts in step-by-step.
  • Don't hardcode any company branding in PDFs. Logos read from res.company configurable fields with sensible fallbacks.
  • Don't introduce 'mrp' or 'quality_control' as a manifest dep. Sub 11 and Sub 12-quality removed them; this work doesn't bring them back.

10. Open items deferred to later sub-projects

  • Spec measurements as a standalone authoring surface (per part × step rule library) — surfaced by screen 15's hard-block warning. Belongs to a future "QC Spec Library" sub-project. For now, hard-block resolution opens whatever the existing spec input form is.
  • Multi-day audit chain on multi-shift jobs — chain-of-custody report renders correctly today. Future work: shift-aware grouping.
  • Bulk operations in Step Library (batch-edit station list across multiple steps) — out of scope for 12a. Customer can edit one at a time.
  • Step library import/export as YAML/JSON — useful for cross-environment promotion but out of scope. Customer copies templates by re-creating in target env.
  • First-off / last-off QC — already deferred from earlier sub-projects (S28-style); still deferred.
  • VEC machine auto-ingest — already deferred; still deferred.
  • Spec measurement dialog UI for hard-block resolution — built in a future sub-project. For now the resolution button opens the existing spec form.

11. References

  • Steelhead screen inventory — 24 screenshots, field-by-field
  • Existing tree editor: fusion_plating/static/src/js/recipe_tree_editor.js (649 lines), recipe_controller.py (367 lines)
  • Existing process node model: fusion_plating/models/fp_process_node.py (531 lines)
  • Battle test scenarios driving constraints: S5, S6, S7, S14, S15, S17, S18, S19, S20 in CLAUDE.md
  • Sub 11 cutout (MRP removal) decisions in CLAUDE.md § Sub 11
  • Sub 12 native quality work decisions in CLAUDE.md § Sub 12