feat: Station matching from receipt (#129) #132

Closed
opened 2026-02-11 03:04:45 +00:00 by egullickson · 1 comment
Owner

Relates to #129

Milestone 3: Station Matching from Receipt

Add Google Places API station matching from extracted receipt merchant name.

Files

  • backend/src/features/fuel-logs/api/fuel-logs.controller.ts (or new endpoint)
  • backend/src/features/stations/external/google-maps/google-maps.client.ts
  • frontend/src/features/fuel-logs/hooks/useReceiptOcr.ts
  • frontend/src/features/fuel-logs/components/ReceiptOcrReviewModal.tsx

Requirements

  • Add station search method to google-maps.client.ts using Places Text Search for merchant name matching
  • Add backend endpoint or service method that takes merchant name string and returns matched station
  • After receipt OCR extraction, call station matching with extracted merchantName
  • Pre-fill locationData in fuel log form with matched station (googlePlaceId, stationName, address)
  • User can change/clear station selection in review modal

Acceptance Criteria

  • Extracted merchant name "Shell" or "BP" returns a matching station with placeId
  • Matched station pre-fills locationData in the fuel log form
  • User can override or clear the auto-matched station
  • Station matching failure (no match) does not block receipt acceptance
  • Station matching works with abbreviated names (e.g., "COSTCO #123")

Tests

  • Test files: backend/src/features/stations/tests/unit/station-matching.test.ts
  • Test type: unit (mock Google Places API)
  • Scenarios:
    • Normal: Known station name returns match
    • Edge: Abbreviated/partial name returns best match
    • Error: No match found returns null gracefully
Relates to #129 ## Milestone 3: Station Matching from Receipt Add Google Places API station matching from extracted receipt merchant name. ### Files - `backend/src/features/fuel-logs/api/fuel-logs.controller.ts` (or new endpoint) - `backend/src/features/stations/external/google-maps/google-maps.client.ts` - `frontend/src/features/fuel-logs/hooks/useReceiptOcr.ts` - `frontend/src/features/fuel-logs/components/ReceiptOcrReviewModal.tsx` ### Requirements - Add station search method to google-maps.client.ts using Places Text Search for merchant name matching - Add backend endpoint or service method that takes merchant name string and returns matched station - After receipt OCR extraction, call station matching with extracted merchantName - Pre-fill locationData in fuel log form with matched station (googlePlaceId, stationName, address) - User can change/clear station selection in review modal ### Acceptance Criteria - [ ] Extracted merchant name "Shell" or "BP" returns a matching station with placeId - [ ] Matched station pre-fills locationData in the fuel log form - [ ] User can override or clear the auto-matched station - [ ] Station matching failure (no match) does not block receipt acceptance - [ ] Station matching works with abbreviated names (e.g., "COSTCO #123") ### Tests - **Test files**: `backend/src/features/stations/tests/unit/station-matching.test.ts` - **Test type**: unit (mock Google Places API) - **Scenarios**: - Normal: Known station name returns match - Edge: Abbreviated/partial name returns best match - Error: No match found returns null gracefully
egullickson added the
status
backlog
type
feature
labels 2026-02-11 03:12:38 +00:00
egullickson added
status
in-progress
and removed
status
backlog
labels 2026-02-11 15:34:50 +00:00
Author
Owner

Milestone: Station Matching from Receipt (#132)

Phase: Execution | Agent: Developer | Status: PASS

Completed

  • Backend: Google Maps Text Search - Added searchStationByName() to google-maps.client.ts using Places Text Search API. Searches "{merchantName} gas station" with type: gas_station filter. Results cached with 1-hour TTL. Returns null gracefully on API errors/no results.
  • Backend: Station Matching Endpoint - Added POST /api/stations/match with StationMatchBody { merchantName }. Service method matchStationFromReceipt() trims input, calls text search, caches matched station for future saveStation calls.
  • Frontend: OCR + Station Matching Flow - After receipt OCR extraction, useReceiptOcr hook calls /api/stations/match with extracted merchantName (non-blocking). MappedFuelLogFields.locationData now includes googlePlaceId and address when station is matched. Review modal shows matched station with place icon and address below the Station field. User can clear the match via close button. Editing merchant name clears any existing match.
  • Tests - 12 unit tests pass covering: known station name match ("Shell", "BP"), abbreviated name match ("COSTCO #123"), no match returns null, API error returns null gracefully, empty/whitespace input handling, rating/photo inclusion, service caching behavior.

Files Changed (10)

File Change
backend/.../google-maps.client.ts Added searchStationByName() + transformTextSearchResult()
backend/.../google-maps.types.ts Added GoogleTextSearchResponse type
backend/.../stations.types.ts Added StationMatchBody, StationMatchResponse
backend/.../stations.service.ts Added matchStationFromReceipt()
backend/.../stations.controller.ts Added matchStation() handler
backend/.../stations.routes.ts Added POST /stations/match route
frontend/.../useReceiptOcr.ts Station matching call after OCR, clearMatchedStation(), enriched locationData
frontend/.../ReceiptOcrReviewModal.tsx Matched station indicator with clear button
frontend/.../FuelLogForm.tsx Pass matchedStation/onClearMatchedStation to modal
backend/.../station-matching.test.ts 12 unit tests

Quality

  • Type-check: PASS (frontend + backend)
  • Lint: PASS (0 errors)
  • Tests: 12/12 PASS

Verdict: PASS | Next: Quality review

## Milestone: Station Matching from Receipt (#132) **Phase**: Execution | **Agent**: Developer | **Status**: PASS ### Completed - **Backend: Google Maps Text Search** - Added `searchStationByName()` to `google-maps.client.ts` using Places Text Search API. Searches `"{merchantName} gas station"` with `type: gas_station` filter. Results cached with 1-hour TTL. Returns null gracefully on API errors/no results. - **Backend: Station Matching Endpoint** - Added `POST /api/stations/match` with `StationMatchBody { merchantName }`. Service method `matchStationFromReceipt()` trims input, calls text search, caches matched station for future `saveStation` calls. - **Frontend: OCR + Station Matching Flow** - After receipt OCR extraction, `useReceiptOcr` hook calls `/api/stations/match` with extracted `merchantName` (non-blocking). `MappedFuelLogFields.locationData` now includes `googlePlaceId` and `address` when station is matched. Review modal shows matched station with place icon and address below the Station field. User can clear the match via close button. Editing merchant name clears any existing match. - **Tests** - 12 unit tests pass covering: known station name match ("Shell", "BP"), abbreviated name match ("COSTCO #123"), no match returns null, API error returns null gracefully, empty/whitespace input handling, rating/photo inclusion, service caching behavior. ### Files Changed (10) | File | Change | |------|--------| | `backend/.../google-maps.client.ts` | Added `searchStationByName()` + `transformTextSearchResult()` | | `backend/.../google-maps.types.ts` | Added `GoogleTextSearchResponse` type | | `backend/.../stations.types.ts` | Added `StationMatchBody`, `StationMatchResponse` | | `backend/.../stations.service.ts` | Added `matchStationFromReceipt()` | | `backend/.../stations.controller.ts` | Added `matchStation()` handler | | `backend/.../stations.routes.ts` | Added `POST /stations/match` route | | `frontend/.../useReceiptOcr.ts` | Station matching call after OCR, `clearMatchedStation()`, enriched locationData | | `frontend/.../ReceiptOcrReviewModal.tsx` | Matched station indicator with clear button | | `frontend/.../FuelLogForm.tsx` | Pass `matchedStation`/`onClearMatchedStation` to modal | | `backend/.../station-matching.test.ts` | 12 unit tests | ### Quality - Type-check: PASS (frontend + backend) - Lint: PASS (0 errors) - Tests: 12/12 PASS *Verdict*: PASS | *Next*: Quality review
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: egullickson/motovaultpro#132