Multi-section design covering: - 3 OWL client actions: fp_shopfloor_landing (replaces fp_shopfloor_tablet + folds in fp_plant_overview), fp_job_workspace (NEW full-screen WO surface), fp_manager_dashboard (refactored — 4 sibling tabs incl. Workflow Funnel, Approval Inbox, At-Risk). - 5 shared OWL services: WorkflowChip, GateViz, SignaturePad, HoldComposer, KanbanCard — reused across all three client actions to enforce one-widget-one-place and prevent terminology drift. - Backend additions: 8 new RPC endpoints, blocker_kind/reason computes on fp.job.step, display_wo_name + late_risk_ratio + active_step_id on fp.job, bottleneck_score on fp.work.centre, auto-pause cron (fixes 411h ghost timer), ACL lift for operator group per "techs wear multiple hats" rule. - Terminology pass: WO # 00001 (display only, sequence rename deferred), Shop Floor / Up Next / Embrittlement Bakes / etc. - 5-phase deploy sequence, each phase independently shippable. - Out of scope (deferred to v2): cost roll-up, cycle time, per-tech throughput, system-wide sequence rename. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
28 KiB
Shop Floor Tablet Redesign — Design Spec
Date: 2026-05-22
Status: Brainstorm complete, awaiting user review
Authors: Garry Singh + Claude
Module owners: fusion_plating_shopfloor, fusion_plating_jobs
Target client: EN Technologies (Fusion Plating)
1. Context
The current Shop Floor tablet view (client action fp_shopfloor_tablet, OWL component ShopfloorTablet) was built during the initial Fusion Plating implementation. Since then the underlying models — fp.job, fp.job.step, fp.job.workflow.state, fp.certificate, fp.thickness.reading, fp.job.consumption, fp.job.node.override, fp.racking.inspection and friends — have grown substantially. Many of those new fields, actions, and workflows are not surfaced on the tablet.
Symptoms observed on a live development instance:
- Step name shows "Active: Blasting" with no WO/customer context
- Qty rendered as "Qty 17/1" — ambiguous direction
- Step position shown as "step 1.1/11" (sequence divided by 10)
- Active timer reading 411:52:16 — a stale start that never finished and was never auto-paused
- "SIGN-OFF REQUIRED" chip is informational only; no actual sign-off control
- Customer spec, drawings, recipe overrides, milestone progress, holds, and most lifecycle actions are invisible
Three roles operate the system: Owner, Manager, Technician. Technicians wear multiple hats (receiving, plating, QC, shipping) — the client explicitly wants minimal gating between roles.
2. Goals
- A technician can manage a WO end-to-end from a single full-screen workspace, without typing into search bars or jumping to the back-office.
- A manager can see at a glance: where every WO is in the workflow, what needs their decision right now, what's trending late, and where the bottlenecks are.
- All recent additions to
fp.job/fp.job.step(workflow milestones, blocker reasons, recipe overrides, customer spec, etc.) are surfaced on the tablet and manager dashboard. - Terminology matches how techs talk on the shop floor — "WO # 00001" not "WH/JOB/00001".
- The system never displays a 411-hour ghost timer.
3. Non-goals (v1)
- Multi-tablet pairing per technician
- Offline-first / PWA mode
- Voice input
- Performance optimization beyond ~500 active jobs (current scale: ~50)
- Webhooks to external dashboards
- Cost roll-up per job, cycle time per recipe, per-tech throughput (P2 — deferred to v2)
- System-wide rename of
fp.jobsequence (WH/JOB/...→WO ...) — display-only on tablet for now; back-office/reports/emails keep current sequence until a separate decision is made
4. Terminology decisions
All approved in brainstorm:
| Element | Was | Is now |
|---|---|---|
| Page title | "Tablet Station" | Shop Floor (with station chip e.g. "@ EN Plating Tank") |
| Document number | WH/JOB/00001 |
WO # 00001 (display only) |
| Active step header | "Active: Blasting" | "WO # 00001 — Blasting" (Step 1 of 11) |
| Qty | "Qty 17/1" | "1 / 17 done" + scrap subtext + mini progress bar |
| Step position | "step 1.1/11" | "Step 1 of 11" |
| Sign-off chip | "SIGN-OFF REQUIRED" | "Finish & Sign Off" action button (replaces plain Finish) |
| Queue heading | "My Queue" | "Up Next" (at this station) |
| Bath state | "OPERATIONAL / LOG: OUT_OF_SPEC" | "Operating" / "Last log out of spec" |
| Bake panel | "Bake Windows" | "Embrittlement Bakes" |
| Gate panel | "First-Piece Gates" | "First-Piece Inspections" |
| Tile set | 6 mixed tiles | 4 tech-relevant tiles: Ready · Running · Bakes Due · Holds (others move to manager dashboard) |
| Stale timer | "411:52:16" | Auto-pause at 8h (configurable) with chatter audit; display switches to "Started Nd ago" past 24h |
5. Architecture — option B (specialized components + shared services)
Three OWL client actions, five shared OWL services, a small set of backend additions. Each client action is independently deployable.
┌────────────────────────────────┐ ┌────────────────────────────────┐ ┌────────────────────────────────┐
│ fp_shopfloor_landing │ │ fp_job_workspace │ │ fp_manager_dashboard │
│ (replaces fp_shopfloor_tablet │ │ NEW — full-screen WO surface │ │ refactored — 4 tabs │
│ + folds in fp_plant_overview)│ │ │ │ │
│ • station-scoped kanban │ │ • sticky header + WO chips │ │ • Workflow Funnel (default) │
│ • All-Plant toggle │ │ • workflow milestone bar │ │ • Approval Inbox │
│ • QR scan, station picker │ │ • step list + side panel │ │ • Plant Board (existing) │
│ • tap card → JobWorkspace │ │ • sticky action rail │ │ • At-Risk │
└────────────────────────────────┘ └────────────────────────────────┘ └────────────────────────────────┘
│ │ │
└──────────────────────────────────┴──────────────────────────────────┘
│
┌────────────┴────────────┐
│ Shared OWL services │
│ WorkflowChip · GateViz │
│ SignaturePad · KanbanCard │
│ HoldComposer │
└─────────────────────────┘
5.1 Shared OWL services
| Service | Used by | Props | Depends on |
|---|---|---|---|
| WorkflowChip | Landing card · Workspace header · Manager funnel | { state: {id, name, color}, nextActionLabel? } |
fp.job.workflow.state records (already shipped). Reads color field. |
| GateViz | Workspace step rows · Manager "Needs Worker" cards | { canStart, blockerKind, blockerReason, jumpTarget? } |
New fp.job.step.blocker_kind + blocker_reason computes |
| SignaturePad | Workspace (Finish & Sign Off) · Cert issue | { title, contextLabel, onSubmit(dataUri), onCancel } |
Odoo dialog service; HTML canvas + pointer events |
| HoldComposer | Workspace (Hold button) · Manager Approval Inbox | { jobId, stepId?, defaultQty, partRef, onCreated(hold) } |
New endpoint /fp/workspace/hold (with photo attachment) |
| KanbanCard | Landing (station + all-plant) · Manager (Plant Board + Workflow Funnel) | { data, density: 'compact'|'normal', showWorkflowChip, showWorkcenter, showAssignedTo, onTap } |
Embeds WorkflowChip + GateViz badge |
Each service is its own file under fusion_plating_shopfloor/static/src/js/components/. Roughly 80–200 lines OWL + 30–80 lines SCSS per service.
5.2 Landing component (fp_shopfloor_landing)
Replaces today's fp_shopfloor_tablet, folds in fp_plant_overview. Single entry surface for technicians.
Layout regions (top-to-bottom):
- Header strip — "Shop Floor" title, station chip, station picker, mode toggle (
Station⟷All Plant), QR scan controls (Code + Camera), refresh indicator. - KPI tile row (4 tiles) — Ready · Running · Bakes Due · Holds. Holds turns red when > 0.
- Kanban board — columns = work centres; cards =
KanbanCard(one per WO at that work centre); urgency-sorted within column (existing logic inplant_overview.pycarries over). Drag-and-drop between columns keeps current behaviour. - Optional left filter rail (collapsed by default) — search box, priority, customer, due-by, blocker filter. Promote the existing plant_overview search bar.
- Footer — auto-refresh indicator + "Last sync HH:MM:SS".
Mode behaviour:
| Mode | Columns shown | Default when |
|---|---|---|
| Station | Paired work centre + Unassigned + next 1–2 work centres in the recipe flow | A station is paired (via QR scan or picker) |
| All Plant | Every active work centre, recipe-flow order | No station paired, OR user toggles |
Toggle persists in localStorage per tablet (same pattern as fp_tablet_station_id).
Card tap behaviour:
action.doAction({
type: 'ir.actions.client',
tag: 'fp_job_workspace',
params: { job_id: card.job_id, focus_step_id: card.current_step_id }
});
Browser back returns to Landing with kanban scroll/mode preserved.
QR scan dispatch (existing /fp/shopfloor/scan endpoint, unchanged):
| Scanned | Behaviour |
|---|---|
FP-STATION:<code> |
Pair tablet, switch to Station mode |
FP-JOB:<name> |
Open JobWorkspace for that WO |
FP-STEP:<id> |
Open JobWorkspace, focus that step |
FP-TANK:<code> / FP-BATH:<name> |
Chemistry quick-log dialog (existing endpoint) |
FP-OVEN:<code> |
Jump to next bake awaiting that oven |
Auto-refresh — every 15s.
Files:
fusion_plating_shopfloor/
controllers/landing_controller.py ← NEW (~250 lines)
static/src/js/shopfloor_landing.js ← OWL (~600 lines)
static/src/xml/shopfloor_landing.xml
static/src/scss/shopfloor_landing.scss
5.3 Job Workspace component (fp_job_workspace)
The heart of the redesign. Full-screen surface a tech opens by tapping a kanban card.
Layout regions (sticky top → scrollable middle → sticky bottom):
| Region | Sticky | Data | Behaviour |
|---|---|---|---|
| Back | top | — | doAction back to Landing, preserves kanban scroll/mode |
| WO header | top | display_wo_name, partner_id, part_catalog_id + rev, qty/qty_done/qty_scrapped, date_deadline, workflow_state_id, quality_hold_count, customer_spec_id |
+1 Done / −1 Done / +1 Scrap quick bumps inline. Holds count → opens Holds drawer. |
| Workflow milestone bar | top | All fp.job.workflow.state records ordered by sequence; current = workflow_state_id; next_milestone_action + next_milestone_label |
Dots: passed (●), current (filled), pending (○). "Next" button on right fires /fp/workspace/advance_milestone. Disabled until preconditions met. |
| Step list (left/center, scrolls) | scrolls | fp.job.step_ids sorted by sequence |
Each row uses step-row template (see below). Active step auto-scrolled and auto-expanded if focus_step_id param set. |
| Side panel (collapsible right) | scrolls | customer_spec_id (PDF), attachments, chatter |
Three sub-cards: Spec (inline via fusion_pdf_preview), Drawings, Notes (chatter — read + quick-add). Collapses to icon strip on narrow screens. |
| Action rail | bottom | — | Always: Create Hold (HoldComposer), Add Note, Photo. Conditional: Issue Cert (when _fp_has_draft_required_certs()), Mark Done / Schedule Delivery / Mark Shipped (per next_milestone_action). |
Step row anatomy:
- Collapsed (default for done/pending/paused): one line — icon + Step N · Name + assigned tech + duration + state badge
- Expanded active (auto for
state == 'in_progress'): recipe chips + instructions + primary + secondary actions - Expanded by tap (any step): same shape, action buttons gated by
can_start
Per-step actions:
| Button | Visible when | Calls |
|---|---|---|
| Start | state in ('ready','paused') AND can_start |
/fp/shopfloor/start_wo |
| Finish (or Finish & Sign Off) | state == 'in_progress' |
/fp/shopfloor/stop_wo (finish=true) OR /fp/workspace/sign_off if requires_signoff |
| Pause | state == 'in_progress' |
step.button_pause() |
| Skip | state in ('ready','paused') AND user is supervisor+ |
step.button_skip() |
| Move Parts | always | FpMovePartsDialog (existing) |
| Move Rack | when kind == 'rack' |
FpMoveRackDialog (existing) |
| Quick QC | when quick_look_prompt_ids non-empty |
step.action_open_quick_look() |
| Operator Inputs | when step has unrecorded inputs | step.action_open_input_wizard() (existing wizard) |
| Photo | always | inline camera → attach to step |
| Stop Timer (correction) | when duration looks wrong | FpStopTimerDialog (existing) |
| Open in backend | always (small icon) | doAction to fp.job.step form (escape hatch) |
When step is blocked (can_start == False), action button row is replaced by GateViz block.
When step is opted out (override_ids says excluded), row shows ✕ icon + "Skipped per recipe override" + supervisor-only "Re-include" button.
Auto-pause integration — if _cron_autopause_stale_steps flips a step to paused, the row's chatter reflects "Auto-paused after Nh idle". Tech can tap Resume.
Auto-refresh — every 15s.
Files:
fusion_plating_shopfloor/
controllers/workspace_controller.py ← NEW (~400 lines)
static/src/js/job_workspace.js ← OWL (~800 lines)
static/src/xml/job_workspace.xml
static/src/scss/job_workspace.scss
static/src/js/components/{workflow_chip,gate_viz,signature_pad,hold_composer,kanban_card}.js
5.4 Manager Dashboard refactor (fp_manager_dashboard)
Same client action, four sibling tabs under a shared header + KPI strip.
KPI strip (extended): keep existing 4 always-on (Unassigned Steps · In Progress · Ready to Ship · Awaiting Assignment) + existing conditional reds (Missed Bakes · Open Holds · Stale Steps · Predecessor Locked) + 2 new: Pending Cert · At-Risk.
Tabs:
| Tab | Default? | Content |
|---|---|---|
| Workflow Funnel | yes | Vertical stack of fp.job.workflow.state records. Each row shows stage chip + count badge + first ~5 KanbanCards + "+ N more" drawer. Bar chart bar behind the row scaled to count. Tap card → JobWorkspace. |
| Approval Inbox | no | 4 grouped strips: Holds to Release (state in ('on_hold','under_review')), Certs to Issue (all_steps_terminal + draft required cert), Scrap to Review (recent qty_scrapped bumps with operator reason), Override Requests (deferred — placeholder). Per-row inline action buttons + bulk-action ("Release all"). |
| Plant Board | no | Today's existing 3-column "Needs Worker / In Progress / Team" view — unchanged behaviour. Becomes one tab among four. |
| At-Risk | no | 3 sub-panels: Trending Late (sorted by late_risk_ratio desc, top 20), Hold Reasons (open holds grouped by hold_reason), Bottleneck Heatmap (work centres ranked by bottleneck_score). |
Cross-tab features: live 8s refresh (existing cadence), QR scan in header, "Take Over Tablet" supervisor handover.
Permissions: dashboard already gated to group_fusion_plating_supervisor+. That stays. Owner + Manager hit this; Technicians don't.
Files:
fusion_plating_shopfloor/
controllers/manager_controller.py ← add 3 endpoints (funnel, approval_inbox, at_risk)
static/src/js/manager_dashboard.js ← refactor: extract Plant Board, add 3 sibling tabs
static/src/xml/manager_dashboard.xml
static/src/scss/manager_dashboard.scss
6. Backend support
6.1 HTTP endpoints
All type='jsonrpc', auth='user'.
NEW — added by this work:
| Endpoint | Lives in | Purpose |
|---|---|---|
POST /fp/landing/kanban |
landing_controller.py |
Station OR all-plant kanban data |
POST /fp/workspace/load |
workspace_controller.py |
Full Job Workspace payload |
POST /fp/workspace/hold |
workspace_controller.py |
HoldComposer create (with photo) |
POST /fp/workspace/sign_off |
workspace_controller.py |
Signature + finish step atomically |
POST /fp/workspace/advance_milestone |
workspace_controller.py |
Fire next_milestone_action |
POST /fp/manager/funnel |
manager_controller.py (add) |
Workflow funnel data |
POST /fp/manager/approval_inbox |
manager_controller.py (add) |
Holds + draft certs + scrap to review |
POST /fp/manager/at_risk |
manager_controller.py (add) |
Late-risk + hold reasons + bottlenecks |
KEPT — unchanged, used by new components via wrappers: /fp/shopfloor/scan, start_wo, stop_wo, start_bake, end_bake, log_chemistry, log_thickness_reading, bump_qty_done, bump_qty_scrapped, mark_gate, pair_station.
DEPRECATED — kept as stubs for 1 release, then removed:
/fp/shopfloor/tablet_overview→ calls/fp/landing/kanbaninternally/fp/shopfloor/plant_overview→ calls/fp/landing/kanban?mode=all_plant/fp/shopfloor/queue→ removed (no replacement)
6.2 Model fields / computes
On fp.job (fusion_plating_jobs/models/fp_job.py):
| Field | Type | Purpose |
|---|---|---|
display_wo_name |
computed Char | "WO # 00001" formatter from name |
late_risk_ratio |
computed Float, stored | remaining_planned_minutes / minutes_to_deadline |
active_step_id |
computed Many2one→fp.job.step | Current in_progress step (Workspace landing focus) |
On fp.job.step (fusion_plating_jobs/models/fp_job_step.py):
| Field | Type | Purpose |
|---|---|---|
blocker_kind |
computed Selection | predecessor · contract_review · parts_not_received · racking_required · manager_input · none |
blocker_reason |
computed Char | Human reason (e.g. "Waiting on Step 3: Activation") |
blocker_jump_target_model |
computed Char | Optional tap-to-jump target model |
blocker_jump_target_id |
computed Integer | Optional tap-to-jump target id |
On fp.work.centre (fusion_plating/models/fp_work_centre.py):
| Field | Type | Purpose |
|---|---|---|
bottleneck_score |
computed Float, non-stored | active_step_count × avg_wait_minutes |
avg_wait_minutes |
computed Float, non-stored | Rolling 7-day avg ready→start wait |
On fusion.plating.process.node (recipe node):
| Field | Type | Purpose |
|---|---|---|
long_running |
Boolean | Opt out of auto-pause (24h bakes etc.) |
6.3 Auto-pause cron
<!-- fusion_plating_jobs/data/fp_cron_data.xml -->
<record id="ir_cron_autopause_stale_steps" model="ir.cron">
<field name="name">FP Jobs: auto-pause stale in-progress steps</field>
<field name="model_id" ref="model_fp_job_step"/>
<field name="state">code</field>
<field name="code">model._cron_autopause_stale_steps()</field>
<field name="interval_number">30</field>
<field name="interval_type">minutes</field>
<field name="active" eval="True"/>
</record>
Method (fp_job_step.py):
@api.model
def _cron_autopause_stale_steps(self):
threshold = float(self.env['ir.config_parameter'].sudo()
.get_param('fp.shopfloor.autopause_threshold_hours', 8))
deadline = fields.Datetime.now() - timedelta(hours=threshold)
stale = self.search([
('state', '=', 'in_progress'),
('date_started', '<', deadline),
('recipe_node_id.long_running', '=', False),
])
for step in stale:
step.button_pause()
step.message_post(body=Markup(
"<b>Auto-paused</b> after %.1fh idle. "
"Resume from the tablet when work continues."
) % threshold)
_logger.info("Auto-paused step %s after %.1fh idle", step.id, threshold)
ir.config_parameter key: fp.shopfloor.autopause_threshold_hours (default 8).
6.4 ACL changes (operator group)
Per "techs wear multiple hats" rule — minimal new gates.
| Model | Read | Write | Create | Unlink | Notes |
|---|---|---|---|---|---|
fp.certificate |
✓ existing | NEW ✓ | — | — | Flip draft → issued from tablet "Issue Cert" |
fp.thickness.reading |
NEW ✓ | NEW ✓ | NEW ✓ | — | Capture Fischerscope readings from tablet |
fp.job.node.override |
NEW ✓ | — | — | — | Read-only — tech sees opt-out badge |
Supervisor-only operations enforced in workspace_controller.py (not via ACL):
- Step Skip (
button_skip) - Hold Release (state transition
on_hold→released) - Override Re-include
6.5 Terminology — display_wo_name
fp.job.display_wo_name is a computed Char that formats name as WO # 00001. All new tablet/dashboard payloads use this field. The underlying fp.job.name (WH/JOB/00001) stays unchanged — reports, emails, back-office forms continue using name.
System-wide sequence rename is out of scope for this work. If pursued separately, it requires: (a) updating ir.sequence prefix to WO , (b) backfill script for existing records, (c) coordination with any external integrations that grep on the old prefix.
7. Build & deploy sequence
Each phase is independently deployable. Rollback is per-phase, not all-or-nothing.
| Phase | Ships | Independently deployable? |
|---|---|---|
| 1 | Shared OWL services + JobWorkspace + workspace_controller + fp.job.step blocker_* computes + display_wo_name. Opens from existing fp.job form smart button. |
Yes — works before Landing refactor |
| 2 | Auto-pause cron + ACL lift + late_risk_ratio + active_step_id computes |
Yes — silent infra |
| 3 | landing_controller + fp_shopfloor_landing component. Old fp_shopfloor_tablet menu redirected. PlantOverview menu hidden. |
Yes — Workspace already works via smart button |
| 4 | 3 new manager endpoints + manager dashboard refactor (4 tabs) + bottleneck_score compute |
Yes |
| 5 | Cleanup: remove deprecated endpoint stubs, retire fp_plant_overview module dir | Last |
8. Testing strategy
8.1 Python tests (fusion_plating_shopfloor/tests/)
| Test | Verifies |
|---|---|
test_display_wo_name |
Formatter handles various name shapes |
test_late_risk_ratio |
Correct ratio with deadline / no deadline / overdue / not started |
test_active_step_id |
Sole in_progress step; empty when none; first-by-sequence when multiple |
test_blocker_kind_and_reason |
Each kind returns correct enum + human string + jump target |
test_autopause_cron |
Stale flips; chatter posted; respects long_running; idempotent |
test_workspace_load_payload |
Full payload shape — keys, types, opted-out marked |
test_workspace_sign_off |
Signature captured, step finished, empty-sig rejected |
test_workspace_advance_milestone |
Fires only when preconditions met; friendly error otherwise |
test_hold_composer_create |
Hold + photo + qty split; rollback on validation error |
test_acl_operator_permissions |
Operator can issue cert, cannot skip step (controller gate) |
test_funnel_and_inbox |
Funnel grouping correct; inbox returns all 4 buckets |
8.2 OWL tests (light)
WorkflowChiprenders correct color per stateGateVizrenders correct copy per blocker_kindSignaturePadreturns non-empty data URI after strokeHoldComposervalidates qty ≤ remaining before submitKanbanCardcollapses chips at compact density
8.3 Manual QA checklist
Lives at docs/qa/shopfloor-redesign-qa.md. 10-step walkthrough covering: pairing → Landing modes → tap card → Workspace → Finish & Sign Off → Create Hold → Manager Funnel → Approval Inbox release → auto-pause test → dark mode.
9. Observability
_logger.INFOon milestone advance, hold create from tablet, sign-off, auto-pause_logger.WARNINGon workspace_load with bad job_id, sign_off with empty data URI_logger.EXCEPTIONon controller failures (existing pattern)- Chatter audit on auto-pause, hold create, milestone advance, sign-off
Future metrics (flagged, no infra now): tablet refresh frequency, time-to-Start from Workspace open, auto-pause rate, hold creation rate.
10. Edge cases
| Case | Handling |
|---|---|
| Job has zero steps | "Recipe not generated" placeholder + back-office link |
| Job has 50+ steps | Standard scroll, no virtualization in v1 |
| All steps in_progress (defensive) | active_step_id picks first by sequence; logs warning |
| No workflow states defined | Bar hides; Next button disabled |
| Step state changed during 15s gap | Refresh corrects; no error toast |
| Two techs tap Start simultaneously | button_start idempotent; second call returns state |
| Wrong station scanned | Header "Unpair" link; localStorage cleared |
| Network drop mid-action | Toast "Saving failed — tap to retry"; UI state preserved |
| 24h-bake step | recipe_node.long_running=True skips auto-pause |
| Customer spec PDF missing | Side panel: "No customer spec attached" |
| Funnel with 200+ jobs in stage | Top 5 cards + "View all (N)" drawer |
| Operator with no facility | Landing prompts pick-or-scan station |
| HoldComposer fails after photo upload | Photo cleaned in except block — no orphans |
| Manual step (no recipe_node_id) | Chips/instructions empty — OK |
| Sign-off step finished from back-office | Workspace re-renders without re-prompting |
| Tech opens Workspace for unassigned job | Allowed — read+write per "many hats" rule |
11. Performance
| Surface | Load shape | Mitigation |
|---|---|---|
| Landing kanban (All Plant, ~400 cards) | Existing plant_overview.py batch prefetch carries over |
None new |
| Workspace load (1 job × ~11 steps) | Trivial | None |
| Manager funnel (~50 jobs × 9 stages) | Trivial | None |
| Manager At-Risk (7-day step state scan) | Potentially heavy on large plants | Cache 60s per facility |
| Auto-pause cron | Filtered query, no N+1 | None |
late_risk_ratio (stored) |
Recomputed on step state change | @api.depends triggers |
12. Rollback strategy
| Phase | Rollback |
|---|---|
| 1 (Workspace) | Hide smart-button entry in fp.job form. Workspace becomes orphan, harmless. |
| 2 (Cron + ACL) | Disable cron via UI. ACL changes are CSV-line edits. |
| 3 (Landing) | Re-enable old fp_shopfloor_tablet menu. Old endpoint stub still active. |
| 4 (Manager) | Revert manager_dashboard.xml to single Plant Board tab. |
| 5 (Cleanup) | Defer if issues — leave stubs longer. |
Only stored field added is late_risk_ratio Float — additive ALTER TABLE, safe to drop.
13. Backwards compatibility
- All existing QR codes keep working —
/fp/shopfloor/scanunchanged - Existing
fp.jobform smart buttons → Workspace opens via doAction - Existing
fp.job.stepform view stays as "Open in backend" escape hatch - Old reports/emails keep showing
WH/JOB/00001(sequence rename deferred)
14. Decisions log
| Decision | Rationale |
|---|---|
| Hybrid mental model (queue → workspace), not pure queue or pure job-first | Queue is the natural entry; full workspace solves "manage the whole job" goal |
| One tablet, no role-segmentation per persona | Client said techs wear multiple hats; minimal gating |
| Architecture B (specialized components + shared services) over mega-component | Each surface has its own lifecycle; shared services enforce consistency |
| Station-scoped kanban as default landing, All Plant as toggle | Matches physical reality (tablet at station) + provides escape hatch |
| Approval Inbox + Workflow Funnel as new manager tabs, Plant Board stays | Tactical (assignment) and strategic (where is everything) views coexist |
| Auto-pause stale timers at 8h default | Solves the 411-hour ghost timer permanently; protects cost/cycle-time math |
| WO # display only; sequence rename deferred | Lower risk; user can choose system-wide rename separately |
| ACL lift for operator group on cert / thickness reading / override read | Per "techs wear many hats" rule; supervisor-only ops enforced in controller, not ACL |
15. Out of scope for v1
- Multi-tablet pairing per tech
- Offline-first / PWA mode
- Voice input
- Performance optimisation beyond ~500 active jobs
- Webhooks to external dashboards
- Cost roll-up per job (P2)
- Cycle time per recipe (P2)
- Notification audit feed (P2)
- Per-tech throughput (P2)
- System-wide
fp.jobsequence rename toWOprefix
Next step: user reviews this spec; once approved, transition to superpowers:writing-plans skill to produce the phased implementation plan.