Skip to content

Domain Model

All schemas are owned by @nx/core. Taxation only subclasses repositories. Source roots: packages/core/src/models/schemas/tax/ and .../pricing/.

1. Full ERD

2. Entities

TaxGroup

PropertyValue
Tabletax.TaxGroup
Sourcepackages/core/src/models/schemas/tax/tax-group/schema.ts
Soft-deleteyes
Owner ID columnmerchantId (nullable — null = system template)

Fields:

FieldTypeRequiredDefaultDescription
idtextSnowflakePrimary key
identifiertextSnowflakeUnique stable code (e.g. VN_DEDUCTION_VAT10)
namejsonbi18n { default, en, vi }
descriptionjsonbi18n
taxMethodtext000_DIRECT / 100_DEDUCTION
statustextACTIVATEDSee enum below
merchantIdtextOwner merchant; null = system

Status enum: ACTIVATED · DEACTIVATED · ARCHIVED (Statuses).

Indexes: UQ_TaxGroup_identifier, IDX_TaxGroup_merchantId, IDX_TaxGroup_merchantId_status.

Relations: merchant (M:1 → Merchant.id), items (1:M → TaxGroupItem, relationName taxGroup).

TaxGroupItem

PropertyValue
Tabletax.TaxGroupItem
Sourcepackages/core/src/models/schemas/tax/tax-group-item/schema.ts
Soft-deleteyes
Owner ID column— (owned via parent taxGroupId)

Fields:

FieldTypeRequiredDefaultDescription
idtextSnowflakePrimary key
taxGroupIdtextParent group
discriminationTypeIdtextClassifies the tax (VAT/PIT/...) via DiscriminationType
typetext100_PERCENTAGETaxMode
valuedecimal(15,4)0Rate or amount
isInclusivebooleanfalseTax embedded in price
priorityinteger0Application order (ascending)
usagetext000_SALE000_SALE / 100_PURCHASE
chargeTargettext000_CUSTOMER000_CUSTOMER / 100_MERCHANT

Indexes: IDX_TaxGroupItem_discriminationTypeId, IDX_TaxGroupItem_taxGroupId.

Relations: taxGroup (M:1 → TaxGroup.id), discriminationType (M:1 → DiscriminationType.id).

TaxSet

PropertyValue
Tablepricing.TaxSet
Sourcepackages/core/src/models/schemas/pricing/tax-set/schema.ts
Soft-deleteyes
Owner ID column— (polymorphic principalId/principalType)

Fields:

FieldTypeRequiredDefaultDescription
idtextSnowflakePrimary key
principalIdtextProduct or ProductVariant id
principalTypetextProduct (default) / ProductVariant (override)
namejsonbi18n (copied from TaxGroup)
statustextACTIVATEDSee enum below
sourceTypetextTaxGroup (auto) / null (manual)
sourceIdtextTaxGroup.id when auto-provisioned

Status enum: ACTIVATED · DEACTIVATED · ARCHIVED.

Indexes: partial unique UPI_TaxSet_principalId_principalType_status (where deletedAt IS NULL), IDX_TaxSet_sourceId, IDX_TaxSet_sourceType_sourceId, IDX_TaxSet_status.

Tax

PropertyValue
Tablepricing.Tax
Sourcepackages/core/src/models/schemas/pricing/tax/schema.ts
Soft-deleteyes
Owner ID column— (owned via taxSetId)

Fields (provisioning-relevant):

FieldTypeRequiredDefaultDescription
idtextSnowflakePrimary key
namejsonbi18n (from DiscriminationType / group)
typetext100_PERCENTAGETaxMode
valuedecimal(15,4)Rate or amount
effectiveFromtimestamptzprovision timeWhen tax becomes active
effectiveTotimestamptzExpiry (null = open)
priorityinteger0Application order
isInclusivebooleanfalseEmbedded in price
shouldApplyOnDiscountedbooleantrueApply on discounted base
isCompoundbooleantrueCompounds on prior taxes
usagetext000_SALESale/purchase
statustextACTIVATED
chargeTargettext000_CUSTOMERCustomer/merchant
taxSetIdtextParent set
taxTypeIdtextLegacy FK to TaxType (dual-write, nullable)
discriminationTypeIdtextClassification (authoritative)

Indexes: IDX_Tax_discriminationTypeId, IDX_Tax_status, IDX_Tax_taxSetId, IDX_Tax_taxTypeId.

TaxType

PropertyValue
Tablepricing.TaxType
Sourcepackages/core/src/models/schemas/pricing/tax-type/schema.ts
Soft-deleteyes
Owner ID columnmerchantId (nullable)

type enum (FixedTaxTypes): 000_VAT, 100_EXCISE, 200_ENVIRONMENTAL, 300_LUXURY, 400_PIT, 999_CUSTOM. chargeTarget: 000_CUSTOMER / 100_MERCHANT. Unique (type, merchantId). Legacy classification — DiscriminationType (scope tax_classification) is now authoritative; Tax.taxTypeId is kept as a dual-write fallback (ADR-0002).

VN reference tables (read-only)

TableKey columnsFK
tax.VnAdministrativeUnitcodeName (UK), fullName, shortName (+ *_en)
tax.VnProvincecode (UK), name, fullName, administrativeUnitCodeVnAdministrativeUnit.codeName
tax.VnWardcode (UK), name, provinceCode, administrativeUnitCodeVnProvince.code, VnAdministrativeUnit.codeName

Served read-only; not seeded by this package's migrations (loaded externally).

3. Cross-entity Invariants

InvariantEnforcement
At most one ACTIVATED TaxSet per (principalId, principalType)Partial unique index UPI_TaxSet_principalId_principalType_status (where not deleted)
Provision is idempotent for the same source groupTaxProvisioningService skips when active TaxSet has sourceType=TaxGroup and matching sourceId
Only TaxGroup-sourced TaxSets are deactivated on (de)provision_deactivateExistingTaxSet guards on sourceType === TaxGroup
Merchant taxMethod must match TaxGroup.taxMethodTaxGroupService.validateTaxGroupForMerchant (DIRECT ≠ DEDUCTION)
Each provisioned Tax mirrors one TaxGroupItemprovisionForProduct loops group items

4. Soft-delete Behavior

BehaviorDetail
Read defaultdeletedAt IS NULL (model defaultFilter)
DeprovisionStatus flip to DEACTIVATEDnot a soft-delete (rows accumulate)
Hard-deleteNever by default
RestoreRe-provisioning creates a fresh ACTIVATED TaxSet; prior DEACTIVATED rows remain

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