Appearance
QA Test Plan — Tab Chu kỳ khách ghé không mua
Version: 2.8
Date: 11/05/2026
Canonical Inputs: SOURCE_OF_TRUTH.md, EVIDENCE_PACK.md, prd.md.
v2.8 — 11/05/2026
| Thay đổi | Section | Ảnh hưởng |
|---|---|---|
| Thêm 5 TC mới: TC-020 info strip, TC-021 sub-text, TC-022 column rename + regression purchase, TC-023 popup column tooltip, TC-024 19 tooltip coverage | D3) Test cases | QA |
| Update TC-014 note — coverage tooltip chuyển sang TC-024 | D3) TC-014 | QA |
| Update 4 row traceability TC-003/004/005/014 + thêm 5 row TC mới | D4) Traceability | QA |
| Mở rộng D5 Exit — TC v2.8 pass + 19 tooltip coverage + mobile responsive | D5) Entry/Exit | QA |
QA dùng file này để kiểm thử đúng oracle nghiệp vụ. Khi expected result mâu thuẫn với PRD A10 hoặc SOURCE_OF_TRUTH.md, ưu tiên canonical truth.
D1) Test scope
| In scope | Out of scope |
|---|---|
| Tab mới, persistence, filter, aggregate, matrix, popup | Export Excel |
Source consultant/do_service, is_zero_order=true | Filter nguồn visit visible |
| Loại khách đã chuyển đổi (DEC-014) | Tự động hoá campaign chăm sóc |
| Empty/error/no-permission state | Churn score |
Regression tab Chu kỳ mua hàng | Sửa thuật toán purchase cycle |
D2) Seed data
| Seed | Mô tả |
|---|---|
| CUST-A Nguyễn Thị Lan | 1 qualified visit consultant, is_zero_order=true, no order same day, no order sau ghé |
| CUST-B Trần Minh Anh | 2 qualified visits do_service, is_zero_order=true, different dates, no order sau visit cuối |
| CUST-C Lê Hoàng Nam | visit consultant nhưng is_zero_order=false vì có order same day |
| CUST-D Phạm Bảo Ngọc | 2 qualified visits cùng ngày khác branch, no order sau visit |
| CUST-E Võ Gia Hân | qualified visit, chưa từng mua |
| CUST-F Đỗ Quỳnh Thư | 1 qualified visit ngày 10/04, có order hợp lệ ngày 12/04 (sau ghé) → đã chuyển đổi, phải bị loại |
| CUST-G Bùi Khánh My | 2 qualified visits 10/04 + 20/04, có order hợp lệ ngày 15/04 (giữa 2 visit) → giữ vì sau mua vẫn ghé tiếp |
| CUST-H Hoàng Diệu Linh | 1 qualified visit 10/04, có order hợp lệ ngày 05/04 (trước ghé) → giữ vì đơn cũ trước ghé |
| CUST-I Ngô Phương Thảo | 1 qualified visit 10/04, có order hủy ngày 12/04 → giữ vì order không hợp lệ |
| CUST-K Lý Hoàng Yến | latest qualified visit visit_source=['do_service','consultant'] (array order ngược cố ý) → test DEC-017 normalize |
| CUST-L Đặng Tuyết Mai | latest qualified visit visit_source=['consultant'] only |
| CUST-M Cao Quỳnh Như | latest qualified visit visit_source=['do_service'] only |
| Branch Q1/Q3 | Dùng để test branch scope và multi-branch |
| Account Manager-Q1 | User chỉ có quyền branch Q1; dùng để test bypass DEC-016 |
| Account Admin-HQ | User Hasura role user nhưng ecommerce_user.branches=[] (HQ pattern không gắn branch); dùng để test no-branch-restriction DEC-016 đúng nghĩa |
| CUST-N Phan Hà My | 2 qualified visits: Q1 ngày 10/04 + Q3 ngày 20/04 (cả 2 consultant, is_zero_order=true); test detail branch_ids drift (DEC-026) |
D3) Test cases
TC-001: Tab mới và persistence (gồm regression DEC-015)
| Field | Value |
|---|---|
| FR | FR-001 |
| Preconditions | User có quyền report group |
| Steps | (1) Mở /r/reports/customer_cycle_report_group → chọn tab Chu kỳ khách ghé không mua → refresh; (2) Chọn tab Chu kỳ khách hàng → refresh; (3) Chọn tab Chu kỳ mua hàng → refresh; (4) Chọn tab Tổng quan → refresh |
| Expected | (1) Tab mới active sau refresh; (2) Sau fix DEC-015: tab Chu kỳ khách hàng active sau refresh (trước fix sẽ rớt về Tổng quan); (3) tab Chu kỳ mua hàng active; (4) tab Tổng quan active. Tất cả tab render đúng panel tương ứng (không có panel sai do to mapping) |
| Priority | P0 |
TC-002: Qualified visit filter
| Field | Value |
|---|---|
| FR | FR-002 |
| Steps | Chọn kỳ chứa CUST-A/B/C/D/E, branch all |
| Expected | CUST-A/B/D/E được tính; CUST-C bị loại vì is_zero_order=false |
| Priority | P0 |
TC-003: Overview copy và no-data
| Field | Value |
|---|---|
| FR | FR-003 |
| Steps | Mở tab với seed có data; sau đó chọn kỳ không có qualified visit |
| Expected | Card/donut dùng copy visit-based; kỳ rỗng hiện empty state tiếng Việt, không NaN, không toast lỗi |
| Priority | P0 |
TC-004: Matrix row semantics
| Field | Value |
|---|---|
| FR | FR-004 |
| Steps | Mở matrix với CUST-A/B/D |
| Expected | Row order đúng: Tổng, Tỷ lệ KH theo thời gian chưa ghé lại, KH chưa quay lại ghé, Cùng ngày, bucket động; CUST-A vào KH chưa quay lại ghé; CUST-D có thể vào Cùng ngày |
| Priority | P0 |
TC-005: Popup detail
| Field | Value |
|---|---|
| FR | FR-005 |
| Steps | Click bucket có CUST-B/D/E -> search theo tên/SĐT -> click row; verify popup title format |
| Expected | Popup có columns visit-based, không export, không service filters; title format {Row} • {Column} (DEC-021) ví dụ KH chưa quay lại ghé • 31 - 60 ngày; CUST-D branch Nhiều chi nhánh; CUST-E Đơn mua gần nhất = Chưa từng mua; click row mở hồ sơ; core tooltip theo UI B9 hiển thị đúng nếu vị trí tương ứng được render |
| Priority | P0 |
TC-006: Regression tab mua hàng
| Field | Value |
|---|---|
| FR | FR-006 |
| Steps | Chạy cùng filter trên tab Chu kỳ mua hàng trước/sau feature |
| Expected | Số liệu/tab labels/popup purchase không đổi; action cũ vẫn gọi đúng |
| Priority | P0 |
TC-008: Branch scope — happy path
| Field | Value |
|---|---|
| FR | FR-002, FR-005 |
| Steps | Login Manager chỉ có Branch Q1, mở tab không chọn chi nhánh |
| Expected | Chỉ thấy khách thuộc branch Q1; popup không leak branch khác |
| Priority | P0 |
TC-009: Trạng thái lỗi hệ thống
| Field | Value |
|---|---|
| FR | FR-003, FR-005 |
| Steps | Mock action aggregate/detail lỗi hệ thống |
| Expected | UI hiện Không thể tải báo cáo. Vui lòng thử lại. hoặc Không thể tải danh sách khách. Vui lòng thử lại. |
| Priority | P1 |
TC-010: Loại khách đã chuyển đổi (DEC-014)
| Field | Value |
|---|---|
| FR | FR-002 |
| Preconditions | Seed CUST-F/G/H/I tồn tại trong kỳ filter |
| Steps | Chọn kỳ chứa CUST-F/G/H/I, branch all → kiểm tra matrix totals → mở popup từng bucket |
| Expected | CUST-F bị loại (order 12/04 sau visit 10/04); CUST-G được giữ (order 15/04 nằm giữa 2 visit, last_visit=20/04 > 15/04); CUST-H được giữ (order 05/04 trước visit 10/04); CUST-I được giữ (order hủy không tính); matrix total_customer đếm CUST-G/H/I, không đếm CUST-F; popup không xuất hiện CUST-F |
| Priority | P0 |
TC-012: Branch scope BE enforcement (DEC-016 — RoleUser footgun + bypass attempt)
| Field | Value |
|---|---|
| FR | FR-002, FR-006 |
| Preconditions | User Manager Q1 với role Hasura user, ecommerce_user.branches=[Q1]; user Admin/HQ với role Hasura user, ecommerce_user.branches=[] (rỗng); seed data ở cả Q1 và Q3. Quan trọng: Hasura action role là user cho cả 2 → Actor.IsAdmin() luôn trả true. Test này verify resolveBranchScope KHÔNG dùng IsAdmin(). |
| Steps | (a) Manager Q1 mở DevTools → trigger query với payload branch_ids=[Q3-uuid] (branch ngoài scope); (b) Manager Q1 trigger payload branch_ids=[Q1-uuid, Q3-uuid] (1 trong scope, 1 ngoài); (c) Manager Q1 không gửi branch_ids → expect kết quả Q1 only; (d) Admin/HQ trigger payload branch_ids=[Q3-uuid]; (e) Admin/HQ không gửi branch_ids; (f) Lặp tương tự với detail action customer_ids chứa khách Q3 |
| Expected | (a) Empty payload ([Q3] ∩ [Q1] = [] → DEC-012), không error, không leak data Q3; (b) Trả data Q1 only ([Q1, Q3] ∩ [Q1] = [Q1]); (c) Trả data Q1 only (allowed=[Q1], requested=[]→fallback allowed); (d) Trả data Q3 (user.branches=[] → no branch restriction, trust input); (e) Trả data toàn bộ (user.branches=[] + input rỗng); (f) Detail action filter ra customer_ids không thuộc scope effective_branches, response không chứa khách Q3 cho Manager Q1. Critical fail mode: nếu BE dùng Actor.IsAdmin() → cả Manager Q1 lẫn Admin-HQ đều bypass intersect → Manager Q1 leak data Q3 — đây là chính xác bug từ review C-01. |
| Priority | P0 |
TC-013: Loại ghé multi-source format (DEC-017)
| Field | Value |
|---|---|
| FR | FR-005 |
| Preconditions | Seed thêm CUST-K: latest qualified visit có visit_source = ['do_service', 'consultant'] (cố ý array order ngược); CUST-L: visit_source = ['consultant'] only; CUST-M: visit_source = ['do_service'] only |
| Steps | Mở popup chứa CUST-K/L/M |
| Expected | CUST-K cột Loại ghé = Tư vấn + Làm dịch vụ (thứ tự cố định, không phải Làm dịch vụ + Tư vấn); CUST-L = Tư vấn; CUST-M = Làm dịch vụ; separator đúng + (1 dấu cách 2 bên), không xuống dòng, không chip |
| Priority | P0 |
TC-015: Cell click guard (DEC-019)
| Field | Value |
|---|---|
| FR | FR-004 |
| Preconditions | Tab mới có matrix với ít nhất 1 cell value 0 và 1 cell value > 0 (CUST-D bucket khác bucket có data) |
| Steps | (1) Hover cell value 0 → kiểm cursor; (2) Click cell value 0 → kiểm popup; (3) Hover cell value > 0 → kiểm cursor; (4) Click cell value > 0 → kiểm popup; (5) Lặp với cột Tổng của row data có value 0 và value > 0. |
| Expected | (1) Cursor default, không có hover highlight; (2) Không mở popup, không crash; (3) Cursor pointer + hover highlight; (4) Mở popup SCR-02 đúng title row + column; (5) Cùng behavior với cột Tổng. |
| Priority | P1 |
TC-016: Performance benchmark (DEC-020)
| Field | Value |
|---|---|
| FR | FR-006 |
| Preconditions | Test env có seed 100k qualified visits + 500k qualified visits (2 dataset riêng); benchmark tool đo P95 |
| Steps | (1) Run aggregate action với 100k seed × 5 lần → ghi P95; (2) Run aggregate action với 500k seed × 5 lần → ghi P95; (3) Run detail action với customer_ids = 1k → ghi P95 ở cả 2 dataset. (4) Compare với target. (5) Nếu fail → BE switch sang SQL aggregation pattern (xem dev-spec C9 fallback) → re-run benchmark. |
| Expected | P95 aggregate < 3s @ 100k AND < 5s @ 500k. P95 detail < 1s @ 100k AND < 2s @ 500k. Pass → ghi báo cáo benchmark vào handoff. Fail → block GA, switch sang SQL fallback và re-test. |
| Priority | P1 |
TC-007: Month semantics (mở rộng cho DEC-018)
| Field | Value |
|---|---|
| FR | FR-002, FR-004 |
| Steps | Chọn Chu kỳ theo tháng, period 3, kỳ 9 tháng |
| Expected | Bucket labels: ≤ 3 tháng, 4 - 6 tháng, 7 - 9 tháng (DEC-018, KHÔNG dùng pattern purchase quirk 3 tháng / 3 - 6 / 6 - 9); bucket boundaries: 90, 180, 270 ngày; khách có days_since_last = 90 → vào bucket ≤ 3 tháng (lower-exclusive bound: 0 < days ≤ 90); khách = 91 → vào 4 - 6 tháng |
| Priority | P1 |
TC-017: Tab label responsive (DEC-022)
| Field | Value |
|---|---|
| FR | FR-001 |
| Preconditions | Tab Chu kỳ khách ghé không mua đã visible |
| Steps | (1) Mở tab ở viewport ≥768px (desktop) → kiểm label tab; (2) Resize browser xuống <768px (mobile) → kiểm label; (3) Click tab ở mobile → kiểm URL + tab key + panel render đúng; (4) Resize ngược lại → kiểm label đổi lại full |
| Expected | (1) Hiển thị Chu kỳ khách ghé không mua; (2) Hiển thị Khách ghé không mua (short form); (3) URL/tab key không đổi (CUSTOMER_CYCLE_VISIT_NO_PURCHASE), panel render đúng; (4) Label trở lại full khi vượt 768px |
| Priority | P1 |
TC-018: Filter validation — date range + duration (DEC-023, DEC-024)
| Field | Value |
|---|---|
| FR | FR-002 |
| Preconditions | Tab visible, có data |
| Steps | (1) Date range > 365 ngày: chọn from = 01/01/2025, to = 30/04/2026 (~485 ngày). (2) Duration by_day = 0: nhập 0 vào bước chu kỳ ở chế độ ngày. (3) Duration by_day = 200. (4) Duration by_day = 30 (valid). (5) Switch sang by_month, duration = 13. (6) Duration by_month = 6 (valid). (7) Mở DevTools, gửi payload duration=9999 lên BE trực tiếp. |
| Expected | (1) FE clamp from = 01/05/2025 + toast Phạm vi báo cáo tối đa 1 năm. Đã điều chỉnh ngày bắt đầu thành 01/05/2025.. (2) Inline error Bước chu kỳ phải từ 1 đến 180 ngày, query không chạy. (3) Cùng inline error. (4) Query chạy bình thường. (5) Inline error Bước chu kỳ phải từ 1 đến 12 tháng. (6) Query chạy bình thường. (7) BE trả error invalid_duration với message tiếng Việt, không crash. |
| Priority | P1 |
TC-014: Copy + core tooltip verification (v2.7)
| Field | Value |
|---|---|
| FR | FR-003, FR-004, FR-005 |
| Preconditions | Tab Chu kỳ khách ghé không mua có data đủ render matrix + popup (CUST-A → CUST-M) |
| Steps | (1) Verify card label = Số lượng khách có lượt ghé không mua; donut 1 = Cơ cấu khách: đã/chưa quay lại ghé; donut 2 = Tỉ lệ KH theo thời gian chưa ghé lại. (2) Verify matrix có row Cùng ngày (không phải 0 ngày); row order: Tổng, Tỷ lệ KH theo thời gian chưa ghé lại, KH chưa quay lại ghé, Cùng ngày, bucket động. (3) Verify popup column headers: Khách hàng, SĐT, Chi nhánh, Lượt ghé gần nhất, Loại ghé, Số lượt ghé không mua trong kỳ, Đơn mua gần nhất, Số ngày chưa ghé lại. (4) Verify CUST-E (chưa từng mua) → cột Đơn mua gần nhất = Chưa từng mua. (5) Hover core tooltip: filter Chi nhánh; matrix rows KH chưa quay lại ghé, Cùng ngày; popup columns Loại ghé, Số lượt ghé không mua trong kỳ, Đơn mua gần nhất, Số ngày chưa ghé lại. |
| Expected | Copy đúng theo UI B0.2/B6; core tooltip render đúng nội dung UI B9, không cắt chữ, không hiển thị key i18n. Tooltip ngoài core là optional/backlog, không block GA nếu chưa implement. |
| Priority | P1 |
V2.8 update: Coverage tooltip required đầy đủ chuyển sang TC-024. TC-014 chỉ verify copy chính của label/row/column (không bao gồm tooltip coverage). Backlog tooltip đã được lift hoàn toàn vào required (DEC-030).
TC-011: Edge case order cùng ngày last visit
| Field | Value |
|---|---|
| FR | FR-002 |
| Preconditions | Seed thêm 1 khách có qualified visit ngày 10/04 và order hợp lệ cùng ngày 10/04 nhưng is_zero_order đã đánh đúng (visit và order trên cùng ngày được gom thì is_zero_order=false, đã loại ở filter qualified) |
| Steps | Tạo khách CUST-J: visit_date=10/04 source=consultant, order created_at=10/04 12:30 (cùng ngày) |
| Expected | Theo migration is_zero_order của visit ngày 10/04 sẽ = false → CUST-J bị loại ngay ở filter qualified visit, không cần trigger DEC-014. Nếu seed cố ép is_zero_order=true để test bypass: order cùng ngày last_visit_date KHÔNG loại khách (theo công thức > chứ không phải >=) → giữ. |
| Priority | P1 |
TC-019: Detail action branch_ids consistency (DEC-026, C-03)
| Field | Value |
|---|---|
| FR | FR-005 |
| Preconditions | User có quyền Q1 + Q3; seed CUST-N: 1 qualified visit Q1 ngày 10/04, 1 qualified visit Q3 ngày 20/04 (cả 2 đều consultant, is_zero_order=true, không có order sau ghé) |
| Steps | (1) User filter branch_ids=[Q1], mở matrix → click bucket chứa CUST-N → kiểm popup; (2) User filter branch_ids=[Q3] → kiểm popup; (3) User filter branch_ids=[] (all) → kiểm popup; (4) Mở DevTools, lặp lại các steps nhưng FE detail action gọi không có branch_ids → kiểm response. |
| Expected | (1) Popup CUST-N: Lượt ghé gần nhất=10/04, branch_name=Q1, Số lượt ghé không mua trong kỳ=1. (2) Popup: last_visit=20/04, branch_name=Q3, count=1. (3) Popup: last_visit=20/04 (max), branch_name=Q3, count=2. (4) BE detail KHÔNG có branch_ids → fallback về resolveBranchScope với input rỗng = toàn bộ allowed = giống case (3). Documentation note: FE BẮT BUỘC truyền branch_ids để tránh drift. |
| Priority | P0 |
TC-020: Info strip rendering + persistence + a11y (DEC-028, FR-007)
| Field | Value |
|---|---|
| FR | FR-007 |
| Preconditions | Tab Chu kỳ khách ghé không mua deployed v2.8; user có quyền report group |
| Steps | (1) Mở tab → verify info strip visible above filter bar với copy đúng theo customer_cycle.info_strip. (2) Đổi filter (loại chu kỳ, thời gian, chi nhánh) → verify strip vẫn visible. (3) Refresh trang ở tab → verify strip visible sau reload. (4) Switch sang tab Chu kỳ mua hàng rồi quay lại tab này → verify strip vẫn visible. (5) Resize browser xuống <768px → verify strip auto-wrap, không cắt chữ. (6) DevTools: kiểm tra localStorage không có key dismissed (no banner state). (7) Filter cho ra empty state (thời gian không có data) → verify strip vẫn visible. (8) Screen reader simulator: verify strip có role="status" + aria-live="polite". |
| Expected | (1) Strip visible đúng copy. (2,3,4) Strip persistent qua mọi state change. (5) Mobile wrap không cắt. (6) localStorage clean — không có dismiss key. (7) Strip render độc lập với data state. (8) A11y attributes hiện diện. |
| Priority | P0 |
TC-021: Sub-text rendering tất cả 8 widget (DEC-029)
| Field | Value |
|---|---|
| FR | FR-003, FR-004 |
| Preconditions | Tab có data render đủ overview + matrix (CUST-A → CUST-M); total_customer thực tế > 0 |
| Steps | (1) Verify Card 1 dưới label Số lượng khách có lượt ghé không mua có sub-text Đã loại khách chuyển đổi sau ghé. (2) Verify Donut 1 dưới label có sub-text Trên tổng {N} KH với {N} interpolate đúng = total_customer (vd 1.250). (3) Donut 2 sub-text giống Donut 1. (4) Matrix header dọc sub-text = Khoảng cách trung bình giữa các lượt ghé. (5) Matrix header ngang sub-text = Tính tới ngày kết thúc kỳ. (6) Row Cùng ngày sub-text = Nhiều lượt ghé qualified trong 1 ngày. (7) Row KH chưa quay lại ghé sub-text = Chỉ có 1 lượt ghé qualified trong kỳ. (8) DevTools inspect: font-size sub-text < font-size label, color muted (gray-500 hoặc text-secondary). (9) Mobile viewport <768px: verify sub-text auto-wrap, không cắt. |
| Expected | Toàn bộ 8 sub-text render đúng copy theo B6 i18n key. Runtime interpolate {total} đúng giá trị. Style smaller + muted. Mobile wrap đúng. |
| Priority | P1 |
TC-022: Column rename T6 + regression tab purchase (DEC-029)
| Field | Value |
|---|---|
| FR | FR-004 |
| Preconditions | Tab mới có data; tab Chu kỳ mua hàng cũng có data để regression |
| Steps | (1) Mở tab Chu kỳ khách ghé không mua → kiểm tra header cột cuối matrix. (2) Hover ℹ️ icon trên header cột (nếu render — DEC-030). (3) Verify logic data binding: với row Khách hàng chưa quay lại value=800, total=1250 → cột cuối phải hiển thị 64.00%. (4) Switch sang tab Chu kỳ mua hàng → kiểm header cột cuối matrix (regression). |
| Expected | (1) Cột cuối tên % trên tổng (KHÔNG phải Tỷ lệ). (2) Tooltip ℹ️ giải thích đúng. (3) Logic không đổi — vẫn render đúng %. (4) Tab Chu kỳ mua hàng cột cuối VẪN tên Tỷ lệ (không bị regression đổi sang % trên tổng). |
| Priority | P0 |
TC-023: Popup column tooltip coverage (DEC-029, DEC-030)
| Field | Value |
|---|---|
| FR | FR-005 |
| Preconditions | Popup detail mở được với data (click bucket có khách trong matrix) |
| Steps | (1) Mở popup. (2) Cho mỗi 6 column header (Chi nhánh, Lượt ghé gần nhất, Loại ghé, Số lượt ghé không mua trong kỳ, Đơn mua gần nhất, Số ngày chưa ghé lại): verify có icon ℹ️ visible inline cạnh tên column. (3) Hover từng icon ℹ️ → verify tooltip hiển thị đúng copy theo UI B9 required. (4) Mobile/tap: long-press hoặc tap icon → verify tooltip toggle. (5) Verify tooltip không cắt chữ trên mobile viewport. |
| Expected | 6 column header có icon ℹ️ visible. Hover/tap hiển thị tooltip đúng copy. Mobile tooltip không cắt. |
| Priority | P1 |
TC-024: 19 required tooltip full coverage (DEC-030)
| Field | Value |
|---|---|
| FR | FR-002, FR-003, FR-004, FR-005 |
| Preconditions | Tab Chu kỳ khách ghé không mua có data render đủ filter + overview + matrix + popup |
| Steps | Verify hover ℹ️ trên 19 vị trí theo UI B9 required v2.8: 4 filter (Chi nhánh, Loại chu kỳ, Bước chu kỳ, Thời gian báo cáo); 3 overview (Card 1, Donut 1, Donut 2); 6 matrix (Header dọc, Header ngang, Row Tổng, Row Tỷ lệ KH theo thời gian, Row KH chưa quay lại ghé, Row Cùng ngày); 6 popup (Chi nhánh, Lượt ghé gần nhất, Loại ghé, Số lượt ghé không mua trong kỳ, Đơn mua gần nhất, Số ngày chưa ghé lại). Tổng 19. Mỗi tooltip verify nội dung khớp B9 (không hiển thị i18n key thô, không cắt chữ trên mobile). |
| Expected | 19/19 tooltip render đúng nội dung. Không có item nào còn classification "Optional"/"Backlog". |
| Priority | P1 |
D4) Traceability
| TC | FR | DEC |
|---|---|---|
| TC-001 | FR-001 | DEC-001, DEC-011, DEC-015, DEC-022 |
| TC-002 | FR-002 | DEC-002, DEC-003 |
| TC-003 | FR-003 | DEC-004, DEC-012, DEC-029 (sub-text overview) |
| TC-004 | FR-004 | DEC-005, DEC-006, DEC-029 (sub-text matrix) |
| TC-005 | FR-005 | DEC-007, DEC-008, DEC-009, DEC-021, DEC-029 (popup column tooltip) |
| TC-006 | FR-006 | DEC-010, DEC-013 |
| TC-007 | FR-002, FR-004 | DEC-006, DEC-018 |
| TC-008 | FR-002, FR-005 | DEC-016 (happy path) |
| TC-009 | FR-003, FR-005 | DEC-012 |
| TC-010 | FR-002 | DEC-014 |
| TC-011 | FR-002 | DEC-014 |
| TC-012 | FR-002, FR-006 | DEC-016 (bypass attempt + RoleUser footgun) |
| TC-013 | FR-005 | DEC-017 |
| TC-014 | FR-003, FR-004, FR-005 | Copy verification (v2.8: tooltip coverage chuyển sang TC-024) |
| TC-015 | FR-004 | DEC-019 |
| TC-016 | FR-006 | DEC-020 |
| TC-017 | FR-001 | DEC-022 |
| TC-018 | FR-002 | DEC-023, DEC-024 |
| TC-019 | FR-005 | DEC-026 (detail branch_ids — review C-03) |
| TC-020 | FR-007 | DEC-028 (info strip permanent) |
| TC-021 | FR-003, FR-004 | DEC-029 (sub-text rendering) |
| TC-022 | FR-004 | DEC-029 (column rename T6 + regression tab purchase) |
| TC-023 | FR-005 | DEC-029, DEC-030 (popup column tooltip) |
| TC-024 | FR-002, FR-003, FR-004, FR-005 | DEC-030 (19 tooltip required full coverage) |
D5) Entry/Exit
Entry
- BE actions deployed to test env.
- FE codegen includes new action types.
- Seed data for CUST-A/B/C/D/E exists.
- QA can access at least one Admin and one branch-scoped Manager account.
Exit
- All P0 pass.
- No purchase-tab regression.
- No English lazy state copy in new tab.
- No unresolved mismatch between matrix totals and popup detail counts.
- TC-020 P0 pass (info strip persistent + a11y).
- TC-022 P0 pass (column rename không break tab purchase).
- TC-021, TC-023, TC-024 P1 pass.
- 19 tooltip required 100% coverage (DEC-030).
- Mobile responsive verify (info strip + sub-text wrap + tooltip not cut).