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

# Bank accounts

<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: "Bank Accounts"
description: "Manajemen rekening bank, kas, dan e-wallet perusahaan — 4 tipe akun, saldo real-time, transfer antar rekening, deposit/withdrawal workflow, dan integrasi POS di SNISHOP ERP."
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

# Bank Accounts

<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="Bank Accounts" width="1920" height="1080" data-path="docs/mintlify/screenshots/finance/saldo.png" />

Halaman Bank Accounts (atau **Saldo**) mengelola seluruh rekening keuangan perusahaan: kas, bank, dan e-wallet. Sistem mendukung **4 tipe akun**, saldo real-time yang di-refresh setiap 30 detik, workflow deposit dengan upload bukti transfer, withdrawal dengan approval admin, dan integrasi langsung dengan POS melalui flag `is_default_pos`. Setiap akun terhubung ke Chart of Accounts (COA) untuk pelaporan keuangan.

## Arsitektur Akun Keuangan

```mermaid theme={null}
graph TB
    subgraph TYPES["4 Tipe Akun"]
        CASH["Cash<br/>Kas tunai"]
        BANK["Bank<br/>BCA, Mandiri, BNI, BRI"]
        EWALLET["E-Wallet<br/>GoPay, OVO, DANA"]
        OTHER["Other<br/>Rekening lain"]
    end

    subgraph FEATURES["Fitur"]
        BAL["Saldo Real-Time<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"]
        POS["POS Integration<br/>is_default_pos"]
    end

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

    CASH --> BAL
    BANK --> BAL
    EWALLET --> BAL
    OTHER --> BAL

    BAL --> DEP
    BAL --> WD
    BAL --> XFER
    BAL --> POS

    CASH --> GL
    BANK --> GL
    EWALLET --> GL
    OTHER --> GL

    GL --> EXP
    GL --> INV
    POS --> POSM
```

## Entity Account

### Field Account

| Field | Tipe | Deskripsi |
| - | - | - |
| name | string | Nama akun (BCA Rupiah, Kas Utama, GoPay) |
| type | enum | cash, bank, e-wallet, other |
| account\_number | string | Nomor rekening |
| initial\_balance | number | Saldo awal saat pembuatan |
| current\_balance | number | **Saldo terkini (server-authoritative)** |
| is\_default\_pos | boolean | Akun default untuk transaksi POS |
| coa\_code | string | Kode COA (1-1100, 1-1200, dst) |
| company\_id | UUID | Multi-tenant isolation |

### COA Code Mapping

| Tipe Akun | Kode COA Default | Contoh |
| - | - | - |
| cash | 1-1000 | Kas Utama, Kas Cabang |
| bank | 1-1100 | BCA Rupiah, Mandiri Rupiah |
| e-wallet | 1-1200 | GoPay, OVO, DANA |
| other | 1-4000 | Rekening PayPal, Rekening Stripe |

## Add Bank Account

### Step-by-Step

1. Klik **"Tambah Rekening"** di halaman Saldo
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 jika kosong |
| Default POS | Opsional | Centang jika ingin jadi akun default POS |

3. Klik **"Simpan"**
4. Sistem create Account entity dengan `current_balance = initial_balance`

### Multiple Accounts

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, tidak digunakan)

### 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 | Aksi |
| - | - | - |
| Low Balance | Saldo \< threshold | Notifikasi kuning |
| Negative Balance | Saldo \< 0 | Notifikasi merah |
| Unusual Transaction | Transaksi tidak biasa | Review required |
| Reconciliation Overdue | Belum rekonsiliasi > 30 hari | Reminder |

## 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: Generate virtual account / instruksi
    S-->>U: Tampilkan instruksi transfer
    U->>U: Transfer manual dari bank lain
    U->>S: Upload bukti transfer
    S->>A: Notify untuk verifikasi
    A->>A: Review bukti transfer
    alt Valid
        A->>ACC: Update current_balance += amount
        A->>S: Mark deposit as verified
        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 |

## 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 balance >= amount
    S->>S: Validate amount >= 50.000
    S->>A: Submit for approval
    A->>A: Review withdrawal request
    alt Approved
        A->>ACC: current_balance -= amount
        A->>S: Create withdrawal record
        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 |

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

* **All accounts view**: Transfer net zero (tidak mempengaruhi total)
* **Single account view**: Transfer tercatat sebagai inflow/outflow

## POS Integration

### Default POS Account

Flag `is_default_pos` menentukan akun mana yang menerima revenue dari POS:

```mermaid theme={null}
graph LR
    POS["POS Transaction<br/>Rp 100.000"] --> DEFAULT["Account with<br/>is_default_pos = true<br/>BCA Rupiah"]
    DEFAULT --> BAL["current_balance<br/>+= 100.000"]
    DEFAULT --> FR["FinancialRecord<br/>source: pos"]
```

### Multiple POS Accounts

Jika ada multiple outlet, setiap outlet bisa punya akun POS sendiri:

| Outlet | Default POS Account |
| - | - |
| Toko Offline | Kas Toko |
| Gudang | BCA Gudang |
| Cabang 2 | Mandiri Cabang 2 |

## Transaction Tracking

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

## Rekonsiliasi Bank

### Import Bank Statement

1. Download statement dari bank (CSV/Excel)
2. Upload ke sistem
3. Auto-match berdasarkan nominal + tanggal
4. Review unmatched items
5. Complete reconciliation

### Manual Reconciliation

1. Input statement balance
2. List outstanding items
3. Calculate adjusted balance
4. Compare with system balance
5. Investigate differences

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

## 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 & Relasi ke General Ledger

### ER Diagram — Bank Account Entities

```mermaid theme={null}
erDiagram
    BankAccount {
        string id PK
        string user_id FK
        string company_id FK
        string account_type "bank | ewallet"
        string provider "BRI, BNI, DANA, GOPAY"
        string account_name
        string account_number
        string description
        boolean is_primary
        number balance
        boolean is_active
    }

    Account {
        string id PK
        string user_id FK
        string company_id FK
        string name
        string type "cash | bank | e-wallet | other"
        string account_number
        string bank_name
        number initial_balance
        number current_balance
        string currency
        boolean is_active
        boolean is_default_pos
        string mode "personal | business"
    }

    FinancialRecord {
        string id PK
        string user_id FK
        string company_id FK
        string account_id FK
        string type "income | expense | transfer"
        number amount
        string category
        string description
        datetime date
        string source
        string transfer_to_account_id FK
        string reference_id
        string reference_type
        number cogs_amount
        number tax_amount
    }

    GLJournalEntry {
        string id PK
        string company_id FK
        date entry_date
        string reference_number
        string reference_type "manual | invoice | purchase_order | transfer | expense"
        string reference_id
        string description
        array line_items
        number total_debit
        number total_credit
        boolean is_balanced
        string status "draft | posted | reversed"
        date posted_date
        string posted_by
    }

    GLAccount {
        string id PK
        string company_id FK
        string account_code
        string account_name
        string account_type "asset | liability | equity | revenue | expense"
        string category
        string normal_balance "debit | credit"
        boolean is_active
        number level
    }

    BankAccount ||--o{ FinancialRecord : "sumber transaksi"
    Account ||--o{ FinancialRecord : "account_id"
    FinancialRecord }o--|| Account : "transfer_to_account_id"
    FinancialRecord }o--o| GLJournalEntry : "reference_id"
    GLJournalEntry ||--|{ GLAccount : "line_items.account_id"
    Account }o--|| GLAccount : "coa_code mapping"
    BankAccount }o--|| Account : "legacy mapping"
```

### Tabel Schema — BankAccount

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| id | string (UUID) | ✓ (auto) | — | Primary key |
| user\_id | string (UUID) | — | — | ID pengguna pemilik akun |
| company\_id | string (UUID) | — | — | ID perusahaan pemilik rekening (multi-tenant) |
| account\_type | enum | ✓ | — | Jenis akun: `bank` atau `ewallet` |
| provider | string | ✓ | — | Nama penyedia: BRI, BNI, BCA, DANA, GOPAY, dst |
| account\_name | string | ✓ | — | Nama pemilik rekening |
| account\_number | string | ✓ | — | Nomor rekening bank atau nomor telepon E-Wallet |
| description | string | — | — | Catatan tambahan (max 1000 karakter) |
| is\_primary | boolean | — | `false` | Penanda rekening utama perusahaan |
| balance | number | — | `0` | Saldo rekening untuk tracking internal |
| is\_active | boolean | — | `true` | Status aktif rekening |

### Tabel Schema — Account (Field Relevan)

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| id | string (UUID) | ✓ (auto) | — | Primary key |
| user\_id | string (UUID) | ✓ | — | ID pengguna pemilik rekening |
| company\_id | string (UUID) | — | — | ID perusahaan (null untuk personal) |
| name | string | ✓ | — | Nama rekening/kantong (Kas, BCA, Mandiri, GoPay) |
| type | enum | ✓ | `cash` | Jenis: `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 pembuatan |
| current\_balance | number | — | `0` | **Saldo server-authoritative** (di-update oleh POS, invoice, expense, void) |
| currency | string | — | `IDR` | Mata uang |
| icon | string | — | 💰 | Icon rekening untuk UI |
| color | string | — | `#3B82F6` | Warna untuk 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: `personal` atau `business` |

### Tabel Schema — GLJournalEntry (Bank-Related)

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| id | string (UUID) | ✓ (auto) | — | Primary key |
| company\_id | string (UUID) | ✓ | — | ID perusahaan |
| entry\_date | date | ✓ | — | Tanggal entri jurnal |
| reference\_number | string | — | — | Nomor referensi (TRX-001, INV-001) |
| reference\_type | enum | — | — | Tipe sumber: `manual`, `invoice`, `purchase_order`, `transfer`, `expense` |
| reference\_id | string | — | — | ID dokumen sumber |
| description | string | ✓ | — | Deskripsi jurnal |
| line\_items | array | ✓ | — | Line items debit/credit (account\_id, account\_code, debit, credit, description) |
| total\_debit | number | — | `0` | Total debit |
| total\_credit | number | — | `0` | Total credit |
| is\_balanced | boolean | — | `false` | Apakah debit = credit |
| status | enum | — | `draft` | Status: `draft`, `posted`, `reversed` |
| posted\_date | date | — | — | Tanggal posting |
| posted\_by | string | — | — | User yang memposting |
| notes | string | — | — | Catatan tambahan |

### Tabel Schema — FinancialRecord (Transaksi Keuangan)

| Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - |
| id | string (UUID) | ✓ (auto) | — | Primary key |
| user\_id | string (UUID) | ✓ | — | ID pengguna |
| company\_id | string (UUID) | — | — | ID perusahaan |
| account\_id | string (UUID) | — | — | ID rekening sumber dana |
| type | enum | ✓ | — | Jenis: `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` atau `business` |
| transfer\_to\_account\_id | string | — | — | ID rekening tujuan (untuk type=transfer) |
| transfer\_fee | number | — | `0` | Biaya transfer |
| reference\_id | string | — | — | ID referensi sumber (invoice, POS, expense) |
| reference\_type | enum | — | — | Tipe referensi: `invoice_payment`, `pos_transaction`, `expense`, `manual`, `transfer`, dst |
| idempotency\_key | string | — | — | Kunci idempoten (cegah duplikasi) |
| is\_inventory\_material | boolean | — | `false` | Pembelian bahan baku (anti double-counting HPP) |
| channel\_fee | number | — | `0` | Potongan biaya marketplace |
| cogs\_amount | number | — | `0` | HPP/COGS yang dibekukan saat transaksi POS |
| tax\_amount | number | — | `0` | Jumlah pajak (PPN keluaran) |

***

## State Machine — Status Rekening Bank

```mermaid theme={null}
stateDiagram-v2
    [*] --> Active: Pembuatan rekening baru

    Active --> Frozen: Pembekuan oleh admin<br/>(investigasi fraud / dispute)
    Active --> Closed: Penutupan rekening<br/>(permintaan user / bank)

    Frozen --> Active:解冻 setelah investigasi selesai<br/>(requires admin approval)
    Frozen --> Closed: Penutupan langsung<br/>(setelah investigasi final)

    Closed --> [*]: Rekening tidak bisa diaktifkan kembali<br/>(archive only)

    state Active {
        [*] --> DapatTransaksi
        DapatTransaksi --> SaldoBertambah: income / deposit / transfer-in
        SaldoBertambah --> DapatTransaksi
        DapatTransaksi --> SaldoBerkurang: expense / withdrawal / transfer-out
        SaldoBerkurang --> DapatTransaksi
    }

    state Frozen {
        [*] --> TidakDapatTransaksi
        TidakDapatTransaksi: Semua transaksi diblokir
        TidakDapatTransaksi: Saldo di-freeze (read-only)
    }

    state Closed {
        [*] --> Archived
        Archived: Data diarsipkan untuk audit
        Archived: Saldo final di-snapshot
    }
```

### Transisi Status

| Dari | Ke | Trigger | Syarat |
| - | - | - | - |
| — | Active | Create rekening | Validasi field required |
| Active | Frozen | Admin freeze | Alasan investigasi wajib diisi |
| Active | Closed | Admin/User close | Saldo harus = 0 atau dipindahkan |
| Frozen | Active | Admin unfreeze | Approval dari finance manager |
| Frozen | Closed | Admin close | Investigasi selesai, saldo = 0 |

### Implikasi Status terhadap Operasi

| Operasi | Active | Frozen | Closed |
| - | - | - | - |
| Deposit | ✓ | ✗ | ✗ |
| Withdrawal | ✓ | ✗ | ✗ |
| Transfer In | ✓ | ✗ | ✗ |
| Transfer Out | ✓ | ✗ | ✗ |
| POS Payment | ✓ | ✗ | ✗ |
| Lihat Saldo | ✓ | ✓ (read-only) | ✓ (snapshot) |
| Riwayat Transaksi | ✓ | ✓ | ✓ (archive) |

***

## Sequence Diagram — Rekonsiliasi Bank

```mermaid theme={null}
sequenceDiagram
    participant U as User (Finance)
    participant S as Sistem ERP
    participant DB as Database
    participant GL as General Ledger
    participant BA as BankAccount

    U->>S: Upload bank statement (CSV/Excel)
    S->>S: Parse file & ekstraksi transaksi bank
    S->>DB: Query FinancialRecord where account_id = target
    DB-->>S: Return daftar transaksi internal

    loop Auto-Matching
        S->>S: Match berdasarkan amount + date ± tolerance
        alt Match ditemukan
            S->>S: Mark transaksi sebagai "reconciled"
        else Tidak match
            S->>S: Mark sebagai "unreconciled"
        end
    end

    S-->>U: Tampilkan hasil matching (matched vs unmatched)
    U->>U: Review unmatched items
    U->>S: Input adjustment / koreksi manual
    S->>DB: Update status reconciliation
    S->>GL: Create GLJournalEntry (jika ada adjustment)

    Note over GL: line_items:<br/>Debit: Bank Account (COA 1-1100)<br/>Credit: Adjustment Account

    GL-->>S: Journal posted
    S->>BA: Update balance jika ada koreksi
    S-->>U: Generate reconciliation report
    S->>DB: Save FinancialReportSnapshot (type: reconciliation)
```

### Status Rekonsiliasi per Transaksi

| Status | Deskripsi | Aksi Selanjutnya |
| - | - | - |
| matched | Transaksi internal cocok dengan statement bank | Tidak perlu aksi |
| unmatched\_internal | Ada di sistem tapi tidak di statement bank | Investigasi: timing difference atau error |
| unmatched\_external | Ada di statement bank tapi tidak di sistem | Input transaksi yang hilang |
| adjusted | Sudah dikoreksi secara manual | Journal entry dibuat |
| pending\_review | Menunggu review finance | Finance harus approve/reject |

***

## Sequence Diagram — Transfer Antar Rekening

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant API as API Server
    participant TX as Transaction Service
    participant ACC_FROM as Account Asal
    participant ACC_TO as Account Tujuan
    participant FR as FinancialRecord
    participant GL as GLJournalEntry
    participant LEDGER as Ledger Lock

    U->>API: POST /api/finance/transfer<br/>{from_account_id, to_account_id, amount}
    API->>API: Validasi: from ≠ to, amount > 0

    API->>TX: Begin transaction
    TX->>LEDGER: Acquire lock (from_account_id, to_account_id)

    TX->>ACC_FROM: SELECT current_balance WHERE id = from_account_id
    ACC_FROM-->>TX: current_balance = 500.000

    TX->>TX: Validate current_balance >= amount

    alt Saldo tidak cukup
        TX-->>API: Error: Insufficient balance
        API-->>U: 400 Bad Request — Saldo tidak cukup
    else Saldo cukup
        TX->>ACC_FROM: UPDATE current_balance = current_balance - amount
        TX->>ACC_TO: UPDATE current_balance = current_balance + amount

        TX->>FR: INSERT FinancialRecord<br/>{type: transfer, account_id: from,<br/>transfer_to_account_id: to, amount}
        FR-->>TX: financial_record_id

        TX->>GL: INSERT GLJournalEntry<br/>{reference_type: transfer,<br/>reference_id: financial_record_id}
        Note over GL: line_items:<br/>Debit: Account Tujuan (COA 1-1100)<br/>Credit: Account Asal (COA 1-1100)
        GL-->>TX: journal_entry_id

        TX->>LEDGER: Release lock
        TX->>TX: Commit transaction
        TX-->>API: Transfer success
        API-->>U: 200 OK — Transfer berhasil
    end
```

### Aturan Transfer

| Aturan | Deskripsi |
| - | - |
| Akun asal ≠ tujuan | Tidak bisa transfer ke diri sendiri |
| Amount > 0 | Nominal harus positif |
| Saldo >= amount | Validasi server-side (current\_balance) |
| Net zero | Total saldo perusahaan tidak berubah |
| Atomic | Semua operasi dalam satu transaction (rollback jika gagal) |
| Idempotent | Menggunakan idempotency\_key untuk cegah duplikasi |
| Journal wajib | GLJournalEntry otomatis dibuat (debit = credit) |

***

## Enum Tables

### account\_type (BankAccount)

| Nilai | Deskripsi | Contoh Provider |
| - | - | - |
| `bank` | Rekening bank konvensional | BCA, Mandiri, BNI, BRI |
| `ewallet` | Dompet elektronik / e-wallet | GoPay, OVO, DANA, ShopeePay |

### type (Account)

| Nilai | Deskripsi | Kode COA Default |
| - | - | - |
| `cash` | Kas tunai | 1-1000 |
| `bank` | Rekening bank | 1-1100 |
| `e-wallet` | Dompet elektronik | 1-1200 |
| `other` | Rekening lain (PayPal, Stripe) | 1-4000 |

### bank\_name / provider

| Nilai | Kategori | Negara |
| - | - | - |
| `BCA` | Bank Swasta | Indonesia |
| `Mandiri` | Bank BUMN | Indonesia |
| `BNI` | Bank BUMN | Indonesia |
| `BRI` | Bank BUMN | Indonesia |
| `BSI` | Bank Syariah | Indonesia |
| `CIMB Niaga` | Bank Swasta | Indonesia |
| `GoPay` | E-Wallet | Indonesia |
| `OVO` | E-Wallet | Indonesia |
| `DANA` | E-Wallet | Indonesia |
| `ShopeePay` | E-Wallet | Indonesia |
| `LinkAja` | E-Wallet | Indonesia |
| `PayPal` | E-Wallet | Internasional |

### reconciliation\_status

| Nilai | Deskripsi | Warna UI |
| - | - | - |
| `matched` | Transaksi cocok antara sistem dan statement bank | Hijau |
| `unmatched_internal` | Ada di sistem, tidak ada di statement bank | Kuning |
| `unmatched_external` | Ada di statement bank, tidak ada di sistem | Orange |
| `adjusted` | Sudah dikoreksi secara manual | Biru |
| `pending_review` | Menunggu review finance | Abu-abu |

### reference\_type (GLJournalEntry)

| Nilai | Deskripsi | Sumber |
| - | - | - |
| `manual` | Entri jurnal manual | Input langsung oleh akuntan |
| `invoice` | Dari invoice | Pembayaran invoice otomatis |
| `purchase_order` | Dari purchase order | Penerimaan barang/jasa |
| `transfer` | Dari transfer antar rekening | Transfer internal |
| `expense` | Dari expense | Pengajuan expense yang approved |

### status (GLJournalEntry)

| Nilai | Deskripsi | Bisa Di-edit |
| - | - | - |
| `draft` | Belum diposting, masih bisa diubah | ✓ |
| `posted` | Sudah diposting ke GL, tidak bisa diubah | ✗ (harus reverse) |
| `reversed` | Sudah dibalik (reversal journal) | ✗ |

### source (FinancialRecord)

| Nilai | Deskripsi |
| - | - |
| `manual` | Input manual oleh user |
| `ai_text` | Dari AI text parsing |
| `ai_scan` | Dari AI scan dokumen |
| `pos` | Dari transaksi POS Kasir |
| `manufacturing` | Dari modul manufaktur |
| `distribution` | Dari modul distribusi |
| `recall` | Dari batch recall |
| `stock_opname` | Dari stock opname |

***

## RBAC — Hak Akses Rekening Bank

| Role | Create | Read | Update | Delete | Freeze/Unfreeze | Approval Deposit | Approval Withdrawal |
| - | - | - | - | - | - | - | - |
| **Owner** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| **Admin** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| **Finance** | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ |
| **Accounting** | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
| **Staff** | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| **POS Operator** | ✗ | ✓ (default POS only) | ✗ | ✗ | ✗ | ✗ | ✗ |

### RLS Policy Summary

| Operasi | Kondisi Akses |
| - | - |
| **Create** | `user_id` = current user, ATAU `company_id` = active\_company\_id, ATAU role = admin |
| **Read** | `user_id` = current user, ATAU `company_id` = active\_company\_id, ATAU role = admin |
| **Update** | `user_id` = current user, ATAU `company_id` = active\_company\_id, ATAU role = admin |
| **Delete** | `user_id` = current user, ATAU `company_id` = active\_company\_id, ATAU role = admin |

### Catatan Keamanan

* Semua mutasi saldo dicatat di `FinancialRecord` dengan `idempotency_key` untuk mencegah duplikasi
* `current_balance` bersifat **server-authoritative** — frontend tidak boleh meng-update langsung
* Transfer antar rekening menggunakan **database lock** untuk mencegah race condition
* Rekening yang di-freeze (`is_active = false`) menolak semua operasi transaksi
* Audit trail tersedia melalui `GLJournalEntry` dengan status `posted` yang tidak bisa di-mutate (hanya reversal)


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