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 Postgresledger.
1. ERD đầy đủ
2. Entities
Ledger
| Thuộc tính | Giá trị |
|---|---|
| Table | ledger.Ledger |
| Source | core/src/models/schemas/ledger/ledger/schema.ts |
| Soft-delete | có |
| Owner ID column | merchantId |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | PK |
type | text | ✓ | — | TLedgerIdentifier (S1a-HKD..S2e-HKD) |
status | text | ✓ | DRAFT | Xem enum bên dưới |
period | text | ✓ | — | YYYY-MN / YYYY-QN / YYYY-Y (vd 2026-M3, 2026-Q1, 2026-Y) |
periodStart / periodEnd | timestamptz | ✓ | — | Biên kỳ |
merchantId | text | ✓ | — | Merchant sở hữu |
isCurrent | boolean | ✓ | true | Cờ version hiện tại |
version | numeric(_,1) | ✓ | 1.0 | Version revision |
previousVersionId | text | null | Version trước (đặt khi revise) | |
summary | jsonb | null | TLedgerSummary (tổng theo từng mẫu) | |
note | jsonb | null | note revision i18n { en, vi } |
Enum trạng thái (LedgerStatuses):
| Giá trị | Mô tả |
|---|---|
DRAFT | Sửa được; cho phép tạo + tạo lại |
200_FINALIZED | Khoá version; phải revise để thay đổi |
ARCHIVED | Bị thay thế bởi một revision đã finalize; chỉ đọc |
400_SUBMITTED | Dành chỗ — đã nộp lên cơ quan thuế (chưa hiện thực) |
Index & ràng buộc:
| Tên | Cột | Loại |
|---|---|---|
PK_Ledger | id | Primary key |
UPQ_Ledger_* | merchantId, type, period, version | Unique partial (deleted_at IS NULL) |
IDX_Ledger_* | isCurrent · merchantId,period · merchantId,periodStart,periodEnd · merchantId,status · previousVersionId · status | Btree |
LedgerJob
| Thuộc tính | Giá trị |
|---|---|
| Table | ledger.LedgerJob |
| Source | core/src/models/schemas/ledger/ledger-job/schema.ts |
| Soft-delete | có |
| Owner ID column | — (qua ledgerId → Ledger) |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | PK |
ledgerId | text | ✓ | — | Sổ sở hữu (soft ref) |
status | text | ✓ | PENDING | Xem enum bên dưới |
attemptCount | integer | ✓ | 0 | Số lần thử trọn đời; không reset khi retry |
processStartAt | timestamptz | — | Mốc phát hiện kẹt | |
processCompletedAt | timestamptz | — | — | |
failureReason | jsonb | — | { default, en?, vi?, errorCode } | |
enqueuedAt | timestamptz | ✓ | — | Lần enqueue đầu |
lastEnqueuedAt | timestamptz | — | Lần re-enqueue cuối |
Enum trạng thái (LedgerJobStatuses): DRAFT, PENDING, PROCESSING, COMPLETED, PARTIAL, REJECTED (luồng active dùng PENDING → PROCESSING → COMPLETED|REJECTED).
Index: IDX_LedgerJob_ledgerId, IDX_LedgerJob_status, IDX_LedgerJob_status_processStartAt (quét job kẹt).
LedgerSnapshot
| Thuộc tính | Giá trị |
|---|---|
| Table | ledger.LedgerSnapshot |
| Source | core/src/models/schemas/ledger/ledger-snapshot/schema.ts |
| Soft-delete | có (+ cột user-audit) |
| Owner ID column | — (qua ledgerId) |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | PK |
ledgerId | text | ✓ | — | Sổ sở hữu; unique |
headerData | jsonb | — | TSnapshotHeaderData (businessName, taxCode, address…) | |
snapshotMeta | jsonb | — | Tổng hợp staleness theo loại (count, maxUpdatedAt) | |
pulledAt | timestamptz | ✓ | — | Thời điểm pull |
hasUnrecordedChange | boolean | ✓ | false | Cờ staleness (chặn finalize) |
lastChangeDetectedAt | timestamptz | — | — |
Index: UQ_LedgerSnapshot_ledgerId (một snapshot mỗi sổ).
LedgerSnapshotEntry
| Thuộc tính | Giá trị |
|---|---|
| Table | ledger.LedgerSnapshotEntry |
| Source | core/src/models/schemas/ledger/ledger-snapshot-entry/schema.ts |
| Soft-delete | có (+ cột user-audit) |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | PK |
snapshotId | text | ✓ | — | Snapshot sở hữu |
rowIndex | integer | ✓ | — | Thứ tự hàng |
originalData | jsonb | null | Hàng nguồn; null với entry do người dùng thêm | |
currentData | jsonb | ✓ | — | Hàng đã sửa/hiệu lực |
Index: IDX_LedgerSnapshotEntry_snapshotId.
MerchantLedgerConfig
| Thuộc tính | Giá trị |
|---|---|
| Table | ledger.MerchantLedgerConfig |
| Source | core/src/models/schemas/ledger/merchant-ledger-config/schema.ts |
| Soft-delete | có (+ cột user-audit) |
| Owner ID column | merchantId |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | PK |
year | integer | ✓ | năm hiện tại | Năm config |
merchantId | text | ✓ | — | Merchant sở hữu |
taxDeclarationLevelId | text | — | FK-soft tới TaxDeclarationLevel | |
taxMethod | text | — | — | |
isMultiSector | boolean | ✓ | false | Chọn rule multiSectorLedgerTypes |
filingSchedules | jsonb | ✓ | [] | { purpose, periodType }[] |
requiredLedgerTypes | jsonb | ✓ | [] | TLedgerIdentifier[] đã tính |
confirmedAt | timestamptz | — | Đặt khi confirm | |
metadata.origin | jsonb | — | `'migration' |
Index: UPQ_MerchantLedgerConfig_merchantId_year (một config mỗi merchant-năm, partial).
TaxDeclarationLevel
| Thuộc tính | Giá trị |
|---|---|
| Table | ledger.TaxDeclarationLevel |
| Source | core/src/models/schemas/ledger/tax-declaration-level/schema.ts |
| Soft-delete | có (+ cột user-audit) |
Trường:
| Trường | Kiểu | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|---|
id | text | ✓ | Snowflake | PK |
code | text | ✓ | — | TIRE_0..TIRE_3 |
name | jsonb | ✓ | — | i18n { en, vi } |
description | jsonb | — | i18n | |
revenueThresholdMin / Max | text | — | Biên dải doanh thu | |
filingScheduleRules | jsonb | ✓ | [] | { purpose, periodType, required, ledgerTypes[], multiSectorLedgerTypes? }[] |
Index: UPQ_TaxDeclarationLevel_code (unique partial).
3. Bất biến xuyên entity
| Bất biến | Cách thực thi |
|---|---|
Nhiều nhất một sổ current đã finalize mỗi (merchantId, type, period, version) | Unique partial index + cờ isCurrent |
revise luôn tạo một hàng DRAFT mới (version+1, isCurrent=false, đặt previousVersionId) | LedgerSnapshotService.revise |
| Đúng một snapshot mỗi sổ | UQ_LedgerSnapshot_ledgerId |
finalize bị chặn khi snapshot hasUnrecordedChange = true | guard LedgerSnapshotService.finalize |
requiredLedgerTypes suy từ filingScheduleRules của bậc thuế × filingSchedules (×isMultiSector); rỗng → [S1a-HKD] | MerchantLedgerConfigService._computeRequiredLedgerTypes |
Worker chỉ động LedgerJob.status — không bao giờ Ledger.status | LedgerWorkerService (ghi Ledger-status đã comment) |
4. Hành vi Soft-delete
| Hành vi | Chi tiết |
|---|---|
| Mặc định đọc | deletedAt IS NULL (mọi repo qua SoftDeletableRepository) |
| Hard-delete | Re-pull snapshot soft-delete entry + snapshot trước đó trước khi tạo lại |
| Unique index | Partial (WHERE deleted_at IS NULL) nên hàng đã soft-delete không chặn tạo lại |