Skip to content

Domain Model

Nguồn schema: packages/core/src/models/schemas/ledger/ - package này không sở hữu schema Drizzle nào; nó re-export repository từ @nx/core. Mọi bảng nằm trong schema Postgres ledger.

1. ERD đầy đủ

2. Entities

Ledger

Thuộc tínhGiá trị
Tableledger.Ledger
Sourcecore/src/models/schemas/ledger/ledger/schema.ts
Soft-delete
Owner ID columnmerchantId

Trường:

TrườngKiểuBắt buộcMặc địnhMô tả
idtextSnowflakePK
typetext-TLedgerIdentifierCode (S1a-HKD..S2e-HKD)
statustextPENDINGXem enum bên dưới
periodtext-YYYY-MN / YYYY-QN / YYYY-Y (vd 2026-M3, 2026-Q1, 2026-Y)
periodStart / periodEndtimestamptz-Biên kỳ
merchantIdtext-Merchant sở hữu
isCurrentbooleantrueCờ version hiện tại
versionnumeric(_,1)1.0Version revision
previousVersionIdtextnullVersion trước (đặt khi revise)
ledgerIdentifierIdtext-Soft ref tới LedgerIdentifier
summaryjsonbnullTLedgerSummary (tổng theo từng mẫu)
notejsonbnullnote revision i18n { en, vi }

Enum trạng thái (LedgerStatuses - tái dùng IGNIS Statuses):

Giá trịMô tả
PENDINGMặc định; draft sửa được (bản ghi tạo mà chưa/chờ job)
NEWBản ghi tạo kèm một job cần xử lý
PROCESSINGĐang tạo
COMPLETEDĐã tạo xong
FAILTạo thất bại
SETTLEDĐã finalize; khoá version - phải revise để thay đổi
ARCHIVEDBị thay thế bởi bản tạo lại/revise; chỉ đọc

Luồng active do người dùng điều khiển dùng PENDING (draft) → SETTLED (finalize) → revisePENDING mới. Worker chỉ động LedgerJob.status - phần ghi Ledger.status đã được comment trong LedgerWorkerService.

Index & ràng buộc:

TênCộtLoại
PK_LedgeridPrimary key
UPQ_Ledger_*merchantId, type, period, versionUnique partial (deleted_at IS NULL)
UQ_Ledger_*ledgerIdentifierId, merchantId, period, versionUnique
IDX_Ledger_*isCurrent · merchantId,period · merchantId,periodStart,periodEnd · merchantId,status · previousVersionId · statusBtree

LedgerJob

Thuộc tínhGiá trị
Tableledger.LedgerJob
Sourcecore/src/models/schemas/ledger/ledger-job/schema.ts
Soft-delete
Owner ID column- (qua ledgerId → Ledger)

Trường:

TrườngKiểuBắt buộcMặc địnhMô tả
idtextSnowflakePK
ledgerIdtext-Sổ sở hữu (soft ref)
statustextPENDINGXem enum bên dưới
attemptCountinteger0Số lần thử trọn đời; không reset khi retry
processStartAttimestamptz-Mốc phát hiện kẹt
processCompletedAttimestamptz--
failureReasonjsonb-{ default, en?, vi?, errorCode }
enqueuedAttimestamptz-Lần enqueue đầu
lastEnqueuedAttimestamptz-Lần re-enqueue cuối

Enum trạng thái (LedgerJobStatuses): DRAFT, PENDING, PROCESSING, COMPLETED, REJECTED (DRAFT = đã tạo nhưng chưa đẩy vào queue; luồng active chạy PENDING → PROCESSING → COMPLETED|REJECTED).

Index: IDX_LedgerJob_ledgerId, IDX_LedgerJob_status, IDX_LedgerJob_status_processStartAt (quét job kẹt), UPQ_LedgerJob_ledgerId (partial - một job đang chạy mỗi sổ khi status IN (PENDING, PROCESSING, DRAFT)).

LedgerSnapshot

Thuộc tínhGiá trị
Tableledger.LedgerSnapshot
Sourcecore/src/models/schemas/ledger/ledger-snapshot/schema.ts
Soft-deletecó (+ cột user-audit)
Owner ID column- (qua ledgerId)

Trường:

TrườngKiểuBắt buộcMặc địnhMô tả
idtextSnowflakePK
ledgerIdtext-Sổ sở hữu; unique
headerDatajsonb-TSnapshotHeaderData (businessName, taxCode, address…)
snapshotMetajsonb-Tổng hợp staleness theo loại (count, maxUpdatedAt)
pulledAttimestamptz-Thời điểm pull
hasUnrecordedChangebooleanfalseCờ staleness (chặn finalize)
lastChangeDetectedAttimestamptz--

Index: UQ_LedgerSnapshot_ledgerId (một snapshot mỗi sổ).

LedgerSnapshotEntry

Thuộc tínhGiá trị
Tableledger.LedgerSnapshotEntry
Sourcecore/src/models/schemas/ledger/ledger-snapshot-entry/schema.ts
Soft-deletecó (+ cột user-audit)

Trường:

TrườngKiểuBắt buộcMặc địnhMô tả
idtextSnowflakePK
snapshotIdtext-Snapshot sở hữu
rowIndexinteger-Thứ tự hàng
originalDatajsonbnullHàng nguồn; null với entry do người dùng thêm
currentDatajsonb-Hàng đã sửa/hiệu lực

Index: IDX_LedgerSnapshotEntry_snapshotId.

LedgerIdentifier

Thuộc tínhGiá trị
Tableledger.LedgerIdentifier
Sourcecore/src/models/schemas/ledger/ledger-identifier/schema.ts
Soft-delete

Danh mục 6 mã mẫu sổ HKD. Seed một lần; được Ledger.typeLedger.ledgerIdentifierId tham chiếu.

Trường:

TrườngKiểuBắt buộcMặc địnhMô tả
idtextSnowflakePK
identifiertext-TLedgerIdentifierCode (S1a-HKD..S2e-HKD); unique
namejsonb-i18n { en, vi }
descriptionjsonb-i18n

Index: unique trên identifier.

TaxTier

Thuộc tínhGiá trị
Tableledger.TaxTier
Sourcecore/src/models/schemas/ledger/tax-tier/schema.ts
Soft-deletecó (+ cột user-audit)

Nhóm dải doanh thu mà merchant thuộc về (TIRE_1..TIRE_4). Seed một lần.

Trường:

TrườngKiểuBắt buộcMặc địnhMô tả
idtextSnowflakePK
codetext-TTaxTierCode (100_TIRE_1..400_TIRE_4)
namejsonb-i18n { en, vi }
descriptionjsonb-i18n
statustextACTIVATEDACTIVATED / DEACTIVATED
businessScaletext000_HOUSEHOLDTBusinessScaleType (000_HOUSEHOLD / 100_ENTERPRISE)
revenueMindecimal(15,4)0Biên dưới của dải (VND)
revenueMaxdecimal(15,4)nullBiên trên của dải (VND); null = không giới hạn

Index: IDX_TaxTier_status, UQ_TaxTier_code (unique partial, deleted_at IS NULL).

LedgerTaxConfig

Thuộc tínhGiá trị
Tableledger.LedgerTaxConfig
Sourcecore/src/models/schemas/ledger/ledger-tax-config/schema.ts
Soft-deletecó (+ cột user-audit)

Một phương pháp thuế khả dụng trong một bậc, kèm tập mẫu sổ mà nó yêu cầu. Seed một lần.

Trường:

TrườngKiểuBắt buộcMặc địnhMô tả
idtextSnowflakePK
codetext-TTaxApproachCode (100_EXEMPT, 201_PERCENTAGE_BOTH, 202_PERCENTAGE_VAT_INCOME_PIT)
taxTierIdtext-Soft ref tới TaxTier
namejsonb-i18n { en, vi }
descriptionjsonb-i18n
requiredLedgersjsonb[]TLedgerIdentifierCode[] mà phương pháp này yêu cầu
statustextACTIVATEDACTIVATED / DEACTIVATED
metadatajsonb-{ isDefault, isSelectable }

Index: IDX_LedgerTaxConfig_tax_tier_id, IDX_LedgerTaxConfig_status, UPQ_LedgerTaxConfig_tax_tier_id_code (unique partial, deleted_at IS NULL).

MerchantTaxConfig

Thuộc tínhGiá trị
Tableledger.MerchantTaxConfig
Sourcecore/src/models/schemas/ledger/merchant-tax-config/schema.ts
Soft-deletecó (+ cột user-audit)
Owner ID columnmerchantId

Lựa chọn onboarding thuế theo từng merchant theo từng năm. Schema và model đã được định nghĩa; package này chưa wire repository/service/controller.

Trường:

TrườngKiểuBắt buộcMặc địnhMô tả
idtextSnowflakePK
merchantIdtext-Merchant sở hữu
yearintegernăm hiện tạiNăm config
taxTierIdtext-Soft ref tới TaxTier
ledgerTaxConfigIdtext-Soft ref tới LedgerTaxConfig (phương pháp đã chọn)
filingPeriodtext-TPeriodType (MONTHLY / QUARTERLY / YEARLY)
declaredAnnualRevenuedecimal(15,4)-Doanh thu năm tự khai (VND)
onboardingStatustextPENDINGPENDING / PARTIAL / COMPLETED
disclaimerAcceptedAttimestamptz-Thời điểm chấp nhận disclaimer
disclaimerAcceptedBytext-Người chấp nhận (cột DB disclaimer_accepted_by)
disclaimerVersiontext-Phiên bản disclaimer đã chấp nhận
metadatajsonb-{ disclaimerHistory: { acceptedAt, version }[] }

Index: IDX_MerchantTaxConfig_year, IDX_MerchantTaxConfig_merchant_id_onboarding_status_year, UPQ_MerchantTaxConfig_merchant_id_year (unique partial, deleted_at IS NULL).

3. Bất biến xuyên entity

Bất biếnCách thực thi
Nhiều nhất một sổ current mỗi (merchantId, type, period, version)Unique partial index + cờ isCurrent
revise luôn tạo một hàng PENDING mới (version+1, isCurrent=false, đặt previousVersionId, yêu cầu nguồn SETTLED)LedgerSnapshotService.revise
Đúng một snapshot mỗi sổUQ_LedgerSnapshot_ledgerId
Nhiều nhất một job đang chạy mỗi sổUPQ_LedgerJob_ledgerId (partial trên PENDING/PROCESSING/DRAFT)
finalize bị chặn khi snapshot hasUnrecordedChange = true; đặt Ledger.status = SETTLEDguard LedgerSnapshotService.finalize
Một phương pháp LedgerTaxConfig là duy nhất theo (taxTierId, code); requiredLedgers của nó quyết định các mẫu sổ merchant phải giữUPQ_LedgerTaxConfig_tax_tier_id_code + seed APPROACH_REQUIRED_LEDGERS
Một MerchantTaxConfig mỗi (merchantId, year)UPQ_MerchantTaxConfig_merchant_id_year
Worker chỉ động LedgerJob.status - không bao giờ Ledger.statusLedgerWorkerService (ghi Ledger-status đã comment)

4. Hành vi Soft-delete

Hành viChi tiết
Mặc định đọcdeletedAt IS NULL (mọi repo qua SoftDeletableRepository)
Hard-deleteRe-pull snapshot soft-delete entry + snapshot trước đó trước khi tạo lại
Unique indexPartial (WHERE deleted_at IS NULL) nên hàng đã soft-delete không chặn tạo lại

5. Trang liên quan

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