> ## 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.

# Saldo

<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: "Saldo"
description: "Manajemen rekening bank dan kas — saldo real-time auto-refresh 30 detik, deposit/withdrawal workflow, transfer antar rekening, mutasi transaksi, dan rekonsiliasi bank di SNISHOP ERP."
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

# Saldo

<img src="https://mintcdn.com/quinnofspicy/e4f_upKhVWcjsUmM/docs/mintlify/screenshots/finance/saldo.png?fit=max&auto=format&n=e4f_upKhVWcjsUmM&q=85&s=88669289c82b4c97753dae3528ea1370" alt="Saldo" width="1920" height="1080" data-path="docs/mintlify/screenshots/finance/saldo.png" />

Halaman Saldo menampilkan seluruh rekening bank dan kas perusahaan yang terdaftar di sistem. Setiap rekening memiliki **saldo real-time** yang di-refresh otomatis setiap **30 detik**, mendukung **deposit dengan upload bukti transfer** (minimum Rp 10.000), **withdrawal dengan approval admin** (minimum Rp 50.000), **transfer antar rekening internal**, dan **rekonsiliasi bank** otomatis maupun manual. Halaman ini terintegrasi langsung dengan General Ledger dan modul Expense/Invoice.

## Arsitektur Saldo

```mermaid theme={null}
graph TB
    subgraph ACCOUNTS["Rekening Perusahaan"]
        CASH["Kas<br/>Cash"]
        BANK["Bank<br/>BCA, Mandiri, BNI, BRI"]
        EWALLET["E-Wallet<br/>GoPay, OVO, DANA"]
        OTHER["Other<br/>PayPal, Stripe"]
    end

    subgraph FEATURES["Fitur Utama"]
        REAL["Real-Time Balance<br/>Auto-refresh 30s"]
        DEP["Deposit<br/>Min Rp 10.000<br/>Upload bukti"]
        WD["Withdrawal<br/>Min Rp 50.000<br/>Admin approval"]
        XFER["Transfer<br/>Antar rekening"]
        RECON["Rekonsiliasi Bank<br/>Auto-match"]
    end

    subgraph INTEGRATION["Integrasi"]
        GL["General Ledger<br/>COA mapping"]
        EXP["Expense<br/>Payment source"]
        INV["Invoice<br/>Payment target"]
        POS["POS<br/>Revenue destination"]
    end

    ACCOUNTS --> FEATURES
    FEATURES --> INTEGRATION
```

## Rekening Bank

### Tambah Rekening Baru

1. Klik **"Tambah Rekening"**
2. Isi form:

| Field | Required | Deskripsi |
| - | - | - |
| Nama Akun | ✓ | Contoh: BCA Rupiah, Kas Utama |
| Tipe | ✓ | cash, bank, e-wallet, other |
| Nomor Rekening | ✓ | Nomor rekening bank |
| Saldo Awal | ✓ | Saldo saat akun dibuat |
| Kode COA | Opsional | Auto-inferred dari tipe |
| Default POS | Opsional | Centang jika akun default POS |

3. Klik **"Simpan"**
4. `current_balance` = `initial_balance`

### Informasi Per Rekening

| Field | Deskripsi |
| - | - |
| Nama Bank | BCA, Mandiri, BNI, BRI, dll |
| Nomor Rekening | Nomor rekening |
| Nama Pemilik | Nama pemilik rekening |
| Tipe Akun | Savings, Current, dll |
| Saldo Saat Ini | `current_balance` (real-time) |
| Saldo Tersedia | Saldo - pending transactions |
| Status | Aktif / Nonaktif |
| Mata Uang | IDR (default) |
| Last Updated | Timestamp terakhir refresh |

### Multiple Rekening

Sistem mendukung unlimited rekening:

| Bank | Contoh Rekening |
| - | - |
| BCA | BCA Rupiah, BCA Dollar |
| Mandiri | Mandiri Rupiah, Mandiri Bisnis |
| BNI | BNI Rupiah |
| BRI | BRI Rupiah |
| E-Wallet | GoPay, OVO, DANA, ShopeePay |
| Kas | Kas Utama, Kas Cabang, Kas Kecil |

## Saldo Real-Time

### Auto-Refresh

* Sistem auto-refresh setiap **30 detik**
* Menampilkan `current_balance` (server-authoritative)
* Bukan field `balance` (undeclared)

### Balance Monitoring

| Indikator | Deskripsi |
| - | - |
| Saldo Saat Ini | `current_balance` dari server |
| Saldo Tersedia | Saldo - pending transactions |
| Pending Transactions | Transaksi yang belum cleared |
| Last Updated | Timestamp terakhir refresh |

### Balance Alerts

| Alert | Trigger | Warna |
| - | - | - |
| Low Balance | Saldo \< threshold | Kuning |
| Negative Balance | Saldo \< 0 | Merah |
| Unusual Transaction | Transaksi tidak biasa | Oranye |
| Reconciliation Overdue | Belum rekonsiliasi > 30 hari | Merah |

## Deposit Workflow

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant S as Sistem
    participant A as Admin
    participant ACC as Account

    U->>S: Request deposit (min Rp 10.000)
    S->>S: Validate amount >= 10.000
    S->>S: Generate instruksi transfer
    S-->>U: Tampilkan instruksi
    U->>U: Transfer manual dari bank lain
    U->>S: Upload bukti transfer (JPG/PNG/PDF)
    S->>A: Notify untuk verifikasi
    A->>A: Review bukti transfer

    alt Valid
        A->>ACC: current_balance += amount
        A->>S: Mark deposit as verified
        S->>S: Create FinancialRecord (type: income)
        S-->>U: Deposit confirmed
    else Invalid
        A->>S: Reject deposit
        S-->>U: Deposit rejected + reason
    end
```

### Deposit Rules

| Rule | Value |
| - | - |
| Minimum deposit | Rp 10.000 |
| Bukti transfer | Required (JPG, PNG, PDF) |
| Approval | Manual oleh admin/finance |
| Balance update | Setelah approved |
| Journal entry | Auto-created |

## Withdrawal Workflow

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant S as Sistem
    participant A as Admin
    participant ACC as Account

    U->>S: Request withdrawal (min Rp 50.000)
    S->>S: Validate amount >= 50.000
    S->>S: Validate current_balance >= amount
    S->>A: Submit for approval

    A->>A: Review withdrawal request

    alt Approved
        A->>ACC: current_balance -= amount
        A->>S: Create withdrawal record
        S->>S: Create FinancialRecord (type: expense)
        S-->>U: Withdrawal approved
    else Rejected
        A->>S: Reject withdrawal
        S-->>U: Withdrawal rejected + reason
    end
```

### Withdrawal Rules

| Rule | Value |
| - | - |
| Minimum withdrawal | Rp 50.000 |
| Balance check | current\_balance >= amount |
| Approval | Required dari admin |
| Balance update | Setelah approved |
| Journal entry | Auto-created |

## Transfer Antar Rekening

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant S as Sistem
    participant FROM as Account Asal
    participant TO as Account Tujuan

    U->>S: Transfer request
    S->>S: Validate from_account != to_account
    S->>S: Validate current_balance >= amount
    S->>FROM: current_balance -= amount
    S->>TO: current_balance += amount
    S->>S: Create FinancialRecord (type: transfer)
    S->>S: Create GLJournalEntry (reference_type: transfer)
    S-->>U: Transfer completed
```

### Transfer Rules

| Rule | Deskripsi |
| - | - |
| Akun asal ≠ tujuan | Tidak bisa transfer ke akun yang sama |
| Balance check | Saldo asal harus cukup |
| Net zero | Total saldo perusahaan tidak berubah |
| Journal entry | Otomatis dibuat untuk audit trail |

### Transfer di Laporan

| View | Treatment |
| - | - |
| All accounts | Transfer net zero (tidak mempengaruhi total) |
| Single account | Transfer tercatat sebagai inflow/outflow |

## Mutasi Transaksi

### Incoming Transactions

| Sumber | Deskripsi |
| - | - |
| Customer Payment | Pembayaran invoice |
| POS Revenue | Revenue dari transaksi POS |
| Transfer In | Transfer dari rekening lain |
| Deposit | Deposit yang sudah approved |
| Interest Income | Bunga bank |
| Other Income | Pendapatan lain |

### Outgoing Transactions

| Sumber | Deskripsi |
| - | - |
| Supplier Payment | Pembayaran PO |
| Expense Payment | Pembayaran expense |
| Transfer Out | Transfer ke rekening lain |
| Withdrawal | Withdrawal yang sudah approved |
| Other Expense | Pengeluaran lain |

### Transaction Details

| Field | Deskripsi |
| - | - |
| Date | Tanggal transaksi |
| Amount | Nominal |
| Description | Keterangan |
| Reference Number | Nomor referensi |
| Category | Kategori transaksi |
| Status | cleared, pending |
| Source | pos, invoice, expense, transfer, dll |

### Filter Mutasi

| Filter | Opsi |
| - | - |
| Date Range | Hari ini, Minggu ini, Bulan ini, Custom |
| Transaction Type | Incoming, Outgoing, All |
| Source | POS, Invoice, Expense, Transfer, dll |
| Amount Range | Min - Max |

## Rekonsiliasi Bank

### Import Bank Statement

1. Download statement dari bank (CSV/Excel)
2. Upload ke sistem
3. Sistem auto-match berdasarkan:
   * Nominal (exact match)
   * Tanggal (±2 hari)
   * Deskripsi (fuzzy match)
4. Review unmatched items
5. Complete reconciliation

### Manual Reconciliation

1. Input statement balance
2. List outstanding items
3. Calculate adjusted balance:
   ```
   Adjusted Balance = Statement Balance + Outstanding Deposits - Outstanding Withdrawals
   ```
4. Compare with system balance
5. Investigate differences
6. Post adjustments jika perlu

### Reconciliation Report

| Item | Deskripsi |
| - | - |
| Reconciled Items | Transaksi yang sudah cocok |
| Unreconciled Items | Transaksi yang belum cocok |
| Differences | Selisih |
| Resolution Actions | Tindakan koreksi |

## Reporting

### Bank Statement

| Report | Deskripsi |
| - | - |
| Monthly Statement | Laporan bulanan per rekening |
| Transaction List | Daftar transaksi |
| Balance Summary | Ringkasan saldo |
| Reconciliation Status | Status rekonsiliasi |

### Cash Flow Report

| Component | Deskripsi |
| - | - |
| Cash Inflow | Total uang masuk |
| Cash Outflow | Total uang keluar |
| Net Cash Flow | Inflow - Outflow |
| Cash Position | Posisi kas terkini |

### Cash Daily Report

**Component**: `CashDailyReport.jsx` (493 lines)

| Field | Deskripsi |
| - | - |
| Date | Tanggal |
| Opening Balance | Saldo awal hari |
| Cash In | Total masuk hari ini |
| Cash Out | Total keluar hari ini |
| Closing Balance | Saldo akhir hari |

**Drill-down**: Klik tanggal untuk lihat transaksi individual

**Trend Analysis**: First half vs second half comparison

## Best Practices

### Security

* Limit akses ke rekening bank (hanya owner/finance)
* Regular password change
* Two-factor authentication
* Audit trail untuk semua transaksi

### Reconciliation

* Rekonsiliasi minimal sebulan sekali
* Investigate semua differences
* Document adjustments
* Keep records untuk audit

### Monitoring

* Check balance harian
* Review transactions mingguan
* Monitor alerts
* Report discrepancies segera

***

## Entity Schema — Saldo & Balance

### Entity Relationship Diagram

```mermaid theme={null}
erDiagram
    Company ||--o{ Account : "memiliki rekening"
    Company ||--o{ BankAccount : "memiliki rekening bank"
    Company ||--o{ FinancialRecord : "mencatat transaksi"
    Company ||--o{ GLJournalEntry : "mencatat jurnal"
    Company ||--o{ GLAccount : "memiliki COA"

    Account ||--o{ FinancialRecord : "sumber dana"
    Account ||--o{ FinancialRecord : "tujuan transfer"
    Account }o--|| BankAccount : "detail bank"
    FinancialRecord }o--o| GLJournalEntry : "referensi jurnal"
    GLJournalEntry ||--|{ GLAccount : "line items (debit/credit)"
    GLAccount ||--o| GLAccount : "parent (hirarki COA)"

    Company {
        string name "nama perusahaan"
        string owner_id PK "ID owner perusahaan"
        string owner_email "email owner"
        string owner_subscription_plan "free|pro|business|advanced|enterprise"
        string industry "retail|manufacturing|services|..."
        string tax_id "NPWP"
        string address "alamat perusahaan"
        string phone "nomor telepon"
        string email "email perusahaan"
        string website "website perusahaan"
        string logo_url "URL logo"
        number employee_count "jumlah karyawan"
        string active_modules "JSON modul aktif"
        object settings "pengaturan perusahaan"
    }

    Account {
        string user_id PK
        string company_id FK
        string name "nama rekening"
        string type "cash|bank|e-wallet|other"
        string account_number "nomor rekening"
        string bank_name "nama bank"
        number initial_balance "saldo awal"
        number current_balance "saldo real-time (server-authoritative)"
        string currency "mata uang (default: IDR)"
        string icon "icon UI"
        string color "warna UI"
        boolean is_active "status aktif"
        boolean is_default_pos "default POS Kasir"
        string notes "catatan"
        string mode "personal|business"
    }

    BankAccount {
        string user_id PK
        string company_id FK
        string account_type "bank|ewallet"
        string provider "nama penyedia (BRI, DANA, dll)"
        string account_name "nama pemilik rekening"
        string account_number "nomor rekening / nomor telepon"
        string description "catatan tambahan"
        boolean is_primary "rekening utama"
        number balance "saldo tracking internal"
        boolean is_active "status aktif"
    }

    FinancialRecord {
        string user_id PK
        string company_id FK
        string account_id FK "rekening sumber dana"
        string type "income|expense|transfer"
        number amount "jumlah transaksi"
        string category "kategori"
        string description "deskripsi"
        datetime date "tanggal transaksi"
        string attachment_url "bukti transaksi"
        string source "manual|pos|ai_scan|..."
        string mode "personal|business"
        string transfer_to_account_id FK "rekening tujuan"
        number transfer_fee "biaya transfer"
        string reference_id "ID dokumen sumber"
        string reference_type "invoice_payment|pos_transaction|..."
        string idempotency_key "kunci idempoten"
        boolean is_inventory_material "anti double-counting"
        number channel_fee "potongan marketplace"
        number cogs_amount "HPP/COGS"
        number tax_amount "jumlah pajak (PPN)"
    }

    GLJournalEntry {
        string company_id PK
        date entry_date "tanggal jurnal"
        string reference_number "nomor referensi"
        string reference_type "manual|invoice|transfer|..."
        string reference_id "ID dokumen sumber"
        string description "deskripsi jurnal"
        array line_items "debit/credit items"
        number total_debit "total debit"
        number total_credit "total kredit"
        boolean is_balanced "debit = credit"
        string status "draft|posted|reversed"
        date posted_date "tanggal posting"
        string posted_by "user yang posting"
        string notes "catatan"
    }

    GLAccount {
        string company_id PK
        string account_code "kode akun (1000, 2100)"
        string account_name "nama akun"
        string account_type "asset|liability|equity|revenue|expense"
        string category "Current Asset, Fixed Asset, dll"
        string sub_category "sub-kategori"
        string parent_account_id FK "akun parent (hirarki)"
        string normal_balance "debit|credit"
        boolean is_active "status aktif"
        string description "deskripsi"
        number level "0: header, 1: main, 2: sub"
    }
```

### Tabel Schema — Company

Entitas perusahaan yang menjadi wadah seluruh data keuangan, rekening, dan transaksi.

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| `name` | string | ✓ | — | Nama perusahaan |
| `owner_id` | string | ✓ | — | ID owner perusahaan |
| `owner_email` | string | ✓ | — | Email owner |
| `owner_subscription_plan` | enum | — | — | Plan membership owner: `free`, `pro`, `business`, `advanced`, `enterprise` |
| `description` | string | — | — | Deskripsi perusahaan |
| `industry` | enum | — | — | Industri: `retail`, `manufacturing`, `services`, `technology`, `food_beverage`, `healthcare`, `education`, `other` |
| `address` | string | — | — | Alamat perusahaan |
| `phone` | string | — | — | Nomor telepon perusahaan |
| `email` | string | — | — | Email perusahaan |
| `website` | string | — | — | Website perusahaan |
| `logo_url` | string | — | — | URL logo perusahaan |
| `tax_id` | string | — | — | NPWP (Nomor Pokok Wajib Pajak) |
| `employee_count` | number | — | `0` | Jumlah karyawan |
| `metadata` | object | — | — | Data tambahan / legacy |
| `landing_page_config` | object | — | — | Konfigurasi landing page perusahaan |
| `business_type` | string | — | — | Kategori bisnis yang dipilih saat onboarding |
| `active_modules` | string | — | — | JSON string berisi array ID modul aktif |
| `settings` | object | — | — | Pengaturan perusahaan (jam kerja, cuti, pajak, dll) |

### Tabel Schema — Account

Entitas utama untuk menyimpan seluruh rekening bank, kas, dan e-wallet perusahaan.

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| `user_id` | string | ✓ | — | ID pengguna pemilik rekening |
| `company_id` | string | — | — | ID perusahaan (null untuk personal) |
| `name` | string | ✓ | — | Nama rekening (e.g., Kas, BCA, Mandiri) |
| `type` | enum | ✓ | `cash` | Jenis rekening: `cash`, `bank`, `e-wallet`, `other` |
| `account_number` | string | — | — | Nomor rekening (opsional) |
| `bank_name` | string | — | — | Nama bank (untuk type=bank) |
| `initial_balance` | number | — | `0` | Saldo awal saat akun dibuat |
| `current_balance` | number | — | `0` | **Saldo real-time server-authoritative**. Di-update oleh POS sales, invoice payment, expense, dan void. Frontend fallback ke derived ledger jika masih 0/null |
| `currency` | string | — | `IDR` | Mata uang |
| `icon` | string | — | `💰` | Icon untuk tampilan UI |
| `color` | string | — | `#3B82F6` | Warna untuk tampilan UI |
| `is_active` | boolean | — | `true` | Status aktif rekening |
| `is_default_pos` | boolean | — | `false` | Rekening default untuk POS Kasir |
| `notes` | string | — | — | Catatan tambahan |
| `mode` | enum | — | `personal` | Mode rekening: `personal`, `business` |

### Tabel Schema — BankAccount

Detail rekening bank dan e-wallet untuk keperluan pembayaran dan transfer.

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| `user_id` | string | — | — | ID pengguna pemilik akun |
| `company_id` | string | — | — | ID perusahaan pemilik rekening |
| `account_type` | enum | ✓ | — | Jenis akun: `bank`, `ewallet` |
| `provider` | string | ✓ | — | Nama penyedia (BRI, BNI, DANA, GOPAY, dll) |
| `account_name` | string | ✓ | — | Nama pemilik rekening |
| `account_number` | string | ✓ | — | Nomor rekening atau nomor telepon E-Wallet |
| `description` | string | — | — | Catatan tambahan (max 1000 karakter) |
| `is_primary` | boolean | — | `false` | Apakah ini rekening utama |
| `balance` | number | — | `0` | Saldo tracking internal (opsional) |
| `is_active` | boolean | — | `true` | Status aktif rekening |

### Tabel Schema — FinancialRecord

Mencatat seluruh transaksi keuangan (pemasukan, pengeluaran, transfer) yang mempengaruhi saldo rekening.

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| `user_id` | string | ✓ | — | ID pengguna |
| `company_id` | string | — | — | ID perusahaan (null untuk personal) |
| `account_id` | string | — | — | ID rekening sumber dana |
| `type` | enum | ✓ | — | Jenis transaksi: `income`, `expense`, `transfer` |
| `amount` | number | ✓ | — | Jumlah transaksi |
| `category` | string | — | — | Kategori transaksi |
| `description` | string | — | — | Deskripsi singkat |
| `date` | datetime | ✓ | — | Tanggal transaksi |
| `attachment_url` | string | — | — | URL bukti transaksi |
| `source` | enum | — | `manual` | Sumber: `manual`, `ai_text`, `ai_scan`, `pos`, `manufacturing`, `distribution`, `recall`, `stock_opname` |
| `mode` | enum | — | `personal` | Mode: `personal`, `business` |
| `transfer_to_account_id` | string | — | — | ID rekening tujuan (untuk type=transfer) |
| `transfer_fee` | number | — | `0` | Biaya transfer (jika ada) |
| `reference_id` | string | — | — | ID referensi dokumen sumber (invoice, POS, expense) |
| `reference_type` | enum | — | — | Tipe referensi: `invoice_payment`, `pos_transaction`, `expense`, `manual`, `transfer`, `production_order`, `distribution_shipment`, `distribution_return`, `batch_recall`, `stock_opname` |
| `idempotency_key` | string | — | — | Kunci idempoten untuk mencegah duplikasi |
| `is_inventory_material` | boolean | — | `false` | Pembelian bahan baku (anti double-counting) |
| `channel_fee` | number | — | `0` | Potongan biaya marketplace / platform fee |
| `cogs_amount` | number | — | `0` | HPP/COGS yang dibekukan saat transaksi POS. Digunakan oleh P\&L untuk menghitung laba kotor |
| `tax_amount` | number | — | `0` | Jumlah pajak (PPN keluaran) |

### Tabel Schema — GLJournalEntry

Entitas jurnal umum (General Ledger) untuk audit trail dan pembukuan double-entry.

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| `company_id` | string | ✓ | — | ID perusahaan |
| `entry_date` | date | ✓ | — | Tanggal entri jurnal |
| `reference_number` | string | — | — | Nomor referensi (TRX-001, INV-001) |
| `reference_type` | enum | — | — | Tipe referensi: `manual`, `invoice`, `purchase_order`, `transfer`, `expense` |
| `reference_id` | string | — | — | ID dokumen sumber |
| `description` | string | ✓ | — | Deskripsi jurnal |
| `line_items` | array | ✓ | — | Line items debit/kredit (lihat sub-tabel di bawah) |
| `total_debit` | number | — | `0` | Total debit |
| `total_credit` | number | — | `0` | Total kredit |
| `is_balanced` | boolean | — | `false` | Apakah total\_debit = total\_credit |
| `status` | enum | — | `draft` | Status: `draft`, `posted`, `reversed` |
| `posted_date` | date | — | — | Tanggal posting ke buku besar |
| `posted_by` | string | — | — | User yang memposting |
| `notes` | string | — | — | Catatan tambahan |

**Sub-tabel `line_items` (array of objects):**

| Field | Tipe | Default | Deskripsi |
| - | - | - | - |
| `account_id` | string | — | ID GLAccount |
| `account_code` | string | — | Kode akun |
| `debit` | number | `0` | Jumlah debit |
| `credit` | number | `0` | Jumlah kredit |
| `description` | string | — | Deskripsi line item |

### Tabel Schema — GLAccount

Chart of Accounts (COA) — daftar akun pembukuan untuk klasifikasi transaksi keuangan.

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| `company_id` | string | ✓ | — | ID perusahaan |
| `account_code` | string | ✓ | — | Kode akun unik (1000, 2100, 4001) |
| `account_name` | string | ✓ | — | Nama akun |
| `account_type` | enum | ✓ | — | Tipe: `asset`, `liability`, `equity`, `revenue`, `expense` |
| `category` | string | — | — | Kategori (Current Asset, Fixed Asset, Receivable) |
| `sub_category` | string | — | — | Sub-kategori akun |
| `parent_account_id` | string | — | — | ID akun parent untuk hirarki COA |
| `normal_balance` | enum | ✓ | — | Saldo normal: `debit`, `credit` |
| `is_active` | boolean | — | `true` | Status aktif |
| `description` | string | — | — | Deskripsi akun |
| `level` | number | — | `0` | Level hirarki: 0=header, 1=main, 2=sub |

## State Machine — Account Status

```mermaid theme={null}
stateDiagram-v2
    [*] --> Active: Buat rekening baru\n(is_active = true)

    state Active {
        [*] --> SaldoNol
        SaldoNol --> SaldoPositif: Deposit / Income\n(current_balance += amount)
        SaldoPositif --> SaldoPositif: Income / Transfer In
        SaldoPositif --> SaldoPositif: Expense / Withdrawal\n(saldo masih >= 0)
        SaldoPositif --> SaldoRendah: Saldo < threshold
        SaldoRendah --> SaldoPositif: Deposit / Transfer In
        SaldoPositif --> SaldoNegatif: Withdrawal/Expense\nmelebihi saldo
        SaldoRendah --> SaldoNegatif: Withdrawal besar
        SaldoNegatif --> SaldoPositif: Deposit pelunasan
    }

    Active --> Inactive: Nonaktifkan rekening\n(is_active = false)
    Inactive --> Active: Aktifkan kembali\n(is_active = true)
    Inactive --> [*]: Hapus rekening
```

### State Transition Rules

| Dari State | Ke State | Trigger | Validasi |
| - | - | - | - |
| `[*]` | Active | Create account | `name` required, `type` required |
| SaldoNol | SaldoPositif | Deposit, Income, Transfer In | `amount > 0` |
| SaldoPositif | SaldoRendah | Expense/Withdrawal | `current_balance < threshold` |
| SaldoRendah | SaldoPositif | Deposit/Transfer In | `current_balance >= threshold` |
| SaldoPositif | SaldoNegatif | Overdraft/Withdrawal besar | `amount > current_balance` |
| Active | Inactive | Nonaktifkan | Tidak ada pending transaction |
| Inactive | Active | Aktifkan kembali | — |

## State Machine — GLJournalEntry Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> Draft: Buat jurnal baru\n(status = 'draft')

    state Draft {
        [*] --> InputLineItems
        InputLineItems --> ValidasiBalanced: Submit jurnal
        ValidasiBalanced --> InputLineItems: Tidak balanced\nkoreksi line_items
        ValidasiBalanced --> SiapPost: total_debit = total_credit\n(is_balanced = true)
    }

    Draft --> Posted: Posting ke buku besar\n(status = 'posted')

    state Posted {
        [*] --> Terkunci
        Terkunci --> Terkunci: Tidak bisa diedit\n(immutable)
    }

    Posted --> Reversed: Reverse / pembalikan\n(status = 'reversed')

    state Reversed {
        [*] --> Dibatalkan
        Dibalikan --> Dibalikan: Final state\n(tidak bisa kembali)
    }

    Reversed --> [*]: Arsipkan
    Draft --> [*]: Hapus jurnal draft
```

### State Transition Rules — GLJournalEntry

| Dari State | Ke State | Trigger | Validasi |
| - | - | - | - |
| `[*]` | Draft | Create journal entry | `company_id`, `entry_date`, `description`, `line_items` required |
| Draft | Draft (Validasi) | Submit untuk validasi | `is_balanced` harus `true` |
| Draft | Posted | Posting ke buku besar | `is_balanced = true`, `total_debit = total_credit` |
| Posted | Reversed | Reverse / pembalikan | Buat jurnal reversal baru |
| Draft | `[*]` | Hapus | Hanya journal berstatus `draft` yang bisa dihapus |

## Sequence Diagram — Perhitungan Saldo Real-Time

```mermaid theme={null}
sequenceDiagram
    participant FE as Frontend
    participant API as API Server
    participant DB as Database
    participant FR as FinancialRecord

    Note over FE: Auto-refresh setiap 30 detik
    FE->>API: GET /api/accounts/:id/balance
    API->>DB: SELECT current_balance FROM Account WHERE id = :id
    DB-->>API: current_balance = 5.000.000

    alt current_balance > 0 (authoritative)
        API-->>FE: { current_balance: 5000000 }
    else current_balance = 0 atau null (fallback)
        API->>FR: SELECT SUM(amount) WHERE account_id = :id AND type = 'income'
        API->>FR: SELECT SUM(amount) WHERE account_id = :id AND type = 'expense'
        API->>API: derived_balance = SUM(income) - SUM(expense) + initial_balance
        API-->>FE: { current_balance: derived_balance }
    end

    FE->>FE: Render saldo + cek alert thresholds
```

## Sequence Diagram — Transfer Dana Antar Rekening

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant API as API Server
    participant ACC_FROM as Account Asal
    participant ACC_TO as Account Tujuan
    participant FR as FinancialRecord
    participant GL as GLJournalEntry

    U->>API: POST /api/transfers { from_account_id, to_account_id, amount }
    API->>API: Validasi: from_account_id ≠ to_account_id
    API->>ACC_FROM: Baca current_balance
    ACC_FROM-->>API: current_balance = 10.000.000

    alt Saldo cukup (current_balance >= amount)
        API->>ACC_FROM: current_balance -= 500.000 → 9.500.000
        API->>ACC_TO: current_balance += 500.000 → 2.500.000
        API->>FR: Create FinancialRecord { type: 'transfer', amount: 500000, account_id: from, transfer_to_account_id: to }
        API->>GL: Create GLJournalEntry { reference_type: 'transfer', line_items: [debit: ACC_TO, credit: ACC_FROM] }
        API->>GL: Validasi: total_debit = total_credit (balanced)
        API-->>U: 200 OK — Transfer berhasil
    else Saldo tidak cukup
        API-->>U: 400 Bad Request — Saldo tidak mencukupi
    end
```

## Sequence Diagram — Rekonsiliasi Bank

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant API as API Server
    participant BS as Bank Statement (CSV)
    participant FR as FinancialRecord
    participant ACC as Account

    U->>API: POST /api/reconciliation/import (upload CSV)
    API->>BS: Parse bank statement entries
    BS-->>API: List transaksi bank [{date, amount, description}]

    loop Setiap transaksi bank
        API->>FR: Cari match: amount exact + date ±2 hari + fuzzy description
        alt Match ditemukan
            API->>API: Mark as reconciled
        else Tidak ada match
            API->>API: Mark as unreconciled
        end
    end

    API-->>U: Hasil rekonsiliasi { matched: 45, unmatched: 3 }

    U->>API: Input statement balance untuk validasi
    API->>API: Hitung Adjusted Balance
    Note over API: Adjusted = Statement Balance + Outstanding Deposits - Outstanding Withdrawals
    API->>ACC: Bandingkan Adjusted Balance vs current_balance

    alt Selisih = 0
        API-->>U: Rekonsiliasi berhasil — saldo cocok
    else Selisih ≠ 0
        API-->>U: Peringatan — selisih ditemukan, perlu investigasi
    end
```

## Sequence Diagram — Deposit dengan Verifikasi Bukti Transfer

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant API as API Server
    participant DB as Database
    participant ACC as Account
    participant FR as FinancialRecord
    participant GL as GLJournalEntry
    participant A as Admin/Finance

    U->>API: POST /api/deposits { account_id, amount, attachment_url }
    API->>API: Validasi: amount >= 10.000
    API->>DB: INSERT FinancialRecord { type: 'income', source: 'manual', status: 'pending_approval' }
    DB-->>API: FinancialRecord created (id: FR-001)
    API->>A: Notifikasi: ada deposit baru perlu verifikasi

    A->>API: GET /api/deposits/FR-001/attachment
    API-->>A: Tampilkan bukti transfer

    alt Bukti valid dan amount cocok
        A->>API: POST /api/deposits/FR-001/approve
        API->>ACC: current_balance += amount
        API->>FR: Update status → 'verified'
        API->>GL: Create GLJournalEntry { reference_type: 'manual', line_items: [debit: Kas, credit: Modal] }
        API->>GL: Validasi: is_balanced = true
        API-->>U: Deposit confirmed — saldo diperbarui
    else Bukti tidak valid
        A->>API: POST /api/deposits/FR-001/reject { reason }
        API->>FR: Update status → 'rejected'
        API-->>U: Deposit ditolak — { reason }
    end
```

## Sequence Diagram — Withdrawal dengan Approval Admin

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant API as API Server
    participant DB as Database
    participant ACC as Account
    participant FR as FinancialRecord
    participant GL as GLJournalEntry
    participant A as Admin/Finance

    U->>API: POST /api/withdrawals { account_id, amount, description }
    API->>API: Validasi: amount >= 50.000
    API->>ACC: Baca current_balance
    ACC-->>API: current_balance = 3.000.000

    alt Saldo tidak cukup
        API-->>U: 400 Bad Request — Saldo tidak mencukupi
    else Saldo cukup
        API->>DB: INSERT FinancialRecord { type: 'expense', source: 'manual', status: 'pending_approval' }
        DB-->>API: FinancialRecord created (id: FR-002)
        API->>A: Notifikasi: ada withdrawal baru perlu approval

        A->>API: Review withdrawal request

        alt Approved
            A->>API: POST /api/withdrawals/FR-002/approve
            API->>ACC: current_balance -= amount
            API->>FR: Update status → 'approved'
            API->>GL: Create GLJournalEntry { reference_type: 'expense', line_items: [debit: Beban, credit: Kas] }
            API->>GL: Validasi: is_balanced = true
            API-->>U: Withdrawal approved — saldo diperbarui
        else Rejected
            A->>API: POST /api/withdrawals/FR-002/reject { reason }
            API->>FR: Update status → 'rejected'
            API-->>U: Withdrawal ditolak — { reason }
        end
    end
```

## Enum Tables

### Enum: `Account.type` (Jenis Rekening)

| Value | Deskripsi | Contoh |
| - | - | - |
| `cash` | Kas / uang tunai | Kas Utama, Kas Cabang, Kas Kecil |
| `bank` | Rekening bank | BCA Rupiah, Mandiri Bisnis, BNI Rupiah |
| `e-wallet` | Dompet elektronik | GoPay, OVO, DANA, ShopeePay |
| `other` | Lainnya | PayPal, Stripe |

### Enum: `BankAccount.account_type`

| Value | Deskripsi |
| - | - |
| `bank` | Rekening bank konvensional |
| `ewallet` | Akun e-wallet / dompet digital |

### Enum: `FinancialRecord.type` (Jenis Transaksi)

| Value | Deskripsi | Pengaruh Saldo |
| - | - | - |
| `income` | Pemasukan | `current_balance += amount` |
| `expense` | Pengeluaran | `current_balance -= amount` |
| `transfer` | Transfer antar rekening | Net zero (sumber -, tujuan +) |

### Enum: `FinancialRecord.source` (Sumber Transaksi)

| Value | Deskripsi |
| - | - |
| `manual` | Input manual oleh user |
| `ai_text` | Dari AI text parsing |
| `ai_scan` | Dari AI scan dokumen |
| `pos` | Transaksi POS Kasir |
| `manufacturing` | Modul manufaktur |
| `distribution` | Modul distribusi |
| `recall` | Batch recall |
| `stock_opname` | Stock opname |

### Enum: `FinancialRecord.reference_type` (Tipe Referensi)

| Value | Deskripsi |
| - | - |
| `invoice_payment` | Pembayaran invoice |
| `pos_transaction` | Transaksi POS |
| `expense` | Pengeluaran |
| `manual` | Input manual |
| `transfer` | Transfer antar rekening |
| `production_order` | Production order |
| `distribution_shipment` | Pengiriman distribusi |
| `distribution_return` | Retur distribusi |
| `batch_recall` | Batch recall |
| `stock_opname` | Stock opname |

### Enum: `GLJournalEntry.status` (Status Jurnal)

| Value | Deskripsi |
| - | - |
| `draft` | Belum diposting, bisa diedit |
| `posted` | Sudah diposting ke buku besar |
| `reversed` | Sudah dibalik (reversal) |

### Enum: `GLJournalEntry.reference_type`

| Value | Deskripsi |
| - | - |
| `manual` | Entri manual |
| `invoice` | Dari invoice |
| `purchase_order` | Dari purchase order |
| `transfer` | Dari transfer |
| `expense` | Dari expense |

### Enum: `GLAccount.account_type` (Tipe Akun COA)

| Value | Saldo Normal | Deskripsi |
| - | - | - |
| `asset` | Debit | Harta / aset perusahaan |
| `liability` | Kredit | Kewajiban / utang |
| `equity` | Kredit | Modal / ekuitas |
| `revenue` | Kredit | Pendapatan |
| `expense` | Debit | Beban / pengeluaran |

### Enum: `GLAccount.normal_balance`

| Value | Deskripsi |
| - | - |
| `debit` | Saldo normal di sisi debit (Asset, Expense) |
| `credit` | Saldo normal di sisi kredit (Liability, Equity, Revenue) |

### Enum: `Account.mode` & `FinancialRecord.mode`

| Value | Deskripsi |
| - | - |
| `personal` | Mode pribadi (personal finance) |
| `business` | Mode bisnis (company finance) |

### Enum: `Company.owner_subscription_plan`

| Value | Deskripsi |
| - | - |
| `free` | Plan gratis — fitur dasar |
| `pro` | Plan profesional — fitur lanjutan |
| `business` | Plan bisnis — fitur lengkap |
| `advanced` | Plan advanced — fitur premium |
| `enterprise` | Plan enterprise — fitur tanpa batas |

### Enum: `Company.industry`

| Value | Deskripsi |
| - | - |
| `retail` | Perdagangan / ritel |
| `manufacturing` | Manufaktur / produksi |
| `services` | Jasa / layanan |
| `technology` | Teknologi |
| `food_beverage` | Makanan dan minuman (F\&B) |
| `healthcare` | Kesehatan |
| `education` | Pendidikan |
| `other` | Industri lainnya |

## RBAC — Hak Akses Saldo

| Aksi | Owner | Admin | Finance | Staff | Viewer |
| - | - | - | - | - | - |
| Lihat daftar rekening | ✓ | ✓ | ✓ | ✓ | ✓ |
| Lihat detail saldo | ✓ | ✓ | ✓ | ✓ | ✓ |
| Tambah rekening baru | ✓ | ✓ | ✓ | ✗ | ✗ |
| Edit rekening | ✓ | ✓ | ✓ | ✗ | ✗ |
| Hapus rekening | ✓ | ✓ | ✗ | ✗ | ✗ |
| Deposit | ✓ | ✓ | ✓ | ✗ | ✗ |
| Withdrawal | ✓ | ✓ | ✓ | ✗ | ✗ |
| Approve withdrawal | ✓ | ✓ | ✗ | ✗ | ✗ |
| Approve deposit | ✓ | ✓ | ✗ | ✗ | ✗ |
| Transfer antar rekening | ✓ | ✓ | ✓ | ✗ | ✗ |
| Import bank statement | ✓ | ✓ | ✓ | ✗ | ✗ |
| Rekonsiliasi bank | ✓ | ✓ | ✓ | ✗ | ✗ |
| Lihat laporan kas | ✓ | ✓ | ✓ | ✓ | ✓ |
| Export laporan | ✓ | ✓ | ✓ | ✗ | ✗ |
| Posting jurnal GL | ✓ | ✓ | ✓ | ✗ | ✗ |
| Reverse jurnal GL | ✓ | ✓ | ✗ | ✗ | ✗ |
| Hapus jurnal draft | ✓ | ✓ | ✓ | ✗ | ✗ |

## RBAC — Row-Level Security (RLS) Policies

Policy RLS yang diterapkan pada setiap entitas berdasarkan data JSONC.

### RLS: `Account`

| Operasi | Kondisi |
| - | - |
| CREATE | `company_id` = `user.active_company_id` DAN `company_id` tidak null/empty, ATAU `created_by_id` = `user.id`, ATAU `user_id` = `user.id`, ATAU user role = `admin` |
| READ | Sama dengan CREATE |
| UPDATE | Sama dengan CREATE |
| DELETE | Sama dengan CREATE |

### RLS: `BankAccount`

| Operasi | Kondisi |
| - | - |
| CREATE | `user_id` = `user.id`, ATAU `created_by_id` = `user.id`, ATAU user role = `admin`, ATAU `company_id` = `user.active_company_id` |
| READ | Sama dengan CREATE |
| UPDATE | Sama dengan CREATE |
| DELETE | Sama dengan CREATE |

### RLS: `FinancialRecord`

| Operasi | Kondisi |
| - | - |
| CREATE | `company_id` = `user.active_company_id` DAN `company_id` tidak null/empty, ATAU `created_by_id` = `user.id`, ATAU `user_id` = `user.id`, ATAU user role = `admin` |
| READ | Sama dengan CREATE |
| UPDATE | Sama dengan CREATE |
| DELETE | Sama dengan CREATE |

### RLS: `GLAccount` & `GLJournalEntry`

| Operasi | Kondisi |
| - | - |
| CREATE | Terbuka (policy kosong — diatur di layer aplikasi) |
| READ | Terbuka (policy kosong — diatur di layer aplikasi) |
| UPDATE | Terbuka (policy kosong — diatur di layer aplikasi) |
| DELETE | Terbuka (policy kosong — diatur di layer aplikasi) |

<Info>
  **Catatan RLS**: Semua entitas menggunakan Row-Level Security (RLS). Akses dibatasi berdasarkan `user_id`, `company_id`, dan `active_company_id`. Admin global dapat mengakses semua data. Data personal dan bisnis terisolasi berdasarkan field `mode`. Entitas GLAccount dan GLJournalEntry menggunakan policy kosong di level database karena kontrol akses dikelola di layer aplikasi/API.
</Info>


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