﻿# Finance App (gfin) — Schema & Module Planning Guide

> **Tahap:** Setelah `glims` selesai dibangun  
> **Tujuan:** Menggantikan Accurate secara penuh sebagai sistem akuntansi internal  
> **Arsitektur:** Laravel Modular Monolith + DDD + CQRS (sesuai `ARCHITECTURE_GUIDE.md`)

---

## Konvensi Tabel (Sudah Berlaku)

| Prefix | Kegunaan | Contoh |
|--------|----------|--------|
| `gfin_m_*` | Master data | `gfin_m_vendors` |
| `gfin_t_*` | Transaksi | `gfin_t_sales_invoices` |
| `gfin_h_*` | History / log | `gfin_h_journal_logs` |
| `gfin_p_*` | Pivot / relasi M:M | `gfin_p_invoice_orders` |
| `gfin_cfg_*` | Konfigurasi sistem | `gfin_cfg_tax_codes` |

**Kolom standar wajib di semua tabel:**
```sql
id              BIGINT UNSIGNED AUTO_INCREMENT PK
uuid            UUID UNIQUE
-- ... kolom bisnis ...
created_by      BIGINT UNSIGNED NULL
updated_by      BIGINT UNSIGNED NULL
deleted_by      BIGINT UNSIGNED NULL
updated_ip      VARCHAR(45) NULL
created_at      TIMESTAMP NULL
updated_at      TIMESTAMP NULL
deleted_at      TIMESTAMP NULL  -- soft delete
```

---

## Tabel yang Sudah Ada

```
gfin_m_banks
gfin_m_budgets
gfin_m_chart_of_accounts
gfin_m_source_accounts
gfin_m_users
gfin_h_budget_logs
gfin_t_fund_requests
gfin_t_fund_payment_attachments
gfin_t_fund_request_attachments
gfin_t_fund_request_report_attachments
gfin_t_journal_entries
gfin_t_journal_details
```

---

## Roadmap Phase

```
Phase 1 — Foundation (Siap sebelum Phase 2 dimulai)
Phase 2 — Accounts Payable & Receivable
Phase 3 — Cash, Bank & Payment Channel
Phase 4 — Perpajakan & Coretax
Phase 5 — Fixed Assets
Phase 6 — Cleanup (hapus Accurate modules)
```

---

## Phase 1 — Foundation

### Module: `AccountingPeriod`

**Tujuan:** Mengontrol periode buku aktif, lock transaksi periode lalu.

```sql
-- gfin_cfg_accounting_periods
id, uuid
year            SMALLINT NOT NULL          -- 2025, 2026
month           TINYINT NULL               -- 1–12 (null jika tahunan)
period_type     ENUM('monthly','annual')
status          ENUM('open','closed','locked')  DEFAULT 'open'
opened_at       TIMESTAMP NULL
closed_at       TIMESTAMP NULL
closed_by       BIGINT UNSIGNED NULL
notes           TEXT NULL
[standard cols]
```

**Aturan bisnis:**
- Transaksi tidak bisa diposting ke periode `closed` atau `locked`
- Tutup buku bulanan → status `closed`, tutup tahunan → `locked`
- Carry forward saldo awal otomatis ke periode berikutnya

---

### Module: `NumberingSequence`

**Tujuan:** Auto-numbering untuk semua dokumen (invoice, payment, memo, dll).

```sql
-- gfin_cfg_numbering_sequences
id, uuid
document_type       VARCHAR(50) UNIQUE NOT NULL  -- 'sales_invoice', 'vendor_payment', dll
prefix              VARCHAR(20) NOT NULL          -- 'SI', 'VP', 'CM'
format              VARCHAR(100) NOT NULL         -- '{PREFIX}/{YEAR}/{MONTH}/{SEQ:5}'
year_reset          BOOLEAN DEFAULT true          -- reset sequence setiap tahun
month_reset         BOOLEAN DEFAULT false         -- reset sequence setiap bulan
current_year        SMALLINT NULL
current_month       TINYINT NULL
last_sequence       INT DEFAULT 0
example             VARCHAR(100) NULL             -- 'SI/2026/04/00001'
is_active           BOOLEAN DEFAULT true
[standard cols]
```

**Aturan bisnis:**
- Sequence increment harus atomic (DB lock/transaction)
- Format bisa dikustomisasi per tipe dokumen
- Tersedia placeholder: `{PREFIX}`, `{YEAR}`, `{MONTH}`, `{SEQ:N}`

---

### Module: `OpeningBalance`

**Tujuan:** Input saldo awal saat migrasi dari Accurate.

```sql
-- gfin_t_opening_balances
id, uuid
accounting_period_id    BIGINT UNSIGNED FK → gfin_cfg_accounting_periods
chart_of_account_id     BIGINT UNSIGNED FK → gfin_m_chart_of_accounts
debit_amount            DECIMAL(18,2) DEFAULT 0
credit_amount           DECIMAL(18,2) DEFAULT 0
notes                   TEXT NULL
is_posted               BOOLEAN DEFAULT false
posted_at               TIMESTAMP NULL
posted_by               BIGINT UNSIGNED NULL
[standard cols]
```

---

### Module: `Currency`

**Tujuan:** Multi-currency dan kurs harian.

```sql
-- gfin_m_currencies
id, uuid
code            VARCHAR(3) UNIQUE NOT NULL  -- IDR, USD, EUR
name            VARCHAR(100)
symbol          VARCHAR(10)
is_base         BOOLEAN DEFAULT false       -- hanya 1 yang true (IDR)
is_active       BOOLEAN DEFAULT true
[standard cols]

-- gfin_t_exchange_rates
id, uuid
currency_id     BIGINT UNSIGNED FK → gfin_m_currencies
rate_date       DATE NOT NULL
rate            DECIMAL(18,6) NOT NULL      -- 1 USD = 16.250,xx IDR
source          ENUM('manual','bi_api','midrate')
[standard cols]
UNIQUE(currency_id, rate_date)
```

---

### Perluas: `ChartOfAccount`

Tambah kolom yang belum ada:

```sql
ALTER gfin_m_chart_of_accounts ADD:
account_category    ENUM('asset','liability','equity','revenue','expense','cogs')
is_header           BOOLEAN DEFAULT false    -- akun group (tidak bisa diposting)
level               TINYINT DEFAULT 1        -- kedalaman hierarki
position            INT NULL                 -- urutan tampilan laporan
allow_posting       BOOLEAN DEFAULT true
tax_code            VARCHAR(20) NULL         -- kode pajak default
currency_id         BIGINT UNSIGNED NULL FK  -- null = base currency
```

---

### Perluas: `JournalEntry`

Tambah kolom untuk reversal dan selisih kurs:

```sql
ALTER gfin_t_journal_entries ADD:
is_reversal         BOOLEAN DEFAULT false
reversed_entry_id   BIGINT UNSIGNED NULL FK → gfin_t_journal_entries  -- jurnal yang di-reverse
reversal_entry_id   BIGINT UNSIGNED NULL FK → gfin_t_journal_entries  -- pointer ke jurnal reversal
reversal_date       DATE NULL
reversal_reason     TEXT NULL
source_type         VARCHAR(50) NULL        -- 'sales_invoice','vendor_payment','depreciation',dll
source_id           BIGINT UNSIGNED NULL    -- id transaksi sumber auto-post
exchange_rate_diff  DECIMAL(18,2) DEFAULT 0 -- selisih kurs (jika multi-currency)
```

---

## Phase 2 — Accounts Payable (AP)

### Module: `Vendor`

```sql
-- gfin_m_vendors
id, uuid
code            VARCHAR(30) UNIQUE NOT NULL
name            VARCHAR(200) NOT NULL
npwp            VARCHAR(20) NULL
nik             VARCHAR(20) NULL            -- untuk vendor perorangan
address         TEXT NULL
city            VARCHAR(100) NULL
province        VARCHAR(100) NULL
postal_code     VARCHAR(10) NULL
phone           VARCHAR(30) NULL
email           VARCHAR(150) NULL
contact_person  VARCHAR(100) NULL
term_of_payment TINYINT DEFAULT 30          -- hari jatuh tempo
credit_limit    DECIMAL(18,2) DEFAULT 0
currency_id     BIGINT UNSIGNED NULL FK
ap_account_id   BIGINT UNSIGNED NULL FK → gfin_m_chart_of_accounts  -- akun hutang default
tax_type        ENUM('pkp','non_pkp') DEFAULT 'non_pkp'
is_active       BOOLEAN DEFAULT true
[standard cols]
```

---

### Module: `PurchaseInvoice`

```sql
-- gfin_t_purchase_invoices
id, uuid
invoice_number      VARCHAR(50) UNIQUE NOT NULL    -- nomor internal
vendor_invoice_no   VARCHAR(100) NULL              -- nomor faktur vendor
vendor_id           BIGINT UNSIGNED FK → gfin_m_vendors
accounting_period_id BIGINT UNSIGNED FK
invoice_date        DATE NOT NULL
due_date            DATE NOT NULL
currency_id         BIGINT UNSIGNED FK
exchange_rate       DECIMAL(18,6) DEFAULT 1
subtotal            DECIMAL(18,2) DEFAULT 0
tax_amount          DECIMAL(18,2) DEFAULT 0
total_amount        DECIMAL(18,2) DEFAULT 0
paid_amount         DECIMAL(18,2) DEFAULT 0
outstanding_amount  DECIMAL(18,2) GENERATED ALWAYS AS (total_amount - paid_amount)
status              ENUM('draft','posted','partial','paid','cancelled') DEFAULT 'draft'
ap_account_id       BIGINT UNSIGNED FK → gfin_m_chart_of_accounts
tax_invoice_no      VARCHAR(30) NULL               -- nomor faktur pajak masukan
notes               TEXT NULL
posted_at           TIMESTAMP NULL
posted_by           BIGINT UNSIGNED NULL
cancelled_at        TIMESTAMP NULL
cancelled_by        BIGINT UNSIGNED NULL
[standard cols]

-- gfin_t_purchase_invoice_lines
id, uuid
purchase_invoice_id BIGINT UNSIGNED FK
description         VARCHAR(500)
qty                 DECIMAL(18,4) DEFAULT 1
unit                VARCHAR(30) NULL
unit_price          DECIMAL(18,4) DEFAULT 0
discount_pct        DECIMAL(5,2) DEFAULT 0
discount_amount     DECIMAL(18,2) DEFAULT 0
subtotal            DECIMAL(18,2) DEFAULT 0
tax_code_id         BIGINT UNSIGNED NULL FK → gfin_cfg_tax_codes
tax_amount          DECIMAL(18,2) DEFAULT 0
total               DECIMAL(18,2) DEFAULT 0
expense_account_id  BIGINT UNSIGNED FK → gfin_m_chart_of_accounts
sort_order          TINYINT DEFAULT 0
[standard cols]

-- gfin_p_purchase_invoice_orders  (pivot ke PO dari modul lain)
id
purchase_invoice_id BIGINT UNSIGNED FK
purchase_order_id   BIGINT UNSIGNED NOT NULL   -- referensi ke PO (cross-module)
purchase_order_type VARCHAR(50) NOT NULL        -- 'procurement', 'glims', dst
qty_billed          DECIMAL(18,4)
amount_billed       DECIMAL(18,2)
created_at, updated_at
```

---

### Module: `VendorPayment`

```sql
-- gfin_t_vendor_payments
id, uuid
payment_number      VARCHAR(50) UNIQUE NOT NULL
vendor_id           BIGINT UNSIGNED FK → gfin_m_vendors
accounting_period_id BIGINT UNSIGNED FK
payment_date        DATE NOT NULL
bank_id             BIGINT UNSIGNED FK → gfin_m_banks
source_account_id   BIGINT UNSIGNED FK → gfin_m_source_accounts
total_amount        DECIMAL(18,2) DEFAULT 0
currency_id         BIGINT UNSIGNED FK
exchange_rate       DECIMAL(18,6) DEFAULT 1
payment_method      ENUM('transfer','cash','giro','cek')
reference_no        VARCHAR(100) NULL           -- no. referensi transfer/giro
status              ENUM('draft','posted','cancelled') DEFAULT 'draft'
notes               TEXT NULL
ap_account_id       BIGINT UNSIGNED FK
cash_account_id     BIGINT UNSIGNED FK
posted_at           TIMESTAMP NULL
posted_by           BIGINT UNSIGNED NULL
[standard cols]

-- gfin_p_vendor_payment_invoices  (satu payment bisa bayar banyak invoice)
id
vendor_payment_id       BIGINT UNSIGNED FK
purchase_invoice_id     BIGINT UNSIGNED FK
amount_applied          DECIMAL(18,2)
discount_taken          DECIMAL(18,2) DEFAULT 0
created_at, updated_at
```

---

### Module: `PurchaseCreditMemo`

```sql
-- gfin_t_purchase_credit_memos
id, uuid
memo_number             VARCHAR(50) UNIQUE NOT NULL
vendor_id               BIGINT UNSIGNED FK
purchase_invoice_id     BIGINT UNSIGNED NULL FK   -- referensi invoice asal
accounting_period_id    BIGINT UNSIGNED FK
memo_date               DATE NOT NULL
total_amount            DECIMAL(18,2) DEFAULT 0
applied_amount          DECIMAL(18,2) DEFAULT 0
status                  ENUM('draft','posted','applied','cancelled') DEFAULT 'draft'
reason                  TEXT NULL
notes                   TEXT NULL
posted_at               TIMESTAMP NULL
posted_by               BIGINT UNSIGNED NULL
[standard cols]
```

---

## Phase 2 — Accounts Receivable (AR)

### Module: `Customer`

> Dapat di-sync dari `glims` atau `ecustomer` via event/API

```sql
-- gfin_m_customers
id, uuid
code                VARCHAR(30) UNIQUE NOT NULL
name                VARCHAR(200) NOT NULL
npwp                VARCHAR(20) NULL
nik                 VARCHAR(20) NULL
address             TEXT NULL
city                VARCHAR(100) NULL
province            VARCHAR(100) NULL
postal_code         VARCHAR(10) NULL
phone               VARCHAR(30) NULL
email               VARCHAR(150) NULL
contact_person      VARCHAR(100) NULL
term_of_payment     TINYINT DEFAULT 30
credit_limit        DECIMAL(18,2) DEFAULT 0
currency_id         BIGINT UNSIGNED NULL FK
ar_account_id       BIGINT UNSIGNED NULL FK → gfin_m_chart_of_accounts
tax_type            ENUM('pkp','non_pkp') DEFAULT 'non_pkp'
is_active           BOOLEAN DEFAULT true
-- referensi ke sistem asal
source_app          VARCHAR(30) NULL            -- 'glims', 'ecustomer'
source_id           BIGINT UNSIGNED NULL        -- id di sistem asal
[standard cols]
```

---

### Module: `SalesInvoice`

```sql
-- gfin_t_sales_invoices
id, uuid
invoice_number      VARCHAR(50) UNIQUE NOT NULL
customer_id         BIGINT UNSIGNED FK → gfin_m_customers
accounting_period_id BIGINT UNSIGNED FK
invoice_date        DATE NOT NULL
due_date            DATE NOT NULL
currency_id         BIGINT UNSIGNED FK
exchange_rate       DECIMAL(18,6) DEFAULT 1
subtotal            DECIMAL(18,2) DEFAULT 0
discount_amount     DECIMAL(18,2) DEFAULT 0
tax_amount          DECIMAL(18,2) DEFAULT 0
total_amount        DECIMAL(18,2) DEFAULT 0
paid_amount         DECIMAL(18,2) DEFAULT 0
outstanding_amount  DECIMAL(18,2) GENERATED ALWAYS AS (total_amount - paid_amount)
status              ENUM('draft','posted','partial','paid','cancelled','overdue') DEFAULT 'draft'
ar_account_id       BIGINT UNSIGNED FK → gfin_m_chart_of_accounts
tax_invoice_id      BIGINT UNSIGNED NULL FK → gfin_t_tax_invoices
notes               TEXT NULL
posted_at           TIMESTAMP NULL
posted_by           BIGINT UNSIGNED NULL
cancelled_at        TIMESTAMP NULL
cancelled_by        BIGINT UNSIGNED NULL
[standard cols]

-- gfin_t_sales_invoice_lines
id, uuid
sales_invoice_id    BIGINT UNSIGNED FK
description         VARCHAR(500)
qty                 DECIMAL(18,4) DEFAULT 1
unit                VARCHAR(30) NULL
unit_price          DECIMAL(18,4) DEFAULT 0
discount_pct        DECIMAL(5,2) DEFAULT 0
discount_amount     DECIMAL(18,2) DEFAULT 0
subtotal            DECIMAL(18,2) DEFAULT 0
tax_code_id         BIGINT UNSIGNED NULL FK → gfin_cfg_tax_codes
tax_amount          DECIMAL(18,2) DEFAULT 0
total               DECIMAL(18,2) DEFAULT 0
revenue_account_id  BIGINT UNSIGNED FK → gfin_m_chart_of_accounts
sort_order          TINYINT DEFAULT 0
[standard cols]

-- gfin_p_sales_invoice_orders  (pivot M:M ke SO dari glims)
id
sales_invoice_id    BIGINT UNSIGNED FK
sales_order_id      BIGINT UNSIGNED NOT NULL    -- referensi ke SO di glims
sales_order_type    VARCHAR(50) NOT NULL         -- 'glims_lhu', 'glims_contract'
qty_billed          DECIMAL(18,4)
amount_billed       DECIMAL(18,2)
created_at, updated_at
```

---

### Module: `CustomerPayment` (Penerimaan Penjualan)

```sql
-- gfin_t_customer_payments
id, uuid
payment_number      VARCHAR(50) UNIQUE NOT NULL
customer_id         BIGINT UNSIGNED FK → gfin_m_customers
accounting_period_id BIGINT UNSIGNED FK
payment_date        DATE NOT NULL
bank_id             BIGINT UNSIGNED FK → gfin_m_banks
total_amount        DECIMAL(18,2) DEFAULT 0
currency_id         BIGINT UNSIGNED FK
exchange_rate       DECIMAL(18,6) DEFAULT 1
payment_method      ENUM('transfer','cash','va','giro','cek')
reference_no        VARCHAR(100) NULL
status              ENUM('draft','posted','cancelled') DEFAULT 'draft'
notes               TEXT NULL
ar_account_id       BIGINT UNSIGNED FK
cash_account_id     BIGINT UNSIGNED FK
payment_channel_id  BIGINT UNSIGNED NULL FK → gfin_t_payment_channels
posted_at           TIMESTAMP NULL
posted_by           BIGINT UNSIGNED NULL
[standard cols]

-- gfin_p_customer_payment_invoices  (satu payment bisa untuk banyak invoice)
id
customer_payment_id BIGINT UNSIGNED FK
sales_invoice_id    BIGINT UNSIGNED FK
amount_applied      DECIMAL(18,2)
discount_taken      DECIMAL(18,2) DEFAULT 0
created_at, updated_at
```

---

### Module: `SalesDebitMemo`

```sql
-- gfin_t_sales_debit_memos
id, uuid
memo_number             VARCHAR(50) UNIQUE NOT NULL
customer_id             BIGINT UNSIGNED FK
sales_invoice_id        BIGINT UNSIGNED NULL FK
accounting_period_id    BIGINT UNSIGNED FK
memo_date               DATE NOT NULL
total_amount            DECIMAL(18,2) DEFAULT 0
applied_amount          DECIMAL(18,2) DEFAULT 0
status                  ENUM('draft','posted','applied','cancelled') DEFAULT 'draft'
reason                  TEXT NULL
notes                   TEXT NULL
posted_at               TIMESTAMP NULL
posted_by               BIGINT UNSIGNED NULL
[standard cols]
```

---

## Phase 3 — Cash, Bank & Payment Channel

### Module: `BankReconciliation`

```sql
-- gfin_t_bank_reconciliations
id, uuid
bank_id                 BIGINT UNSIGNED FK → gfin_m_banks
accounting_period_id    BIGINT UNSIGNED FK
recon_date              DATE NOT NULL
statement_balance       DECIMAL(18,2)           -- saldo dari rekening koran
book_balance            DECIMAL(18,2)           -- saldo di buku
difference              DECIMAL(18,2) GENERATED ALWAYS AS (statement_balance - book_balance)
status                  ENUM('draft','reconciled') DEFAULT 'draft'
reconciled_at           TIMESTAMP NULL
reconciled_by           BIGINT UNSIGNED NULL
notes                   TEXT NULL
[standard cols]

-- gfin_t_bank_recon_items
id, uuid
bank_reconciliation_id  BIGINT UNSIGNED FK
type                    ENUM('outstanding_check','deposit_transit','bank_charge','bank_interest','error')
description             VARCHAR(300)
amount                  DECIMAL(18,2)
is_matched              BOOLEAN DEFAULT false
reference_id            BIGINT UNSIGNED NULL    -- FK ke transaksi terkait
reference_type          VARCHAR(100) NULL
[standard cols]
```

---

### Module: `CashAdvance` (diperluas dari `FundRequest`)

```sql
-- gfin_t_cash_advances  (refactor dari gfin_t_fund_requests)
id, uuid
advance_number          VARCHAR(50) UNIQUE NOT NULL
requester_id            BIGINT UNSIGNED FK      -- id user pemohon
department              VARCHAR(100) NULL
purpose                 TEXT
amount_requested        DECIMAL(18,2)
amount_approved         DECIMAL(18,2) NULL
amount_realized         DECIMAL(18,2) NULL      -- realisasi penggunaan
amount_returned         DECIMAL(18,2) NULL      -- sisa dikembalikan
status                  ENUM('draft','submitted','approved','disbursed','settled','cancelled')
bank_id                 BIGINT UNSIGNED NULL FK
payment_date            DATE NULL
settlement_date         DATE NULL
advance_account_id      BIGINT UNSIGNED FK → gfin_m_chart_of_accounts
expense_account_id      BIGINT UNSIGNED NULL FK
accounting_period_id    BIGINT UNSIGNED FK
notes                   TEXT NULL
[standard cols]
```

---

### Module: `PettyCash`

```sql
-- gfin_m_petty_cash_funds
id, uuid
name                    VARCHAR(100)
custodian_id            BIGINT UNSIGNED        -- penanggung jawab
bank_id                 BIGINT UNSIGNED FK
petty_cash_account_id   BIGINT UNSIGNED FK
fund_limit              DECIMAL(18,2)
current_balance         DECIMAL(18,2)
is_active               BOOLEAN DEFAULT true
[standard cols]

-- gfin_t_petty_cash_transactions
id, uuid
petty_cash_fund_id      BIGINT UNSIGNED FK
transaction_date        DATE NOT NULL
type                    ENUM('replenishment','expense','refund')
description             VARCHAR(300)
amount                  DECIMAL(18,2)
expense_account_id      BIGINT UNSIGNED NULL FK
reference_no            VARCHAR(100) NULL
accounting_period_id    BIGINT UNSIGNED FK
[standard cols]
```

---

### Module: `PaymentChannel` (VA & Integrasi Bank)

```sql
-- gfin_cfg_payment_gateways  (konfigurasi per bank/provider)
id, uuid
name                    VARCHAR(100)            -- 'BCA VA', 'Mandiri VA', 'QRIS'
provider                ENUM('bca','mandiri','bri','bni','qris','xendit','midtrans')
is_active               BOOLEAN DEFAULT true
config                  JSON                    -- client_id, secret (encrypted)
webhook_secret          VARCHAR(255) NULL       -- untuk verifikasi HMAC
[standard cols]

-- gfin_t_payment_channels  (satu per invoice)
id, uuid
sales_invoice_id        BIGINT UNSIGNED FK → gfin_t_sales_invoices
payment_gateway_id      BIGINT UNSIGNED FK → gfin_cfg_payment_gateways
va_number               VARCHAR(50) NULL
qr_code                 TEXT NULL
amount                  DECIMAL(18,2)
expired_at              TIMESTAMP NULL
status                  ENUM('pending','paid','expired','cancelled') DEFAULT 'pending'
paid_at                 TIMESTAMP NULL
external_reference      VARCHAR(200) NULL       -- referensi dari bank/provider
[standard cols]

-- gfin_h_payment_notifications  (log semua webhook dari bank — JANGAN dihapus)
id, uuid
payment_gateway_id      BIGINT UNSIGNED FK
raw_payload             JSON                    -- body asli dari webhook
headers                 JSON NULL               -- headers untuk debugging
signature               VARCHAR(500) NULL       -- HMAC yang diterima
is_valid_signature      BOOLEAN NULL
is_processed            BOOLEAN DEFAULT false
processed_at            TIMESTAMP NULL
error_message           TEXT NULL
created_at              TIMESTAMP
```

> **Keamanan:**  
> - Verifikasi HMAC signature sebelum proses webhook  
> - `config` pada `payment_gateways` di-encrypt via Laravel `encrypt()`  
> - `raw_payload` disimpan selalu untuk audit

---

## Phase 4 — Perpajakan & Coretax

### Module: `TaxSetup`

```sql
-- gfin_cfg_tax_codes
id, uuid
code                    VARCHAR(20) UNIQUE NOT NULL     -- 'PPN-12', 'PPH23-2'
name                    VARCHAR(100)
tax_type                ENUM('ppn','pph21','pph22','pph23','pph4a2','other')
rate                    DECIMAL(6,4)                    -- 0.1200 = 12%
is_collecting           BOOLEAN DEFAULT true            -- true=pungut, false=dipungut
account_id              BIGINT UNSIGNED FK → gfin_m_chart_of_accounts
is_active               BOOLEAN DEFAULT true
effective_date          DATE NOT NULL
expired_date            DATE NULL
[standard cols]

-- gfin_cfg_withholding_types  (kode objek pajak PPh)
id, uuid
code                    VARCHAR(20) UNIQUE NOT NULL     -- '23-01', '23-04'
name                    VARCHAR(200)
tax_type                ENUM('pph23','pph4a2','pph21','pph22')
rate_pkp                DECIMAL(6,4)                   -- tarif untuk ber-NPWP
rate_non_pkp            DECIMAL(6,4)                   -- tarif tanpa NPWP (1.5x)
is_active               BOOLEAN DEFAULT true
[standard cols]
```

---

### Module: `TaxInvoice` (Faktur Pajak — integrasi Coretax)

```sql
-- gfin_t_tax_invoices
id, uuid
tax_invoice_number      VARCHAR(30) NULL UNIQUE         -- nomor seri dari DJP
sales_invoice_id        BIGINT UNSIGNED NULL FK → gfin_t_sales_invoices
customer_id             BIGINT UNSIGNED FK → gfin_m_customers
invoice_date            DATE NOT NULL
dpp                     DECIMAL(18,2)                   -- Dasar Pengenaan Pajak
ppn_amount              DECIMAL(18,2)
ppn_percentage          DECIMAL(5,2) DEFAULT 12.00
customer_npwp           VARCHAR(20) NULL
customer_name           VARCHAR(200) NULL
customer_address        TEXT NULL
status                  ENUM('draft','submitted','approved','rejected','replaced','cancelled')
coretax_status          ENUM('not_submitted','pending','approved','rejected') DEFAULT 'not_submitted'
coretax_reference       VARCHAR(100) NULL               -- referensi dari Coretax API
coretax_submitted_at    TIMESTAMP NULL
coretax_response        JSON NULL                       -- respons lengkap dari API
notes                   TEXT NULL
[standard cols]

-- gfin_t_tax_invoices_incoming  (Faktur Pajak Masukan — dari PurchaseInvoice)
id, uuid
tax_invoice_number      VARCHAR(30) NULL UNIQUE
purchase_invoice_id     BIGINT UNSIGNED NULL FK → gfin_t_purchase_invoices
vendor_id               BIGINT UNSIGNED FK → gfin_m_vendors
invoice_date            DATE NOT NULL
dpp                     DECIMAL(18,2)
ppn_amount              DECIMAL(18,2)
ppn_percentage          DECIMAL(5,2) DEFAULT 12.00
vendor_npwp             VARCHAR(20) NULL
vendor_name             VARCHAR(200) NULL
status                  ENUM('draft','claimed','credited','rejected') DEFAULT 'draft'
coretax_status          ENUM('not_submitted','pending','approved','rejected') DEFAULT 'not_submitted'
coretax_reference       VARCHAR(100) NULL
coretax_submitted_at    TIMESTAMP NULL
coretax_response        JSON NULL
period_month            TINYINT NULL                    -- masa pajak
period_year             SMALLINT NULL
notes                   TEXT NULL
[standard cols]

-- gfin_h_coretax_api_logs  (log semua komunikasi ke Coretax)
id, uuid
tax_invoice_id          BIGINT UNSIGNED NULL FK
action                  VARCHAR(50)                     -- 'submit', 'cancel', 'replace'
request_payload         JSON
response_payload        JSON NULL
http_status             SMALLINT NULL
is_success              BOOLEAN NULL
error_message           TEXT NULL
created_at              TIMESTAMP
```

---

### Module: `Withholding` (Bukti Potong PPh)

```sql
-- gfin_t_withholding_certificates
id, uuid
certificate_number      VARCHAR(50) UNIQUE NOT NULL
vendor_id               BIGINT UNSIGNED FK → gfin_m_vendors
purchase_invoice_id     BIGINT UNSIGNED NULL FK
withholding_type_id     BIGINT UNSIGNED FK → gfin_cfg_withholding_types
certificate_date        DATE NOT NULL
dpp                     DECIMAL(18,2)
tax_rate                DECIMAL(6,4)
tax_amount              DECIMAL(18,2)
vendor_npwp             VARCHAR(20) NULL
vendor_name             VARCHAR(200) NULL
accounting_period_id    BIGINT UNSIGNED FK
status                  ENUM('draft','posted','cancelled') DEFAULT 'draft'
posted_at               TIMESTAMP NULL
[standard cols]
```

---

## Phase 5 — Fixed Assets

### Module: `FixedAsset`

```sql
-- gfin_m_asset_categories
id, uuid
code                    VARCHAR(20) UNIQUE NOT NULL
name                    VARCHAR(100)
depreciation_method     ENUM('straight_line','declining_balance')
useful_life_months      SMALLINT                        -- umur ekonomis dalam bulan
asset_account_id        BIGINT UNSIGNED FK              -- akun aset
accum_dep_account_id    BIGINT UNSIGNED FK              -- akun akumulasi penyusutan
dep_expense_account_id  BIGINT UNSIGNED FK              -- akun beban penyusutan
salvage_rate            DECIMAL(5,4) DEFAULT 0          -- nilai sisa (0–1)
is_active               BOOLEAN DEFAULT true
[standard cols]

-- gfin_m_fixed_assets
id, uuid
asset_code              VARCHAR(50) UNIQUE NOT NULL
name                    VARCHAR(200)
asset_category_id       BIGINT UNSIGNED FK → gfin_m_asset_categories
acquisition_date        DATE NOT NULL
acquisition_cost        DECIMAL(18,2)
salvage_value           DECIMAL(18,2) DEFAULT 0
current_book_value      DECIMAL(18,2)
accumulated_depreciation DECIMAL(18,2) DEFAULT 0
useful_life_months      SMALLINT
depreciation_start_date DATE NULL
location                VARCHAR(200) NULL
serial_number           VARCHAR(100) NULL
purchase_invoice_id     BIGINT UNSIGNED NULL FK → gfin_t_purchase_invoices
status                  ENUM('active','disposed','sold','damaged') DEFAULT 'active'
disposal_date           DATE NULL
disposal_amount         DECIMAL(18,2) NULL
notes                   TEXT NULL
[standard cols]

-- gfin_t_depreciation_schedules  (jadwal penyusutan per bulan)
id, uuid
fixed_asset_id          BIGINT UNSIGNED FK → gfin_m_fixed_assets
accounting_period_id    BIGINT UNSIGNED FK
depreciation_date       DATE NOT NULL
depreciation_amount     DECIMAL(18,2)
book_value_before       DECIMAL(18,2)
book_value_after        DECIMAL(18,2)
journal_entry_id        BIGINT UNSIGNED NULL FK → gfin_t_journal_entries
is_posted               BOOLEAN DEFAULT false
posted_at               TIMESTAMP NULL
[standard cols]
```

---

## Phase 6 — Cleanup

Setelah semua phase selesai dan data Accurate sudah dimigrasikan:

- [ ] Hapus module `AccurateIntegrationMonitoring`
- [ ] Hapus module `AccuratePaymentMonitoring`
- [ ] Drop tabel terkait integrasi Accurate setelah verified
- [ ] Update `FINANCE_SCHEMA_PLANNING_GUIDE.md` dengan status final

---

## Posting Rule (Journal Auto-Post)

Setiap transaksi yang di-`post` wajib menghasilkan `JournalEntry` + `JournalDetail` otomatis:

| Transaksi | Debet | Kredit |
|-----------|-------|--------|
| SalesInvoice posted | Piutang Usaha (AR) | Pendapatan |
| CustomerPayment posted | Kas/Bank | Piutang Usaha (AR) |
| PurchaseInvoice posted | Beban/Persediaan | Hutang Usaha (AP) |
| VendorPayment posted | Hutang Usaha (AP) | Kas/Bank |
| CashAdvance disbursed | Uang Muka | Kas/Bank |
| CashAdvance settled | Beban | Uang Muka |
| Depreciation posted | Beban Penyusutan | Akum. Penyusutan |
| TaxInvoice ppn | PPN Keluaran | — (sudah dalam SalesInvoice) |
| Withholding posted | Hutang PPh | Kas/Bank |
| PettyCash expense | Beban | Kas Kecil |
| PettyCash replenishment | Kas Kecil | Kas/Bank |
| PurchaseCreditMemo posted | Hutang Usaha (AP) | Beban/Return |
| SalesDebitMemo posted | Piutang Usaha (AR) | Pendapatan/Koreksi |
| BankRecon bank charge | Beban Administrasi Bank | Kas/Bank |
| BankRecon bank interest | Kas/Bank | Pendapatan Bunga |
| Selisih kurs (gain) | Kas/Bank | Pendapatan Selisih Kurs |
| Selisih kurs (loss) | Beban Selisih Kurs | Kas/Bank |

> Semua posting wajib menggunakan `AccountingPeriod` yang `open`, dan wajib lock jika periode `closed`.

---

## Integrasi Antar Aplikasi

```
glims (LHU / Sales Order)
    → event: SalesOrderCompleted
    → gfin: auto-create SalesInvoice (draft)
    → gfin: sync Customer data

glims / ecustomer
    → event: CustomerUpdated
    → gfin: update gfin_m_customers (source_app='glims')

gfin (SalesInvoice posted)
    → generate TaxInvoice (draft)
    → push ke Coretax API (async Queue)
    → generate PaymentChannel VA (async)

Bank Webhook → gfin
    → verifikasi HMAC signature
    → log ke gfin_h_payment_notifications
    → update PaymentChannel status
    → auto-post CustomerPayment
```

---

## Laporan Standar (Module `Reporting` — diperluas)

| Laporan | Query Basis |
|---------|------------|
| Neraca (Balance Sheet) | `gfin_m_chart_of_accounts` + `gfin_t_journal_details` group by type |
| Laba Rugi (P&L) | Revenue & Expense accounts per periode |
| Arus Kas (Cash Flow) | Mutasi akun Kas & Bank |
| Buku Besar (General Ledger) | `journal_details` per akun |
| Trial Balance | Saldo debet/kredit semua akun |
| Aging Piutang | `sales_invoices` `outstanding_amount > 0` group by umur |
| Aging Hutang | `purchase_invoices` `outstanding_amount > 0` group by umur |
| Realisasi Anggaran | `gfin_m_budgets` vs `journal_details` |
| Rekap PPN | `tax_invoices` per periode |
| Rekap PPh | `withholding_certificates` per periode |

---

## Urutan Development per Phase

```
Setiap phase ikuti urutan:
1. Migration (tabel baru)
2. Domain/Models
3. Domain/Enums (status, type)
4. Domain/Events
5. Domain/Contracts (RepositoryInterface)
6. Application/Actions (Commands)
7. Application/Queries
8. Infrastructure/Repositories
9. Infrastructure/Routes + Controllers
10. Resources/Pages (React + Inertia)
11. Tests (Feature test per use case)
```
