Skip to content
jad-Elevator-web

Case study · client work

Jad Elevateur

Built for an elevator installation and maintenance company in Morocco: one back office for worksites, stock, invoicing, cash, attendance and payroll.

Role
Sole engineer, from the brief to production
Year
2026

The challenge

An elevator company wanted its whole operation in one back office, under Moroccan legal rules, with the margins hidden from anyone not cleared to see them.

In numbers

  1. 9 days17 to 25 July 2026: every business module in place, 534 of the project’s 605 commits.
  2. 186API routes, each naming the roles and the scope allowed to call it.
  3. 13Business rules, numbered P1 to P13 and cited in the code where they apply.
  4. 3Languages: French first, then English and Arabic, right to left.

What it does

One loop, from contract to cashbox

A client signs a worksite contract or places an order. Products leave the stock, a quote becomes an invoice or a delivery note with its legal number, and payments arrive in cash, by cheque or by transfer before landing in a cashbox.

Purchasing runs through supplier requests that restock the inventory when they are received. On the people side: daily attendance, leave against an 18-day yearly quota, missions to other cities at a per-diem rate, and a monthly salary statement for each member.

Every change is written to an append-only journal, and the dashboard is split by role: operations for everyone, finance only for those cleared to see it.

The operating loop

How a worksite turns into cash

One worksite from the signature to the cashbox, and the purchase that refills the stock it used. Each arrow is a flow the API implements.

Clientcompany or person · ICEChantiercontract · lines · chargesStockledger · alertsDevis → FactureFAC-2026-0007Paymentcash · cheque · transferCaisseappend-only ledgerFournisseurdemandesignstakes productsquote, then invoicepaid in partslands inlow stock: demandereceived: restock

Money

Money is never a float, and never edited

Every amount is an integer number of centimes and every VAT rate is in basis points: 2000 is 20%. Counters such as what has been paid or a cashbox’s balance move only by atomic increments, never by assignment.

The cashbox ledger is append-only. Each row carries the balance after it, and a mistake is corrected the way an accountant would: by a contre-passation, an inverse row, never by editing or deleting the first one.

Prices, costs, the issuer’s legal identifiers (ICE, IF, RC, patente, CNSS, RIB) and the buyer are frozen into a document when it is issued. Closing a worksite freezes its margin; validating a salary statement freezes its numbers, so a later attendance fix cannot move a month already paid.

How it was built

Every module, the same six steps

  1. Step 1 of 6

    Spec

    A deep-dive before any code: schema, endpoints, screens and acceptance criteria. Ten of them, kept in the repository.

  2. Step 2 of 6

    API module

    One module per domain on Bun and Hono: Mongoose models, Zod schemas, and routes that each declare their roles and scope.

  3. Step 3 of 6

    Back office

    The matching slice in React: typed routes, query hooks, forms validated with the same rules, French first.

  4. Step 4 of 6

    Scopes

    The back office’s permission list is synced from the API by a script, so no button shows for an action the server would refuse.

  5. Step 5 of 6

    API tests

    Thirty Python scripts drive the real API flow by flow: orders, payments, closures, payroll, reversals.

  6. Step 6 of 6

    Claude Code

    Both repositories carry Claude Code’s instructions, skills and agents: the same playbook, applied to every module.

Finance visibility

Margins never leave the server

Who may see costs, margins and payroll totals is decided on two floors: an organisation-wide switch per role, on for managers and off for HR by default, then a block on a single member. Admins always see them.

The rule is applied to the response itself, not to the screen. For someone not cleared, the fields are absent from what the API sends, so no browser tool can reveal them.

A block on a member takes effect on their next request. The organisation switch is cached for 30 seconds, so flipping it lands within half a minute, without anyone logging out.

Built with

  • Bun
  • Hono
  • MongoDB
  • Mongoose
  • Redis
  • WebSocket
  • Zod
  • React 19
  • Vite
  • TanStack Router
  • TanStack Query
  • TanStack Table
  • Zustand
  • Tailwind CSS
  • shadcn/ui
  • PM2
  • Docker
  • Claude Code

Where it stands

Every module of the brief, built

Attendance, payroll and missions, leave, products and stock, worksites, clients and invoicing, the two cashboxes, cheques, supplier requests and the dashboards are in, in French, English and Arabic. Still to come: a read-only login for employees and interns.

Talk about yours
Modules in the client’s brief, each built as an API module and a back-office slice.
11
Roles, from intern to admin; three of them sign in to the back office.
5
Next caseAissam Portfolio