API (api-v2)

POS & shifts

/pos/v1/* is the till's whole API. It covers tills, catalogue reads, sales, the cash drawer lifecycle, till sections and POS customer lookups. pos is an orchestrator; shift and till-section own their tables.


Catalogue reads

RouteGuard
get-tillsauthenticated. Returns (shop, location) pairs where the user holds pos.operate, with currency, taxRate, taxesIncluded, canManageSections.
get-catalog, lookup-barcode, get-categoriespos.operate
get-modifier-groupspos.operate
  • Reads span every branch, so a cashier can check another branch's stock.
  • Prices are converted from USD to the shop currency.
  • get-catalog sorts in-stock items first (PR #85).

Sales

RouteGuard
create-ordershop access + at pos.operate for the till's location
verify-discount-pinpos.operate
  • Tenders: cash, card, wallet. The UI calls wallet "Whish" (POS ADR 0002, API PR #77).
  • Split tender is allowed. The till sends the cash part with an amount and the other part without an amount, and the server fills the remainder (pos.payments.ts).
  • Change is computed on the server. "Covers the total" means rounded half-up to the cent, mirrored on the till.
  • Card and wallet payments post to 1030 Card money in transit.
  • Every sale is stamped with the open shift (API ADR 0001), and a cash sale requires an open drawer (ADR 0002). The till goes further and requires a drawer for any sale.
  • Idempotent: the till generates one crypto.randomUUID() per basket, not per attempt. A replay returns idempotentReplay: true.
  • An optional customerId attaches a customer. Since PR #83 the order shows that customer.

Cash drawer (shifts)

RouteGuard
open-shiftat shifts.open
get-open-shiftpos.operate
get-expense-categories, record-cash-movementcash.move
close-shift, get-my-shifts, get-shift, get-shift-salesshifts.open or shifts.manage
/merchant/shift/v1/get-shiftsshifts.manage

Lifecycle:

  1. Open with a float (zero allowed). A user has at most one open drawer. Opening a second returns 409.
  2. Cash movements:
    • drop, pay_in and pay_out, each with a required reason.
    • A pay_out also names an expense category and posts to the ledger as an expense (PR #71).
  3. Close with a counted amount and an optional note:
    • Expected cash is frozen at close, and variance = counted − expected (shift.math.ts).
    • Cash refunds made with paidFromDrawer are included in expected cash.

POS ADR 0001 rules that the cashier never sees the running expected cash. get-open-shift is shaped so the till doesn't need it.

Till sections

Tables: till_section, till_section_product

RouteGuard
get-till-sectionspos.operate
create, update, delete, reorder, move, add/remove-till-section-productspos.operate + wide pos.sections.manage
  • Sections nest. A section holds products or child sections, never both.
  • Deleting a section that still has children is refused.
  • Sections hold products, so adding one variant adds the whole product.
  • Colours: one of 9 named tints, or none.

POS customers

RouteGuard
search-customers, find-customers-by-phonepos.operate + customers.view
create-customer, update-customerpos.operate + wide customers.write

find-customers-by-phone powers the till's live duplicate-phone warning. It warns but doesn't block, because families share phones.

In flight

PRWhat
#78 feat/pos-catalogue-syncServe a till its own copy of the catalogue (syncCatalog, getLiveFigures)
#82 feat/offline-first-tillAccept and record sales a till made offline

See Offline till.

Previous
Discounts