# Dispatch ↔ Delivery_App integration contract (PROVISIONAL v0.1)

**Status:** derived from the Polestar module pattern (`app/modules/polestar_dea`) — the
sibling that the DealerPro Dispatch module (Pranav) will mirror. There is **no dispatch
module in the repo yet**, so every name below is a placeholder to be reconciled with
Pranav's real tables/endpoints. The Flutter app hides all of this behind one adapter
(`lib/services/api/dispatch_api.dart`); reconciling = editing `dispatch_api_http.dart` only.

Owners: **Pranav** = DealerPro dispatcher/web side + server endpoints. **This app** = driver side.

---

## 1. Roles & where things live (from the Polestar blueprint)

- **Drivers** are stored in the shared `cipher_central` schema (Polestar uses
  `cipher_central.ps_drivers`: `driverId, driverName, mobile, username, password(bcrypt),
  email, driverImei, active, schema_name, user_id, tenant_id`). Dispatch will have the
  equivalent — assume `cipher_central.dispatch_drivers` (or reuse `ps_drivers`).
- **Jobs** are per-tenant, split 3 ways in Polestar: `ps_services` (slot + status int),
  `ps_jobs` (job + `customerId` FK into the shared CRM `customers`/`customer_phones`/
  `customer_emails`), `ps_job_drivers` (assignment: `driverStatus`, `driverAcceptReject`,
  `driverNotifiedTime`, `driverCurrentLatitude/Longitude`, `driverJobStartTime`,
  `driverJobCompletedTime`). Dispatch mirror: `dispatch_services`, `dispatch_jobs`,
  `dispatch_job_drivers`.
- **Customer coords** live in `ps_customer_details.customer_lat/customer_lng` (per-tenant);
  Dispatch mirror: `dispatch_customer_details` or lat/lng directly on `dispatch_jobs`.
- **Push:** there is **no FCM** in this stack. Polestar's driver app **polls** its jobs and
  authenticates against `cipher_central.ps_drivers`. Realtime nudges go through the internal
  WebSocket/FastAPI service (`cipher_backend`) via `NotificationsManager::notifyUsers()`
  (config `app/config/websocket.php` → `https://dealerpro.matrackinc.com/cipher-api/notifications/notify`).
  This app does the same: **poll on interval + optional WebSocket subscribe.**
- **Maps/geo:** codebase standard is **HERE Maps** (`window.hereMapsApiKey`); proximity is
  **Haversine** (`polestar_dea/api/calendar/find_nearby_mobile_jobs.php`, 10 mi / 2 h). No
  HERE Routing API is used yet — route ORDER is produced server-side by the dispatcher; this
  app renders the ordered stops and hands off navigation to the device map app.

---

## 2. Endpoints the app calls (provisional; base URL is configurable in `lib/config/env.dart`)

Base: `{DISPATCH_API_BASE}` — default guess `https://staging2-dealerpro.matrackinc.com/dealer/app/modules/dispatch/api/driver`
Auth: bearer token from login, sent as `Authorization: Bearer <token>` (Polestar uses
username/password against `ps_drivers`; swap to whatever session mechanism Pranav exposes).

| Verb | Path | Body / Query | Returns |
|---|---|---|---|
| POST | `/login` | `{username, password}` | `{token, driverId, driverName, schemaName, tenantId}` |
| GET  | `/jobs?date=YYYY-MM-DD` | — | `[DispatchJob]` assigned to this driver, in stop order |
| GET  | `/route?date=YYYY-MM-DD` | — | `{stops:[{jobId, sequence, distanceMiles, etaMinutes}], totalMiles, totalMinutes}` |
| POST | `/jobs/{id}/accept` | — | `{ok}` |
| POST | `/jobs/{id}/reject` | `{reason}` | `{ok}` |
| POST | `/jobs/{id}/start` | — | `{ok}` (status → en_route, stamps driverJobStartTime) |
| POST | `/jobs/{id}/location` | `{lat, lng}` | `{ok}` (periodic GPS ping while en route) |
| POST | `/jobs/{id}/complete` | `{note?, proofPhotoBase64?}` | `{ok}` (status → delivered, stamps driverJobCompletedTime) |

WebSocket (optional realtime): `{DISPATCH_WS_URL}` default
`wss://dealerpro.matrackinc.com/cipher-api/ws`; message `{type:"dispatch_job_assigned", jobId, date}`
tells the app to refresh. If WS is unavailable the app polls `/jobs` every `pollInterval`.

---

## 3. DispatchJob shape (what the app expects back)

```jsonc
{
  "jobId": "123",
  "jobName": "Deliver GPS unit x2",
  "customerName": "Jane Smith",
  "customerPhone": "+1770...",
  "customerEmail": "jane@ex.com",
  "address": "123 Peachtree St, Atlanta, GA 30303",
  "lat": 33.7590, "lng": -84.3880,
  "instructions": "Leave at front desk",
  "units": 2,                       // optional
  "weight": null,                   // optional
  "shipmentType": "expedited",      // regular | expedited | overnight (from DEALER_PORTAL_SHIPPING_TYPES)
  "scheduledStart": "2026-08-22 09:00:00",
  "scheduledEnd":   "2026-08-22 12:00:00",
  "status": "assigned",             // see §4
  "stopOrder": 1
}
```

## 4. Status lifecycle (app-facing enum → Polestar `ps_services.status` int for reference)

`assigned` (6) → `accepted` (4) → `en_route` (2/10) → `delivered` (11/12).
Plus `rejected` (5) and `cancelled`. The adapter maps app enum ↔ Pranav's real codes.

---

## 5. What Pranav needs to confirm to finalize this

1. Driver auth mechanism + drivers table name (reuse `ps_drivers`, or new `dispatch_drivers`?).
2. Real `{DISPATCH_API_BASE}` and the 8 endpoint paths above (or his equivalents).
3. Whether the **route order** is computed server-side (preferred) or the app should compute
   it (a client-side nearest-neighbour + 2-opt fallback ships in `lib/utils/distance.dart`).
4. Realtime: WS topic/message shape, or poll-only for v1.
5. Status codes to map onto §4.
