CRM OMT — Cash Collection Management System
A multi-shop POS / cash control / reconciliation web app for the typical Lebanese cell-phone or mixed-retail shop that also runs an OMT and/or Whish counter alongside repair and item sales.
The problem
Most Lebanese cell shops don't just sell phones and do repairs — they also act as OMT and/or Whish agents, sell Alfa / Touch / Ogero recharges, take EDL bill payments, and swap between USD and LBP. All of that money flows through one drawer, in two currencies, across multiple employees and shifts, mixed with goods and repair income.
The result for the owner is the recurring "incompatible cash" pain: at the end of the day the drawer doesn't match what the system should hold, and there is no clear accountability per cashier or per shift.
What this app gives the owner
- Per-employee, per-shift accountability with a blind close: the cashier declares the cash count, the system computes what was expected, and any variance is recorded against that exact shift.
- Live variance panel at end-of-shift (Expected / Counted / Δ in USD and LBP).
- Owner overview tab — open shifts, open alerts, today's gross, recent closed-shift variances, and a 30-day cashier scorecard.
- All money streams in one ledger — OMT send/receive, Whish, Alfa/Touch/Ogero recharge, EDL bill, FX swap, deposits/withdrawals, refunds, voids, mid-day safe drops, repair, goods sale.
- DB-enforced accounting: atomic sale coupling, cash-movement sign guards, voids reverse movements, idempotency keys, RLS by shop and role.
- Alerts pipeline for chronic shorts, void spikes, override spikes, reconciliation backlog, after-hours activity, voucher write-offs, stock shrinkage.
Architecture
The schema started life on Supabase but the app no longer uses Supabase at runtime. It runs against a self-hosted Postgres + a thin Express API:
- DB:
postgres:16-alpinevia docker-compose.yml. The Supabase migrations undersupabase/migrations/are replayed on first boot byserver/db/init/01_run_migrations.sh, preceded by anauth.usersshim (00_auth_shim.sql) so the originalauth.uid()/ RLS policies keep working. - API:
server/src/index.js— Express. Issues JWTs withbcrypt+jsonwebtoken, opens a pooled connection per request, setsrequest.jwt.claim.*andSET LOCAL ROLE authenticatedso RLS evaluates against the caller. Generic/rpc/:fnroute forwards toapp.<fn>(...)SQL functions;/from/:viewexposes an allow-listed set of read views. - Frontend: Vite + React 18 + TypeScript + shadcn/ui + Tailwind. The
module at
src/integrations/supabase/client.tsis a drop-in shim oversrc/lib/api.ts— same.auth,.rpc,.from(...)surface, but talks to the Express server.
See CLAUDE.md for the full design notes and conventions.
Run it
# one-time
cp server/.env.example server/.env # set JWT_SECRET, etc.
npm install
npm --prefix server install
# day-to-day (DB + API + Web all together)
npm run dev:all
# DB → docker container crm_omt_db on :5432
# API → node server on :4000
# WEB → vite on :5173
# build the frontend
npm run build
# wipe & rebuild the DB (reruns migrations + reseeds the admin)
npm run db:reset
Default seed admin (override via env in docker-compose.yml):
- email:
admin@local.test - password:
ChangeMe123!
Tech stack
React 18, TypeScript, Vite, shadcn/ui, Tailwind, TanStack Query, react-hook-form, zod · Express 4, pg, bcrypt, jsonwebtoken · PostgreSQL 16 · Docker Compose. 💼 Employee Payment Reconciliation Dashboard A mini web application built with React and Supabase that tracks daily collection vs deposit transactions for employees and enforces a carry-forward balance logic. If an employee's deposit on a given day is less than the collection amount, the shortfall is rolled over and must be cleared by future deposits. The app processes transaction data, maintains a running balance, and generates an intuitive dashboard to visualize employee payment behavior.
Key Features:
Fetch and display employee-wise transaction data
Calculate running outstanding balances per employee
Apply deposit payments to past shortfalls based on business rules
Built using React, TypeScript, and Supabase (PostgreSQL backend)