Giao diện
Feature Inventory — SAPP-26878 · Tạo dữ liệu test doanh thu (Ops → HubSpot → SAP) ​
Kiểm kê tính năng đã triển khai, dựng từ code. Đầu vào cho FS as-built (Mode B). Mọi dòng có bằng chứng
file:dòng(đường dẫn tính từ root repomind_keep); không có bằng chứng → §8.Công thức truy vết:
ba/system-profile.md§3. Cơ chế dùng chung (secret gate, quyền,env_systems, auto-login Ops, SAP log trace, Ops passthrough, Ops SQL runner nói chung, lưu trạng thái tool) ghi "→ nền tảng" — tài liệu này chỉ mô tả 26878 dùng chúng thế nào.
| Field | Value |
|---|---|
| Project | ba/projects/2026-SAPP-Works-tools |
| Mode | B – As-built |
| Ngày đọc code | 2026-09-30 |
| Commit | e77c47a |
| Điểm vào BA cung cấp | Sub-tool sapp-26878 (sapp.010, "SAPP 26878: Create deal instance") — route /works/sapp/tools/sapp-26878 |
| Thành phần chạm tới | FE · BE (api) · hệ ngoài: HubSpot (dev portal / prod portal), SAPP Ops API + Ops DB, SAP service (lms-be) |
| Status | Draft |
Tóm tắt một đoạn (Fact từ code). Tool là một màn stepper cho tester tự dựng dữ liệu test cho dự án "Doanh thu thực hiện" (tiêu đề màn SAPP-26878 Doanh thu thực hiện (Ops → HubSpot → SAP), fe/src/ui/components/works/sapp/sapp-26878-tool.tsx:1967). Có 3 nhóm chip: Phase 1 · hóa đơn & tiền vé (5 luồng dựng deal + 5 màn đồng bộ lại), Trace (tra log SAP, chạy lượt quét, 3 màn gửi lại thô) và Phase 2 · doanh thu thực hiện (9 luồng) (sapp-26878-tool.tsx:2038-2150). Mọi luồng được khai báo dạng dữ liệu trong một file spec duy nhất sapp-26878-flows.ts và chạy qua cùng một stepper.
1. Phạm vi đã truy vết ​
- Trong phạm vi:
- Route FE
fe/app/works/sapp/tools/sapp-26878/page.tsx→ componentSapp26878Tool(ⓘ route import thẳng component, không có page trongfe/src/ui/pages/works/sapp/tools/—fe/app/works/sapp/tools/sapp-26878/page.tsx:2,10). - Spec luồng
fe/src/ui/components/works/sapp/sapp-26878-flows.ts(6 283 dòng): 5 luồng phase 1 + 9 luồng phase 2, mọi bước. - Component bước:
sapp-26878-step1.tsx(tạo deal),-step-guide,-step-stage,-step-sync,-step-link,-step-resync,-step-ops,-step-option,-step-classcode,-step-scan,-step-sql,-step-skip,-history,-resync-tool,-manual-scan,sap-resync-panel.tsx. - Service FE
fe/src/data/services/sapp/sapp-26878.service.ts+ các hàm Ops/SAP-log/option mà luồng gọi. - BE: các route
sapp-26878/*trongbe/apps/api/src/sapp/sapp.controller.ts, các routesapp-ops/*mà spec gọi (be/apps/api/src/sapp/sapp-ops.controller.ts), lưu run trongbe/libs/modules/src/lib/sapp/common.service.ts, DTObe/libs/interface/src/lib/dto/sapp/sapp-26878.dto.ts, hằng sốbe/libs/constants/src/lib/sapp/sapp-26878.constants.ts.
- Route FE
- Ngoài phạm vi (dừng truy vết): secret gate / quyền /
env_systems/ auto-login Ops (→ nền tảng); bên trongSapLogCommonService,SappOpsSqlCommonService, Ops passthrough (→ nền tảng); nội dung từng câu SQL trongsapp-26878-sql.constants.ts(chỉ ghi id + đọc/ghi); logic phía lms-be / SAP service / webhook HubSpot (hệ ngoài — chỉ trích từ hint trong code vàshared/external/); sapp-26297 (dùng chung SQL runner). Không có entity DB cho 26878 — Grep26878trongbe/libs/database/src/lib/entities/= 0 kết quả; "run" lưu trong Redis (xem §4).
2. Điểm vào (Entry points) ​
2.1 Màn / chip (FE) ​
| ID | Loại | Tên / đường dẫn người dùng thấy | Bằng chứng |
|---|---|---|---|
| E-01 | Màn web | /works/sapp/tools/sapp-26878 — query ?flow=&env=&step= giữ vị trí | fe/app/works/sapp/tools/sapp-26878/page.tsx:9-11; fe/src/ui/components/works/sapp/sapp-26878-tool.tsx:263-297 |
| E-02 | Chip phase 1 (luồng) | US-03 · Học phí thường, US-05 · Gia hạn / Bảo lưu, US-06 · CBE / Cert / Becker, US-11 · Chuyển đổi, US-13 · Chuyển nhượng | sapp-26878-flows.ts:2389; sapp-26878-tool.tsx:197-202 |
| E-03 | Chip phase 1 (đồng bộ lại) | Re-sync US-03/05/06/11/13 = story US-08/09/10/12/14 | sapp-26878-flows.ts:2416-2446; sapp-26878-tool.tsx:182-189,2214-2216 |
| E-04 | Chip Trace | Trace log SAP (đọc, chạy cả prod) | sapp-26878-tool.tsx:206-211,2201-2212 |
| E-05 | Chip Trace | Chạy lượt quét (quét doanh thu thủ công) | sapp-26878-tool.tsx:213-218,2190-2200 |
| E-06 | Chip Trace | Gửi lại bút toán (P2) · Gửi lại lớp (P2) · Gửi lại giao dịch (P1) | fe/src/ui/components/works/sapp/sap-resync-panel.tsx:41-62; sapp-26878-tool.tsx:2105-2122,2182-2189 |
| E-07 | Chip phase 2 (luồng) | US-01 · DTTH theo buổi … US-09 · DTTH đồng bộ ngược (9 chip) | sapp-26878-flows.ts:6262-6272; sapp-26878-tool.tsx:2131-2148 |
| E-08 | Modal | Log SAP (trace theo các deal của lượt), Lịch sử (run history của luồng) | sapp-26878-tool.tsx:1001-1023,1375-1390 |
2.2 API BE mà tool gọi (mỗi route có 3 bản prod|staging|dev trừ khi ghi khác) ​
| ID | Loại | Endpoint | Bằng chứng |
|---|---|---|---|
| E-10 | API | POST /api/v1/sapp/{env}/sapp-26878/deals — tạo deal | be/apps/api/src/sapp/sapp.controller.ts:2647,2667,2719 |
| E-11 | API | GET /api/v1/sapp/{env}/sapp-26878/deals — danh sách deal tool đã tạo | sapp.controller.ts:2687,2703,2739 |
| E-12 | API | GET /api/v1/sapp/{env}/sapp-26878/deals/{dealId} — đọc pipeline/stage | sapp.controller.ts:2827,2844,2857 |
| E-13 | API | PATCH /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/stage | sapp.controller.ts:2960,2981,3002 |
| E-14 | API | POST /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/assign-class-code | sapp.controller.ts:3146,3166,3186 |
| E-15 | API | POST /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/sync-course-package | sapp.controller.ts:3206,3223,3240 |
| E-16 | API | POST /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/link-source-deal | sapp.controller.ts:3258,3280,3302 |
| E-17 | API | POST /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/resync | sapp.controller.ts:3369,3389,3409 |
| E-18 | API | PUT / GET /api/v1/sapp/{env}/sapp-26878/runs/{flow} | sapp.controller.ts:3468,3489,3509,3529,3546,3558 |
| E-19 | API | POST / GET /api/v1/sapp/{env}/sapp-26878/contacts | sapp.controller.ts:3651,3669,3687,3705,3722,3738 |
| E-20 | API | GET /api/v1/sapp/{env}/sapp-26878/pipelines/{flow} | sapp.controller.ts:3755,3772,3789 |
| E-21 | API | GET /api/v1/sapp/{dev|staging}/sapp-26878/us01/catalogs (không có prod) | sapp.controller.ts:797,809 |
| E-22 | API | GET /api/v1/sapp/{dev|staging}/sapp-26878/us01/class-codes (không có prod) | sapp.controller.ts:773,785 |
| E-23 | API | POST /api/v1/sapp/dev/hubspot/{deals|contacts}/properties/{property}/options — chỉ dev | sapp.controller.ts:821,1101; FE fe/src/data/services/sapp/add-property-option.service.ts:36 |
| E-24 | API (→ nền tảng) | POST /api/v1/sapp/{env}/sap-log/trace, …/scan-runs | sapp.controller.ts:3841,3886 |
| E-25 | API (→ nền tảng) | GET /api/v1/sapp/{dev|staging}/sap-log/manual-scan | sapp.controller.ts:3991,4003 |
| E-26 | API (→ nền tảng) | POST /api/v1/sapp/{dev|staging}/sap-log/sign-callback | sapp.controller.ts:4017,4033 |
| E-27 | API (→ nền tảng) | Ops passthrough …/sapp-ops/* mà spec gọi (ping, classes, class-room, class-schedules, students, transfer, teachers, auto-update-schedule, product-orders, transactions, confirm, sap-revenue resync) | be/apps/api/src/sapp/sapp-ops.controller.ts:634-2386; map op→service fe/src/ui/components/works/sapp/sapp-26878-step-ops.tsx:1011-1070 |
| E-28 | API (→ nền tảng) | GET …/{dev|staging}/sapp-ops/sql/status, POST …/sql/run | sapp-ops.controller.ts:2404,2416,2430,2442 |
Hàm FE tương ứng: fe/src/data/services/sapp/sapp-26878.service.ts:42 (create deal), :55 (list deals), :73 (get deal), :91 (stage), :114 (resync), :135 (upsert run), :149 (list runs), :167 (link), :191 (sync), :207 (create contact), :221 (list contacts), :240 (pipelines), :261 (catalogs), :276 (class-codes), :304 (assign class code); URL ở fe/src/data/constants/api.ts:230-311.
3. Hành động người dùng / hệ thống ​
3.0 Khung chung của stepper (áp cho mọi luồng) ​
| ID | Ai (role) | Làm gì | Điểm vào | Kết quả quan sát được | Bằng chứng |
|---|---|---|---|---|---|
| A-01 | Tester | Chọn portal Dev / Staging / Prod | E-01 | Luồng dựng data chỉ bật Dev + Staging; Prod chỉ bật cho chip Trace. Đổi chip sang chip không cho phép portal hiện tại → tool tự chuyển portal và hiện thông báo | sapp-26878-tool.tsx:91-119,1923-1935,1977-2006 |
| A-02 | Tester | Chọn chip luồng | E-02/E-07 | Mở stepper của luồng; mỗi (luồng, portal) là một "sitting" riêng | sapp-26878-tool.tsx:1944-1950,2173-2180 |
| A-03 | Hệ thống (FE) | Lưu / khôi phục sitting | E-01 | Toàn bộ trạng thái (bước đang xem, deal đã tạo, bước đã ack/move/sync/link/resync/ops/skip, form, "bag" id, identity test, runId) lưu sessionStorage key sapp:26878:session:{env}:{flow}; mất khi đóng tab | sapp-26878-tool.tsx:314-391,439-526,754-788 |
| A-04 | Tester | Mở một bước | E-01 | Chỉ mở được bước ≤ bước chưa xong đầu tiên; bấm bước bị khóa → hiện lý do (không tooltip) | sapp-26878-tool.tsx:888-906,1113-1127 |
| A-05 | Hệ thống (FE) | Tự chuyển bước | E-01 | Xong một bước → sau 1 200 ms tự sang bước sau; có nút "Ở lại bước này" | sapp-26878-tool.tsx:359,918-930,1202-1221 |
| A-06 | Tester | "Bỏ qua bước này" (mọi bước) | E-01 | Bước coi như xong; tester có thể nhập tay các giá trị bước lẽ ra sinh ra (deal_id, class_id, order_amount…); bỏ qua bước deal có deal_id → ghi chain ở chế độ existing | fe/src/ui/components/works/sapp/sapp-26878-step-skip.tsx:28-60,79-100; sapp-26878-tool.tsx:798-824,1225-1240 |
| A-07 | Tester | Reset luồng (bấm 2 lần) | E-01 | Xóa sitting của đúng (luồng, portal), về bước 1, remount | sapp-26878-tool.tsx:494-502,1905-1916 |
| A-08 | Tester | Mở "Log SAP" | E-08 → E-24 | Tra log SAP theo mọi deal id lượt này đã tạo; nút tắt khi chưa có deal | sapp-26878-tool.tsx:957-960,1001-1014,1375-1381 |
| A-09 | Tester | Mở "Lịch sử" | E-08 → E-18 | Liệt kê run của luồng trên portal (mới nhất trước); bắn đồng bộ lại từ run cũ | fe/src/ui/components/works/sapp/sapp-26878-history.tsx:46-105 |
| A-10 | Hệ thống (FE) | Ghi run history | E-18 | Ghi (fire-and-forget, lỗi bị nuốt) mỗi lần tạo xong một deal, upsert theo runId của sitting; mỗi lần bấm đồng bộ lại ghi một entry mới vào bucket story (us08…us14) kèm trigger/sourceRunId/sourceFlow | sapp-26878-tool.tsx:1317-1340,1515-1547; sapp-26878-history.tsx:92-101 |
3.1 Loại bước (kind) — mỗi loại làm gì, gọi gì, ghi vào đâu ​
| ID | Kind (nhãn điển hình) | Làm gì | Điểm vào (API) | Ghi lên hệ thống nào | Điều kiện "xong" | Bằng chứng |
|---|---|---|---|---|---|---|
| A-20 | deal — "Tạo deal" | Chọn pipeline + stage (danh mục theo flow/env), điền property theo spec, tạo/chọn contact, tạo deal; hoặc "Dùng deal có sẵn" (nhập id) | E-20, E-19, E-10 | HubSpot deal + contact (dev portal hoặc prod portal khi staging) | Có chain[deal]; luồng một-bước-nhiều-deal cần đủ mọi deal | sapp-26878-step1.tsx:365,379,510-620; sapp-26878-tool.tsx:866-869,1272-1352 |
| A-21 | guide — "Đơn hàng Ops", "Giao dịch trong đơn"… | Chỉ hướng dẫn làm tay trên SAPP OPS (không gọi API), hiện deal id cần mang theo và "recipe" đơn | — | — (tester tự làm trên Ops) | Bấm "Đã hiểu"/ack | sapp-26878-flows.ts:509-534; sapp-26878-tool.tsx:870,1360-1373 |
| A-22 | stage — "Chuyển deal stage", "Kéo sang Won", "Hoàn thiện hồ sơ" | Đọc stage hiện tại, gợi ý stage theo nhãn (trim, không phân biệt hoa thường), PATCH dealstage | E-12, E-20, E-13 | HubSpot deal dealstage → kéo Won là nơi event SAP bắn ra (theo hint) | Move thành công | fe/src/ui/components/works/sapp/sapp-26878-step-stage.tsx:35,76-122; sapp-26878-flows.ts:620-632,1136-1140 |
| A-23 | sync — "Sync gói khóa học" (US-11 Deal gốc) | Gộp 6 property lộ trình vào goi_khoa_hoc | E-15 | HubSpot deal goi_khoa_hoc | Gọi thành công | sapp-26878-step-sync.tsx:52; sapp-26878-flows.ts:1845-1857 |
| A-24 | link — "Gắn deal gốc" (Deal trừ US-11/13) | Ghi id Deal gốc vào deal_id_goc_cua_chuyen_doi của Deal trừ | E-16 | HubSpot deal | Gọi thành công; cần đủ 2 deal | sapp-26878-step-link.tsx:59; sapp-26878-tool.tsx:1559-1587; sapp-26878-flows.ts:1876-1881,2237-2242 |
| A-25 | resync — "Đồng bộ lại hóa đơn / reconcile" | Ghi chuỗi Đồng bộ lại vào trang_thai_dong_bo_hoa_don (invoice) hoặc trang_thai_tu_dong_reconcile (reconcile); trả về giá trị cũ | E-17 | HubSpot deal → webhook bắn SAP (theo hint) | Bấm ít nhất 1 lần | sapp-26878-step-resync.tsx:58,75; be/apps/api/src/sapp/sapp.controller.ts:3335-3366 |
| A-26 | ops — các bước phase 2 | Gọi một thao tác Ops (form field có pre-fill từ "bag", dropdown danh mục, template), thu id từ response vào bag | E-27 (+E-21/E-22 cho dropdown) | SAPP Ops (qua passthrough) | Gọi thành công | sapp-26878-step-ops.tsx:411-456,640-690,1011-1070; sapp-26878-flows.ts:107-312 |
| A-27 | option — "Thêm option mã lớp" | Thêm mã lớp vào option list của 3 property enumeration (lop_dang_ky_cloned_, lop_dang_ky deal, lop_dang_ky contact) | E-23 | Định nghĩa property HubSpot dev | Đủ 3 property; chỉ bật ở dev, staging phải làm tay rồi bỏ qua | sapp-26878-step-option.tsx:40-44,69-117,165-166; sapp-26878-flows.ts:3080-3093 |
| A-28 | classcode — "Gán mã lớp" | Ghi mã lớp lên lop_dang_ky_cloned_ + lop_dang_ky của Deal và lop_dang_ky của Contact (bỏ qua nếu đã đúng) | E-14 | HubSpot deal + contact | Gọi thành công | sapp-26878-step-classcode.tsx:68; sapp.controller.ts:3040-3100; be/libs/constants/src/lib/sapp/sapp-26878.constants.ts:132-135 |
| A-29 | scan — "Chạy lượt quét" | Bắn một lượt quét doanh thu ngay (bỏ điều kiện chỉ Thứ 7); tắt ở prod | E-25 | SAP service (lms-be) | Gọi thành công | sapp-26878-step-scan.tsx:56-61,89; sapp-26878-flows.ts:3322-3330 |
| A-30 | trace — "Đọc kết quả" | Đọc trail SAP lọc theo deal_id (mặc định) hoặc class_code, tự chạy các "check" kịch bản | E-24 | — (chỉ đọc) | Luôn coi là xong | sapp-26878-tool.tsx:883-884,1465-1501; sapp-26878-flows.ts:386-507 |
| A-31 | sql (panel gắn vào bất kỳ bước) | Hiện câu SQL đã thế id từ bag; câu đọc có thể tự chạy; câu ghi tô đỏ + window.confirm; chạy theo statement id, không gửi text SQL | E-28 | SAPP Ops DB (dev/staging) | Không ảnh hưởng gate | sapp-26878-step-sql.tsx:40,200-213,388; sapp-26878-flows.ts:545-618; sapp-26878-tool.tsx:1640-1650 |
3.2 Phase 1 — luồng và bước (US-03, US-05, US-06, US-11, US-13) ​
Ghi chú hệ thống: phase 1 = dựng deal → (tester tạo đơn/giao dịch trên Ops) → kéo Won để SAP phát hóa đơn; đồng bộ lại = story US-08/09/10/12/14 (sapp-26878-flows.ts:2397-2414).
F-P1-01 · US-03 · Học phí thường (7 bước, 1 deal us03) — sapp-26878-flows.ts:1165-1217
| ID | Bước | Kind | Làm gì / ghi vào đâu | Bằng chứng |
|---|---|---|---|---|
| A-40 | 1 Tạo deal | deal | Deal pipeline học phí; bắt buộc doi_tuong_mua_hang, thoi_gian_ket_thuc_dao_tao_bo_sung + CCCD (cá nhân) / MST (doanh nghiệp); cảnh báo mềm nếu thiếu ngay_ghi_nhan_doanh_thu, co_so_hoc__new_; ẩn goi_khoa_hoc, tvts_uu_ai_bo_sung_ap_dung, loai_phi_dich_vu | sapp-26878-flows.ts:936-980,1172 |
| A-41 | 2 Đơn hàng Ops | guide | Tester tạo đơn trên SAPP OPS cho deal bước 1 | sapp-26878-flows.ts:1112-1118,1173-1179 |
| A-42 | 3 Soạn thảo hợp đồng | stage | Gợi ý Soạn thảo hợp đồng | sapp-26878-flows.ts:1120-1124 |
| A-43 | 4 Giao dịch trong đơn | guide | Tạo + confirm từng transaction trên Ops; tổng ≥ order amount | sapp-26878-flows.ts:987-993,1126-1134 |
| A-44 | 5 Kéo sang Won | stage | Gợi ý Won - Hoàn tất thủ tục (mốc bắn event SAP) | sapp-26878-flows.ts:1136-1140 |
| A-45 | 6 Đồng bộ lại hóa đơn | resync | invoice trên chính deal (story US-08) | sapp-26878-flows.ts:1147-1154 |
| A-46 | 7 Đồng bộ lại reconcile | resync | reconcile trên chính deal (story US-08) | sapp-26878-flows.ts:1156-1163 |
F-P1-02 · US-05 · Gia hạn / Bảo lưu (7 bước, deal us05) — sapp-26878-flows.ts:1359-1411
| ID | Bước | Kind | Làm gì | Bằng chứng |
|---|---|---|---|---|
| A-50 | 1 Tạo deal | deal | Bắt buộc loai_phi_dich_vu (chỉ Phí gia hạn/Phí bảo lưu), tai_khoan_nhan_phi_dich_vu, bu_san_pham_cung_cap_dich_vu, muc_phi_can_dong, khoa_hoc_bao_luu_gia_han, doi_tuong_mua_hang + CCCD+họ tên / MST+tên cty; cảnh báo BU = CGMA, loại phí lệch tên pipeline | sapp-26878-flows.ts:1219-1299 |
| A-51 | 2 Đơn hàng Ops | guide | Không bắt buộc — chỉ khi test cấn trừ tự động | sapp-26878-flows.ts:1301-1308 |
| A-52 | 3 Thông tin thanh toán | stage | Gợi ý Thông tin thanh toán | sapp-26878-flows.ts:1310-1314 |
| A-53 | 4 Giao dịch trong đơn | guide | Chỉ khi test cấn trừ; cấn trừ cần sap_sync_status = SUCCESS | sapp-26878-flows.ts:1316-1324 |
| A-54 | 5 Hoàn thành phí DV | stage | Gợi ý Hoàn thành phí dịch vụ (dev) / Won - Hoàn thành 100% phí dịch vụ (UAT) | sapp-26878-flows.ts:1326-1337 |
| A-55 | 6–7 Đồng bộ lại hóa đơn / reconcile | resync | Story US-09, kèm caveat ngày bấm | sapp-26878-flows.ts:1339-1357,708-709,767 |
F-P1-03 · US-06 · CBE / Cert / Becker (7 bước, deal us06) — sapp-26878-flows.ts:1583-1635
| ID | Bước | Kind | Làm gì | Bằng chứng |
|---|---|---|---|---|
| A-60 | 1 Tạo deal | deal | Bắt buộc loai_phi_dich_vu, muc_phi_can_dong, doi_tuong_mua_hang + nhóm có điều kiện theo loại phí (vd CBE → mon_thi_cbe) | sapp-26878-flows.ts:1413-1450 |
| A-61 | 2 Đơn hàng Ops | guide | Không bắt buộc; đơn ADD_ON → test_subject của sản phẩm thắng property môn thi | sapp-26878-flows.ts:1526-1536 |
| A-62 | 3 Thông tin thanh toán | stage | sapp-26878-flows.ts:1538-1542 | |
| A-63 | 4 Giao dịch trong đơn | guide | Chỉ khi có đơn bước 2 | sapp-26878-flows.ts:1544-1550 |
| A-64 | 5 Hoàn thành cung cấp DV | stage | Gợi ý Hoàn thành cung cấp dịch vụ / Won - …; không phải Hoàn thành phí dịch vụ | sapp-26878-flows.ts:1552-1561 |
| A-65 | 6–7 Đồng bộ lại hóa đơn / reconcile | resync | Story US-10, caveat môn thi + ngày | sapp-26878-flows.ts:1563-1581,715-716,768 |
F-P1-04 · US-11 · Chuyển đổi (18 bước, chuỗi 3 deal, dựng bằng chainSteps) — sapp-26878-flows.ts:1976-2009
| ID | Khối / bước | Kind | Làm gì | Bằng chứng |
|---|---|---|---|---|
| A-70 | 1a Deal gốc: 1 Tạo deal · 2 Đơn hàng Ops · 3 Sync gói khóa học · 4 Chuyển stage (Soạn thảo hợp đồng) · 5 Giao dịch · 6 Kéo Won (Won - Hoàn tất thủ tục) | deal/guide/sync/stage/guide/stage | Deal học phí thường như US-03; bước sync ghi goi_khoa_hoc để Ops dựng Registered Course cho đơn trừ | sapp-26878-flows.ts:1024-1082,1637-1660,1834-1873 |
| A-71 | 1b Deal trừ: 7 Tạo deal · 8 Gắn deal gốc · 9 Đơn hàng Ops (đơn điều chỉnh giảm, original_deal_id = Deal gốc, final_price âm) · 10 Stage Thông tin thanh toán · 11 Giao dịch · 12 Kéo Won (không sinh sự kiện) | deal/link/guide/stage/guide/stage | sapp-26878-flows.ts:1684-1700,1875-1921 | |
| A-72 | 1c Deal cộng: 13 Tạo deal · 14 Đơn hàng Ops (đơn chuyển đổi, original_deal_id = Deal trừ) · 15 Stage · 16 Giao dịch · 17 Kéo Won (deal duy nhất bắn event, sinh 3 chứng từ) | deal/guide/stage/guide/stage | sapp-26878-flows.ts:1747-1765,1923-1964 | |
| A-73 | 18 Đồng bộ lại hóa đơn (chỉ Deal cộng, chỉ invoice) | resync | Story US-12; 3 caveat | sapp-26878-flows.ts:1966-1974,1985-1994,770-774 |
| A-74 | Recipe đơn order-cong / order-tru hiện cùng deal id thật | (guide) | sapp-26878-flows.ts:1997-2008 |
F-P1-05 · US-13 · Chuyển nhượng (17 bước, chuỗi 3 deal, 2 học viên) — sapp-26878-flows.ts:2337-2374
| ID | Khối / bước | Kind | Làm gì | Bằng chứng |
|---|---|---|---|---|
| A-80 | 1a Deal gốc (HV chuyển ra): 1 Tạo deal · 2 Đơn Ops · 3 Stage · 4 Giao dịch · 5 Kéo Won | deal/guide/stage/guide/stage | Không có bước sync | sapp-26878-flows.ts:2011-2020,2205-2234 |
| A-81 | 1b Deal trừ (HV chuyển ra): 6 Tạo deal · 7 Gắn deal gốc · 8 Đơn chuyển nhượng RA (CHUYEN_NHUONG, phải tạo trước đơn nhận) · 9 Stage · 10 Giao dịch · 11 Kéo Won (không sinh sự kiện) | sapp-26878-flows.ts:2059-2068,2236-2280 | ||
| A-82 | 1c Deal cộng (HV nhận, contact khác): 12 Tạo deal · 13 Đơn chuyển nhượng VÀO (KHONG_GHI_NHAN_PHI) · 14 Stage · 15 Giao dịch · 16 Kéo Won (bắn event) | sapp-26878-flows.ts:2131-2140,2282-2325 | ||
| A-83 | 17 Đồng bộ lại hóa đơn (chỉ Deal cộng) | resync | Story US-14 | sapp-26878-flows.ts:2327-2335,2346-2358,775-779 |
Màn đồng bộ lại theo story (ngoài stepper)
| ID | Ai | Làm gì | Điểm vào | Kết quả | Bằng chứng |
|---|---|---|---|---|---|
| A-90 | Tester | Mở chip Re-sync US-0x, chọn luồng nguồn, chọn run cũ, bấm invoice/reconcile trên deal được phép | E-03 → E-18 (GET runs luồng nguồn), E-17 | Ghi Đồng bộ lại lên deal; ghi entry mới vào bucket story; hiện log các lần bấm | fe/src/ui/components/works/sapp/sapp-26878-resync-tool.tsx:77-106,195-334; sapp-26878-flows.ts:2458-2470 |
| A-91 | Tester | Gửi lại thô theo id: transaction (P1), entry, class (P2) | E-06 → E-27 | Ops gửi lại SAP | sap-resync-panel.tsx:41-62,120-130 |
| A-92 | Tester | Chip "Chạy lượt quét" + danh sách scan runs | E-05 → E-25, E-24 | Bắn quét, xem các lượt quét | sapp-26878-tool.tsx:2190-2200; fe/src/ui/components/works/sapp/sapp-26878-manual-scan.tsx:32 |
3.3 Phase 2 — "Doanh thu thực hiện" (DTTH), 9 luồng ​
Xương sống US-01 (18 bước) — các luồng khác tái dùng bước của US-01 bằng reuseStep / inGroup / withField (sapp-26878-flows.ts:3364-3432,4094-4107,4140-4183). Deal của mọi luồng phase 2 dựng trên spec deal US-03 (flow BE us03, pipeline học phí) cộng 2 property mã lớp (sapp-26878-flows.ts:2639-2660).
F-P2-01 · US-01 · DTTH theo buổi — sapp-26878-flows.ts:2789-3353
| ID | Bước | Kind | Làm gì / API / hệ thống | Bag vào → ra | Bằng chứng |
|---|---|---|---|---|---|
| A-100 | 1 Kiểm tra kết nối Ops | ops + sql | pingOps (Ops API); SQL p2.ping.1/2 tự chạy: bảng có thật không, có deleted_at không, schema nào | — | sapp-26878-flows.ts:2797-2833 |
| A-101 | 2 Đếm lớp của môn | ops | countClasses (GET classes, lọc subject_id, category=STANDARD) → số thứ tự lớp mới = total + 1 | → subject_code, class_total, class_seq | sapp-26878-flows.ts:2834-2862,2584-2605 |
| A-102 | 3 Tạo lớp | ops + sql | createClass (Ops) status=DRAFT; code = template {subject_code}03.{class_seq} (dev) hoặc chọn từ pool mã (staging); SQL p2.takencodes hiện mã đã dùng | → course_id, class_id, class_code | sapp-26878-flows.ts:2863-2985; pool: sapp-26878-step-ops.tsx:429-456 |
| A-103 | 4 Tạo buổi học | ops | autoUpdateSchedule sinh buổi từ lịch tuần cố định (không tạo tay từng buổi) | class_id → | sapp-26878-flows.ts:2988-3021 |
| A-104 | 5 Gán phòng học | ops | updateClassRoom (PUT class-room); lớp không ONLINE bắt buộc có phòng | class_id → | sapp-26878-flows.ts:3023-3049 |
| A-105 | 6 Mở lớp (PUBLIC) | ops | updateClass DRAFT → PUBLIC | class_id → | sapp-26878-flows.ts:3050-3079 |
| A-106 | 7 Thêm option mã lớp | option | 3 property HubSpot (dev); staging làm tay | class_code → | sapp-26878-flows.ts:3080-3093 |
| A-107 | 8 Tạo Deal | deal | Như US-03 + bắt buộc lop_dang_ky_cloned_, lop_dang_ky; pre-fill từ bag | → deal_id, contact_* | sapp-26878-flows.ts:3095,2639-2714; sapp-26878-tool.tsx:1294-1316 |
| A-108 | 9 Gán mã lớp | classcode | Ghi mã lớp lên deal + contact | deal_id,contact_id,class_code → | sapp-26878-flows.ts:3096-3104; sapp-26878-tool.tsx:1392-1408 |
| A-109 | 10 Tạo đơn hàng | ops | createProductOrder (Ops) với thông tin học viên từ bag, sản phẩm chọn từ danh mục (giá theo sản phẩm); chặn nếu đơn ra 0đ | deal_id,contact_* → order_id, order_amount | sapp-26878-flows.ts:3106-3229; sapp-26878-step-ops.tsx:640-672 |
| A-110 | 11 Soạn thảo hợp đồng | stage | HubSpot | sapp-26878-flows.ts:3230-3236 | |
| A-111 | 12 Tạo giao dịch | ops | createTransaction trong đơn; amount = order_amount (readOnly type TUITION_PAYMENT) | order_id,order_amount → transaction_id | sapp-26878-flows.ts:3237-3295,3250-3259 |
| A-112 | 13 Xác nhận giao dịch | ops | confirmTransaction — bước gọi SAP, sinh số dư 3387 | transaction_id → | sapp-26878-flows.ts:3296-3307 |
| A-113 | 14 Kéo sang Won | stage | Won - Hoàn tất thủ tục (bắn event hóa đơn) | sapp-26878-flows.ts:3308-3314 | |
| A-114 | 15 Hoàn thiện hồ sơ | stage | Won - Hoàn thiện hồ sơ — action tự thêm học viên vào lớp | sapp-26878-flows.ts:2748-2752,3315-3321 | |
| A-115 | 16 Chạy lượt quét | scan | Manual scan | sapp-26878-flows.ts:3322-3330 | |
| A-116 | 17 Đọc kết quả | trace | Lọc theo deal_id; check: standard_hours_remaining giảm dần, mẫu số không trùng từng buổi | sapp-26878-flows.ts:2715-2747,3331-3342 | |
| A-117 | 18 Trả lại mã lớp (staging) | guide + sql | SQL p2.retire.1 (đọc) / p2.retire.2 (ghi: đổi code thành removed_{ts}_{mã} + đặt deleted_at); bỏ qua trên dev | sapp-26878-flows.ts:2519-2546,2767-2787,3343-3350 |
Các luồng phase 2 còn lại (liệt kê theo thứ tự bước; "U01:x" = tái dùng bước x của US-01)
| ID | Luồng | Số bước | Chuỗi bước | Khác biệt / bước riêng | Bằng chứng |
|---|---|---|---|---|---|
| A-120 | F-P2-02 · US-02 · DTTH lớp online | 15 | U01:1,2 → Tạo lớp ONLINE (riêng, có flexible_days, finished_at) → U01:6,7,8,9 → U01:10 (mặc định sản phẩm ONLINE) → U01:11–16 → Đọc kết quả (check: ghi trọn một lần, không lesson_hours, center ALL, loại dòng sổ cái) | Không có "Tạo buổi học", "Gán phòng", không có "Trả lại mã lớp" | sapp-26878-flows.ts:3434-3562,3563-3620,3621-3647 |
| A-121 | F-P2-03 · US-03 · DTTH chốt sổ | 19 | U01:1,2 → Tạo lớp (không gửi finished_at) → U01:4,5,6,7,8,9,10,11,12,13,14,15 → Xóa buổi đuôi (tùy chọn) (deleteClassSchedule, lặp được, SQL p2.deletetail tự đọc) → Kéo buổi về quá khứ (tùy chọn) (SQL us03.backdate, có câu ghi) → U01:16 → Đọc kết quả (check true-up là bút toán cuối; SQL us03.trace.*) | Không có "Trả lại mã lớp" | sapp-26878-flows.ts:3677-3828,3829-3875,3877-3932,3934-4036,4037-4082 |
| A-122 | F-P2-04 · US-04 · DTTH ghi bù (2 học viên A/B) | 29 | Hàng A: U01:1,2,3(sửa started_at),5,4 → Kéo buổi về quá khứ (SQL us04.backdate) → Xóa buổi đuôi → U01:6,7 → 8 bước học viên A (Tạo Deal…Hoàn thiện hồ sơ) → Quét lần 1 (nền của A); Hàng B: Dựng mốc cửa sổ (SQL us04.window, câu ghi) → 8 bước học viên B → U01:16 → Đọc kết quả lọc theo class_code (check CATCH_UP) | 2 deal flow BE us04-a, us04-b | sapp-26878-flows.ts:4109-4139,4140-4183,4192-4450,4452-4497 |
| A-123 | F-P2-05 · US-05 · DTTH rời lớp (A rời / B nhận) | 30 | Hàng A: U01:1,2,3(sửa started_at),5,4 → Kéo hai buổi về quá khứ (SQL us05.backdate) → U01:6,7 → 8 bước HV A → U01:16; Hàng B: 8 bước HV B → Đọc học viên trong lớp (listClassStudents → class_user_0/1) → Chuyển nhượng suất học (transferStudent transfer_from/transfer_to = users.id) → Đọc kết quả (check LEAVE_REVERSAL…) → Quét lại sau khi đảo → Kiểm dừng xử lý (SQL us05.stop) | Chuyển nhượng trong cùng lớp | sapp-26878-flows.ts:4514-4549,4550-4868,4869-4906 |
| A-124 | F-P2-06 · US-06 · DTTH đảo buổi | 22 | U01:1,2,3,5,4,6,7,8,9,10,11,12,13,14,15,16 → Kiểm buổi đã được ghi (trace + SQL us06.baseline tự chạy → class_schedule_id) → Tạo giáo viên (createTestTeacher → teacher_id) → Kiểm buổi có học viên (guide + SQL us06.link) → Dời buổi (updateClassSchedule) → Hủy buổi (trigger thay thế) (cancelClassSchedule) → Đọc kết quả (check LESSON_REVERSAL, batch, scope_evidence) | Dùng MỘT trong hai trigger mỗi lượt | sapp-26878-flows.ts:4939-5337,5338-5380 |
| A-125 | F-P2-07 · US-07 · Trường chiều | 18 | U01:1–16 → Đọc trường chiều (check department=CX, exp_rev_code=REV.0005, sub_bu, center, item_code, mẫu số AC-M1.4; SQL us07.dims) → Dựng dữ liệu lệch (SQL us07.skew, câu ghi) | Không API riêng | sapp-26878-flows.ts:5402-5584,5585-5617 |
| A-126 | F-P2-08 · US-08 · DTTH đồng bộ lại | 22 | U01:1–16 → Đọc sổ cái (SQL us08.read) → Dựng dòng lỗi (SQL us08.seed → entry_id, câu ghi) → Đồng bộ lại theo dòng (resyncEntry) → Đọc kết quả gửi lại → Đồng bộ lại theo lớp (resyncClassEntries) → Dựng sáu chốt chặn (SQL us08.guards.1–6, đều ghi) | sapp-26878-flows.ts:5635-5903,5904-5948 | |
| A-127 | F-P2-09 · US-09 · DTTH đồng bộ ngược | 21 | U01:1–16 → Đọc sổ cái và tracking → Dựng callback đã ký (signSapCallback, SQL us09.sign tự chạy → entry_id, event_id) → Bắn callback (guide: tester tự chạy curl, hiệu lực ~300 s, dùng 1 lần) → Đọc kết quả → Dựng các ca còn lại (SQL us09.cases, câu ghi) | Việc bắn callback là tay, ngoài tool | sapp-26878-flows.ts:5958-6208,6209-6252 |
Ánh xạ US ↔ luồng (Fact từ code): phase 1 chip us03/us05/us06/us11/us13 ↔ story đồng bộ lại us08/us09/us10/us12/us14 (sapp-26878-flows.ts:2397-2414); phase 2 chip id us01, us02, us03-p2, us04, us05-p2, us06-p2, us07, us08-p2, us09-p2 (sapp-26878-flows.ts:2376-2387,6274-6283). Runbook xác nhận "US-03 phase 1 khác hẳn US-03 phase 2", hậu tố -p2 vì lý do đó (shared/runbooks/sapp/sapp-26878/README.md:12-13).
4. Dữ liệu ​
Không có bảng DB riêng của tool. Trạng thái nằm ở 3 nơi: sessionStorage (FE), Redis (BE), và chính các hệ ngoài (HubSpot / Ops / SAP).
| Entity / bảng | Field | Type | Bắt buộc | Mặc định / enum | Bằng chứng |
|---|---|---|---|---|---|
Redis sapp:26878:deal-instances:{env} | id, dealname | string | — | Danh sách deal tool đã tạo; cũng là bộ đếm tên deal và danh sách cho phép đọc/sửa deal | be/libs/constants/src/lib/sapp/sapp-26878.constants.ts:183-184; be/libs/modules/src/lib/sapp/common.service.ts:1615-1663 |
Redis sapp:26878:contact-instances:{env} | id, name | string | — | Contact tool đã tạo | sapp-26878.constants.ts:191-192 |
Redis sapp:26878:run-history:{env}:{flow} (Run) | id, flow, env, createdAt, updatedAt, deals[] | string / array | id, deals | TTL 30 ngày (ms), tối đa 50 run/flow/env | sapp-26878.constants.ts:278-296; common.service.ts:1134-1171 |
| Run.deals[] (RunDeal) | flow, label, dealId, dealname, contactId, mode | string | không | mode: new | existing | be/libs/interface/src/lib/dto/sapp/sapp-26878.dto.ts:187-215 |
| Run (chỉ bucket re-sync) | trigger, sourceRunId, sourceFlow | enum/string | không | trigger: invoice | reconcile — ⚠️ khai trong DTO nhưng service không lưu (xem R-30) | sapp-26878.dto.ts:248-269,285-298; common.service.ts:1145-1156 |
| Request tạo deal | properties, contactIds, flow | map / string[] / enum | không | flow ∈ ESapp26878Flow (13 giá trị: us03, us04-a/b, us05-a/b, us05, us06, us11-source/minus/plus, us13-source/minus/plus) | sapp-26878.dto.ts:20-44; sapp-26878.constants.ts:302-360 |
| Whitelist property deal | 52 property (vd doi_tuong_mua_hang, lop_dang_ky_cloned_, trang_thai_dong_bo_hoa_don, deal_id_goc_cua_chuyen_doi, amount…) | string | — | Ngoài list → 400 13027; hs_object_id bỏ qua | sapp-26878.constants.ts:18-99,176 |
| Request tạo contact | name, firstname, lastname, email, phone_number, address, province, d_o_b, cccd_passport, lop_dang_ky | string | name (FE bắt buộc) | phone_number copy sang phone; cccd_passport → cccd_ho_chieu | sapp-26878.dto.ts:46-70; sapp.controller.ts:3579-3610; sapp-26878-step1.tsx:532-543 |
| Request stage | dealstage | string | có | id stage đọc từ pipelines | sapp-26878.dto.ts:177-185 |
| Request link | source_deal_id | string | có | ghi vào deal_id_goc_cua_chuyen_doi | sapp-26878.dto.ts:145-151; sapp-26878.constants.ts:169-170 |
| Request resync | target | enum | có | invoice→trang_thai_dong_bo_hoa_don, reconcile→trang_thai_tu_dong_reconcile; giá trị ghi Đồng bộ lại | sapp-26878.dto.ts:313-319; sapp-26878.constants.ts:250-273 |
| Request assign class code | class_code, contact_id | string | class_code | ghi deal lop_dang_ky_cloned_,lop_dang_ky; contact lop_dang_ky | sapp-26878.dto.ts:349-361; sapp-26878.constants.ts:132-135 |
| Danh mục pipeline theo flow × env | id pipeline | string[] | — | Prod = [] cho mọi flow; staging dùng pipeline [UAT-TEST] trên portal prod | sapp-26878.constants.ts:382-507 |
FE sessionStorage sapp:26878:session:{env}:{flow} | viewStep, dealIndex, chain, acked, moved, synced, linked, resynced, opsDone, skipped, forms, bag, identities, runId | JSON | — | runId = crypto.randomUUID() mỗi sitting | sapp-26878-tool.tsx:314-350,517 |
| FE "bag" id phase 2 | subject_code, class_total, class_seq, course_id, class_id, class_code, deal_id, contact_*, order_id, order_amount, transaction_id, tail_class_schedule_id, survivor_schedule_id, class_user_0/1, teacher_id, class_schedule_id, entry_id, event_id | string | — | Sinh từ response Ops / kết quả SQL / nhập tay khi bỏ qua | sapp-26878-flows.ts:2849-2860,2985,3227,3293,3838,3894,4625,4978,4990,5687,6000 |
| Danh mục phase 2 (subjects, courses, facilities, staff, products, classrooms) | theo env | constant BE | — | Chỉ dev + staging; thiếu env → 501 | sapp.controller.ts:648-657; sapp-26878-flows.ts:89-105 |
| Statement SQL catalogue | 59 id (p2.*, us03.* … us09.*) | id | — | Thực thi theo id, không nhận text SQL; FE id = {idPrefix}.{i+1} | be/libs/constants/src/lib/sapp/sapp-26878-sql.constants.ts; sapp-26878-flows.ts:602-618 |
| Tên deal sinh tự động | dealname | string | — | [IT TEST][SAPP-26878] {nhãn flow} {n}, n đếm theo cùng tiền tố | sapp-26878.constants.ts:512-539; common.service.ts:1628-1634 |
5. Rule & validation tìm thấy ​
| ID | Rule (ngôn ngữ nghiệp vụ) | Loại | Áp dụng ở | Bằng chứng | Nghi là bug? |
|---|---|---|---|---|---|
| R-01 | Luồng dựng data chỉ chạy trên Dev và Staging; Prod chỉ mở cho màn tra log | điều kiện | A-01 | sapp-26878-tool.tsx:91-119 | Không (nhưng xem R-33) |
| R-02 | Một bước chỉ mở khi mọi bước trước đã xong; lùi lại luôn được | trạng thái | A-04 | sapp-26878-tool.tsx:888-894 | Không |
| R-03 | Bước "Đọc kết quả" (trace) luôn coi là xong — không chặn bước sau | trạng thái | A-30 | sapp-26878-tool.tsx:883-884 | Không |
| R-04 | Deal ghi nhận bằng id có sẵn ("existing") → mọi bước không phải deal thuộc deal đó coi như đã xong (bị đánh dấu bỏ qua) | trạng thái | A-20, A-06 | sapp-26878-tool.tsx:847-857 | ⚠️ Observed (Q-06) |
| R-05 | Tạo deal: bắt buộc pipeline, stage, các property required* theo nhánh cá nhân/doanh nghiệp, nhóm có điều kiện; deal cần contact → contact mới phải có tên hoặc chọn ít nhất 1 contact id | validation | A-20 | sapp-26878-step1.tsx:448-485 | Không |
| R-06 | Property "khuyến nghị" để trống và cảnh báo theo luồng chỉ là cảnh báo mềm, không chặn tạo deal | validation | A-20 | sapp-26878-step1.tsx:487-508; sapp-26878-flows.ts:893-894 | Không |
| R-07 | Chế độ "Dùng deal có sẵn" chỉ cần Deal id, không kiểm tra deal có tồn tại / do tool tạo | validation | A-20 | sapp-26878-step1.tsx:452-455,511-521 | ⚠️ Observed (Q-06) |
| R-08 | Giá trị trống không gửi lên khi tạo deal; pipeline, dealstage luôn gửi | validation | A-20 | sapp-26878-step1.tsx:577-584 | Không |
| R-09 | BE chỉ nhận property trong whitelist; key lạ → 400 SAPP_26878_INVALID_DEAL_PROPERTY (13027); hs_object_id bỏ qua | validation | E-10 | sapp.controller.ts:2596-2621 | Không |
| R-10 | dealname do server sinh, không cho client đặt; bộ đếm theo tiền tố flow | công thức | E-10 | sapp.controller.ts:2623-2645; common.service.ts:1624-1634 | Không |
| R-11 | Đọc / chuyển stage / gán mã lớp / sync / link / resync chỉ cho deal nằm trong danh sách deal tool đã tạo trên env đó; ngược lại 400 13033 | điều kiện | E-12..E-17 | sapp.controller.ts:2762-2773,2789,2810,2892,3047,3112,3341 | Không |
| R-12 | Chuyển stage chỉ ghi dealstage | giới hạn | A-22 | sapp.controller.ts:2755-2757,2810-2815 | Không |
| R-13 | Stage gợi ý so khớp theo nhãn (bỏ khoảng trắng, không phân biệt hoa thường), không theo id | điều kiện | A-22 | sapp-26878-step-stage.tsx:35,100-107; sapp-26878-flows.ts:624-629 | Không |
| R-14 | Danh mục pipeline theo flow × env; flow lạ → SAPP_26878_UNKNOWN_FLOW; env không có pipeline (prod) → mảng rỗng, không gọi HubSpot | điều kiện | E-20 | sapp.controller.ts:3616-3649 | Không |
| R-15 | Sync gói khóa học: gộp giá trị (tách ;) từ 6 property lộ trình, chỉ nhận giá trị có trong option không ẩn của goi_khoa_hoc, gộp với giá trị cũ; không có gì mới → không ghi | công thức | A-23 | sapp.controller.ts:2887-2957; sapp-26878.constants.ts:114-121 | Không |
| R-16 | Link deal gốc & gán mã lớp: không ghi nếu giá trị đã đúng (changed=false) | điều kiện | A-24, A-28 | sapp.controller.ts:3075-3083,3121-3126 | Không |
| R-17 | Đồng bộ lại: luôn ghi Đồng bộ lại (vô điều kiện), trả về giá trị cũ; giá trị cũ rỗng → FE cảnh báo "chưa từng qua SAP" sau khi đã ghi | điều kiện | A-25 | sapp.controller.ts:3328-3366; sapp-26878-step-resync.tsx:75,145 | Không (có chủ ý theo comment) |
| R-18 | Deal/trigger được phép đồng bộ lại lấy từ chính các bước resync của luồng: US-11/13 chỉ Deal cộng + invoice; US-03/05/06 cả invoice và reconcile | điều kiện | A-90, A-09 | sapp-26878-flows.ts:2458-2470 | Không |
| R-19 | "Thêm option mã lớp" chỉ chạy ở dev (staging dùng credential portal prod) | điều kiện | A-27 | sapp-26878-step-option.tsx:69-72,117 | Không |
| R-20 | Pool mã lớp (staging, recycle=true): mã phải có option trên cả 3 property, đúng tiền tố môn trong danh mục, và chưa có lớp nào giữ (kể cả lớp soft-delete); dev trả pool rỗng → FE dùng template | điều kiện / công thức | A-102 | sapp.controller.ts:659-771; sapp-26878-step-ops.tsx:445-456 | Không |
| R-21 | Mã lớp mới (dev) = {subject_code}03.{class_seq}, class_seq = số lớp STANDARD hiện có + 1; tên [SAPP-26878] {subject_code} - {class_seq} | công thức | A-101, A-102 | sapp-26878-flows.ts:2584-2605 | ⚠️ Observed (race — Q-11) |
| R-22 | Đơn hàng Ops ra số tiền 0 → báo lỗi, không đánh dấu bước xong | validation | A-109 | sapp-26878-step-ops.tsx:640-646 | Không |
| R-23 | amount của giao dịch lấy từ order_amount của đơn (không mặc định) | công thức | A-111 | sapp-26878-flows.ts:3254-3259; sapp-26878-step-ops.tsx:670-672 | Không |
| R-24 | Field Ops trống bị bỏ khỏi payload, trừ field allowEmpty (vd link_meeting) | validation | A-26 | sapp-26878-flows.ts:154-162 | Không |
| R-25 | Chạy lượt quét bị tắt ở prod (không có route prod) | điều kiện | A-29 | sapp-26878-step-scan.tsx:56,89; fe/src/data/constants/api.ts:261-266 | Không |
| R-26 | SQL: chỉ chạy dev/staging; câu ghi không bao giờ tự chạy và phải qua hộp xác nhận; BE chỉ nhận statement id | validation | A-31 | sapp-26878-step-sql.tsx:40,200-213; sapp-26878-flows.ts:584-590,607-612 | Không |
| R-27 | Check trace không đọc được field → "unknown", không phải "fail" | điều kiện | A-30 | sapp-26878-flows.ts:382-384 | Không |
| R-28 | Run history: upsert theo id (một entry / sitting), giữ 50 mới nhất, TTL 30 ngày làm mới mỗi lần ghi; GET trả mới nhất trước | giới hạn | A-10 | common.service.ts:1134-1205 | Không |
| R-29 | Mỗi lần bấm đồng bộ lại tạo một entry run mới (id mới) trong bucket story | trạng thái | A-10, A-90 | sapp-26878-tool.tsx:1526-1545; sapp-26878-history.tsx:95-101 | Không |
| R-30 | Khi lưu run, service chỉ giữ id/flow/env/createdAt/updatedAt/deals, bỏ trigger/sourceRunId/sourceFlow client gửi | trạng thái | A-10, A-90 | common.service.ts:1145-1156 vs sapp-26878.dto.ts:285-298 | ⚠️ Observed (Q-01) |
| R-31 | Identity test (SĐT, CCCD, email suy từ SĐT, tên chứa SĐT, địa chỉ) sinh một lần/deal, chỉ điền vào ô chưa bị sửa và chỉ vào property mà luồng hiển thị | công thức | A-20 | sapp-26878-tool.tsx:399-404,541-547,616-710 | Không |
| R-32 | Danh mục phase 2 thiếu env → 501 (không trả rỗng); không có prod | điều kiện | E-21 | sapp.controller.ts:640-657 | Không |
| R-33 | BE có route prod cho tạo deal/contact, chuyển stage, gán mã lớp, sync, link, resync, runs, và các thao tác ghi Ops (POST prod/sapp-ops/classes, …/product-orders, …/confirm…); chặn prod chỉ nằm ở FE | điều kiện | E-10..E-18, E-27 | sapp.controller.ts:2647,2960,3146,3206,3258,3369,3468; sapp-ops.controller.ts:1261,2179,2290 | ⚠️ Observed (Q-02) |
| R-34 | Param {flow} của runs khai kiểu ESapp26878Chip (chỉ phase 1 + story) nhưng không có kiểm tra runtime → FE ghi được bucket phase 2 (us01, us04…) | validation | E-18 | sapp.controller.ts:3496-3499; sapp-26878.constants.ts:202-244; sapp-26878-tool.tsx:1337 | ⚠️ Observed (Q-03) |
Trạng thái & chuyển trạng thái (một bước trong sitting, FE):
text
[KHÓA] --(mọi bước trước xong)--> [MỞ]
[MỞ] --(deal tạo / guide ack / stage move / sync / link / resync / ops|option|scan|classcode thành công)--> [XONG] --(1,2s)--> mở bước kế
[MỞ] --("Bỏ qua bước này", có/không nhập giá trị)--> [XONG · bỏ qua]
[MỞ] --(deal của khối ở chế độ existing)--> [XONG · bỏ qua] (mọi kind trừ deal)
[trace] = luôn [XONG]
[BẤT KỲ] --(Reset 2 lần)--> sitting mới, bước 1Trạng thái property đồng bộ (theo hint/constant, không phải state machine của tool):
text
trang_thai_dong_bo_hoa_don / trang_thai_tu_dong_reconcile:
(trống: chưa qua SAP) --(kéo Won, lms-be callback)--> SUCCESS | FAILED
SUCCESS|FAILED --(tool ghi "Đồng bộ lại")--> "Đồng bộ lại" --(lms-be chạy lại)--> SUCCESS | FAILED(sapp-26878.constants.ts:255-262; sapp-26878-flows.ts:669-677)
6. Quyền, tích hợp, thông báo, job ​
| Loại | Chi tiết | Bằng chứng |
|---|---|---|
| Quyền | Mọi route 26878 / sap-log / sapp-ops mang @Public(); lớp chặn thật (secret gate) → nền tảng | sapp.controller.ts:2648,2688,2961,3147,3370,3469,3652,3756,774,798; sapp-ops.controller.ts (→ nền tảng) |
| Tích hợp ngoài — HubSpot | Tạo deal/contact, đọc/ghi property deal & contact, đọc pipelines, đọc property definition, thêm option property (dev). Staging dùng credential portal prod (pipeline [UAT-TEST]) | sapp.controller.ts:2623-2645,2810-2815,3040-3100,3616-3649,700-715; sapp-26878-tool.tsx:85-90 |
| Tích hợp ngoài — SAPP Ops API | 18 thao tác mà spec dùng (ping, classes, class-room, update class, auto-update-schedule, product-orders, transactions, confirm, students, transfer, teachers, class-schedules update/cancel/delete, sap-revenue resync entry/class). Teacher test dùng bộ field cố định theo env ở BE | sapp-26878-flows.ts (Grep op: " = 18 op); sapp-ops.controller.ts:498-505 |
| Tích hợp ngoài — SAPP Ops DB | Chạy statement SQL theo id (dev/staging) — gồm câu ghi (backdate buổi, seed mốc cửa sổ, ép dòng FAILED, trả mã lớp…); BE cũng dùng p2.takencodes.1 để tính pool mã | sapp.controller.ts:746-760; sapp-26878-flows.ts:3900,4226,4340,4351,4575,5537,5691,5836-5865,6156,6162 |
| Tích hợp ngoài — SAP service (lms-be) | Tra trail/scan-runs (cả prod), manual scan (dev/staging, GET có tác dụng phụ), dựng callback đã ký (dev/staging); tester tự bắn curl | sapp.controller.ts:3841-3930,3991-4047; fe/src/data/services/sapp/sap-log.service.ts:56-63,79-88; sapp-26878-flows.ts:6072-6086 |
| Tích hợp gián tiếp | Kéo Won / ghi Đồng bộ lại → webhook HubSpot → lms-be → SAP (tool không gọi SAP trực tiếp ở phase 1) — Fact theo hint & comment | sapp-26878-flows.ts:1139,1151; sapp-26878.constants.ts:255-262 |
| Thông báo | Không có mail/push/MQTT trong tool. Mail kế toán là hành vi của lms-be được nhắc trong hint | sapp-26878-flows.ts:1282,727 |
| Job / cron | Không có job trong repo này. Lượt quét tuần (Thứ 7) và vòng đối soát 15 phút thuộc SAP service — tool chỉ có nút quét thủ công | sapp-26878-tool.tsx:213-218; sapp-26878-flows.ts:6082 |
7. Drift — tài liệu nói khác code ​
| ID | Tài liệu (file + section) | Tài liệu nói | Code đang làm | Bằng chứng |
|---|---|---|---|---|
| DR-01 | shared/api-docs/sapp.md §"PUT and GET …/runs/{flow}" (dòng 2032-2049, mẫu 2056-2077) | Entry re-sync mang trigger, sourceRunId, sourceFlow; mẫu response có 3 field | Service không lưu 3 field này → GET không bao giờ trả; FE log hiện — và không hiện "từ luồng" | common.service.ts:1145-1156; fe/src/ui/components/works/sapp/sapp-26878-resync-tool.tsx:228-238 |
| DR-02 | shared/api-docs/sapp.md §runs, bảng flow (dòng 2027-2034) | Bucket chỉ us03/05/06/11/13 + us08/09/10/12/14 | FE ghi/đọc thêm bucket phase 2 (us01, us02, us03-p2, us04, us05-p2…us09-p2) | sapp-26878-tool.tsx:1337,1384-1389; sapp-26878-flows.ts:6274-6283 |
| DR-03 | shared/api-docs/sapp.md §runs, dòng 2079-2080 | Lỗi SAPP_26878_DEAL_NOT_FOUND (13033) | Tên hằng là SAPP_26878_DEAL_NOT_STAGED (13033); FAILED_TRIGGER_SAPP_26878_RESYNC (13036) khai nhưng không route nào ném | be/libs/constants/src/lib/error-codes/error-codes.constants.ts:704-722; Grep FAILED_TRIGGER_SAPP_26878_RESYNC chỉ 1 kết quả (định nghĩa) |
| DR-04 | shared/api-docs/sapp.md §"SAPP-26878 tool: HubSpot test data" (bảng route 1391-1428) và toàn file | Không có route assign-class-code | Route POST {env}/sapp-26878/deals/{dealId}/assign-class-code tồn tại và bước "Gán mã lớp" dùng nó | sapp.controller.ts:3146,3166,3186; fe/src/data/constants/api.ts:308-311; Grep assign-class-code trong sapp.md = 0 |
| DR-05 | shared/api-docs/sapp.md §POST deals, dòng 1443-1447 | Liệt kê nhãn flow cho tên deal: US-03, US-05, US-06, US-11×3, US-13×3 | Còn 4 nhãn phase 2: US-04 Ghi bù · Học viên A/B, US-05 Rời lớp · Học viên A/B | sapp-26878.constants.ts:515-529 |
| DR-06 | shared/runbooks/sapp/sapp-26878/phase-2/staging/README.md §0.1 "Vòng đời một mã lớp" (dòng 113-120) và các lượt 6d–6h ghi "Trả lại mã" | Bước cuối "Trả lại mã lớp" của mọi lượt | Chỉ US-01 có bước "Trả lại mã lớp (staging)"; US-02…US-09 không có bước này trong spec | sapp-26878-flows.ts:3343-3350 vs :3628-3647,4044-4082,4459-4496,4876-4906,5345-5380,5592-5617,5911-5948,6216-6252 |
| DR-07 | shared/external/sapp/sapp-26878/README.md dòng 182, 198 | "chip US-04 (28 bước)", "US-06 24 bước" | Spec hiện có US-04 = 29 bước, US-06 = 22 bước (có thể là thay đổi sau ngày ghi) | sapp-26878-flows.ts:4459-4496,5345-5380 |
| DR-08 | shared/api-docs/sapp.md §runs, dòng 2082-2084 | "Chưa endpoint nào trong mục này được gọi thật" | Tool gọi PUT runs mỗi lần tạo deal / bấm resync (có thể doc chỉ cũ) | sapp-26878-tool.tsx:1337,1526 |
| DR-09 | Comment trong code sapp26878ResyncRules | "Chỉ US-13 chưa khai bước resync (tới khi US-14 dựng)" | US-13 đã có bước resync US-14 | sapp-26878-flows.ts:2453-2456 vs :2361-2369 |
| DR-10 | Comment signSapCallback trong dispatcher | "flow is pinned to dev" | Phase 2 đã chạy được staging (integration #40BE) | sapp-26878-step-ops.tsx:1060-1063; shared/integration/README.md:202 |
| DR-11 | Hint các bước US-01 "Tạo lớp" / "Tạo đơn hàng" | "copy sang Deal ở bước 5", "gắn đúng Deal ở bước 5" | Deal là bước 8 của US-01 | sapp-26878-flows.ts:2874,3113 vs :3095 |
8. Câu hỏi cho BA (code không trả lời được) ​
| ID | Câu hỏi | Vì sao cần | Chặn bước sau? |
|---|---|---|---|
| Q-01 | R-30/DR-01: Service bỏ trigger/sourceRunId/sourceFlow khi lưu run → log đồng bộ lại hiện —, không biết lần bấm là invoice hay reconcile và từ run nào. Cố ý hay bug? Nếu bug, BE phải sửa (handover qua shared/integration/). | Quyết định ghi thành BR ("log mỗi lần bấm có trigger + nguồn") hay OQ | Có |
| Q-02 | R-33: BE mở route prod cho mọi thao tác ghi (tạo deal, kéo stage Won, resync, tạo đơn/confirm giao dịch Ops) — chặn prod chỉ ở FE. Đây là thiết kế chấp nhận được (có secret gate nền tảng) hay rủi ro cần đóng? | Kéo Won trên prod bắn sự kiện SAP thật (sapp-26878-tool.tsx:85-90) | Có |
| Q-03 | R-34/DR-02: Run history phase 2 hoạt động nhờ BE không kiểm {flow} runtime, trong khi enum ESapp26878Chip chỉ có phase 1. Phase 2 có được chính thức lưu history không? | Viết BR về lịch sử test phase 2 | Có |
| Q-04 | Modal "Lịch sử" luôn hiện ở mọi luồng; với luồng phase 2, SAPP_26878_RESYNC_STORY[flowId] = không có → story.toUpperCase() sẽ lỗi (⚠️ Observed, suy từ code, chưa chạy). Đã biết? Phase 2 có cần màn lịch sử không? | Quyết định BR hay bug | Có |
| Q-05 | Mục đích nghiệp vụ tổng: tool dành cho ai (dev/QA nội bộ, kế toán, SAPP)? "Tester" trong code là ai? | Viết Actor / US Statement | Có |
| Q-06 | R-04/R-07: Chọn "Dùng deal có sẵn" không kiểm deal id, và đánh dấu mọi bước còn lại của deal đó là xong. Ở phase 2 (1 deal cho cả luồng) điều này đánh dấu luôn các bước Ops (tạo lớp, đơn, giao dịch, quét). Cố ý? | Quyết định BR "deal có sẵn" | Có |
| Q-07 | DR-06: Chỉ US-01 có bước "Trả lại mã lớp (staging)". Các luồng phase 2 khác chạy trên staging trả mã bằng cách nào (runbook ghi có trả)? Có cần thêm bước? | Rủi ro cạn pool mã staging (sapp.controller.ts:659-687) | Có |
| Q-08 | US-09: shared/external/sapp/sapp-26878/README.md:222 ghi bộ ký /sign-callback dựng body bị webhook từ chối, đang ⏳ Chờ SAPP, chặn 2 ca bắt buộc (ERR-05, FAILED không artifact). Trạng thái hiện tại? US-09 phase 2 coi là "chưa nghiệm thu"? | Phạm vi FS as-built US-09 | Có |
| Q-09 | Manual scan là GET nhưng gây tác dụng phụ (bắn lượt quét) — chấp nhận ở nền tảng? | Ghi rủi ro kỹ thuật | Không |
| Q-10 | Phase 1: tạo đơn hàng / giao dịch Ops là làm tay (guide), phase 2 thì tool tự gọi Ops. Có kế hoạch tự động hóa phase 1 không, hay đây là phạm vi chốt? | Ranh giới phạm vi | Không |
| Q-11 | R-21: số thứ tự lớp lấy từ lượt đếm tại thời điểm bấm — người khác tạo lớp xen giữa → trùng mã (hint tự nêu). Có cần rule chống trùng? | Rủi ro dữ liệu | Không |
| Q-12 | Danh sách tester cố định trong SAPP_26878_PROVIDED_OPTIONS.hubspot_owner_id (3 người, hard-code FE). Ai duy trì danh sách này? | BR cấu hình | Không |
| Q-13 | Ý đồ từng story US-01…US-09 (phase 2) và US-08…US-14 (re-sync) trong FS gốc: code chỉ có hint/AC id (AC-M1.4, AC-9.5, BR-03…). BA có FS gốc để đối chiếu? | Viết BR/AC as-built chính xác | Có |
| Q-14 | Không kiểm chứng được sự khớp id câu SQL FE ({idPrefix}.{i+1}) với 59 id BE cho từng panel (chỉ kiểm tiền tố). Cần một lượt đối chiếu kỹ thuật? | Nút "Chạy" có thể tắt âm thầm khi lệch id (sapp-26878-flows.ts:607-612) | Không |
| Q-15 | Bước "Đọc kết quả" không bao giờ chặn (R-03) — nghiệm thu dựa vào tester đọc check. Có cần gate "check phải xanh" không? | BR nghiệm thu | Không |
9. Nhật ký nguồn đã đọc ​
| File / section | Mục đích |
|---|---|
ba/_workflow/templates/feature-inventory/Template.md | Khuôn tài liệu |
ba/system-profile.md §3–§5 | Công thức truy vết, quy ước |
fe/app/works/sapp/tools/sapp-26878/page.tsx | Route |
fe/src/ui/components/works/sapp/sapp-26878-flows.ts (1–2520 đọc chi tiết; 2584–3363 US-01; 3364–6283 dàn ý + step list từng luồng) | Xương sống flow/bước, rule, US mapping |
fe/src/ui/components/works/sapp/sapp-26878-tool.tsx (60–960, 995–1025, 1100–1135, 1196–1655, 1870–2222) | Stepper, gate, session, run history, chip, env |
fe/src/ui/components/works/sapp/sapp-26878-step1.tsx (420–650 + grep) | Tạo deal/contact, validation |
sapp-26878-step-stage/-sync/-link/-option/-classcode/-scan/-skip/-sql/-resync.tsx (grep + đoạn) | Hành vi từng kind |
fe/src/ui/components/works/sapp/sapp-26878-step-ops.tsx (400–470, 640–690, 1005–1071) | Danh mục, pool mã, dispatcher op |
fe/src/ui/components/works/sapp/sapp-26878-history.tsx (46–125) | Modal lịch sử, bug nghi Q-04 |
fe/src/ui/components/works/sapp/sapp-26878-resync-tool.tsx (1–60, 195–334) | Màn đồng bộ lại theo story |
fe/src/ui/components/works/sapp/sap-resync-panel.tsx (grep) | 3 chip gửi lại thô |
fe/src/data/constants/api.ts:226-437 | URL |
fe/src/data/services/sapp/sapp-26878.service.ts, sapp-ops.service.ts, sap-log.service.ts:50-100 (grep) | Service FE |
be/apps/api/src/sapp/sapp.controller.ts (150–172, 640–820, 2560–3570, 3575–3845, 3975–4050 + grep) | Route, assert deal, sync, link, assign, resync, runs, contacts, pipelines, catalogs, class-codes |
be/apps/api/src/sapp/sapp-ops.controller.ts (grep route) | Route Ops / SQL mà spec gọi |
be/libs/modules/src/lib/sapp/common.service.ts:1095-1210,1610-1665 | Lưu run Redis, tạo deal instance |
be/libs/interface/src/lib/dto/sapp/sapp-26878.dto.ts (grep + 20–75, 145–186, 313–362) | DTO |
be/libs/constants/src/lib/sapp/sapp-26878.constants.ts (18–539) | Whitelist, enum, key Redis, pipeline, nhãn |
be/libs/constants/src/lib/sapp/sapp-26878-sql.constants.ts (grep id) | Catalogue SQL |
be/libs/constants/src/lib/error-codes/error-codes.constants.ts:674-722 | Mã lỗi |
be/libs/database/src/lib/entities/ (Grep 26878 = 0) | Xác nhận không có entity |
shared/api-docs/sapp.md (heading 1373–2753; đọc 1373–1452, 2015–2086) | Đối chiếu Drift |
shared/runbooks/sapp/sapp-26878/README.md, phase-2/staging/README.md (heading + 110–125, 580–600) | Drift DR-06, US mapping |
shared/external/sapp/sapp-26878/README.md (ledger 176–222), 2026-09-22-mind-sign-callback-lech-dto.md:1-25 | Fact ý đồ/trạng thái US-03…US-09 phase 2 |
shared/integration/README.md:202 (#40BE) | Phase 2 chạy staging |