Files
monk-code-screen/README.md
T

66 lines
2.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# monk-code-screen
Billing/invoicing/payments backend skeleton. Bun + Hono + `bun:sqlite` (zero network deps at runtime; TS runs natively, no build step).
## ⚡ START COMMAND (sticky)
```sh
bun run dev # http://localhost:3000, hot reload
```
Other commands:
```sh
bun run start # no watch
bun run db:reset # wipe data.db and reseed
bunx tsc --noEmit # typecheck
```
## Layout
```
src/index.ts Hono app + routes
src/ingest.ts CSV parser/normalizer + transactional insert (shared by seed and POST /ingest)
src/db.ts opens data.db, applies schema, seeds from invoices.csv if empty (runs on import)
src/schema.sql customers / invoices / payments (idempotent)
invoices.csv seed data — deliberately messy; DO NOT MODIFY
```
DB is `data.db` at repo root (WAL, FK on). Boot always re-applies schema, so adding a table = edit schema.sql + restart.
## Routes
```sh
curl localhost:3000/health
curl localhost:3000/customers
curl localhost:3000/customers/<cus_id>/invoices
curl localhost:3000/invoice/INV-1011 # by invoice_number or surrogate inv_ id
curl -X POST localhost:3000/ingest --data-binary @invoices.csv
curl -X POST localhost:3000/payment/INV-1011 \
-H 'content-type: application/json' \
-d "{\"idempotency_key\":\"$(uuidgen)\",\"amount\":1500000,\"currency\":\"USD\"}"
```
- `POST /ingest`: CSV as raw request body. Rows failing validation (negative values, empty currency, missing/unparseable amount or dates) are still stored, with status `corrupted`. Returns `{ status, ingested, errors, rows }`. No idempotency — re-posting the same file appends duplicates.
- `GET /invoice/:id`: invoice (with status) + its payments (with PSP-owned status). Resolves surrogate id or invoice_number (newest wins on duplicate numbers).
- `POST /payment/:invoice_id`: zod-validated `{idempotency_key: uuid, amount, currency}`; only `open`/`partially_paid` invoices are payable (409 otherwise — includes `corrupted`), repeated key replays the original payment (200), else inserts a `pending` payment (201).
Money is stored as integer minor units (cents): CSV `150``15000`.
## Seed data
Seeded by ingesting `invoices.csv` (25 rows → 22 open + 3 corrupted, 21 customers):
- `INV-1006` (line 7): empty currency code
- `INV-1010`: negative quantity and amount
- `INV-1020`: missing amount
Duplicate invoice numbers (`INV-1001` ×2, `INV-1006` reused) are ingested as-is — not a validation rule on the canvas. Customers dedupe by email case-insensitively (`Acme Corp`/`ACME CORPORATION`/`acme corp` → one customer); rows without email get a customer keyed by name.
## Extension cheatsheet (interview)
- New route: add to `src/index.ts`, `db.query(...).all()/get()`, `db.run(sql, [params])`
- Multi-write: `db.transaction(() => { ... })()`
- Validation: `z.object({...}).safeParse(await c.req.json())`
- New table: append `CREATE TABLE IF NOT EXISTS` to schema.sql, restart (or `db:reset` if changing existing tables)