**Dai** ("owe" in Swahili) is a mobile-first credit ledger for informal and semi-formal market traders in Kenya. It replaces the trader's memory or notebook, answering one question instantly: **"Who owes me money, and how much?"**
# Dai — Market Trader Credit Ledger
**Dai** ("owe" in Swahili) is a mobile-first credit ledger for informal and semi-formal market traders in Kenya. It replaces the trader's memory or notebook, answering one question instantly: **"Who owes me money, and how much?"**
- Record credit and payments in seconds
- See each customer's current balance and full history
- Works offline; transactions sync safely and exactly once when connectivity returns
- Individual trader accounts with strict data isolation
## Stack
| Layer | Technology |
|---|---|
| Frontend | React, TypeScript, Vite, Tailwind CSS v4, PWA (`vite-plugin-pwa`) |
| Backend | Flask, SQLAlchemy, PostgreSQL, Flask-Migrate (Alembic) |
| Offline | IndexedDB (`idb`) + idempotent sync with client-generated UUIDs |
| Money | Integer minor units (1 KSh = 100 cents), balance always derived from the ledger |
## Repository layout
```
backend/ Flask API (app factory, blueprints, models, tests)
frontend/ React + Vite PWA
```
## Backend quick start
```bash
cd backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt
cp .env.example .env # adjust DATABASE_URL
flask --app run.py db upgrade # or: flask db upgrade (migrations dir)
flask --app run.py seed # optional demo data
flask --app run.py run --debug
```
Tests (SQLite in-memory by default, no database needed):
```bash
cd backend && source .venv/bin/activate
pytest
```
### PostgreSQL parity run
The suite runs unmodified against PostgreSQL and is the release gate. It
verifies eight ledger invariants, including the concurrency guards that rely
on real row locking (`WITH FOR UPDATE` — a no-op on SQLite):
1. customer data isolation between traders
2. exact derived ledger balance
3. payments never exceed the outstanding balance
4. idempotent client-UUID sync
5. duplicate sync batches never double-post
6. voiding reverses the balance without mutating history
7. concurrent payments on one custome …