Skip to main content

Cashier Management — Gap Analysis (AC Review)

Tài liệu đối chiếu Acceptance Criteria tổng quát với implementation hiện tại trong:

  • Backend: kioskgaming_backend/src/modules/cashier-management/
  • Cashier portal: kioskgaming_cashier/
  • Spec: spec.md
  • QC: QC checklist

Ngày review: 2026-06-10


Tổng quan nhanh

NhómTrạng thái
Core flow (login, shift, opening, deposit/redeem, blind close)~70% có
Enforcement chặt (device, limit, handover, auto-lock UI)Còn gap
Shop-side (tạo cashier, no-show, reconciliation)Phần lớn ở Shop portal — Cashier chỉ dùng

Cashier portal hiện có 7 màn hình: Login, My Shifts, Opening Cash, Active Shift, Cash Drop, Closing, Handover — khớp QC ship gate.


Acceptance Criteria — Ma trận đối chiếu

#Acceptance CriteriaTrạng tháiGhi chú
1Shop tạo Cashier thuộc đúng một Shop✅ BackendModel shopId; Shop UI chỉ tạo username/password
2Cashier login bằng email, username hoặc phone✅ Backend / ⚠️ Shop UIfindByIdentifier; Shop chưa form email/phone
3Shop tạo shift với ngày, time, Cashier, devicecashierShiftScheduleBizService
4Chặn Cashier overlap shiftSHIFT_OVERLAP 409
5Chặn device overlap shiftCùng logic overlap
6Cashier chỉ start shift trên đúng device assign⚠️ Một phầnChỉ check fingerprint nếu có; thiếu so deviceId
7Cashier nhập opening cash trước giao dịchStart shift bắt buộc denomination
8Cashier chỉ cash tx với PlayerpaymentMethod = 'cash' cứng
9Deposit không yêu cầu Player OTPCounter flow không OTP
10Redemption theo limit và approval✅ Backend / ⚠️ FELogic có; UI hiển thị sai khi pending
11Mọi tx lưu cashierId, shiftId, shopId, deviceIdCashierTransaction model
12Cashier chỉ xem tx shift của mìnhlistByCashier filter
13Cashier không sửa/hủy/reverse txKhông có API
14Closing count blind countblindCloseShift + ClosingCountPage
15Tự động tính Expected + DiscrepancyexpectedClosingCashService
16Discrepancy → Shop Manager reviewpending_manager_review + notify
17No-show chỉ Shop xử lý, không notify AdminnoShowJob → shop only
18Device dùng bởi nhiều Cashier ở shift khácOverlap validation cho phép reuse tuần tự
19Block Device ≠ Suspend CashierTách service
20Handover theo denomination + xác nhận người nhận⚠️ Một phầnAPI có; flow cross-cashier chưa đúng
21Auto-Locked vẫn reconciliation⚠️ Backend ✅ / FE ❌BE cho close; FE block auto_locked
22Mọi action quan trọng có audit log⚠️ Một phầnThiếu create/suspend cashier, revoke device

Đã đáp ứng — Không cần thêm ngay

Authentication

  • Login bằng identifier (username / email / phone): CashierRepository.findByIdentifier, cashierAuthBizService.login
  • Cashier portal: CashierLoginPage gửi identifier + deviceFingerprint

Shift scheduling & overlap

  • Tạo shift scheduled với cashier + device + time: cashierShiftScheduleBizService
  • Chặn overlap cashier và device: _assertNoOverlap409 SHIFT_OVERLAP

Start shift & opening cash

  • Start shift với denomination: cashierShiftOpsBizService.startShift
  • UI: OpeningCashPagePOST /cashier/shifts/:id/start
  • Opening cash bắt buộc trước khi shift active → transaction chỉ khi shift active

Transactions

  • Deposit cash, không OTP: cashierTransactionBizService.depositCredits (paymentMethod = 'cash')
  • Redemption approval: cashierRedemptionApprovalBizService (threshold + daily limit qua env)
  • Ghi đủ metadata: cashierId, shiftId, shopId, deviceId trên CashierTransaction
  • Không có API sửa/hủy/reverse giao dịch

Blind closing & discrepancy

  • Blind close: blindCloseShift — expected chỉ trả sau submit
  • UI: ClosingCountPage không hiện expected trước submit
  • Discrepancy → pending_manager_review + notify shop (notifyShiftDiscrepancy)

Jobs & notifications

  • No-show: noShowJob → status no_show, audit, notify shop (không admin)
  • Auto-lock: autoLockJob → status auto_locked, notify shop
  • Worker: cashierManagementWorker.js (tick 60s)

Device & cashier status

  • Block device revoke sessions: cashierDeviceBizService.setDeviceStatus
  • Suspend cashier revoke sessions + suspend shift: shopCashierManagementBizService._setStatus
  • Hai luồng tách biệt

Audit (phần đã có)

  • Shift started/closed, deposit, redeem, cash drop, handover initiate/confirm, redemption approve/reject, shift reconciled

Gap quan trọng — Cashier cần bổ sung

P0 — Auto-Locked: Cashier không close được trên UI

Vấn đề: Backend blindCloseShift cho phép active, auto_locked, suspended. Frontend shiftAccess.ts chỉ coi active là operable (SHIFT_JOIN_STATUSES = ['active']).

Hệ quả: ClosingCountPage, CashDropPage, ActiveShiftPage redirect/block khi auto_locked.

Vi phạm AC: Shift bị Auto-Locked vẫn phải thực hiện reconciliation.

Cần làm:

  • Mở rộng canJoinShift hoặc thêm canCloseShift gồm auto_locked (và có thể suspended)
  • CTA "Close shift" trên MyShiftsPage khi shift auto_locked
  • Banner thông báo shift đã bị khóa

File liên quan:

  • kioskgaming_cashier/src/utils/shiftAccess.ts
  • kioskgaming_cashier/src/pages/ClosingCountPage.tsx
  • kioskgaming_cashier/src/pages/MyShiftsPage.tsx

P0 — Start shift trên đúng device assign (chưa chặt)

Vấn đề: startShift chỉ:

  1. Check device assigned có status === 'active'
  2. Check fingerprint nếu cả request và device record đều có fingerprint

Thiếu:

  • So sánh deviceId từ request/session với shift.deviceId
  • Bắt buộc fingerprint khi device đã có fingerprint
  • Reject khi device chưa được shop approve (nếu assign device pending)

File: kioskgaming_backend/src/modules/cashier-management/services/biz/cashierShiftOpsBizService.js (khoảng dòng 102–116)

Cần thêm:

if (shift.deviceId && deviceId && String(deviceId) !== String(shift.deviceId)) {
throw { code: 'DEVICE_MISMATCH', statusCode: 403 };
}

P1 — Redemption pending approval — UX sai

Vấn đề: Backend trả pendingApproval: true khi cần duyệt. ActiveShiftPage luôn toast "Redeem completed".

Cần làm:

  • Kiểm tra res.data.pendingApproval trong doRedeem
  • Hiển thị trạng thái pending_approval trong danh sách giao dịch
  • Toast: "Chờ Shop Manager duyệt" thay vì completed
  • Không cho cashier coi là đã trả tiền cho player

File: kioskgaming_cashier/src/pages/ActiveShiftPage.tsx


P1 — Handover giữa 2 Cashier — flow chưa đúng spec

Spec (§10): Cashier A bàn giao → Cashier B (hoặc Manager) xác nhận. Closing A → Opening B.

Vấn đềChi tiết
UI initiate chỉ list shift của chính cashierassignedShifts.filter(s => s.id !== id) — không chọn shift Cashier B
Confirm không verify người nhậnconfirmHandover không check cashierId === pending.toCashierId
Không đóng shift A sau handoverChỉ tạo record, không transition shift A
Không notify handover discrepancySpec §15 yêu cầu notify Shop Manager

Cần làm:

  • API/UI chọn incoming shift theo shop (shift cashier khác, scheduled, cùng device nếu cần)
  • Verify receiver khi confirm
  • Đóng shift A / chuẩn bị mở shift B theo spec
  • Notify nếu discrepancy !== 0

File:

  • kioskgaming_backend/src/modules/cashier-management/services/biz/cashierHandoverBizService.js
  • kioskgaming_cashier/src/pages/HandoverPage.tsx

P2 — Shop tạo Cashier thiếu email/phone

Vấn đề: Model có email, phone, loginMethod. updateCashierProfile hỗ trợ patch. Shop UI chỉ tạo username + password (CashiersPage.tsx).

Ảnh hưởng AC: Cashier login bằng email/username/phone — backend sẵn sàng nhưng shop không nhập email/phone khi tạo.

Cần làm (Shop portal):

  • Form tạo/sửa cashier: fullName, email, phone, loginMethod
  • Validate unique email/phone trong shop

P2 — Transaction limit cho Deposit — chưa có

Spec §5.1: Hệ thống kiểm tra transaction limit.

Module cashier không gọi transactionLimitsValidationService. Redemption có threshold/daily qua env; deposit chưa có.

Cần làm: Validate min/max per-tx và daily deposit limit trong depositCredits.


P2 — Audit log chưa đủ (spec §14)

Thiếu:

  • cashier_created
  • cashier_suspended / cashier_terminated / cashier_unsuspended
  • device_access_revoked

shopCashierManagementBizService suspend/terminate/create không gọi auditBiz dù đã inject.


P3 — Revoke Cashier access trên device — chưa implement

Có bảng cashier_device_access + CashierDeviceAccessRepository, nhưng không wire trong bootstrapCashierManagement — không API, không enforce khi login/start shift.

Spec §11.2: Revoke quyền một cashier trên một device; device vẫn active cho cashier khác.


P3 — Auto-lock — thiếu cảnh báo và notify Cashier

Spec §9:

  1. Cảnh báo Cashier trước scheduled end
  2. Closing grace period cho giao dịch đang xử lý
  3. Notify Cashier + Shop Manager

Hiện tại: Job set auto_locked + Telegram shop (notifyAutoLock). Không in-app warning, không notify cashier.

Cần làm (Cashier FE):

  • Banner countdown trước scheduledEndAt
  • Banner khi auto_locked
  • (Tùy chọn) polling/WebSocket cho trạng thái shift

Gap nhỏ — Nên fix

GapGhi chú
loginMethod không enforceAuth không check method đã cấu hình
Logout chỉ xóa localStorageKhông gọi cashier-auth/logout — session server còn đến hết TTL
Player balance không hiển thịDeposit/redeem dễ nhập sai số
Shift pending_reconciliation / completedCashier xem được nhưng UI chưa summary reconciliation rõ
Handover initiate không yêu cầu closing trướcSpec: A dừng giao dịch + closing count trước handover
Device pending approval UXStart fail nếu shop chưa approve — cần hướng dẫn rõ hơn
Test coverage mỏngChủ yếu unit helper/state machine; thiếu integration tx/device/handover
openedAt set khi tạo scheduled shiftopeningCashBalance: 0, openedAt: scheduledStartAt — có thể gây nhầm reporting

Acceptance Criteria nên bổ sung vào checklist

Các mục spec đã có nhưng list AC tổng quát chưa nêu:

  1. Device phải được Shop approve trước khi start shift trên device đó.
  2. Cashier nhận cảnh báo trước scheduled end và khi shift auto-locked.
  3. Redemption pending approval — Cashier thấy trạng thái chờ duyệt, không coi là hoàn tất.
  4. Handover cross-cashier — A chọn shift của B; chỉ B (hoặc Manager) được confirm.
  5. Deposit transaction limits — per-tx và/hoặc daily.
  6. Cashier xem balance Player khi deposit/redeem (read-only).
  7. Revoke device access per cashier — tách với block device.
  8. Server-side logout — revoke session khi Sign out.
  9. Cashier xem shift report sau completed (read-only, không thấy shop balance).
  10. Shop Portal deposit vs Cashier deposit — hai luồng song song: Shop giữ CDN add credits; Cashier deposit gắn shift/cash ledger.

Đề xuất ưu tiên triển khai

flowchart TD
P0A[Fix auto_locked closing UI] --> P0B[Device ID validation on start]
P0B --> P1A[Redemption pending UX]
P1A --> P1B[Handover cross-cashier flow]
P1B --> P2A[Deposit limits + audit gaps]
P2A --> P3A[Auto-lock warnings + cashier notify]
Ưu tiênHạng mụcLý do
P0Auto-locked → cho close trên Cashier appBlock reconciliation hàng ngày
P0Device ID match shift.deviceIdSecurity / fraud
P1Redemption pending approval UXTránh trả tiền sai
P1Handover A→B đúng specCa kế tiếp không start được
P2Shop form email/phone cashierLogin AC chưa usable thực tế
P2Deposit transaction limitsCompliance
P3Audit suspend/create, revoke device accessAudit trail đầy đủ
P3Auto-lock warnings + notify cashierSpec §9

File map — Cashier module

Backend (kioskgaming_backend/src/modules/cashier-management/)

FileVai trò
services/biz/cashierAuthBizService.jsLogin/logout
services/biz/cashierShiftScheduleBizService.jsTạo/sửa shift, overlap
services/biz/cashierShiftOpsBizService.jsStart shift, blind close
services/biz/cashierTransactionBizService.jsDeposit/redeem
services/biz/cashierRedemptionApprovalBizService.jsApproval rules
services/biz/cashierHandoverBizService.jsHandover
services/biz/cashierCashDropBizService.jsCash drop
services/biz/cashierReconciliationBizService.jsManager reconcile
jobs/noShowJob.jsNo-show detection
jobs/autoLockJob.jsAuto-lock
utils/shiftStateMachine.jsStatus transitions
ports/cashierNotifyPort.jsTelegram notifications

Cashier portal (kioskgaming_cashier/src/)

FileMàn hình
pages/CashierLoginPage.tsxLogin
pages/MyShiftsPage.tsxDanh sách shift
pages/OpeningCashPage.tsxOpening cash / start
pages/ActiveShiftPage.tsxDeposit/redeem + tx list
pages/CashDropPage.tsxCash drop
pages/ClosingCountPage.tsxBlind close
pages/HandoverPage.tsxHandover
utils/shiftAccess.tsQuyền truy cập theo status
services/cashierApi.tsAPI client

Kết luận

Phần Cashier core (login, assigned shifts, opening cash, deposit/redeem cash, blind close, cash drop, list tx shift mình) đã có khung backend + FE tương đối đầy đủ.

Cần bổ sung chủ yếu ở enforcement + UX nghiệp vụ:

  • Device assignment chặt hơn (deviceId === shift.deviceId)
  • Auto-locked reconciliation trên Cashier app
  • Handover giữa hai cashier
  • Redemption pending state trên UI
  • Transaction limits cho deposit
  • Notify/cảnh báo auto-lock cho cashier
  • Shop tạo cashier đủ email/phone để login AC hoạt động thực tế

Tham chiếu