PRD: Ví, voucher & sổ cái
| Module | Thanh toán & Giao dịch (CORE-08) | PRD ID | PRD-WAL-001 |
| Status | Shipped | Owner | Payment & Finance squad |
| Date | 2026-05-28 | Version | v1.0 |
| Packages | @nx/finance · @nx/payment · @nx/ledger | URD | WAL · VCH · CAT |
TL;DR
Cung cấp cho merchant một nơi để ghi nhận dòng tiền đằng sau mỗi khoản thanh toán: các tài khoản (ví) nơi số dư tiền mặt, ngân hàng, QR và mobile POS được giữ, các voucher ghi sổ kép cân bằng cho mọi chuyển động, và auto-posting ghi sổ các giao dịch bán hàng hoàn tất, đơn mua hàng đã nhận, và chuyển động kho - mỗi sự kiện đúng một lần. Kết quả: chủ có thể thấy số dư thực và đối chiếu sổ sách, thay vì chỉ biết một khoản thanh toán đã được đánh dấu paid.
1. Context & Problem
KICKO có thể chuyển một khoản thanh toán sang trạng thái paid, nhưng không có nơi nào để ghi nhận dòng tiền phát sinh. Không có nơi để số dư tiền mặt, ngân hàng hay QR được giữ, không có ghi sổ kép, và không có cách phân loại thu và chi - nên một merchant có thể thu tiền nhưng không thể thấy số dư hay đối chiếu sổ sách. Việc theo dõi chi phí và khả năng nhìn thấy tài chính ở cấp chủ bị chặn, đây là yêu cầu bắt buộc cho nghiệp vụ kế toán HKD/SME mà KICKO hướng tới.
Tính năng này xây dựng phần tài chính trên nền vòng đời thanh toán hiện có: các tài khoản nơi tiền được giữ, voucher cân bằng cho mọi chuyển động, danh mục thu/chi được seed sẵn, và auto-posting kết nối các sự kiện bán hàng, mua hàng và kho vào sổ cái. "Account" là thuật ngữ chuẩn cho nơi tiền được giữ, thay thế tên gọi "wallet" trước đây.
2. Goals & Non-Goals
Goals
- Tài khoản tài chính loại CASH, BANK, QR code và mobile POS, mỗi tài khoản có số dư đầu kỳ và số dư hiện tại, các tài khoản kiểm soát nội bộ được duy trì tự động, và một tài khoản mặc định cho mỗi merchant nhận thu nhập bán hàng được auto-post.
- Voucher ghi sổ kép cân bằng ở bốn loại - RECEIPT, PAYMENT, TRANSFER, ADJUSTMENT - với vòng đời
DRAFT → ISSUED → VOIDED, đánh số theo từng merchant/từng loại, và hủy-bằng-bút-toán-đảo (không xóa cứng). - Auto-posting từ sự kiện nghiệp vụ: một khoản thanh toán bán hàng hoàn tất post một RECEIPT, một đơn mua hàng đã nhận post một PAYMENT, và một lần xuất/điều chỉnh kho post voucher tương ứng - mỗi cái idempotent theo source event id.
- Phân loại danh mục thu/chi: 14 danh mục hệ thống được seed sẵn (được bảo vệ, gắn loại INCOME hoặc EXPENSE) cùng các danh mục tùy chỉnh riêng từng merchant với phân cấp cha.
Non-Goals
- Hoàn tiền / đảo giao dịch có cấu trúc qua lại nhà cung cấp thanh toán gốc (URD §7 - Planned).
- Bảng đối chiếu tiền mặt theo ca (URD §7 - Planned).
- Quy đổi đa tiền tệ và đối chiếu liên tiền tệ.
- Phát hành hóa đơn thuế - thuộc module Thuế & Hóa đơn.
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Độ phủ sổ cái | 100% khoản thanh toán bán hàng hoàn tất, PO đã nhận, và chuyển động kho đều post voucher tương ứng |
| Tính toàn vẹn số dư | Mọi voucher đều cân bằng (tổng DEBIT = tổng CREDIT); không có bút toán mất cân bằng |
| Idempotency | Mỗi source event post tối đa một voucher, kể cả khi gửi lại - không có trùng lặp |
| Đối chiếu | Chủ có thể đọc số dư tài khoản trực tiếp khớp với tổng các dòng sổ cái đã post |
4. Personas & Use Cases
| Persona | Mục tiêu trong tính năng này |
|---|---|
| Chủ (Owner) | Thấy số dư thực, phân loại thu/chi, đối chiếu sổ sách, hủy bút toán sai |
| Quản lý (Manager) | Quản lý tài khoản, tạo voucher thủ công, xem sổ cái |
| Thu ngân (Cashier) | Thu tiền để auto-post thu nhập (không truy cập sổ cái) |
Core scenarios: chủ tạo các tài khoản nơi tiền được giữ → một khoản thanh toán bán hàng hoàn tất auto-post một RECEIPT cân bằng vào tài khoản mặc định → một PO đã nhận hoặc chuyển động kho auto-post voucher tương ứng một lần → chủ xem sổ cái, phân loại bút toán theo danh mục, và hủy bất kỳ sai sót nào qua một bút toán đảo cân bằng.
5. User Stories
- Là một chủ, tôi muốn tạo các tài khoản loại tiền mặt, ngân hàng, QR và mobile POS với số dư đầu kỳ, để tiền có nơi để giữ và tôi thấy được số dư hiện tại.
- Là một chủ, tôi muốn một khoản thanh toán bán hàng hoàn tất auto-post một RECEIPT cân bằng vào tài khoản mặc định của tôi, để thu nhập xuất hiện trong sổ cái mà không cần nhập tay.
- Là một chủ, tôi muốn một đơn mua hàng đã nhận và một lần xuất/điều chỉnh kho auto-post voucher tương ứng đúng một lần, để chi phí và chuyển động kho tự đối chiếu.
- Là một chủ, tôi muốn tạo một voucher thủ công (draft → issue) và hủy bằng bút toán đảo, để chỉnh sửa sổ sách mà không bao giờ xóa lịch sử tài chính.
- Là một chủ, tôi muốn thu và chi được phân loại theo danh mục, với danh mục tùy chỉnh nằm dưới một danh mục cha, để báo cáo tiền đến từ đâu và đi về đâu.
- Là một quản lý, tôi muốn chỉ thấy các tài khoản của những merchant mà tôi được grant quyền truy cập, để tầm nhìn tôn trọng scope của tôi.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | Tạo tài khoản loại CASH, BANK, QR code, mobile POS; mỗi tài khoản theo dõi số dư đầu kỳ và số dư hiện tại | URD-WAL-001 · URD-WAL-003 |
| FR-2 | Duy trì tài khoản kiểm soát nội bộ (ví dụ kho, COGS) tự động | URD-WAL-002 |
| FR-3 | Một tài khoản mặc định cho mỗi merchant nhận thu nhập bán hàng được auto-post | URD-WAL-004 |
| FR-4 | Các role không phải chủ chỉ thấy tài khoản của những merchant mà họ được grant quyền | URD-WAL-005 |
| FR-5 | Mọi voucher đều cân bằng (tổng DEBIT = tổng CREDIT) ở bốn loại RECEIPT / PAYMENT / TRANSFER / ADJUSTMENT | URD-VCH-001..002 |
| FR-6 | Một khoản thanh toán bán hàng hoàn tất auto-post một RECEIPT vào tài khoản mặc định; một PO đã nhận auto-post một PAYMENT; một lần xuất/điều chỉnh kho auto-post voucher tương ứng | URD-VCH-003..005 |
| FR-7 | Mỗi voucher ghi lại source event của nó để cùng một sự kiện chỉ post một lần (idempotent) | URD-VCH-006 |
| FR-8 | Voucher được đánh số theo từng merchant và từng loại (ví dụ PT / PC / PCK / PKT) | URD-VCH-007 |
| FR-9 | Chủ có thể tạo voucher thủ công (draft → issue) và hủy qua bút toán đảo cân bằng (không xóa cứng) | URD-VCH-008..009 |
| FR-10 | Một lần chuyển tiền giữa hai tài khoản được ghi nhận là voucher tổng-bằng-không | URD-VCH-010 |
| FR-11 | 14 danh mục hệ thống được seed sẵn, được bảo vệ, gắn loại INCOME hoặc EXPENSE; chủ có thể thêm danh mục tùy chỉnh với phân cấp cha | URD-CAT-001..003 |
Toàn văn requirement và tiêu chí chấp nhận nằm trong URD Thanh toán & Giao dịch. PRD này tham chiếu thay vì viết lại.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Toàn vẹn dữ liệu | Mọi voucher phải cân bằng (tổng DEBIT = tổng CREDIT); số dư hiện tại của tài khoản luôn bằng tổng các dòng sổ cái đã post của nó |
| Idempotency | Mỗi sự kiện nghiệp vụ post tối đa một voucher, khóa theo source event id; gửi lại không có hiệu ứng lần hai |
| Bất biến | Không xóa cứng lịch sử tài chính - chỉnh sửa thực hiện qua voucher bút toán đảo cân bằng |
| Tenancy & authz | Mọi tài khoản, voucher và danh mục đều scoped theo merchant (x-merchant-id); tầm nhìn của role không phải chủ giới hạn ở các merchant được grant |
| Độ chính xác | Tính toán tiền tệ dùng float(value, 4) |
| i18n | Nhãn/loại/trạng thái hiển thị cho người dùng là song ngữ ({ en, vi }) |
8. UX & Flows
Các màn hình chính (trong apps/client): danh sách và tạo tài khoản, màn hình voucher (bảng giao dịch chỉnh sửa được, phần party/transaction, banner hủy, tổng quan), và tổng quan sổ cái với số dư và lịch sử voucher.
9. Data & Domain
| Entity | Vai trò |
|---|---|
FinanceAccount | Nơi tiền được giữ - loại CASH / BANK / QR_CODE / MOBILE_POS, số dư đầu kỳ + hiện tại; bao gồm tài khoản kiểm soát nội bộ |
FinanceVoucher | Một bút toán cân bằng thuộc một loại (RECEIPT / PAYMENT / TRANSFER / ADJUSTMENT) với vòng đời DRAFT → ISSUED → VOIDED và source event id |
| Dòng sổ cái (transaction) | Một dòng DEBIT hoặc CREDIT trong một voucher, tác động số dư của một tài khoản, có thể được phân loại theo danh mục |
FinanceCategory | Nhãn thu/chi - 14 danh mục hệ thống được bảo vệ cùng các danh mục tùy chỉnh với phân cấp cha |
| Voucher sequence | Nguồn đánh số theo từng merchant, từng loại |
| Ledger snapshot / job | Các bút toán snapshot và queue job sổ cái (tách API + worker, tiến độ qua WebSocket) |
Chỉ mang tính khái niệm - schema đầy đủ và bất biến nằm trong finance domain model.
10. Dependencies & Assumptions
Depends on
- Vòng đời thanh toán (URD-PAY) - một khoản thanh toán hoàn tất là thứ kích hoạt auto-posting RECEIPT.
- Đơn hàng (module Đơn hàng) - sale chuyển tiếp finance metadata trên
payment.success. - Kho hàng (module Kho hàng) - sự kiện nhận purchase-order và xuất/điều chỉnh kho dẫn động các bút toán PAYMENT / ADJUSTMENT.
@nx/ledger- bộ máy ledger snapshot và job (tách API/worker).
Assumptions
- Mỗi merchant có một tài khoản mặc định được chỉ định để nhận thu nhập bán hàng auto-post.
- 14 danh mục hệ thống được seed khi sổ cái tài chính của merchant khởi tạo.
- Sự kiện nghiệp vụ mang theo một source event id ổn định cho idempotency.
11. Risks & Open Questions
| Risk / question | Mitigation / status |
|---|---|
| Post trùng nếu một sự kiện bị gửi lại | Mỗi voucher ghi lại source event id; việc post là idempotent |
| Một voucher mất cân bằng có thể làm hỏng sổ cái | Bất biến được enforce: tổng DEBIT phải bằng tổng CREDIT trước khi issue |
| Hủy so với giữ lịch sử tài chính | Hủy là một bút toán đảo cân bằng; voucher gốc không bao giờ bị xóa cứng |
| Không hỗ trợ đa tiền tệ | Ngoài scope; mỗi voucher một tiền tệ được ghi nhận như một ràng buộc |
| Đảo tài chính trên một nguồn đã bị đảo (ví dụ revert PO) | Mở: định nghĩa đường bù trừ xuyên module |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P2 (sổ cái tài chính) - xem URD feature catalog |
| Rollout | Tất cả merchant; không feature flag |
| Migration | Không có migration dữ liệu; tài khoản tài chính và 14 danh mục hệ thống được seed khi khởi tạo merchant |
| Launch criteria | Auto-post từ bán hàng/mua hàng/kho được verify idempotent; mọi voucher cân bằng; số dư hiện tại khớp tổng dòng sổ cái; tạo thủ công + hủy-bằng-bút-toán-đảo được verify |
| Monitoring | Số lượng voucher theo merchant, tỷ lệ lỗi auto-post, kiểm tra nhất quán số-dư-vs-sổ-cái, số lần va chạm idempotency |
13. FAQ
Tiền đi đâu khi một giao dịch bán hàng được thanh toán? Một khoản thanh toán bán hàng hoàn tất auto-post một RECEIPT voucher cân bằng vào tài khoản mặc định của merchant - không cần nhập tay.
Có thể xóa một voucher nếu nó sai không? Không. Lịch sử tài chính không bao giờ bị xóa cứng; một voucher đã issue được sửa bằng một bút toán đảo cân bằng để hủy nó trong khi vẫn giữ bản gốc.
Điều gì ngăn cùng một sự kiện post hai lần? Mỗi voucher ghi lại source event id của nó; nếu sự kiện được gửi lại, không có voucher thứ hai được tạo.
Khác biệt giữa một tài khoản và một danh mục là gì? Tài khoản là nơi tiền được giữ (tiền mặt, ngân hàng, QR, mobile POS); danh mục phân loại một chuyển động thuộc loại thu hay chi nào.
Tôi có thể đổi tên hoặc xóa các danh mục được seed không? 14 danh mục hệ thống được bảo vệ và không thể xóa. Bạn có thể thêm các danh mục tùy chỉnh riêng từng merchant, có thể nằm dưới một danh mục cha.
Cái này có phát hành hóa đơn thuế không? Không - việc phát hành hóa đơn thuế thuộc module Thuế & Hóa đơn.
References
- URD: Thanh toán & Giao dịch - Accounts & Wallets · Vouchers & Ledger · Categories
- Builds on: Payment Lifecycle
- Module: Thanh toán & Giao dịch - overview + traceability
- Developer: @nx/finance · @nx/payment · @nx/ledger · finance domain model