# Kitchen & Orders API Documentation

Full production specification for the Kitchen & Orders module.
Covers live active orders, single-order live polling, item updates, order history, status management, and WebSocket real-time events.

## Base URL
```
/api/orders
```

---

## Table of Contents
1.  [Dashboard Stats](#1-get-apiordersstats-summary)
2.  [Active Live Orders](#2-get-apiordersactive)
3.  [Single Order Live Snapshot](#3-get-apiorderslive)
4.  [Order History (Paginated)](#4-get-apiordershistory)
5.  [Export History as CSV](#5-get-apiordershistoryexport)
6.  [Sync Table → Order](#6-post-apiorderssync-from-tabletableid)
7.  [Get Single Order (Full Detail)](#7-get-apiordersorderid)
8.  [Update Order Status (KDS)](#8-patch-apiordersorderidstatus)
9.  [Update Order Items (Live KOT)](#9-patch-apiordersorderiditemsitems)
10. [Checkout / Settle Order](#10-post-apiordersorderidcheckout)
11. [WebSocket Events](#11-websocket-events)
12. [Auto-Sync: Table → Order Flow](#12-auto-sync-table--order-flow)

---

## 1. `GET /api/orders/stats/summary`
**Auth:** Admin JWT required  
**Description:** Dashboard summary metrics.

**Response:**
```json
{
  "success": true,
  "data": {
    "active_orders": 5,
    "today_revenue": 4520.50,
    "orders_completed_today": 12,
    "status_breakdown": {
      "pending":   2,
      "preparing": 2,
      "served":    1,
      "completed": 12
    }
  }
}
```

---

## 2. `GET /api/orders/active`
**Auth:** Admin JWT required  
**Description:** Returns all live, non-completed orders with their items. Combine with WebSocket `order_created` / `order_updated` for a fully live dashboard (no manual refresh needed).

**Response:**
```json
{
  "success": true,
  "total": 3,
  "data": [
    {
      "order_id": "TBL7-20260418-143022",
      "table_id": 7,
      "table_number": "T7",
      "waiter_id": "WID-104",
      "waiter_name": "John Doe",
      "customer_name": "Raj Kumar",
      "pax": 4,
      "status": "preparing",
      "subtotal": 430.00,
      "tax": 51.60,
      "discount": 0.00,
      "total": 481.60,
      "created_at": "2026-04-18T14:30:22Z",
      "updated_at": "2026-04-18T14:35:10Z",
      "table_status": "Running",
      "items": [
        {
          "id": 1,
          "order_id": "TBL7-20260418-143022",
          "menu_item_id": 12,
          "name": "Butter Chicken",
          "quantity": 2,
          "unit_price": 180.00,
          "item_discount": 0.00,
          "is_complimentary": false,
          "line_total": 360.00
        }
      ]
    }
  ]
}
```

---

## 3. `GET /api/orders/:orderId/live`
**Auth:** Admin JWT required  
**Description:** Lightweight real-time snapshot of one order — includes `table_status` directly from `restaurant_tables`. Use this as a **polling fallback** (every 5–10 s) when WebSocket is unavailable, or to verify state after receiving a WebSocket event.

**Response:**
```json
{
  "success": true,
  "data": {
    "order_id": "TBL7-20260418-143022",
    "table_id": 7,
    "table_number": "T7",
    "waiter_id": "WID-104",
    "waiter_name": "John Doe",
    "customer_name": "Raj Kumar",
    "pax": 4,
    "status": "preparing",
    "subtotal": 430.00,
    "tax": 51.60,
    "discount": 0.00,
    "total": 481.60,
    "created_at": "2026-04-18T14:30:22Z",
    "updated_at": "2026-04-18T14:38:00Z",
    "table_status": "Running",
    "items": [
      {
        "id": 1,
        "menu_item_id": 12,
        "name": "Butter Chicken",
        "quantity": 2,
        "unit_price": 180.00,
        "item_discount": 0.00,
        "is_complimentary": false,
        "line_total": 360.00
      }
    ]
  }
}
```

---

## 4. `GET /api/orders/history`
**Auth:** Admin JWT required  
**Query Params:**

| Param    | Type   | Example        | Description                                 |
|----------|--------|----------------|---------------------------------------------|
| `page`   | int    | `1`            | Page number (default: 1)                    |
| `limit`  | int    | `20`           | Records per page (default: 20, max: 100)    |
| `search` | string | `TBL7`         | Search by order_id, table_number, invoice_id, customer_name |
| `date`   | string | `2026-04-18`   | Exact date filter (YYYY-MM-DD)              |
| `status` | string | `completed`    | Filter by `completed` or `cancelled`        |

**Response:**
```json
{
  "success": true,
  "data": [
    {
      "order_id": "TBL7-20260418-130011",
      "table_number": "T7",
      "customer_name": "Meena Sharma",
      "waiter_id": "WID-104",
      "waiter_name": "John Doe",
      "status": "completed",
      "subtotal": 300.00,
      "tax": 36.00,
      "discount": 20.00,
      "total": 316.00,
      "created_at": "2026-04-18T13:00:11Z",
      "updated_at": "2026-04-18T13:45:00Z",
      "invoice_id": "INV-20260418-134500",
      "payment_method": "UPI",
      "amount_paid": 316.00,
      "settled_by": "admin",
      "settled_at": "2026-04-18T13:45:00Z",
      "is_split_bill": false,
      "billing_ticket_number": "T7-20260418-130011",
      "bill_number": 42,
      "items": [
        {
          "name": "Dal Makhani",
          "quantity": 1,
          "unit_price": 150.00,
          "line_total": 150.00
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 1,
    "totalPages": 1
  }
}
```

---

## 5. `GET /api/orders/history/export`
**Auth:** Admin JWT required  
**Description:** Downloads order history as a CSV file.

**Query Params:**

| Param  | Type   | Example       | Description               |
|--------|--------|---------------|---------------------------|
| `from` | string | `2026-04-01`  | Start date (YYYY-MM-DD)   |
| `to`   | string | `2026-04-18`  | End date (YYYY-MM-DD)     |

**Response:** `Content-Type: text/csv` file download.

---

## 6. `POST /api/orders/sync-from-table/:tableId`
**Auth:** Admin JWT required  
**Description:** Manual bridge. Takes an active `restaurant_tables` session (with items in `table_cart_items`) and creates a corresponding record in `orders` + `order_items`.  

> **Note:** This endpoint is called **automatically** by the server when `PATCH /api/tables/:id/status` transitions a table to `Running` or `RunningKOT`. You only need to call this manually if the auto-sync did not trigger (e.g. table was already Running before the feature was deployed).

**URL Param:** `:tableId` — the `restaurant_tables.id`

**Response:**
```json
{
  "success": true,
  "message": "Order synced and created from active table session.",
  "data": {
    "order_id": "TBL7-20260418-143022",
    "status": "pending",
    "total": 481.60,
    "items": [...]
  }
}
```

---

## 7. `GET /api/orders/:orderId`
**Auth:** Admin JWT required  
**Description:** Full details of one order: items, invoice (if settled), and full status audit log.

**Response:**
```json
{
  "success": true,
  "data": {
    "order_id": "TBL7-20260418-143022",
    "status": "preparing",
    "items": [...],
    "invoice_id": null,
    "status_log": [
      { "old_status": null,      "new_status": "pending",   "changed_by": "system:table-status", "changed_at": "..." },
      { "old_status": "pending", "new_status": "preparing", "changed_by": "admin",               "changed_at": "..." }
    ]
  }
}
```

---

## 8. `PATCH /api/orders/:orderId/status`
**Auth:** Admin JWT required  
**Description:** Only `completed` and `cancelled` may be set via this endpoint.  

> ⚠️ **`pending`, `preparing`, `ready`, `served` are automatically managed by the table status.**  
> Do **not** call this endpoint for those — update the table status instead.

**Status mapping (table → order):**

| `PATCH /api/tables/:id/status` | Automatically sets order to |
|---|---|
| `Running` | `pending` |
| `RunningKOT` | `preparing` |
| `Printed` | `ready` |
| `Paid` | `served` |

**Request Body (manual):**
```json
{
  "status": "cancelled",
  "changed_by": "manager"
}
```

**Allowed values:** `completed` • `cancelled`  
Attempting `pending / preparing / ready / served` returns **403** with a hint pointing to the correct table endpoint.

**Success Response:**
```json
{
  "success": true,
  "message": "Order TBL7-20260418-143022 status updated to 'cancelled'.",
  "data": {
    "order_id": "TBL7-20260418-143022",
    "status": "cancelled",
    "previous_status": "pending",
    "updated_at": "2026-04-18T14:35:10.000Z"
  }
}
```

**403 Response (blocked table-driven status):**
```json
{
  "success": false,
  "message": "Order status 'preparing' is automatically managed by table status. Update the table status via PATCH /api/tables/:id/status instead.",
  "hint": {
    "Running → pending":      "PATCH /api/tables/:id/status  { \"status\": \"Running\" }",
    "RunningKOT → preparing": "PATCH /api/tables/:id/status  { \"status\": \"RunningKOT\" }",
    "Printed → ready":        "PATCH /api/tables/:id/status  { \"status\": \"Printed\" }",
    "Paid → served":          "PATCH /api/tables/:id/status  { \"status\": \"Paid\" }"
  }
}
```

---

## 9. `PATCH /api/orders/:orderId/items`
**Auth:** Admin JWT required  
**Description:** Replace all items on a live (non-completed, non-cancelled) order. Useful when the waiter adds more dishes or removes an item after the order was already synced to the KDS.  
Re-calculates subtotal / tax / total and triggers WebSocket `order_items_updated`.

**Request Body:**
```json
{
  "items": [
    {
      "menu_item_id": 12,
      "name": "Butter Chicken",
      "quantity": 2,
      "unit_price": 180.00,
      "item_discount": 0,
      "is_complimentary": false,
      "selected_modifiers": []
    },
    {
      "menu_item_id": 9,
      "name": "Garlic Naan",
      "quantity": 4,
      "unit_price": 40.00,
      "item_discount": 0,
      "is_complimentary": false,
      "selected_modifiers": []
    }
  ]
}
```

**Response:**
```json
{
  "success": true,
  "message": "Order TBL7-20260418-143022 items updated. New total: 641.60.",
  "data": {
    "order_id": "TBL7-20260418-143022",
    "subtotal": 520.00,
    "tax": 62.40,
    "discount": 0.00,
    "total": 582.40,
    "items": [...]
  }
}
```

---

## 10. `POST /api/orders/:orderId/checkout`
**Auth:** Admin JWT required  
**Description:** Settles the order. Creates an invoice, marks order `completed`, resets the physical table to `Blank`, and clears cart data.  
Triggers WebSocket `order_completed` + `table_updated`.

**Request Body:**
```json
{
  "paymentMethod": "UPI",
  "amountPaid": 481.60,
  "settledBy": "cashier-admin",
  "isSplitBill": false,
  "notes": "Customer paid via GPay"
}
```

**Response:**
```json
{
  "success": true,
  "message": "Order checked out and invoice created.",
  "data": {
    "order_id": "TBL7-20260418-143022",
    "invoice_id": "INV-20260418-144500",
    "amount_paid": 481.60,
    "payment_method": "UPI",
    "settled_at": "2026-04-18T14:45:00.000Z"
  }
}
```

---

## 11. WebSocket Events

Connect using Socket.io to the backend base URL. All events are emitted globally.

### `order_created`
Emitted when a table transitions to `Running` / `RunningKOT` (**auto-sync**) or when `POST /api/orders/sync-from-table/:tableId` creates a new order ticket manually.
```json
{
  "orderId":     "TBL7-20260418-143022",
  "tableId":     7,
  "tableNumber": "T7",
  "status":      "pending",
  "total":       481.60,
  "itemCount":   3,
  "createdAt":   "2026-04-18T14:30:22.000Z"
}
```

### `order_updated`
Emitted when `PATCH /api/orders/:orderId/status` transitions a ticket's state.  
Now includes `tableId` and the **live** `tableStatus` from `restaurant_tables`.
```json
{
  "orderId":        "TBL7-20260418-143022",
  "status":         "ready",
  "previousStatus": "preparing",
  "updatedAt":      "2026-04-18T14:45:00.000Z",
  "tableId":        7,
  "tableStatus":    "Running"
}
```

### `order_items_updated`
Emitted when `PATCH /api/orders/:orderId/items` replaces the live KOT items.
```json
{
  "orderId":   "TBL7-20260418-143022",
  "tableId":   7,
  "subtotal":  520.00,
  "tax":       62.40,
  "discount":  0.00,
  "total":     582.40,
  "itemCount": 2,
  "items":     [...],
  "updatedAt": "2026-04-18T14:50:00.000Z"
}
```

### `order_completed`
Emitted when `POST /api/orders/:orderId/checkout` finalises the order and generates an invoice.
```json
{
  "orderId":       "TBL7-20260418-143022",
  "invoiceId":     "INV-20260418-144500",
  "tableId":       7,
  "tableNumber":   "T7",
  "amountPaid":    481.60,
  "paymentMethod": "UPI",
  "settledAt":     "2026-04-18T14:45:00.000Z"
}
```

### `table_updated`
Emitted by **all** table status-changing actions:
- `PATCH /api/tables/:id/status` — every table status change
- `PATCH /api/orders/:orderId/status` — mirrors order ↔ table status
- `POST /api/orders/:orderId/checkout` — table reset to Blank
- `POST /api/tables/:id/settle` — after settlement
- `POST /api/tables/:id/clear` — after clearing

Payload includes `table_number` for direct use in the UI without a lookup, plus `current_order_started_at` for table timers:
```json
{
  "id":                       7,
  "table_number":             "T7",
  "status":                   "Running",
  "current_order_started_at": "2026-04-22T17:00:00.000Z"
}
```

When a table is reset to `Blank`, `current_order_started_at` is `null`.

---

## 12. Auto-Sync: Table → Order Flow

The status update now sets `current_order_started_at` with `COALESCE(current_order_started_at, NOW())`, reads it back from MySQL, and includes that exact value in both the HTTP response and `table_updated` WebSocket event.

```
PATCH /api/tables/:id/status  { status: "Running" }
         │
         ├─► UPDATE restaurant_tables SET status = 'Running'
         │
         ├─► Emit  table_updated  { id, table_number, status: "Running" }
         │
         └─► autoSyncOrderFromTable(tableId)   [fire-and-forget]
                  │
                  ├─► Check if active order already exists → skip if yes
                  ├─► Fetch table_cart_items + cart discount + tax config
                  ├─► INSERT into orders + order_items + order_status_log
                  └─► Emit  order_created  { orderId, tableId, ... }
```

This means the admin **Live Orders** dashboard automatically shows a new order card the moment a waiter marks a table as Running — no manual sync needed.

---

## Waiter-Side Stub (Future)

A dedicated waiter route is stubbed for your later use:

- `POST /api/orders/waiter/create` — Waiter places a new order from their device.
- `GET  /api/orders/waiter/my-tables` — Waiter sees their assigned tables' order statuses.

These will require a `waiter` JWT from `POST /api/waiters/login`.
