Files
Odoo-Modules/fusion_plating/docs/superpowers/specs/2026-06-03-box-tracking-sticker-redesign-design.md
gsinghpal d531faad12 feat(fusion_plating): box-level tracking (fp.box) + thermal job-sticker redesign
Box registry: new fp.box model (fusion_plating_receiving), one record per
received box, auto-created when a receiving is marked Counted (idempotent
_fp_sync_boxes — grows/shrinks with box_count_in, never touches an advanced
box). Status received -> racked -> in_process -> packed -> shipped, per-box
scannable QR (/fp/box/<id> controller). Backfill migration for receivings
counted before tracking shipped. Boxes list/kanban/form + receiving smart
button.

Job stickers redesigned (thermal label, 6x4 in / 152x102mm, mm layout @
paperformat dpi=96 so mm maps 1:1 in wkhtmltopdf — see rule 14):
- Internal Job Sticker = Layout A, ONE per job (shop notes from
  x_fc_internal_description, job QR).
- External Job Sticker = Layout B, ONE per fp.box (BOX n/N, per-box QR,
  factory company logo, customer-facing notes). Dynamic MASK badge
  (x_fc_masking_enabled) + BAKE block (x_fc_bake_instructions), length-tiered
  notes font. Display logic in fp.job._fp_sticker_data().

Also retains the SO/WO box-sticker MemoryError fix in report_fp_wo_sticker.xml
(per-box loop sourced from fp.receiving.box_count_in + 100-label safety cap).

Verified live on entech: 111 boxes backfilled (31 receivings), External renders
one page per box, Internal one per job, scan endpoint 303->login.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 13:21:54 -04:00

130 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Box-Level Tracking + Job Sticker Redesign — Design Spec
Date: 2026-06-03
Status: Approved (brainstormed with client), implementation in progress.
## Summary
Two coupled deliverables:
1. **Job sticker redesign** (thermal-label-friendly, 6×4 in / 152×102 mm):
- **Internal Job Sticker → Layout A** (stacked: identity band + full-width
instructions), printed **one per job**.
- **External Job Sticker → Layout B** (left identity rail + tall instructions
column), printed **one per box**, carrying the **box identity** (BOX n/N)
and a **per-box QR**. Shows the **factory logo** (`env.company.logo`).
2. **Box-level tracking**: a new `fp.box` registry, one record per received box,
auto-created at receiving, with a status workflow and per-box scannable QR.
## Decisions (locked with client)
| Q | Decision |
|---|---|
| Label size | Keep 6×4 in (152×102 mm). |
| Redesign goals | Readability/scan-speed + thermal print quality (no grey fills — solid-black bands + knockout white text; thick rules; bold sans). |
| Masking on label | **MASK badge** (on/off flag) when `sale.order.line.x_fc_masking_enabled` is true. No detail text. |
| Baking on label | **BAKE block** showing `sale.order.line.x_fc_bake_instructions` text, only when present. Also a BAKE flag for at-a-glance. |
| Notes source | Internal = `x_fc_internal_description`; External = SO line `name` (customer-facing). |
| Long notes | Notes-dominant zone, **length-tiered font shrink** to keep to **one label**, clip with "…see traveller" only in the extreme. |
| Factory logo | On **External only** (header), from `env.company.logo``logo_web` → company partner image. Internal stays clean. Thermal caveat: prefer a mono/high-contrast logo. |
| Box tracking depth | **Box registry** — per-box record, status, scannable QR. (Not box-contents.) |
| Internal copies | **One per job.** |
| External copies | **One per box.** |
| Box QR | **Per-box** — encodes `/fp/box/<id>`. |
## Label layouts (approved mockups)
Both labels: outer 0.9 mm border, `overflow:hidden` single-page guard, dynamic
blocks render only when their field has content.
**Layout A (Internal, per job):** full-width stacked rows —
`[logo | WO# band + INTERNAL tag | QR]``Part# + MASK/BAKE flags`
one-line field strip `Customer · PO · Qty · Due · Thk``BAKE` block →
`NOTES` (full width, `x_fc_internal_description`, length-tiered, bottom padding).
**Layout B (External, per box):** absolute two-column —
- Left rail (50 mm): `logo` → black band `WORK ORDER <wo> | BOX n / N`
`MASK/BAKE` flags → per-box QR → `Part#``Customer``PO/Qty``Due/Thk`.
- Right column: `BAKE` block → `NOTES` (customer description, length-tiered).
- Full-height divider (rail `border-right`). CUSTOMER copy.
Reference mockups (Chrome-rendered, true 6×4):
`~/Downloads/fusion_sticker_concepts/Sticker-A-Internal-LongNotes.*`,
`Sticker-B-External.*`. Final proof renders through entech wkhtmltopdf.
## `fp.box` model (fusion_plating_receiving)
| Field | Type | Notes |
|---|---|---|
| `name` | Char | Sequence, e.g. `BOX/<wo-or-recv>/01`. |
| `box_number` | Integer | n (1..N). |
| `box_count` | Integer | N (related/snapshot of receiving `box_count_in`). |
| `receiving_id` | M2O `fp.receiving` | Origin. ondelete cascade. |
| `sale_order_id` | M2O `sale.order` | Related from receiving. |
| `job_id` | M2O `fp.job` | Resolved (single-job SO = that job; multi-job = first/SO-level, see edge cases). |
| `partner_id` | M2O `res.partner` | Related (customer). |
| `state` | Selection | `received → racked → in_process → packed → shipped` (+ `lost`/`cancelled`). |
| `qr` | Binary/compute | Encodes `<base_url>/fp/box/<id>`. |
| `location_note` | Char | Optional free text "where is it now". |
| `scan_event_ids` | (phase 2) | Per-scan log — deferred. |
Constraints: `(receiving_id, box_number)` unique. Append-only-ish; state advances.
## Auto-create at receiving
When `fp.receiving.box_count_in = N` is set and the receiving is confirmed
(state hook — reuse the existing box-count chatter point at
`fp_receiving.py:~1191`), create/sync N `fp.box` rows (1..N), linked to the
receiving + resolved job. **Idempotent**: changing N adds/removes trailing rows
(never renumbers existing tracked boxes). Manager can regenerate.
## Scanning
- Controller route `/fp/box/<int:box_id>` → resolves the box, shows its job /
status, allows advancing state (received→…→shipped). Tie into the existing
shopfloor scan wedge (`request.env.user` attribution — no `tablet_tech_id`).
- **Reconciliation**: helper flags a receiving/job whose boxes haven't all
reached `shipped` (so none are lost — matches the "ship back in the same
boxes" Sub-8 rule).
## Label binding
- **External job sticker** (`fusion_plating_jobs.report_fp_job_sticker_template`):
iterate the job's `fp.box` records → **one label per box** (Layout B), each
with its `box_number/box_count` + per-box QR (`/fp/box/<id>`). Replaces the
current `range(box_count_in)` loop in `report_fp_wo_sticker_inner`. When a job
has no `fp.box` rows yet, fall back to a single label (BOX 1/1).
- **Internal job sticker** (`report_fp_job_sticker_internal_template`): **one per
job** (Layout A), job QR (`/fp/job/<id>`), no box loop.
- Shared inner keeps the 100-label hard safety cap (defense-in-depth from the
WO-30072 OOM fix).
## UI
- Boxes list + kanban (group by `state`) under **Operations**; form with state
buttons + scan QR.
- Smart buttons: box count on `fp.receiving` and `fp.job` forms.
## Module placement
- Model + auto-create + views/menu/ACL → `fusion_plating_receiving`.
- Scan controller → `fusion_plating_receiving` (or shopfloor).
- Label templates → `fusion_plating_jobs` (job stickers) + shared inner in
`fusion_plating_reports`.
## Edge cases / open
- **Multi-job SO** (one SO line → multiple jobs via serial/thickness grouping):
boxes are physical (per shipment/receiving). MVP links a box to the SO's
primary job; the external sticker prints the SO's boxes. Revisit if a real
multi-job-per-box case appears.
- **Box ↔ part for multi-part SO**: out of MVP (registry, not contents).
- Per-box qty/contents = future "registry + contents" upgrade.
## Deploy / verify
entech (LXC 111 / pve-worker5), `-u fusion_plating_receiving fusion_plating_jobs
fusion_plating_reports` with the revert-on-failure guard. Verify: render both
stickers for a real job through wkhtmltopdf; confirm auto-create on a test
receiving; scan a box id.