Architecture
Architecture
A TaxQuery carries the amount, whether it is net or gross, the buyer
jurisdiction (place of supply, resolved from laravel-geo), the customer type,
the product category, the selling entity's registrations, and an optional buyer
exemption.
DefaultTaxCalculator reads the place of supply's tax profile, selects the
TaxRegime keyed by its regimeModule, and delegates. The regime returns a
TaxAssessment: treatment, the net/tax/gross split, the place of supply, the rate
applied, a human-readable reason, and (only when a buyer certificate drove the
outcome) the applied exemption.
Exemption override
After the regime returns its verdict, the calculator applies any
TaxQuery::$exemption deny-by-default: a valid exemption that covers the taxed
jurisdiction rewrites a would-be Standard line to Exempt (net kept, tax 0),
and leaves every other treatment — reverse-charge, not-registered, zero-rated,
already-exempt — untouched. See exemptions.
Own the logic, source the data
- Logic (owned): place-of-supply, B2B/B2C reverse-charge determination, inclusive/exclusive handling, rate application and rounding, and the assessment itself all live in the engine.
- Data (sourced): the register supplies percentages, effective windows, category scopes, rule parameters and boundary artifacts. Contracts allow a host to replace these sources. The engine decides whether and how to apply them.
- Adapter logic: compiling artifacts, translating classifications and choosing
applicable records are executable code, even under the
Registernamespace. - Host inputs: amounts, seller registrations, product mappings, addresses, validated tax IDs, exemptions and supply dates describe the transaction.
Some reference facts still ship in PHP: StaticEuTerritories carries postal ranges
and regional rate substitutions; UsLocalStructure carries state lists;
CategoryMap maps public classes to register categories. They remain data or
maintained mappings by meaning, irrespective of their storage format. Moving the
main rate source to the register has not externalized every tax fact.
The data/engine validation matrix records the current boundary and evidence. Fixture tests check logic against controlled data; independent reference cases check the assembled system. Passing the latter does not isolate data correctness from correct interpretation and calculation.
Deny-by-default
- No regime modelled for a jurisdiction →
UnsupportedJurisdiction. - No rate available from the source →
UnresolvedTaxRate. - Conflicting/unsupported published rules or missing delivery facts →
UnresolvedTaxRule.
The rounding and delivery contracts keep these rules sourced while the engine applies them and reconciles the invoice. Remaining publication requests are recorded in cadastre feedback.
Neither ever degrades to a silent 0% — a wrong tax outcome is a real liability.