Skip to content

v3.1 — 30/04/2026

Thay đổiSectionẢnh hưởng
Thêm B2.1A nguyên tắc UX bắt buộcB2.1AUI/UX + FE
Thêm variant wireframes A-F và ma trận Role × VariantB2.2 / B2.8AUI/UX + FE + QA
Đổi phần rà soát rủi ro sang ngôn ngữ public-friendly B-QUALITYB-QUALITYAll

Đặc tả giao diện (UI Spec) — Tổng hợp tài chính đơn hàng

Tham chiếu: PRD v3.0 | Phiên bản UI Spec: v3.1 | Ngày: 30/04/2026


Mục đích: mô tả màn hình, thay đổi, trạng thái, tương tác, copy hiển thị cho FE/UI/QA, đủ căn cứ triển khai và kiểm thử thống nhất. Đọc trước: decision-brief.mdB-PREB0B1B2 SCR-01/02B-POSTB-QUALITY. Văn phong: theo templates/_LANGUAGE_RULES.md + _STYLE_GUIDE.md. CTA dùng tiếng Việt; format VN.

Tài liệu đầu vào chuẩn

FileVai tròNếu xung đột
source-of-truth.mdTruth chuẩn + Phương án đã chốtƯu tiên cao nhất
evidence-pack.mdLayout UI hiện tại, ứng viên reuseƯu tiên bằng chứng code/screen
prd.mdFR, lifecycle, công thứcTheo truth đã khóa

B-PRE) Discovery checklist

B-PRE.1) Tìm component/page hiện có liên quan

bash
# Tìm OrderReceiverInfo và XCard pattern
grep -rE "OrderReceiverInfo|XCard.*section" diva-admin/src/modules/ecommerce/components/order/

# Tìm App Settings Order pattern
grep -rE "AppSettingGroups\.order|SystemTable" diva-admin/src/modules/settings/

# Tìm permission pattern
grep -rE "useGlobalStore|hasPermission|view_financial" diva-admin/src/modules/ecommerce/

# Tìm GraphQL action query pattern
grep -rE "GetListTourLimit|action.*query" diva-admin/src/modules/ecommerce/graphql/

B-PRE.2) Bảng inventory đã check

Hạng mụcĐã check?File / màn hiện cóGhi chú
Page / route hiện có/e/service-order/:id qua OrderDetail.tsxReuse
Component reusableOrderReceiverInfo.tsx, XCard, XDetailLayoutMount section mới vào sidebar
Form/dialog liên quanApp Settings Order: SystemTable.tsx, SettingItemEditorExtend group order.fixed_cost
Field/cột hiện cóSidebar không có dòng tài chính riêng; Tabs có Thanh toán + Hoa hồngTổng hợp vào sidebar
CTA hiện cóSidebar có CTA chuyển tab; không có CTA tài chínhThêm "Thử lại" cho error state
Filter / searchSidebar không filterN/A
State hiện cóOrderDetail có loading skeleton + error toastSection tài chính tự có state riêng
Permission gatinguseGlobalStore.hasPermission(moduleId, actionId)Reuse cho 2 action mới
NotificationOrder detail không có notification kèmN/A day-1
Export columnsKhông có export sidebarN/A day-1
Tooltip/hintSidebar có tooltip cho BRANCH, PROMOTIONThêm tooltip cho 8 chỉ số
Mobile/responsive.detail.is-inside 100% widthSection mới phải responsive
Analytics eventsOrder detail có order_detail_viewed eventThêm financial_summary_viewed, loss_warning_shown

B-PRE.3) Phân loại reuse

Phần featurePhân loạiEvidenceDelta cần
Section sidebar mới🔧 ExtendOrderReceiverInfo.tsx:569-1452 đã có cấu trúc XCard + sectionMount ServiceOrderFinancialSummary trước section DỮ LIỆU
App Settings group🔧 ExtendAppSettingGroups.order chỉ có taxThêm fixed_cost group vào enum + render
Permission gating✅ ReuseuseGlobalStore.hasPermission() đã cóGọi với 2 action mới
Error/loading state✅ ReuseXCard có sẵn skeleton + error patternApply cho section mới
Tooltip pattern✅ Reuseq-tooltip + i18n keyThêm 8 tooltip mới
Cảnh báo lỗ banner🆕 Build mớiChưa có pattern banner cảnh báo trong sidebarComponent mới LossWarningBanner

B0) Hiện trạng UI và quy ước thay đổi (As-Is + Delta Contract)

B0.1) Bảng kiểm kê đầy đủ

UI IDMàn / routeSectionBlock / field / actionThứ tựHành vi hiện tạiPermissionMobile?Delta StatusHành vi đíchEvidence
SCR-01-BLK-01/e/service-order/:idSidebar trái → HeaderOrder code + status + customer name1Hiển thị ORD-{code}, status badge, tên kháchAll authenticated100% widthKEEPKhông đổiOrderReceiverInfo.tsx:569-650
SCR-01-BLK-02/e/service-order/:idSidebar trái → POS create/refundCTA refund (POS only)2Hiện CTA "Hoàn tiền" cho user POSPOS roleNhư cũKEEPKhông đổiOrderReceiverInfo.tsx:680-720
SCR-01-BLK-03/e/service-order/:idSidebar trái → ACCOUNTField tài khoản KH3Hiện account info + editAllNhư cũKEEPKhông đổiOrderReceiverInfo.tsx:780-830
SCR-01-BLK-04/e/service-order/:idSidebar trái → CONTACTSố điện thoại + địa chỉ4Hiển thị + click-to-callAllNhư cũKEEPKhông đổiOrderReceiverInfo.tsx:870-920
SCR-01-BLK-05/e/service-order/:idSidebar trái → HOA HỒNGField hoa hồng tổng5Hiển thị tổng commissioncommission.viewNhư cũKEEPKhông đổiOrderReceiverInfo.tsx:980-1020
SCR-01-BLK-06/e/service-order/:idSidebar trái → BRANCHTên chi nhánh6Hiện branch name + tooltipAllNhư cũKEEPKhông đổiOrderReceiverInfo.tsx:1080-1120
SCR-01-BLK-07/e/service-order/:idSidebar trái → PROMOTIONKhuyến mãi áp dụng7Hiển thị voucher/discountAllNhư cũKEEPKhông đổiOrderReceiverInfo.tsx:1180-1220
SCR-01-BLK-FIN/e/service-order/:idSidebar trái → TÀI CHÍNH (MỚI)8 chỉ số tài chính + cảnh báo lỗ8 (mới)view_financial_summaryview_financial_pnlResponsiveNEWMount ServiceOrderFinancialSummary(mới)
SCR-01-BLK-08/e/service-order/:idSidebar trái → DỮ LIỆUCreated at, updated at9 (cũ là 8)Metadata auditAllNhư cũMOVEVẫn KEEP nội dung, đẩy thứ tự xuống 1 vị tríOrderReceiverInfo.tsx:1320-1360
SCR-01-BLK-09/e/service-order/:idSidebar trái → LIÊN KẾTRelated orders10 (cũ là 9)Click navigateAllNhư cũMOVEVẫn KEEP nội dung, đẩy thứ tự xuống 1 vị tríOrderReceiverInfo.tsx:1400-1440
SCR-02-BLK-01/s/app-settings/orderSettings tableField "Thuế VAT"1Editor cho taxAdminMobile drawerKEEPKhông đổiSystemTable.tsx:36-55
SCR-02-BLK-FIN/s/app-settings/orderSettings tableField "Tỷ lệ chi phí cố định (%)" (MỚI)2 (mới)AdminMobile drawerNEWEditor cho order.fixed_cost.rate, validate 0-100(mới)

B0.2) Từ điển Delta Status

StatusÝ nghĩaEvidence bắt buộc
KEEPGiữ nguyênGhi rõ vẫn hiển thị ở target wireframe
MOVEĐổi vị tríUX flow + ghi rõ trước/sau
NEWThêm mớiPRD FR/AC ref

B0.3) Tiêu chí hoàn thành B0

  • [x] 100% UI hiện hữu của 2 màn đã inventory ở B0.1 (11 dòng)
  • [x] Mọi UI ID có Delta Status
  • [x] Mọi dòng có Evidence file:line
  • [x] 2 dòng MOVE (BLK-08, BLK-09) ghi rõ chỉ đổi thứ tự, không đổi behavior
  • [x] Target wireframe ở B2 vẽ cả vùng KEEP xung quanh

B0.4) Ma trận Field × Surface (BẮT BUỘC)

Mọi chỉ số tài chính + setting fixed cost phải xuất hiện đủ surfaces.

FieldList/SectionDetail/FormPopup/ModalExportSearchFilterPermissionMobileNotificationDefaultValidationTooltip
revenue (Doanh thu)Có (sidebar dòng 1)N/AN/AN/A day-1N/AN/Asummary ∨ pnlHiển thịN/ATừ order.amountNULL → "Tổng giá trị đơn hàng (chưa trừ giảm giá)"
paid (Đã thu)Có (sidebar dòng 2)N/AN/AN/A day-1N/AN/Asummary ∨ pnlHiển thịN/AAggregate parent invoiceCó thể âm khi refund"Tổng tiền khách đã thanh toán, gồm cả hoàn tiền"
debt (Còn nợ)Có (sidebar dòng 3)N/AN/AN/A day-1N/AN/Asummary ∨ pnlHiển thịN/ATừ debt_amount computedNULL → 0đ"Số tiền khách còn nợ"
commission (Hoa hồng)Có (sidebar dòng 4 — PnL only)N/AN/AN/AN/AN/Apnl onlyHiển thịN/ASUM order_commissions.amountNULL → 0đ"Tổng hoa hồng phải trả nhân viên"
tour_cost (Tour)Có (sidebar dòng 5 — PnL only)N/AN/AN/AN/AN/Apnl onlyHiển thịN/ASUM tour_moneyNULL → 0đ"Tổng tiền tour KTV"
fixed_cost (Chi phí cố định)Có (sidebar dòng 6 — PnL only, ẩn nếu rate=NULL)N/AN/AN/AN/AN/Apnl onlyHiển thịN/Aorder.fixed_cost_amountNULL → ẩn dòng"Overhead vận hành phân bổ theo tỷ lệ thực thu cao nhất"
profit_estimated (Lợi nhuận tạm tính)Có (sidebar dòng 7 — PnL only)N/AN/AN/AN/AN/Apnl onlyHiển thịN/AFORMULA-007NULL → "Doanh thu − chi phí đã chốt. Chưa gồm chi phí vật tư."
margin_estimated (Tỷ suất)Có (sidebar dòng 8 — PnL only)N/AN/AN/AN/AN/Apnl onlyHiển thịN/AFORMULA-008revenue=0 → "Lợi nhuận tạm tính / Doanh thu × 100%"
loss_warning (Banner cảnh báo lỗ)Có (banner trong section, dưới tiêu đề)N/AN/AN/AN/AN/Apnl onlyResponsive (banner stacked)N/A day-1Auto hiện khi profit_estimated < 0N/AN/A (icon + copy đầy đủ)
fixed_cost_rate (Setting App)N/ACó (App Settings Order)N/AN/AN/AN/AAdmin (settings.update)Drawer trên mobileN/ANULL (chưa cấu hình)NUMERIC 0-100, max 2 decimals"Tỷ lệ % thực thu phân bổ cho chi phí cố định (mặt bằng, điện nước...)"

B0.5) Ma trận State × Screen

Màn / khốiDefaultLoadingEmptyError (retry)No permissionPartial / pending
SCR-01 Section TÀI CHÍNHHiện 3-8 dòng tùy permissionSkeleton 3 dòng (placeholder pulsing)N/A (đơn luôn có data)"Không thể tải tài chính. Vui lòng thử lại." + nút Thử lạiẨN section hoàn toàn (không leak)Muted note "Chi phí vật tư đang phát triển — chưa cộng vào lợi nhuận tạm tính" cuối section
SCR-01 Banner cảnh báo lỗHiện warning banner "Đơn lỗ tạm tính: −250.000đ. Chưa gồm chi phí vật tư."N/AẨn banner khi profit_estimated >= 0N/AẨn banner (không có pnl permission)N/A
SCR-02 Field "Tỷ lệ chi phí cố định (%)"Hiện input số với value hiện tại hoặc placeholder "Chưa cấu hình"Spinner trong input khi savePlaceholder "Chưa cấu hình" khi value=NULLError banner "Không thể lưu. Vui lòng thử lại."Ẩn field (Admin only)N/A

B1) Bản đồ màn hình và hành trình

B1.1) Danh sách màn

SCRTênRouteLoạiMô tả
SCR-01Chi tiết đơn dịch vụ/e/service-order/:id🔧 ExtendMount section TÀI CHÍNH trong sidebar
SCR-02App Settings Order/s/app-settings/order🔧 ExtendThêm field "Tỷ lệ chi phí cố định (%)"
SCR-03Dynamic Permission Matrix/s/permission-matrix✅ ReuseCấp/thu hồi 2 action mới (không đổi UI)

B1.2) Hành trình end-to-end

Hành trình theo vai trò:

Vai tròEntry pointĐường mànKết quả
Admin/s/app-settings/orderSCR-02 → lưu setting → đơn mới có rateCấu hình áp dụng cho đơn mới
Lễ tân/e/service-order/:idSCR-01 Variant A (3 dòng summary)Biết doanh thu/đã thu/còn nợ
Manager/e/service-order/:idSCR-01 Variant A mặc định; Variant B nếu được cấp view_financial_pnlĐốc thu công nợ; chỉ xem PnL khi được cấp quyền
Kế toán/e/service-order/:idSCR-01 Variant A mặc định; Variant B nếu được cấp view_financial_pnlĐối soát thu/nợ; không mặc định thấy chi phí/lợi nhuận
Admin/BOD/e/service-order/:idSCR-01 Variant B (8 dòng + cảnh báo lỗ nếu có)Phát hiện đơn lỗ

B1.3) Phạm vi thay đổi tối thiểu

Đụng 2 màn hiện hữu (SCR-01, SCR-02) — không phải full new screen.

Màn hiện cóBằng chứng hiện trạngThay đổiVị trí updateVì sao đủ
/e/service-order/:idOrderReceiverInfo.tsx:569-1452Mount component sidebar mớiTrước section DỮ LIỆU (sau PROMOTION)Sidebar đã có cấu trúc section, chỉ thêm 1 section, không cần redesign
/s/app-settings/orderSystemTable.tsx:36-55Thêm 1 field input số trong group orderSau field "Thuế VAT"Settings table đã render dynamic theo enum, chỉ thêm enum + editor

B2) SCR-01: Chi tiết đơn dịch vụ — Section TÀI CHÍNH

B2.1) Ngữ cảnh nghiệp vụ

Câu hỏiCần chốt
Ai dùng?Lễ tân + Manager + Kế toán mặc định xem 3 dòng summary; Admin + BOD và user được cấp view_financial_pnl xem 8 dòng + cảnh báo lỗ
Vào màn để quyết định gì?Lễ tân: nhắc khách thanh toán; Manager: đốc thu công nợ; BOD: kiểm tra đơn lỗ
Dữ liệu chính8 chỉ số từ GetOrderFinancialSummary action
CTA chính / phụKhông CTA (read-only); CTA phụ "Thử lại" khi error
Điều không hiểu nhầm"Lợi nhuận tạm tính" ≠ "Lợi nhuận kế toán" (phải có copy "Chưa gồm chi phí vật tư")

B2.1A) Nguyên tắc UX bắt buộc

Nguyên tắcQuyết định cho UI/UX
Mục tiêu 5 giâyKhi mở đơn, user phải biết ngay: đơn còn nợ không, đã thu bao nhiêu, có rủi ro lỗ tạm tính không.
Thứ tự ưu tiên thông tinCòn nợLợi nhuận tạm tínhĐã thuDoanh thu → nhóm chi phí.
Dữ liệu nhạy cảmKhông hiển thị placeholder khóa, blur, hoặc dòng bị che cho cost/profit/margin. Nếu không có view_financial_pnl, các dòng PnL không render.
Vật tư pendingKhông coi chi phí vật tư là 0đ. Với user PnL, luôn hiện note "Chi phí vật tư đang phát triển..." khi API trả note_pending_material=true.
Tên gọi lợi nhuậnLuôn ghi "Lợi nhuận tạm tính"; không rút gọn thành "Lợi nhuận" trong label, tooltip, analytics hay test case.
Đơn đã hủyVẫn hiển thị theo đúng permission để đối soát, kèm trạng thái "Đơn đã hủy - chỉ đối soát"; không thêm CTA thao tác tài chính.
Thiết kế thị giácUI spec chỉ khóa intent: summary, breakdown, warning, muted note, emphasis. Màu, font, spacing dùng design token hiện có của Quasar/Diva UI.

B2.2) Bố cục (wireframe)

Variant B - User có PnL (view_financial_pnl) gắn vào UI hiện tại:

text
/e/service-order/:id

┌────────────────────────────────────────────────────────────────────────┐
│ [Header — Order code + status + customer name]   (giữ nguyên)         │
│ [POS create/refund block]                        (giữ nguyên)         │
│ [ACCOUNT / CONTACT / HOA HỒNG / BRANCH / PROMOTION]   (giữ nguyên)    │
│                                                                        │
│ >>> SECTION TÀI CHÍNH MỚI <<<                                          │
│ ┌──────────────────────────────────────────────┐                      │
│ │ TÀI CHÍNH                              ⓘ      │                      │
│ │ ⚠ Đơn lỗ tạm tính: −250.000đ.                │ ← banner (pnl only,  │
│ │   Chưa gồm chi phí vật tư.       [×]          │   khi profit < 0)    │
│ │                                                │                      │
│ │ Doanh thu              3.500.000đ              │ ← summary (3 dòng)   │
│ │ Đã thu                 1.500.000đ              │                      │
│ │ Còn nợ                 2.000.000đ              │                      │
│ │ ────────────────────────────────────           │                      │
│ │ Hoa hồng                 350.000đ              │ ← pnl (5 dòng tiếp)  │
│ │ Chi phí tour             180.000đ              │                      │
│ │ Chi phí cố định          225.000đ      ⓘ      │                      │
│ │ Lợi nhuận tạm tính     2.745.000đ              │                      │
│ │ Tỷ suất                   78,43%               │                      │
│ │                                                │                      │
│ │ ℹ Chi phí vật tư đang phát triển              │ ← muted note (pnl)   │
│ │   — chưa cộng vào lợi nhuận tạm tính.         │                      │
│ └──────────────────────────────────────────────┘                      │
│                                                                        │
│ [DỮ LIỆU]                                  (giữ nguyên, đẩy xuống)    │
│ [LIÊN KẾT]                                 (giữ nguyên, đẩy xuống)    │
└────────────────────────────────────────────────────────────────────────┘

Variant A - User chỉ có summary (view_financial_summary):

text
┌──────────────────────────────────────────────┐
│ TÀI CHÍNH                              ⓘ      │
│ Doanh thu              3.500.000đ              │
│ Đã thu                 1.500.000đ              │
│ Còn nợ                 2.000.000đ              │
└──────────────────────────────────────────────┘

Variant C - Không có quyền tài chính:

text
[PROMOTION]                      (giữ nguyên)
[DỮ LIỆU]                        (giữ nguyên)
[LIÊN KẾT]                       (giữ nguyên)

Ghi chú: Section TÀI CHÍNH không mount, không skeleton, không CTA xin quyền.

Variant D - Đang tải / lỗi API:

text
┌──────────────────────────────────────────────┐
│ TÀI CHÍNH                              ⓘ      │
│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒                 │
│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒                 │
│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒                 │
└──────────────────────────────────────────────┘

Khi lỗi:
┌──────────────────────────────────────────────┐
│ TÀI CHÍNH                              ⓘ      │
│ Không thể tải tài chính. Vui lòng thử lại.   │
│ [Thử lại]                                     │
└──────────────────────────────────────────────┘

Variant E - Đơn đã hủy:

text
┌──────────────────────────────────────────────┐
│ TÀI CHÍNH                              ⓘ      │
│ [Icon trạng thái] Đơn đã hủy - chỉ đối soát   │ ← badge trạng thái
│                                                │
│ Doanh thu              3.500.000đ              │ ← readonly
│ Đã thu                 1.500.000đ              │
│ Còn nợ                       0đ                │
│ ...                                            │
└──────────────────────────────────────────────┘

Variant F - Đơn cũ chưa có fixed cost rate:

text
┌──────────────────────────────────────────────┐
│ TÀI CHÍNH                              ⓘ      │
│ Doanh thu              3.500.000đ              │
│ Đã thu                 1.500.000đ              │
│ Còn nợ                 2.000.000đ              │
│ ────────────────────────────────────           │
│ Hoa hồng                 350.000đ              │
│ Chi phí tour             180.000đ              │
│ Lợi nhuận tạm tính     2.970.000đ              │
│ Tỷ suất                   84,86%               │
│                                                │
│ ℹ Đơn này tạo trước khi cấu hình chi phí cố    │
│   định nên không có dòng "Chi phí cố định".    │
└──────────────────────────────────────────────┘

B2.3) Phân loại reuse + điểm update

Phân loạiFile hiện cóVị trí updateLý do
🔧 ExtendOrderReceiverInfo.tsx:569-1452Trong cùng XCard lớn, sau PROMOTION (line ~1220), trước DỮ LIỆU (line ~1320)Sidebar đã có chuỗi section, chèn vào đúng vị trí logic — sau scope nghiệp vụ (promotion), trước metadata (dữ liệu/liên kết)

B2.4) Quy ước field / cột / CTA mới hoặc thay đổi

Ref về B0.4 Field × Surface matrix.

Field/CTALoạiHiển thị ở đâuMặc địnhValidationĐiều kiện hiển thịTooltipẢnh hưởng export/search
revenueRead-only sốSidebar dòng 1Từ order.amountNULL → Có ≥1 financial action"Tổng giá trị đơn hàng (chưa trừ giảm giá)"N/A day-1
paidRead-only sốSidebar dòng 2Aggregate parent invoiceCó thể âm khi refundCó ≥1 financial action"Tổng tiền khách đã thanh toán"N/A
debtRead-only sốSidebar dòng 3order.debt_amountNULL → 0đCó ≥1 financial action"Số tiền khách còn nợ"N/A
commissionRead-only sốSidebar dòng 4SUM order_commissions.amountNULL → 0đview_financial_pnl"Tổng hoa hồng phải trả nhân viên"N/A
tour_costRead-only sốSidebar dòng 5SUM tour_moneyNULL → 0đview_financial_pnl"Tổng chi phí tour KTV"N/A
fixed_costRead-only sốSidebar dòng 6order.fixed_cost_amountNULL → ẩn dòng; 0 → "0đ"view_financial_pnl AND fixed_cost_amount IS NOT NULL"Overhead vận hành theo tỷ lệ thực thu cao nhất (high-water mark)"N/A
profit_estimatedRead-only sốSidebar dòng 7FORMULA-007NULL → ; <0 → dùng negative-value stateview_financial_pnl"Doanh thu − chi phí đã chốt. Chưa gồm chi phí vật tư."N/A
margin_estimatedRead-only %Sidebar dòng 8FORMULA-008revenue=0 → view_financial_pnl"Lợi nhuận tạm tính / Doanh thu × 100%"N/A
Nút "Thử lại"CTA secondaryTrong error stateAPI lỗi"Thử lại tải tài chính"

B2.5) Thanh lọc

#ComponentLoạiMặc địnhHành viReset dây chuyền
Không có filter trong section (read-only theo order)

B2.6) Bảng / nội dung

Section này không phải table-first → dùng "Content blocks": 1 banner cảnh báo (optional) + 8 rows label/value + 1 note pending (optional).

BlockStyleNội dung
Tiêu đề "TÀI CHÍNH"Header section theo style section hiện có, có icon ⓘ tooltipCố định
Banner cảnh báo lỗDùng warning severity/token hiện có, có icon cảnh báo và nút đóngAnimation ngắn khi mount; không dùng màu hard-code trong spec
Row label-valueLabel trái, value phải, số tiền dễ scan và không làm vỡ layout sidebar3 rows với summary; 8 rows với PnL
DividerTách nhóm summary và nhóm chi phí/PnLChỉ hiện khi user có pnl
Note pendingMuted note/token phụ, có icon thông tinChỉ hiện khi user có pnl và note_pending_material=true

B2.7) Quy ước tương tác

Tình huốngHành vi
Mount sectionTự động fetch action GetOrderFinancialSummary({order_id}) khi component mount
Refresh đơn (edit/payment)Refetch action khi route.query.refresh thay đổi (theo pattern OrderDetail hiện có)
Đóng banner cảnh báo lỗBấm × → banner ẩn cho session hiện tại; mở lại đơn → banner hiện lại (không persist localStorage)
Tooltip hoverHover ⓘ icon 300ms delay → hiện tooltip
Permission revoke giữa sessionLần fetch tiếp theo → BE trả 403 → FE ẩn section, KHÔNG show error toast
Click vào sốKhông có click action (read-only)

B2.8) Ma trận phân quyền

PortalVai tròAction cầnHiển thịKhi thu hồi quyền
adminLễ tân (mặc định seed)view_financial_summary3 dòng summarySection ẩn sau refetch tiếp theo
adminManager chi nhánh (mặc định seed)view_financial_summary3 dòng summarySection ẩn sau refetch
adminKế toán (mặc định seed)view_financial_summary3 dòng summarySection ẩn sau refetch
adminAdmin (mặc định seed)view_financial_pnl8 dòng đầy đủ + banner lỗ + note pendingSection ẩn sau refetch
adminBOD (mặc định seed)view_financial_pnl8 dòng đầy đủ + banner lỗ + note pendingSection ẩn sau refetch
adminKhác (chưa cấp)Ẩn section hoàn toàn (không skeleton, không leak)
pos/crm/staffTất cả role(chưa cấp day-1)Ẩn sectionCó thể cấp sau qua Dynamic Permission

B2.8A) Ma trận Role × Variant

Role / nhóm userPermission mặc địnhVariant UIGhi chú bắt buộc
Lễ tânview_financial_summaryVariant AKhông thấy chi phí, lợi nhuận, tỷ suất, banner lỗ
Manager chi nhánhview_financial_summaryVariant AChỉ xem PnL nếu Admin cấp thêm view_financial_pnl
Kế toánview_financial_summaryVariant AChỉ xem PnL nếu Admin cấp thêm view_financial_pnl
Adminview_financial_summary + view_financial_pnlVariant BThấy đủ PnL, banner lỗ, note vật tư pending
BODview_financial_summary + view_financial_pnlVariant BThấy đủ PnL, banner lỗ, note vật tư pending
User không có quyền tài chínhVariant CSection không mount, không skeleton, không toast
User PnL mở đơn cũ fixed_cost_rate=NULLview_financial_pnlVariant FẨn dòng "Chi phí cố định", hiện note giải thích

B2.9) Ma trận trạng thái (ref B0.5)

Trạng tháiHiển thị
Đang tảiSkeleton 3 rows pulsing (skeleton match summary layout)
RỗngN/A (đơn luôn có data financial)
Lỗi API"Không thể tải tài chính. Vui lòng thử lại." + nút "Thử lại"
Không quyềnẨn section hoàn toàn (route guard + permission check ở FE)
Đơn đã hủySection hiện theo đúng quyền + badge "Đơn đã hủy - chỉ đối soát"
Vật tư pendingMuted note "Chi phí vật tư đang phát triển..." cuối section

B2.10) Phản hồi sau thao tác

Hành độngPhản hồi UICopy mẫuHành động tiếp
Mở đơn (mount)Skeleton → fade-in dataHiển thị section
API lỗiInline error trong section"Không thể tải tài chính. Vui lòng thử lại."Bấm "Thử lại" → refetch
Đóng banner lỗAnimation fade-out 150msBanner ẩn cho session
Hover tooltipTooltip popover(theo dictionary B9)
Permission thay đổi (admin cấp/thu hồi)Refetch order detail tự độngSection hiện/ẩn theo quyền mới

B2.11) Mapping UI theo vòng đời

Đơn dịch vụ có lifecycle: Bản nháp → Đã xác nhận → Đang phục vụ → Hoàn thành → Đã thanh toán hoặc → Đã hủy. Section TÀI CHÍNH hiển thị mọi state.

Trạng thái đơnCTA hiển thịField sửa đượcBadge / copy
Bản nháp (status='draft')Không (read-only)KhôngBadge trạng thái "Đơn chưa xác nhận" dùng muted state
Đã xác nhận (status='confirmed')KhôngKhôngKhông badge
Hoàn thành (status='completed')KhôngKhôngKhông badge
Đã thanh toán đủ (debt=0)KhôngKhôngBadge trạng thái "Đã thanh toán" trên dòng "Còn nợ"
Đã hủy (status='cancelled')KhôngKhôngBadge trạng thái "Đơn đã hủy - chỉ đối soát" trên đầu section

B3) Luồng người dùng

Luồng 1 — Lễ tân nhắc khách thanh toán (đường thành công)

Lễ tân mở /e/service-order/ORD-202604-0123
  → Section TÀI CHÍNH skeleton 1 giây
  → Hiện: Doanh thu 3.500.000đ / Đã thu 1.500.000đ / Còn nợ 2.000.000đ
  → Lễ tân thấy còn nợ → bấm CONTACT block → click số ĐT
  → Gọi khách nhắc thanh toán

Luồng 2 — Admin/BOD phát hiện đơn lỗ

BOD hoặc user có `view_financial_pnl` mở đơn → section hiện đầy đủ 8 dòng
  → Warning banner "Đơn lỗ tạm tính: −250.000đ. Chưa gồm chi phí vật tư."
  → BOD bấm tab "Hoa hồng" để check chi tiết
  → Phát hiện hoa hồng KTV cao bất thường → kiểm tra với Manager

Luồng 3 — Permission revoked giữa session (lỗi)

Manager đang xem đơn (3 dòng summary)
  → Admin vào Permission Matrix thu hồi `view_financial_summary` của Manager
  → Manager refresh đơn (F5 hoặc đổi tab)
  → API trả 403 → FE ẩn section hoàn toàn (không error toast)
  → Manager hỏi Admin → Admin cấp lại quyền → refresh → section hiện lại

Luồng 4 — Đơn đã hủy

Manager mở đơn ORD-202604-0099 (đã hủy)
  → Section TÀI CHÍNH hiện theo quyền hiện tại của Manager
  → Nếu chỉ có summary: hiện 3 dòng summary + badge "Đơn đã hủy - chỉ đối soát"
  → Nếu được cấp pnl: hiện đủ 8 dòng + badge "Đơn đã hủy - chỉ đối soát"
  → Tất cả số readonly, không có CTA action

B4) Đặc tả thông báo

Điều kiện kích hoạtKênhMẫu nội dungChống trùng
(Day-1: KHÔNG có notification)
Phase 2: đơn lỗ phát hiệnEmail gửi BOD"Đơn {code} có lợi nhuận tạm tính âm: {amount}đ"1 email/đơn/ngày

B5) Đặc tả xuất dữ liệu

Day-1: KHÔNG export. Phase 2: export Excel financial summary cho Manager (báo cáo theo tháng).


B6) Từ điển nội dung hiển thị

KeyTiếng ViệtEN (i18n)Ngữ cảnh
financial.section.titleTài chínhFinancialHeader section sidebar
financial.row.revenueDoanh thuRevenueSidebar row 1
financial.row.paidĐã thuPaidSidebar row 2
financial.row.debtCòn nợDebtSidebar row 3
financial.row.commissionHoa hồngCommissionSidebar row 4 (pnl)
financial.row.tour_costChi phí tourTour costSidebar row 5 (pnl)
financial.row.fixed_costChi phí cố địnhFixed costSidebar row 6 (pnl)
financial.row.profit_estimatedLợi nhuận tạm tínhProfit estimatedSidebar row 7 (pnl)
financial.row.margin_estimatedTỷ suấtMarginSidebar row 8 (pnl)
financial.banner.lossĐơn lỗ tạm tính: −{amount}đ. Chưa gồm chi phí vật tư.Estimated loss order: −{amount}. Material cost not included.Banner cảnh báo
financial.note.pending_materialChi phí vật tư đang phát triển — chưa cộng vào lợi nhuận tạm tính.Material cost in development — not included in estimated profit.Note cuối section (pnl)
financial.badge.cancelledĐơn đã hủy - chỉ đối soátCancelled - for reconciliation onlyBadge đầu section; icon trạng thái do UI kit render
financial.error.fetch_failedKhông thể tải tài chính. Vui lòng thử lại.Failed to load financials. Please retry.Error state
financial.cta.retryThử lạiRetryCTA error
settings.field.fixed_cost.labelTỷ lệ chi phí cố định (%)Fixed cost rate (%)App Settings field
settings.field.fixed_cost.placeholderChưa cấu hìnhNot configuredInput placeholder
settings.field.fixed_cost.hintTỷ lệ % thực thu phân bổ cho chi phí cố địnhPercentage of paid amount allocated for fixed costHint dưới input
settings.field.fixed_cost.error.rangeTỷ lệ phải trong khoảng 0-100%Rate must be between 0-100%Validation error

B7) Sự kiện phân tích

Sự kiệnĐiều kiện kích hoạtThuộc tínhKPI liên quan
financial_summary_viewedUser mở SCR-01 và section TÀI CHÍNH render thành cônguser_role, permission_level (summary/pnl), order_id, is_cancelledTime-to-financial-info
financial_loss_warning_shownBanner cảnh báo lỗ renderorder_id, loss_amount, user_roleSố case BOD phát hiện đơn lỗ
financial_loss_warning_dismissedUser bấm × đóng bannerorder_id, time_visible_msUX feedback
financial_summary_fetch_failedAPI trả errororder_id, error_codeAPI performance
settings_fixed_cost_rate_updatedAdmin lưu rate mớiold_value, new_valueAudit

B8) Quy tắc responsive và khả năng truy cập

Responsive

BreakpointHành vi
Desktop (≥1024px)Sidebar 404px width, section TÀI CHÍNH nằm trong scroll-y nếu sidebar overflow
Tablet (768-1023px)Sidebar thu hẹp theo layout hiện có; typography dùng responsive token, layout không đổi
Mobile (<768px).detail.is-inside 100% width, section TÀI CHÍNH stack full-width, label-value vẫn flex space-between (không stack vertical)

Accessibility / keyboard

Vấn đềQuy tắc
Thứ tự focusSection nằm trong tab order tự nhiên của sidebar, sau PROMOTION
Banner cảnh báo lỗrole="alert" + aria-live="polite"
Tooltip ⓘaria-label="Xem giải thích {chỉ số}" cho icon
Nút × đóng banneraria-label="Đóng cảnh báo"
Số tiềnĐọc bằng screen reader: "Doanh thu, ba triệu năm trăm ngàn đồng" (i18n locale vi-VN)
Skeleton loadingaria-busy="true" trong khi loading

B9) Từ điển tooltip

MànField/IconNội dung tooltipĐiều kiện hiện
SCR-01Tiêu đề "TÀI CHÍNH" ⓘ"Tổng hợp tài chính của 1 đơn dịch vụ. Chưa gồm chi phí vật tư."Hover icon ⓘ
SCR-01Doanh thu ⓘ (nếu cần)"Tổng giá trị đơn hàng (chưa trừ giảm giá đã ghi vào order)"Hover hàng
SCR-01Đã thu ⓘ"Tổng tiền khách đã thanh toán, gồm cả hoàn tiền đã ghi nhận"Hover hàng
SCR-01Còn nợ ⓘ"Số tiền khách còn nợ theo order.debt_amount"Hover hàng
SCR-01Hoa hồng ⓘ"Tổng hoa hồng phải trả nhân viên đã ghi nhận"Hover hàng (pnl)
SCR-01Chi phí tour ⓘ"Tổng chi phí tour KTV theo project_task_assignee.tour_money"Hover hàng (pnl)
SCR-01Chi phí cố định ⓘ"Overhead vận hành phân bổ theo tỷ lệ % thực thu CAO NHẤT trong lịch sử (high-water mark — không giảm khi refund)"Hover hàng (pnl)
SCR-01Lợi nhuận tạm tính ⓘ"Doanh thu − Hoa hồng − Chi phí tour − Chi phí cố định. CHƯA gồm chi phí vật tư."Hover hàng (pnl)
SCR-01Tỷ suất ⓘ"Lợi nhuận tạm tính / Doanh thu × 100%"Hover hàng (pnl)
SCR-02Tỷ lệ chi phí cố định (%) ⓘ"Tỷ lệ % thực thu phân bổ cho chi phí cố định (mặt bằng, điện nước, vận hành). Chỉ áp dụng cho đơn mới tạo SAU khi lưu."Hover icon ⓘ

B-Trường hợp cá biệt

#Trường hợpHành vi kỳ vọng
1Đơn cũ có fixed_cost_rate=NULL (tạo trước khi config)Ẩn dòng "Chi phí cố định" trong section. Tooltip ⓘ tiêu đề ghi "Đơn này tạo trước khi cấu hình chi phí cố định."
2Setting rate=0 (Admin set chủ động)Hiện "Chi phí cố định: 0đ" (không ẩn)
3Đơn có refund làm paid âmVẫn hiển thị paid âm với prefix , ví dụ "Đã thu: −500.000đ"
4Permission revoke đột ngột (admin cấu hình)Lần fetch tiếp theo → ẩn section. KHÔNG show error toast
5API timeout > 5sHiện error state + nút "Thử lại". Không loop retry tự động
6Sidebar nhiều block → overflow trên màn nhỏCSS max-height: calc(100vh - 64px) + overflow-y: auto cho sidebar
7Đơn có 0 commission, 0 tour, 0 vật tưVẫn render đầy đủ với "0đ" cho từng dòng (không ẩn)
8revenue=0 (đơn lỗi)Hiển thị Doanh thu: —, Tỷ suất: — (không tính được)
9Khách đã thanh toán đủ (debt=0)Badge trạng thái "Đã thanh toán" trên dòng "Còn nợ"
10User vừa được cấp view_financial_pnl (refetch)Section render lại với 8 dòng, có animation fade-in các row mới

B-POST) Verification

B-POST.1) 12 Checkpoint completeness

  • [x] As-Is đầy đủ: B0.1 inventory bao phủ 11 UI ID (3 cũ + 2 NEW + 6 KEEP/MOVE) với Evidence file:line
  • [x] Delta status đầy đủ: mọi UI ID có Delta Status (KEEP/MOVE/NEW)
  • [x] Field × Surface ma trận: 10 field/CTA mới đều có dòng B0.4 với 12 cột đầy đủ
  • [x] State × Screen ma trận: 3 màn × 6 state đã có ở B0.5
  • [x] Reuse classification: SCR-01 = 🔧 Extend, SCR-02 = 🔧 Extend, SCR-03 = ✅ Reuse + file path + delta
  • [x] Wireframe context: B2.2 vẽ đầy đủ vùng KEEP (Header → POS → ACCOUNT → CONTACT → HOA HỒNG → BRANCH → PROMOTION → DỮ LIỆU → LIÊN KẾT) xung quanh section mới
  • [x] CTA hierarchy: Section read-only → primary CTA = không có; secondary "Thử lại" cho error state
  • [x] Validation đầy đủ: Field fixed_cost_rate có rule 0-100, max 2 decimals; format VN cho mọi số
  • [x] Permission matrix: B2.8 có 7 dòng phân quyền per portal × role
  • [x] Confirm modal: N/A (section read-only, không có destructive action)
  • [x] Empty với CTA: Section TÀI CHÍNH không có empty state (đơn luôn có data); SCR-02 placeholder "Chưa cấu hình"
  • [x] Format VN: mọi tiền 1.250.000đ, −250.000đ; tỷ lệ 78,43%, 15,50%

B-POST.2) Cross-spec consistency check

  • [x] Mọi field B0.4 có trong PRD A4 FR (revenue→FR-001, paid→FR-001/002, debt→FR-001/002, commission→FR-001 PnL, tour_cost→FR-001 PnL, fixed_cost→FR-001/004/005 PnL, profit_estimated→FR-001/007 PnL, margin_estimated→FR-001 PnL, fixed_cost_rate→FR-003)
  • [x] Mọi field B0.4 có trong Dev Spec C4: order.fixed_cost_rate, order.fixed_cost_amount, app_setting key
  • [x] Mọi action mới có TC trong QA: financial_summary_viewed, loss_warning_shown, permission revoke
  • [x] Mọi state B0.5 có TC: loading, error, no-permission, partial pending
  • [ ] Điền _consistency-matrix.md → tasking sau

B-QUALITY) Rà soát rủi ro thiếu sót

Rủi ro QA thường gặp

#Rủi roĐã cover ở
1Thiếu test field mớiB0.4 ép trace 8 chỉ số + setting; QA ép TC theo từng field
2Thiếu state empty/loading/errorB0.5 ma trận có 6 state cho 3 màn
3Thiếu permission denied testB2.8 + ma trận 7 dòng portal × role; QA TC-PERM-*
4Thiếu confirm modalN/A — section read-only, KHÔNG destructive (đã ghi rõ)
5Thiếu format VNB-POST.1 checkpoint #12 + Tooltip B9 dùng format VN
6Boundary value không cụ thểValidation fixed_cost_rate ∈ [0, 100] + 2 decimals; QA ép boundary -1, 0, 100, 100.01, 99.999

Rủi ro UI/UX thường gặp

#Rủi roĐã cover ở
7Thêm field không nói nằm đâuB0.4 cột "Hiển thị ở đâu" + B2.6 layout chi tiết với thứ tự rows
8Wireframe không vẽ vùng KEEPB2.2 vẽ đầy đủ Header → POS → ACCOUNT → CONTACT → HOA HỒNG → BRANCH → PROMOTION → SECTION MỚI → DỮ LIỆU → LIÊN KẾT
9Field không có defaultB2.4 cột "Mặc định" có giá trị cho mọi field
10Empty state không có CTASCR-02 placeholder "Chưa cấu hình" + Section TÀI CHÍNH N/A (đơn luôn có data)
11Modal không nói triggerN/A — không dùng modal
12Filter không nói defaultN/A — không có filter trong section read-only

Rủi ro PO/BA thường gặp

#Rủi roĐã cover ở
13Spec ghi đè behavior cũB0.1 ghi rõ KEEP cho 9 block hiện hữu (Header/POS/ACCOUNT/...); MOVE cho 2 block (DỮ LIỆU, LIÊN KẾT) chỉ đẩy thứ tự
14Out-of-scope không rõPRD A2 Non-goals 7 dòng có cột Lý do
15Decision không có ≥2 phương ánPRD Z) DEC-001/002/003/012 đều có ≥2 phương án + lý do

Cách dùng

  • ✅ Mọi rủi ro thiếu sót đã có ghi nhận
  • ⏳ Còn 1 gap: _consistency-matrix.md chưa sinh → next task