Skip to content

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 repo mind_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.

FieldValue
Projectba/projects/2026-SAPP-Works-tools
ModeB – As-built
Ngày đọc code2026-09-30
Commite77c47a
Điểm vào BA cung cấpSub-tool sapp-26878 (sapp.010, "SAPP 26878: Create deal instance") — route /works/sapp/tools/sapp-26878
Thành phần chạm tớiFE · BE (api) · hệ ngoài: HubSpot (dev portal / prod portal), SAPP Ops API + Ops DB, SAP service (lms-be)
StatusDraft

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 → component Sapp26878Tool (ⓘ route import thẳng component, không có page trong fe/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/* trong be/apps/api/src/sapp/sapp.controller.ts, các route sapp-ops/* mà spec gọi (be/apps/api/src/sapp/sapp-ops.controller.ts), lưu run trong be/libs/modules/src/lib/sapp/common.service.ts, DTO be/libs/interface/src/lib/dto/sapp/sapp-26878.dto.ts, hằng số be/libs/constants/src/lib/sapp/sapp-26878.constants.ts.
  • 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 trong SapLogCommonService, SappOpsSqlCommonService, Ops passthrough (→ nền tảng); nội dung từng câu SQL trong sapp-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 — Grep 26878 trong be/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) ​

IDLoạiTên / đường dẫn người dùng thấyBằng chứng
E-01Mà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-02Chip 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ượngsapp-26878-flows.ts:2389; sapp-26878-tool.tsx:197-202
E-03Chip phase 1 (đồng bộ lại)Re-sync US-03/05/06/11/13 = story US-08/09/10/12/14sapp-26878-flows.ts:2416-2446; sapp-26878-tool.tsx:182-189,2214-2216
E-04Chip TraceTrace log SAP (đọc, chạy cả prod)sapp-26878-tool.tsx:206-211,2201-2212
E-05Chip TraceChạy lượt quét (quét doanh thu thủ công)sapp-26878-tool.tsx:213-218,2190-2200
E-06Chip TraceGử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-07Chip 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-08ModalLog 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) ​

IDLoạiEndpointBằng chứng
E-10APIPOST /api/v1/sapp/{env}/sapp-26878/deals — tạo dealbe/apps/api/src/sapp/sapp.controller.ts:2647,2667,2719
E-11APIGET /api/v1/sapp/{env}/sapp-26878/deals — danh sách deal tool đã tạosapp.controller.ts:2687,2703,2739
E-12APIGET /api/v1/sapp/{env}/sapp-26878/deals/{dealId} — đọc pipeline/stagesapp.controller.ts:2827,2844,2857
E-13APIPATCH /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/stagesapp.controller.ts:2960,2981,3002
E-14APIPOST /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/assign-class-codesapp.controller.ts:3146,3166,3186
E-15APIPOST /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/sync-course-packagesapp.controller.ts:3206,3223,3240
E-16APIPOST /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/link-source-dealsapp.controller.ts:3258,3280,3302
E-17APIPOST /api/v1/sapp/{env}/sapp-26878/deals/{dealId}/resyncsapp.controller.ts:3369,3389,3409
E-18APIPUT / GET /api/v1/sapp/{env}/sapp-26878/runs/{flow}sapp.controller.ts:3468,3489,3509,3529,3546,3558
E-19APIPOST / GET /api/v1/sapp/{env}/sapp-26878/contactssapp.controller.ts:3651,3669,3687,3705,3722,3738
E-20APIGET /api/v1/sapp/{env}/sapp-26878/pipelines/{flow}sapp.controller.ts:3755,3772,3789
E-21APIGET /api/v1/sapp/{dev|staging}/sapp-26878/us01/catalogs (không có prod)sapp.controller.ts:797,809
E-22APIGET /api/v1/sapp/{dev|staging}/sapp-26878/us01/class-codes (không có prod)sapp.controller.ts:773,785
E-23APIPOST /api/v1/sapp/dev/hubspot/{deals|contacts}/properties/{property}/options — chỉ devsapp.controller.ts:821,1101; FE fe/src/data/services/sapp/add-property-option.service.ts:36
E-24API (→ nền tảng)POST /api/v1/sapp/{env}/sap-log/trace, …/scan-runssapp.controller.ts:3841,3886
E-25API (→ nền tảng)GET /api/v1/sapp/{dev|staging}/sap-log/manual-scansapp.controller.ts:3991,4003
E-26API (→ nền tảng)POST /api/v1/sapp/{dev|staging}/sap-log/sign-callbacksapp.controller.ts:4017,4033
E-27API (→ 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-28API (→ nền tảng)GET …/{dev|staging}/sapp-ops/sql/status, POST …/sql/runsapp-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) ​

IDAi (role)Làm gìĐiểm vàoKết quả quan sát đượcBằng chứng
A-01TesterChọn portal Dev / Staging / ProdE-01Luồ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áosapp-26878-tool.tsx:91-119,1923-1935,1977-2006
A-02TesterChọn chip luồngE-02/E-07Mở stepper của luồng; mỗi (luồng, portal) là một "sitting" riêngsapp-26878-tool.tsx:1944-1950,2173-2180
A-03Hệ thống (FE)Lưu / khôi phục sittingE-01Toà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 tabsapp-26878-tool.tsx:314-391,439-526,754-788
A-04TesterMở một bướcE-01Chỉ 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-05Hệ thống (FE)Tự chuyển bướcE-01Xong 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-06Tester"Bỏ qua bước này" (mọi bước)E-01Bướ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ế độ existingfe/src/ui/components/works/sapp/sapp-26878-step-skip.tsx:28-60,79-100; sapp-26878-tool.tsx:798-824,1225-1240
A-07TesterReset luồng (bấm 2 lần)E-01Xóa sitting của đúng (luồng, portal), về bước 1, remountsapp-26878-tool.tsx:494-502,1905-1916
A-08TesterMở "Log SAP"E-08 → E-24Tra log SAP theo mọi deal id lượt này đã tạo; nút tắt khi chưa có dealsapp-26878-tool.tsx:957-960,1001-1014,1375-1381
A-09TesterMở "Lịch sử"E-08 → E-18Liệ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-10Hệ thống (FE)Ghi run historyE-18Ghi (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/sourceFlowsapp-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 ​

IDKind (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-20deal — "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-10HubSpot 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 dealsapp-26878-step1.tsx:365,379,510-620; sapp-26878-tool.tsx:866-869,1272-1352
A-21guide — "Đơ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"/acksapp-26878-flows.ts:509-534; sapp-26878-tool.tsx:870,1360-1373
A-22stage — "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 dealstageE-12, E-20, E-13HubSpot deal dealstage → kéo Won là nơi event SAP bắn ra (theo hint)Move thành côngfe/src/ui/components/works/sapp/sapp-26878-step-stage.tsx:35,76-122; sapp-26878-flows.ts:620-632,1136-1140
A-23sync — "Sync gói khóa học" (US-11 Deal gốc)Gộp 6 property lộ trình vào goi_khoa_hocE-15HubSpot deal goi_khoa_hocGọi thành côngsapp-26878-step-sync.tsx:52; sapp-26878-flows.ts:1845-1857
A-24link — "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-16HubSpot dealGọi thành công; cần đủ 2 dealsapp-26878-step-link.tsx:59; sapp-26878-tool.tsx:1559-1587; sapp-26878-flows.ts:1876-1881,2237-2242
A-25resync — "Đồ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-17HubSpot deal → webhook bắn SAP (theo hint)Bấm ít nhất 1 lầnsapp-26878-step-resync.tsx:58,75; be/apps/api/src/sapp/sapp.controller.ts:3335-3366
A-26ops — các bước phase 2Gọ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 bagE-27 (+E-21/E-22 cho dropdown)SAPP Ops (qua passthrough)Gọi thành côngsapp-26878-step-ops.tsx:411-456,640-690,1011-1070; sapp-26878-flows.ts:107-312
A-27option — "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ỏ quasapp-26878-step-option.tsx:40-44,69-117,165-166; sapp-26878-flows.ts:3080-3093
A-28classcode — "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-14HubSpot deal + contactGọi thành côngsapp-26878-step-classcode.tsx:68; sapp.controller.ts:3040-3100; be/libs/constants/src/lib/sapp/sapp-26878.constants.ts:132-135
A-29scan — "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 ở prodE-25SAP service (lms-be)Gọi thành côngsapp-26878-step-scan.tsx:56-61,89; sapp-26878-flows.ts:3322-3330
A-30trace — "Đọ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ảnE-24— (chỉ đọc)Luôn coi là xongsapp-26878-tool.tsx:883-884,1465-1501; sapp-26878-flows.ts:386-507
A-31sql (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 SQLE-28SAPP Ops DB (dev/staging)Không ảnh hưởng gatesapp-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

IDBướcKindLàm gì / ghi vào đâuBằng chứng
A-401 Tạo dealdealDeal 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_vusapp-26878-flows.ts:936-980,1172
A-412 Đơn hàng OpsguideTester tạo đơn trên SAPP OPS cho deal bước 1sapp-26878-flows.ts:1112-1118,1173-1179
A-423 Soạn thảo hợp đồngstageGợi ý Soạn thảo hợp đồngsapp-26878-flows.ts:1120-1124
A-434 Giao dịch trong đơnguideTạo + confirm từng transaction trên Ops; tổng ≥ order amountsapp-26878-flows.ts:987-993,1126-1134
A-445 Kéo sang WonstageGợi ý Won - Hoàn tất thủ tục (mốc bắn event SAP)sapp-26878-flows.ts:1136-1140
A-456 Đồng bộ lại hóa đơnresyncinvoice trên chính deal (story US-08)sapp-26878-flows.ts:1147-1154
A-467 Đồng bộ lại reconcileresyncreconcile 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

IDBướcKindLàm gìBằng chứng
A-501 Tạo dealdealBắ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 pipelinesapp-26878-flows.ts:1219-1299
A-512 Đơn hàng OpsguideKhông bắt buộc — chỉ khi test cấn trừ tự độngsapp-26878-flows.ts:1301-1308
A-523 Thông tin thanh toánstageGợi ý Thông tin thanh toánsapp-26878-flows.ts:1310-1314
A-534 Giao dịch trong đơnguideChỉ khi test cấn trừ; cấn trừ cần sap_sync_status = SUCCESSsapp-26878-flows.ts:1316-1324
A-545 Hoàn thành phí DVstageGợ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-556–7 Đồng bộ lại hóa đơn / reconcileresyncStory US-09, kèm caveat ngày bấmsapp-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

IDBướcKindLàm gìBằng chứng
A-601 Tạo dealdealBắ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-612 Đơn hàng OpsguideKhông bắt buộc; đơn ADD_ON → test_subject của sản phẩm thắng property môn thisapp-26878-flows.ts:1526-1536
A-623 Thông tin thanh toánstagesapp-26878-flows.ts:1538-1542
A-634 Giao dịch trong đơnguideChỉ khi có đơn bước 2sapp-26878-flows.ts:1544-1550
A-645 Hoàn thành cung cấp DVstageGợ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-656–7 Đồng bộ lại hóa đơn / reconcileresyncStory US-10, caveat môn thi + ngàysapp-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

IDKhối / bướcKindLàm gìBằng chứng
A-701a 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/stageDeal 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-711b 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/stagesapp-26878-flows.ts:1684-1700,1875-1921
A-721c 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/stagesapp-26878-flows.ts:1747-1765,1923-1964
A-7318 Đồng bộ lại hóa đơn (chỉ Deal cộng, chỉ invoice)resyncStory US-12; 3 caveatsapp-26878-flows.ts:1966-1974,1985-1994,770-774
A-74Recipe đơ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

IDKhối / bướcKindLàm gìBằng chứng
A-801a Deal gốc (HV chuyển ra): 1 Tạo deal · 2 Đơn Ops · 3 Stage · 4 Giao dịch · 5 Kéo Wondeal/guide/stage/guide/stageKhông có bước syncsapp-26878-flows.ts:2011-2020,2205-2234
A-811b 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-821c 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-8317 Đồng bộ lại hóa đơn (chỉ Deal cộng)resyncStory US-14sapp-26878-flows.ts:2327-2335,2346-2358,775-779

Màn đồng bộ lại theo story (ngoài stepper)

IDAiLàm gìĐiểm vàoKết quảBằng chứng
A-90TesterMở chip Re-sync US-0x, chọn luồng nguồn, chọn run cũ, bấm invoice/reconcile trên deal được phépE-03 → E-18 (GET runs luồng nguồn), E-17Ghi Đồng bộ lại lên deal; ghi entry mới vào bucket story; hiện log các lần bấmfe/src/ui/components/works/sapp/sapp-26878-resync-tool.tsx:77-106,195-334; sapp-26878-flows.ts:2458-2470
A-91TesterGửi lại thô theo id: transaction (P1), entry, class (P2)E-06 → E-27Ops gửi lại SAPsap-resync-panel.tsx:41-62,120-130
A-92TesterChip "Chạy lượt quét" + danh sách scan runsE-05 → E-25, E-24Bắn quét, xem các lượt quétsapp-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

IDBướcKindLàm gì / API / hệ thốngBag vào → raBằng chứng
A-1001 Kiểm tra kết nối Opsops + sqlpingOps (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-1012 Đếm lớp của mônopscountClasses (GET classes, lọc subject_id, category=STANDARD) → số thứ tự lớp mới = total + 1→ subject_code, class_total, class_seqsapp-26878-flows.ts:2834-2862,2584-2605
A-1023 Tạo lớpops + sqlcreateClass (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_codesapp-26878-flows.ts:2863-2985; pool: sapp-26878-step-ops.tsx:429-456
A-1034 Tạo buổi họcopsautoUpdateSchedule 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-1045 Gán phòng họcopsupdateClassRoom (PUT class-room); lớp không ONLINE bắt buộc có phòngclass_id →sapp-26878-flows.ts:3023-3049
A-1056 Mở lớp (PUBLIC)opsupdateClass DRAFT → PUBLICclass_id →sapp-26878-flows.ts:3050-3079
A-1067 Thêm option mã lớpoption3 property HubSpot (dev); staging làm tayclass_code →sapp-26878-flows.ts:3080-3093
A-1078 Tạo DealdealNhư 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-1089 Gán mã lớpclasscodeGhi mã lớp lên deal + contactdeal_id,contact_id,class_code →sapp-26878-flows.ts:3096-3104; sapp-26878-tool.tsx:1392-1408
A-10910 Tạo đơn hàngopscreateProductOrder (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_amountsapp-26878-flows.ts:3106-3229; sapp-26878-step-ops.tsx:640-672
A-11011 Soạn thảo hợp đồngstageHubSpotsapp-26878-flows.ts:3230-3236
A-11112 Tạo giao dịchopscreateTransaction trong đơn; amount = order_amount (readOnly type TUITION_PAYMENT)order_id,order_amount → transaction_idsapp-26878-flows.ts:3237-3295,3250-3259
A-11213 Xác nhận giao dịchopsconfirmTransaction — bước gọi SAP, sinh số dư 3387transaction_id →sapp-26878-flows.ts:3296-3307
A-11314 Kéo sang WonstageWon - Hoàn tất thủ tục (bắn event hóa đơn)sapp-26878-flows.ts:3308-3314
A-11415 Hoàn thiện hồ sơstageWon - Hoàn thiện hồ sơ — action tự thêm học viên vào lớpsapp-26878-flows.ts:2748-2752,3315-3321
A-11516 Chạy lượt quétscanManual scansapp-26878-flows.ts:3322-3330
A-11617 Đọc kết quảtraceLọc theo deal_id; check: standard_hours_remaining giảm dần, mẫu số không trùng từng buổisapp-26878-flows.ts:2715-2747,3331-3342
A-11718 Trả lại mã lớp (staging)guide + sqlSQL p2.retire.1 (đọc) / p2.retire.2 (ghi: đổi code thành removed_{ts}_{mã} + đặt deleted_at); bỏ qua trên devsapp-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)

IDLuồngSố bướcChuỗi bướcKhác biệt / bước riêngBằng chứng
A-120F-P2-02 · US-02 · DTTH lớp online15U01: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-121F-P2-03 · US-03 · DTTH chốt sổ19U01: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-122F-P2-04 · US-04 · DTTH ghi bù (2 học viên A/B)29Hà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-bsapp-26878-flows.ts:4109-4139,4140-4183,4192-4450,4452-4497
A-123F-P2-05 · US-05 · DTTH rời lớp (A rời / B nhận)30Hà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ớpsapp-26878-flows.ts:4514-4549,4550-4868,4869-4906
A-124F-P2-06 · US-06 · DTTH đảo buổi22U01: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ượtsapp-26878-flows.ts:4939-5337,5338-5380
A-125F-P2-07 · US-07 · Trường chiều18U01: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êngsapp-26878-flows.ts:5402-5584,5585-5617
A-126F-P2-08 · US-08 · DTTH đồng bộ lại22U01: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-127F-P2-09 · US-09 · DTTH đồng bộ ngược21U01: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 toolsapp-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ảngFieldTypeBắt buộcMặc định / enumBằng chứng
Redis sapp:26878:deal-instances:{env}id, dealnamestring—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 dealbe/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, namestring—Contact tool đã tạosapp-26878.constants.ts:191-192
Redis sapp:26878:run-history:{env}:{flow} (Run)id, flow, env, createdAt, updatedAt, deals[]string / arrayid, dealsTTL 30 ngày (ms), tối đa 50 run/flow/envsapp-26878.constants.ts:278-296; common.service.ts:1134-1171
Run.deals[] (RunDeal)flow, label, dealId, dealname, contactId, modestringkhôngmode: new | existingbe/libs/interface/src/lib/dto/sapp/sapp-26878.dto.ts:187-215
Run (chỉ bucket re-sync)trigger, sourceRunId, sourceFlowenum/stringkhôngtrigger: 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 dealproperties, contactIds, flowmap / string[] / enumkhôngflow ∈ 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 deal52 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ỏ quasapp-26878.constants.ts:18-99,176
Request tạo contactname, firstname, lastname, email, phone_number, address, province, d_o_b, cccd_passport, lop_dang_kystringname (FE bắt buộc)phone_number copy sang phone; cccd_passport → cccd_ho_chieusapp-26878.dto.ts:46-70; sapp.controller.ts:3579-3610; sapp-26878-step1.tsx:532-543
Request stagedealstagestringcóid stage đọc từ pipelinessapp-26878.dto.ts:177-185
Request linksource_deal_idstringcóghi vào deal_id_goc_cua_chuyen_doisapp-26878.dto.ts:145-151; sapp-26878.constants.ts:169-170
Request resynctargetenumcóinvoice→trang_thai_dong_bo_hoa_don, reconcile→trang_thai_tu_dong_reconcile; giá trị ghi Đồng bộ lạisapp-26878.dto.ts:313-319; sapp-26878.constants.ts:250-273
Request assign class codeclass_code, contact_idstringclass_codeghi deal lop_dang_ky_cloned_,lop_dang_ky; contact lop_dang_kysapp-26878.dto.ts:349-361; sapp-26878.constants.ts:132-135
Danh mục pipeline theo flow × envid pipelinestring[]—Prod = [] cho mọi flow; staging dùng pipeline [UAT-TEST] trên portal prodsapp-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, runIdJSON—runId = crypto.randomUUID() mỗi sittingsapp-26878-tool.tsx:314-350,517
FE "bag" id phase 2subject_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_idstring—Sinh từ response Ops / kết quả SQL / nhập tay khi bỏ quasapp-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 envconstant BE—Chỉ dev + staging; thiếu env → 501sapp.controller.ts:648-657; sapp-26878-flows.ts:89-105
Statement SQL catalogue59 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ự độngdealnamestring—[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 ​

IDRule (ngôn ngữ nghiệp vụ)LoạiÁp dụng ởBằng chứngNghi là bug?
R-01Luồng dựng data chỉ chạy trên Dev và Staging; Prod chỉ mở cho màn tra logđiều kiệnA-01sapp-26878-tool.tsx:91-119Không (nhưng xem R-33)
R-02Một bước chỉ mở khi mọi bước trước đã xong; lùi lại luôn đượctrạng tháiA-04sapp-26878-tool.tsx:888-894Không
R-03Bước "Đọc kết quả" (trace) luôn coi là xong — không chặn bước sautrạng tháiA-30sapp-26878-tool.tsx:883-884Không
R-04Deal 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áiA-20, A-06sapp-26878-tool.tsx:847-857⚠️ Observed (Q-06)
R-05Tạ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 idvalidationA-20sapp-26878-step1.tsx:448-485Không
R-06Property "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 dealvalidationA-20sapp-26878-step1.tsx:487-508; sapp-26878-flows.ts:893-894Không
R-07Chế độ "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ạovalidationA-20sapp-26878-step1.tsx:452-455,511-521⚠️ Observed (Q-06)
R-08Giá trị trống không gửi lên khi tạo deal; pipeline, dealstage luôn gửivalidationA-20sapp-26878-step1.tsx:577-584Không
R-09BE chỉ nhận property trong whitelist; key lạ → 400 SAPP_26878_INVALID_DEAL_PROPERTY (13027); hs_object_id bỏ quavalidationE-10sapp.controller.ts:2596-2621Không
R-10dealname do server sinh, không cho client đặt; bộ đếm theo tiền tố flowcông thứcE-10sapp.controller.ts:2623-2645; common.service.ts:1624-1634Khô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ệnE-12..E-17sapp.controller.ts:2762-2773,2789,2810,2892,3047,3112,3341Không
R-12Chuyển stage chỉ ghi dealstagegiới hạnA-22sapp.controller.ts:2755-2757,2810-2815Không
R-13Stage 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ệnA-22sapp-26878-step-stage.tsx:35,100-107; sapp-26878-flows.ts:624-629Không
R-14Danh 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ệnE-20sapp.controller.ts:3616-3649Không
R-15Sync 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 ghicông thứcA-23sapp.controller.ts:2887-2957; sapp-26878.constants.ts:114-121Không
R-16Link deal gốc & gán mã lớp: không ghi nếu giá trị đã đúng (changed=false)điều kiệnA-24, A-28sapp.controller.ts:3075-3083,3121-3126Khô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ệnA-25sapp.controller.ts:3328-3366; sapp-26878-step-resync.tsx:75,145Không (có chủ ý theo comment)
R-18Deal/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ệnA-90, A-09sapp-26878-flows.ts:2458-2470Không
R-19"Thêm option mã lớp" chỉ chạy ở dev (staging dùng credential portal prod)điều kiệnA-27sapp-26878-step-option.tsx:69-72,117Không
R-20Pool 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ứcA-102sapp.controller.ts:659-771; sapp-26878-step-ops.tsx:445-456Không
R-21Mã 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ứcA-101, A-102sapp-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 xongvalidationA-109sapp-26878-step-ops.tsx:640-646Không
R-23amount của giao dịch lấy từ order_amount của đơn (không mặc định)công thứcA-111sapp-26878-flows.ts:3254-3259; sapp-26878-step-ops.tsx:670-672Không
R-24Field Ops trống bị bỏ khỏi payload, trừ field allowEmpty (vd link_meeting)validationA-26sapp-26878-flows.ts:154-162Không
R-25Chạy lượt quét bị tắt ở prod (không có route prod)điều kiệnA-29sapp-26878-step-scan.tsx:56,89; fe/src/data/constants/api.ts:261-266Không
R-26SQL: 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 idvalidationA-31sapp-26878-step-sql.tsx:40,200-213; sapp-26878-flows.ts:584-590,607-612Không
R-27Check trace không đọc được field → "unknown", không phải "fail"điều kiệnA-30sapp-26878-flows.ts:382-384Không
R-28Run 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ướcgiới hạnA-10common.service.ts:1134-1205Không
R-29Mỗi lần bấm đồng bộ lại tạo một entry run mới (id mới) trong bucket storytrạng tháiA-10, A-90sapp-26878-tool.tsx:1526-1545; sapp-26878-history.tsx:95-101Không
R-30Khi lưu run, service chỉ giữ id/flow/env/createdAt/updatedAt/deals, bỏ trigger/sourceRunId/sourceFlow client gửitrạng tháiA-10, A-90common.service.ts:1145-1156 vs sapp-26878.dto.ts:285-298⚠️ Observed (Q-01)
R-31Identity 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ứcA-20sapp-26878-tool.tsx:399-404,541-547,616-710Không
R-32Danh mục phase 2 thiếu env → 501 (không trả rỗng); không có prodđiều kiệnE-21sapp.controller.ts:640-657Không
R-33BE 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ệnE-10..E-18, E-27sapp.controller.ts:2647,2960,3146,3206,3258,3369,3468; sapp-ops.controller.ts:1261,2179,2290⚠️ Observed (Q-02)
R-34Param {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…)validationE-18sapp.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 1

Trạ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ạiChi tiếtBằng chứng
QuyềnMọi route 26878 / sap-log / sapp-ops mang @Public(); lớp chặn thật (secret gate) → nền tảngsapp.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 — HubSpotTạ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 API18 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 ở BEsapp-26878-flows.ts (Grep op: " = 18 op); sapp-ops.controller.ts:498-505
Tích hợp ngoài — SAPP Ops DBChạ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 curlsapp.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ếpKé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 & commentsapp-26878-flows.ts:1139,1151; sapp-26878.constants.ts:255-262
Thông báoKhông có mail/push/MQTT trong tool. Mail kế toán là hành vi của lms-be được nhắc trong hintsapp-26878-flows.ts:1282,727
Job / cronKhô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ôngsapp-26878-tool.tsx:213-218; sapp-26878-flows.ts:6082

7. Drift — tài liệu nói khác code ​

IDTài liệu (file + section)Tài liệu nóiCode đang làmBằng chứng
DR-01shared/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 fieldService 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-02shared/api-docs/sapp.md §runs, bảng flow (dòng 2027-2034)Bucket chỉ us03/05/06/11/13 + us08/09/10/12/14FE 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-03shared/api-docs/sapp.md §runs, dòng 2079-2080Lỗ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émbe/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-04shared/api-docs/sapp.md §"SAPP-26878 tool: HubSpot test data" (bảng route 1391-1428) và toàn fileKhông có route assign-class-codeRoute 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-05shared/api-docs/sapp.md §POST deals, dòng 1443-1447Liệt kê nhãn flow cho tên deal: US-03, US-05, US-06, US-11×3, US-13×3Cò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/Bsapp-26878.constants.ts:515-529
DR-06shared/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ượtChỉ US-01 có bước "Trả lại mã lớp (staging)"; US-02…US-09 không có bước này trong specsapp-26878-flows.ts:3343-3350 vs :3628-3647,4044-4082,4459-4496,4876-4906,5345-5380,5592-5617,5911-5948,6216-6252
DR-07shared/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-08shared/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-09Comment 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-14sapp-26878-flows.ts:2453-2456 vs :2361-2369
DR-10Comment 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-11Hint 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-01sapp-26878-flows.ts:2874,3113 vs :3095

8. Câu hỏi cho BA (code không trả lời được) ​

IDCâu hỏiVì sao cầnChặn bước sau?
Q-01R-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 OQCó
Q-02R-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-03R-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 2Có
Q-04Modal "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 bugCó
Q-05Mụ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 StatementCó
Q-06R-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-07DR-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-08US-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-09Có
Q-09Manual 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ậtKhông
Q-10Phase 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 viKhông
Q-11R-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ệuKhông
Q-12Danh 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ìnhKhô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ácCó
Q-14Khô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-15Bướ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 thuKhông

9. Nhật ký nguồn đã đọc ​

File / sectionMục đích
ba/_workflow/templates/feature-inventory/Template.mdKhuôn tài liệu
ba/system-profile.md §3–§5Công thức truy vết, quy ước
fe/app/works/sapp/tools/sapp-26878/page.tsxRoute
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-437URL
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-1665Lư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-722Mã 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-25Fact ý đồ/trạng thái US-03…US-09 phase 2
shared/integration/README.md:202 (#40BE)Phase 2 chạy staging