Case study · Personal
Prepaid-balance bar and stock web app for an association
Members pay from a prepaid balance; admins manage stock and accounts through an append-only, auditable ledger.
In brief
- Web app for an association's bar, replacing a browser-only mock-up: members pay from a prepaid balance, admins manage stock, accounts and top-ups.
- Every balance and stock change is an append-only movement; a nightly check proves the stored values still match, and a cancellation writes the inverse instead of deleting.
- Specified, designed, reviewed and accepted by me; code written with Claude Code. About 430 unit and API tests and 56 browser journeys run before each deployment.
Key facts
- Role
- Sole developer
- Team
- Solo project
- Period
- Status
- Built in October 2026 with two rounds of client feedback
- AI assistance
- major Specification, architecture, review, tests and acceptance by me; code written with Claude Code.
- Stack
Context
A project for the bar of an association, which is not named here. Its only tool was a clickable HTML mock-up that kept its data in the browser. The work went through two formal rounds of client feedback in October 2026.
Problem
Several people sell, restock and top up accounts. Data kept in one browser cannot be shared, audited or backed up. The association needed a real multi-user application where every euro and every bottle can be traced.
My role, the team and AI assistance
Solo project. I wrote the requirements (about 900 lines, with requirement IDs that the commit messages cite), chose the architecture, reviewed the code and ran the tests and acceptance. The code was written with Claude Code from those requirements.
Constraints
- Money: no rounding errors, no double charge if a request is sent twice, no sale beyond the allowed overdraft.
- Traceability: nothing that touches a balance or the stock may be deleted.
- Shared device: a bar tablet used by several people must work without a personal password, without opening the rest of the app.
- Personal data: members can get an export of their data.
Solution and architecture
- Three roles: members buy from a catalogue, pay from their balance, see their history, suggest products and pay their membership fee; admins manage stock, restocking, accounts, top-ups, per-account overdraft limits, a dashboard and a journal exported to Excel; a bar-service mode, usable only from a registered tablet, counts supplies and keeps the shopping list.
- Ledger: balance, stock and supplies are stored values backed by append-only movement tables that record the value after each movement and its author. Money is stored in integer cents, and business rules are also enforced as SQL
CHECKconstraints. - Purchase: one immediate SQLite transaction re-checks stock and balance, takes prices from the server, copies names and prices into the sale lines and uses an idempotency key, so two buyers cannot take the last item at the same time.
- Security: Argon2id passwords, a dummy hash check so response time does not reveal whether an account exists, account lock-out after repeated failures, database sessions storing only token hashes in
httpOnlycookies, same-origin checks on every write and role checks on every route. - Layers: services take the database as a parameter and raise typed errors; API routes validate input with Zod and translate errors into responses.
- Demo: a separate demo instance; demo code is removed from the production build at compile time, and each database records its mode so a demo reset can never touch production.
Stack
TypeScript, SvelteKit and Svelte 5, Vite, Node.js, SQLite with better-sqlite3 and Drizzle ORM, Zod, Argon2id, ExcelJS, Vitest, Playwright, nginx, systemd.
Quality and verification
- About 430 Vitest cases on services, migrations applied to existing data and API routes called through the real request hooks.
- 56 Playwright journeys on a phone and on the bar tablet, each in dark and light theme, plus a demo project.
- The deployment script refuses to ship unless type checks, lint and both test suites pass; it backs up the database before migrations and rolls back if the health check fails.
- A nightly job checks that the sum of the movements equals each stored balance and stock level.
Results and status
Built in October 2026 and reviewed with the client in two feedback rounds. Production usage is not reported here.
What I would do differently
- Run the test suites in continuous integration as well as in the deployment script.
- Write the anonymised public description and screenshots at the same time as the requirements, rather than afterwards.
- Load-test the purchase path on the real tablet before the first use, not only in emulation.
Links
- Source code: private repository.
- Screenshots: to come, from the demo instance.