Receipt Tracker, Build Complete (2026-06-13)¶
Mobile receipt capture + pending-reconciliation queue that links receipts to bank transactions once SimpleFIN catches up (1-2 day lag), then attaches the image to the matching ERPNext Journal Entry. Built and tested end-to-end, live.
What shipped¶
| Piece | Where |
|---|---|
receipts table |
forge_finance_db.py SCHEMA + helpers (insert_receipt idempotent on image_path, list_receipts, set_receipt_match, set_receipt_erpnext_file); init() creates it |
| Capture + review UI | forge_finances_ui_receipts.py blueprint; registered in forge_finances_app.py; nav item in forge_finances_ui_base.NAV_ITEMS |
| Reconcile engine | forge_finance_receipt_reconcile.py (--apply / dry-run / --no-erpnext) |
| Timer | forge-finance-receipt-reconcile.timer (08:00 CT) + .service, enabled |
| CF hostname | receipts.justinsforge.com on VR Alliance tunnel to 127.0.0.1:8096 |
| CF Access | app 0a3afa54-a1de-45d3-9c19-5cb94ac56641, allow [email protected] only |
| Memory | memory/general/reference_receipt_tracker.md + MEMORY.md index entry |
Surfaces¶
- receipts.justinsforge.com: camera capture, business chips (WIEB/JWVR/NOVA/SIP/GOC),
optional notes. Stores
data/receipts/<date>_<uuid>.<ext>, insertsstatus=pending. Bare-host root redirect (before_requestin the app) sends it to/receipts/. - Receipts tab on the hub (
/receipts/review): pending receipts with a manual-match dropdown of nearby business charges, recently matched/auto with ERPNext attach state, "Open capture" button to the mobile site.
Reconcile logic (08:00 CT, after the 07:00 sync)¶
- Pending receipt, find business-slice outflow txns (effective scope =
COALESCE(NULLIF(t.scope_override,''), a.scope, 'personal'), source IN simplefin/bankcsv,superseded=0) within +/-2 days of capture. Exactly one, auto-linkstatus=matched. Zero or >1, stay pending for manual match. - Matched + no
erpnext_file_id, look up the JE (cheque_no=FINDB-<txn_id>, doctype Journal Entry), upload image via/api/method/upload_file, store the File docname. No JE yet, retry next run.
READ-ONLY on accounts/transactions; WRITES only receipts + ADDS File attachments.
Logged intent + design in data/finance/COORDINATION.md before the schema touch.
Tested (real, end-to-end)¶
- Capture page + host redirect + finances dashboard unchanged (200/302/200).
- Upload POST stored image to disk + inserted pending row; re-insert same path returned same id (idempotent).
- Review tab renders pending + thumbnails; image serve 200.
- Matcher against live lake: unique-charge day, 1 candidate (would auto-link); ambiguous day, 5 candidates (stays pending).
- ERPNext attach against a live JE (ACC-JV-2026-00211 / FINDB-27854): upload 200,
File
93732a2867created,erpnext_file_idstored, re-run skipped (idempotent),_existing_filecaught it. Then deleted the test File (202) and all test receipt rows + images, so ERPNext and the receipts table are clean for production. - CF: public
https://receipts.justinsforge.com/returns 302 to the Access login (edge + tunnel + Access wired).
Design decision: JE, not Purchase Invoice¶
forge_erpnext_daily_post.py posts business expenses as Journal Entries (not Purchase
Invoices), so the JE found by cheque_no=FINDB-<txn_id> is the only correct attach
target. Expense Claim is employee reimbursement, not applicable. Flagged to erp-migration
in COORDINATION.md in case they ever want PIs instead.
Open / future¶
- First real auto-link happens the morning after Justin captures a receipt whose charge
has posted (SimpleFIN lag). Watch the 08:00 run; failures notify via
forge_notify.sh. - Receipts dated on/before the ERPNext seam (2026-06-08) may be HELD (no JE), so they link to a txn but stay attach-pending until erp-migration releases the JE. Expected.
- No receipt-delete UI yet (only capture + manual match). Add if Justin wants to discard a bad photo from the review tab.
[Claude Code]