PRD: Kênh bán
| Module | Commerce (CORE-03) | PRD ID | PRD-SC-001 |
| Status | Shipped | Owner | Commerce squad |
| Date | 2026-06-01 | Version | v1.0 |
| Packages | @nx/commerce · apps/bo · apps/client | URD | SC |
TL;DR
Cho phép một merchant bán qua một hoặc nhiều kênh bán (dine-in, takeout, delivery), được tạo tự động khi onboarding và sau đó quản lý qua aggregate của merchant hoặc thêm hàng loạt vào một merchant có sẵn. Mỗi kênh mang các liên kết inventory-location và cấu hình terminal/print riêng, một slug duy nhất theo từng merchant, và một định danh hệ thống - và không thể vô hiệu hóa khi vẫn còn các đơn hàng đang hoạt động phụ thuộc vào nó. Kết quả: kiểm soát ở cấp kênh việc lấy stock từ đâu và hóa đơn in như thế nào, không còn các lượt tra cứu rộng theo toàn merchant tùy tiện.
1. Context & Problem
Một merchant bán qua nhiều kênh - dine-in, takeout, delivery - và mỗi kênh hành xử khác nhau: nó có thể lấy stock từ một location khác, in tới một terminal khác, hoặc dùng một mẫu hóa đơn khác. Commerce vốn đã tạo (các) kênh mặc định một thao tác duy nhất khi onboarding và quản lý chúng bên trong aggregate của merchant, nhưng kênh chỉ là một bản ghi mỏng: inventory location và cấu hình terminal/print nằm ở scope merchant, nên hai kênh dưới cùng một merchant không thể khác nhau, và các lượt tra cứu cấu hình được khóa theo merchant thay vì theo kênh. Cũng không có bộ chặn nào ngăn việc vô hiệu hóa một kênh trong khi nó vẫn còn các đơn hàng đang hoạt động phụ thuộc, dẫn tới nguy cơ bỏ rơi các giao dịch đang dang dở.
Increment này nâng kênh bán thành một đơn vị cấu hình hạng nhất: các liên kết location và cấu hình terminal/print chuyển lên kênh, các lượt tra cứu cấu hình được scope theo sale-channel-id, và một bộ chặn vô hiệu hóa bảo vệ các đơn hàng đang dang dở - cùng với các màn hình back-office được làm lại để người dùng quản lý tất cả qua một giao diện nhất quán.
2. Goals & Non-Goals
Goals
- (Các) kênh mặc định được tạo khi onboarding và quản lý như một phần của các thao tác aggregate của merchant.
- Tạo hàng loạt kênh cho một merchant có sẵn, với slug duy nhất theo từng merchant và một định danh hệ thống được sinh ra khi tạo.
- Gắn các inventory location vào một kênh (tùy chọn khi tạo) để một kênh lấy stock từ (các) location riêng của nó.
- Mang cấu hình terminal và print-template trên kênh, với các lượt tra cứu cấu hình được scope theo sale-channel-id.
- Chặn việc vô hiệu hóa một kênh vẫn còn các đơn hàng đang hoạt động phụ thuộc.
- Các màn hình tạo/sửa back-office nhất quán cho việc quản lý kênh.
Non-Goals
- CRUD kênh bán độc lập bên ngoài aggregate của merchant - Dự kiến (xem Commerce URD Non-Goals).
- Phân cấp kênh (cha-con) - URD-SC-006 là ưu tiên Could và nằm ngoài increment này.
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Cấu hình theo kênh | 100% lượt tra cứu terminal/print/cấu hình được khóa theo sale-channel-id (không có fallback rộng theo merchant) |
| Độ phủ onboarding | Mọi merchant đã onboarding đều có ít nhất một kênh mặc định kèm định danh hệ thống |
| Tính toàn vẹn slug | Không có slug kênh trùng trong cùng một merchant |
| An toàn đơn hàng | Không có lượt vô hiệu hóa kênh đang còn đơn hàng hoạt động phụ thuộc |
4. Personas & Use Cases
| Persona | Mục tiêu trong tính năng này |
|---|---|
| Owner | Thiết lập các kênh bán khi onboarding; kiểm soát mỗi kênh lấy stock từ đâu và in như thế nào |
| Manager | Thêm/sửa kênh cho một merchant có sẵn, liên kết inventory location, quản lý cấu hình terminal/print |
| Thu ngân (client) | Bán đúng kênh để stock được lấy từ đúng location và hóa đơn in đúng |
Core scenarios: onboarding tạo kênh mặc định → owner thêm hàng loạt hoặc quản lý qua aggregate thêm kênh → liên kết inventory location và cấu hình terminal/print theo từng kênh → vô hiệu hóa một kênh không dùng (bị chặn nếu còn đơn hàng hoạt động phụ thuộc).
5. User Stories
- Là một owner, tôi muốn (các) kênh mặc định được tạo khi onboarding, để tôi có thể bán ngay lập tức.
- Là một manager, tôi muốn tạo hàng loạt kênh cho một merchant có sẵn, để mở rộng kênh bán mà không cần chạy lại onboarding.
- Là một manager, tôi muốn mỗi kênh liên kết (các) inventory location riêng, để một kênh lấy stock từ đúng nơi.
- Là một manager, tôi muốn cấu hình terminal và print-template trên kênh, để hóa đơn in tới đúng thiết bị với đúng mẫu.
- Là một manager, tôi muốn một slug duy nhất cho mỗi kênh trong merchant, để các định danh kênh không xung đột.
- Là một owner, tôi muốn một kênh đang còn đơn hàng hoạt động phụ thuộc được bảo vệ khỏi việc vô hiệu hóa, để không bỏ rơi các giao dịch đang dang dở.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | (Các) kênh mặc định được tạo khi onboarding | URD-SC-001 |
| FR-2 | Kênh được quản lý như một phần của các thao tác aggregate của merchant | URD-SC-002 |
| FR-3 | Kênh có thể được tạo hàng loạt cho một merchant có sẵn | URD-SC-003 |
| FR-4 | Slug kênh là duy nhất trong cùng một merchant | URD-SC-004 |
| FR-5 | Một định danh hệ thống duy nhất được sinh ra khi tạo và không thể sửa | URD-SC-005 |
| FR-6 | Kênh có thể được vô hiệu hóa hoặc lưu trữ; việc vô hiệu hóa bị chặn khi kênh còn đơn hàng hoạt động phụ thuộc | URD-SC-007 |
| FR-7 | Inventory location có thể gắn vào một kênh (tùy chọn khi tạo); một kênh lấy stock từ (các) location đã liên kết | URD-SC-002 |
| FR-8 | Cấu hình terminal và print-template được mang trên kênh; các lượt tra cứu cấu hình được scope theo sale-channel-id | URD-SC-002 |
Toàn văn yêu cầu và tiêu chí nghiệm thu nằm trong Commerce URD. PRD này tham chiếu chúng thay vì lặp lại.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Toàn vẹn dữ liệu | Kênh + các liên kết inventory-location được ghi một thao tác duy nhất bên trong aggregate; một partial-unique index đảm bảo một liên kết duy nhất cho mỗi (saleChannelId, inventoryLocationId) |
| Tenancy & authz | Mọi thao tác được scope theo từng merchant (x-merchant-id); kiểm soát bởi các permission kênh bán của commerce |
| Nhất quán | Aggregate create/update áp dụng quy tắc smart-update của merchant (chỉ ID = xóa, ID+data = cập nhật, không ID = tạo) một thao tác duy nhất |
| An toàn đơn hàng | Một kênh còn đơn hàng hoạt động phụ thuộc không thể bị vô hiệu hóa (được kiểm tra trước khi đổi status) |
| i18n | Nhãn kênh 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/bo): màn hình danh sách, tạo và sửa kênh bán với một SaleChannelForm / form thông tin chung dùng chung; bộ chọn kênh cùng ràng buộc inventory-location/terminal xuất hiện trong apps/client tại điểm bán.
9. Data & Domain
| Entity | Vai trò |
|---|---|
SaleChannel | Bản ghi kênh bán - tên, slug duy nhất theo merchant, định danh hệ thống, status, cấu hình terminal/print |
SaleChannelInventoryLocation | Dòng liên kết nối một kênh với một hoặc nhiều inventory location (sắp theo priority) |
| Cấu hình kênh | Cấu hình được mã hóa/khóa, tra cứu theo sale-channel-id |
Chỉ ở mức khái niệm - schema và bất biến đầy đủ nằm trong commerce domain model.
10. Dependencies & Assumptions
Phụ thuộc vào
- Merchant aggregate (URD-MER) - kênh được tạo và quản lý bên trong aggregate của merchant.
- Onboarding (URD-ORG) - (các) kênh mặc định được tạo một thao tác duy nhất khi onboarding.
- Inventory location (Inventory) - một kênh liên kết tới các inventory location có sẵn.
Giả định
- Một merchant tồn tại (hoặc đang được tạo trong cùng aggregate) trước khi quản lý kênh.
- Các inventory location tồn tại trước khi có thể liên kết vào một kênh.
11. Risks & Open Questions
| Risk / câu hỏi | Mitigation / trạng thái |
|---|---|
| Cấu hình kênh trôi dạt sau khi chuyển tra cứu khỏi merchant-id | Lượt tra cứu được khóa nhất quán theo sale-channel-id; fallback rộng theo merchant đã bị gỡ |
| Trùng liên kết inventory-location trên một kênh | Partial-unique index trên (saleChannelId, inventoryLocationId) |
| Vô hiệu hóa một kênh giữa lúc đang bán | Bộ chặn ngăn vô hiệu hóa khi còn đơn hàng hoạt động tham chiếu kênh |
| CRUD kênh độc lập bên ngoài aggregate | Ngoài phạm vi; Dự kiến theo URD Non-Goals |
| Phân cấp kênh (cha-con) | Ưu tiên Could (URD-SC-006); hoãn lại |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P1 (nền tảng) - xem URD feature catalog |
| Rollout | Mọi merchant; không feature flag |
| Migration | Backfill onboarding đảm bảo các merchant có sẵn có một kênh mặc định; cấu hình terminal/print chuyển lên kênh |
| Launch criteria | Onboarding tạo một kênh mặc định; batch/aggregate create đã kiểm chứng; slug duy nhất theo merchant được thực thi; tra cứu cấu hình theo kênh đã kiểm chứng; bộ chặn vô hiệu hóa chặn các kênh còn đơn hàng hoạt động phụ thuộc |
| Monitoring | Số kênh trên mỗi merchant, tỷ lệ chặn vô hiệu hóa, lỗi tra cứu cấu hình theo sale-channel-id |
13. FAQ
Tôi có thể quản lý một kênh bên ngoài aggregate của merchant không? Không trong increment này - kênh được quản lý qua aggregate của merchant hoặc thêm hàng loạt vào một merchant có sẵn. CRUD kênh độc lập là Dự kiến.
Mỗi kênh có location stock riêng không? Một kênh có thể liên kết một hoặc nhiều inventory location (tùy chọn khi tạo), nên nó lấy stock từ (các) location riêng thay vì một location duy nhất rộng theo merchant.
Tại sao tôi không thể vô hiệu hóa một kênh? Vì nó vẫn còn đơn hàng hoạt động phụ thuộc. Bộ chặn vô hiệu hóa bảo vệ các giao dịch đang dang dở; hãy đóng hoặc chuyển các đơn hàng đó trước.
Cấu hình terminal/print giờ nằm ở đâu? Trên kênh. Các lượt tra cứu cấu hình được scope theo sale-channel-id, nên hai kênh dưới cùng một merchant có thể in khác nhau.
Phân cấp kênh có được hỗ trợ không? Không - phân cấp kênh cha-con (URD-SC-006) là ưu tiên Could và không thuộc increment này.
References
- URD: Commerce - Sale Channels
- PRD liên quan: Organization & Merchant · Config & Deletion Policy
- Module: Commerce - tổng quan + traceability
- Developer: @nx/commerce · domain model