# 01 — Master Data Layer (Technical Digest)

> Source: `files/manufacturing_spec/markdown/01_master_data.md` (+ governing principles from `00_architecture_and_principles.md`, `README.md`). This digest is lossless: every entity, field, enum, rule and formula from the source file is reproduced here. An implementer must NOT need to reopen the original.

---

## 0. Governing principles inherited (from `00`)

This layer inherits all cross-cutting rules. The ones that bind Master Data directly:

1. **Genericity mandate (prime directive).** Never hard-code a single factory's behaviour. Every factory-specific behaviour = a configuration field/enum + branching logic that **defaults to the common behaviour**. The Melamine tableware factory is a *stress-test example only*, NOT something to hard-code.
2. **Frozen Snapshots.** When a Production Order is released, it snapshots the *active* BOM and Routing. Later master-data edits MUST NOT affect already-released orders (protects historical cost + audit). → Master Data is the *snapshot source*.
3. **Status as State Machine.** Every entity with a `status` has explicit states, transitions, preconditions, side-effects. No ad-hoc status changes.
4. **Multi-level everything.** BOM must support arbitrary nesting (finished good → sub-assembly → component → raw). Use recursion.
5. **Configuration over code.** Every variant point is a field.
6. **Resource abstraction.** Capacity is consumed by `Resource` objects with `resource_type ∈ {Machine, Tool, Labor}`. An operation's effective capacity is constrained by the **minimum** available across all required resources.

### Master enums relevant to this layer (defined in `00`)
```
production_type     ∈ { MakeToStock, MakeToOrder, AssembleToOrder, TollManufacturing }
material_ownership  ∈ { Own, Customer }
procurement_type    ∈ { Buy, Make }
costing_method      ∈ { Standard, Actual, Average, FIFO }
labor_calc          ∈ { Hourly, PieceRate }
issue_type          ∈ { Manual, Backflush, AutoIssue }
issue_level         ∈ { PerOrder, PerShift, PerOperation }
resource_type       ∈ { Machine, Tool, Labor }
```

---

## 1.1 Purpose of the Master Data layer

Master Data holds the **stable definitions** that all transactions reference:
- the product recipe — **BOM**
- the manufacturing path — **Routing**
- the production resources — **Work Center / Tool**
- item settings.

Created once; changed only via **controlled change orders (ECO)**. "If master data is wrong, everything downstream is wrong."

---

## 1.2 Entity: BOM (Bill of Materials)

**Purpose:** the product's manufacturing identity — what components and quantities go into one unit. Must support **multi-level nesting**.

### BOM_Header — fields
| Field | Notes / rule |
|---|---|
| `bom_id` | PK |
| `parent_item_id` | FK → Item |
| `bom_type` | enum `{ Production, Engineering, Phantom }` |
| `version` | multiple versions kept for history |
| `base_quantity` | usually 1 — the unit the BOM is defined per |
| `uom` | unit of measure of the parent |
| `status` | enum `{ Draft, Active, Inactive, Obsolete }` |
| `effective_from` / `effective_to` | validity window |
| `created_by` / `approved_by` | approval audit |

### BOM_Line — fields
| Field | Notes / rule |
|---|---|
| `bom_id` | FK → BOM_Header |
| `line_no` | line sequence |
| `component_id` | FK → Item |
| `quantity` | per one `base_quantity` of parent |
| `uom` | line unit |
| `scrap_percentage` | expected waste; **MRP multiplies qty by `(1 + scrap%)`** |
| `issue_type` | enum `{ Manual, Backflush, AutoIssue }` |
| `issue_level` | enum `{ PerOrder, PerShift, PerOperation }` |
| `operation_seq` | which routing operation consumes this line — **the link BOM ↔ Routing** |
| `substitute_items` | alternatives if primary unavailable |

### Rules / generic-design notes
- **Base quantity defaults to 1** (per piece) for flexibility. Shared packaging (e.g. one carton per set) is handled **at the parent assembly level**, not forced onto each piece.
- **Multi-level:** a BOM_Line's `component_id` may itself have a BOM (sub-assembly / semi-finished). **MRP explodes recursively.**
- **Low-value consumables** (negligible cost, hard to meter per unit) should NOT be BOM lines — treat as **overhead**. Provide a flag, or omit and account in an overhead pool.
- `bom_type = Phantom` is an enum value (a phantom/transient assembly that is exploded through, not stocked). The spec names it but does not further detail phantom explosion behaviour here.

### Melamine stress-test (example, do NOT hard-code)
4-level BOM proven:
```
Set (50 pcs)                          ← level 0, type=Production
 ├─ Large dish ×6                      ← level 1, procurement=Make
 │   └─ Melamine powder 0.15kg, scrap 3%   ← level 3 raw
 │   └─ Ready decor cutout ×1          ← level 2, semi-finished
 │        └─ Decor sheet, scrap 15%    ← level 3 raw
 ├─ ... other pieces
 └─ Printed carton ×1                  ← packaging at SET level (not piece)
Glaze: NOT a BOM line → overhead consumable (sprayed, negligible).
```

---

## 1.3 Entity: Routing

**Purpose:** the manufacturing path — the sequence of operations, who does each, how long. Enables scheduling and time calculation.

### Routing_Header — fields
| Field | Notes / rule |
|---|---|
| `routing_id` | PK |
| `item_id` | FK → Item |
| `routing_type` | enum `{ Production, Repair, Inspection }` |
| `version` | versioned |
| `lot_size_from` / `lot_size_to` | a product may have **different routings per batch size** |
| `status` | enum `{ Draft, Active, Inactive }` |
| `effective_from` / `effective_to` | validity window |
| `total_lead_time` | **computed** |

### Routing_Operation — fields
| Field | Notes / rule |
|---|---|
| `operation_no` | sequence 10, 20, 30 — **multiples of 10 to allow inserts** (15, 25) without renumbering |
| `description` | text |
| `work_center_id` | FK → Work_Center |
| `tool_id` | FK → Tool, **nullable** — the mold/die required |
| `setup_time` | fixed, independent of batch size |
| `run_time_per_unit` | per-unit run time |
| `cavity_count` | units produced per cycle — **DEFAULT 1**; >1 for multi-cavity |
| `cycle_time` | time for one cycle/press — use with `cavity_count` |
| `queue_time` | wait before start — adds to lead time, **not cost** |
| `move_time` | transfer to next op — lead time, **not cost** |
| `inspection_required` | bool |
| `critical_operation` | bool — **cannot be skipped** |
| `required_skill_code` | skill needed |

### Formulas / rules
- **Per-piece time for molding / multi-output:**
  ```
  time_per_piece = cycle_time / cavity_count
  ```
  Default `cavity_count = 1` collapses this to normal behaviour for discrete manufacturers.
- `queue_time` / `move_time` affect lead time but are usually **NOT costed**.
- An operation may reference a **Tool** in addition to a Work Center — **both must be available to run** (resource intersection, §1.5).
- `cavity_count` is the **key generalization** for molding / multi-output processes.

### Melamine stress-test
```
Plain dish routing:
  Op 10 Press (WC-PRESS, MOLD-LG, cycle 70s, cavity 1)  → 70s/piece
  Op 15 Cooling (queue, no cost)
  Op 20 Deflash (WC-DEFLASH)
  Op 30 Sanding (WC-SAND)
  Op 40 Grading (WC-QC) ← produces grade distribution
Multi-cavity proof: cup mold cavity=4, cycle 60s → 15s/piece (4× throughput, same machine).
Printed dish: SAME routing (decor prepared separately as semi-finished).
```

---

## 1.4 Entity: Work Center

**Purpose:** where work happens. **The interface between production and accounting — all costs flow through it.**

### Fields
| Field | Notes / rule |
|---|---|
| `wc_id` | PK |
| `description` | text |
| `category` | enum `{ Machine, Labor, SetupGroup }` |
| `plant_location` | location |
| `cost_center_id` | FK → Cost_Center |
| `capacity_unit` | enum `{ Hours, Pieces, Kg }` |
| `daily_capacity` | per single unit |
| `capacity_multiplier` | **number of identical machines/workers** in this WC (capacity pool) |
| `efficiency_percent` | efficiency factor |
| `utilization_percent` | utilization factor |
| `calendar_id` | shifts & holidays |
| `setup_cost_rate` | rate |
| `labor_cost_rate` | rate |
| `machine_cost_rate` | rate |
| `overhead_rate` | rate |
| `labor_calc` | enum `{ Hourly, PieceRate }` — see §1.6 |
| `bottleneck_flag` | bool |

### Effective-capacity formula
```
effective_capacity = daily_capacity × capacity_multiplier × efficiency% × utilization%
```
**CRP uses effective (not gross) capacity.**

### Rules / generic-design notes
- `capacity_multiplier` lets **N identical machines be ONE work center** (a "pool of capacity"). Use separate WCs only when operations/costs genuinely differ.
- **Decision rule (what defines a separate WC):** the *type of operation and its cost*, NOT the count of machines. Identical machines, identical work, identical cost = one WC with a multiplier. Different work or different cost = separate WCs.

### Melamine stress-test
```
WC-PRESS: 2 groups × 2 presses each. Modeled as multiplier=2 per group, 3 shifts, eff 85%. Bottleneck=true.
Machine_cost_rate = (depreciation + maintenance + cooling water) / press-hours.
Labor: PieceRate (operator paid per piece, NOT hourly).
Note: operator runs 2 presses alternately → worker may be the real constraint if manual handling time exceeds cycle time (machine-coupling).
```

---

## 1.5 Entity: Tool / Mold (Resource generalization)

**Purpose:** in molding/tooling industries the **tool is the capacity constraint, not the machine.** Generalized as a `Resource` of `resource_type = Tool`.

### Fields
| Field | Notes / rule |
|---|---|
| `tool_id` | PK |
| `description` | text |
| `product_id` | which product it makes — **or many** |
| `cavity_count` | pieces per cycle |
| `setup_time` | mount/dismount — **often large** |
| `status` | enum `{ Available, Mounted, Maintenance, Retired }` |
| `life_cycles` | expected cycles before replacement (usage depreciation) |
| `cycles_used` | running counter |
| `purchase_cost` | for depreciation per cycle |

### Formulas / rules
- **Resource abstraction:** Machine, Tool, Labor are all `Resource` with `resource_type`. An operation may require **several resources simultaneously**.
  ```
  available_start = MAX(earliest free time across all required resources)
  ```
- **Usage depreciation:** tools wear by **cycles, not calendar**. Only mounted/running tools accrue wear.
  ```
  depreciation_per_piece = purchase_cost / life_cycles / cavity_count
  ```
- A factory with no tooling simply has **no Tool resources** — the abstraction collapses gracefully.

### Melamine stress-test
```
50 molds total, but only ~4 mounted (running) at a time → only those 4 depreciate.
Mold cost from 40k EGP up. Setup (mounting) heavy → drives large lot sizing.
Some molds multi-cavity (e.g. tea-set tray mold = 4 small pieces/cycle).
```

---

## 1.6 Generic point: Labor calculation mode

`labor_calc` on the **Work Center (or item)** decides how direct labor cost is computed:
```
if labor_calc == Hourly:    cost = labor_hours × labor_rate
if labor_calc == PieceRate: cost = quantity × piece_rate   (piece_rate may vary by product/size)
```
**Consequence:** under `PieceRate`, a slow worker costs *himself* (earns less), not the factory — so there is **no traditional Labor Efficiency Variance**; the deviation shifts to machine/overhead. **The system must handle both modes.**

---

## 1.7 Dependencies & Integrations (this layer)

- **Depends on:** Item Master (host ERP), Calendar, Skills/Tools master, Approval workflow (ECO).
- **Feeds:** MRP (BOM explosion), Production Order (snapshot source), Costing (standard cost build-up), CRP (capacity definition).

---

## State machines (status enums as transitions)

- **BOM_Header.status:** `Draft → Active → Inactive → Obsolete` (`{ Draft, Active, Inactive, Obsolete }`).
- **Routing_Header.status:** `Draft → Active → Inactive` (`{ Draft, Active, Inactive }`).
- **Tool.status:** `Available → Mounted → Maintenance → Retired`; Maintenance/Available cycling possible (`{ Available, Mounted, Maintenance, Retired }`). Only `Mounted` accrues cycle wear.

---

## Config / variant points introduced by this layer

| Config field | Where | Purpose / default |
|---|---|---|
| `bom_type` | BOM_Header | Production / Engineering / Phantom |
| `base_quantity` | BOM_Header | default 1 (per piece) |
| `scrap_percentage` | BOM_Line | MRP multiplier `(1+scrap%)` |
| `issue_type` | BOM_Line | Manual / Backflush / AutoIssue |
| `issue_level` | BOM_Line | PerOrder / PerShift / PerOperation |
| `substitute_items` | BOM_Line | alternates |
| `routing_type` | Routing_Header | Production / Repair / Inspection |
| `lot_size_from/to` | Routing_Header | routing-per-batch-size |
| `cavity_count` | Routing_Operation & Tool | multi-output; default 1 |
| `capacity_multiplier` | Work Center | machine pool; default 1 |
| `category` | Work Center | Machine / Labor / SetupGroup |
| `capacity_unit` | Work Center | Hours / Pieces / Kg |
| `labor_calc` | Work Center / item | Hourly / PieceRate |
| `bottleneck_flag` | Work Center | constraint marker |
| `resource_type` | Resource (Tool/Machine/Labor) | abstraction switch |
| Low-value-consumable flag | BOM line / item | omit from BOM → overhead |

---

## Explicit "expects from the host ERP" list

The Master Data layer assumes these already exist in Moon ERP and integrates (does not rebuild):

1. **Item / Product Master** — `parent_item_id`, `component_id`, `item_id` all FK → host Item. Must carry `procurement_type {Buy, Make}` and a UoM.
2. **UoM master + conversions** — every BOM line/header and WC capacity carries a `uom`/`capacity_unit`; conversions between them are assumed available.
3. **Cost Center master** — `Work_Center.cost_center_id` FK; "all costs flow through the WC into accounting."
4. **Calendar master** — `Work_Center.calendar_id` (shifts & holidays) feeds effective capacity / lead-time.
5. **Skills master** — `Routing_Operation.required_skill_code` references an HR/skills catalog.
6. **Approval workflow (ECO / change orders)** — master data is "changed via controlled change orders"; `approved_by` on BOM_Header.
7. **GL / Cost accounting** — WC rates (`setup/labor/machine/overhead`) and tool depreciation feed standard cost build-up and GL.
8. **User master** — `created_by` / `approved_by`.
9. **Downstream module hooks** — MRP (recursive BOM explosion), Production Order (frozen snapshot of active BOM+Routing), CRP (effective capacity), Costing (standard cost build-up).
</content>
</invoke>
