The Situation
A Swiss service business was running its entire back office out of spreadsheets and a word processor. Quotes were built by copying last month’s document and editing the numbers. Invoices were typed by hand, and the Swiss QR payment slip at the bottom — the part the bank actually reads — was pasted in from a separate online generator, with the reference number entered manually each time. One wrong digit in that 27-character reference and the payment lands unmatched.
Expenses lived in a second spreadsheet, receipts in a folder named by date if they were named at all. There was no single place to answer the questions that actually run a business: how much is outstanding right now, what did we spend this quarter, what’s the balance on each account, are we profitable this year. Producing those numbers meant an afternoon of manual reconciliation, and the answer was only as trustworthy as the last person who touched the file.
The brief was direct: replace the whole patchwork with one system the owner controls, that produces correct Swiss documents, and that never loses data.
What I Built
I designed and built a full ERP from the ground up — a Node.js / Express application backed by SQLite, self-hosted on a hardened VPS. Not a template, not a SaaS subscription with someone else holding the data, but a single owned system tailored to how this business actually works.
Document workflow with Swiss QR-Bill. The core is a document engine covering the full chain a Swiss service business issues: quote (Offerte) → order confirmation (Auftragsbestätigung) → invoice (Rechnung) → handover protocol (Abnahmeprotokoll). Each document is composed in a rich-text editor, line items and totals calculated automatically, and rendered to a clean A4 PDF. Invoices carry a fully compliant Swiss QR-Bill payment section with a correctly generated, check-digit-validated reference number — the exact piece that used to be copy-pasted and error-prone is now generated by the system every time. A one-click action turns an accepted quote into an invoice without re-entering anything.
Finance, cash flow and P&L. A finance module tracks expenses by custom category, with receipts uploaded and stored against each entry (file type verified by inspecting the actual file bytes, not just the extension). It records other income, holds an opening balance per bank account, and assembles a chronological cash-flow ledger with a running balance across accounts. Year-end profit-and-loss and cash-flow reports export to Excel in one click — the afternoon of reconciliation became a button.
Client CRM and analytics. A client register feeds autocomplete in the document editor, so issuing a document to a returning customer is a couple of keystrokes. Renaming a client cascades cleanly through their entire invoice history. An analytics dashboard shows totals, outstanding, and received at a glance, with filters by status and period and per-document detail — the “where do we stand right now” question answered live.
Security, built in from the start. This system holds financial data, so it was hardened rather than bolted shut afterwards: modern password hashing with automatic upgrade of older hashes on login, full CSRF protection, rate-limited and bot-checked login, login-attempt logging with an admin view, strict file-upload validation, and defence against path-traversal and cross-site scripting throughout. At the edge, a firewall restricts access by country, an application firewall filters traffic, and a strict set of security headers is enforced.
Self-hosting, deployment and disaster recovery. The application runs under a process manager behind nginx with TLS, on infrastructure I provisioned and locked down (key-only SSH, fail2ban, a tight firewall). Every push to the repository triggers an automated pipeline — sync, install, restart, and a health check that confirms the app is actually serving before the deploy is called done. Backups run nightly to off-site storage with a year of retention, an hourly database snapshot keeps the worst-case data loss under an hour, and a monthly automated restore drill proves the backups actually restore — because an untested backup is just a hope. Uptime monitoring and error tracking page the owner if anything breaks. The whole thing is also a PWA, so it installs on a phone like a native app.
Results
- One system replaced the spreadsheet-and-word-processor patchwork — quotes, order confirmations, invoices, handover protocols, expenses, cash flow, clients, and analytics in one place.
- Swiss QR-Bill reference errors eliminated — references are generated and check-digit validated, not copy-pasted.
- Quote → invoice in one click — no document is ever re-typed from scratch.
- Live financial picture — outstanding, received, expenses, per-account balance, and year-to-date P&L available instantly instead of after an afternoon of reconciliation.
- Year-end reporting reduced to a single export — P&L and cash flow to Excel on demand.
- Worst-case data loss under one hour, with monthly restore drills proving recovery actually works.
- The owner controls the data — self-hosted, owned, no per-seat SaaS fees and no third party holding the books.
What Made the Difference
The decision that shaped everything was building a single owned system instead of stitching together off-the-shelf tools. An invoicing SaaS plus a separate bookkeeping app plus a cloud drive for receipts would have covered the features — but the data would have lived in three companies’ databases, the Swiss QR-Bill would still have been someone else’s idea of correct, and every workflow would have been bent to fit the tools. Owning the system meant it could be shaped to the business rather than the other way around.
The second was treating reliability as a feature, not an afterthought. For a system that holds the books, “it works on my machine” is not enough — what matters is that a bad deploy can’t reach users, that a failed disk doesn’t cost a quarter of records, and that the backups have been proven to restore. The automated health-checked deployment, the hourly snapshots, and the monthly restore drill are the difference between a tool the owner trusts with the business and one they keep a nervous spreadsheet copy of “just in case.”