Spesifikasi Teknis Buku Kas Umum (BKU)
๐ ๏ธ SPESIFIKASI TEKNIS SISTEM & ARSITEKTUR
Modul Buku Kas Umum & Cash Subledger Engine (Single-Point Entry)#
ERP BUMDes Bolulango Stack: Next.js 15 (App Router) + Supabase PostgreSQL (Migration 265) + TypeScript (Strict) + Tailwind CSS + SafeMoney Engine
๐๏ธ 1. Arsitektur Komponen & Alur Data#
Modul Buku Kas Umum (BKU) merupakan pilar utama pengelolaan kas dan bank (Cash & Bank Subledger) dalam sistem ERP BUMDes Bolulango. BKU berfungsi sebagai instrumen pencatatan kronologis mutasi kas riil (uang tunai brankas dan saldo rekening bank operasional) dari 20+ unit usaha secara real-time yang dapat direkonsiliasi sempurna dengan Buku Besar (General Ledger) akun kas 1-1xxx, Neraca Saldo, Laba Rugi, Neraca, dan Laporan Arus Kas.
Memuat diagram alur...
โ๏ธ 2. Core Engine Specifications#
2.1. Identifikasi Akun Kas & Bank serta Resolusi Akun Global (unit_id IS NULL)
Untuk menjamin tidak ada akun non-kas yang mencemari BKU sekaligus mendukung skema pemetaan COA hibrida (akun kas spesifik unit + akun kas global holding), kueri akun menggunakan penyaringan spesifik pada tabel coa:
typescriptlet accountQuery = supabase .from('coa') .select('id, code, name, unit_id') .or('code.like.1-100%,code.like.1-101%,code.like.1-102%,code.like.1-103%,code.eq.1101,code.eq.1102') .ilike('account_type', 'asset') if (targetUnitId) { if (consolidated && allowedUnitIds.length > 0) { accountQuery = accountQuery.or(`unit_id.in.(${allowedUnitIds.join(',')}),unit_id.is.null`) } else { accountQuery = accountQuery.or(`unit_id.eq.${targetUnitId},unit_id.is.null`) } }
Kriteria ini mencakup:
1-100%: Kas Tunai Utama / Brankas Kantor Holding1-101%: Kas Operasional / Kas Kasir Unit1-102%: Kas Bank Operasional (Bank Mandiri, Bank BRI, BPD)1-103%: Kas Kecil / Petty Cash Perbendaharaan1101,1102: Fallback format kode numerik 4 digit- Filter Posisi Kas Unit (
shouldIncludeAccountPosition): Karena kueri menyertakan akun COA global (unit_id IS NULL), setiap barisjournal_line_itemstetap difilter ketat berdasarkanjournal_entries.unit_id(danline.unit_id), sehingga akun kas global yang dipakai dalam jurnal unit dihitung secara presisi tanpa membocorkan saldo unit lain.
2.2. Arsitektur Storno 2-Tahap, Sinkronisasi Lintas Laporan, & Migrasi SQL 265
Untuk mengeliminasi selisih saldo (unreconciled variance) antara BKU, Buku Besar, Laporan Keuangan SAK EP, dan mesin Tutup Buku Akhir Tahun:
- Siklus Hidup Status Transaksi (
approved,posted, &reversed):
- Jurnal Manual Terverifikasi: Memiliki status
approved. - Jurnal Otomatis Subsistem: Transaksi dari mesin POS kasir, pembayaran piutang simpan pinjam USP, pembagian SHU, dan transfer antar-brankas memiliki status
posted. - Jurnal Historis Terkoreksi (Storno 2-Tahap):
- Melalui
reverseJournalEntry()dilib/actions/journal-actions.ts(prefixJRN-REV-) maupun RPCrpc_void_treasury_transaction()(prefixVOID-), jurnal asli yang dibatalkan diubah statusnya menjadireverseddan dipasangkan dengan jurnal pembalik baru berstatusapproved/posted. - Keduanya tetap ditarik ke dalam laporan sehingga jejak audit (audit trail) historis utuh dan efek bersih terhadap saldo kas/buku besar bernilai Rp 0.
- Sinkronisasi Filter Status pada 10 Laporan Keuangan & AI CaLK:
Seluruh kueri pembacaan laporan keuangan di
lib/actions/finance/report.ts,lib/actions/finance/closing.ts,lib/actions/closing.ts,lib/actions/units.ts, danlib/actions/ai_calk.tstelah diseragamkan menggunakan:
typescript.in('journal_entries.status', ['approved', 'posted', 'reversed'])
Cakupan sinkronisasi mencakup: (1) BKU, (2) Subledger Transaksi Unit Terakhir (getRecentUnitTransactions), (3) Buku Besar (getGeneralLedger), (4) Neraca Saldo (getTrialBalance), (5) Kertas Kerja 10 Kolom (getWorksheet10Column), (6) Laba Rugi (getIncomeStatement), (7) Neraca (getBalanceSheet), (8) Laporan Arus Kas (getCashFlowReport), (9) Laporan Perubahan Ekuitas (getEquityChangesReport), (10) Ringkasan Eksekutif & Rasio Keuangan, serta (11) Generator AI Catatan atas Laporan Keuangan (generateCaLKSections).
- Sinkronisasi Lapisan Database PostgreSQL (
supabase/migrations/265_bku_storno_and_rpc_status_remediation.sql): Enam fungsi RPC PostgreSQL di tingkat basis data telah diperbarui agar menyertakan'reversed'dan pola Storno 2-Tahap:
public.rpc_void_treasury_transaction: Mengizinkan pembatalan transaksi berstatuspostedmaupunapproved, menandai jurnal asal sebagai'reversed', dan menerbitkan jurnal pembalikVOID-berstatus'posted'.public.get_closing_balances: Menyertakanstatus IN ('approved', 'posted', 'reversed')untuk perhitungan ikhtisar laba rugi tutup buku akhir tahun.public.get_snapshot_balances: Menyertakanstatus IN ('approved', 'posted', 'reversed')untuk pembuatan snapshot saldo bulanan/tahunan.public.get_trial_balance: Menyertakanstatus IN ('approved', 'posted', 'reversed')pada RPC Neraca Saldo database.public.get_account_balance_at_date: Menyertakanstatus IN ('approved', 'posted', 'reversed')untuk pengecekan saldo per tanggal cut-off.public.check_inventory_integrity: Menyertakanstatus IN ('approved', 'posted', 'reversed')pada rekonsiliasi nilai persediaan fisik vs buku besar.
2.3. Data Pump Streaming Pagination (Anti-Truncation Engine)
PostgREST Supabase menerapkan batas default 1.000 baris per kueri. Tanpa streaming batching, entitas dengan ribuan transaksi historis akan mengalami pemotongan data saldo awal secara senyap (silent truncation).
BKU menerapkan loop Data Pump Pagination dengan klausa deterministik .order('id', { ascending: true }) pada kueri saldo awal dan transaksi mutasi:
typescript// Saldo Awal Streaming Loop 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) beginningBalance = safeAdd(beginningBalance, safeSubtract(debit, credit)) }) if (chunk.length < begStep) begHasMore = false else begFrom += begStep } }
2.4. SafeMoney Arithmetic Standard (Anti-IEEE 754 Floating Drift)
Perhitungan kas tidak diperkenankan menggunakan operator bawaan JavaScript biasa guna mencegah cents drift.
Seluruh penjumlahan dan pengurangan saldo kas menggunakan fungsi presisi dari lib/accounting.ts:
safeMoney(val): Pembulatan sanitasi ke 2 desimal.safeAdd(a, b): Penjumlahan berbasis sen integer.safeSubtract(a, b): Pengurangan berbasis sen integer.
Formula Running Balance BKU:
Saldo Berjalan_i = Saldo Berjalan_{i-1} + Debit_i - Credit_i
2.5. Prioritas Deskripsi Baris Transaksi
Untuk memberikan visibilitas penuh bagi auditor dan pengawas desa, narasi transaksi diutamakan dari rincian pos belanja kas (line.description) sebelum melakukan fallback ke judul umum voucher (journal_entries.description):
typescriptconst effectiveDescription = (line.description || '').trim() || (line.journal_entries?.description || '').trim() || '-'
2.6. Pengurutan Kronologis Multi-Tier Deterministik
Mutasi kas diurutkan secara ketat pada memori backend agar merefleksikan urutan fisik transaksi di lapangan:
- Tanggal Transaksi (
entry_date ASC) - Waktu Pembuatan (
created_at ASC) - Nomor Bukti Voucher (
entry_number ASC) - Aliran Kas (Debit / Uang Masuk didahulukan sebelum Kredit / Uang Keluar)
- Tie-Breaker Deterministik (
id ASC)
2.7. Paralelisasi Backend & Reaktivitas Klien Non-Blocking (v2026.4)
Untuk mengatasi latensi pemuatan laporan dan memastikan pergantian filter (unit usaha, bulan, tahun) berlangsung instan tanpa jeda layar:
- Paralelisasi Kueri Backend (
Promise.all): Empat proses mandiri di dalamgetBKUReport()dieksekusi secara simultan:
fetchBeginningBalance(): Agregasi saldo awal via data pumpfetchPeriodTransactions(): Pengambilan mutasi transaksi periode berjalanfetchBusinessUnits(): Resolusi metadata nama unit usahafetchClosingStatus(): Verifikasi status tutup kas dan profil entitas Waktu eksekusi backend terpangkas hingga ~80% (dari 52.7 detik menjadi 9.5 detik pada test suite).
- Reaktivitas Klien Bebas Freeze (Zero-Latency Client Reactivity):
- Menghapus sinkronisasi
router.replacedenganuseTransitionyang memicu eksekusi ulang Server Component Next.js dan efek layar membeku (opacity-60). - Menggunakan
window.history.replaceStateuntuk sinkronisasi senyap (silent update) parameter URL, dipadukan dengan pemanggilan Server Action mandiri di sisi klien. - Mengimplementasikan
activeFetchIdRefuntuk perlindungan terhadap race condition saat pengguna mengganti filter dengan cepat. - Mengeliminasi state desync loop di mana state lokal tertimpa oleh data basi (stale initialData).
๐ 3. Keamanan & Multi-Unit RBAC#
3.1. Isolasi Akses Tingkat Server Action
Fungsi getBKUReport() mengintegrasikan resolveFinanceRBAC(unitId) secara wajib:
typescriptexport async function getBKUReport(unitId: string = 'all', startDate?: string, endDate?: string, consolidated: boolean = false) { await requireRoleGuard() const { effectiveUnitId, isRestricted } = await resolveFinanceRBAC(unitId) const targetUnitId = effectiveUnitId === 'all' ? null : effectiveUnitId // Jika pengguna operasional terisolasi tapi tidak memiliki unit asosiasi, tolak akses dengan payload kosong if (targetUnitId === 'none') { return { beginningBalance: 0, entries: [], finalBalance: 0, isClosed: false } }
3.2. Whitelist Hak Akses Holding vs Unit
Halaman app/dashboard/keuangan/bku/page.tsx membagi pengguna ke dalam dua kelompok hak akses:
- Holding Whitelist (
HOLDING_ROLES):super_admin,superadmin,direktur,bendahara,sekretaris,pengawas,pengawas_1,pengawas_2,auditor,admin_keuangan,admin_keuangan_pusat. Hak: Bebas melihat BKU konsolidasi holding atau memilih salah satu dari 20 unit usaha. - Unit Restricted (
ROLES_RESTRICTED_FALLBACK):manajer_unit,kepala_unit,kepala_sub_unit,admin_unit,kasir,kasir_toko,admin_gudang,kolektor_usp,koordinator_kandang,anak_kandang,unit_manager,staf_unit. Hak: Terkunci mutlak pada unit kerja masing-masing (assigned_unit_id). Percobaan injeksi parameterunitId=alldiabaikan dan dipaksa kembali ke unit miliknya.
3.3. Proteksi Rute Cetak & Anti-Spoofing Parameter unitId
Rute cetak web /print/buku-kas dan seluruh sub-rute /print/* diproteksi secara berlapis untuk mencegah kebocoran data (information disclosure) maupun pemalsuan identitas unit (unit metadata spoofing):
- Edge Middleware (
middleware.ts&lib/supabase/middleware.ts): Mencegat akses tanpa autentikasi ke pola rute/print/:path*dan mengarahkan ke halaman login dengan preservasi query parameterreturnUrl. - Server Action & Page Role Guard (
requireRoleGuard()): Memverifikasi sesi aktif dan otorisasi role sebelum merender laporan buku kas di sisi server. - Anti-Spoofing Parameter
unitId(validatedUnitId): Padaapp/print/buku-kas/page.tsx, parameter queryunitIddivalidasi terhadap peran pengguna (HOLDING_ROLESvsassigned_unit_id). Pengguna unit operasional yang mencoba menyuntikkan?unitId=<unit-lain>pada URL dipaksa kembali kevalidatedUnitId = assigned_unit_idbaik untuk kueri transaksi BKU, resolusi nama unit header, maupun struktur tanda tangan pejabat.
๐ 4. Spesifikasi Ekspor & Pencetakan Dokumen#
4.1. Harmonisasi Kolom, Berita Acara Adaptif, & Lembar Pengesahan Pejabat (Print Web, Excel, & PDF)
Untuk menjamin konsistensi 100% antara tampilan cetak browser (Print Web), lembar kerja spreadsheet (Excel), dan dokumen portabel (PDF), sistem menerapkan aturan penyelarasan terpusat:
- Deteksi Holding vs Unit Operasional:
- Jika unit adalah
Semua Unit (Konsolidasi),Kantor Pusat BUMDes (Holding), atau parameter kosong: - Pejabat Kiri:
Mengetahui,danDirektur BUMDes Bolulango(BINTANG RAIHAN GOBEL). - Pejabat Kanan:
Bandungan, [Tanggal Cetak]danBendahara BUMDes(ANSHORUDDIN YUSUF). - Jika unit adalah unit bisnis operasional (misalnya Unit Peternakan Ayam Petelur):
- Pejabat Kiri:
Mengetahui,danKepala Unit [Nama Unit]. - Pejabat Kanan:
Bandungan, [Tanggal Cetak]danBendahara Unit [Nama Unit].
- Standar Tipografi & Format Nama Pejabat:
- Bebas Tanda Kurung: Format kurung
( ... )ditiadakan agar menghasilkan tampilan korporat modern. - Huruf Kapital Penuh (Uppercase) & Tebal (Bold): Nama pejabat dicetak dengan huruf besar tebal (contoh:
BINTANG RAIHAN GOBEL). - Garis Bawah Presisi (Underline): Garis bawah dicetak sepanjang teks nama resmi. Pada Excel menggunakan OpenXML
underline: true, pada PDF menggunakan garis vektor dinamis berbasis kalkulasigetTextWidth(), dan pada Cetak Web menggunakan border bawah formal.
- Status Dinamis Berita Acara Penutupan Kas (Print Web & PDF):
- Apabila
Math.abs(finalBalance) < 0.01, label status menampilkanSTATUS: RESMI DITUTUP (Rp 0 Klop). - Apabila masih terdapat saldo kas akhir berjalan (
Math.abs(finalBalance) >= 0.01), label status menampilkanSTATUS: POSISI KAS BERJALAN (Saldo Buku).
- Format Lembar Kerja Excel (
ExcelExporter.addTableSheet):
- Kop Surat Resmi 10 Baris: Memuat identitas BUMDes, alamat domisili, kontak, garis pembatas ganda, judul laporan, nama unit, dan periode.
- Format Mata Uang Akuntansi: Seluruh kolom nominal menggunakan format akuntansi
"Rp. "#,##0;("Rp. "#,##0);"-". - Tata Letak Pengesahan Simetris: Kolom kiri digabung (merged) pada kolom A..D, dan kolom kanan pada kolom E..H dengan perataan tengah (center aligned) dan borderless.
- Dynamic Running Balance Formula: Formula spreadsheet dinamis
=H{prev}+F{curr}-G{curr}diinjeksi pada data non-filter.
- Kanal Ekspor Dokumen:
- Print Web (
/print/buku-kas): Memanfaatkan komponenFormalSignaturesyang terintegrasi langsung dengangetManagementStructure()untuk resolusi pejabat otomatis. - Ekspor PDF (
exportBKUPDF): Tabel 8 kolom standar A4 landscape dengan penyesuaian otomatis untuk Buku Kas Pembantu Bank (tanpa konsideran uang tunai) dan Buku Kas Pembantu Kas (tanpa saldo simpanan bank). - Ekspor Excel (
exportBKUExcel): Menghasilkan workbook OpenXML berkecepatan tinggi dengan rekapitulasi mutasi dan formula dinamis. - Ekspor CSV (
exportToCSV): Skema 8 kolom delimitasi koma standar untuk integrasi aplikasi perpajakan dan audit eksternal.
๐งช 5. Matriks Verifikasi & Pengujian Otomatis#
Modul BKU dilindungi oleh 24 fokus pengujian otomatis (38 unit tests aktif) pada __tests__/actions/bku_remediation.test.ts:
| ID Tes | Fokus Pengujian | Kriteria Lolos |
|---|---|---|
BKU-T01 | Role Guard & Isolasi RBAC | Menolak pengguna anonim & mengunci akun kasir ke unit sendiri |
BKU-T02 | Harmonisasi Status posted | Transaksi approved dan posted keduanya dihitung ke mutasi kas |
BKU-T03 | Data Pump Pagination | Saldo awal mengagregasi ribuan baris (> 1.000) tanpa terpotong |
BKU-T04 | Prioritas Keterangan Baris Item | line.description lebih diprioritaskan daripada voucher header |
BKU-T05 | Presisi SafeMoney Arithmetic | Menghitung akumulasi saldo berjalan tanpa floating point drift |
BKU-T06 | Audit Trail Metadata | Memuat reference_type, is_automated, dan status pada tiap baris data |
BKU-T07 | Presisi Tutup Buku Konsolidasi | Tidak mengunci holding secara keliru jika hanya 1 unit yang tutup buku |
BKU-T08 | Presisi Tutup Buku Unit Mandiri | Mengunci unit tunggal secara akurat ketika periode telah ditutup |
BKU-T09 | Subledger getRecentUnitTransactions | Filter status sah approved, posted & reversed, agregasi multi-baris kas, dan SafeMoney |
BKU-T10 | Standardisasi Format Ekspor | Format 8 kolom baku pada PDF & Excel, rekapitulasi mutasi, dan nama file dinamis |
BKU-T10b | Kop Surat 10 Baris & Format Rp OpenXML | Header kop BUMDes resmi dan format mata uang akuntansi Excel |
BKU-T11 | Guard Rentang Tanggal Tak Valid | Menolak kueri secara aman jika startDate > endDate |
BKU-T12 | Fallback Ketiadaan Akun Kas | Mengembalikan payload aman saldo Rp 0 jika unit belum memiliki COA kas |
BKU-T13 | Konsistensi Konstanta RBAC Terpusat | Klasifikasi akurat holding vs restricted roles menggunakan konstanta global |
BKU-T14 | Konsideran Penutupan Kas | Rincian kas tunai dan saldo bank dipisahkan presisi dalam berita acara |
BKU-T15 | Segregasi Buku Pembantu Kas vs Bank | Filter akun kasir tunai vs rekening bank terisolasi sempurna |
BKU-T16 | Paginasi Klien & Saldo Pindahan | Perhitungan saldo pindahan antar halaman akurat tanpa duplikasi baris, serta sanitasi pageSize |
BKU-T17 | Multi-Bank Breakdown Granular | Perhitungan saldo individual per rekening bank pada penutupan kas |
BKU-T18 | Ekspor Adaptif Buku Pembantu | PDF & Excel mengadaptasi konsideran kas/bank sesuai jenis buku pembantu |
BKU-T19 | Filter Kategori & Subtotal Aman | Penyertaan badge filter kategori dan subtotal mutasi aman |
BKU-T20 | Harmonisasi Tanda Tangan & Pejabat | Format Direktur/Bendahara, nama kapital bold underline, bebas kurung |
BKU-T21 | Remediasi Storno, Multi-Line Kas & Akun Global | Deteksi JRN-REV-, agregasi multi-baris kas subledger, resolusi COA kas global unit_id IS NULL, dan status berita acara PDF |
BKU-T22 | Paralelisasi Kueri & Arsitektur Non-Blocking | Eksekusi paralel via Promise.all, penanganan graceful error, dan proteksi balapan kueri |
BKU-T23 | Master In-Memory Caching & Latency Optimization | In-memory master caching COA & Unit (server) + SWR cache (klien), pemangkasan latensi hingga 90.5% (273 ms) |
๐ 6. Riwayat Revisi Dokumen#
| Versi | Tanggal | Penulis | Ringkasan Perubahan |
|---|---|---|---|
v2026.1 | 12 Mei 2026 | Tim Engineering ERP | Spesifikasi awal mesin BKU, Data Pump Pagination, dan SafeMoney Arithmetic. |
v2026.2 | 18 September 2026 | Tim Engineering ERP | Penambahan segregasi Buku Pembantu Kas/Bank, multi-bank breakdown, dan harmonisasi tanda tangan 3 kanal ekspor. |
v2026.3 | 24 Oktober 2026 | Tim Audit & Engineering | Remediasi paripurna BKU-01 s.d. BKU-14: siklus Storno 2-Tahap (JRN-REV- & VOID-), Migrasi SQL 265 (6 RPC PostgreSQL), sinkronisasi status reversed pada 10 laporan keuangan + AI CaLK, resolusi COA kas global (unit_id IS NULL), dan anti-spoofing RBAC /print/buku-kas. |
v2026.4 | 27 Oktober 2026 | Tim Audit & Engineering | Optimalisasi performa dan reaktivitas instan BKU: paralelisasi kueri backend via Promise.all (penurunan latensi ~80%), eliminasi loop desinkronisasi state klien, mitigasi Next.js router freeze dengan window.history.replaceState, dan perlindungan activeFetchIdRef anti-race condition. |
v2026.5 | 27 Oktober 2026 | Tim Audit & Engineering | Audit komprehensif filter BKU & arsitektur caching dua tingkat (Two-Tier High Performance Caching): in-memory master caching COA kas dan business units di server dengan proteksi promise cache stampede (memangkas latensi eksekusi dari 2.899 ms ke 273 ms), SWR cache in-memory di sisi klien (respons 0 ms pada navigasi bolak-balik), dan pemisahan non-blocking sinkronisasi UnitContext. |