Skip to content

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ạngEngineMIME TypeMục đích
PDFTypst NodeCompilerapplication/pdfNộp chính thức, in ấn
XLSXExcelJSapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetChỉ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ẫuHướngCó Địa điểm Kinh doanhMô tả
S1a-HKDDọcSổ doanh thu/bán hàng chính — date, code, description, revenue amount
S2a-HKDNgangThu/chi chi tiết với phân tích thuế theo nhóm
S2b-HKDNgangBản ghi mua hàng và chi phí
S2c-HKDDọcSổ theo dõi tồn kho — hàng vào/ra
S2d-HKDNgangKhôngBản ghi thu/chi (không có địa điểm kinh doanh cố định)
S2e-HKDNgangKhôngBả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 TypstLoại Ledger
common.typMacro chia sẻ — header, footer, layout, định dạng số/ngày
s1a-hkd.typS1a-HKD
s2a-hkd.typS2a-HKD
s2b-hkd.typS2b-HKD
s2c-hkd.typS2c-HKD
s2d-hkd.typS2d-HKD
s2e-hkd.typS2e-HKD

3.2. Inject Dữ liệu

PdfGeneratorService inject dữ liệu thông qua cơ chế shadow file của Typst:

typescript
// 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:

typst
#let data = json("data.json")

3.3. Component Chia sẻ (common.typ)

ComponentMục đích
HeaderHai 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)
FooterKhối chữ ký HKD (địa điểm, ngày, người đại diện, kế toán, giám đốc)
LayoutLề 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àydd/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ới fontArgs: [{ 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ằngGiá 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_BORDERCả 4 cạnh mỏngTất cả ô dữ liệu
CURRENCY_FMT'#,##0.###'Ô số tiền
COLOR_GREEN_DARKFF5CB85CNền header chính
COLOR_GREEN_LIGHTFF8FD18FHàng header phụ
COLOR_GREEN_PRODUCTFFC8E6C9Hàng sản phẩm/danh mục
COLOR_YELLOW_IMPORTFFFFFDE7Hàng nhập/mua
REGULATION_TEXTThông tư 152/2025/TT-BTCTham chiếu quy định ở footer

4.2. Layout Sheet trên từng Biểu mẫu

Biểu mẫuCộtTính năng Chính
S1a-HKDSTT, Date, Code, Description, RevenueSổ doanh thu đơn giản; một hàng trên mỗi giao dịch
S2a-HKDSTT, Date, Code, Description, Cột thuế…Phân tích thuế nhiều cột; nhóm thuế làm header cột
S2b-HKDSTT, Date, Code, Description, Cột AmountMua hàng và chi phí với nhóm danh mục
S2c-HKDSTT, Date, Code, Description, In/Out, BalanceTheo dõi tồn kho với số dư chạy
S2d-HKDGiống S2a-HKD (không có header địa điểm)Không có trường địa chỉ kinh doanh
S2e-HKDGiống S2b-HKD (không có header địa điểm)Không có trường địa chỉ kinh doanh

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ườngLoạiMô tả
businessNamestringTên merchant
businessAddressstringĐịa chỉ kinh doanh (chỉ biểu mẫu có địa điểm)
businessTaxCodestringMã số thuế
periodDescriptionstringNhãn period tiếng Việt (vd: Quý 1 năm 2026)
titlestringTiêu đề biểu mẫu (vd: SỔ DOANH THU BÁN HÀNG, DỊCH VỤ)
currentDay / currentMonth / currentYearnumberNgày ký

5.2. S1a-HKD Riêng

typescript
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

typescript
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:

typescript
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:

typescript
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:

StateTriggerHành động
null (ban đầu)Lần gọi generate() đầuNodeCompiler.create({ fontArgs, workspace })
ActiveCác generate đồng thờiTái sử dụng — shadow file đảm bảo cô lập dữ liệu
Lỗi compilationException từ compiler.pdf()replaceCompiler() — tạo instance mới, sau đó throw
Testing / reset cưỡng bứcdispose()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 đến generate() 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ó LedgerWorkerService riêng — vì vậy không có truy cập đồng thời đến cùng một compiler.

8. Trang liên quan

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