Skip to content

Live (part 2) — notify + email on INVOICE UPLOAD

Goal

Part 2 of T-037. When a user uploads a vendor invoice (createReceipt / pages/api/records/vendor-invoices/upload), raise an in-app notification + email. Today only the project-invoice POST calls notifyInvoiceCreated; vendor/receipt uploads are silent. Reuse notifyInvoiceCreated or add a vendor-receipt variant.

Log

  • 2026-06-14 created (T-037 split, owner).
  • 2026-07-09 — doing (Records Infrastructure; routed via EOP Local Assistance fork, owner 2026-07-09). ✅ Read AGENTS.md · checked the board by scope (no dup; siblings T-038/T-040/T-041 cover matching-side events and stay out of scope — this is the UPLOAD path only). Source: Records (Infrastructure) · https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
  • Verified silent: pages/api/records/vendor-invoices/upload.ts calls createReceipt but no notify producer; only the project-invoice POST calls notifyInvoiceCreated.
  • Chosen: vendor-receipt variant (not notifyInvoiceCreated, which is project-invoice-shaped — it requires projectId/year/invoicePath and links to /projects/…). New notifyVendorInvoiceUploaded reuses the existing receipt_uploaded type (category records) and routes to accounting personnel via notifyRoles — recipients + email flow through the SAME established roles/email-policy machinery, nothing new invented. Called best-effort after createReceipt (never blocks the upload response).
  • Email note: like every *_created producer this is info severity → in-app is always delivered; email fires only when the records category's email policy / severity dictates (identical to the invoice-created sibling). Flagged for the owner in case they want it to always email.
  • Full unfiltered tsc --noEmit clean. PR open for review → flips to done on merge.
  • 2026-07-09 — done (merged to main via PR #876, no deploy — owner: commit + merge every change, don't deploy; main 🟡🟡 = manual). Verdict: shipped as planned — new notifyVendorInvoiceUploaded (reuses receipt_uploaded/records, routed to accounting via notifyRoles) fired best-effort after createReceipt. SHAs: d1beed56 (feat) · this close-out · PR #876 merge commit. Email: info → in-app always; email follows the records category policy (owner may bump to always-email — flagged). Blast-radius: lib/notifications/notify.ts (+ index.ts barrel), pages/api/records/vendor-invoices/upload.ts. Scope: upload path only — the matching-side events (T-038/T-040/T-041) remain separate.