Skip to content

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 đổiSectionẢnh hưởng
Coverage matrix + US-3.2 đổi table Khách hàng 12 → 13 cột sau khi tách Dư ví DIVADư ví KMD1.1 Functional coverage matrix · D2 Requirements MatrixQA
QA cần verify risk highlight theo tổng Dư ví DIVA + Dư ví KM > 5tr, không highlight riêng từng cộtUS-3.2QA, FE

D1. Phạm vi test (Coverage)

D1.1 Functional coverage matrix

Feature areaTest typePriorityOwner
Sub-tab Tổng quan (KPI cards · 4 charts · alerts · rankings)Functional + VisualP0QA
Sub-tab Giao dịch (table 10 cột · expand · 2 local filters — DEC-U12 · sum cards)Functional + PerformanceP0QA
Sub-tab Khách hàng (segment cards · behavior bar · table 13 cột read-only · Xuất Excel ở header)Functional + IntegrationP0QA
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 accuracyP0QA + Kế toán
Shared filter bar (3 element: Chi nhánh · Khoảng thời gian · Tìm kiếm)FunctionalP0QA
Phân quyền v2 fine-grained (3 actions: view · export · view_full_phone) per role matrixSecurityP0QA + DevOps
4 MVs P1 (order_daily · card_daily · customer_stats · finance_daily) — schema migration + data accuracy reconciliationBackendP0BE
Charts time-series luôn theo ngày (không có toggle Tuần/Tháng — DEC-U08)FunctionalP0QA
MV refresh + data freshness (15-min cron 3 MVs · 30-min 1 MV)BackendP1BE
Export Excel server-side (label đồng bộ tiếng Việt: Xuất Excel / Xuất {Tên báo cáo} — DEC-U10)PerformanceP0BE + QA
Feature flag rollbackOperationsP0DevOps

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 IDUser StoryTest CasesAcceptance
US-1.1Xem KPI tổng quanTC-OV-01..088 KPI cards đúng số + so sánh kỳ trước · click navigate đúng sub-tab
US-1.2Xem cảnh báo vận hànhTC-OV-09..123 mức alert hiển thị · [Xem] navigate đúng filter
US-1.3Xem biểu đồ xu hướngTC-OV-13..164 charts grid 2×2 · chỉ theo ngày (DEC-U08 — không có toggle Tuần/Tháng) · responsive
US-1.4Xem top thẻ / NV / KHTC-OV-17..193 mini-tables · top NV navigation disabled + tooltip P3+
US-2.1Tra cứu giao dịchTC-TX-01..06Table 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.2Expand chi tiết đơnTC-TX-07..103 inner tabs · lazy load < 200ms
US-2.3Quick navigationTC-TX-11..13Mã đơn / KH navigate được · NV bán disabled
US-3.1Phân khúc KH tự độngTC-CT-01..054 segment cards · click filter table · % đúng (mẫu số đúng)
US-3.2Tra cứu KHTC-CT-06..12Table 13 cột · expand 3 tabs · SDT mask theo permission
US-3.3Tải Excel danh sách KHTC-CT-13Nú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.4Chỉ số hành vi KHTC-CT-17, TC-CT-18, TC-CT-CYCLE-01..053 metrics (AOV · Tái nạp · Chu kỳ TB) · trend label dynamic theo filter · cycle = customer-weighted mean (DEC-T09)
US-4.1Dashboard tài chínhTC-FN-01..088 KPI cards · công thức dòng tiền hiển thị
US-4.2DT theo thời gianTC-FN-09..12Tab Tổng hợp · group by · line chart kỳ trước
US-4.3Quản lý công nợTC-FN-13..15Tab 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ồngTC-FN-19..22Tab HH · expand đơn · chỉ tính status='S'
US-4.5DT theo PTTTTC-FN-23..25Tab PTTT · donut + table
US-4.6Xuất Excel cho kế toánTC-FN-26..305 export server-side · progress · 50K rows < 60s
US-7.1Lọc CN (70 CN)TC-FT-01..04Branch selector · grouped tree · search · default theo role
US-7.2Khoảng thời gianTC-FT-05..09Dropdown 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ó customer table). account_type cộ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 >= 5

D3.2 Test scenarios — Coverage matrix

ScenarioLoại thẻTrạng thái TTKH segmentSố đơnMục đích test
S1Cố định 10trĐã thanh toánHoạt động1Happy path
S2Linh hoạt 7,5trCòn nợ (paid 5tr / total 7,5tr)Hoạt động1Công nợ logic
S3Cố định 20trĐã TTVIP5Top customer ranking
S4Cả Cố định + Linh hoạt mixĐã TTMới2Sub-breakdown "X CĐ + Y LH"
S5Cố định 5trChưa TT (paid_amount = 0)Mới1Edge: paid_amount = 0
S6Linh hoạt 10trĐã TTNgủ đông1Segment classification
S7Cố định 1trĐã TT, hủyHoạt động1Cancel logic exclusion
S8Cố định 10tr × 70 CNĐủ mixĐủ mix100+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:

  1. Login admin → vào /r/reports/prepaid-card-analytics
  2. 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:

  1. 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:

  1. 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:

  1. Đứng ở Sub-tab Giao dịch. Gõ "TT-1232" vào shared search top (filter bar trên cùng).
  2. Đợi 300ms (debounce).
  3. Verify bảng Giao dịch filter đúng (q apply trên order.code).
  4. Gõ "Nguyễn" → bảng filter theo customer.display_name.
  5. Gõ "0900000001" → bảng filter theo customer.phone_search.
  6. Gõ "VIP-001" → bảng filter theo prepaid_card.code.
  7. Verify NO local search input ở sub-tab (DEC-U12 conformance).
  8. 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:

  1. 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:

  1. 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:

  1. 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:

  1. 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_customers query 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:

  1. 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_count củ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:

  1. 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:

  1. 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:

  1. Filter "Tháng này" = tháng 4/2026 (giả sử today = 30/04/2026), xem KPI.
  2. Đổ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:

  1. 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:

  1. 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:

  1. 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:

  1. 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_daily cũ — đã tách)
  • [ ] Cron MV refresh ổn định (4 MVs P1: 3 × 15min + 1 × 30min)
  • [ ] Function compute_prepaid_alerts deployed (KHÔNG có search_prepaid_global — defer P2)
  • [ ] region_branch data verified — query trả 70 CN có region_id (V7 evidence)
  • [ ] Dynamic Permission v2 module report.prepaid_analytics registered + 3 actions seeded (V6 evidence)
  • [ ] parent_id IS NULL filter 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_daily có 2 cột total_vi_diva_napped + total_vi_km_napped từ parent_invoice.reference_amount + parent_invoice.wallet_promotion_amount (V9-V10 evidence)
  • [ ] mv_prepaid_finance_daily đã refactor: bỏ cột wallet_promotion_amount cũ (đã được Review L8 chứng minh dùng SAI), thay bằng split rõ theo payment_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 = true on staging
  • [ ] Test data seed đã chạy (D3.1) — dùng ecommerce_user template (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_promotion payment, (f) đơn chỉ cash/bank không touch ví (TC-D3.WALLET-1..6)
  • [ ] QA test environment có Redis + MinIO running. notification-api KHÔ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

SeverityDefinitionSLA
P0 — CriticalBlock chức năng chính · sai số liệu tài chính · security/permission leak · crashFix trong 4h
P1 — HighWorkaround có nhưng UX kém · sai số liệu phụ · perf > 2× targetFix trước go-live
P2 — MediumUX rough · format/wording · perf 1-2× targetFix Phase 1.x
P3 — LowCosmetic · nice-to-haveBacklog

D7. Rủi ro và giảm thiểu cho QA

RiskMitigation
Data trong staging không reflect production sizeRun perf test với synthetic 1M+ orders dataset
MV refresh fail trong test → false positive bugCó script restart cron + verify MV state trước mỗi test run
Schema mismatch (BE chưa verify) làm fail unit testQA chỉ start sau khi BE confirm Section 0 verification
Reconciliation với báo cáo cũ phát hiện chênh lệchDocument rõ "expected delta" do tab cũ có bug (export skip 1000 rows + exclude flexible) — có thể chấp nhận

Reference: