Skip to content

Domain Model

Pricing owns no schema. All tables below are defined in @nx/core under packages/core/src/models/schemas/pricing/, in the PostgreSQL pricing schema. Pricing re-exports them as repositories.

1. Full ERD

2. Entities

One block per table. Schema source: packages/core/src/models/schemas/pricing/.

FareSet

PropertyValue
Tablepricing.FareSet
Sourcecore/src/models/schemas/pricing/fare-set/schema.ts
Soft-deleteyes
Owner ID columnproductVariantId (soft ref, no FK)

Fields: id, name (i18n), description (i18n), status (default DEACTIVATED), productVariantId (not null), common columns.

Indexes: IDX on productVariantId, status.

Status enumFareSetStatuses: ACTIVATED, DEACTIVATED. One ACTIVATED set per variant is the selection invariant.

Fare

PropertyValue
Tablepricing.Fare
Sourcecore/src/models/schemas/pricing/fare/schema.ts
Soft-deleteyes

Fields:

FieldTypeRequiredDefaultDescription
idtextSnowflakePK
name / descriptioni18n jsonb{ default, en, vi }
statustextACTIVATEDFareStatuses
typetextnullFareTypes.SALE / OVERRIDE / null (child)
effectiveFrom / effectiveTotimestamptzEffective window
minQuantity / maxQuantitydecimal(15,4)Quantity gate
amountdecimal(15,4)Price (null for OVERRIDE parent group)
rulesCount / childrenCountintegerDenormalized counters
fareSetIdtextOwning FareSet
parentIdtextnullParent fare (group)

Indexes: IDX on fareSetId, parentId, status.

Relations: fareSet (M:1), parent (self M:1), children (1:M), fareRules (1:M via polymorphic Rule).

Status enumFareStatuses: ACTIVATED, DEACTIVATED, ARCHIVED.

Rule

PropertyValue
Tablepricing.Rule
Sourcecore/src/models/schemas/pricing/rule/schema.ts
Soft-deleteyes

Fields: polymorphic principalType / principalId, data-type value columns (tValue/nValue/bValue/jValue/boValue + dataType), attribute (not null), priority (not null), operator (not null).

ConceptValues
principalType (RulePrincipalTypes)Fare, Promotion, PromotionMethod
operator (RuleOperators)EQ, NE, NEQ, GT, GTE, LT, LTE, IN, INQ, NIN, CONTAINS
context (RuleContexts, in metadata)source (buy rule), target (discount rule)

CONTAINS is the inverse of IN — context value is an array, rule value scalar. Used by the FBT override (orderProductVariantIds CONTAINS leadVariantId).

TaxSet

PropertyValue
Tablepricing.TaxSet
Sourcecore/src/models/schemas/pricing/tax-set/schema.ts
Soft-deleteyes

Fields: polymorphic principalType / principalId, name (i18n), status (default ACTIVATED), sourceType, sourceId.

Indexes: unique-partial on (principalId, principalType, status) where deletedAt IS NULL; IDX on sourceId, (sourceType, sourceId), status.

principalType='ProductVariant' → item-level taxes; principalType='Merchant' → order-level taxes.

Tax

FieldTypeRequiredDefaultDescription
namei18n jsonb
typetext100_PERCENTAGETaxModes: AMOUNT / PERCENTAGE / PER_UNIT_AMOUNT
valuedecimal(15,4)Rate or fixed amount
effectiveFromtimestamptz
effectiveTotimestamptz
priorityinteger0Ascending; same priority share a base
isInclusivebooleanfalseEmbedded in displayed price (back-calc)
shouldApplyOnDiscountedbooleantrueDiscount-aware basis
isCompoundbooleantrueCompounds on prior taxes
minQuantity / maxQuantitydecimal(15,4)Quantity condition
usagetext000_SALETaxUsages.SALE / PURCHASE
chargeTargettext000_CUSTOMERCUSTOMER (on invoice) / MERCHANT (PIT)
taxSetIdtextOwning TaxSet
taxTypeIdtextClassification
discriminationTypeIdtextVN tax classification ref

Indexes: IDX on discriminationTypeId, status, taxSetId, taxTypeId.

TaxType

PropertyValue
Tablepricing.TaxType
Sourcecore/src/models/schemas/pricing/tax-type/schema.ts

Fields: type (FixedTaxTypes: VAT, EXCISE, ENVIRONMENTAL, LUXURY, PIT, CUSTOM), name/description (i18n), chargeTarget, status, merchantId (nullable → system type).

Indexes: unique on (type, merchantId); IDX on merchantId, (merchantId, status).

Cost

PropertyValue
Tablepricing.Cost
Sourcecore/src/models/schemas/pricing/cost/schema.ts

Fields: polymorphic principalType / principalId (getCurrentCost/getEffectiveCost hardcode ProductVariant), user-audit columns, amount (not null), note, effectiveFrom (not null), effectiveTo (nullable = current/open-ended).

Only one open-ended (null effectiveTo) cost per variant. Not yet wired into either calculator.

Promotion

FieldTypeRequiredDefaultDescription
codetextnullNull = auto-apply only
enableStackingbooleantrue
isAutomaticbooleanfalse
isTaxInclusivebooleanfalse
namei18n
effectiveFrom / effectiveTotimestamptz
typetextSTANDARDPromotionTypes
statustextDRAFTPromotionStatuses
usageLimit / usageCountintegerusageCount=0
rulesCountinteger0Denormalized
merchantIdtext

Indexes: unique-partial on code where deletedAt IS NULL; IDX on merchantId, (merchantId, status).

PromotionMethod

Fields: promotionId (not null), type (PromotionMethodType), value (not null), targetType (not null), allocation, currency (default VND), exchangeRate (default 1), maxQuantity, sourceRulesCount/targetRulesCount, BuyGet fields (buyGetTargetQuantity, buyGetSourceMinQuantity).

Indexes: unique-partial on promotionId where deletedAt IS NULL (one method per promotion).

3. Cross-entity Invariants

InvariantEnforcement
Exactly one ACTIVATED FareSet per productVariantIdWorker idempotency + FareSetStatuses.canDeactivate() always false
OVERRIDE fare wins over default; else lowest-priced farePricingFareCalculatorService / FareCalculatorService selection
Child fares evaluated only when their AND-rules all passPricingRuleEvaluatorService (lodash get() attribute path)
Taxes within the same priority share a base; higher priorities compoundPricingTaxCalculatorService / TaxCalculatorService priority grouping
Inclusive tax back-calculated out of displayed pricetax calculator isInclusive branch
One open-ended Cost (effectiveTo IS NULL) per variantCostService.updateCurrentCost closes prior before opening new
One PromotionMethod per Promotionunique-partial index on promotionId
FBT override seeded at most once per (related FareSet, lead variant)PricingWorkerService._isFbtOverrideAlreadySeeded

4. Soft-delete Behavior

BehaviorDetail
Read defaultdeletedAt IS NULL (model defaultFilter)
Hard-deleteNot used by default
Unique constraintsPartial — scoped WHERE deletedAt IS NULL (FareSet, TaxSet, Promotion code, PromotionMethod) so a soft-deleted row frees the slot

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.