← All work

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
Installment System
1 / 6

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
  1. 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.

  2. 02

    Amounts are whole pounds

    No floating-point numbers in any calculation on money, so no rounding error accumulates.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

  9. 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. 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. 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. 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. 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. 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

1 / 10

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 screens

Every 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

Login screenThe session is a row in the database, not just a cookie
Payments screen — the starting pointEach transaction type has its own path and its own record
Collections screenThe repayment percentage is computed from the actual instalment rows, not typed in by hand
Instalment calculatorThe showroom's real pricing rule, implemented in one place

The request cycle — from submission to execution

Sales request boardA request moves through gated stages, not a free-form status
The same board through the investigator's eyesPermissions change what the server returns
Approvals screenExecution stays locked until the manager approves
New instalment request formValidation at entry, and the instalment is calculated live
Benevolent loansA separate track with the same discipline and no interest at all

Treasuries and money movement

Treasuries and staff floatsEach treasury balance is derived from its movements, never written
Treasury movement logEvery pound in or out has a row that explains it
Financial transactionsOne table carrying every display feature the system needs
Confirming digital transfersDigital money does not enter the treasury until a person reviews it
Jam'iyyas — rotating savings groupsA recurring monthly commitment tracked inside the same treasury
Partner settlementThe profit split is computed from an actual ledger

Client files and instalments

Client registerPaid and remaining are calculated per client
Client fileThe client's whole history on a single screen
Instalment matrixEvery due date in a single grid
Client levelsThe classification rules live in the database, not in the code

Stock and suppliers

Stock — unit by unitEvery unit is tracked through to the contract it went out on
SuppliersEach supplier balance is derived from their ledger

Permissions and audit

Users and permissionsOne permission matrix drives both the interface and the server
Activity logEvery change is recorded with a before and after picture
ReportsReport figures match the operational screens
Recycle binDeletion is reversible; the data is never actually removed
Database documentationThe documentation lives inside the app and is tied to the real schema

The same screen in different roles

Payments screen on a cashier accountA cashier sees only their own float
Client register on a cashier accountSame data, fewer permissions

What changed

BeforeAfter
"How much is left on this client?" — open and reconcile three sourcesOne number on the screen, derived from the instalments
Calculating the instalment — on a hand calculator every timeOne formula, the same number in the calculator and in the contract
Who is late today — flipping pages from memoryA work list ordered by what is due
Who took the money / who approved — not recordedAn audit log with actor, time, and the value before and after
Treasury balance — worked out at the end of the dayDerived live from the movements
Manager approval — verbalA gate locked in the code; no execution without it
Collecting outside the showroom — paperThe 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.