Domain Model
Pricing không sở hữu schema nào. Mọi bảng dưới đây được định nghĩa trong
@nx/coredướipackages/core/src/models/schemas/pricing/, trong schema PostgreSQLpricing. Pricing re-export chúng như repository.
1. ERD đầy đủ
2. Entities
Một khối mỗi bảng. Nguồn schema:
packages/core/src/models/schemas/pricing/.
FareSet
| Thuộc tính | Giá trị |
|---|---|
| Table | pricing.FareSet |
| Source | core/src/models/schemas/pricing/fare-set/schema.ts |
| Soft-delete | có |
| Owner ID column | productVariantId (soft ref, không FK) |
Trường: id, name (i18n), description (i18n), status (mặc định DEACTIVATED), productVariantId (not null), cột chung.
Index: IDX trên productVariantId, status.
Enum status — FareSetStatuses: ACTIVATED, DEACTIVATED. Một set ACTIVATED mỗi variant là bất biến chọn lựa.
Fare
| Thuộc tính | Giá trị |
|---|---|
| Table | pricing.Fare |
| Source | core/src/models/schemas/pricing/fare/schema.ts |
| Soft-delete | có |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | PK |
name / description | i18n jsonb | — | { default, en, vi } | |
status | text | ✓ | ACTIVATED | FareStatuses |
type | text | null | FareTypes.SALE / OVERRIDE / null (child) | |
effectiveFrom / effectiveTo | timestamptz | — | Cửa sổ hiệu lực | |
minQuantity / maxQuantity | decimal(15,4) | — | Cổng số lượng | |
amount | decimal(15,4) | — | Giá (null cho nhóm OVERRIDE parent) | |
rulesCount / childrenCount | integer | — | Counter denormalize | |
fareSetId | text | ✓ | — | FareSet sở hữu |
parentId | text | null | Fare cha (nhóm) |
Index: IDX trên fareSetId, parentId, status.
Relations: fareSet (M:1), parent (self M:1), children (1:M), fareRules (1:M qua Rule đa hình).
Enum status — FareStatuses: ACTIVATED, DEACTIVATED, ARCHIVED.
Rule
| Thuộc tính | Giá trị |
|---|---|
| Table | pricing.Rule |
| Source | core/src/models/schemas/pricing/rule/schema.ts |
| Soft-delete | có |
Trường: principalType / principalId đa hình, cột giá trị theo data-type (tValue/nValue/bValue/jValue/boValue + dataType), attribute (not null), priority (not null), operator (not null).
| Khái niệm | Giá trị |
|---|---|
principalType (RulePrincipalTypes) | Fare, Promotion, PromotionMethod |
operator (RuleOperators) | EQ, NE, NEQ, GT, GTE, LT, LTE, IN, INQ, NIN, CONTAINS |
context (RuleContexts, trong metadata) | source (rule mua), target (rule giảm giá) |
CONTAINSlà nghịch đảo củaIN— giá trị context là mảng, giá trị rule là scalar. Dùng bởi FBT override (orderProductVariantIds CONTAINS leadVariantId).
TaxSet
| Thuộc tính | Giá trị |
|---|---|
| Table | pricing.TaxSet |
| Source | core/src/models/schemas/pricing/tax-set/schema.ts |
| Soft-delete | có |
Trường: principalType / principalId đa hình, name (i18n), status (mặc định ACTIVATED), sourceType, sourceId.
Index: unique-partial trên (principalId, principalType, status) where deletedAt IS NULL; IDX trên sourceId, (sourceType, sourceId), status.
principalType='ProductVariant'→ thuế cấp item;principalType='Merchant'→ thuế cấp order.
Tax
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
name | i18n jsonb | ✓ | — | — |
type | text | 100_PERCENTAGE | TaxModes: AMOUNT / PERCENTAGE / PER_UNIT_AMOUNT | |
value | decimal(15,4) | ✓ | — | Tỷ lệ hoặc số cố định |
effectiveFrom | timestamptz | ✓ | — | — |
effectiveTo | timestamptz | — | — | |
priority | integer | 0 | Tăng dần; cùng priority chia một base | |
isInclusive | boolean | ✓ | false | Nhúng trong giá hiển thị (back-calc) |
shouldApplyOnDiscounted | boolean | ✓ | true | Cơ sở nhận biết giảm giá |
isCompound | boolean | ✓ | true | Cộng dồn trên thuế trước |
minQuantity / maxQuantity | decimal(15,4) | — | Điều kiện số lượng | |
usage | text | ✓ | 000_SALE | TaxUsages.SALE / PURCHASE |
chargeTarget | text | ✓ | 000_CUSTOMER | CUSTOMER (trên hoá đơn) / MERCHANT (PIT) |
taxSetId | text | ✓ | — | TaxSet sở hữu |
taxTypeId | text | — | Phân loại | |
discriminationTypeId | text | ✓ | — | Ref phân loại thuế VN |
Index: IDX trên discriminationTypeId, status, taxSetId, taxTypeId.
TaxType
| Thuộc tính | Giá trị |
|---|---|
| Table | pricing.TaxType |
| Source | core/src/models/schemas/pricing/tax-type/schema.ts |
Trường: type (FixedTaxTypes: VAT, EXCISE, ENVIRONMENTAL, LUXURY, PIT, CUSTOM), name/description (i18n), chargeTarget, status, merchantId (nullable → loại hệ thống).
Index: unique trên (type, merchantId); IDX trên merchantId, (merchantId, status).
Cost
| Thuộc tính | Giá trị |
|---|---|
| Table | pricing.Cost |
| Source | core/src/models/schemas/pricing/cost/schema.ts |
Trường: principalType / principalId đa hình (getCurrentCost/getEffectiveCost hardcode ProductVariant), cột user-audit, amount (not null), note, effectiveFrom (not null), effectiveTo (nullable = hiện tại/mở).
Chỉ một cost mở (null
effectiveTo) mỗi variant. Chưa wire vào calculator nào.
Promotion
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
code | text | null | Null = chỉ auto-apply | |
enableStacking | boolean | ✓ | true | — |
isAutomatic | boolean | ✓ | false | — |
isTaxInclusive | boolean | ✓ | false | — |
name | i18n | ✓ | — | — |
effectiveFrom / effectiveTo | timestamptz | — | — | |
type | text | ✓ | STANDARD | PromotionTypes |
status | text | ✓ | DRAFT | PromotionStatuses |
usageLimit / usageCount | integer | usageCount=0 | — | |
rulesCount | integer | ✓ | 0 | Denormalize |
merchantId | text | — | — |
Index: unique-partial trên code where deletedAt IS NULL; IDX trên merchantId, (merchantId, status).
PromotionMethod
Trường: promotionId (not null), type (PromotionMethodType), value (not null), targetType (not null), allocation, currency (mặc định VND), exchangeRate (mặc định 1), maxQuantity, sourceRulesCount/targetRulesCount, trường BuyGet (buyGetTargetQuantity, buyGetSourceMinQuantity).
Index: unique-partial trên promotionId where deletedAt IS NULL (một method mỗi promotion).
3. Bất biến xuyên entity
| Bất biến | Cách thực thi |
|---|---|
Đúng một FareSet ACTIVATED mỗi productVariantId | Idempotency của worker + FareSetStatuses.canDeactivate() luôn false |
| Fare OVERRIDE thắng default; nếu không, fare giá thấp nhất | Chọn lựa PricingFareCalculatorService / FareCalculatorService |
| Child fare chỉ đánh giá khi mọi AND-rule của nó pass | PricingRuleEvaluatorService (lodash get() attribute path) |
Thuế cùng priority chia một base; priority cao hơn cộng dồn | Gom theo priority PricingTaxCalculatorService / TaxCalculatorService |
| Thuế inclusive back-calc ra khỏi giá hiển thị | Nhánh isInclusive của tax calculator |
Một Cost mở (effectiveTo IS NULL) mỗi variant | CostService.updateCurrentCost đóng cái trước trước khi mở cái mới |
| Một PromotionMethod mỗi Promotion | unique-partial index trên promotionId |
| FBT override seed nhiều nhất một lần mỗi (FareSet liên quan, lead variant) | PricingWorkerService._isFbtOverrideAlreadySeeded |
4. Hành vi Soft-delete
| Hành vi | Chi tiết |
|---|---|
| Mặc định đọc | deletedAt IS NULL (defaultFilter của model) |
| Hard-delete | Mặc định không dùng |
| Ràng buộc unique | Partial — scope WHERE deletedAt IS NULL (FareSet, TaxSet, Promotion code, PromotionMethod) nên hàng đã soft-delete giải phóng slot |