Skip to content

feat: route-based inventory check#177

Open
johnsmccain wants to merge 6 commits intoNOVUS-X:mainfrom
johnsmccain:main
Open

feat: route-based inventory check#177
johnsmccain wants to merge 6 commits intoNOVUS-X:mainfrom
johnsmccain:main

Conversation

@johnsmccain
Copy link
Copy Markdown

feat: Route-Based Inventory Check

Summary

Implements the route-based inventory check feature end-to-end. When an artisan's booking transitions to IN_PROGRESS, the system computes a geographic corridor along their route to the job site, queries external store inventory APIs for each BOM item at stores within that corridor, and dispatches push notifications to the artisan for any items found in stock. Clients can suppress the entire flow by setting a client_supply_override flag on their booking.

Closes #162


What changed

Database

  • New bom_items table — stores Bill of Materials line items per booking
  • New inventory_check_results table — persists per-store, per-item availability results
  • New notification_events table — audit log for every FCM push notification sent
  • Extended bookings table with client_supply_override (bool) and artisan_device_token (str)
  • Alembic migration: 001_add_inventory_bom_notification_tables

Backend services

  • RouteService (services/route_service.py) — Haversine-based bounding-box corridor computation with configurable half-width (default 500 m)
  • StoreAdapterProtocol + MockStoreAdapter (services/inventory/) — pluggable interface for store API integrations
  • InventoryCheckerService (services/inventory/checker.py) — corridor filtering, Redis caching (TTL 5 min), per-adapter timeout (5 s), full error isolation
  • NotificationService (services/notification_service.py) — FCM HTTP v1, capped at 5 notifications per run, persists NotificationEvent rows regardless of FCM outcome
  • run_inventory_check background task (services/inventory/tasks.py) — wired into the booking status endpoint; skips when client_supply_override=True; never raises

API endpoints

  • GET /inventory/{booking_id}/results — returns inventory results; marks results older than 24 h as stale
  • POST /bookings/{booking_id}/supply-override — client-only; validates ownership and booking status (PENDING/CONFIRMED); returns HTTP 403 otherwise

Frontend

  • InventoryResultsPanel component — renders results with availability badges, stale badges, and pre-pay links; shows empty state when no data
  • ClientSupplyOverrideToggle component — checkbox that disables for non-pending/confirmed bookings; calls API on change; shows sonner toasts
  • Integrated ClientSupplyOverrideToggle into the bookings dashboard page
  • Added api.inventory.getResults() and api.inventory.setSupplyOverride() to lib/api.ts

Config additions

INVENTORY_CACHE_TTL=300      # Redis TTL for store API responses (seconds)
STORE_API_TIMEOUT_S=5        # Per-adapter request timeout (seconds)
FCM_PROJECT_ID=              # Firebase project ID (optional)
FCM_SERVICE_ACCOUNT_JSON=    # Firebase service account JSON string (optional)

Tests

50 backend tests added, all passing:

File Tests
test_route_service.py 15
test_inventory_checker.py 8
test_inventory_schemas.py 5
test_notification_service.py 6
test_background_task.py 5
test_inventory_endpoints.py 11

Frontend Vitest tests added for InventoryResultsPanel (9 tests) and ClientSupplyOverrideToggle (8 tests).

Run backend tests:

cd backend
pytest app/tests/test_route_service.py app/tests/test_inventory_checker.py \
       app/tests/test_inventory_schemas.py app/tests/test_notification_service.py \
       app/tests/test_background_task.py app/tests/test_inventory_endpoints.py -v

Run frontend tests (after npm install):

cd frontend
npx vitest run components/booking/

Architecture

POST /bookings/{id}/status (IN_PROGRESS)
  └─ BackgroundTasks.add_task(run_inventory_check)
       ├─ check client_supply_override → skip if true
       ├─ RouteService.compute_corridor(artisan_coords, job_site_coords)
       ├─ InventoryCheckerService.run_check(bom_items, corridor)
       │    ├─ filter adapters by corridor
       │    ├─ Redis cache check per (store, sku)
       │    └─ persist InventoryCheckResult rows
       └─ NotificationService.send_batch(results) → max 5 FCM pushes
            └─ persist NotificationEvent rows

Notes

  • FCM notifications are skipped when FCM_PROJECT_ID is not set — safe to deploy without credentials
  • The Alembic migration must be applied before running any DB-touching tests: alembic upgrade head
  • Real store adapters are not wired yet — the InventoryCheckerService is initialised with an empty adapter list until store integrations are added
  • Redis is optional; the checker falls through to live API calls if Redis is unavailable

Checklist

  • New models registered in db/base.py
  • Alembic migration created
  • All new settings have defaults (no required env vars added)
  • Background task never raises unhandled exceptions
  • FCM credentials read from env, not hardcoded
  • 50 backend tests passing
  • Frontend components pass TypeScript diagnostics

- Add BOMItem, InventoryCheckResult, NotificationEvent models
- Extend Booking with client_supply_override and artisan_device_token
- Add Alembic migration for new tables/columns
- Implement RouteService with Haversine corridor computation
- Add pluggable StoreAdapterProtocol and MockStoreAdapter
- Implement InventoryCheckerService with Redis caching and error isolation
- Implement NotificationService with FCM HTTP v1 and 5-notification cap
- Wire run_inventory_check background task to IN_PROGRESS booking transition
- Add GET /inventory/{booking_id}/results and POST /bookings/{booking_id}/supply-override endpoints
- Add InventoryResultsPanel and ClientSupplyOverrideToggle frontend components
- Add api.inventory methods to frontend API client
- 50 backend tests passing
@drips-wave
Copy link
Copy Markdown

drips-wave bot commented Mar 26, 2026

@johnsmccain Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@gabito1451
Copy link
Copy Markdown
Collaborator

hi @johnsmccain please help look in to failing CI

@johnsmccain
Copy link
Copy Markdown
Author

done

@gabito1451
Copy link
Copy Markdown
Collaborator

please resolve conflicts @johnsmccain, also look in to ci fail

- contracts.yml: fix toolchain version (1.90.0 -> stable)
- contracts.yml: add --features testutils to cargo test
- ci.yml: replace STELLAR_ESCROW_PUBLIC with ESCROW_CONTRACT_ID
- frontend-check.yml: add push trigger on main branch
@johnsmccain
Copy link
Copy Markdown
Author

Hi @gabito1451 review pr please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ISSUE-21 · 🔵 (Agentic) — Predictive Hardware Store Inventory Connector

2 participants