Add read endpoints for counter sales

The ingest only ever wrote. A bill that reached pos_orders was safe and
completely unreachable — no screen in the product could show it, and the
only way to see a day's counter takings was to query the database by
hand.

Three endpoints: a paged bill list, one bill with its lines, and a
summary split the three ways somebody actually asks for — by tender for
reconciling a drawer, by day for a chart, by till for an outlet running
several counters.

locationid is required on all of them and is the authorisation boundary,
so a caller cannot page through another shop's takings by omitting a
parameter. Fetching a bill under the wrong outlet returns 404 even when
the reference is a real one.

Dates match businessdate rather than arrival, because a till that was
offline overnight uploads yesterday's bills this morning and they belong
to yesterday. The list is ordered by billedat for the same reason —
sorting by arrival would interleave a recovered backlog through today.

Unlike the ingest handlers these answer in the usual envelope: they are
read by the web app, not by a terminal, and nothing about them is bound
to the till's contract.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Suriya
2026-08-03 19:17:03 +05:30
parent ef647d3395
commit 8709556704
8 changed files with 472 additions and 6 deletions

View File

@@ -52,11 +52,41 @@ the standing cost of the split.
### HTTP
Base path: `/live/api/v1/pos`
**Written by a terminal** — bare-ack responses, see below.
| Method | Path | Purpose |
|---|---|---|
| `POST` | `/live/api/v1/pos/orders` | Completed bills |
| `POST` | `/live/api/v1/pos/customers` | Shoppers registered at a till |
| `GET` | `/live/api/v1/pos/catalogue` | Product pull. Query: `store_id`, `since`, `page`, `page_size` |
| `POST` | `/orders` | Completed bills |
| `POST` | `/customers` | Shoppers registered at a till |
| `GET` | `/catalogue` | Product pull. Query: `store_id`, `since`, `page`, `page_size` |
**Read by the web app** — normal `{code, message, status, details}` envelope.
| Method | Path | Purpose |
|---|---|---|
| `GET` | `/sales` | Bills for an outlet, newest first |
| `GET` | `/sales/detail` | One bill with its lines |
| `GET` | `/sales/summary` | Totals by tender, day and till |
| `GET` | `/health/terminal` | One till's live state |
| `GET` | `/health/location` | Every till at a shop |
`/sales` and `/sales/summary` take: **`locationid` (required)**, `fromdate`,
`todate` (YYYY-MM-DD, matched on `businessdate`), `terminalid`, `cashiername`,
`paymentmode`, `pageno`, `pagesize`.
`/sales/detail` takes `locationid` and `reference` — the terminal's order UUID,
the invoice number, or the `posorderid`, whichever the caller happens to have.
**`locationid` is the authorisation boundary.** Every read is scoped to one
outlet; omitting it is an error rather than a page through every shop's takings,
and asking for a bill under the wrong outlet returns 404 even when the reference
is valid.
Dates match `businessdate` — the day the sale was rung, not the day it reached
us. A till that was offline overnight uploads yesterday's bills this morning and
they belong to yesterday.
These answer with a **bare ack**, not the usual `{code, message, status}`
envelope — the terminal reads `accepted` from the top level of the body: