diff --git a/fusion_plating/CLAUDE.md b/fusion_plating/CLAUDE.md index 8cce0a39..b2053271 100644 --- a/fusion_plating/CLAUDE.md +++ b/fusion_plating/CLAUDE.md @@ -420,6 +420,107 @@ Plan: [docs/superpowers/plans/2026-05-22-shopfloor-tablet-redesign-plan.md](docs - Don't add `web.assets_web_dark` entries to the manifest — Odoo 19 auto-compiles `web.assets_backend` SCSS into both bundles - Don't bypass `_fp_should_block_predecessors()` when computing step blockers — keep `blocker_kind=predecessor` logic in sync with `can_start` +## Shop Floor — Plant View kanban (2026-05-23 redesign) + +**Default Shop Floor surface** for new installs (gated by feature flag +`ir.config_parameter['fusion_plating_shopfloor.layout']`, values `legacy` +or `v2`). Legacy per-step kanban (`fp_shopfloor_landing`) remains +accessible by flipping the flag back to `legacy` in Settings → Fusion +Plating. + +**Why redesign:** the per-step kanban produced one card per recipe step +per column, so a 14-step recipe spawned 9+ cards for ONE job across the +board. With 17 active jobs the board showed 100+ duplicate cards across +narrow columns. The new design is **one card per `fp.job`** at the +**department level** — recipe step count no longer drives layout width. + +**Spec:** `docs/superpowers/specs/2026-05-23-shopfloor-plant-view-design.md` +**Plan:** `docs/superpowers/plans/2026-05-23-shopfloor-plant-view-plan.md` + +### Layout — 9 fixed columns in process sequence + +`Receiving → Masking → Blasting → Racking → Plating → Baking → +De-Racking → Final inspection → Shipping` + +Columns are first-class — they always render in this exact order, never +reorder, never collapse when empty. Driven by `fp.work.centre.area_kind` +Selection (added 2026-05-23). Each `fp.job.step.area_kind` is computed +(stored) from `work_centre.area_kind` with a fallback to a step-kind +dispatch table (`_STEP_KIND_TO_AREA` in `fusion_plating_jobs/models/fp_job_step.py`). + +**Spec D3:** all wet-line steps (Soak Clean, Electroclean, Acid Dip, +Etch, Desmut, Zincate, Rinse, E-Nickel, Chrome, Anodize, Black Oxide, +Drying) roll up into the **Plating** column. The tank chip on the card +distinguishes them. + +**Spec D4:** De-Masking folds into De-Racking (no separate column). + +**Spec D5:** Contract Review (paperwork) cards live in Receiving with a +purple "📋 QA-005" chip — they're admin gates, not physical work. + +### Card state catalog — 13 mutually-exclusive states + +`fp.job.card_state` is a stored Char computed in `_compute_card_state` +(see `fusion_plating_jobs/models/fp_job.py`). Explicit precedence +dispatch matching spec §6.2 — first match wins: + +`no_parts → on_hold → awaiting_signoff → awaiting_qc → bake_due → +predecessor_locked → idle_warning → done → contract_review → +running_mine/running → ready_mine/ready` + +Each state has a distinct background tint + left-border color + chip + +mini-timeline marker color. See `_plant_card.scss` for the mapping. The +"mine" variants (`ready_mine`, `running_mine`) light up only when the +active step's work centre is in `res.users.paired_work_centre_ids` (the +M2M holds one row in MVP, mirrors the existing single-station picker). + +### Backend — single endpoint, denormalized payload + +`/fp/landing/plant_kanban` (controller in +`fusion_plating_shopfloor/controllers/plant_kanban.py`) returns +`{ok, mode, paired_station, kpis, columns, cards}` in one JSONRPC call. +Frontend has zero per-card RPCs — every card field comes pre-formatted +from the controller's `_render_card`. State-chip text (with elapsed +times, operator names, hours-idle) is interpolated server-side. + +### Frontend — OWL component tree + +``` +FpPlantKanban (client action 'fp_plant_kanban') +└── FpTabletLock (PIN gate wrapper) + ├── PlantHeader (KPIs + filter chips + mode toggle + station picker) + └── Board (9 × Column) + ├── FpColumnHeader (with 'You're here' badge for paired column) + └── FpPlantCard[] (each with FpMiniTimeline) +``` + +Polls every 10s. Filter state persists in localStorage. All 13 card +states styled via `.state-` CSS modifier classes on a single +shared `.o_fp_plant_card` base. The mini-timeline renders 9 colored +dots driven by `fp.job.mini_timeline_json` (Python emits the array +shape — frontend just maps state → CSS class). + +### Critical implementation gotchas (project rules applied) + +- **OWL templates only expose `Math` as a JS global** (Rule 20). All + coercion (String, Number, parseInt) MUST happen in JS — `tag_chip_class()` + / `progress_style` etc. live in plant_card.js, not in the XML. +- **SCSS @import is forbidden** (Rule 8). `_plant_tokens.scss` loads + FIRST in the manifest's `web.assets_backend`; subsequent component + partials get the `$plant-*` vars via the concatenated bundle. +- **Dark mode** via `$o-webclient-color-scheme == dark` compile-time + branch in `_plant_tokens.scss` (NOT runtime class selectors). + +### How to switch back to legacy + +```sql +UPDATE ir_config_parameter SET value = 'legacy' + WHERE key = 'fusion_plating_shopfloor.layout'; +``` + +Or use Settings → Fusion Plating → Shop Floor Layout. Both surfaces +write the same `ir.config_parameter` key. + ## Deployment ### odoo-entech (LXC 111 on pve-worker5) diff --git a/fusion_plating/fusion_plating_shopfloor/models/res_config_settings.py b/fusion_plating/fusion_plating_shopfloor/models/res_config_settings.py index bc83dfa0..5ba99be3 100644 --- a/fusion_plating/fusion_plating_shopfloor/models/res_config_settings.py +++ b/fusion_plating/fusion_plating_shopfloor/models/res_config_settings.py @@ -23,7 +23,7 @@ class ResConfigSettings(models.TransientModel): ('v2', 'Plant View (one card per job, 9 columns)'), ], string='Shop Floor Layout', - default='legacy', + default='v2', config_parameter='fusion_plating_shopfloor.layout', help='Switches the Shop Floor client action between the legacy ' 'per-step kanban and the v2 plant view. Defaults to legacy '