Rate breakdown
Rate breakdown
A US rooftop rate is not one number, it is a stack: Kansas City's 9.125% is the state's 6.5%, plus Wyandotte County's 1%, plus the city's 1.625%. Charging the combined figure is only half the obligation — the seller then remits to each authority separately, and a return needs to know how much of the tax belongs to whom.
That split is knowable only where the rate is stacked. Once the percentages are
summed the information is gone, and no consumer can recover it from the total.
So the source keeps it: a stacked rate carries its
RateComponents, and the engine turns them into a
TaxBreakdown on the assessment.
Reading a breakdown
$assessment = $tax->assess($query);
foreach ($assessment->breakdown?->lines ?? [] as $line) {
printf(
"%-16s %6s%% %s\n",
$line->label(), // "209", "ALAMEDA", or the level when unnamed
$line->percentage, // 1.625
$line->tax->getAmount()
);
}
state 6.5% 6.50
209 1% 1.00
36000 1.625% 1.63
TaxBreakdown also answers the two questions a filing asks:
$assessment->breakdown->total(); // === $assessment->tax
$assessment->breakdown->atLevel(JurisdictionLevel::State); // the state line(s)
The parts sum to the whole
total() equals the assessment's tax exactly, and that is a guarantee, not a
coincidence. The engine allocates the real total across the components rather than
applying each authority's rate to the net on its own.
The difference is not academic. On a $1.00 supply in Kansas City:
| State 6.5% | County 1% | City 1.625% | Sum | |
|---|---|---|---|---|
| Rate applied per authority | 0.07 | 0.01 | 0.02 | 0.10 |
| Allocated from the real total | 0.06 | 0.01 | 0.02 | 0.09 |
The tax actually charged was $0.09. Recomputing per authority invents a cent that was never collected, and a return built from those lines does not reconcile with the invoices behind it.
The remainder goes to the largest fractional shares (the Hamilton method), not to whichever authority the source happened to list first — list order is an implementation detail of the source, not a statement about who is owed the odd cent, and an authority levying 0% must never receive one.
When there is no breakdown
breakdown is null whenever the split is not known, and null means unknown.
A consumer filing per jurisdiction must treat it as missing data — never as "one
authority takes all of it".
It is null when:
- the rate source did not decompose the rate (a flat national rate, a static table, a point-in-polygon service returning one all-in figure);
- the supply was not taxed — reverse-charged, exempt, not-registered, zero-rated;
- a buyer exemption overrode the regime's verdict.
What a source emits
A TaxRateSource populates TaxRate::$components when — and only when — it knows
the split. The components must sum to the rate; a TaxRate whose components do
not reconcile throws RateComponentsDoNotReconcile at construction.
That is deliberately strict. A breakdown that is slightly wrong is worse than none at all: it has exactly the shape of an authoritative split, so it gets remitted on, and the shortfall surfaces at audit rather than at calculation. A source that cannot decompose a rate supplies no components.
new TaxRate('9.125', RateKind::Standard, 'cbox-tax', components: [
new RateComponent(JurisdictionLevel::State, '6.5'),
new RateComponent(JurisdictionLevel::County, '1', '209'),
new RateComponent(JurisdictionLevel::City, '1.625', '36000'),
]);
code and name are provenance, never invention — a source with no published
name for an authority leaves them null rather than deriving a plausible label.
What the shipped US source emits
RegisterRateSource decomposes by the
state's rate basis:
| Rate basis | Example | Components |
|---|---|---|
| Component | KS, NC | The state share plus one line per authority the boundary index matched |
| Combined | CA | Two lines: the state share, and the aggregate local remainder |
| — (state-only rate) | any state, no rooftop | None — the state share is the absence of a stack, not a stack of one |
| — (reduced category) | MO grocery | None — a product rule, not a stack of authorities |
A combined-basis record (California's CDTFA place rates) publishes one figure that
already contains the state share, so the per-authority split is not available in
the data. Subtracting the known state share still yields the state/local split that
drives remittance, so it is reported — but the remainder is levelled local, not
city, because it aggregates every district taxing that address. Attributing it to
the named city would put other authorities' money on the city's line.
Known limitation: one taxable base
Every line reports taxableAmount as the supply's full net, because the engine
applies a single taxable base across the stack. The one exception is a state that
taxes a share of the price (below): there every line reports that share of the net.
That is correct for the states modelled today, but it would not be for a state that exempts a category at the state level while its localities still tax it — Illinois and Missouri both do this for groceries. Supporting that needs a per-level taxability seam, which does not exist yet, so a per-level base is deliberately not claimed here.
A share of the price
Some states tax only part of the price. Texas taxes data processing, SaaS and
hosting at 6.25% on 80% of the price, and its cities, counties and transit
authorities tax the same 80%. Arizona taxes prime contracting on 65% of the gross.
The register publishes each state's own rate and, beside it, a taxable_base rule
on the supplied_item with a category, a proportion and (from schema 2.8)
localTaxes.
The resolved TaxRate keeps the statutory rate and carries the share separately:
$a = $calculator->assess($query); // Texas SaaS in Austin, $100.00 net
$a->rate->percentage; // 8.25 (6.25 state + 1 city + 1 transit)
$a->rate->taxableShare; // 80
$a->rate->effectivePercentage(); // 6.6, the figure the money is computed from
$a->tax; // USD 6.60
$a->breakdown->lines[0]->taxableAmount; // USD 80.00 on every line
The components still sum to percentage, and the tax is split across them in
proportion to their own rates. Tax-inclusive prices, invoice rounding and
per-authority returns all use the effective percentage.
The rule applies to its category and everything beneath it, with the nearest rung
winning. What localTaxes says decides what the share covers:
localTaxes |
Meaning | Engine |
|---|---|---|
same_share |
Every local tax in the state is computed on the same share. | The share applies to the whole stack. |
unstated |
The rule speaks for the state's own tax only. | Applied where the rate is the state's tax alone. With any local tax in the stack it throws UnresolvedTaxRule, because the local base is not published. |
| absent | A rule published before schema 2.8. | Not applied: the whole price is taxed, as before. |
A remote seller's elected figure (Texas' single local rate, a flat total) always
includes a local part, so it keeps a same_share share and refuses an unstated
one. A delivery charge on a line taxed on a share refuses, like any delivery of
partially exempt goods.