Skip to content
AM
All projects
ERP / Finance Featured Private

BuildLedger Construction ERP

A production-ready construction ERP for projects, materials, labour, vendors, expenses, client payments, and invoices. I built the backend financial engines for cash flow, receivables and payables, and project net positions, plus inter-project fund transfers and settlements that track which projects owe money without double-counting internal transfers.

  • React
  • TypeScript
  • Node.js
  • Express.js
  • Prisma
  • PostgreSQL
  • React Query
  • JWT
  • Cloudflare R2
  • Tailwind CSS

Private project, demo on request

This is client or employer work, so the source code is confidential and the live app sits behind a login. I can't share screenshots or credentials publicly, but I'm happy to walk you through it on a call: a live demo, the architecture and selected code.

Request a demo

Architecture

BuildLedger Construction ERP architecture diagram

Overview

BuildLedger is a single-tenant construction ERP: a React SPA talking to a versioned REST API (/api/v1) backed by PostgreSQL. The backend is a modular monolith with 17 domain modules (projects, materials, labour, vendors, expenses, client payments, invoices, salary slips, fund transfers, documents, reports, users, audit, deletion requests and more), each split into the same layers:

text
routes → controller → service → repository → Prisma → PostgreSQL
  • Routes attach auth, permission checks and Zod validation
  • Controllers translate HTTP into service calls
  • Services own business rules (who can void, how settlement works)
  • Repositories are the only layer that touches Prisma

System

text
React + TypeScript (Vercel)
  │  React Query cache · JWT access token · httpOnly refresh cookie
  ▼
Express API (Railway)
  │  helmet → request timing → pino logging → CORS → rate limits
  │  → auth → permissions → validation → module
  ├──► PostgreSQL (Prisma ORM, NUMERIC(14,2) money columns)
  └──► Cloudflare R2 (documents via presigned URLs)

Key design decisions

Money is never a float. All money columns are NUMERIC(14,2), and invoice and payment math goes through a shared Decimal helper that rounds once, half-up, to 2 decimal places. Floating-point drift (0.1 + 0.2 ≠ 0.3) never reaches a printed invoice.

Inter-project transfers without double counting. When one project funds another, the money moves between projects; it isn't company income or expense. The financial engine nets every pair of projects into a single balance (A→B minus B→A), so each project sees a clear receivable (they owe us) or payable (we owe them). Internal transfers never inflate company-wide totals. Net position = available cash + receivables − payables.

Financial records are voided, not deleted. Transfers and entries are voided with a reason, who did it and when. Real deletions go through an approval workflow: a user requests, a reviewer approves or rejects, and a snapshot of the record is kept so it can be restored.

Everything is audited. A Prisma middleware records every write (old value, new value, who, when) in an audit log, using the request context so no module has to remember to do it.

Access is scoped per project. Admins and accountants see everything. Other roles see only projects granted to them, and that list is read from the database (cached for 60s), not from the JWT. Removing someone's access takes effect within a minute, not when their token expires.

Defense in depth on writes. Global rate limiting, plus a stricter limiter on financial write routes (payments, materials, labour, vendors, expenses, transfers).

Quality

  • Vitest + Supertest tests for money math, invoice totals, project scoping (including IDOR checks), auth, uploads, and race conditions in deletion review
  • OpenAPI docs generated with Swagger
  • Request timing and structured logs with request IDs for tracing slow calls

Key Implementation Highlights

  1. Built the project financial engine: cash in hand, receivables, payables and net position per project

    Finance
  2. Pairwise settlement of inter-project fund transfers without double-counting internal money movement

    Finance
  3. Exact money arithmetic with NUMERIC(14,2) and a Decimal helper instead of floats

    Data
  4. Per-project access scoping read from the DB, so revoked access applies within 60s, with IDOR tests

    Security
  5. JWT access tokens with rotating refresh tokens in httpOnly cookies, plus rate limits on financial writes

    Security
  6. Automatic audit log of every write via Prisma middleware, with old and new values

    Audit
  7. Void-with-reason for financial records and an approval workflow for deletions, with snapshot restore

    Workflow
  8. Modular monolith: 17 domain modules with a strict routes → controller → service → repository layering

    Architecture
  9. Document uploads to Cloudflare R2 using presigned URLs

    Storage
  10. Vitest + Supertest suite covering money math, scoping, auth and concurrent deletion reviews

    Testing

Interested in the technical details?

Let's Talk