PRD: Tách và gộp check
| Module | Bán hàng | PRD ID | PRD-CHK-001 |
| Trạng thái | Sẵn sàng dev | FEAT | CHK · BANA-1433 |
| Epic | — | Plane | BANA-1563 |
| Ngày | 2026-04-02 | Phiên bản | v1.0 |
| Gói | @nx/sale | URD | CHK |
| Surface | Sale · POS | ||
| Phụ trách | Phát Nguyễn | ||
TL;DR
Cho phép quán phục vụ tại bàn tách đơn của một bàn thành nhiều check thanh toán độc lập - mỗi check có item, số lượng riêng, thậm chí khách hàng riêng - và gộp các đơn riêng lẻ lại. Đơn tự hoàn tất khi tất cả check đã thanh toán. Nhóm khách tự trả phần của mình mà không cần nhân viên can thiệp thủ công.
Trạng thái triển khai: aggregate check, phân bổ theo từng check, tách, gộp, hoàn tác và hoàn tất theo thanh toán đều đã dựng. Hai điểm thiết kế khác với spec này trong code hiện tại: (1) các broadcast sự kiện check / gộp-đơn real-time chưa được nối (các phát sự kiện này đang bị tắt; chỉ có một cập nhật cấp đơn phát ra khi các check hoàn tất đơn); và (2) guard tách check hiện chỉ nhận đơn DRAFT, không phải đơn đã checkout (PROCESSING) như spec này giả định.
1. Context & Problem
Quán phục vụ tại bàn thường xuyên gặp bàn khách muốn trả riêng - mỗi người một phần, đôi khi theo hồ sơ khách hàng khác nhau - và cũng cần gộp các đơn được mở riêng lẻ cho cùng một nhóm. Coi đơn hàng như một đơn vị thanh toán duy nhất chặn cả hai nhu cầu: không có cách phân bổ item cụ thể giữa các nhóm thanh toán, không có cách hoàn tất đơn theo từng check, và không có cách gọn gàng để gộp đơn này vào đơn khác.
Increment này xây dựng năng lực check trên nền vòng đời đơn hàng hiện có - phân bổ item vào từng check, đối soát việc hoàn tất, cùng luồng gộp đơn để kết hợp hai đơn hàng nháp - lấp một khoảng trống lớn cho F&B và mọi tình huống hoá đơn nhiều khách mà BANA hướng tới.
2. Goals & Non-Goals
Goals
- Tách một đơn đã checkout thành nhiều check thanh toán độc lập.
- Phân bổ item theo từng check - gán item và số lượng cụ thể cho mỗi check, có chặn phân bổ vượt mức.
- Một khách hàng khác cho mỗi check, để mỗi nhóm thanh toán mang liên kết khách hàng riêng.
- Đơn hàng tự động hoàn tất khi tất cả check của nó hoàn tất, được điều khiển bởi luồng thanh toán thành công.
- Hoàn tác việc tách khi chưa có check nào được thanh toán.
- Gộp đơn hàng cho hai đơn PROCESSING trong cùng merchant.
- Sự kiện check real-time (tạo / cập nhật / gộp / hoàn tác / đã trả) và một sự kiện gộp-đơn để giữ màn hình POS và bếp đồng bộ.
Non-Goals
- Luồng hoàn tiền / trả hàng - Non-Goal của URD (Planned).
- Tích hợp nhà cung cấp thanh toán - chỉ nối phần bàn giao qua luồng thanh toán thành công (xem Thanh toán).
- Engine thuế / phát hành hoá đơn điện tử theo từng check.
- Thay đổi tồn kho khi phân bổ check (xem Kho hàng).
3. Success Metrics
| Metric | Mục tiêu / tín hiệu |
|---|---|
| Mức dùng tách | Tỷ lệ đơn phục vụ tại bàn dùng check khi có yêu cầu tách hoá đơn |
| Toàn vẹn phân bổ | Không check nào được phân bổ vượt quá số lượng item còn lại của đơn |
| Độ chính xác hoàn tất | Đơn chỉ hoàn tất khi tất cả check của nó hoàn tất - không đóng sớm/muộn |
| An toàn hoàn tác | Hoàn tác thành công khi chưa check nào được thanh toán; không bao giờ sau khi một check đã trả |
| Độ trễ đồng bộ | POS / bếp phản ánh sự kiện check & gộp theo real-time (mục tiêu; broadcast check/gộp riêng chưa được nối) |
4. Personas & Use Cases
| Persona | Mục tiêu trong tính năng này |
|---|---|
| Thu ngân | Tách hoá đơn của một bàn thành các check, phân bổ item, thu tiền theo từng check, gộp đơn |
| Phục vụ / Lễ tân | Kết hợp các đơn mở riêng cho cùng một nhóm khách |
| Quản lý / Chủ | Tin rằng một đơn đóng đúng lúc mọi check đã thanh toán xong |
Kịch bản chính: đơn đã checkout được tách thành các check → phân bổ item, số lượng (và khách hàng riêng nếu cần) cho từng check → khách tự thanh toán check của mình → đơn hoàn tất khi tất cả check xong. Chưa check nào được trả thì có thể hoàn tác; hai đơn nháp cùng nhóm khách có thể gộp lại.
5. User Stories
- Là một thu ngân, tôi muốn tách một đơn đã checkout thành nhiều check, để mỗi khách tự trả phần của mình.
- Là một thu ngân, tôi muốn phân bổ item và số lượng cụ thể cho một check, để việc tách hoá đơn phản ánh đúng những gì mỗi khách đã dùng.
- Là một thu ngân, tôi muốn gắn một khách hàng khác cho một check, để mỗi nhóm thanh toán giữ liên kết khách hàng riêng.
- Là một thu ngân, tôi muốn đơn hàng tự động hoàn tất khi tất cả check đã trả, để không phải đóng đơn thủ công.
- Là một thu ngân, tôi muốn hoàn tác việc tách khi chưa check nào được trả, để sửa một lần tách nhầm.
- Là một phục vụ, tôi muốn gộp hai đơn cho cùng một nhóm khách, để một hoá đơn duy nhất đại diện cho cả bàn.
6. Functional Requirements
| # | Yêu cầu | URD ref |
|---|---|---|
| FR-1 | Tách một đơn đã checkout thành nhiều check thanh toán độc lập | URD-CHK-001 |
| FR-2 | Phân bổ item và số lượng cụ thể cho mỗi check, có chặn phân bổ vượt mức | URD-CHK-002 |
| FR-3 | Cho phép một khách hàng khác cho mỗi check | URD-CHK-003 |
| FR-4 | Đơn tự động hoàn tất khi tất cả check của nó hoàn tất, điều khiển bởi luồng thanh toán thành công | URD-CHK-004 |
| FR-5 | Hoàn tác việc tách khi chưa có check nào được thanh toán | URD-CHK-005 |
| FR-6 | Tách một đơn thành nhiều đơn, chỉ ở DRAFT | URD-ORD-012 |
| FR-7 | Gộp nhiều đơn, chỉ ở DRAFT / cùng merchant | URD-ORD-013 |
| FR-8 | Phát sự kiện check real-time (tạo / cập nhật / gộp / hoàn tác / đã trả) và một sự kiện gộp-đơn - chưa triển khai: các phát sự kiện check/gộp đang bị tắt; hiện chỉ có một cập nhật cấp đơn phát ra khi các check hoàn tất đơn | URD-CHK-001..005 |
Toàn văn yêu cầu và tiêu chí chấp nhận nằm trong URD Đơn hàng. PRD này tham chiếu chúng thay vì lặp lại.
7. Non-Functional Requirements
| Khía cạnh | Yêu cầu |
|---|---|
| Toàn vẹn dữ liệu | Phân bổ theo từng check không bao giờ vượt số lượng còn lại của đơn; trạng thái hoàn tất là dẫn xuất, không gán tay |
| Nhất quán | Tách, phân bổ, hoàn tác và gộp là một thao tác duy nhất (transactional) - lỗi một phần không để lại check viết dở |
| Tenancy & authz | Mọi thao tác giới hạn trong merchant của chính người dùng; gộp giới hạn trong cùng merchant; có kiểm soát theo permission |
| Real-time | Thay đổi trạng thái check và gộp dự kiến broadcast qua cập nhật real-time để đồng bộ POS/KDS - các broadcast check/gộp riêng chưa được nối (chỉ một cập nhật cấp đơn phát ra khi đơn hoàn tất theo check) |
| Khả năng khôi phục | Các bản ghi có thể khôi phục, không bị xoá cứng |
| i18n | Nhãn/trạng thái hiển thị cho người dùng là song ngữ ({ en, vi }) |
8. UX & Flows
Màn hình chính: chi tiết đơn với thao tác tách hoá đơn, trình phân bổ theo từng check, thanh toán theo check, và gộp đơn.
9. Data & Domain
| Entity | Vai trò |
|---|---|
| Check | Một nhóm thanh toán độc lập trong một đơn - trạng thái, khách hàng tuỳ chọn, tổng tiền |
| Phân bổ item theo từng check | Một phân bổ theo check của một order item - tham chiếu item + số lượng |
| Order | Đơn cha; hoàn tất khi tất cả check của nó hoàn tất |
| Sự kiện real-time | Sự kiện check real-time dự kiến (tạo / cập nhật / gộp / hoàn tác / đã trả) và một sự kiện gộp-đơn - chưa được phát trong code hiện tại |
Chỉ ở mức khái niệm - schema đầy đủ và bất biến trong domain model của sale.
10. Dependencies & Assumptions
Phụ thuộc vào
- Vòng đời Sale Order (URD-ORD) - một check được tách từ một đơn đã checkout; gộp thao tác trên đơn nháp/processing.
- Thanh toán - luồng thanh toán thành công điều khiển việc hoàn tất check và, qua đó, hoàn tất đơn.
- Khách hàng - liên kết khách hàng tuỳ chọn theo từng check.
Giả định
- Đơn đã được checkout (PROCESSING) trước khi tách thành các check.
- Xác nhận thanh toán đến qua luồng thanh toán thành công để đánh dấu một check đã trả.
11. Risks & Open Questions
| Rủi ro / câu hỏi | Giảm thiểu / trạng thái |
|---|---|
| Phân bổ có thể vượt số lượng còn lại | Chặn phân bổ vượt mức ngay tại thời điểm phân bổ |
| Hoàn tác sau khi một check đã trả | Chỉ cho hoàn tác khi chưa check nào được trả |
| Tranh chấp khi nhiều check trả gần như đồng thời | Hoàn tất dẫn xuất từ tất-cả-check-hoàn-tất; điều khiển bởi luồng thanh toán thành công, idempotent |
| Tồn kho không thay đổi theo phân bổ check | Ngoài phạm vi - thuộc Kho hàng |
| Thuế / hoá đơn điện tử theo từng check | Ngoài phạm vi - thuộc Thuế & Hóa đơn |
12. Release Plan & Launch Criteria
| Khía cạnh | Kế hoạch |
|---|---|
| Phase | P2 - xem danh mục tính năng Đơn hàng (MoSCoW: Should) |
| Rollout | Mọi merchant; không có feature flag |
| Migration | Aggregate check mới; không backfill dữ liệu |
| Tiêu chí ra mắt | Tách → phân bổ → trả-theo-từng-check → đơn tự hoàn tất được kiểm chứng đầu-cuối; hoàn tác được kiểm chứng khi chưa trả; gộp đơn được kiểm chứng cho cùng merchant/branch |
| Giám sát | Lượng tách theo merchant, tỷ lệ từ chối phân bổ vượt mức, nhất quán hoàn tất check-vs-đơn, việc giao sự kiện |
13. FAQ
Khi nào một đơn có thể tách thành các check? Theo thiết kế, một check được tách từ một đơn đã checkout (PROCESSING), nhưng trong code hiện tại guard tách chỉ nhận đơn DRAFT (cần chỉnh status check cho khớp ý định). Bản thân việc tách/gộp đơn chỉ ở DRAFT.
Mỗi check có thể có khách hàng riêng không? Có - có thể gắn một khách hàng khác cho mỗi check.
Khi nào đơn hoàn tất? Tự động, khi tất cả check của nó hoàn tất - điều khiển bởi luồng thanh toán thành công, không phải đóng thủ công.
Tôi có thể hoàn tác một lần tách không? Có, bằng cách hoàn tác - nhưng chỉ khi chưa check nào được thanh toán.
Tách hoá đơn có thay đổi tồn kho hay phát hành hoá đơn theo từng check không? Không - thay đổi tồn kho thuộc Kho hàng và phát hành thuế / hoá đơn điện tử nằm ngoài phạm vi ở đây.
References
- URD: Đơn hàng - CHK (Tách hoá đơn) · ORD (tách/gộp)
- Liên quan: Thanh toán · Kho hàng
- Module: Đơn hàng - URD
- Developer: @nx/sale · domain model