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

# Financial reports

<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: "Financial Reports"
description: "4 laporan keuangan utama — Laba Rugi (P\&L), Neraca (Balance Sheet), Neraca Saldo (Trial Balance), dan Budget vs Actual dengan dual-source engine, completeness audit, dan print support di SNISHOP ERP."
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

# Financial Reports

<img src="https://mintcdn.com/quinnofspicy/e4f_upKhVWcjsUmM/docs/mintlify/screenshots/finance/financial-reports.png?fit=max&auto=format&n=e4f_upKhVWcjsUmM&q=85&s=707350783fd087d7b29b741258c874f4" alt="Financial Reports" width="1920" height="1080" data-path="docs/mintlify/screenshots/finance/financial-reports.png" />

Halaman Financial Reports menghasilkan 4 laporan keuangan utama: **Laba Rugi (P\&L)**, **Neraca (Balance Sheet)**, **Neraca Saldo (Trial Balance)**, dan **Budget vs Actual**. Sistem menggunakan **dual-source engine** yang memprioritaskan GL Journal Entries dan fallback ke FinancialRecord, dilengkapi **completeness audit** yang menilai apakah data cukup untuk laporan audited atau hanya provisional. Setiap laporan mendukung **5 pilihan periode** dan **print-ready layout** dengan header khusus.

## Arsitektur Financial Reports

```mermaid theme={null}
graph TB
    subgraph SOURCES["Dual Source Engine"]
        GL["GL Journal Entries<br/>(Preferred)"]
        FR["FinancialRecord<br/>(Fallback)"]
    end

    subgraph REPORTS["4 Laporan Utama"]
        PL["Laba Rugi<br/>Profit & Loss"]
        BS["Neraca<br/>Balance Sheet"]
        TB["Neraca Saldo<br/>Trial Balance"]
        BVA["Budget vs Actual"]
    end

    subgraph FEATURES["Fitur"]
        PERIOD["Period Selector<br/>5 opsi"]
        COMPARE["Comparison<br/>Antar periode"]
        AUDIT["Completeness Audit<br/>Audited/Provisional"]
        PRINT["Print Support<br/>Print-only header"]
        EXPORT["Export<br/>PDF/Excel/CSV"]
    end

    subgraph COMPONENTS["Report Components"]
        PLC["ProfitLossReport<br/>337 lines"]
        BSC["BalanceSheetReport<br/>122 lines"]
        TBC["TrialBalanceReport<br/>117 lines"]
        BVAC["BudgetVsActualReport<br/>201 lines"]
    end

    GL --> REPORTS
    FR --> REPORTS
    REPORTS --> FEATURES
    REPORTS --> COMPONENTS
```

## 4 Laporan Utama

### 1. Laba Rugi (Profit & Loss)

**Component**: `ProfitLossReport.jsx` (337 lines)

#### Struktur P\&L

```
┌─────────────────────────────────────┐
│ REVENUE (Pendapatan)                │
│ ├─ Penjualan Produk                 │
│ ├─ Penjualan Jasa                   │
│ └─ Pendapatan Lain                  │
│ Total Revenue: Rp XXX               │
├─────────────────────────────────────┤
│ COST OF GOODS SOLD (HPP)            │
│ ├─ Bahan Baku                       │
│ ├─ Tenaga Kerja Langsung            │
│ └─ Overhead Produksi                │
│ Total COGS: Rp XXX                  │
├─────────────────────────────────────┤
│ GROSS PROFIT (Laba Kotor)           │
│ = Revenue - COGS: Rp XXX            │
├─────────────────────────────────────┤
│ OPERATING EXPENSES (Beban Operasional)│
│ ├─ Gaji                             │
│ ├─ Sewa                             │
│ ├─ Listrik & Air                    │
│ ├─ Marketing                        │
│ └─ Beban Lain                       │
│ Total Operating Expenses: Rp XXX    │
├─────────────────────────────────────┤
│ OPERATING PROFIT (Laba Operasional) │
│ = Gross Profit - OpEx: Rp XXX       │
├─────────────────────────────────────┤
│ OTHER INCOME/EXPENSES               │
│ ├─ Pendapatan Bunga                 │
│ └─ Beban Bunga                      │
├─────────────────────────────────────┤
│ NET PROFIT (Laba Bersih)            │
│ = Operating Profit + Other: Rp XXX  │
└─────────────────────────────────────┘
```

#### Dual-Source Logic

```mermaid theme={null}
graph TB
    START["Generate P&L"] --> CHECK{GL Journal<br/>Entries ada?}
    CHECK -->|Ya| GL["Use GL Journal Entries<br/>(Preferred)"]
    CHECK -->|Tidak| FR["Use FinancialRecord<br/>(Fallback)"]
    GL --> COGS["Check hasBookedCogs"]
    FR --> COGS
    COGS --> ADJUST["Adjust for COGS<br/>Avoid double-counting"]
    ADJUST --> CHANNEL["Deduct Channel Fees<br/>(FIN-05)"]
    CHANNEL --> AUDIT["evaluateCompleteness()"]
    AUDIT --> OUTPUT["Report Output<br/>Status: audited/provisional"]
```

#### Completeness Audit

| Status | Kriteria | Confidence |
| - | - | - |
| **Audited** | Semua data lengkap, GL journals posted | High |
| **Provisional** | Ada data missing, GL tidak lengkap | Medium |

**Issues yang terdeteksi**:

* Missing GL journals
* Unposted drafts
* Missing COGS data
* Channel fees not recorded

#### COGS Awareness

Sistem mencegah double-counting COGS:

| Check | Fungsi |
| - | - |
| `hasBookedCogs` | Cek apakah COGS sudah tercatat di GL |
| `isInventoryMaterial` | Exclude bahan baku inventori dari expense |
| Channel fee deduction | FIN-05: deduct marketplace/payment gateway fees |

### 2. Neraca (Balance Sheet)

**Component**: `BalanceSheetReport.jsx` (122 lines)

#### Struktur Neraca

```
┌─────────────────────────────────────┐
│ ASET (Assets)                       │
│ ├─ Aset Lancar                      │
│ │  ├─ Kas & Bank                    │
│ │  ├─ Piutang Usaha                 │
│ │  ├─ Persediaan                    │
│ │  └─ Beban Dibayar Dimuka          │
│ ├─ Aset Tetap                       │
│ │  ├─ Peralatan                     │
│ │  ├─ Kendaraan                     │
│ │  └─ Akumulasi Penyusutan          │
│ Total Aset: Rp XXX                  │
├─────────────────────────────────────┤
│ LIABILITAS (Liabilities)            │
│ ├─ Liabilitas Jangka Pendek         │
│ │  ├─ Hutang Usaha                  │
│ │  ├─ Hutang Pajak                  │
│ │  └─ Beban Masih Harus Dibayar     │
│ ├─ Liabilitas Jangka Panjang        │
│ │  └─ Hutang Bank                   │
│ Total Liabilitas: Rp XXX            │
├─────────────────────────────────────┤
│ EKUITAS (Equity)                    │
│ ├─ Modal Saham                      │
│ ├─ Laba Ditahan                     │
│ └─ Laba Tahun Berjalan              │
│ Total Ekuitas: Rp XXX               │
├─────────────────────────────────────┤
│ VALIDASI                            │
│ Total Aset = Total Liabilitas +     │
│              Ekuitas                │
│ |Assets - (Liabilities + Equity)| < 1│
└─────────────────────────────────────┘
```

#### Balance Check

```
Math.abs(totalAssets - (totalLiabilities + totalEquity)) < 1
```

Jika tidak balance, berarti ada kesalahan pencatatan.

### 3. Neraca Saldo (Trial Balance)

**Component**: `TrialBalanceReport.jsx` (117 lines)

#### Struktur Trial Balance

| Account Code | Account Name | Type | Debit | Kredit |
| - | - | - | - | - |
| 1-1000 | Kas | Asset | Rp 50.000.000 | - |
| 1-1100 | BCA | Asset | Rp 150.000.000 | - |
| 1-2000 | Piutang | Asset | Rp 30.000.000 | - |
| 2-1000 | Hutang Usaha | Liability | - | Rp 20.000.000 |
| 3-1000 | Modal | Equity | - | Rp 100.000.000 |
| 4-1000 | Penjualan | Revenue | - | Rp 200.000.000 |
| 5-1000 | HPP | Expense | Rp 80.000.000 | - |
| 5-2000 | Gaji | Expense | Rp 25.000.000 | - |
| 5-3000 | Sewa | Expense | Rp 15.000.000 | - |
| **Total** | | | **Rp 350.000.000** | **Rp 320.000.000** |

#### Validasi

```
Math.abs(totalDebit - totalCredit) < 1
```

#### Color Coding

| Type | Warna |
| - | - |
| Asset | Biru |
| Liability | Merah |
| Equity | Ungu |
| Revenue | Hijau |
| Expense | Oranye |

### 4. Budget vs Actual

**Component**: `BudgetVsActualReport.jsx` (201 lines)

#### Dual-Source Actual

Actual dihitung dari dua sumber:

| Source | Deskripsi |
| - | - |
| FinancialRecord | Per kategori |
| GL Journal | Expense accounts |

#### Category Aliases

Sistem menggunakan fuzzy matching untuk kategori:

| Canonical | Aliases |
| - | - |
| salary | gaji, beban gaji, upah |
| raw\_material | bahan baku, persediaan |
| operational | operasional, beban operasional |
| production\_cost | biaya produksi, HPP |

#### Status per Category

| Status | Condition | Warna |
| - | - | - |
| OK | actual ≤ 80% planned | Hijau |
| Warning | 80% \< actual ≤ 100% | Kuning |
| Over | actual > 100% planned | Merah |

#### Visualisasi

* **Bar chart**: Planned vs Actual per kategori
* **Variance line**: Selisih planned vs actual
* **Trend line**: Historical comparison

## Period Selector

### 5 Periode

| Period | Deskripsi |
| - | - |
| This Month | Bulan ini |
| Last Month | Bulan lalu |
| Last 3 Months | 3 bulan terakhir |
| This Year | Tahun ini |
| Custom | Custom date range |

## Comparison

### Perbandingan Antar Periode

| Comparison | Deskripsi |
| - | - |
| Current vs Previous | vs bulan/kuartal/tahun lalu |
| Current vs Budget | vs anggaran |
| Year-over-Year | vs tahun lalu (periode sama) |
| Trend Analysis | Tren dari waktu ke waktu |

### Output Comparison

| Metric | Current | Previous | Change | Change % |
| - | - | - | - | - |
| Revenue | Rp 100.000.000 | Rp 90.000.000 | +Rp 10.000.000 | +11.1% |
| COGS | Rp 40.000.000 | Rp 38.000.000 | +Rp 2.000.000 | +5.3% |
| Gross Profit | Rp 60.000.000 | Rp 52.000.000 | +Rp 8.000.000 | +15.4% |
| OpEx | Rp 30.000.000 | Rp 28.000.000 | +Rp 2.000.000 | +7.1% |
| Net Profit | Rp 30.000.000 | Rp 24.000.000 | +Rp 6.000.000 | +25.0% |

## Print Support

Setiap laporan memiliki **print-only header** yang muncul saat di-print:

| Component | Deskripsi |
| - | - |
| Company Logo | Logo perusahaan |
| Company Name | PT Selera Pedas Nusantara |
| Report Title | Judul laporan |
| Period | Periode laporan |
| Generated Date | Tanggal generate |
| Page Number | Nomor halaman |

## Export

| Format | Use Case |
| - | - |
| PDF | Arsip, presentasi, audit |
| Excel | Analisis lebih lanjut, manipulasi data |
| CSV | Integrasi dengan sistem lain |
| Print | Cetak langsung |

## Filter & Search

| Filter | Opsi |
| - | - |
| Report Type | P\&L, Neraca, Trial Balance, Budget vs Actual |
| Period | 5 opsi |
| Account | Pilih akun tertentu (untuk Trial Balance) |
| Category | Pilih kategori (untuk Budget vs Actual) |

## Best Practices

### Monthly Close

* Generate laporan bulanan secara rutin
* Pastikan seluruh transaksi sudah tercatat dan diposting
* Review completeness audit status
* Reconcile semua akun

### Analysis

* Gunakan perbandingan periode untuk identifikasi tren
* Investigasi variance signifikan
* Track KPI keuangan (margin, ROI, dll)
* Benchmark dengan industri

### Compliance

* Follow accounting standards (PSA)
* Keep records organized
* Prepare untuk audit
* Tax compliance

***

## Entity Relationship Diagram

```mermaid theme={null}
erDiagram
    FinancialReportSnapshot {
        string company_id PK
        string report_type "profit_loss | balance_sheet | cash_flow | budget_vs_actual | trial_balance | custom"
        string report_name
        date start_date
        date end_date
        string generated_by_user_id FK
        datetime generated_date
        object report_data "JSON data laporan"
        string template_id FK
        string notes
    }

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

    GLAccount {
        string company_id PK
        string account_code UK
        string account_name
        string account_type "asset | liability | equity | revenue | expense"
        string category
        string sub_category
        string parent_account_id FK
        string normal_balance "debit | credit"
        boolean is_active
        string description
        number level "0: header, 1: main, 2: sub"
    }

    FinancialRecord {
        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 attachment_url
        string source "manual | ai_text | ai_scan | pos | manufacturing | distribution | recall | stock_opname"
        string mode "personal | business"
        string transfer_to_account_id FK
        number transfer_fee
        string reference_id
        string reference_type
        string idempotency_key
        boolean is_inventory_material
        number channel_fee
        number cogs_amount
        number tax_amount
    }

    Account {
        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
        string icon
        string color
        boolean is_active
        boolean is_default_pos
        string notes
        string mode "personal | business"
    }

    Budget {
        string user_id FK
        string company_id FK
        string name
        string description
        string category
        number planned_amount
        number actual_amount
        string period "monthly | quarterly | yearly | custom"
        date start_date
        date end_date
        string status "active | completed | archived"
        number alert_threshold
    }

    FinancialReportSnapshot }o--|| GLJournalEntry : "mengambil data jurnal"
    FinancialReportSnapshot }o--|| FinancialRecord : "fallback source"
    GLJournalEntry }o--o{ GLAccount : "line_items.account_id"
    FinancialRecord }o--|| Account : "account_id (sumber dana)"
    FinancialRecord }o--o| Account : "transfer_to_account_id"
    Budget }o--|| FinancialRecord : "actual_amount dari transaksi"
    GLAccount }o--o| GLAccount : "parent_account_id (hirarki COA)"
```

***

## Tabel Schema

### FinancialReportSnapshot

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `company_id` | `string` | Ya | ID perusahaan pemilik laporan |
| `report_type` | `string` (enum) | Ya | Jenis laporan: `profit_loss`, `balance_sheet`, `cash_flow`, `budget_vs_actual`, `trial_balance`, `custom` |
| `report_name` | `string` | Ya | Nama laporan yang di-generate |
| `start_date` | `date` | Ya | Tanggal mulai periode laporan |
| `end_date` | `date` | Ya | Tanggal akhir periode laporan |
| `generated_by_user_id` | `string` | Ya | ID user yang men-generate laporan |
| `generated_date` | `datetime` | Ya | Timestamp kapan laporan di-generate |
| `report_data` | `object` (JSON) | Ya | Data hasil kalkulasi laporan (struktur bervariasi per tipe) |
| `template_id` | `string` | Tidak | ID template laporan (jika menggunakan template) |
| `notes` | `string` | Tidak | Catatan tambahan untuk laporan |

### FinancialRecord

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `user_id` | `string` | Ya | ID pengguna pencatat transaksi |
| `company_id` | `string` | Tidak | ID perusahaan (null untuk personal) |
| `account_id` | `string` | Tidak | ID rekening/kantong sumber dana |
| `type` | `string` (enum) | Ya | Jenis transaksi: `income`, `expense`, `transfer` |
| `amount` | `number` | Ya | Jumlah transaksi |
| `category` | `string` | Tidak | Kategori transaksi (untuk grouping & reporting) |
| `description` | `string` | Tidak | Deskripsi singkat transaksi |
| `date` | `datetime` | Ya | Tanggal dan waktu transaksi |
| `attachment_url` | `string` | Tidak | URL bukti transaksi (struk/faktur) |
| `source` | `string` (enum) | Tidak | Sumber transaksi: `manual`, `ai_text`, `ai_scan`, `pos`, `manufacturing`, `distribution`, `recall`, `stock_opname` |
| `mode` | `string` (enum) | Tidak | Mode pencatatan: `personal`, `business` |
| `transfer_to_account_id` | `string` | Tidak | ID rekening tujuan (khusus `type=transfer`) |
| `transfer_fee` | `number` | Tidak | Biaya transfer (default: 0) |
| `reference_id` | `string` | Tidak | ID referensi dokumen sumber (invoice, POS, expense) |
| `reference_type` | `string` (enum) | Tidak | Tipe referensi: `invoice_payment`, `pos_transaction`, `expense`, `manual`, `transfer`, `production_order`, `distribution_shipment`, `distribution_return`, `batch_recall`, `stock_opname` |
| `idempotency_key` | `string` | Tidak | Kunci idempoten untuk mencegah duplikasi |
| `is_inventory_material` | `boolean` | Tidak | Flag bahan baku inventori (anti double-counting COGS) |
| `channel_fee` | `number` | Tidak | Potongan biaya marketplace/platform fee |
| `cogs_amount` | `number` | Tidak | COGS/HPP yang dibekukan saat transaksi POS |
| `tax_amount` | `number` | Tidak | Jumlah pajak (PPN keluaran untuk revenue) |

### GLJournalEntry

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `company_id` | `string` | Ya | ID perusahaan |
| `entry_date` | `date` | Ya | Tanggal entri jurnal |
| `reference_number` | `string` | Tidak | Nomor referensi (TRX-001, INV-001, dll) |
| `reference_type` | `string` (enum) | Tidak | Tipe referensi: `manual`, `invoice`, `purchase_order`, `transfer`, `expense` |
| `reference_id` | `string` | Tidak | ID dokumen sumber |
| `description` | `string` | Ya | Deskripsi jurnal |
| `line_items` | `array[object]` | Ya | Line items dengan debit/credit per akun |
| `total_debit` | `number` | Tidak | Total debit (default: 0) |
| `total_credit` | `number` | Tidak | Total kredit (default: 0) |
| `is_balanced` | `boolean` | Tidak | Apakah debit = credit (default: false) |
| `status` | `string` (enum) | Tidak | Status entri: `draft`, `posted`, `reversed` |
| `posted_date` | `date` | Tidak | Tanggal posting ke GL |
| `posted_by` | `string` | Tidak | ID user yang memposting |
| `notes` | `string` | Tidak | Catatan tambahan |

#### Struktur `line_items[]`

| Field | Tipe | Deskripsi |
| - | - | - |
| `account_id` | `string` | ID GLAccount yang di-debit/kredit |
| `account_code` | `string` | Kode akun (denormalized untuk performa) |
| `debit` | `number` | Nilai debit (default: 0) |
| `credit` | `number` | Nilai kredit (default: 0) |
| `description` | `string` | Keterangan per line item |

### GLAccount

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

### Account (Rekening/Kantong)

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `user_id` | `string` | Ya | ID pengguna pemilik rekening |
| `company_id` | `string` | Tidak | ID perusahaan (null untuk personal) |
| `name` | `string` | Ya | Nama rekening (Kas, BCA, Mandiri, dll) |
| `type` | `string` (enum) | Ya | Jenis: `cash`, `bank`, `e-wallet`, `other` |
| `account_number` | `string` | Tidak | Nomor rekening |
| `bank_name` | `string` | Tidak | Nama bank (khusus `type=bank`) |
| `initial_balance` | `number` | Tidak | Saldo awal (default: 0) |
| `current_balance` | `number` | Tidak | Saldo terkini (server-authoritative) |
| `currency` | `string` | Tidak | Mata uang (default: IDR) |
| `icon` | `string` | Tidak | Icon UI (default: 💰) |
| `color` | `string` | Tidak | Warna UI (default: #3B82F6) |
| `is_active` | `boolean` | Tidak | Status aktif (default: true) |
| `is_default_pos` | `boolean` | Tidak | Rekening default POS Kasir (default: false) |
| `notes` | `string` | Tidak | Catatan tambahan |
| `mode` | `string` (enum) | Tidak | Mode: `personal`, `business` |

### Budget

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `user_id` | `string` | Ya | ID pengguna pemilik anggaran |
| `company_id` | `string` | Tidak | ID perusahaan |
| `name` | `string` | Ya | Nama anggaran |
| `description` | `string` | Tidak | Penjelasan detail (max 1000 karakter) |
| `category` | `string` | Ya | Kategori pengeluaran |
| `planned_amount` | `number` | Ya | Jumlah yang direncanakan |
| `actual_amount` | `number` | Tidak | Jumlah aktual (default: 0) |
| `period` | `string` (enum) | Tidak | Periode: `monthly`, `quarterly`, `yearly`, `custom` |
| `start_date` | `date` | Ya | Tanggal mulai periode |
| `end_date` | `date` | Ya | Tanggal akhir periode |
| `status` | `string` (enum) | Tidak | Status: `active`, `completed`, `archived` |
| `alert_threshold` | `number` | Tidak | Persentase threshold alert (default: 80) |

***

## State Machine — Report Generation Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> Idle: User membuka halaman Financial Reports

    state Idle {
        [*] --> SelectReport
        SelectReport --> SelectPeriod: Pilih tipe laporan
        SelectPeriod --> ConfigureFilter: Pilih periode & filter
        ConfigureFilter --> ReadyGenerate: Konfirmasi parameter
    }

    Idle --> Generating: Klik "Generate"

    state Generating {
        [*] --> ValidateParams
        ValidateParams --> CheckGLSource: Parameter valid
        CheckGLSource --> FetchGLData: GL Journal Entries ada
        CheckGLSource --> FetchFinancialRecord: GL tidak ada (fallback)
        FetchGLData --> CalculateBalances
        FetchFinancialRecord --> CalculateBalances
        CalculateBalances --> EvaluateCompleteness: Hitung saldo & total
        EvaluateCompleteness --> BuildReportData: audit status determined
        BuildReportData --> CreateSnapshot: Simpan snapshot
    }

    Generating --> SnapshotCreated: Snapshot berhasil dibuat
    Generating --> GenerateFailed: Error saat kalkulasi

    state SnapshotCreated {
        [*] --> DisplayReport
        DisplayReport --> CanExport: Laporan tampil di UI
        CanExport --> CanPrint: Pilih export format
        CanPrint --> CanExport: Print atau export
    }

    SnapshotCreated --> Idle: User generate laporan baru
    GenerateFailed --> Idle: Retry dari awal

    state CanExport {
        [*] --> ExportPDF
        [*] --> ExportExcel
        [*] --> ExportCSV
    }
```

### Transisi Status Snapshot

| Dari | Ke | Trigger | Kondisi |
| - | - | - | - |
| `[*]` | `generating` | User klik Generate | Parameter periode valid |
| `generating` | `created` | Snapshot tersimpan | Kalkulasi berhasil, data tersimpan |
| `generating` | `failed` | Error | Data sumber tidak cukup / kalkulasi error |
| `created` | `exported` | User export | PDF/Excel/CSV berhasil di-generate |
| `created` | `printed` | User print | Print layout aktif |
| `failed` | `[*]` | Retry | User kembali ke form |

***

## Sequence Diagrams

### Report Snapshot Generation

```mermaid theme={null}
sequenceDiagram
    actor User as User / Finance
    participant UI as FinancialReports.jsx
    participant Engine as Dual Source Engine
    participant GL as GLJournalEntry
    participant FR as FinancialRecord
    participant GLA as GLAccount
    participant DB as Snapshot DB

    User->>UI: Pilih laporan & periode
    UI->>Engine: generateReport(type, startDate, endDate)

    Engine->>GL: query({ entry_date, status: "posted" })
    GL-->>Engine: journal entries (jika ada)

    alt GL entries ditemukan
        Engine->>GLA: resolve account codes
        GLA-->>Engine: account mappings
        Engine->>Engine: aggregate by account_type
    else GL kosong (fallback)
        Engine->>FR: query({ date, company_id })
        FR-->>Engine: financial records
        Engine->>Engine: aggregate by category
    end

    Engine->>Engine: evaluateCompleteness()
    Note over Engine: Cek: missing journals,<br/>unposted drafts, COGS gaps

    Engine->>Engine: build report_data JSON
    Engine->>DB: save FinancialReportSnapshot
    DB-->>Engine: snapshot saved

    Engine-->>UI: { report_data, audit_status }
    UI-->>User: Tampilkan laporan + badge audited/provisional
```

### Balance Sheet (Neraca) Generation

```mermaid theme={null}
sequenceDiagram
    actor User as User / Finance
    participant UI as BalanceSheetReport.jsx
    participant Engine as Dual Source Engine
    participant GL as GLJournalEntry
    participant GLA as GLAccount
    participant FR as FinancialRecord

    User->>UI: Generate Neraca (periode)
    UI->>Engine: generateBalanceSheet(startDate, endDate)

    Engine->>GL: query posted entries in period
    GL-->>Engine: journal entries

    Engine->>GLA: get all accounts
    GLA-->>Engine: Chart of Accounts

    Engine->>Engine: Group by account_type

    rect rgb(200, 230, 255)
        Note over Engine: ASET (account_type = asset)
        Engine->>Engine: Sum debit balances
        Engine->>Engine: Separate Current vs Fixed Assets
    end

    rect rgb(255, 220, 220)
        Note over Engine: LIABILITAS (account_type = liability)
        Engine->>Engine: Sum credit balances
        Engine->>Engine: Separate Short-term vs Long-term
    end

    rect rgb(230, 220, 255)
        Note over Engine: EKUITAS (account_type = equity)
        Engine->>Engine: Sum credit balances
        Engine->>Engine: Add retained earnings + current period P&L
    end

    Engine->>Engine: Validate: |Assets - (Liabilities + Equity)| < 1

    alt Balance valid
        Engine-->>UI: { totalAssets, totalLiabilities, totalEquity, balanced: true }
    else Balance tidak valid
        Engine-->>UI: { totalAssets, totalLiabilities, totalEquity, balanced: false, warning }
    end

    UI-->>User: Tampilkan Neraca + status balance
```

### Profit & Loss (Laba Rugi) Generation

```mermaid theme={null}
sequenceDiagram
    actor User as User / Finance
    participant UI as ProfitLossReport.jsx
    participant Engine as Dual Source Engine
    participant GL as GLJournalEntry
    participant FR as FinancialRecord
    participant COGS as COGS Module
    participant CH as Channel Fee (FIN-05)

    User->>UI: Generate Laba Rugi (periode)
    UI->>Engine: generateProfitLoss(startDate, endDate)

    Engine->>GL: query posted entries in period
    GL-->>Engine: journal entries

    alt hasBookedCogs = true
        Engine->>COGS: get booked COGS from GL
        COGS-->>Engine: COGS amounts per account
    else hasBookedCogs = false
        Engine->>FR: query FinancialRecord
        FR-->>Engine: records with cogs_amount
        Engine->>COGS: extract COGS from records
        COGS-->>Engine: COGS amounts
    end

    Engine->>Engine: Calculate Revenue
    Note over Engine: account_type = revenue

    Engine->>Engine: Calculate COGS
    Note over Engine: Exclude isInventoryMaterial<br/>from expense accounts

    Engine->>Engine: Gross Profit = Revenue - COGS

    Engine->>Engine: Calculate Operating Expenses
    Note over Engine: account_type = expense<br/>(exclude COGS accounts)

    Engine->>CH: deduct channel fees
    CH-->>Engine: adjusted expenses

    Engine->>Engine: Operating Profit = Gross Profit - OpEx

    Engine->>Engine: Other Income/Expenses
    Engine->>Engine: Net Profit = Operating Profit + Other

    Engine->>Engine: evaluateCompleteness()
    Note over Engine: Check: missing journals,<br/>unposted drafts, COGS gaps,<br/>channel fees not recorded

    Engine-->>UI: { revenue, cogs, grossProfit, opEx, operatingProfit, netProfit, auditStatus }
    UI-->>User: Tampilkan P&L + badge audited/provisional
```

***

## Enum Tables

### `report_type` (FinancialReportSnapshot)

| Nilai | Deskripsi |
| - | - |
| `profit_loss` | Laporan Laba Rugi (P\&L) — pendapatan, HPP, beban operasional, laba bersih |
| `balance_sheet` | Neraca — aset, liabilitas, ekuitas pada titik waktu tertentu |
| `cash_flow` | Arus Kas — aliran masuk dan keluar kas |
| `budget_vs_actual` | Perbandingan anggaran vs realisasi per kategori |
| `trial_balance` | Neraca Saldo — daftar saldo semua akun GL untuk validasi |
| `custom` | Laporan kustom berdasarkan template |

### `period` (Budget & Report Period Selector)

| Nilai | Deskripsi |
| - | - |
| `monthly` | Periode bulanan |
| `quarterly` | Periode kuartalan (3 bulan) |
| `yearly` | Periode tahunan |
| `custom` | Periode kustom (date range manual) |

### `report_status` (Audit Completeness)

| Nilai | Deskripsi | Confidence |
| - | - | - |
| `audited` | Semua data lengkap, GL journals posted, tidak ada missing data | High |
| `provisional` | Ada data missing, GL tidak lengkap, atau unposted drafts | Medium |

### `status` (GLJournalEntry)

| Nilai | Deskripsi |
| - | - |
| `draft` | Entri belum diposting, masih dapat diedit |
| `posted` | Entri sudah diposting ke GL, mempengaruhi saldo akun |
| `reversed` | Entri dibalik (reversal) untuk koreksi |

### `reference_type` (GLJournalEntry)

| Nilai | Deskripsi |
| - | - |
| `manual` | Jurnal manual yang diinput langsung |
| `invoice` | Auto-generated dari invoice |
| `purchase_order` | Auto-generated dari purchase order |
| `transfer` | Dari transaksi transfer antar akun |
| `expense` | Dari pencatatan beban |

### `account_type` (GLAccount)

| Nilai | Saldo Normal | Deskripsi |
| - | - | - |
| `asset` | Debit | Aset/harta perusahaan |
| `liability` | Kredit | Kewajiban/hutang |
| `equity` | Kredit | Modal dan ekuitas pemilik |
| `revenue` | Kredit | Pendapatan/penjualan |
| `expense` | Debit | Beban/pengeluaran |

### `source` (FinancialRecord)

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

### `type` (FinancialRecord)

| Nilai | Deskripsi |
| - | - |
| `income` | Transaksi pemasukan/pendapatan |
| `expense` | Transaksi pengeluaran/beban |
| `transfer` | Transfer antar rekening |

### `reference_type` (FinancialRecord)

| Nilai | Deskripsi |
| - | - |
| `invoice_payment` | Pembayaran invoice |
| `pos_transaction` | Transaksi POS/Kasir |
| `expense` | Pencatatan beban |
| `manual` | Input manual |
| `transfer` | Transfer antar akun |
| `production_order` | Order produksi |
| `distribution_shipment` | Pengiriman distribusi |
| `distribution_return` | Retur distribusi |
| `batch_recall` | Penarikan batch produk |
| `stock_opname` | Stock opname |

### `type` (Account)

| Nilai | Deskripsi |
| - | - |
| `cash` | Kas/tunai |
| `bank` | Rekening bank |
| `e-wallet` | Dompet digital (GoPay, OVO, dll) |
| `other` | Lainnya |

### `status` (Budget)

| Nilai | Deskripsi |
| - | - |
| `active` | Anggaran aktif dan sedang berjalan |
| `completed` | Anggaran sudah selesai (periode berakhir) |
| `archived` | Anggaran diarsipkan |

***

## RBAC — Hak Akses Financial Reports

| Role | View Laporan | Generate Laporan | Export/Print | Edit GL Journal | Post Journal | Delete Journal | Manage GL Account |
| - | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
| **Super Admin** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Admin** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
| **Finance** | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Accounting** | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| **Manager** | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| **Viewer** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |

### Catatan RBAC

| Aturan | Deskripsi |
| - | - |
| Company-scoped | Semua data financial reports di-filter berdasarkan `company_id` user |
| Personal fallback | User dapat melihat FinancialRecord personal miliknya (`user_id = auth.uid`) |
| GL Journal posting | Hanya role Finance ke atas yang dapat memposting jurnal (`draft` → `posted`) |
| Journal reversal | Hanya Admin dan Super Admin yang dapat mem-reverse jurnal yang sudah posted |
| Snapshot immutability | FinancialReportSnapshot yang sudah dibuat tidak dapat diubah (append-only audit trail) |
| RLS FinancialRecord | Row-level security memfilter berdasarkan `company_id` atau `user_id` |


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