# 01 — Master Data Layer

> Foundation layer. Defines what a product is and how it's made. If master data is wrong, everything downstream is wrong. Inherits all rules from `00_architecture_and_principles.md`.

---

## 1.1 Purpose

Master Data holds the stable definitions that all transactions reference: the product recipe (BOM), the manufacturing path (Routing), the production resources (Work Center / Tool), and item settings. It is created once and changed via controlled change orders (ECO).

---

## 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
```
- bom_id            (PK)
- parent_item_id    (FK → Item)
- bom_type          ∈ { 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            ∈ { Draft, Active, Inactive, Obsolete }
- effective_from / effective_to
- created_by / approved_by
```

### BOM_Line
```
- bom_id            (FK → BOM_Header)
- line_no
- component_id      (FK → Item)
- quantity          (per one base_quantity of parent)
- uom
- scrap_percentage  (expected waste; MRP multiplies qty by (1+scrap%))
- issue_type        ∈ { Manual, Backflush, AutoIssue }
- issue_level       ∈ { PerOrder, PerShift, PerOperation }
- operation_seq     (which routing operation consumes it — links BOM↔Routing)
- substitute_items  (alternatives if primary unavailable)
```

### 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 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 simply omit and account in overhead pool.

### Melamine test case
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, and how long. Enables scheduling and time calculation.

### Routing_Header
```
- routing_id        (PK)
- item_id           (FK → Item)
- routing_type      ∈ { Production, Repair, Inspection }
- version
- lot_size_from / lot_size_to   (a product may have different routings per batch size)
- status            ∈ { Draft, Active, Inactive }
- effective_from / effective_to
- total_lead_time   (computed)
```

### Routing_Operation
```
- operation_no      (sequence: 10, 20, 30 — multiples of 10 to allow inserts)
- description
- 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
- 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)
- inspection_required (bool)
- critical_operation  (bool — cannot be skipped)
- required_skill_code
```

### Generic-design notes
- **Operation numbering in 10s** is a convention so an operation can be inserted (15, 25) without renumbering.
- **cavity_count is the key generalization for molding/multi-output processes.** The real per-piece time is:
  ```
  time_per_piece = cycle_time / cavity_count
  ```
  Default cavity_count = 1 makes this collapse 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, see §1.5).

### Melamine test case
```
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, see 1.2).
```

---

## 1.4 Entity: Work Center

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

```
- wc_id             (PK)
- description
- category          ∈ { Machine, Labor, SetupGroup }
- plant_location
- cost_center_id    (FK → Cost_Center)
- capacity_unit     ∈ { Hours, Pieces, Kg }
- daily_capacity    (per single unit)
- capacity_multiplier (number of identical machines/workers in this WC)
- efficiency_percent
- utilization_percent
- calendar_id       (shifts & holidays)
- setup_cost_rate
- labor_cost_rate
- machine_cost_rate
- overhead_rate
- labor_calc        ∈ { Hourly, PieceRate }   ← see §1.6
- bottleneck_flag
```

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

### 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 doing identical work at identical cost = one WC with a multiplier. Different work or different cost = separate WCs.

### Melamine test case
```
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 type `Tool`.

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

### Generic-design notes
- **Resource abstraction:** Machine, Tool, and 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 test case
```
50 molds total, but only ~4 mounted (running) at any time → only those 4 depreciate.
Mold cost from 40k EGP up. Setup (mounting) is heavy → drives large lot sizing.
Some molds multi-cavity (e.g. tea-set tray mold yields 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).
