18

Spesifikasi Teknis Buku Kas Umum (BKU)

Updated: Nov 2026Oleh: SystemSistem ERP & Teknologi Inti

๐Ÿ› ๏ธ 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:

typescript
let 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 Holding
  • 1-101%: Kas Operasional / Kas Kasir Unit
  • 1-102%: Kas Bank Operasional (Bank Mandiri, Bank BRI, BPD)
  • 1-103%: Kas Kecil / Petty Cash Perbendaharaan
  • 1101, 1102: Fallback format kode numerik 4 digit
  • Filter Posisi Kas Unit (shouldIncludeAccountPosition): Karena kueri menyertakan akun COA global (unit_id IS NULL), setiap baris journal_line_items tetap difilter ketat berdasarkan journal_entries.unit_id (dan line.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:

  1. 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() di lib/actions/journal-actions.ts (prefix JRN-REV-) maupun RPC rpc_void_treasury_transaction() (prefix VOID-), jurnal asli yang dibatalkan diubah statusnya menjadi reversed dan dipasangkan dengan jurnal pembalik baru berstatus approved / 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.
  1. 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, dan lib/actions/ai_calk.ts telah 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).

  1. 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 berstatus posted maupun approved, menandai jurnal asal sebagai 'reversed', dan menerbitkan jurnal pembalik VOID- berstatus 'posted'.
  • public.get_closing_balances: Menyertakan status IN ('approved', 'posted', 'reversed') untuk perhitungan ikhtisar laba rugi tutup buku akhir tahun.
  • public.get_snapshot_balances: Menyertakan status IN ('approved', 'posted', 'reversed') untuk pembuatan snapshot saldo bulanan/tahunan.
  • public.get_trial_balance: Menyertakan status IN ('approved', 'posted', 'reversed') pada RPC Neraca Saldo database.
  • public.get_account_balance_at_date: Menyertakan status IN ('approved', 'posted', 'reversed') untuk pengecekan saldo per tanggal cut-off.
  • public.check_inventory_integrity: Menyertakan status 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):

typescript
const 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:

  1. Tanggal Transaksi (entry_date ASC)
  2. Waktu Pembuatan (created_at ASC)
  3. Nomor Bukti Voucher (entry_number ASC)
  4. Aliran Kas (Debit / Uang Masuk didahulukan sebelum Kredit / Uang Keluar)
  5. 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:

  1. Paralelisasi Kueri Backend (Promise.all): Empat proses mandiri di dalam getBKUReport() dieksekusi secara simultan:
  • fetchBeginningBalance(): Agregasi saldo awal via data pump
  • fetchPeriodTransactions(): Pengambilan mutasi transaksi periode berjalan
  • fetchBusinessUnits(): Resolusi metadata nama unit usaha
  • fetchClosingStatus(): Verifikasi status tutup kas dan profil entitas Waktu eksekusi backend terpangkas hingga ~80% (dari 52.7 detik menjadi 9.5 detik pada test suite).
  1. Reaktivitas Klien Bebas Freeze (Zero-Latency Client Reactivity):
  • Menghapus sinkronisasi router.replace dengan useTransition yang memicu eksekusi ulang Server Component Next.js dan efek layar membeku (opacity-60).
  • Menggunakan window.history.replaceState untuk sinkronisasi senyap (silent update) parameter URL, dipadukan dengan pemanggilan Server Action mandiri di sisi klien.
  • Mengimplementasikan activeFetchIdRef untuk 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:

typescript
export 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 parameter unitId=all diabaikan 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):

  1. 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 parameter returnUrl.
  2. Server Action & Page Role Guard (requireRoleGuard()): Memverifikasi sesi aktif dan otorisasi role sebelum merender laporan buku kas di sisi server.
  3. Anti-Spoofing Parameter unitId (validatedUnitId): Pada app/print/buku-kas/page.tsx, parameter query unitId divalidasi terhadap peran pengguna (HOLDING_ROLES vs assigned_unit_id). Pengguna unit operasional yang mencoba menyuntikkan ?unitId=<unit-lain> pada URL dipaksa kembali ke validatedUnitId = assigned_unit_id baik 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:

  1. Deteksi Holding vs Unit Operasional:
  • Jika unit adalah Semua Unit (Konsolidasi), Kantor Pusat BUMDes (Holding), atau parameter kosong:
  • Pejabat Kiri: Mengetahui, dan Direktur BUMDes Bolulango (BINTANG RAIHAN GOBEL).
  • Pejabat Kanan: Bandungan, [Tanggal Cetak] dan Bendahara BUMDes (ANSHORUDDIN YUSUF).
  • Jika unit adalah unit bisnis operasional (misalnya Unit Peternakan Ayam Petelur):
  • Pejabat Kiri: Mengetahui, dan Kepala Unit [Nama Unit].
  • Pejabat Kanan: Bandungan, [Tanggal Cetak] dan Bendahara Unit [Nama Unit].
  1. 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 kalkulasi getTextWidth(), dan pada Cetak Web menggunakan border bawah formal.
  1. Status Dinamis Berita Acara Penutupan Kas (Print Web & PDF):
  • Apabila Math.abs(finalBalance) < 0.01, label status menampilkan STATUS: RESMI DITUTUP (Rp 0 Klop).
  • Apabila masih terdapat saldo kas akhir berjalan (Math.abs(finalBalance) >= 0.01), label status menampilkan STATUS: POSISI KAS BERJALAN (Saldo Buku).
  1. 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.
  1. Kanal Ekspor Dokumen:
  • Print Web (/print/buku-kas): Memanfaatkan komponen FormalSignatures yang terintegrasi langsung dengan getManagementStructure() 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 TesFokus PengujianKriteria Lolos
BKU-T01Role Guard & Isolasi RBACMenolak pengguna anonim & mengunci akun kasir ke unit sendiri
BKU-T02Harmonisasi Status postedTransaksi approved dan posted keduanya dihitung ke mutasi kas
BKU-T03Data Pump PaginationSaldo awal mengagregasi ribuan baris (> 1.000) tanpa terpotong
BKU-T04Prioritas Keterangan Baris Itemline.description lebih diprioritaskan daripada voucher header
BKU-T05Presisi SafeMoney ArithmeticMenghitung akumulasi saldo berjalan tanpa floating point drift
BKU-T06Audit Trail MetadataMemuat reference_type, is_automated, dan status pada tiap baris data
BKU-T07Presisi Tutup Buku KonsolidasiTidak mengunci holding secara keliru jika hanya 1 unit yang tutup buku
BKU-T08Presisi Tutup Buku Unit MandiriMengunci unit tunggal secara akurat ketika periode telah ditutup
BKU-T09Subledger getRecentUnitTransactionsFilter status sah approved, posted & reversed, agregasi multi-baris kas, dan SafeMoney
BKU-T10Standardisasi Format EksporFormat 8 kolom baku pada PDF & Excel, rekapitulasi mutasi, dan nama file dinamis
BKU-T10bKop Surat 10 Baris & Format Rp OpenXMLHeader kop BUMDes resmi dan format mata uang akuntansi Excel
BKU-T11Guard Rentang Tanggal Tak ValidMenolak kueri secara aman jika startDate > endDate
BKU-T12Fallback Ketiadaan Akun KasMengembalikan payload aman saldo Rp 0 jika unit belum memiliki COA kas
BKU-T13Konsistensi Konstanta RBAC TerpusatKlasifikasi akurat holding vs restricted roles menggunakan konstanta global
BKU-T14Konsideran Penutupan KasRincian kas tunai dan saldo bank dipisahkan presisi dalam berita acara
BKU-T15Segregasi Buku Pembantu Kas vs BankFilter akun kasir tunai vs rekening bank terisolasi sempurna
BKU-T16Paginasi Klien & Saldo PindahanPerhitungan saldo pindahan antar halaman akurat tanpa duplikasi baris, serta sanitasi pageSize
BKU-T17Multi-Bank Breakdown GranularPerhitungan saldo individual per rekening bank pada penutupan kas
BKU-T18Ekspor Adaptif Buku PembantuPDF & Excel mengadaptasi konsideran kas/bank sesuai jenis buku pembantu
BKU-T19Filter Kategori & Subtotal AmanPenyertaan badge filter kategori dan subtotal mutasi aman
BKU-T20Harmonisasi Tanda Tangan & PejabatFormat Direktur/Bendahara, nama kapital bold underline, bebas kurung
BKU-T21Remediasi Storno, Multi-Line Kas & Akun GlobalDeteksi JRN-REV-, agregasi multi-baris kas subledger, resolusi COA kas global unit_id IS NULL, dan status berita acara PDF
BKU-T22Paralelisasi Kueri & Arsitektur Non-BlockingEksekusi paralel via Promise.all, penanganan graceful error, dan proteksi balapan kueri
BKU-T23Master In-Memory Caching & Latency OptimizationIn-memory master caching COA & Unit (server) + SWR cache (klien), pemangkasan latensi hingga 90.5% (273 ms)

๐Ÿ“œ 6. Riwayat Revisi Dokumen#

VersiTanggalPenulisRingkasan Perubahan
v2026.112 Mei 2026Tim Engineering ERPSpesifikasi awal mesin BKU, Data Pump Pagination, dan SafeMoney Arithmetic.
v2026.218 September 2026Tim Engineering ERPPenambahan segregasi Buku Pembantu Kas/Bank, multi-bank breakdown, dan harmonisasi tanda tangan 3 kanal ekspor.
v2026.324 Oktober 2026Tim Audit & EngineeringRemediasi 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.427 Oktober 2026Tim Audit & EngineeringOptimalisasi 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.527 Oktober 2026Tim Audit & EngineeringAudit 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.

Apakah panduan ini membantu Anda?