-- Receipt scans as a second producer on the order lane. -- -- A grocery shop paid with a supermarket gift card settles against no statement and -- arrives in no mail, so the pantry scan is the only touchpoint that ever sees it. Rather -- than a second ingestion mechanism, a scan lands as a manual transaction -- (statement_id IS NULL) and the existing pending-reconciliation queue resolves it — -- with needsCardMatch() already excluding cash/credits rows for which no card leg is -- ever coming. -- The receipt as text, kept as evidence rather than parsed into a decision. The token -- that distinguishes a gift card from a bank card was only findable by reading two -- payments side by side on one receipt; whether it holds across merchants is answerable -- from stored blocks and not at all from none. ALTER TABLE expense_metadata ADD COLUMN IF NOT EXISTS tender_raw TEXT; -- The image the extraction came from. Not the idempotency key -- a photo and the store's -- e-receipt PDF of one purchase hash differently -- but exact where it applies, and the -- only thing a later cross-source dedupe against an emailed or Paperless copy could match -- on. ALTER TABLE expense_metadata ADD COLUMN IF NOT EXISTS receipt_sha256 TEXT; CREATE INDEX IF NOT EXISTS idx_expense_receipt_sha256 ON expense_metadata (receipt_sha256) WHERE receipt_sha256 IS NOT NULL; -- Legs of one split-tender shop, so a $40.75 gift-card row can be shown as part of a -- $114.57 purchase instead of an orphan. The shared receipt identity is carried in -- order_reference ('pantry:#'); this column is -- what makes the group queryable without parsing that string. ALTER TABLE expense_metadata ADD COLUMN IF NOT EXISTS receipt_group TEXT; CREATE INDEX IF NOT EXISTS idx_expense_receipt_group ON expense_metadata (receipt_group) WHERE receipt_group IS NOT NULL;