feat: Maintenance Receipt Upload with OCR Auto-populate (#16) #161

Merged
egullickson merged 11 commits from issue-16-maintenance-receipt-upload-ocr into main 2026-02-13 22:19:45 +00:00
Owner

Summary

  • Add receipt upload and camera capture to the maintenance record form with OCR auto-population
  • New maintenance receipt extraction pipeline in Python OCR microservice (Gemini + regex patterns)
  • Review modal with confidence indicators and inline field editing
  • Email ingestion system for forwarded receipts via Resend inbound webhooks
  • Pending vehicle association resolution UI for email-ingested receipts
  • Receipt document linking to maintenance records with display on detail view

Linked issues

Fixes #16
Fixes #150
Fixes #151
Fixes #152
Fixes #153
Fixes #154
Fixes #155
Fixes #156
Fixes #157
Fixes #158
Fixes #159
Fixes #160

Type

  • Feature
  • Bug fix
  • Chore / refactor
  • Docs

Test plan

  • Unit tests
  • Integration tests
  • Manual verification

Commands / steps:

  1. Verify OCR extraction endpoint processes maintenance receipt images and returns structured fields
  2. Test review modal displays extracted fields with confidence scores and allows inline editing
  3. Confirm accepted OCR results auto-populate the maintenance record form via setValue()
  4. Verify receipt document is stored and linked to the created maintenance record
  5. Test email ingestion webhook receives forwarded receipts and triggers processing pipeline
  6. Verify pending vehicle association banner appears on dashboard for unresolved email receipts
  7. Test resolution dialog allows associating email receipts with vehicles and creating records
  8. Confirm Pro tier gating shows lock icon and upgrade dialog for non-Pro users
  9. Test camera capture on mobile and file upload on desktop (PDF, PNG, JPG, HEIC)
  10. Verify responsive layout on both mobile and desktop viewports

Screenshots / UI notes (if applicable)

  • Mobile: Bottom sheet drawer for review modal, 44px touch targets
  • Desktop: Dialog modal for review modal
  • Confidence indicators use 4-dot system matching existing fuel receipt pattern
  • Pending association banner on dashboard with count badge

Checklist

  • Acceptance criteria met (from linked issue)
  • No secrets committed
  • Logging is appropriate (no PII)
  • Docs updated (if needed)
## Summary - Add receipt upload and camera capture to the maintenance record form with OCR auto-population - New maintenance receipt extraction pipeline in Python OCR microservice (Gemini + regex patterns) - Review modal with confidence indicators and inline field editing - Email ingestion system for forwarded receipts via Resend inbound webhooks - Pending vehicle association resolution UI for email-ingested receipts - Receipt document linking to maintenance records with display on detail view ## Linked issues Fixes #16 Fixes #150 Fixes #151 Fixes #152 Fixes #153 Fixes #154 Fixes #155 Fixes #156 Fixes #157 Fixes #158 Fixes #159 Fixes #160 ## Type - [x] Feature - [ ] Bug fix - [ ] Chore / refactor - [ ] Docs ## Test plan - [ ] Unit tests - [ ] Integration tests - [x] Manual verification **Commands / steps:** 1. Verify OCR extraction endpoint processes maintenance receipt images and returns structured fields 2. Test review modal displays extracted fields with confidence scores and allows inline editing 3. Confirm accepted OCR results auto-populate the maintenance record form via `setValue()` 4. Verify receipt document is stored and linked to the created maintenance record 5. Test email ingestion webhook receives forwarded receipts and triggers processing pipeline 6. Verify pending vehicle association banner appears on dashboard for unresolved email receipts 7. Test resolution dialog allows associating email receipts with vehicles and creating records 8. Confirm Pro tier gating shows lock icon and upgrade dialog for non-Pro users 9. Test camera capture on mobile and file upload on desktop (PDF, PNG, JPG, HEIC) 10. Verify responsive layout on both mobile and desktop viewports ## Screenshots / UI notes (if applicable) - Mobile: Bottom sheet drawer for review modal, 44px touch targets - Desktop: Dialog modal for review modal - Confidence indicators use 4-dot system matching existing fuel receipt pattern - Pending association banner on dashboard with count badge ## Checklist - [x] Acceptance criteria met (from linked issue) - [x] No secrets committed - [x] Logging is appropriate (no PII) - [ ] Docs updated (if needed)
egullickson added 11 commits 2026-02-13 16:34:50 +00:00
- New MaintenanceReceiptExtractor: Gemini-primary extraction with regex
  cross-validation for dates, amounts, and odometer readings
- New maintenance_receipt_validation.py: cross-validation patterns for
  structured field confidence adjustment
- New POST /extract/maintenance-receipt endpoint reusing
  ReceiptExtractionResponse model
- Per-field confidence scores (0.0-1.0) with Gemini base 0.85,
  boosted/reduced by regex agreement

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add receipt_document_id FK on maintenance_records, update types/repo/service
to support receipt linking on create and return document metadata on GET.
Add OCR proxy endpoint POST /api/ocr/extract/maintenance-receipt with
tier gating (maintenance.receiptScan) through full chain: routes -> controller
-> service -> client.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add useMaintenanceReceiptOcr hook mirroring fuel receipt OCR pattern,
MaintenanceReceiptReviewModal with confidence indicators and inline editing,
and maintenance-receipt.types.ts for extraction field types. Includes
category/subtype suggestion via keyword matching from service descriptions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add tier-gated "Scan Receipt" button to MaintenanceRecordForm
- Wire useMaintenanceReceiptOcr hook with CameraCapture and ReviewModal
- Auto-populate form fields from accepted OCR results via setValue
- Upload receipt as document and pass receiptDocumentId on record create
- Show receipt thumbnail + "View Receipt" button in edit dialog
- Add receipt indicator chip on records list rows
- Add receiptDocumentId and receiptDocument to frontend types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create email_ingestion_queue table with UNIQUE email_id constraint
- Create pending_vehicle_associations table with documents FK
- Seed 3 email templates: receipt_processed, receipt_failed, receipt_pending_vehicle
- Add TypeScript types for queue records, associations, and Resend webhook payloads
- Register email-ingestion in migration runner order

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ResendInboundClient: webhook signature verification via Svix, email
  fetch/download/parse with mailparser
- POST /api/webhooks/resend/inbound endpoint with rawBody, signature
  verification, idempotency check, queue insertion, async processing
- Config: resend_webhook_secret (optional) in secrets schema
- Route registration in app.ts following Stripe webhook pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- EmailIngestionRepository: queue CRUD (insert, update status, get,
  find by email ID), pending vehicle association management, mapRow
  pattern for snake_case -> camelCase conversion
- EmailIngestionService: full processing pipeline with sender validation,
  attachment filtering (PDF/PNG/JPG/JPEG/HEIC, <10MB), dual OCR
  classification (fuel vs maintenance), vehicle association logic
  (single-vehicle auto-associate, multi-vehicle pending), retry handling
  (max 3 attempts), and templated email replies (confirmation, failure,
  pending vehicle)
- Updated controller to delegate async processing to service
- Added receipt_processed/receipt_failed/receipt_pending_vehicle to
  TemplateKey union type

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New ReceiptClassifier module with keyword-based classification for
  fuel vs maintenance receipts from email text and OCR raw text
- Classifier-first pipeline: classify from email subject/body keywords
  before falling back to OCR-based classification
- Fuel keywords: gas, fuel, gallons, octane, pump, diesel, unleaded,
  shell, chevron, exxon, bp
- Maintenance keywords: oil change, brake, alignment, tire, rotation,
  inspection, labor, parts, service, repair, transmission, coolant
- Confident classification (>= 2 keyword matches) routes to specific
  OCR endpoint; unclassified falls back to both endpoints + rawText
  classification + field-count heuristic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract all notification logic from EmailIngestionService into
  dedicated EmailIngestionNotificationHandler class
- Add notification_logs entries for every email sent (success/failure)
- Add in-app user_notifications for all error scenarios (no vehicles,
  no attachments, OCR failure, processing failure)
- Update email templates with enhanced variables: merchantName,
  totalAmount, date, guidance
- Update pending vehicle notification title to 'Vehicle Selection Required'
- Add sample variables for receipt templates in test email flow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add pending vehicle association resolution UI (refs #160)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 8m40s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 52s
Deploy to Staging / Verify Staging (pull_request) Successful in 8s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 7s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
1bf550ae9b
Backend: Add authenticated endpoints for pending association CRUD
(GET/POST/DELETE /api/email-ingestion/pending). Service methods for
resolving (creates fuel/maintenance record) and dismissing associations.

Frontend: New email-ingestion feature with types, API client, hooks,
PendingAssociationBanner (dashboard), PendingAssociationList, and
ResolveAssociationDialog. Mobile-first responsive with 44px touch
targets and full-screen dialogs on small screens.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
egullickson merged commit f0b1e57089 into main 2026-02-13 22:19:45 +00:00
egullickson deleted branch issue-16-maintenance-receipt-upload-ocr 2026-02-13 22:19:47 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: egullickson/motovaultpro#161