This document complements docs/design/tax-engine.md and docs/design/tax-holidays.md. Every case shown here is exercised by an integration test in tests/QIIUB.Tests.Integration/Features/Tax/ — when QA finds a behavior question, find the matching case below first, then point at the test.
1TaxRate, TaxGroup, TaxGroupRate
QIIUB models tax as individual rates (a percentage with a code) bundled into named groups via a many-to-many junction. The same physical rate can appear in any number of groups without cloning.
TaxRate
Rate affects every group that references it (future sales only — historical sales are frozen via SaleTax.TaxRateValue snapshot).| Code | Name | Rate |
|---|---|---|
| ESTATAL | PR State IVU | 10.5% |
| MUNICIPAL | PR Municipal IVU | 1.0% |
| ITBIS | RD ITBIS Estándar | 18.0% |
| FL_STATE | FL Sales Tax | 6.0% |
TaxGroup (with junction)
TaxGroupRate.Priority. LocationProduct.TaxGroupId is authoritative — that's what the calculator reads at sale time.| Group name | Rates (Priority order) |
|---|---|
| PR IVU Normal | ESTATAL (0), MUNICIPAL (1) |
| Non-Taxable | — no rates — |
| RD ITBIS Estándar | ITBIS (0) |
| FL Sales Tax | FL_STATE (0) |
The "Non-Taxable" seed group
Every new merchant DB is provisioned with one seeded group: Non-Taxable (zero rates). It's universal across every jurisdiction — gift cards, Rx exemptions, certain services don't collect tax. Jurisdiction-specific groups (PR IVU Normal, FL Sales Tax) are merchant-configured.
Calculator pipeline
The calculator is a pure function. The endpoint pre-loads everything; the calculator returns the verdict per line.
The four classifications
| Verdict | SaleTax rows | TaxAmount | Discriminator at report time |
|---|---|---|---|
| Taxed | 1+ | > 0 | Standard sale |
| Exempt | 0 | — | Resolved group has no rates (Rx, gift card, services) |
| Waived (cashier) | 1+ if rated; 0 if rate-less | 0 | SaleLineItem.TaxOverrideReasonCodeId NOT NULL |
| Waived (cert) | 1+ if rated; 0 if rate-less | 0 | Sale.CustomerCertNumber NOT NULL |
| Mapped (holiday) | 1+ from ToTaxGroup, often 0 rates | per ToTaxGroup | SaleLineItem.TaxHolidayId NOT NULL |
| Mapped (B2B profile) | 1+ from mapped group | per mapped rates | TaxHolidayId NULL + customer has TaxProfile |
2PR IVU Standard
The default jurisdiction setup for a Puerto Rico merchant. Every product's LocationProduct.TaxGroupId points at this group.
TaxGroup: "PR IVU Normal"
| Priority | Rate Code | % | Compounding |
|---|---|---|---|
| 0 | ESTATAL | 10.5% | No |
| 1 | MUNICIPAL | 1.0% | No |
3Rx — intrinsically exempt
Pharmacy compounds and prescription drugs sell tax-free under PR law. The merchant creates a sub-typed exempt group (separate from generic Non-Taxable) so reports can distinguish "Rx exempt this month" from "gift cards exempt this month."
TaxGroup: "Exempt — Rx"
| No rates assigned. The calculator classifies any line in this group as Exempt. |
4B2B reseller profile (SC 2916)
A Hacienda Form SC 2916 reseller default-exempts only the 1% municipal IVU. The merchant models this as a CustomerTaxProfile that remaps lines whose default group is "PR IVU Normal" to a "Resale-State-Only" group containing only the 10.5% state rate.
CustomerTaxProfile: "Reseller PR SC 2916"
| Map: From | → | Map: To |
|---|---|---|
| PR IVU Normal (full 11.5%) | → | Resale-State-Only (10.5% state) |
| Catch-all (NULL) | → | Not used — non-resale items keep their group |
5Tax holiday — Carta Circular 25-10 (hurricane prep)
An admin pre-configures the holiday once. The calculator picks it up automatically during the window. No master-data mutation.
TaxHoliday: "HUR-PREP-2026"
| Code | HUR-PREP-2026 |
| Window (local AST) | 2026-05-22 00:00 → 2026-05-26 00:00 |
| Window (UTC stored) | [2026-05-22 04:00, 2026-05-26 04:00) — half-open |
| TimeZone | America/Puerto_Rico |
| Remap target | → Non-Taxable (zero rates) |
| IsActive | Active |
Scope
| Type | Target | MaxUnitPrice |
|---|---|---|
| Category | Generators (portátiles) | $3,000 |
| Category | Batteries (AA, AAA, C, D, 6V, 9V) | — no cap — |
| Category | LED Flashlights, Lanterns, Candles | — no cap — |
| Category | First Aid Kits, Fire Extinguishers | — no cap — |
Locations
Empty — applies to ALL stores (single-jurisdiction merchant).
6Walk-in customer — Taxed
The reference case. No customer, no holiday, no override — pipeline falls through to the default branch.
Inputs
Pipeline
San Juan, PR
7Back-to-school holiday — Mapped to Non-Taxable
Carta Circular 24-12. Holiday is active; product matches a scoped category. The calculator remaps to the holiday's destination group (Non-Taxable, zero rates) and stamps the snapshot.
Inputs
Pipeline
San Juan, PR
8Hurricane prep — generator at $3,000 cap (boundary)
Carta Circular 25-10 caps generators at $3,000 per unit. The cap is inclusive (≤). Per-unit, not per-line — a $1,500 generator × 2 still qualifies.
| Generator UnitPrice | Qty | Cap check | Verdict | Tax |
|---|---|---|---|---|
| $200 | 1 | 200 ≤ 3000 ✓ | Mapped | $0.00 |
| $3,000 | 1 | 3000 ≤ 3000 ✓ (inclusive) | Mapped | $0.00 |
| $3,001 | 1 | 3001 ≤ 3000 ✗ | Taxed | $345.12 (11.5%) |
| $1,500 | 2 | 1500 ≤ 3000 ✓ (UnitPrice, not LineExtended) | Mapped | $0.00 |
| $5,000 | 1 | 5000 ≤ 3000 ✗ | Taxed | $575.00 |
9B2B reseller buying in-scope item during a holiday
Holiday wins over the customer's profile. The reseller would normally pay 10.5% (state portion only, per their SC 2916 cert mapping). During an active back-to-school holiday on an in-scope item, the holiday's 0% applies because it's statutory and more favorable.
| Date | Item | Pipeline path | Result |
|---|---|---|---|
| Jul 18 (in window) | School uniform (in scope) | holiday → Non-Taxable, snapshot stamped | Mapped — $0.00 |
| Jul 18 (in window) | Cleaning supplies (out of scope) | profile → Resale-State-Only (10.5%) | Mapped — $5.25 (10.5%) |
| Jul 25 (post-window) | School uniform | profile → Resale-State-Only (10.5%) | Mapped — $5.25 (10.5%) |
SaleLineItem.TaxHolidayId NOT NULL; the profile-path line has it NULL but the customer has TaxProfileId. Same merchant, same customer, same product — the snapshot tells the report which path fired.
10Government cert customer — Waived
A government agency with a full-exemption cert. The cert wins over a holiday because it's a customer-level statutory mechanism. Snapshot lands on Sale.CustomerCertNumber, NOT on the line's TaxHolidayId.
Inputs
Pipeline
San Juan, PR
11Cashier override — Waived (god-mode)
Override is the highest-priority branch. It always wins, regardless of cert, holiday, or profile. Snapshot lives on SaleLineItem.TaxOverrideReasonCodeId + TaxOverrideNotes — a clear "explicit human intent" audit signal.
Inputs
Pipeline
San Juan, PR
Reason: MGR-OVERRIDE — Damaged box discount
12Pharmacy Rx — Exempt (intrinsically)
The default branch with a rate-less group. The calculator emits zero SaleTax rows and classifies as Exempt. Different from Waived — the line is exempt by its TaxGroup, not by a customer-level mechanism.
Inputs
Pipeline
San Juan, PR
13Multi-jurisdiction — same merchant, PR + FL stores
A merchant chain has stores in San Juan and Miami. Each PR holiday must NOT fire on Florida sales, and vice versa. The TaxHolidayLocation junction makes this explicit.
| Sale at | Holiday's Locations check | Verdict | Tax |
|---|---|---|---|
| San Juan, PR | contains SanJuanLocationId ✓ | Mapped | $0.00 (PR holiday applied) |
| Miami, FL | does NOT contain MiamiLocationId ✗ | Taxed | FL sales tax applied as normal |
14Return after the window — snapshot mirrors original
Customer returns a school uniform on Jul 25 — the back-to-school window ended Jul 19. The original sale was Mapped (0% tax via holiday). The refund must mirror.
| Step | What the engine reads | Outcome |
|---|---|---|
| Original Jul 18 sale | SaleTax: 0 rows · TaxHolidayCode = "BACK-TO-SCHOOL-2026-JUL" | $0 tax collected |
| Return Jul 25 | Original.SaleTax (frozen): 0 rows Original.TaxClassification = Mapped Original.TaxHolidayId NOT NULL | $0 tax refunded — mirrors original |
15Catalog churn — product added mid-window
Holiday active, scope = category "School Supplies". Merchant adds a new product P to the category at 14:00. Sale of P at 14:00:01.
| Time | System state | Result |
|---|---|---|
| Jul 18 13:59 | Product P does not exist | — |
| Jul 18 14:00:00 | Admin assigns P to "School Supplies" | ProductCategory row created |
| Jul 18 14:00:01 | Sale of P · resolver joins ProductCategory live | Mapped — 0% applied |
ProductCategory at sale time — it doesn't snapshot scope at holiday creation. Frozen snapshots would force the merchant to re-edit the holiday every time a SKU changes category, which is operationally hostile in a fast-moving catalog. The live join is one extra category lookup per line — negligible.
16How the snapshot fields support filterable reports
The combination of SaleLineItem.TaxClassification + the snapshot fields means every line falls into a filterable bucket. No need to reconstruct rules from the master records, even when those master records change later.
| Filter | Bucket meaning | Use case |
|---|---|---|
| TaxClassification = Mapped AND TaxHolidayId IS NOT NULL |
Holiday-waived sales — group by TaxHolidayCode for per-event totals |
Hacienda planilla supporting evidence; merchant ROI on the back-to-school promotion |
| TaxClassification = Waived AND TaxOverrideReasonCodeId IS NOT NULL |
Cashier-override-waived sales | Audit cashier-discretion overrides; flag stores with anomalous override frequency |
| TaxClassification = Waived AND CustomerCertNumber IS NOT NULL |
Government / charity cert sales — group by cert | Annual sales-by-cert report for cert customers' own filings |
| TaxClassification = Mapped AND TaxHolidayId IS NULL |
B2B-profile-mapped sales — group by customer's profile | Reseller-by-reseller breakdown for SC 2916 audit |
| TaxClassification = Exempt | Intrinsically-exempt items — group by resolved TaxGroupId (Rx vs gift card vs services) |
Rx sales reporting; subtype-by-subtype exemption breakdown |
| TaxClassification = Taxed | Standard taxable sales | The default report; no special filter needed |