# Modul Inventory

Mencakup: Bahan Baku (Raw Material), Stok Produk. File: [inventoryService.js](../../src/services/inventoryService.js), [MaterialForm.jsx](../../src/pages/inventory/MaterialForm.jsx), [MaterialList.jsx](../../src/pages/inventory/MaterialList.jsx), [StockList.jsx](../../src/pages/inventory/StockList.jsx), [StockEdit.jsx](../../src/pages/inventory/StockEdit.jsx)

## Konsep penting: `stock_type` produk menentukan cara stok dihitung

Tiap produk (lihat [04-products.md](04-products.md)) punya `stock_type`:
- **`manual`** → stok diatur langsung di halaman Stok Produk (per cabang)
- **`recipe`** → stok dihitung otomatis dari ketersediaan bahan baku (resep produk → bahan baku apa & berapa banyak terpakai per 1 unit produk). Stoknya **global**, tidak per cabang — semua cabang berbagi stok bahan baku yang sama.

Ini mempengaruhi UI di banyak tempat: `StockEdit.jsx` mengunci semua input kalau produk tipe `recipe` (user diarahkan untuk edit stok bahan baku-nya saja), `StockList.jsx` menghitung qty berbeda untuk recipe vs manual.

---

## 1. Bahan Baku (Raw Material)

Form: [MaterialForm.jsx](../../src/pages/inventory/MaterialForm.jsx)

**Field:** gambar (upload), `name*`, `unit_id*`, `stock` (stok saat ini), `min_stock` (ambang notifikasi stok rendah), dan tabel **"Produk yang Menggunakan"** — daftar produk berbasis resep yang memakai bahan baku ini + jumlah pemakaian per unit + satuan pemakaian (boleh beda dari satuan bahan baku, mis. bahan baku disimpan per kg tapi dipakai per gram).

**Validasi sebelum submit:** `name` & `unit_id` wajib. Untuk tiap baris "produk yang menggunakan" yang sudah pilih produk, `quantity` wajib diisi.

**Flow submit — 2 langkah berantai:**
```
1. POST /v1/raw-materials  (create)  atau  PUT /v1/raw-materials/{id}  (update)
   → body: name, unit_id, stock?, min_stock?, image (file kalau ada)

2. PUT /v1/raw-materials/{materialId}/products
   → body: { products: [{ product_id, quantity, unit_id }, ...] }
   → ini full sync (replace), bukan tambah — kirim semua baris valid (product_id & quantity terisi)
   → kalau langkah 2 gagal, langkah 1 (data bahan baku) TETAP tersimpan — toast khusus:
     "Data bahan baku tersimpan, tapi gagal menyimpan produk terkait"
```

**Endpoint:**
| Aksi | Method | Endpoint |
|---|---|---|
| List | GET | `/v1/raw-materials` |
| Detail | GET | `/v1/raw-materials/{id}` |
| Create | POST | `/v1/raw-materials` |
| Update | PUT/POST+`_method` | `/v1/raw-materials/{id}` |
| Sync produk pemakai | PUT | `/v1/raw-materials/{id}/products` |
| Delete | DELETE | `/v1/raw-materials/{id}` |
| Export/Import | GET/POST | `/export/excel`, `/export/pdf`, `/import/template`, `/import` |

**Expected response detail** (`GET /v1/raw-materials/{id}`): `{ id, name, unit_id, stock, min_stock, image, product_materials: [{ id, product_id, quantity, unit_id }] }`

---

## 2. Stok Produk (List & Edit)

### List — `StockList.jsx`
`GET /v1/stock-products` — list gabungan semua produk (manual + recipe) dengan informasi stok. Filter di FE: cari nama, filter cabang, filter level stok (`available` qty>5, `low` 0<qty≤5, `empty` qty=0 — ambang `LOW_STOCK_THRESHOLD = 5` hardcoded di FE).

**Perhitungan qty yang ditampilkan (`qtyFor`):**
- Kalau ada filter cabang aktif **dan** produk bukan `recipe` → ambil `branch_stocks.find(s => s.branch_id === branch).quantity`
- Selain itu (tanpa filter cabang, atau produk `recipe`) → pakai `item.quantity ?? item.qty ?? item.total_stock` (qty global)

### Edit — `StockEdit.jsx`
Hanya untuk produk `manual` (kalau `recipe`, semua input di-disable + banner info "stoknya otomatis dihitung dari bahan baku").

**Field:** stok per cabang (`branch_stocks[]` — tiap baris: pilih cabang + jumlah stok). Bisa tambah/hapus baris per cabang.

**Flow submit — bukan "set stok absolut" langsung, tapi via stock mutation:**
```
Untuk tiap baris cabang yang sudah dipilih branch_id:
  POST /v1/stock-mutations
  body: { type: 'product', ref_id: stockData.id, branch_id, mutation_type: 'adjustment', quantity, note: 'Manual adjustment' }
```
**Penting:** `quantity` yang dikirim adalah **nilai stok baru yang diinginkan** (bukan delta/selisih) — backend tipe mutasi `adjustment` mengasumsikan ini sebagai stok absolut baru, bukan tambahan. Kalau ada N baris cabang, ini N request `POST` sequential (await dalam loop `for`).

**Endpoint:**
| Aksi | Method | Endpoint | Body |
|---|---|---|---|
| List stok | GET | `/v1/stock-products` | — |
| List mutasi stok | GET | `/v1/stock-mutations` | params filter (tanggal dll, tidak dipakai UI saat ini) |
| Catat mutasi/adjustment | POST | `/v1/stock-mutations` | `{ type, ref_id, branch_id, mutation_type, quantity, note }` |
| Export | GET | `/v1/stock-products/export/excel`, `/export/pdf` | blob |

**Catatan:** Tidak ada halaman terpisah untuk melihat histori mutasi stok (`mutations()` di service ada tapi tidak dipakai di halaman manapun saat ini) — kalau ada kebutuhan audit trail stok, endpoint `GET /v1/stock-mutations` sudah siap dipakai tapi UI-nya belum dibangun.
