PRD: Đăng ký, định danh, kích hoạt & thiết bị ngoại vi
| Module | Thiết bị | PRD ID | PRD-DEV-001 |
| Trạng thái | Chưa tạo item | FEAT | DEV |
| Epic | — | Plane | BANA-1539 |
| Ngày | 2026-07-21 | Phiên bản | v0.5 |
| Gói | @nx/commerce | URD | DEV |
| Surface | Client · Chủ/QLSale · POSBO · Vận hành | ||
| Phụ trách | Device squad | ||
Thiết bị làm gì
Back Office có một trang quản lý đội thiết bị - danh sách, tạo, sửa - và tìm thiết bị theo phạm vi các merchant người dùng đã tham gia. Mỗi thiết bị bắt buộc gắn với đúng một merchant, mang một Định danh thiết bị tự sinh, và có một trường Trạng thái vận hành theo vòng đời năm bước. Danh mục loại thiết bị hôm nay có 8 loại, gồm cả một Cash Drawer được đăng ký và chọn dùng y hệt mọi thiết bị khác.
Điểm chốt mới trong bản này: định danh, kiểm tra, và kích hoạt
Bản v0.3 để ngỏ một câu hỏi: "tự đăng ký lúc đăng nhập lần đầu" nghĩa là gì chính xác, và "kích hoạt tự động" hoạt động ra sao? Bản v0.4 này chốt lại nghiệp vụ đó thành ba quy tắc rõ ràng, thay cho mô tả mơ hồ trước đây:
- Một thiết bị, một định danh bền vững. Định danh thiết bị không được phép chỉ sống trong bộ nhớ cục bộ của ứng dụng (mất khi gỡ cài đặt) - nó phải bền vững qua các lần cài đặt lại trên cùng phần cứng.
- Đăng nhập là kiểm tra trước, tạo mới sau. Ứng dụng gọi một API hỏi "định danh này đã thuộc merchant hiện tại chưa?" - có thì hoạt động bình thường, không tạo gì cả; chưa thì hệ thống tạo một thiết bị mới, ở trạng thái NEW.
- Kích hoạt là một hành động rời rạc. Thiết bị NEW không tự chuyển sang ACTIVATED. Hệ thống sinh một Mã kích hoạt dưới hai dạng - QR (quét camera) và Barcode (quét máy quét/gõ tay, phục vụ thiết bị không camera) - và ai đó phải quét/nhập đúng mã đó rồi bấm "Kích hoạt". Mỗi mã chỉ dùng được một lần. Cơ chế này được chọn không phụ thuộc bộ nhớ trình duyệt, nên vẫn hoạt động khi chính sách ITP (Intelligent Tracking Prevention - chặn cookie hoặc tập lệnh theo dõi hành vi giữa các trang web khác nhau) giới hạn thời gian lưu trữ phía client và khiến phép kiểm tra tự động ở bước 2 kém tin cậy.
Ba quy tắc trên là forward-spec - chưa có dòng code nào cho check-API, sinh mã, hay kích hoạt dùng-một-lần hôm nay; luồng hiện tại chỉ là client tự tạo/tái dùng một deviceId lưu trong session cục bộ, không đảm bảo bền vững qua cài lại và không có bước kích hoạt tường minh.
Cash Drawer là một loại thiết bị có thật
Cash Drawer (500_CASH_DRAWER) được đăng ký và tìm kiếm qua đúng luồng như một máy POS - không phải chỗ giữ chỗ, và không chỉ là cờ cashDrawerEnabled trên một máy POS (một cơ chế khác, cũng có thật; xem URD Thiết bị để phân biệt). Bản PRD trước chưa ghi lại đầy đủ danh mục loại thiết bị - còn thiếu cả loại 300_BROWSER, tự đăng ký bởi phiên trình duyệt web.
Tình trạng đội thiết bị chưa có gì để quan sát
Chưa có gì theo dõi việc một thiết bị đang hư hỏng vật lý, đang sửa chữa, hay đã thanh lý. Schema đã có sẵn các trường lastMaintenanceAt / nextMaintenanceAt / vendor (Đã dựng), nhưng không có gì tính ra cờ đến hạn/quá hạn từ đó, không có gì thông báo cho ai, và chưa có khái niệm "Hư hỏng" hay "Đã thanh lý" ở đâu cả - chủ sở hữu chỉ biết một máy POS bị hỏng khi thu ngân không bán hàng được và gọi điện phàn nàn.
1. Mục tiêu & Loại trừ
Mục tiêu
- (Đã dựng) Một trang thiết bị ở back-office - danh sách, tạo, sửa - với form chia phần (thông tin chung, cấu hình phần cứng, cấu hình phần mềm, cài đặt khác), ở cả app client và BO.
- (Đã dựng) Gán merchant bắt buộc - mỗi thiết bị thuộc về đúng một merchant; không có quyền sở hữu organizer trên model.
- (Đã dựng) Khả năng tìm thấy thiết bị qua chỉ mục tìm kiếm (kể cả khớp theo trường mô tả), giới hạn ở các merchant người dùng đã tham gia.
- (Một phần) Một trường trạng thái vận hành có kiểm soát (NEW → ACTIVATED → DEACTIVATED → SUSPENDED → ARCHIVED) với soft-delete và mã thiết bị duy nhất theo từng merchant. Các giá trị và soft-delete đã có; kiểm tra chuyển trạng thái không được áp dụng - status là trường CRUD tự do.
- (Dự kiến, chốt nghiệp vụ trong bản này) Định danh thiết bị bền vững - không đổi qua các lần gỡ cài đặt/cài đặt lại app trên cùng phần cứng.
- (Dự kiến, chốt nghiệp vụ trong bản này) Kiểm tra-rồi-tạo khi đăng nhập - gọi API kiểm tra thiết bị đã thuộc merchant hiện tại chưa; có thì dùng bình thường, chưa thì tạo thiết bị mới ở trạng thái NEW.
- (Dự kiến, chốt nghiệp vụ trong bản này) Kích hoạt dùng-một-lần qua Barcode/QR - thiết bị mới tự sinh một Mã kích hoạt (QR + Barcode); quét/nhập mã rồi bấm "Kích hoạt" chuyển NEW → ACTIVATED; mỗi mã chỉ dùng được một lần. Giải quyết cả trường hợp trình duyệt không giữ được định danh do chính sách ITP, và trường hợp thiết bị không có chức năng scan QR.
- (Dự kiến) Stack ngoại vi & tình trạng: máy in hóa đơn, máy quét mã vạch, SoundBox, và màn hình tình trạng dựa trên heartbeat kèm vô hiệu hóa từ xa. Chưa cái nào được đấu nối trên backend - việc in chạy trong app POS native.
- (Đã dựng, trước đây chưa ghi lại) Cash Drawer được đăng ký như một loại thiết bị riêng, đi qua đúng luồng đăng ký/vòng đời/tìm kiếm như mọi thiết bị khác, và được đưa ra làm danh sách chọn khi mở ca trên một máy POS đã bật ngăn kéo tiền.
- (Dự kiến) Theo dõi Tình trạng thiết bị (Đang hoạt động / Đang bảo trì / Hư hỏng), độc lập với trường Trạng thái vận hành.
- (Dự kiến) Thông báo hư hỏng - đánh dấu một thiết bị Hư hỏng đẩy cảnh báo thời gian thực đến đầu mối phụ trách thiết bị/bảo trì của merchant.
- (Dự kiến) Đánh dấu đến hạn bảo trì - danh sách đội thiết bị đánh dấu một thiết bị đến hạn/quá hạn dựa trên ngày bảo trì kế tiếp đã ghi nhận, kèm thông báo nhắc nhở.
- (Dự kiến) Lý do ngừng dùng khi lưu trữ (Đã thanh lý / Thất lạc / Bị mất cắp / Đã thay thế / Khác).
Loại trừ
- Đấu nối backend VNPAY Terminal (đã có chỗ giữ dữ liệu; backend chưa kết nối - phát hiện QE).
- Tích hợp máy in nhãn và hỗ trợ máy POS Windows.
- Chấp nhận thanh toán không tiếp xúc SoftPOS / NFC - Dự kiến (P3).
- Kích hoạt vật lý tín hiệu bật ngăn kéo tiền - chưa có lệnh này trong code hôm nay; nhân viên vẫn mở ngăn kéo bằng tay. Đăng ký và chọn một thiết bị Cash Drawer nằm trong phạm vi; kích hoạt vật lý thì không.
- Ai được phép bấm "Kích hoạt" (vai trò cụ thể - chủ sở hữu, quản lý, hay bất kỳ ai có quyền truy cập màn hình thiết bị) - đề xuất mặc định chủ sở hữu/quản lý, chốt chính xác thuộc phạm vi thiết kế phân quyền, không phải PRD này.
- Quy trình phiếu bảo trì / điều phối kỹ thuật viên đầy đủ (phân công, SLA, theo dõi tiến độ sửa chữa) - đi qua module Helpdesk, độc lập với PRD này.
- UI quản lý driver ngoại vi nâng cao.
- App màn hình bếp, phục vụ và giao hàng.
2. Thước đo thành công
| Thước đo | Mục tiêu / tín hiệu |
|---|---|
| Độ phủ định danh | Mọi phiên app phân giải về một bản ghi thiết bị qua định danh thiết bị của request - không có phiên mồ côi |
| Định danh bền vững | Gỡ cài đặt rồi cài đặt lại app trên cùng phần cứng luôn nhận lại đúng định danh cũ - không sinh định danh mới |
| Kiểm tra-rồi-tạo đúng | Đăng nhập trên phần cứng đã thuộc merchant không tạo bản ghi mới; đăng nhập trên phần cứng chưa thuộc merchant tạo đúng một bản ghi NEW |
| Kích hoạt an toàn | Mỗi Mã kích hoạt chỉ hoàn tất kích hoạt đúng một lần; mọi lần dùng lại đều bị từ chối |
| Quan sát đội thiết bị | Chủ sở hữu liệt kê, tìm kiếm và sửa được mọi thiết bị đã đăng ký từ back-office |
| Độ chính xác tình trạng | Trạng thái online/offline khớp ngưỡng heartbeat 5 phút / offline 15 phút |
| Độ tin cậy ngoại vi | Lỗi in/quét/SoundBox báo rõ ràng; thanh toán không bao giờ bị chặn bởi SoundBox mất kết nối |
| Độ trễ báo hư hỏng | Một thiết bị đánh dấu Hư hỏng đến được đầu mối bảo trì của merchant gần như tức thời |
| Tỷ lệ quá hạn bảo trì | Xu hướng thiết bị bị đánh dấu quá hạn bảo trì giảm dần theo thời gian, không chỉ tích lũy mà không ai xử lý |
| Độ chính xác hạch toán ngừng dùng | Mọi thiết bị đã lưu trữ đều mang một Lý do ngừng dùng |
3. Persona & Tình huống
| Persona | Mục tiêu trong tính năng này |
|---|---|
| Chủ sở hữu | Đăng ký, quản lý và giám sát đội thiết bị; kích hoạt thiết bị mới qua mã; vô hiệu hóa từ xa thiết bị bị xâm phạm; biết thiết bị nào đang hỏng, quá hạn bảo trì, hay đã thanh lý |
| Quản lý | Xem thiết bị và tình trạng, kích hoạt thiết bị mới qua mã, cấu hình ngoại vi trong phạm vi |
| Thu ngân | Vận hành POS trên thiết bị đã kích hoạt; ghép nối và dùng máy in / máy quét / SoundBox |
| Kỹ thuật viên / Nhân viên bảo trì | Nhận thông báo hư hỏng, sửa chữa hoặc bảo dưỡng thiết bị vật lý, rồi gỡ Tình trạng về Đang hoạt động |
Tình huống cốt lõi (kiểm tra - tạo - kích hoạt): thu ngân đăng nhập trên một máy POS mới → ứng dụng gọi API kiểm tra, không thấy định danh này thuộc merchant → hệ thống tạo một thiết bị mới ở trạng thái NEW và sinh Mã kích hoạt (QR + Barcode) → quản lý quét mã đó bằng một thiết bị khác (hoặc nhập tay) rồi bấm "Kích hoạt" → thiết bị chuyển NEW → ACTIVATED, mã bị vô hiệu hóa → thu ngân bắt đầu bán hàng. Lần đăng nhập kế tiếp trên đúng máy đó, kể cả sau khi gỡ cài đặt và cài lại app, hệ thống nhận lại đúng định danh cũ và không tạo gì thêm.
Tình huống trình duyệt (ITP): chủ sở hữu mở Web POS trên Safari đã bật ITP - trình duyệt không giữ được định danh cũ giữa các phiên (kể cả từ IndexedDB), nên phép kiểm tra ở bước 2 coi đây là thiết bị mới mỗi lần. Vì việc kích hoạt qua Mã kích hoạt không phụ thuộc bộ nhớ trình duyệt, chủ sở hữu vẫn kích hoạt được phiên đó bằng cách quét/nhập mã hiển thị trên màn hình.
Luồng tình trạng đội thiết bị (forward-spec): một máy POS ngừng hoạt động giữa ca → thu ngân hoặc quản lý đánh dấu nó Hư hỏng → thiết bị bị chặn khỏi phiên mới và cảnh báo thời gian thực đến đầu mối bảo trì → kỹ thuật viên sửa xong và gỡ Tình trạng → thiết bị phục vụ bán hàng trở lại.
4. User Stories
- Là chủ sở hữu, tôi muốn một trang back-office để liệt kê, tạo và sửa thiết bị, để quản lý cả đội thiết bị ở một nơi.
- Là chủ sở hữu, tôi muốn mỗi thiết bị mang một định danh bền vững không đổi qua các lần cài đặt lại, để cùng một cái máy không bao giờ bị hiểu nhầm thành nhiều thiết bị khác nhau.
- Là chủ sở hữu, tôi muốn hệ thống tự kiểm tra khi đăng nhập xem thiết bị đã thuộc merchant của tôi chưa, để không phải đăng ký lại phần cứng đã biết mỗi lần cài app.
- Là chủ sở hữu, tôi muốn kích hoạt một thiết bị mới bằng cách quét hoặc nhập một mã, để tôi chủ động xác nhận đúng thiết bị nào được phép giao dịch, thay vì nó tự động được cấp quyền.
- Là chủ sở hữu, tôi muốn Mã kích hoạt chỉ dùng được một lần, để không ai dùng lại mã cũ để kích hoạt một thiết bị khác.
- Là chủ sở hữu, tôi muốn kích hoạt được cả bằng Barcode lẫn QR, để những máy không có camera vẫn kết nối được.
- Là chủ sở hữu, tôi muốn gán thiết bị cho một merchant, để giới hạn nó theo đơn vị kinh doanh.
- Là chủ sở hữu, tôi muốn tìm thiết bị theo tên, định danh, mã, trạng thái và loại, để tìm nhanh một máy.
- Là thu ngân, tôi muốn máy in, máy quét và SoundBox ghép nối vào thiết bị của mình, để in hóa đơn, quét sản phẩm và nghe xác nhận thanh toán.
- Là quản trị viên, tôi muốn thấy tình trạng thiết bị và vô hiệu hóa từ xa thiết bị bị xâm phạm, để bảo vệ đội thiết bị.
- Là chủ sở hữu, tôi muốn đăng ký một ngăn kéo tiền như thiết bị riêng của nó, để chọn được nó khi mở ca thay vì nó là một cờ vô hình trên máy POS.
- Là chủ sở hữu, tôi muốn được thông báo ngay khi một thiết bị bị đánh dấu Hư hỏng, để một máy POS hỏng được sửa trước khi làm mất doanh thu cả một ca.
- Là chủ sở hữu, tôi muốn danh sách đội thiết bị đánh dấu thiết bị quá hạn bảo trì, để phát hiện trước khi nó hỏng đột xuất.
- Là chủ sở hữu, tôi muốn mọi thiết bị đã lưu trữ ghi lại lý do ngừng dùng, để biết ngay thiết bị nào đã thanh lý so với thất lạc, mất cắp hay thay thế.
- Là kỹ thuật viên, tôi muốn gỡ Tình trạng của thiết bị về Đang hoạt động sau khi sửa xong, để nó xử lý bán hàng trở lại mà không ai phải đụng vào Trạng thái vận hành bên dưới.
5. Yêu cầu chức năng
| # | Yêu cầu | Trạng thái | URD ref |
|---|---|---|---|
FR-001 | Một thiết bị được tạo với tên song ngữ (Anh/Việt), một loại thiết bị, và bắt buộc gắn với một merchant. • Hệ thống tự sinh một Định danh thiết bị ổn định kể từ lúc tạo. | ✅ | URD-DEV-001..003 |
FR-002 | Trường Trạng thái vận hành nhận năm giá trị theo thứ tự: new → active → deactivated → suspended → archived, cùng cơ chế soft-delete khi gỡ một thiết bị. • Các giá trị và soft-delete đã có sẵn, nhưng kiểm tra chuyển trạng thái hợp lệ không được áp dụng - status hiện là một trường CRUD tự do, có thể đặt sang bất kỳ giá trị nào bất kể giá trị hiện tại. | 🔶 | URD-DEV-004..009 |
FR-003 | Mã thiết bị là trường tùy chọn; khi được cung cấp, nó phải duy nhất trong phạm vi từng merchant. | ✅ | URD-DEV-011 |
FR-004 | Khi đăng nhập trên một thiết bị, ứng dụng gọi API kiểm tra định danh thiết bị đã thuộc merchant hiện tại hay chưa. • Có → thiết bị hoạt động bình thường, không tạo bản ghi mới. • Chưa → hệ thống tạo một bản ghi thiết bị mới ở trạng thái NEW cho merchant đó. • Hiện trạng: client đã tự tạo/tái dùng một deviceId lưu trong session cục bộ, nhưng chưa đảm bảo bền vững qua cài đặt lại (FR chưa gọi đúng API kiểm tra-thuộc-merchant như đặc tả) và chưa có bước kích hoạt tường minh (xem FR-018). | 🔶 | URD-DEV-013 · URD-DEV-025..026 |
FR-005 | Thiết bị tìm được theo tên, định danh, mã, trạng thái và loại thông qua chỉ mục tìm kiếm, giới hạn trong phạm vi các merchant mà người dùng đã tham gia. | ✅ | URD-DEV-014 |
FR-006 | Mỗi thiết bị được tạo phải gắn với đúng một merchant ngay từ đầu; việc gán đổi lại được qua màn sửa. • Không tồn tại thiết bị không thuộc merchant nào, và không có khái niệm sở hữu ở cấp organizer trên model thiết bị. | ✅ | URD-DEV-016 |
FR-007 | Mỗi thiết bị lưu một bộ thông tin phần cứng (nhà sản xuất, model, serial, IMEI, MAC, processor, RAM, lưu trữ, màn hình, pin) và thông tin phần mềm (hệ điều hành, phiên bản OS, phiên bản app, firmware, thời điểm cập nhật cuối, driver ngoại vi). | ✅ | URD-DEV-017..018 |
FR-008 | Một header định danh thiết bị được mang theo lời gọi, nhưng resolver hiện chỉ hoạt động trong luồng hành động ca của POS - một hành động ca phân giải ra thiết bị rồi suy ra merchant của nó. • Chưa có resolver theo mỗi request áp dụng cho toàn nền tảng, và chưa có entity phiên-thiết bị (device session) riêng. Gap này tách biệt với FR-004/FR-018 - dù luồng kiểm tra-tạo-kích hoạt được xây đúng đặc tả, resolver toàn nền tảng vẫn là một hạng mục riêng. | 🔶 | URD-MON-001 |
FR-009 | Đặc tả yêu cầu: thiết bị gửi heartbeat mỗi 5 phút, được coi là offline sau 15 phút im lặng; một màn hình tình trạng hiển thị online/offline, lần thấy cuối và phiên bản app; và một thao tác vô hiệu hóa từ xa cho thiết bị bị xâm phạm. • Hiện trạng: có trường lưu lần-thấy-cuối nhưng không có gì ghi vào nó, và chưa có endpoint heartbeat, tình trạng hay thao tác từ xa nào. | 🚧 | URD-MON-001..005 |
FR-010 | Đặc tả yêu cầu: máy in hóa đơn, máy quét mã vạch và SoundBox ghép nối vào một thiết bị, báo lỗi rõ ràng khi ghép nối hoặc dùng thất bại. • Hiện trạng: chưa có đấu nối backend nào cho máy quét hay SoundBox; việc in hiện chạy hoàn toàn trong lớp native của app POS, không qua backend thiết bị. | 🚧 | URD-PRN · URD-SCN · URD-SBX |
FR-011 | Đặc tả yêu cầu (Dự kiến, P3): chấp nhận thanh toán không tiếp xúc qua SoftPOS/NFC trên Android, xác thực qua VNPAY KYC. Chưa hạng mục nào trong nhóm này được xây. | 🚧 | URD-NFC-001..004 |
FR-012 | Mỗi thiết bị lưu ngày mua, hết hạn bảo hành, nhà cung cấp, ngày bảo trì gần nhất, ngày bảo trì kế tiếp, và ghi chú tự do. • Tất cả đều là trường thuần trên form tạo/sửa hôm nay - dữ liệu lưu và hiển thị đúng. • Chưa có gì tính ra trạng thái đến hạn/quá hạn từ ngày bảo trì kế tiếp (đó là FR-014) và chưa có gì thông báo cho ai (FR-016). | ✅ | URD-DEV-012 |
FR-013 | Một ngăn kéo tiền đăng ký y hệt một máy POS hay máy tính bảng - cùng form, cùng vòng đời, cùng chỉ mục tìm kiếm - chỉ khác loại thiết bị là Cash Drawer. • Khi một máy POS đã bật ngăn kéo tiền mở ca, luồng mở ca đưa ra danh sách chọn các thiết bị Cash Drawer đã ACTIVATED để gắn vào. • Khác với cờ cashDrawerEnabled trên chính máy POS (chỉ quyết định máy đó có cần ngăn kéo hay không) và khác với phiên đối soát tiền mặt theo ca (ShiftDrawer, thuộc tính năng Ca làm việc nhiều nhân viên của Sale) theo dõi tiền đầu ca và tiền đã đếm dựa trên ngăn kéo đã chọn. | ✅ | URD-DEV-024 |
FR-014 | Danh sách đội thiết bị tính, cho mỗi thiết bị, việc ngày hiện tại đã vượt qua ngày bảo trì kế tiếp đã ghi nhận hay chưa; một thiết bị đã qua ngày đó được đánh dấu đến hạn/quá hạn. • Một thông báo nhắc nhở được gửi khi ngày đó sắp tới (thời gian nhắc trước chính xác đề xuất, chờ Thiết kế xác nhận - xem NFR). | 🚧 | URD-DEV-020 |
FR-015 | Một thuộc tính Tình trạng mới (Đang hoạt động / Đang bảo trì / Hư hỏng), độc lập với vòng đời Trạng thái vận hành năm bước đã có - một thiết bị có thể ACTIVATED (được phép giao dịch) trong khi Hư hỏng (hỏng vật lý). • Quy tắc đề xuất, chờ xác nhận điểm chặn chính xác: đánh dấu Hư hỏng cho một thiết bị đang vận hành sẽ chặn nó khỏi việc mở phiên POS mới hoặc xử lý bán hàng cho đến khi kỹ thuật viên gỡ Tình trạng về Đang hoạt động, mà không đụng đến Trạng thái vận hành bên dưới của thiết bị. | 🚧 | URD-DEV-021 |
FR-016 | Đánh dấu một thiết bị Hư hỏng phát ra một sự kiện mà backbone thông báo của nền tảng (PRD-MON-001) phân phối đến đầu mối phụ trách thiết bị/bảo trì của merchant theo thời gian thực. • Pipeline đó (ingest → worker → bản ghi đã lưu → đẩy trực tiếp) đã được build, nhưng theo PRD-MON-001 hiện chưa có producer nào nạp vào nó - FR này sẽ là một trong những producer thật đầu tiên. | 🚧 | URD-DEV-022 |
FR-017 | Lưu trữ một thiết bị (URD-DEV-008) giờ yêu cầu chọn một Lý do ngừng dùng - Đã thanh lý, Thất lạc, Bị mất cắp, Đã thay thế, hoặc Khác. • Danh sách đội thiết bị và xuất CSV (FR-005/URD-DEV-015) lọc/báo cáo được theo lý do này. | 🚧 | URD-DEV-023 |
FR-018 | Khi hệ thống tạo một thiết bị mới (theo FR-004), nó tự sinh một Mã kích hoạt duy nhất, thể hiện dưới hai dạng song song: QR code và Barcode. • Kích hoạt hoàn tất bằng cách quét hoặc nhập đúng mã đó rồi bấm "Kích hoạt", chuyển thiết bị từ NEW sang ACTIVATED. • Mỗi Mã kích hoạt chỉ dùng được đúng một lần - dùng lại (kể cả trên chính thiết bị đó) bị từ chối. • Bản Barcode song song QR để phục vụ thiết bị không có camera; toàn bộ cơ chế không phụ thuộc bộ nhớ trình duyệt, nên hoạt động cả khi chính sách ITP giới hạn lưu trữ phía client trên Web POS. Đây là nghiệp vụ hoàn toàn mới, chưa có dòng code nào hôm nay. | 🚧 | URD-DEV-027..028 · URD-WEB-006 |
5.1 Tiêu chí nghiệm thu
- Tạo một thiết bị mới từ back-office với tên song ngữ, loại và một merchant bắt buộc → định danh thiết bị được sinh tự động, ổn định, không đổi qua các lần tải lại (FR-001).
- Đặt trạng thái thiết bị sang bất kỳ giá trị nào trong 5 giá trị vòng đời, kể cả bỏ qua bước trung gian → được chấp nhận vì kiểm tra chuyển trạng thái chưa được áp dụng (FR-002 - 🔶).
- Tạo hai thiết bị cùng mã ở hai merchant khác nhau → cả hai được tạo thành công; tạo hai thiết bị cùng mã trong cùng một merchant → lần tạo thứ hai bị từ chối (FR-003).
- 🔶 Đăng nhập trên một phần cứng mới → gọi API kiểm tra thiết bị đã thuộc merchant chưa; nếu chưa, tạo một bản ghi NEW - chưa kiểm thử được đầy đủ theo đặc tả, vì luồng hiện tại dùng
deviceIdlưu cục bộ, không đảm bảo bền vững qua cài đặt lại (FR-004). - Tìm kiếm theo tên, định danh, mã, trạng thái hoặc loại từ back office → chỉ trả về thiết bị thuộc các merchant người dùng đã tham gia (FR-005).
- Tạo một thiết bị mà không chọn merchant → bị chặn vì merchant là trường bắt buộc; sửa một thiết bị để gán lại sang merchant khác → thành công (FR-006).
- Lưu thông tin phần cứng (serial, IMEI, MAC...) và phần mềm (OS, phiên bản app...) cho một thiết bị → dữ liệu được lưu và hiển thị lại đúng khi tải lại màn sửa (FR-007).
- Gọi một API ngoài luồng hành động ca/POS kèm header định danh thiết bị → không có resolver nào phân giải ra thiết bị, vì chưa có resolver toàn nền tảng (FR-008 - 🔶).
- 🚧 Thiết bị gửi heartbeat, hiển thị tình trạng online/offline theo ngưỡng 5/15 phút, và vô hiệu hóa được từ xa - chưa kiểm thử được vì endpoint heartbeat/tình trạng/thao tác từ xa chưa tồn tại (FR-009).
- 🚧 Máy in, máy quét, SoundBox ghép nối và báo lỗi rõ ràng qua backend thiết bị - chưa kiểm thử được vì chưa có đấu nối backend (FR-010).
- 🚧 SoftPOS/NFC nhận thanh toán không tiếp xúc trên Android qua VNPAY KYC - chưa kiểm thử được, Dự kiến P3 (FR-011).
- Lưu ngày mua, hết hạn bảo hành, nhà cung cấp, ngày bảo trì gần nhất/kế tiếp và ghi chú cho một thiết bị → dữ liệu lưu và hiển thị đúng khi tải lại màn sửa (FR-012).
- Đăng ký một thiết bị với loại Cash Drawer → đi qua đúng luồng tạo/sửa/tìm kiếm như mọi thiết bị khác; mở ca trên một máy POS đã bật ngăn kéo tiền → chỉ các thiết bị Cash Drawer đã ACTIVATED được đưa ra để gắn (FR-013).
- 🚧 Ngày bảo trì kế tiếp của một thiết bị là hôm nay hoặc đã qua → nó hiện là đến hạn/quá hạn trên danh sách đội thiết bị, và một thông báo nhắc nhở kích hoạt khi ngày đó sắp tới - chưa kiểm thử được, logic đánh dấu/nhắc nhở chưa tồn tại (FR-014).
- 🚧 Đánh dấu một thiết bị ACTIVATED là Hư hỏng → nó không mở được phiên POS mới hay xử lý bán hàng nữa, mà không đổi Trạng thái vận hành bên dưới; gỡ Tình trạng về Đang hoạt động → nó xử lý bán hàng trở lại - chưa kiểm thử được, trường Tình trạng chưa tồn tại (FR-015).
- 🚧 Đánh dấu một thiết bị Hư hỏng → đầu mối bảo trì được chỉ định của merchant nhận thông báo thời gian thực - chưa kiểm thử được, phụ thuộc cả trigger phía Thiết bị lẫn producer đầu tiên của pipeline thông báo (FR-016).
- 🚧 Lưu trữ một thiết bị và chọn Lý do ngừng dùng "Đã thanh lý" → lý do được ghi lại; lọc danh sách đội thiết bị theo "Đã thanh lý" → trả về đúng thiết bị đó - chưa kiểm thử được, trường này chưa tồn tại (FR-017).
- 🚧 Một thiết bị mới được tạo (kiểm tra-tạo trả về "chưa thuộc merchant") → một Mã kích hoạt duy nhất được sinh, hiển thị cả QR và Barcode - chưa kiểm thử được, cơ chế sinh mã chưa tồn tại (FR-018).
- 🚧 Quét/nhập đúng Mã kích hoạt còn hiệu lực, bấm "Kích hoạt" → thiết bị chuyển NEW → ACTIVATED, mã bị vô hiệu hóa; lặp lại đúng thao tác đó với cùng mã → bị từ chối vì mã đã dùng - chưa kiểm thử được, luồng kích hoạt chưa tồn tại (FR-018).
6. Yêu cầu phi chức năng
| Khía cạnh | Yêu cầu |
|---|---|
| Toàn vẹn dữ liệu | Một thiết bị thuộc đúng một merchant bắt buộc; định danh bất biến; bản ghi dùng soft-delete |
| Định danh | (Dự kiến) Định danh thiết bị phải suy ra/phục hồi được từ phần cứng, bền vững qua các lần cài đặt lại; mọi lời gọi API mang định danh thiết bị - hiện chỉ các hành động POS/ca phân giải một thiết bị. Không có entity device-session |
| Bảo mật kích hoạt | (Dự kiến) 🚧 đề xuất: Mã kích hoạt là chuỗi đủ dài để không đoán được (độ dài/entropy cụ thể chờ Bảo mật xác nhận), bị vô hiệu hóa ngay sau lần dùng hợp lệ đầu tiên, và không phụ thuộc bộ nhớ trình duyệt để xác minh |
| Phân tách & phân quyền | Thao tác giới hạn theo merchant; tìm kiếm thiết bị chỉ trong các merchant người dùng đã tham gia; truy cập API yêu cầu xác thực |
| Hiệu năng / quy mô | Tìm kiếm thiết bị dựa trên chỉ mục; nhịp heartbeat 5 phút giới hạn tải ghi |
| Khả năng phục hồi | SoundBox mất kết nối không bao giờ chặn thanh toán; lỗi in/quét hiện rõ, không âm thầm bỏ qua |
| i18n | Tên thiết bị và nhãn hướng người dùng song ngữ (Anh & Việt); hóa đơn in đúng ký tự tiếng Việt |
| Độ trễ thông báo hư hỏng | (Dự kiến) Một đánh dấu Hư hỏng đến đầu mối bảo trì gần như tức thời, đi trên đúng kênh mà PRD-MON-001 đã build cho hoạt động thiết bị |
| Thời gian nhắc trước bảo trì | (Dự kiến) 🚧 đề xuất: một nhắc nhở kích hoạt trước một số ngày cấu hình được so với ngày bảo trì kế tiếp đã ghi (số mặc định chính xác chờ Thiết kế xác nhận) |
7. UX & Luồng
Màn hình chính: trang thiết bị ở back-office - danh sách/bảng, tạo, sửa, và form chia phần (chung, cấu hình phần cứng, cấu hình phần mềm, cài đặt khác) nằm dưới navigation đã xác thực; màn hình kích hoạt hiển thị/quét Mã kích hoạt (QR + Barcode). Các luồng ngoại vi POS (quét, in, SoundBox, SoftPOS) nằm trong app POS với lớp native thiết bị của nó.
Báo hư hỏng → chặn → thông báo → xử lý xong (forward-spec)
Đánh dấu đến hạn bảo trì định kỳ (forward-spec)
8. Dữ liệu & Miền nghiệp vụ
| Khái niệm | Vai trò |
|---|---|
| Bản ghi thiết bị | Tên song ngữ, loại, Trạng thái vận hành, Định danh thiết bị, relation merchant bắt buộc (không có quyền sở hữu organizer), thông tin phần cứng/phần mềm, cờ ngăn kéo tiền |
| Loại thiết bị | 8 giá trị hôm nay: Máy POS, Máy trạm POS, POS Di động, Trình duyệt Web, Máy tính bảng, Máy quét mã vạch (độc lập), Cash Drawer, Khác - xem URD Thiết bị để có đầy đủ danh mục |
| Thông tin phần cứng | Nhà sản xuất, model, serial, IMEI, MAC, processor, RAM, lưu trữ, màn hình, pin |
| Thông tin phần mềm | OS, phiên bản OS, phiên bản app, firmware, cập nhật cuối, driver ngoại vi |
| Thông tin bảo trì | Ngày mua, hết hạn bảo hành, nhà cung cấp, ngày bảo trì gần nhất, ngày bảo trì kế tiếp, ghi chú - tất cả Đã dựng (FR-012); đánh dấu đến hạn/quá hạn từ ngày bảo trì kế tiếp là 🚧 (FR-014) |
| Mã kích hoạt (mới, khái niệm) | Mã dùng-một-lần sinh khi tạo thiết bị mới, thể hiện dưới dạng QR và Barcode; chuyển thiết bị NEW → ACTIVATED khi quét/nhập đúng rồi bấm "Kích hoạt" (FR-018) |
| Tình trạng thiết bị (mới, khái niệm) | Đang hoạt động / Đang bảo trì / Hư hỏng - độc lập với Trạng thái vận hành; điều khiển hành vi chặn và thông báo hư hỏng (FR-015/016) |
| Lý do ngừng dùng (mới, khái niệm) | Đã thanh lý / Thất lạc / Bị mất cắp / Đã thay thế / Khác - ghi khi lưu trữ một thiết bị (FR-017) |
Cash Drawer (thiết bị) so với ShiftDrawer (Sale) | Bản ghi thiết bị Cash Drawer là mục trong đội thiết bị của phần cứng vật lý; ShiftDrawer là phiên tài chính riêng theo ca của Sale tham chiếu đến thiết bị Cash Drawer đã chọn - hai thứ liên quan nhau, không phải cùng một entity |
| Device session | (Dự kiến) một phiên đang hoạt động gắn vào một thiết bị, mở lúc đăng nhập - hiện chưa có entity này |
| Chỉ mục tìm kiếm thiết bị | Lập chỉ mục thiết bị, kể cả trường mô tả |
Chỉ ở mức khái niệm - schema và bất biến đầy đủ trong commerce domain model.
9. Phụ thuộc & Giả định
Phụ thuộc vào
- Miền commerce - sở hữu bản ghi thiết bị, gán merchant bắt buộc, thông tin phần cứng/phần mềm, và trường trạng thái.
- Nền tảng tín hiệu thời gian thực - edge thời gian thực cho sự kiện thiết bị/thanh toán và chỉ mục tìm kiếm thiết bị.
- Client apps - back office (quản lý đội thiết bị), app POS (UI POS), và lớp native thiết bị của nó (USB/NFC/máy in/máy quét); màn hình quét/nhập Mã kích hoạt dùng chung hạ tầng quét với
SCN, kể cả phím tắt chuyển chế độ quét/nhập trên desktop (URD-SCN-008). - PRD-MON-001 - Thông báo & tín hiệu thiết bị - pipeline thông báo mà cảnh báo hư hỏng (FR-016) sẽ đi trên đó; đã build end-to-end cho một use case thật, nhưng chưa có producer nào cho một luồng hoạt động chung.
- PRD-SHF-001 - Ca làm việc nhiều nhân viên - sở hữu
ShiftDrawer, phiên đối soát tiền mặt theo ca tham chiếu đến thiết bị Cash Drawer đã chọn.
Giả định
- Một merchant tồn tại trước khi tạo thiết bị - việc gán là bắt buộc, không tùy chọn.
- Phần cứng cho phép suy ra hoặc phục hồi một định danh bền vững (machine identifier, thiết bị lưu trữ an toàn cấp hệ điều hành, hoặc cơ chế tương đương) - không phải mọi nền tảng client đều có sẵn cùng một cơ chế, nên cách suy ra định danh có thể khác nhau giữa POS/di động và trình duyệt. Trên bản web, định danh được lưu trong IndexedDB của trình duyệt (không phải localStorage) - xem URD-WEB-006.
- Người bấm "Kích hoạt" có quyền quản lý thiết bị trong phạm vi merchant đó (đề xuất chủ sở hữu/quản lý - chốt chính xác thuộc thiết kế phân quyền).
- Phần cứng được chứng nhận (VNPAY V-POS, Sunmi T2) sẵn có cho kịch bản máy POS.
- Một merchant có ít nhất một đầu mối phụ trách thiết bị/bảo trì để nhận thông báo hư hỏng; nếu không có, đánh dấu Hư hỏng vẫn được ghi nhận nhưng không có gì được gửi đi.
Rủi ro & Câu hỏi mở
| Rủi ro / câu hỏi | Giảm thiểu / trạng thái |
|---|---|
| Trùng bản ghi thiết bị nếu định danh không bền vững qua các lần cài đặt lại | Định danh phải suy ra/phục hồi được từ phần cứng (URD-DEV-025), không chỉ sống trong local storage; kiểm tra-rồi-tạo tái dùng bản ghi đã có theo đúng định danh đó |
| Trình duyệt bật ITP khiến phép kiểm tra tự động coi cùng một máy là thiết bị mới mỗi lần | Kích hoạt qua Mã kích hoạt (QR/Barcode) không phụ thuộc bộ nhớ trình duyệt, nên vẫn hoàn tất được dù phép kiểm tra tự động không đáng tin |
| Mã kích hoạt bị lộ (chụp màn hình, chia sẻ) có thể bị dùng trước bởi người không liên quan | Mỗi mã chỉ dùng được một lần - dùng xong lập tức vô hiệu; ai được phép bấm "Kích hoạt" chốt ở thiết kế phân quyền (mở, xem Loại trừ) |
| Backend VNPAY Terminal chưa đấu nối (đã có chỗ giữ) | Ngoài phạm vi - phát hiện QE đã đánh dấu; theo dõi cho tăng tiến sau |
| SoftPOS / NFC phụ thuộc VNPAY KYC và phần cứng Android | Dự kiến (P3); ẩn tùy chọn trên iOS, chặn khi NFC không khả dụng |
| Tải ghi heartbeat ở quy mô đội thiết bị | Giới hạn bởi nhịp 5 phút; xem lại nếu tải tăng |
| Xóa dữ liệu từ xa khi thiết bị offline | Đưa vào queue và áp dụng lúc kết nối lại |
| Thông báo hư hỏng phụ thuộc một pipeline chưa có producer (PRD-MON-001) | Theo dõi như một phụ thuộc chung; FR-016 của PRD này không ra mắt độc lập |
| Quy tắc Hư hỏng-chặn-phiên (FR-015) chưa được xác nhận với điểm chặn chính xác ở đăng nhập POS/mở ca | Quy tắc đề xuất chờ Thiết kế/Kỹ thuật ký duyệt; đánh dấu như vậy trong URD (C-14) |
| Chưa cấu hình đầu mối phụ trách thiết bị/bảo trì cho một merchant | Đánh dấu Hư hỏng vẫn ghi nhận; không có gì được gửi cho đến khi định nghĩa cấu hình đầu mối - câu hỏi mở |
Câu hỏi thường gặp
| Câu hỏi | Trả lời |
|---|---|
| Tôi có phải đăng ký thủ công mọi thiết bị không? | Không bắt buộc - đăng nhập trên phần cứng chưa thuộc merchant tự tạo một thiết bị NEW; back-office vẫn hỗ trợ tạo thủ công. Luồng kiểm tra-rồi-tạo này là dự kiến, chưa dựng theo đúng đặc tả. |
| Một thiết bị thuộc về merchant hay organizer? | Một merchant bắt buộc - mỗi thiết bị được tạo gắn với đúng một merchant. Không có quyền sở hữu organizer trên model. |
| Vì sao thiết bị mới không tự động kích hoạt luôn? | Vì kích hoạt là một quyết định có chủ đích, không phải một tác dụng phụ của việc cài app - ai đó phải xác nhận đúng thiết bị được cấp quyền giao dịch bằng cách quét/nhập Mã kích hoạt. |
| Nếu tôi dùng lại một Mã kích hoạt đã dùng rồi thì sao? | Bị từ chối. Mỗi mã chỉ hoàn tất kích hoạt đúng một lần, kể cả khi thao tác lại trên chính thiết bị đó. |
| Vì sao cần cả Barcode lẫn QR? | QR cần camera để quét; không phải thiết bị nào cũng có camera. Barcode phục vụ đúng nhóm đó - quét bằng máy quét mã vạch hoặc nhập tay. |
| Định danh thiết bị có mất khi tôi gỡ cài đặt app không? | Không nên mất - đây là bất biến cốt lõi của bản v0.4 này. Cài đặt lại trên đúng phần cứng phải nhận lại đúng định danh cũ. |
| SoundBox mất kết nối có chặn thanh toán không? | Không - thanh toán vẫn thành công; thông báo âm thầm được bỏ qua. |
| SoftPOS có sẵn chưa? | Chưa - SoftPOS / NFC là Dự kiến (P3), chỉ Android, và yêu cầu VNPAY KYC. |
| Tôi có đăng ký được một ngăn kéo tiền như thiết bị riêng không? | Có - ngay hôm nay. Chọn loại thiết bị Cash Drawer; nó đi qua đúng luồng đăng ký/vòng đời/tìm kiếm như mọi thiết bị khác. |
| Làm sao biết thiết bị nào đang hỏng ngay bây giờ? | Chưa thể - Tình trạng thiết bị (Đang hoạt động / Đang bảo trì / Hư hỏng) chưa tồn tại. |
| Tôi có được thông báo tự động nếu một máy POS hỏng không? | Chưa - thông báo hư hỏng (FR-016) phụ thuộc cả logic mới phía Thiết bị lẫn việc pipeline thông báo có producer thật đầu tiên. |
| Tôi có xem được thiết bị nào đã thanh lý so với thất lạc hay thay thế không? | Chưa - Lý do ngừng dùng chưa tồn tại. |
Kế hoạch phát hành & Tiêu chí
| Khía cạnh | Kế hoạch |
|---|---|
| Phase | DEV / POS / PRN / SCN / MON = P2 (Một phần); MOB / WEB / SBX = P1 (Đã dựng); NFC = P3 (Dự kiến) - xem catalog tính năng URD |
| Triển khai | Tất cả organizer; không feature flag |
| Di trú | Dọn model - gỡ cột sở hữu organizer, thêm relation merchant bắt buộc, sửa unique index thành theo từng merchant; thêm trường Định danh thiết bị bền vững, Mã kích hoạt, Tình trạng thiết bị, Lý do ngừng dùng - không cần backfill dữ liệu hiện có |
| Tiêu chí ra mắt | Các tiêu chí nghiệm thu §5.1 đã ✅ đạt cho list/create/edit ở back-office, tìm kiếm thiết bị theo phạm vi merchant, ghi thông tin bảo trì, và đăng ký Cash Drawer; các mục đánh dấu 🚧/🔶 (định danh bền vững, kiểm tra-rồi-tạo, kích hoạt dùng-một-lần qua Barcode/QR, heartbeat/tình trạng, ngoại vi, SoftPOS, đánh dấu đến hạn bảo trì, Tình trạng thiết bị, thông báo hư hỏng, lý do ngừng dùng) còn chờ |
| Giám sát | (Dự kiến) tỷ lệ kiểm tra-rồi-tạo đúng, số phiên mồ côi, số lần dùng lại Mã kích hoạt bị từ chối, độ chính xác online/offline của heartbeat, tỷ lệ lỗi ngoại vi, độ trễ báo hư hỏng, số thiết bị quá hạn bảo trì, phân bổ Lý do ngừng dùng |
Tham chiếu
- URD: Thiết bị - DEV · MON · PRN · SCN · SBX · NFC · MOB · WEB · POS · DSX
- PRD liên quan: PRD-MON-001 - Thông báo & tín hiệu thiết bị · PRD-DSX-001 - Ngữ cảnh ca & liên kết máy in theo thiết bị · PRD-SHF-001 - Ca làm việc nhiều nhân viên
- Module: Thiết bị - URD
- Developer: @nx/commerce · @nx/signal · domain model
- Apps: sale-renderer · sale-main · client