POS Feature Plan · pos-v1.4
How the Setpoint POS will handle Thai VAT — calculating 7% on every sale, printing legally compliant tax invoices, and generating sequential invoice numbers per order.
What we're building and why
In Thailand, a receipt is a government-recognised 'Tax Invoice'. It must include the club's registered name, government business number, a unique sequential receipt number, and the VAT amount shown separately. We'll update every receipt the POS prints to include all of this.
Thailand's current VAT rate is 7%. It applies to court time, food and drinks, rackets, and memberships. The POS will automatically add 7% to every order at checkout. Staff don't need to think about it.
Thai law requires VAT on the combined total: items + service charge. So ฿100 food + ฿10 service = ฿110 base, then 7% VAT = ฿7.70. The POS will handle this automatically using the existing is_taxable flag.
A manager enters the club's registered business name, address, and government business number once in a new settings screen. All receipts are filled in correctly from that point on. Cashiers don't need to do anything differently.
Thai law requires every tax invoice to have a unique sequential number (INV-2026-000001, INV-2026-000002, etc.). Generated automatically for every order. Counter resets each new year. No two receipts ever share a number.
Occasionally a company booking a court needs a full corporate receipt for their accounting team. The cashier can enter the company's details before completing the sale. This is off by default — regular customers never see this prompt.
What applies at a sports club POS
In force until 30 Sep 2026
Exports only — not applicable locally
Raw unprocessed agriculture, healthcare — not sports clubs or cafés
Above this: mandatory registration + monthly filing
| Sale Type | Rate | Reason |
|---|---|---|
| Court rental (padel, tennis, pickleball) | 7% | Commercial venue — amateur sports exemption does not apply to businesses charging fees |
| Membership fees | 7% | Taxable commercial service |
| Prepared food & beverages (café) | 7% | Restaurant/café service is taxable — the food exemption only covers raw unprocessed agriculture |
| Sports equipment & accessories | 7% | Standard taxable goods |
| Service charge (10%) | Included in VAT base | Thai law: VAT applies to the combined subtotal + service charge |
| Tips / gratuity | Not applicable | Voluntary — not subject to VAT |
Key rule — VAT on service charge
What already exists — we build on top of it
| Component | How we use it |
|---|---|
| pos_taxes table | Already has name, rate, is_inclusive, apply_to_all, is_active. Will be used as-is — just create a 'VAT 7%' entry. |
| pos_order_taxes table | Already designed to record per-order tax breakdowns. Will be populated at checkout. |
| Tax CRUD UI (/items/taxes) | Already built. Staff can create, edit, and toggle taxes. No changes needed. |
| Service charge is_taxable flag | Already exists on pos_order_service_charges. Will use this to include taxable service charges in the VAT base. |
| tax_total_cents on orders | Column already exists on pos_orders. Will be populated correctly at checkout. |
What we're adding
Tax engine: computes VAT (inclusive or exclusive), returns per-tax line results for recording.
Stores club's 13-digit government business number, registered name, address, and branch — all required on Thai tax invoices.
Generates unique sequential invoice numbers (e.g. INV-2026-000042). Atomic — prevents duplicates.
Adds tax_invoice_number, tax_invoice_type, and buyer detail columns.
UI for the operator to enter their registration details once. All receipts use it automatically.
'Tax Invoice' header, seller business number, invoice number, VAT line — all legally required on Thai receipts.
6 phases · 3 new migrations
Wire the existing tax configuration into the checkout so VAT is computed and recorded on every order. The infrastructure is already in place — this phase activates it.
A settings page where the manager enters the club's government-registered details once. These appear on every receipt automatically.
Every order gets a unique sequential invoice number automatically at checkout. Resets each year. No duplicates.
Update receipts to display all legally required Thai tax invoice fields.
For the rare case where a company (not a regular customer) needs a full corporate invoice for their accounts department. Optional, off by default — regular customers never see this.
Load realistic test data and run automated tests to confirm everything works correctly before going live.
Needs answers from Nestor before build starts
| # | Question | Default |
|---|---|---|
| Q1 | Prices before or after VAT? Are menu prices shown before VAT (e.g. ฿100 + 7% = ฿107), or does the listed price already include VAT (฿107 shown, ฿7 extracted)? | Before VAT — 7% added at checkout |
| Q2 | Is the club VAT registered? Has it exceeded the 1.8M THB threshold and registered with the Revenue Department? | Assume yes — build for registered |
| Q3 | Service charge in VAT base? Thai law requires VAT on (items + service charge) combined — confirm the club follows this. | Yes — Thai law requires it |
| Q4 | Corporate receipts in v1? Should Phase 5 (company details at terminal) be included in the first release, or deferred? | Defer to v2 |
| Q5 | Invoice number format? Preferred format — e.g. INV-2026-000001 or something else? | INV-{YYYY}-{000000} |
| Q6 | Any VAT-free items? Are there items that should not have VAT, or is 7% universal across everything? | Universal 7% on all items |
| Q7 | Show VAT before payment? Should the cashier screen show the VAT breakdown before the customer pays? | Yes — show in checkout summary |
| Q8 | Historical orders? Past orders were recorded without VAT. Leave as-is or add a marker indicating they predate tax? | Leave as-is — no changes to history |