Add cash management schema and immediate variance alerts
This commit is contained in:
@@ -1,3 +1,90 @@
|
||||
# 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-alpine` via [docker-compose.yml](docker-compose.yml).
|
||||
The Supabase migrations under [`supabase/migrations/`](supabase/migrations/)
|
||||
are replayed on first boot by [`server/db/init/01_run_migrations.sh`](server/db/init/01_run_migrations.sh),
|
||||
preceded by an `auth.users` shim ([`00_auth_shim.sql`](server/db/init/00_auth_shim.sql))
|
||||
so the original `auth.uid()` / RLS policies keep working.
|
||||
- **API:** [`server/src/index.js`](server/src/index.js) — Express. Issues JWTs
|
||||
with `bcrypt` + `jsonwebtoken`, opens a pooled connection per request, sets
|
||||
`request.jwt.claim.*` and `SET LOCAL ROLE authenticated` so RLS evaluates
|
||||
against the caller. Generic `/rpc/:fn` route forwards to `app.<fn>(...)`
|
||||
SQL functions; `/from/:view` exposes an allow-listed set of read views.
|
||||
- **Frontend:** Vite + React 18 + TypeScript + shadcn/ui + Tailwind. The
|
||||
module at [`src/integrations/supabase/client.ts`](src/integrations/supabase/client.ts)
|
||||
is **a drop-in shim** over [`src/lib/api.ts`](src/lib/api.ts) — same
|
||||
`.auth`, `.rpc`, `.from(...)` surface, but talks to the Express server.
|
||||
|
||||
See [CLAUDE.md](CLAUDE.md) for the full design notes and conventions.
|
||||
|
||||
## Run it
|
||||
|
||||
```bash
|
||||
# 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](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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user