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

6.4 KiB
Raw Blame History

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.logologo_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 · ThkBAKE 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 / NMASK/BAKE flags → per-box QR → Part#CustomerPO/QtyDue/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.