Appearance
QA Test Plan — Prepaid Card Analytics Tab
Date: 2026-05-04 · Phase: 1 (MVP) Parent: prd.md · dev-spec.md · ui-spec.mdScope: 4 sub-tab Phase 1 (Tổng quan · Giao dịch · Khách hàng · Tài chính). Marketing + Nhân viên defer Phase 3+ — KHÔNG test.
v3.15 — 14/05/2026
| Thay đổi | Section | Ảnh hưởng |
|---|---|---|
Coverage matrix + US-3.2 đổi table Khách hàng 12 → 13 cột sau khi tách Dư ví DIVA và Dư ví KM | D1.1 Functional coverage matrix · D2 Requirements Matrix | QA |
QA cần verify risk highlight theo tổng Dư ví DIVA + Dư ví KM > 5tr, không highlight riêng từng cột | US-3.2 | QA, FE |
D1. Phạm vi test (Coverage)
D1.1 Functional coverage matrix
| Feature area | Test type | Priority | Owner |
|---|---|---|---|
| Sub-tab Tổng quan (KPI cards · 4 charts · alerts · rankings) | Functional + Visual | P0 | QA |
| Sub-tab Giao dịch (table 10 cột · expand · 2 local filters — DEC-U12 · sum cards) | Functional + Performance | P0 | QA |
| Sub-tab Khách hàng (segment cards · behavior bar · table 13 cột read-only · Xuất Excel ở header) | Functional + Integration | P0 | QA |
Sub-tab Tài chính (4 tab con: Tổng hợp DT · Công nợ · Hoa hồng · PTTT · 5 nút Xuất {Tên báo cáo}) | Functional + Data accuracy | P0 | QA + Kế toán |
| Shared filter bar (3 element: Chi nhánh · Khoảng thời gian · Tìm kiếm) | Functional | P0 | QA |
Phân quyền v2 fine-grained (3 actions: view · export · view_full_phone) per role matrix | Security | P0 | QA + DevOps |
4 MVs P1 (order_daily · card_daily · customer_stats · finance_daily) — schema migration + data accuracy reconciliation | Backend | P0 | BE |
| Charts time-series luôn theo ngày (không có toggle Tuần/Tháng — DEC-U08) | Functional | P0 | QA |
| MV refresh + data freshness (15-min cron 3 MVs · 30-min 1 MV) | Backend | P1 | BE |
Export Excel server-side (label đồng bộ tiếng Việt: Xuất Excel / Xuất {Tên báo cáo} — DEC-U10) | Performance | P0 | BE + QA |
| Feature flag rollback | Operations | P0 | DevOps |
D1.2 Ngoài phạm vi (Out of scope)
- Sub-tab Marketing (defer P3+)
- Sub-tab Nhân viên (defer P3+)
- Global Search xuyên sub-tab (defer P2)
- MVs
mv_prepaid_staff_stats,mv_prepaid_campaign_stats(defer P3+) - Bulk SMS / ZNS / Gán NV trong Sub-tab Khách hàng (DEC-U09 — out of scope kể cả P1.x)
- Multi-select checkbox trong bảng KH (DEC-U09 — read-only)
- Xác nhận thanh toán write action ở badge "Còn nợ" (P1 chỉ navigate, write action defer)
- Toggle Ngày/Tuần/Tháng trên charts (DEC-U08 — luôn theo ngày)
Compare Mode toggle(DEC-U04 — bỏ)- Migration
branch_region(đã córegion_branch— chỉ data check)
D2. Requirements Matrix (US → Test Case)
| US ID | User Story | Test Cases | Acceptance |
|---|---|---|---|
| US-1.1 | Xem KPI tổng quan | TC-OV-01..08 | 8 KPI cards đúng số + so sánh kỳ trước · click navigate đúng sub-tab |
| US-1.2 | Xem cảnh báo vận hành | TC-OV-09..12 | 3 mức alert hiển thị · [Xem] navigate đúng filter |
| US-1.3 | Xem biểu đồ xu hướng | TC-OV-13..16 | 4 charts grid 2×2 · chỉ theo ngày (DEC-U08 — không có toggle Tuần/Tháng) · responsive |
| US-1.4 | Xem top thẻ / NV / KH | TC-OV-17..19 | 3 mini-tables · top NV navigation disabled + tooltip P3+ |
| US-2.1 | Tra cứu giao dịch | TC-TX-01..06 | Table 10 cột · 2 local filters (loại thẻ/trạng thái — DEC-U12 bỏ local search) · shared search top apply scope · pagination |
| US-2.2 | Expand chi tiết đơn | TC-TX-07..10 | 3 inner tabs · lazy load < 200ms |
| US-2.3 | Quick navigation | TC-TX-11..13 | Mã đơn / KH navigate được · NV bán disabled |
| US-3.1 | Phân khúc KH tự động | TC-CT-01..05 | 4 segment cards · click filter table · % đúng (mẫu số đúng) |
| US-3.2 | Tra cứu KH | TC-CT-06..12 | Table 13 cột · expand 3 tabs · SDT mask theo permission |
| US-3.3 | Tải Excel danh sách KH | TC-CT-13 | Nút [📥 Xuất Excel] ở header bảng (góc phải) · click → server-side export toàn bộ KH theo filter (DEC-U09 — KHÔNG có multi-select/SMS/ZNS/Gán NV) |
| US-3.4 | Chỉ số hành vi KH | TC-CT-17, TC-CT-18, TC-CT-CYCLE-01..05 | 3 metrics (AOV · Tái nạp · Chu kỳ TB) · trend label dynamic theo filter · cycle = customer-weighted mean (DEC-T09) |
| US-4.1 | Dashboard tài chính | TC-FN-01..08 | 8 KPI cards · công thức dòng tiền hiển thị |
| US-4.2 | DT theo thời gian | TC-FN-09..12 | Tab Tổng hợp · group by · line chart kỳ trước |
| US-4.3 | Quản lý công nợ | TC-FN-13..15 | Tab Công nợ · phân mức (< 15d / 15-30d / > 30d) · chỉ 1 hành động [📞 Gọi] (TT/Ghi chú = OUT OF SCOPE Phase 1, P1 read-only) |
| US-4.4 | Đối soát hoa hồng | TC-FN-19..22 | Tab HH · expand đơn · chỉ tính status='S' |
| US-4.5 | DT theo PTTT | TC-FN-23..25 | Tab PTTT · donut + table |
| US-4.6 | Xuất Excel cho kế toán | TC-FN-26..30 | 5 export server-side · progress · 50K rows < 60s |
| US-7.1 | Lọc CN (70 CN) | TC-FT-01..04 | Branch selector · grouped tree · search · default theo role |
| US-7.2 | Khoảng thời gian | TC-FT-05..09 | Dropdown gộp presets · custom range · max 365d · trend label dynamic |
D3. Dữ liệu seed
D3.1 Seed cơ sở dữ liệu
🛑 V1 BLOCKER — Schema chưa confirmed: Codebase dùng
ecommerce_user(KHÔNG cócustomertable).account_typecột CHƯA xác nhận trong codebase — KHÔNG được hardcode trong seed. Template dưới dùng placeholder<CUSTOMER_FIELDS>— BE thay đúng cột thực tế trước khi chạy seed. Có thể rule là implicit (KH = user có ≥ 1 prepaid order, không cần filter explicit).
sql
-- Region (đã có sẵn — verify only — V7)
SELECT * FROM region_branch; -- expect ≥ 4 (HCM, HN, ĐN, CT)
-- Branch (verify all 70 có region_id)
SELECT id, name, region_id FROM branch WHERE deleted_at IS NULL; -- expect 70
-- Customers seed (test environment) — dùng ecommerce_user
-- 🛑 V1 BLOCKER: thay <CUSTOMER_FIELDS> bằng cột thực sau BE confirm
INSERT INTO ecommerce_user (display_name, phone_number /*, <CUSTOMER_FIELDS> */) VALUES
('TC Khách Hoạt Động', '0900000001'),
('TC Khách Ngủ Đông', '0900000002'),
('TC Khách Rủi Ro', '0900000003'),
('TC Khách Mới', '0900000004'),
('TC Khách VIP', '0900000005');
-- Sau seed users, tạo prepaid orders cho từng KH với các pattern segment behaviors (D3.2)
-- Prepaid orders với các segment behaviors:
-- - KH Hoạt Động: paid_at = today, last wallet usage = today
-- - KH Ngủ Đông: paid_at = 45 days ago, last wallet usage = 35 days ago
-- - KH Rủi Ro: paid_at = 80 days ago, last wallet usage = 70 days ago
-- - KH Mới: paid_at = today, no wallet usage yet (test segment 'new' đầu CASE)
-- - KH VIP: total_paid > 20tr OR order_count >= 5D3.2 Test scenarios — Coverage matrix
| Scenario | Loại thẻ | Trạng thái TT | KH segment | Số đơn | Mục đích test |
|---|---|---|---|---|---|
| S1 | Cố định 10tr | Đã thanh toán | Hoạt động | 1 | Happy path |
| S2 | Linh hoạt 7,5tr | Còn nợ (paid 5tr / total 7,5tr) | Hoạt động | 1 | Công nợ logic |
| S3 | Cố định 20tr | Đã TT | VIP | 5 | Top customer ranking |
| S4 | Cả Cố định + Linh hoạt mix | Đã TT | Mới | 2 | Sub-breakdown "X CĐ + Y LH" |
| S5 | Cố định 5tr | Chưa TT (paid_amount = 0) | Mới | 1 | Edge: paid_amount = 0 |
| S6 | Linh hoạt 10tr | Đã TT | Ngủ đông | 1 | Segment classification |
| S7 | Cố định 1tr | Đã TT, hủy | Hoạt động | 1 | Cancel logic exclusion |
| S8 | Cố định 10tr × 70 CN | Đủ mix | Đủ mix | 100+ | Multi-branch aggregation |
D3.3 Dataset performance
- Min dataset: 10K orders, 1K customers, 10 branches → smoke test load < 500ms
- Real dataset: 100K customers + 1M orders + 70 branches → production-like load test
- Stress dataset: 5M orders → MV refresh time + query degradation profiling
D4. Ca kiểm thử chọn lọc P0 critical
TC-OV-01: KPI "Tiền thu vào" tính đúng
Pre-condition: Seed S1 (1 đơn 10tr đã thanh toán). Steps:
- Login admin → vào
/r/reports/prepaid-card-analytics - Sub-tab Tổng quan, filter Tháng này. Expected: KPI "Tiền thu vào" = 10.000.000đ. Tooltip hiển thị "Tổng tiền thực tế thu được từ bán thẻ trả trước...".
TC-OV-19: Top NV navigation disabled (Phase 1)
Pre-condition: Có dữ liệu trong Top 5 NV section. Steps:
- Hover tên NV bất kỳ trong Top 5. Expected: Cursor
not-allowed+ tooltip "Sub-tab Nhân viên ra mắt Phase 3+". Click → KHÔNG navigate.
TC-TX-01: Table giao dịch hiển thị 10 cột
Pre-condition: Seed S1, S2, S4. Steps:
- Sub-tab Giao dịch. Expected: Bảng có 10 cột đúng thứ tự: Mã đơn · Ngày TT · Khách hàng · Tên thẻ (badge inline) · SL · Tiền thu · Nạp ví · Trạng thái · Nhân viên · Chi nhánh. Cột Ngày TT / Khách hàng / Nhân viên hiển thị multi-line.
TC-TX-04: Shared search apply scope cho Sub-tab Giao dịch (DEC-U12)
Pre-condition: Seed S1 (đơn TT-1232..., KH "Nguyễn Trần Thảo Nguyên", thẻ VIP-001). Steps:
- Đứng ở Sub-tab Giao dịch. Gõ "TT-1232" vào shared search top (filter bar trên cùng).
- Đợi 300ms (debounce).
- Verify bảng Giao dịch filter đúng (q apply trên
order.code). - Gõ "Nguyễn" → bảng filter theo
customer.display_name. - Gõ "0900000001" → bảng filter theo
customer.phone_search. - Gõ "VIP-001" → bảng filter theo
prepaid_card.code. - Verify NO local search input ở sub-tab (DEC-U12 conformance).
- Click sang Sub-tab Khách hàng → keyword "VIP-001" GIỮ NGUYÊN ở shared input nhưng scope đổi sang
customer.name + phone_search. Expected:
- Cả 4 keyword (mã đơn / KH / SDT / mã thẻ) trả đúng đơn ở sub-tab Giao dịch.
- Min 2 chars validation hoạt động.
- Persist keyword cross-tab + placeholder dynamic theo tab.
TC-CT-04: Segment KH Mới (chưa dùng ví)
Pre-condition: Seed S5 (KH mới mua thẻ hôm nay, chưa dùng ví — last_used_at IS NULL). Steps:
- Sub-tab Khách hàng. Expected: KH này trong segment "🔵 KH Mới" — KHÔNG phải "inactive". (Verify fix bug CASE order — new check trước.)
TC-CT-17: AOV (Giá trị đơn hàng trung bình)
Pre-condition: 100 đơn, tổng thu 240tr. Steps:
- Sub-tab Khách hàng, xem section "Chỉ số hành vi khách hàng". Expected: AOV = 2.400.000đ/đơn. Tooltip: "Doanh thu TB mỗi đơn mua thẻ...". Trend label dynamic ("so với tháng trước" nếu filter Tháng này).
TC-CT-18: Tỷ lệ tái nạp (Repurchase Rate)
Pre-condition: 1.000 KH có thẻ trong kỳ, 680 KH có prepaid_order_count >= 2. Steps:
- Sub-tab Khách hàng, xem section "Chỉ số hành vi khách hàng". Expected: Tỷ lệ tái nạp = 68,00%. Tooltip khớp A9 Glossary. Trend label theo filter.
TC-CT-CYCLE (FORMULA-013 — Customer-weighted mean per DEC-T09)
Ref: PRD A10 FORMULA-013 + DEC-T09. Mục tiêu: chứng minh metric không bị power user bias, không cộng outlier reactivation, không lẫn KH mua 1 lần duy nhất.
TC-CT-CYCLE-01: Customer-weighted (KHÔNG bị bias bởi power user)
Pre-condition (seed):
- KH-VIP: 20 đơn prepaid trong kỳ filter, mỗi đơn cách nhau đều ~18 ngày (19 khoảng × 18 ngày)
- KH-thường #1..#100: mỗi KH 2 đơn prepaid, gap ~180 ngày (1 khoảng × 180 ngày per KH, đơn n+1 nằm trong kỳ filter)
Steps:
- Sub-tab Khách hàng, xem KPI "Chu kỳ trung bình".
Expected:
- ✅ Hiển thị ~178 ngày (customer-weighted:
(18 + 180×100) / 101 ≈ 178) - ❌ KHÔNG được ra ~154 ngày (interval-weighted:
(19×18 + 100×180) / 119 ≈ 154) → fail nếu BE dùng Option 1 - Tooltip phải chứa cụm "trung bình theo khách"
contributing_customersquery trả 101
TC-CT-CYCLE-02: Outlier cap (> 180 ngày bị loại)
Pre-condition (seed):
- KH-A: 3 đơn prepaid với gap (30 ngày, 400 ngày, 30 ngày) — khoảng 400 ngày là reactivation sau 1 năm ngủ đông
- Chỉ duy nhất KH-A trong tập filter
Steps:
- Sub-tab Khách hàng, xem KPI "Chu kỳ trung bình".
Expected:
- ✅ Hiển thị 30 ngày (chỉ 2 khoảng hợp lệ: 30 + 30, AVG = 30; khoảng 400 ngày bị loại bởi
gap_days <= 180) cycle_countcủa KH-A trong MV = 2 (KHÔNG phải 3)last_cycle_closing_at= ngày của khoảng 30 cuối cùng (KHÔNG phải ngày sau khoảng 400)
TC-CT-CYCLE-03: KH 1 đơn duy nhất bị exclude khỏi denominator
Pre-condition (seed):
- KH-B: 1 đơn prepaid duy nhất (chưa bao giờ tái nạp)
- KH-C: 2 đơn prepaid với gap 50 ngày, đơn n+1 nằm trong kỳ filter
Steps:
- Sub-tab Khách hàng, xem KPI "Chu kỳ trung bình".
Expected:
- ✅ Hiển thị 50 ngày (chỉ KH-C đóng góp; KH-B
cycle_count = 0→ exclude) contributing_customers= 1 (KHÔNG phải 2)- KH-B vẫn xuất hiện trong Customer Table (5.2) với segment "Mới" hoặc "Hoạt động" tuỳ
last_used_at
TC-CT-CYCLE-04: Edge case — không có KH nào ≥ 2 đơn
Pre-condition (seed):
- Toàn bộ tập filter chỉ có KH có 1 đơn duy nhất (vd: kỳ filter rất hẹp, KH mới hoàn toàn)
Steps:
- Sub-tab Khách hàng, xem KPI "Chu kỳ trung bình".
Expected:
- ✅ Hiển thị
—(KHÔNG hiện 0 ngày, NaN, hay "0,00 ngày") - Trend label so kỳ trước ẨN (vì cả 2 kỳ đều
—) contributing_customers= 0 — Phase 1 KHÔNG có thông báo nội dung, chỉ render dấu—
TC-CT-CYCLE-05: Cycle-closes-in-period scope
Pre-condition (seed):
- KH-D: 3 đơn prepaid (ngày 2025-12-15, 2026-02-10, 2026-04-20)
- Khoảng 1: 12/15 → 02/10 (~57 ngày), đơn n+1 thuộc tháng 2/2026
- Khoảng 2: 02/10 → 04/20 (~69 ngày), đơn n+1 thuộc tháng 4/2026
Steps:
- Filter "Tháng này" = tháng 4/2026 (giả sử today = 30/04/2026), xem KPI.
- Đổi filter sang "Tháng này" = tháng 2/2026 (qua "Tùy chọn"), xem KPI.
Expected:
- Step 1 (filter tháng 4): chỉ khoảng 2 đếm → cycle = 69 ngày
- Step 2 (filter tháng 2): chỉ khoảng 1 đếm → cycle = 57 ngày
- KHÔNG được trộn cả 2 khoảng vào AVG của 1 period (chứng minh scope filter đúng)
TC-FN-04: Lợi nhuận gộp công thức
Pre-condition: Tiền thu = 850tr, HH = 38tr, Nạp ví = 920tr. Steps:
- Sub-tab Tài chính, KPI Lợi nhuận gộp. Expected: LN gộp = 850 − 38 − (920 − 850) = 742tr. KHÔNG bị nhân đôi/sai do fix Finance MV double count.
TC-FT-09: Date range max 365 ngày
Steps:
- Khoảng thời gian → Tùy chọn → chọn from = 1 năm trước + 1 ngày, to = today. Expected: Button "Áp dụng" disabled + warning "Tối đa 1 năm".
TC-RB-01: Phân quyền Branch Manager
Pre-condition: User role branch_manager, gán CN Q.1. Steps:
- Login → vào tab Analytics. Expected: Tab visible. Branch selector chỉ hiện CN Q.1 (không thấy CN khác). Số liệu KPI chỉ tính trên CN Q.1.
TC-RB-02: Phân quyền Staff (no permission)
Pre-condition: User role staff chưa cấp module report.prepaid_analytics. Steps:
- Login → check menu Báo cáo. Expected: Tab Analytics ẨN trong menu. Truy cập trực tiếp URL → redirect về
/r/reports.
D5. Tiêu chí Entry / Exit
Entry (bắt đầu QA) — Mirror SOT HOLD gate
- [ ] 13 verification points V1-V13 ALL CONFIRMED với evidence (xem SOURCE_OF_TRUTH.md Section 4). KHÔNG bắt đầu QA nếu còn V open. Sub-group V9-V13 = wallet semantics (Review L8) là blocker lớn nhất — phải reconcile với baseline finance report trước khi unlock.
- [ ] All P0 dev tasks complete (per plan.md Chunk 1 + Chunk 2 — bao gồm Khách hàng P1)
- [ ] Backend 4 MVs P1 deployed on staging:
mv_prepaid_order_daily·mv_prepaid_card_daily·mv_prepaid_customer_stats·mv_prepaid_finance_daily(KHÔNG cósummary_dailycũ — đã tách) - [ ] Cron MV refresh ổn định (4 MVs P1: 3 × 15min + 1 × 30min)
- [ ] Function
compute_prepaid_alertsdeployed (KHÔNG cósearch_prepaid_global— defer P2) - [ ]
region_branchdata verified — query trả 70 CN córegion_id(V7 evidence) - [ ] Dynamic Permission v2 module
report.prepaid_analyticsregistered + 3 actions seeded (V6 evidence) - [ ]
parent_id IS NULLfilter applied trong tất cả MV/SQL invoice (V8 evidence) - [ ] PRD A10.0 Canonical Wallet Table 5 hàng đã được cập nhật từ PROVISIONAL → LOCKED với codebase evidence (V9-V13 evidence)
- [ ] MV
mv_prepaid_order_dailycó 2 cộttotal_vi_diva_napped+total_vi_km_nappedtừparent_invoice.reference_amount+parent_invoice.wallet_promotion_amount(V9-V10 evidence) - [ ]
mv_prepaid_finance_dailyđã refactor: bỏ cộtwallet_promotion_amountcũ (đã được Review L8 chứng minh dùng SAI), thay bằng split rõ theopayment_method_id='wallet'vs'wallet_promotion'(V12-V13 evidence) - [ ] Reconciliation report so sánh 5 số canonical (Tiền thu / Ví Diva nạp / Ví KM nạp / Ví Diva dùng / Ví KM dùng) với baseline
search_report_serviceở 5 ngày sample → chênh lệch ≤ 0.5% hoặc PO + Kế toán ký nhận chấp nhận chênh lệch (V11 evidence) - [ ] Feature flag
FEATURE_PREPAID_ANALYTICS_V2 = trueon staging - [ ] Test data seed đã chạy (D3.1) — dùng
ecommerce_usertemplate (V1) - [ ] Test seed mở rộng cho V9-V13 đã chuẩn bị: (a) đơn discount (giá bán < base value), (b) đơn phụ thu (giá bán > base), (c) đơn parent + multi sub_invoices, (d) đơn thanh toán mixed
wallet+wallet_promotion, (e) đơn chỉwallet_promotionpayment, (f) đơn chỉcash/bankkhông touch ví (TC-D3.WALLET-1..6) - [ ] QA test environment có Redis + MinIO running.
notification-apiKHÔNG cần (DEC-U09 — bulk SMS/ZNS đã bỏ khỏi Analytics)
Exit (sẵn sàng go-live)
- [ ] 100% P0 test cases pass (estimated 60+ TC)
- [ ] ≥ 95% P1 test cases pass
- [ ] 0 P0 bugs open
- [ ] ≤ 3 P1 bugs open with workaround documented
- [ ] Performance: Tổng quan load < 500ms (production-like dataset)
- [ ] Performance: Export 50K rows < 60s
- [ ] Reconciliation: số liệu MV match báo cáo cũ ± 0.1% trên 3 tháng dataset
- [ ] Security: SDT masked đúng theo permission · export audit log working
- [ ] Rollback drill: bật/tắt feature flag → tab cũ/mới hoạt động đúng < 1 phút
- [ ] Sign-off: PO + Tech Lead + QA Lead + Kế toán (đại diện) approve
D6. Định nghĩa mức độ defect
| Severity | Definition | SLA |
|---|---|---|
| P0 — Critical | Block chức năng chính · sai số liệu tài chính · security/permission leak · crash | Fix trong 4h |
| P1 — High | Workaround có nhưng UX kém · sai số liệu phụ · perf > 2× target | Fix trước go-live |
| P2 — Medium | UX rough · format/wording · perf 1-2× target | Fix Phase 1.x |
| P3 — Low | Cosmetic · nice-to-have | Backlog |
D7. Rủi ro và giảm thiểu cho QA
| Risk | Mitigation |
|---|---|
| Data trong staging không reflect production size | Run perf test với synthetic 1M+ orders dataset |
| MV refresh fail trong test → false positive bug | Có script restart cron + verify MV state trước mỗi test run |
| Schema mismatch (BE chưa verify) làm fail unit test | QA chỉ start sau khi BE confirm Section 0 verification |
| Reconciliation với báo cáo cũ phát hiện chênh lệch | Document rõ "expected delta" do tab cũ có bug (export skip 1000 rows + exclude flexible) — có thể chấp nhận |
Reference:
- Spec details: prd.md · dev-spec.md · ui-spec.md
- Implementation steps: plan.md
- Operations: go-live-checklist.md
- Handoff: handoff.md