Skip to content

CAP-COM-CAT-004Calculate tax from buyer geography

Markdown

CAP-COM-CAT-004

Calculate tax from buyer geography

Markdown
  • DocumentDraft
  • Product intentProposed
  • ImplementationInconsistent

Planning

Feature
FEAT-COM-CAT-01
Key concepts
CON-COM-CAT-001
Actors
ACT-COM-001
Related
CAP-COM-CAT-013
Open questions
OQ-COM-CAT-002
Proved by
ATS-COM-CAT-004

KindCapabilityProduct areaCommerceModuleCatalog and Selling ConfigurationRevision1

Read atservices-commerce c8b66a98

The Formal Contract

The narrative above explains; the sections below are authoritative — the rules, scenarios and tests this document binds.

Outcomes
Each taxable line is taxed by the most specific zone the purchaser’s address falls in.A purchase that requires an address to be taxed is prevented from being placed without one.
Surfaces
api
Intent sources
services-commerce:docs/milestones/plans/COMM-realignment-2026-10-22.md (proposed, awaiting Andrew)

Controls

19 controls apply, from the corpus control catalogue.

TC-AZ-001 TC-AZ-002 TC-AZ-003 TC-AZ-004 TC-AZ-005 TC-AZ-006 TC-TEN-001 TC-TEN-002 TC-TEN-003 TC-CRY-001 TC-DAT-001 TC-DAT-002 TC-DAT-003 TC-LOG-004 TC-LOG-005 TC-PI-001 TC-PI-003 TC-PRV-001 TC-PRV-002

Permissions

KeyChecked atScope
commerce.tax-zones.readapitenant
commerce.tax-zones.createapitenant
commerce.tax-zones.updateapitenant
commerce.tax-zones.deleteapitenant
commerce.tax-zones.bulkapitenant

Business Rules

10 rules, 8 to be proved before approval. A rule's product intent is derived from its origin: 0 confirmed by a product decision, the rest inheriting this capability's proposed.

IdRuleKindEnforced atProvenance
01must prove
Proposed
A tax zone uses an assigned two-letter country code, optionally narrowed by region and postal prefix; invalid country codes are refused on the country field.
no proof recorded yet
validationapi, databasetest
services-commerce:modules/configuration/tests/e2e/tax-zones.e2e.spec.js#answers 400 to a country code that is not two upper-case letters, naming the field
02must prove
Proposed
A tax rate requires a name, an integer number of basis points within the supported ceiling, and dates whose start does not exceed their end.
no proof recorded yet
validationapi, databasetest
services-commerce:modules/configuration/tests/e2e/tax-zones.e2e.spec.js#answers 400 to a rate past the ceiling and to a rate without a name, naming the field
03must prove
Proposed
Tax edits reject whitespace-only optional values and clear a field such as postal prefix only when an explicit null is supplied.
no proof recorded yet
validationapitest
services-commerce:modules/configuration/tests/unit/api/blank-tax-zone-values.spec.js#refuses a whitespace-only postal_code_prefix on a replacement 400 naming the field, instead of clearing H2X
04must prove
Proposed
A rate with no product-type selection covers all types; a selected list limits coverage to those types and cannot exceed one hundred entries.
no proof recorded yet
calculationservice, apitest
services-commerce:modules/pricing/tests/unit/tax/zone-tax-calculation.spec.js#applies to every product type when it names none, and to the named ones alone otherwise
05must prove
Proposed
Tax determination selects the most specific matching active zone, preferring postal prefix over region over country and retaining list order for equal specificity.
no proof recorded yet
calculationservicetest
06must prove
Proposed
Line tax sums separately calculated rates, each rounded to a whole minor currency unit with exact halves moving away from zero.
origin: COMM-M1 D5 (docs/milestones/COMM-M1-set-up-what-you-sell.md)
no proof recorded yet
calculationservicetest
services-commerce:modules/pricing/tests/unit/tax/zone-tax-calculation.spec.js#sums every applicable rate per line, each rounded on its own, and names each component
07must prove
Proposed
Potentially taxable checkout lines require a tax address; an address-free display quote instead reports zero tax and a warning.
no proof recorded yet
policyservicetest
services-commerce:modules/pricing/tests/unit/tax/zone-table-tax.spec.js#refuses a line a zone’s rate for every product type could tax, naming the line and its product
08
Proposed
Commerce uses workspace tax zones unless configured to delegate tax to Accounting, and rejects quotes configured with an unknown provider.
policyservicetest
services-commerce:modules/pricing/tests/unit/tax/commerce-tax-port.spec.js#is zone-table when the variable is unset or blank
09
Proposed
Replacing a tax zone replaces its rates at the read version, and removing the zone removes its rates.
invariantservice, databasetest
services-commerce:modules/configuration/tests/e2e/tax-zones.e2e.spec.js#deletes a zone with its rates, after which it reads 404; deleting it again is 404
10must prove
Proposed
Tax-zone inspection requires read permission, while creation, replacement and removal require administrator write permissions.
no proof recorded yet
permissionapitest

Scenarios

IdGivenWhenThenProvenance
01Canada and a more specific Quebec H2X zone are configuredCalculate for a Quebec address beginning H2XThe prefix-specific zone supplies the tax rather than the country-only zonetest
services-commerce:modules/pricing/tests/unit/tax/zone-tax-calculation.spec.js#matches the most specific zone the address satisfies

The Markdown files under specs/ in rhythmsoftware/rhythm-v2-docs are the canonical source of truth. This reader is a generated, read-only projection of them.