← All projects

autocare/

A job-management system for HI-OCTEN CAR CARE SERVICES. It is built to take over from the shop's paper notebook: seven short screens to record a finished job, a total the owner cannot mistype, and a printable receipt for every one.

role
Sole developer. Schema, backend, interface, deployment
stack
Next.js 15 · Prisma · PostgreSQL · next-auth · next-intl
started
August 2026
status
Live on Vercel, not yet in use at the shop
The sign-in screen in Filipino, the language the shop actually uses.
The sign-in screen in Filipino, the language the shop actually uses.

“This is a good system for my talyer.”

The shop owner, who is also my grandfather. The system is live, but the shop has not switched over from the notebook yet.
The problem

Totals added by hand, in front of the customer

The shop recorded every job in a notebook: the customer, the car, the parts, the labour, what was paid. Totals were added by hand at the counter, in front of the customer, which is exactly when arithmetic goes wrong. Nobody could answer "how much did we make last month" without turning pages, and a lost notebook meant a lost year.

The build

Seven screens, one decision each

The system is shaped around the one screen the owner will use most, Record New Job. It runs as a seven-step wizard with a single decision on each screen, because a twenty-field form on a phone at a busy counter is a form that gets filled in wrong.

The money

One file computes every peso

Every peso in the system runs through one file, lib/calc.ts. The browser imports it to render the live total. The server imports the same function to compute what it stores. They cannot disagree.

Two rules are enforced rather than merely intended. The total is never an input: in the interface it is rendered text, not a form field. And the server does not trust the client. The validation schema will not even accept subtotal, total, balance or status, so a client that posts them has them dropped and the server recomputes everything from the line items. A test fails if that stops being true.

Arithmetic runs in whole centavos rather than floats. Adding ten lines of ₱0.07 gives exactly ₱0.70, which is the sort of bug you otherwise find after a customer does.
Run it yourself

That file, computing in this page

Change any number below. The totals are computed by a port of lib/calc.ts, the same function the shop's browser and the shop's server both run. It was checked against the original over 2,000 generated cases, every field compared, with no disagreements. Nothing is sent anywhere: the arithmetic happens in your browser, and window.autocareCalc is there if you would rather poke at it from the console.

Parts

The rest of the job

Parts₱286.20
Labour₱450.00
Subtotal₱736.20
Discount₱50.00
Total₱686.20
Paid₱500.00
Balance₱186.20
StatusPARTIAL

The first line is the one worth watching. Ten lots of 0.07 added the way a spreadsheet or a hand-rolled loop would add them comes to 0.7000000000000002, and the parts figure above says ₱286.20 anyway. That gap is the whole reason the money is kept in whole centavos. Set the quantity to 4 or 8 and the naive sum lands exactly; at 3, 5 or 10 it does not. The quantities that survive are the powers of two, which is not something anybody should need to know in order to run a car shop.

Security

What stands between a request and the till

Details

Two languages, and what version one leaves out

The interface ships in English and Filipino, switchable per user. The people running the shop think in Filipino, and a translation stops being a nice extra when the app is somebody's till. Receipts render to PDF in the browser. Continuous integration runs lint, typecheck, unit tests, a migration, a seed, database-backed integrity and access-control tests, and a production build on every push.

Public sign-up, online payments, SMS and multi-branch support are all absent from version one. The schema leaves room for each of them, with notes on where they would go.