res.partner has no `mobile` field in this Odoo 19 build, so
_fp_resolve_delivery_defaults crashed with AttributeError when a job's
last shop step finished and auto-created a delivery
(button_finish -> _fp_check_advance_post_shop -> _fp_create_delivery).
This blocked operators from finishing the step at all.
Guard the read with the codebase's 'x' in obj._fields pattern so it
falls back to phone, and still picks up mobile on instances that define
it. Deployed + verified on entech (restart, no -u; pure Python change).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Internal Job Sticker is no longer a separate Layout A: it's now a COPY of the
External sticker (Layout B, one per box, logo + WO + BOX + QR + rail fields +
prominent PLATING THICKNESS banner) but feeds the INTERNAL description and
labels its notes "INTERNAL NOTES" so the shop copy can't be confused with the
customer copy. The old Layout-A body template is deleted.
- Removed the Internal sticker from the fp.job Print menu (binding_model_id ->
False); it now prints from the Receiving screen instead.
- Added "External Sticker" + "Internal Sticker" print buttons to the fp.receiving
form header (shown once a WO exists). Each renders one label per tracked box
for the receiving's work order (passes a single WO so the SO-scoped box loop
doesn't reprint each label per job).
Verified on entech (WO-30094 / RCV-30096): internal renders Layout B with the
internal description + INTERNAL NOTES; external unchanged; both receiving buttons
return the right report actions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Caveat on the existing "custom-header reports need .article for UTF-8" rule:
the dpi=96 mm-based job stickers are the exception — adding .article re-routes
through Odoo's standard report CSS and blows up the mm/dpi layout. For those,
strip the non-ASCII glyph to ASCII in _clean() instead. (Learned fixing the
'375ºF' bake-text mojibake on the external sticker.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Relocate plating thickness out of the cramped rail (where it shared a row
with Due and wrapped) to a big 21pt "PLATING THICKNESS" banner at the top of
the main panel — the team's most-watched spec, now hard to miss. Gated on a
new has_thk flag (real value with a digit; skips empty/'N/A'). Due takes the
full rail row.
- Fix the bake-text degree mojibake: operators type 'º' (U+00BA) for "375ºF";
through this sticker's lightweight html_container path (no .article UTF-8
wrapper) it renders "375°F". Adding a .article wrapper fixes encoding but
blows up the dpi=96 mm layout (tested), so _clean() now strips º/°/˚ to clean
ASCII -> "375F".
Verified on entech (WO-30094).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The left rail (overflow:hidden, fixed height) was ~8mm over its budget, so the
last grid row (Due | Thk) fell off the bottom and rendered as an empty band.
Reclaimed the height: r-logo 11→9mm, r-wo 14→13mm, r-qrflags 36→32mm (+ qfwrap-full
33→31mm / qffull line-height 36→32mm to match), r-fld padding 1→0.7mm. Due/Thk
now render fully. Verified on entech (WO-30094, PO 980933709).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drop the <br/> between label and value (.lbl is display:block, so the
value already sits beneath it; the <br/> added a blank-line gap).
- Rebalance widths: PO# 34→25%, Qty 16→13%, Due 30→26%, Thk 20→36% + nowrap,
so a thickness RANGE (e.g. 0.0025" - 0.0030") stays on ONE line instead of
wrapping. Verified on entech (WO-30096).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Internal Job Sticker (Layout A) redesign per operator feedback:
- Factory (ENTECH) logo added to the black header band on a white chip
(mirrors the external sticker; visible on the dark band + thermal print).
- Customer moved out of row 3 up next to Part# in row 2. Part# keeps the
dominant width (stripped customer name is short + consistent), MASK/BAKE
flags still float at the Part# edge.
- Row 3 now PO# / Qty / Due / Thk (4 fields, customer removed) with bigger
values (13/14/12/11pt) spread across the full width.
- Internal header QR trimmed 30→27mm so the QR-driven band is shorter; the
freed height flows to the NOTES block.
Rendered + verified on entech (WO-30072 / AMP-CANA).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The recipe-cert-toggles feature (fb6cccc8) taught
fp.job._resolve_required_cert_types to suppress thickness for recipes
with requires_thickness_report=False (passivation, chemical conversion,
anodize seal-only). But the actual thickness-data ENFORCEMENT never got
the memo: both fp.certificate.action_issue's hard gate AND the
Issue-Certs wizard's readiness hint re-derived 'needs thickness' from
partner flags only and ignored the recipe. Result: a passivation CoC for
a thickness/strict customer could never be issued — the gate demanded
Fischerscope data the process physically cannot produce.
Consolidate the partner-flag + recipe-suppression logic into one
fp.certificate._fp_needs_thickness_data() helper and route both the gate
and the wizard through it, so the cert-type resolver and the issue-time
gate can never drift again. Add regression tests: passivation recipe
suppresses the issue gate even for strict-thickness customers; a normal
recipe still enforces (control, guards aerospace).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Clone-verify fixes: the HTTP request runs as base.user_admin, so set/read
x_fc_signature_image on that user (not self.env.user / uid 1); give the step a
recipe_node_id so button_finish passes the S21 no-recipe-link gate (also fixes
the pre-existing test_sign_off_finishes_step). 5/5 pass on an entech clone.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
onFinishStep: if the user has a saved Plating Signature, show FpSignatureConfirm
(one-tap, preview); otherwise open the draw-pad. Factored _openSignaturePad +
_commitSignOff (sends null data URI when using the saved signature).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Confirm-with-preview dialog (saved-signature preview + Sign & Finish + Use a
different signature). Registered after the signature_pad assets; version bump.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
/fp/workspace/sign_off: signature_data_uri now optional; a supplied drawing
persists to res.users.x_fc_signature_image (SELF_WRITEABLE) and the wasted
per-step ir.attachment is dropped; no drawing + a saved signature just finishes.
/fp/workspace/load exposes user_has_plating_signature + user_plating_signature.
Merged 3 new tests into the existing TestWorkspaceSignOff.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>