feat(GRO-2155): route CRUD + optimization endpoint (Phase 2.1) #175

Merged
Flea Flicker merged 1 commits from feat/gro-2155-route-optimize-endpoints-dev into dev 2026-06-08 13:57:08 +00:00
Member

GRO-2155 — Route Optimization: Route CRUD + optimization endpoint (Phase 2.1)

Phase 2.1 of GRO-1164 Mobile Groomer Route Optimization. Builds on the Phase 1 geocoding work (GRO-2152 schema, GRO-2153 service, GRO-2154 endpoints).

What's included

  • GET /api/routes/daily?staffId=&date= — fetches (creating a draft if absent) the groomer's daily route plus all persisted stops, enriched with appointment + client detail.
  • POST /api/routes/optimize { staffId, date } — pulls the day's non-cancelled, geocoded appointments, optimizes the visiting order, and persists stopOrder, travelMinsFromPrev, travelDistanceKmFromPrev, and route totalTravelMins/totalDistanceKm/optimizedAt in a single transaction (stops fully replaced on re-optimize).
  • Optimization engine (src/services/routeOptimization.ts): Google Maps Directions API with optimizeWaypoints: true when a key is configured (businessSettings.googleMapsApiKey decrypted, or GOOGLE_MAPS_API_KEY); otherwise an offline nearest-neighbor TSP heuristic over great-circle distance. Degrades to the heuristic if Google errors mid-run.
  • >25-stop edge case — chunked into sub-routes (Directions waypoint cap) with a user warning; sub-routes stitched continuously.
  • AuthrequireRole("manager","groomer") on /routes/*; groomers are restricted in-handler to their own route; receptionists denied. Un-geocoded appointments are skipped and surfaced under skipped[]/warnings[].

Tests

  • src/__tests__/routeOptimization.test.ts — haversine, leg estimation, nearest-neighbor ordering, Google path (mocked fetch, drops return leg), >25 chunking, and Google→heuristic fallback. Full suite green locally (623 tests), typecheck + lint clean.

UAT Playbook

  • Updated UAT_PLAYBOOK.md §4.16 — Route Optimization (Route CRUD + Optimize) with 11 new test cases (daily fetch/draft, optimize, re-optimize replace, skip un-geocoded, empty/single, >25 chunk, groomer-own/cross-groomer/receptionist auth, staffId-required, date validation).

Targets dev (Phase 1, CI-gated self-merge).

## GRO-2155 — Route Optimization: Route CRUD + optimization endpoint (Phase 2.1) Phase 2.1 of [GRO-1164](/GRO/issues/GRO-1164) Mobile Groomer Route Optimization. Builds on the Phase 1 geocoding work ([GRO-2152](/GRO/issues/GRO-2152) schema, [GRO-2153](/GRO/issues/GRO-2153) service, [GRO-2154](/GRO/issues/GRO-2154) endpoints). ### What's included - **`GET /api/routes/daily?staffId=&date=`** — fetches (creating a `draft` if absent) the groomer's daily route plus all persisted stops, enriched with appointment + client detail. - **`POST /api/routes/optimize`** `{ staffId, date }` — pulls the day's non-cancelled, geocoded appointments, optimizes the visiting order, and persists `stopOrder`, `travelMinsFromPrev`, `travelDistanceKmFromPrev`, and route `totalTravelMins`/`totalDistanceKm`/`optimizedAt` in a single transaction (stops fully replaced on re-optimize). - **Optimization engine** (`src/services/routeOptimization.ts`): Google Maps Directions API with `optimizeWaypoints: true` when a key is configured (`businessSettings.googleMapsApiKey` decrypted, or `GOOGLE_MAPS_API_KEY`); otherwise an offline **nearest-neighbor TSP heuristic** over great-circle distance. Degrades to the heuristic if Google errors mid-run. - **>25-stop edge case** — chunked into sub-routes (Directions waypoint cap) with a user warning; sub-routes stitched continuously. - **Auth** — `requireRole("manager","groomer")` on `/routes/*`; groomers are restricted in-handler to their **own** route; receptionists denied. Un-geocoded appointments are skipped and surfaced under `skipped[]`/`warnings[]`. ### Tests - `src/__tests__/routeOptimization.test.ts` — haversine, leg estimation, nearest-neighbor ordering, Google path (mocked fetch, drops return leg), >25 chunking, and Google→heuristic fallback. Full suite green locally (623 tests), typecheck + lint clean. ### UAT Playbook - Updated **UAT_PLAYBOOK.md §4.16 — Route Optimization (Route CRUD + Optimize)** with 11 new test cases (daily fetch/draft, optimize, re-optimize replace, skip un-geocoded, empty/single, >25 chunk, groomer-own/cross-groomer/receptionist auth, staffId-required, date validation). Targets `dev` (Phase 1, CI-gated self-merge).
Flea Flicker added 1 commit 2026-06-08 13:55:31 +00:00
feat(GRO-2155): route CRUD + optimization endpoint (Phase 2.1)
CI / Test (pull_request) Successful in 23s
CI / Lint & Typecheck (pull_request) Successful in 25s
CI / Build & Push Docker Images (pull_request) Successful in 57s
852b064972
Adds the core route engine for Mobile Groomer Route Optimization:

- src/services/routeOptimization.ts — order a day's geocoded stops via
  Google Directions (optimizeWaypoints:true) when a Maps API key is
  configured, else an offline nearest-neighbor TSP heuristic over
  great-circle distance. Handles the >25-stop edge case by chunking into
  sub-routes (Directions waypoint cap) with a user warning, degrades to
  the heuristic if Google errors mid-run, and resolves the API key from
  businessSettings.googleMapsApiKey (decrypted) / GOOGLE_MAPS_API_KEY.
- src/routes/routes.ts — GET /api/routes/daily (fetch/create draft route
  + enriched stops) and POST /api/routes/optimize (generate/re-optimize,
  persist stopOrder + travelMinsFromPrev + travelDistanceKmFromPrev and
  route totals/optimizedAt in one transaction). Auth: manager (any) or
  groomer (own route only); receptionists denied. Un-geocoded
  appointments are skipped and surfaced.
- src/index.ts — mount /api/routes under requireRole("manager","groomer").
- Unit tests for haversine, leg estimation, nearest-neighbor ordering,
  the Google path (mocked fetch), chunking, and fallback.
- UAT_PLAYBOOK.md §4.16 — new Route Optimization test cases.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
Flea Flicker merged commit d0c0b1b646 into dev 2026-06-08 13:57:08 +00:00
Sign in to join this conversation.