Cerbiol Gadgets point of sale
A point of sale and inventory system the shop runs on every day, with per-device IMEI tracking, part payments, invoices, debt follow-up and an audit log.
- When
- Live in production
- Context
- Cerbiol International Co Ltd, a phone and gadget retailer in Nigeria
- Role
- Solo build
- Next.js 16
- React 19
- TypeScript
- Supabase
- PostgreSQL
- Row-level security
- Tailwind CSS v4
- Vercel
The problem
The shop tracked stock, part payments and debts on paper. Devices are individually identifiable by IMEI, so quantity-based inventory could not say which handset was sold to whom, and no record showed who changed what.


What the shop uses it for
- Devices are tracked one by one by IMEI or serial number; accessories are tracked by quantity.
- A sale takes full or part payment in cash, transfer or card, and records which business account a transfer landed in.
- Every sale produces a printable invoice, numbered INV-YYYYMMDD-####, carrying its payment history and outstanding balance.
- Outstanding debts are tracked with follow-up payments against the original invoice.
- Staff accounts get a temporary password and a forced change on first login, and can be deactivated and reactivated.
How the money is protected
- Money is only ever written through two PostgreSQL functions, process_sale and record_payment. Each one locks the rows it touches and updates stock, totals, payments and the audit log in a single transaction, so a half-finished sale cannot exist.
- Both functions run SECURITY DEFINER with their own checks: only an active, signed-in user can call them, and the seller recorded is always the signed-in user, never a value sent by the browser.
- Supabase row-level security backs the functions, and the service-role key never leaves the server.
- An audit log records who did what, which is what makes a discount or a price change answerable.
How it ships
- Database changes go out as dated migrations, and each function is backed up before it is replaced, so a rollback is one file.
- Production deploys run from main through Vercel preview builds on every pull request, and merges happen after shop hours.
The result
- The shop runs its daily trading on it: sales, part payments, debts and staff accounts.
- Every money-moving action is transactional, attributable and reversible by record.
Private client repository. The screenshots are redacted.
Keep reading