Domain Model
Tất cả schema do
@nx/coresở hữu. Taxation chỉ kế thừa repository. Thư mục gốc nguồn:packages/core/src/models/schemas/tax/và.../pricing/.
1. ERD đầy đủ
2. Entities
TaxGroup
| Thuộc tính | Giá trị |
|---|---|
| Bảng | tax.TaxGroup |
| Nguồn | packages/core/src/models/schemas/tax/tax-group/schema.ts |
| Soft-delete | có |
| Cột Owner ID | merchantId (nullable - null = template hệ thống) |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | Khóa chính |
identifier | text | ✓ | Snowflake | Mã ổn định, duy nhất (vd VN_DEDUCTION_VAT10) |
name | jsonb | ✓ | - | i18n { default, en, vi } |
description | jsonb | - | i18n | |
taxMethod | text | ✓ | - | 000_DIRECT / 100_DEDUCTION |
status | text | ✓ | ACTIVATED | Xem enum bên dưới |
merchantId | text | - | Merchant sở hữu; null = system |
Enum status: ACTIVATED · DEACTIVATED · ARCHIVED (Statuses).
Index: UQ_TaxGroup_identifier, IDX_TaxGroup_merchantId, IDX_TaxGroup_merchantId_status.
Quan hệ: merchant (M:1 → Merchant.id), items (1:M → TaxGroupItem, relationName là taxGroup).
TaxGroupItem
| Thuộc tính | Giá trị |
|---|---|
| Bảng | tax.TaxGroupItem |
| Nguồn | packages/core/src/models/schemas/tax/tax-group-item/schema.ts |
| Soft-delete | có |
| Cột Owner ID | - (sở hữu qua parent taxGroupId) |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | Khóa chính |
taxGroupId | text | ✓ | - | Group cha |
discriminationTypeId | text | ✓ | - | Phân loại thuế (VAT/PIT/...) thông qua DiscriminationType |
type | text | ✓ | 100_PERCENTAGE | TaxMode |
value | decimal(15,4) | 0 | Tỷ lệ hoặc số tiền | |
isInclusive | boolean | ✓ | false | Thuế nhúng trong giá |
priority | integer | 0 | Thứ tự áp dụng (tăng dần) | |
usage | text | ✓ | 000_SALE | 000_SALE / 100_PURCHASE |
chargeTarget | text | ✓ | 000_CUSTOMER | 000_CUSTOMER / 100_MERCHANT |
Index: IDX_TaxGroupItem_discriminationTypeId, IDX_TaxGroupItem_taxGroupId.
Quan hệ: taxGroup (M:1 → TaxGroup.id), discriminationType (M:1 → DiscriminationType.id).
TaxSet
| Thuộc tính | Giá trị |
|---|---|
| Bảng | pricing.TaxSet |
| Nguồn | packages/core/src/models/schemas/pricing/tax-set/schema.ts |
| Soft-delete | có |
| Cột Owner ID | - (đa hình qua principalId/principalType) |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | Khóa chính |
principalId | text | ✓ | - | id Product hoặc ProductVariant |
principalType | text | ✓ | - | Product (mặc định) / ProductVariant (override) |
name | jsonb | - | i18n (sao chép từ TaxGroup) | |
status | text | ✓ | ACTIVATED | Xem enum bên dưới |
sourceType | text | - | TaxGroup (tự động) / null (thủ công) | |
sourceId | text | - | TaxGroup.id khi provision tự động |
Enum status: ACTIVATED · DEACTIVATED · ARCHIVED.
Index: partial unique UPI_TaxSet_principalId_principalType_status (where deletedAt IS NULL), IDX_TaxSet_sourceId, IDX_TaxSet_sourceType_sourceId, IDX_TaxSet_status.
Tax
| Thuộc tính | Giá trị |
|---|---|
| Bảng | pricing.Tax |
| Nguồn | packages/core/src/models/schemas/pricing/tax/schema.ts |
| Soft-delete | có |
| Cột Owner ID | - (sở hữu qua taxSetId) |
Trường (liên quan provisioning):
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | Khóa chính |
name | jsonb | ✓ | - | i18n (từ DiscriminationType / group) |
type | text | ✓ | 100_PERCENTAGE | TaxMode |
value | decimal(15,4) | ✓ | - | Tỷ lệ hoặc số tiền |
effectiveFrom | timestamptz | ✓ | thời điểm provision | Thời điểm thuế bắt đầu có hiệu lực |
effectiveTo | timestamptz | - | Thời điểm hết hạn (null = để mở) | |
priority | integer | 0 | Thứ tự áp dụng | |
isInclusive | boolean | ✓ | false | Nhúng trong giá |
shouldApplyOnDiscounted | boolean | ✓ | true | Áp trên phần giá đã giảm |
isCompound | boolean | ✓ | true | Cộng dồn trên các thuế trước đó |
usage | text | ✓ | 000_SALE | Bán/mua |
status | text | ✓ | ACTIVATED | - |
chargeTarget | text | ✓ | 000_CUSTOMER | Khách hàng/merchant |
taxSetId | text | ✓ | - | Set cha |
taxTypeId | text | - | FK legacy tới TaxType (ghi kép, nullable) | |
discriminationTypeId | text | ✓ | - | Phân loại (có thẩm quyền) |
Index: IDX_Tax_discriminationTypeId, IDX_Tax_status, IDX_Tax_taxSetId, IDX_Tax_taxTypeId.
TaxType
| Thuộc tính | Giá trị |
|---|---|
| Bảng | pricing.TaxType |
| Nguồn | packages/core/src/models/schemas/pricing/tax-type/schema.ts |
| Soft-delete | có |
| Cột Owner ID | merchantId (nullable) |
Enum type (FixedTaxTypes): 000_VAT, 100_EXCISE, 200_ENVIRONMENTAL, 300_LUXURY, 400_PIT, 999_CUSTOM. chargeTarget: 000_CUSTOMER / 100_MERCHANT. Unique (type, merchantId). Phân loại legacy - DiscriminationType (scope tax_classification) nay là nguồn có thẩm quyền; Tax.taxTypeId giữ làm phương án dự phòng ghi kép (ADR-0002).
Bảng tham chiếu VN (read-only)
| Bảng | Cột key | FK |
|---|---|---|
tax.VnAdministrativeUnit | codeName (UK), fullName, shortName (+ *_en) | - |
tax.VnProvince | code (UK), name, fullName, administrativeUnitCode | → VnAdministrativeUnit.codeName |
tax.VnWard | code (UK), name, provinceCode, administrativeUnitCode | → VnProvince.code, VnAdministrativeUnit.codeName |
Phục vụ chỉ đọc; không được seed bởi migration của package này (nạp từ bên ngoài).
3. Bất biến xuyên entity
| Bất biến | Cách thực thi |
|---|---|
Nhiều nhất một TaxSet ACTIVATED cho mỗi (principalId, principalType) | Partial unique index UPI_TaxSet_principalId_principalType_status (với dòng chưa xoá) |
| Provision idempotent cho cùng một source group | TaxProvisioningService bỏ qua khi TaxSet đang active có sourceType=TaxGroup và sourceId khớp |
| Chỉ TaxSet có nguồn TaxGroup mới bị deactivate khi (de)provision | _deactivateExistingTaxSet chỉ tác động khi sourceType === TaxGroup |
Merchant taxMethod phải khớp TaxGroup.taxMethod | TaxGroupService.validateTaxGroupForMerchant (DIRECT ≠ DEDUCTION) |
Mỗi Tax được provision phản ánh một TaxGroupItem | provisionForProduct lặp qua các item của group |
4. Hành vi Soft-delete
| Hành vi | Chi tiết |
|---|---|
| Đọc mặc định | deletedAt IS NULL (model defaultFilter) |
| Deprovision | Chuyển status thành DEACTIVATED - không phải soft-delete (các dòng dồn lại) |
| Hard-delete | Không bao giờ, theo mặc định |
| Khôi phục | Provision lại tạo một TaxSet ACTIVATED mới; các dòng DEACTIVATED trước đó vẫn còn |