WFDASH-WEBAPP/public/pos
Dave 8232a87f99 feat(pos): ex-VAT totals, ad-hoc custom lines, fixed discount tiers
Three changes to the POS sale pipeline, all in one commit because
pos-service.ts / types.ts / pos-app.js are each touched by all three and
splitting them would only produce intermediate broken states.

VAT is now charged on top, not backed out

Catalogue prices are ex-VAT: `selling_price` is derived as
`cost_price x markup` from the supplier's ex-VAT cost, with no VAT
uplift anywhere (verified — median selling/cost across the catalogue is
exactly 1.8). The till nevertheless treated them as VAT-inclusive and
recovered the net with `total / 1.15`, so every sale was recorded with
an understated net and an understated VAT line.

An R100 ex-VAT line now rings up as R115. The rate comes from the till's
letterhead, read server-side rather than accepted off the wire, and is
stored per sale so a later letterhead rate change cannot rewrite what
was charged. All 3,623 catalogue prices are untouched.

Ad-hoc "custom" lines

A cashier can now ring up something that was never a catalogue item —
"bolt cutting" at R20 agreed at the counter — via a new
"+ Add a custom item" composer. The line prints on the slip and lands in
pos_sale_items, but carries no stock_item relation and takes no stock
movement: no ledger row, no current_qty decrement, no catalogue entry.

pos_sale_items.stock_item is no longer required at the schema level and
an explicit `is_custom` flag marks the line, rather than inferring it
from a blank relation — so "how much did we make on bolt cutting this
month" is a plain query, and a future stock-less line type cannot be
confused with a corrupt one.

Custom lines are refused on account sales: those raise a delivery note
the customer signs for and feed their statement, so an ad-hoc charge
must not slip onto one. Enforced server-side, not just in the UI.

Discounts are now 20% or 40%, nothing else

The R-off / %-off pills and the free-typed number box are gone. The
server treats DISCOUNT_PERCENT_OPTIONS as an allowlist and refuses
anything outside it rather than coercing it, so a stale till cannot
invent a discount. The discount lands on the ex-VAT subtotal, making
"20% off" a true 20% off both the net and the VAT on it.

Two defects found while doing this

pos_sales was missing six fields that checkoutSale has been writing all
along (gross_subtotal, gross_vat, gross_total, discount_amount,
discount_kind, discount_input). PocketBase accepts unknown fields
without error and silently drops them, so all 192 historical sales lack
them and a discounted sale reprinted as though no discount had been
applied. Added, plus vat_percent.

History also passed gross_total (VAT-inclusive) where the receipt
derives the gross VAT from an ex-VAT base — a reprint double-counted
VAT. Corrected.

Verification: 3860 tests pass (14 new); typecheck, lint and production
build clean; live PocketBase probe confirms a pos_sale_items row with
stock_item="" and is_custom=true round-trips, with the probe removed
afterwards.
2026-10-12 22:54:09 +02:00
..
fa fix(pos): correct Font Awesome webfont URL in bundled CSS 2026-08-31 18:04:14 +02:00
cashup.html feat(pos): refund a whole sale, put the goods back, print a credit slip 2026-10-07 23:41:09 +02:00
epos-print.js feat(pos): refund a whole sale, put the goods back, print a credit slip 2026-10-07 23:41:09 +02:00
history.html feat(pos): ex-VAT totals, ad-hoc custom lines, fixed discount tiers 2026-10-12 22:54:09 +02:00
index.html feat(pos): letterhead service + 2 API endpoints + vat_percent on Letterhead type 2026-09-04 09:13:53 +02:00
letterhead-config.js feat(pos): letterhead service + 2 API endpoints + vat_percent on Letterhead type 2026-09-04 09:13:53 +02:00
manifest.json chore(pos): bump SW cache to v13, skip /api/ caching, scope manifest to /pos/ 2026-08-31 01:48:49 +02:00
pos-app.js feat(pos): ex-VAT totals, ad-hoc custom lines, fixed discount tiers 2026-10-12 22:54:09 +02:00
pos-config.js feat(pos): enhance POS configuration to handle missing API key prompts gracefully and add slip preview functionality in settings 2026-09-04 11:55:38 +02:00
pos-search.js feat(pos): tiered catalog search — word order, plurals, glued SKUs 2026-10-06 23:36:12 +02:00
pos.html feat(pos): ex-VAT totals, ad-hoc custom lines, fixed discount tiers 2026-10-12 22:54:09 +02:00
receipt.html feat(pos): ex-VAT totals, ad-hoc custom lines, fixed discount tiers 2026-10-12 22:54:09 +02:00
settings.html feat(pos): add slip-layout.html block editor, move preview out of settings.html 2026-09-04 13:42:01 +02:00
slip-layout.html fix(pos): print the letterhead's own logo, and match delivery note to the sale slip 2026-09-06 23:03:24 +02:00
slip-layout.js fix(pos): print the letterhead's own logo, and match delivery note to the sale slip 2026-09-06 23:03:24 +02:00
sw.js feat(pos): refund a whole sale, put the goods back, print a credit slip 2026-10-07 23:41:09 +02:00
test-longpress.html feat(pos): enhance POS configuration to handle missing API key prompts gracefully and add slip preview functionality in settings 2026-09-04 11:55:38 +02:00
theme.js