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

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

Relates to #129

Milestone 3: Station Matching from Receipt

Files

  • backend/src/features/stations/api/stations.controller.ts (or new endpoint)
  • backend/src/features/stations/api/stations.routes.ts
  • 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

Architecture

Station matching is a separate frontend call after OCR extraction:

  1. Frontend receives ReceiptOcrReviewModal with extractedFields.merchantName
  2. Frontend calls POST /api/stations/match with { merchantName }
  3. Backend calls Google Places Text Search via google-maps.client.ts
  4. Frontend pre-fills locationData with matched station (googlePlaceId, stationName, address)

Requirements

  • Add station search method to google-maps.client.ts using Places Text Search (or Find Place From Text) for merchant name matching
  • google-maps.client.ts station search method uses 5000ms timeout (5 seconds)
  • If Places API exceeds timeout, log warning and return null (no match)
  • Add POST /api/stations/match backend endpoint that takes { merchantName } and returns matched station (name, placeId, address) or null
  • After receipt OCR extraction, frontend calls 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
  • Frontend handles null gracefully (no station pre-fill, user selects manually)

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")
  • Places API timeout (>5s) returns null without error to user

Tests

  • Test files: backend/src/features/stations/tests/unit/station-matching.test.ts (NEW)
  • Test type: unit (mock Google Places API)
  • Scenarios:
    • Normal: Known station name returns match with placeId and address
    • Edge: Abbreviated/partial name returns best match
    • Error: No match found returns null gracefully
    • Error: Places API timeout returns null with logged warning
Relates to #129 ## Milestone 3: Station Matching from Receipt ### Files - `backend/src/features/stations/api/stations.controller.ts` (or new endpoint) - `backend/src/features/stations/api/stations.routes.ts` - `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` ### Architecture Station matching is a separate frontend call after OCR extraction: 1. Frontend receives ReceiptOcrReviewModal with `extractedFields.merchantName` 2. Frontend calls `POST /api/stations/match` with `{ merchantName }` 3. Backend calls Google Places Text Search via `google-maps.client.ts` 4. Frontend pre-fills `locationData` with matched station (googlePlaceId, stationName, address) ### Requirements - Add station search method to `google-maps.client.ts` using Places Text Search (or Find Place From Text) for merchant name matching - `google-maps.client.ts` station search method uses 5000ms timeout (5 seconds) - If Places API exceeds timeout, log warning and return null (no match) - Add `POST /api/stations/match` backend endpoint that takes `{ merchantName }` and returns matched station (name, placeId, address) or null - After receipt OCR extraction, frontend calls 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 - Frontend handles null gracefully (no station pre-fill, user selects manually) ### 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") - Places API timeout (>5s) returns null without error to user ### Tests - **Test files**: `backend/src/features/stations/tests/unit/station-matching.test.ts` (NEW) - **Test type**: unit (mock Google Places API) - **Scenarios**: - Normal: Known station name returns match with placeId and address - Edge: Abbreviated/partial name returns best match - Error: No match found returns null gracefully - Error: Places API timeout returns null with logged warning
egullickson added the
status
backlog
type
feature
labels 2026-02-11 03:51:14 +00:00
egullickson added
status
in-progress
and removed
status
backlog
labels 2026-02-11 18:57:33 +00:00
Author
Owner

Milestone: Station Matching from Receipt

Phase: Execution | Agent: Developer | Status: PASS

Summary

Station matching from receipt was largely implemented in prior sub-issues (#139, #140). This milestone addressed the remaining gaps against #141's acceptance criteria.

Changes Made

Backend (google-maps.client.ts):

  • Added timeout: 5000 (5s) to searchStationByName() axios call per requirements
  • Added timeout-specific detection (ECONNABORTED / timeout message) that logs warn instead of error
  • Non-timeout errors continue to log at error level

Tests (station-matching.test.ts):

  • Added test: "should return null with logged warning on Places API timeout"
  • Verifies logger.warn called with timeout details, logger.error NOT called
  • All 13 station matching tests pass

Acceptance Criteria Verification

  • Extracted merchant name "Shell" or "BP" returns matching station with placeId (pre-existing)
  • Matched station pre-fills locationData in fuel log form (pre-existing in useReceiptOcr.ts)
  • User can override or clear auto-matched station (pre-existing in ReceiptOcrReviewModal.tsx)
  • Station matching failure does not block receipt acceptance (pre-existing non-blocking pattern)
  • Station matching works with abbreviated names e.g. "COSTCO #123" (pre-existing test)
  • Places API timeout (>5s) returns null without error to user (NEW - this commit)

Test Results

PASS station-matching.test.ts (13/13 tests)
0 TypeScript errors in stations feature
0 lint errors (49 pre-existing warnings)

Commit: 4e5da47 on branch issue-129-expand-ocr-fuel-receipt-maintenance

Verdict: PASS | Next: Quality review

## Milestone: Station Matching from Receipt **Phase**: Execution | **Agent**: Developer | **Status**: PASS ### Summary Station matching from receipt was largely implemented in prior sub-issues (#139, #140). This milestone addressed the remaining gaps against #141's acceptance criteria. ### Changes Made **Backend (`google-maps.client.ts`)**: - Added `timeout: 5000` (5s) to `searchStationByName()` axios call per requirements - Added timeout-specific detection (`ECONNABORTED` / timeout message) that logs `warn` instead of `error` - Non-timeout errors continue to log at `error` level **Tests (`station-matching.test.ts`)**: - Added test: "should return null with logged warning on Places API timeout" - Verifies `logger.warn` called with timeout details, `logger.error` NOT called - All 13 station matching tests pass ### Acceptance Criteria Verification - [x] Extracted merchant name "Shell" or "BP" returns matching station with placeId (pre-existing) - [x] Matched station pre-fills locationData in fuel log form (pre-existing in `useReceiptOcr.ts`) - [x] User can override or clear auto-matched station (pre-existing in `ReceiptOcrReviewModal.tsx`) - [x] Station matching failure does not block receipt acceptance (pre-existing non-blocking pattern) - [x] Station matching works with abbreviated names e.g. "COSTCO #123" (pre-existing test) - [x] Places API timeout (>5s) returns null without error to user (NEW - this commit) ### Test Results ``` PASS station-matching.test.ts (13/13 tests) 0 TypeScript errors in stations feature 0 lint errors (49 pre-existing warnings) ``` Commit: `4e5da47` on branch `issue-129-expand-ocr-fuel-receipt-maintenance` *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#141