Appearance
Sales-CSKH Debt Follow-up & Handover — QA Test Plan
Canonical Source of Truth:
SOURCE_OF_TRUTH.md— DEC-001: codebase Diva là ground truth, đây là hardening pass (không greenfield). Slug: sales-cskh-debt-handover · Version: v1.0 hardening · Updated: 2026-05-14 Spec Index: prd.md · ui-spec.md · dev-spec.md · go-live-checklist.md · handoff.md
FR/AC: prd.md A5 + A12 · Wireframes + codebase mapping: ui-spec.md §0 · Code paths: dev-spec.md C11
ⓘ Hardening QA approach
Đây là test plan cho hardening pass trên feature đã build sẵn ~85% trong codebase. Định hướng:
- Regression-first — verify component đã có (🟢 Reuse) vẫn hoạt động đúng spec
- Delta-focused — test deep cho component cần extend (🟡 Extend): KPI mới, scope chip, leaderboard, drawer NV
- Critical path E2E — luồng nghiệp vụ end-to-end: filter → KPI → drill-down → alert → handover → rollback
- Permission hardening — least-data per DEC-012 cho 5 bảng debt_*; test field-level masking
- Capacity gate — benchmark SCR-01-TAB-ANALYTICS @ 1.5M order rows trước go-live (DEC-013 gate cho materialized snapshot)
Tham chiếu test code paths:
- FE Web:
diva-admin/src/modules/debt-manager/+customer-handover/ - FE Mobile:
diva-flutter/staff/lib/presentation/modules/debt_management/ - BE:
diva-backend/services/ecommerce-api/action/+scheduler/daily_debt_alert.go - DB: function
customer_debt_dashboard_order_scope()(migration1776054869987_*)
D) QA Test Plan
D1. Scope & Coverage
Feature: sales-cskh-debt-handover v4.11 MVP-Lite + Mobile + Quản lý lịch nhắc + Navigation & Tooltip + Review Fix + UX Usability P0+P1+P2 + Debt UI Sync + Analytics Tab Sync. Coverage: 100% FR (20/20) + 100% NFR (10/10) + Mobile screens (5/5) + Drawer web (2/2) + UX P0 (4 TC) + UX P1 (6 TC) + UX P2 (4 TC) + Analytics (7 TC).
D2. Requirements Test Matrix
| Req | Layer | Scenario chính | Tiêu chí pass | Status |
|---|---|---|---|---|
| FR-001 | E2E | Filter cascade + preset | < 2s p95 (30 ngày); cascade đúng | Planned |
| FR-002 | Int+E2E | DS tư vấn theo hoa hồng | Đúng quyền, role, % | Planned |
| FR-003 | Int | 1 khách nhiều sự kiện tư vấn | cùng ngày/cùng CN: visit=1, unique=1; thêm 4 ngày khác: visit=5, unique=1 | Planned |
| FR-004 | Int | Closed + conversion | Mua+thu>0; N/A khi 0 | Planned |
| FR-005 | Int | KPI nợ distinct+equivalent | Sai số ≤1%; N/A rule | Planned |
| FR-006 | Int | Weighted + settlement | Đúng TZ VN | Planned |
| FR-007 | Unit+Int | Aging boundary 29/30/59/60/89/90 | Không overlap | Planned |
| FR-008 | Int | Threshold override | branch ?? global; validate min=1 max=365 | Planned |
| FR-009 | Int+E2E | Job cảnh báo hằng ngày | ≥95% gửi; không trùng; push mobile | Planned |
| FR-010 | E2E | Handover wizard 3 bước | Preview + confirm đúng; rollback toàn bộ | Planned |
| FR-011 | Int | Audit log | 100% có log immutable (incl. rollback) | Planned |
| FR-012 | E2E | Export XLSX theo filter | Chunking 200k; đúng quyền; 13/18 cột đúng | Planned |
| MOB-01 | E2E Mobile | KPI cá nhân hiển thị đúng | Data khớp web; filter thời gian | Planned |
| MOB-02 | E2E Mobile | Danh sách nợ + CTA | Scope chip vai trò đúng; gọi, LH, lịch hoạt động | Planned |
| MOB-05 | E2E Mobile | Push notification deeplink | Tap → đúng màn hình | Planned |
| FR-013 | E2E | Quản lý lịch nhắc follow-up | Tạo→xem→đánh dấu xong; noti in-app khi đến giờ; timeline đúng | Planned |
| FR-014 | E2E | Role-gated tab Thống kê | Chỉ Manager/Admin thấy tab; Staff không thấy | Planned |
| FR-015 | Int+E2E | 6 KPI cố định tab Thống kê | Value/sub-label/trend/progress đúng theo bộ lọc | Planned |
| FR-016 | E2E | So sánh nợ theo Chi nhánh/Nhân viên | Đổi mode + sort (Đã thu/Còn nợ) recalc đúng, giữ filter global | Planned |
| FR-017 | E2E | Xu hướng nợ 3 line | Mặc định bật 3 line, toggle legend đúng, tooltip số liệu đúng | Planned |
| FR-018 | Int+E2E | Leaderboard thu nợ | Sort/pagination/tie-break đúng; top 3 highlight đúng | Planned |
| FR-019 | E2E | Drawer chi tiết nhân viên | Mở từ leaderboard; KPI + aging + top5 đúng; CTA Xem chi tiết quay về tab Công nợ với filter nhân sự | Planned |
| FR-020 | E2E | Export XLSX tab Thống kê | Xuất đúng theo filter hiện tại; đủ 2 sheet (summary + leaderboard detail) | Planned |
| — (CTA) | Int | markCustomerContacted + debt_contact_log | Đánh dấu/hủy đúng; log append-only; trạng thái cuối đúng | Planned |
| FR-008-API | Int | upsertDebtAlertConfig validation edge cases | min/max/notify_time/branch scope/unique constraint pass | Planned |
| UX-P0-01 | E2E | Action Card hiện/ẩn + CTA navigate | Hiện khi nợ ≥30d; ẩn khi không có; CTA → tab Công nợ + pre-filter Nhóm nợ đúng; dismiss persist localStorage | Planned |
| UX-P0-02 | E2E | Default sort SCR-03 + MOB-02 | Load trang → sort overdue_days DESC, outstanding_amount DESC; click header → override | Planned |
| UX-P0-03 | E2E | CTA overflow menu SCR-03 | Chỉ [📞 Gọi] hiện trực tiếp; [⋯] chứa 3 items; nợ ≥90d → nút Gọi đỏ #B71C1C | Planned |
| UX-P0-04 | E2E | Debt scope chip role | SCR-01 tab Công nợ + MOB-02 hiển thị scope chip Sale/Telesale/CSKH; tooltip match; dữ liệu recalc đúng | Planned |
| UX-P1-01 | E2E | Sub-label KPI cards | Mỗi card có sub-label đúng (VD: "45 khách mua / 67 khách"); N/A → "Chưa đủ dữ liệu"; font 12px #757575 | Planned |
| UX-P1-02 | E2E Mobile | MOB-03 collapsible sections | 3 sections collapse/expand; badge count đúng; "Dịch vụ nợ" collapse khi >2 đơn | Planned |
| UX-P1-03 | E2E | SCR-03-DRAWER overlay mode | Overlay (không push); backdrop dim; click ngoài đóng; bảng SCR-03 không co | Planned |
| UX-P1-04 | E2E Mobile | MOB-02 card CTA + overflow | Card hiển thị đúng metadata; CTA [Gọi ngay] [Tạo lịch] [⋯] hoạt động; tap card → MOB-03 | Planned |
| UX-P1-05 | E2E | SCR-01 Dashboard 2 tabs | Tab "Hiệu suất" = Block A + bảng; tab "Công nợ" = Block B + Aging + bảng nợ; filter giữ nguyên khi chuyển tab | Planned |
| UX-P1-06 | E2E Mobile | MOB-03 sticky summary card | Sticky khi scroll xuống; 48px; name + tổng nợ + badge + [📞]; ẩn khi scroll lên | Planned |
| UX-P2-01 | E2E | KPI full-set nhất quán | Block B luôn hiện full bộ KPI nợ trên web/mobile; không còn control toggle; metric/value khớp theo filter | Planned |
| UX-P2-02 | E2E | Quick Handover mode | Scope "Toàn bộ" → wizard 2 bước; bảng chi tiết khách ẩn; scope "Lọc" → wizard 3 bước bình thường | Planned |
| UX-P2-03 | E2E | Daily Focus Top 5 | 5 khách ưu tiên; sort: nhắc quá hạn > nợ ≥90d chưa LH > overdue DESC; [📞] hoạt động; hide/show persist | Planned |
| UX-P2-04 | E2E | Onboarding Tour 3 bước | Lần đầu SCR-01 → tour hiện; 3 steps đúng target; "Bỏ qua" set flag; hoàn thành set flag; lần sau không hiện; "Xem lại" từ menu ⓘ | Planned |
D3. Seed Data
| Dataset | Nội dung bắt buộc |
|---|---|
| Seed-KPI-01 | 1 khách nhiều sự kiện tư vấn (cùng ngày + khác ngày + khác chi nhánh) — kiểm soát visit vs unique |
| Seed-COMMISSION-01 | 1 đơn nhiều DV, nhiều role, nhiều %; có case 2 Sale 50/50 |
| Seed-COMMISSION-02 | 1 đơn 3 DV mixed: DV1 = 1 Sale 100% (auto primary), DV2 = 2 Sale 50/50 (cần chọn radio), DV3 = chỉ CSKH 100% (không áp dụng primary Sale) |
| Seed-COMMISSION-03 | 1 đơn 1 DV, 3 Sale (40/30/30) — test radio 3 options + validation chưa chọn |
| Seed-KPI-02 | Khách mua chưa thu + khách có thu |
| Seed-DEBT-01 | Nợ outstanding > 0 và = 0 |
| Seed-DEBT-02 | Nợ payments nhiều kỳ (weighted + settlement) |
| Seed-DEBT-03 | Nợ ở boundary aging: 29, 30, 59, 60, 89, 90 ngày |
| Seed-CONFIG-01 | Branch có override + không có |
| Seed-ALERT-01 | Owner chain đủ 3 cấp fallback |
| Seed-HANDOVER-01 | KH để test wizard 3 bước + rollback 24h |
| Seed-EXPORT-01 | Dữ liệu > 200k rows |
| Seed-MANAGER-01 | Manager quản lý 3 branch; test RBAC scope |
| Seed-MOBILE-01 | User mobile với KPI + nợ để test MOB-01…03 |
| Seed-FOLLOWUP-01 | Lịch nhắc: 2 pending (1 quá hạn, 1 chưa đến), 2 done, 1 cancelled. Cùng 1 khách + khách khác |
| Seed-ACTION-CARD-01 | Data có nợ ≥30d (trigger Action Card) + data không có nợ ≥30d (ẩn Action Card) |
| Seed-DAILY-FOCUS-01 | 7+ khách với mixed priorities: 2 có lịch nhắc quá hạn, 2 nợ ≥90d chưa LH, 3 nợ 30-89d — verify top-5 ranking |
| Seed-ONBOARDING-01 | User mới chưa có localStorage flag debt_onboarding_completed |
| Seed-QUICK-HANDOVER-01 | Nhân viên có 45 khách, test cả scope "Toàn bộ" (2-step) và scope "Lọc" (3-step) |
D4. Critical Path Test Scenarios
| TC-CP | Mô tả | FR Covered |
|---|---|---|
| TC-CP-01 | Filter cascade → KPI → drill-down → xuất XLSX đúng quyền | FR-001…004, 012 |
| TC-CP-02 | Job gửi noti (in-app + push) → mở danh sách → xử lý → CRM | FR-005, 007, 009 |
| TC-CP-03 | Handover SCR-05→06→07→08 → audit → rollback 24h toàn bộ | FR-010, 011 |
| TC-CP-04 | Owner rule + threshold: test đủ 3 cấp fallback | FR-008, 009 |
| TC-CP-05 | Manager scope: chỉ thấy branch đang quản lý | DEC-013 |
| TC-CP-06 | Mobile: push → tap → MOB-02 → gọi → đánh dấu LH | MOB-02, 05 |
| TC-CP-07 | Perf query + freshness + monitor alerts | NFR-001, 002, 006, 009 |
| TC-CP-08 | 50/50 commission (new flow): 1 DV, 2 Sale cùng % → user chọn "Phụ trách chính" trong popup hoa hồng, owner đúng người chọn. KPI mỗi người 50%, không chồng chéo | DEC-QA-006, FR-009 |
| TC-CP-08b | 50/50 commission (legacy): 1 DV, 2 Sale cùng %, cùng created_at, không có cờ is_primary_owner → tie-break user_id ASC chọn đúng 1 owner | DEC-QA-006, FR-009 |
| TC-CP-08c | 1 Sale duy nhất (auto primary): 1 DV, 1 Sale 100% → auto set is_primary_owner = true, KHÔNG hiện radio. Owner = Sale đó. Label hiện "✅ Phụ trách chính" readonly | DEC-003, FR-002 |
| TC-CP-08d | 3+ Sale (multi-select): 1 DV, 3 Sale (40/30/30) → hiện radio 3 options, user chọn Sale 2 → is_primary_owner = Sale 2. KPI: mỗi người nhận đúng % riêng, owner = Sale 2 | DEC-003, FR-002 |
| TC-CP-08e | Validation chưa chọn: 2+ Sale trên DV, user chưa chọn radio "Phụ trách chính" → nút "Xác nhận" disabled + warning "Vui lòng chọn người phụ trách chính cho Sale trên dịch vụ [tên DV]" | FR-002 |
| TC-CP-08f | Multi-DV mixed: Đơn 3 DV — DV1 có 1 Sale (auto primary), DV2 có 2 Sale (cần chọn), DV3 chỉ CSKH (không áp dụng) → chỉ DV2 hiện radio, submit chỉ enabled khi DV2 đã chọn | FR-002, DEC-003 |
| TC-CP-09 | Handover limit: thử bàn giao 201 khách → block + error "Tối đa 200 khách/lần" | DEC-017, DEC-TECH-009 |
| TC-CP-10 | Tạo lịch nhắc → xem lại trong drawer (web) → đánh dấu Đã xong → status chuyển done | FR-013 |
| TC-CP-11 | Lịch nhắc quá hạn (remind_at < now, pending) → hiện badge 🔴 Quá hạn trong drawer + counter trên nút toolbar | FR-013, DEC-UX-009 |
| TC-CP-12 | Đến giờ nhắc → hệ thống gửi in-app noti (NTF-FOLLOWUP-REMIND) → user click → mở SCR-03 drawer | FR-013, DEC-020 |
| TC-CP-13 | Xem timeline lịch nhắc cho 1 khách: hiện đúng thứ tự + trạng thái từng lần nhắc | FR-013 |
| TC-CP-14 | markCustomerContacted: đánh dấu → hủy đánh dấu → đánh dấu lại → debt_contact_log có 3 bản ghi, trạng thái cuối = true | FR-005, CTA |
| TC-CP-15 | upsertDebtAlertConfig: validate min=1 max=365, notify_time 06:00–10:00, branch scope consistency, unique constraint | FR-008, DEC-TECH-002 |
| TC-CP-16 | P0 Action Card + Sort: Mở SCR-01 có nợ ≥30d → Action Card hiện đúng badge count + CTA "Xử lý ngay" → click → tab Công nợ, apply filter nhóm nợ cao nhất → bảng sort overdue_days DESC → Quay lại dismiss "Ẩn hôm nay" → refresh → vẫn ẩn (localStorage) → đổi ngày → hiện lại | UX-P0-01, UX-P0-02 |
| TC-CP-17 | P0 CTA Overflow + Scope Chip: SCR-03 load → dòng chỉ có [📞 Gọi] + [⋯] → click ⋯ → dropdown 3 items → "Đánh dấu Đã LH" → icon ✅ → mở lại ⋯ → thành "Hủy đánh dấu" → đổi scope chip Sale/Telesale/CSKH → data recalc đúng scope → cột "% tham gia" hiện | UX-P0-03, UX-P0-04 |
| TC-CP-18 | P1 Dashboard Tabs + Sub-labels: SCR-01 mở → tab "Hiệu suất" default → KPI cards có sub-label (VD: "45 khách mua / 67 khách") → đổi tab "Công nợ" → Block B + Aging hiện → filter giữ nguyên → KPI "Tỷ lệ chuyển đổi" N/A → sub-label = "Chưa đủ dữ liệu" | UX-P1-01, UX-P1-05 |
| TC-CP-19 | P1 Overlay Drawer: SCR-03 click [📅 Lịch nhắc (2)] → drawer overlay hiện bên phải → backdrop dim → bảng SCR-03 KHÔNG bị co → click backdrop → drawer đóng → click lại → scroll lịch nhắc → tạo mới → toast thành công | UX-P1-03 |
| TC-CP-20 | P1 Mobile Card CTA + Collapsible + Sticky: MOB-02 → card hiển thị đúng Tham gia / Phụ trách chính / Đã gọi ... → tap [Gọi ngay] → gọi → quay lại → hỏi "Đã LH?" → tap [Tạo lịch] → mở MOB-04 → tap card → MOB-03 → scroll xuống → sticky summary hiện (48px) → section "Lịch sử LH" collapsed → tap expand → badge count đúng | UX-P1-02, UX-P1-04, UX-P1-06 |
| TC-CP-21 | P2 KPI Full-set + Daily Focus: SCR-01 tab Công nợ → Block B hiển thị full bộ KPI nợ mặc định (không toggle) → đổi filter → giá trị recalc đúng → Qua SCR-03 → Daily Focus top 5 hiện → verify thứ tự: nhắc quá hạn trước → nợ ≥90d → overdue DESC → click [📞] → gọi → click "Ẩn" → reload → vẫn ẩn | UX-P2-01, UX-P2-03 |
| TC-CP-22 | P2 Quick Handover: SCR-05 chọn nguồn + scope "Toàn bộ" (45 khách) → stepper hiện 2 bước → click "Tiếp theo" → Step 2 gộp: chọn đích + Preview + Tóm tắt + 2 checkbox → tick đủ → "Xác nhận bàn giao" enabled → confirm → SCR-08 → Quay lại SCR-05 → chọn scope "Theo bộ lọc" → stepper hiện 3 bước bình thường → Step 2 có bảng chi tiết khách | UX-P2-02 |
| TC-CP-23 | P2 Onboarding Tour: Clear localStorage → mở SCR-01 lần đầu → tour Step 1/3 hiện (spotlight KPI Nợ) → "Tiếp theo" → Step 2/3 (Aging Chart) → "Tiếp theo" → Step 3/3 (Lịch nhắc) → "Hoàn thành" → toast → reload → tour KHÔNG hiện → menu ⓘ → "Xem lại hướng dẫn" → tour hiện lại → "Bỏ qua" → confirm → tour đóng | UX-P2-04 |
| TC-CP-24 | Analytics End-to-End: Manager mở tab Thống kê → verify 6 KPI cố định (Tổng tiền nợ, Đã thu trong kỳ, Tỷ lệ thu nợ, Khách còn nợ, Đã tất toán, Tỷ lệ nợ/doanh thu) → đổi chart mode Chi nhánh/Nhân viên + đổi sort Đã thu/Còn nợ → mở leaderboard và sort → click 1 nhân sự mở drawer chi tiết → bấm Xem chi tiết quay về tab Công nợ và auto-set filter nhân sự + scroll tới bảng phù hợp → quay lại tab Thống kê và bấm Xuất XLSX để xác nhận export theo bộ lọc hiện tại | FR-014…020 |
D5. Entry / Exit Criteria
Entry: PRD + UI/UX (Web + Mobile) + Dev Spec review xong. Seed data sẵn sàng. PD-007/008/009 đã confirm hoặc feature flag tắt cho phần chưa confirm.
Exit: 100% TC FR pass. 100% NFR đạt ngưỡng. Không còn S1/S2. UAT sign-off (Web + Mobile).