Sovereign Ledger

A comprehensive financial management system built for a health-worker cooperative in Garut, Indonesia, deployed on-premise and in active use.

🔒 NDA · KPRI Warga Kesehatan
Laravel 13 Livewire 4 Alpine.js PostgreSQL Tailwind CSS Docker Full-Stack Solo Build

The Problem

KPRI Warga Kesehatan Kabupaten Garut, a health-worker cooperative in Garut, Indonesia, needed a complete financial management system. They had been running on Excel spreadsheets for years, with their full transaction history locked in legacy files. Off-the-shelf solutions were either too expensive or couldn't handle the cooperative-specific business logic (compulsory savings, social funds, multi-type loans).

The core challenge was to build and deploy a production-grade financial system as a solo developer: double-entry accounting, role-based access for five user roles, automated monthly batch processing, professional reporting, and a full data migration from their existing Excel records.

The Architecture

graph TB A[Docker: PHP-FPM + Nginx] --> B[Laravel 13 + Livewire 4] B --> C[PostgreSQL] B --> D[Alpine.js Frontend] C --> E[Chart of Accounts] C --> F[Transaction Ledger] C --> G[Loan Management] B --> H[Automated Batch Processing] H --> I[Monthly Savings] H --> J[Social Funds] H --> K[Loan Installments] B --> L[PDF/CSV Reporting] style B fill:#6c5ce7,stroke:#7c6df0,color:#fff style C fill:#00d2ff,stroke:#00b8e6,color:#0a0a0f style A fill:#f7c948,stroke:#e0b830,color:#0a0a0f

Why It's Hard

  • Double-entry accounting from scratch: Every transaction must balance. A full Chart of Accounts hierarchy, audit trails, and period locking. Getting this right as a solo developer, without an accounting background, required deep domain learning.
  • Legacy data migration: The cooperative's complete transaction history lived in Excel files spanning years. Importing, cleaning, and reconciling this into a normalized PostgreSQL schema was a data engineering challenge in itself.
  • Five distinct user roles: Admin, teller, member, auditor, and super-admin, each with different permissions, views, and workflows. Role-based access control had to be airtight for a financial system handling real money.
  • On-premise Docker deployment: The system runs on the cooperative's own hardware: PHP-FPM + Nginx + PostgreSQL in Docker containers, with Supervisor-managed scheduling for automated monthly batch jobs and persistent volume backups.

Key Features Delivered

  • Full Chart of Accounts: hierarchical account structure with double-entry enforcement
  • Automated monthly batch processing: compulsory savings deductions, social fund contributions, and loan installment postings run on schedule
  • Multi-type loan management: automatic installment scheduling, interest calculation, and repayment tracking
  • Role-based access control: five user roles with granular permissions
  • Professional reporting: PDF reports with KOP letterhead branding, CSV exports, and printable ledgers via Laravel DOMPDF + PhpSpreadsheet
  • Legacy data reconciliation: full import and reconciliation of years of cooperative transaction history from Excel

Inside the System

Three screens from the deployment. The dashboard is the operational view, grouping member, loan, and savings totals in one place. The transaction form is where the accounting model becomes concrete: every entry names a source and a destination account from the Chart of Accounts, so the journal is balanced at the point of capture rather than corrected afterwards.

Operations dashboard with panels for active members, active loans, outstanding balance, savings, and operational cost.
Operations dashboard. Member, loan, and savings totals grouped in a single view, so the office can read the state of the cooperative without opening each module.
Transaction entry form with transaction direction, category, amount, and source and destination Chart of Accounts selectors.
Transaction entry. Source and destination accounts are chosen from the Chart of Accounts at capture time, and the entry carries a verification flag for the auditor role.
Monthly payroll deduction invoice generator with year, month, and member category selectors above an invoice history table.
Faktur Potong Gaji. The monthly salary-deduction invoice generator. A period and member category are selected to produce a batch, and each batch is recorded in the history table below.

What I'd Do Differently

  • Add automated testing earlier. Financial software without tests is terrifying. I added PHPUnit tests partway through, but having them from day one, especially for the double-entry balance checks and loan calculations, would have saved debugging time.
  • Use Laravel's built-in features more aggressively. I initially underused Laravel's queuing, notifications, and event system. Refactoring batch processing to use Laravel queues with Supervisor made the system significantly more robust.
  • Design the role-permission system with more granularity. Five roles worked for the initial deployment, but as the cooperative grows, finer-grained permissions would reduce the need for custom overrides per user.
  • Build a dedicated data import pipeline. The legacy Excel migration was done semi-manually. A repeatable ETL pipeline would have made the reconciliation process faster and more auditable.

Key Takeaways

Building a production financial system for a real institution, as a solo developer, teaches you more than any tutorial. Double-entry accounting is a constraint that shapes every design decision. The system is deployed on-premise at the cooperative and used daily. That's a different level of accountability than a side project.
Interested in working together?

I'm open to full-time, contract, and freelance work.

Get in touch →
← Back to Projects