Skip to content

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.