PRD: Fare & Tax Pricing Engine
| Module | Pricing | PRD ID | PRD-FARE-001 |
| Status | Ready to dev | FEAT | FARE |
| Epic | — | Plane | BANA-1513 |
| Date | 2026-06-05 | Version | v1.0 |
| Packages | @nx/pricing | URD | FARE |
| Surface | BackendClient · Owner/Mgr | ||
| Owner | Phát Nguyễn | ||
What the Fare & Tax Pricing Engine is
Every product variant has a fare set - the container for its base fare and any conditional child fares; the system auto-creates a fare set + base fare the moment a variant appears, so every variant is priceable right away. An owner sets a parent fare under one of two strategies - OVERRIDE (always wins) or SALE (competes as a discount) - then adds child fares carrying their own price and eligibility rules (quantity, time window, and so on). The engine picks the winning fare by resolving the highest-priority tier first; within that tier an OVERRIDE fare always beats a SALE fare, and among valid fares of the same kind the lowest price wins; with no valid child fare it falls back to the base price.
Alongside that, an owner attaches percentage, fixed, or per-unit taxes to a variant's tax set, chooses inclusive (baked into the price) or exclusive (added on top), and taxes can compound on top of earlier ones in priority order. At checkout the sale flow calls one pricing simulation; the result is an immutable pricing snapshot - the applied fare and taxes per line plus order totals - so pricing is consistent and auditable every time. A line with no tax configuration still prices fine, just at zero tax - it is never blocked by missing configuration.
Why one engine is needed
Prices used to be hard-coded on the product or computed ad hoc at checkout - no consistent home for taxes, rule-based prices, or an auditable breakdown. Merchants need one engine that selects the right fare per variant, computes taxes correctly (inclusive/exclusive, compound, item-level), and returns one consistent priced result the sale flow consumes at checkout without re-implementing pricing logic. That is the foundation for reliable cost, tax, and margin reporting in the HKD/SME bookkeeping BANA targets.
The engine builds fare selection and tax computation on top of the product-variant catalog, confines every record to the user's own merchant, and exposes a pricing simulation the sale flow consumes.
One example, start to finish
Variant "White T-shirt" has a base price of 100,000 VND. The owner adds a SALE parent fare with a child fare priced at 80,000 VND, carrying the rule "quantity purchased is 10 or more". This variant's tax set attaches a 10% VAT, exclusive.
| Step | What happens | Result |
|---|---|---|
| A customer buys 12 shirts | The engine evaluates the quantity rule: 12 ≥ 10, the child fare is valid | The 80,000 VND child fare wins, not the 100,000 VND base price |
| Tax is computed on the selected unit price | 10% exclusive VAT, added on top | Tax per shirt = 80,000 x 10% = 8,000 VND |
| The pricing simulation returns the line snapshot | 12 shirts, unit price 80,000 VND | subtotal 960,000 VND, tax 96,000 VND, total 1,056,000 VND |
| A customer buys 5 shirts (a separate order) | The quantity rule fails (5 < 10), the child fare is eliminated | The engine falls back to the base price of 100,000 VND per shirt, with no error |
The easiest thing to get wrong
The base price competes at its own priority tier too
The base price doesn't automatically lose to a child fare sitting in the same priority tier. The base price defaults to priority 0; a child fare also set at priority 0 has to compete on price fairly against the base price, like any other pair - having a child fare present doesn't automatically make it win.
Conversely, when a valid OVERRIDE fare is present in the tier that is currently winning, it wins immediately, with no price comparison against anyone else in that tier - not even a cheaper SALE fare.