docs: order verdicts are built; record the four load-bearing shape decisions
ci / lint-test (push) Successful in 41s
ci / lint-test (push) Successful in 41s
This commit is contained in:
@@ -183,8 +183,45 @@ category-level ratio that "looks wrong" usually is not.
|
|||||||
offset account, 39 rows, $37,980.24) are categorised `transfers`, indistinguishable
|
offset account, 39 rows, $37,980.24) are categorised `transfers`, indistinguishable
|
||||||
from ordinary internal transfers. The loan model below is unbuilt.
|
from ordinary internal transfers. The loan model below is unbuilt.
|
||||||
|
|
||||||
**`order_reviews` is a table wired to nothing** — 0 rows, no API, no UI, no
|
### Order verdicts — "never order from here again"
|
||||||
writes. The "never order from here again" capability does not exist.
|
|
||||||
|
Built 2026-07-28 (migration 0024). `order_reviews` was previously a table wired
|
||||||
|
to nothing; it now backs `GET`/`PUT /api/transactions/[id]/review`, with the UI
|
||||||
|
inside `components/order-details.tsx`. Logic in `lib/order-reviews.ts`.
|
||||||
|
|
||||||
|
Four things about the shape, each of which is load-bearing:
|
||||||
|
|
||||||
|
- **Per person, not per order.** `UNIQUE (transaction_id, participant_id)`. A
|
||||||
|
shared meal produces two opinions that routinely disagree, and the
|
||||||
|
disagreement is the useful part. `PUT` defaults to the **signed-in user**,
|
||||||
|
not the owner — Sonu authenticates through the same Traefik OAuth as
|
||||||
|
participant 4, so an owner default would file her verdict under his name.
|
||||||
|
- **Four levels** — `loved`, `liked`, `ok`, `never`. Three collapsed the
|
||||||
|
distinction that decides a re-order: "loved" and "liked" both mean "would
|
||||||
|
order again" but only one is worth a detour, and "ok" is not a
|
||||||
|
recommendation.
|
||||||
|
- **Item verdicts key on the item DESCRIPTION**, not its index — an index is
|
||||||
|
meaningless across orders, and "the Pad Thai here is good" has to survive
|
||||||
|
into the next order from the same merchant. Pooled case-folded across the
|
||||||
|
merchant's orders. Only `loved`/`never`: a per-item "ok" answers neither
|
||||||
|
question you ask at order time.
|
||||||
|
- **An ABSENT `item_verdicts` means "leave them alone"; `[]` clears them.**
|
||||||
|
Without that distinction a note-only save wipes every per-item opinion — the
|
||||||
|
same shape as the bug that reset `settled` on split rewrites, and just as
|
||||||
|
invisible on screen.
|
||||||
|
|
||||||
|
**Sharing is a real 50/50 split, not a flag.** The "Shared with Sonu" toggle
|
||||||
|
writes `transaction_splits`, because the split already IS the record that an
|
||||||
|
order was shared and two records of one fact drift apart. Safe to clear on an
|
||||||
|
order because an ingested order is post-cutover by construction.
|
||||||
|
|
||||||
|
The merchant signal is *derived* by aggregating a merchant's orders on
|
||||||
|
`expense_metadata.merchant_normalized` — never `transactions.merchant_name`,
|
||||||
|
which is a bank descriptor and reads differently for the same restaurant on
|
||||||
|
different nights.
|
||||||
|
|
||||||
|
`/api/orders/ingest` returns `prior_verdict` so the n8n Slack nudge can warn at
|
||||||
|
the moment an order lands.
|
||||||
|
|
||||||
### The shared loan
|
### The shared loan
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user