PRD: Chứng từ & hạch toán
| Module | Tài chính (CORE-12) | PRD ID | PRD-VCH-001 |
| Status | In-progress | Owner | Finance squad |
| Date | 2026-05-22 | Version | v0.1 |
| Packages | @nx/finance · @nx/core · @nx/commerce | URD | VCH · WAL |
TL;DR
Cho phép merchant ghi nhận mọi dòng tiền dưới dạng một chứng từ ghi sổ kép cân đối thay vì một con số tổng chạy theo từng ví. Các tài khoản tiền có kiểu (tiền mặt, ngân hàng, QR, mobile-POS) cùng các tài khoản kiểm soát nội bộ được seed theo từng merchant, và một ma trận payment integration điều hướng một payment đã thanh toán tới đúng tài khoản và tự động phát hành chứng từ phù hợp. Kết quả: bán hàng, mua hàng, và biến động kho được hạch toán vào sổ sách một cách tự động, idempotent, và có liên kết ngược về chứng từ nguồn.
1. Context & Problem
Sổ sách của merchant phải ghi nhận mọi dòng tiền dưới dạng một chứng từ kế toán cân đối - chứ không phải một con số tổng duy nhất bị thay đổi theo từng ví. Mô hình finance-wallet cũ chỉ có thể giữ một con số cho mỗi ví: nó không thể diễn đạt phiếu thu so với phiếu chi, không thể cân đối bên nợ với bên có, và không cung cấp tài khoản kiểm soát nội bộ cho giá trị kho hay giá vốn hàng bán. Điều đó khiến sổ sách không thể kiểm toán và chặn mọi báo cáo thu/chi đáng tin cậy cho nhu cầu kế toán HKD/SME mà KICKO hướng tới.
Vì sao bây giờ: đợt refactor payment cần một nơi để các payment bán hàng vào sổ sách. Một schema account có kiểu cùng một lớp điều hướng payment integration biến mỗi payment đã thanh toán thành một chứng từ cân đối, được điều hướng chính xác. Đây là nền tảng cho mô hình ghi sổ kép của module Tài chính.
2. Goals & Non-Goals
Goals
- Thay thế mô hình ví cũ bằng một schema account có kiểu (tiền mặt, ngân hàng, QR, mobile-POS) cùng các tài khoản kiểm soát nội bộ, được seed theo từng merchant.
- Ghi nhận các dòng tiền dưới dạng chứng từ cân đối với các dòng ledger line bên nợ/bên có thông qua voucher service.
- Điều hướng hạch toán từ payment integration để một payment đã thanh toán tự động phát hành chứng từ đúng đối với tài khoản và danh mục được điều hướng.
- Liên kết mỗi chứng từ với chứng từ nguồn của nó và giữ cho việc hạch toán tự động idempotent dưới các event lặp lại.
Non-Goals
- Theo dõi ngân sách, P&L, và dự báo dòng tiền (Non-Goals của URD).
- Tự động hóa chi phí định kỳ / theo lịch.
- Chụp ảnh hóa đơn / OCR.
- Quy đổi đa tiền tệ trong cùng một chứng từ - một tiền tệ duy nhất, mặc định VND.
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Độ phủ hạch toán tự động | 100% payment bán hàng thành công tạo ra một chứng từ thu tương ứng |
| Tính toàn vẹn cân đối | Mọi chứng từ đã phát hành đều cân đối (bên nợ bằng bên có ở những nơi cả hai bên áp dụng); không có hạch toán mất cân đối |
| Idempotency | Một payment event phát lại không bao giờ tạo ra chứng từ thứ hai |
| Khả năng truy vết | Mọi chứng từ tự phát hành đều liên kết ngược về chứng từ nguồn (đơn bán, PO, biến động kho) |
| Seed tài khoản | Mọi merchant mới đều được tạo các tài khoản mặc định và kiểm soát nội bộ |
4. Personas & Use Cases
| Persona | Mục tiêu trong tính năng này |
|---|---|
| Owner | Thấy các dòng tiền được hạch toán thành các chứng từ cân đối đối với đúng tài khoản, có áp danh mục |
| Kế toán / Quản lý | Tin tưởng sổ sách phản ánh bán hàng và mua hàng mà không cần nhập liệu tay |
| Hệ thống (payment integration) | Điều hướng một payment đã thanh toán tới đúng tài khoản/danh mục và phát hành chứng từ tự động |
Core scenarios: một merchant mới được seed các tài khoản mặc định và kiểm soát có kiểu → một payment bán hàng thành công → payment integration điều hướng nó tới đúng tài khoản và danh mục → một chứng từ thu cân đối được tự phát hành, liên kết với đơn nguồn → một event phát lại bị bỏ qua (idempotent).
5. User Stories
- Là owner, tôi muốn mỗi merchant mới khởi đầu với các tài khoản mặc định và kiểm soát nội bộ, để sổ sách sẵn sàng trước lần bán hàng đầu tiên.
- Là kế toán, tôi muốn mọi dòng tiền được ghi nhận dưới dạng một chứng từ cân đối với các dòng nợ/có, để sổ sách là ghi sổ kép có thể kiểm toán, chứ không phải một con số tổng chạy.
- Là owner, tôi muốn một đơn bán đã thanh toán tự phát hành một chứng từ thu đối với tài khoản được điều hướng, để tôi không phải ghi nhận doanh thu thường lệ bằng tay.
- Là hệ thống, tôi muốn một ma trận payment integration ánh xạ một payment tới đúng tài khoản và danh mục, để việc hạch toán được điều hướng một cách xác định.
- Là kế toán, tôi muốn mỗi chứng từ liên kết với chứng từ nguồn của nó, để tôi có thể truy vết một hạch toán ngược về đơn hoặc biến động của nó.
- Là hệ thống, tôi muốn việc hạch toán tự động là idempotent, để một payment event lặp lại không bao giờ hạch toán trùng.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | Giữ các tài khoản tiền có kiểu (tiền mặt, ngân hàng, QR, mobile-POS) cùng các tài khoản kiểm soát nội bộ, sở hữu theo từng merchant | URD-WAL-001 |
| FR-2 | Seed các tài khoản mặc định và kiểm soát nội bộ của mỗi merchant khi merchant được tạo | URD-WAL-003 |
| FR-3 | Duy trì một tài khoản mặc định mỗi kiểu mỗi merchant để điều hướng hạch toán tự động | URD-WAL-002 |
| FR-4 | Ghi nhận các dòng tiền dưới dạng chứng từ kiểu thu, chi, chuyển khoản, hoặc điều chỉnh | URD-VCH-001 |
| FR-5 | Mọi chứng từ đều cân đối - bên nợ bằng bên có ở những nơi cả hai bên áp dụng | URD-VCH-002 |
| FR-6 | Tự phát hành một chứng từ thu khi một payment bán hàng thành công, phân loại là Sale | URD-VCH-003 |
| FR-7 | Tự phát hành một chứng từ chi cho vendor khi một purchase order được nhận | URD-VCH-004 |
| FR-8 | Tự hạch toán giá vốn hàng bán và điều chỉnh kho vào các tài khoản kiểm soát nội bộ khi kho biến động | URD-VCH-005 |
| FR-9 | Hạch toán tự động là idempotent - một event lặp lại không bao giờ hạch toán trùng | URD-VCH-006 |
| FR-10 | Liên kết mỗi chứng từ với chứng từ nguồn của nó (đơn bán, PO, biến động kho, thủ công) | URD-VCH-007 |
| FR-11 | Đánh số mỗi chứng từ đã phát hành theo từng merchant, từng kiểu, từng tháng | URD-VCH-008 |
| FR-12 | Hỗ trợ vòng đời thủ công (draft → phát hành, xóa một draft), chuyển khoản giữa tài khoản, và void bằng đảo ngược | URD-VCH-009..011 |
Toàn bộ nội dung yêu cầu và tiêu chí chấp nhận nằm trong Finance URD. PRD này tham chiếu chúng thay vì lặp lại.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Data integrity | Một chứng từ và các dòng ledger line của nó được ghi cùng nhau; mọi chứng từ đều cân đối (bên nợ bằng bên có ở những nơi cả hai bên áp dụng) |
| Idempotency | Hạch toán tự động được khóa theo event nguồn nên một event phát lại không bao giờ hạch toán trùng |
| Immutability | Chứng từ đã phát hành không bao giờ bị xóa cứng; việc sửa được thực hiện bằng đảo ngược cân đối (void), giữ nguyên bản gốc |
| Tenancy & authz | Tài khoản và chứng từ được scope theo merchant (x-merchant-id); người dùng không phải admin chỉ thấy tài khoản của các merchant được grant |
| Precision | Phép tính tiền tệ dùng float(value, 4); một tiền tệ duy nhất mỗi chứng từ, mặc định VND |
| i18n | Nhãn/kiểu/danh mục hiển thị cho người dùng là song ngữ ({ en, vi }) |
8. UX & Flows
Việc hạch toán được điều khiển bởi event, không phải bởi màn hình: merchant nhận một payment trong apps/client, và chứng từ thu được phát hành tự động bởi finance worker. Các màn hình quản lý tài khoản/chứng từ và chuyển khoản thủ công đang dần xuất hiện ở P2.
9. Data & Domain
| Entity | Vai trò |
|---|---|
finance-account | Một tài khoản tiền có kiểu (tiền mặt, ngân hàng, QR, mobile-POS) hoặc tài khoản kiểm soát nội bộ; giữ một số dư chạy |
Voucher | Một chứng từ kế toán cân đối cho một dòng tiền (thu, chi, chuyển khoản, điều chỉnh), liên kết với nguồn của nó |
LedgerLine | Một dòng nợ hoặc có bên trong một chứng từ, mang theo tài khoản, danh mục tùy chọn, số tiền, và snapshot số dư trước/sau |
payment-integration | Ma trận điều hướng ánh xạ một payment đã thanh toán tới đúng tài khoản và danh mục |
| Danh mục | Phân loại thu/chi áp lên một chứng từ |
Chỉ mang tính khái niệm - schema, enum, và bất biến đầy đủ nằm trong finance domain model.
10. Dependencies & Assumptions
Depends on
- Payment (payment integration của
@nx/core) - một payment bán hàng thành công là trigger điều hướng và hạch toán chứng từ thu. - Commerce / Merchant (
@nx/commerce) - một merchant mới được reconcile các tài khoản mặc định và kiểm soát nội bộ tự động. - Inventory (
@nx/inventory) - việc nhận purchase order và biến động kho điều khiển các chứng từ chi/giá vốn. - Categories (URD-CAT) - các danh mục hệ thống phân loại các chứng từ tự sinh.
Assumptions
- Các tài khoản kiểm soát nội bộ của merchant tồn tại trước khi việc hạch toán giá vốn được thực hiện.
- Tồn tại đúng một tài khoản mặc định mỗi kiểu mỗi merchant để điều hướng xác định.
- Payment event mang đủ ngữ cảnh (số tiền, phương thức, chứng từ nguồn) để điều hướng và liên kết chứng từ.
11. Risks & Open Questions
| Risk / question | Mitigation / status |
|---|---|
| Một payment event phát lại có thể hạch toán trùng | Hạch toán tự động là idempotent - khóa theo event nguồn |
| Hạch toán giá vốn trước khi tài khoản kiểm soát tồn tại | Tài khoản kiểm soát được seed khi tạo merchant; việc hạch toán yêu cầu chúng phải có sẵn |
| Đảo ngược/void một chứng từ gắn với một payment đã tất toán | Void bằng đảo ngược cân đối giữ nguyên bản gốc; đường bù trừ được theo dõi khi tính năng trưởng thành |
| Chỉ một tiền tệ | Ngoài phạm vi; được ghi nhận như một ràng buộc (mặc định VND) |
| Hạch toán tự động cho nhận PO, biến động kho, chuyển khoản, và void vẫn đang trưởng thành | Được theo dõi dưới cùng feature VCH khi chúng được giao; redesign account + hạch toán điều khiển bởi payment là nền tảng |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P1 (nền tảng) - xem feature catalog của URD |
| Rollout | Tất cả merchant; không feature flag |
| Migration | Redesign schema account (finance-wallet → finance-account); seed tài khoản mặc định và kiểm soát nội bộ theo từng merchant |
| Launch criteria | Đơn bán đã thanh toán tự phát hành một chứng từ thu cân đối, có liên kết; event phát lại không hạch toán trùng; merchant mới được tạo các tài khoản đã seed |
| Monitoring | Khối lượng và tỷ lệ lỗi hạch toán tự động, kiểm tra chứng từ mất cân đối, số lượt bỏ qua do idempotency |
13. FAQ
Cái gì thay thế mô hình ví cũ? Một schema finance-account có kiểu với các hằng kiểu tài khoản cùng các tài khoản kiểm soát nội bộ (kho, giá vốn hàng bán), được seed theo từng merchant.
Merchant có ghi nhận doanh thu bán hàng bằng tay không? Không - một payment bán hàng thành công tự phát hành một chứng từ thu, phân loại là Sale, đối với tài khoản được điều hướng.
Làm sao một payment biết hạch toán vào tài khoản nào? Một ma trận payment-integration điều hướng một payment đã thanh toán tới đúng tài khoản và danh mục; sau đó finance worker phát hành chứng từ.
Cái gì ngăn một event phát lại hạch toán trùng? Hạch toán tự động là idempotent - một event lặp lại không bao giờ tạo ra chứng từ thứ hai.
Một chứng từ đã phát hành có thể bị xóa không? Không - chứng từ đã phát hành được void bằng một đảo ngược cân đối, giữ nguyên bản gốc để kiểm toán.
References
- URD: Finance - Vouchers & Posting · Accounts
- Builds on: Categories · Ledger Lines
- Module: Tài chính - tổng quan + truy vết
- Developer: @nx/finance · domain model