Move 'All Jobs' and 'Steps' under fusion_plating_shopfloor's
existing 'Shop Floor' submenu (between Tablet Station and Bake
Windows). The standalone 'Jobs' parent menu is gone — it was
redundant once the operator UIs (Plant Overview / Tablet /
Manager Desk) consolidated under Shop Floor.
Final structure under Plating:
Sales / *
Configurator / *
Shop Floor
Manager Desk
Plant Overview
Tablet Station
All Jobs ← moved here (seq 15)
Steps ← moved here (seq 17, supervisor+)
Bake Windows
First-Piece Gates
Receiving & Inspection
Operations / *
Configuration → Work Centres (manager)
...rest
Work Centres stays under Configuration (no shopfloor equivalent).
New menu file lives in fusion_plating_jobs/views (jobs depends on
shopfloor; core can't reference shopfloor xmlids).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the parallel OWL/controller stack I built in
fusion_plating_jobs (job_process_tree, job_plant_overview,
job_manager_dashboard, job_tablet, job_*.scss, plus parallel
controllers and action XML files). Refactors the existing
fusion_plating_shopfloor components in place to bind to
fp.job / fp.job.step instead of mrp.production / mrp.workorder.
End state:
- ONE operator UI module (shopfloor) instead of two parallel ones
- Existing token system (_fp_shopfloor_tokens.scss) reused as
designed - no duplicate jobs tokens
- Existing /fp/shopfloor/* RPC URLs preserved (no integration
breakage); workorder_id kwargs accepted as legacy aliases for
step_id / job_id so older tablet clients keep working
- Existing visual designs preserved - only the data layer
underneath changed
- Process Tree button on fp.job form now points at
fusion_plating_shopfloor's fp_process_tree client action
- Bake Windows / First-Piece Gates / Bake Oven / Operator Queue
models stay where they were
- legacy_menu_hide.xml trimmed: only the bridge_mrp Production
Priorities entry remains; the 3 shopfloor menus (Manager Desk,
Plant Overview, Tablet Station) are now visible (the canonical
native consoles)
Manifests:
- fusion_plating_jobs 19.0.3.1.0 -> 19.0.4.0.0 (consolidation bump,
no more bundled JS/SCSS, only job_scan controller retained)
- fusion_plating_shopfloor 19.0.14.4.0 -> 19.0.15.0.0 (asset bundle
cache-bust + significant controller refactor)
Tests pass on entech: 0 failed, 0 errors of 41 tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The (Native) suffix was a temporary distinguisher during parallel
coexistence with bridge_mrp. Now that fp.job is THE primary
system, the suffix is just noise. Removed across all 5 menus and
both client-action labels.
Also restructured the Jobs submenu under Plating:
- Renamed root from 'Plating Jobs (Native)' (seq=4, manager-only)
to just 'Jobs' (seq=2, operator-visible). Now appears right
below the Plating app header — first-click access for operators.
- All Jobs (was 'Jobs') at seq=20
- Tablet Station at seq=5 (operator entry point)
- Plant Overview at seq=10
- Manager Dashboard at seq=15 (supervisor+ only)
- Steps (renamed from 'Steps (Admin)') at seq=30 (supervisor+ only)
- Work Centres (was 'Work Centres (Native)') in Configuration
Hidden one more legacy menu: bridge_mrp's 'Production Priorities'
(mrp.workorder ordering UI — fp.job has its own priority field).
Manifest unchanged (no new files); skipping version bump.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two follow-up seed scripts after the main demo reset:
1. seed_work_centres.py: creates one fp.work.centre per existing
fusion.plating.work.center (matched by code), classifying kind
from name/code keywords. Then backfills work_centre_id on every
fp.job.step whose recipe_node has a legacy work_center_id with
a matching native code. Plant Overview kanban now has columns
instead of one big 'Unassigned' bucket.
2. seed_part_coatings.py: assigns existing fp.coating.config rows
(with recipes) to up to 20 bare fp.part.catalog rows
round-robin. Field on the part is x_fc_default_coating_config_id.
Future SO confirms via these parts will naturally generate full
recipe-linked steps via _generate_steps_from_recipe.
Both idempotent — re-running creates nothing new.
Run on entech: 9 native work centres created, all 234 existing
fp.job.step rows bound. Parts with coating: 2 -> 22 (28 -> 8 bare).
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cleanup_demo_data.py: deletes ALL fp.job, fp.job.step, timelogs,
mrp.production, mrp.workorder, and dependent records (deliveries,
certs, holds, portal jobs, racking inspections, uninvoiced SOs).
Resets the fp.job sequence. Preserves masters. Force-cancels MOs/SOs
via SQL UPDATE before unlink to bypass Odoo's _unlink_except_done
and _unlink_except_draft_or_cancel guards.
seed_demo_data.py: creates 31 fp.job rows distributed across all
6 states (draft=5, confirmed=6, in_progress=8, on_hold=3, done=6,
cancelled=3). In_progress jobs have mixed step states with real
timelogs to simulate a live shop floor. Falls back to direct
fp.job creation when a customer's parts have no coating/recipe,
ensuring customer variety even with sparse coating data.
Both scripts: idempotent (safe to re-run), commit at end, walk
dependents bottom-up to avoid FK violations. Used to reset entech
demo data after the migration trial.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 4 client-action SCSS files I shipped in Phase 6 ignored the
project's documented design system (CLAUDE.md "Card Styling" + "Dark
Mode" rules) and used hardcoded hex / var(--bs-*) for surfaces.
Result: dark mode rendered white-text-on-white-card.
Companion to "changes" (22573e7) which already landed
_fp_jobs_tokens.scss + the job_plant_overview.scss refactor.
This commit finishes the job:
- Refactored job_process_tree.scss, job_manager_dashboard.scss and
job_tablet.scss to reference the $fp-* tokens — zero hardcoded
hex on theme-sensitive surfaces. Three-layer contrast applied per
CLAUDE.md (page → container → card).
- Process tree keeps the intentional Steelhead-style dark-slate
card fill in BOTH bundles (deliberate visual choice, not a theme
bug); page / header / connectors / empty state are now token-
driven so they look right against light or dark page surfaces.
- Manifest assets list reordered so _fp_jobs_tokens.scss compiles
first in web.assets_backend (CLAUDE.md rule: SCSS variables in
earlier files are visible to later files in the same bundle).
This is what makes the compile-time
$o-webclient-color-scheme branch in the partial actually take
effect for the four consumer files.
Verified on entech: light bundle (web.assets_backend) and dark
bundle (web.assets_web_dark) both compile without SCSS errors and
emit distinct surface hexes (light: #f3f4f6 page / #ffffff card;
dark: #1a1d21 page / #22262d card).
Manifest 19.0.3.0.0 → 19.0.3.1.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three batched changes that close out the original 10-phase
migration plan.
1. Phase 5 — Job Margin report bound to fp.job (replaces the
mrp.production-bound report_wo_margin). Per-step labour cost
table + margin summary using existing fp.job.step.cost_total
from Phase 1.
2. Polish:
- Real implementations for fp.job.step.button_pause,
button_skip, button_cancel (was NotImplementedError stubs).
button_pause closes the open timelog and sums duration_actual,
mirroring button_finish; button_skip/cancel transition state
with UserError guards.
- Explicit ondelete= policies on fp.job's cross-module Many2ones
(part_catalog/coating restrict, customer_spec/portal/delivery
set null) — was implicit set null.
- Standard Nexa Systems author/website/maintainer/support block
on fusion_plating_jobs manifest, suppressing the install
warning.
3. Legacy hide:
- New 'Plating Legacy Menus' group (group_fusion_plating_legacy_menus)
— nobody in it by default.
- Old shopfloor Manager Desk + Plant Overview + Tablet Station
menus restricted to that group, hiding them from operators
now that the native equivalents under 'Plating Jobs (Native)'
exist. (Note: ir.ui.menu uses group_ids in Odoo 19, not the
deprecated groups_id alias.)
Manifest 19.0.2.4.0 → 19.0.3.0.0. fusion_plating_shopfloor added
to depends so the legacy menu xmlid references resolve at install
time.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Operator-facing touchscreen UI. Three modes:
- job_picker: list of active jobs as big touch cards
- job_detail: job header + steps list, click a step to view detail
- step_detail: big Start/Finish buttons depending on state
Backend: 4 JSON-RPC endpoints under /fp/jobs/tablet/* for jobs
list, job detail, start step, finish step. Calls through to
fp.job.step.button_start / button_finish so all the audit
preservation, timelog creation, duration_actual roll-up logic
from Phase 1 still applies.
Menu entry 'Tablet Station (Native)' at sequence 3 (top) of the
Plating Jobs (Native) submenu inside the existing Plating app.
Manifest 19.0.2.3.0 → 19.0.2.4.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two operator-facing client actions for the native job model.
Plant Overview: kanban with columns = fp.work.centre, cards = active
fp.job.step rows (ready/in_progress/paused). Drag a card to a different
column to reassign the step's work_centre_id; click to open the step
form. Backend: /fp/jobs/plant_overview returns columns with cards;
/fp/jobs/plant_overview/move_card reassigns work_centre.
Manager Dashboard: list of in-flight fp.job rows with progress bars,
deadline (overdue highlight), current_step / current_location, and a
priority side-bar (rush=red, high=orange, normal=blue, low=grey). Click
a row to open the job form. State-count pills filter by state. Backend:
/fp/jobs/manager_dashboard returns rows + state counts.
Both menu entries land inside the existing 'Plating Jobs (Native)'
submenu under the Plating app (manager-only). The menu items are
defined in this module rather than in fusion_plating core, because
the action xmlids they reference aren't loaded yet at the time the
core menu file is parsed (fusion_plating_jobs depends on core, not
the other way round).
Manifest 19.0.2.2.0 → 19.0.2.3.0. Three new SCSS, three new JS,
three new XML files registered in web.assets_backend.
Verified on entech: module loaded clean, all 41 fusion_plating_jobs
tests pass, asset bundle regenerates without errors, both menus and
both client actions registered in ir_ui_menu / ir_act_client.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports fusion_plating_shopfloor's process_tree.js to bind to fp.job
instead of mrp.production. Consumes the /fp/jobs/process_tree
JSON endpoint built in Phase 6 lean.
Renders the recipe tree as cards. Each operation card shows the
step state (pending/ready/in_progress/done/etc.) when there's a
matching fp.job.step. Click an operation card -> open the step
form. Click Back -> return to the job form.
New 'Process Tree' button on the fp.job form (manager-only)
launches the client action with job_id context.
Manifest 19.0.2.1.0 -> 19.0.2.2.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three changes to support live cutover on entech (2026-04-25):
1. Bridge_mrp gate: sale.order.action_confirm in
fusion_plating_bridge_mrp now skips _fp_auto_create_mo when the
x_fc_use_native_jobs config flag is True. Without this, every SO
confirm would create both an mrp.production AND an fp.job
(duplicate work). The gate is the only modification to bridge_mrp
during the migration — the rest stays untouched.
2. Menu nesting: Plating Jobs (Native) now lives INSIDE the existing
Plating app (parent=menu_fp_root) instead of as a separate
top-level app. Two parallel 'Plating' apps was confusing UX. Work
Centres (Native) goes under the existing Configuration sub-menu.
'(Native)' suffix is temporary — drops at full legacy removal.
3. Migration script robustness: per-MO savepoints (so one bad MO
doesn't abort the whole transaction with cascading 'transaction
aborted' errors) + extended partner resolver fallback chain
(warehouse partner → company partner) for orphan demo MOs without
SO link or x_fc_customer_id. Verified: 43 MOs + 297 WOs migrated
on entech with 0 errors after these fixes.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comprehensive summary of work performed Apr 25 evening through
Apr 26 morning while user was asleep:
- Status of each phase (Phase 1+2 tested on entech, Phase 3-7
committed locally + pushed but untested due to mid-session
Tailscale SSH lockout)
- All commit hashes
- Architecture decisions made autonomously (parallel coexistence,
Phase 6 lean, qc_check_id deferred, migration context flag)
- Files created vs untouched per constraints
- Recommended morning checklist for resuming work
- Honest assessment of what's solid vs unverified
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migration script now sets context fp_jobs_migration=True before
creating fp.job records. action_confirm and button_mark_done check
this flag and skip side-effects (portal job creation, QC check,
racking inspection, delivery, certificate, notification dispatch)
when migrating.
Without this, the migration would double-create portal jobs / QC
checks / racking inspections — once via bridge_mrp's original
create on the source MO, once via jobs module's lifecycle hook on
the new fp.job mirror. With the gate, the migration script
explicitly rebinds the existing dependents via x_fc_job_id.
Manifest 19.0.2.0.0 → 19.0.2.1.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds legacy_mrp_production_id (Integer index) on fp.job and
legacy_mrp_workorder_id on fp.job.step. Used as the idempotency
key during cutover migration.
Three scripts under fusion_plating_jobs/scripts/:
- audit_pre_migration.py — counts and data-quality concerns BEFORE
- migrate_to_fp_jobs.py — copies MO->fp.job, WO->fp.job.step, time
logs, rebinds cross-refs (batches,
holds, certs, readings, portals,
inspections, deliveries). Idempotent.
- audit_post_migration.py — counts and verifies AFTER
Migration is run manually from \`odoo shell\` at cutover (not as
auto post-migration hook, for safety). README explains usage.
Tests verify the legacy id fields exist and the migration script
files are well-formed Python.
Manifest 19.0.1.9.0 -> 19.0.2.0.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 6 originally scoped the full operator UI rewrite (Plant
Overview, Tablet, Manager Dashboard, Process Tree). Tailscale SSH
to entech is currently unavailable, so live in-browser
verification of OWL/JS components isn't possible. Shipping a lean
Phase 6 with the data-layer pieces:
1. /fp/job/<id> scan controller — when a user scans a fp.job
sticker, lands them on the fp.job form (or the process tree
action once that's wired). Mirrors fusion_plating_reports' /fp/wo/
pattern.
2. /fp/jobs/process_tree JSON endpoint — returns the recipe tree
serialized with each node tagged by its fp.job.step state,
ready for an OWL component to render. The component itself is
deferred (see README.md).
The bigger UI deferrals (kanban, tablet, manager dashboard) are
documented in README.md. They get their own focused project after
cutover — the data layer is complete, so they can land
incrementally without touching fp.job/fp.job.step.
Tests verify controller imports + serialization shape (no HTTP
because TransactionCase doesn't easily simulate request context).
Manifest 19.0.1.8.0 → 19.0.1.9.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two parallel report definitions for the native job model:
1. Job Sticker (6x4 inch custom paperformat) bound to fp.job. Prints
WH/JOB/... ID, customer, SO, qty, due date, recipe, step
progress. QR encodes /fp/job/<id> for scan-to-job navigation.
2. Job Traveller bound to fp.job, A4 portrait. Job header + all
fp.job.step rows in sequence order with operator sign-off
column.
Coexists with fusion_plating_reports' MO/WO bindings — both print
menus stay live during migration.
Deferred reports (use existing during migration; rebind at cutover):
- BoL, Packing Slip, Invoice (read from SO, no fp.job change needed)
- WO Margin (cost rollup; rebuild against fp.job.step.cost_total
in phase-end polish)
Adds fusion_plating_reports to fusion_plating_jobs depends.
Tests deferred to post-Tailscale-restore: 3 new tests verify
report actions are registered + sticker template renders without
QWeb errors. Module file content verified locally as
well-formed XML.
Manifest 19.0.1.7.0 → 19.0.1.8.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Adds 'job_confirmed' and 'job_complete' trigger events to
fp.notification.template (legacy 'mo_confirmed' / 'mo_complete'
stay for bridge_mrp).
- fp.job.action_confirm and button_mark_done now fire those
notifications best-effort via fp.notification.template._dispatch
(silent skip if templates absent or notifications module missing).
- Adds x_fc_source ['mrp', 'jobs'] tag to fusion.plating.kpi.value
so Phase 9 dashboards can filter or display both sources.
- Verified aerospace/nuclear/cgp/safety modules don't directly
reference mrp.production or mrp.workorder.
Configurator integration was already covered by Task 2.5's SO
confirm hook (reads x_fc_part_catalog_id and x_fc_coating_config_id
from sale.order.line).
Manifest 19.0.1.6.0 -> 19.0.1.7.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds x_fc_job_id / x_fc_step_id Many2ones via _inherit on:
- fusion.plating.batch (workorder_id stays for legacy MRP-bound batches)
- fusion.plating.quality.hold
- fp.certificate
- fp.thickness.reading
- fusion.plating.delivery (parallel to existing job_ref Char)
- fp.racking.inspection (parallel to existing production_id)
fp.job.action_confirm now also calls a best-effort racking-inspection
auto-create. The current fp.racking.inspection still has a required
production_id, so the helper skips cleanly when this job has no MO link
(pure-native mode). Phase 9 cutover flips the required FK to fp.job.
Strategy: parallel coexistence — bridge_mrp's existing fields stay
populated; this adds NEW fields populated by the native flow. Phase 9
cutover stops populating the old fields.
Adds fusion_plating_batch + fusion_plating_receiving to jobs module
depends.
Note: spec referenced fp.batch / fp.quality.hold; the actual models
in this codebase are fusion.plating.batch / fusion.plating.quality.hold
— used the real model names.
Manifest 19.0.1.5.0 → 19.0.1.6.0. 29 jobs tests pass.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Task 2.6: fp.job.action_confirm auto-creates fusion.plating.portal.job
with x_fc_job_id back-reference. Idempotent (skip if already linked).
- Task 2.7: fp.job.action_confirm checks customer.x_fc_requires_qc
and best-effort creates a fusion.plating.quality.check (the model
lives in bridge_mrp; runtime-detected to avoid dep cycle).
- Task 2.8: fp.job.button_mark_done sets state='done', date_finished,
auto-creates draft fusion.plating.delivery and best-effort triggers
fp.certificate generation.
- Task 2.9: account.move.action_post links invoice -> fp.job via SO
origin lookup, updates portal_job state to complete and stamps
invoice_ref.
5 new tests cover: portal job creation + idempotency, mark_done state
+ delivery, cancel-then-mark-done blocked.
Best-effort patterns (try/except + runtime model detection) used for
QC + cert because their target models are in dependent modules
that this module doesn't depend on by design.
qc_check_id field on fp.job still deferred — adding it here would
require depending on bridge_mrp.
Manifest 19.0.1.4.0 -> 19.0.1.5.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Settings flag controls which SO confirm path runs. Default False
keeps the legacy bridge_mrp / mrp.production flow on entech.
Setting True diverts confirm into fp.job creation.
Both hooks coexist — bridge_mrp's _fp_auto_create_mo and the new
_fp_auto_create_job — but only one creates records per SO confirm
(controlled by the flag).
The new _fp_auto_create_job mirrors bridge_mrp's grouping logic
(x_fc_wo_group_tag), recipe resolution (coating → part), and
traceability fields (origin, sale_order_line_ids).
Settings UI shows the flag in a 'Fusion Plating Jobs' app section
of the standard Configuration menu.
3 new tests cover: flag off no-op, flag on creates job, idempotency.
Manifest 19.0.1.3.0 → 19.0.1.4.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Native port of fusion_plating_bridge_mrp's
_generate_workorders_from_recipe method. Walks the recipe tree,
creates one fp.job.step per 'operation' node, formats child 'step'
nodes as step instructions on chatter, respects opt-in/out
overrides from fp.job.node.override.
Adaptations from the original:
- Creates fp.job.step (not mrp.workorder)
- Maps fusion.plating.work.center to fp.work.centre via forward
link (x_fc_fp_work_centre_id) or code fallback
- Uses native field names (job_id, work_centre_id, etc.)
- Drops work_role_id (not on fp.job.step yet — Task 2.6+)
- Drops _fp_autofill_default_equipment (not yet on step)
5 new tests cover: basic generation, idempotency, no-recipe skip,
opt-in override behaviour, recipe_node_id link.
Manifest 19.0.1.2.0 → 19.0.1.3.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror of fusion.plating.job.node.override from bridge_mrp, but
bound to fp.job. bridge_mrp's version stays alive for legacy MO
flow during the migration. Both coexist.
Adds override_ids One2many to fp.job via _inherit, plus
unique(job_id, node_id) constraint.
Note: spec-suggested _sql_constraints syntax is deprecated in
Odoo 19 ("Model attribute '_sql_constraints' is no longer
supported, please define model.Constraint on the model"). Used
the new class-attribute form: _unique_job_node = models.Constraint(...).
Verified the UNIQUE index is created on the table.
3 new tests: create, uniqueness, one2many backref.
Manifest 19.0.1.1.1 -> 19.0.1.2.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code review noted this was asymmetric — only part_catalog_id had
explicit index=True among the 5 new fields, and Phase 1 core fp.job
relies on Odoo's implicit FK btree for ALL Many2ones (no explicit
indexes). Removed for consistency.
Important I2 (no explicit ondelete= policies) is deferred to a
phase-end polish task that addresses both Phase 1 core and Phase 2
extension fields uniformly.
Manifest 19.0.1.1.0 → 19.0.1.1.1.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 of the 6 deferred fields from Phase 1 Task 1.4 land here in
fusion_plating_jobs:
- part_catalog_id (fp.part.catalog from configurator)
- coating_config_id (fp.coating.config from configurator)
- customer_spec_id (fusion.plating.customer.spec from quality)
- portal_job_id (fusion.plating.portal.job from portal)
- delivery_id (fusion.plating.delivery from logistics)
qc_check_id deferred to Task 2.7 — its target model
(fusion.plating.quality.check) still lives in
fusion_plating_bridge_mrp and we don't depend on bridge_mrp from
this module. Task 2.7 will address QC sourcing.
6 unit tests (5 field-presence + 1 integration creating linked
records).
Manifest 19.0.1.0.0 → 19.0.1.1.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Empty module that will host the native job bridge during Phase 2
of the migration. Coexists with fusion_plating_bridge_mrp during
the migration period — both can be installed simultaneously
without conflict.
Depends on:
- fusion_plating (fp.job, fp.job.step, fp.work.centre from Phase 1)
- fusion_plating_configurator (fp.part.catalog, fp.coating.config)
- fusion_plating_portal (fusion.plating.portal.job)
- fusion_plating_logistics (fusion.plating.delivery)
- fusion_plating_quality (fusion.plating.customer.spec)
- fusion_plating_certificates (fp.certificate)
These deps are why these fields couldn't live in fusion_plating
core (would invert the dep graph). All cross-module fields on
fp.job and fp.job.step land here via _inherit in subsequent tasks.
auto_install=False — opt-in only.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2 was previously outlined as 'rename bridge_mrp → jobs'.
That's destructive on entech. Revised strategy: build
fusion_plating_jobs IN PARALLEL with bridge_mrp. A settings flag
(x_fc_use_native_jobs) controls which path SO confirm takes.
Default False = legacy MO flow stays. Cutover (Phase 9) flips
the flag.
Phase 2 breakdown into 11 tasks (2.1–2.11), totaling ~5 days
engineering. All preserve bridge_mrp untouched until cutover.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- I1: Lock time_log_ids list inside step Audit tab to read-only
(no create/edit/delete on the nested list). Audit timelog rows
are produced exclusively by button_start / button_finish; if a
manager could hand-edit them, cost_total rollups would silently
drift.
- I2: Add explicit list view (decoration on state) and search view
(filters by state/kind, group_by state/work_centre/job) for
fp.job.step. The Steps (Admin) menu was using Odoo's default
auto-list with no filter, which would be unusable after a few
weeks of step accumulation. Action now references the search
view explicitly.
Manifest 19.0.8.7.0 -> 19.0.8.7.1.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Manager-only views during Phase 1 — operator UI rebuilt in Phase 6.
Top-level menu 'Plating Jobs (new)' (seq=47) groups the three
new act_window actions (Jobs, Steps Admin, Work Centres) so the
foundational models can be exercised through the UI without
touching the operator-facing menus that still serve mrp.production
and mrp.workorder.
Job form has Steps/Source/Costs notebook tabs. Step form has
Equipment/Plating Spec/Audit/Instructions tabs (Audit shows
the time log rows from Task 1.7). Search filters by state,
priority, partner, facility.
Manifest 19.0.8.6.1 → 19.0.8.7.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- I1: Add supervisor ACL row on fp.job.step.timelog. The other
job-related models all have a 3-tier (operator/supervisor/
manager) pattern; timelog had been operator+manager only with
no comment explaining why. Adding the supervisor row for
consistency — supervisors can write timelogs the same as
operators.
- I2: Capture fields.Datetime.now() once in button_start and
reuse for both the step's first-start audit timestamp and the
new timelog's date_started. Mirrors button_finish's pattern.
Eliminates microsecond drift between two related timestamps.
- M6: Update stale comment in fp_job_step.py that said
'duration_actual will be sum of timelog rows once Task 1.7
lands' — Task 1.7 has landed; replace with current behaviour.
Manifest 19.0.8.6.0 → 19.0.8.6.1.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each button_start opens a fresh timelog row; button_finish closes
the open row and recomputes step.duration_actual as the sum of all
interval durations. Replicates Odoo MRP's mrp.workorder.time_ids
granularity natively (no mrp dep).
Schema: step_id (M2O cascade), user_id, date_started,
date_finished, duration_minutes (computed, stored).
ACLs: operator get create permission on timelogs because
button_start creates them.
Tests: test_start_creates_timelog (asserts the log row exists,
date_finished is False, user_id is the current user) and
test_finish_closes_timelog (asserts log gets date_finished, has a
non-negative duration, and step.duration_actual matches).
Manifest 19.0.8.5.1 -> 19.0.8.6.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- I1: Replace 'Task 1.6' markers in stub method comments and
NotImplementedError messages with forward-looking phrasing.
Task 1.6 is what just shipped (the field expansion); the action
stubs are deferred to an unspecified future task. Stale markers
would have confused future readers/operators.
- I2: Add test_cost_total_recomputes_when_rate_changes — insurance
test that verifies @api.depends('cost_per_hour') triggers through
the related-from-work_centre chain. Catches future Odoo upgrades
that break related-depends.
Manifest 19.0.8.5.0 → 19.0.8.5.1.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Equipment: bath_id, tank_id, rack_id (all in core). oven_id deferred
to a bridge module — fusion.plating.bake.oven lives in shopfloor and
core can't depend on it. masking_material_id deferred too — model
fusion.plating.masking.material does not yet exist anywhere; will be
added when the masking model lands.
Audit: signoff_user_id (readonly), facility_id (related from
work_centre_id, stored).
Plating spec: thickness_target, thickness_uom (um/mil/in),
dwell_time_minutes, bake_setpoint_temp, bake_actual_duration,
bake_chart_recorder_ref (Nadcap audit trail).
Recipe-related: requires_signoff, auto_complete, is_manual,
customer_visible (all related from recipe_node_id, stored, so
operator sees current values without re-querying process.node).
Cost rollup: cost_per_hour related from work_centre_id, cost_total
computed (duration_actual / 60 x rate), currency_id related too.
Full rollup-from-timelogs lands in Task 1.7.
Tests cover: facility_id related-field, thickness_uom default,
cost_total zero/non-zero paths.
Manifest 19.0.8.4.1 -> 19.0.8.5.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- I2: Add TODO comment block + stub button_pause/button_skip/
button_cancel that raise NotImplementedError. Makes the missing
state-machine paths explicit instead of invisible gaps. Future
Task 1.6 wires the real implementations; shop-floor buttons in
Task 1.8 can already point to the right method names.
- I3: button_finish now preserves first-finish audit timestamp
via 'if not step.date_finished:' guard, mirroring button_start.
Future rework flow that re-opens a step won't lose the original
finish data. The duration_actual rollup landing in Task 1.7 will
use timelog rows for the most-recent interval if needed.
- I4: step_count and step_done_count are now store=True so list
views and stat buttons (Task 1.8) don't recompute across all
job rows on every render. step_progress_pct and current_step_id
stay non-stored - they're cheap derivatives. Split compute methods
so stored + non-stored fields don't share one method (Odoo flags
the mix as inconsistent and recomputes stored fields whenever the
non-stored one is read, defeating the perf gain).
Manifest 19.0.8.4.0 -> 19.0.8.4.1.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-operation model replacing mrp.workorder for plating. Each step
instantiates from a recipe operation node (recipe_node_id link).
Container/step nodes from the recipe template are rendered at view
time via that link — they don't get rows here. See spec §5.2
Option A.
7-state machine: pending → ready → in_progress → done, plus
paused, skipped, cancelled. button_start/button_finish enforce
the transitions.
Job header gets step_ids + step_count, step_done_count,
step_progress_pct, current_step_id (computed from steps).
Equipment, audit fields, plating-spec fields, time logs, and
release-ready validation come in Tasks 1.6 and 1.7.
Manifest 19.0.8.3.1 → 19.0.8.4.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Originally Task 1.4 was to add all spec §5.1 extension fields to
fp.job in core. The dependency-graph audit during implementation
revealed that 6 of those fields point to models in dependent
modules (configurator, quality, portal, logistics, bridge_mrp).
Adding them in core would invert the dependency graph.
Spec §5.1 now has a Module column. Core-safe fields stay in
fusion_plating/models/fp_job.py; cross-module fields are deferred
to their owning modules via _inherit = 'fp.job' in Phase 2.
Plan Task 1.4 narrative updated to reflect the reduced scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Move NOTE comment from between @api.depends and the function
into a proper docstring on _compute_costs.
- Make currency_id required=True (default still in place — only
a programmatic write would null it, but Monetary fields need a
non-null anchor).
- Add test_current_location_for_confirmed to cover the title-case
fallback branch in _compute_current_location.
Manifest 19.0.8.3.0 → 19.0.8.3.1.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Scope was reduced from spec's full §5.1 list because 6 of the 10
plating-specific fields point to models in dependent modules
(configurator, quality, portal, logistics, bridge_mrp). Adding
those Many2ones in core would invert the dependency graph. They
move to their owning modules via _inherit = 'fp.job' and get
re-bundled by fusion_plating_jobs in Phase 2.
This commit lands the core-safe subset:
- sale_order_line_ids (sale_management is in core depends)
- recipe_id, start_at_node_id (process.node is in core)
- invoice_ids (account is reachable via sale_management → sale)
- Cost rollup: quoted_revenue / actual_cost / margin / margin_pct
with placeholder compute (actual_cost = 0 until Task 1.5 wires
fp.job.step.cost_total)
- current_location stub (full Bath/Oven rendering in Task 1.6)
Tests cover the cost-rollup math and the current_location stub.
Spec §5.1 has been re-tabulated with explicit 'Module' column.
Manifest 19.0.8.2.1 → 19.0.8.3.0.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Sequence: add noupdate="1" to fp_job_sequences.xml. Without it,
every module update resets number_next to 1, corrupting the live
job-number stream. Matches fp_sequence_data.xml convention.
- action_cancel now raises UserError on an already-cancelled job
instead of silently rewriting state. Audit-grade traceability
expects explicit failures.
- Added TODO marker for action_hold / action_resume /
action_revert_to_confirmed so future authors don't bypass the
state-machine guards.
- Tests: added cannot_cancel_done (covers the dead-code UserError
branch) and cannot_cancel_already_cancelled.
Manifest version bumped 19.0.8.2.0 -> 19.0.8.2.1.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Spec §5.3: document that default_oven_id is deferred to the
fusion_plating_jobs bridge module (fusion.plating.bake.oven lives
in shopfloor; core can't depend on it).
- Plan: align ACL blocks for Tasks 1.2/1.3/1.5/1.7 to use
group_fusion_plating_operator for the lowest tier instead of
base.group_user. Caught by the code-quality reviewer on Task 1.2;
this prevents the same bug recurring in later tasks.
- Plan Task 1.2 test name corrected:
test_facility_required_for_active_centre →
test_facility_optional_at_create.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Header model replacing mrp.production. mail.thread for chatter,
priority/state/deadline tracking, sequence WH/JOB/00001+. Tests
cover create, confirm, cancel, and forbidden double-confirm.
State machine:
draft -> confirmed -> in_progress -> done
v ^
cancelled (rework reverts here)
on_hold can be entered from confirmed or in_progress.
Step relations come in Task 1.5; SO/recipe/portal/cost extension
fields come in Task 1.4.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ACL: use group_fusion_plating_operator for read-only tier instead of
base.group_user, matching the established hierarchy in the rest of
ir.model.access.csv
- Test: rename test_facility_required_for_active_centre →
test_facility_optional_at_create. Old name claimed the opposite of
what the assertion checks.
- Manifest version bumped 19.0.8.1.0 → 19.0.8.1.1.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces mrp.workcenter for plating. Domain-specific kinds
(wet_line/bake/mask/rack/inspect/other) drive release-ready
validation on steps. ACLs follow existing supervisor/manager
hierarchy.
Note: spec listed default_oven_id Many2one to fusion.plating.oven
but that model does not exist in core (the bake-oven model
fusion.plating.bake.oven lives in fusion_plating_shopfloor, which
core cannot depend on). Field omitted; the bridge that adds
fp.job/fp.job.step can re-introduce it via _inherit later.
Part of: native job model migration (spec 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The QR <img> src used `% quote(_scan_url)` to URL-encode the value,
but `quote` isn't always in the QWeb render context — particularly
on the fusion_pdf_preview / account render chains. Result:
KeyError: 'quote' when printing from the browser.
_scan_url is always base_url + '/fp/wo/<int>' — no characters that
need encoding. Replaced the % + quote() formatting with simple
string concatenation, dropping the quote dependency entirely.
Smoke verified on entech: MO + WO stickers render cleanly at 27KB
each (with QR image included).
fusion_plating_reports → 19.0.7.2.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User reported two issues with the sticker:
1. "Print → WO Box Sticker" didn't appear on the MO form
(WH/MO/00067). The operator workflow lives on the MO form, not
the WO — binding only to mrp.workorder meant they couldn't see
the option. Now bound to BOTH:
* mrp.workorder (per-WO sticker)
* mrp.production (per-MO sticker — prints the MO friendly
name after "WO #" so it reads naturally in shop-floor
vocabulary)
Internal refactor: factored the layout into a shared inner
template report_fp_wo_sticker_inner; the two outer templates
normalise their input to the same _order_id / _scan_id / _mo
variables and t-call the inner.
2. Design polish. The previous layout was a plain label/value
table that looked rough. Redesigned with:
* Proper sticker chrome: 0.5mm black border, 1.5mm rounded
corners, edge padding.
* Header row with bottom border rule separating logo+WO-# on
the left from QR+caption on the right.
* Grid rows now alternate white / #f4f5f7 zebra-striping with
a right-aligned vertical rule between label and value.
* ALL-CAPS, letter-spaced, gray-333 labels at 7.5pt; values
at 8.5pt with strong (9.5pt, 700) emphasis on the key data
(PO, Part Number, Qty) so it reads at a glance from across
the warehouse.
* Helvetica Neue font stack.
* "SCAN TO OPEN" caption under the QR.
Scan endpoint updated: /fp/wo/<id> now tries mrp.production first
(operator home form) then falls back to mrp.workorder. Numeric
collisions between the two id spaces are possible; MO wins because
the MO view carries the full context.
fusion_plating_reports → 19.0.7.1.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Client is migrating from Steelhead and needs to keep the small
parts-box sticker format the warehouse crew already knows. Two
pieces shipped together so scanning is seamless from day one:
1. report_fp_wo_sticker — 4x3" QWeb label bound to mrp.workorder.
Layout mirrors the Steelhead sticker:
* ENTECH logo top-left (via env.company.logo)
* QR code top-right encoding /fp/wo/<id>
* Grid: PO (RO) / Customer / Process / Part Number / Due
Date / Qty / Notes
Dedicated paperformat_fp_wo_sticker at 102x76mm, 300 DPI,
landscape, 3mm margins — sized for thermal / inkjet label
printers without shrink-to-fit.
Binding added so "Print → WO Box Sticker" appears on every
mrp.workorder record.
2. FpWoScanController — GET /fp/wo/<int:wo_id> redirects the
scanner straight to the work-order form
(/odoo/action-mrp.action_mrp_workorder/<id>). auth='user' so
logged-in scanners land on the WO immediately; others bounce
through Odoo's login and return to the same URL. No custom
client work needed — any phone camera, handheld barcode
scanner, or tablet browser opens the URL on scan.
Process row resolution chain: part.default_process_id →
coating.recipe_id → fallback. So the sticker prints whichever
process is actually going to drive WO generation for this line,
matching the direct-order wizard's Effective Process column.
fusion_plating_reports → 19.0.7.0.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
User request: on customer-facing PDFs the single "Part" column was
stacking part number + revision + description together. Split into
two distinct columns so customers see Part Number in its own field
and the Description column carries only the prose.
Macro split:
* customer_line_part_number — strong part number + "(Rev X)"
* customer_line_description — line.name + any populated line
metadata (serial, job#, thickness)
* customer_line_header (legacy) kept as a thin wrapper that
t-calls both macros stacked so older reports still render.
Reports updated — each gains a new first "PART NUMBER" column and
the old "PART" column renamed to "DESCRIPTION":
* report_fp_sale (both portrait variants)
* report_fp_invoice (both portrait variants)
* report_fp_packing_slip (both variants — delivery + MO-origin)
Column widths rebalanced per report. Section / note colspans bumped
to account for the extra column.
report_fp_bol left as-is — its "Description of Goods" td is a
freight-convention combined field, intentionally keeps the stacked
layout via the legacy customer_line_header macro.
fusion_plating_reports → 19.0.6.0.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Customer feedback: some customers don't send their PO with the
initial order — they send it days or weeks later. The system was
blocking SO confirmation without a PO, which forced the shop to
either wait on paperwork or ask a manager for a formal override.
New estimator-level path: a "PO Pending" boolean on sale.order +
an optional "PO Expected By" date.
* Confirm without a PO# / PO document when PO Pending is ticked.
* action_confirm skips the hard error if po_pending OR po_override
is set (keeps the existing manager-override path too).
* On confirm with PO Pending, the system schedules a chase
activity for po_expected_date (or +3 days if blank), assigned
via mail.activity so it shows up in the sales user's activity
list. Chatter note logged so audit is obvious.
* Direct-order wizard: po_number and po_attachment_file become
optional. Ticking "PO Pending" in the wizard is the trade-in;
a help note under the toggle explains the chase behaviour.
* Once the PO arrives, user fills in the PO# / uploads the doc,
and turns PO Pending off — existing downstream flow resumes.
Difference from x_fc_po_override (kept):
* PO Override = manager waiver, permanent ("handshake deal").
* PO Pending = estimator flag, time-boxed ("customer will send it
by Friday").
fusion_plating_configurator → 19.0.14.0.0
fusion_plating_invoicing → 19.0.3.0.0
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>