Appearance
Decision Brief — Tab Chu kỳ khách ghé không mua
Version: 2.7 Date: 05/05/2026 Profile: M Canonical Inputs: SOURCE_OF_TRUTH.md, EVIDENCE_PACK.md, prd.md, ui-spec.md, dev-spec.md, qa-test-plan.md, handoff.md.
Đây là cửa vào 5 phút cho PO/TL/Sếp. Đọc file này để nắm scope và quyết định; đọc các file execution khi cần giao việc chi tiết.
1) Tóm tắt quyết định
Bổ sung tab Chu kỳ khách ghé không mua trong report group Chu kỳ khách hàng để Marketing/CSKH nhìn thấy nhóm khách đã ghé chi nhánh nhưng không phát sinh doanh số và chưa chuyển đổi sau lượt ghé qualified gần nhất. Feature không sửa tab Chu kỳ mua hàng; tab mới dùng nguồn all_customer_visits với is_zero_order=true và visit_source thuộc consultant / do_service, kèm exclude rule DEC-014 cho khách đã chuyển đổi.
Phase 1 reuse shell filter/matrix/popup hiện có để giảm effort, nhưng không reuse wording hoặc output purchase-oriented. Popup mới hiển thị 1 khách/row để đội chăm sóc gọi lại — popup KHÔNG chứa khách đã chuyển đổi.
2) Package map
| File | Người đọc | Nội dung chính |
|---|---|---|
prd.md | PO/BA, TL | Scope, FR/AC, formula nghiệp vụ |
ui-spec.md | UI/FE/QA | As-is inventory, delta contract, wireframe, copy |
dev-spec.md | BE/FE/TL | Action/query/output contract, formulas implementation |
qa-test-plan.md | QA/PO | Test oracle, cases, seed data |
handoff.md | Delivery team | RACI, timeline, task order, blockers |
Internal artifacts như EVIDENCE_PACK.md, SOURCE_OF_TRUTH.md, _consistency-matrix.md được giữ trong diva-group để audit nguồn, không publish vào sidebar dva-doc.
3) Scope lock
| Trong phase 1 | Ngoài phase 1 |
|---|---|
Tab thứ 4 trong /r/reports/customer_cycle_report_group | Export Excel |
Reuse filter Loại chu kỳ, Bước chu kỳ, Thời gian, Chi nhánh | Filter nguồn visit cho user tự chọn |
Hidden rules is_zero_order=true, source consultant/do_service | Churn score hoặc card cảnh báo riêng |
| 3 card/2 donut visit-based | Compare với tab Chu kỳ mua hàng |
Matrix Chu kỳ khách ghé | Tự động tạo campaign chăm sóc |
| Popup detail 1 khách/row, search tên/SĐT | Field doanh thu/financial trong popup |
4) Quyết định có ảnh hưởng triển khai
| ID | Quyết định | Tác động |
|---|---|---|
| DEC-003 | Khách ghé không mua bám is_zero_order=true | BE action dùng all_customer_visits, không suy từ order.total |
| DEC-005 | Row KH chưa quay lại ghé thay row Số khách có khả năng rời bỏ | UI/QA phải check copy và matrix order |
| DEC-006 | by_month = duration * 30 ngày | Test month boundary không dùng tháng lịch |
| DEC-007 | Popup 1 khách/row, branch theo latest qualified visit | Detail action phải aggregate theo customer |
| DEC-010 | Action/query mới riêng | Không sửa reportPurchaseCycle/reportPurchaseCycleDetail |
| DEC-012 | Không data là empty payload, không error | FE hiển thị empty state, không toast lỗi |
| DEC-014 | Loại khách đã chuyển đổi sau ghé | BE action thêm CTE converted_customers (EXCEPT) trước khi tính bucket; QA seed CUST-F/G/H/I |
| DEC-015 | Fix kèm bug tab persistence + to mapping | FE phải sửa CustomerCycleReport.tsx:37 và whitelist getSavedTab() trong cùng PR; QA TC-001 cover regression tab Chu kỳ khách hàng |
| DEC-016 | BE enforce branch scope độc lập với FE | BE thêm helper resolveBranchScope(ctx, requested) query user.branches; nếu user.branches rỗng = no branch restriction, nếu non-empty = intersect với requested; intersection rỗng mới trả empty payload; KHÔNG dùng Actor.IsAdmin(); QA TC-012 attempt bypass |
| DEC-017 | Loại ghé multi-source format cố định | BE normalize visit_source theo thứ tự consultant → do_service; FE utility formatVisitSource join +; QA TC-013 |
| v2.3/v2.7 (terminology) | Rename T1-T5 + giữ tooltip reference trong UI B9 | FE implement core tooltip set cho phase 1; tooltip mở rộng vẫn giữ làm optional/backlog, không block GA; QA TC-014 verify copy + core tooltip |
| DEC-018 | Bucket by_month label sạch cho tab mới | Dev C3 implement buildBuckets riêng (KHÔNG reuse purchase); QA TC-007 verify labels |
| DEC-019 | Cell value 0 → disable click | FE conditional click handler theo value; QA TC-015 |
| DEC-020 | Performance NFR + benchmark gate + SQL fallback | BE benchmark trước GA; nếu fail → switch sang PostgreSQL function với window/CTE; block GA nếu không pass; QA TC-016 |
| DEC-021 | Popup title {Row} • {Column} | Component popup nhận rowLabel + columnLabel riêng (không build sẵn); QA TC-005 verify |
| DEC-022 | Tab label responsive ≥768px/<768px | FE dùng $q.screen.lt.md chọn i18n key full/short; tab key + URL không đổi; QA TC-017 |
| DEC-023 | Date range max 365 ngày + clamp + toast | FE clamp onChange; BE validate defense in depth (invalid_date_range); QA TC-018 |
| DEC-024 | Bước chu kỳ [1,180]/[1,12] validation | FE inline error theo type_cycle; BE validate (invalid_duration); QA TC-018 |
| DEC-026 | Detail action nhận branch_ids để khớp matrix bucket | BE detail re-apply resolveBranchScope rồi filter qualified_visits; QA TC-019 cross-branch drift |
| Review fix C-01 | DEC-016 revision — KHÔNG dùng Actor.IsAdmin() (returns true cho RoleUser) | Dev C8 verification matrix + threat model; QA TC-012 expanded RoleUser footgun scenario |
| Review fix C-02 | Order enum canonical order_canceled/prepaid_canceled | Update toàn package; nếu dùng sai canceled → khách CUST-I bị wrongly excluded |
| Review fix M-01 | FORMULA-002+ filter target_customers | PRD/Dev formulas update; tránh donut/matrix tính cả khách đã chuyển đổi |
5) Impact
| Layer | Impact |
|---|---|
| FE | Thêm tab key/panel, component report visit, query GraphQL, matrix/popup copy |
| BE | Thêm 2 actions: aggregate và detail visit no purchase |
| Metadata | Thêm action input/output trong Hasura metadata |
| DB | Không tạo bảng mới; đọc all_customer_visits, ecommerce_user, branch, order |
| QA | Regression tab mua hàng và test source/no-revenue/empty/multi-branch |
6) Rủi ro chính
| Risk | Guard |
|---|---|
| User hiểu nhầm là chu kỳ mua hàng | Rewrite toàn bộ title/row/copy sang visit-based + core tooltip cho thuật ngữ dễ hiểu sai |
| Regression tab cũ | Action/query/component mới riêng, không sửa contract cũ |
| Empty data bị coi là lỗi | Action mới trả payload rỗng hợp lệ |
| Same-day cross-branch không deterministic | Popup branch hiển thị Nhiều chi nhánh |
| Manager bypass branch scope | DEC-016 BE intersect; QA TC-012 |
| Liệt khách đã chuyển đổi vào danh sách CSKH | DEC-014 EXCEPT converted_customers; QA TC-010 |
| Tab cũ rớt persistence sau refresh | DEC-015 fix kèm; QA TC-001 |
Loại ghé thứ tự không đồng nhất | DEC-017 fixed order; QA TC-013 |
| Bucket label by_month gây nhầm | DEC-018 đổi convention sạch cho tab mới |
| Popup empty mở từ cell value=0 | DEC-019 disable click |
| Action timeout @ scale lớn | DEC-020 benchmark gate + SQL fallback plan |
| Tab label overflow trên mobile | DEC-022 responsive label |
| User chọn date range nhiều năm → matrix khó đọc | DEC-023 max 365 ngày + clamp |
| User nhập bước chu kỳ extreme | DEC-024 FE/BE validate range |
| Popup title inconsistent giữa tab cũ/mới | DEC-021 lock format {Row} • {Column} riêng cho tab mới |
| Export làm nở scope/dữ liệu nhạy cảm | Defer export sang phase sau |
7) Handoff pointer
Team triển khai nên đi theo thứ tự:
- BE tạo aggregate/detail actions và metadata.
- FE thêm tab + GraphQL queries + generated types.
- FE build report, matrix, popup từ shell hiện có.
- QA chạy regression tab cũ trước khi UAT tab mới.
Timeline/RACI chi tiết nằm trong handoff.md.