Skip to main content

QR Ordering – Amendment: Mobile App & Print Flow (KOT / Invoice)

QR Ordering (APP to Print KOT & Ticket).pdf

# QR Ordering – Amendment: Mobile App & Print Flow (KOT / Invoice)

**Status:** Draft v0.1 – for review
**Scope:** Staff mobile app (iOS/Android), guest web ordering, push-triggered printing
**Out of scope:** Aegis internals (existing order management system, already integrated)

---

## 1. Summary

| # | Change | Notes |
|---|--------|-------|
| 1 | Guest scans table/outlet QR and places **Dine-in** or **Takeaway** order | Phone camera → browser (no app install) |
| 2 | Staff can place the same orders on behalf of a guest | Same ordering web app, inside the staff app |
| 3 | iOS / Android app with a **WebView** that loads the same web app | Staff use the app; guests use camera + browser |
| 4 | **KOT** and **Invoice** printing from the app to **Network (LAN) / Bluetooth** printers | Triggered by push notification |

---

## 2. Actors & Components

| Component | Responsibility |
|-----------|----------------|
| **Guest Browser** | Scan QR, order via web app |
| **Staff App (iOS/Android)** | WebView hosting the web app + **native print layer** + push receiver |
| **QR Ordering Backend** | Receives orders, forwards to Aegis, resolves print device(s), sends push, tracks print jobs |
| **Aegis** | Order management system. Source of truth for order data **and** printable data (KOT & Invoice) |
| **FCM / APNs** | Push delivery to Android / iOS |
| **Printer** | Network (ESC/POS over TCP 9100) or Bluetooth thermal printer |

---

## 3. High-Level Architecture

```mermaid
flowchart LR
    G[Guest Browser<br/>QR scan] -->|Order| B[QR Ordering Backend]
    S[Staff App<br/>WebView] -->|Order| B
    B <-->|Order Place & Response| A[Aegis]
    B -->|Push: jobId only| P{FCM / APNs}
    P --> D[Print Device<br/>Staff App at outlet]
    D -->|Get printable data| A
    D -->|ESC/POS| PR[Network / BT Printer]
    D -->|Print status ack| B
```

---

## 4. Ordering Flow (Context)

```mermaid
flowchart TD
    A[Scan QR / Staff opens app] --> B{Order type}
    B -->|Dine-in| C[Table context from QR / staff selects table]
    B -->|Takeaway| D[Customer details]
    C --> E[Add items & submit]
    D --> E
    E --> F[QR Backend validates & pushes order to Aegis]
    F --> G[Aegis confirms order]
    G --> H[Print Trigger - see section 5]
```

---

## 5. Print Flow

### 5.1 Rules

1. When an order is punched (guest / staff / Aegis), the **QR backend** sends a push notification to the correct device(s) in that outlet.
2. Android → **FCM**. iOS → **APNs**.
3. On receiving the push, the app calls the **Aegis API** to fetch the printable data.
4. Aegis returns the data; the device prints it.
5. **Paper size, printer type and design are dynamic** (not hardcoded in the app).
6. Two formats: **KOT** and **Invoice**.
7. Printable data for both formats always comes from the Aegis API, **called after** the push is received.
8. The push carries **only identifiers** (no order/PII data).
9. After printing, it sends printed status to  **Aegis API**

### 5.2 Main Sequence – KOT

```mermaid
sequenceDiagram
    autonumber
    participant U as Guest / Staff
    participant W as Web App
    participant Q as QR Backend
    participant A as Aegis
    participant PS as FCM / APNs
    participant D as Staff App (Print Device)
    participant PR as Printer

    U->>W: Place order
    W->>Q: POST /orders
    Q->>A: Create order
    A-->>Q: orderId, status
    Q->>Q: Create PrintJob (type=KOT, status=PENDING)
    Q->>Q: Resolve target device(s) by outlet + printer mapping
    Q->>PS: Send push {jobId, type=KOT, outletId, orderId}
    PS-->>D: Deliver push
    D->>Q: Ack received (jobId)
    D->>A: GET printable data (orderId, type=KOT, printer profile)
    A-->>D: Printable payload
    D->>D: Render per paper width / template
    D->>PR: Send print bytes (TCP / Bluetooth)
    PR-->>D: Printed / error
    D-->>A: Printed Status
    D->>Q: PATCH /print-jobs/{jobId} status=PRINTED
    Q-->>U: Order confirmed
```

### 5.3 Main Sequence – Invoice

```mermaid
sequenceDiagram
    autonumber
    participant S as Staff / Aegis
    participant Q as QR Backend
    participant PS as FCM / APNs
    participant D as Staff App
    participant A as Aegis
    participant PR as Printer

    S->>Q: Bill requested / settled (event)
    Q->>Q: Create PrintJob (type=INVOICE)
    Q->>PS: Push {jobId, type=INVOICE, orderId}
    PS-->>D: Deliver push
    D->>A: GET printable data (orderId, type=INVOICE)
    A-->>D: Invoice payload
    D->>PR: Print
    PR-->>D: OK
    D->>A: Status = PRINTED
```

---

## 6. Push Payload (Proposed)

```json
{
  "jobId": "pj_8f3a...",
  "type": "KOT",
  "outletId": "OUT_101",
  "orderId": "ORD_55021",
  "printerId": "PRN_KITCHEN_1",
  "createdAt": "2026-10-07T12:30:00Z"
}
```

- Android: **FCM data message**, `priority: high`.
- iOS: **APNs** with `content-available: 1` (see risk R1 – likely needs a visible alert as well).
- No customer data or items in the push.

---

## 7. Printable Data – Dynamic Design

Aegis returns the printable data; the device only renders it. Recommended contract:

**Request**
```
GET /print/data?orderId=ORD_55021&type=KOT&paperWidth=80&printerId=PRN_KITCHEN_1
```

**Response (structured, preferred)**
```json
{
  "type": "KOT",
  "paper": { "widthMm": 80, "charsPerLine": 48, "codepage": "UTF-8" },
  "blocks": [
    { "t": "text",  "v": "KOT #123",       "align": "center", "size": "large", "bold": true },
    { "t": "kv",    "k": "Table",          "v": "T-12" },
    { "t": "line" },
    { "t": "item",  "qty": 2, "name": "Chicken Momo", "mods": ["Extra spicy"] },
    { "t": "qr",    "v": "https://..." },
    { "t": "cut" }
  ]
}
```

| Aspect | Approach |
|--------|----------|
| **Paper size** | 58 mm / 80 mm configured per printer profile; sent as a request param and/or defined in Aegis |
| **Printer type** | Per-printer profile: connection (LAN IP:port / BT MAC), protocol (ESC/POS), codepage |
| **Design** | Aegis owns the template. App renders blocks → ESC/POS bytes |
| **Non-Latin text** | If printer lacks the font (e.g. Devanagari), render as bitmap/raster |
| **Formats** | `KOT` and `INVOICE` – same renderer, different payload |

> Open item: Aegis returns **(a)** structured blocks, **(b)** raw ESC/POS bytes, or **(c)** an image. See Question Q3.

---

## 8. Device & Printer Registry

Backend needs to know *which device prints what*.

| Table | Fields |
|-------|--------|
| `print_devices` | deviceId, outletId, platform, fcm/apns token, appVersion, lastSeen, isPrintStation |
| `printers` | printerId, outletId, name, connection (LAN/BT), address, paperWidth, role (KITCHEN / BAR / BILLING) |
| `device_printer_map` | deviceId ↔ printerId (which device can reach which printer) |
| `print_jobs` | jobId, orderId, type, printerId, targetDeviceId, status, attempts, timestamps, error |

**App registration flow:** login → select outlet → register push token → (if print station) discover & save printers → heartbeat.

---

## 9. App Architecture (WebView Shell)

```mermaid
flowchart TD
    subgraph App[Staff App iOS / Android]
        WV[WebView<br/>QR ordering web app]
        BR[JS Bridge]
        PUSH[Push Handler]
        PQ[Print Queue + Retry]
        RND[ESC/POS Renderer]
        CON[Printer Connectors<br/>TCP / Bluetooth]
    end
    WV <--> BR
    PUSH --> PQ
    BR --> PQ
    PQ --> RND --> CON
```

- The web app can also trigger a **manual reprint** via the JS bridge.
- Guests never use the app: the browser version is the same web app **without** the bridge.

---

## 10. Risks & Design Concerns

| ID | Risk | Mitigation |
|----|------|------------|
| **R1** | **iOS silent push is not reliable.** APNs may throttle or drop it, and it will not wake an app the user force-quit. KOT is time-critical. | Use a visible alert push + Notification Service Extension where possible; keep a **WebSocket/SSE or short-poll fallback** while the app is open; recommend a dedicated, always-open print tablet |
| **R2** | **Android Doze / OEM battery killers** can delay FCM. | High-priority data messages, foreground service on print station, battery-optimization exemption |
| **R3** | **Duplicate prints** if several devices in an outlet receive the push. | One designated print station per printer, or a "claim job" API (first device wins) |
| **R4** | **Printed twice on push retry.** | Idempotent `jobId`; device ignores already-PRINTED jobs |
| **R5** | **Aegis credentials on the device.** | Short-lived token issued by QR backend, or QR backend proxies the Aegis call |
| **R6** | **Printer offline / out of paper.** | Local queue, retry with backoff, visible print-queue screen, manual reprint |
| **R7** | **Bluetooth on iOS** requires BLE-compatible printers (classic BT SPP is not supported on iOS). | Use LAN printers on iOS, or confirm BLE printer models |
| **R8** | **Push payload data leakage.** | IDs only; fetch data over HTTPS |

---