PRD: Tìm/quét Barcode tại quầy & Định danh
| Module | Sản phẩm | PRD ID | PRD-PID-001 |
| Trạng thái | Sẵn sàng dev | FEAT | PID |
| Epic | BANA-1319 | Plane | BANA-1752 |
| Ngày | 2026-07-06 | Phiên bản | v1.0 |
| Gói | @nx/commerce | URD | PID |
| Surface | Sale · POS | ||
| Phụ trách | Phát Nguyễn | ||
Tìm/quét Barcode tại quầy & Định danh là gì
Thu ngân quét hoặc gõ một barcode tại quầy, hệ thống trả lời dứt khoát: đúng biến thể mà mã đó đại diện, đã lên giá và sẵn sàng cho giỏ hàng - hoặc báo ngay là không nhận ra mã này để thu ngân chuyển sang tìm kiếm. PRD này chốt lại phần định danh mà mọi luồng bán tại quầy - chế độ quầy bán lẻ, bước bỏ-qua-picker bằng barcode - đang ngầm dựa vào nhưng chưa ai thật sự đặc tả: mã trùng nhiều lược đồ thì ưu tiên cái nào, không tìm thấy thì báo ra sao, và quét máy hay gõ tay có chắc ra cùng một kết quả.
Vì sao cần một hợp đồng định danh riêng
Barcode đã gán được cho biến thể từ lâu (URD-PID-001…004, đã xây), và một bước tra cứu đơn giản cũng đã quét ra được biến thể tương ứng - cả product picker lẫn chế độ quầy bán lẻ đều đang dựa vào bước tra cứu này như một nền có sẵn (URD-PCK-007, URD-RTL-005). Vấn đề là không PRD nào thật sự sở hữu hành vi định danh - cả hai đều coi "quét xong là có hàng trong giỏ" như một hộp đen:
- Chưa có quy ước cho không tìm thấy. Thu ngân sẽ thấy gì khi quét một mã không khớp gì cả vẫn chưa được định nghĩa - tra cứu hiện tại không đảm bảo trả về một kết quả rõ ràng, xử lý tiếp được.
- Chưa có quy tắc xử lý khi mã trùng lược đồ. Định danh chỉ duy nhất trong từng lược đồ (URD-PID-001), không duy nhất toàn hệ thống - cùng một chuỗi hoàn toàn có thể vừa là mã vạch của biến thể này, vừa là mã hàng của biến thể khác.
- Chưa đảm bảo quét máy và gõ tay cho cùng một kết quả - POS đã có sẵn loại thiết bị
BARCODE_SCANNER, nhưng việc gõ tay cùng dãy số phải cho kết quả giống hệt máy quét mới chỉ là giả định. - Chưa có chỗ cho barcode cân ký. Nhãn in từ cân ở tạp hóa thường ghép tiền tố cố định, mã hàng, và trọng lượng hoặc giá ngay trong barcode - kiểu mã này cần đọc khác hẳn barcode sản phẩm thường.
Một lượt quét, từ mã tới giỏ hàng
Thu ngân quét một mã tại quầy. Hệ thống tra theo thứ tự lược đồ cố định - mã vạch trước, rồi mã hàng, mã QR, slug, mã hệ thống - và lấy kết quả của lược đồ đầu tiên khớp. Nếu chuỗi đó vừa là mã vạch của biến thể A vừa là mã hàng của biến thể B, kết quả luôn là biến thể A vì mã vạch đứng trước. Biến thể tìm được mang theo giá hiện hành và tình trạng còn hàng, giống hệt một lượt chọn tay - món được thêm vào giỏ ngay. Nếu chuỗi không khớp gì, thu ngân nhận một thông báo không tìm thấy rõ ràng và được mời chuyển sang tìm theo tên hoặc mã hàng, thay vì quét lại vô ích cùng một mã.
Chỗ dễ sai nhất
Một chuỗi có thể hợp lệ ở hai lược đồ cùng lúc
Định danh chỉ duy nhất trong từng lược đồ, không duy nhất toàn hệ thống. Một merchant hoàn toàn có thể gán cùng một chuỗi vừa là mã vạch của biến thể này, vừa là mã hàng của biến thể khác - cả hai đều hợp lệ khi tạo.
Một tra cứu ngây thơ có thể trả về biến thể sai, hoặc cả hai. Thứ tự ưu tiên lược đồ cố định là cách duy nhất khiến việc phân giải luôn xác định.
1. Mục tiêu & Loại trừ
Mục tiêu
- Quét hoặc gõ một barcode tại quầy luôn ra đúng một biến thể, sẵn sàng thêm vào giỏ (URD-PID-005).
- Quét một mã không khớp gì thì luôn nhận được thông báo "không tìm thấy" rõ ràng - không bao giờ crash hay lặng thinh không phản hồi - kèm gợi ý chuyển sang tìm theo tên/SKU (URD-PID-006).
- Nếu một chuỗi trùng nhiều lược đồ định danh, hệ thống luôn phân giải theo một thứ tự ưu tiên cố định - không bao giờ mập mờ hay trả về hai kết quả (URD-PID-007).
- Dù nhập từ máy quét phần cứng/keyboard-wedge hay gõ tay, mã đều đi qua đúng một tra cứu và cho cùng kết quả (URD-PID-008).
- Biến thể tìm ra mang đúng giá và tình trạng còn hàng theo kênh, y như khi chọn thủ công (URD-PID-009).
- Barcode cân ký (nhãn cân) được đọc theo đúng đoạn mã hàng, còn đoạn trọng lượng/giá nhúng trong mã được hiểu là số lượng/giá (URD-PID-010, mức Có thể - mục tiêu vươn xa cho v1).
Loại trừ
- Cơ chế giỏ hàng/thanh toán sau khi đã ra được biến thể - thuộc về Sale Order (
ORD) và Chế độ quầy bán lẻ (RTL). - Giao diện chọn tùy chọn (option picker) và cơ chế bỏ-qua-bằng-quét của riêng nó - thuộc về Product Picker (
PCK); PRD này chỉ đưa ra kết quả định danh để picker bỏ qua dựa vào. - Nhập, đảm bảo duy nhất, và in barcode khi chủ gán một mã cho biến thể - phần này đã đặc tả và xây xong trong PRD-PRD-001 (URD-PID-001…004).
- Giao diện ghép nối/cấu hình máy quét phần cứng - v1 cứ giả định máy quét chạy chế độ keyboard-wedge (HID) tiêu chuẩn, đúng như mô hình thiết bị
BARCODE_SCANNERsẵn có; một luồng ghép nối riêng để sau nếu thật sự cần. - Trải nghiệm quét bằng camera (mobile/tablet) - quy tắc định danh không quan tâm mã đến từ thiết bị nào, nó chỉ nhìn thấy một chuỗi đã giải mã; giao diện chụp bằng camera là việc của front-end (tài liệu bàn giao FE).
- Định danh khi offline - Phase 2 chưa bán hàng offline (đã chốt, xem Loại trừ của PRD-RTL-001).
2. Thước đo thành công
| Thước đo | Mục tiêu / tín hiệu |
|---|---|
| Độ trễ quét-đến-ra-hàng | Cảm giác tức thời trên phần cứng POS hiện tại - không chậm hơn cảm nhận so với luồng picker hôm nay |
| Độ rõ ràng khi không tìm thấy | 100% lượt quét không khớp trả về thông báo "không tìm thấy" tường minh, xử lý tiếp được - không lỗi âm thầm |
| Sự cố định danh mập mờ | Bằng không - quét một chuỗi đăng ký ở nhiều lược đồ vẫn luôn ra đúng một kết quả xác định |
| Nhất quán quét máy/gõ tay | Kết quả giống hệt nhau dù mã đến từ máy quét phần cứng hay nhập tay |
3. Persona & Tình huống
| Persona | Mục tiêu trong tính năng này |
|---|---|
| Thu ngân bán lẻ | Đứng ở quầy scan-first cả ngày; cần mọi lượt quét đều ra đúng món, không lần nào bị kẹt |
| Thu ngân F&B | Muốn các món đóng gói/mang đi nhanh (nước đóng chai, snack) bỏ qua picker chỉ bằng một lượt quét |
| Thu ngân tạp hóa | Quét nhãn cân, nơi barcode mã hóa cả trọng lượng/giá chứ không chỉ tên hàng |
| Owner | Gán barcode cho biến thể và mong nó chạy tốt tại quầy, không bị lẫn giữa các lược đồ |
Tình huống chính: thu ngân quét (hoặc gõ) một mã tại quầy → hệ thống trả về đúng một biến thể, kèm giá và tình trạng còn hàng hiện hành → món được chuyển sang giỏ hàng. Nếu mã không khớp gì, thu ngân được báo rõ và có thể tìm theo tên/SKU thay vì quét mù lặp lại.
4. User Stories
| # | Là một | Tôi muốn | Để |
|---|---|---|---|
| 01 | thu ngân | quét barcode một sản phẩm và đúng biến thể đó vào giỏ ngay | khỏi qua picker, khỏi đoán |
| 02 | thu ngân | khi quét trúng mã hệ thống không biết, được báo rõ là không tìm thấy | chuyển sang tìm theo tên thay vì quét đi quét lại vô ích |
| 03 | Owner | gán một barcode cho biến thể và tin rằng quét nó tại quầy luôn ra đúng biến thể đó | không bao giờ lạc sang một biến thể khác chỉ vì tình cờ trùng mã ở lược đồ khác |
| 04 | thu ngân tạp hóa | quét nhãn cân và hệ thống thêm đúng món với số lượng/giá đã cân | không bị đơn giá cố định |
5. Yêu cầu chức năng
Đáp ứng URD PID URD-PID-005…010 (Bắt buộc 005-007; Nên có 008-009; Có thể 010).
| # | Yêu cầu | Trạng thái | URD ref |
|---|---|---|---|
FR-001 | Một mã được nhập/quét luôn ra đúng một biến thể, không bao giờ một danh sách. • Tra cứu nhận một chuỗi thô cùng merchant của người gọi, trả về đúng một biến thể hoặc một kết quả không tìm thấy. • Một phiên bản cơ bản đã chạy (barcode đã quét ra biến thể cho bước bỏ-qua-picker của PRD-PCK-001 và chế độ quầy bán lẻ của PRD-RTL-001) - đây là lý do trạng thái 🔶 thay vì 🚧. • Cái còn thiếu: một hợp đồng chính thức, duy nhất mà mọi bên gọi vào đều dựa vào, thay vì mỗi bên tự cài lại phần phân giải riêng. | 🔶 | URD-PID-005 |
FR-002 | Một mã không khớp gì luôn trả về kết quả "không tìm thấy" rõ ràng, có cấu trúc (NOT_FOUND), khác hẳn lỗi hệ thống.• Tra cứu không bao giờ ném lỗi và không bao giờ trả về một "thành công rỗng". • Quầy hiển thị một thông báo rõ ràng và mời thu ngân chuyển sang ô tìm theo tên/mã hàng ngay, không phải quét đi quét lại cùng một mã. | 🚧 | URD-PID-006 |
FR-003 | Một thứ tự lược đồ cố định quyết định kết quả khi một chuỗi khớp nhiều lược đồ - mã vạch → mã hàng → mã QR → slug → mã hệ thống. • Định danh chỉ duy nhất trong từng lược đồ, nên cùng một chuỗi hoàn toàn có thể vừa hợp lệ là mã vạch của biến thể này, vừa là mã hàng của biến thể khác. • Tra cứu lấy kết quả của lược đồ đầu tiên khớp theo đúng thứ tự trên. • Mã vạch và mã hàng đứng đầu vì đây là hai lược đồ thu ngân thật sự quét/gõ tại quầy; slug và mã hệ thống đứng cuối vì phục vụ tra cứu nội bộ/API, không phải nhập tại quầy. | 🚧 | URD-PID-007 |
FR-004 | Mã từ máy quét phần cứng và mã gõ tay đi qua cùng một lượt tra cứu, cho cùng kết quả. • Máy quét phần cứng/keyboard-wedge chỉ đơn giản gõ hộ các phím, không khác gõ tay, nên không có một "đường quét riêng" nào cần giữ đồng bộ. • Đảm bảo bởi chính cách xây - chỉ một lối vào - chứ không phải bằng test riêng cho từng nguồn nhập. | 🚧 | URD-PID-008 |
FR-005 | Biến thể tìm được mang theo giá và tình trạng còn hàng theo kênh, cùng với danh tính của nó. • Kết quả gộp biến thể với giá đã giải quyết qua quy tắc FAR sẵn có, và tình trạng còn hàng theo kênh - đúng hình dạng một lượt chọn thủ công sẽ trả về.• Một món được quét và một món được chọn tay là như nhau một khi đã vào tới giỏ. | 🚧 | URD-PID-009 |
FR-006 | Đoạn mã hàng của một barcode nhãn cân được tra riêng, tách khỏi đoạn trọng lượng/giá nhúng trong mã. • Merchant bán theo cân tự cấu hình quy ước tách đoạn trên chuỗi quét thô - thường một tiền tố cố định, một đoạn mã hàng, và một đoạn trọng lượng/giá nhúng (kiểu phổ biến trên nhãn cân tạp hóa ở Việt Nam). • Chỉ đoạn mã hàng được đưa vào tra cứu định danh bình thường; đoạn nhúng được đọc riêng thành số lượng hoặc giá ghi-đè, không bao giờ bị coi là một phần của định danh. | 🚧 | URD-PID-010 |
5.1 Tiêu chí nghiệm thu
- Quét barcode đã đăng ký của một biến thể tại quầy → đúng biến thể đó ra kết quả, kèm giá và tình trạng còn hàng hiện hành, sẵn sàng thêm vào giỏ.
- Quét hoặc gõ một mã không khớp định danh nào trong merchant → quầy nhận về kết quả "không tìm thấy" tường minh và được mời tìm theo tên/SKU, không hề crash hay im lặng.
- Đăng ký cùng một chuỗi vừa là mã vạch của biến thể A, vừa (riêng biệt) là mã hàng của biến thể B → quét chuỗi đó luôn ra biến thể A (mã vạch thắng mã hàng), không bao giờ ra cả hai, không báo lỗi.
- Đưa cùng một mã vào một lần qua máy quét phần cứng, một lần gõ tay → cả hai lượt đều chạm đúng một tra cứu và cho ra kết quả giống hệt nhau.
- Định danh một biến thể theo barcode → kết quả kèm theo giá hiện hành và tình trạng còn hàng theo kênh, đúng như khi chọn thủ công.
- (Có thể) Quét một nhãn cân có nhúng trọng lượng → món hàng được định danh theo đoạn mã hàng, còn trọng lượng nhúng được đọc vào số lượng/giá của dòng hàng chứ không cản trở việc tra cứu.
6. Yêu cầu phi chức năng
| Khía cạnh | Yêu cầu |
|---|---|
| Xác định, không đoán | Cùng một chuỗi luôn ra cùng một kết quả với cùng trạng thái merchant - không có chuyện ngẫu nhiên hay "khớp đâu lấy đó" giữa các lược đồ khi chưa có thứ tự tường minh |
| Giới hạn theo merchant | Tra cứu được thiết kế để chỉ nhìn trong phạm vi merchant của người gọi, giống mọi thao tác đọc sản phẩm/biến thể khác (C-01, C-07) - hiện nhánh tra cứu barcode chưa lọc theo merchant, xem gap ở Hỏi đáp |
| Hiệu năng | Đủ nhanh để cảm giác tức thời tại quầy - không chậm hơn luồng picker/tìm kiếm hiện tại |
| Đa ngôn ngữ | Thông báo "không tìm thấy" và gợi ý tìm kiếm đều song ngữ (EN/VI), khớp phần còn lại của POS |
7. UX & Luồng
PRD này chỉ đặc tả phần định danh - hình dạng yêu cầu/phản hồi và các quy tắc phân giải bên trong nó. Còn màn hình nào gọi vào (ô quét ở chế độ quầy bán lẻ, bước bỏ-qua-picker-bằng-barcode của product picker) thì đã có PRD riêng của chúng (PRD-RTL-001, PRD-PCK-001) lo; đợt này chỉ đảm bảo cả hai gọi vào đúng một hành vi đã định nghĩa rõ, thay vì mỗi bên tự đoán một kiểu.
8. Dữ liệu & Miền nghiệp vụ
| Khái niệm | Vai trò |
|---|---|
ProductIdentifier | Bảng dữ liệu sẵn có (scheme, identifier, principal_id) đang phục vụ tra cứu SYSTEM/SLUG/SKU/BARCODE/QRCODE (URD-PID-001…004) - dùng nguyên trạng, không đổi schema |
| Thứ tự ưu tiên lược đồ | Quy tắc phân giải mới (mã vạch trước mã hàng trước các lược đồ khác), áp dụng khi một chuỗi quét được khớp với nhiều hơn một lược đồ |
| Barcode cân ký | Một quy tắc đọc trên chuỗi quét thô - tách đoạn mã hàng khỏi đoạn trọng lượng/giá nhúng theo tiền tố đã cấu hình, rồi mới tra đoạn mã hàng như một định danh bình thường |
Chỉ ở mức khái niệm - schema đầy đủ nằm trong mô hình miền commerce.
9. Phụ thuộc & Giả định
Phụ thuộc vào
| # | Feature | Phụ thuộc điều gì |
|---|---|---|
| 01 | Dữ liệu định danh sẵn có (URD-PID-001…004, đã xây) | Đợt này chỉ thêm quy tắc tra cứu/phân giải bên trên, không lưu trữ gì mới. |
| 02 | Loại thiết bị BARCODE_SCANNER (đã có mô hình) | v1 giả định máy quét HID/keyboard-wedge tiêu chuẩn, không cần tích hợp thiết bị mới. |
| 03 | Giải quyết fare (FAR) | Giá của biến thể tìm ra vẫn đi qua đúng quy tắc giải quyết fare hiện có, không có đường tắt. |
Được dùng bởi
- PRD-RTL-001 Retail Business Type - chế độ quầy scan-first gọi tra cứu này ở mọi lượt quét (URD-RTL-005).
- PRD-PCK-001 Product Picker - bước bỏ-qua-bằng-barcode (URD-PCK-007) gọi tra cứu này thay vì mở bộ chọn tùy chọn.
Giả định
| # | Giả định | Sai thì sao |
|---|---|---|
| 01 | Merchant nào muốn dùng barcode cân ký sẽ tự cấu hình quy ước tiền tố/đoạn mã của mình; v1 không tự đoán định dạng. | Quy ước tiền tố khác nhau theo hãng cân; cấu hình sai thì đoạn mã hàng bị tách sai và lượt quét đó không định danh được đúng biến thể. |
| 02 | Thiết bị gửi yêu cầu đã được xác thực và giới hạn theo merchant, như mọi yêu cầu POS khác. | Một yêu cầu không được giới hạn đúng phạm vi merchant có thể tra cứu định danh chéo sang dữ liệu của merchant khác. |
10. Kế hoạch phát hành & Tiêu chí
| Khía cạnh | Kế hoạch |
|---|---|
| Giai đoạn | P2 - Tháng 7/2026 (WK27-31), cùng đợt với Retail end-to-end |
| Triển khai | Mọi merchant, không cần feature flag - chỉ là bổ sung cho một tra cứu vốn đã giới hạn theo merchant |
| Di trú dữ liệu | Không - dùng nguyên trạng dữ liệu ProductIdentifier hiện có |
| Tiêu chí phát hành | AC-PID-02 và AC-PID-03 pass trên một menu merchant thật, có cả nhóm hàng bán lẻ lẫn F&B; hành vi không-tìm-thấy và ưu tiên lược đồ được xác minh xong trước khi PRD-RTL-001 thí điểm vào tháng 8 |
| Theo dõi | Độ trễ quét-đến-ra-hàng, tỉ lệ không-tìm-thấy (dấu hiệu dữ liệu thiếu sạch), các lần va chạm giữa lược đồ |
Tham chiếu
- URD: Product - Định danh (
PID) - PRD liên quan: PRD-PRD-001 Danh mục sản phẩm, biến thể & định danh · PRD-PCK-001 Product Picker · PRD-RTL-001 Retail Business Type
- Module: Product - URD · Phase 2 - By Month
- Epic BANA-1319
- Lập trình viên: @nx/commerce
Rủi ro & Câu hỏi mở
| # | Rủi ro / câu hỏi | Giảm thiểu / trạng thái |
|---|---|---|
| 01 | Một merchant lỡ đăng ký cùng một chuỗi vừa là mã vạch của biến thể này, vừa là mã hàng của biến thể khác (hợp lệ hôm nay, vì tính duy nhất chỉ tính theo từng lược đồ) - tra cứu ngây thơ có thể ra một trong hai, hoặc ra cả hai | Thứ tự ưu tiên lược đồ cố định (URD-PID-007) khiến việc phân giải luôn xác định; đề xuất bước tiếp theo: cảnh báo chủ ngay lúc gán định danh nếu chuỗi đó đã dùng ở lược đồ khác |
| 02 | Quy ước tiền tố của barcode cân ký khác nhau tùy quốc gia/hãng cân (cân tạp hóa ở Việt Nam thường dùng dải tiền tố riêng) | Chưa chuẩn hóa trong v1 (mức Có thể, URD-PID-010); coi là cấu hình theo merchant, xem lại khi có merchant tạp hóa thí điểm cho thấy định dạng nhãn thực tế |
| 03 | Quét bằng camera (mobile/tablet) có thể giải mã khác máy quét phần cứng (dễ dính khoảng trắng/ký tự thừa) | Quy tắc định danh chỉ nhận một chuỗi đã giải mã sạch; việc chuẩn hóa theo từng thiết bị là việc của front-end, không thuộc phạm vi PRD này |
| 04 | Tra cứu định danh hiện chưa lọc theo merchant ở nhánh mã vạch; cần đưa merchant vào ràng buộc + truy vấn | Gap đang theo dõi - ràng buộc duy nhất ở tầng hệ thống hiện là toàn cục (không có merchantId trong unique index) và nhánh tra cứu mã vạch không lọc theo merchant; đề xuất bước tiếp theo: đưa merchantId vào cả ràng buộc duy nhất lẫn truy vấn tra cứu trước khi launch diện rộng |
Câu hỏi thường gặp
| # | Câu hỏi | Trả lời |
|---|---|---|
| 01 | Tính năng này có thay product picker hay chế độ quầy bán lẻ không? | Không. Cả hai vẫn giữ UX và cách xử lý giỏ hàng riêng; PRD này chỉ là thứ chúng gọi bên dưới để biến một chuỗi đã quét thành một biến thể. |
| 02 | Nếu hai biến thể ở hai merchant khác nhau lỡ trùng đúng một chuỗi barcode thì sao? | Định danh được thiết kế để duy nhất theo từng merchant; hiện ràng buộc duy nhất ở tầng hệ thống còn là toàn cục (chưa đưa merchant vào) và nhánh tra cứu mã vạch cũng chưa lọc theo merchant - đang theo dõi để hai merchant dùng chung được một mã vạch vật lý mà không đụng nhau. Trường hợp cùng merchant nhưng khác lược đồ (URD-PID-007) thì dùng quy tắc ưu tiên như bình thường. |
| 03 | QR code có được xử lý không? | Có. Mã QR là một lược đồ định danh sẵn có, đi qua đúng tra cứu và thứ tự ưu tiên như mã vạch/mã hàng. |