feat(plugins): bundled era-based tax rate tables (CH, EU, UK, US) + update-tax-rates skill - #727
Open
pozylon wants to merge 7 commits into
Open
feat(plugins): bundled era-based tax rate tables (CH, EU, UK, US) + update-tax-rates skill#727pozylon wants to merge 7 commits into
pozylon wants to merge 7 commits into
Conversation
- eraRates: compile/sort bundled {validFrom, rate} eras and resolve the rate
applicable at a reference date (clamping to the earliest known era)
- applyTaxRateToTaxableRows: the common tax math of all regional adapters
(gross rows get tax extracted via offset+tax rows, net rows additive)
- resolveDeliveryLocation: delivery-address > billing-address > order-country
resolution incl. regionCode; isDeliveryAddressInCountry delegates to it
…able Rates move from hardcoded conditionals into ch-tax-rates.json (bundled and emitted to lib/ by tsc), with the full era history back to 2001 and ESTV source/verification metadata. The public SwissTaxCategories API is unchanged; both Swiss adapters now share applyTaxRateToTaxableRows. Behavior fix: 2011-2017 orders were priced with the 2018-2023 rates (7.7%/2.5%/3.7%); the era table now resolves the legally correct 8.0%/2.5%/3.8% for those dates.
…ry era tables eu-tax-rates.json bundles standard/reduced/reduced2/super_reduced/parking era histories for all 27 member states, verified against the European Commission's published rates (EC annex history + recent changes: DE 2020 temporary cut, LU 2023, CZ/EE 2024, SK/RO/EE 2025, FI/LT 2026). The product-eu-tax/delivery-eu-tax adapters mirror the Swiss pair: the delivery country picks the table, a product's eu-tax-category:<name> tag or the provider's eu-tax-category configuration picks the category, defaulting to the standard rate (unknown categories fall back to standard, never to zero). Greece resolves under both GR (ISO) and EL (EU convention). Cross-border regimes (OSS, reverse charge) stay out of scope.
uk-tax-rates.json bundles the standard (17.5 → 15 → 17.5 → 20 incl. the 2008-2010 crisis cut), reduced (8 → 5) and zero categories, verified against gov.uk / the House of Commons Library rate history. The product-uk-tax/delivery-uk-tax adapters mirror the Swiss pair for the UK VAT area (GB + Isle of Man); products pick categories via uk-tax-category:<name> tags, providers via a uk-tax-category configuration entry, default standard.
…rates us-tax-rates.json bundles the statewide base rate for the 50 states + DC (incl. mandatory statewide local components where quoted that way, era history for recent changes: LA 2025, NM 2022/23, SD temporary cut, DC's enacted 2026-10-01 increase). Documented approximation: no county/city add-ons, category taxability, nexus or sourcing rules — full US compliance needs a tax service. The product-us-sales-tax/delivery-us-sales-tax adapters resolve the state from the delivery address regionCode; unknown states apply no tax (logged) rather than a made-up fallback. Products opt out via us-tax-category:exempt tags, delivery providers via a us-tax-category=exempt configuration entry. Net price rows get tax added on top (US convention), gross rows extracted.
Maintainer workflow for all four bundled tables (CH/EU/UK/US): fetch the official sources, cross-check every change against a second source, append-only era updates, never guess a rate, update tests, and present the diff with quoted sources for human review — rates change as reviewed, versioned diffs instead of runtime fetches.
pozylon
force-pushed
the
feat/swiss-tax-rates-json
branch
from
August 20, 2026 10:01
3f6d67b to
5ecd2ad
Compare
One page per adapter following the Swiss tax doc template (installation, category/tag/provider configuration, net-vs-gross handling, adapter details), a prominent statewide-approximation warning for the US pair, the pricing plugin index extended, and the Swiss pages updated for the era-based JSON tables incl. the corrected 2011-2017 and 2001-2010 rate columns.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Moves tax rates out of code into bundled, era-based JSON tables — versioned, reviewable, and historically complete — and adds aligned tax plugins for the EU, the UK and the US next to the existing Swiss pair, plus an
update-tax-ratesmaintainer skill that verifies/updates all bundled tables against official sources.Targets v4.8.x. Rates are deliberately not fetched at runtime: tax rates are legally binding, so every change must be a reviewable diff, and the applicable rate follows the supply date — which requires the full era history, not "the current rate".
Commits (atomic per plugin)
eraRates(compile/sort{validFrom, rate}eras, resolve by reference date, clamp before first era),applyTaxRateToTaxableRows(the common adapter tax math: gross rows get tax extracted via offset+tax rows, net rows additive),resolveDeliveryLocation(delivery-address → billing-address → order-country, incl.regionCode;isDeliveryAddressInCountrynow delegates).SwissTaxCategoriesreadsch-tax-rates.json(emitted tolib/by tsc via ESM JSON import attributes). Public API unchanged. Behavior fix: 2011–2017 orders were priced at the 2018–2023 rates (7.7/2.5/3.7%); the table resolves the legally correct 8.0/2.5/3.8%. History back to 2001, earlier dates clamp.eu-tax-rates.jsonwith per-country era histories for all 27 member states (verified against the EC's published rates; incl. DE 2020 temporary cut, LU 2023, CZ/EE 2024, SK/RO/EE 2025, FI/LT 2026). Destination country picks the table;eu-tax-category:<name>product tag or provider config picks the category, defaulting to standard (unknown categories fall back to standard, never zero). Greece resolves as GR and EL. OSS/reverse-charge out of scope.regionCode; unknown state applies no tax (logged) instead of a made-up fallback;us-tax-category:exempttag / provider config opt-outs. Documented approximation: no county/city add-ons, category taxability, nexus or sourcing — full US compliance needs a tax service..claude/skills/update-tax-rates: per-region official sources (ESTV/Fedlex, EC TEDB, gov.uk, state DORs), cross-check rule, append-only eras, never guess, tests updated with each change, human review before commit.Verification
@unchainedshop/plugins: 136/136 unit tests pass, including new suites for era histories, exact effective-date boundary switches, tag/provider category resolution, and per-file JSON-consistency checks (ascending eras, plausible rates, source/verification metadata)tsc -bclean; all four JSON tables emitted tolib/and loadable from the compiled ESM outputAll rate data was researched with per-country/state source citations; figures that could not be verified against an official source were excluded rather than guessed. Effective dates for long-standing US rates use the source table's as-of anchor date (documented in the research; single-era states resolve identically for all past dates via era clamping).
Related: #726