Retail — appliance sales on instalment · Deployed and running
Installment System
A complete system for running instalment sales at an appliance showroom — from the customer's request to the last instalment, with every approval step and every pound in between.
- Client
- An appliance showroom (not named) — The showroom is not named anywhere in this package.
- System type
- Full internal operations system

The showroom sold on instalments with no system: paper notebooks for the contracts and schedules, Excel files for follow-up, and a hand calculator for every sale. This system puts the whole cycle in one place — request, investigation, manager approval, contract execution, instalment schedule, collections, treasuries and stock — where each step unlocks the next, balances are derived rather than typed, and every change writes an audit row with a before/after snapshot.
- 67
- Database tables
- 51
- API routes
- 250
- Server actions
- 61
- Permission keys
- 26
- Pages in navigation
- 97,855
- Lines of code
- 1,063
- Commits
- Sector
- Retail — appliance sales on instalment
- System type
- Full internal operations system (requests · contracts · collections · treasuries · stock · reports)
- Coverage
- 26 pages in the app · 51 API routes · 67 database tables
- Users
- Showroom staff in different roles: manager, cashier, investigator
- Language
- Arabic throughout, right-to-left, with Eastern Arabic numerals (٠-٩)
- Devices
- Desktop and mobile — the same system, not a cut-down version
- Timeline
- 26 January 2026 to 5 July 2026 (1,063 commits)
The problem before the system
The showroom sold on instalments with no system. The work was spread across three things at once: paper notebooks for the contracts and the instalment schedules, Excel files for follow-up, and hand-calculator arithmetic for every sale.
Read the detailHide the detail5 points
- No single correct number
- — The treasury balance was on one sheet, the receipts in an Excel file, and what the client still owed in a third notebook. A simple question like "how much is left on this client?" meant opening three sources and reconciling them.
- The instalment was recalculated from scratch every time
- — Appliance price, down payment, administrative fees, margin by term — all of it on a hand calculator, and any multiplication slip stayed in the contract until its last day.
- Collections ran on memory
- — Who is late today? The answer was flipping through pages, so whoever was late stayed late until somebody happened to remember them.
- Responsibility was not on the record
- — Who took this money? Who approved this request? Paper does not answer.
- Staff floats grew unnoticed
- — Nobody noticed, because there was no number updating in real time.
The approach
Read the detailHide the detail
Before any code, the first job was understanding how the showroom actually works — not how it is supposed to work.
Selling on instalment is not a transaction, it is a relationship that runs for up to 20 months. The system has to hold that whole relationship: the appliance, the guarantor, the instalment schedule, every payment, and every follow-up call.
Trust is the real product. The showroom hands over goods against a promise. So the investigation, the guarantor and the trust receipt are not paperwork formalities — they are the substance of the business, and they have to be mandatory steps in the system rather than optional fields.
Money comes in through more than one door — cash at the showroom, a digital wallet, and collection out in the street. Any system that does not unify those paths into one understandable treasury will leave a gap.
Staff will not drop their work to fill in data. Any screen that takes longer than the paper did will not get used.
Engineering decisions that actually matter
Read the detailHide the detail14 points
- 01
Balances are derived, not stored
Any treasury balance is the result of one formula over its actual movements, and no path in the system allows a balance to be edited by hand.
- 02
Amounts are whole pounds
No floating-point numbers in any calculation on money, so no rounding error accumulates.
- 03
The pricing rule lives in one place
Exactly the same formula feeds the calculator, the request form and the contract. The number the salesperson quotes is the number in the contract.
- 04
Soft delete everywhere
Every row is restorable from a central recycle bin, and every query filters out the deleted — so nothing is lost to a mis-click.
- 05
One permission matrix
A single registry feeds the database rows, the hiding of menu items, and the page guard on the server — so the three cannot disagree.
- 06
Enforcement on the server, not in the UI
Hiding a button is a convenience for the user; the real block is in the action itself and in the query constraints.
- 07
Auditing with a before/after snapshot
The activity log answers "what changed, from what, to what, and who did it" — not just a descriptive line.
- 08
On-device cache with "show now, refresh quietly"
A cold open paints from the device immediately and the refresh happens in the background — and any edit invalidates the affected cache straight away.
- 09
No polling
The system is cache-first with invalidation on change, instead of asking the server every few seconds and burning resources for nothing.
- 10
Heavy columns excluded from lists
Images stored as base64 text are not loaded on list screens, only on the detail screen — so lists stay light.
- 11
Dates in the local time zone via Intl
The time zone observes daylight saving, so day comparison is done with a calendar-day string rather than a fixed offset hard-coded in — which is what stops an instalment being counted late a day early.
- 12
One reference table
Every table in the system matches the same specification: sorting, search, filters, column selection, resizable columns, a per-user saved view state, and a useful empty state.
- 13
Printing through a standalone window
Receipts and statements are built as a self-contained page with correct print rules, instead of depending on the screen layout.
- 14
Internal identifiers are never shown
The user sees readable codes like CNT-1039, not database identifiers.
Rules enforced in the code
The core of the system is that each step unlocks the next, and no state is set without the decision of the step before it. The path is: new request → investigation → manager approval → contract execution → instalment schedule, with a rejection branch that requires a recorded reason.
Read the detailHide the detail7 points
A request cannot reach approval before investigation
The investigator visits and writes their assessment, and that assessment is stored with the request — so the manager's decision rests on something written down.
Execution is locked without the manager's approval
No contract is created and no goods leave before that decision.
Execution itself has three conditions that must be met
The client has been contacted, the product has been ordered, and the trust receipt has been signed. These are real switches that keep the execute button locked.
The down payment must be collected in full
Before the contract can be executed.
Terms are 8, 14 or 20 months only
Enforced at the database level itself, not at the UI level.
Repayment is sequential
You cannot pay an instalment before the one preceding it, so the order of the book never gets scrambled.
Digital payment stays pending
Until the manager sees the transfer receipt image and confirms. Up to that moment the wallet balance has not moved.
Arabic and responsiveness
Not a translation — Arabic by design from the start. The whole layout is right-to-left, and displayed numbers are Eastern Arabic (٠-٩) through a global converter that runs before paint so there is no flash of Latin digits.
Read the detailHide the detail5 points
- Wide tables scroll inside their own container and the page itself never scrolls sideways.
- Field font size is large enough that mobile does not auto-zoom when typing.
- Height uses dynamic viewport units, because of the browser bars that move on Android.
- It installs as an app: there is a manifest and icons, so staff add it to the home screen and open it without a browser bar.
- Collecting outside the showroom runs on the same complete system on mobile, not a reduced version of it.
Logging in from mobile
Governance and traceability
Read the detailHide the detail
61 permission keys — 28 for pages, 30 for actions, 3 for reports — all from a single registry.
Every change writes an audit row linked to the entity, with a before and after snapshot, the actor and the time.
Every deletion is restorable from a central recycle bin.
Restrictions live at query level — a staff member sees their own float, not somebody else's.
The screens
28 screensEvery name, number and amount in the screenshots is fully generated demo data, produced on a separate, disposable local database instance with a seeded random generator. There is no real customer data and no real amount, and no real figure was rescaled — so the showroom's actual numbers cannot be inferred from these images. The showroom's own identity (name, address, phone, wallet number) was written into the source code in 40 files, including logo SVGs that hold the name as text; it was substituted in the copy that the captured build came from. Before every shot the page text is swept, and a check rejects the shot outright if any forbidden string is found — no image was written to disk without passing that check.
Daily work
The request cycle — from submission to execution
Treasuries and money movement
Client files and instalments
Stock and suppliers
Permissions and audit
The same screen in different roles
What changed
| Before | After |
|---|---|
| "How much is left on this client?" — open and reconcile three sources | One number on the screen, derived from the instalments |
| Calculating the instalment — on a hand calculator every time | One formula, the same number in the calculator and in the contract |
| Who is late today — flipping pages from memory | A work list ordered by what is due |
| Who took the money / who approved — not recorded | An audit log with actor, time, and the value before and after |
| Treasury balance — worked out at the end of the day | Derived live from the movements |
| Manager approval — verbal | A gate locked in the code; no execution without it |
| Collecting outside the showroom — paper | The same complete system on mobile |
The operational impact here is described in terms of what became possible, not in performance figures — because the showroom's real operating numbers are not measured here, and were deliberately not measured.
The numbers
System size
- Page files
- 40
- Pages in navigation
- 26
- API routes
- 51
- Database tables
- 67
- Enum types
- 52
- Server actions
- 250
- Permission keys (28 page · 30 action · 3 report)
- 61
- Lines of code
- 97,855
- UI components (23 of them shared primitives)
- 115
- Test files
- 90
- Commits
- 1,063
- Database migrations
- 15
- First commit → last commit
- 2026-01-26 → 2026-07-05
Every figure here is measured by a re-runnable command from the repository root. None of them is a guess.
Figures visible in the screenshots
- Generated clients
- 64
- Contracts
- 54
- Instalments
- 876
- Approved payments
- 428
- Pending payments
- 7
- Stock units (110 sold · 81 available)
- 191
- Sales requests (across 6 stages)
- 23
- Benevolent loan requests
- 18
These come from the generated demo data, not from real operation. They are listed so anyone reviewing the screenshots can see that what is in them is internally consistent: the seed script checks its own invariants before it finishes and fails loudly if any of them breaks.
Pricing rule check
- Cash price (EGP)
- 27,500
- Down payment (EGP)
- 5,500
- Instalment — 8 months
- 4,390
- Instalment — 14 months
- 2,480
- Instalment — 20 months
- 1,845
The figures shown in the calculator screenshot. They were recomputed by hand from the pricing rule (subtract the down payment, add 4% administrative fees, then apply a margin per term, rounded up to the nearest 5 EGP) and they match; each plan's total equals the instalment times the number of months. The amounts themselves are demo data.
Figures deliberately not claimed
- The showroom's real customer count
- —
- Real sales volume or revenue
- —
- Active user count
- —
- Hours saved / error reduction
- —
- Load time or performance figures
- —
- Test coverage percentage
- —
- Development time in hours
- —
None of these appears anywhere in this document, because none of them is measured. The test file count is measured (90) but coverage was never run; the commit dates are measured but the actual hours worked are not. The production database was deliberately never touched, so nothing about the showroom's real customers, sales or revenue is stated. There is no before/after measurement, so no "saved X hours" or "cut errors by Y%" claim is made.




