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

# Expenses

<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: "Expenses"
description: "Pencatatan dan pelaporan pengeluaran perusahaan — 8 sumber transaksi, kategori expense, integrasi GL, dan filtering di SNISHOP ERP."
--------------------------------------------------------------------------------------------------------------------------------------------------

# Expenses

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

Halaman Expenses menampilkan seluruh pengeluaran perusahaan dari berbagai sumber: input manual, POS, manufacturing, inventory, dan AI extraction. Setiap expense tercatat sebagai `FinancialRecord` dengan `source` yang menunjukkan asal transaksi, memungkinkan tracking dan filtering berdasarkan modul penghasil. Halaman ini terintegrasi langsung dengan General Ledger melalui auto-journaling dan dengan Budget Planner untuk variance analysis.

## Arsitektur Expense Tracking

```mermaid theme={null}
graph TB
    subgraph SOURCES["8 Sumber Expense"]
        MANUAL["Manual<br/>Input langsung"]
        AI_TEXT["AI Text<br/>Extract dari teks"]
        AI_SCAN["AI Scan<br/>Extract dari gambar"]
        POS_SRC["POS<br/>Transaksi kasir"]
        MFG_SRC["Manufacturing<br/>Production cost"]
        DIST["Distribution<br/>Biaya distribusi"]
        RECALL["Recall<br/>Biaya penarikan"]
        SO["Stock Opname<br/>Selisih stok"]
    end

    subgraph RECORD["FinancialRecord"]
        TYPE["type: expense"]
        CAT["category: 11 kategori"]
        SRC["source: 8 sumber"]
        REF["reference_type: 10+ tipe"]
        AMT["amount, cogs_amount, channel_fee, tax_amount"]
    end

    subgraph PROCESS["Processing"]
        GL["Auto-Journal<br/>GLJournalEntry"]
        BUDGET["Budget Check<br/>Variance analysis"]
        ACCT["Account Update<br/>current_balance"]
    end

    subgraph OUTPUT["Output"]
        LIST["Daftar Expense<br/>Filter & Search"]
        REPORT["Laporan Expense<br/>Per kategori/sumber/periode"]
        EXPORT["Export<br/>CSV/Excel/PDF"]
    end

    MANUAL --> RECORD
    AI_TEXT --> RECORD
    AI_SCAN --> RECORD
    POS_SRC --> RECORD
    MFG_SRC --> RECORD
    DIST --> RECORD
    RECALL --> RECORD
    SO --> RECORD

    RECORD --> PROCESS
    RECORD --> OUTPUT
    TYPE --> GL
    SRC --> BUDGET
    AMT --> ACCT
```

## FinancialRecord untuk Expense

### Field FinancialRecord

| Field | Tipe | Deskripsi |
| - | - | - |
| type | enum | income, expense, transfer |
| category | string | Kategori expense (11 opsi) |
| amount | number | Nominal expense |
| source | enum | manual, ai\_text, ai\_scan, pos, manufacturing, distribution, recall, stock\_opname |
| reference\_type | enum | expense, invoice\_payment, purchase\_order, production\_order, transfer, dll |
| reference\_id | UUID | ID entitas referensi |
| cogs\_amount | number | COGS jika applicable |
| channel\_fee | number | Biaya marketplace/payment gateway |
| tax\_amount | number | Pajak |
| is\_inventory\_material | boolean | Flag anti-double-counting |
| account\_id | UUID | Akun yang digunakan |
| company\_id | UUID | Multi-tenant isolation |

### 8 Sumber Expense

| Source | Deskripsi | Contoh |
| - | - | - |
| **manual** | Input manual dari form | Expense claim karyawan |
| **ai\_text** | Extract dari teks (AI) | Paste receipt text → AI extract |
| **ai\_scan** | Extract dari gambar (AI Vision) | Scan receipt → AI extract |
| **pos** | Dari transaksi POS | Refund, void, adjustment |
| **manufacturing** | Dari production batch | Spoilage cost, abnormal loss |
| **distribution** | Dari distribusi | Biaya kirim, packaging |
| **recall** | Dari product recall | Biaya penarikan produk |
| **stock\_opname** | Dari stock opname | Selisih stok (shrinkage) |

## Filter & Search

### Filter Options

| Filter | Opsi |
| - | - |
| Date Range | Hari ini, Minggu ini, Bulan ini, Kuartal ini, Tahun ini, Custom |
| Source | Manual, AI Text, AI Scan, POS, Manufacturing, Distribution, Recall, Stock Opname |
| Category | 11 kategori expense |
| Account | Pilih akun tertentu atau semua |
| Amount Range | Min - Max |
| Reference Type | Expense, Invoice Payment, PO, Production Order, dll |

### Search

| Field | Deskripsi |
| - | - |
| Description | Search by kata kunci di deskripsi |
| Amount | Search by nominal exact |
| Reference | Search by nomor referensi |

## AI Expense Extraction

### Extract from Text

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant AI as AI Engine
    participant S as Sistem
    participant FR as FinancialRecord

    U->>AI: Paste receipt text
    AI->>AI: Gemini Vision / OpenAI Vision
    AI->>AI: Extract: amount, category, description, date
    AI-->>S: Return structured JSON
    S->>FR: Create FinancialRecord (source: ai_text)
    S-->>U: Preview extracted data
    U->>S: Confirm / Edit
    S->>FR: Finalize record
```

### Extract from Image

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant CAM as Camera/Upload
    participant AI as AI Vision
    participant S as Sistem
    participant FR as FinancialRecord

    U->>CAM: Scan receipt / Upload photo
    CAM->>AI: Base64 image
    AI->>AI: Gemini Vision (first)
    alt Success
        AI-->>S: Structured JSON
    else Fail
        AI->>AI: OpenAI Vision (fallback)
        AI-->>S: Structured JSON
    end
    S->>FR: Create FinancialRecord (source: ai_scan)
    S-->>U: Preview extracted data
    U->>S: Confirm / Edit
```

### Extracted Fields

| Field | AI Extract | Editable |
| - | - | - |
| Amount | ✓ | ✓ |
| Category | ✓ (auto-detect) | ✓ |
| Description | ✓ | ✓ |
| Date | ✓ | ✓ |
| Account | ✗ | ✓ (manual) |

## Expense Categories

### 11 Kategori

| Kategori | Deskripsi | Contoh |
| - | - | - |
| salary | Gaji karyawan | Gaji bulanan, bonus, tunjangan |
| raw\_material | Bahan baku | Cabai, bawang, ayam, minyak |
| operational | Operasional | Listrik, air, internet, sewa |
| production\_cost | Biaya produksi | Gas, packaging, label |
| travel | Perjalanan | Transport, hotel, makan selama travel |
| meals | Makan | Makan karyawan, meeting |
| accommodation | Akomodasi | Sewa tempat, hotel |
| equipment | Peralatan | Beli alat produksi, komputer |
| office\_supplies | Perlengkapan kantor | ATK, tinta, kertas |
| training | Pelatihan | Kursus, sertifikasi, workshop |
| other | Lainnya | Biaya yang tidak masuk kategori lain |

## Reporting

### Expense by Category

| Metric | Deskripsi |
| - | - |
| Total per Category | Jumlah expense per kategori |
| Percentage | Persentase terhadap total |
| Trend | Perbandingan dengan periode sebelumnya |
| Budget Variance | Planned vs actual |

### Expense by Source

| Source | Count | Total Amount |
| - | - | - |
| Manual | 45 | Rp 12.500.000 |
| AI Scan | 23 | Rp 3.200.000 |
| POS | 12 | Rp 1.800.000 |
| Manufacturing | 8 | Rp 5.600.000 |
| Stock Opname | 3 | Rp 450.000 |

### Expense by Period

| Period | Total | Avg/Day |
| - | - | - |
| Today | Rp 1.200.000 | - |
| This Week | Rp 8.400.000 | Rp 1.200.000 |
| This Month | Rp 35.000.000 | Rp 1.166.667 |
| This Year | Rp 420.000.000 | Rp 1.150.685 |

## Integration

### General Ledger

* Auto-journaling saat expense approved
* COA mapping via `resolveExpenseGLMapping()`
* Reconciliation marker jika gagal

### Budget Planner

* Track actual vs planned per kategori
* Alert saat threshold tercapai
* Variance analysis

### Account

* Update `current_balance` saat expense posted
* Multi-account support

### AI Engine

* Gemini 1.5 Flash (primary)
* OpenAI GPT-4o-mini (fallback)
* Vision extraction dari gambar/PDF

## Best Practices

### Categorization

* Pilih kategori yang tepat untuk laporan akurat
* Gunakan `is_inventory_material` untuk bahan baku inventori
* Separate personal vs business expense

### Documentation

* Upload receipt untuk setiap expense
* Clear description
* Timely recording

### Review

* Review expense mingguan
* Check budget variance bulanan
* Investigasi unusual expenses

***

## Entity Schema & Relasi

### Entity Relationship Diagram

```mermaid theme={null}
erDiagram
    Expense ||--o| ApprovalWorkflow : "menggunakan"
    Expense ||--o| ApprovalRequest : "memiliki"
    Expense }o--|| GLAccount : "sumber dana (account_id)"
    Expense }o--|| FinancialRecord : "tercatat sebagai"
    Expense }o--o| GLJournalEntry : "di-posting ke"

    ApprovalWorkflow ||--o{ ApprovalRequest : "mengatur"
    ApprovalWorkflow {
        UUID id PK
        UUID company_id FK
        string workflow_name
        string description
        enum document_type
        jsonb approval_levels
        jsonb amount_limits
        boolean is_active
    }

    ApprovalRequest {
        UUID id PK
        UUID company_id FK
        UUID workflow_id FK
        string document_type
        UUID document_id FK
        UUID requester_id FK
        datetime submission_date
        number amount
        string description
        int current_approval_level
        jsonb approval_history
        enum overall_status
        datetime final_approval_date
    }

    Expense {
        UUID id PK
        UUID company_id FK
        UUID requester_id FK
        string expense_code UK
        date expense_date
        enum category
        string description
        number amount
        string currency
        string receipt_url
        enum status
        UUID approval_workflow_id FK
        UUID approval_request_id FK
        enum approval_status
        enum payment_method
        date payment_date
        string reference_number
        string notes
        UUID account_id FK
        string recipient_name
        boolean is_inventory_material
        string approved_by
        string idempotency_key
    }

    FinancialRecord {
        UUID id PK
        UUID user_id FK
        UUID company_id FK
        UUID account_id FK
        enum type
        number amount
        string category
        string description
        datetime date
        string attachment_url
        enum source
        enum mode
        UUID transfer_to_account_id FK
        number transfer_fee
        UUID reference_id FK
        enum reference_type
        string idempotency_key
        boolean is_inventory_material
        number channel_fee
        number cogs_amount
        number tax_amount
    }

    GLJournalEntry {
        UUID id PK
        UUID company_id FK
        date entry_date
        string reference_number
        enum reference_type
        UUID reference_id FK
        string description
        jsonb line_items
        number total_debit
        number total_credit
        boolean is_balanced
        enum status
        date posted_date
        string posted_by
        string notes
    }

    GLAccount {
        UUID id PK
        UUID company_id FK
        string account_code UK
        string account_name
        enum account_type
        string category
        string sub_category
        UUID parent_account_id FK
        enum normal_balance
        boolean is_active
        string description
        int level
    }
```

## Tabel Schema

### Expense — 22 Field

| No | Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - | - |
| 1 | `id` | UUID | ya | auto | Primary key |
| 2 | `company_id` | UUID | **ya** | — | ID perusahaan (multi-tenant isolation) |
| 3 | `requester_id` | UUID | **ya** | — | ID karyawan yang mengajukan expense |
| 4 | `expense_code` | string | tidak | auto-generate | Kode pengeluaran unik (contoh: `EXP-2026-0001`) |
| 5 | `expense_date` | date | **ya** | — | Tanggal pengeluaran |
| 6 | `category` | enum | **ya** | — | Kategori pengeluaran (11 opsi — lihat Enum Table) |
| 7 | `description` | string | tidak | — | Deskripsi singkat pengeluaran |
| 8 | `amount` | number | **ya** | — | Nominal pengeluaran |
| 9 | `currency` | string | tidak | `IDR` | Mata uang (default Rupiah) |
| 10 | `receipt_url` | string | tidak | — | URL bukti pengeluaran / invoice |
| 11 | `status` | enum | tidak | `draft` | Status lifecycle expense (lihat State Machine) |
| 12 | `approval_workflow_id` | UUID | tidak | — | ID ApprovalWorkflow yang digunakan |
| 13 | `approval_request_id` | UUID | tidak | — | ID ApprovalRequest terkait |
| 14 | `approval_status` | enum | tidak | `pending` | Status persetujuan: pending, approved, rejected |
| 15 | `payment_method` | enum | tidak | — | Metode bayar: company\_account, reimbursement, direct\_payment |
| 16 | `payment_date` | date | tidak | — | Tanggal pembayaran direalisasikan |
| 17 | `reference_number` | string | tidak | — | Nomor referensi pembayaran |
| 18 | `notes` | string | tidak | — | Catatan tambahan |
| 19 | `account_id` | UUID | tidak | — | ID rekening sumber dana pembayaran (FK ke GLAccount) |
| 20 | `recipient_name` | string | tidak | — | Nama penerima atau supplier |
| 21 | `is_inventory_material` | boolean | tidak | `false` | Flag anti-double-counting dengan HPP |
| 22 | `approved_by` | string | tidak | — | User ID atau email approver |
| 23 | `idempotency_key` | string | tidak | — | Kunci idempoten mencegah double submission |

### FinancialRecord — 22 Field

| No | Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - | - |
| 1 | `id` | UUID | ya | auto | Primary key |
| 2 | `user_id` | UUID | **ya** | — | ID pengguna pencatat |
| 3 | `company_id` | UUID | tidak | — | ID perusahaan (null untuk personal) |
| 4 | `account_id` | UUID | tidak | — | ID rekening / kantong sumber dana |
| 5 | `type` | enum | **ya** | — | Jenis transaksi: income, expense, transfer |
| 6 | `amount` | number | **ya** | — | Jumlah transaksi |
| 7 | `category` | string | tidak | — | Kategori transaksi (11 opsi expense) |
| 8 | `description` | string | tidak | — | Deskripsi singkat |
| 9 | `date` | datetime | **ya** | — | Tanggal dan waktu transaksi |
| 10 | `attachment_url` | string | tidak | — | URL bukti transaksi |
| 11 | `source` | enum | tidak | `manual` | Sumber: manual, ai\_text, ai\_scan, pos, manufacturing, distribution, recall, stock\_opname |
| 12 | `mode` | enum | tidak | `personal` | Mode pencatatan: personal, business |
| 13 | `transfer_to_account_id` | UUID | tidak | — | ID rekening tujuan (untuk type=transfer) |
| 14 | `transfer_fee` | number | tidak | `0` | Biaya transfer (jika ada) |
| 15 | `reference_id` | UUID | tidak | — | ID referensi transaksi sumber (invoice, pos, expense) |
| 16 | `reference_type` | enum | tidak | — | Tipe referensi: invoice\_payment, pos\_transaction, expense, manual, transfer, production\_order, distribution\_shipment, distribution\_return, batch\_recall, stock\_opname |
| 17 | `idempotency_key` | string | tidak | — | Kunci idempoten transaksi keuangan |
| 18 | `is_inventory_material` | boolean | tidak | `false` | Flag anti-double-counting pembelian bahan baku |
| 19 | `channel_fee` | number | tidak | `0` | Potongan biaya marketplace / platform fee |
| 20 | `cogs_amount` | number | tidak | `0` | COGS/HPP yang dibekukan saat transaksi POS |
| 21 | `tax_amount` | number | tidak | `0` | Jumlah pajak (PPN keluaran untuk revenue) |

### GLJournalEntry — 14 Field

| No | Field | Tipe | Required | Default | Deskripsi |
| - | - | - | - | - | - |
| 1 | `id` | UUID | ya | auto | Primary key |
| 2 | `company_id` | UUID | **ya** | — | ID perusahaan |
| 3 | `entry_date` | date | **ya** | — | Tanggal entri jurnal |
| 4 | `reference_number` | string | tidak | — | Nomor referensi (TRX-001, INV-001, EXP-001) |
| 5 | `reference_type` | enum | tidak | — | Tipe referensi: manual, invoice, purchase\_order, transfer, expense |
| 6 | `reference_id` | UUID | tidak | — | ID dokumen sumber |
| 7 | `description` | string | **ya** | — | Deskripsi jurnal |
| 8 | `line_items` | jsonb\[] | **ya** | — | Array line items: account\_id, account\_code, debit, credit, description |
| 9 | `total_debit` | number | tidak | `0` | Total debit (harus = total\_credit) |
| 10 | `total_credit` | number | tidak | `0` | Total credit (harus = total\_debit) |
| 11 | `is_balanced` | boolean | tidak | `false` | Validasi debit = credit |
| 12 | `status` | enum | tidak | `draft` | Status: draft, posted, reversed |
| 13 | `posted_date` | date | tidak | — | Tanggal saat jurnal di-post ke GL |
| 14 | `posted_by` | string | tidak | — | User yang mem-post jurnal |
| 15 | `notes` | string | tidak | — | Catatan tambahan |

***

## State Machine — Expense Lifecycle

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

    draft --> submitted: Submit untuk approval
    draft --> [*]: Hapus (draft only)

    submitted --> approved: Approver menyetujui
    submitted --> rejected: Approver menolak

    approved --> paid: Pembayaran direalisasikan
    approved --> submitted: Revisi (jika ada perubahan)

    rejected --> draft: Revisi & resubmit
    rejected --> [*]: Dibatalkan

    paid --> [*]: Selesai

    state draft {
        [*] --> Input_data
        Input_data --> Upload_bukti
        Upload_bukti --> Siap_submit
    }

    state submitted {
        [*] --> Menunggu_Approval
        Menunggu_Approval --> Level_2: Approve L1
        Level_2 --> Final: Approve L2
    }

    state approved {
        [*] --> Menunggu_Pembayaran
        Menunggu_Pembayaran --> Proses_Bayar
        Proses_Bayar --> Posting_GL
    }

    state paid {
        [*] --> GL_Tercatat
        GL_Tercatat --> Rekonsiliasi
    }
```

### Transisi Status

| Dari | Ke | Trigger | Kondisi |
| - | - | - | - |
| `[*]` | `draft` | User membuat expense baru | Field required terpenuhi |
| `draft` | `submitted` | User klik "Submit" | Amount > 0, receipt terlampir (opsional) |
| `draft` | `[*]` | User hapus expense | Hanya status draft yang bisa dihapus |
| `submitted` | `approved` | Approver klik "Approve" | Semua level approval terpenuhi |
| `submitted` | `rejected` | Approver klik "Reject" | Komentar wajib diisi |
| `approved` | `paid` | Finance proses pembayaran | account\_id & payment\_date terisi |
| `approved` | `submitted` | Revisi amount/kategori | Approval ulang diperlukan |
| `rejected` | `draft` | Requester revisi & resubmit | Status direset ke draft |
| `paid` | `[*]` | Selesai (terminal state) | GLJournalEntry tercipta otomatis |

***

## Sequence Diagrams

### 1. Expense Submission

```mermaid theme={null}
sequenceDiagram
    participant U as Requester
    participant FE as Frontend
    participant API as Backend API
    participant DB as Database
    participant AW as ApprovalWorkflow
    participant AR as ApprovalRequest

    U->>FE: Isi form expense
    FE->>FE: Validasi client-side
    FE->>API: POST /api/expenses
    API->>API: Generate expense_code
    API->>API: Cek idempotency_key
    API->>DB: INSERT Expense (status=draft)
    DB-->>API: Expense created

    U->>FE: Klik "Submit for Approval"
    FE->>API: PATCH /api/expenses/:id (status=submitted)
    API->>API: Resolve workflow berdasarkan amount & category
    API->>AW: GET ApprovalWorkflow (document_type=expense_request)
    AW-->>API: Workflow config (levels, amount_limits)
    API->>AR: CREATE ApprovalRequest
    API->>DB: UPDATE Expense (approval_workflow_id, approval_request_id)
    API-->>FE: Success
    FE-->>U: "Expense berhasil diajukan"
```

### 2. Approval Flow (Multi-Level)

```mermaid theme={null}
sequenceDiagram
    participant AP1 as Approver L1
    participant API as Backend API
    participant AR as ApprovalRequest
    participant DB as Database
    participant EXP as Expense
    participant AP2 as Approver L2

    AP1->>API: GET /api/approvals/pending
    API-->>AP1: Daftar approval menunggu

    AP1->>API: POST /api/approvals/:id/approve
    API->>AR: UPDATE approval_history (level=1, status=approved)
    API->>API: Cek current_approval_level < max_levels

    alt Single-Level Approval
        API->>AR: UPDATE overall_status = approved
        API->>EXP: UPDATE status = approved
        API-->>AP1: "Expense approved"
    else Multi-Level Approval
        API->>AR: UPDATE current_approval_level = 2
        API->>AP2: Notifikasi approval L2 diperlukan
        AP2->>API: POST /api/approvals/:id/approve
        API->>AR: UPDATE approval_history (level=2, status=approved)
        API->>AR: UPDATE overall_status = approved
        API->>EXP: UPDATE status = approved
        API-->>AP2: "Expense approved (final)"
    end
```

### 3. GL Posting Setelah Expense Approved

```mermaid theme={null}
sequenceDiagram
    participant FIN as Finance
    participant API as Backend API
    participant EXP as Expense
    participant GL_MAP as GL Mapping
    participant JRN as GLJournalEntry
    participant ACC as GLAccount
    participant FR as FinancialRecord

    FIN->>API: POST /api/expenses/:id/pay
    API->>EXP: UPDATE status = paid, payment_date = today
    API->>EXP: UPDATE account_id, reference_number

    API->>GL_MAP: resolveExpenseGLMapping(category)
    GL_MAP-->>API: { debit_account, credit_account }

    API->>JRN: CREATE GLJournalEntry
    Note over JRN: reference_type = "expense"<br/>reference_id = expense.id<br/>line_items:<br/>  Dr: Expense Account<br/>  Cr: Bank/Cash Account

    API->>JRN: Validasi is_balanced (debit = credit)

    alt Balanced
        API->>JRN: UPDATE status = posted
        API->>ACC: UPDATE current_balance (credit account -amount)
        API->>FR: CREATE FinancialRecord (type=expense, source=manual)
        API-->>FIN: "Pembayaran berhasil, jurnal terposting"
    else Not Balanced
        API->>JRN: UPDATE status = draft (reconciliation marker)
        API-->>FIN: "WARNING: Jurnal tidak balanced, perlu review"
    end
```

***

## Enum Tables

### expense\_status

| Value | Deskripsi | Warna Badge |
| - | - | - |
| `draft` | Expense masih dalam tahap pembuatan, belum diajukan | Grey |
| `submitted` | Expense sudah diajukan dan menunggu approval | Blue |
| `approved` | Expense disetujui oleh approver, siap dibayar | Green |
| `rejected` | Expense ditolak oleh approver | Red |
| `paid` | Pembayaran sudah direalisasikan dan jurnal GL terposting | Teal |

### expense\_category

| Value | Label | Deskripsi | Contoh |
| - | - | - | - |
| `salary` | Gaji | Gaji karyawan, bonus, tunjangan | Gaji bulanan staff |
| `raw_material` | Bahan Baku | Pembelian bahan baku produksi | Cabai, bawang, ayam |
| `operational` | Operasional | Biaya operasional harian | Listrik, air, internet |
| `production_cost` | Biaya Produksi | Biaya langsung produksi | Gas, packaging, label |
| `travel` | Perjalanan | Biaya perjalanan dinas | Transport, hotel |
| `meals` | Makan | Konsumsi karyawan dan meeting | Makan siang tim |
| `accommodation` | Akomodasi | Sewa tempat dan akomodasi | Sewa gudang |
| `equipment` | Peralatan | Pembelian peralatan dan aset | Mesin, komputer |
| `office_supplies` | Perlengkapan Kantor | ATK dan perlengkapan kantor | Kertas, tinta printer |
| `training` | Pelatihan | Biaya pelatihan dan pengembangan | Workshop, sertifikasi |
| `other` | Lainnya | Biaya di luar kategori di atas | Biaya miscellaneous |

### is\_inventory\_material

| Value | Deskripsi | Efek |
| - | - | - |
| `true` | Expense untuk pembelian bahan baku persediaan | Expense TIDAK dihitung sebagai beban di P\&L (mencegah double-counting dengan HPP). Dicatat sebagai penambahan inventori. |
| `false` | Expense beban biasa | Expense langsung diakui sebagai beban di P\&L pada periode berjalan. |

### expense\_payment\_method

| Value | Deskripsi |
| - | - |
| `company_account` | Pembayaran langsung dari rekening perusahaan |
| `reimbursement` | Karyawan membayar dulu, kemudian direimburse |
| `direct_payment` | Pembayaran langsung ke vendor/supplier |

### approval\_status

| Value | Deskripsi |
| - | - |
| `pending` | Menunggu keputusan approver |
| `approved` | Disetujui oleh approver |
| `rejected` | Ditolak oleh approver |

### gl\_journal\_status

| Value | Deskripsi |
| - | - |
| `draft` | Jurnal dibuat tapi belum di-post |
| `posted` | Jurnal sudah di-post ke General Ledger |
| `reversed` | Jurnal dibalik (reversal) karena koreksi |

***

## RBAC — Hak Akses Expense

| Role | Create | Read (Own) | Read (All) | Update (Own) | Approve | Pay & Post GL | Delete (Draft) |
| - | - | - | - | - | - | - | - |
| **Admin** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| **Finance Manager** | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| **Finance Staff** | ✓ | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ |
| **Manager** | ✓ | ✓ | ✓ (divisi) | ✓ | ✓ (sesuai level) | ✗ | ✗ |
| **Employee** | ✓ | ✓ | ✗ | ✓ (draft only) | ✗ | ✗ | ✗ |

### Catatan RBAC

* **Multi-tenant isolation**: Setiap expense terikat pada `company_id`, data antar perusahaan terisolasi sepenuhnya.
* **Approval delegation**: Manager hanya bisa approve expense dari divisinya sendiri, sesuai `amount_limits` di ApprovalWorkflow.
* **Draft protection**: Hanya pemilik expense atau admin yang bisa mengedit/menghapus expense berstatus `draft`.
* **Immutable setelah paid**: Expense berstatus `paid` tidak bisa diubah — koreksi dilakukan via journal reversal.
* **Idempotency**: Setiap transaksi keuangan menggunakan `idempotency_key` untuk mencegah duplikasi.


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