Brent Ceulemans
All work

Case 02 · Under NDA

The system a company spends its day inside

An installation company outgrew its spreadsheets, then discovered that off-the-shelf software did not fit the way it actually works. This is what replaced it: one platform covering sales, planning, field execution, logistics, finance and a customer portal — and the decisions underneath it.

Role
Sole developer — domain model to deploys
Running since
Early 2026, in daily production use
Users
Dozens of people daily, plus a customer portal
Stack
React · TypeScript · Postgres · Supabase

Context

What it replaced

Every company that reaches a certain size has the same three tools: a spreadsheet nobody dares touch, a shared mailbox, and one person who remembers how it all fits together. It works until it does not — usually on the day that person is on holiday.

The obvious answer is to buy something. That fails for a specific reason: packaged CRM assumes a sales process, packaged ERP assumes a factory, and a company that installs things at a customer’s address is neither. You end up bending the business to fit the software, and the spreadsheet quietly comes back to hold whatever did not fit.

So the brief was not “digitise this”. It was: model what this company actually does, and make every role’s day shorter rather than better documented.

Scope

Five layers, one database

every layer owns its colour

  1. Leads

    • qualification
    • source attribution
    • follow-up
  2. Quotes

    • line pricing
    • VAT regimes
    • digital signing
  3. Site

    • planning
    • field execution
    • stock
  4. Invoices

    • reconciliation
    • VAT returns
    • collections
  5. Portal

    • customer & partner
    • roles & rights
    • row-level security

A job moves left to right through these, and every handover is a place where a spreadsheet used to live. Putting them on one database is what removes the re-typing — and what makes access control the hardest problem on the page.

Engineering

Decisions that mattered

and what the alternative costs

A quote is a contract, so its lines are immutable

A quote that has been signed cannot change — not its prices, not its VAT regime, not its line order. Editing one creates a new revision and leaves the signed version exactly as the customer saw it. The invoice is then generated from the signed lines rather than re-derived from current prices.

Why not the other way

The alternative looks simpler for about a year, until a price list update silently rewrites what a customer agreed to. That is not a bug you find in testing; you find it in a dispute.

The portal sits on the same database, so RLS is the floor

Customers and partners read their own projects out of the same tables the finance team works in. There is no mirror database and no sync job. Every table the portal touches carries a row-level policy, and the policy — not the query, not the component — decides what comes back.

Why not the other way

A separate read model is a second source of truth, and a second source of truth drifts. Keeping one database means a policy is the only thing standing between a customer and the rest of the company, which is exactly the pressure it should be under. I test them by signing in as the wrong user and watching the request fail.

Money invariants are checked by the database, on a schedule

Invoice totals must equal the sum of their lines. VAT must match the regime on the signed quote. A payment must never reconcile against an invoice from another customer. These are scheduled checks against the live data, and they alert only when the state changes — first time wrong, and again when it goes right.

Why not the other way

Financial bugs are silent. Nothing throws; a number is simply wrong, and stays wrong until someone recounts by hand. An alarm that fires every fifteen minutes gets muted within a day and is worthless after that, so it fires on transitions instead.

Each role gets its own screen, not a filtered version of one screen

The planner, the field team, the warehouse and the bookkeeper look at the same project through four different interfaces. The field app shows today, this address, these materials, this photo. The bookkeeper never sees a planning grid.

Why not the other way

One screen with everything hidden behind permissions is how enterprise software becomes something people avoid. The cost of building four focused screens is real; the cost of one screen nobody wants to open is higher, and it shows up as a spreadsheet reappearing on someone’s desktop.

Process

How the work goes

  1. 01

    Sit with the work before modelling it

    The domain model comes out of watching how a job actually moves, not out of a requirements document. The words people already use become the table names.

  2. 02

    Ship one layer end to end

    Quoting, all the way from lead to signed PDF, before anything else is started. A layer that is live teaches you more in a week than a quarter of planning.

  3. 03

    Put the rules in the database

    Constraints, policies and invariant checks, because application code is where rules go to be forgotten. If it matters, it should refuse the write.

  4. 04

    Own the deploy and the Saturday phone call

    Architecture, migrations, deploys and the incident are the same job. Knowing you will be the one woken up changes what you are willing to ship.

Run the check, read the output, then say it works.