# ERD — BANDUNGCRAFT.COM

> Ini adalah representasi konseptual dari `DATABASE_DESIGN.md`. Engine tetap sesuai: InnoDB, utf8mb4, DECIMAL untuk uang, FK RESTRICT.

```mermaid
erDiagram
    users ||--o{ user_roles : has
    roles ||--o{ user_roles : assigned
    roles ||--o{ role_permissions : grants
    permissions ||--o{ role_permissions : via
    customers ||--o{ customer_addresses : has
    categories ||--o{ products : contains
    products ||--o{ product_images : photos
    products ||--o{ product_variants : variants
    products ||--|| inventory : stock
    products ||--o{ stock_movements : tracked
    customers ||--o{ custom_orders : requests
    custom_orders ||--o{ custom_order_images : refs
    custom_orders ||--o{ custom_order_files : files
    custom_orders ||--o{ quotations : offered
    quotations ||--o{ quotation_items : breakdown
    quotations ||--o{ quotation_revisions : revised
    customers ||--o{ orders : places
    orders ||--o{ order_items : contains
    orders ||--o{ payments : multiple
    orders ||--o{ invoices : billed
    orders ||--o{ shipments : shipped
    orders ||--o{ production_jobs : produced
    printers ||--o{ production_jobs : prints
    materials ||--o{ production_jobs : uses
    materials ||--o{ material_movements : moves
    production_jobs ||--o{ production_files : files
    production_jobs ||--o{ production_updates : updates
    users ||--o{ cash_sessions : opens
    cash_sessions ||--o{ transactions : groups
    transactions ||--o{ transaction_items : items
    transactions ||--o{ transaction_payments : paid
    expense_categories ||--o{ expenses : classifies
    journal_entries ||--o{ journal_entry_lines : lines
    users ||--o{ activity_logs : acts
```

## Relasi kunci

- **Satu order → banyak payment** (DP + pelunasan). `orders.paid/due` dihitung dari payment yang `success`, bukan angka manual.
- **Customer → order/custom order** satu-ke-banyak; address terpisah agar histori pengiriman tidak timpa alamat utama.
- **Product → inventory (1:1)** untuk `qty_on_hand`, stok masuk/keluar selalu melalui `stock_movements` (audit trail, bukan UPDATE langsung).
- **Quotation → order** bersifat opsional dan satu-ke-satu: `orders.quotation_id` FK ke quotations, tidak semua order berasal dari quotation (POS bisa langsung).
- **Cash session → transaction**: transaksi POS tanpa sesi tidak tercatat di tutup kasir; suntik `cash_session_id` dari session yang aktif.

## Unique & index yang mencegah akar masalah

- `products.sku`, `quotations.quotation_number`, `orders.order_number`, `invoices.invoice_number`, `transactions.transaction_number` semuanya UNIQUE.
- `payments.idempotency_key` UNIQUE mencegah tombol double-click membuat dua payment dengan payload sama.
- `product_variants` UNIQUE (product, color, size) mencegah varian duplikat.
- `inventory.product_id` sebagai PK mencegah dua baris inventory untuk produk yang sama.
