name: reference_receipt_tracker description: Receipt tracker, mobile capture at receipts.justinsforge.com plus a Receipts tab on the finances hub, with a daily reconcile that links receipts to bank txns and attaches images to ERPNext JEs type: reference
Receipt Tracker¶
Two surfaces over one Flask app (forge-finances.service, Console :8096), plus a
daily reconcile. Solves the SimpleFIN lag: a receipt photo arrives 1-2 days before
its bank transaction, so receipts land pending and get linked once the txn appears.
Surfaces¶
receipts.justinsforge.com(mobile capture). Camera button (<input capture="environment">), business chips (WIEB / JWVR / NOVA / SIP / GOC), optional notes. Submit stores the image todata/receipts/<YYYY-MM-DD>_<uuid>.<ext>and inserts areceiptsrow asstatus=pending. Rides the VR Alliance CF tunnel to127.0.0.1:8096; CF Access app0a3afa54-...allows[email protected]only.- Receipts tab on
finances.justinsforge.com(/receipts/review): pending receipts with a manual-match dropdown, recently matched/auto receipts with their ERPNext attach state, and an "Open capture" button to the mobile site.
Both hosts tunnel to the same origin; forge_finances_app.py has a before_request
that sends receipts.justinsforge.com/ to /receipts/ while finances/ stays the
dashboard.
Code¶
scripts/forge_finances_ui_receipts.py, blueprint:/receipts/(capture page),/receipts/upload(POST),/receipts/image/<id>,/receipts/review(hub tab),/receipts/<id>/match(manual link). Registered inforge_finances_app.py; nav item added inforge_finances_ui_base.NAV_ITEMS("Receipts", camera glyph).scripts/forge_finance_receipt_reconcile.py, the daily engine (see below).- DB:
receiptstable inforge_finance_db.py(init()creates it). Columns:id, created_at, image_path (UNIQUE), company, notes, status, matched_txn_id, erpnext_file_id, updated_at. Helpers:insert_receipt(idempotent on image_path),list_receipts,get_receipt,set_receipt_match,set_receipt_erpnext_file. ConstantsRECEIPT_STATUSES,RECEIPT_COMPANIES.
Reconcile (forge_finance_receipt_reconcile.py)¶
Timer forge-finance-receipt-reconcile.timer at 08:00 CT (after the 07:00 sync).
Dry-run by default; --apply writes. --no-erpnext links only.
- For each
pendingreceipt, find business-slice outflow txns whose effective scope (COALESCE(NULLIF(t.scope_override,''), a.scope, 'personal'), source IN simplefin/bankcsv,superseded=0) equals the receipt company, dated within +/-2 days of capture. Exactly one candidate, auto-link (status=matched). Zero or >1, staypending(never guess between two). - For each matched receipt with no
erpnext_file_id, look up the JE the daily post engine made (cheque_no=FINDB-<txn_id>, doctype Journal Entry), upload the image via/api/method/upload_fileas a private attachment, store the File docname. No JE yet (txn unposted or held behind the seam), skip and retry next run.
Auth reuses forge_erpnext_post_books.load_env() / pb.SITE (token header,
Host: erpnext.justinsforge.com); upload is multipart via requests. Idempotent:
links once, and _existing_file checks for an already-attached File before
re-uploading, so a crash between upload and DB write never double-attaches.
Why 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 today. Expense Claim is employee reimbursement, not applicable.
Notes¶
- READ-ONLY on accounts/transactions (shared lake contract, see
data/finance/COORDINATION.md); WRITES only thereceiptstable and ADDS File attachments to existing JEs. Never writes GL, never creates/cancels JEs. - Receipt images are user data:
data/receipts/.gitignorekeeps the dir, ignores the photos. - Related: [[reference_forge_biz_apps]], [[reference_erpnext_daily_post]], [[reference_cloudflare]].
[Claude Code]