Commit Graph
3 Commits
Author SHA1 Message Date
siddharthd 17028c79ff Accept grocery receipts scanned in pantry as candidate spend
ci / lint-test (push) Successful in 48s
Adds /api/receipts/ingest as a sibling to the order lane, sharing its shape but
making one decision differently: nothing is parked. An order can wait for its
statement because it is already visible as an email; a gift-card grocery shop is
visible nowhere at all, so a scan that produces no transaction produces nothing
anyone can see. Every payment becomes a manual row immediately and the existing
pending-reconciliation queue resolves the ones with a card leg coming.

One transaction per tender leg. A $114.57 shop settled $40.75 gift card +
$73.82 Mastercard has a statement line for $73.82 only. A single row marked
credits is excluded from the queue while that line double-counts; marked card it
is searched for at 1% of $114.57 and never matches. Either way the shop books
$188.39. Per-leg rows make each amount the settled amount, so the matcher works
untouched.

Reconciliation now carries expense_metadata across. It already moved overrides,
tags and splits from the manual row to the statement row and left metadata
behind, which did not matter while metadata only came from an email that made
its own transaction. It matters now that it carries a shop's line items:
unmoved, the contents vanish at exactly the moment the statement line appears,
and COLES 0556 MANOR LAKES stays as unreadable as before anything was scanned.
transaction_id is UNIQUE, so a statement row that already has metadata keeps it
and the pantry row is flagged rather than raising a constraint violation.

Also regenerates the Prisma model. card_last4, currency, flags, reconciled_at,
matched_transaction_id, platform and route have been in the database since
migrations 0019/0020 and were absent from schema.prisma — regenerating the
client from it would have dropped columns the order lane writes on every ingest.

23 integration tests against the real schema, built from the three receipts that
drove the design. Existing suites unchanged: 104 unit, 144 integration.
2026-07-30 13:40:32 +10:00
siddharthd b8cd1b0f89 fix(reconcile): prevent split/tag double-counting on reconciled transactions
Move splits, tags and overrides from manual to statement side on reconcile
(delete from manual after copying) instead of just copying. Add read-time
filter to exclude reconciled manual transactions from balance and shared
transaction queries. Also adds participant filter to shared expenses page.
2026-05-11 19:15:41 +10:00
siddharthd 4a49add277 feat: CSV import and batch reconciliation UI
- Add reconciled_with_id column to transactions (links manual → statement tx)
- CSV import wizard: 4-step modal (upload → map columns → review → done)
  - Handles any bank format via column mapping with localStorage presets
  - Single signed or separate debit/credit column modes
  - Editable preview table before committing
  - Auto-tags all imported rows with 'csv-import'
- Batch reconcile page: shows all unreconciled manual transactions with
  potential statement matches (date ±3 days, amount ±1%) pre-fetched
  - Select matches across multiple rows, apply all at once
  - Copies overrides/tags/splits from manual → statement tx atomically
  - Manual tx marked reconciled (linked), hidden from main transactions view
  - Transactions with no matches shown separately
- Import CSV button on transactions page
- Reconcile nav item in sidebar
2026-04-13 06:23:08 +10:00