125 lines
5.0 KiB
Markdown
125 lines
5.0 KiB
Markdown
# 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!`
|
|
|
|
## Shop deployment quickstart
|
|
|
|
For a machine in the shop, use the full guide at [docs/SHOP_SETUP.md](docs/SHOP_SETUP.md).
|
|
|
|
Fast path:
|
|
|
|
```bash
|
|
git clone https://github.com/Krikorios/OMT-SM.git
|
|
cd OMT-SM
|
|
|
|
cp .env.example .env
|
|
cp server/.env.example server/.env
|
|
|
|
npm install
|
|
npm --prefix server install
|
|
|
|
./scripts/start_prod.sh
|
|
```
|
|
|
|
Important:
|
|
|
|
- Set strong secrets in `.env` and `server/.env` before first production use.
|
|
- Match `POSTGRES_PASSWORD` in `.env` with the password inside `server/.env` `DATABASE_URL`.
|
|
|
|
## 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)
|
|
|