Mẫu báo cáo HKD
1. Tổng quan
Dịch vụ Ledger tạo 6 biểu mẫu kế toán theo quy định Việt Nam thuộc dòng HKD (Hộ Kinh Doanh). Mỗi loại biểu mẫu có hai định dạng đầu ra:
| Định dạng | Engine | MIME Type | Mục đích |
|---|---|---|---|
Typst NodeCompiler | application/pdf | Nộp chính thức, in ấn | |
| XLSX | ExcelJS | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | Chỉnh sửa dữ liệu, phân tích |
Cả hai đầu ra được tạo song song từ cùng một object dữ liệu đã được validate.
2. Registry Mẫu
| Mã Biểu mẫu | Hướng | Có Địa điểm Kinh doanh | Mô tả |
|---|---|---|---|
S1a-HKD | Dọc | Có | Sổ doanh thu/bán hàng chính — date, code, description, revenue amount |
S2a-HKD | Ngang | Có | Thu/chi chi tiết với phân tích thuế theo nhóm |
S2b-HKD | Ngang | Có | Bản ghi mua hàng và chi phí |
S2c-HKD | Dọc | Có | Sổ theo dõi tồn kho — hàng vào/ra |
S2d-HKD | Ngang | Không | Bản ghi thu/chi (không có địa điểm kinh doanh cố định) |
S2e-HKD | Ngang | Không | Bản ghi mua hàng (không có địa điểm kinh doanh cố định) |
"Có Địa điểm Kinh doanh" kiểm soát việc header có bao gồm các trường businessAddress, quận/huyện và tỉnh/thành hay không.
3. Mẫu PDF Typst
3.1. Vị trí File
Templates là các file source được biên dịch, được load tại runtime từ resources/templates/ (tương đối với process.cwd()).
| File Typst | Loại Ledger |
|---|---|
common.typ | Macro chia sẻ — header, footer, layout, định dạng số/ngày |
s1a-hkd.typ | S1a-HKD |
s2a-hkd.typ | S2a-HKD |
s2b-hkd.typ | S2b-HKD |
s2c-hkd.typ | S2c-HKD |
s2d-hkd.typ | S2d-HKD |
s2e-hkd.typ | S2e-HKD |
3.2. Inject Dữ liệu
PdfGeneratorService inject dữ liệu thông qua cơ chế shadow file của Typst:
// Serialize data to JSON at a virtual path inside the workspace
compiler.mapShadow(shadowPath, Buffer.from(JSON.stringify(data)));
// Render
const pdfBuffer = compiler.pdf({ mainFilePath });
// Always clean up
compiler.unmapShadow(shadowPath);Path của shadow file là resources/templates/data.json. Templates đọc nó qua:
#let data = json("data.json")3.3. Component Chia sẻ (common.typ)
| Component | Mục đích |
|---|---|
| Header | Hai cột — mã biểu mẫu chính phủ + thông tin merchant (tên, mã số thuế, địa chỉ, người đại diện) |
| Footer | Khối chữ ký HKD (địa điểm, ngày, người đại diện, kế toán, giám đốc) |
| Layout | Lề trang, viền bảng, cấu hình font (Times New Roman) |
| Định dạng số | Quy ước Việt Nam: dấu chấm là phân tách hàng nghìn, dấu phẩy là thập phân |
| Định dạng ngày | dd/mm/yyyy |
| Văn bản quy định | (Kèm theo Thông tư số 152/2025/TT-BTC ngày 31 tháng 12 năm 2025...) |
File font phải có mặt trong
resources/fonts/.NodeCompilerđược khởi tạo vớifontArgs: [{ fontPaths: [FONT_DIR] }].
4. Mẫu XLSX (ExcelJS)
4.1. Hằng Style Chia sẻ
Được định nghĩa dưới dạng hằng cấp module trong xlsx-generator.service.ts:
| Hằng | Giá trị | Dùng cho |
|---|---|---|
HEADER_FONT | { bold: true, color: white, size: 10 } | Hàng header cột |
BODY_FONT | { size: 10 } | Hàng dữ liệu |
BOLD_FONT | { bold: true, size: 10 } | Hàng tổng/summary |
THIN_BORDER | Cả 4 cạnh mỏng | Tất cả ô dữ liệu |
CURRENCY_FMT | '#,##0.###' | Ô số tiền |
COLOR_GREEN_DARK | FF5CB85C | Nền header chính |
COLOR_GREEN_LIGHT | FF8FD18F | Hàng header phụ |
COLOR_GREEN_PRODUCT | FFC8E6C9 | Hàng sản phẩm/danh mục |
COLOR_YELLOW_IMPORT | FFFFFDE7 | Hàng nhập/mua |
REGULATION_TEXT | Thông tư 152/2025/TT-BTC | Tham chiếu quy định ở footer |
4.2. Layout Sheet trên từng Biểu mẫu
| Biểu mẫu | Cột | Tính năng Chính |
|---|---|---|
| S1a-HKD | STT, Date, Code, Description, Revenue | Sổ doanh thu đơn giản; một hàng trên mỗi giao dịch |
| S2a-HKD | STT, Date, Code, Description, Cột thuế… | Phân tích thuế nhiều cột; nhóm thuế làm header cột |
| S2b-HKD | STT, Date, Code, Description, Cột Amount | Mua hàng và chi phí với nhóm danh mục |
| S2c-HKD | STT, Date, Code, Description, In/Out, Balance | Theo dõi tồn kho với số dư chạy |
| S2d-HKD | Giống S2a-HKD (không có header địa điểm) | Không có trường địa chỉ kinh doanh |
| S2e-HKD | Giống S2b-HKD (không có header địa điểm) | Không có trường địa chỉ kinh doanh |
4.3. Footer
Tất cả sheet XLSX bao gồm một hàng footer 2 cột với văn bản quy định và khối chữ ký (cùng cấu trúc với common.typ của Typst).
FOOTER_COLUMN_LENGTH = 2 — merge qua 2 cột.
5. Yêu cầu Dữ liệu
5.1. Trường Chung (tất cả biểu mẫu)
| Trường | Loại | Mô tả |
|---|---|---|
businessName | string | Tên merchant |
businessAddress | string | Địa chỉ kinh doanh (chỉ biểu mẫu có địa điểm) |
businessTaxCode | string | Mã số thuế |
periodDescription | string | Nhãn period tiếng Việt (vd: Quý 1 năm 2026) |
title | string | Tiêu đề biểu mẫu (vd: SỔ DOANH THU BÁN HÀNG, DỊCH VỤ) |
currentDay / currentMonth / currentYear | number | Ngày ký |
5.2. S1a-HKD Riêng
interface IS1aEntry {
code: string; // order number / reference code
transDate: string; // transaction date (ISO)
description: string;
amount: number;
}
// Data shape
{
...commonFields,
entries: IS1aEntry[];
totalRevenue: number;
}Nguồn dữ liệu: thật — fetch từ FinanceTransactionRepository.findTransactionWithSaleOrder().
5.3. S2a-HKD Riêng
interface ITaxValue {
revenue: number;
vat: number;
pit?: number;
}
interface IS2aEntry {
code: string;
transDate: string;
description: string;
taxValues: Record<string, ITaxValue>; // keyed by tax group key
}
interface ITaxGroup {
key: string;
groupName: string;
label: string;
totalRevenue: number;
totalVat: number;
totalPit?: number;
}
// Data shape
{
...commonFields,
taxGroups: ITaxGroup[]; // define column headers
entries: IS2aEntry[];
}Nguồn dữ liệu: fixture (chờ tích hợp thật).
5.4. Các Biểu mẫu Khác (S2b, S2c, S2d, S2e)
Cấu trúc dữ liệu được định nghĩa trong các schema Zod ở @nx/core (SCHEMA_MAP keyed bởi TLedgerIdentifiers). Hiện được phục vụ từ các file fixture trong resources/fixtures/.
6. Validation Schema
Trước khi render, LedgerWorkerService validate dữ liệu thô bằng:
const schema = SCHEMA_MAP[type]; // from @nx/core
const parsed = schema.safeParse(rawData);Nếu validation thất bại, job được mark REJECTED với thông báo lỗi Zod — không có file nào được tạo.
Sau khi tạo, summary được trích xuất qua:
const summaryData = SUMMARY_SCHEMA_MAP[type].parse(parsedData);
// Saved to ledger.summary (jsonb)Cả hai map đều được export từ @nx/core và keyed bởi giá trị TLedgerIdentifiers.
7. Vòng đời Compiler
NodeCompiler của Typst là một singleton được quản lý bởi PdfGeneratorService:
| State | Trigger | Hành động |
|---|---|---|
null (ban đầu) | Lần gọi generate() đầu | NodeCompiler.create({ fontArgs, workspace }) |
| Active | Các generate đồng thời | Tái sử dụng — shadow file đảm bảo cô lập dữ liệu |
| Lỗi compilation | Exception từ compiler.pdf() | replaceCompiler() — tạo instance mới, sau đó throw |
| Testing / reset cưỡng bức | dispose() | Set thành null — lần gọi tiếp theo sẽ khởi tạo lại |
Ghi chú về đồng thời: NodeCompiler sử dụng shadow file tại path cố định (
data.json). Các cuộc gọi đồng thời đếngenerate()từ cùng một service instance sẽ làm hỏng dữ liệu của nhau. Trong production, mỗi Kafka consumer chạy trong execution context riêng, và mỗi consumer instance cóLedgerWorkerServiceriêng — vì vậy không có truy cập đồng thời đến cùng một compiler.
8. Trang liên quan
- Generation Pipeline — render nằm ở đâu trong luồng worker
- Domain Model —
Ledger.type/summary - ADR-0003 Typst để render PDF