> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quinnsambal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bom

<style>
  {`
    [class*="max-w-none"][class*="table"] {
      display: block !important;
      overflow-x: auto !important;
      max-width: 100% !important;
      width: 100% !important;
      flex-grow: 0 !important;
    }
    [class*="max-w-none"][class*="table"] > table {
      width: 100% !important;
      max-width: 100% !important;
      table-layout: fixed !important;
    }
    .mermaid {
      max-width: 100% !important;
      overflow-x: auto !important;
    }
    article svg[role="img"] {
      max-width: 100% !important;
      height: auto !important;
    }
    article img, .prose img {
      max-width: 100% !important;
      height: auto !important;
    }
    img[src*="LOGO"], img[src*="logo"] {
      max-width: 120px !important;
      max-height: 40px !important;
      width: auto !important;
      height: auto !important;
      object-fit: contain !important;
    }
    `}
</style>

***

title: "Bill of Materials (BOM)"
description: "Resep produksi lengkap dengan komponen, quantity, cost per unit, version control, dan auto-calculation untuk production orders."
----------------------------------------------------------------------------------------------------------------------------------------------

# Bill of Materials (BOM)

<img src="https://mintcdn.com/quinnofspicy/ny1xnfpEa_OBdv6T/docs/mintlify/screenshots/manufacturing/manufacturing.png?fit=max&auto=format&n=ny1xnfpEa_OBdv6T&q=85&s=81c758e5ea3e7d551211fe84baad045b" alt="Bill of Materials" width="1920" height="1080" data-path="docs/mintlify/screenshots/manufacturing/manufacturing.png" />

Bill of Materials (BOM) adalah resep digital yang mendefinisikan secara persis bahan baku apa saja yang dibutuhkan, berapa quantity-nya, dan berapa biayanya untuk memproduksi satu unit produk jadi. BOM menjadi fondasi dari production planning, cost calculation, dan raw material consumption. Setiap perubahan resep tercatat dalam version control sehingga kamu bisa melacak kapan dan mengapa BOM berubah.

## Arsitektur BOM

```mermaid theme={null}
graph TB
    subgraph "Definisi BOM"
        P[Produk Jadi<br/>Ayam Suwir Petir 150g]
        C1[Cabai Merah<br/>50g @ Rp50/g]
        C2[Bawang Merah<br/>20g @ Rp40/g]
        C3[Bawang Putih<br/>10g @ Rp35/g]
        C4[Ayam Suwir<br/>150g @ Rp80/g]
        C5[Minyak Goreng<br/>30ml @ Rp20/ml]
        C6[Kemasan & Label<br/>1 set @ Rp700]
    end

    subgraph "Kalkulasi"
        TC[Total Cost per Unit<br/>Σ (qty x harga)]
        VC[Version Control<br/>Track Perubahan]
    end

    subgraph "Penggunaan"
        PO[Production Order<br/>Auto-calculate kebutuhan]
        RM[Raw Material Check<br/>Cek ketersediaan]
        COST[Cost Tracking<br/>HPP per unit]
    end

    P --> C1 & C2 & C3 & C4 & C5 & C6
    C1 & C2 & C3 & C4 & C5 & C6 --> TC
    TC --> PO & COST
    VC --> TC
    PO --> RM
```

## Entity Relationship Diagram

Diagram berikut menunjukkan relasi antar entitas inti dalam modul manufacturing SNISHOP ERP. BOM menjadi pusat yang menghubungkan produk jadi, bahan baku, production order, hingga lot batch dan pergerakan stok.

```mermaid theme={null}
erDiagram
    CompanyPOSProduct ||--o{ BillOfMaterials : "memiliki resep (product_id)"
    CompanyPOSProduct ||--o{ RawMaterial : "sumber identitas bahan"
    CompanyPOSProduct ||--o{ ProductionOrder : "output produksi"
    CompanyPOSProduct ||--o{ Inventory : "stok per lokasi"
    CompanyPOSProduct ||--o{ LotBatch : "lot produk"

    BillOfMaterials {
        string id PK
        string company_id FK
        string product_id FK
        string product_sku
        string product_type
        string product_name
        string category_key
        string variant_key
        number size_grams
        string bom_version
        array raw_materials
        array packaging_materials
        number total_material_cost
        boolean is_active
        date effective_date
        string output_type
        boolean is_locked
        string parent_bom_id
        number yield_expected_percentage
        number base_quantity
        string base_unit
        string status
        string version
        object qc_sop_parameters
        string notes
    }

    RawMaterial {
        string id PK
        string company_id FK
        string material_code
        string material_name
        string product_id FK
        string product_type
        string category
        string unit_of_measure
        number current_stock
        number min_stock
        number max_stock
        number reorder_point
        number unit_cost
        string supplier_id FK
        number lead_time_days
        number shelf_life_days
        boolean expiry_required
        string storage_location
    }

    ProductionOrder {
        string id PK
        string company_id FK
        string po_number
        string production_batch_id
        string bom_id FK
        string product_id FK
        number quantity_to_produce
        number quantity_produced
        number quantity_defected
        string priority
        string status
        string production_tier
        string wip_status
        array raw_materials_used
        array materials_actual
        array returned_materials
        number total_material_cost
        number direct_labor_cost
        number overhead_cost
        number unit_hpp
        string cost_status
        string qc_result
        boolean haccp_verified
        string output_lot_id FK
        array input_lot_ids
    }

    LotBatch {
        string id PK
        string lot_number
        string product_id FK
        string production_order_id FK
        number quantity
        number quantity_remaining
        string status
        string quality_status
        string item_type
        array input_lots
        number cost_per_unit
    }

    Inventory {
        string id PK
        string product_id FK
        string location_id FK
        number quantity
        number reserved_quantity
        number available_quantity
        number blocked_quantity
        number unit_cost
        string stock_status
    }

    StockMovement {
        string id PK
        string inventory_id FK
        string movement_type
        number quantity
        number stock_before
        number stock_after
        string reference_type
        string lot_id FK
        string reference_id
    }

    HACCPVerification {
        string id PK
        string production_order_id FK
        string product_id FK
        number temperature_c
        array checklist
        string qc_result
        string status
    }

    QualityCheck {
        string id PK
        string raw_material_id FK
        string purchase_order_id FK
        array checklist
        string overall_status
        number approved_quantity
        number rejected_quantity
    }

    HPPCalculation {
        string id PK
        string product_id FK
        string production_order_id FK
        number total_production_cost
        number hpp_per_unit
        string cost_status
    }
```

### Ringkasan Relasi Utama

| Relasi | Tipe | Keterangan |
| - | - | - |
| `CompanyPOSProduct` → `BillOfMaterials` | One-to-Many | Satu produk bisa punya banyak versi BOM, tapi hanya satu yang `active` |
| `BillOfMaterials` → `RawMaterial` | Many-to-Many | BOM merujuk banyak bahan baku via array `raw_materials[]`; satu bahan bisa dipakai di banyak BOM |
| `BillOfMaterials` → `ProductionOrder` | One-to-Many | Satu BOM bisa digunakan oleh banyak production order; BOM yang sudah dipakai akan `is_locked = true` |
| `ProductionOrder` → `LotBatch` | One-to-Many | Satu production order menghasilkan satu output lot (`output_lot_id`) dan mengonsumsi banyak input lot (`input_lot_ids[]`) |
| `ProductionOrder` → `HACCPVerification` | One-to-One | Setiap production order memiliki satu verifikasi HACCP |
| `ProductionOrder` → `HPPCalculation` | One-to-One | Setiap production order menghasilkan satu kalkulasi HPP |
| `LotBatch` → `Inventory` | One-to-Many | Satu lot tersimpan di satu atau lebih lokasi inventory |
| `Inventory` → `StockMovement` | One-to-Many | Setiap perubahan stok tercatat sebagai movement |
| `QualityCheck` → `RawMaterial` | Many-to-One | QC memeriksa bahan baku yang datang dari supplier |

## Complete Entity Schema — BillOfMaterials

Tabel berikut mendokumentasikan **setiap field** pada entitas `BillOfMaterials` beserta tipe data, kewajiban, nilai default, dan deskripsi lengkap.

### Field Identitas & Produk Output

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `company_id` | `string` | **Ya** | - | ID perusahaan (multi-tenant). Setiap BOM terikat ke satu perusahaan. |
| `product_id` | `string` | **Ya** | - | ID produk jadi dari `CompanyPOSProduct` yang menjadi output BOM. |
| `product_sku` | `string` | Tidak | - | SKU master output, disnapshot dari produk pada saat BOM dibuat. |
| `product_type` | `string` | Tidak | - | Jenis produk output dari master (`finished_good`, `semi_finished`, dll). |
| `product_name` | `string` | Tidak | - | Nama produk output (snapshot). |
| `category_key` | `string` | Tidak | - | Kategori output snapshot: `kecil`, `besar`, `pouch`, `bundle`. |
| `category_name` | `string` | Tidak | - | Label kategori canonical. |
| `variant_key` | `string` | Tidak | - | Varian output snapshot: `original`, `extra_spicy`, `bundle`. |
| `variant_name` | `string` | Tidak | - | Label varian canonical. |
| `variant_label` | `string` | Tidak | - | Gabungan kategori dan varian untuk referensi POS/inventory/manufacturing. |
| `size_grams` | `number` | Tidak | - | Ukuran produk output dalam gram. |
| `base_product_key` | `string` | Tidak | - | Kunci produk utama sebelum kategori/varian. |

### Field Resep & Komponen

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `raw_materials` | `array<object>` | **Ya** | - | Daftar bahan baku yang dibutuhkan. Setiap item merujuk ke `RawMaterial` atau `CompanyPOSProduct`. |
| `raw_materials[].material_id` | `string` | - | - | ID bahan baku dari entitas `RawMaterial`. |
| `raw_materials[].product_id` | `string` | - | - | ID `CompanyPOSProduct` jika input berasal dari master produk. |
| `raw_materials[].product_type` | `string` | - | - | Tipe produk canonical bahan. |
| `raw_materials[].product_sku` | `string` | - | - | SKU bahan baku. |
| `raw_materials[].category_key` | `string` | - | - | Kategori canonical bahan. |
| `raw_materials[].category_name` | `string` | - | - | Label kategori bahan. |
| `raw_materials[].variant_key` | `string` | - | - | Varian canonical bahan. |
| `raw_materials[].variant_name` | `string` | - | - | Label varian bahan. |
| `raw_materials[].variant_label` | `string` | - | - | Label gabungan bahan. |
| `raw_materials[].size_grams` | `number` | - | - | Ukuran bahan dalam gram. |
| `raw_materials[].base_product_key` | `string` | - | - | Kunci produk utama bahan. |
| `raw_materials[].material_name` | `string` | - | - | Nama bahan baku (snapshot). |
| `raw_materials[].quantity_per_unit` | `number` | - | - | Jumlah bahan per 1 unit produk jadi. |
| `raw_materials[].unit` | `string` | - | - | Satuan bahan (gram, kg, ml, liter, pcs). |
| `raw_materials[].unit_cost` | `number` | - | - | Harga per satuan bahan pada saat BOM dibuat/diupdate. |
| `raw_materials[].total_cost` | `number` | - | - | `quantity_per_unit` x `unit_cost`. |
| `packaging_materials` | `array<object>` | Tidak | - | Daftar kemasan dan bahan pendukung yang dibutuhkan (MFG-02). |
| `packaging_materials[].material_id` | `string` | - | - | ID bahan kemasan. |
| `packaging_materials[].material_name` | `string` | - | - | Nama bahan kemasan (botol, segel, label, dll). |
| `packaging_materials[].quantity_per_unit` | `number` | - | - | Jumlah kemasan per unit produk jadi. |
| `packaging_materials[].unit` | `string` | - | - | Satuan kemasan (pcs, ml, lembar). |
| `packaging_materials[].unit_cost` | `number` | - | - | Harga per satuan kemasan. |
| `total_material_cost` | `number` | Tidak | - | Total biaya seluruh bahan baku per unit produk jadi. |

### Field Versi & Siklus Hidup

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `bom_version` | `string` | Tidak | - | Versi BOM untuk tracking perubahan internal. |
| `version` | `string` | Tidak | `"1.0"` | Versi BOM eksplisit (1.0, 1.1, 2.0, dst). Format: `X.Y` dimana X = major change, Y = minor change. |
| `status` | `enum` | Tidak | `"draft"` | Status siklus hidup BOM: `draft`, `active`, `archived`, `obsolete`. |
| `is_active` | `boolean` | Tidak | `true` | Flag cepat apakah BOM sedang aktif digunakan. |
| `is_locked` | `boolean` | Tidak | `false` | Apakah BOM sudah terkunci karena pernah digunakan berproduksi (MFG-02). BOM terkunci tidak bisa diedit langsung — harus clone ke versi baru. |
| `effective_date` | `date` | Tidak | - | Tanggal berlakunya BOM ini. |
| `parent_bom_id` | `string` | Tidak | - | ID versi BOM induk sebelum revisi/kloning. Membentuk chain versi. |
| `notes` | `string` | Tidak | - | Catatan perubahan atau informasi tambahan. |

### Field Konfigurasi Output & QC

| Field | Tipe | Wajib | Default | Deskripsi |
| - | - | - | - | - |
| `output_type` | `enum` | Tidak | `"finished_good"` | Tipe output BOM: `finished_good` (barang jadi siap edar) atau `semi_finished` (barang setengah jadi olahan, MFG-01). |
| `base_quantity` | `number` | Tidak | `1` | Basis kuantitas output resep (misal: 10 atau 100). Digunakan untuk scaling resep. |
| `base_unit` | `string` | Tidak | `"pcs"` | Satuan basis output resep (kg, pcs, pack, liter). |
| `yield_expected_percentage` | `number` | Tidak | `100` | Estimasi persentase rendemen output (MFG-02). Misalnya 95% berarti dari 100 unit input diharapkan 95 unit output baik. |
| `qc_sop_parameters` | `object` | Tidak | - | Snapshot parameter SOP untuk gate QC release (MFG-04). |
| `qc_sop_parameters.temperature_min_c` | `number` | - | - | Suhu minimum proses (Celsius). |
| `qc_sop_parameters.temperature_max_c` | `number` | - | - | Suhu maksimum proses (Celsius). |
| `qc_sop_parameters.cooking_time_min_minutes` | `number` | - | - | Waktu masak minimum (menit). |
| `qc_sop_parameters.pressure_min_bar` | `number` | - | - | Tekanan minimum proses (bar). |

## State Machine — Siklus Hidup BOM

BOM memiliki siklus hidup yang ketat untuk menjamin integritas data produksi. Setiap transisi status dicatat dan diaudit.

```mermaid theme={null}
stateDiagram-v2
    [*] --> draft : BOM baru dibuat

    draft --> active : Activate BOM<br/>(semua komponen valid)
    draft --> obsolete : Hapus / Batalkan<br/>(tidak jadi dipakai)

    active --> active : Update minor<br/>(version +0.1, is_locked = false)
    active --> archived : Versi baru di-activate<br/>(versi lama di-archive)
    active --> obsolete : Tidak berlaku lagi<br/>(produk discontinued)

    archived --> active : Re-activate<br/>(rollback ke versi ini)
    archived --> obsolete : Final archive<br/>(tidak akan dipakai lagi)

    obsolete --> [*] : Terminal state

    note right of active
        is_locked = true
        setelah digunakan di
        ProductionOrder pertama
    end note

    note right of draft
        BOM bisa diedit bebas
        sebelum di-activate
    end note
```

### Detail Transisi Status

| Dari | Ke | Trigger | Konsekuensi |
| - | - | - | - |
| `draft` | `active` | User klik **Activate BOM** | BOM siap digunakan untuk Production Order. Validasi: semua komponen memiliki `quantity_per_unit > 0`. |
| `draft` | `obsolete` | User hapus BOM | BOM tidak pernah digunakan. Bisa dihapus permanen. |
| `active` | `active` | Edit minor (version +0.1) | **Hanya jika `is_locked = false`**. Jika sudah locked, harus clone ke versi baru. |
| `active` | `archived` | Versi baru di-activate | BOM lama tetap tersimpan sebagai arsip. Tidak bisa diedit lagi. |
| `active` | `obsolete` | Produk discontinued | BOM ditandai tidak berlaku. Semua PO terkait tetap tersimpan. |
| `archived` | `active` | Rollback | Versi arsip diaktifkan kembali. Versi aktif saat ini menjadi `archived`. |

### Aturan Penguncian (Lock Mechanism)

```mermaid theme={null}
flowchart TD
    A[Production Order dibuat<br/>merujuk BOM-X] --> B{BOM-X<br/>is_locked?}
    B -->|Ya| C[PO langsung pakai<br/>BOM-X yang terkunci]
    B -->|Tidak| D[Set is_locked = true<br/>pada BOM-X]
    D --> E[PO pakai BOM-X]
    C --> F[Produksi berjalan]
    E --> F

    G[User ingin edit BOM-X] --> H{is_locked?}
    H -->|Ya| I[Tidak bisa edit langsung!<br/>Clone ke BOM-Y (versi baru)]
    H -->|Tidak| J[Edit BOM-X langsung<br/>version +0.1]
    I --> K[BOM-Y jadi draft<br/>edit → activate]
```

**Implikasi bisnis**: Setelah BOM digunakan untuk produksi pertama kali, resep terkunci. Perubahan resep harus melalui versi baru sehingga traceability produksi tidak terputus — production order lama tetap merujuk ke resep yang berlaku saat produksi berlangsung.

## Contoh BOM — Ayam Suwir Petir (150g)

| Bahan Baku | Qty | Satuan | Harga/Satuan | Subtotal | % dari Total |
| - | - | - | - | - | - |
| Ayam suwir | 150 | gram | Rp 80 | Rp 12.000 | 57.1% |
| Cabai merah | 50 | gram | Rp 50 | Rp 2.500 | 11.9% |
| Bawang merah | 20 | gram | Rp 40 | Rp 800 | 3.8% |
| Bawang putih | 10 | gram | Rp 35 | Rp 350 | 1.7% |
| Minyak goreng | 30 | ml | Rp 20 | Rp 600 | 2.9% |
| Garam | 5 | gram | Rp 10 | Rp 50 | 0.2% |
| Gula | 3 | gram | Rp 15 | Rp 45 | 0.2% |
| Penyedap | 2 | gram | Rp 25 | Rp 50 | 0.2% |
| Kemasan pouch | 1 | pcs | Rp 500 | Rp 500 | 2.4% |
| Label sticker | 1 | pcs | Rp 200 | Rp 200 | 1.0% |
| **Total Bahan Baku** | | | | **Rp 17.095** | **81.4%** |
| Tenaga Kerja | - | - | - | Rp 2.500 | 11.9% |
| Overhead | - | - | - | Rp 1.405 | 6.7% |
| **HPP Total** | | | | **Rp 21.000** | **100%** |
| **Harga Jual** | | | | **Rp 45.000** | |
| **Gross Profit** | | | | **Rp 24.000** | **53.3%** |

## Membuat BOM

### Step-by-Step

```mermaid theme={null}
flowchart LR
    A[Pilih Produk<br/>dari Katalog] --> B[Tambah Komponen<br/>Bahan Baku Satu per Satu]
    B --> C[Input Qty & Satuan<br/>per Komponen]
    C --> D[Review Total Cost<br/>per Unit]
    D --> E[Activate BOM<br/>Siap Digunakan]
    E --> F[Auto-Calculate<br/>saat Create PO]
```

| Step | Aksi | Detail |
| - | - | - |
| 1 | Buka halaman produk (`/companyproducts`) | Pilih produk yang akan dibuat BOM-nya |
| 2 | Klik **Edit BOM** | Form BOM terbuka |
| 3 | Klik **Add Component** | Pilih bahan baku dari dropdown |
| 4 | Input quantity per unit produk | Contoh: 50 gram cabai per 1 unit produk |
| 5 | Pilih satuan | gram, kg, ml, liter, pcs |
| 6 | Harga otomatis terisi | Dari data harga bahan baku terakhir |
| 7 | Tambah komponen lain | Ulangi step 3-6 |
| 8 | Review total cost | Sistem hitung otomatis |
| 9 | Klik **Activate BOM** | BOM siap digunakan untuk produksi |

### Field BOM

| Field | Tipe | Wajib | Deskripsi |
| - | - | - | - |
| **Produk** | Dropdown | Ya | Produk jadi yang akan diproduksi |
| **Komponen** | Dropdown | Ya | Bahan baku dari katalog |
| **Quantity** | Number | Ya | Jumlah yang dibutuhkan per 1 unit produk |
| **Satuan** | Dropdown | Ya | gram, kg, ml, liter, pcs, box |
| **Harga per Satuan** | Auto | - | Diambil dari harga beli terakhir |
| **Subtotal** | Auto | - | Qty x Harga |
| **Notes** | Text | Tidak | Catatan (opsional) |

## Version Control

```mermaid theme={null}
graph LR
    V1[v1.0<br/>2026-01-15<br/>Initial BOM] --> V2[v1.1<br/>2026-04-20<br/>Update qty cabai]
    V2 --> V3[v1.2<br/>2026-07-10<br/>Tambah komponen]
    V3 --> V4[v2.0<br/>2026-10-01<br/>Resep baru - Active]
    
    style V4 fill:#22c55e,color:#fff
    style V1 fill:#94a3b8,color:#fff
    style V2 fill:#94a3b8,color:#fff
    style V3 fill:#94a3b8,color:#fff
```

| Field | Deskripsi |
| - | - |
| **Version Number** | Format: X.Y (X = major change, Y = minor change) |
| **Date Created** | Tanggal versi ini dibuat |
| **Created By** | User yang membuat perubahan |
| **Changes** | Deskripsi perubahan yang dilakukan |
| **Status** | Active (sedang dipakai) / Inactive (arsip) |

### Rollback

Jika perlu kembali ke versi sebelumnya:

1. Buka riwayat versi BOM
2. Pilih versi yang diinginkan
3. Klik **Rollback**
4. Konfirmasi — versi aktif saat ini menjadi inactive
5. Versi yang dipilih menjadi active kembali

### Aturan Versioning

| Jenis Perubahan | Impact | Contoh | Versi Baru |
| - | - | - | - |
| **Minor** (Y +0.1) | Tidak mengubah struktur resep | Koreksi typo nama bahan, update harga satuan | 1.0 → 1.1 |
| **Major** (X +1.0) | Mengubah komposisi atau proses | Tambah/hapus komponen, ubah quantity signifikan, ganti bahan | 1.2 → 2.0 |
| **Clone** (BOM baru dari existing) | Membuat versi independen | Resep alternatif untuk channel berbeda, eksperimen | BOM baru v1.0 |

## Manajemen Versi BOM — Detail Teknis

### Chain of Descent (Silsilah Versi)

Setiap BOM menyimpan `parent_bom_id` yang merujuk ke BOM induk. Ini membentuk chain yang memungkinkan audit trail lengkap:

```mermaid theme={null}
graph BT
    BOM1[BOM-001<br/>v1.0<br/>status: archived<br/>is_locked: true] --> BOM2[BOM-002<br/>v1.1<br/>status: archived<br/>is_locked: true]
    BOM2 --> BOM3[BOM-003<br/>v2.0<br/>status: active<br/>is_locked: false]
    
    style BOM3 fill:#22c55e,color:#fff
    style BOM1 fill:#94a3b8,color:#fff
    style BOM2 fill:#94a3b8,color:#fff
```

### Kapan BOM Harus di-Clone vs Edit?

| Kondisi | Aksi | Alasan |
| - | - | - |
| `is_locked = false`, perubahan minor | Edit langsung | BOM belum pernah dipakai produksi |
| `is_locked = true`, perubahan minor | Clone → versi baru (Y +0.1) | BOM sudah dipakai, harus preserve versi lama |
| `is_locked = true`, perubahan komposisi | Clone → versi baru (X +1.0) | Major change membutuhkan versi baru |
| Produk sama, resep alternatif | Clone → BOM independen | Channel berbeda mungkin butuh kemasan berbeda |

## Sequence Diagram — Konsumsi BOM saat Produksi

Diagram berikut menunjukkan alur lengkap bagaimana BOM digunakan saat production order diproses, dari perencanaan hingga konsumsi bahan dan rilis output.

```mermaid theme={null}
sequenceDiagram
    actor User as Operator Produksi
    participant PO as ProductionOrder
    participant BOM as BillOfMaterials
    participant INV as Inventory
    participant LOT as LotBatch
    participant SM as StockMovement

    User->>PO: Buat Production Order<br/>(pilih BOM, qty 100 unit)
    PO->>BOM: Load BOM aktif untuk produk
    BOM-->>PO: Return raw_materials[],<br/>packaging_materials[],<br/>qc_sop_parameters

    Note over PO: Hitung kebutuhan:<br/>qty_per_unit x quantity_to_produce

    PO->>INV: Cek ketersediaan bahan<br/>(available_quantity)
    INV-->>PO: Return stok per bahan

    alt Semua bahan cukup
        PO-->>User: Status: planned<br/>Semua bahan tersedia
    else Ada bahan kurang
        PO-->>User: Warning: bahan kurang<br/>Saran: buat Purchase Order
    end

    User->>PO: Start Production<br/>(status → in_progress)
    PO->>PO: Set is_locked = true<br/>pada BOM (jika belum locked)

    loop Untuk setiap bahan di raw_materials[]
        PO->>INV: Alokasi lot FIFO/FEFO
        INV-->>PO: Return lot dengan qty tersedia
        PO->>SM: Catat StockMovement<br/>(type: raw_material_outbound)
        SM->>INV: Kurangi quantity
        PO->>PO: Catat di materials_actual[]<br/>dengan lot allocation
    end

    Note over PO: Produksi berjalan...<br/>Monitor duration vs standard_time

    User->>PO: Input hasil produksi<br/>(quantity_produced, quantity_defected)

    PO->>PO: Hitung efficiency_status<br/>(duration vs standard_time)
    PO->>PO: Hitung fail_rate<br/>(defected / produced x 100)

    PO->>LOT: Buat output LotBatch<br/>(output_lot_id)
    LOT-->>PO: Return lot_id

    PO->>INV: Tambah stok produk jadi<br/>(finished_good)
    INV->>SM: Catat StockMovement<br/>(type: in, ref: qc_release)
```

### Sequence Diagram — Posting Konsumsi dengan Idempotency

Sistem SNISHOP ERP menggunakan mekanisme idempotency untuk mencegah double-posting konsumsi bahan:

```mermaid theme={null}
sequenceDiagram
    actor User as Operator
    participant API as Server API
    participant PO as ProductionOrder
    participant TX as Transaction

    User->>API: POST /production/{id}/start
    API->>PO: Cek start_idempotency_key
    
    alt Key sudah ada (retry)
        PO-->>API: Return existing result
        API-->>User: 200 OK (idempotent)
    else Key baru
        API->>PO: Set start_idempotency_key
        API->>PO: Set consumption_posting_started_at
        API->>TX: Begin transaction
        TX->>PO: Post konsumsi bahan (FIFO allocation)
        TX->>PO: Update materials_actual[]
        TX->>PO: Set consumption_posting_idempotency_key
        TX-->>API: Commit
        API-->>User: 200 OK
    end
```

## Auto-Calculate untuk Production Order

Saat membuat production order, sistem otomatis menghitung kebutuhan bahan baku berdasarkan BOM:

**Contoh: Production Order 100 jar Ayam Suwir Petir (150g)**

| Bahan Baku | Qty per Unit | x | Jumlah Produksi | = | Total Kebutuhan | Stok Tersedia | Status |
| - | - | - | - | - | - | - | - |
| Ayam suwir | 150g | x | 100 | = | 15.000g (15kg) | 20.000g | Cukup |
| Cabai merah | 50g | x | 100 | = | 5.000g (5kg) | 3.000g | Kurang 2kg |
| Bawang merah | 20g | x | 100 | = | 2.000g (2kg) | 5.000g | Cukup |
| Bawang putih | 10g | x | 100 | = | 1.000g (1kg) | 2.000g | Cukup |
| Minyak goreng | 30ml | x | 100 | = | 3.000ml (3L) | 10.000ml | Cukup |

> **Alert**: Sistem menampilkan warning untuk bahan yang kurang dan menyarankan untuk membuat Purchase Order.

### Perhitungan dengan Yield Expected

Jika BOM memiliki `yield_expected_percentage = 95`, maka untuk menghasilkan 100 unit baik:

```
Input yang dibutuhkan = quantity_to_produce / (yield_expected_percentage / 100)
                      = 100 / 0.95
                      = 105.26 → dibulatkan 106 unit input
```

Sistem otomatis mengoreksi kebutuhan bahan baku berdasarkan rendemen yang diestimasi.

## Cost Rollup — Kalkulasi Biaya dari BOM

### Struktur Biaya Produksi

BOM menjadi dasar kalkulasi HPP (Harga Pokok Produksi) yang diroll-up dari beberapa komponen biaya:

```mermaid theme={null}
graph TB
    subgraph "Direct Materials (BOM raw_materials[])"
        DM1[Ayam Suwir<br/>150g x Rp80 = Rp12.000]
        DM2[Cabai Merah<br/>50g x Rp50 = Rp2.500]
        DM3[Bawang-bawang<br/>30g x avg = Rp1.150]
        DM4[Bumbu lainnya<br/>10g x avg = Rp145]
    end

    subgraph "Packaging Materials (BOM packaging_materials[])"
        PM1[Pouch + Label<br/>1 set x Rp700]
    end

    subgraph "Semi-Finished Cost"
        SF[Bahan intermediate<br/>dari production tier sebelumnya]
    end

    subgraph "Conversion Costs"
        DL[Tenaga Kerja Langsung<br/>direct_labor_cost]
        OH[Overhead Pabrik<br/>overhead_cost]
    end

    DM1 & DM2 & DM3 & DM4 --> DMC[Total Direct Materials]
    PM1 --> PMC[Total Packaging Cost]
    SF --> SFC[Total Semi-Finished Cost]
    DL & OH --> CC[Total Conversion Cost]

    DMC & PMC & SFC & CC --> TBC[Total Batch Cost]
    TBC --> |"÷ quantity_produced"| UHPP[HPP per Unit]
```

### Tabel Rollup Biaya — Contoh Batch 100 Unit

| Komponen Biaya | Sumber Data | Perhitungan | Total |
| - | - | - | - |
| **Direct Materials** | `BOM.raw_materials[]` | Σ (qty\_per\_unit x unit\_cost) x qty\_produced | Rp 16.395.000 |
| **Packaging Materials** | `BOM.packaging_materials[]` | Σ (qty\_per\_unit x unit\_cost) x qty\_produced | Rp 700.000 |
| **Semi-Finished Cost** | `ProductionOrder.semi_finished_cost` | Dari batch produksi sebelumnya | Rp 0 |
| **Direct Labor** | `ProductionOrder.direct_labor_cost` | Input manual / kalkulasi waktu | Rp 250.000 |
| **Overhead** | `ProductionOrder.overhead_cost` | Alokasi proporsional | Rp 140.500 |
| **Total Batch Cost** | `ProductionOrder.total_batch_cost` | Σ semua di atas | Rp 17.485.500 |
| **By-product Credit** | `ProductionOrder.byproduct_credit` | Pengurang (jika ada sisa bernilai) | - Rp 0 |
| **Abnormal Loss** | `ProductionOrder.abnormal_loss_amount` | Exclude dari HPP produk baik | - Rp 0 |
| **Allocated to Good** | `ProductionOrder.allocated_cost_to_good` | Total - byproduct - abnormal | Rp 17.485.500 |
| **HPP per Unit** | `ProductionOrder.unit_hpp` | allocated\_cost\_to\_good ÷ quantity\_produced | **Rp 174.855** |

### Cost Status — Status Kelengkapan Biaya

| Status | Kondisi | Implikasi |
| - | - | - |
| `complete` | Semua lot bahan memiliki harga tercatat | HPP final, bisa digunakan untuk laporan keuangan |
| `provisional_incomplete` | Ada lot bahan yang belum tercatat harganya (`has_incomplete_cost_input = true`) | HPP sementara, perlu update saat harga tersedia |
| `zero_output_pending` | Output baik = 0 (semua defect / belum ada output) | HPP belum bisa dihitung |

### Produktivitas & Rendemen

| Metrik | Rumus | Contoh | Interpretasi |
| - | - | - | - |
| `mass_yield_percentage` | (output\_mass ÷ input\_mass) x 100 | 85% | 15% massa hilang (penyusutan, evaporasi, waste) |
| `productivity_ratio` | unit\_output ÷ kg\_input | 4.0 botol/kg | Dari 1 kg pasta dihasilkan 4 botol |
| `fail_rate` | quantity\_defected ÷ quantity\_produced x 100 | 2.5% | Target: \< 5% |
| `efficiency_status` | duration ÷ standard\_time | 110% → `normal` | > 120% = `inefficient` |

## Bundle/Package BOM

Untuk produk bundle, BOM berisi produk jadi lain (bukan bahan baku mentah):

**Contoh: Paket Sambal Komplit**

| Komponen | Qty | Tipe | Stok Komponen |
| - | - | - | - |
| Ayam Suwir Petir (150g) | 1 | Finished Good | 100 jar |
| Baby Cumi Judes (150g) | 1 | Finished Good | 80 jar |
| Sambal Tuna Mercon (150g) | 1 | Finished Good | 60 jar |

**Maksimal paket yang bisa dibuat**: 60 (limited by Sambal Tuna Mercon)

Saat paket dijual:

* Stok masing-masing komponen berkurang 1
* Paket tidak punya stok sendiri — stok virtual = min(stok komponen)

## Integrasi BOM dengan QC & HACCP

BOM tidak hanya mendefinisikan bahan, tetapi juga parameter kualitas yang harus dipenuhi saat produksi:

```mermaid theme={null}
sequenceDiagram
    participant BOM as BillOfMaterials
    participant PO as ProductionOrder
    participant HACCP as HACCPVerification
    participant QC as QualityCheck

    Note over BOM: qc_sop_parameters:<br/>temp: 85-95°C<br/>time: min 30 menit

    PO->>HACCP: Mulai verifikasi HACCP
    HACCP->>BOM: Load qc_sop_parameters
    BOM-->>HACCP: Return temperature_min/max,<br/>cooking_time_min, pressure_min

    loop Monitoring selama produksi
        HACCP->>HACCP: Cek checklist<br/>(sanitasi, APD, suhu)
        HACCP->>HACCP: Catat temperature_c<br/>vs target_temperature_c
    end

    HACCP->>HACCP: Set status = verified<br/>(jika semua checklist pass)
    HACCP->>PO: Set haccp_verified = true

    PO->>QC: Rilis untuk QC produk jadi
    QC->>QC: Inspeksi visual, aroma,<br/>tekstur, kemasan
    QC->>PO: Set qc_result = pass/fail/pending/rework
```

### Parameter QC dari BOM

| Parameter BOM | Digunakan di | Fungsi |
| - | - | - |
| `qc_sop_parameters.temperature_min_c` | HACCP Verification | Batas bawah suhu proses |
| `qc_sop_parameters.temperature_max_c` | HACCP Verification | Batas atas suhu proses |
| `qc_sop_parameters.cooking_time_min_minutes` | HACCP Verification | Waktu masak minimum |
| `qc_sop_parameters.pressure_min_bar` | HACCP Verification | Tekanan minimum (jika applicable) |
| `yield_expected_percentage` | Production Order | Estimasi rendemen untuk planning bahan |

## BOM Reporting

### Cost Report

| Metrik | Deskripsi |
| - | - |
| **Cost per Component** | Biaya setiap bahan per unit |
| **Total Cost per Product** | Total HPP per unit |
| **Cost Trend** | Perubahan biaya dari waktu ke waktu |
| **Cost Comparison** | Perbandingan biaya antar produk |
| **Cost Impact** | Dampak perubahan BOM terhadap total cost |

### Usage Report

| Metrik | Deskripsi |
| - | - |
| **Component Usage** | Total pemakaian bahan per periode |
| **Top Used Components** | Bahan yang paling banyak digunakan |
| **Usage Trend** | Tren pemakaian dari waktu ke waktu |
| **Waste Analysis** | Selisih planned vs actual consumption |

### Variance Report

| Metrik | Deskripsi | Target |
| - | - | - |
| **Planned vs Actual** | Perbandingan kebutuhan BOM vs pemakaian aktual | Selisih \< 3% |
| **Variance Amount** | Selisih dalam unit | - |
| **Variance %** | Selisih persentase | \< 3% |
| **Root Cause** | Penyebab variance (jika ada) | Documented |

### Traceability Report

| Metrik | Deskripsi | Sumber Data |
| - | - | - |
| **Lot Genealogy** | Silsilah lot: input lot → output lot | `LotBatch.input_lots[]` |
| **Material Traceability** | Bahan baku dari supplier mana, dipakai di batch apa | `ProductionOrder.materials_actual[].allocations[]` |
| **Batch Recall Readiness** | Kecepatan identifikasi batch terdampak | Waktu dari identifikasi hingga list batch terdampak \< 2 jam |
| **Cost per Batch** | Biaya aktual per batch produksi | `ProductionOrder.total_batch_cost` |

## Best Practices

| Praktik | Detail |
| - | - |
| **Update BOM saat resep berubah** | Jangan biarkan BOM outdated — setiap perubahan resep harus langsung di-update |
| **Verify dengan production team** | Sebelum activate, konfirmasi dengan tim produksi bahwa qty sudah benar |
| **Regular BOM audit** | Review BOM setiap kuartal untuk memastikan akurasi |
| **Document semua perubahan** | Setiap versi harus punya catatan perubahan yang jelas |
| **Update harga bahan berkala** | Harga bahan baku berubah — update BOM agar HPP tetap akurat |
| **Monitor cost trend** | Jika cost naik terus, cari cara optimasi (supplier alternatif, efisiensi proses) |
| **Gunakan yield\_expected\_percentage** | Set rendemen realistis agar planning bahan lebih akurat dan mengurangi waste |
| **Pisahkan raw\_materials dan packaging\_materials** | Gunakan array terpisah untuk bahan baku dan kemasan agar reporting lebih jelas |
| **Set qc\_sop\_parameters di BOM** | Parameter QC harus terdefinisi di BOM agar setiap produksi otomatis punya standar kualitas |
| **Lock BOM setelah produksi pertama** | Jangan edit BOM yang sudah dipakai — clone ke versi baru untuk menjaga traceability |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.