Commit Graph

762 Commits

Author SHA1 Message Date
gsinghpal
21e42e7b48 feat(jobs): button_start auto-routes contract_review steps to QA-005
User feedback on WH/JOB/00341 (S00279 retest): clicking Start on
the Contract Review step changed state to in_progress but didn't
take them to QA-005. They had to then click Finish & Next twice
to land on the form — confusing flow.

Better UX: when an operator clicks Start on a step where
recipe_node.default_kind='contract_review', the step starts AND
the QA-005 form opens immediately. Operator signs/dismisses,
navigates back, hits Finish & Next once → step finishes + advances.

Implementation:
  fp.job.step.button_start, after super() returns and the
  receiving check runs, calls _fp_contract_review_redirect()
  (existing helper). If it returns an action, return that
  instead of the parent's result. Single-record only — bulk
  button_start (job-level start-all) shouldn't navigate.

Helper logic unchanged — same gate matrix:
  * recipe_node.default_kind == 'contract_review'
  * job has part_catalog_id
  * review state NOT in (complete, dismissed)

When review is already complete, the gate clears: button_start
returns the normal True so the operator can advance the step
without bouncing through QA-005 again.

Tests:
  test_button_start_routes_cr_step_to_qa005 — start opens QA-005
  test_button_start_does_not_route_when_review_complete — start
    does NOT redirect once review is signed off

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 22:33:55 -04:00
gsinghpal
d53fd53b80 feat(jobs): Record Inputs OWL Dialog (v4) — replaces list-as-cards hack
Scrapped the v2/v3 form-view + list-as-cards CSS approach after
extensive failure to make Odoo's editable list look like cards.
Built a proper OWL Dialog component instead, mirroring the pattern
used by fusion_plating_shopfloor's move_parts_dialog.js.

What changed
============
* New OWL Dialog: fp_record_inputs_dialog.js
  - Loads step + prompt definitions via /fp/record_inputs/load
  - Renders each prompt as a semantic <div class="o_fp_ri_card">
  - Per-row widget chosen by input_type:
      numeric/temperature/thickness/time_seconds/ph -> number input
      boolean/pass_fail   -> custom CSS toggle (clearer than Bootstrap)
      date                -> datetime-local input
      photo               -> file picker w/ preview + clear
      multi_point_thickness -> 5-cell grid + live average
      bath_chemistry_panel  -> pH/Conc/Temp/Bath grid
      selection           -> dropdown sourced from selection_options
      text/signature/...  -> text input
  - Live in-range hint for numeric prompts
      ("in range" / "below target" / "above target")
  - Save validates ad-hoc rows have a Prompt label
  - Save dispatches the next_action returned by the wizard model
    (e.g. action_finish_and_advance for the Finish & Next flow)

* New XML template: fp_record_inputs_dialog.xml
  Full DOM control. No fighting Odoo's list view, no class-stripping
  bugs from canUseFormatter, no read-mode-vs-edit-mode CSS dance.

* New SCSS: fp_record_inputs_dialog.scss
  - Dark mode aware (compile-time @if $o-webclient-color-scheme==dark)
  - Pure semantic selectors (.o_fp_ri_card, .o_fp_ri_input, etc.)
  - 14 surface tokens with light/dark hex pairs
  - Tablet polish via @media (max-width: 768px)
  - Custom toggle widget (no <input type="checkbox"> hidden trick)

* New controller: controllers/record_inputs.py
  - /fp/record_inputs/load: returns step + prompts payload
  - /fp/record_inputs/commit: creates a wizard, populates lines,
    calls action_commit (reuses existing audit-trail / synthetic
    move semantics — no commit logic duplicated)

* fp_job_step.py wired to dispatch the new action
  - _fp_open_input_wizard returns
    { type: 'ir.actions.client', tag: 'fp_record_inputs_dialog' }
  - action_open_input_wizard same
  - Contract-review redirect gate preserved (Sub 4 work intact)

* Manifest registers JS/XML/SCSS in BOTH backend + dark bundles
  per the dark-mode pattern in CLAUDE.md.

What was kept
=============
* fp.job.step.input.wizard TransientModel — UNCHANGED. The new
  controller's commit endpoint creates a wizard record and calls
  action_commit() on it, so all the audit-trail / synthetic-move
  / chatter logic stays in Python where it belongs.
* v2 + v3 form views still exist in the XML file. If the OWL
  dialog ever fails, switch action_open_input_wizard back to
  ir.actions.act_window with view_id=v2 or v3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 22:17:30 -04:00
gsinghpal
328599d539 fix(jobs): v3 wizard — chrome on <td>, verified from Odoo source (16.3)
I was wrong about the DOM. Verified from Odoo 19 source on entech:

  web/static/src/views/fields/float/float_field.xml
  web/static/src/views/fields/char/char_field.xml
  web/static/src/views/list/list_renderer.xml

Float/Char fields render as a BARE <span> (read mode) or BARE
<input class="o_input"> (edit mode) directly inside the <td>.
There is NO .o_field_widget wrapper. So all my prior CSS targeting
.o_field_widget matched nothing.

Also discovered: Odoo's getCellClass() in list_renderer.js calls
canUseFormatter() which strips custom <field> classes when the
column has widget="..." set:

    canUseFormatter(column, record) {
        if (column.widget) {
            return false;   // ← class stripped here
        }
        ...
    }

So o_fp_iw_value class doesn't even land on cells with
widget="boolean_toggle"/"image". Those cells render natively;
boolean toggle and image styling now targets the widgets directly
wherever they appear (.o_boolean_toggle, .o_field_image).

Fix: put visible chrome (border, bg, padding, min-height) on the
<td> itself for prompt/meta/value/extras cells. Make inner span
and input transparent + inherit. Focus ring travels up via
:focus-within on the td.

Cells now look like obvious input boxes from first paint, regardless
of whether the user has clicked into edit mode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 22:06:53 -04:00
gsinghpal
875828c588 fix(jobs): v3 wizard — chrome on wrapper, not <input> (v19.0.8.16.2)
Root cause user kept seeing inputs as bare/borderless text:
  Odoo's <list editable="bottom"> renders each cell as a read-mode
  <span> inside .o_field_widget UNTIL the user clicks the cell.
  Only then does an <input> swap in. My CSS was targeting
  `td.o_fp_iw_value input { ... }` so the chrome only appeared on
  focus. Every other (unclicked) cell looked like dead text.

Fix:
  Move all input chrome (border, bg, padding, min-height) to the
  .o_field_widget wrapper which is ALWAYS in the DOM. Then make
  the inner <input> / <span> transparent so they inherit. Effect:
  the cell looks like an input box from first paint, regardless
  of focus state. Focus ring travels up via :focus-within.

Special widgets (boolean toggle, photo upload, multi-point,
bath panel) opt OUT of the wrapper chrome via :has() so they
keep their own visual treatment.

Same fix applied to .o_fp_iw_extra cells (composite types).

User reproduction: WH/JOB/00339 → Record on Masking step. After
hard-refresh + this build, every value cell should read as an
obvious input box even before the operator clicks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:58:34 -04:00
gsinghpal
efef7859cd fix(jobs): Record Inputs v3 wizard — visual bug fixes (v19.0.8.16.1)
Four visible bugs reported by user after deploy:

1. Type + Unit pills overlapped at top-right of every card.
   Root cause: both <field>s carried the same .o_fp_iw_meta class
   AND both mapped to grid-area: meta. CSS Grid stacked them on
   top of each other so the labels rendered as overlap garbage
   (e.g. "eachnber", "Time(secs)Time(seconds)").
   Fix: distinct classes (.o_fp_iw_meta_type / .o_fp_iw_meta_unit)
   each in its own grid column. Grid is now 4 columns wide:
     "prompt | type | unit | trash"

2. Input borders barely visible in dark mode (#343942 on #22262d).
   Operators couldn't tell where to click.
   Fix: brighter border using $fp-iw-ink-faint instead of $fp-iw-border.
   Hover bumps to $fp-iw-ink-mute. Focus uses brand purple. Also
   added a slight surface tint ($fp-iw-page) so empty inputs read
   as obviously-interactive instead of blending into the card.

3. Photo widget rendered enormous (full card width).
   Root cause: max-width applied only to the preview image, not
   to the .o_field_image container itself.
   Fix: max-width 240px on .o_field_image AND its inner controls.

4. Numeric values floated centered in empty space.
   Root cause: input width wasn't stretching to its grid cell;
   default Odoo numeric-cell text-align: right plus our missing
   width: 100% left tiny inputs centered in the value area.
   Fix: explicit width: 100%, text-align: left, and 420px
   max-width on the .o_field_widget container.

Bonus polish:
  * Trash icon hidden by default (opacity: 0), reveals to 0.6 on
    row hover, full opacity on direct hover. Reduces visual noise
    for the common case where operator just types and saves.
  * Boolean toggle scale bumped from 1.4 to 1.5 + adds left margin
    so the switch sits properly inside the value cell.
  * Mobile (<900px) grid collapses to: prompt|trash / type|unit /
    value / extras — keeps the type+unit pair on one row but lets
    them flow naturally below the prompt.

No model changes. SCSS + XML view only. v2 view still in place
for instant rollback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:38:06 -04:00
gsinghpal
9794a98de9 feat(jobs): Sub 13 sequential step enforcement + Sub 12e v3 wizard
Two coherent feature drops shipping together because their fp_job_step
edits overlap. Both target operator workflow correctness.

## Sub 13 — Sequential step enforcement (recipe + per-step)

Background:
  Investigation on WH/JOB/00339 showed operators starting Incoming
  Inspection while Contract Review was still in_progress. Audit:
  98.7% of recipe operations system-wide had requires_predecessor_done
  = false (the legacy per-step opt-in defaults off, recipe authors
  rarely tick the box).

Architecture:
  Recipe-level toggle + per-step opt-out (Option A from /investigate).
  * fusion.plating.process.node.enforce_sequential — Boolean on the
    recipe root. Default True. When True, every operation under this
    recipe waits for earlier-sequence steps to finish before it can
    start.
  * fusion.plating.process.node.parallel_start — Boolean on operation
    nodes. When True, this step bypasses the sequential gate (e.g.
    paperwork or QA review that runs alongside production).
  * Mirrored on fp.step.template (parallel_start) so library steps
    carry the flag into snapshots.
  * fp.job.enforce_sequential — related from recipe_id. Snapshotted
    at job creation so a recipe author flipping the recipe's flag
    AFTER job generation does NOT change behaviour mid-run.
  * fp.job.step.parallel_start — related from recipe_node_id.
  * Decision matrix (encapsulated in
    fp.job.step._fp_should_block_predecessors):
        recipe.enforce_sequential | step.parallel_start | step.req_pred_done | block?
        --------------------------|---------------------|--------------------|------
                 True             |       False         |        any         |  YES
                 True             |       True          |        any         |   no
                 False            |        any          |       True         |  YES
                 False            |        any          |       False        |   no
  * Manager bypass via context fp_skip_predecessor_check=True (existing).

Runtime gates:
  * fp.job.step.button_start — calls _fp_should_block_predecessors;
    raises UserError naming the blocking earlier step(s).
  * fp.job.step.can_start — computed Boolean for view-side disable.
  * Move wizard predecessor check
    (fusion_plating_shopfloor/controllers/move_controller.py) — uses
    the same helper so tablet + backend behave identically.

UI surface:
  * Recipe form (fp_process_node_views.xml) — enforce_sequential
    toggle on recipe root, parallel_start checkbox on operations.
  * Step template form — parallel_start checkbox.
  * Simple Recipe Editor (inline library form) — Parallel Start
    checkbox + legacy flag demoted with muted styling + supervisor
    group gate.
  * Recipe Tree Editor (properties panel) — both flags exposed,
    only-show on the right node_type.
  * Controllers updated to allowlist + payload the new fields.

Migration:
  fusion_plating/migrations/19.0.18.12.0/post-migrate.py — sets
  enforce_sequential = TRUE on every existing recipe-root node.
  Idempotent. User confirmed dev-stage data, so retroactive flip
  is safe (no production jobs to disrupt).

Tests:
  TestSequentialEnforcement (10 tests) covering:
    * sequential mode blocks out-of-order start
    * first step always startable
    * predecessor finish/skip unlocks next
    * parallel_start opts out of gate
    * free-flow mode bypasses gate
    * legacy requires_predecessor_done still honoured in free-flow
    * manager bypass via context
    * can_start compute reflects state correctly
    * library template parallel_start snapshots into recipe-node

## Sub 12e — Record Inputs Wizard v3 (card layout, dark-mode aware)

Background:
  v2 wizard was a 17-column wide editable table. Operators got lost
  finding which value column applied to their row's type, horizontal
  scroll required on tablets, composite types crammed into one row.

New layout:
  * Each measurement renders as a stacked card (CSS Grid + display
    transformation on the existing list widget — preserves inline
    editing, no JS rewrite).
  * Card header: prompt name (large, bold) + type/unit pills.
  * Card body: ONLY the value widget for this row's type
    (number / boolean / date / text / photo / multi-point / panel).
  * Composite types (multi-point thickness 5x reading + avg, bath
    panel 4 fields) get inline sub-grid inside the card.
  * Empty state ("no measurement prompts") with friendly CTA.

Dark mode:
  * SCSS branches at compile time on $o-webclient-color-scheme
    (per fusion-plating/CLAUDE.md note).
  * Tokens: 7 surface colours + 4 ink levels with light/dark hex
    pairs, all behind var(--fp-*) custom properties for per-deploy
    override.
  * Registered in BOTH web.assets_backend AND web.assets_web_dark
    so each bundle compiles its own palette.

Tablet polish:
  @media (max-width: 900px) — collapse meta below prompt + bump
  numeric input min-height to 56px.

Defensive:
  * v2 view kept in the XML file (instant rollback by changing one
    view_id ref).
  * `:has(.o_invisible_modifier)` rule drops empty cells out of the
    grid so Odoo's invisible="..." doesn't punch holes in layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:24:12 -04:00
gsinghpal
ee80673579 fix(contract-review): WO step routes to QA-005 + auto-stage on part create
Two bugs fixed in one drop, both targeting the contract review (QA-005)
enforcement gap reported on entech.

## Bug 1 — WO step routed to wrong wizard

Symptom: clicking Finish & Next or Record on a Contract Review step in
WH/JOB/00339 opened the generic measurement wizard with three fake
prompts (Reviewer Initials / Date Reviewed / QA-005 Approved). No path
to the actual QA-005 form from the work order.

Root cause: action_finish_and_advance + action_open_input_wizard had no
branch for recipe_node.default_kind == 'contract_review'. The step.kind
mapping collapses contract_review -> 'other' so kind-based detection
wouldn't have worked either; gate has to live at the recipe-node layer.

Fix in fusion_plating_jobs/models/fp_job_step.py (v19.0.8.14.6):
- action_finish_and_advance:329 calls _fp_contract_review_redirect
  before the input-wizard branch
- action_open_input_wizard:844 same gate, keeps Record button consistent
- _fp_contract_review_redirect:866 (new) returns the part's
  action_start_contract_review() unless review.state in
  (complete, dismissed) — gate clears so the step can finish after
  the operator signs QA-005.

## Bug 2 — Part create did not enforce contract review

Symptom: spec called for a banner-only UX. User wanted true automatic
enforcement on first part creation under an enforced customer.

Fix in fusion_plating_quality/models/fp_part_catalog.py (v19.0.4.10.0):
- @api.model_create_multi def create() override
- _fp_enforce_contract_review_on_create() helper auto-stages the
  fp.contract.review record AND surfaces three prominent reminders:
    1. Sticky bus.bus warning toast (top-right, doesn't auto-dismiss)
    2. mail.activity (To Do) on the part for the current user
    3. Smart button on the part form lights up (review now exists)
- Idempotent: skips parts that already carry a review id
- Soft-fails: bus or activity outage doesn't block part creation
- create()-only — write/update flows never re-trigger

Sub 4's existing info banner stays as a fourth surface.

## Tests

- fusion_plating_jobs/tests/test_fp_job_extensions.py:
  +TestContractReviewStepRouting (5 tests covering both routing methods,
  the complete/dismissed gate-clear, and non-CR step regression)
- fusion_plating_quality/tests/test_part_catalog_contract_review_enforcement.py
  (NEW): 9 tests covering auto-create, batch create, idempotency,
  activity surface, bus surface, write-must-not-retrigger, soft-fail.
- docs/superpowers/tests/2026-04-22-sub4-smoke.py: flipped the
  "no review yet" assertion to "review auto-created" to match new
  behavior. Sign-flow assertions unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:02:52 -04:00
gsinghpal
1da27ed6bf changes 2026-05-01 00:20:40 -04:00
gsinghpal
bdcbd86db2 changes 2026-04-30 18:08:36 -04:00
gsinghpal
4213c44e51 feat(simple-editor): node_type bug fix + inline library authoring + back nav
Bucket 1 — Generation bug fix
- post-migrate.py for 19.0.18.8.0 promotes flat 'step' children of
  recipes to 'operation' so fp.job._generate_steps() picks them up.
  Filter is narrow: only direct children of node_type='recipe' get
  flipped, tree-editor sub-steps (parent.node_type='operation') are
  untouched. Idempotent. Posts an audit chatter note on each affected
  recipe.
- Simple Editor controller hardcodes node_type='operation' on insert
  + snapshot-import path so future recipes start correct.

Bucket 2 — Inline library authoring
- 6 new JSONRPC routes (/fp/simple_recipe/library/load + save +
  seed_defaults + input/{add,write,remove}, /fp/simple_recipe/tank/list).
- + New Step button in the right pane opens an inline form with name /
  kind / icon / instructions / stations / flags / prompts table.
- Pencil icon on each library row reopens the same form prefilled.
- Step Kind picker leads with 'Generic — no automatic behaviour'.
- 'Seed defaults from kind' calls action_seed_default_inputs server-side
  for kinds that have curated default prompts.

Bucket 3 — Back nav
- '← Recipes' button in the header (or '← Part' when opened from
  Process Composer) mirrors recipe_tree_editor.js, with
  clearBreadcrumbs:true to avoid stack pollution.

Verified on entech: LGPS1104's 19 'step' children now show as
'operation', migration chatter note posted on the recipe, asset cache
busted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 16:16:14 -04:00
gsinghpal
b8d064b180 docs(simple-editor): design — bug fix + inline library authoring + breadcrumbs
Spec for the upcoming Simple Recipe Editor refinement:
- Fix node_type bug so Simple-Editor recipes generate job steps
- Inline + New Step / pencil-edit library authoring with prompts
- Back button + breadcrumb-aware navigation (mirrors tree editor)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 16:08:34 -04:00
gsinghpal
c5d21e0519 changes 2026-04-30 00:21:08 -04:00
gsinghpal
f990f29019 feat(plating-jobs): state-aware sort + default Open filter on Plating Jobs
The Plating Jobs list was sorted priority-desc, deadline-asc, id-desc — which
mixed Done (closed) jobs in with Confirmed (open) jobs and made the list look
chaotic to managers. Done jobs from weeks ago surfaced above active work.

Two changes:

1. New stored compute fp.job.state_priority (Integer, indexed) ranks states
   by managerial relevance: in_progress=0, confirmed=1, draft=2, on_hold=3,
   done=4, cancelled=5. _order now leads with state_priority asc, then
   priority desc, then date_deadline asc, then id desc. Active work bubbles
   to the top automatically.

2. Plating Jobs action defaults to a new 'Open' filter
   (state not in done, cancelled). Managers see only active work by default;
   they untick the filter to see history. Added On Hold + Cancelled filters
   too for full state coverage.

Verified on entech: top 10 jobs are now all in_progress, sorted by deadline
ascending. Existing 26-row list goes from chaotic to focused.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 23:48:34 -04:00
gsinghpal
f7fcd03bfc fix(quick-look): dark-mode-aware instructions panel
The Operator Instructions panel had a hardcoded inline style
(background: #f8f9fa) which became a white-on-dark unreadable blob
in dark mode. Replaced with a CSS class backed by an SCSS file that
branches at compile-time via $o-webclient-color-scheme — registered
in both web.assets_backend (light) and web.assets_web_dark (dark)
bundles per the CLAUDE.md pattern.

Tokens: panel bg #f8f9fa light / #22262d dark; border #d8dadd /
#3a3f47; text #212529 / #e8eaed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 23:40:55 -04:00
gsinghpal
555dd5421f feat(jobs): step details quick-look modal for backend managers
Click a step's name in the embedded job-form list → opens a read-only
modal with everything a manager wants in one scroll: equipment,
schedule, master collect-measurements banner, operator instructions
(rich-text from recipe_node.description), measurement prompts list,
and values recorded so far.

Implementation: separate read-only form view bound to the embedded
field via context={'form_view_ref': '...'}. The standalone editable
form view stays registered for the Job Steps menu, so direct
navigation still loads the editable variant.

Three new computed/related fields on fp.job.step:
- quick_look_instructions (Html, related from recipe_node_id.description)
- quick_look_prompt_ids (filtered+sorted recipe_node.input_ids, step_input only)
- quick_look_recorded_value_ids (search across moves: input_value rows
  whose move.from_step_id == self.id)

Plus a small action_open_full_form method that escapes from the modal
to the editable form when the manager actually needs to edit.

Edge cases:
- No recipe_node_id → instructions panel shows empty-state hint
- collect_measurements=False → amber banner: "Master switch off — no
  values will be collected at runtime"
- Multiple moves on same step → values list shows all, newest first

Spec: docs/superpowers/specs/2026-04-30-step-details-modal-design.md.
Verified on entech: step "11. Hard Anodize Type III" populates with
516 chars instructions + 7 prompts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 23:35:08 -04:00
gsinghpal
875548c547 fix(operator-wizard): surface office-authored instructions to operators
Critical UX gap discovered in production-environment battle test: when
operator hits "Mark Done" and the input wizard fires, they only saw the
measurement prompts list. The rich-text instructions written by the
office (recipe_node.description) never reached the operator at the
exact moment they need them.

Fixed: wizard model gains instructions (Html, computed from
step.recipe_node_id.description) + has_instructions flag.
Form view renders the instructions in a prominent blue alert at the
top of the wizard, above the Measurements list. Hidden when blank
so operators on instruction-less steps don't see noise.

Also: extend default_kind Selection on fusion.plating.process.node to
match fp.step.template — both models now have the same 24 kinds. Without
this, recipe authors could pick a kind in the library template form
that the recipe-node Selection rejected with a ValueError.

Battle test artifact:
- Recipe "Hard Anodize Type III + Dye + Seal" (id=1863) — 23 steps,
  105 measurement prompts, rich-text operator instructions per step
- SO S00278 for ABC Manufactoring confirmed → fp.job 1236 / WH/JOB/00337
  with all 23 steps materialized, 105 prompts visible to operators
- Wizard test: step "11. Hard Anodize Type III" → 516 chars of
  instructions render + 7 input prompts in the form

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 23:05:21 -04:00
gsinghpal
ec0a07fbe9 fix(audit-trail): 3 production bugs found via end-to-end Anodize battle test
Battle-tested complete workflow on entech: ABC Manufacturing + Anodize
recipe (id=136) cloned to part-variant (id=1775) → SO S00276 confirmed →
fp.job 1234 with 17 steps → recorded 56 measurement values exercising all
13 input types (incl. all 4 new types) → CoC chronological report renders
69KB with all values incl. photo thumbnails.

Bugs found and fixed:

1. fp.process.node.input_ids missing copy=True — when a master recipe
   was cloned per-part (the standard variant pattern), the operator
   prompts on each step did NOT get copied to the variant. Result: jobs
   built from variants ran with zero prompts even though the master had
   them. Fixed: input_ids now copy=True so cloning auto-duplicates.

2. CoC chronological template read dest.input_ids where dest is
   fp.job.step. Steps don't carry input_ids — that field lives on the
   recipe node. Result: AttributeError aborted the entire CoC render.
   Fixed: walk via dest.recipe_node_id.input_ids; preserves the existing
   collect=True filter.

3. CoC chronological template used hasattr() in a t-value expression.
   QWeb's expression engine doesn't expose Python builtins, raised
   KeyError: 'hasattr'. Fixed: use 'collect' in i._fields instead.

Also enhanced photo rendering in CoC: was just "[Attachment]" placeholder;
now renders an actual <img> thumbnail (max 80px tall) plus the filename.

Battle-test script saved to fusion_plating/scripts/bt_e2e_anodize_v2.py
for re-runs / regression testing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 22:53:59 -04:00
gsinghpal
b187192c58 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>
2026-04-29 22:13:54 -04:00
gsinghpal
bbf2476f01 plan(step-library): full implementation plan for audit expansion + per-recipe configurability
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:56:18 -04:00
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
gsinghpal
df43737b1b spec(deadlines): per-part effective deadlines with customer-profile cascade
Resolution chain: explicit override → days offset → part lead time → order
commitment. Adds x_fc_default_lead_time_days on part catalog; per-line
effective_part_deadline + effective_internal_deadline computes; order-level
completion_date rollup + is_late_forecast warning.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 21:08:21 -04:00
gsinghpal
a2fe1fcbcc changes 2026-04-29 03:35:33 -04:00
gsinghpal
6ac6d24da6 CHANGES 2026-04-28 19:43:16 -04:00
gsinghpal
2a9fd478f5 Update fp_racking_inspection_views.xml 2026-04-28 19:42:41 -04:00
gsinghpal
13e300d90e changes 2026-04-28 19:39:37 -04:00
gsinghpal
2d42b33d68 docs(claude.md): log Sub 12a/12b/12c + Phase 1/2/3 menu reorg + ergo fixes
Records full session work for future Claude Code sessions:

Sub 12a — Simple Recipe Editor + Step Library
  3 new models, additive fields on process.node + node.input,
  fp_simple_recipe_editor OWL action with drop-position simulator,
  11 JSONRPC endpoints, snapshot semantics, post_init seeding.

Sub 12b — Move Parts / Move Rack / Rack Parts / Stop Timer dialogs
  fp.rack.tag, fp.job.step.move + .input.value, racking_state on
  fusion.plating.rack (orthogonal to wear state), state machine on
  existing fp.job.step.timelog (no parallel labor model), 12 tablet
  endpoints, 4 OWL dialogs with fp-resolve-rack custom event,
  manager-bypass flags, plant overview Racks pane.

Sub 12c — Reports + Labor History
  Operator Traveller v2 (A4 landscape, paper-style), chronological
  CoC body via fp.certificate.body_style + coc_body_router,
  Labor History views, gap-fix bundle (rack travel ticket PDF,
  per-customer cert statement 3-tier resolution, captured Actual
  values from move.input.value).

Phase 1/2/3 — Menu reorganization
  Top-level: 17 → 6 (operator-visible). Industry verticals nested
  under Compliance hub. Move Log/Labor History/Maintenance under
  Operations. Certificates under Quality.
  Configuration: 36 flat → 7 themed folders + Settings sibling.
  Group-gating: KPIs/Move Log/Replenishment Suggestions →
  supervisor+. Operator now sees ~5 top-levels instead of ~10.

Landing page resolver
  action_fp_resolve_plating_landing server action, user override →
  company default → Sale Orders fallback. x_fc_pickable_landing
  Boolean tag for curated picklist.

Production Line / Routing Station rename
  fusion.plating.work.center → 'Production Line' (shop-layout, owns
  tanks). fp.work.centre → 'Routing Station' (per-step routing,
  cost-per-hour, mrp.workcenter replacement). Model IDs unchanged.

Other ergonomics
  Tank field labels (Code → Tank Number, Tank → Tank Name) +
  state-control header buttons. SO smart-button 'Plating Jobs' → 'WO'.
  Default landing screen = Sale Orders.
  Drop-position simulator in Simple Recipe Editor.

Updated 'Menu Structure' section near top of CLAUDE.md to reflect
new 6-top-level layout + 7-folder Configuration grouping.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 23:06:25 -04:00
gsinghpal
afcd128f83 fix: rename ambiguous 'Work Center' / 'Work Centres' menus
Two distinct entities were both labelled 'Work Centre' (US/UK spelling
the only differentiator — confusing). Renamed by purpose, model IDs
unchanged so all 12+9 existing cross-refs keep working:

fusion.plating.work.center → 'Production Line'
  Physical shop-layout grouping that owns tanks. Examples: 'Line 1 —
  EN', 'Anodize Line', 'Prep Bay'. Has tank_ids (O2M),
  supported_process_ids (M2M), capacity_per_day. The thing tanks live
  in.

fp.work.centre → 'Routing Station'
  Per-job-step routing entity (post-Sub-11 mrp.workcenter replacement).
  Has 'kind' selection (wet_line / bake / mask / rack / inspect),
  cost_per_hour for fp.job.step rollup, default_bath_id +
  default_tank_id for release-ready validation. The thing a job step
  routes through.

Conceptually a Production Line CONTAINS many Routing Stations (e.g.
'EN Line' production line has wet-line, bake, inspect routing
stations on it).

Updated:
- _description on both models
- string= on the name fields
- list/form/search view strings
- act_window names ('Production Lines' / 'Routing Stations')
- menu items in fp_menu.xml + fp_jobs_menu.xml
- doc comments in both model files explaining the distinction

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:55:39 -04:00
gsinghpal
5f6c7af2a7 feat(phase3): tighten group-gating on operator-irrelevant top-levels
Three targeted gates so operators no longer see admin/audit views:

- KPIs (menu_fp_dashboard) → supervisor+. Operators don't need
  dashboards; their tablet shows what they need to do next.
- Move Log (menu_fp_job_step_move) → supervisor+. Operators see
  their own moves on the tablet; this top-level menu is the
  audit-of-everyone-else view.
- Replenishment Suggestions (menu_fp_replenishment_suggestions) →
  supervisor+. Purchasing decision, not operator concern.

Other top-levels were already correctly gated:
- Sales / Configurator → estimator
- Shipping & Receiving → group_fp_receiving
- Compliance hub → supervisor+
- Configuration → manager
- Shop Floor / Quality → operator (correctly visible to floor staff)
- Operations parent stays open; child menus enforce per-action gates

Net effect: a fresh operator now sees ~5 top-level menus instead of
the previous ~10. Supervisors see ~8. Managers see all.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:51:30 -04:00
gsinghpal
46715410a9 fix(phase2): restructure fp_menu.xml so buckets defined before children
Within fp_menu.xml itself, the menu_fp_replenishment_rules entry
referenced menu_fp_config_materials_tanks which was defined later in
the same file. Odoo's data loader is strictly top-down within a file.

Reorganized by section: 1) root, 2) Configuration + 7 buckets,
3) Compliance hub, 4) Operations parent, 5) all child menus (referencing
parents already defined above).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:48:06 -04:00
gsinghpal
62c1315997 fix(phase2): load fp_menu.xml first so bucket folders exist before refs
fp_rack_tag_views.xml (and several other view files) reference the
new Phase-2 Configuration sub-folder menus (menu_fp_config_*) defined
in fp_menu.xml. Odoo's data loader is strictly sequential within a
module, so fp_menu.xml must come before any file that references
its bucket xmlids.

Caught by entech upgrade (ParseError on rack-tag menuitem).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:46:53 -04:00
gsinghpal
3641b78a66 feat(phase2): Configuration grouped into 7 themed folders
Collapses the flat ~36-entry Configuration list into 7 navigable
folders + Settings (sibling, stays at top of Configuration). Existing
menu IDs unchanged so bookmarks + cross-module data refs still work
— only parent-id moves.

New folder menus (defined in fusion_plating core):
  menu_fp_config_shop_setup       Shop Setup
  menu_fp_config_recipes_steps    Recipes & Steps
  menu_fp_config_materials_tanks  Materials & Tanks
  menu_fp_config_workforce        Workforce
  menu_fp_config_quality_docs     Quality & Documents
  menu_fp_config_pricing_billing  Pricing & Billing
  menu_fp_config_reference_data   Reference Data

Routing per item (sources updated in their owning module):
  Shop Setup       Facilities, Work Centers, Work Centres, Process
                   Categories, Process Types, Bake Ovens, Shopfloor
                   Stations, Vehicles
  Recipes & Steps  Step Library, QC Checklist Templates, Quality Points
  Materials & Tanks  Bath Parameters, Replenishment Rules, Chemicals,
                     Rack Tags, Calibration Equipment, Calibration Events
  Workforce        Operator Certifications, Shop Roles, Training Types,
                   Quality Teams
  Quality & Documents  Customer Specs, Approved Vendor List, Quality
                       Tags, Quality Reasons, Quality Stages, N299
                       Levels, Notification Templates, Notification Log
  Pricing & Billing  Invoice Strategy Defaults, Account Holds
  Reference Data   Value Sets, Value Rotations
  (Settings remains as a sibling at top of Configuration, manager-gated)

Versions bumped: fusion_plating, fusion_plating_quality, _safety,
_shopfloor, _logistics, _culture, _invoicing, _notifications, _nuclear.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:45:21 -04:00
gsinghpal
0ad382e1a6 feat(phase1): top-level menu consolidation + landing-page resolver
Phase 1 collapses the Plating app's 17 top-level menus down to 6
domains (Sales, Operations, Receiving & Shipping, Quality,
Compliance, Configuration) so users no longer scroll a 17-item
sidebar to find one thing.

Re-parented (no XML id changes — bookmarks still work):
- fusion_plating_compliance.menu_fp_compliance_root
    → menu_fp_compliance_hub  (renamed 'General')
- fusion_plating_safety.menu_fp_safety_root
    → menu_fp_compliance_hub  (renamed 'Safety / WHMIS')
- fusion_plating_aerospace.menu_fp_aerospace
    → menu_fp_compliance_hub  (renamed 'Aerospace (AS9100 / Nadcap)')
- fusion_plating_nuclear.menu_fp_nuclear
    → menu_fp_compliance_hub  (renamed 'Nuclear (CSA N299 / CNSC)')
- fusion_plating_cgp.menu_fp_cgp
    → menu_fp_compliance_hub  (renamed 'Controlled Goods (CGP)')
- fusion_plating_certificates.menu_fp_certificates
    → menu_fp_quality (Certs are a Quality output, not a separate
      top-level concern)
- fusion_plating_bridge_maintenance.menu_fp_maintenance
    → menu_fp_operations
- fusion_plating.menu_fp_job_step_move (Move Log)
    → menu_fp_operations
- fusion_plating.menu_fp_job_step_timelog (Labor History)
    → menu_fp_operations

The new menu_fp_compliance_hub is supervisor-gated; underlying
verticals retain their own group locks (CGP officer, etc.).

Settings menu remains manager-gated through inheritance from
menu_fp_config (already in place).

NEW — Plating landing-page resolver:
- ir.actions.act_window.x_fc_pickable_landing  (Boolean tag for
  curated picklist; flagged on Sale Orders, Quotations, Process
  Recipes for Phase 1; more in Phase 2)
- res.company.x_fc_default_landing_action_id (admin sets fallback)
- res.users.x_fc_plating_landing_action_id (per-user override)
- ir.actions.server action_fp_resolve_plating_landing — picks
  user → company → Sale Orders fallback at click time
- menu_fp_root rewired to call the resolver
- User profile + Settings tabs surface the dropdowns

Configurator's earlier menu_fp_root override (action_fp_sale_orders
direct) removed — core's resolver now owns the routing.

Versions bumped: fusion_plating 19.0.11.0.0, configurator
19.0.17.16.0, plus 7 dependent modules patched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:33:37 -04:00
gsinghpal
3098fcfaf9 feat(sub12a+): drop-position simulator in Simple Recipe Editor
Replaces the per-row 'highlight whole row' feedback (operator
couldn't tell whether the new step would land before or after the
hovered row) with a precise insertion-point indicator.

How it works:
- Each row's onDragOver computes ev.clientY vs row midpoint.
  Above midpoint → insertion index = rowIndex (BEFORE).
  Below midpoint → insertion index = rowIndex + 1 (AFTER).
- An <o_fp_drop_indicator> div lives BEFORE the first row and
  AFTER every row. When state.dragOverIndex matches that slot's
  index, the div expands from height:0 to a 2.25rem dashed-green
  reservation strip with a ghost-preview chip ('↓ insert here →
  <icon> <step name>').
- onDragStart captures the dragged step/template's name + icon
  into state.dragPreviewLabel/Icon for the chip text.
- Smooth 80ms height/margin transition so the line glides between
  slots as the cursor moves rather than blinking.
- Trailing dropzone retains its existing 'Drop here to add at end'
  styling. Empty list shows 'Drag a library step here to start'.
- onDrop reads from state.dragOverIndex (set by the most-recent
  onDragOver) so we drop at the simulated position exactly.
- onDragLeave guards against child-element flicker via
  relatedTarget contains() check.
- onDragEnd clears state.dragPreviewLabel/Icon so a half-completed
  drag (cancelled by Esc) doesn't leave the chip stuck on screen.

fusion_plating → 19.0.10.4.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 22:08:51 -04:00
gsinghpal
7d3b8f132a fix(sub12c+): close 3 known gaps — rack travel ticket, cert statement, CoC actuals
Gap 1 — Rack Travel Ticket PDF (Sub 12b's Save+Print 404):
  + report_fp_rack_travel.xml in fusion_plating_reports — A5 landscape
    single page, big rack name, Code 128 of FP-RACK:<name>, tag chips,
    contained part-batches table.
  + ir.actions.report bound to fusion.plating.rack so it appears in
    the rack form's Print menu too.
  + Sub 12b's rack_parts_dialog.js Save+Print URL fixed to use the
    standard /report/pdf/<xmlid>/<id> route.

Gap 2 — Per-customer cert statement:
  + res.company.x_fc_default_cert_statement (company-level fallback).
  + res.partner.x_fc_cert_statement (per-customer override).
  + Surfaced on the partner form under the existing Cert + Document
    Routing block.
  + Chronological CoC body resolves: customer override → company
    default → hardcoded AS9100/ISO 9001 boilerplate. Three-tier
    fallback so existing certs without overrides keep working.

Gap 3 — Chronological CoC 'Actual' column:
  + Build a captured_values_by_input dict from the move's
    transition_input_value_ids (Sub 12b captures these on every
    Move Parts commit).
  + Render typed Actual: text → as-is, number → with target unit,
    boolean → PASS/FAIL, date → formatted, attachment → '[Attachment]'
    placeholder.
  + Falls back to prompts from the destination step's step_input list
    when no values were captured (still useful as audit-of-what-was-
    asked even if blank).

Version bumps:
  fusion_plating → 19.0.10.3.0
  fusion_plating_reports → 19.0.10.1.0
  fusion_plating_certificates → 19.0.5.3.0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:55:48 -04:00
gsinghpal
504c8f34db feat(sub12c): Labor History views + Plating menu (Task 4)
Plating → Labor History (sequence 64, between Move Log 62 and
Aerospace 65). List view colour-coded by state (info/warning/
success/muted), with billed_pct progressbar and rich field optionals.

Search filters: My Timers (default), Today, Running, Paused, Pending
Reconciliation, Reconciled. Group-by: Operator, Job, Date.

Form view (read-only header with statusbar): identity fields readonly,
billed_hrs/min/sec editable for supervisors+ until state=reconciled.
Notes group at bottom. create=false (timers are runtime-produced;
manual creation goes through the tablet flow).

ACL rows for fp.job.step.timelog already shipped in Sub 12b's CSV
(operator/supervisor/manager) — no security changes needed here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:43:07 -04:00
gsinghpal
9d88c25136 feat(sub12c): chronological CoC body + body_style opt-in router (Task 3)
New template: fusion_plating_reports.coc_body_chronological.
Walks fp.job.step.move records in time order (chain-of-custody).
Per-move heading 'Step Name (Tank Code)' with 'Moved By / Time / Qty'
meta line + a 5-column measurement sub-table (Name / Description /
Target / Actual / Recorded By) when the destination step has captured
inputs. Heading-only when there are no inputs (gating moves).

New router template: coc_body_router. Picks chronological vs classic
based on fp.certificate.body_style. Existing certs default to 'classic'
so no regressions. Both English + French CoC templates rerouted.

fp.certificate.body_style ('classic' | 'chronological') exposed on
the cert form alongside certified_by_id. Operator picks per cert.

Sign-off block reuses the existing owner_user_id signature pattern +
x_fc_coc_signature_override fallback. Cert statement boilerplate is
inline (Sub 12d will move it to a configurable per-customer field).

The Actual column in the measurement sub-table is rendered blank
because Sub 12a/12b runtime captures step_input values via the
operator's per-step input form which lives in a model not yet wired
into this template — Sub 12d follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:42:03 -04:00
gsinghpal
12fcd11016 feat(sub12c): operator traveller v2 — paper-style A4 landscape (Task 2)
Replaces the minimal portrait template with the Amphenol-style paper
sheet (screens 16-18):
- Header: company logo + barcode (Code 128) + WO# + Date In + Due
  Date + Type + Order# + PO# + WO-Generated-By + customer block.
- Item Information panel: Part# / Rev / Mat / Catg / S/N + Item-Name +
  Qty Rec / VIS INSP / Rework / Special Requirements / Stamp-Date.
- Process-Sheet header: recipe name + category + spec/info.
- Routing table (11 cols): Step / Tank / Operation+Actuals (recipe
  inputs render as 'Actual <name>: ____ unit' lines) / Instruction /
  Unit / Material / Voltage / Time(min) / Temp / Stamp / Date.

Targets pulled from recipe-node fields when present (Sub 12a authored),
'N/A' otherwise. Heavily defensive QWeb — every cross-module field
access ('part_catalog_id' / 'coating_config_id' / 'qty_received' /
'special_requirements' / 'serial_number' / 'base_material' /
'customer_facing_description' / 'time_min_target' / etc.) guarded with
'X in record._fields' checks so the report renders cleanly even when
some Sub 12a/12b fields aren't yet populated.

New paperformat: A4 landscape narrow margins, 90 dpi.

Action ID + report_name unchanged so existing form-button bindings
keep working (binding_model_id still points at fp.job).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:39:41 -04:00
gsinghpal
f55193fb1b feat(sub12c): bump versions + manifest scaffolding (Task 1)
fusion_plating → 19.0.10.2.0 (Labor History views)
fusion_plating_jobs → 19.0.7.0.0 (Operator Traveller v2)
fusion_plating_reports → 19.0.10.0.0 (Chronological CoC body)

Adds data entries for the 2 new XML files (timelog views + coc
chronological).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:37:24 -04:00
gsinghpal
34528a5d3d docs(sub12c): implementation plan — 5 tasks (down from original 18)
Tightened from the original 18-task plan after inspecting existing
templates:
- report_coc_en / report_coc_fr already exist with Nadcap/AS9100/CGP
  logos, signature, certified_by — solid. Add a chronological body
  alongside, don't rebuild.
- company.x_fc_nadcap_logo etc already exist on res.company. Skip.
- The native fp.job traveller is minimal (post-Sub-11) and needs the
  paper-style upgrade. Replace its body, not the action.
- fp.job.step.timelog state machine landed in Sub 12b — Sub 12c just
  ships views + menu.

5-task breakdown:
1. Bump versions + manifest scaffolding
2. Operator Traveller v2 (A4 landscape, paper-style, target columns)
3. Chronological CoC body + body_style opt-in router
4. Labor History list/form/search + Plating menu
5. Deploy to entech + smoke test

Out of scope: rack travel ticket PDF (Sub 12b's Save+Print 404 stays
flagged), per-customer cert statement (boilerplate inline for now).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:36:06 -04:00
gsinghpal
e718a47e3e fix(sub12b): to_step_id required → ondelete='restrict'
Odoo 19 disallows ondelete='set null' on a required M2O. Switched to
restrict — destination steps can't be unlinked while move-log rows
reference them, which is the right audit-safety behavior anyway.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:19:58 -04:00
gsinghpal
11dbbf578e feat(sub12b): plant overview Racks pane (Task 16)
Controller: extend /fp/shopfloor/plant_overview return payload to
include 'racks' array (filtered to loaded/in_use/awaiting_unrack
states). Each entry has tag chips, part count, current node
breadcrumb, current step + tank code, and a precomputed
next_step_id (next sequence in the job's recipe — operator
overrides at runtime in the Move Rack dialog).

JS: state.racks populated from payload. New openMoveRackDialog()
method spawns FpMoveRackDialog. Notification when rack has no
successor (last step of job).

XML: top section above the existing work-centre columns. Renders
rack rows with tags, part count, breadcrumb, and primary MOVE RACK
button per row. Visible only when state.racks.length > 0.

SCSS: minimal styling for the racks pane (extends move_dialogs.scss
to keep all Sub 12b styles in one file).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:19:05 -04:00
gsinghpal
902f3e8398 feat(sub12b): wire Move Parts + Stop Timer dialogs into tablet (Task 15)
ShopfloorTablet component:
- Imports the 3 new OWL dialogs.
- useService('dialog') for spawning.
- Listens for 'fp-resolve-rack' window CustomEvent fired from inside
  FpMovePartsDialog → spawns FpRackPartsDialog inline.
- New methods: openMovePartsDialog(from, to) + openStopTimerDialog(id).
  Refresh tablet after commit/reconcile so the UI reflects new state.

Listener cleanup on unmount.

Note: the actual buttons that call these methods are added to the
existing tablet XML in a follow-up step — for now they are wired but
not surfaced. Operators get them after Task 16 + smoke test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:16:15 -04:00
gsinghpal
11bc0ca742 feat(sub12b): shared SCSS for Move/Rack/Timer dialogs (Task 14)
3-column grid layout for field rows (label / input / hint).
Compliance prompts + Blockers blocks have their own backgrounds.
Soft blockers amber + left border, hard blockers red + left border —
matches the spec's protocol.

Token pattern + dark-mode @if branch (CLAUDE.md rule: Odoo 19 doesn't
flip dark mode via runtime DOM class; we branch at SCSS compile time
on $o-webclient-color-scheme).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:15:12 -04:00
gsinghpal
270f427d7f feat(sub12b): Move Rack + Stop Timer OWL dialogs (Task 13)
Move Rack: rack name in title via getter, tag chips, batches list
(read-only), Type + To Node + To Station picker. Atomic Save commits
all batches via /fp/tablet/move_rack/commit.

Stop Timer: opens with state already at 'stopped' (server flipped on
load via /labor_timer/stop), pre-fills billed_* from accrued.
Operator edits → Save (state → reconciled).

Save & Start New Timer chains into a fresh timer for the same step
via the start_new=True flag — mirrors screen 10's right-most button.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:14:30 -04:00
gsinghpal
48c06c40c9 feat(sub12b): OWL Rack Parts sub-dialog (Task 12)
Mirrors screens 7-8. Searchable empty-rack picker with debounced
typeahead via /fp/tablet/rack/list_empty. QR Scan button prompts
operator for FP-RACK:<name> token, resolves via /fp/tablet/rack/
scan_qr.

Save commits the racking via /fp/tablet/rack_parts/commit. Save+Print
opens /web/report/pdf/fp.rack.travel/<id> in a new tab — that report
ships in Sub 12c, returns 404 until then. Plain Save works today.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:13:04 -04:00
gsinghpal
6d046f2881 feat(sub12b): OWL Move Parts dialog (Task 11)
Mirrors Steelhead screens 1-3, 14-15. Loads preview on mount,
re-checks hard-blockers on commit. MOVE (n) button disabled when
hard-blocked OR required prompt blank — improvement over Steelhead's
silent disabled state (we show a tooltip listing reasons).

Inline 'Resolve' button next to each blocker. For rack-required,
fires a window CustomEvent ('fp-resolve-rack') the parent tablet
catches to open the Rack Parts sub-dialog.

Typed input rendering by input_type — text/number/checkbox/select/
datetime, plus support for time_hms and signature/photo (text input
for now; full upload widget in Sub 12c).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:11:49 -04:00
gsinghpal
a521b7c37b feat(sub12b): consolidated tablet controller — Move/Rack/Timer (Tasks 8-10+17)
11 routes (consolidated from plan Tasks 8/9/10/17):
  Move Parts:
    /fp/tablet/move_parts/preview
    /fp/tablet/move_parts/commit
  Move Rack:
    /fp/tablet/move_rack/preview
    /fp/tablet/move_rack/commit
  Rack Parts:
    /fp/tablet/rack_parts/commit
    /fp/tablet/rack/list_empty
    /fp/tablet/rack/scan_qr
  Persistent labor timer:
    /fp/tablet/labor_timer/start
    /fp/tablet/labor_timer/pause
    /fp/tablet/labor_timer/resume
    /fp/tablet/labor_timer/stop
    /fp/tablet/labor_timer/reconcile

Manager-bypass context flags (Task 17 wired in here for cohesion):
  fp_skip_predecessor_check  → bypasses S14 lock
  fp_skip_rack_assignment    → bypasses requires_rack_assignment
  fp_skip_transition_form    → bypasses required transition prompts

All bypass uses post to chatter on the move record naming the user
+ which flags fired. Group check enforced (manager-only).

_safe() wrapper: UserError → JSONRPC-friendly {ok: False, error: msg}
so the OWL components can show a flash without crashing.

Field naming follows existing fp.job.step.timelog convention
(date_started / date_finished, NOT started_at / stopped_at).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:10:28 -04:00
gsinghpal
3bed76aea4 feat(sub12b): persistent state machine on fp.job.step.timelog
Extends the existing timelog (used by S1/S2 battle tests) with:
- state: running / paused / stopped / reconciled (default running)
- last_paused_at + total_paused_seconds (drives accrued compute)
- accrued_seconds (compute, depends date_started/_finished/paused)
- billed_hrs/min/sec + billed_total_seconds + billed_pct (compute)
- product_id (split-by-product reconciliation per screen 10)
- notes
- job_id (related, indexed — for fp.job.active_timer_ids O2M)

Field naming follows the existing date_started / date_finished
convention (NOT started_at / stopped_at as my plan said — adjusted
inline to match what's already in the file).

The existing battle tests use the timelog without state — default
'running' so they're unaffected. State only flips when Sub 12b's
Stop Timer dialog commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:08:22 -04:00
gsinghpal
dcd6df71c0 feat(sub12b): fp.job.step + fp.job — rack/move/traveller fields
fp.job.step:
  + requires_rack_assignment (related from recipe_node_id)
  + requires_transition_form (related)
  + move_ids (O2M from_step_id), incoming_move_ids (O2M to_step_id)
  + is_racked (compute, stored, depends rack_id) — drives tablet
    rack-vs-parts greyed-button guard
  + qty_at_step_start, qty_at_step_finish (advanced by move commits)

  NOTE: existing 'rack_id' field is reused as the 'current rack' pointer
  (already there on line 95). Adding requires_rack_assignment as a
  related from recipe_node_id for runtime gate evaluation.

fp.job:
  + qty_received, qty_visual_inspection_rejects, qty_rework
  + special_requirements (Text — paper traveller header)
  + active_timer_ids (filtered O2M, depends on Task 7's state field)
  + move_ids (O2M to fp.job.step.move)

All additive. No removed fields. Existing battle tests unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:07:19 -04:00
gsinghpal
0794f7e3c9 feat(sub12b): move-log list/form/search + Plating menu
Plating → Move Log (sequence 62, between Logistics 60 and Aerospace 65).
Form is read-only (create=false) since moves are produced by the
tablet flow, not the desktop UI.

Search filters: Today, Scrap/Rework, Racked. Group-by: Job, Operator,
Transfer Type.

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