From bef2a082997b3c8f977f3cce643f42ab8c9d7320 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 31 Aug 2026 02:25:28 +0200 Subject: [PATCH] docs(pos): record created PB collections, live test results, catalog paging Co-Authored-By: Claude --- .../2026-08-30-wfsales-pos-implementation.md | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-wfsales-pos-implementation.md b/docs/superpowers/plans/2026-08-30-wfsales-pos-implementation.md index b6f1c73..cb80983 100644 --- a/docs/superpowers/plans/2026-08-30-wfsales-pos-implementation.md +++ b/docs/superpowers/plans/2026-08-30-wfsales-pos-implementation.md @@ -2075,14 +2075,50 @@ Smaller adjustments made along the way: `/pos/*`, all four endpoints 401 without a valid key, and `/catalog` + `/customers` return real PocketBase data with one. -### Blocked before Task 20 can run +### PocketBase collections — created 2026-08-31 + +Created directly against the live instance via the superuser API (the user +asked for this explicitly; `AGENTS.md`'s "user does it in PB admin" convention +was waived for this one-off). Script kept at `/tmp/pb-create-pos.mjs`, not +committed — the repo still runs no migrations against the live instance. + +- **`pos_sales`** (`pbc_4138824761`) — all API rules null (superuser-only). + Fields: `created`/`updated` autodate, `sale_number`, `payment_method` + (select: cash/card/eft/account), `cash_given`, `change_given`, `subtotal`, + `vat`, `total`, `customer` (relation → customers), `client_ref`. + Indexes: `idx_pos_sales_created`, and a **partial** unique index + `idx_pos_sales_client_ref ... WHERE client_ref != ''` — a plain UNIQUE would + have rejected every sale after the first that carried an empty `client_ref`. +- **`pos_sale_items`** (`pbc_1600525088`) — all API rules null. Fields: + `created`/`updated` autodate, `sale` (relation → pos_sales, required, + cascadeDelete), `stock_item` (relation → stock_items, required), `sku`, + `description`, `qty`, `unit_cost`, `unit_price`. Index on `sale`. + +### Live end-to-end results (dev server, real PocketBase) + +- Cash sale of 3 units: sale written, VAT split correct (R300 → R260.87 + + R39.13), change R50, `current_qty` 7 → 4, one ledger row. +- Replay of the same `client_ref`: returned the original sale with + `duplicate: true`, wrote nothing, `current_qty` stayed 4. Idempotency holds. +- `account` and `eft` sales: recorded under their own method with + `cash_given` 0, customer relation populated, stock still decremented. +- Over-selling (999 of 7): HTTP 409 with per-line shortage detail. +- All test rows deleted and `current_qty` restored to 7 afterwards. + +### Still blocked before the till can take real money -- `pos_sales` / `pos_sale_items` collections do not exist yet (`/sales` returns - 500). Per the prerequisite at the top of this plan, plus two additions: - `payment_method` must include `eft` and `account`, and `pos_sales` needs a - `client_ref` text field with a unique index. - **No sellable stock.** Of 373 active `stock_items`, zero have a `selling_price` and zero have a `markup`; only 98 have a `cost_price`. The POS - prices from `selling_price`, so today every line would ring up at R0.00. This - needs a pricing decision before the till can take real money — see the handover - notes for the options. + prices from `selling_price`, so every line currently rings up at R0.00. The + user opted to populate `selling_price` in the main app rather than add a + code-side fallback. +- Bluetooth thermal printing is still unverified — needs the physical printer. + +### Catalog paging (added 2026-08-31, beyond the original plan) + +The till was rendering all 373 items at once. `listActiveStockCatalog` now +sorts `-created` and `PosCatalogItem` carries `created`; the frontend renders +50 at a time behind a Show-more button that also auto-loads on scroll. Header +shows "Recently added" while browsing, "Results" once filtered; changing the +search or category resets to the first chunk. Scroll position survives +re-renders so adding to the cart no longer jumps to the top.