Skip to content

Sales-CSKH Debt Follow-up & Handover — UI Spec

v2.0 — 15/05/2026

Thay đổiSectionẢnh hưởng
KPI cards đổi đơn vị "Triệu" → VND đầy đủ + suffix "đ" (Ref BUG-PROD-002)Spec KPI CardsFE, QA
Tooltip "Doanh thu ròng" rewrite — phân biệt "ghi nhận kế toán ≠ tiền thực thu" (Ref DEC-032)B9 Web Dashboard + KPI Cards tableFE, QA
SCR-02 cột "Cuộc gọi" LOCKED — source ecommerce_user.last_contacted_at từ app mobile (Ref DEC-028)SCR-02 columns + B9 SCR-02 tooltipFE, QA
SCR-02 cột "% tham gia" — weighted avg multi-order + tooltip drill-down (Ref DEC-029 + FORMULA-008)SCR-02 columns + B9 tooltipFE, QA
SCR-02 cột "Dịch vụ" — tie-break rule + multi-DV format (Ref DEC-027 + FORMULA-007)SCR-02 representative tie-breakFE, QA
SCR-03 cột "Nhóm nợ"/"Quá hạn" — customer-level rollup max(overdue_days) (Ref DEC-023 + FORMULA-006B)SCR-03 columnsFE, BE
B4 Notification — operator >= + 4 re-trigger rules + NTF-DEBT-BUCKET-ESCALATE mới (Ref DEC-025/026)B4 Notification SpecBE, QA
Banner "Ưu tiên xử lý nợ" — as-of NOW + visual cue cam + sub-label cảnh báo (Ref DEC-033)Spec Priority Card StripFE, BE
KPI Edge Cases bổ sung 5 cases (multi-order bucket, Manager view, Manager+Sale toggle, BOD no branch, DV null)B-EdgeCases KPIQA
Scheduler Edge Cases bổ sung 5 cases (cross-day resend, bucket transition, partial pay, settled drop, exact threshold)B-EdgeCases SchedulerBE, QA

Canonical Inputs: Source-of-Truth là SOURCE_OF_TRUTH.md (DEC-001 — codebase hiện tại là ground truth, HTML là intent). Slug: sales-cskh-debt-handover Version: v2.0 hardening pass · Updated: 2026-05-15 Spec Index: prd.md · dev-spec.md · qa-test-plan.md · go-live-checklist.md · handoff.md · _prod-issues.md


ⓘ Cách đọc UI Spec này

Đây là UI hardening spec cho feature đã build sẵn trong Diva, KHÔNG phải spec greenfield. Mỗi màn hình/component được mô tả với 3 lớp:

  1. Existing surface — route, file, screen tên thực trong codebase
  2. Spec target — ASCII wireframe, copy text, hành vi đầy đủ (từ HTML intent v4.12)
  3. Delta — phần khác biệt giữa hiện tại và target (Reuse 🟢 / Extend 🟡 / Build mới 🔴)

Quy tắc: Khi spec target khác với codebase hiện tại, codebase thắng (DEC-001). Nếu cần thay đổi codebase, ghi rõ trong "Delta" để dev biết chính xác phải sửa file nào.


§0. Codebase Mapping — Mỗi SCR/MOB ánh xạ tới surface nào

Bảng này là single source of mapping giữa SCR/MOB-IDs trong spec và file thực tế trong codebase. Dev đọc bảng này TRƯỚC khi đọc các section dưới.

Web (diva-admin) — Route /dm/debt

SCR-IDTab/RouteExisting componentFile pathDelta
SCR-01Layout /dm/debt (parent)DebtManager.tsxsrc/modules/debt-manager/pages/DebtManager.tsx🟢 Reuse — layout shell + tab nav
SCR-01 (Hiệu suất tư vấn tab)/dm/debt/consulting-performanceDebtManagerConsultingPerformance.tsxsrc/modules/debt-manager/component/consulting-performance/🟡 Extend — bổ sung KPI cards mới (Khách mua trong kỳ, Tỉ lệ chuyển đổi) nếu thiếu
SCR-01-TAB-ANALYTICS/dm/debt/statisticsDebtManagerStatistics.tsxsrc/modules/debt-manager/component/statistics/🟡 Extend — leaderboard + drawer chi tiết NV nếu chưa có
SCR-01-ANALYTICS-DRAWERDrawer phải bên trong tab statisticsDebtManagerUserItem.tsx (anchor)src/modules/debt-manager/component/🟡 Extend — drawer 720px chi tiết KPI + Top 5 khách
SCR-02Drill-down từ KPI cardDebtManagerConsultingPerformance.tsx (drilldown handler)src/modules/debt-manager/component/consulting-performance/🟡 Extend — bảng chi tiết tư vấn theo lượt/khách
SCR-03/dm/debt/debtDebtManagerDebt.tsxsrc/modules/debt-manager/component/debt/🟡 Extend — bổ sung scope chip vai trò + summary banner + daily focus
SCR-03-DRAWERDrawer phải bên trong tab debtDebtManagerActionViewCalendar.tsx + DebtManagerReminderScheduleHistory.tsx + DebtManagerItemReminderOrderList.tsxsrc/modules/debt-manager/component/🟢 Reuse — drawer logic đã có
SCR-03 Popup tạo lịch nhắcPopup overlayDebtManagerCreateReminderSchedule.tsxsrc/modules/debt-manager/component/🟢 Reuse — popup đã có
SCR-04Settings menuDebtManagerSetting.tsxsrc/modules/settings/pages/DebtManagerSetting.tsx🟢 Reuse — config form global/branch + history đã có
SCR-05 (Bước 1: chọn nguồn)Wizard handoverCustomerHandoverSelectSource.tsx + CustomerHandoverSelectScope.tsxsrc/modules/user/components/customer-handover/🟢 Reuse — wizard sẵn
SCR-06 (Bước 2: chọn đích)WizardCustomerHandoverSelectDestination.tsx + CustomerHandoverSelectStaffReception.tsx + CustomerHandoverSelectItemStaff.tsxsrc/modules/user/components/customer-handover/🟢 Reuse — preview wizard sẵn
SCR-07 (Bước 3: xác nhận)WizardCustomerHandoverConfirmRecheck.tsx + CustomerHandoverConfirm.tsxsrc/modules/user/components/customer-handover/🟢 Reuse — confirm wizard sẵn
SCR-08 (Kết quả + audit)Detail pageCustomerHandoverDetail.tsx + CustomerHandoverHistoryTable.tsxsrc/modules/user/components/customer-handover/customer-handover-detail/ + customer-handover-history/🟢 Reuse — detail + history sẵn; rollback action call BE rollback_customer_handover
Filter bar (date + staff + branch) — chungHeader tất cả tabDebtManagerFilter.tsxsrc/modules/debt-manager/component/🟢 Reuse
Charts (aging, line trend)Bên trong tabsDebtLineChart.tsx, DebtProgressBar.tsxsrc/modules/debt-manager/component/🟢 Reuse
Export XLSXCTA toàn pageXExcel.tsx core + XTable.tsx export featuresrc/components/core/file/, src/components/core/table/🟢 Reuse

Mobile (diva-flutter staff) — Module debt_management

MOB-IDExisting screenFile pathDelta
MOB-01debt_management_screen.dart (2 tabs: Consultation + Debt)lib/presentation/modules/debt_management/debt_management/views/🟡 Extend — thêm KPI cards layout cho tab Consultation overview
MOB-02debt_management_debt_tab.extension.dart (debt tab body, ListView khách nợ)lib/presentation/modules/debt_management/debt_management/views/🟡 Extend — pre-filter overdue + scope chip vai trò
MOB-03debt_customer_detail_screen.dart (chi tiết khách nợ + collapsible sections)lib/presentation/modules/debt_management/debt_customer_detail/views/🟡 Extend — thêm section Lịch sử LH + lịch nhắc collapsible
MOB-04debt_create_reminder_sheet.dart (bottom sheet 1038 dòng, form đầy đủ)lib/presentation/modules/debt_management/widgets/🟢 Reuse
MOB-05notification_screen.dart + AppNotificationRoute.dart (deep link handler)lib/presentation/modules/notification/views/, lib/presentation/route/🟡 Extend — thêm notification types: isDebtHandoverCompleted, action card
Reusable widget — khách nợ carddebt_customer_summary_card.dartlib/presentation/modules/debt_management/widgets/🟢 Reuse
Reusable widget — aging badgedebt_overdue_badge.dart + debt_status_pill.dartlib/presentation/modules/debt_management/widgets/🟢 Reuse
Reusable widget — click-to-calldebt_click2call.dartlib/presentation/modules/debt_management/widgets/🟢 Reuse

Backend surfaces được FE/Mobile tham chiếu

SurfaceBackend file/actionTham chiếu trong UI
Function aging buckets (0-29/30-59/60-89/≥90)customer_debt_dashboard_order_scope() — migration 1776054869987_add_customer_debt_statistics_dashboard_apisSCR-01 KPI cards · SCR-03 badge · MOB-01/02
Result table dashboarddashboard_customer_debt_overview_result, dashboard_customer_debt_ranking_result, dashboard_customer_debt_trend_resultSCR-01 KPI · SCR-01-TAB-ANALYTICS leaderboard
Search functionsearch_report_customer_debt_managementSCR-03 table · MOB-02 list
Daily alert schedulerservices/ecommerce-api/scheduler/daily_debt_alert.go + debt_alert_config + debt_alert_logNTF-DEBT-DAILY-001 + SCR-04 config
Handover actioncustomer_handover_support (action), rollback_customer_handover (action)SCR-05→08
Followup task lifecycledebt_followup_task table + debt_followup_notification_schedule table + event trigger debt_followup_task_notificationSCR-03-DRAWER · Popup tạo lịch · MOB-04
Contact logdebt_contact_log (record liên hệ + optional mark task done)Nút [✓ Đã LH]

Nguồn evidence chi tiết: Xem EVIDENCE_PACK.md để có quote SQL/Go/TS với line numbers.


§1. Trạng thái hardening — Map FR → Delta

Bảng này tóm tắt FR-001 đến FR-020 và phần nào cần build/sửa cho hardening pass:

FR-IDMô tả ngắnCodebase statusDelta cần làm
FR-001Bộ lọc thời gian + nhân sự🟢 Reuse DebtManagerFilter.tsxĐảm bảo filter dùng branch_id từ Hasura header + scope chip
FR-002DS khách đã tư vấn🟢 Reuse DebtManagerConsultingPerformance.tsxBổ sung toggle "Theo lượt / Theo khách" nếu chưa có
FR-003KPI tiếp khách🟢 Reuse aging function + result tableKPI card layout — extend nếu thiếu
FR-004KPI mua + chuyển đổi🟡 Extend formulas (xem prd.md A10 FORMULA-002)Đảm bảo formula dùng đúng cột
FR-005KPI công nợ🟢 Reuse dashboard_customer_debt_overview_result
FR-006KPI thời gian thu nợ🟡 Extend formula FORMULA-005 (avg_days_to_collect)Validate SQL
FR-007Aging buckets🟢 Reuse function customer_debt_dashboard_order_scope()
FR-008Cấu hình ngưỡng cảnh báo🟢 Reuse DebtManagerSetting.tsx + debt_alert_configValidate UI hardening (history view + branch override)
FR-009Cảnh báo nợ hằng ngày🟢 Reuse scheduler daily_debt_alert + planner + debt_alert_notification_scheduleValidate runbook + monitor
FR-010Workflow bàn giao khách🟢 Reuse CustomerHandover* wizardValidate limit 200 khách/lần + rollback 24h
FR-011Audit handover🟢 Reuse customer_handover_log + CustomerHandoverHistoryTable.tsx
FR-012Export XLSX🟢 Reuse XExcel.tsx + XTable.tsxĐảm bảo respect filter + permission
FR-013Quản lý lịch nhắc follow-up🟢 Reuse debt_followup_task + DebtManagerCreateReminderSchedule.tsx + drawerValidate notification flow + permission
FR-014…020Tab Thống kê (Admin/Manager)🟡 Extend DebtManagerStatistics.tsxThêm leaderboard, segment so sánh, drawer NV

Marker convention: 🟢 = code đã đủ, chỉ cần test/doc · 🟡 = code có ~80%, cần extend nhỏ · 🔴 = phải build mới (KHÔNG có FR nào ở mức này — đã verify trong discovery).


§2. Nguyên tắc UI hardening

  1. Không tạo route mới/dm/debt/{consulting-performance|debt|statistics} đã có; thay đổi label/copy text trong i18n nếu cần khớp HTML target.
  2. Không tạo component mới khi component có hơn 80% logic giống — chỉ extend.
  3. Permission ground truth là Dynamic Permission v2 (DEC-011 — module_id=debt_manager, action access|create|update|view_all|export). KHÔNG hard-code theo role name. Wireframe permission matrix dưới đây mô tả intent — implementation phải resolve qua permission action.
  4. Branch scoping qua X-Hasura-Branch-Id header — Manager chọn 1 trong N branch đang quản lý (URQL header phải set khi đổi branch picker).
  5. Sensitive field least-data (DEC-012) — số điện thoại, debt_amount: BE phải apply permission, FE chỉ là defense-in-depth.
  6. Wireframe ASCII trong spec dưới đây mô tả TARGET STATE sau hardening. Codebase hiện tại có thể đã match, hoặc cần extend nhỏ. Dev check side-by-side trước khi sửa.

§3. Phần chính: chi tiết UI/UX (B1 → B-Onboarding)

Từ section dưới đây trở đi là nội dung UI spec đầy đủ chuyển hóa từ HTML intent v4.12. Mọi SCR/MOB đều ánh xạ tới existing component theo §0 ở trên.


B1. Screen Map

WEB (Desktop)
├── 📊 Báo cáo
│   ├── SCR-01  Báo cáo hiệu suất tư vấn & công nợ
│   │   ├── SCR-01-TAB-ANALYTICS  Tab Thống kê (Admin/Manager only)
│   │   │   └── SCR-01-ANALYTICS-DRAWER  Chi tiết nhân viên (drawer phải)
│   │   ├── SCR-02  Chi tiết danh sách tư vấn
│   │   ├── SCR-03  Danh sách nợ quá hạn cần xử lý
│   │   │   └── SCR-03-DRAWER  Quản lý lịch nhắc chăm sóc (drawer phải)
│   │   └── SCR-04  Cài đặt cảnh báo nợ quá hạn
│   │
│   └── 👥 Bàn giao khách hàng
│       ├── SCR-05  Bước 1 — Chọn nhân viên nghỉ & phạm vi bàn giao
│       ├── SCR-06  Bước 2 — Chọn người tiếp nhận & xem trước
│       ├── SCR-07  Bước 3 — Kiểm tra & xác nhận bàn giao
│       └── SCR-08  Kết quả bàn giao & lịch sử thao tác

MOBILE (Diva Partner App)
├── 🏠 MOB-01  Trang chủ — Hiệu suất cá nhân
├── 📋 MOB-02  Danh sách khách nợ quá hạn
├── 👤 MOB-03  Chi tiết khách nợ & lịch nhắc
├── 📅 MOB-04  Tạo lịch nhắc chăm sóc (bottom sheet)
└── 🔔 MOB-05  Trung tâm thông báo

B2. Screen Inventory

ScreenTiêu đề hiển thị trên UIMô tả ngắnPersonaFRPlatformMở từ đâu
SCR-01Báo cáo hiệu suất tư vấn & công nợTổng quan KPI + bảng danh sách kháchSale, CSKH, ManagerFR-001…007, 012WebMenu [Báo cáo]
SCR-01-TAB-ANALYTICSThống kêKPI quản trị công nợ + chart + leaderboard nhân sựManager, AdminFR-014…020WebTab Thống kê trong SCR-01
SCR-01-ANALYTICS-DRAWERChi tiết nhân viênDrawer KPI nợ chi tiết + aging + Top 5 khách nợManager, AdminFR-019WebBấm tên nhân viên trong leaderboard
SCR-02Chi tiết danh sách tư vấnXem từng lượt / khách duy nhất đã tư vấnManager, Team leadFR-002, 003, 004WebBấm card KPI trên SCR-01
SCR-03Nợ quá hạn cần xử lýDanh sách khách nợ + hành động gọi/nhắc/đánh dấuSale, CSKH, ManagerFR-005, 007, 009, 013WebBấm card “Khách nợ” trên SCR-01 hoặc noti nợ
SCR-03-DRAWERLịch nhắc chăm sóc của tôiDrawer quản lý tất cả lịch nhắc + timeline theo kháchSale, CSKH, ManagerFR-013WebBấm nút [📅 Lịch nhắc] trên SCR-03
SCR-04Cài đặt cảnh báo nợ quá hạnCấu hình ngưỡng ngày + giờ gửi + override chi nhánhAdmin, ManagerFR-008WebMenu [Cài đặt]
SCR-05Bàn giao khách — Bước 1: Chọn nguồnChọn nhân viên nghỉ + phạm vi + ngày hiệu lựcManager, HR OpsFR-010WebMenu [Khách hàng] → [Bàn giao]
SCR-06Bàn giao khách — Bước 2: Xem trướcChọn người nhận + xem so sánh trước/sauManager, HR OpsFR-010WebBấm [Tiếp theo] từ SCR-05
SCR-07Bàn giao khách — Bước 3: Xác nhậnTóm tắt + cam kết + ghi chúManager, HR OpsFR-010, 011WebBấm [Tiếp theo] từ SCR-06
SCR-08Kết quả bàn giao & lịch sử thao tácXem audit trail + rollback trong 24hManager, HR OpsFR-011WebSau khi xác nhận SCR-07
MOB-01Hiệu suất cá nhânKPI tư vấn + chốt + nợ cho bản thânSale, CSKH, TelesaleFR-003…005, 007MobileTab [🏠 Home]
MOB-02Khách nợ quá hạnDanh sách khách nợ + CTA gọi/nhắc/đánh dấuSale, CSKH, TelesaleFR-005, 007, 009MobileTab [📋 Nợ] hoặc tap push noti
MOB-03Chi tiết khách nợ & lịch nhắcThông tin nợ + dịch vụ + lịch sử LH + lịch nhắcSale, CSKH, TelesaleFR-005, 009, 013MobileTap 1 khách trên MOB-02
MOB-04Tạo lịch nhắc chăm sócBottom sheet nhập ngày/giờ/nội dung nhắcSale, CSKH, TelesaleFR-009, 013MobileBấm [📅 Lịch] trên MOB-02/03
MOB-05Trung tâm thông báoDanh sách noti nợ + bàn giaoSale, CSKH, TelesaleFR-009, FR-013MobileTab [🔔 Thông báo]

B3. Luồng thao tác chính (User Flows)

FlowMô tảCác bước chi tiếtFR
FLOW-01Nhân viên xem báo cáo ngày (web)Menu [Báo cáo] → SCR-01 → chọn bộ lọc → xem KPI → bấm [Xuất XLSX]FR-001…004, 012
FLOW-02Xử lý cảnh báo nợ (web)Nhận noti in-app → bấm noti → SCR-03 → xem danh sách → bấm [📞 Gọi] / [✓ Đã LH] → bấm [→ CRM] mở chi tiết kháchFR-005, 007, 009
FLOW-02MXử lý cảnh báo nợ (mobile)Nhận push → tap noti → MOB-02 → bấm khách → MOB-03 → bấm [📞 Gọi] → quay lại → đánh dấu [✓ Đã LH]FR-005, 007, 009
FLOW-03Manager so sánh hiệu suất nhân viênMenu [Báo cáo] → SCR-01 → chọn Chi nhánh → chọn Vai trò → xem bảng so sánh → bấm tên khách → SCR-02FR-004…007, 012
FLOW-04Cấu hình ngưỡng cảnh báoMenu [Cài đặt] → SCR-04 → sửa ngưỡng ngày / giờ gửi → bấm [Lưu thay đổi]FR-008
FLOW-05Bàn giao khách khi nhân sự nghỉMenu [Khách hàng] → [Bàn giao] → SCR-05 chọn nguồn → [Tiếp theo] → SCR-06 chọn đích + xem preview → [Tiếp theo] → SCR-07 tick cam kết + [Xác nhận] → SCR-08 xem kết quảFR-010, 011
FLOW-06Tạo và theo dõi lịch nhắc (web)SCR-03 → bấm [📅] cạnh khách → popup tạo lịch → bấm [Tạo lịch] → bấm [📅 Lịch nhắc] trên toolbar → SCR-03-DRAWER mở → xem danh sách → bấm [✅ Đã xong]FR-013
FLOW-06MTạo và theo dõi lịch nhắc (mobile)MOB-02 → tap khách → MOB-03 → bấm [📅 Lịch] → MOB-04 bottom sheet → tạo xong → cuộn xuống section “Lịch nhắc” xem lại → bấm [✅ Xong]FR-013
FLOW-07Nhận nhắc follow-up (web)Đến giờ nhắc → noti in-app xuất hiện → bấm noti → SCR-03 → drawer tự mở filter theo khách → xử lýFR-013
FLOW-08Manager/Admin xem thống kê công nợMenu [Báo cáo] → SCR-01 → tab Thống kê → xem 6 KPI + 2 biểu đồ + leaderboard → bấm nhân viên mở drawer → bấm [Xem chi tiết] quay về tab Công nợ với filter nhân sựFR-014…020

B3.1 Bản đồ điều hướng giữa các màn hình (Navigation Map)

Web Desktop

                        MENU CHÍNH

              ┌─────────────┼──────────────────┐
              ▼             ▼                  ▼
         [Báo cáo]    [Khách hàng]       [Cài đặt]
              │             │                  │
              ▼             ▼                  ▼
          SCR-01        SCR-05             SCR-04
       Dashboard      Bàn giao B1       Cấu hình
              │             │            cảnh báo
     ┌────────┼────────┐    ▼
     ▼        ▼        ▼  SCR-06
  SCR-02   SCR-03    Xuất  Bàn giao B2
  Chi tiết  Nợ quá   XLSX     │
  tư vấn    hạn               ▼
     │        │             SCR-07
     ▼        │             Xác nhận
  [→ CRM]    ├───────┐        │
  (tab mới)  ▼       ▼        ▼
          SCR-03   Popup    SCR-08
          DRAWER   Tạo     Kết quả
          Lịch     lịch    + Audit
          nhắc     nhắc   + Rollback

Mobile (Diva Partner App)

        BOTTOM TAB BAR

    ┌─────────┼──────────┬──────────┐
    ▼         ▼          ▼          ▼
 [🏠 Home] [📋 Nợ]   [🔔 TB]    [👤 Tôi]
    │         │          │
    ▼         ▼          ▼
 MOB-01    MOB-02     MOB-05
 KPI       Nợ quá    Thông báo
 cá nhân   hạn
    │         │
    │         ▼
    │      MOB-03
    │      Chi tiết
    │      khách nợ
    │         │
    │         ├──────────┐
    │         ▼          ▼
    │      MOB-04    Section
    │      Tạo lịch  Lịch nhắc
    │      nhắc

    └──→ MOB-02  (tap card Nợ / chart nhóm nợ)

Bảng điều hướng chi tiết (Bấm vào đâu → Mở màn nào)

Từ SCR-01 (Dashboard)

Vị trí bấmĐích đếnHành viGhi chú
Card “Lượt tư vấn”SCR-02Chuyển trang, giữ bộ lọc thời gianFilter = tất cả trạng thái
Card “Khách đã tư vấn”SCR-02Chuyển trang, toggle = “Theo khách đã tư vấn”Auto toggle
Card “Khách mua trong kỳ”SCR-02Chuyển trang, filter = “Đã mua trong kỳ”Pre-filter trạng thái
Card “Khách đang nợ”SCR-01 / tab Công nợChuyển sang tab Công nợ, giữ filter hiện tạiGiữ filter hiện tại
Card “Khách nợ quy đổi”SCR-01 / tab Công nợChuyển sang tab Công nợ, giữ filter hiện tại
Card “Tổng tiền nợ”SCR-01 / tab Công nợChuyển sang tab Công nợ, giữ filter hiện tạiGiữ filter hiện tại
Card “Tiền nợ quy đổi”SCR-01 / tab Công nợChuyển sang tab Công nợ, giữ filter hiện tại
Tab “Thống kê”SCR-01-TAB-ANALYTICSChuyển nội dung trong SCR-01 sang view thống kê (chỉ Manager/Admin)Giữ top filter hiện tại
Tên NV trong leaderboardSCR-01-ANALYTICS-DRAWERMở drawer phải, hiển thị chi tiết nhân viênDrawer 720px
CTA [Xem chi tiết] trong drawerSCR-01 / tab Công nợĐóng drawer, chuyển tab Công nợ, set filter Nhân sự = {staff_id}Cuộn tới bảng nợ
Aging bar chart (click bucket)→ Bảng BLOCK D bên dướiNếu đang ở tab Công nợ: cuộn xuống bảng + apply filter bucket tương ứngCùng trang, cuộn
Tên khách trong bảngCRMMở tab mới đến trang chi tiết khách CRMTab mới
Nút [→ CRM] trong bảngCRMMở tab mớiTab mới
Nút [Xuất XLSX]→ Download fileTải file XLSX theo bộ lọc đang chọnAsync nếu > 50k rows

Từ SCR-02 (Chi tiết tư vấn)

Vị trí bấmĐích đếnHành viGhi chú
[← Quay lại Dashboard]SCR-01Quay lại, giữ bộ lọc thời gianBreadcrumb
Tên khách trong bảngCRMMở tab mớiTab mới
Nút [Xuất XLSX]→ Download fileTải file theo filter + quyền

Từ SCR-03 (Nợ quá hạn)

Vị trí bấmĐích đếnHành viGhi chú
Nút [📞 Gọi] cạnh khách→ Gọi điệnMở tính năng gọi trên web/SIP
Nút [📅 Tạo lịch] cạnh kháchPopup tạo lịch nhắcMở popup, điền sẵn tên khách + tiền nợPopup (overlay)
Nút [✓ Đã LH] cạnh khách→ Cập nhật tại chỗToggle trạng thái liên hệ, không chuyển trangInline action
Nút [→ CRM] cạnh kháchCRM chi tiết kháchMở tab mớiTab mới
Nút [📅 Lịch nhắc (N)] trên toolbarSCR-03-DRAWERMở drawer phải, hiện tất cả lịch nhắc trong scope filter hiện tạiDrawer 420px
[Lọc nhóm ≥90 →] trên summary banner→ Filter bảngGiữ nguyên filter hiện tại, tự filter Nhóm nợ = ≥90, cùng trangCùng trang
Nút [Xuất XLSX]→ Download fileTải file XLSX theo tab + filter
Tên khách trong bảngCRMMở tab mớiTab mới

Từ SCR-03-DRAWER (Drawer lịch nhắc)

Vị trí bấmĐích đếnHành viGhi chú
Nút [✅ Đã xong]→ Cập nhật tại chỗChuyển status → done, cập nhật UI ngayInline action
Nút [❌ Hủy]→ Popup xác nhậnHỏi “Bạn chắc muốn hủy lịch nhắc này?” → [Xác nhận] / [Quay lại]Confirm dialog
Nút [📞 Gọi]→ Gọi điệnMở tính năng gọi
Nút [+ Tạo lịch nhắc mới]Popup tạo lịch nhắcMở popup tạo mới, drawer vẫn mở phía sauPopup + drawer
Tên kháchCRM chi tiếtMở tab mớiTab mới
Nút [✕] đóng drawerSCR-03 (đóng drawer)Đóng drawer, quay lại bảng nợ

Từ SCR-04 (Cấu hình cảnh báo)

Vị trí bấmĐích đếnHành viGhi chú
Nút [Lưu thay đổi]→ Cập nhật tại chỗLưu config, hiện toast “Đã lưu. Hiệu lực từ lần chạy tiếp theo.”Toast thành công
Nút [+ Thêm override]→ Inline formMở dòng mới ngay trong bảngInline
Nút [Sửa] cạnh branch→ Inline editChuyển dòng sang chế độ sửaInline
Nút [Xóa] cạnh branch→ Popup xác nhận“Xóa cấu hình riêng của chi nhánh này?” → [Xác nhận]Confirm dialog

Từ SCR-05 → 06 → 07 → 08 (Bàn giao)

Vị trí bấmĐích đếnHành viGhi chú
SCR-05: Nút [Tiếp theo: Chọn đích →]SCR-06Chuyển bước wizard, validate trước khi quaValidate: nguồn + phạm vi + ngày
SCR-06: Nút [Tiếp theo: Xác nhận →]SCR-07Chuyển bước wizardValidate: đích đã chọn
SCR-06: Nút [← Quay lại]SCR-05Quay lại bước trước, giữ data đã nhậpGiữ state
SCR-07: Nút [Xác nhận bàn giao]SCR-08Thực hiện handover, chuyển sang kết quảDisabled khi chưa tick đủ 2 checkbox
SCR-07: Nút [← Quay lại]SCR-06Quay lại bước 2Giữ state
SCR-08: Nút [↩ Hoàn tác bàn giao]Popup xác nhận rollbackMở popup, yêu cầu nhập lý doChỉ hiện khi còn trong 24h
SCR-08: Nút [← Về Dashboard]SCR-01Quay về dashboard chính
SCR-08: Nút [Xem danh sách khách]SCR-03 filter theo handoverMở danh sách nợ, filter owner = người nhậnPre-filter
Mỗi bước: Nút [Hủy]Popup xác nhận huỷ“Bạn chắc muốn huỷ? Dữ liệu đã nhập sẽ mất.” → [Xác nhận] / [Tiếp tục]Confirm dialog

Mobile — Điều hướng giữa màn hình

| Từ | Vị trí bấm | Đích đến | Hành vi | | --------------- | ------------------------------------- | ------------------------- | ------------------------------------- | -------------------- | | MOB-01 | Tap card “Khách nợ” | → MOB-02 | Navigate danh sách nợ theo filter hiện tại | Giữ filter hiện tại | | MOB-01 | Tap chart nhóm nợ | → MOB-02 | Navigate + pre-filter nhóm nợ đã chọn | Pre-filter | | MOB-01 | Nút [→ Xem danh sách nợ quá hạn] | → MOB-02 | Navigate danh sách nợ | — | | MOB-02 | Tap 1 dòng khách | → MOB-03 | Navigate đến chi tiết khách nợ | Truyền customer_id | | MOB-02 | Nút [Gọi ngay] trên card | → Gọi điện (native) | Mở phone app, quay lại hỏi “Đã LH?” | Native intent | | MOB-02 | Nút [Tạo lịch] trên card | → MOB-04 bottom sheet | Mở bottom sheet tạo lịch nhắc | Bottom sheet | | MOB-02 | Nút [⋯] trên card | → Menu action | Mở action sheet: Đã liên hệ / Xem chi tiết / CRM | Inline menu | | MOB-03 | Nút [📞 Gọi ngay] | → Gọi điện (native) | Mở phone app | Native intent | | MOB-03 | Nút [📅 Lịch] | → MOB-04 bottom sheet | Tạo lịch nhắc cho khách này | Bottom sheet | | MOB-03 | Nút [→ CRM] | → Web CRM (external) | Mở browser ngoài app | External link | | MOB-03 | Nút [✅ Xong] trong section lịch nhắc | → Cập nhật tại chỗ | Chuyển status done, refresh section | Inline action | | MOB-03 | Nút [❌ Hủy] trong section lịch nhắc | → Confirm bottom sheet | Xác nhận hủy | Bottom sheet | | MOB-03 | Nút [+ Tạo] trong section lịch nhắc | → MOB-04 bottom sheet | Tạo lịch nhắc mới | Bottom sheet | | MOB-05 | Tap noti “Nợ quá hạn” | → MOB-02 | Navigate vào danh sách nợ theo payload deeplink | Deeplink | | MOB-05 | Tap noti “Bàn giao” | → MOB-01 | Navigate, refresh KPI | Deeplink | | Bottom tab [🏠] | — | → MOB-01 | Tab Home | Always available | | Bottom tab [📋] | — | → MOB-02 | Tab Nợ | Always available | | Bottom tab [🔔] | — | → MOB-05 | Tab Thông báo | Badge = unread count | | Bottom tab [👤] | — | → Trang cá nhân | Cài đặt, đăng xuất | — |

Từ Notification (Web In-app)

NotiBấm vàoĐích đếnHành vi
NTF-DEBT-DAILY-001Bấm notiSCR-01 / tab Công nợChuyển sang tab Công nợ, apply pre-filter theo payload (ưu tiên nhóm nợ cao nhất)
NTF-HANDOVER-TARGETBấm notiSCR-08 (kết quả handover)Chuyển đến trang kết quả bàn giao
NTF-HANDOVER-CREATORBấm notiSCR-08Chuyển đến trang kết quả bàn giao
NTF-FOLLOWUP-REMINDBấm notiSCR-01 / tab Công nợ + tự mở SCR-03-DRAWERMở tab Công nợ, drawer filter theo customer_id từ noti

B4. Notification Spec (Web In-app + Mobile Push)

Template IDPlatformTriggerTitleBodyDedupe
NTF-DEBT-DAILY-001In-app + Mobile PushJob hằng ngày, overdue_days >= threshold_days (Ref DEC-025) — gửi mỗi ngày nếu vẫn quá hạn (Ref DEC-026 rule 1)⚠️ Nợ quá hạn cần xử lý"Bạn có khách nợ ≥ ngày hôm nay. Tổng nợ: đ"1 lần/ngày/(owner, customer, current_bucket)
NTF-DEBT-BUCKET-ESCALATEIn-app + Mobile PushCustomer chuyển bucket lên cấp cao hơn (Ref DEC-026 rule 2) — vd watch_list → high_risk⚠️ Khách chuyển nhóm rủi ro"Khách chuyển sang nhóm ( ngày). Nợ: đ"Per (owner, customer, bucket_transition)
NTF-HANDOVER-TARGETIn-app + Mobile PushHandover confirm thành công✅ Khách mới được bàn giao" khách từ đã chuyển cho bạn (hiệu lực )"Per handover ID
NTF-HANDOVER-CREATORIn-appHandover confirm thành công✅ Bàn giao thành công"Bàn giao — khách từ sang đã hoàn tất"Per handover ID
NTF-FOLLOWUP-REMINDIn-app (web only)Scheduler kiểm tra remind_at ≤ now()📅 Nhắc follow-up khách nợ"Nhắc: — Khách , nợ đ ( ngày)"Per task_id

Quy tắc bất biến:

  • NTF-DEBT-DAILY-001: Chỉ gửi cho owner chính, chỉ khi overdue_days >= threshold_days (Ref DEC-025). KHÔNG dedupe cross-day — gửi mỗi ngày nếu vẫn quá hạn (DEC-026 rule 1).
  • Customer-level bucket trong payload: dùng FORMULA-006B (max bucket), KHÔNG order-level. Vd: KH có 1 đơn 95d + 1 đơn 5d → noti gắn bucket very_high_risk.
  • Partial payment (DEC-026 rule 3): Khách thanh toán 1 phần nhưng còn nợ → vẫn nhận noti hằng ngày. Field total_debt payload = SUM(remaining) còn lại.
  • Settled drop (DEC-026 rule 4): Khách settled hết → drop khỏi list từ ngày sau.
  • Bucket escalation (NTF-DEBT-BUCKET-ESCALATE): Gửi 1 lần riêng vào ngày customer chuyển bucket cao hơn — KHÔNG thay thế NTF-DEBT-DAILY-001 cùng ngày, mà gửi BỔ SUNG (user sẽ nhận 2 noti: daily + escalation).
  • Thiếu biến bắt buộc → không gửi + ghi log lỗi
  • Mobile push: badge count = overdue_count; deeplink → MOB-02
  • NTF-FOLLOWUP-REMIND: Chỉ in-app web (DEC-020), gửi khi remind_at ≤ now()status = 'pending'. Deeplink → tab Công nợ (web). Không push mobile.
  • Mỗi noti phải có: template_id, recipient_id, created_at, deeplink_route. Timezone: Asia/Ho_Chi_Minh.

B5. Permission Matrix (Đầy đủ — Tất cả màn hình)

Rule Manager (DEC-013): Manager chỉ xem data trong các branch mình đang quản lý. Manager quản lý 3 branch → dropdown Chi nhánh chỉ hiển thị 3 branch đó.

SCR-01 — Dashboard

QuyềnSale / CSKH / TelesaleManagerAdmin
Xem dashboard✅ Chỉ data của mình✅ Các branch đang quản lý✅ Toàn bộ
Filter theo nhân sự khác✅ Trong branch quản lý
Filter theo chi nhánh✅ Chỉ branch đang quản lý✅ Tất cả
Xem tên khách⚠️ Masked
Xuất XLSX✅ Data của mình✅ Data branch quản lý

SCR-01-TAB-ANALYTICS — Thống kê

QuyềnSale / CSKH / TelesaleManagerAdmin
Xem tab “Thống kê”❌ Ẩn tab✅ Trong branch đang quản lý✅ Toàn bộ
Dùng segment so sánh Chi nhánh/NV✅ Trong scope branch được phân quyền
Dùng sort chip Đã thu/Còn nợ
Xem leaderboard + mở drawer nhân sự
Xuất XLSX tab Thống kê✅ Dữ liệu branch quản lý

SCR-02 — Drill-down Tư Vấn

QuyềnSale / CSKH / TelesaleManagerAdmin
Xem danh sách✅ Của mình✅ Team trong branch quản lý
Xem % hoa hồng người khác
Xuất XLSX✅ Của mình✅ Team

SCR-03 — Nợ Quá Hạn

QuyềnSale / CSKH / TelesaleManagerAdmin
Dùng scope chip vai trò (Sale/Telesale/CSKH)✅ Vận hành
Xem nợ nhân viên khác✅ Trong branch quản lý
Gọi điện
Tạo lịch nhắc
Đánh dấu Đã LH
Thao tác hàng loạt✅ Của mình✅ Vận hành
Mở drawer lịch nhắc✅ Của mình✅ Trong branch quản lý
Đánh dấu lịch nhắc Đã xong/Hủy✅ Lịch của mình✅ Lịch trong branch
Xuất XLSX✅ Của mình✅ Branch quản lý

SCR-04 — Cấu Hình Cảnh Báo (Ref: DEC-004 — branch_override ?? global)

QuyềnSale / CSKH / TelesaleManagerAdmin
Xem cấu hình✅ Xem
Sửa global config
Sửa branch override✅ Branch mình quản lý✅ Tất cả
Xem lịch sử thay đổi✅ Branch mình

SCR-05…08 — Handover Wizard

QuyềnSale / CSKH / TelesaleManagerHR OpsAdmin
Tạo handover✅ Nhân sự trong branch quản lý✅ Tất cả
Chọn nguồn/đích ngoài branch
Xem preview
Xác nhận handover
Rollback trong 24h✅ Tạo bởi mình
Xem audit log✅ Branch quản lý✅ Tất cả
Xuất XLSX audit (SCR-08)

Mobile — Diva Partner App

QuyềnSale / CSKH / TelesaleManager
Xem KPI cá nhân — MOB-01✅ Của mình✅ Của mình
Xem KPI team trên mobile❌ (dùng web)
Xem danh sách nợ — MOB-02✅ Của mình✅ Của mình
Gọi điện (MOB-02, MOB-03)
Đánh dấu Đã LH (MOB-02)
Xem chi tiết khách nợ — MOB-03✅ Của mình✅ Của mình
Xem lịch nhắc (MOB-03 section)✅ Của mình✅ Của mình
Đánh dấu lịch nhắc Đã xong/Hủy (MOB-03)✅ Lịch của mình✅ Lịch của mình
Tạo lịch nhắc — MOB-04
Xem thông báo — MOB-05
Nhận push notification

B6. State Matrix

ScreenLoadingEmptyErrorKhông quyềnPartial
SCR-01Skeleton KPI + skeleton table“Chưa có dữ liệu tư vấn” + [Đặt lại bộ lọc]Banner đỏ + [Thử lại]Chặn toàn bộKPI OK, bảng lỗi + retry tại chỗ
SCR-01-TAB-ANALYTICSSkeleton KPI + chart + leaderboard“Không có dữ liệu thống kê trong kỳ này”Banner + [Thử lại]Ẩn tab (Staff)KPI OK, chart/bảng lỗi cục bộ
SCR-01-ANALYTICS-DRAWERSkeleton drawer 720px— (mở từ dòng đã có dữ liệu)Banner + [Thử lại]Chặn drawerKPI OK, aging/top5 lỗi cục bộ
SCR-01 Action CardẨn (chờ data)Ẩn (không có nợ ≥30d)Ẩn (fallback — không block trang)ẨnN/A
SCR-02Skeleton table“Không có dữ liệu tư vấn theo bộ lọc”Banner + [Thử lại]Chặn toàn bộBảng OK, export lỗi + retry
SCR-03 Debt listSkeleton rows“Tuyệt vời! 🎉 Không có khách nào nợ quá hạn”Retry tại chỗChặn toàn bộAction disabled
SCR-03-DRAWERSkeleton rows“Chưa có lịch nhắc nào”Banner + retryChặn drawerN/A — single query
SCR-04Skeleton form“Chưa có cấu hình override cho chi nhánh nào” (Phần 2)Banner + retryChặn toàn bộN/A — single form
SCR-05…07Skeleton step— (wizard luôn có data từ bước trước)Giữ data đã nhập + banner lỗiChặn flowPreview lỗi → retry
SCR-05…07 (Quick Handover)Skeleton step (2 bước)— (wizard luôn có data từ bước trước)Giữ data đã nhập + banner lỗiChặn flowN/A — bước 2 gộp preview+confirm
SCR-08Skeleton timeline— (luôn có handover vừa tạo)Banner “Không thể tải kết quả bàn giao” + [Thử lại]Chặn toàn bộTimeline OK, rollback lỗi + retry
MOB-01Skeleton cards“Chưa có dữ liệu tư vấn”Banner + [Thử lại]N/A — mobile chỉ xem data mìnhKPI OK, chart lỗi
MOB-02Skeleton list“Không có nợ quá hạn 🎉”Banner + retryN/A — mobile chỉ xem data mìnhN/A — single list
MOB-03Skeleton chi tiết + skeleton lịch nhắc— (luôn có data từ MOB-02)Retry tại chỗ từng sectionN/A — mobile chỉ xem data mìnhChi tiết OK, lịch nhắc lỗi
MOB-03 (section lịch nhắc)Skeleton rows“Chưa có lịch nhắc”Retry tại chỗN/AN/A — sub-section
MOB-04 (bottom sheet)Spinner trên nút [Tạo lịch]— (form luôn sẵn sàng)Toast “Tạo lịch nhắc thất bại” + giữ form dataN/A — ai cũng tạo đượcN/A — single form
MOB-05Skeleton list“Chưa có thông báo nào”Banner + retryN/A — mobile chỉ xem noti mìnhN/A — single list
SCR-01 KPI Debt GridẨn (chờ KPI load)Ẩn (không có Block B data)ẨnẨnN/A
SCR-03 Daily FocusSkeleton 5 rowsẨn (tất cả nợ < 30 ngày)Ẩn (fallback — không block trang)ẨnN/A
SCR-01 Onboarding TourKhông hiện khi loadingN/A — hiện theo localStorage flagN/A — tour tự ẩn nếu target element lỗiN/AN/A

B7. Copy Text & Content

Error Messages

Lỗii18n KeyMessageAction
Load thất bạidebt.error.load_failed“Không thể tải dữ liệu. Vui lòng thử lại.”[Thử lại]
Export XLSX thất bạidebt.error.export_failed“Xuất file thất bại. Dữ liệu quá lớn hoặc có lỗi hệ thống.”[Thử lại] [Liên hệ hỗ trợ]
Handover thất bạidebt.error.handover_failed“Bàn giao thất bại. Vui lòng thử lại hoặc liên hệ Admin.”[Thử lại]
Rollback hết hạndebt.error.rollback_expired“Thời gian hoàn tác đã hết. Liên hệ Admin để xử lý thủ công.”
Không có quyềndebt.error.permission_denied“Bạn không có quyền thực hiện thao tác này.”[← Quay lại]
Nguồn = Đíchdebt.error.source_eq_target“Người giao và người nhận không được là cùng 1 nhân viên.”
Nhân viên không activedebt.error.staff_inactive“Nhân viên này không còn hoạt động. Vui lòng chọn người khác.”
Đang có handover pendingdebt.error.handover_pending“Đang có bàn giao chưa hoàn tất cho nhân viên này.”[Xem bàn giao →]
Filter không có kháchdebt.error.filter_empty“Bộ lọc không tìm thấy khách nào. Thử điều kiện khác.”
Vượt giới hạn handoverdebt.error.handover_limit“Tối đa 200 khách/lần bàn giao. Vui lòng chia nhỏ phạm vi.”
Scheduler lỗi (Ops alert)debt.ops.scheduler_failed“debt_scheduler_run_failed — [timestamp] — [error detail]”Xem runbook

Empty State Messages

Screeni18n KeyMessage
SCR-01 emptydebt.empty.dashboard“Chưa có dữ liệu tư vấn”
SCR-02 emptydebt.empty.no_consultation“Không có dữ liệu tư vấn theo bộ lọc”
SCR-03 debt list emptydebt.empty.no_overdue“Tuyệt vời! Không có khách nào nợ quá hạn”
MOB-01 emptydebt.mobile.empty.dashboard“Chưa có dữ liệu tư vấn”
MOB-02 emptydebt.mobile.empty.no_overdue“Không có nợ quá hạn”
Filter tương laidebt.empty.future_date“Chưa có dữ liệu cho kỳ này”
Drawer lịch nhắc emptydebt.empty.no_followup“Chưa có lịch nhắc nào”
MOB-03 lịch nhắc emptydebt.mobile.empty.no_followup“Chưa có lịch nhắc”
MOB-05 emptydebt.mobile.empty.no_notification“Chưa có thông báo nào”
SCR-04 branch emptydebt.empty.no_branch_override“Chưa có cấu hình override cho chi nhánh nào”

Badge & Status Labels

Contexti18n KeyText
Followup quá hạn badgedebt.followup.badge.overdue“Quá hạn”
Followup đã xongdebt.followup.badge.done“Đã xong”
Followup đã hủydebt.followup.badge.cancelled“Đã hủy”
Followup chờdebt.followup.badge.pending“Chờ nhắc”

Success / Toast Messages

Actioni18n KeyMessage
Đánh dấu Đã LHdebt.success.mark_contacted“Đã đánh dấu liên hệ khách hàng”
Hủy đánh dấu LHdebt.success.unmark_contacted“Đã hủy đánh dấu liên hệ”
Lưu cấu hình (SCR-04)debt.success.config_saved“Đã lưu. Hiệu lực từ lần chạy tiếp theo.”
Handover thành côngdebt.success.handover_done“Bàn giao thành công khách”
Rollback thành côngdebt.success.rollback_done“Hoàn tác thành công. khách đã trả về ”
Tạo lịch nhắcdebt.success.followup_created“Đã tạo lịch nhắc”
Đánh dấu lịch nhắc Đã xongdebt.success.followup_done“Đã hoàn thành lịch nhắc”
Hủy lịch nhắcdebt.success.followup_cancelled“Đã hủy lịch nhắc”
Export thành côngdebt.success.export_done“Xuất file thành công”

Notification Templates

Templatei18n KeyText
NTF-DEBT-DAILY-001debt.noti.debt_daily“Bạn có khách nợ quá ngày. Tổng nợ: đ”
NTF-HANDOVER-TARGETdebt.noti.handover_target“ khách từ đã chuyển cho bạn (hiệu lực )”
NTF-HANDOVER-CREATORdebt.noti.handover_creator“Bàn giao — khách từ sang đã hoàn tất”
NTF-FOLLOWUP-REMINDdebt.noti.followup_remind“Nhắc: — Khách , nợ đ ( ngày)”

B8. Analytics Event Mapping

EventTriggerScreenKPI Link
kpi_dashboard_viewedMở SCR-01 / MOB-01SCR-01, MOB-01Usage
dashboard_filter_appliedBấm filterSCR-01…03Usage funnel
report_export_clickedBấm Xuất XLSXSCR-01, SCR-02, SCR-03, SCR-08FR-012
debt_alert_openedClick noti nợ (web + mobile)SCR-03, MOB-02KPI-1
followup_task_createdTạo lịch chăm sócSCR-03 (popup), MOB-03→MOB-04Debt resolution
mark_contacted_clickedĐã liên hệSCR-03, MOB-02Ops efficiency
call_initiatedTap nút GọiSCR-03, SCR-03-DRAWER, MOB-02, MOB-03Engagement
handover_completedXác nhận bước 3SCR-08KPI vận hành
handover_rollback_requestedBấm rollbackSCR-08NFR-008
push_notification_tappedTap push trên mobileMOB-02, MOB-01Push engagement
followup_task_completedĐánh dấu Đã xongSCR-03-DRAWER, MOB-03Debt resolution
followup_task_cancelledHủy lịch nhắcSCR-03-DRAWER, MOB-03
followup_drawer_openedMở drawer lịch nhắcSCR-03Usage
followup_remind_sentNoti nhắc gửi thành công— (server)Scheduler
action_card_cta_clickedBấm “Xử lý ngay” trên Action CardSCR-01, MOB-01Engagement
action_card_dismissedBấm “Ẩn hôm nay” / swipe dismiss Action CardSCR-01, MOB-01UX feedback
overflow_menu_openedBấm [⋯] trên dòng bảng nợSCR-03Usage pattern
dashboard_tab_switchedChuyển tab “Hiệu suất tư vấn” ↔ “Công nợ”SCR-01Navigation pattern
kpi_card_clickedClick KPI card để navigate (→ SCR-02 / tab Công nợ)SCR-01Drill-down funnel
aging_bucket_clickedClick segment trên Aging Chart để apply filter Nhóm nợ cho debt action areaSCR-01Feature adoption
debt_scope_chip_changedThay đổi scope chip vai trò (Sale/Telesale/CSKH)SCR-01 tab Công nợ, MOB-02Filter behavior
debt_card_action_openedBấm CTA trực tiếp hoặc mở menu [⋯] trên card nợMOB-02Mobile UX adoption
mob03_section_toggledExpand/collapse section (Dịch vụ nợ / Lịch sử LH / Lịch nhắc)MOB-03Mobile UX adoption
debt_kpi_fullset_viewedMở tab Công nợ và hiển thị full bộ KPI nợSCR-01 tab Công nợ, MOB-01Feature adoption
daily_focus_call_clickedBấm [📞] trong Daily FocusSCR-03Engagement
daily_focus_hiddenBấm [Ẩn ▴] Daily FocusSCR-03UX feedback
daily_focus_view_allBấm “Xem toàn bộ danh sách”SCR-03Navigation
quick_handover_usedHoàn thành handover qua Quick mode (2 steps)SCR-05→06Feature adoption
onboarding_startedTour bắt đầu (step 1 hiện)SCR-01Onboarding
onboarding_completedTour hoàn thành (step 3 done)SCR-01Onboarding
onboarding_skippedBấm “Bỏ qua” trong tourSCR-01Onboarding
onboarding_replayedBấm “Xem lại hướng dẫn” từ menu ⓘSCR-01Re-engagement

B9. Bảng Tooltip (Tooltip Dictionary)

Tooltip hiển thị khi hover (web) hoặc long-press (mobile) lên các phần tử tương tác. Mục đích: hướng dẫn user mới và giảm chọn nhầm chức năng.

Web — Dashboard & Báo cáo (SCR-01)

Phần tửTooltip texti18n key
Card “Lượt tư vấn”“Số lượt tư vấn theo (Khách + Chi nhánh + Ngày) có phát sinh tư vấn (consultant)”debt.tooltip.visited_count
Card “Khách đã tư vấn”“Số khách hàng khác nhau đã được tư vấn (không đếm trùng)”debt.tooltip.unique_customers
Card “Khách mua trong kỳ”“Khách đã mua dịch vụ hoặc sản phẩm và có thực thu > 0 trong kỳ”debt.tooltip.closed_count
Card “Tỷ lệ chuyển đổi”“= Khách mua trong kỳ ÷ Khách đã tư vấn × 100%”debt.tooltip.conversion_rate
Card "Doanh thu ròng" (Ref DEC-032)"Doanh thu ròng (ghi nhận kế toán) = Tổng giá đơn − Chiết khấu − Hoàn trả. ⚠️ Đây là doanh thu đã ghi nhận, KHÔNG phải tiền đã thu về quỹ. Phần khách còn nợ chưa được trừ. Để xem tiền thực thu → KPI 'Đã thu trong kỳ'."debt.tooltip.net_revenue
Card “Khách đang nợ”“Khách còn dư nợ > 0đ tính đến thời điểm hiện tại”debt.tooltip.debt_customer_count
Card “Khách nợ quy đổi”“Số khách nợ được quy đổi theo % hưởng của bạn. VD: 2 người 50/50 thì mỗi người tính 0.5 khách”debt.tooltip.debt_customer_equivalent
Card “Tổng tiền nợ”“Tổng công nợ còn lại tại cuối kỳ theo bộ lọc hiện tại”debt.tooltip.total_debt
Card “Tiền nợ quy đổi”“Phần tiền nợ được tính cho bạn theo tỷ lệ hoa hồng của bạn”debt.tooltip.allocated_debt
Card “Tỷ lệ nợ/doanh thu”“= Tổng tiền nợ ÷ Doanh thu ròng × 100%”debt.tooltip.debt_ratio
Biểu đồ phân loại nhóm nợ“Phân bố khách nợ theo số ngày quá hạn. Bấm vào từng nhóm để xem chi tiết”debt.tooltip.aging_chart
Nút [Xuất XLSX] (SCR-01)“Tải báo cáo về file Excel theo bộ lọc hiện tại”debt.tooltip.export_xlsx
Filter “Chi nhánh”“Lọc theo chi nhánh bạn đang quản lý”debt.tooltip.filter_branch
Filter “Nhân viên”“Lọc theo nhân viên phụ trách (owner chính)”debt.tooltip.filter_staff
Nút [Xem chi tiết] drill-down“Mở danh sách tư vấn chi tiết theo bộ lọc”debt.tooltip.drill_down

Web — Chi tiết danh sách tư vấn (SCR-02)

Phần tửTooltip texti18n key
Toggle "Theo lượt / Theo khách""Chuyển giữa đếm theo lượt tư vấn và đếm theo khách đã tư vấn"debt.tooltip.scr02_toggle_view
Cột "Cuộc gọi" (Ref DEC-028)"Cuộc gọi gần nhất từ app Diva Staff cho khách này"debt.tooltip.scr02_last_call
Cột "% tham gia" (Ref DEC-029 + FORMULA-008)"% phụ trách nợ của bạn, weighted theo tổng giá trị các đơn của khách. Hover để xem chi tiết per-đơn"debt.tooltip.scr02_participation_pct
Tooltip drill-down "% tham gia" (Ref FORMULA-008)Format: "% tham gia: {weighted}% (weighted theo tổng giá trị đơn) — Chi tiết theo đơn: • Đơn #{code} ({money}): {pct}% — Tổng: ..."debt.tooltip.scr02_participation_drilldown
Filter "Trạng thái""Lọc theo: Tất cả / Đã mua trong kỳ / Chưa mua trong kỳ"debt.tooltip.scr02_filter_status
Nút [Xuất XLSX] (SCR-02)"Tải danh sách tư vấn về file Excel"debt.tooltip.scr02_export
Nút [← Quay lại Dashboard]"Quay về trang báo cáo hiệu suất chính"debt.tooltip.scr02_back

Web — Danh sách nợ quá hạn (SCR-03)

Phần tửTooltip texti18n key
Scope chip “Sale”“Bật/tắt phạm vi dữ liệu theo vai trò Sale trong tab Công nợ”debt.tooltip.scope_sale
Scope chip “Telesale”“Bật/tắt phạm vi dữ liệu theo vai trò Telesale trong tab Công nợ”debt.tooltip.scope_telesale
Scope chip “CSKH”“Bật/tắt phạm vi dữ liệu theo vai trò CSKH trong tab Công nợ”debt.tooltip.scope_cskh
Badge aging 0-29 ngày“Nợ mới — chưa quá hạn nghiêm trọng”debt.tooltip.aging_0_29
Badge aging 30-59 ngày“Cảnh báo — cần liên hệ sớm”debt.tooltip.aging_30_59
Badge aging 60-89 ngày“Nợ nguy hiểm — ưu tiên xử lý”debt.tooltip.aging_60_89
Badge aging ≥90 ngày“Nợ rất cũ — cần báo Manager xử lý”debt.tooltip.aging_90_plus
Nút [📞 Gọi]“Gọi điện cho khách. Sau khi gọi sẽ hỏi đánh dấu liên hệ”debt.tooltip.btn_call
Nút [📅 Lịch]“Tạo lịch nhắc chăm sóc cho khách này”debt.tooltip.btn_schedule
Nút [✓ Đã LH]“Đánh dấu đã liên hệ khách hàng hôm nay”debt.tooltip.btn_contacted
Nút [→ CRM]“Mở hồ sơ khách hàng trên CRM (tab mới)”debt.tooltip.btn_crm
Nút [📅 Lịch nhắc (N)] trên toolbar“Mở danh sách lịch nhắc chăm sóc của tôi. Số đỏ = lịch quá hạn”debt.tooltip.btn_open_drawer
Checkbox chọn nhiều“Chọn nhiều khách để thao tác hàng loạt (đánh dấu liên hệ)”debt.tooltip.bulk_select
Nút [Đã đánh dấu LH] (bulk)“Đánh dấu đã liên hệ cho tất cả khách đang chọn”debt.tooltip.bulk_contacted
Filter “Nhóm nợ”“Lọc theo nhóm ngày nợ quá hạn”debt.tooltip.filter_bucket
Filter “Owner”“Lọc theo nhân viên phụ trách nợ”debt.tooltip.filter_owner
Nút [Lọc nhóm ≥90 →]“Nhanh chóng lọc xem chỉ khách nợ ≥90 ngày”debt.tooltip.filter_shortcut_90
Nút [Xuất XLSX] (SCR-03)“Tải danh sách nợ quá hạn về file Excel”debt.tooltip.scr03_export

Web — Drawer lịch nhắc (SCR-03-DRAWER)

Phần tửTooltip texti18n key
Nút [✅ Xong]“Đánh dấu lịch nhắc đã hoàn thành”debt.tooltip.followup_done
Nút [❌ Hủy]“Hủy lịch nhắc này (cần xác nhận)”debt.tooltip.followup_cancel
Nút [📞 Gọi] (trong drawer)“Gọi điện cho khách từ lịch nhắc”debt.tooltip.drawer_call
Badge 🔴 “Quá hạn”“Lịch nhắc đã quá thời gian đặt — cần xử lý ngay”debt.tooltip.followup_overdue
Filter “Trạng thái”“Lọc theo: Tất cả / Chờ nhắc / Đã xong / Đã hủy / Quá hạn”debt.tooltip.followup_filter_status
Nút [×] đóng drawer“Đóng panel lịch nhắc”debt.tooltip.drawer_close

Web — Cài đặt cảnh báo (SCR-04)

Phần tửTooltip texti18n key
Field “Ngưỡng ngày quá hạn”“Số ngày nợ quá hạn để bắt đầu gửi cảnh báo (mặc định: 30 ngày)”debt.tooltip.threshold_days
Field “Giờ gửi cảnh báo”“Thời điểm gửi noti cảnh báo nợ hàng ngày (mặc định: 07:00)”debt.tooltip.alert_time
Toggle “Bật/Tắt cảnh báo”“Bật hoặc tắt gửi cảnh báo nợ quá hạn cho chi nhánh này”debt.tooltip.alert_toggle
Nút [Lưu cấu hình]“Lưu thay đổi cài đặt cảnh báo”debt.tooltip.save_config

Web — Bàn giao khách (SCR-05 → SCR-08)

Phần tửTooltip texti18n key
Dropdown “Nhân viên nghỉ” (SCR-05)“Chọn nhân viên sẽ ngừng phụ trách — khách sẽ chuyển đi”debt.tooltip.select_source
Dropdown “Người tiếp nhận” (SCR-06)“Chọn nhân viên sẽ nhận phụ trách khách hàng”debt.tooltip.select_target
Nút [Tiếp theo] (SCR-05, 06)“Chuyển sang bước tiếp theo trong wizard bàn giao”debt.tooltip.wizard_next
Nút [← Quay lại] (SCR-06, 07)“Quay về bước trước (dữ liệu đã nhập được giữ nguyên)”debt.tooltip.wizard_back
Checkbox “Tôi xác nhận…” (SCR-07)“Bắt buộc tick để kích hoạt nút Xác nhận bàn giao”debt.tooltip.confirm_checkbox
Nút [Xác nhận bàn giao] (SCR-07)“Thực hiện chuyển toàn bộ khách sang người nhận. Có thể hoàn tác trong 24h”debt.tooltip.confirm_handover
Nút [↩ Hoàn tác] (SCR-08)“Trả toàn bộ khách về nhân viên cũ. Chỉ khả dụng trong 24h sau bàn giao”debt.tooltip.rollback_handover
Nút [← Về Dashboard] (SCR-08)“Quay về trang báo cáo hiệu suất chính”debt.tooltip.back_dashboard
Nút [Xem danh sách khách] (SCR-08)“Mở danh sách nợ quá hạn, filter sẵn theo đợt bàn giao này”debt.tooltip.view_transferred
Nút [📋 Sao chép] (SCR-08)“Sao chép mã bàn giao vào clipboard”debt.tooltip.copy_handover_code
Nút [Xuất XLSX] (SCR-08)“Tải audit bàn giao về file Excel”debt.tooltip.scr08_export
Badge “Còn X giờ” (SCR-08)“Thời gian còn lại để hoàn tác bàn giao (tối đa 24h)”debt.tooltip.rollback_countdown
Badge “Đã hết hạn” (SCR-08)“Không thể hoàn tác — đã quá 24h. Liên hệ Admin”debt.tooltip.rollback_expired

Mobile — Diva Partner App (MOB-01 → MOB-05)

Phần tửTooltip (long-press)i18n key
Card “Lượt TV” (MOB-01)“Số lượt tư vấn theo (Khách + Chi nhánh + Ngày) có phát sinh tư vấn”debt.mobile.tooltip.visited
Card “Khách đang nợ” (MOB-01)“Tap để xem danh sách khách đang nợ”debt.mobile.tooltip.debt_card
Chart aging (MOB-01)“Tap vào nhóm nợ để xem danh sách chi tiết”debt.mobile.tooltip.aging_chart
Nút [📞 Gọi] (MOB-02, 03)“Gọi điện cho khách qua ứng dụng điện thoại”debt.mobile.tooltip.call
Nút [📅 Lịch] (MOB-02, 03)“Mở form tạo lịch nhắc chăm sóc”debt.mobile.tooltip.schedule
Nút [✓ Đã LH] (MOB-02)“Đánh dấu đã liên hệ”debt.mobile.tooltip.contacted
Nút [→ CRM] (MOB-03)“Mở hồ sơ khách trên trình duyệt”debt.mobile.tooltip.crm
Nút [✅ Xong] (MOB-03)“Hoàn thành lịch nhắc này”debt.mobile.tooltip.followup_done
Nút [❌ Hủy] (MOB-03)“Hủy lịch nhắc (cần xác nhận)”debt.mobile.tooltip.followup_cancel
Nút [+ Tạo] (MOB-03)“Tạo lịch nhắc mới cho khách này”debt.mobile.tooltip.followup_create
Field “Ngày nhắc” (MOB-04)“Chọn ngày và giờ muốn nhận nhắc”debt.mobile.tooltip.remind_date
Field “Nội dung” (MOB-04)“Nội dung sẽ hiện trong noti nhắc nhở”debt.mobile.tooltip.remind_content
Nút [Tạo lịch ✓] (MOB-04)“Lưu lịch nhắc và đóng form”debt.mobile.tooltip.submit_followup
Nút [Hủy] (MOB-04)“Đóng form không lưu”debt.mobile.tooltip.cancel_followup
Badge 🔔 (header)“Số thông báo chưa đọc”debt.mobile.tooltip.noti_badge
Bottom tab [🏠]“Trang chủ — hiệu suất cá nhân”debt.mobile.tooltip.tab_home
Bottom tab [📋]“Danh sách khách nợ quá hạn”debt.mobile.tooltip.tab_debt
Bottom tab [🔔]“Trung tâm thông báo”debt.mobile.tooltip.tab_noti

B-Desktop) UX Chi Tiết — Web Desktop

Platform: Web Admin / CRM | Breakpoint: 1280px+


SCR-01 — Báo cáo hiệu suất tư vấn & công nợ

Layout (1440px)

┌──────────────────────────────────────────────────────────────────┐
│ HEADER                                                           │
│ "Báo cáo Hiệu suất Tư vấn & Công nợ"          [Xuất XLSX ▼]    │
├──────────────────────────────────────────────────────────────────┤
│ TAB: [📊 Hiệu suất tư vấn]  [💰 Công nợ]                       │
│ FILTER BAR — sticky, height 56px, background #FAFAFA            │
│ [Hôm nay] [Tuần này] [Tháng này] [Tùy chỉnh]                   │
│ [📅 Từ ngày ──── Đến ngày]  (disabled trừ khi chọn Tùy chỉnh)  │
│ [Chi nhánh: Tất cả ▼]  [Vai trò: Tất cả ▼]  [Nhân sự: Tất cả ▼]│
├──────────────────────────────────────────────────────────────────┤
│ ACTION CARD — Việc cần làm hôm nay (ẩn khi không có nợ ≥30d)   │
│ 🔴 5 khách nợ ≥90 ngày · 🟠 3 khách nợ 60-89 ngày               │
│ 📅 2 lịch nhắc quá hạn                                          │
│ [Xử lý ngay →]                                    [Ẩn hôm nay] │
├──────────────────────────────────────────────────────────────────┤
│ KHI TAB = "📊 Hiệu suất tư vấn"                                │
│  BLOCK A — KPI tiếp khách & chuyển đổi                         │
│  BLOCK D — Bảng tư vấn: Khách | Dịch vụ | % Của tôi | Ngày TV | Trạng thái │
├──────────────────────────────────────────────────────────────────┤
│ KHI TAB = "💰 Công nợ"                                         │
│  SCOPE CHIP: [☑ Sale] [☑ Telesale] [☑ CSKH]                   │
│  BLOCK B — KPI nợ (full bộ card, không toggle)                │
│  BLOCK C — Phân loại nhóm nợ                                   │
│  BLOCK D — Bảng nợ: Khách | Liên hệ gần nhất | Phụ trách chính│
│                    | % tham gia | Tiền nợ | Nhóm nợ | Quá hạn │
│                    | Hành động                                  │
└──────────────────────────────────────────────────────────────────┘

Spec Dashboard Tabs

TabNội dung hiển thịDefaultLý do tách
📊 Hiệu suất tư vấnBlock A (KPI tiếp khách & chuyển đổi) + Block D (bảng danh sách tư vấn)✅ Default khi mởSale xem hằng ngày — focus tư vấn + chuyển đổi
💰 Công nợScope chip vai trò + Block B (KPI nợ) + Block C (Aging chart) + Block D (bảng nợ quá hạn)Manager xem khi cần phân tích nợ

Behavior:

  • Tab giữ nguyên filter bar (không reset khi chuyển tab)
  • URL hash: /report/sales-debt-performance#consultation vs #debt
  • Action Card hiện trên cả 2 tab (vì liên quan urgency nợ)
  • [Xuất XLSX] export theo tab đang active
  • Nếu click KPI card nợ trong tab Tư vấn → tự chuyển sang tab Công nợ và giữ nguyên top filter/scope chip đang chọn

Spec Scope Chip Vai trò trong tab “Công nợ”

Thuộc tínhMô tả
Vị tríNằm trên cùng hàng với top filter, ngay trước nút Xuất XLSX
Kiểu hiển thịCheckbox chip: Sale / Telesale / CSKH
Vai tròFilter theo nhóm vai trò tham gia xử lý nợ trong tab Công nợ
DefaultTất cả chip bật
ScopeẢnh hưởng Block B, Block C, Priority Card strip, Block D, export
PersistGiữ trong session khi user đổi date/filter khác

Spec Filter Bar

ComponentTypeDefaultCascade behavior
Preset thời gianButton groupTháng nàyClick → cập nhật date range
Date range pickerRange inputĐầu tháng – Hôm nayChỉ enabled khi chọn “Tùy chỉnh”
Chi nhánhSingle selectTất cả (theo quyền)Đổi → reset Nhân sự về “Tất cả”
Vai tròMulti-select chipTất cảĐổi → filter danh sách Nhân sự theo role
Nhân sựSingle selectTất cảCascade từ Branch + Role. Hiển thị: [Avatar] Tên — Role

Quy tắc multi-select Vai trò:

  • Multi-select role = OR logic: hiển thị tất cả nhân sự thuộc bất kỳ role đã chọn
  • VD: chọn “Sale” + “CSKH” → danh sách Nhân sự hiển thị tất cả Sale và CSKH
  • KPI dashboard: aggregate tất cả nhân sự match filter (không tách theo role)
  • Nếu chọn 1 nhân sự cụ thể → chỉ hiển thị KPI của riêng người đó
  • Nếu không chọn role nào (= “Tất cả”) → hiển thị tất cả nhân sự trong branch scope

Spec Ma trận phạm vi bộ lọc

Bộ lọc / Điều khiểnHiển thị ở đâuẢnh hưởng đến đâuGhi chú rõ nghĩa
Preset thời gian + Date rangeSCR-01, cả 2 tabToàn bộ KPI/card/chart/bảng/export của tab đang activeLà filter toàn cục. Với tab Công nợ, date_to là mốc snapshot nợ
Chi nhánh (top filter)SCR-01, cả 2 tabToàn bộ dữ liệu của tab đang activeĐổi → reset Nhân sự về Tất cả
Vai tròSCR-01, cả 2 tabToàn bộ dữ liệu của tab đang activeChỉ là filter scope cho danh sách nhân sự + aggregate dashboard
Nhân sựSCR-01, cả 2 tabToàn bộ dữ liệu của tab đang activeNếu chọn 1 người cụ thể, mọi KPI/bảng chỉ còn dữ liệu của người đó
Tab chính Hiệu suất tư vấn / Công nợSCR-01Đổi module hiển thịGiữ nguyên top filter khi chuyển tab
Scope chip Sale/Telesale/CSKHSCR-01 tab Công nợBlock B, Block C, Priority Card strip, Block D, export debtLà filter scope theo vai trò tham gia nợ
Chi nhánh (local debt filter)SCR-03Toàn bộ thành phần của debt view trong tab Công nợStaff ẩn. Manager/Admin dùng để slice debt scope trong tab nợ
Owner (local debt filter)SCR-03Toàn bộ thành phần của debt view trong tab Công nợNếu top filter Nhân sự đã chọn 1 người cụ thể → local Owner tự set cùng giá trị và chuyển sang trạng thái readonly. Khi top filter quay về Tất cả → local Owner mở lại để user chọn
Nhóm nợSCR-03Summary Banner, Daily Focus, Block D, export debtĐây là working-set filter của debt action area. Block C vẫn giữ full distribution theo scope hiện tại và chỉ highlight nhóm đang chọn, không recalc lại segment
Tìm kháchSCR-03Chỉ Block DLà filter truy xuất nhanh, không làm thay đổi KPI/chart/banner

Spec As-of Date cho dữ liệu nợ

Quy tắcMô tả
Mốc snapshotToàn bộ dữ liệu nợ trong tab Công nợ được tính tại cuối ngày date_to theo timezone Asia/Ho_Chi_Minh
date_toQuyết định outstanding_amount, overdue_days, Nhóm nợ, Khách đang nợ, Tổng tiền nợ, Tiền nợ quy đổi, Tỷ lệ thu nợ, Đã thu trong kỳ, Priority Card strip
date_fromKhông loại các khoản nợ cũ hơn nếu chúng vẫn còn outstanding tại date_to; giữ để đồng bộ report context, so sánh kỳ và metadata export
Không có custom rangeNếu user chọn preset như Hôm nay / Tuần này / Tháng này, hệ thống map ra date_to tương ứng ngày cuối của preset đang xem
Công thức overdueoverdue_days = max(0, end_of_day(date_to) - debt_due_date) tính theo số ngày lịch
Hệ quảMột khoản nợ phát sinh trước date_from nhưng còn outstanding tại date_to vẫn phải xuất hiện trong tab Công nợ

Ví dụ: chọn 01/02 → 28/02/2026, tab Công nợ phản ánh ảnh chụp nợ tại 23:59:59 ngày 28/02/2026, không phải “ngay lúc user đang xem”.

Spec KPI Cards

Nguyên tắc sub-label: Mỗi KPI card hiển thị sub-label giải thích trực tiếp bên dưới giá trị chính (font 12px, color #757575). Giúp user hiểu KPI ngay mà không cần hover tooltip. Tooltip ⓘ vẫn giữ cho chi tiết đầy đủ.

Nguyên tắc format đơn vị tiền (Ref BUG-PROD-002 LOCKED): Mọi KPI giá trị tiền hiển thị bằng VND đầy đủ + suffix "đ" (vd 125.000.000đ). KHÔNG dùng đơn vị "Triệu" / "Tỷ" / "M" / "B" để tránh nhầm lẫn unit format. Số > 1 tỷ vẫn hiển thị đầy đủ với separator nghìn (1.250.000.000đ).

CardFieldGiá trị chínhSub-label trực tiếpTooltip ⓘClickVisibility
Lượt tư vấnvisit_count128 lượt ↗"Đếm theo (Khách + Chi nhánh + Ngày) có source consultant. Nhiều sự kiện tư vấn cùng ngày chỉ tính 1 lượt"→ SCR-02✅ Luôn hiện
Khách đã tư vấnunique_customer_count67 khách"Đếm theo customer_id, không trùng lặp"→ SCR-02✅ Luôn hiện
Khách mua trong kỳclosed_customer_count45 khách"Khách đã mua VÀ có thực thu > 0 trong kỳ"→ SCR-02 filter=closed✅ Luôn hiện
Tỷ lệ chuyển đổiconversion_rate67.2%"45 khách mua / 67 khách""= Khách mua trong kỳ ÷ Khách đã tư vấn. N/A khi mẫu số = 0"✅ Luôn hiện
Doanh thu ròngnet_revenue (Ref FORMULA-003 + DEC-032)125.000.000đ"≠ tiền thực thu""Doanh thu ghi nhận kế toán = Tổng giá đơn − Chiết khấu − Hoàn trả. ⚠️ Phần khách còn nợ CHƯA được trừ. Tiền thực thu → KPI 'Đã thu trong kỳ' (Ref DEC-022 + DEC-032)"✅ Luôn hiện (Sale: × % HH; Manager/BOD: raw branch/system — Ref DEC-024)
Khách đang nợdebt_customer_count_distinct67 khách"Khách còn dư nợ > 0đ tại snapshot date_to"→ tab Công nợ✅ Luôn hiện
Khách nợ quy đổidebt_customer_equivalent18.5%"Theo % HH của bạn""Số khách nợ quy đổi theo tỷ lệ tham gia/hoa hồng"→ tab Công nợ✅ Luôn hiện
Tiền nợ quy đổiallocated_debt_amount18.500.000đ"Phần tiền nợ quy đổi theo tỷ lệ tham gia/hoa hồng"→ tab Công nợ✅ Luôn hiện
Tổng tiền nợtotal_debt_amount45.200.000đ"23 khách còn nợ""Tổng outstanding tại snapshot date_to"→ tab Công nợ✅ Luôn hiện
Tỷ lệ thu nợdebt_collection_rate77.5%"Tỷ lệ đã thu trên tổng phải thu trong kỳ"✅ Luôn hiện
TB ngày thu nợavg_recovery_days_weighted18.3 ngày"Càng thấp càng tốt""Số ngày thu nợ trung bình có trọng số theo số tiền"✅ Luôn hiện
Đã thu trong kỳcollected_amount_in_period62.000.000đ"Tổng tiền đã thu trong kỳ lọc"✅ Luôn hiện
Tỷ lệ nợ/doanh thu (headline)debt_ratio36.2%(+8.00%)“= Tổng nợ ÷ Doanh thu ròng”✅ Luôn hiện

Sub-label rules:

  • Font: 12px, color #757575, italic
  • Hiển thị trực tiếp dưới giá trị chính, không cần hover
  • Khi giá trị = N/A → sub-label đổi thành "Chưa đủ dữ liệu"
  • Sub-label dùng dữ liệu thực (e.g. "45 khách mua / 67 khách" thay đổi theo filter)

Trend indicator: ↗ (xanh) / ↘ (đỏ) so với kỳ trước cùng độ dài. Chỉ hiển thị khi có đủ data kỳ trước.

Hiển thị KPI Block B: luôn hiển thị full bộ KPI trên web và mobile để đồng bộ nhận thức giữa 2 nền tảng, không dùng toggle ẩn/hiện.

Spec Priority Card Strip — "Ưu tiên xử lý nợ quá hạn" (Ref DEC-033)

Quy tắc as-of TODAY (Ref DEC-033 LOCKED): Banner LUÔN tính số khách + tiền nợ as-of NOW(), KHÔNG phụ thuộc filter date của dashboard. Đây là CTA hành động hôm nay, KHÔNG phải report kỳ. Scope branch + Nhân sự của filter VẪN áp dụng (Manager xem branches mình quản lý; Sale xem của mình).

Thành phầnMô tả
Vị tríNgay dưới top filter, phía trên Block B/C
Data scope (Ref DEC-033)As-of NOW() — KHÔNG dùng date_to filter. COUNT khách + SUM tiền nợ tính theo snapshot HIỆN TẠI. Filter branch + Nhân sự của dashboard VẪN áp dụng (không phải global)
Hiển thị khiCó ≥1 khách nợ quá hạn >= threshold_days AS-OF NOW
Header"Ưu tiên xử lý nợ quá hạn ({count})" + summary tổng nợ + count theo bucket
Sub-label cảnh báo (Ref DEC-033)"⚡ Dữ liệu hiện tại — không phụ thuộc bộ lọc thời gian" (font 11px, italic, color #FF8800) — luôn hiện để phân biệt với KPI cards
Visual cueBackground cam nhạt (#FFF4E5) + border-left 4px cam (#FF8800) + icon ⚠️ — phân biệt với KPI cards trung tính
Nội dung chínhDanh sách card ngang khách ưu tiên (desktop) / card dọc (mobile)
Card itemTên, SĐT, badge Nhóm nợ, Tổng nợ, Quá hạn, % tham gia (weighted Ref DEC-029), CTA [Gọi] [Tạo lịch]
Hành vi click [Xử lý ngay →]Mở SCR-03 (tab Công nợ) với filter date RESET về TODAY (date_from = date_to = NOW()). Filter branch + Nhân sự giữ nguyên
Hành vi click card hoặc CTA [Gọi]/[Tạo lịch]Mở flow xử lý nợ tại chỗ (không chuyển màn)
Empty stateẨn banner hoàn toàn nếu count = 0 AS-OF NOW
i18n keysdebt.priority_strip.title: "Ưu tiên xử lý nợ quá hạn" · debt.priority_strip.sublabel_realtime: "⚡ Dữ liệu hiện tại — không phụ thuộc bộ lọc thời gian"

Logic edge cases:

  • User filter "tháng 1/2024" (quá khứ) → Banner vẫn hiển thị "5 khách nợ HÔM NAY" với sub-label cảnh báo. Click [Xử lý ngay] → reset filter date về TODAY → mở SCR-03
  • Manager filter Nhân sự = staff X → Banner đếm khách nợ AS-OF NOW của staff X
  • Không có khách nợ overdue >= threshold AS-OF NOW → Banner ẨN hoàn toàn (không hiển thị empty state)

Spec Biểu đồ phân loại nhóm nợ (Block C)

Thuộc tínhMô tả
Loại biểu đồHorizontal stacked bar chart (1 thanh ngang, 4 segments)
Vị tríBlock C — toàn chiều ngang, giữa Block A+B (KPI) và Block D (bảng)
Hiển thị trên tabChỉ trên tab “💰 Công nợ”. Ẩn trên tab “📊 Hiệu suất tư vấn”
Dữ liệu nguồnAggregate từ getDebtOverdueList → group by aging bucket → count + sum

4-Color Mapping:

BucketMàuHexÝ nghĩa
0–29 ngày🟢 Xanh#4CAF50Nợ mới, chưa quá hạn nghiêm trọng
30–59 ngày🟡 Vàng#FFC107Cần theo dõi
60–89 ngày🟠 Cam#FF9800Rủi ro cao
≥90 ngày🔴 Đỏ#F44336Nghiêm trọng, ưu tiên xử lý

Mỗi segment hiển thị:

  • Số khách (VD: 12 khách)
  • Phần trăm (VD: 52%)
  • Tổng tiền nợ (VD: 27.800.000đ)

Behavior:

Hành độngMô tả
Click segmentApply local filter Nhóm nợ = bucket đã click. Ảnh hưởng Summary Banner, Daily Focus, Block D và export debt. VD: click 🔴 → working set chỉ còn nhóm ≥90d
Click segment đang activeBỏ local filter Nhóm nợ → working set quay về Tất cả
Hover segment (web)Tooltip: "{bucket_label}: {count} khách · {amount}đ · {percent}%"
Cursorpointer trên mỗi segment
Active stateSegment được chọn có border 2px solid #000. Các segment khác opacity 0.4. Chart vẫn giữ full distribution của scope hiện tại, không recalc theo local filter Nhóm nợ
AnimationBar segments animate từ trái sang phải, stagger 100ms mỗi segment
Empty stateKhi tất cả bucket = 0: Ẩn chart, hiện text "Không có khách nợ quá hạn 🎉"
Segment = 0Bucket không có khách → không render segment (không hiện thanh rỗng)
ResponsiveMin-width segment: 40px. Nếu segment quá nhỏ → hiện tooltip thay vì label inline
i18n keysdebt.aging.bucket_0_29: “0–29 ngày”
debt.aging.bucket_30_59: “30–59 ngày”
debt.aging.bucket_60_89: “60–89 ngày”
debt.aging.bucket_90_plus: “≥90 ngày”
debt.aging.empty: “Không có khách nợ quá hạn”
debt.aging.tooltip: “{label}: {count} khách · {amount} · {percent}%”

Mapping bảng danh sách theo tab

Theo IA mới, không còn 1 bảng danh sách dùng chung cho cả 2 tab. Mỗi tab dùng 1 bảng riêng với mục tiêu và cột hiển thị khác nhau.

TabBảng dùngCột hiển thị chính
Hiệu suất tư vấnSCR-02Khách / Dịch vụ / % Của tôi / Ngày TV / Trạng thái
Công nợSCR-03Khách / Liên hệ gần nhất / Phụ trách chính / Tiền nợ / % tham gia / Nhóm nợ / Quá hạn / Hành động

Nguyên tắc:

  • Tab Hiệu suất tư vấn tập trung vào conversion sau tư vấn, không hiển thị cột nợ.
  • Tab Công nợ tập trung vào follow-up thu nợ, không hiển thị cột dịch vụ tư vấn hay trạng thái mua trong kỳ.
  • Nếu cần xem chi tiết từng bảng, dùng spec riêng ở SCR-02SCR-03 bên dưới.

SCR-02 — Chi tiết danh sách tư vấn

Layout

┌──────────────────────────────────────────────────────────┐
│ ← Quay lại Dashboard                                     │
│ "Danh sách tư vấn — Trần Văn Sale — Tháng 2/2026"       │
├──────────────────────────────────────────────────────────┤
│ [Trạng thái: Tất cả ▼ | Đã mua trong kỳ | Chưa mua trong kỳ] │
│ Toggle: [Theo lượt tư vấn ●] [Theo khách đã tư vấn]     │
├──────────────────────────────────────────────────────────┤
│ Hiển thị: 1–50 / 128                      [Xuất XLSX]   │
│ Khách    │ Dịch vụ   │ % Của tôi  │ Ngày TV  │ Trạng thái│
│ A        │ Cắt tóc   │ 100%       │ 20/02    │ ✅ Đã mua trong kỳ │
│ A        │ Nhuộm     │ 50% ①      │ 20/02    │ ✅ Đã mua trong kỳ │
│ B        │ Spa       │ 100%       │ 21/02    │ Chưa mua trong kỳ │
└──────────────────────────────────────────────────────────┘
① Tooltip: "Nhuộm: Sale A (50%) | Sale B (50%) — Bạn là Sale A"

Toggle “Theo lượt / Theo khách đã tư vấn”:

  • Theo lượt (default): Hiển thị theo grain lượt tư vấn hiện tại (customer_id, branch_id, date)consultant
  • Theo khách đã tư vấn: Gộp theo customer_id, hiển thị customer summary row neo theo lần tư vấn gần nhất

Spec Quy tắc gộp dòng (SCR-02)

Chế độGrain dòngQuy tắc hiển thị cột
Theo lượt tư vấn1 row = 1 lượt theo grain (customer_id, branch_id, date) có source consultantKhách: có thể lặp lại theo ngày/chi nhánh. Dịch vụ, % Của tôi, Ngày TV lấy theo bản ghi representative trong ngày. Trạng thái tính theo khách trong kỳ lọc: nếu khách có ≥1 giao dịch thỏa điều kiện mua trong kỳ thì mọi row của khách đó hiện Đã mua trong kỳ
Theo khách đã tư vấn1 row = 1 customer_id trong kỳ lọcĐây là customer summary row. Dịch vụ, % Của tôi, Ngày TV phản ánh touchpoint tư vấn gần nhất của khách trong kỳ để user biết lần follow-up cuối. Trạng thái phản ánh kết quả mua của cả khách trong kỳ lọc, không chỉ riêng lượt gần nhất

Tie-break cho representative row (Theo khách đã tư vấn) — Ref DEC-027 + FORMULA-007:

  • Sort canonical: consultation_date DESC, order_service_id DESC LIMIT 1
  • Nếu vẫn bằng nhau → consultation_id ASC để deterministic

Cột "Dịch vụ" — quy tắc multi-DV (Ref FORMULA-007):

  • 1 DV duy nhất trong lượt/kỳ: hiển thị tên DV (vd "Tẩy nốt ruồi")
  • ≥ 2 DV cùng lượt/kỳ: hiển thị "{tên DV representative} (+{N-1} DV khác)" — vd "Tẩy nốt ruồi (+2 DV khác)"
  • Tooltip khi hover: liệt kê đầy đủ tất cả DV theo format • {tên DV} — {% Của tôi}
  • Edge case: DV name NULL → hiển thị "Dịch vụ #{order_service_id}"; không có DV nào → "—"

Ghi chú UX: Trong mode Theo khách đã tư vấn, row không đại diện cho một giao dịch duy nhất mà là tóm tắt 1 khách trong kỳ. Vì vậy cột Trạng thái phải được hiểu là "khách này có mua trong kỳ hay không", còn Dịch vụ / % Của tôi / Ngày TV là dấu vết của lần tư vấn gần nhất.

Spec Filter Bar (SCR-02)

ComponentTypeDefaultBehavior
Trạng tháiButton groupTất cảTất cả / Đã mua trong kỳ / Chưa mua trong kỳ. Click → filter bảng
Toggle viewSwitchTheo lượt tư vấnChuyển đổi chế độ hiển thị. Không reset filter Trạng thái

Spec Bảng danh sách tư vấn (SCR-02)

CộtSortableFormatGhi chú
KháchText + link → CRM
Dịch vụText với multi-DV format (Ref FORMULA-007)Format: 1 DV → tên DV; ≥2 DV → "{tên rep} (+N-1 DV khác)" với tooltip liệt kê đầy đủ. Tie-break: consultation_date DESC, order_service_id DESC
Cuộc gọi (Ref DEC-028 LOCKED)"Gần nhất: HH:mm DD/MM/YYYY" + icon phone xanhSource: ecommerce_user.last_contacted_at (denormalized field, được app mobile cập nhật khi Sale gọi qua click-to-call). Scope: all-time, không filter theo dashboard period. Empty: "Chưa gọi" nếu last_contacted_at IS NULL. Tooltip: "Cuộc gọi gần nhất từ app Diva Staff cho khách này". Lưu ý format: dùng DD/MM/YYYY (uppercase MM = tháng), KHÔNG dùng DD/mm/YYYY (lowercase mm = phút) — Ref BUG-PROD-001
% tham gia (Ref DEC-029 + FORMULA-008)N.NN% + icon ⓘWeighted average theo order.total khi KH có nhiều đơn cùng row tư vấn. Công thức: SUM(pct × order.total) / SUM(order.total). Tooltip drill-down phải liệt kê chi tiết per-order với format đầy đủ (xem FORMULA-008 ví dụ). Single order: = % của đơn đó (no aggregation). Edge cases: denominator = 0 → "—". Ref DEC-024: ẨN cột này trong view Manager/BOD (không có % HH)
Ngày TVDD/MM/YYYYNgày tư vấn
Trạng thái✅ Đã mua trong kỳ / Chưa mua trong kỳ

Pagination: 50 rows/trang. Format: Hiển thị: 1–50 / {total}. Default sort: ngày TV DESC (tư vấn gần nhất lên đầu).


SCR-03 — Nợ quá hạn cần xử lý

Layout

┌────────────────────────────────────────────────────────────────┐
│ "Nợ quá hạn cần xử lý"                                        │
│ ℹ️ Dữ liệu cập nhật lúc 07:00 hôm nay, 24/02/2026            │
├────────────────────────────────────────────────────────────────┤
│ SCOPE CHIP: [☑ Sale] [☑ Telesale] [☑ CSKH]                  │
├────────────────────────────────────────────────────────────────┤
│ SUMMARY BANNER (màu theo nhóm nợ cao nhất hiện có)             │
│ 🔴 Bạn có 5 khách nợ ≥90 ngày — ưu tiên xử lý ngay             │
│ Tổng: 23 khách | 85.000.000đ nợ quá hạn  [Lọc nhóm ≥90 →]      │
├────────────────────────────────────────────────────────────────┤
│ 🎯 DAILY FOCUS — Top 5 khách ưu tiên hôm nay        [Ẩn ▴]   │
│ ┌─────────────────────────────────────────────────────────────┐│
│ │ 1. Nguyễn A · 6,500k · 95d 🔴   [📞] Chưa LH 15 ngày     ││
│ │ 2. Lê C     · 4,800k · 72d 🟠   [📞] Nhắc hôm nay 10:00  ││
│ │ 3. Phạm D   · 4,200k · 68d 🟠   [📞] Chưa LH 8 ngày      ││
│ │ 4. Trần B   · 3,200k · 45d 🟡   [📞] Chưa LH 20 ngày     ││
│ │ 5. Hoàng E  · 2,100k · 35d 🟡   [📞] Lịch nhắc quá hạn   ││
│ └─────────────────────────────────────────────────────────────┘│
│           [Xem toàn bộ danh sách ↓]                            │
├────────────────────────────────────────────────────────────────┤
│ [Nhóm nợ: Tất cả ▼] [Chi nhánh ▼] [Owner ▼] [🔍 Tìm khách...] │
├────────────────────────────────────────────────────────────────┤
│ [📅 Lịch nhắc (2) 🔴]                                    [Xuất] │
│ TOOLBAR (hiện khi chọn ≥1 row)                                 │
│ ☑ 3 đã chọn  [✓ Đánh dấu LH]│
├──┬──────────┬──────────┬────────┬────────┬────────┬────────┬───────────┤
│☐ │ Khách    │ Liên hệ gần nhất │ Owner chính │ Tiền │ % tham │ Nhóm nợ │ Quá hạn │ Hành động │
│  │          │ chính    │ trách  │ nợ     │ nợ     │ hạn    │           │
├──┼──────────┼──────────┼────────┼────────┼────────┼────────┼───────────┤
│☐ │▶Nguyễn A │ Đã gọi: 2 lần     │ Nguyễn B    │6,500k│ 30%   │≥90 🔴    │95 ngày  │[📞 Gọi] [⋯]│
│  │ 3DV·2đơn │ Gần nhất 09:00... │             │      │       │          │         │            │
│  ├──────────┴──────────┴────────┴────────┴────────┴────────┤           │
│  │ (expand) CHI TIẾT NỢ:                         │           │
│  │ 📋 ĐH-001 · 15/01  Cắt tóc 2,000k  47d 🟡    │           │
│  │                     Nhuộm   3,000k  47d 🟡    │           │
│  │ 📋 ĐH-002 · 20/02  Massage 1,500k   5d 🟢    │           │
│  ├──────────┬──────────┬────────┬────────┬────────┬────────┤           │
├──┼──────────┼──────────┼────────┼────────┼────────┼────────┼───────────┤
│☐ │▶Trần B   │ Chưa gọi lần nào  │ Nguyễn B    │3,200k│ 50%   │30-59🟡   │45 ngày  │[📞 Gọi] [⋯]│
│  │ 1DV·1đơn │                  │             │      │       │          │         │            │
└──┴──────────┴──────────┴────────┴────────┴────────┴────────┴───────────┘
│ [< 1 2 3 >]                                   25/trang ▼      │
└────────────────────────────────────────────────────────────────┘

Spec Sắp xếp mặc định bảng

Thành phầnMô tả
Default sortoverdue_days DESC — khách nợ lâu nhất lên đầu
Lý doNgười dùng cần xử lý khách rủi ro cao nhất trước, giảm thời gian tìm kiếm
Secondary sortoutstanding_amount DESC (khi overdue_days bằng nhau)
User overrideBấm header cột sortable → đổi sort. Reset khi chuyển tab/filter

Spec Filter Bar (SCR-03)

ComponentTypeDefaultBehavior
Nhóm nợSingle select dropdownTất cảOptions: Tất cả / 0–29 / 30–59 / 60–89 / ≥90. Click → áp dụng working-set filter cho Summary Banner, Daily Focus, Block D và export debt. Block C không recalc, chỉ highlight nhóm đang active
Chi nhánhSingle select dropdownTất cả (theo quyền)Staff: ẩn (chỉ xem data mình). Manager: branch scope. Admin: tất cả
OwnerSingle select dropdownTất cảCascade từ Chi nhánh. Staff: ẩn. Manager/Admin: chọn nhân viên trong branch. Nếu top filter Nhân sự đã chọn 1 người cụ thể thì local Owner tự sync theo người đó và chuyển readonly
Tìm kháchText inputSearch by customer_name. Debounce 300ms. Min 2 ký tự

Reset: Đổi scope chip vai trò (Sale/Telesale/CSKH) không reset Nhóm nợ; chỉ recalc dữ liệu theo scope mới. Nếu top filter Nhân sự đang khóa local Owner, local filter vẫn giữ readonly.

Spec Bảng nợ quá hạn (SCR-03)

CộtSortableFormatGhi chú
Khách▶ Tên + N DV·M đơn dướiClick ▶/tên → expand chi tiết nợ theo đơn
Liên hệ gần nhấtĐã gọi N lần + Gần nhất: HH:mm DD/MM/YYYYCột riêng cho lịch sử gọi/liên hệ. Nếu chưa có lịch sử gọi: hiển thị Chưa gọi lần nào
Phụ trách chínhTextTên owner chính. “Tôi” nếu là current user
Tiền nợ#,### đSUM outstanding tất cả DV nợ
% tham giaN%Hiển thị theo weighted average theo outstanding_amount
Nhóm nợBadge màuAging bucket cao nhất (customer-level rollup — Ref FORMULA-006B + DEC-023). Lấy max(overdue_days) trong tất cả ORDER nợ của khách (KHÔNG order-level). Vd: KH có đơn 15d + 95d → bucket = very_high_risk. Biểu đồ phân loại + bảng count đều dùng customer-level (1 KH = 1 unit ở bucket cao nhất)
Quá hạnN ngàySố ngày quá hạn cao nhất trong tất cả DV nợ
Hành động[👁 Xem] [📅 Tạo lịch]Có thể bổ sung icon gọi theo cấu hình role

Pagination: 25 rows/trang. Dropdown chọn: 25 / 50 / 100.

Spec Quy tắc gộp dòng (SCR-03)

Chủ đềQuy tắc
Grain dòng1 row = 1 customer_id trong scope filter hiện tại
Scope dữ liệuInclude khách có ≥1 dòng nợ outstanding thỏa top filter (thời gian/chi nhánh/vai trò/nhân sự) và local filter (nhóm nợ/owner/tìm kiếm)
Tiền nợTổng outstanding của các dòng nợ nằm trong scope hiện tại của row
Owner chínhdebt_owner_id theo rule owner của từng dòng nợ; ở row tổng hợp hiển thị owner có outstanding lớn nhất trong row scope
% tham giaWeighted average theo outstanding_amount trong row scope, làm tròn 1 chữ số thập phân; tooltip liệt kê chi tiết từng dòng DV
Liên hệ gần nhấtLấy từ debt_contact_log: call_count = tổng số lần gọi/liên hệ đã ghi nhận cho khách trong scope hiện tại; last_call_time = contacted_at mới nhất. Nếu chưa có log → Chưa gọi lần nào
Nhóm nợLấy nhóm nợ cao nhất của các dòng nợ trong row scope = max(overdue_days)
Quá hạnSố ngày quá hạn cao nhất của các dòng nợ trong row scope
Expand rowChỉ hiển thị các đơn/dịch vụ nợ thuộc row scope hiện tại, không hiển thị dòng nợ ngoài scope

Ví dụ: cùng 1 khách có 3 đơn nợ với 2 owner khác nhau. Row vẫn gộp theo khách (1 row/customer), nhưng tooltip % tham gia và phần expand sẽ thể hiện đầy đủ phân bổ theo từng đơn/dịch vụ và owner.

Spec Toolbar Batch Actions (SCR-03)

Thuộc tínhMô tả
Hiện khiChọn ≥1 row qua checkbox
Vị tríSticky bar giữa filter bar và bảng
Nội dung☑ {N} đã chọn [✓ Đánh dấu LH]
Action “Đánh dấu LH”Batch mark tất cả row đã chọn là “Đã liên hệ”. Gọi markCustomerContacted × N lần (parallel, max 10 concurrent)
ConfirmKhông cần confirm (action nhẹ, có thể undo từng row)
FeedbackToast: "Đã đánh dấu liên hệ {N} khách". Mỗi row hiện icon ✅
UndoKhông có batch undo. User mở overflow menu [⋯] → “Hủy đánh dấu LH” từng row
Nút [📅 Lịch nhắc (N) 🔴]Luôn hiện (không thuộc toolbar). Click → mở SCR-03-DRAWER. Badge count = số lịch nhắc pending. 🔴 khi có ≥1 quá hạn
i18n keysdebt.toolbar.selected: “ đã chọn”
debt.toolbar.mark_contacted: “Đánh dấu LH”
debt.toolbar.toast_contacted: “Đã đánh dấu liên hệ khách”

Spec Expandable Row — Chi tiết nợ theo đơn hàng

Thành phầnMô tả
TriggerClick ▶ icon hoặc tên khách để expand/collapse
Mặc địnhCollapsed — chỉ hiện dòng tổng hợp: tổng nợ + số DV + số đơn + aging cao nhất
Nội dung expandNhóm theo đơn hàng (order_code + ngày đơn), mỗi đơn liệt kê DV nợ bên dưới
Mỗi dòng DVTên dịch vụ + số tiền nợ + số ngày quá hạn + badge aging (tính riêng từ debt_due_date của order_service)
Sắp xếp đơnĐơn cũ nhất trước (ngày đơn ASC)
Sắp xếp DVTiền nợ DESC trong mỗi đơn
Dòng tổng hợp (collapsed)Hiện: N DV · M đơn bên dưới tên khách
Cột "Nhóm nợ"Customer-level aging bucket — max(overdue_days) trong tất cả ORDER nợ của khách (Ref FORMULA-006B + DEC-023). Tránh double-count nếu KH có nhiều đơn ở nhiều bucket — chỉ gán 1 KH vào bucket cao nhất
Cột "Quá hạn"Hiển thị số ngày quá hạn cao nhất = max(overdue_days) trong tất cả order nợ của khách (consistent với cột Nhóm nợ)
Cột “Tiền nợ”Tổng tất cả dòng DV nợ (= SUM outstanding per order_service)

Spec CTA Buttons

Nguyên tắc: Chỉ hiện 1 CTA chính ([📞 Gọi]) trực tiếp trên dòng. Các action còn lại gom vào overflow menu [⋯] để giảm noise thị giác. Lý do: 25 dòng × 4 nút = 100+ nút trên 1 trang gây “decision paralysis”.

NútVị tríHành độngBehavior
📞 GọiHiện trực tiếp trên dòngMở tính năng gọi trên diva partnerCTA chính. Nợ ≥90d → nút màu #B71C1C (đỏ). Nợ < 90d → màu neutral
⋯ MenuHiện trực tiếp bên cạnh nút GọiMở dropdown menuClick → hiện dropdown 3 mục bên dưới
├ 📅 Tạo lịch nhắcTrong menu ⋯Mở popup tạo lịch nhắcXem spec popup
├ ✓ Đánh dấu Đã LHTrong menu ⋯Toggle trạng thái liên hệLưu timestamp + user_id. Icon ✅ khi đã LH
└ → Xem CRMTrong menu ⋯Mở CRM chi tiết kháchMở tab mới

Overflow menu [⋯] behavior:

  • Click → dropdown hiện 3 items, đóng khi click ngoài
  • Mỗi item có icon + label tiếng Việt
  • Nếu khách đã đánh dấu LH → item “Đánh dấu Đã LH” đổi thành “Hủy đánh dấu LH”

Spec Daily Focus — Top 5 khách ưu tiên

Thuộc tínhMô tả
Vị tríSCR-03, giữa Summary Banner và Filter Bar
Hiển thị choStaff (dữ liệu cá nhân), Manager/Admin theo scope filter hiện tại
Số lượngTop 5 khách. Không phân trang, không “load more”
Thuật toán sắp xếpƯu tiên phức hợp: (1) Có lịch nhắc quá hạn, (2) Nợ ≥90 ngày chưa LH, (3) overdue_days DESC, (4) outstanding_amount DESC
Mỗi dòng hiển thị#. Tên khách · tiền nợ · số ngày quá hạn + badge · [📞] · context hint
Context hintLogic: Lịch nhắc quá hạn > Nhắc hôm nay HH:MM > “Chưa LH N ngày” > “LH lần cuối DD/MM”
CTA[📞] — gọi trực tiếp. Click tên khách → scroll xuống dòng tương ứng trong bảng chính
Toggle ẩn/hiện[Ẩn ▴] / [Hiện ▾] — persist localStorage key debt_daily_focus_visible
DefaultHiện khi có ≥1 khách nợ ≥30 ngày. Ẩn nếu tất cả nợ < 30 ngày
Link cuối[Xem toàn bộ danh sách ↓] → smooth scroll đến bảng chính (Block D)
RefreshCập nhật khi thay đổi filter. Luôn lấy từ dữ liệu đã filter
i18n keysdebt.daily_focus.title: “Top 5 khách ưu tiên hôm nay”
debt.daily_focus.no_contact: “Chưa LH ngày”
debt.daily_focus.reminder_today: “Nhắc hôm nay ”
debt.daily_focus.reminder_overdue: “Lịch nhắc quá hạn”
debt.daily_focus.view_all: “Xem toàn bộ danh sách”

Spec Summary Banner (SCR-03)

Thuộc tínhMô tả
Vị tríSCR-03, ngay dưới scope chip vai trò, trên Daily Focus (nếu có) và Filter Bar
Mục đíchCảnh báo nhanh mức độ nghiêm trọng nợ — 1 dòng glanceable
Hiển thị khiCó ≥1 khách nợ quá hạn (overdue_days > 0) trong tab đang active
Ẩn khiTất cả khách nợ có overdue_days = 0 (chưa quá hạn)

Color logic (background + icon theo nhóm nợ cao nhất hiện có):

Nhóm nợ cao nhấtBackgroundBorder-leftIconText mẫu
≥90 ngày#FFEBEE (đỏ nhạt)4px #B71C1C🔴“Bạn có 5 khách nợ ≥90 ngày — ưu tiên xử lý ngay”
60–89 ngày#FFF3E0 (cam nhạt)4px #E65100🟠“Bạn có 3 khách nợ 60-89 ngày — cần theo dõi”
30–59 ngày#FFF8E1 (vàng nhạt)4px #F57F17🟡“Bạn có 6 khách nợ 30-59 ngày”
0–29 ngày#E8F5E9 (xanh nhạt)4px #2E7D32🟢“Tất cả khách nợ trong vùng an toàn”

Dynamic text template:

"{icon} Bạn có {{count_highest}} khách nợ {{bucket_label}} — {{urgency_text}}"
"Tổng: {{total_count}} khách | {{total_amount}}đ nợ quá hạn  [CTA]"

Behavior:

Hành độngMô tả
CTA [Lọc nhóm ≥90 →]Click → apply local filter Nhóm nợ = ≥90 ngày cho Summary Banner, Daily Focus, Block D và export debt. Label CTA dynamic theo nhóm nợ cao nhất
CTA khi nhóm nợ max < 90[Lọc nhóm {bucket_label} →] — VD: [Lọc nhóm 60-89 ngày →]
Đổi scope chipBanner cập nhật theo tổ hợp scope vai trò mới (Sale/Telesale/CSKH)
Đổi filterBanner recalculate theo working set đang active
TransitionFade-in 200ms khi xuất hiện, slide-up 150ms khi ẩn
Manager xemHiện aggregate tất cả staff trong branch (không chỉ của mình)
i18n keysdebt.banner.critical: “Bạn có khách nợ ≥90 ngày — ưu tiên xử lý ngay”
debt.banner.warning: “Bạn có khách nợ — cần theo dõi”
debt.banner.info: “Bạn có khách nợ ”
debt.banner.safe: “Tất cả khách nợ trong vùng an toàn”
debt.banner.total: “Tổng: khách | nợ quá hạn”
debt.banner.filter_cta: “Lọc nhóm ”
┌───────────────────────────────────────────┐
│ 📅 Tạo lịch nhắc — Nguyễn Văn A          │
│ Tổng nợ: 6.500.000đ | 3 DV · 2 đơn       │
│ ĐH-001: Cắt tóc 2,000k 47d 🟡            │
│         Nhuộm   3,000k 47d 🟡             │
│ ĐH-002: Massage 1,500k  5d 🟢            │
├───────────────────────────────────────────┤
│ Ngày nhắc *  [DD/MM/YYYY]  [HH:MM]        │
│ Nội dung *   [Gọi nhắc thanh toán...]     │
│ Ghi chú      [Thêm ghi chú...]            │
│ Gán cho      [Tôi ▼] (Manager có thể gán) │
├───────────────────────────────────────────┤
│               [Hủy]    [Tạo lịch]         │
└───────────────────────────────────────────┘

Lưu ý: Phần chi tiết nợ trong popup chỉ hiện tối đa 3 đơn gần nhất. Nếu > 3 đơn → hiện dòng “+N đơn khác” dạng link. Không collapse — giữ popup nhỏ gọn.

SCR-03-DRAWER — Lịch nhắc chăm sóc của tôi (Overlay Drawer)

Trigger: Nút [📅 Lịch nhắc (N)] trên toolbar SCR-03 hoặc nút [📅] bên cạnh từng khách. Mở từ khách cụ thể: Drawer lọc sẵn theo customer_id đó. Mở từ toolbar: Hiển thị tất cả lịch nhắc của user.

Layout mode: Overlay (không push)

  • Drawer phủ lên bảng SCR-03, không đẩy bảng sang trái
  • Background SCR-03 bị mờ (backdrop rgba(0,0,0,0.4))
  • Click vùng mờ hoặc nút [✕] → đóng drawer, bảng trở lại bình thường
  • Lý do: Tránh bảng 8 cột bị co lại khi drawer mở. User focus 1 việc (quản lý lịch nhắc), xong đóng drawer quay lại bảng nguyên vẹn.
┌─── SCR-03 (nền mờ, backdrop) ─────────┬─────────────────────────────────────┐
│                                       │ ← Lịch nhắc của tôi          [✕]  │
│  (Danh sách nợ quá hạn               │─────────────────────────────────────│
│   bị mờ — click để đóng drawer)      │ [Tất cả ▼] [Chờ] [Quá hạn] [Xong]  │
│                                       │ [🔍 Tìm tên khách...]              │
│                                       │─────────────────────────────────────│
│                                       │ HÔM NAY (2 nhắc)                   │
│                                       │ ┌─────────────────────────────────┐ │
│                                       │ │ 🔴 QUÁ HẠN                    │ │
│                                       │ │ Nguyễn Văn A · 6.500.000đ     │ │
│                                       │ │ 3DV · 2đơn · cao nhất 95d 🔴   │ │
│                                       │ │ 📅 09:00 — Gọi nhắc thanh toán│ │
│                                       │ │ Ghi chú: Hẹn trả cuối tháng   │ │
│                                       │ │ [✅ Đã xong] [❌ Hủy] [📞 Gọi]│ │
│                                       │ └─────────────────────────────────┘ │
│                                       │ ┌─────────────────────────────────┐ │
│                                       │ │ 🟡 CHỜ NHẮC                   │ │
│                                       │ │ Trần Thị B · 3.200.000đ       │ │
│                                       │ │ 1DV · 1đơn · 45d 🟡            │ │
│                                       │ │ 📅 14:00 — Nhắc trả tiền DV   │ │
│                                       │ │ [✅ Đã xong] [❌ Hủy] [📞 Gọi]│ │
│                                       │ └─────────────────────────────────┘ │
│                                       │─────────────────────────────────────│
│                                       │ NGÀY MAI (1 nhắc)                   │
│                                       │ ┌─────────────────────────────────┐ │
│                                       │ │ 🟢 CHỜ NHẮC                   │ │
│                                       │ │ Lê Văn C · 1.500.000đ         │ │
│                                       │ │ 1DV · 1đơn · 15d 🟢            │ │
│                                       │ │ 📅 03/03 10:00 — Gọi hỏi thăm│ │
│                                       │ │ [✅ Đã xong] [❌ Hủy] [📞 Gọi]│ │
│                                       │ └─────────────────────────────────┘ │
│                                       │─────────────────────────────────────│
│                                       │ ĐÃ HOÀN THÀNH (collapse)           │
│                                       │ ▶ 5 lịch nhắc đã xong trong 7 ngày│
│                                       │─────────────────────────────────────│
│                                       │ [+ Tạo lịch nhắc mới]              │
│                                       └─────────────────────────────────────┘
└───────────────────────────────────────┘

Khi mở từ 1 khách cụ thể (Nguyễn Văn A):

┌─────────────────────────────────────┐
│ ← Lịch nhắc — Nguyễn Văn A   [✕]  │
│ Tổng nợ: 6.500.000đ · cao nhất 95d │
│─────────────────────────────────────│
│ DỊCH VỤ NỢ (3 DV · 2 đơn)         │
│                                     │
│ 📋 ĐH-001 · 15/01/2026             │
│ ├ Cắt tóc    2.000.000đ  47d 🟡    │
│ └ Nhuộm      3.000.000đ  47d 🟡    │
│                                     │
│ 📋 ĐH-002 · 20/02/2026             │
│ └ Massage    1.500.000đ   5d 🟢    │
│                                     │
│ Tổng: 6.500.000đ                   │
│─────────────────────────────────────│
│ LỊCH SỬ FOLLOW-UP (3 lần nhắc)     │
│                                     │
│ ● 02/03 09:00 — Gọi nhắc TT       │
│   Trạng thái: 🔴 Quá hạn           │
│   Ghi chú: Hẹn trả cuối tháng      │
│   [✅ Đã xong] [❌ Hủy]            │
│                                     │
│ ● 25/02 10:00 — Nhắc qua Zalo      │
│   Trạng thái: ✅ Đã xong            │
│   Hoàn thành: 25/02 10:32 bởi Tôi  │
│                                     │
│ ● 20/02 09:00 — Gọi nhắc lần 1     │
│   Trạng thái: ✅ Đã xong            │
│   Hoàn thành: 20/02 09:15 bởi Tôi  │
│                                     │
│─────────────────────────────────────│
│ [+ Tạo lịch nhắc mới cho khách này]│
└─────────────────────────────────────┘

Spec Drawer Lịch Nhắc

Thành phầnMô tả
Width420px (fixed right drawer)
Nhóm theoNgày (Hôm nay / Ngày mai / DD/MM/YYYY)
Sắp xếpremind_at ASC trong mỗi nhóm ngày
Badge trạng thái🔴 Quá hạn (pending + remind_at < now) · 🟡 Chờ nhắc (pending + remind_at ≥ now) · 🟢 Chờ nhắc (xa, > 3 ngày) · ✅ Đã xong · ❌ Đã hủy
Section “Đã hoàn thành”Collapse mặc định, chỉ hiện 7 ngày gần nhất
Mở từ toolbarHiện tất cả lịch nhắc của user (assigned_to = me)
Mở từ nút kháchFilter theo customer_id, hiện timeline follow-up
Nút “Đã xong”Chuyển status → done, ghi completed_at = now()
Nút “Hủy”Confirm dialog → chuyển status → cancelled
Counter trên nút toolbarBadge đỏ = số lịch nhắc quá hạn (pending + overdue)

Spec Scope Chip Vai trò (SCR-03)

Tiêu chíQuy tắc
Điều kiện scopeSale: include dòng nợ có role Sale; Telesale: include dòng nợ có role Telesale; CSKH: include dòng nợ có role CSKH
Kết hợp nhiều chipOR logic giữa các chip đang bật
Grain danh sách1 row = 1 customer_id trong scope filter hiện tại
Cột “Owner chính”Hiển thị owner của dòng nợ có outstanding lớn nhất trong row scope
Cột “% tham gia”Weighted average participation theo outstanding_amount trong row scope
Cột “Liên hệ gần nhất”Đã gọi N lần + Gần nhất: ... hoặc Chưa gọi lần nào
Quyền đánh dấu LH, tạo lịchTheo RBAC user hiện tại
Quyền chọn hàng loạtTheo RBAC user hiện tại

SCR-04 — Cài đặt cảnh báo nợ quá hạn

Layout

┌─────────────────────────────────────────────────────────┐
│ "Cấu hình cảnh báo nợ quá hạn"                         │
│ ℹ️ Thay đổi có hiệu lực từ lần chạy tiếp theo (07:00)  │
├─────────────────────────────────────────────────────────┤
│ PHẦN 1 — CẤU HÌNH TOÀN HỆ THỐNG (Global)              │
│  Ngưỡng mặc định:  [30] ngày ⓘ                         │
│  Giờ gửi cảnh báo: [07:00] VN timezone                 │
│  Trạng thái:       [● Đang bật ▼]                       │
│                              [Lưu thay đổi]             │
├─────────────────────────────────────────────────────────┤
│ PHẦN 2 — OVERRIDE THEO CHI NHÁNH                        │
│                    [+ Thêm override chi nhánh]          │
│  Chi nhánh Q1  │ 45 ngày  │ [Sửa] [Xóa]                │
│  Chi nhánh Q3  │ 60 ngày  │ [Sửa] [Xóa]                │
├─────────────────────────────────────────────────────────┤
│ PHẦN 3 — LỊCH SỬ THAY ĐỔI                              │
│  24/02 09:15 — Admin Nguyễn: global 30→45 ngày          │
│  22/02 14:30 — Admin Trần: thêm override Q1             │
└─────────────────────────────────────────────────────────┘

Validation

FieldRuleError
Ngưỡng ngàyInteger, min=1, max=365“Ngưỡng phải từ 1 đến 365 ngày”
Giờ gửiHH:MM, trong 06:00–10:00“Giờ gửi phải từ 06:00–10:00”
Chi nhánh overrideChọn từ dropdown, không trùng“Chi nhánh này đã có cấu hình override”

Spec PHẦN 3 — Lịch sử thay đổi

Thuộc tínhMô tả
Nguồn dữ liệudebt_alert_config audit fields (created_by, updated_by, updated_at) + snapshot diff
Số dòng hiệnTối đa 20 entries gần nhất. Scroll nếu nhiều hơn
Format mỗi dòng{DD/MM HH:mm} — {user_name}: {action_description}
Action typesglobal_threshold_changed (“global {old}→{new} ngày”), global_time_changed (“giờ gửi {old}→{new}”), global_status_changed (“bật/tắt cảnh báo”), branch_override_added (“thêm override {branch_name}”), branch_override_updated (“sửa override {branch_name}: {old}→{new} ngày”), branch_override_deleted (“xóa override {branch_name}”)
Sắp xếpupdated_at DESC (mới nhất lên trên)
Quyền xemManager: chỉ thấy entries liên quan branch mình. Admin: thấy tất cả
Empty state“Chưa có lịch sử thay đổi”
i18n key (empty)debt.config.history_empty

SCR-05 — Bàn giao khách — Bước 1: Chọn nhân viên nghỉ

Layout

┌────────────────────────────────────────────────────────────────┐
│ "Bàn giao danh sách khách hàng"                               │
│ STEP: [① Chọn nguồn] ─── ② Chọn đích ─── ③ Xác nhận         │
│ PROGRESS: ████░░░░░░░░ 33%                                    │
├────────────────────────────────────────────────────────────────┤
│ 1A — NHÂN VIÊN NGHỈ (NGUỒN) *                                 │
│ [🔍 Tìm tên nhân viên...]                                      │
│  👤 Nguyễn Văn Sale · Sale · Chi nhánh Q1                    │
│     Đang quản lý: 45 khách · Có nợ: 12 khách                 │
│                                  [Chọn nhân viên này]         │
├────────────────────────────────────────────────────────────────┤
│ 1B — PHẠM VI BÀN GIAO *                                       │
│  ⦿ Toàn bộ danh sách (45 khách)                               │
│  ○ Theo bộ lọc:                                               │
│    [Nhóm nợ ▼]    [Từ ngày ▼]  [Đến ngày ▼]                  │
│    → Kết quả lọc: 12 khách                                    │
├────────────────────────────────────────────────────────────────┤
│ 1C — NGÀY HIỆU LỰC *                                          │
│  [📅 DD/MM/YYYY]  ≥ hôm nay                                   │
│  ℹ️ Từ ngày này, cảnh báo nợ sẽ gửi về người tiếp nhận       │
├────────────────────────────────────────────────────────────────┤
│ [Hủy]                           [Tiếp theo: Chọn đích →]     │
└────────────────────────────────────────────────────────────────┘

Validation

TrườngRuleHành vi khi sai
NguồnBắt buộc, phải activeNút Tiếp theo disabled / error message
Phạm vi filter≥ 1 khách nếu chọn filter“Bộ lọc không ra khách nào”
Ngày hiệu lực≥ hôm nay“Ngày hiệu lực phải từ hôm nay trở đi”

Quick Handover Mode (khi Phạm vi = “Toàn bộ”)

Khi user chọn scope “Toàn bộ danh sách” ở Bước 1, wizard gộp Bước 2 + 3 thành một bước duy nhất, giảm từ 3 bước → 2 bước.

Thuộc tínhMô tả
TriggerPhạm vi = “Toàn bộ danh sách” (radio ⦿ đầu tiên) ở SCR-05
BehaviorBước 2 hiển thị: Chọn đích + Preview + Tóm tắt + Cam kết + Xác nhận (gộp SCR-06 + SCR-07)
Stepper UI[① Chọn nguồn] ─── [② Chọn đích & Xác nhận] (2 steps thay vì 3)
Progress50% → 100% (thay vì 33% → 66% → 100%)
ẨnBảng “DANH SÁCH KHÁCH SẼ CHUYỂN” — vì scope = toàn bộ, không cần duyệt từng khách
Hiện thêmSection Tóm tắt + Cam kết + Ghi chú (từ SCR-07) ngay dưới Preview
Nút CTA[Xác nhận bàn giao] thay vì [Tiếp theo: Xác nhận →]
FallbackNếu scope ≠ “Toàn bộ” (chọn filter) → wizard 3 bước như bình thường
Lý doToàn bộ = đã rõ scope, xem preview đủ. Bỏ bước xác nhận riêng giảm 1 click
Quick Handover — Bước 2 (gộp):
┌────────────────────────────────────────────────────────────────┐
│ STEP: ✅ Chọn nguồn ─── [② Chọn đích & Xác nhận]              │
│ PROGRESS: ████████████ 100%                                   │
├────────────────────────────────────────────────────────────────┤
│ NHÂN VIÊN TIẾP NHẬN (ĐÍCH) *                                  │
│ [🔍 Tìm...]                                                    │
│  👤 Trần Thị CSKH · CSKH · Q1                                │
│     Hiện có: 30 khách · Sau bàn giao: 75 khách ⚠️             │
├────────────────────────────────────────────────────────────────┤
│ PREVIEW                                                        │
│  TRƯỚC                     │  SAU                             │
│  Owner: Nguyễn Sale        │  Owner: Trần CSKH               │
│  Khách: 45                 │  Khách: 75 (+45)                 │
│  Có nợ: 12 khách           │  Có nợ: +12                      │
│  Tổng nợ: 85tr             │  Tổng nợ thêm: 85tr              │
├────────────────────────────────────────────────────────────────┤
│ TÓM TẮT                                                       │
│  📤 Từ: Nguyễn Văn Sale · 📥 Đến: Trần Thị CSKH              │
│  📅 Hiệu lực: 01/03/2026 · 👥 45 khách · 💰 85.000.000đ      │
├────────────────────────────────────────────────────────────────┤
│ CAM KẾT (bắt buộc tick đủ 2)                                  │
│  ☐ Tôi đã kiểm tra preview thông tin bàn giao *               │
│  ☐ Tôi hiểu có thể rollback toàn bộ trong 24h *               │
│ GHI CHÚ: [Lý do bàn giao...]                                  │
├────────────────────────────────────────────────────────────────┤
│ [← Quay lại]                    [Xác nhận bàn giao]           │
└────────────────────────────────────────────────────────────────┘

SCR-06 — Bàn giao khách — Bước 2: Chọn người tiếp nhận & xem trước

Layout

┌────────────────────────────────────────────────────────────────┐
│ STEP: ✅ Chọn nguồn ─── [② Chọn đích & preview] ─── ③ Xác nhận│
│ PROGRESS: ████████░░░░ 66%                                    │
├────────────────────────────────────────────────────────────────┤
│ NHÂN VIÊN TIẾP NHẬN (ĐÍCH) *                                  │
│ [🔍 Tìm...]                                                    │
│  👤 Trần Thị CSKH · CSKH · Q1                                │
│     Hiện có: 30 khách · Sau bàn giao: 75 khách ⚠️             │
│ ⚠️ Số khách sau bàn giao cao (75). Cân nhắc chia nhỏ.        │
├────────────────────────────────────────────────────────────────┤
│ PREVIEW BEFORE / AFTER (Bắt buộc xem trước khi tiếp theo)    │
│  TRƯỚC                     │  SAU                             │
│  Owner: Nguyễn Sale        │  Owner: Trần CSKH               │
│  Khách: 45                 │  Khách: 75 (+45)                 │
│  Có nợ: 12 khách           │  Có nợ: +12                      │
│  Tổng nợ: 85tr             │  Tổng nợ thêm: 85tr              │
│  Alert: Nguyễn Sale        │  Alert: Trần CSKH (từ 01/03)    │
├────────────────────────────────────────────────────────────────┤
│ DANH SÁCH KHÁCH SẼ CHUYỂN                                     │
│ [🔍 Tìm] [Nhóm nợ ▼]                                          │
│ #  Khách       Tiền nợ     Quá hạn    Nhóm nợ                │
│ 1  Nguyễn A    5.000.000đ  95 ngày    ≥90 🔴                  │
│ 2  Trần B      3.200.000đ  45 ngày    30-59 🟡                 │
├────────────────────────────────────────────────────────────────┤
│ [← Quay lại]                    [Tiếp theo: Xác nhận →]      │
└────────────────────────────────────────────────────────────────┘

SCR-07 — Bàn giao khách — Bước 3: Kiểm tra & xác nhận

Layout

┌────────────────────────────────────────────────────────────────┐
│ STEP: ✅ Chọn nguồn ─── ✅ Chọn đích ─── [③ Xác nhận]        │
│ PROGRESS: ████████████ 100%                                   │
├────────────────────────────────────────────────────────────────┤
│ TÓM TẮT BÀN GIAO                                              │
│  📤 Từ:         Nguyễn Văn Sale (Sale · Q1)                   │
│  📥 Đến:        Trần Thị CSKH (CSKH · Q1)                   │
│  📅 Hiệu lực:   01/03/2026                                    │
│  👥 Số khách:   45 khách                                      │
│  💰 Tổng nợ:    85,000,000 đ                                  │
├────────────────────────────────────────────────────────────────┤
│ CAM KẾT (bắt buộc tick đủ 2 mới bật nút Xác nhận — DEC-UX-008)│
│  ☐ Tôi đã kiểm tra danh sách và xem preview *                 │
│  ☐ Tôi hiểu có thể rollback toàn bộ trong 24h *               │
├────────────────────────────────────────────────────────────────┤
│ GHI CHÚ (tuỳ chọn)                                            │
│  [Lý do bàn giao / ghi chú thêm...]                           │
├────────────────────────────────────────────────────────────────┤
│ ⚠️ Sau khi xác nhận:                                           │
│  • Trần Thị CSKH nhận cảnh báo nợ từ 01/03/2026              │
│  • Nguyễn Văn Sale ngừng nhận cảnh báo nợ này                │
│  • Audit trail lưu vĩnh viễn, không thể xóa                  │
├────────────────────────────────────────────────────────────────┤
│ [← Quay lại]         [Xác nhận bàn giao] ← disabled khi chưa tick│
└────────────────────────────────────────────────────────────────┘

SCR-08 — Kết quả bàn giao & lịch sử thao tác

Layout — Thành công, trong 24h

┌────────────────────────────────────────────────────────────────┐
│ ✅ BÀN GIAO THÀNH CÔNG                                         │
│ Mã: HO-2026-0224-001                       [📋 Sao chép]      │
├────────────────────────────────────────────────────────────────┤
│ Từ: Nguyễn Văn Sale → Đến: Trần Thị CSKH                     │
│ Hiệu lực: 01/03/2026 · Khách: 45 · Tổng nợ: 85.000.000đ     │
├────────────────────────────────────────────────────────────────┤
│ AUDIT TIMELINE (immutable)                                     │
│ ● 24/02 09:15 — Admin Lê: Tạo handover HO-2026-0224-001      │
│ ● 24/02 09:15 — System: 45 khách chuyển thành công            │
│ ● 24/02 09:16 — System: Noti gửi cho Trần Thị CSKH           │
├────────────────────────────────────────────────────────────────┤
│ ROLLBACK — Còn 18 giờ 45 phút                                  │
│  ⚠️ Có thể hoàn tác toàn bộ trước 09:15 ngày 25/02/2026      │
│  Lưu ý: Chỉ rollback toàn bộ 45 khách, không hỗ trợ partial  │
│                              [↩ Hoàn tác bàn giao này]        │
├────────────────────────────────────────────────────────────────┤
│ [← Về Dashboard]      [Xem danh sách 45 khách đã chuyển]     │
└────────────────────────────────────────────────────────────────┘

Layout — Sau 24h

│ ROLLBACK — Đã hết hạn                                          │
│  🔒 Thời gian hoàn tác đã hết (09:15 ngày 25/02/2026)        │
│  Liên hệ Admin nếu cần xử lý thủ công                        │
┌────────────────────────────────────────────┐
│ ↩ XÁC NHẬN HOÀN TÁC — HO-2026-0224-001    │
├────────────────────────────────────────────┤
│ • 45 khách trở về: Nguyễn Văn Sale         │
│ • Alert nợ gửi về Sale Nguyễn              │
│ • Thao tác không thể hoàn tác lần nữa      │
│ Lý do hoàn tác *: [__________________]     │
├────────────────────────────────────────────┤
│ [Hủy]           [Xác nhận hoàn tác]       │
└────────────────────────────────────────────┘

Platform: iOS & Android | Screen chuẩn: 390px (iPhone 14) Scope MVP: Xem KPI + Xem/xử lý nợ cơ bản + Nhận push notification Handover Wizard: Pending PD-007


Mobile Scope Summary

Ref: DEC-012 — Mobile scope: Diva Partner App, chỉ xem + hành động cơ bản. Nhân viên cần theo dõi mọi lúc.

Tính năngMVPGhi chú
Xem KPI cá nhân (MOB-01)
Xem danh sách nợ quá hạn (MOB-02)
Xem chi tiết khách nợ (MOB-03)
Gọi điện từ appNative phone app
Đánh dấu Đã liên hệ
Tạo lịch nhắc (MOB-04)
Nhận push notification (MOB-05)FCM (DEC-018)
Drill-down tư vấn❌ Phase 2Quá phức tạp cho màn nhỏ
Cấu hình ngưỡng cảnh báoChỉ web
Handover Wizard❌ Phase 2PD-007 resolved → Phase 2
Export XLSXPD-009 resolved → mobile chỉ xem (DEC-019)
Xem report của người khácMobile chỉ xem data cá nhân

MOB-01 — Quản lý hiệu suất (mobile overview)

Layout (390px)

┌─────────────────────────────────────┐
│ ←         Quản lý hiệu suất         │
│─────────────────────────────────────│
│ [Hiệu suất tư vấn] [Công nợ]        │
│─────────────────────────────────────│
│ [🔍 Tìm kiếm]                 [⚙]  │
│─────────────────────────────────────│
│  ┌───────────────────────────────┐  │
│  │ ⚠ Ưu tiên xử lý nợ quá hạn    │  │
│  │ Nợ quá hạn: 85.000.000đ       │  │
│  │ 4 khách nợ ≥90 ngày           │  │
│  │ 3 khách nợ 60-80 ngày   [→]   │  │
│  └───────────────────────────────┘  │
│─────────────────────────────────────│
│  KHI TAB = "Hiệu suất tư vấn"       │
│  ┌──────────────┬──────────────┐    │
│  │ Khách đã TV  │ Đã mua kỳ    │    │
│  │ 67 khách     │ 45 khách     │    │
│  └──────────────┴──────────────┘    │
│  ┌──────────────┬──────────────┐    │
│  │ Lượt tư vấn  │ Tỷ lệ CĐ     │    │
│  │ 128 lượt     │ 67.2%        │    │
│  │              │ ₍45/67 khách₎│    │
│  └──────────────┴──────────────┘    │
│  ┌───────────────────────────────┐  │
│  │ Doanh thu ròng                │  │
│  │ 125.000.000đ                  │  │
│  │ ₍Đã thu 62M (49,6%) · Nợ 63M₎ │  │
│  └───────────────────────────────┘  │
│─────────────────────────────────────│
│ Danh sách hiệu suất tư vấn          │
│ ┌─────────────────────────────────┐ │
│ │ Nguyễn Trần Nguyên      [Đã mua]│ │
│ │ 0123456789                      │ │
│ │ Tham gia: 30%  Ngày TV:20/02/26 │ │
│ │ Gần nhất: 09:00 20/02/2026      │ │
│ │ [DV-1][DV-2]                    │ │
│ └─────────────────────────────────┘ │
│ [Tải thêm...]                      │
└─────────────────────────────────────┘

Sub-label KPI mobile (P1 — TASK-022): Mỗi KPI card hiện sub-label dưới giá trị chính (font 12px, color #757575). Ký hiệu ₍...₎ trong wireframe = sub-label. Khi giá trị = N/A → sub-label = "Chưa đủ dữ liệu". Spec sub-label chi tiết giống SCR-01 Block A/B (xem section Sub-label rules).

Sub-label rule riêng cho card "Doanh thu ròng" mobile (Ref BUG-PROD-006 — Option 1 LOCKED 2026-05-15):

  • Lý do: Vai trò user trên mobile chính = Sale CSKH làm việc thu hồi nợ — họ cần thông tin vận hành (đã thu / % thu / còn nợ), không phải warning wording như web.
  • Format sub-label: Đã thu {collected_short} ({collection_rate}%) · Nợ {outstanding_short}
    • collected_short = collected_amount_in_period rút gọn (M/Tỷ) — VD: 62.000.000đ62M, 1.500.000.000đ1,5T
    • collection_rate = collected_amount_in_period / actual_revenue * 100 — định dạng 1 chữ số thập phân với dấu , (VD: 49,6%)
    • outstanding_short = total_debt_amount rút gọn cùng quy tắc
    • Dấu phân tách 3 chỉ số: · (interpunct, có space 2 bên)
  • Edge cases:
    • actual_revenue = 0 (chưa có booking trong kỳ) → sub-label = "Chưa có doanh thu trong kỳ"
    • collected_amount_in_period = 0 (booking nhưng chưa thu) → sub-label = Đã thu 0đ (0%) · Nợ {outstanding}
    • total_debt_amount = 0 (đã thu hết) → sub-label = Đã thu {collected} (100%) · Đã tất toán (badge xanh)
  • Format số tiền primary: actual_revenue hiển thị VND đầy đủ + suffix đ (Ref rule line 858 + BUG-PROD-002 LOCKED). KHÔNG dùng "Triệu" / "Tỷ" cho primary value.
  • Khác biệt vs Web (SCR-01 line 866): Web sub-label = "≠ tiền thực thu" (DEC-032 LOCKED — wording cảnh báo audit-friendly). Mobile sub-label = 3 chỉ số vận hành (thông tin dày). Khác nhau intentional vì target audience khác (Manager/BOD audit ở web, Sale CSKH operation ở mobile).

Header & tabs mobile:

  • Tiêu đề màn: Quản lý hiệu suất
  • Có 2 tab nội dung: Hiệu suất tư vấn / Công nợ
  • Tab Hiệu suất tư vấn là default khi mở màn
  • Chuyển tab không reset ô tìm kiếm; mỗi tab giữ state local riêng nếu cần

Khi tab = Công nợ (mobile):

  • Hiển thị Ưu tiên xử lý nợ quá hạn dạng card strip (dọc).
  • Hiển thị Block Chỉ số Công nợ cùng bộ KPI như web: Khách đang nợ, Khách nợ quy đổi, Tiền nợ quy đổi, Tổng tiền nợ, Tỷ lệ thu nợ, TB ngày thu nợ, Đã thu trong kỳ, headline Tỷ lệ nợ/doanh thu.
  • Hiển thị Phân loại nhóm nợ và danh sách card khách nợ ngay trong cùng tab.
  • Mỗi card khách: Tên, SĐT, Tổng nợ, Quá hạn, % tham gia, Đã gọi..., Phụ trách chính, CTA [Gọi ngay] [Tạo lịch].

Filter mobile: Sticky search bar + nút mở bottom sheet filter. Trên tab Hiệu suất tư vấn, filter sheet có thể chứa thời gian / vai trò / chi nhánh theo quyền.

Danh sách hiệu suất tư vấn (mobile card list):

  • Dùng card thay cho table row của web.
  • Mỗi card hiển thị: Tên + badge trạng thái → SĐTTham gia + Ngày tư vấnGần nhất: HH:mm DD/MM/YYYY → danh sách dịch vụ rút gọn.
  • Gần nhất là metadata phụ, thay cho wording Đã gọi ... để giữ đúng ngữ cảnh tư vấn.
  • Badge trạng thái dùng đúng 2 trạng thái: Đã mua / Chưa mua.
  • Tải thêm hoặc phân trang lazy-load ở cuối danh sách.

MOB-02 — Danh sách khách nợ quá hạn

Layout

┌─────────────────────────────────────┐
│ ← Danh sách nợ quá hạn             │
│─────────────────────────────────────│
│ [☑ Sale] [☑ Telesale] [☑ CSKH]       │
│─────────────────────────────────────│
│ 🔴 5 khách nợ ≥90 ngày cần ưu tiên │
│ 23 khách · 85.000.000đ tổng nợ      │
│─────────────────────────────────────│
│ [🔍 Tìm...] [⚙ Bộ lọc]            │
│─────────────────────────────────────│
│ ┌─────────────────────────────────┐ │
│ │ Nguyễn Văn A        [≥90 🔴]   │ │
│ │ 0901 234 567                   │ │
│ │ Tổng nợ: 5.000.000đ  · 95 ngày │ │
│ │ Tham gia: 30%                  │ │
│ │ Phụ trách chính: Nguyễn Sale   │ │
│ │ Đã gọi 2 lần · 09:00 20/02/2026│ │
│ │ [Gọi ngay] [Tạo lịch] [⋯]      │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ Trần Thị B        [30-59 🟡]   │ │
│ │ 0901 888 999                   │ │
│ │ Tổng nợ: 3.200.000đ  · 45 ngày │ │
│ │ Tham gia: 30%                  │ │
│ │ Phụ trách chính: Nguyễn Sale   │ │
│ │ Đã gọi 1 lần · 14:30 18/02/2026│ │
│ │ [Gọi ngay] [Tạo lịch] [⋯]      │ │
│ └─────────────────────────────────┘ │
│  [Tải thêm...]                      │
│─────────────────────────────────────│
│  [🏠] [📋 Nợ] [🔔] [👤]            │
└─────────────────────────────────────┘

Sắp xếp mặc định: overdue_days DESC — khách nợ lâu nhất lên đầu, sau đó outstanding_amount DESC. Giống logic web SCR-03.

Card layout (Mobile responsive):

  • Card dùng thay cho table row của web. Không cố giữ parity theo cột.
  • Mỗi card hiển thị theo thứ tự ưu tiên: Tên + badgeSĐTTổng nợ + Quá hạn% tham giaPhụ trách chínhĐã gọi ...
  • Phụ trách chính hiển thị dạng text line, không dùng box nền riêng để tránh tăng chiều cao card.
  • Đã gọi {call_count} lần · {last_call_time} là metadata phụ, nằm ngay dưới Phụ trách chính.
  • Luôn hiển thị dòng Phụ trách chính để tránh mơ hồ khi 1 khách có nhiều người tham gia.
  • CTA hiển thị trực tiếp dưới card: [Gọi ngay] [Tạo lịch] [⋯]
  • Nút [⋯] gom action phụ như Đã liên hệ, Xem chi tiết, Mở CRM nếu cần.
  • Button sizing: min 44×44px tap target (iOS HIG / Material Design)

Quick filter chips (MOB-02):

  • Hiển thị 1 hàng chip ngay trước danh sách card để lọc nhanh theo Nhóm nợ.
  • Options: Tất cả, ≥90, 60–89, 30–59, 0–29.
  • Chip Xử lý ngay (nếu dùng label này trên UI) phải map sang Nhóm nợ = ≥90 để tránh mơ hồ.
  • Khi chip active: chỉ filter working set của danh sách card (giống web), không làm thay đổi KPI/aging ở phần tổng quan nếu chúng đang hiển thị trên cùng màn.

Behavior nút Gọi mobile:

  1. Tap → Mở phone app native với số khách
  2. Sau khi gọi, quay lại → hỏi "Đã liên hệ thành công?" [Có] [Không]
  3. [Có] → tự toggle Đã LH

MOB-03 — Thông tin chi tiết khách nợ & lịch nhắc

Layout

┌─────────────────────────────────────┐
│ ← Nguyễn Văn A         [≥90 🔴]   │
│─────────────────────────────────────│
│  Tổng nợ: 6.500.000đ               │  ← STICKY SUMMARY
│  Quá hạn cao nhất: 95 ngày         │  ← (cố định khi scroll)
│  Owner: Tôi                         │
│  SĐT: 0901 234 567   [📞 Gọi ngay]│
│═════════════════════════════════════│
│  ▼ DỊCH VỤ NỢ (3DV · 2đơn) 6,500k │  ← collapsible
│                                     │
│  📋 ĐH-001 · 15/01/2026            │
│  ├ Cắt tóc    2.000.000đ  47d 🟡   │
│  └ Nhuộm      3.000.000đ  47d 🟡   │
│                                     │
│  📋 ĐH-002 · 20/02/2026            │
│  └ Massage    1.500.000đ   5d 🟢   │
│─────────────────────────────────────│
│  ▼ LỊCH SỬ LIÊN HỆ (2)             │  ← collapsible
│  20/02 10:15 — Tôi: Đã LH, hẹn TT │
│  15/02 09:00 — Tôi: Gọi, ko nghe  │
│  [+ Thêm ghi chú liên hệ]          │
│─────────────────────────────────────│
│  ▼ LỊCH NHẮC (3 · 1🔴 quá hạn) [+] │  ← collapsible, luôn mở
│  ┌─────────────────────────────────┐│
│  │ 🔴 02/03 09:00 — Gọi nhắc TT  ││
│  │    Quá hạn · Ghi chú: Hẹn cuối ││
│  │    [✅ Xong] [❌ Hủy]          ││
│  └─────────────────────────────────┘│
│  ┌─────────────────────────────────┐│
│  │ ✅ 25/02 10:00 — Nhắc Zalo     ││
│  │    Đã xong lúc 25/02 10:32     ││
│  └─────────────────────────────────┘│
│  ┌─────────────────────────────────┐│
│  │ ✅ 20/02 09:00 — Gọi nhắc #1   ││
│  │    Đã xong lúc 20/02 09:15     ││
│  └─────────────────────────────────┘│
│─────────────────────────────────────│
│  [📞 Gọi]  [📅 Lịch]  [→ CRM]     │
└─────────────────────────────────────┘

Section DỊCH VỤ NỢ — Spec:

Thành phầnMô tả
Nhóm theoĐơn hàng (order_code + ngày đơn)
Sắp xếp đơnĐơn cũ nhất trước (ngày đơn ASC)
Sắp xếp DV trong đơnTheo số tiền nợ DESC
Mỗi dòng DV hiển thịTên dịch vụ + số tiền nợ + số ngày quá hạn + badge aging màu
Aging mỗi dòngTính riêng từ debt_due_date của order_service đó
Badge trên headerLấy aging bucket cao nhất trong tất cả dòng DV nợ
Dòng tổngHiển thị tổng tiền nợ tất cả DV
CollapseNếu > 5 đơn → collapse từ đơn thứ 4, nút "Xem thêm N đơn"

Spec Sticky Summary Card (MOB-03)

Thành phầnMô tả
Khi nào stickyKhi user scroll xuống quá phần header (tên + thông tin chính)
Nội dung sticky1 dòng compact: Nguyễn Văn A · 6.500.000đ · [≥90 🔴] + nút [📞]
Chiều cao48px, background #FFFFFF, shadow bottom 0 2px 4px rgba(0,0,0,0.1)
Khi scroll lênSticky ẩn (header gốc hiện lại). Transition: slide up 200ms
Lý doUser scroll 5+ sections dài, mất context khách đang xem. Sticky giữ thông tin cốt lõi

Spec Collapsible Sections (MOB-03)

SectionHeaderDefault stateBadge count
DỊCH VỤ NỢ▼ Dịch vụ nợ (3DV · 2đơn) 6,500kCollapsed nếu > 2 đơn. Expanded nếu ≤ 2 đơnTổng tiền nợ
LỊCH SỬ LIÊN HỆ▼ Lịch sử liên hệ (2)Collapsed mặc địnhSố lần LH
LỊCH NHẮC FOLLOW-UP▼ Lịch nhắc (3 · 1🔴 quá hạn) [+]Expanded mặc định (vì là action chính)Số nhắc + badge đỏ quá hạn

Behavior:

  • Tap header / → toggle expand/collapse. Animation: slide 200ms
  • Badge count hiện ngay trên header → user biết số lượng mà không cần expand
  • Section "Lịch nhắc" luôn mở nếu có ≥1 nhắc quá hạn (auto-expand)
  • Tổng chiều dài trang giảm ~50% khi "Dịch vụ nợ" + "Lịch sử" collapsed

MOB-04 — Tạo lịch nhắc chăm sóc (bottom sheet)

Layout

┌─────────────────────────────────────┐
│ ▬ (drag handle)                     │
│ Tạo lịch nhắc — Nguyễn Văn A       │
│ Nợ: 6.500.000đ · 3DV · 2đơn        │
│─────────────────────────────────────│
│ Ngày nhắc *  [DD/MM/YYYY] [HH:MM]   │
│ Nội dung *   [Gọi nhắc thanh toán] │
│ Ghi chú      [Thêm ghi chú...]      │
│─────────────────────────────────────│
│ [Hủy]              [Tạo lịch ✓]    │
└─────────────────────────────────────┘

Lưu ý: Bottom sheet mobile chỉ hiện dòng tổng hợp nợ (tổng tiền + số DV + số đơn), không expand chi tiết. Chi tiết xem ở MOB-03 phía trên.


MOB-05 — Trung tâm thông báo

Layout

┌─────────────────────────────────────┐
│ ← Thông báo                         │
│─────────────────────────────────────│
│ HÔM NAY                             │
│ ┌─────────────────────────────────┐ │
│ │ ⚠️ Nợ quá hạn · 07:01          │ │
│ │ Bạn có 23 khách nợ quá 30 ngày │ │
│ │ → Xem danh sách                 │ │
│ └─────────────────────────────────┘ │
│ HÔM QUA                             │
│ ┌─────────────────────────────────┐ │
│ │ ✅ Bàn giao · 23/02 09:15      │ │
│ │ 45 khách từ Nguyễn Sale         │ │
│ │ đã chuyển về bạn (hiệu lực 1/3)│ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘

Deeplink behavior:

  • Tap NTF-DEBT-DAILY-001 → MOB-02 (Danh sách nợ)
  • Tap NTF-HANDOVER-TARGET → MOB-01 (Home, refresh KPI)
  • App chưa mở → Mở app → navigate thẳng đến màn hình đúng
Màn hìnhCustom SchemeUniversal Link (fallback)Ghi chú
MOB-01 Homedivapartner://homehttps://partner.diva.vn/home
MOB-02 Nợ quá hạndivapartner://debt/overduehttps://partner.diva.vn/debt/overdueMở danh sách nợ theo filter mặc định của user
MOB-02 filter bucketdivapartner://debt/overdue?bucket=>=90https://partner.diva.vn/debt/overdue?bucket=>=90Filter preset
MOB-03 Chi tiết kháchdivapartner://debt/customer/{customer_id}https://partner.diva.vn/debt/customer/{customer_id}
MOB-05 Notificationdivapartner://notificationshttps://partner.diva.vn/notifications


B-Export) Export Spec — XLSX Only


Export SCR-01 — Dashboard / Danh sách

Trigger: Nút [Xuất XLSX] góc trên phải Phạm vi: Theo bộ lọc đang active + quyền user Row cap: 200,000 rows/job — vượt → chunking tự động Tên file: bao-cao-cong-no_{branch_code}_{YYYYMMDD}_{HHmm}.xlsxSheet: "Báo cáo công nợ" | Header: bold, background #E3F2FD | Freeze: Row 1 + Col 1-2

#HeaderNguồnFormat
1Mã khách hàngcustomer_codeText
2Tên khách hàngcustomer_nameText
3Số điện thoạicustomer_phoneText (không format)
4Người phụ tráchdebt_owner_nameText
5Chi nhánhbranch_nameText
6Dịch vụservice_namesText (phân cách ", ")
7% Hoa hồngcommission_percent0.00%
8Ngày tư vấn gần nhấtlast_consultation_dateDD/MM/YYYY
9Số tiền nợ (đ)outstanding_amount#,###
10Quá hạn (ngày)overdue_daysInteger (0 nếu chưa quá hạn)
11Nhóm nợaging_bucket"0-29" / "30-59" / "60-89" / "≥90" / "—"
12Ngày bắt đầu nợdebt_start_dateDD/MM/YYYY
13Hạn thanh toándebt_due_dateDD/MM/YYYY

Export SCR-03 — Nợ Quá Hạn

Tên file: no-qua-han_{branch_code}_{YYYYMMDD}_{HHmm}.xlsxSheet: "Nợ quá hạn"

Bao gồm tất cả 13 cột SCR-01, cộng thêm:

#HeaderNguồnFormat
14Đã liên hệis_contacted"Có" / "Không"
15Lần liên hệ cuốilast_contacted_atDD/MM/YYYY HH:MM
16Người liên hệcontacted_by_nameText
17Ghi chú liên hệcontact_noteText
18% tham giaparticipation_percent0.00%

Export SCR-08 — Audit Handover

Quyền: Admin / HR Ops only Tên file: audit-ban-giao_{YYYYMMDD}_{HHmm}.xlsxSheet: "Audit bàn giao"

HeaderNguồnFormat
Mã bàn giaohandover_idText
Từ nhân viênsource_owner_nameText
Đến nhân viêntarget_owner_nameText
Ngày hiệu lựceffective_dateDD/MM/YYYY
Số kháchcustomer_countInteger
Tổng nợ (đ)debt_amount#,###
Người thực hiệncreated_by_nameText
Thời gian thực hiệncreated_atDD/MM/YYYY HH:MM:SS
Đã rollbackis_rolled_back"Có" / "Không"
Thời gian rollbackrollback_atDD/MM/YYYY HH:MM:SS
Người rollbackrollback_by_nameText
Lý do rollbackrollback_reasonText


B-EdgeCases) Edge Cases


Handover Edge Cases

CaseHành viError message
Nguồn = ĐíchBlock ngay Bước 1"Người giao và người nhận không được trùng nhau"
Nguồn không có kháchBlock Bước 1"Nhân viên này không có khách để bàn giao"
Nguồn không activeBlock Bước 1"Nhân viên này không còn hoạt động"
Đích không activeBlock Bước 2"Nhân viên tiếp nhận không còn hoạt động. Chọn người khác."
Đang có handover pending cho nguồnBlock Bước 1"Đang có bàn giao chưa hoàn tất cho nhân viên này"
Filter lọc 0 kháchBlock "Tiếp theo""Bộ lọc không tìm thấy khách nào. Thử điều kiện khác."
Rollback sau 24hBlock nút rollback"Thời gian hoàn tác đã hết. Liên hệ Admin."
Rollback partialKhông hỗ trợ MVP (DEC-015)"Chỉ hỗ trợ hoàn tác toàn bộ. Để chuyển lại 1 phần, tạo handover mới."
Khách chốt đơn mới trong 24h sau handoverRollback vẫn thực hiện đượcĐơn mới giữ nguyên owner mới
2 user cùng rollback 1 handoverFirst write winsNgười thứ 2 nhận: "Bàn giao này đã được hoàn tác"

Followup Task Edge Cases

CaseHành viError message
Tạo lịch nhắc cho khách đã tất toán nợCho phép tạo (vẫn cần follow-up quan hệ)
Đánh dấu Đã xong task đã Đã xongBlock"Lịch nhắc này đã hoàn thành"
Hủy task đã Đã xongBlock"Không thể hủy lịch nhắc đã hoàn thành"
remind_at trong quá khứ khi tạoBlock"Ngày nhắc phải từ bây giờ trở đi"
Nhiều lịch nhắc cùng khách cùng giờCho phép (user tự quản lý)
Handover khách sang người khác → lịch nhắc cũGiữ nguyên assigned_to cũ. User tạo mới nếu cầnBadge warning khi drawer mở
Scheduler followup reminder lỗiRetry lần sau (5 phút). Không alert Ops (noti nhẹ)Ghi log followup_remind_failed
User xóa lịch nhắcSoft delete (deleted_at). Không hiển thị trong danh sách

Scheduler Edge Cases

CaseHành vi
Owner resolution không ra ai (3 cấp đều null)Bỏ qua, ghi log owner_resolution_failed, tăng metric
Job retry trong ngàyDedupe key (owner, customer, current_bucket, date) → lần 2 skip, không gửi trùng
Cross-day re-trigger (Ref DEC-026 rule 1)Khách vẫn quá hạn ngày sau → gửi noti mới (dedupe key date khác → KHÔNG skip). User nhận noti mỗi ngày
Bucket transition (Ref DEC-026 rule 2)Khách chuyển bucket lên cao hơn → gửi NTF-DEBT-BUCKET-ESCALATE riêng + vẫn gửi NTF-DEBT-DAILY-001 bucket mới. User nhận 2 noti trong ngày
Partial payment (Ref DEC-026 rule 3)Khách thanh toán 1 phần, còn nợ → vẫn nằm list. total_debt payload = SUM(remaining) còn lại. KHÔNG reset trạng thái
Settled drop (Ref DEC-026 rule 4)Khách settled (paid = total) trong ngày X → ngày X+1 trở đi không nhận noti nữa. Nếu phát sinh nợ mới sau đó → reset chu kỳ noti từ đầu
Trigger at exact threshold (Ref DEC-025)overdue_days == threshold_days → CÓ gửi (inclusive >=). Test seed: overdue = 30, threshold = 30
Khách vừa handover trong ngày → scheduler chạy sauGửi cho owner mới (tại thời điểm scheduler chạy), cũ không nhận
threshold_days = 0 (misconfiguration)Validate khi save: min = 1. Không cho lưu.
Scheduler fail hoàn toànAlert Ops ≤ 5 phút (NFR-006), retry auto, ghi log
Push mobile delivery thất bạiGhi log, không retry (fallback: in-app web)

KPI Edge Cases

CaseHành vi
unique_customer_count = 0conversion_rate = N/A (không phải 0%)
net_revenue = 0 (Ref FORMULA-003)debt_ratio = N/A, hiển thị "—"
Tất cả payments vào ngày phát sinh nợavg_recovery_days_weighted = 0
Khách có nhiều đơn, 1 đơn hoàn trảactual_revenue của đơn đó đã pre-computed (đã trừ refund). KHÔNG cần thêm logic FE (Ref DEC-022)
Khách có nhiều đơn ở nhiều bucket (Ref FORMULA-006B + DEC-023)Customer-level rollup: gán KH vào bucket cao nhất (max(overdue_days)). Vd: KH có đơn 15d + 95d → bucket = very_high_risk. Biểu đồ phân loại + bảng count: 1 KH = 1 unit. Tổng tiền nợ trong bucket = SUM remaining của tất cả đơn KH gán bucket đó
Filter chọn ngày tương laiEmpty state "Chưa có dữ liệu cho kỳ này"
1 dịch vụ có 2 Sale 50/50, cùng % và cùng created_atTie-break: user_id ASC
debt_start_date nullSkip khỏi aging calculation, ghi log debt_date_missing
Manager/BOD view các KPI quy đổi (Ref DEC-024 + A12.4)ẨN cột "Khách nợ quy đổi" / "Tiền nợ quy đổi" / "% Của tôi" — thay bằng raw aggregate trong branch/system scope. API trả view_role: manager/bod với raw values
Manager kiêm Sale (vừa quản lý vừa được phân khách)UI có pill switcher giữa "View của tôi" (individual quy đổi) và "View team" (raw aggregate). Mặc định: view team
BOD chưa có branch assigned trong allowed listTrả empty + banner "Hệ thống chưa có data trong scope của bạn"
Khách có DV null trong DB (cột Dịch vụ SCR-02)Hiển thị "Dịch vụ #{order_service_id}". Nếu lượt TV không có DV nào → cell "—"
Khách có nhiều DV cùng lượt TV (Ref FORMULA-007)Hiển thị "{tên DV rep} (+N-1 DV khác)". Tooltip liệt kê tất cả

Mobile Edge Cases

CaseHành vi
Mất kết nốiHiển thị cache + banner "Đang offline, dữ liệu có thể chưa mới nhất" (xem chi tiết offline bên dưới)
Push notification tap khi app chưa mởMở app → navigate đúng màn hình (deep link scheme)
Gọi xong, quay lại app sau > 5 phútVẫn hỏi "Đã liên hệ thành công?" (không tự timeout)
Số điện thoại khách nullNút [📞 Gọi] disabled + tooltip "Chưa có số điện thoại"
FCM token expired / revokedTự động refresh token khi app mở lần tiếp; nếu fail → fallback in-app web
User tắt push notification ở OS levelIn-app badge "Bạn đã tắt thông báo. Bật lại trong Cài đặt."

Mobile Offline Behavior (Chi tiết)

Dữ liệuCache TTLHành vi offlineSync khi có mạng
KPI cá nhân (MOB-01)30 phútHiển thị data cached + badge timestamp "Cập nhật lúc HH:MM"Auto refresh
Danh sách nợ (MOB-02)15 phútHiển thị list cached, CTA gọi vẫn hoạt động (native phone)Auto refresh
Chi tiết khách (MOB-03)15 phútHiển thị cached, nút Gọi hoạt độngAuto refresh
Đánh dấu Đã LHQueue locally (optimistic UI: hiển thị xanh ngay)Sync khi có mạng, nếu fail → revert UI + toast lỗi
Tạo lịch nhắcQueue locally (hiển thị pending indicator)Sync khi có mạng
Banner offline"Đang offline — dữ liệu có thể chưa mới nhất" (sticky top)Tự ẩn khi reconnect

B-Onboarding) First-time Onboarding Tour

Hướng dẫn 3 bước cho user mới, chỉ hiện lần đầu truy cập module. Giảm thời gian onboard và support ticket.

Trigger

Điều kiệnMô tả
Khi nào hiệnLần đầu user mở SCR-01 (Dashboard) VÀ localStorage key debt_onboarding_completed chưa tồn tại
Không hiệnUser đã hoàn thành tour HOẶC đã bấm "Bỏ qua"
ResetKhông tự reset. Admin có thể reset qua setting user nếu cần

Tour Steps (3 bước)

StepTarget elementTitleMô tảVị trí tooltip
1/3Block B — KPI Nợ"KPI nợ của bạn""Đây là tổng quan nợ bạn đang phụ trách. Số đỏ = nợ quá hạn cần ưu tiên xử lý."Bottom
2/3Aging Bucket Chart (Block C)"Phân nhóm theo thời gian nợ""Biểu đồ chia khách nợ thành 4 nhóm. Click vào nhóm để lọc khu vực xử lý nợ bên dưới; biểu đồ vẫn giữ phân bổ tổng."Top
3/3Nút [📅 Lịch nhắc] trên SCR-01 Action Card (CTA "Xử lý ngay")"Quản lý lịch nhắc""Từ đây bạn có thể xem danh sách nợ cần xử lý. Bấm vào khách để tạo lịch nhắc — hệ thống sẽ gửi notification đúng giờ."Left

Lưu ý Step 3: Tour chạy hoàn toàn trên SCR-01 (Dashboard). Step 3 spotlight nút CTA "Xử lý ngay" trên Action Card (cùng trang) thay vì navigate sang SCR-03 — tránh phá vỡ flow tour. User tự navigate sau khi hoàn thành tour.

UI Components

Step 1/3:
┌──────────────────────────────────────────────────┐
│  ┌─ spotlight ──────────────────────────────────┐│
│  │ ░░░░░░ Block B — KPI Nợ ░░░░░░░░░░░░░░░░░░░ ││
│  └──────────────────────────────────────────────┘│
│  ┌─ tooltip (bottom) ──────────────────────────┐ │
│  │ 📍 1/3 — KPI nợ của bạn                      │ │
│  │ Đây là tổng quan nợ bạn đang phụ trách.      │ │
│  │ Số đỏ = nợ quá hạn cần ưu tiên xử lý.       │ │
│  │                                              │ │
│  │ [Bỏ qua]          [Tiếp theo →] ● ○ ○       │ │
│  └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘

Behavior

Thuộc tínhMô tả
BackdropOverlay rgba(0,0,0,0.5) + spotlight cutout trên target element
Navigation[Tiếp theo →] / [← Quay lại] (từ step 2). Step 3: [Hoàn thành ✓]
Bỏ qua[Bỏ qua] → confirm dialog "Bạn có thể xem lại hướng dẫn bất kỳ lúc nào từ menu ⓘ" → set debt_onboarding_completed = true
Hoàn thànhStep 3 [Hoàn thành ✓] → set debt_onboarding_completed = true + toast "Sẵn sàng! Bạn có thể bắt đầu quản lý công nợ."
Dot indicator● ○ ○ → ● ● ○ → ● ● ● (hiện step hiện tại)
ScrollTự scroll target element vào viewport trước khi hiện tooltip
Xem lạiMenu ⓘ trên header SCR-01 → "Xem lại hướng dẫn" → reset tour
MobileTour giống desktop, tooltip hiện full-width bottom sheet thay vì positioned tooltip
i18n keysdebt.onboarding.step1_title: "KPI nợ của bạn"
debt.onboarding.step1_desc: "Đây là tổng quan nợ…"
debt.onboarding.step2_title: "Phân nhóm theo thời gian nợ"
debt.onboarding.step2_desc: "Biểu đồ chia khách nợ…"
debt.onboarding.step3_title: "Quản lý lịch nhắc"
debt.onboarding.step3_desc: "Tạo lịch nhắc để không quên…"
debt.onboarding.skip: "Bỏ qua"
debt.onboarding.next: "Tiếp theo"
debt.onboarding.back: "Quay lại"
debt.onboarding.finish: "Hoàn thành"
debt.onboarding.toast_done: "Sẵn sàng! Bạn có thể bắt đầu quản lý công nợ."