// Overview
A CRM portal built around the money side of client work. Client records, invoice generation, payment tracking and follow-up email all live in one system, so an invoice goes out, chases itself when it is late, and stops chasing the moment it is paid.
// The problem
Invoicing was spread across a spreadsheet, a document template and someone’s inbox. Nobody could answer “who owes us money right now?” without rebuilding the answer by hand, and overdue invoices were only chased when somebody happened to remember. The work was not hard, it was repetitive — which is exactly the work that quietly stops happening.
// What I built
A full-stack portal. A Next.js dashboard holds client records, invoices and a live view of what is outstanding; a Node.js API generates invoices from saved rates and line items and renders them to PDF. PostgreSQL stores clients, invoices and payments relationally, so every payment is tied to its invoice and every invoice to its client. A scheduled worker handles the follow-up: it finds invoices approaching or past their due date, sends the right reminder template, and logs the send against the invoice.
Two details did most of the work. Totals and tax are calculated server-side, so the figures on an invoice cannot be edited from the browser. And invoice numbers are drawn from a sequence allocated in the same transaction that writes the invoice — which keeps numbering gapless and unique even when two invoices are raised at the same moment, the kind of thing an accountant notices long before a developer does.




