# Backend API Requirements — Merchant Web Console

**Audience:** Backend team.
**Purpose:** These are the endpoints the merchant web app needs before it can go to staging with
zero mock data. Every UI spot listed below currently renders an explicit
`Awaiting backend API [Rxx]` placeholder (or a known-wrong stand-in like the
`delivered × 355` revenue) until the matching endpoint ships.

## Conventions (match the existing live API)
- **Base / proxy:** Fiesta REST at `https://fiesta.nearle.app/live/api/v1/web/<group>/<action>`.
  The frontend calls it through the dev proxy as `/fiesta/live/api/v1/web/...`.
- **Auth:** same as current read endpoints (no extra header required for GETs).
- **Envelope:** every response is
  ```json
  { "code": 200, "status": true, "message": "OK", "details": <payload> }
  ```
  `details` is the documented payload (array or object). On failure return
  `{ "code": 4xx, "status": false, "message": "<reason>" }`.
- **Types:** money = INR as a plain 2-dp number (e.g. `442800.00`), no currency symbol/string.
  Dates = `YYYY-MM-DD`. Timestamps = ISO-8601 (`2026-06-06T14:21:00`). Ids = integers.
- **Tenant scope (current):** `tenantid=1087`, `applocationid=1`, primary `locationid=1097`.
- **Paging:** where listed, accept `pageno` (1-based) and `pagesize`.

## Index
| Id | Endpoint | Method | Powers (UI) | Priority |
|---|---|---|---|---|
| [R1] | `/orders/getrevenuesummary` | GET | Revenue KPIs, OTIF, leaderboard, fulfilment health | **P0** |
| [R2] | `/orders/gettimeseries` | GET | Reports main chart, 7-day ledger | **P0** |
| [R3] | `/products/getproductanalytics` | GET | Product matrix sales + per-product detail | **P0** |
| [R4] | `/imports/getimportlogs` (+ `/imports/upload`) | GET/POST | Import audit stream + validator | P1 |
| [R5] | `/products/getcatalogpresets` | GET | Inventory catalog presets (optional) | P2 |
| [R6] | `/tenants/getsettings` (+ `/tenants/updatesettings`) | GET/PUT | Settings + Brand studio | P1 |
| [R7] | `/insights/getforecast` | GET | Operations forecast tiles (optional) | P2 |
| [R8] | `/products/stocktransfer` | POST | Operations stock-transfer modal | P1 |
| [R9] | `/partners/getridertelemetry` | GET | Fleet rating, GPS, ETA | P1 |
| [R10] | `/deliveries/getdispatchbuckets` | GET | StoreDetail intraday dispatch | P1 |
| [R11] | `/customers/getcustomeranalytics` | GET | Customer CSAT/AOV/retention | P1 |
| [R12] | `/alerts/getalerts` | GET | Operational alerts feed | P1 |
| [R13] | `/reports/getschedules` (+ POST) | GET/POST | Scheduled reports (optional) | P2 |

Plus **mutation routes** already named in the endpoint sheet but not yet shape-documented — see
the last section.

---

## [R1] Revenue summary  ·  `GET /orders/getrevenuesummary`
Real money totals. Today the app fakes revenue as `delivered × 355` — replace that.

**Request (query params)**
| param | type | required | notes |
|---|---|---|---|
| `tenantid` | int | ✓ | |
| `locationid` | int |  | omit → all outlets (overall); set → store-wise |
| `fromdate` | date | ✓ | |
| `todate` | date | ✓ | |

**Response `details`** (single object)
```json
{
  "tenantid": 1087,
  "locationid": null,
  "grossrevenue": 1248302.00,
  "netrevenue": 1180500.00,
  "profit": 354216.00,
  "marginpct": 28.4,
  "ordercount": 4921,
  "deliveredcount": 4102,
  "cancelledcount": 142,
  "otifpct": 98.2,
  "avgordervalue": 1580.00,
  "prev_grossrevenue": 1090000.00,
  "prev_ordercount": 4370
}
```
`prev_*` = same metric for the immediately preceding equal-length window (lets the UI show
`+14.2%` deltas without a second call). `otifpct` powers the OTIF / fulfilment-health tiles.
Frontend hook to add: `useFiestaRevenueSummary`.

---

## [R2] Orders / revenue time-series  ·  `GET /orders/gettimeseries`
Powers the Reports main chart (Orders / Revenue / Cancelled / SKUs) and StoreDetail 7-day ledger.

**Request**
| param | type | required | notes |
|---|---|---|---|
| `tenantid` | int | ✓ | |
| `locationid` | int |  | omit → overall |
| `granularity` | enum | ✓ | `day` \| `month` \| `year` |
| `fromdate` | date | ✓ | |
| `todate` | date | ✓ | |

**Response `details`** (array, one bucket per period)
```json
[
  { "label": "2026-06-01", "orders": 312, "revenue": 442800.00,
    "delivered": 298, "cancelled": 9, "activeskus": 72 }
]
```
`label` = `YYYY-MM-DD` (day), `YYYY-MM` (month), or `YYYY` (year). Removes all hardcoded chart
arrays and the region-scaling multipliers (filter by `locationid` instead).
Frontend hook: `useFiestaTimeSeries`.

---

## [R3] Product sales analytics  ·  `GET /products/getproductanalytics`
True units-sold + revenue per product, plus the per-product/per-outlet detail the matrix expands.

**Request**
| param | type | required | notes |
|---|---|---|---|
| `tenantid` | int | ✓ | |
| `locationid` | int |  | omit → across all outlets |
| `subcategoryid` | int |  | |
| `fromdate` / `todate` | date |  | sales window (default month-to-date) |
| `keyword` | string |  | |
| `pageno` / `pagesize` | int |  | |

**Response `details`** (array)
```json
[
  { "productid": 88231, "productname": "Ponni Raw Rice 10kg",
    "sku": "PONNI-RICE-10K", "categoryid": 2, "subcategoryname": "Rice",
    "image": "https://…",
    "unitssold": 12402, "revenue": 868140.00, "trend": "up",
    "closingstock": 1402, "maxcapacity": 2000,
    "binlocation": "BIN-C12", "lastauditdate": "2026-06-05", "verified": true,
    "outlets": [ { "locationid": 1097, "locationname": "RS Puram", "stock": 840 } ] }
]
```
`trend` ∈ `up|flat|down`. `outlets[]` = real per-hub split (replaces hardcoded 60/40).
If the catalog doesn't track `binlocation`/`lastauditdate`, return `null` — the UI will hide them
rather than fake them. Frontend hook: `useFiestaProductAnalytics`.

---

## [R4] Import / sync audit log  ·  `GET /imports/getimportlogs`  +  `POST /imports/upload`
Replaces the mock "Live Sync Audit Stream" and the hardcoded "14 duplicate SKUs" validator.

**GET request:** `tenantid` (✓), `pageno`, `pagesize`.
**GET `details`**
```json
[
  { "importid": 921, "batchref": "#IMP_0921_A", "type": "Inventory Sync",
    "source": "ERP Export", "rows": 421, "result": "SUCCESS",
    "message": "421 rows imported", "createdat": "2026-05-31T09:12:00" }
]
```
`result` ∈ `SUCCESS|FAILED|PARTIAL`.

**POST `/imports/upload`** (multipart, field `file` = CSV; plus `tenantid`) → returns the new log row
plus a validation block:
```json
{ "importid": 922, "result": "PARTIAL",
  "validation": { "passed": false, "duplicateskus": ["ST-SONA-25K"],
                  "badcolumns": [45, 82], "headerversion": "v2.8" } }
```
Frontend hooks: `useFiestaImportLogs`, `useFiestaImportUpload` (mutation).

---

## [R5] Catalog presets *(optional)*  ·  `GET /products/getcatalogpresets`
Inventory "Nilgiris Dairy / Coimbatore Heritage" quick-add packs.
**Request:** `tenantid` (✓). **`details`:**
```json
[ { "presetid": 3, "name": "Nilgiris Dairy Fresh Pack",
    "items": [ { "name": "Ooty Butter 500g", "sku": "DY-OOT-BTR",
                 "subcategoryname": "Dairy", "price": 340, "image": "https://…" } ] } ]
```
> Alternative: skip this and reuse master catalog (`/products/getproducts`) filtered by subcategory.

---

## [R6] Merchant settings + branding  ·  `GET /tenants/getsettings`  +  `PUT /tenants/updatesettings`
Replaces the localStorage `DEFAULTS` and the Brand Design Studio local state.

**GET request:** `tenantid` (✓). **GET `details`:**
```json
{
  "tenantid": 1087,
  "contactemail": "ops@ragul.com", "contactphone": "+91…",
  "minordervalue": 199, "deliverycharge": 29, "prepmins": 20,
  "deliverywindowmins": 45, "cancelwindowsecs": 120, "autoassignrider": true,
  "defaulttaxpercent": 5, "codenabled": true, "onlinepaymentenabled": true,
  "defaultregion": "Coimbatore", "defaultnewuserrole": 4,
  "ordernotifications": true, "lowstockalerts": true, "dailysummaryemail": false,
  "syncinterval": 15, "sandboxmode": false,
  "branding": { "themename": "Kaveri Org", "primarycolor": "#16a34a",
                "secondarycolor": "#f59e0b", "baglabel": "Freshly Harvested",
                "ecoverified": true, "stickerpattern": "radial" }
}
```
**PUT body:** same object (partial allowed) → returns the updated object.
Frontend hooks: `useFiestaSettings`, `useFiestaUpdateSettings` (mutation).

---

## [R7] Forecast insight *(optional)*  ·  `GET /insights/getforecast`
Operations "Forecast Efficiency 92% / ₹1.9L savings". Recommend dropping the tiles until a model
exists; if kept: **request** `tenantid` (✓), `locationid` (opt). **`details`:**
```json
{ "forecastefficiencypct": 92, "estsavingsperweek": 190000.00, "horizondays": 7 }
```

---

## [R8] Stock transfer  ·  `POST /products/stocktransfer`
Operations transfer-stock modal.
**Body:**
```json
{ "tenantid": 1087, "productid": 88231,
  "fromlocationid": 1097, "tolocationid": 1101, "quantity": 100 }
```
**`details`:** updated stock rows for both locations
```json
[ { "locationid": 1097, "productid": 88231, "closingstock": 740 },
  { "locationid": 1101, "productid": 88231, "closingstock": 360 } ]
```

---

## [R9] Rider telemetry  ·  `GET /partners/getridertelemetry`
Replaces hardcoded rider `rating`, `battery`, `lastPing` and the fake GPS route / "9 MINS" / "1.2 km".

**Request:** `applocationid` (✓), `tenantid` (opt), `userid` (opt → single rider).
**`details`** (array)
```json
[
  { "userid": 7781, "name": "Karthikeyan R", "status": "Delivering",
    "rating": 4.7, "completedtoday": 12, "battery": 84,
    "lastping": "2026-06-06T14:21:00", "zone": "RS Puram",
    "lat": 11.0041, "lng": 76.9612,
    "activedelivery": { "deliveryid": 99213, "etamins": 9, "distancekm": 1.2,
                        "destlat": 11.0102, "destlng": 76.9550 } }
]
```
`status` ∈ `Delivering|Idle|Offline`. `activedelivery` = `null` when the rider is idle.
Frontend hook: `useFiestaRiderTelemetry`.

---

## [R10] Intraday dispatch buckets  ·  `GET /deliveries/getdispatchbuckets`
Replaces StoreDetail `intervalSlots` (Morning Rush / Midday / Evening Peak computed from %).

**Request:** `tenantid` (✓), `locationid` (✓), `date` (✓, default today).
**`details`** (array, fixed buckets)
```json
[ { "bucket": "06:00-10:00", "label": "Morning Rush",
    "orders": 64, "revenue": 88400.00, "status": "PEAK" } ]
```
`status` ∈ `PEAK|HIGH|NORMAL|LOW`. Frontend hook: `useFiestaDispatchBuckets`.

---

## [R11] Customer analytics  ·  `GET /customers/getcustomeranalytics`
Replaces hardcoded CSAT 5.0 / retention 88.4% / AOV ₹1,580.

**Request:** `tenantid` (✓), `locationid` (opt), `customerid` (opt → single-customer rollup).
**`details`** (object)
```json
{
  "retentionpct": 88.4, "avgordervalue": 1580.00, "csat": 4.9,
  "totalcustomers": 1240,
  "percustomer": { "customerid": 55012, "orderscount": 18,
                   "totalspent": 28450.00, "csat": 5.0, "lastorderdate": "2026-06-04" }
}
```
`percustomer` present only when `customerid` is supplied.
> Customer order **history** itself already exists: `GET /orders/getorders?customerid=` — wire that, no new API.
Frontend hook: `useFiestaCustomerAnalytics`.

---

## [R12] Operational alerts  ·  `GET /alerts/getalerts`
Replaces the mock `operationalAlerts` (stock-critical, latency, rebalance…).

**Request:** `tenantid` (✓), `locationid` (opt), `status` (`open|all`), `pageno`, `pagesize`.
**`details`** (array)
```json
[ { "alertid": 5012, "tenantid": 1087, "locationid": 1097,
    "type": "critical", "title": "Stock Critical: RS Puram",
    "details": "Dairy inventory below 5%.",
    "createdat": "2026-06-06T08:24:00", "acknowledged": false } ]
```
`type` ∈ `critical|warning|info`. Frontend hook: `useFiestaAlerts`.

---

## [R13] Scheduled reports *(optional)*  ·  `GET /reports/getschedules`  +  `POST /reports/createschedule`
Replaces 3 hardcoded calendar events. Drop if not on the roadmap.
**GET `details`:**
```json
[ { "scheduleid": 11, "name": "Monthly Assortment Audit", "cron": "0 0 1 * *",
    "nextrun": "2026-07-01T00:00:00", "format": "PDF", "recipients": ["ops@ragul.com"] } ]
```
**POST body:** `{ tenantid, name, cron, format, recipients[] }` → returns the created schedule.

---

## Mutation routes already named in the endpoint sheet (publish request/response, then we wire)
These are marked `REVIEW_REQUIRED` in `Fiesta_All_Endpoints_With_Tables_And_Params.xlsx`. We need
the exact request body + success response for each:

| Action | Suggested endpoint | Body (fields we'll send) |
|---|---|---|
| Create web order | `POST /orders/create` | tenantid, locationid, customerid, items[{productid,qty,price}], paymenttype |
| Update order status | `PUT /orders/updatestatus` | orderheaderid, status, financialflag |
| Assign rider / dispatch | `POST /deliveries/assign` | orderheaderid, tenantid, locationid, partnerid, userid |
| Update pickup / delivered | `PUT /deliveries/updatestatus` | deliveryid, status |
| Add multi-product stock | `POST /products/addstock` | tenantid, locationid, items[{productid,qty,cost}] |
| Create / update / delete product | `POST/PUT/DELETE /products/...` | product fields |
| Onboard tenant / store / location | `POST /tenants/onboard`, `POST /tenants/createlocation` | tenant + location fields |

---

## Frontend status (for reference)
- All read endpoints that **already exist** are wired (orders/deliveries/stock/customers/users/
  locations/order-line-items/etc.).
- Each `[Rxx]` above corresponds to a visible `AwaitingApi` placeholder in the UI, so QA can see
  exactly which screens are still blocked.
- The moment an endpoint here ships with the documented shape, wiring it is one hook + swapping the
  placeholder — no redesign needed.
