# tax — tax / VAT rules, discounts & subscription proration

Tax, VAT and pricing-rules capability pack: US state sales tax and international VAT/GST/consumption tax from a built-in reference rate table (salesTax, vat, listRates), add tax net-to-gross...


A **pure** Leumas2 domain adapter pack (zero npm deps, Node built-ins only). It is the reusable
**rules engine** for consumption tax, discounts, and subscription proration. All money math runs
through cent-accurate rounding to avoid IEEE-754 float drift, and results are plain JSON.

## What it does

| Tool | Purpose |
|---|---|
| `salesTax` | US state sales tax (or explicit rate) net → gross, from the built-in table |
| `vat` | International VAT/GST; `inclusive:true` backs tax out of a gross amount |
| `addTax` | net → gross given a rate |
| `removeTax` | gross → net (back out the tax) |
| `compoundTax` | stack multiple rates — `sequential` (tax-on-tax) or `parallel` (state+county+city) |
| `discount` | apply ONE discount: `percent`, `fixed`, or `coupon` (with optional `min` floor) |
| `tieredPrice` | graduated bracket pricing (`graduated`) or single-tier flat (`flat`) |
| `volumeDiscount` | bulk per-unit discount by quantity `breaks` |
| `proration` | prorate a mid-cycle subscription change by remaining-period fraction (`charge`/`credit`) |
| `marginPrice` | cost-plus pricing — `margin` (% of price) or `markup` (% of cost) |
| `listRates` | dump the reference rate table (`us` / `intl` / `coupons` / `all`) or one `region` |

## Rate table

`data/rates.json` is a static reference table: US 50 states + DC (combined state-level base sales
tax), 30+ countries (standard national VAT/GST/consumption tax), and a small coupon catalog. Rates
are decimal fractions (`0.0725` = 7.25%). These are **indicative reference values for estimation**,
not tax advice — for filing, use an authoritative source. Pass an explicit `rate` to bypass the table.

Rates accept either form: a decimal fraction (`0.2`) or a percent (`20`) — any value `> 1` is read as
a percent.

## Usage

```js
import tax from '@leumas/adapter-tax'; // or the registry: registry.run('tax', 'salesTax', {...})

tax.adapters.salesTax({ amount: 100, region: 'CA' });
// → { net: 100, rate: 0.0725, tax: 7.25, gross: 107.25, region: 'US-CA', regionName: 'California', taxType: 'sales' }

tax.adapters.vat({ amount: 120, region: 'GB', inclusive: true });
// → { inclusive: true, net: 100, tax: 20, gross: 120, rate: 0.2, ... }

tax.adapters.compoundTax({ amount: 100, rates: [0.05, { rate: 0.07, label: 'PST' }], mode: 'sequential' });

tax.adapters.discount({ amount: 80, coupon: 'SAVE20' });
// → { original: 80, kind: 'coupon:percent', discount: 16, final: 64, ... }

tax.adapters.tieredPrice({ quantity: 250, tiers: [{ upTo: 100, price: 1 }, { upTo: 200, price: 0.8 }, { upTo: null, price: 0.5 }] });

tax.adapters.proration({ amount: 30, periodStart: '2026-01-01', periodEnd: '2026-02-01', changeDate: '2026-01-16', direction: 'charge' });

tax.adapters.marginPrice({ cost: 70, marginPercent: 30, mode: 'margin' }); // → price 100
```

Every tool takes ONE args object (maps 1:1 onto an HTTP POST body) and throws `TypeError`/`RangeError`
on bad input; unknown region/coupon lookups throw a `status: 404` error.

## DRY boundary (respect it)

- **finance** owns money-**over-time** math: compound interest, loan amortization, TVM / NPV / IRR.
- **invoice** owns billing **documents**: line items → a rendered invoice, invoice numbers, late fees.
- **tax** (this pack) owns tax **rules**, discount **rules**, and subscription **proration** — the
  numbers you compute *before* they land in an invoice line or a finance model.

Where they touch (an invoice line carries a `taxRate`/`discount`), invoice does its own inline math;
this pack is the standalone engine you call to *derive* tax and discount figures — and a natural
companion to **PassNode** for prorating plan/seat changes mid-cycle.


---
Source: shared/engines/adapters/domain/tax/README.md
Canonical: https://docs.leumas.tech/p/adapters/domain/tax
