A building-materials retailer running multiple physical stores had a problem that looks like a software problem and is really a consistency problem. Enquiries arrived by phone, WhatsApp and walk-in, and lived wherever the person who took them put them. Stock was tracked per branch, in practice by whoever was at the counter. Quotations were typed from scratch every time.
The visible cost was time. The expensive cost was inconsistency: two branches quoting the same customer differently, and stock promised in one outlet that had already been sold in another.
Why quotation was the right place to start
It would have been easy to start with inventory, because inventory is the thing that sounds broken. We started with the quotation instead.
A quotation is where the enquiry, the price and the stock all have to be true at the same time. Build that path properly and the other two are forced to be correct as a side effect. Build inventory first and you get a beautiful stock module that nobody updates, because updating it is not part of anyone's day.
So the flow is: enquiry captured once, with source and owner. Quotation generated from that enquiry, pulling current price and current availability. Nothing retyped, and nothing quoted that the chain cannot actually supply.
Architecture
| Layer | Choice | Why |
|---|---|---|
| Frontend | Next.js | Server-rendered listings so a slow counter connection still loads; one codebase for staff and owner views |
| API | Separate Node service | Kept apart from the web app so the two restart independently — a UI deploy never interrupts an in-progress quotation |
| Database | MongoDB | Product attributes in this trade are genuinely irregular — tiles, adhesives and fittings do not share a column set, and forcing them into one is a schema you rewrite every quarter |
| Hosting | Dedicated cloud instance, per-service systemd units | Each service restarts on its own; the owner's team never touches a terminal |
The web app and the API run as separate supervised services, which matters more than it sounds: it means the thing the staff use at the counter and the thing that computes a quotation do not share a failure.
What actually changed for the team
Enquiries are in one place with an owner, so nothing sits in a personal phone. Stock is one number per outlet that both branches read from the same source. Quotations go out in the time it takes to review them rather than the time it takes to type them.
The owner gets what they did not have before: the whole chain on one screen. Not a report someone compiles at month end — the current state, now.
Ownership
The client owns the source code, the database and the hosting account. The stack is deliberately conventional — Next.js, Node, MongoDB — because a retailer in this trade should be able to hire anyone to maintain it, not only the firm that built it.
