# 23 — Manufacturing Build Roadmap (Phased, Execution-Grade)

> Reconciles the spec's prescribed 12-step build order (`md/06-spec-integration.md` §6.4) with Moon ERP
> reality (existing `Modules/Production` skeleton — `md/10-production-now.md`; Inventory/Purchases rails —
> `md/11-inventory-purchases.md`; Accounting rails — `md/12-accounting-core.md`; neighbors/FE — `md/13-neighbors-fe.md`).
> Direction A (decided): **EXTEND `Modules/Production` in place** — zero data, zero inbound FKs, full FE +
> 19-permission scaffolding already exist. Rebuild is unwarranted.

---

## 1. Reconciliation: spec build order vs Moon ERP build order

Spec §6.4 prescribes: 1 Master Data → 2 MRP-Settings/StdCost → 3 MPS → 4 MRP → 5 CRP → 6 Production Order
→ 7 Material Issue → 8 Confirmation → 9 Goods Receipt → 10 Costing → 11 SFC → 12 Integrations (last).

**Three deliberate inversions for Moon ERP:**

| # | Spec says | Moon plan does | Why |
|---|---|---|---|
| 1 | Integrations LAST (step 12) | Integrations **woven into every phase** | Spec rule 00 §0.4-6 ("every actual event posts a GL entry, real-time, not batch") contradicts its own step 12. Moon's house recipe (`CreateJournalEntry` Action calls, `ApproveIssue`/`ApproveReceipt`) makes integration the cheapest part, and the existing module's single biggest gap IS the missing Inventory/GL posting. |
| 2 | Full Planning (MPS/MRP/CRP) before Execution | **Execution-with-real-postings FIRST**, Planning in Phase 4 | A factory can run manual production orders profitably without MRP; it cannot run MRP without trusted stock balances and BOMs. The existing module already has order lifecycle + FE — closing the stock/GL gap converts it from a "book-only toy" to a usable system in one phase. |
| 3 | MPS with forecast consumption early (step 3) | **MPS-lite deferred**; MRP v1 driven by confirmed `sales_orders` + reorder points | Moon has NO forecasting source (gap confirmed in `02-spec-planning` digest). Building forecast-consumption/time-fences before a demand source exists yields dead code. |

Everything else keeps the spec's dependency chain: Master Data → Order/Issue/Confirm/GR → Costing → Planning → SFC → Toll/advanced. The genericity mandate (00 §0.2: config field + branch, never hard-coded factory behaviour) governs every phase.

**Sizing legend (PHASE-level):** S ≈ ≤1 dev-week · M ≈ 2–4 dev-weeks · L ≈ 4–8 dev-weeks (single senior dev + Claude Code; FE included).
⚠️ Distinct from the GAP-level legend in 21 (S ≤ ~3 dev-days / M ≤ ~3 dev-weeks / L > 3 dev-weeks) — do not map gap sizes onto phase sizes when budgeting.

---

## 2. Phase 0 — Hardening & Foundations (S)

**Scope (no factory-visible features; everything later stands on this):**
- Consolidate the 3 defensive migrations (`2026_03_31_000001/000002/000003`) into clean additive migrations going forward; new tables use `mfg_` prefix; existing 7 tables are KEPT as-is (see §9 migration strategy).
- Create the missing recipe layers in `Modules/Production`: `app/Actions/`, `app/Events/`, `app/Listeners/`, `app/Services/`; wire `EventServiceProvider`.
- Register a `production` contributor in Core `PermissionDependencyRegistry` (mirror `LISServiceProvider::register('lis', ...)`) — expand/mapFor/presets/groupOrder for the existing 19 permissions.
- Add `Production` case to `Modules/Core/app/Enums/ApprovalModule.php` (+ `label()` arm + translation key).
- Activate the declared-but-unused `BomStatus` enum: cast on `BillOfMaterials`, backfill `status` from `is_active`.
- `SettingDefinition` seeder skeleton for the **`production.*`** keys (ONE settings namespace, matching the `production.*` permission prefix and `'production'` sequence key — the same namespace every posting Action resolves; C2 closed); Pest scaffolding (`tests/Feature`, `tests/Unit` currently `.gitkeep` only).
- **Spec-addenda track (owner: product architect + factory SME · timebox: 1 week, parallel to the hardening work · hard gate: no Phase 1 exit without it):** author the written addenda 21 mandates for all Direction-B gaps (B1–B25). D-01..D-22 below ratify the contested ones; B-gaps without a decision row (B5 rework flow, B6 co/by-products, B11 multi-plant netting, B19 currency basis, B20 WIP count, etc.) each get a one-page addendum signed off by the board.

**Modules touched:** Production, Core.
**Prerequisite gap-fixes:** none (this phase IS the fix layer).
**Usable increment:** none functional — engineering debt cleared; CI green with first Pest suite; all spec addenda written.
**Risks:** low. Migration consolidation must be proven idempotent on the dev DB that caused the original triple-guard pattern.

---

## 3. Phase 1 — Master Data v2 + Real Execution: stock-posting & GL-posting orders (L) ⭐ first usable increment

**Scope — tables:**
- Extend `bill_of_materials`: `bom_type` enum {Production, Engineering, Phantom}, `status` (BomStatus, now real), `effective_from/to`, `base_quantity` semantics; extend `bom_components`: `issue_type` {Manual, Backflush, AutoIssue}, `issue_level` {PerOrder, PerShift, PerOperation}, `operation_seq`, `scrap_percentage` (rename/alias `waste_percentage`); substitutes in the child table **`mfg_bom_substitutes`** (`bom_component_id`, `substitute_product_id`, `priority`, `ratio` — per mapping row 3, D-18; no JSON column); allow component `product_id` to itself own a BOM (multi-level recursion + cycle guard).
- Extend `production_orders`: `production_type`, `material_ownership`, `order_type` {Standard, Rework, Repair}, `wip_account_id`, snapshot stamps `bom_snapshot_version`/`snapshot_taken_at`; new statuses (see state machine below).
- New: `mfg_material_issues` + `mfg_material_issue_lines` (thin headers over Inventory `InventoryIssue` via `reference_type='production_order'`) and `mfg_goods_receipts` + lines (over `InventoryReceipt`). **Definitive per the mapping (D-19)** — the mfg doc tables exist precisely because Inventory documents have no home for `issue_type`/`issue_level`/`operation_no`/grade/`quality_status` fields; using Inventory documents directly is NOT an option. GR lines carry `quality_status` defaulting to **Released** until the QMS gates land in Phase 6 (D-21).

**Scope — state machine (replaces current draft/confirmed/in_progress/completed/cancelled):**
`Planned → Released → InProcess → Completed → Closed` (+ `Cancelled` from Planned/Released — spec gap filled per house pattern). `ProductionOrderStatus` enum rewritten with `can*()` guards; data mapping: draft→Planned, confirmed→Released, in_progress→InProcess, completed→Completed, cancelled→Cancelled (no rows exist in prod, so mapping is for dev/demo data only).
- **Release side-effects:** freeze snapshot (`production_order_materials`/`_operations` already copy BOM — formalize as immutable after Release), reserve stock, number via existing `SequenceService('production','order')`.
- **Close side-effects:** WIP residual settlement (Phase 1: settle to FG cost / P&L rounding; full variance decomposition arrives Phase 3).

**Scope — services/actions (all new, house recipe):**
- `Actions/ReleaseProductionOrder` — snapshot + `StockService::reserve()` per component.
- `Actions/IssueMaterials` — creates draft `InventoryIssue` (`reference_type='production_order'`) → `app(ApproveIssue::class)->execute()` (FIFO/WAC cost via `StockService::getIssueCost`) → posts **DR WIP / CR Inventory** via `Modules\Accounting\Actions\CreateJournalEntry` (`source_type='production_order'`, `entry_type='production_material_issue'`, lines stamped `cost_center_id`) → releases reservation.
- `Actions/ReceiveFinishedGoods` — `InventoryReceipt` at rolled-up WIP cost → `ApproveReceipt` (writes cost layer) → **DR FG / CR WIP**.
- `Actions/CloseProductionOrder` — WIP zero-out + lock.
- Domain events (canonical names per mapping/D-17): `ProductionOrderReleased`, `MaterialIssued`, `GoodsReceiptPosted`, `ProductionOrderClosed` (+ listeners doing the GL calls — keeps Actions composable).

**Scope — screens (Angular, extend `features/production`):** BOM editor v2 (versions/status/multi-level tree), order release/issue/receive dialogs, stock-aware availability panel.

**Modules touched:** Production, Inventory, Accounting, Core.

**Prerequisite gap-fixes (Direction A):**
1. **Inventory:** implement `StockService::reserve()/release()` writing the existing-but-orphan `reserved_quantity` column (`...500001:16`); make `ApproveIssue` check `available_quantity` not gross.
2. **Inventory:** add production `MovementType` values (`production_issue`, `production_receipt`, `scrap`) or adopt `reference_type` convention (decision D-05); add the `ProductionOrder` case to BOTH `ReceiptReferenceType` and `IssueReferenceType` backed enums (md/11: `reference_type` is enum-typed — A27).
3. **Accounting:** seed WIP / FG / Scrap Expense detail accounts via `AutoAccountService::createChildAccount()`; seed `production.{wip,fg,scrap}_account_id` setting keys (resolved via `SettingsService`, fallback chains à la `PostLabInvoice` — same `production.*` namespace Phase 0 seeded).
4. **Production:** link `production_centers` → `cost_centers` (`cost_center_id` FK; `account_id` column stays for the WC-level GL account).

**Usable increment:** a factory can define multi-level versioned BOMs and work centers, create → release → issue materials (real stock deduction at FIFO/WAC + WIP journal) → record output → receive FG into a warehouse at actual rolled-up cost (real stock + GL) → close the order. **Manual production with correct inventory and ledger — the module stops lying to accounting.**

**Risks:** state-machine rename ripples into Angular order service/guards (full parity exists — must update both sides atomically); WIP cost roll-up correctness (cover with Pest money-assertions); reservation race conditions (DB transactions + `lockForUpdate`). **Sizing risk:** this phase sits at the aggressive end of L for a single dev (BOM versioning + multi-level + atomic BE+FE state-machine rename + two posting rails + reservations + FE dialogs); pre-agreed fallback if it slips = split into 1a (master data + state machine) and 1b (posting rails), with "factory operational" moving to end of 1b.

---

## 4. Phase 2 — Routing, Tools/Molds, Operation Confirmations & Conversion Costing (L)

**Scope — tables:**
- `mfg_routing_headers` {item/product_id, routing_type {Production, Repair, Inspection}, version, lot_size_from/to, status, effective_from/to} + `mfg_routing_operations` {operation_no (steps of 10), work_center_id, tool_id nullable, setup_time, run_time_per_unit, **cavity_count default 1**, cycle_time, queue_time, move_time, inspection_required, critical_operation, required_skill_code}. `bom_operations` becomes legacy/migrated into routing (one auto-generated routing per existing BOM).
- `mfg_tools` {code, product_id, cavity_count, setup_time, status {Available, Mounted, Maintenance, Retired}, life_cycles, cycles_used, purchase_cost, `cmms_asset_id` FK→`cmms_assets`, `fixed_asset_id` FK→Accounting `fixed_assets` (register exists — verified)}.
- **Extend `production_centers` (the mapping row-6 work-center columns, scheduled HERE — closes the hidden prerequisite):** `capacity_unit`, `daily_capacity`, `capacity_multiplier`, `efficiency_percent`, `utilization_percent`, `setup_cost_rate`, `labor_cost_rate`, `machine_cost_rate`, `labor_calc` {Hourly, PieceRate}, **`cost_driver`** {LaborHours, MachineHours, DirectMaterialCost, UnitsProduced} (the overhead-rate denominator — A23), `bottleneck_flag`, `plant_location` (`calendar_id` follows in Phase 4 with the calendar entity).
- `mfg_confirmations` (header: order, operation_no, confirmation_type {Partial, Final, Reversal}, operator_id, work_center_id, shift, timestamp) + `mfg_confirmation_yields` (grade_code, quantity, unit_sale_price) + `mfg_confirmation_details` (scrap_quantity, rework_quantity, setup/run actual, labor_hours, machine_hours, reason_code).
- Extend `production_order_operations`: `status` {Waiting, Ready, InProgress, Completed} enum, `tool_id`, `confirmed_by`, `confirmed_quantity`, actual timestamps.

**Scope — logic:** `time_per_piece = cycle_time / cavity_count`; effective WC capacity = daily × multiplier × efficiency% × utilization%; **Backflush** auto-issue at confirmation (`consumed = input × BOM qty`; shortage policy = block, configurable — D-08); labor cost branches on `labor_calc` {Hourly: hours×rate | PieceRate: qty×piece_rate}; conversion-cost postings **DR WIP / CR Labor Applied + Machine/MOH Applied** per confirmation (`entry_type='production_labor'/'production_overhead'`; OH amount = `production_centers.cost_driver` × `overhead_rate`); multi-grade GR (one line per grade, NRV value-based allocation: `grade.cost = total × (qty×price)/total_sale_value`); tool `cycles_used += qty/cavity_count` on confirmation; usage depreciation `purchase_cost / life_cycles / cavity_count` into overhead, posted via `CreateJournalEntry` against the verified `fixed_assets` register (needs the net-new `DepreciationMethod::UnitsOfProduction` case — prerequisite 5).

**Scope — screens:** routing editor, tool registry, confirmation entry (qty + grade distribution + scrap/reason), operation board (precursor of SFC terminals).

**Modules touched:** Production, Accounting, Inventory, HRM, CMMS.

**Prerequisite gap-fixes (Direction A):**
1. **Labor rate source = `production_centers.labor_cost_rate` (D-06, per mapping §4):** HRM `employees.hourly_rate`/`piece_rate` are **deferred post-v1** as an optional employee override — NO HRM schema change in this phase (today only `basic_salary`; `PayrollService.php:48-49` derives at payroll-time only).
2. **CMMS:** add `cmms_asset_meters` time-series table so mold shot-counts accumulate and feed `cmms_pm_schedules.meter_field/meter_threshold` (today free-string with no data source).
3. **Accounting:** seed Labor Applied / MOH Applied accounts + `production.labor_applied_account_id` etc.
4. **HRM/SFC identity:** confirm `employees.user_id` (unique) as the operator→employee→rate chain.
5. **Accounting (tool depreciation):** fixed-asset register EXISTS (`fixed_assets`, `FixedAssetService`, `DepreciationEntry` — verified); add the net-new `DepreciationMethod::UnitsOfProduction` enum case + meter-driven `DepreciationEntry` generation (A26).

**Usable increment:** operation-level execution with actual times, hourly OR piece-rate labor, machine/overhead absorption into WIP, multi-grade output with value-based cost split, tool life tracking with PM triggers. **Real conversion cost — unit cost per grade is finally true.**

**Risks:** NRV allocation and cavity math are the spec's most "exotic" logic — pure-function unit tests mandatory; routing-vs-BOM `operation_seq` linkage integrity; CMMS meter table is a cross-module change needing CMMS owner sign-off.

---

## 5. Phase 3 — Costing Engine: Standard Costs, 7 Variances, Period Settlement (M)

**Scope — tables:** `mfg_standard_costs` {product_id, std_material/labor/overhead/total_cost, last_updated, update_frequency}; `mfg_variance_records` {production_order_id, variance_type, amount, percentage, classification, owner, investigation_flag}; **`mfg_item_mrp_settings` extension table CREATED here** (pulled forward from Phase 4 — D-20) carrying the per-item `costing_method` enum {Standard, Actual, Average, FIFO} (mixed methods supported) + procurement/ownership columns — per mapping rows 1/10/23 `products` is NOT bloated and the free-text `products.cost_method` stays deprecated/ignored (NO products enum migration); planning columns of the same table are populated in Phase 4; `cost_centers.type` column {Production, Service, Auxiliary}.

**Scope — logic/actions:**
- `Actions/RollUpStandardCost` — BOM × material std + routing × WC rates (recursive).
- `Actions/ComputeOrderVariances` on Close: MPV, MUV, LRV, LEV, VOSV, VOEV, FOVV (LRV/LEV n/a under PieceRate — deviation shifts to machine/OH per spec); tolerance bands <2% Normal / 2–5% Monitor / >5% Investigate; owner routing via notifications (Purchasing/Production/Management/Sales roles).
- GR posting switches (for Standard-method items) to **DR FG @std + DR/CR Variance / CR WIP @actual**.
- Monthly job: applied-vs-actual overhead → over/under-applied to P&L; runs **before** `CloseFiscalPeriod` (which rejects unposted entries); hook `PeriodClosed` guard.
- Reuse `CostAllocationService` + `AllocationRule` for Service→Production cost-center distribution (Direct method only v1; Step-Down/Reciprocal deferred — D-09).
- Variance Pareto report + cost dashboards.

**Modules touched:** Production, Accounting.
**Prerequisite gap-fixes (Direction A):** `cost_centers.type` column; 7 variance accounts + over/under-applied OH account seeded; standard-cost revaluation policy for on-hand stock (D-10); **FOVV inputs (A25):** budgeted fixed-OH spend per cost center REUSES the existing Accounting `budgets`/`budget_lines` rail (`cost_center_id` + monthly `m1..m12` — verified in backend); budgeted production VOLUME is net-new — add `production_centers.budgeted_monthly_volume` + entry screen; with no approved budget for the period, FOVV auto-descopes and 6 variances are declared (D-22).
**Usable increment:** management gets the spec's "most valuable output" — per-order variance decomposition with owners and tolerance routing, plus standard costing for FG while raw materials stay Average/FIFO.
**Risks:** variance math must reconcile to the GL to the cent (property-based Pest tests: Σ variances = WIP residual); period-close ordering with Accounting team.

---

## 6. Phase 4 — Planning: MRP, Pegging, Purchase Requisitions, CRP, MPS-lite (L)

**Scope — tables:** `mfg_item_mrp_settings` planning columns ACTIVATED (table created in Phase 3 — D-20; AccBpExt pattern over Core `products`: mrp_type, procurement_type {Buy, Make}, material_ownership, lot_sizing_rule {Exact, Fixed, MinMax, EOQ, PeriodOrder}, lot/min/max, safety_stock, lead_time_days, reorder_point); `mfg_mrp_runs` + `mfg_mrp_planned_orders` (transient, regenerated per run; each carries `source_demand_id` for pegging) + `mfg_mrp_exceptions` (late/reschedule-in/out/cancel); `mfg_crp_loads` {resource_id, resource_type, period, required_load, available_capacity, utilization_pct, status OVERLOAD/OK/UNDERLOAD}; `mfg_peggings` {production_order_id, sales_order_id, allocated_quantity}; `mfg_delivery_schedules`; **`mfg_work_calendars` + `mfg_calendar_shifts`** (working days/shifts/holidays — the spec's unspecified calendar, built here because CRP needs it; add `production_centers.calendar_id`, completing the row-6 work-center EXTEND begun in Phase 2).
- MPS-lite: `mfg_mps_headers/lines` capturing demand = confirmed `sales_orders` (+ manual forecast entry screen as the interim forecast source — D-11).

**Scope — logic:** level-by-level netting `Net = MAX(0, Gross − (on_hand + open POs + open WOs − reserved) + safety_stock)`; per-line scrap inflation; lot-sizing rules; lead-time offset; recursive BOM explosion for Make items; **toll branch**: `material_ownership=Customer` → never raise a PO, alert on shortage; outputs → Planned Production Orders (creates `production_orders` in Planned) and Planned POs → **new `Modules\Purchases\Actions\CreatePurchaseRequest`** (extracted from controller-only logic) → existing `convertFromRequest` flow; CRP loads from routing times ÷ effective capacity per calendar (maintenance downtime subtracted via the read-only `CMMS\GetDowntimeWindows` Action); CTP-lite promised date written back to Sales (D-12/D-16).

**Scope — screens:** MRP run cockpit (run → planned orders → firm/convert), exception inbox, CRP load board (per WC/tool, OVERLOAD red), pegging view per sales order, item MRP settings tab on product.

**Modules touched:** Production, Purchases, Sales, Inventory, Core.
**Prerequisite gap-fixes (Direction A):** `Purchases\Actions\CreatePurchaseRequest` (no programmatic PR creation today); `sales_orders.promised_date` column (canonical single name — D-16) + `Sales\Actions\SetPromisedDate` — Sales has no ATP/CTP field; calendar entity (net-new, spec gap).
**Usable increment:** planner runs MRP from real demand+stock, gets purchase requests and planned production orders with exception messages, sees capacity overloads before releasing, and every production order is pegged to the sales orders it serves.
**Risks:** MRP correctness on deep BOMs (golden-file Pest fixtures: melamine 6000-dish case from the spec, incl. 1-cavity 148% overload → 4-cavity 33h resolution); run performance (queue the run, chunked); pegging re-allocation policy on re-run is a spec gap — v1 = delete-and-rebuild non-firmed pegs.

---

## 7. Phase 5 — Shop Floor Control: Terminals, Auto-Advance, Live Dashboard (M)

**Scope:** NO new business engine (~80% exists per spec 05) — reuses `production_order_operations` as the live surface.
- Auto-advance: on terminal **[Done]** → create Confirmation (Phase 2 engine, GL implied) → `OperationCompleted` event → listener `routing.get_next()` → next op `Ready` + notify its work center; last op → order `Completed` (GR-ready).
- Terminal UI: clone `features/pos/pos-layout` pattern — per-WC Ready queue sorted by priority, large touch targets, [Start]/[Done], qty + grade + scrap/reason prompt, strict sequence enforcement.
- Supervisor dashboard: live per-order current-op + progress %, Waiting-queue-per-resource bottleneck board.
- Transport: polling v1 (30s) or Laravel Reverb websockets (D-13); offline-tolerant queueing deferred.
- New permissions `production.shopfloor.{operate,supervise}`.

**Modules touched:** Production, HRM (operator identity), FE.
**Prerequisite gap-fixes (Direction A):** broadcast channel decision; order `priority` already exists on `production_orders` (reuse for queue sort).
**Usable increment:** paperless floor — workers Start/Done at terminals, costs become live (no end-of-day entry), supervisors see bottlenecks in real time, actual times accumulate for future OEE/routing refinement.
**Risks:** UX on real shop hardware; concurrency (two operators, one operation — `lockForUpdate` + idempotent confirmations); spec gaps (no pause/downtime state) — add `OnHold` to operation enum as a Moon extension.

---

## 8. Phase 6 — Toll Manufacturing, Batch Genealogy & Neighbor Deep-Hooks (L)

**Scope:**
- **Toll/consignment (per mapping row 17, D-15):** dedicated consignment warehouses flagged `warehouses.is_consignment` (held, never valued, never purchasable); issue/GR lines carry `ownership` {Own, Customer}; a full ownership dimension on balances/movements stays deferred. Release skips reservation-as-asset; Issue = tracking-only movement (no RM credit); GR to customer ownership; invoice = conversion fee only (**DR Customer / CR Toll-Service Revenue** via Sales invoice integration).
- **Batch/lot first-class (Inventory gap-fix):** `inventory_batches` master + batch-keyed balances/cost-layer dimension; genealogy link issued-input-lots → produced-FG-lot; FEFO pick suggestion on issue. (Today batch_number is free-text; Serial is first-class, batch is not.)
- **QMS hooks:** add `operation_no` to `qms_inspections`/`qms_inspection_plans` + real FKs to `production_orders`; in-process gates on `inspection_required` operations; GR `quality_status` resolved from inspection result; `Actions/RaiseNcrFromScrap` (reason-code threshold → `qms_non_conformances`).
- **CMMS hook:** downtime work orders subtract from CRP available capacity.
- **HRM hook:** piece-rate confirmed quantities pushed to payroll via NET-NEW `Modules\HRM\Actions\RecordPieceworkEntry` → HRM-owned `hrm_piecework_entries` (payroll reads its OWN table; HRM never reads `mfg_*`).

**Modules touched:** Production, Inventory, Sales, Accounting, QMS, CMMS, HRM.
**Usable increment:** the factory can take customer-material toll jobs with clean books, trace any FG batch back to raw lots (recall-ready), gate operations on QC, and pay piece-rate from confirmations.
**Risks:** batch-keyed inventory is the largest cross-module schema change in the program — isolate behind `StockService`, feature-flag per company; toll GL treatment needs accountant sign-off.

**Explicit future phases (interfaces reserved, NOT in this roadmap):** APS detailed scheduling/Gantt, OEE computation (actual times already captured Phase 5), PLM/ECO change orders, IoT machine data, Step-Down/Reciprocal allocation.

---

## 9. Data Migration & Seed Strategy (existing 7 Production tables)

1. **Keep all 7 tables; rename nothing.** Zero inbound FKs from other modules and zero production data (verified) → migration risk near-zero. New tables take `mfg_` prefix; existing names stay for FE/API stability.
2. **Consolidate defensively:** Phase 0 adds ONE new baseline-verification migration asserting the 7 tables' final shape; the 3 guarded legacy migrations stay untouched (history) but all future change is additive `ALTER`s — no more `force_create`/raw-SQL pattern.
3. **Status mapping migration** (Phase 1): data-update migration maps draft→planned, confirmed→released, in_progress→in_process, completed→completed, cancelled→cancelled; enum strings stay snake_case in DB. Angular models updated in the same release.
4. **Column activation:** `production_centers.account_id` (dead) gains real use as WC GL account; add `cost_center_id`; `source/target_warehouse_id` on orders become required-at-Release.
5. **`bom_operations` → routing migration** (Phase 2): generate one `mfg_routing_header` (version 1.0, Active) per BOM that has operations; copy operations with `cavity_count=1`; keep `bom_operations` read-only for one release, then drop.
6. **Seeders:** `ProductionDatabaseSeeder` (currently empty) gains: (a) `ManufacturingSettingsSeeder` — `SettingDefinition` rows for all `production.*_account_id` keys (the module's single settings namespace); (b) `ManufacturingAccountsSeeder` — WIP, FG, Labor Applied, MOH Applied, Scrap Expense, 7 variance accounts via `AutoAccountService::createChildAccount()`; (c) `ManufacturingDemoSeeder` — the melamine stress-test dataset (powder RM, 1-cavity + 4-cavity molds, press WC, A/B/C grades) used by both demos and Pest golden tests; (d) permission appends to Core `RolePermissionSeeder` per phase.

## 10. Testing Strategy (Pest, house pattern, 80%+ per phase gate)

| Phase | Suite | Key assertions |
|---|---|---|
| 0 | `tests/Unit/Enums`, `tests/Feature/Permissions` | enum guards; PermissionDependencyRegistry expand/presets; ApprovalModule case |
| 1 | `Feature/OrderLifecycle`, `Feature/MaterialIssue`, `Feature/GoodsReceipt`, `Unit/SnapshotImmutability` | Release reserves stock; Issue decrements stock + posts balanced DR WIP/CR Inventory; GR posts DR FG/CR WIP; snapshot immune to BOM edit after Release; cancel forbidden after InProcess; multi-company isolation (TenantAware) |
| 2 | `Unit/CavityMath`, `Unit/NrvAllocation`, `Feature/Confirmation`, `Feature/Backflush`, `Feature/ToolLife` | time_per_piece=cycle/cavity; Σ grade costs = total; PieceRate vs Hourly postings; backflush shortage blocks; cycles_used increments + PM trigger |
| 3 | `Unit/VarianceFormulas` (property-based: Σ7 variances = WIP residual), `Feature/PeriodSettlement` | each variance formula vs golden numbers; tolerance band routing; settlement refused in closed period |
| 4 | `Unit/MrpExplosion` (golden-file melamine fixture), `Unit/LotSizing`, `Feature/MrpToPurchaseRequest`, `Feature/CrpOverload` | multi-level netting w/ scrap; toll branch never raises PO; 148%→33h cavity scenario; pegging totals = order qty |
| 5 | `Feature/AutoAdvance`, `Feature/TerminalConcurrency` | Done sets next op Ready; last op completes order; double-confirm idempotent |
| 6 | `Feature/TollFlow`, `Feature/BatchGenealogy`, `Feature/QmsGates` | customer stock never valued; FG lot traces to RM lots; inspection_required blocks advance |

Every GL-touching test asserts journal balance AND `source_type/source_id` linkage. CI gate: suite green + coverage ≥80% before phase exit (TDD per house rules).

## 11. Decisions Needed from Management

| # | Decision (القرار المطلوب) | Options (الخيارات) | Recommendation (التوصية) |
|---|---|---|---|
| D-01 | Module identity | New `Modules/Manufacturing` vs extend `Modules/Production` | **Extend Production in place** (zero data/FKs, FE+permissions exist) |
| D-02 | Order state machine | Keep current 5 statuses vs adopt spec machine (Planned→Released→InProcess→Completed→Closed +Cancelled) | **Adopt spec machine** with mapping migration |
| D-03 | Cost Center system-of-record | Mfg-owned vs Accounting-owned | **Accounting `cost_centers`** + add `type` column; WCs reference it |
| D-04 | Costing v1 method | Standard from day one vs Actual (FIFO/WAC) first, Standard in Phase 3 | **Actual first** — rails exist; standard needs the variance engine |
| D-05 | Inventory movement typing | New `MovementType` values vs `reference_type` convention only | **New enum values** (cleaner reporting) — Inventory owner sign-off |
| D-06 | Labor rate source | `employees.hourly_rate/piece_rate` vs WC rate vs HR service | **WC rate v1 (`production_centers.labor_cost_rate`); HRM stored rates + `GetLaborRate` deferred post-v1 as optional override** (matches mapping §4) |
| D-07 | Toll manufacturing timing | Phase 6 vs pull forward | **Phase 6** unless a toll customer is signed (then swap with Phase 4) |
| D-08 | Backflush shortage policy | Block confirmation vs allow negative vs partial | **Block (default), per-warehouse `allow_negative_stock` respected** |
| D-09 | Service-cost allocation methods | Direct only vs build Step-Down/Reciprocal | **Direct v1** (exists via `CostAllocationService`) |
| D-10 | Std-cost revaluation of on-hand | Revalue + revaluation JE vs new-receipts-only | **Revalue with JE** at standard activation (accountant sign-off) |
| D-11 | Forecast source for MPS | Manual entry screen vs import vs defer MPS entirely | **Manual entry + confirmed SO consumption (MPS-lite)** |
| D-12 | Sales promised-date writeback | Auto-write CTP date vs advisory only | **Advisory field v1** (`sales_orders.promised_date` — name per D-16), auto later |
| D-13 | SFC real-time transport | Polling vs Laravel Reverb websockets | **Polling v1, Reverb in Phase 5.1** if floor latency hurts |
| D-14 | Batch-keyed inventory scope | Full batch dimension on balances/layers vs genealogy-table-only | **Full batch dimension, feature-flagged per company** |
| D-15 | Consignment/toll ownership model | `warehouses.is_consignment` flag vs `ownership` dimension on balances/movements | **`is_consignment` warehouses v1** (mapping row 17); ownership dimension deferred |
| D-16 | Sales promise column name | `promised_date` vs `promised_delivery_date` vs `production_promised_date` | **`sales_orders.promised_date`** (mapping row 27) — single name everywhere, Action `SetPromisedDate` |
| D-17 | Canonical event/Action names | mapping set vs contracts/roadmap variants | **Mapping set ratified:** `ProductionOrderReleased, MaterialIssued, OperationCompleted, ConfirmationPosted, GoodsReceiptPosted, ProductionOrderClosed, MrpRunCompleted`; settlement Action = `CloseProductionOrder` |
| D-18 | BOM substitutes structure | `mfg_bom_substitutes` child table vs `substitute_items` JSON | **Child table** (priority + ratio — mapping row 3) |
| D-19 | Issue/GR document ownership | mfg doc tables wrapping Inventory rails vs Inventory documents directly | **mfg doc tables — definitive** (Inventory docs lack issue_type/issue_level/operation_no/grade) |
| D-20 | Per-item `costing_method` home & phase | `mfg_item_mrp_settings` vs `products.costing_method` enum | **`mfg_item_mrp_settings`, created in Phase 3** (no `products` bloat; planning columns filled Phase 4) |
| D-21 | GR `quality_status` before QMS gates | Auto-Released vs OnHold-by-default | **Auto-Released for Phases 1–5**; QMS-resolved from Phase 6 |
| D-22 | FOVV v1 policy | Compute from existing `budget_lines` + new budgeted volume vs descope | **Compute when an approved budget exists** (`budget_lines` REUSE + `production_centers.budgeted_monthly_volume`); otherwise auto-descope to 6 declared variances |

---

**Bottom line:** 6 phases (0:S, 1:L, 2:L, 3:M, 4:L, 5:M, 6:L). The factory is genuinely operational (real stock + real GL on manual orders) at the END OF PHASE 1; every later phase adds a self-contained capability (conversion costing → variances → MRP/CRP → live floor → toll/traceability) without rework, because integrations are built in from the start rather than bolted on at step 12.
