test(intake): cover the review findings, and CLAUDE.md for the rules they set
ci / lint-test (push) Successful in 59s
ci / lint-test (push) Successful in 59s
Unit: word-boundary merchant matching (Bigals Pizza is not an IGA), mrkdwn escaping vs the plain_text button label, a foreign total on a card, and the finish_reason gate. Integration: the NULL-platform wildcard on both the food and grocery paths, candidates crossing platforms, EXCLUDE_RECONCILED_SOURCE, the pinned metadata row, the panel reading the row the write landed on, the once-per-batch undo baseline, the priceless-upload hold, Create re-matching, two pending cards booking once, a pantry-resolved sibling, and a USD receipt. Eleven of the thirteen fail against the pre-fix tree; the exceptions are the no-batch-id baseline guard (a no-change assertion) and the concurrent-tap one, which is a race and passed either way.
This commit is contained in:
@@ -640,7 +640,36 @@ and the cross-repo contract are in smarthome `plans/intake-router-slice-plan.md`
|
||||
`intake_use_candidate` / `intake_create` buttons; a transaction is created
|
||||
only on the explicit tap, ING-10-style (`statement_id NULL`, `owner_id` from
|
||||
`shared_by` → `participants.email`, else `DEFAULT_OWNER_ID`;
|
||||
`source='intake'`, `source_ref=batch_id` so a second press is a no-op).
|
||||
`source='intake'`, `source_ref=batch_id` so a second press is a no-op —
|
||||
written as `ON CONFLICT … DO NOTHING`, because a SELECT-then-INSERT loses a
|
||||
double tap). A non-AUD receipt is booked the way `order-ingestion.ts` books
|
||||
one: `amount` native, `amount_aud` **NULL**, `foreign_currency_*` set — no FX
|
||||
rate exists at intake and inventing one is worse than admitting there is none.
|
||||
- **A NULL platform means "no platform", not "any platform".** An in-store tap
|
||||
(`coles`, `woolworths`, `other`) matches only rows whose `expense_metadata`
|
||||
carries no platform of its own. The wildcard reading let a till receipt match
|
||||
the Uber Eats order for the same shop on the same day and overwrite its items.
|
||||
The **candidates** query is the opposite and deliberately so: it has no
|
||||
platform filter at all, because the card is a human choice shown precisely
|
||||
because the exact key missed — the platform is one of the things that may be
|
||||
wrong, so the list must widen there, not narrow.
|
||||
- **The Create tap re-runs the match before it inserts**, and checks for a
|
||||
resolved sibling batch. The card is minutes old by the time it is pressed and
|
||||
the email/order bridge books orders on its own schedule; `findDuplicateBatch`
|
||||
ignores *unresolved* batches by design, so two pending cards for one order
|
||||
would otherwise each book it.
|
||||
- **One `expense_metadata` row, agreed on by three queries.** A transaction can
|
||||
own a row *and* be the `matched_transaction_id` of another; `matchTransaction`
|
||||
returns the row it resolved through and `replaceLineItems` writes to exactly
|
||||
that one. All three (match, write, `GET /api/transactions/[id]/order`) order
|
||||
by `(transaction_id = $1) DESC **NULLS LAST**, id` — the `NULLS LAST` is
|
||||
load-bearing, because Postgres orders `DESC` as `NULLS FIRST` and the row that
|
||||
merely *points* at the transaction has a NULL `transaction_id`, so the clause
|
||||
meant to prefer the owned row preferred the other one.
|
||||
- **`intake_batches.previous_line_items` is the undo baseline, written once.**
|
||||
Returning whatever the write just read makes a pantry retry get its own write
|
||||
back as the "previous" state, so undo restores the thing it undid. Recorded
|
||||
under `COALESCE(previous_line_items, …)` and read back.
|
||||
- **Grocery never writes finance here.** The ORIGINAL files go to pantry
|
||||
`POST /api/capture` with `intake.finance_target_transaction_id` +
|
||||
`finance_action: enrich|create`; pantry's confirm calls
|
||||
|
||||
Reference in New Issue
Block a user