Appearance
v1.4 — 27/03/2026
| Thay đổi | Section | Ảnh hưởng |
|---|---|---|
| Đổi label dropdown "Commission tư vấn" → "Hoa hồng tư vấn" (DEC-012) | B2 Filter Bar, B7 Copy Text, B9 Tooltip | FE |
UI Spec — Báo cáo doanh số cá nhân
Ref: PRD v1.3 | Date: 2026-03-23
B1) Screen Map
| SCR | Tên | Route | Mô tả |
|---|---|---|---|
| SCR-00 | Báo cáo doanh số cá nhân (Hub) | /r/reports/employee_daily_commission_group | Tab container: 3 tabs |
| SCR-01 | Tab: Doanh số theo ngày | Tab panel trong SCR-00 | Filter + pivot table + export (build mới) |
| SCR-02 | Popup chi tiết giao dịch ngày | QDialog (trên SCR-01) | Drill-down: danh sách giao dịch commission + truy thu |
| SCR-03 | Tab: Doanh thu theo đơn hàng | Tab panel trong SCR-00 | Import EmployeeRevenueReport component |
| SCR-04 | Tab: Tiền tour | Tab panel trong SCR-00 | Import TourIncomeReport component |
B1.1) SCR-00: Tab Container (Ref: FR-009, DEC-011)
Layout
┌─ Báo cáo doanh số cá nhân ──────────────────────────────────────┐
│ │
│ [ Doanh số theo ngày ✓ ] [ Doanh thu theo đơn hàng ] [ Tiền tour ] │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Nội dung tab đang active │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘Tabs
| # | Tab label | Query param | Component | Lazy load |
|---|---|---|---|---|
| 1 | Doanh số theo ngày | ?tab=daily (default) | EmployeeDailyCommission (build mới) | No (default) |
| 2 | Doanh thu theo đơn hàng | ?tab=revenue | EmployeeRevenueReport (import hiện có) | Yes |
| 3 | Tiền tour | ?tab=tour | TourIncomeReport (import hiện có) | Yes |
Route redirect (bookmark safe)
| Route cũ | Redirect → |
|---|---|
/r/reports/employee_revenue_report_group | /r/reports/employee_daily_commission_group?tab=revenue |
/r/reports/tour_income_report_group | /r/reports/employee_daily_commission_group?tab=tour |
B2) SCR-01: Doanh số theo ngày (Tab default)
Layout
┌─────────────────────────────────────────────────────────────┐
│ [Loại: Commission tư vấn ▼] [◀ 03/2026 ▶] [CN ▼] [CV ▼] │
│ [📥 Tải xuống] │
├─────┬──────────────────┬────────┬────────┬───┬────────┬─────┤
│ Mã │ Họ tên │ 01/03 │ 02/03 │...│ 31/03 │Tổng │
├─────┼──────────────────┼────────┼────────┼───┼────────┼─────┤
│DV220│Nguyễn Thị Giang │3.700.00│4.000.00│ │7.018.00│ xxx │
│DV250│Mai Thùy Dương │3.331.00│2.600.00│ │7.729.00│ xxx │
│DV250│Nguyễn Thị Uyên │ 0 │ 0 │ │ 0 │ 0 │
│ ... │ ... │ ... │ ... │ │ ... │ ... │
└─────┴──────────────────┴────────┴────────┴───┴────────┴─────┘Filter Bar
| # | Component | Type | Default | Behavior |
|---|---|---|---|---|
| 1 | Loại doanh số | QSelect | "Hoa hồng tư vấn" | Options: "Hoa hồng tư vấn", "Tiền tour", "Truy thu commission". Switch → re-fetch data. Khi chọn "Truy thu commission" → ẩn filter chức vụ |
| 2 | Tháng | MonthPickerWithButton | Tháng hiện tại | Nút ◀ ▶ prev/next. Format: MM/YYYY |
| 3 | Chi nhánh | BranchSelect | Tất cả | Multi-select. Empty = tất cả |
| 4 | Chức vụ | JobPositionSelect | Tất cả | Multi-select. Empty = tất cả |
| 5 | Tải xuống | QBtn icon download | — | Click → export Excel |
Pivot Table
| Cột | Width | Align | Format | Sticky |
|---|---|---|---|---|
| Mã NV | 100px | Left | Text | Yes (sticky left) |
| Họ tên | 180px | Left | Text | Yes (sticky left) |
| 01/DD — 31/DD | 110px each | Right | xxx.xxx.xxx (VND) | No |
| Tổng | 130px | Right | xxx.xxx.xxx (VND) bold | Yes (sticky right) |
Đặc tả cột ngày:
- Số cột = số ngày thực tế trong tháng (28/29/30/31)
- Header format:
DD/MM(VD: 01/03, 02/03, ..., 31/03) - Giá trị 0 hiển thị
0(không để trống)
Cell click (drill-down):
- Ô số tiền (cột ngày + cột Tổng): cursor pointer, hover highlight
- Click → mở SCR-02 popup chi tiết
- Click cột ngày → popup filter theo NV + ngày đó
- Click cột Tổng → popup filter theo NV + cả tháng
Sort:
- Mặc định:
employee_codeASC - Click header cột ngày/tổng → sort DESC/ASC
Scroll:
- Horizontal scroll cho cột ngày (sticky Mã NV + Họ tên bên trái, Tổng bên phải)
- Vertical scroll cho danh sách NV
Permission Matrix
| Role | Xem page | Export |
|---|---|---|
| HCNS (được gán report) | ✅ | ✅ |
| BOD | ✅ | ✅ |
| IT Leader/Staff | ✅ | ✅ |
| Roles khác | ❌ | ❌ |
State Matrix
| State | Hiển thị |
|---|---|
| Loading | Skeleton loading trên table area |
| Empty (không có data) | "Không có dữ liệu cho tháng này" centered trong table area |
| Error (query fail) | "Đã xảy ra lỗi. Vui lòng thử lại." + nút Retry |
| No Permission | Route guard redirect (không vào được page) |
B2.1) SCR-02: Popup chi tiết giao dịch ngày (Ref: FR-008, DEC-010)
Layout
┌──────────────────────────────────────────────────────────────────────────┐
│ Chi tiết doanh số — Nguyễn Thị Giang — 01/03/2026 [✕] │
├────┬────────────┬────────────┬───────────────────┬───────────┬──────────┤
│ # │Ngày TT │Mã đơn hàng│Loại ĐH │Mã GD │Nhóm GD │
├────┼────────────┼────────────┼───────────────────┼───────────┼──────────┤
│ 1 │01/03/2026 │DV01009623 │ĐH dịch vụ │TT02071583 │🏦 CK NH │
│ 2 │01/03/2026 │DV01009624 │ĐH dịch vụ │TT02071584 │💵 Tiền mặt│
│ 3 │01/03/2026 │DV01009623 │Truy thu commission│WD02089312 │— │
└────┴────────────┴────────────┴───────────────────┴───────────┴──────────┘
... │Khách hàng │ Doanh thu TƯ VẤN │
├─────────────────────┼──────────────────┤
... │NGUYỄN HƯƠNG │ 1.500.000 │
... │LÊ THỊ LINH │ 2.200.000 │
... │— │ -1.000.000 │ ← số âm, màu đỏCột popup
| # | Cột | Width | Source (Commission) | Source (Truy thu) |
|---|---|---|---|---|
| 1 | Ngày thanh toán | 120px | invoice.paid_at | transaction_request.updated_at |
| 2 | Mã đơn hàng | 130px | order.code (clickable) | Mã đơn gốc bị hoàn (transaction_request.order_id → order.code) |
| 3 | Loại đơn hàng | 170px | "ĐH dịch vụ" / "ĐH mỹ phẩm" | "Truy thu commission" (text đỏ) |
| 4 | Mã giao dịch | 120px | invoice.code | transaction_request.code (fallback: transaction_request.id) |
| 5 | Nhóm giao dịch | 150px | payment_method + icon | — |
| 6 | Khách hàng | 180px | customer.display_name + SĐT + mã KH | — |
| 7 | Doanh thu tư vấn | 150px | amount (dương, align right) | -amount (số âm, màu đỏ, align right) |
Data source
Frontend gọi 2 query song song, merge kết quả:
search_report_employee(ecommerce DB) — commission orders dương- Hasura query trên
transaction+transaction_request(wallet DB) — bút toán truy thu
Lưu ý: Amount trong wallet DB luôn >= 0 (CHECK constraint). Frontend negate giá trị khi render clawback rows.
Hiển thị số âm
| Điều kiện | Style |
|---|---|
amount > 0 | Màu mặc định, align right |
amount < 0 | Màu đỏ, format -xxx.xxx.xxx, align right |
| Loại ĐH = "Truy thu commission" | Text đỏ hoặc badge để phân biệt |
Popup theo dropdown
| Dropdown đang chọn | Click ô → popup hiện |
|---|---|
| Commission tư vấn | Commission orders + bút toán truy thu (merge cả 2) |
| Tiền tour | Chi tiết tour (chỉ query tour, không merge clawback) |
| Truy thu commission | Chỉ bút toán truy thu (chỉ query wallet) |
State
| State | Hiển thị |
|---|---|
| Loading | Skeleton loading trong dialog body |
| Empty | "Không có giao dịch" centered |
| Error | "Đã xảy ra lỗi. Vui lòng thử lại." + nút Retry |
B3) Export Excel Spec
| Field | Giá trị |
|---|---|
| Format | .xlsx |
| Tên file | doanh-so-nv-{MM-YYYY}_{YYYYMMDDHHmmss}.xlsx |
| Sheet name | Doanh số NV |
Columns
| # | Header | Width | Format |
|---|---|---|---|
| 1 | Mã NV | 15 | Text |
| 2 | Họ tên | 25 | Text |
| 3-N | DD/MM/YYYY (mỗi ngày) | 15 | Number #,##0 |
| N+1 | Tổng | 18 | Number #,##0 bold |
Styles
| Element | Style |
|---|---|
| Header row | Bold, background excelHeaderStyle, center align |
| Data — text | Left align |
| Data — number | Right align, format #,##0 |
| Tổng column | Bold |
B7) Copy Text Dictionary
| Key | Text (VI) | Context |
|---|---|---|
page_title | Báo cáo doanh số cá nhân | Page header (hub) |
tab_daily | Doanh số theo ngày | Tab label |
tab_revenue | Doanh thu theo đơn hàng | Tab label |
tab_tour | Tiền tour | Tab label |
type_commission | Hoa hồng tư vấn | Dropdown option |
type_tour | Tiền tour | Dropdown option |
type_clawback | Truy thu commission | Dropdown option |
popup_title | Chi tiết doanh số — {name} — | Popup header |
popup_empty | Không có giao dịch | Popup empty state |
clawback_label | Truy thu commission | Loại ĐH trong popup |
col_employee_code | Mã NV | Table header |
col_employee_name | Họ tên | Table header |
col_total | Tổng | Table header |
btn_download | Tải xuống | Button label |
empty_state | Không có dữ liệu cho tháng này | Empty table |
error_state | Đã xảy ra lỗi. Vui lòng thử lại. | Error state |
btn_retry | Thử lại | Error retry button |
B9) Tooltip Dictionary
| Element | Tooltip text |
|---|---|
| Dropdown "Loại" | Chọn loại doanh số: Hoa hồng tư vấn (tiền hoa hồng thực nhận từ đơn hàng, không tính thanh toán bằng ví) hoặc Tiền tour (tiền từ thực hiện tour dịch vụ) |
| Cột Tổng | Tổng doanh số trong tháng = tổng tất cả ngày |
| Nút Tải xuống | Xuất file Excel chứa toàn bộ dữ liệu đang hiển thị |
| Option "Truy thu commission" | Hiển thị số tiền commission bị thu hồi khi đơn hàng hoàn tiền. Ghi nhận theo ngày duyệt hoàn |
| Ô số tiền (hover) | Click để xem chi tiết giao dịch |
B10) Edge Cases
| # | Case | Behavior |
|---|---|---|
| 1 | NV không có commission/tour nào trong tháng | Vẫn hiển thị row với tất cả cột = 0 (nếu NV thuộc filter branch/job) |
| 2 | Tháng 2 nhuận (29 ngày) | Cột tự động = 29 ngày |
| 3 | Filter không có NV nào match | Hiển thị empty state |
| 4 | NV thuộc nhiều department/branch | Hiển thị 1 row (SQL đã group by user_id) |
| 5 | Commission amount = 0 | Hiển thị 0 (vẫn có row) |
| 6 | Tour money = NULL | Không tính (SQL filter tour_money > 0) |
| 7 | Export khi table empty | Tạo file Excel chỉ có header, không có data rows |
| 8 | Tháng chưa đến (future) | Hiển thị table trống — data chưa có |
| 9 | Switch loại đang loading | Cancel request cũ, fetch request mới |
| 10 | NV bị soft delete giữa tháng | Không hiển thị (SQL filter deleted_at IS NULL) |
| 11 | Click ô = 0 | Mở popup rỗng: "Không có giao dịch" |
| 12 | Click ô truy thu, order_id = NULL | Cột "Mã đơn hàng" hiện — |
| 13 | transaction_request.code = NULL | Dùng transaction_request.id (UUID) làm fallback |
| 14 | Click ô khi dropdown = "Tiền tour" | Popup chỉ hiện chi tiết tour, không merge clawback |
| 15 | Click ô khi dropdown = "Truy thu commission" | Popup chỉ hiện bút toán truy thu |
| 16 | Chọn "Truy thu commission" → filter chức vụ | Ẩn/disable filter chức vụ (wallet DB không có department) |
| 17 | Truy cập route cũ /r/reports/employee_revenue_report_group | Redirect → ?tab=revenue, hiển thị tab "Doanh thu theo đơn hàng" |
| 18 | Truy cập route cũ /r/reports/tour_income_report_group | Redirect → ?tab=tour, hiển thị tab "Tiền tour" |
| 19 | URL có ?tab=invalid | Fallback về tab default "Doanh số theo ngày" |
| 20 | Switch tab giữ filter state | Mỗi tab có filter riêng (không share), giữ state khi switch qua lại |