Files
Odoo-Modules/fusion_plating/docs/superpowers/specs/2026-04-25-fp-native-job-model-design.md
gsinghpal f7a4cba5a8 docs(jobs): split fp.job §5.1 fields by module ownership (Task 1.4)
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>
2026-04-24 21:54:35 -04:00

32 KiB
Raw Blame History

Native Plating Job Model — Design

Date: 2026-04-25 Module scope: All modules in fusion_plating/ that touch mrp.production or mrp.workorder (~12 modules). Net effect: replace Odoo MRP integration with a native plating job model. Status: Design approved 2026-04-25. All §10 decisions locked per recommendations (see §10 footer). Implementation plan written: docs/superpowers/plans/2026-04-25-fp-native-job-model.md. Owner: Nexa Systems Predecessor: entech is currently in production on the MRP-bridged model (fusion_plating_bridge_mrp). This spec replaces that model.


1. Why we're doing this

Plating is not assembly. Odoo MRP is built around BoM consumption, multi-level assembly, and material-driven scheduling. Plating is process: parts in → baths → parts out. The "BoM" is bath chemistry (already its own model), not stock items. Steelhead, ProShop, PROPLATE, JobBOSS — none of them model plating as MRP. They all use a job/router model because plating IS different.

We bridged into Odoo MRP three months ago to get free machinery (kanban, work-centre cost, MRP menus). The cost has been:

  • Operators see N work orders for one job (8 WOs for a typical recipe).
  • Three different views to understand one job (MO form, WO form, Plant Overview).
  • 5,000+ LOC in fusion_plating_bridge_mrp whose only job is keeping MRP and our plating models in sync.
  • Recipe overrides, work-centre mapping fallbacks, WO state syncing — most of our hardest bugs come from this sync.
  • Future features (operator mobile app, AI step suggestions, IoT auto-actions) get more expensive every time we add coupling.

With the user's "I don't care about MRP dependencies" call, the trade-off flips. Native model is simpler today, simpler tomorrow, and the data model finally matches the domain.

2. Goal

Replace mrp.production and mrp.workorder (in the plating context) with a native job model:

  • fp.job — one record per plating job. Replaces mrp.production.
  • fp.job.step — one record per operation within the job. Replaces mrp.workorder.
  • The recipe template (fusion.plating.process.node) is unchanged — it's already domain-correct. fp.job.step instantiates from it via recipe_node_id.

Operator UX target: scan a sticker → land on the job page → see the process tree → tap an operation → start/finish/hold. One screen, one mental model.

Manager UX target: list of jobs (kanban or tree), drill into a job, see the same process tree with cost/time aggregates.

3. In scope

  1. New models: fp.job, fp.job.step. Field schema in §4.
  2. SO → job hook: sale.order.action_confirm() creates fp.job directly (replaces the current SO → MO bridge in fusion_plating_bridge_mrp/models/sale_order.py).
  3. Recipe → steps generator: fp.job._generate_steps_from_recipe() walks the recipe tree and creates fp.job.step rows (replaces _generate_workorders_from_recipe).
  4. Refactor 12 modules to use fp.job / fp.job.step instead of mrp.production / mrp.workorder. Module-by-module plan in §6.
  5. Migrate live data from entech: every mrp.production row becomes an fp.job row; every mrp.workorder row becomes an fp.job.step. Migration spec in §7.
  6. New views: Job form (with embedded process tree), job kanban, step form (manager-only), redesigned tablet station and plant overview.
  7. Reports rewritten against new model: WO sticker, job traveller, WO margin, bill of lading, packing slip.
  8. Cert generation, notifications, deliveries, invoicing, batches, holds — all rebound to fp.job / fp.job.step.
  9. Drop sale_mrp and mrp dependencies from the fusion_plating_* chain except where genuinely needed (TBD per §10).

4. Out of scope

  • Recipe template model (fusion.plating.process.node) stays as-is. No schema changes, no migration needed for it.
  • The customer portal (fusion_plating_portal) UI — only its model link (x_fc_production_idx_fc_job_id).
  • Bath chemistry model (fusion.plating.bath.log) and IoT data ingestion — they link to baths/tanks, not to MOs/WOs.
  • KPI definitions (fusion.plating.kpi) — only the rollup queries.
  • Customer-facing terminology — operators still say "WO #00033". The label format is preserved; only the underlying model changes.
  • Removing mrp module entirely from Odoo. We just stop depending on it from our modules. If managers still want the standard Manufacturing menu for any reason, they can — but our flow doesn't use it.

5. Data Model

5.1 fp.job

Replaces mrp.production for plating jobs. One record per shop-floor job.

Module ownership: fp.job lives in fusion_plating core. Cross-module fields (referencing models from fusion_plating_configurator, _portal, _logistics, _quality, _bridge_mrp) cannot live in core without inverting the dependency graph. Each owning module extends fp.job via _inherit to add its field. The Phase 2 module fusion_plating_jobs becomes the umbrella that pulls all the extensions together. Ownership is called out in the Module column below.

Field Type Module Notes
name Char core Sequence: WH/JOB/00033. The legacy "WH/MO/00033" labels stay only on migrated records (see §7).
state Selection core draft, confirmed, in_progress, done, cancelled, on_hold
partner_id Many2one(res.partner) core Customer; copied from SO
product_id Many2one(product.product) core Reference part product (for inventory only)
qty Float core Quantity to plate
qty_done Float core Quantity completed
qty_scrapped Float core Quantity scrapped (rolled up from holds)
date_deadline Datetime core Promised completion date
date_planned_start Datetime core Planned start
date_started Datetime core Actual start (first step start)
date_finished Datetime core Actual completion
origin Char core SO name for traceability
sale_order_id Many2one(sale.order) core Source SO (sale_management is in core depends)
sale_order_line_ids Many2many(sale.order.line) core Lines that fed this job (group_tag collapse)
recipe_id Many2one(fusion.plating.process.node) core The recipe template used
step_ids One2many(fp.job.step, job_id) core The operations
step_count Integer core Computed
step_done_count Integer core Computed
step_progress_pct Float core Computed: step_done_count / step_count * 100
current_step_id Many2one(fp.job.step) core The operation currently in progress (or next ready)
facility_id Many2one(fusion.plating.facility) core Hard gate at confirm
manager_id Many2one(res.users) core Plating manager
priority Selection core low, normal, high, rush (operator-relevant ordering)
invoice_ids Many2many(account.move) core Linked invoices (account is reachable via sale_management → sale → account)
quoted_revenue Monetary core From SO
actual_cost Monetary core Computed from steps + consumables
margin Monetary core Computed
margin_pct Float core Computed
start_at_node_id Many2one(fusion.plating.process.node) core Rework: start at this recipe node
current_location Char core Computed: "Queued: Bath 3" / "In progress: Oven A" / "Ready to ship"
mail.thread, mail.activity.mixin Inherits core Chatter
part_catalog_id Many2one(fp.part.catalog) fusion_plating_configurator (_inherit = 'fp.job') The actual part being plated; primary identifier
coating_config_id Many2one(fp.coating.config) fusion_plating_configurator The coating spec
customer_spec_id Many2one(fusion.plating.customer.spec) fusion_plating_quality Optional spec
portal_job_id Many2one(fusion.plating.portal.job) fusion_plating_portal Customer portal binding
delivery_id Many2one(fusion.plating.delivery) fusion_plating_logistics The shipment
qc_check_id Many2one(fusion.plating.quality.check) fusion_plating_jobs (Phase 2) Active QC check; model lives in current bridge_mrp, will move to jobs module
certificate_ids One2many(fp.certificate, job_id) fusion_plating_certificates Certs generated
batch_ids One2many(fp.batch, job_id) fusion_plating_batch Batches that ran through
quality_hold_ids One2many(fp.quality.hold, job_id) fusion_plating_quality Holds raised
consumption_ids One2many(fp.job.consumption, job_id) fusion_plating_jobs (Phase 2) Consumables
override_ids One2many(fp.job.node.override, job_id) fusion_plating_jobs (Phase 2) Per-job opt-in/out

State machine:

draft → confirmed → in_progress → done
            ↓                       ↑
        cancelled            (can revert to confirmed if rework)
            
on_hold can be entered from confirmed/in_progress

5.2 fp.job.step

Replaces mrp.workorder. One record per operation node from the recipe.

Field Type Notes
name Char Operation name (from recipe node)
job_id Many2one(fp.job, ondelete=cascade) Parent job
sequence Integer Order (10, 20, 30...)
recipe_node_id Many2one(fusion.plating.process.node) Source recipe operation
state Selection pending, ready, in_progress, paused, done, skipped, cancelled
kind Selection wet, bake, mask, rack, inspect, other (computed from equipment/name; today on WO as x_fc_kind)
work_centre_id Many2one(fp.work.centre) New native model — see §5.3
bath_id, tank_id, rack_id, oven_id Many2one Equipment — same as today
masking_material_id Many2one Mask kind
assigned_user_id Many2one(res.users) Operator assignment
started_by_user_id Many2one(res.users) Audit
finished_by_user_id Many2one(res.users) Audit
signoff_user_id Many2one(res.users) Audit (sign-off gates)
date_started Datetime First start (timer)
date_finished Datetime Last finish
duration_expected Float Minutes
duration_actual Float Minutes (sum of intervals)
time_log_ids One2many(fp.job.step.timelog) Start/stop intervals — gives us the granularity Odoo had
instructions Html Step-level instructions (formatted from child step nodes of the recipe)
thickness_target Float For plating WOs
thickness_uom Selection µm/mil/in
dwell_time_minutes Float Recipe-spec'd dwell
bake_setpoint_temp Float Bake WOs only
bake_actual_duration Float Bake WOs only
bake_chart_recorder_ref Char Nadcap audit
requires_signoff Boolean Related from recipe_node_id
auto_complete Boolean Related from recipe_node_id
is_manual Boolean Related from recipe_node_id
customer_visible Boolean Related from recipe_node_id
contract_review_user_ids Many2many(res.users) For contract review approver gate
work_role_id Many2one(fp.work.role) Required role for assignee
cost_per_hour Monetary From work centre (or operator)
cost_total Monetary Computed: duration × rate
quality_hold_ids One2many(fp.quality.hold, step_id) Holds raised at this step
is_release_ready Boolean Computed: required fields filled per kind

State machine:

pending → ready → in_progress → done
            ↓         ↓ ↑
        skipped     paused
                       ↓
                   cancelled
  • pending = step exists but earlier siblings not done
  • ready = predecessors done (or first step of job)
  • in_progress = operator started timer
  • paused = operator stopped timer without finishing (intentional break, end of shift)
  • done = operator finished + sign-off (if required) recorded
  • skipped = opt-in step that wasn't activated for this job, OR start-at-node skipped this step
  • cancelled = job cancelled or rework removed this step

5.3 fp.work.centre (new model)

We replace mrp.workcenter with our own model since work centres for plating are domain-specific (a tank line, a bake oven, a rack station — not assembly cells).

Field Type Notes
name Char "Bath Line 1", "Oven A", "Rack Station"
code Char Short code
facility_id Many2one(fp.facility) Which facility
kind Selection wet_line, bake, mask, rack, inspect, other
cost_per_hour Monetary For margin calculations
default_bath_id, default_tank_id Many2one(fusion.plating.bath/.tank) Single-line shop convenience
default_oven_id Many2one(fusion.plating.bake.oven) Deferred to fusion_plating_jobs bridge module via _inheritbake.oven is defined in fusion_plating_shopfloor which fusion_plating core cannot depend on. Bridge module can depend on shopfloor and adds this field there.
active Boolean

This replaces x_fc_mrp_workcenter_id mapping that the recipe operations have today.

5.4 fp.job.step.timelog

Granular start/stop tracking. Each timer pause creates a record.

Field Type Notes
step_id Many2one(fp.job.step)
user_id Many2one(res.users)
date_started Datetime
date_finished Datetime NULL while running
duration_minutes Float Computed

5.5 What stays unchanged

  • fusion.plating.process.node (recipe template) — unchanged
  • fusion.plating.process.node.input (operator inputs) — unchanged
  • fp.batch, fp.certificate, fp.thickness.reading — only their backlink fields rename
  • fp.portal.job — only its x_fc_production_id field renames to job_id
  • fp.delivery — only its job-back-reference rebinds
  • fp.quality.hold, fp.ncr, fp.capa — only backlinks rebind
  • fp.notification.template, fp.notification.log — trigger event names update
  • IoT (fusion_plating_iot), bath chemistry (fusion.plating.bath.log), KPIs (fusion.plating.kpi) — none touch jobs/steps directly, no changes

6. Module-by-module refactor plan

Listed in dependency order — refactor can/should happen along this gradient.

6.1 fusion_plating (core)

Effort: 3 days

  • Define fp.job, fp.job.step, fp.job.step.timelog, fp.work.centre models.
  • Sequences (ir.sequence records for fp.job, fp.job.step).
  • Security (groups already exist: Operator/Supervisor/Manager/Admin; ACLs added).
  • Move fusion.plating.job.node.override from bridge_mrp into core, rebind to fp.job.

6.2 fusion_plating_bridge_mrpfusion_plating_jobs (rename + gut)

Effort: 5 days

The current bridge module becomes the home of SO→job hooks, recipe→steps generator, and computed rollups. Most of its weight (the WO sync logic) goes away.

  • Rename module dir to fusion_plating_jobs.
  • Migrate _fp_auto_create_mo_fp_auto_create_job on sale.order.
  • Migrate _generate_workorders_from_recipefp.job._generate_steps_from_recipe. Simpler: no MRP work-centre mapping fallback, no mrp.workorder.create quirks.
  • Move all x_fc_* fields that currently sit on mrp.production to fp.job natively.
  • Move all x_fc_* fields on mrp.workorder to fp.job.step natively.
  • Drop sale_mrp from __manifest__.py depends. Drop mrp if confirmed (§10 Q3).
  • Quality check, racking inspection, cert generator, delivery hooks — all rebind.

6.3 fusion_plating_batch

Effort: 0.5 day

  • workorder_idstep_id (Many2one fp.job.step)
  • production_id (related) → job_id (related from step_id.job_id)
  • Views and access rules trivial rename.

6.4 fusion_plating_quality

Effort: 1 day

  • fp.quality.hold adds job_id, step_id fields (replaces production_id, workorder_id from bridge).
  • NCR and CAPA reference holds via current relations — no schema change.
  • Views: hold form refers to job/step instead of MO/WO.

6.5 fusion_plating_certificates

Effort: 0.5 day

  • fp.certificate.production_idfp.certificate.job_id
  • fp.thickness.reading.production_idfp.thickness.reading.job_id
  • Cert auto-creation hook moves from MO done to fp.job.button_mark_done.

6.6 fusion_plating_invoicing

Effort: 0.5 day

  • Invoice → portal job linkage stays; just walks via job instead of MO.
  • Account hold gating (on SO confirm, invoice post, delivery) stays — same partner-level field.

6.7 fusion_plating_logistics

Effort: 0.5 day

  • fp.delivery.job_ref resolution rebinds: from MO.name to fp.job.name (same string format if we use WH/JOB/00033).
  • Delivery auto-creation hook on fp.job.button_mark_done.

6.8 fusion_plating_portal

Effort: 0.5 day

  • fp.portal.job.x_fc_production_idjob_id (Many2one fp.job).
  • Portal templates: same — they read MO.name; if we keep WH/JOB/... format, no UI change.
  • Auto-create on fp.job.action_confirm().

6.9 fusion_plating_configurator

Effort: 1 day

  • Configurator wizard already creates SO lines; no direct MO touch.
  • The action_view_mrp_production button on SO becomes action_view_jobs.
  • Recipe assignment paths stay (part catalog → coating config → job).

6.10 fusion_plating_notifications

Effort: 1 day

  • Trigger event names: mo_confirmedjob_confirmed, mo_completejob_complete, wo_startedstep_started, etc.
  • Existing customer-facing template content unchanged (uses partner/SO/cert vars).
  • Hook from fp.job.button_mark_done and fp.job.step.button_finish.

6.11 fusion_plating_shopfloor

Effort: 6 days ← biggest single chunk

This is where operators live. Full rewrite of the operator UI.

  • Plant Overview (kanban): one card per fp.job.step in ready or in_progress, grouped by fp.work.centre. Drag-drop changes work centre on the step.
  • Tablet Station: scan job sticker → land on job page. Job page shows: process tree (the IS-the-job tree) + ready/in-progress steps highlighted + start/finish/hold/sign-off buttons.
  • Process Tree client action: now the primary job view (not a separate drill-down). Renders fp.job.step records as cards with state colours, hierarchy from recipe_node_id.parent_id chain.
  • Manager Dashboard: list of jobs with progress %, current step location, manager actions (assign worker, take over, raise hold).
  • All RPC routes (/fp/shopfloor/start_wo etc.) renamed and rebound.

6.12 fusion_plating_reports

Effort: 3 days

  • WO Box Sticker — already mostly model-agnostic; rebind the inner template's _mo resolution. Print URL /fp/job/<id> instead of /fp/wo/<id>.
  • Job Traveller — loops over fp.job.step_ids instead of mrp.production.workorder_ids.
  • WO Margin Report — same rollup, different model.
  • BoL, Packing Slip, Invoice — all read from sale_order anyway, only the cross-ref to job needs updating.
  • Hide-default-reports XML: drop the mrp.production / mrp.workorder hides (they're no longer in our app).

6.13 fusion_plating_kpi

Effort: 0.5 day

  • KPI rollup queries: mrp.productionfp.job, mrp.workorderfp.job.step.
  • KPI definitions stay; SQL/ORM domain rewrites.

6.14 fusion_plating_receiving

Effort: 0.5 day

  • Soft gate hook on fp.job.action_confirm (currently on MO confirm).
  • fp.racking.inspection.production_idjob_id.

6.15 Other modules with MRP touchpoints

  • fusion_plating_aerospace, fusion_plating_nuclear, fusion_plating_cgp, fusion_plating_safety: minimal — none reference MO/WO directly per the audit. Verify in §7 testing.
  • fusion_plating_compliance and bridge_quality, bridge_documents, bridge_maintenance, bridge_sign: light touch — rebind any MO/WO refs to job/step.

6.16 Data scripts / tests

Effort: 2 days

  • 10+ scripts in scripts/ and docs/superpowers/tests/ query mrp.production/mrp.workorder. Rewrite to query fp.job/fp.job.step.

Total effort estimate

Phase Days
Core models + sequences + security 3
fusion_plating_jobs (gut + rebuild) 5
Modules 6.36.10 (8 modules, light) 5.5
Configurator 1
Notifications 1
Shopfloor (full rewrite) 6
Reports 3
KPI + Receiving + other 1.5
Scripts + tests rewrite 2
Migration script (§7) 3
Test on entech-clone 5
Cutover + burn-in 3
Total 39 working days ≈ 8 weeks

Calendar time with iteration: 911 weeks.


7. Migration strategy

entech is in production. Migration must be reversible until we're confident, and must preserve every link operators / accounting depend on.

7.1 Approach: Big-bang with scripted migration + 2-week shadow period

  1. Build new models alongside existing ones in a feature branch. Both coexist in the codebase.
  2. Run migration script on a clone of entech (not entech itself). Validate E2E: every report renders, every cert PDF reproduces, every link resolves.
  3. Cutover weekend on entech: ~4 hour window. Steps in §7.4.
  4. Shadow period (weeks 12 post-cutover): keep mrp.production / mrp.workorder tables as read-only snapshots. If anything goes wrong, we can revert to the snapshot via a reverse migration script.
  5. After 2 weeks of stable operation, drop the MRP tables.

7.2 Migration script

Location: fusion_plating_jobs/migrations/19.0.8.0.0/post-migration.py

For each mrp.production row:

  1. Create fp.job with same id (use fp_job_id_seq aligned to MRP id space, or keep separate sequence and store the legacy id in legacy_mrp_production_id for audit).
  2. Copy fields: name, partner_id, product_id, product_qty → qty, dates, origin, state (mapping in §7.3), all x_fc_* extension fields.
  3. For each child mrp.workorder, create fp.job.step with all x_fc_* fields.
  4. Migrate mrp.workorder.time_ids (if present) to fp.job.step.timelog.
  5. Rebind every cross-reference: cert.production_id, batch.production_id, delivery.job_ref, portal_job.x_fc_production_id, etc.
  6. Preserve chatter: copy mail.message records from MO/WO to corresponding job/step (Odoo's res_id + model rebinding).
  7. Audit log: write fp_migration.log with row counts, mismatches, warnings.

7.3 State mapping

MRP state fp.job state
draft draft
confirmed confirmed
progress in_progress
to_close in_progress (will be done after final ops)
done done
cancel cancelled
MRP WO state fp.job.step state
pending pending
waiting pending
ready ready
progress in_progress
done done
cancel cancelled

7.4 Cutover plan (single weekend window)

Friday 6pm: Stop operators. Final MOs of the week wrapped up. Friday 8pm: Backup full database. Tag as pre_fp_job_migration. Friday 9pm: Deploy new module bundle to entech. Run migration script. Estimated 30 min. Friday 10pm: Smoke test — open a recent job, print sticker, scan, render CoC, generate margin report. If anything fails, restore from 8pm backup; abort. Saturday/Sunday: Live shop is offline anyway (weekend). Time to fix anything that surfaced. Monday 7am: Operators come in. Manager + tech on site for the first 2 hours. Following 2 weeks: Daily check-ins. Active monitoring of error logs. MRP tables still present (read-only).

7.5 Rollback plan

If the cutover fails or unrecoverable issues surface within 7 days:

  1. Stop operators.
  2. Restore Friday 8pm DB backup.
  3. Revert deploy to previous module bundle.
  4. Reopen as previous-MRP system.

After 7 days, rollback becomes "forward fix only" — too much new shop activity to restore.

7.6 Data preservation guarantees

  • Every historical job remains queryable. Old MOs become old fp.job records.
  • Every chatter message preserved (Odoo's mail.message.res_id rebinding).
  • Every PDF attachment preserved (ir.attachment.res_id rebinding).
  • Every cert, thickness reading, batch, hold preserved with intact links.
  • Audit-trail integrity for Nadcap / aerospace customers is critical; the migration script must verify zero loss before commit. We'll add a pre-migration audit script that snapshots counts and a post-migration audit that re-validates.

8. Test strategy

8.1 Unit tests

  • fp.job state machine transitions
  • fp.job.step state machine transitions
  • _generate_steps_from_recipe with: simple recipe, nested sub_processes, opt-in/out overrides, start-at-node rework, missing work-centre mapping
  • Migration script: round-trip a snapshot of entech data through migrate, verify every row's fields match expectation

8.2 Integration tests (end-to-end on a fresh DB)

  • Quote → SO → confirm → job created → recipe assigned → steps generated → start step → log time → finish step → all steps done → job done → portal job ready_to_ship → delivery created → CoC generated → invoice posted → portal job complete
  • Quality hold raised mid-job → blocks finish → released → job continues
  • Rework (start-at-node) job → only later steps generated
  • Cancel a confirmed job → all steps cancelled → portal job cancelled → no cert

8.3 E2E on entech-clone

  • Restore entech production DB to a staging container.
  • Run migration script.
  • Replay last 30 days of operator actions through the new UI.
  • Run every report, every cert, every notification trigger.
  • Diff against pre-migration snapshot: identify any data drift.

8.4 Performance baseline

  • Plant Overview load time with 1000 active steps: target < 1.5s
  • Job form open with 50-step recipe: target < 800ms
  • Report rendering (CoC, traveller, sticker): target < 3s

9. Risk register

Risk Likelihood Impact Mitigation
Migration script silently drops chatter on a record Medium Medium Pre/post-migration audit scripts compare message counts
A module we forgot still references mrp.workorder, fails at runtime Medium Medium CI grep in pre-deploy that fails on mrp.workorder / mrp.production references in our code
Cert PDF differs vs current (audit/Nadcap impact) Low High Render 100 sample CoCs pre-migration, render same post-migration, byte-diff
Operators confused by new UI High Medium Beta with 2 operators 1 week before cutover; live training Monday morning
Plant Overview slower than current Medium Low Performance baseline test in §8.4; index fp_job_step.state, work_centre_id
Account hold gate breaks → invoices post that shouldn't Low High Unit tests + integration test for held-customer invoice attempt
Rollback needed >7 days post-cutover Low High Forward-fix only after day 7; treat the new model as canonical
mrp Odoo module updates break our coexistence Low Low We're un-depending; if mrp updates, we don't care
Missed dependency causes module install failure Medium Low Test install on fresh DB before cutover

10. Open decisions (sign-off needed)

These need a yes/no from you before I write the implementation plan:

Q1. Naming: fp.job vs fp.work.order?

Operators currently say "WO #00033". The label format is independent of the model name — we can call the model fp.job and still print "WO #00033" on the sticker.

Recommendation: fp.job. Cleaner, doesn't pretend to be Odoo MRP, "work order" is a UI label not a model name.

Your call: ___ fp.job ___ fp.work.order ___ other:______

Q2. Sticker/sequence label format: keep WH/MO/00033 or switch to WH/JOB/00033?

Existing labels printed on real boxes around the shop say WH/MO/00033. Reprinting all of them is a real cost.

Recommendation: Keep WH/MO/... for migrated records (preserve the label that's on the box). Use WH/JOB/... for new records going forward. Both formats render identically on the sticker as "WO #...".

Your call: ___ keep WH/MO/... for everything ___ switch new ones to WH/JOB/... ___ switch all (re-label boxes) ___ other:______

Q3. Drop mrp module dependency entirely, or keep it installed but unused?

Removing mrp removes the standard Manufacturing app from the menu — managers who occasionally peek at standard MRP views lose them. Keeping it installed means ~150MB of unused tables in the DB.

Recommendation: Keep mrp installed (low cost) but drop it from our modules' depends. We don't use it; it sits idle. We can revisit removing later.

Your call: ___ keep installed ___ uninstall fully ___ other:______

Q4. fp.work.centre — new model, or extend mrp.workcenter?

We could keep mrp.workcenter as the work-centre table even though we drop the rest of MRP. Saves us 1 model worth of refactor.

Recommendation: New fp.work.centre. Plating work centres are different from assembly work centres (kind = wet_line / bake / mask / rack), and we already have x_fc_facility_id, x_fc_fp_work_center_id extensions on mrp.workcenter. Cleaner to start fresh.

Your call: ___ new fp.work.centre ___ keep mrp.workcenter ___ other:______

Q5. Step model granularity — operations only, or full recipe tree?

Option A: fp.job.step = operations only (matches current MRP WO behaviour). Container nodes (recipe / sub_process) and step nodes (instructions) are pulled from the recipe template at render time. Option B: fp.job.step mirrors the full recipe tree (operations + containers + steps).

Recommendation: Option A. Simpler model, current working pattern, render hierarchy via JOIN at view time. Steelhead's UX achievable without DB-level tree.

Your call: ___ A (ops only) ___ B (full tree) ___ other:______

Q6. Migration approach — big-bang weekend cutover, or parallel-run?

Parallel-run = both systems live for 2 weeks, jobs created in both, comparing output. More robust but more complex.

Recommendation: Big-bang with shadow period (§7.1). Simpler, lower error surface. Cutover is ~4 hours on a weekend.

Your call: ___ big-bang ___ parallel-run ___ other:______

Q7. Implementation pace — single sprint or phased?

  • Single sprint: 810 weeks one engineer, full cutover at end.
  • Phased: ship fp.job/fp.job.step models first (week 4); migrate one module per week thereafter; cutover at the end (week 12+). More UI churn for operators during the transition.

Recommendation: Single sprint. Operators only switch UI once.

Your call: ___ single sprint ___ phased ___ other:______


Decisions locked (2026-04-25)

# Decision Locked answer
Q1 Model name fp.job
Q2 Sticker label format Keep WH/MO/... for migrated records; new records use WH/JOB/.... Both render as "WO #..." on the sticker.
Q3 mrp Odoo module Keep installed but drop from our depends
Q4 Work centre model New fp.work.centre
Q5 Step model granularity Option A — operations only (flat list, hierarchy via JOIN at view time)
Q6 Migration approach Big-bang weekend cutover with 2-week shadow period
Q7 Implementation pace Single sprint, ~8 weeks engineering

11. Next steps

After you sign off on §10:

  1. I write the implementation plan (docs/superpowers/plans/...) — concrete per-day task breakdown, branch strategy, commit cadence.
  2. We create a feature branch: feat/fp-native-job-model.
  3. We start with §6.1 (core models) and follow the dependency order through §6.16.
  4. End-of-week demos to you against an entech-clone.
  5. Cutover weekend scheduled with 4 weeks notice to give the shop time to plan.

If something in §10 or anywhere else is wrong / unclear / debatable, flag it now — fixing the spec is cheap; fixing committed code is not.