Spesifikasi Teknis Buku Besar
๐ ๏ธ SPESIFIKASI TEKNIS SISTEM & ARSITEKTUR
Modul Buku Besar & General Ledger Engine (Double-Entry Financial System)#
ERP BUMDes Mandiri Sejahtera 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: Memperhitungkan status sah
['approved', 'posted'], memastikan jurnal storno dan transaksi modul otomatis tercatat sempurna. - Pengurutan Deterministik: Saldo berjalan dipastikan stabil menggunakan pemeringkat tie-breaker
idA.localeCompare(idB).
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. 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.
๐ 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] |