Shop-floor sign-off currently makes operators redraw a signature every
time, and the drawing is discarded (reports read x_fc_signature_image).
Spec: use the saved Plating Signature (one-tap confirm-with-preview);
draw once when absent and persist it to x_fc_signature_image so future
sign-offs + reports reuse it. Tablet-workspace scope; no model/migration.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The jobs __manifest__.py data list references views/res_users_views.xml
(Plating Signature pad on the user preferences + full user form), and the
file was deployed live to entech, but it was never `git add`ed — so the
committed manifest pointed at a file absent from the repo. Fresh installs /
CI (and any clean-checkout deploy) failed with
`FileNotFoundError: .../fusion_plating_jobs/views/res_users_views.xml`.
Retrieved the live file from entech and committed it as-is.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Order-entry shortcut: when masking is toggled ON for an Express order line,
an amber "MASK" button appears to attach reference image(s)/PDF(s). The files
ride the existing _fp_apply_express_overrides_to_job path onto the job's
masking step, so the operator sees exactly what to mask — no recipe edit or
custom prompt needed.
- configurator: masking_attachment_ids on the wizard line + SO line;
action_upload_masking_ref; override branch writes refs onto mask steps;
amber multi-file MASK button (express_action_btns) shown when masking is on.
- jobs: x_fc_masking_attachment_ids on fp.job.step (per-step) + computed
rollup on fp.job; office "Masking Refs" form page (readonly preview).
- shopfloor: workspace step payload carries masking_refs (sudo'd attachment
read, rule 13m); operator sees thumbnail/PDF tiles on the mask step that
open in Odoo's full-screen FileViewer (zoom + swipe).
Verified end-to-end on entech: SO-line refs land on the mask step + job
rollup (WO-30091); payload mask_refs shape correct (is_image, /web/image).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Applied the same QR treatment to the Internal (Layout A) header QR: bumped the
box to 30mm and added the ~10% quiet-zone crop wrapper so the pattern fills the
box (finders intact), centered via the table cell. HD (1000px) already applied.
Verified live (WO-30072).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The no-tags QR used line-height centering, which wkhtmltopdf renders slightly
high (extra white at the bottom). Switched to a single-cell table with
vertical-align:middle (same mechanism as the with-tags case) so the QR centers
in its cell with balanced top/bottom margin. Verified live (WO-30072).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The barcode bakes a ~12% white quiet-zone border around the QR. Render the QR
oversized inside an overflow:hidden wrapper offset to clip ~10% off each edge
(under the quiet zone — finder patterns stay intact), so the black pattern
fills the box and reads bigger. Applied to both the full-width (no-tags) and
shared (with-tags) QR. White label cell around the wrapper preserves the scan
margin. Verified live (WO-30072, WO-30090) — finders intact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All three barcode_data_uri('QR', ...) calls bumped from 300px to 1000px
(under Odoo's 1.2M-pixel barcode cap, per rule 14). At the ~34mm display
size that's ~750 dpi — crisp on the label printer. Verified: PDF now embeds
a 1000x1000 QR XObject.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Taller QR row (30->36mm) and the QR now expands to a full-width centered ~34mm
when a job has neither masking nor baking (was leaving the right half empty);
when tags are present, QR ~32mm on the left with MASK/BAKE stacked on the right.
Logo/WO-band/field rows trimmed to fund the bigger QR. Verified live (WO-30072
no-tags full QR; WO-30090 BAKE tag).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
External Job Sticker rail: combined the separate QR row and MASK/BAKE flags
row into a single row — QR enlarged to ~28mm on the LEFT, MASK/BAKE badges
stacked on the RIGHT. WO band trimmed 18->16mm to free the vertical space.
Verified live on entech (WO-30090, BAKE present).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Surface switches between the plant kanban and job workspace used
doAction({..., target: "current"}), which APPENDS to Odoo 19's
controller/breadcrumb stack -- so the /odoo/... URL grew one segment
per switch, and the tablet lock/unlock window.location.reload()
preserved the bloat, compounding it every lock cycle. Switched those
navigations to target: "main" (Odoo sets clearBreadcrumbs when
action.target === "main" -> _computeStackIndex returns 0 -> stack
resets to a single action). The genuine one-level drill-down
(onJumpToBlocker -> hold/NCR form) keeps target: "current" so
breadcrumb-back still works there.
Also embeds the multi-rack racking panel inside the Racking step row
(gated on step.area_kind == 'racking') instead of a job-level section,
tying it to the recipe's Racking step.
19.0.37.0.1 -> 19.0.37.0.3. Both changes live on entech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Approved design for splitting a WO's parts across multiple racks + grouping
multiple WOs on one rack, plus the Phase 1 implementation plan (split +
independent movement). Phases 2 (grouping + Station screen) and 3 (Plant
Kanban rollup) are noted for follow-up plans.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Plant Kanban + Job Workspace made phone-responsive: height:100% + single
internal scroll (was 100vh, broke mobile scroll), compact header/workflow
bar, receiving part-line stacking so fields don't overflow, responsive
lock-screen tile grid.
- +/- stepper on the receiving "Boxes received" field.
- Multi-rack Racking panel (Phase 1): split a WO's parts across racks
(+Add Rack / Divide Equally / manual qty + Unassigned counter) on the Job
Workspace, shown only when the WO is at the Racking step (area_kind based,
excludes De-Racking). New /fp/racking/* controller.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Backend "Shipping & Receiving" menu lowered from Shop Manager to Technician
(all higher roles inherit Technician, so none lose access).
- Technicians granted r/w/c on fp.outbound.package and the manual/generate
label wizards — shipping parity with shop managers so they can generate
outbound labels and manage packages in the backend.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- _compute_area_kind: name-based override so de-rack/de-mask steps land in the
De-Racking column and bake/oven steps in Baking, regardless of a mis-tagged
recipe kind (fixed WO cards scattering into the wrong shop-floor columns).
- fp.rack.load jobs extension: racking-step resolution by area_kind (not the
corrupt kind), equal-split/override ops, fp.job qty_racked/unracked rollups,
and independent rack movement (per-line moves) + de-racking unrack.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Customers created from the Express Order / quote-configurator / part-catalog
pickers now default customer_rank=1, so they stay visible in those pickers and
the Customers menu (were landing at rank 0 and disappearing). The field context
is a real dict, not a string — Odoo 19 web_read does with_context(**context),
which throws TypeError on a str and broke the form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- group_fp_office_user now implies base.group_partner_manager so every office/
manager role can create contacts (Technicians excluded). Fixes the live
"create a company contact, it doesn't show" report (AccessError on save).
- New fp.rack.load + fp.rack.load.line models (multi-rack split at Racking,
Phase 1) with sequence, ACLs, equal-split math, and tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidated handoff added to the Partial Order Handling section: the bugs
that only live tablet testing surfaced (phantom stage cards, scan-button
icons/labels, dark-mode undefined --bs-* vars, from-step predecessor block,
seeded-stage auto-finish on drain, gating fall-forward) and the open items
(discoverability badges, Scrap/Rework standalone buttons, automated tests
not written, dark-mode chip polish). Docs only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A "Ready for X" gating step (fp.step.kind code='gating') maps to
area_kind='receiving' in the taxonomy. For a MID-recipe gate (e.g.
"Ready for processing" between Racking and Plating) that snapped the
job's card back to the far-left Receiving column when work advanced into
it — the job looked like it vanished from the board.
_compute_area_kind now detects gating via the stable kind code and
resolves a gating step's column to the NEXT non-gating step's area (so
"Ready for processing" shows in Plating), keeping cards flowing
left→right. Falls back to the last real stage for a trailing gate.
Non-gating steps unchanged. Helpers: _fp_is_gating_step / _fp_raw_area_kind
(no recursion) / _fp_resolve_area_kind.
area_kind is a stored compute — recomputed all 537 live steps on entech.
Verified: WO-30061 "Ready for processing" area receiving→plating, card now
renders in the Plating column.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_fp_try_autofinish_on_drain guarded on _fp_has_real_incoming() — the WRONG
direction. The first stage (e.g. Racking) is fed by the qty_at_step seed,
not an incoming move, so it never auto-finished when all its parts were
sent forward (operator sent everything out of Racking, step stayed
in_progress at qty 0). Now guards on a real OUTGOING move (parts left),
which covers the seeded first stage.
Still best-effort + gated: button_finish runs the required-input / sign-off
gates, so a step with an unrecorded required input (Racking's "Count the
Parts") won't auto-finish — it stays in_progress for a manual finish after
the input is recorded. Verified on entech.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Move dialog's predecessor check flagged every unfinished step before
the destination — including the from_step itself, which is in-progress by
definition when advancing partial parts out of it. So any "Send → next"
to a not-yet-started step showed a hard "Predecessor not done: <from_step>"
blocker and greyed out SEND (reproduced on WO-30061: Racking → Ready for
processing). This broke partial advance for ALL quantities, not just
1-part orders.
Fix: _blockers_for_move only blocks unfinished steps STRICTLY BETWEEN
from_step and to_step (you'd be skipping an incomplete intermediate
stage). Immediate-next advance is allowed; skip-ahead still blocked;
backward (rework) moves unblocked. Verified on entech: blocker no longer
fires for Racking → Ready for processing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause (verified against the live compiled bundle): Odoo's backend
CSS never DEFINES --bs-body-color / --bs-secondary-color / --bs-*-bg as
custom properties (0 definitions; they're only referenced). So every
color: var(--bs-body-color, #1d1d1f) — and the earlier --bs-secondary-color
swap — resolved to the dark hex fallback in BOTH light and dark mode.
That's why the prior swaps never worked. Backend dark mode here is runtime
[data-bs-theme=dark] + SCSS literals, not those vars.
Fix: the finish-block dialog text now INHERITS the modal's theme-correct
colour (same as the readable title + "Count the Parts" list items) — the
broken line was the only one setting an explicit var() colour. Tinted
banners use translucent rgba() instead of color-mix-with-undefined-var.
Verified in the served bundle: o_fp_finish_block_msg{font-weight:500;}
(no colour override).
CLAUDE.md dark-mode guidance corrected (it had wrongly recommended those
undefined vars).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "N required input(s) haven't been recorded yet" line still read as
dark/dim in dark mode after the --text-secondary→--bs-secondary-color
swap, because --bs-secondary-color is muted/low-opacity. That line is
primary instruction text, so use the full-contrast var(--bs-body-color)
instead (+ font-weight 500). Reserve --bs-secondary-color for genuinely
secondary text.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Operators saw dark-on-dark (invisible) text in the workspace + "Cannot
Finish Step" dialog in Odoo dark mode.
Root cause: var(--text-secondary, #xxx) — a made-up variable that doesn't
exist in Odoo, so it always fell back to the hardcoded dark hex (invisible
on dark). Used 33× across job_workspace.scss + 5 component stylesheets.
Replaced with the real dark-aware var(--bs-secondary-color).
Also fixed paired backgrounds that would hide the now-theme-flipped text:
- finish-block action note → var(--bs-tertiary-bg) (was #f3f4f6).
- Tinted status banners (finish-block step, overtime timer, receiving
status) → color-mix over var(--bs-body-bg) + var(--bs-body-color).
Odoo's bootstrap lacks the BS5.3 -bg-subtle/-text-emphasis vars
(verified against _root.scss), so color-mix is the dark-aware path.
Solid accent pills/dots (white text) and the color-coded plant-card chips
(light-bg + dark-text, readable in both) intentionally left as-is.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Matched, intentional look for the two scan controls:
- Scan QR (camera, primary sticker-scan) — accent-filled blue, fa-qrcode.
- Enter Code (manual / scanner-gun) — accent-tinted secondary, fa-keyboard-o.
Both now use Font Awesome icons (no emoji), inline-flex aligned icon+label.
Enter Code's class restructured so scan-alt persists alongside the active
state when the drawer is open.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The QrScanner component renders its own fa-qrcode icon; the board passed
it label '📷 Camera', so the camera button showed two icons (QR + camera
emoji). Drop the emoji → one icon.
Also clarify the two scan paths (they do different things):
- "Scan QR" = camera scan of the printed job sticker (primary path)
- "Enter Code" = manual / hardware scanner-gun text drawer (no camera)
Reordered so the camera (sticker) scan reads first. Other QrScanner call
sites already pass plain/no labels — this was the only double-icon.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Regression from the partial-order board: _job_presences emitted a card
for any area containing a `ready` step. These recipes seed ALL downstream
steps to `ready` at job creation, so a job showed in every future stage
at once (e.g. WO-30061 across racking/receiving/plating/inspection) even
though no parts had advanced there.
Fix: a stage shows ONLY where parts physically are (qty_at_step > 0,
which includes the first-active seed) OR where a step is in_progress/
paused. A merely ready/pending future step with no parts no longer shows.
Strict sequential progress falls out for free — the qty_at_step seed sits
on the lowest-sequence non-terminal step and advances as each completes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Operators can now see and advance a job's parts across multiple stages
at once (e.g. 10 Masking / 20 Plating / 20 Baking on one 50-part job).
Tracking model C (fluid per-stage quantities + existing hold/scrap/
rework records for exceptions); board option 2 (a card per occupied
stage); wait-to-reconverge close. Additive only — no new model, no
migration, no change to the close/cert/ship lifecycle.
Board (fusion_plating_shopfloor/controllers/plant_kanban.py):
- One card PER (job, stage), composite key "{job_id}:{area}". Unsplit
jobs render exactly as before. _job_presences/_render_presence;
primary presence keeps full job card_state, secondary presences
derive state from their focus step.
Card (plant_card.js/.xml/.scss):
- "20 of 50 here" badge; tap opens the workspace focused on that
stage's step (focus_step_id, already accepted by the workspace).
Move + light-up (move_controller.py, fusion_plating_jobs/fp_job_step.py):
- Availability/pre-fill now from qty_at_step (step had no qty_done/
qty_scrapped fields — the old read was always 0, dead path).
- Forward move auto-flips destination pending->ready (no auto-start;
labour timer stays explicit) and auto-finishes a drained source
(best-effort). Predecessor gate is qty-aware: a step with real
arrived parts is startable regardless of upstream completion
(_fp_has_real_incoming, single source of truth for can_start /
blocker / button_start / move blockers).
Operator advance (job_workspace.js):
- "Send -> <next>" action on in_progress/paused steps opens the slimmed
Move dialog (qty steppers, no keyboard; advanced fields collapsed).
Was only wired into the deprecated shopfloor_tablet before.
Close (fp_job.py):
- button_mark_done counts move-based scrap (_fp_scrapped_via_moves) into
qty_scrapped and derives qty_done = qty - scrapped (was blindly
= job.qty, over-counting). Reconciliation gate unchanged.
Static-validated: pyflakes (py), lxml parse (xml), node --check (js).
Dynamic tests + browser check need an installed env (entech/trial) —
plating modules can't install on the local Community DB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Design for parts fanning across shop-floor stages (e.g. 10 at Masking,
20 at Plating, 20 at Baking on one 50-part job):
- Tracking model C — fluid per-stage quantities via existing qty_at_step;
failed/held/rework subsets ride existing hold/scrap/rework records.
- Board Option 2 — a card per stage-presence (composite job:area keys);
unsplit jobs render identically to today.
- Easy-advance operator flow — one "Send to next" action, steppers /
rack-tap (no keyboard), intent-named Hold/Scrap/Rework buttons.
- Light-up plumbing — auto-ready on arrival, qty-aware predecessor gate,
auto-finish source on drain; no auto-start (labour accuracy).
- Close — wait to reconverge; close/cert/ship/invoice lifecycle unchanged.
Additive only: no new core model, no data migration, no change to the
quantity model, OWL component tree, or close lifecycle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Separate internal employees from the customer portal: suppress the
fusion_plating_portal sidebar for internal users, redirect them to the
clock page, and add a finalized-payslip view (inline paystub + optional
PDF) under /my/clock/payslips in fusion_clock.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Default-hide six order-line columns that aren't part of the plating
view (Product/product_template_id, Description Template, Specification,
Delivered Qty, Invoiced Qty, Taxes) by flipping them to optional="hide".
They stay available via the optional-columns toggle. Default-visible set
is now Customer-Facing, Part, Process/Recipe, Thickness, Serial, Job #,
Effective Deadline, Qty, Unit, Unit Price, Amount — for both quotations
and sales orders.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove the unused Customer Specification field and the redundant
Delivery Date (Lead Time covers it) from the customer-facing SO
confirmation and invoice PDFs (portrait + landscape). SO info row goes
5->4 columns (Delivery Date gone); the Customer Job # / Spec / Delivery
Method row goes 3->2 (Spec gone). Internal docs (traveller, sticker) and
the CoC process "Specification(s)" section are left untouched.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tooling/additional charge lines (any product line with no part catalog)
no longer print in the parts table — they render in the totals block
under the subtotal with their entered label + amount. Subtotal is now
parts-only; tax + grand total are unchanged (the charge is still a real
taxed line in the data). Applies to SO confirmation and invoice, both
portrait and landscape. Also aligns the invoice S/N cell to the SO's
multi-serial rendering.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a cohesive, restrained colour layer using the existing express
tokens (light/dark aware): faint plum gradient washes on the PO card,
legend bar, table header, and Order Summary header; filled accent
gradient pills (EXPRESS / CAD); accent rules on the section title,
summary header, and Grand Total footer. Adds an $xpr-accent-tint token
plus four composed gradient tokens.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The right-aligned value column squeezed the Additional Charge and Tax
dropdowns to a sliver. Move each picker into the (wider) label column,
stacked under its label at full width, so every value cell is now a
single amount that lines up cleanly in the right column under the
vertical divider.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Switch the summary rows from a flex space-between layout to a fixed
two-column grid (label | value) so a vertical divider on the label
cell's right edge lines up across every row. Values are right-aligned
into a clean amount column; the Grand Total footer keeps the divider at
the heavier rule weight.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Restyle the Order Summary card into a clean bordered table — a tinted
"Order Summary" caption bar, a divider line under every row, and an
accent-tinted Grand Total footer with a strong top rule. Uses the
existing light/dark express tokens so it renders correctly in both
colour schemes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Express order entry now has a single "Lot Order" toggle on the header
instead of a per-line "Lot" checkbox. When on, every line shows Lot
Total and prices as a flat lot (unit price derived = lot total / qty,
qty preserved for production); when off, the Lot Total column is hidden
and lines price per unit as usual. Keeps the order summary clean for the
common per-unit case.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>