quietmason
OPERATIONS · case notes

Replacing spreadsheets with one API

Short answer: A studio group tracked appointments, stock and staff commissions in spreadsheets, one file per branch. The same client could hold two records and monthly commissions were rebuilt by hand. The replacement was one API with a single record per client and per appointment, and the branches reading from it. Handover included the schema, the migration of existing rows, and a check that the commission figure now comes out of the data.

Checked on 2026-10-20

A studio group with several branches tracked appointments, stock and staff commissions in spreadsheets. Every branch kept its own file, the same client could hold two appointment records, and monthly commission numbers were reconstructed by hand at the end of each month.

Constraints

What shaped the work?

Four constraints shaped the build: branches that had been keeping their own files, appointments and stock that had to share one record, commissions computed by hand, and staff who would not use anything slower than a spreadsheet.

  • Staff were not going to learn a new desktop tool — the admin had to work in a browser and be usable on a tablet at the counter.
  • Customers already lived inside a chat app, so the customer-facing surface had to be a Mini Program rather than a web login.
  • Money-adjacent numbers (stock movements, commissions) had to be auditable: who changed what, when.
  • Existing historical records needed to survive the move, including the inconsistencies.
Stack

What does it run on?

React (admin) · Django REST API · MySQL · WeChat Mini Program

19Django apps
39data models
60API endpoints
30admin screens
69schema migrations
99automated tests
Statusin daily use
Deliveredsource, schema, run book
Verifywalk-through, tests included
Build

What did we actually build?

The build replaced four spreadsheets with one API. What follows is the shape of it: the tables, the endpoints the branches call, and the two reports that used to be assembled by hand at month end.

  • One API behind both surfaces: booking, stock, staff, reporting. No business rule lives in the client apps, so the admin and the Mini Program can never disagree.
  • Booking carries explicit conflict rules (staff, room, service duration) instead of the soft warnings a spreadsheet gives.
  • Stock movements are recorded as events tied to the service that consumed them, so a reconciliation can be reconstructed retrospectively.
  • Commission is a function of stored events, not a typed number — recalculating an old month gives the same answer.
  • The accountant exports one file per month from the admin rather than extracting anything from the database.
Decisions

The choices that mattered

Single API, thin clients

Two front ends over one rulebook beats two implementations that drift apart in month two.

Events, not balances

A stored balance cannot answer "why is this number different from last month".

Import the mess, then clean

We imported the historical rows verbatim and flagged the duplicates, rather than silently merging records and losing the trail.

Boring stack

Django and MySQL: a future maintainer can read it without a framework tour.

Left open

What we would do next

  • Branch-level performance views — the manager currently reads branch comparisons off raw exports.
  • Reminder sequence for no-shows, driven from the booking records rather than a separate tool.
Honesty note

What is not on this page

No client name, no screenshots of their data and no metrics: those are theirs. What is here is the engineering, which is the part a buyer can evaluate. On a call we will walk through the code.