Spesifikasi Teknis Buku Besar
๐ ๏ธ SPESIFIKASI TEKNIS SISTEM & ARSITEKTUR
Modul Buku Besar & General Ledger Engine (Double-Entry Financial System)#
BUMDes Bolulango โ Kabupaten Bone Bolango, Gorontalo Stack: Next.js 15 (App Router) + Supabase PostgreSQL + TypeScript (Strict) + Tailwind CSS + SafeMoney Engine
๐๏ธ 1. Arsitektur Komponen & Alur Data#
Sistem Buku Besar (General Ledger) merupakan inti pembukuan berpasangan (double-entry bookkeeping) yang mengonsolidasikan seluruh transaksi dari 20 unit usaha BUMDes secara real-time sesuai dengan Standar Akuntansi Keuangan Entitas Privat (SAK EP).
Memuat diagram alur...
โ๏ธ 2. Core Engine Specifications#
2.1. Double-Entry Posting & Normal Balance Engine
Setiap akun perkiraan memiliki karakteristik saldo normal (normal_balance: 'debit' atau 'credit'). Sistem menghitung posisi saldo berjalan (running balance) secara sekuensial berdasarkan urutan kronologis entry_date ASC:
Running Balance_i = Running Balance_{i-1} + Delta_i
Di mana:
- Akun Saldo Normal Debit (Aset, Beban, HPP): Delta_i = Debit_i - Credit_i
- Akun Saldo Normal Kredit (Kewajiban, Ekuitas, Pendapatan): Delta_i = Credit_i - Debit_i
2.2. Kepatuhan SAK EP: Isolasi Saldo Awal Akun Nominal vs Akun Riil
Sesuai Standar Akuntansi Keuangan Entitas Privat (SAK EP) dan prinsip tutup buku tahunan:
- Akun Nominal (Pendapatan & Beban / Akun Kepala 4, 5, 6, 7, 8, 9):
- Saldo akun nominal ditutup ke Ikhtisar Laba Rugi / Saldo Laba pada setiap akhir tahun buku.
- Saldo awal (beginning balance) akun nominal di Buku Besar dibatasi hanya sejak 1 Januari tahun fiskal berjalan (Year-to-Date / YTD):
entry_date >= ${selectedYear}-01-01danentry_date < startDate. - Transaksi pendapatan dan beban dari tahun-tahun sebelumnya tidak diperkenankan bocor ke saldo awal tahun berjalan.
- Akun Riil / Neraca (Aset, Kewajiban, Ekuitas / Akun Kepala 1, 2, 3):
- Saldo akun riil bersifat kumulatif sepanjang masa (all-time cumulative), dihitung dari awal mula pencatatan transaksi BUMDes (
entry_date < startDate).
2.3. Data Pump Streaming Pagination (Anti-Truncation Engine)
Secara default, Supabase/PostgREST menerapkan batas query sebesar 1.000 baris per request. Pada buku besar dengan volume transaksi historis tinggi, query biasa tanpa paginasi akan memotong kalkulasi saldo awal.
Modul ini mengimplementasikan pola Data Pump Pagination pada query Saldo Awal dan Mutasi Periode dengan pengurutan deterministik .order('id', { ascending: true }):
typescript// Cuplikan Data Pump Pagination pada getGeneralLedger() let begFrom = 0 const begStep = 1000 let begHasMore = true while (begHasMore) { const { data: chunk } = await begQuery.range(begFrom, begFrom + begStep - 1) if (!chunk || chunk.length === 0) { begHasMore = false } else { chunk.forEach((line: any) => { const debit = safeMoney(line.debit) const credit = safeMoney(line.credit) if (isNormalDebit) { beginningBalance = safeAdd(beginningBalance, safeSubtract(debit, credit)) } else { beginningBalance = safeAdd(beginningBalance, safeSubtract(credit, debit)) } }) if (chunk.length < begStep) begHasMore = false else begFrom += begStep } }
Karakteristik Kunci:
- Memory-Efficient: Kalkulasi saldo berjalan dilakukan per chunk, tanpa memuat seluruh baris historis ke dalam memori Node.js sekaligus.
- Infinite History: Mampu mengagregasi ratusan ribu mutasi saldo awal tanpa batas kuota PostgREST.
- Harmonisasi Status Jurnal (
approved,posted,reversed): Memperhitungkan status sah['approved', 'posted', 'reversed'], memastikan pasangan jurnal asal (status = 'reversed') dan jurnal pembalik Storno (status = 'approved') keduanya dihitung sehingga saling mengeliminasi menjadi Rp 0,00. - Pengurutan Deterministik: Saldo berjalan dipastikan stabil menggunakan pemeringkat tie-breaker
idA.localeCompare(idB). - Resolusi Metadata Akun Tanpa Mutasi: Apabila akun COA belum memiliki mutasi pada periode berjalan (
lines.length === 0),getGeneralLedger()tetap mengembalikan objekaccountInfo(code,name,account_type,normal_balance) besertabeginningBalancedanendingBalance.
2.4. SafeMoney Arithmetic Standard (Anti-IEEE 754 Floating Drift)
Operasi floating point bawaan JavaScript (0.1 + 0.2 = 0.30000000000000004) dapat menyebabkan akumulasi selisih sen (cents drift) pada buku besar dan laporan keuangan.
Seluruh perhitungan finansial distandardisasi menggunakan pustaka lib/accounting.ts:
| Fungsi | Rumus Aritmatika Presisi | Tujuan |
|---|---|---|
safeMoney(val) | Math.round(Number(val) * 100) / 100 | Sanitasi input dan pembulatan ke 2 desimal |
safeAdd(a, b) | (Math.round(a * 100) + Math.round(b * 100)) / 100 | Penjumlahan berbasis integer cent |
safeSubtract(a, b) | (Math.round(a * 100) - Math.round(b * 100)) / 100 | Pengurangan berbasis integer cent |
safeSum(array) | array.reduce(...) / 100 | Agregasi total debit/kredit tanpa residu floating point |
2.5. Mode Setahun Penuh (month=0), Standarisasi URL & Drill-Down COA
- Mode Audit Tahunan Penuh (
month=0):
- Buku Besar mendukung audit kumulatif satu tahun penuh melalui seleksi parameter
month=0("Semua Bulan / Setahun Penuh"). - Kalkulasi Saldo Awal: Dihitung tepat per 1 Januari tahun terpilih (
entry_date < ${selectedYear}-01-01). Untuk akun nominal (pendapatan/beban), saldo awal per 1 Januari otomatis bernilai Rp 0,00 sesuai prinsip tutup buku tahunan SAK EP. - Cakupan Mutasi: Mengambil seluruh transaksi jurnal dari 1 Januari hingga 31 Desember tahun fiskal berjalan dengan running balance kronologis tak terputus.
- Standarisasi Parameter
?unitId=& Fallback Kompatibilitas:
- URL parameter Buku Besar diseragamkan menggunakan key
?unitId=. - Untuk menjaga kompatibilitas tautan lama atau bookmark pengguna, Server Component (
page.tsx) dan Client Component (LedgerClient.tsx) menerapkan fallback:const targetUnitId = searchParams.unitId || searchParams.unit || 'all'.
- Drill-Down 1-Klik Terisolasi Unit dari Master COA:
- Halaman Daftar Akun (
COAClient.tsx) menyediakan tautan drill-down interaktif pada saldo akun. - Tautan otomatis membawa konteks unit kerja aktif:
/dashboard/keuangan/buku-besar?accountId={acc.id}&unitId={activeUnitId}&year=${selectedYear}, mencegah kekeliruan auditor melihat saldo unit lain.
- Konstanta Tahun Fiskal Terpusat:
- Pemilihan tahun fiskal memanfaatkan konstanta terpusat
FISCAL_YEARS = [2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030].
2.6. Prioritas Narasi Keterangan Baris Jurnal
Rincian mutasi buku besar mengutamakan keterangan spesifik baris transaksi (journal_line_items.description), contoh: "Beli 5 Rim Kertas A4 Sidu", sebelum melakukan fallback ke deskripsi umum voucher header (journal_entries.description), contoh: "Operasional Kantor Unit Toko".
2.6. Server-Side Prefetching (SSR) & Proteksi Audit
- Zero Flash Loading Flicker: Halaman
/dashboard/keuangan/buku-besarmelakukan prefetching data mutasi dan saldo awal di server (page.tsx), sehingga tampilan tabel langsung instan ter-render saat request pertama. - Badge Visual Audit: Nomor referensi transaksi dilengkapi penanda
AUTO(subsistem POS, Pinjaman USP, Billing Air) atauPOSTED. - Read-Only Navigation: Tautan nomor bukti voucher otomatis diarahkan ke filter pencarian jurnal guna memproteksi integritas transaksi double-entry antar subsistem dari perubahan manual yang tidak sah.
2.7. Benteng Integritas Database & Mesin Jurnal (Migrasi 267)
Untuk menjamin keutuhan Buku Besar dan kepatuhan SAK EP secara mutlak pada lapisan PostgreSQL maupun Server Actions:
- Imutabilitas Kode Akun COA (
trg_guard_coa_code_immutability):
- Trigger database
BEFORE UPDATE OF code OR DELETE ON public.coadan validasi Server ActionupdateAccount()menolak perubahan kode akun (code) maupun penghapusan baris COA apabila akun tersebut sudah memiliki transaksi historis dipublic.journal_line_items. - Format kode akun baru divalidasi dengan
COA_CODE_FORMAT_REGEX(^[1-8]-d{3}(\.d{2})?$).
- Penguncian Edit Jurnal Non-Draft (
update_journal_atomic&updateManualJournalEntry):
- Hanya jurnal berstatus
'draft'yang diizinkan diedit secara langsung. Jurnal yang telah berstatus'approved','posted','locked', atau'reversed'ditolak pada level RPC maupun Server Action; koreksi wajib melalui mekanisme Jurnal Pembalik (Storno / Reversal).
- Penguncian Periode Fiskal Dua Arah (
is_period_locked&check_period_lock_on_journal):
- Menangani jurnal level Holding (
target_unit_id IS NULL) dan memeriksa baik tanggal/unit asal (OLD) maupun tanggal/unit tujuan (NEW) saat operasiUPDATE, sehingga jurnal pada periode yang sudah ditutup buku tidak dapat digeser tanggalnya ke periode terbuka.
- Presisi Deteksi Akun Kas/Bank Likuid (
isCashOrBankAccountCode):
- Pengecekan saldo kas anti-minus dan aturan wajib 3 foto bukti kas keluar hanya menargetkan akun kas/bank likuid (
1-100,1-101,1-102,1-103beserta sub-akun.xx), sehingga tidak menghasilkan false-positive pada akun Piutang, Persediaan, Pajak Dibayar Dimuka, atau CKPN (1-104s/d1-199).
๐ 3. Keamanan & Multi-Unit RBAC#
3.1. Role Guard & Master CoA Filtering
Endpoint Server Actions dilindungi secara deklaratif dengan isolasi akun aktif dan unit bisnis:
typescriptexport async function getAccountsList(unitId?: string | null): Promise<AccountOption[]> { await requireRoleGuard() const supabase = createAdminClient() let query = supabase .from('coa') .select('id, code, name, unit_id, is_active') .eq('is_active', true) if (unitId && unitId !== 'all') { query = query.or(`unit_id.is.null,unit_id.eq.${unitId}`) } return (await query.order('code', { ascending: true })).data || [] }
3.2. Resolusi Hierarki Unit Bisnis
Akses data transaksi dibatasi berdasarkan hierarki unit:
- Pengguna Konsolidasi (
super_admin,direktur,bendahara,sekretaris,pengawas,auditor,admin_keuangan): Dapat melihat seluruh unit (unitId = 'all') atau memfilter per unit tertentu. - Pengguna Unit Terbatas (
manajer_unit,kepala_unit,admin_unit,kasir,admin_gudang,kolektor_usp,staf_unit): Terisolasi mutlak ke unit kerja mereka (assigned_unit_id). Opsi konsolidasi seluruh unit dinonaktifkan.
๐ 4. Spesifikasi Ekspor (PDF, Excel & CSV)#
Format ekspor buku besar diselaraskan 100% antara tampilan layar, PDF, CSV, dan Excel:
- Header Metadata:
- Judul Dokumen:
BUKU BESAR - Periode:
Periode: [Nama Bulan] [Tahun](contoh:Periode: September 2026) - Identitas Akun:
Akun: [Kode Akun] - [Nama Akun](contoh:Akun: 1-101 - Kas Operasional)
- Urutan Kolom Baku:
- Kolom:
Tanggal,No. Ref,Keterangan,Debit,Kredit,Saldo
- Baris Saldo Awal:
- Format:
['-', '-', 'Saldo Awal Periode', 0, 0, beginningBalance]
- Baris Total Akhir:
- Format:
['TOTAL DEBIT / KREDIT', '', '', totalDebit, totalCredit, endingBalance]
- Sanitasi Nama File:
- Menggunakan sanitasi regex
/[^a-zA-Z0-9_-]/guntuk menghindari karakter ilegal pada sistem operasi:Buku-Besar-[Nama_Akun]-[Periode].xlsx/.pdf
๐งช 5. Matriks Pengujian Otomatis#
Verifikasi modul buku besar dijamin oleh 14 unit test pada __tests__/actions/general_ledger_remediation.test.ts:
| ID Tes | Fokus Pengujian | Kriteria Lolos |
|---|---|---|
GL-F01 | Role Guard getAccountsList | Menolak akses tanpa otorisasi (throws Forbidden) |
GL-F01 | RBAC Authorized getAccountsList | Mengembalikan daftar akun saat otorisasi valid |
GL-F02 | Data Pump Pagination | Mengambil mutasi bertahap (chunk 1.000) dan menghitung saldo awal tepat |
GL-F03 | Excel Export Metadata & Urutan Kolom | Header periode, akun, dan kolom [Tanggal, No. Ref, Keterangan, Debit, Kredit, Saldo] |
GL-F05 | Floating Point Precision | Menghitung akumulasi desimal presisi tinggi tanpa drift IEEE 754 |
GL-F06 | SAK EP Nominal Account YTD | Saldo awal akun pendapatan/beban dibatasi mulai 1 Jan tahun berjalan |
GL-F06 | SAK EP Real Account All-Time | Saldo awal akun neraca (aset/kewajiban/ekuitas) berjalan kumulatif |
GL-F08 | Isolasi RBAC Unit Non-Holding | Mengunci role operasional (kasir, admin_gudang, manajer_unit) ke unitnya |
GL-F08 | Fleksibilitas RBAC Holding | Mengizinkan role holding (super_admin, direktur, bendahara) konsolidasi |
GL-F09 | Filter Master CoA Aktif | Menyaring akun dengan is_active: true |
GL-F09 | Isolasi CoA Spesifik Unit | Menyaring akun global + akun milik unit usaha aktif |
GL-F10 | Prioritas Narasi Baris Jurnal | Mengutamakan line.description di atas journal_entries.description |
GL-F11 | Audit Trail Flags & Proteksi | Mendeteksi reference_type, status, dan is_automated: true untuk subsistem |
GL-F11 | Standardisasi PDF Export | Memastikan tabel PDF menggunakan kolom [Tanggal, No. Ref, Keterangan, Debit, Kredit, Saldo] |