# MandiLive — multi-mandi agri commodity auction platform

Vanilla **PHP 8.1+ / MySQL 8 / JavaScript** — no framework, no Composer, no build step. Money is **integer paise** everywhere; never floats.

## Setup (XAMPP or any LAMP)

1. Copy this folder to `htdocs/mandilive` (Windows XAMPP: `C:\xampp\htdocs\mandilive`).
2. Create the database: open phpMyAdmin → SQL tab → paste the whole of `schema.sql` → Go. (Or `mysql -u root < schema.sql`.)
3. Edit `config.php` if your MySQL user/password isn't `root` / empty.
4. Make `uploads/` writable by the web server: `chmod -R 775 uploads` (Linux) — XAMPP Windows needs nothing.
5. Seed demo data: visit `http://localhost/mandilive/seed.php` once.
6. **Cron (safety net, recommended):** every minute run `php /path/to/mandilive/cron.php`
   - Linux crontab: `* * * * * php /path/.../cron.php`
   - Windows: Task Scheduler → run `php.exe C:\xampp\htdocs\mandilive\cron.php` each minute.
   - Without cron the platform still works: every API poll and page load runs the same lazy tick, so auctions close as long as *someone* has a page open. Cron only guarantees closes when nobody is watching.
7. Open `http://localhost/mandilive/` — public price board. Login at `auth.php`.

## Demo logins (after seed.php)

| Role | Login | Credential |
|---|---|---|
| Super admin | super@mandilive.test | Password@123 |
| Mandi admin (Salem) | admin.slm@mandilive.test | Password@123 |
| Mandi admin (Erode) | admin.erd@mandilive.test | Password@123 |
| Gate operator | gate.slm@mandilive.test | Password@123 |
| Assayer | assay.slm@mandilive.test | Password@123 |
| Auctioneer | auction.slm@mandilive.test | Password@123 |
| Accountant | acct.slm@mandilive.test | Password@123 |
| Auditor (read-only) | auditor@mandilive.test | Password@123 |
| Farmer (Tamil UI, has agent) | mobile **9000000001** | OTP shown on screen (dev mode) |
| Farmers | 9000000002 · 9000000003 | OTP on screen |
| Agent | 9000000009 | OTP on screen |
| Traders (₹5L/₹3L/₹2L limits) | 9111111111 · 9111111112 · 9111111113 | OTP on screen |
| Trader awaiting approval | 9111111114 | OTP on screen |

The seed leaves the system mid-story: one **open turmeric auction live** (two bids in), one **sealed paddy auction live**, farmer 9000000001 has a **sale awaiting accept/reject**, trader 9111111111 **owes a payment** (mock Pay works end-to-end → gate pass → dispatch), and yesterday's five paid trades feed the **public price board** and are **eligible for a settlement run** in the accountant screen today (T+1 cycle).

## 5-minute demo script

1. `index.php` — min/max/modal prices + arrivals; CSV/JSON export links.
2. Login trader 9111111111 → live grid → open turmeric auction → bid (quick +₹ buttons); watch anti-snipe extend if you bid in the last 60 s.
3. Login farmer 9000000001 (UI switches to Tamil) → accept the groundnut sale → trade is struck, fees computed, agreement/invoice generated.
4. Back as the trader → Pay now → mock gateway success → accountant issues gate pass → gate operator verifies token → lot dispatched.
5. Accountant → run settlement for yesterday → farmers marked paid (mock payouts), lots closed.
6. Gate operator: turn off Wi-Fi, record an arrival — it queues in localStorage; back online it syncs with a UUID `client_ref` (no duplicate lots, server-assigned lot numbers only).

## Architecture notes (what is deliberate)

- **Lazy auction scheduler**: `engine_tick()` runs on every API/page hit, guarded by MySQL `GET_LOCK`, plus `cron.php` as the safety net. Deviation from the spec's queued-jobs design — no queue infra in shared hosting.
- **Bids are append-only**; the auction row's `high_*` columns are a cache, `bids` is the source of truth. Retraction is a separate append row, allowed only where the mandi enables it.
- **Fees are computed once at strike and persisted** per-line (`trade_lines`), so changing mandi rates never rewrites history.
- **Sealed auctions** reveal nothing until close, then show a ranked top-10 with masked bidder codes.
- **Seller confirmation** window is per-mandi (`confirm_window_min`, 0 = auto-strike). Nothing is held from the trader while the seller decides.
- **Offline gate entry** is idempotent on a client-generated UUID; lot numbers are only ever server-assigned.
- **i18n**: full English + Tamil dictionaries; farmer screens honour the user's `lang`.

## NOT implemented / known gaps (read before trusting this in production)

1. **Never load-tested or security-audited.** This is a demo-grade codebase.
2. **Staff 2FA** — spec asks for it; only email+password with lockout is built.
3. **KYC document upload/verification** — registration collects fields only; no file upload, no verifier UI.
4. **Real payment gateway** — `payments.php` is a MOCK that always succeeds/fails on demand. The webhook signature check exists, but you must write a real adapter (Razorpay/Cashfree) behind the same interface before real money moves.
5. **Price-adjustment disputes** — disputes support uphold or cancel+refund only; partial price adjustment isn't built.
6. **EXIF stripping needs GD** — without the GD extension photos are stored as uploaded (location metadata intact). Enable `gd` in php.ini.
7. **Gate-pass QR uses an external image service** (api.qrserver.com) — offline mandis get the token as text only; swap in a local QR lib for production.
8. **SMS OTP is not wired to any provider** — dev mode prints the OTP on screen (`dev_show_otp` in config). Integrating MSG91/Twilio is a TODO in `auth.php`.
9. **No automated tests** and this build environment had **no PHP/MySQL to execute against** — syntax and structure were verified statically (bracket balance, i18n key coverage, defined-vs-called functions, `node --check` on app.js), but you must run schema+seed once and click through before demoing.
10. **Single-server assumptions** — file uploads on local disk, sessions in files, `GET_LOCK` for the tick. Fine for one box; not for a cluster.
11. See `COMPLIANCE-TODO.md` for the legal/regulatory list (APMC, GST, stamp duty, data protection). **Invented business rules that need your veto**: fee payer split (buyer: market+platform+GST; seller: cess+agent commission), grade cutoffs in the seed, T+1 settlement default, 48 h payment deadline.

## File map

`index.php` public prices · `auth.php` OTP/staff login+registration · `dashboard.php` role router · `gate.php` arrivals/weigh/exit-verify · `assay.php` grading · `auctioneer.php` schedule/pause/cancel/relist · `auction.php` live room · `api.php` JSON (poll/bid/retract/live_list/gate_sync) · `trader.php` limits+dues · `farmer.php` lots+confirmation+payments · `ma.php` mandi admin · `sa.php` super admin/auditor · `acct.php` payments+settlement+disputes · `pay.php` mock checkout+webhook · `print.php` agreement/invoice/gate-pass/verify · `engine.php` auction core · `fees.php` fee lines · `sm.php` state machine · `gate_lib.php` idempotent lot create · `seed.php` demo data · `cron.php` tick.
