Audit Modul Buku Kas Umum (BKU) 2026
AUDIT KOMPREHENSIF MODUL BUKU KAS UMUM (BKU)
Edisi Sistem: v2026.3 | Tanggal Pelaksanaan: 18 September 2026 Auditor Sistem: Antigravity AI Senior Technical Auditor & Finance Core Team
📋 1. Eksekutif Ringkasan & Ruang Lingkup Audit#
Audit mendalam ini mengevaluasi seluruh komponen Modul Buku Kas Umum (BKU) dalam sistem ERP BUMDes Bolulango. BKU merupakan instrumen pencatatan kronologis seluruh mutasi kas & bank yang berfungsi sebagai single source of truth untuk likuiditas riil entitas.
Komponen yang Diaudit
Memuat diagram alur...
| Dimensi Audit | File Utama | Status |
|---|---|---|
| Server Page (SSR) | page.tsx (app/dashboard/keuangan/bku/) | ✅ |
| Client Component | BKUClient.tsx | ✅ |
| Backend Engine | report.ts (getBKUReport L257-L424) | ✅ |
| Print Page | page.tsx (app/print/buku-kas/) | ✅ |
| Export Utils | export-utils.ts (exportBKUExcel L593-L607) | ✅ |
| Safe Arithmetic | accounting.ts (L1-L45) | ✅ |
| Alternate Data Source | journal-quick.ts (getRecentUnitTransactions L109-L232) | ✅ |
| Dokumentasi | PANDUAN_BKU_HOLDING.md | ✅ |
🏆 2. Scorecard Hasil Audit#
| # | Area Audit | Skor | Verdict |
|---|---|---|---|
| 1 | Arsitektur & Separation of Concerns | 🟢 A | Excellent |
| 2 | Backend Engine (getBKUReport) | 🟢 A | Solid |
| 3 | Safe Money Arithmetic | 🟢 A+ | Gold Standard |
| 4 | Unit Filtering & Konsolidasi | 🟢 A | Correct |
| 5 | RBAC & Access Control (Halaman BKU) | 🟢 A | Enforced |
| 6 | UI/UX Client Component | 🟢 A | Premium |
| 7 | Tutup Buku (Closing) Integration | 🟢 A | Working |
| 8 | Print Page (Buku Kas) | ✅ A | FIXED |
| 9 | Export Excel (exportBKUExcel) | ✅ A | FIXED |
| 10 | Export CSV | 🟢 B+ | Minor Issues |
| 11 | Negative Balance Warning | 🟢 A | Proactive |
| 12 | Dokumentasi SOP | 🟢 B+ | Good |
| OVERALL | 🟢 A- | P0 Fixed • Medium items open |
✅ 3. Temuan POSITIF (Keunggulan Sistem)#
3.1 Backend Engine Matang & Akurat
getBKUReport() di report.ts:257-424 menunjukkan implementasi yang sangat solid:
- ✅ Identifikasi Akun Kas Strict: Filter menggunakan
code.like.1-100%,1-101%,1-102%,1-103%plus fallback1101,1102— menutup semua kemungkinan format kode COA. - ✅ Status Guard: Hanya jurnal
status = 'approved'yang masuk BKU. Draft journals tidak pernah mencemari saldo kas. - ✅ Safe Money Arithmetic: Menggunakan
safeMoney(),safeAdd(),safeSubtract()dariaccounting.ts— mengeliminasi IEEE 754 floating point errors pada akumulasi Rupiah. - ✅ Saldo Awal Kalkulasi Benar:
beginningBalancedihitung dari semua journal_line_items pada akun kas sebelumstartDate, bukan hardcoded. - ✅ Running Balance Kronologis: Sort multi-level yang sangat baik:
entry_date → created_at → entry_number → debit-first → id fallback. - ✅ Eliminasi Konsolidasi Sudah Dihapus: Komentar di L335-338 menjelaskan kenapa filter TRF-% dihapus (menyembunyikan suntikan dana → saldo minus).
3.2 RBAC Berlapis
Server (page.tsx) → getCurrentUser() → isRestricted check
→ assignedUnitId binding
Backend (getBKUReport) → requireRoleGuard()
- ✅ Role
manajer_unit,kepala_unit,kepala_sub_unit,admin_unitotomatis di-restrict ke unit mereka. - ✅
requireRoleGuard()di backend menambahkan lapisan keamanan kedua.
3.3 Konsolidasi Hierarki Multi-Level
getAllChildUnitIds() memungkinkan consolidated view yang recursive — mendukung struktur holding → sub-unit → sub-sub-unit tanpa batas kedalaman.
3.4 UI/UX Premium
- ✅ Glassmorphism cards dengan dark mode support penuh.
- ✅ Summary cards (Saldo Awal, Mutasi Kas, Saldo Akhir) informatif.
- ✅ Warning banner untuk saldo negatif (defensive UI).
- ✅ Consolidated vs Isolated toggle yang intuitif.
- ✅ Link ke jurnal edit dari setiap baris referensi (drill-down audit trail).
- ✅ Print-ready CSS classes (
print:hidden,print:bg-transparent, dll). - ✅ Loading state dengan opacity transition.
3.5 Integrasi Ekosistem
- ✅ Transfer Kas dan Treasury actions me-
revalidatePath('/dashboard/keuangan/bku')setelah mutasi. - ✅ Sidebar navigation terdaftar sebagai menu utama.
- ✅ AI ActionCardRenderer memiliki redirect alias
/dashboard/bku→/dashboard/keuangan/bku.
🔴 4. Temuan KRITIS (Harus Diperbaiki)#
4.1 [CRITICAL] ✅ [FIXED] Print Page Menggunakan Data Source Berbeda & Cacat Logika Saldo
✅ RESOLVED — 18 September 2026
app/print/buku-kas/page.tsxtelah direfactor penuh untuk menggunakangetBKUReport()sebagai data source tunggal. Draft journals tidak akan pernah masuk cetakan. Running balance kini sepenuhnya server-side.FormalSignaturesunitName juga diperbaiki dari hardcoded ke nama unit sebenarnya. Indikator status tutup buku ditambahkan.
Halaman Utama BKU: Menggunakan getBKUReport() dari lib/actions/finance/report.ts
- ✅ Filter
status = 'approved' - ✅ Safe money arithmetic
- ✅ Multi-level sort
- ✅ Proper debit/credit columns
Halaman Print: Menggunakan getRecentUnitTransactions() dari lib/actions/journal-quick.ts
- ❌ TIDAK memfilter
status = 'approved'— jurnal DRAFT bisa masuk ke cetakan resmi - ❌ Menggunakan model data
amount+typebukandebit/credit— mismatch dengan struktur BKU - ❌ Running balance dihitung ulang di JSX (line 162) dengan logika yang cacat:
tsx// CACAT: amount > 0 = masuk, amount < 0 = keluar // Tapi getRecentUnitTransactions SELALU return amount positif + type field const currentBalance = beginningBalance + transactions.slice(0, idx + 1) .reduce((acc, curr) => acc + (curr.amount > 0 ? curr.amount : 0) - (curr.amount < 0 ? Math.abs(curr.amount) : 0), 0 )
Masalah: getRecentUnitTransactions() return amount = Math.max(debit, credit) (selalu positif) dan type = 'in' | 'out'. Logika amount < 0 tidak pernah true, sehingga semua transaksi dianggap "masuk" dan saldo running balance selalu naik.
- ❌ Transactions di-reverse (
processed.reverse()di L229) untuk display "newest first", tapi print page menampilkannya sequential — urutan salah. - ❌ Tidak menggunakan Safe Money Arithmetic — rentan floating point errors.
Severity: 🔴 CRITICAL — Dokumen resmi yang dicetak dan ditandatangani bisa menampilkan angka yang salah.
Rekomendasi: Print page harus menggunakan getBKUReport() yang sama sebagai data source, atau minimal di-refactor agar menggunakan data source dengan kualitas yang setara.
4.2 [CRITICAL] ✅ [FIXED] Export Excel Menggunakan Field yang Tidak Ada
✅ RESOLVED — 18 September 2026
exportBKUExcel()diexport-utils.tstelah diperbaiki. Field mapping sekarang benar:beginningBalance,entries,finalBalance,ref.
typescript// exportBKUExcel expects: data.startBalance // ❌ Actual: data.beginningBalance data.transactions // ❌ Actual: data.entries data.endBalance // ❌ Actual: data.finalBalance t.evidence_no // ❌ Actual: t.ref
Akibat: File Excel yang dihasilkan akan berisi:
- Saldo Awal:
0(bukan angka sebenarnya) - Kolom transaksi: kosong/undefined
- Saldo Akhir:
0
Severity: 🔴 CRITICAL — Export Excel menghasilkan data kosong/salah. User mungkin tidak sadar karena file tetap ter-download.
Rekomendasi: Selaraskan field mapping di exportBKUExcel() dengan output getBKUReport():
typescriptexport function exportBKUExcel(data: any, period: string) { const exporter = new ExcelExporter() const rows = [] rows.push(['BUKU KAS UMUM', `Periode: ${period}`, '', '', '', '']) rows.push(['']) rows.push(['Saldo Awal', '', '', '', '', data.beginningBalance || 0]) // FIX if (data.entries) { // FIX: was data.transactions data.entries.forEach((t: any) => { rows.push([t.date, t.ref || '', t.description, t.debit || 0, t.credit || 0, t.balance || 0]) // FIX }) } rows.push(['Saldo Akhir', '', '', '', '', data.finalBalance || 0]) // FIX exporter.addTableSheet('BKU', ['Tanggal', 'No Bukti', 'Uraian', 'Penerimaan', 'Pengeluaran', 'Saldo'], rows) exporter.save('BKU.xlsx') }
🟡 5. Temuan MEDIUM (Sebaiknya Diperbaiki)#
5.1 [MEDIUM] ✅ [FIXED] Export CSV Hitung Total dari filteredEntries, Bukan All Entries
✅ RESOLVED — 18 September 2026
totalMasukAll/totalKeluarAllsekarang selalu dihitung dariallEntries(tidak terpengaruh search filter)- Summary card Mutasi Kas kini selalu menampilkan angka keseluruhan periode
- Footer table menampilkan konteks filter:
"Total Keseluruhan (filter aktif: X/Y transaksi)"- Baris tambahan "Total Terfilter" (amber row) muncul di bawah footer saat search aktif
- CSV export selalu mengekspor ALL entries, bukan hanya yang terfilter
- Fix memory leak
URL.revokeObjectURL()juga diterapkan- Fix CSV escaping: handle newline dan special chars
5.2 [MEDIUM] ✅ [FIXED] Redundansi Panggilan getAllChildUnitIds
✅ RESOLVED — 18 September 2026
getAllChildUnitIds()kini di-cache sekali di awalgetBKUReport()sebagaicachedChildUnitIds. Ketiga titik penggunaan (account filter, beginning balance, transactions) menggunakan hasil yang sama. Mengeliminasi 2 dari 3 recursive DB round-trips.
5.3 [MEDIUM] Print Page Tidak Memanfaatkan FormalSignatures dengan Benar
Di print/buku-kas/page.tsx:185-189:
tsx<FormalSignatures auditId={`bku-print-{unit_id}-{month}-${year}`} variant="unit" unitName="Unit" // ❌ Hardcoded "Unit" — bukan nama unit sebenarnya />
Nama unit pada tanda tangan tercetak "Unit" alih-alih nama unit bisnis yang sebenarnya.
5.4 [MEDIUM] ✅ [FIXED] Tidak Ada Pagination / Virtualisasi untuk Data Besar
✅ RESOLVED — 18 September 2026 Pagination 100 baris per halaman diterapkan di
BKUClient.tsxmenggunakanpaginatedEntries. Kontrol navigasi halaman (prev/next + nomor halaman) ditampilkan di bawah tabel dengan UI premium. Page di-reset otomatis saat filter atau search berubah.
5.5 [MEDIUM] Print Page Tidak Menampilkan Status Tutup Buku
Halaman utama BKU menampilkan badge "🔒 Terkunci (Tutup Buku)" tapi halaman print tidak memiliki indikator ini sama sekali.
🔵 6. Temuan MINOR (Nice to Have)#
6.1 [LOW] Duplicated Dark Mode Classes
6.2 [LOW] any Type Pervasive
6.3 [LOW] CSV Export Tidak Menangani Karakter Khusus Sepenuhnya
6.4 [LOW] FormalReportHeader Period Format Inkonsisten
6.5 [LOW] URL Object Leak di CSV Export
🏗️ 7. Analisis Arsitektur#
7.1 Kepatuhan SAK EP
| Kriteria SAK EP | Status | Keterangan |
|---|---|---|
| BKU mencatat pergerakan kas riil (bukan accrual) | ✅ | Hanya akun kas/bank (1-1xx) yang masuk |
| Transaksi non-kas (penyusutan, amortisasi) tidak masuk BKU | ✅ | Filter by COA code |
| Saldo awal = akumulasi historis sebelum periode | ✅ | Dihitung dari semua approved journals < startDate |
| Kronologis (urut tanggal) | ✅ | Multi-level sort |
| Hanya jurnal yang disetujui (approved) | ✅ Backend / ❌ Print | Print page tidak filter status |
| Saldo berjalan akurat | ✅ Backend / ❌ Print | Print page running balance cacat |
📊 8. Matriks Risiko & Prioritas Perbaikan#
| # | Temuan | Severity | Impact | Effort | Status |
|---|---|---|---|---|---|
| 4.1 | Print Page data source & saldo cacat | 🔴 CRITICAL | Dokumen resmi salah | Medium | ✅ FIXED |
| 4.2 | Export Excel field mismatch | 🔴 CRITICAL | Excel kosong/salah | Low | ✅ FIXED |
| 5.1 | CSV total inkonsisten saat filter aktif | 🟡 MEDIUM | Laporan menyesatkan | Low | ✅ FIXED |
| 5.2 | Redundansi getAllChildUnitIds | 🟡 MEDIUM | Performance | Low | ✅ FIXED |
| 5.3 | Print unitName hardcoded | 🟡 MEDIUM | Tanda tangan salah | Low | ✅ FIXED |
| 5.4 | Tidak ada pagination | 🟡 MEDIUM | Performance mobile | Medium | ✅ FIXED |
| 5.5 | Print tidak tampilkan status tutup buku | 🟡 MEDIUM | Konteks audit hilang | Low | ✅ FIXED |
| 6.1 | Duplicated CSS classes | 🔵 LOW | Bundle size | Low | ✅ FIXED |
| 6.2 | Pervasive any types | 🔵 LOW | Type safety | Medium | ✅ FIXED |
| 6.3 | CSV escaping kurang lengkap | 🔵 LOW | Rare edge case | Low | ✅ FIXED |
| 6.4 | Inkonsistensi format periode | 🔵 LOW | UX confusion | Low | ✅ FIXED |
| 6.5 | URL object leak | 🔵 LOW | Minor memory leak | Low | ✅ FIXED |
🛠️ 9. Rekomendasi Perbaikan Strategis#
9.1 Fase 1: Critical Fixes (P0)
A. Perbaiki Print Page BKU
Ganti data source di print/buku-kas/page.tsx untuk menggunakan getBKUReport().
B. Perbaiki Export Excel
Selaraskan field mapping di exportBKUExcel().
9.2 Fase 2: Medium Priority (P1-P2)
- Pisahkan total keseluruhan vs total terfilter
- Cache
getAllChildUnitIdsdi awalgetBKUReport() - Fix unitName di FormalSignatures print page
- Tambahkan indikator tutup buku di print page
- Implement pagination
9.3 Fase 3: Polish (P3)
✅ RESOLVED — 18 September 2026
- Clean up duplicated CSS classes (Cleared)
- Define proper TypeScript interfaces (
lib/types/bku.ts) and eliminatedanyusage.- Standardize period format (using em-dash
—consistently) across dashboard, print page, and exports.- Use proper CSV library (Optional, CSV is stable now)
- Tambahkan
URL.revokeObjectURL()setelah download
🔒 10. Audit Keamanan#
| Aspek | Status | Detail |
|---|---|---|
| Server-side role guard | ✅ | requireRoleGuard() di setiap server action |
| Unit-level isolation | ✅ | isRestricted + assignedUnitId di page.tsx |
| SQL Injection prevention | ✅ | Menggunakan Supabase SDK (parameterized queries) |
| XSS prevention | ✅ | React auto-escaping |
| RBAC Bypass Risk | 🟢 | Semua mutating action sudah dilindungi requireRoleGuard |
| Data exfiltration risk | 🟢 | ✅ Terselesaikan. Setiap aksi Export CSV, Excel, dan Print PDF dicatat secara silent ke dalam tabel audit_logs (RLS Tier-5) mencakup: actor, timestamp, unit_id, dan jumlah baris data. |
| Injeksi HTML/XSS | 🟢 | Data description sudah di-escape otomatis oleh React |
📝 11. Kesimpulan#
Modul BKU pada ERP BUMDes Bolulango memiliki arsitektur backend yang kuat dan mesin komputasi yang akurat. Safe money arithmetic, multi-level sorting, dan RBAC berlapis menunjukkan standar engineering yang matang.
Namun, terdapat 2 cacat kritis yang memerlukan perbaikan segera:
- Print page menggunakan data source berbeda yang tidak memfilter status jurnal dan memiliki logika running balance yang cacat — berisiko menghasilkan dokumen resmi dengan angka salah.
- Export Excel menggunakan field mapping yang salah — menghasilkan file dengan data kosong.
Kedua masalah ini bersifat "silent failure" — sistem tidak error, file tetap ter-download, tapi isinya salah. Ini adalah tipe bug yang paling berbahaya karena user mungkin tidak menyadarinya.
❗ IMPORTANT Rekomendasi Utama: Perbaiki kedua temuan P0 sebelum akhir periode pelaporan. Setelah perbaikan, lakukan cross-check manual antara halaman BKU dashboard, print preview, dan file Excel/CSV untuk memverifikasi konsistensi angka.
Dokumen audit ini dibuat secara sistematis oleh Antigravity AI Senior Technical Auditor untuk memastikan transparansi, akuntabilitas, dan kualitas tata kelola keuangan BUMDes Bolulango menuju standar Gold Standard.