Giao diện
Feature Inventory — SAPP-24395: Contact / Deal instances & Bulk product creation ​
Kiểm kê tính năng đã triển khai, dựng từ code. Đầu vào cho FS as-built (Mode B) và là "bức tranh hiện tại" cho Mode C. Mọi dòng phải có bằng chứng
file:dòng; không có bằng chứng → không ghi vào bảng (đưa sang §8 Câu hỏi).Công thức truy vết:
ba/system-profile.md§3.⚠️ Toàn bộ nội dung dựng từ đọc source tại commit
e77c47a, KHÔNG chạy app / API thật. Mọi "Kết quả quan sát được" là suy từ code, chưa đo trên môi trường 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 | Nhóm SAPP-24395 dưới /works/sapp/tools: sapp-24395 (sapp.006, "SAPP 24395: Contact / Deal instances") và sapp-24395-products (sapp.007, "SAPP 24395: Bulk product creation"); shared/api-docs/sapp.md §"sapp-24395 tool: …" |
| Thành phần chạm tới | FE (CMS) · BE (api) · Redis · HubSpot (portal prod & dev) · SAPP Ops REST API |
| Status | Draft |
1. Phạm vi đã truy vết ​
- Trong phạm vi:
- Màn
/works/sapp/tools/sapp-24395(tạo contact / deal mẫu trên HubSpot theo 14 deal case, 3 môi trường dev / staging / prod). - Màn
/works/sapp/tools/sapp-24395-products(tạo hàng loạt payment product trên SAPP Ops theo session nhiều bước). - 12 route HubSpot
/api/v1/sapp/{prod|staging|dev}/sapp-24395/{contacts|deals…}và 6 route/api/v1/sapp/sapp-24395/products/sessions…. - Service
SappCommonService(phần 24395),HubSpotCommonService.createOrReuseContact / createDeal,SappService(course categories / subjects / payment products), hằng số deal case, danh sách 102 product cứng, DTO, error code, khoá Redis.
- Màn
- Ngoài phạm vi (dừng truy vết tại đây):
@Public()/ secret gate / quyền theoTOOL_CODES→ nền tảng.- Đọc
env_systems(EnvSystemCommonService,EnvSystemMapCommonService) → nền tảng. - Auto-login SAPP Ops (
SappAuthCommonService), Ops passthrough / SQL, SAP log trace → nền tảng (tool 24395 không dùng auto-login; xem R-24). - Component dùng chung
HubspotPropertyFieldvà routeGET /api/v1/sapp/{env}/hubspot/{object}/properties/{property}(tool "Property") — chỉ ghi là tool 24395 gọi tới để dựng widget. - Shell / registry / lưu trạng thái tool chung (
fe/src/ui/pages/works/sapp/tools/shell.tsx) → nền tảng. - Tool SAPP-26878 dùng lại
createDealInstance/createContactInstance— chỉ ghi nhận việc dùng chung.
2. Điểm vào (Entry points) ​
| 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-24395 — nhóm "SAPP 24395 - Tự động ghi nhận doanh thu" › "Contact / Deal instances" (code sapp.006) | fe/app/works/sapp/tools/sapp-24395/page.tsx:9; fe/src/ui/pages/works/sapp/tools/registry.ts:69-77; fe/src/data/constants/tool-codes.ts:37 |
| E-02 | Màn web | /works/sapp/tools/sapp-24395-products — "Bulk product creation" (code sapp.007) | fe/app/works/sapp/tools/sapp-24395-products/page.tsx:9; fe/src/ui/pages/works/sapp/tools/registry.ts:78-82; fe/src/data/constants/tool-codes.ts:38 |
| E-03 | API | POST /api/v1/sapp/{prod|dev|staging}/sapp-24395/contacts | be/apps/api/src/sapp/sapp.controller.ts:2190 (prod), :2210 (dev), :2261 (staging); controller prefix sapp v1 :157 |
| E-04 | API | GET /api/v1/sapp/{prod|dev|staging}/sapp-24395/contacts | be/apps/api/src/sapp/sapp.controller.ts:2230, :2246, :2281 |
| E-05 | API | POST /api/v1/sapp/{prod|dev|staging}/sapp-24395/deals/:dealCase | be/apps/api/src/sapp/sapp.controller.ts:2297, :2337, :2401 |
| E-06 | API | GET /api/v1/sapp/{prod|dev|staging}/sapp-24395/deals | be/apps/api/src/sapp/sapp.controller.ts:2377, :2389, :2438 |
| E-07 | API | POST /api/v1/sapp/sapp-24395/products/sessions (Bước 1) | be/apps/api/src/sapp/sapp.controller.ts:2460 |
| E-08 | API | POST /api/v1/sapp/sapp-24395/products/sessions/:sessionKey/categories/create (Bước 1.5, tùy chọn) | be/apps/api/src/sapp/sapp.controller.ts:2478 |
| E-09 | API | POST /api/v1/sapp/sapp-24395/products/sessions/:sessionKey/subjects (Bước 2) | be/apps/api/src/sapp/sapp.controller.ts:2503 |
| E-10 | API | POST /api/v1/sapp/sapp-24395/products/sessions/:sessionKey/subjects/create (Bước 2.5, tùy chọn) | be/apps/api/src/sapp/sapp.controller.ts:2525 |
| E-11 | API | POST /api/v1/sapp/sapp-24395/products/sessions/:sessionKey/create (Bước 3) | be/apps/api/src/sapp/sapp.controller.ts:2550 |
| E-12 | API | GET /api/v1/sapp/sapp-24395/products/sessions/:sessionKey (xem snapshot) | be/apps/api/src/sapp/sapp.controller.ts:2570 |
Đường nối FE → URL: fe/src/data/constants/api.ts:222-227 (E-03…E-06), :440-449 (E-07, E-09…E-12). E-08 không có URL phía FE (xem DR-07).
3. Hành động người dùng / hệ thống ​
Role: code không phân biệt role (mọi route @Public(); gate thật nằm ở nền tảng) → ghi "Người dùng CMS".
3a. Tool Contact / Deal instances (E-01) ​
| ID | Ai (role) | Làm gì | Điểm vào | Kết quả quan sát được | Bằng chứng |
|---|---|---|---|---|---|
| A-01 | Người dùng CMS | Chọn môi trường ở tab: Dev (mặc định), Staging; tab Prod bị vô hiệu hoá ("Tạm thời vô hiệu hóa") | E-01 | Mỗi lần đổi tab, toàn bộ panel được dựng lại, danh sách + lựa chọn contact theo từng môi trường | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:49-54, :989, :1014-1021, :1038 |
| A-02 | Hệ thống (FE) | Khi mở panel một môi trường, tải danh sách contact và deal đã tạo bởi tool trong môi trường đó | E-01 → E-04, E-06 | Hiện "Contact đã tạo (n)" và "Deal đã tạo (n)"; nút "Tải lại" gọi lại | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:908-928, :341-348; fe/src/data/services/sapp/sapp-24395.service.ts:39-47, :67-75 |
| A-03 | Người dùng CMS | Nhập thông tin contact (Họ tên *, Email, SĐT, Địa chỉ, Tỉnh/TP, Ngày sinh) rồi bấm "Tạo contact" | E-01 → E-03 | Tạo contact trên HubSpot portal của môi trường; thêm {id, name} vào danh sách; thông báo "Đã tạo contact instance" | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:64-93, :257-290; be/apps/api/src/sapp/sapp.controller.ts:2094-2108; be/libs/modules/src/lib/sapp/common.service.ts:1547-1582 |
| A-04 | Hệ thống (BE) | Nếu email đã tồn tại trên HubSpot (409), cập nhật contact cũ bằng dữ liệu vừa nhập và trả contact đó với cờ reused = true | E-03 | Contact cũ bị ghi đè các trường gửi lên; vẫn được thêm vào danh sách tool | be/libs/modules/src/lib/hubspot/common.service.ts:622-661; be/libs/modules/src/lib/sapp/common.service.ts:1554-1564 |
| A-05 | Người dùng CMS | Tick chọn các contact trong danh sách để gắn vào deal | E-01 | Số contact đã chọn hiện ở mục 2 ("Sẽ gán n contact: …") | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:363-381, :934-941, :598-601 |
| A-06 | Người dùng CMS | Chọn 1 trong 14 deal case; chọn "Tài khoản nhận phí dịch vụ" (danh sách lấy từ HubSpot); nhập CCCD/Hộ chiếu; nhập các trường riêng của case (Deal Lost: chọn loại "Học phí" 3 đợt hoặc "Phí dịch vụ" 2 đợt + Loại phí dịch vụ) | E-01 | Đổi case thì xoá dữ liệu trường riêng đã nhập; đổi tab loại thanh toán ở Deal Lost cũng chỉ gửi trường của tab đang chọn | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:100-136, :148-224, :427-457, :643-819 |
| A-07 | Người dùng CMS | Bấm "Tạo deal" | E-01 → E-05 | Tạo deal trên HubSpot, tên tự sinh [IT TEST][SAPP-24395] {nhãn case} {số thứ tự}, gắn các contact đã chọn; thêm {id, dealname} vào danh sách; thông báo "Đã tạo deal instance" | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:459-524; be/apps/api/src/sapp/sapp.controller.ts:2312-2335; be/libs/modules/src/lib/sapp/common.service.ts:1615-1663 |
| A-08 | Người dùng CMS | Mở "lịch sử deal đã tạo", tìm theo tên deal | E-01 | Modal lọc danh sách deal theo chuỗi | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:609-630, :823-899 |
3b. Tool Bulk product creation (E-02) ​
| ID | Ai (role) | Làm gì | Điểm vào | Kết quả quan sát được | Bằng chứng |
|---|---|---|---|---|---|
| A-09 | Hệ thống (FE) | Khi mở màn, nếu trình duyệt còn lưu sessionKey thì tải lại snapshot session và nhảy tới bước đã đạt | E-02 → E-12 | "Đang khôi phục session…"; nếu session hết hạn thì xoá key đã lưu (im lặng) | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:210-245; fe/src/data/constants/local-storage.ts:6 |
| A-10 | Người dùng CMS | Bước 1 — bấm "Bắt đầu — Nạp categories" | E-02 → E-07 | Tạo session mới, nạp toàn bộ course category của SAPP Ops, tính các category trong danh sách cứng còn thiếu; thông báo "Đã nạp n categories" | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:260-273, :562-571; be/libs/modules/src/lib/sapp/common.service.ts:2614-2638 |
| A-11 | (Chỉ BE) | Bước 1.5 — tạo các course category còn thiếu (một phần hoặc tất cả) | E-08 | Tạo category trên SAPP Ops, thêm vào session, báo created / failed. FE không có nút / không gọi | be/libs/modules/src/lib/sapp/common.service.ts:2739-2799; không có trong fe/src/data/services/sapp/sapp-24395-products.service.ts |
| A-12 | Người dùng CMS | Bước 2 — nạp subjects | E-02 → E-09 | Nạp toàn bộ subject SAPP Ops vào session, tính subject code còn thiếu, session sang bước 2; thông báo "Đã nạp n subjects. Thiếu m subject code." | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:275-294; be/libs/modules/src/lib/sapp/common.service.ts:2703-2728 |
| A-13 | Người dùng CMS | Bước 2.5 — tick chọn subject code thiếu (hoặc không chọn = tạo tất cả) rồi tạo | E-02 → E-10 | Tạo subject trên SAPP Ops (tên = code) dưới category tương ứng; báo "Tạo thành công x, lỗi y. Còn thiếu z subject." | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:296-321, :345-358; be/libs/modules/src/lib/sapp/common.service.ts:2810-2890 |
| A-14 | Người dùng CMS | Bước 3 — tạo products | E-02 → E-11 | BE: duyệt 102 dòng cứng, bỏ qua dòng đã có tên trùng, báo lỗi dòng thiếu category/subject, tạo phần còn lại (tuần tự). ⚠️ FE xử lý kết quả như một session (xem R-33) | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:323-343; be/libs/modules/src/lib/sapp/common.service.ts:2900-3000 |
| A-15 | Người dùng CMS | Di chuyển giữa các bước bằng stepper / "Quay lại" / "Tiếp" | E-02 | Chỉ xem được bước n khi session đã đạt bước n-1; khoá khi đang bận | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:250-255, :411-456, :508-527 |
| A-16 | Người dùng CMS | Bấm "Bắt đầu lại" | E-02 | Chỉ xoá key trong trình duyệt + state màn; session phía Redis vẫn còn tới khi hết TTL | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:126-141, :498-507 |
| A-17 | Hệ thống (FE) | Khi API báo session không tồn tại → reset và hiện "Session đã hết hạn … vui lòng bắt đầu lại từ bước 1" | E-02 | ⚠️ Điều kiện so khớp có thể không bao giờ đúng (R-34) | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:45, :143-163 |
4. Dữ liệu ​
Tool không có entity / bảng DB riêng. Trạng thái lưu ở Redis (store Keyv namespace mind-keep, be/libs/services/src/lib/redis/redis.module.ts:19) và ghi thẳng sang hệ thống ngoài.
4a. Khoá Redis ​
| Entity / bảng | Field | Type | Bắt buộc | Mặc định / enum | Bằng chứng |
|---|---|---|---|---|---|
Redis sapp:24395:contact-instances:{env} | mảng { id, name, reused? } | JSON array | — | Không TTL (không truyền ttl, module không đặt mặc định) | be/libs/constants/src/lib/sapp/sapp.constants.ts:231-232; be/libs/modules/src/lib/sapp/common.service.ts:1566-1569; be/libs/services/src/lib/redis/redis.module.ts:18-20 |
Redis sapp:24395:deal-instances:{env} | mảng { id, dealname } | JSON array | — | Không TTL; độ dài mảng dùng làm bộ đếm tên deal | be/libs/constants/src/lib/sapp/sapp.constants.ts:239-240; be/libs/modules/src/lib/sapp/common.service.ts:1624-1650 |
Redis sapp:24395:product-session:{sessionKey} | xem 4c | JSON object | — | TTL 6 giờ, đặt lại mỗi lần ghi | be/libs/constants/src/lib/sapp/sapp.constants.ts:57-61; be/libs/modules/src/lib/sapp/common.service.ts:2600-2608 |
localStorage trình duyệt sapp_24395_product_session_key | sessionKey | string | — | — | fe/src/data/constants/local-storage.ts:6; fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:188-191 |
4b. Request / response Contact & Deal ​
| Entity / bảng | Field | Type | Bắt buộc | Mặc định / enum | Bằng chứng |
|---|---|---|---|---|---|
| Body tạo contact | name → HubSpot leadin_h_tnipa7107b_39faf8acbcd97440649d6d3d99a5b929 ([TVTS] Họ tên) | string | Có | — | be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:13-14; be/apps/api/src/sapp/sapp.controller.ts:1731-1732, :2097-2099 |
| Body tạo contact | email → email | string | Không | Chỉ gửi khi có | be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:16-17; be/apps/api/src/sapp/sapp.controller.ts:2100 |
| Body tạo contact | phone_number → mkt__phone_number | string | Không | — | …sapp-instance.dto.ts:19-20; be/apps/api/src/sapp/sapp.controller.ts:2101-2102 |
| Body tạo contact | address → dia_chi; province → khuvucsinhsong; d_o_b → d_o_b | string | Không | — | …sapp-instance.dto.ts:22-29; be/apps/api/src/sapp/sapp.controller.ts:2103-2106 |
| Response contact | id, name, reused | string / string|null / boolean | — | reused = true khi dùng lại contact có sẵn | be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:59-71 |
| Body tạo deal | contactIds | string[] | Không | [] | …sapp-instance.dto.ts:33-38; be/apps/api/src/sapp/sapp.controller.ts:2321 |
| Body tạo deal | taiKhoanNhanPhiDichVu → tai_khoan_nhan_phi_dich_vu | string | Không | ACCA - Kể từ 01/01/2026 | …sapp-instance.dto.ts:40-44; be/libs/constants/src/lib/sapp/sapp.constants.ts:243-247; be/apps/api/src/sapp/sapp.controller.ts:2080-2083 |
| Body tạo deal | cccd_passport → cccd_ho_chieu | string | Không | — | …sapp-instance.dto.ts:46-50; be/apps/api/src/sapp/sapp.controller.ts:2084-2086 |
| Body tạo deal | properties (map tên nội bộ HubSpot → giá trị) | object | Không | Không có whitelist | …sapp-instance.dto.ts:52-56; be/apps/api/src/sapp/sapp.controller.ts:2074-2079 |
| Path | :dealCase | enum 14 giá trị | Có | xem 4d | be/libs/constants/src/lib/sapp/sapp.constants.ts:274-289 |
| Response deal | id, dealname | string / string|null | — | — | …sapp-instance.dto.ts:85-91 |
| Deal Lost — số tiền các đợt | so_tien_hoc_vien_thanh_toan_lan_1/2/3, phi_dich_vu_dong_lan_1/2 | string số | Không | '0' khi không gửi | be/libs/constants/src/lib/sapp/sapp.constants.ts:253-259; be/apps/api/src/sapp/sapp.controller.ts:2068-2073 |
| Deal Lost — ngày / phương thức | Học phí: ngay_thanh_toan_hoc_phi_lan_1..3, phuong_thuc_thanh_toan_lan_1..3; Phí DV: ngay_thanh_toan_phi_dv_lan_1, ngay_thanh_toan_phi_lan_2, phuong_thuc_thanh_toan_phi_lan_1/2, loai_phi_dich_vu | string | Không | — (chỉ FE định nghĩa) | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:148-194 |
| Trường riêng theo case (FE) | cbe/cert/becker/gia-han/bao-luu/tu-van-khoa-hoc: deal_id_da_thanh_toan_phi; chuyen-doi-ghi-nhan-doanh-thu: deal_id_bi_lost, deal_id_da_thanh_toan_phi; …-dieu-chinh-giam-original: hoc_phi_goc, amount, goi_khoa_hoc; …-nhan-chuyen-nhuong-tru: goi_khoa_hoc_chuyen_nhuong, amount; …-tang-doanh-thu-deal-tru: khoa_hoc_dem_chuyen_doi, amount. Ba case chuyen-nhuong-hv-chuyen-nhuong, chuyen-doi-khong-ghi-nhan-phi, chuyen-nhuong-hv-nhan-chuyen-nhuong: không có trường riêng | string | Không | — | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:203-224 |
4c. Session bulk product ​
| Entity / bảng | Field | Type | Bắt buộc | Mặc định / enum | Bằng chứng |
|---|---|---|---|---|---|
| Session | sessionKey | string (UUID do server sinh) | — | randomUUID() | be/libs/modules/src/lib/sapp/common.service.ts:2619 |
| Session | step | number | — | 1 / 2 / 3 | be/libs/modules/src/lib/sapp/common.service.ts:241-243; be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:260-261 |
| Session | categories {name,id}[], subjects {code,id}[] | array | — | subjects = [] ở bước 1 | be/libs/modules/src/lib/sapp/common.service.ts:244-245, :2621-2622 |
| Session | missingCategoryNames | string[] | — | tính ở bước 1 | be/libs/modules/src/lib/sapp/common.service.ts:247, :2623 |
| Session | categoryResults {created[{name,categoryId}], failed[{name,reason}]} | object | — | sau bước 1.5 | be/libs/modules/src/lib/sapp/common.service.ts:217-233, :2785 |
| Session | missingSubjectCodes {code,category}[] | array | — | tính ở bước 2 | be/libs/modules/src/lib/sapp/common.service.ts:192-195, :2713 |
| Session | subjectResults {created[{code,subjectId,name}], failed[{code,category,reason}]} | object | — | sau bước 2.5 | be/libs/modules/src/lib/sapp/common.service.ts:197-215, :2876 |
| Session | results {created[{name,productId}], skipped[…], failed[…]} | object | — | sau bước 3 | be/libs/modules/src/lib/sapp/common.service.ts:153-185, :2988 |
| Session | createdAt | ISO string | — | lúc bắt đầu | be/libs/modules/src/lib/sapp/common.service.ts:2624 |
| Body bước 1.5 | categoryNames | string[] | Không | [] = tạo tất cả | be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:247-254 |
| Body bước 2.5 | subjectCodes | string[] | Không | [] = tạo tất cả | be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:214-221 |
| Danh sách product cứng | 102 dòng {category, subjectCode, constructionMode, price}: ACCA 49 · CFA 10 · CMA 12 · IFRS 10 · Short courses 18 · CGMA 3 | const | — | constructionMode ∈ OFFLINE / ONLINE / BLENDED / HYBRID / LIVE_ONLINE / OTHER; price 3.000.000 … 9.500.000 | be/libs/constants/src/lib/sapp/sapp-24395-products.constants.ts:14-19, :23-137 (mục :24, :75, :87, :101, :113, :133); enum be/libs/constants/src/lib/sapp/sapp.constants.ts:41-48 |
| Product tạo trên SAPP Ops | name = [SAPP-24395][AUTO] {category} {subjectCode} {constructionMode}, category_id, subject_id, construction_mode, price, status = ACTIVE | — | — | — | be/libs/modules/src/lib/sapp/common.service.ts:2928, :2961-2968; be/libs/constants/src/lib/sapp/sapp.constants.ts:51-54 |
4d. Bảng deal case — giá trị cố định server gắn vào deal ​
Nhãn case: be/libs/constants/src/lib/sapp/sapp.constants.ts:292-314. Bảng cấu hình: be/apps/api/src/sapp/sapp.controller.ts:1739-2032 (prod :1743-1828, staging :1832-1931, dev :1932-2031). Mọi case, mọi môi trường đều có tai_khoan_nhan_phi_dich_vu = ACCA - Kể từ 01/01/2026.
:dealCase | Nhãn (xuất hiện trong tên deal) | prod & staging: pipeline / dealstage | dev: pipeline / dealstage | loai_phi_dich_vu — staging & dev (prod: không gửi) | Bằng chứng (prod / staging / dev) |
|---|---|---|---|---|---|
lost | Deal Lost | 877604025 / 1316240409 | 836815496 / 1244260910 | '' (client được ghi đè) | :1744 / :1833 / :1933 |
cbe | Deal CBE | 877604025 / 1316240409 | 836815496 / 1244260910 | Phí đăng ký thi CBE | :1750 / :1840 / :1940 |
cert | Deal CERT | 877604025 / 1316240409 | 836815496 / 1244260910 | Phí tài khoản Cert | :1756 / :1847 / :1947 |
becker | Deal BECKER | 877604025 / 1316240409 | 836815496 / 1244260910 | Phí tài khoản Becker | :1762 / :1854 / :1954 |
gia-han | Deal Gia hạn | 877604025 / 1316240409 | 836815496 / 1244260910 | Phí gia hạn | :1768 / :1861 / :1961 |
bao-luu | Deal Bảo lưu | 877604025 / 1316240409 | 836815496 / 1244260910 | Phí bảo lưu | :1774 / :1868 / :1968 |
chuyen-doi-ghi-nhan-doanh-thu | Deal Chuyển đổi - Ghi nhận doanh thu | 882511134 / 1325314701 | 878176910 / 1317215384 | Chuyển đổi | :1780 / :1875 / :1975 |
chuyen-nhuong-hv-chuyen-nhuong | Deal Chuyển nhượng - HV Chuyển nhượng | 877604025 / 1316240409 | 878343020 / 1317287583 | Chuyển nhượng | :1786 / :1882 / :1982 |
chuyen-doi-khong-ghi-nhan-phi | Deal Chuyển đổi - Không ghi nhận phí | 882511134 / 1325314701 | 878176910 / 1317215384 | Không ghi nhận phí | :1792 / :1889 / :1989 |
chuyen-nhuong-hv-nhan-chuyen-nhuong | Deal Chuyển nhượng - HV Nhận Chuyển nhượng | 877604025 / 1316240409 | 878343020 / 1317287583 | Không ghi nhận phí | :1798 / :1896 / :1996 |
tu-van-khoa-hoc | Deal Tư vấn khóa học | 877604025 / 1316240409 | 836815496 / 1244260910 | Tư vấn khóa học | :1804 / :1903 / :2003 |
chuyen-doi-dieu-chinh-giam-original | Original Deal dành cho Chuyển đổi điều chỉnh giảm | 882511134 / 1325314701 | 878176910 / 1317215384 | Chuyển đổi | :1810 / :1910 / :2010 |
chuyen-nhuong-hv-nhan-chuyen-nhuong-tru | Deal trừ dành cho Chuyển nhượng HV nhận chuyển nhượng | 877604025 / 1316240409 | 878343020 / 1317287583 | Chuyển nhượng | :1816 / :1917 / :2017 |
chuyen-doi-tang-doanh-thu-deal-tru | Deal trừ dành cho Deal Chuyển đổi ghi nhận doanh thu | 882511134 / 1325314701 | 878176910 / 1317215384 | Chuyển đổi | :1822 / :1924 / :2024 |
Chú thích trong code: pipeline 877604025 = "Pipeline test UAT environment" (:1775), 882511134 = "IT test đóng tiền chuyển đổi học phí" (:1781). Ý nghĩa nghiệp vụ từng case: code chỉ có nhãn — xem Q-01.
5. Rule & validation tìm thấy ​
5a. Contact / Deal ​
| ID | Rule (ngôn ngữ nghiệp vụ) | Loại | Áp dụng ở | Bằng chứng | Nghi là bug? |
|---|---|---|---|---|---|
| R-01 | Họ tên contact là bắt buộc (FE chặn khi trống; BE validate string bắt buộc) | validation | A-03 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:277-281; be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:13-14 | Không |
| R-02 | Chỉ gửi sang HubSpot các trường contact có giá trị; FE bỏ trường rỗng sau khi trim | điều kiện | A-03 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:283-288; be/apps/api/src/sapp/sapp.controller.ts:2100-2106 | Không |
| R-03 | CCCD/Hộ chiếu không nằm trên contact mà được ghi lên deal (cccd_ho_chieu) | điều kiện | A-06, A-07 | be/apps/api/src/sapp/sapp.controller.ts:2084-2086; be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:46-50 | Không |
| R-04 | Nếu email contact đã tồn tại trên HubSpot: không báo lỗi mà lấy contact cũ và cập nhật nó bằng dữ liệu vừa nhập | điều kiện | A-04 | be/libs/modules/src/lib/hubspot/common.service.ts:636-660 | ⚠️ Observed — ghi đè contact có thật (Q-05) |
| R-05 | Danh sách contact/deal của tool chỉ lưu id + tên (không lưu email/SĐT) | giới hạn | A-02 | be/libs/modules/src/lib/sapp/common.service.ts:1557-1569, :1642-1650 | Không |
| R-06 | Danh sách contact/deal tách riêng theo từng môi trường (prod / staging / dev), kể cả khi staging và prod dùng chung portal | điều kiện | A-02 | be/libs/constants/src/lib/sapp/sapp.constants.ts:231-240 | Không |
| R-07 | Deal case không thuộc 14 giá trị → lỗi SAPP_24395_INVALID_DEAL_CASE (13011) | validation | A-07 | be/apps/api/src/sapp/sapp.controller.ts:2034-2046; be/libs/constants/src/lib/error-codes/error-codes.constants.ts:594-598 | Không |
| R-08 | Tên deal do server sinh: [IT TEST][SAPP-24395] {nhãn case} {n}, với n = số deal tool đã tạo trong môi trường đó + 1 (đếm chung mọi case); client không đặt được tên | công thức | A-07 | be/apps/api/src/sapp/sapp.controller.ts:2325-2327; be/libs/modules/src/lib/sapp/common.service.ts:1629-1634 | Không |
| R-09 | Bộ đếm tên deal đọc-rồi-ghi danh sách Redis không khoá: hai lệnh tạo đồng thời có thể trùng số thứ tự và mất một mục trong danh sách | công thức | A-07 | be/libs/modules/src/lib/sapp/common.service.ts:1624-1650 | ⚠️ Observed (Q-06) |
| R-10 | Thứ tự ưu tiên thuộc tính deal (thấp → cao): số tiền đợt mặc định 0 (chỉ Deal Lost) → giá trị cố định được phép ghi đè (chỉ loai_phi_dich_vu của Deal Lost) → properties client gửi → giá trị cố định được bảo vệ (pipeline, dealstage, …) → taiKhoanNhanPhiDichVu → cccd_passport | công thức | A-07 | be/apps/api/src/sapp/sapp.controller.ts:2048-2088; be/libs/constants/src/lib/sapp/sapp.constants.ts:253-267 | Không |
| R-11 | Deal Lost: 5 trường số tiền các đợt mặc định 0 nếu client không gửi | công thức | A-07 | be/apps/api/src/sapp/sapp.controller.ts:2068-2073; be/libs/constants/src/lib/sapp/sapp.constants.ts:253-259 | Không |
| R-12 | properties client gửi không có whitelist: mọi tên thuộc tính deal (trừ các khoá cố định của case) đều được chuyển thẳng sang HubSpot | giới hạn | A-07 | be/apps/api/src/sapp/sapp.controller.ts:2074-2079; so sánh tool 26878 có whitelist :2596-2622 | ⚠️ Observed (Q-07) |
| R-13 | Pipeline / dealstage / loại phí được server gắn cố định theo (môi trường, case) — xem bảng 4d; 4 case "Chuyển đổi" dùng pipeline riêng; ở dev 3 case "Chuyển nhượng" cũng dùng pipeline riêng | điều kiện | A-07 | be/apps/api/src/sapp/sapp.controller.ts:1739-2032 | Không |
| R-14 | Ở prod không có loai_phi_dich_vu cố định cho case nào, trong khi staging (cùng portal prod) và dev có — cùng một case trên cùng portal cho ra deal khác nhau tuỳ chọn "prod" hay "staging"; ở prod client còn tự ghi loai_phi_dich_vu được cho mọi case | điều kiện | A-07 | be/apps/api/src/sapp/sapp.controller.ts:1744-1827 so với :1833-1930 | ⚠️ Observed (Q-03) |
| R-15 | "Tài khoản nhận phí dịch vụ" mặc định ACCA - Kể từ 01/01/2026; FE lấy danh sách lựa chọn từ HubSpot, nếu giá trị đang chọn không còn thì quay về mặc định hoặc lựa chọn đầu tiên; FE luôn gửi trường này | điều kiện | A-06 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:95-98, :447-457, :517-523; be/libs/constants/src/lib/sapp/sapp.constants.ts:247 | Không |
| R-16 | Deal Lost: người dùng chọn một loại thanh toán — "Học phí" tối đa 3 đợt hoặc "Phí dịch vụ" tối đa 2 đợt (+ Loại phí dịch vụ); chỉ gửi trường của loại đang chọn và có giá trị | điều kiện | A-06 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:148-194, :501-516 | Không |
| R-17 | Đổi deal case thì xoá các trường riêng đã nhập; CCCD và tài khoản nhận phí được giữ | điều kiện | A-06 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:413-432 | Không |
| R-18 | Deal có thể tạo mà không gắn contact nào | điều kiện | A-07 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:598-601; be/libs/modules/src/lib/hubspot/common.service.ts:542-557 | Không |
| R-19 | "staging" không có portal HubSpot riêng — dùng đúng credential prod, tức ghi vào portal HubSpot production | điều kiện | A-03, A-07 | be/libs/constants/src/lib/hubspot/hubspot.constants.ts:19-26; be/apps/api/src/sapp/sapp.controller.ts:1829-1831 | ⚠️ Observed (Q-02) |
| R-20 | Tab Prod bị tắt trên FE, nhưng route prod phía BE vẫn mở (@Public()) | giới hạn | A-01 | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:52-53; be/apps/api/src/sapp/sapp.controller.ts:2190-2191, :2297-2298 | ⚠️ Observed (Q-02) |
| R-21 | Danh sách contact / deal trong Redis không bao giờ hết hạn và không có API xoá — bộ đếm tên deal tăng mãi | giới hạn | A-02, A-07 | be/libs/modules/src/lib/sapp/common.service.ts:1569, :1650 (không ttl); be/libs/services/src/lib/redis/redis.module.ts:18-20 | ⚠️ Observed (Q-08) |
| R-22 | Contact có thể trả name = null; FE tìm deal bằng dealname.toLowerCase() không chặn null | validation | A-08 | be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:89-90; fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:839 | ⚠️ Observed (Q-11) |
5b. Bulk product creation ​
| ID | Rule (ngôn ngữ nghiệp vụ) | Loại | Áp dụng ở | Bằng chứng | Nghi là bug? |
|---|---|---|---|---|---|
| R-23 | Tool bulk product không chia môi trường: một cặp base URL + token duy nhất; môi trường thực tế là nơi SAPP_BASE_API_URL đang trỏ tới | điều kiện | A-10…A-14 | be/libs/modules/src/lib/sapp/common.service.ts:278-302; be/libs/constants/src/lib/sapp/sapp.constants.ts:16-25 | ⚠️ Observed (Q-04) |
| R-24 | Token SAPP Ops là JWT tĩnh trong env_systems, phải thay tay khi hết hạn; thiếu URL hoặc token → SAPP_API_NOT_CONFIGURED (13012). Không dùng auto-login → nền tảng | điều kiện | A-10…A-14 | be/libs/modules/src/lib/sapp/common.service.ts:283-302; be/libs/constants/src/lib/error-codes/error-codes.constants.ts:599-603 | Không |
| R-25 | Session sống 6 giờ, tính lại từ lần ghi gần nhất (mỗi bước ghi đều đặt lại TTL); hết hạn → SAPP_24395_PRODUCT_SESSION_NOT_FOUND (13017, HTTP 404) | trạng thái | A-09…A-14 | be/libs/modules/src/lib/sapp/common.service.ts:2585-2608; be/libs/constants/src/lib/sapp/sapp.constants.ts:61; be/libs/constants/src/lib/error-codes/error-codes.constants.ts:624-628 | Không |
| R-26 | Nạp category / subject / product theo trang 100 dòng cho tới khi trang cuối < 100 dòng | giới hạn | A-10, A-12, A-14 | be/libs/modules/src/lib/sapp/common.service.ts:2496-2518, :2527-2548, :2562-2581 | Không |
| R-27 | Category / subject "thiếu" = tên category / mã subject trong danh sách cứng không khớp (không phân biệt hoa thường, bỏ khoảng trắng đầu cuối) với dữ liệu SAPP Ops | công thức | A-10, A-12 | be/libs/modules/src/lib/sapp/common.service.ts:2640-2696 | Không |
| R-28 | Tạo category / subject thiếu: gửi danh sách rỗng = tạo tất cả; tên / mã không nằm trong danh sách thiếu bị bỏ qua; lỗi từng mục được báo, không dừng cả lượt | điều kiện | A-11, A-13 | be/libs/modules/src/lib/sapp/common.service.ts:2746-2776, :2823-2867 | Không |
| R-29 | Subject mới được tạo với tên = mã, dưới category tra theo tên từ session; không tìm thấy category → lỗi "Category not found" cho mục đó | điều kiện | A-13 | be/libs/modules/src/lib/sapp/common.service.ts:2838-2853 | Không |
| R-30 | Tạo subject thiếu (2.5) và tạo product (3) yêu cầu session đã qua bước 2; bước 3 còn yêu cầu session có ít nhất 1 subject; nếu không → SAPP_24395_PRODUCT_SESSION_SUBJECTS_NOT_LOADED (13018, HTTP 409). Tạo category thiếu (1.5) không kiểm tra bước | trạng thái | A-11, A-13, A-14 | be/libs/modules/src/lib/sapp/common.service.ts:2816-2821, :2905-2910, :2739-2745; be/libs/constants/src/lib/error-codes/error-codes.constants.ts:629-633 | Không |
| R-31 | Bước 3: product có tên trùng (không phân biệt hoa thường) với product đang có trên SAPP Ops mang tiền tố [SAPP-24395][AUTO] → đưa vào "skipped" ("Product already exists"); thiếu category và/hoặc subject → "failed" với lý do tương ứng; lỗi API → "failed"; còn lại → "created". Chạy lại bước 3 không tạo trùng | điều kiện | A-14 | be/libs/modules/src/lib/sapp/common.service.ts:2921-2985 | Không |
| R-32 | Nếu SAPP Ops trả thành công nhưng không kèm id, mục vẫn được coi là "created" với id rỗng (category / subject / product) | validation | A-11, A-13, A-14 | be/libs/modules/src/lib/sapp/common.service.ts:2773-2775, :2864-2866, :2981-2984 | ⚠️ Observed (Q-12) |
| R-33 | FE coi kết quả bước 3 là một session: ghi đè state session bằng báo cáo {created, skipped, failed}, lưu sessionKey không xác định vào trình duyệt, đọc data.results (không có) → thông báo "Tạo thành công 0, lỗi 0" và màn bước 3 không hiện kết quả | trạng thái | A-14 | fe/src/data/services/sapp/sapp-24395-products.service.ts:61-71; fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:183-196, :323-343, :821-822; BE trả results be/apps/api/src/sapp/sapp.controller.ts:2558-2563, be/libs/modules/src/lib/sapp/common.service.ts:2987-2989 | ⚠️ Observed — suy từ source, chưa chạy (Q-09) |
| R-34 | FE nhận diện "session hết hạn" bằng cách so mã lỗi với chuỗi "SAPP_24395_PRODUCT_SESSION_NOT_FOUND", trong khi BE trả mã "13017" → rất có thể không bao giờ khớp; người dùng chỉ thấy lỗi chung, key cũ không bị xoá | validation | A-09, A-17 | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:45, :145, :230; be/libs/constants/src/lib/error-codes/error-codes.constants.ts:624-628; FE lấy nguyên body lỗi fe/src/data/services/api-service.ts:257-266 | ⚠️ Observed — suy từ source (Q-10) |
| R-35 | FE không hiển thị danh sách "skipped" và không hiển thị missingCategoryNames / nút tạo category thiếu | giới hạn | A-10, A-14 | fe/src/data/models/sapp-24395-products.interface.ts:35-61 (không có skipped, missingCategoryNames); fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:856-912 | ⚠️ Observed (Q-13) |
| R-36 | Bước 3 tạo tuần tự, có thể chạy vài phút: server nâng timeout HTTP lên 10 phút; mỗi lệnh gọi SAPP Ops timeout 10 phút | giới hạn | A-14 | be/apps/api/src/main.ts:104-109; be/libs/constants/src/lib/sapp/sapp.constants.ts:34-38; be/libs/services/src/lib/sapp/sapp.service.ts:45 | Không |
| R-37 | Giá product được tính sẵn trong danh sách cứng: trong cùng category, level cao hơn giá cao hơn, từ 3.000.000 bước 500.000; level không rõ coi như level 1 (theo chú thích code) | công thức | A-14 | be/libs/constants/src/lib/sapp/sapp-24395-products.constants.ts:3-12 | Không |
| R-38 | Màn bulk product cảnh báo "API gốc chặn CORS", chỉ chạy được khi chạy local / expose qua ngrok và sửa SAPP_BASE_API_URL | giới hạn | A-10 | fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:384-408 | ⚠️ Observed — BE gọi SAPP Ops server-to-server (be/libs/services/src/lib/sapp/sapp.service.ts:36-45), CORS không áp dụng (Q-14) |
Trạng thái & chuyển trạng thái (session bulk product):
text
[Chưa có] --POST sessions (nạp categories, tính missingCategoryNames)--> [step 1]
[step 1|2|3] --categories/create (không kiểm bước; thêm category, step giữ nguyên)--> [step giữ nguyên]
[step 1|2|3] --subjects (nạp lại toàn bộ subjects, tính missingSubjectCodes)--> [step 2] (giữ results cũ nếu có)
[step ≥2] --subjects/create (thêm subject, step giữ nguyên)--> [step giữ nguyên]
[step ≥2 và subjects ≠ rỗng] --create (tạo products, lưu results)--> [step 3]
[bất kỳ] --6 giờ không ghi--> [Hết hạn → 13017]Trạng thái hiển thị FE: completedStep = session.step; bước n xem được khi completedStep ≥ n-1 (fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:250-255).
6. Quyền, tích hợp, thông báo, job ​
| Loại | Chi tiết | Bằng chứng |
|---|---|---|
| Quyền | Cả 18 route đều @Public() → gate thực tế (secret / TOOL_CODES sapp.006, sapp.007) thuộc nền tảng | be/apps/api/src/sapp/sapp.controller.ts:2191, :2211, :2231, :2247, :2262, :2282, :2298, :2338, :2378, :2390, :2402, :2439, :2461, :2479, :2504, :2526, :2551, :2571; fe/src/data/constants/tool-codes.ts:37-38 |
| Tích hợp ngoài — HubSpot, ghi | Tạo / cập nhật contact, tạo deal (+ liên kết deal→contact). dev → portal dev (SAPP_DEV_HUBSPOT_*); prod và staging → portal HubSpot production (SAPP_PROD_HUBSPOT_*) | be/libs/modules/src/lib/hubspot/common.service.ts:47-58, :542-557, :622-661; be/libs/constants/src/lib/hubspot/hubspot.constants.ts:10-30 |
| Tích hợp ngoài — HubSpot, đọc | Định nghĩa thuộc tính contact / deal để dựng widget và danh sách "Tài khoản nhận phí" (route của tool Property → nền tảng/tool khác) | fe/src/data/services/sapp/sapp-24395.service.ts:82-95; fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:315-327, :692-700 |
| Tích hợp ngoài — SAPP Ops REST, ghi | POST /api/v1/course_categories, POST /api/v1/subjects, POST /api/v1/payment-products trên base URL SAPP_BASE_API_URL (môi trường không xác định từ code — tuỳ giá trị cấu hình; có thể là prod) | be/libs/constants/src/lib/sapp/sapp.constants.ts:28-32; be/libs/services/src/lib/sapp/sapp.service.ts:114-150; be/libs/modules/src/lib/sapp/common.service.ts:2759, :2849, :2961 |
| Tích hợp ngoài — SAPP Ops REST, đọc | GET course_categories, GET subjects, GET payment-products?name=[SAPP-24395][AUTO] | be/libs/services/src/lib/sapp/sapp.service.ts:72-112; be/libs/modules/src/lib/sapp/common.service.ts:2499, :2530, :2565-2569 |
| Cấu hình | env_systems: SAPP_BASE_API_URL, SAPP_API_TOKEN, SAPP_{PROD,DEV}_HUBSPOT_ACCESS_TOKEN → đọc qua nền tảng | be/libs/constants/src/lib/env-system/env-system.constants.ts:10-18; be/libs/constants/src/lib/sapp/sapp.constants.ts:22-25 |
| Lưu trạng thái | Redis (danh sách contact/deal không TTL; session 6 giờ) + localStorage (sessionKey) | xem §4a |
| Thông báo (mail / push / MQTT / Discord) | Không có. Chỉ popup trong màn (showSuccess / showError) | fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:265-271; fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:147-160 |
| Job / cron | Không có | Grep 24395 trong be/apps/schedule — không kết quả |
| Ghi log | Lỗi mỗi handler được log theo tên handler | ví dụ be/apps/api/src/sapp/sapp.controller.ts:2126, :2473 |
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:797 §POST …/deals/:dealCase | "The per-case property sets within an environment are currently identical"; prod/staging và dev "currently differ only in their pipeline / dealstage ids" | Trong cùng môi trường, 4 case Chuyển đổi dùng pipeline khác (prod/staging 882511134); ở dev 3 case Chuyển nhượng cũng khác; staging/dev còn có loai_phi_dich_vu khác nhau theo case | be/apps/api/src/sapp/sapp.controller.ts:1780-1826, :1843-1927, :1975-2027 |
| DR-02 | shared/api-docs/sapp.md:722, :797 §staging | Staging dùng "the same fixed per-case deal properties as prod" | Staging có loai_phi_dich_vu theo case; prod không có | be/apps/api/src/sapp/sapp.controller.ts:1743-1828 so với :1832-1931 |
| DR-03 | shared/api-docs/sapp.md:832 §Request body properties | loai_phi_dich_vu là thuộc tính cố định "win over … cannot be overridden" | Với Deal Lost client ghi đè được (doc :879 lại nói đúng điều này — mâu thuẫn nội bộ); ở prod không cố định cho case nào nên client ghi được | be/libs/constants/src/lib/sapp/sapp.constants.ts:267; be/apps/api/src/sapp/sapp.controller.ts:2058-2067 |
| DR-04 | shared/api-docs/sapp.md:879 §Deal Lost | loai_phi_dich_vu "defaults to empty when omitted" | Chỉ đúng ở staging/dev; ở prod không gửi thuộc tính này | be/apps/api/src/sapp/sapp.controller.ts:1744-1749 vs :1833-1839 |
| DR-05 | shared/api-docs/sapp.md:745, :771-775 §POST …/contacts | Response { id, name }; không nhắc trường hợp email trùng | Email trùng → cập nhật contact cũ, response có thêm reused | be/libs/modules/src/lib/hubspot/common.service.ts:622-661; be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:66-70 |
| DR-06 | shared/api-docs/sapp.md:1153 | "The session lives in Redis for 6 hours" | 6 giờ tính từ lần ghi gần nhất (TTL đặt lại mỗi bước) | be/libs/modules/src/lib/sapp/common.service.ts:2600-2608 |
| DR-07 | shared/api-docs/sapp.md:1133-1148 §"The flow the FE should implement" | FE nên hiển thị missingCategoryNames và cho tạo category thiếu (bước 1.5) trước khi tạo subject | FE không có URL, service, type hay UI cho bước 1.5 | fe/src/data/constants/api.ts:439-449; fe/src/data/services/sapp/sapp-24395-products.service.ts:1-82; fe/src/data/models/sapp-24395-products.interface.ts:47-61 |
| DR-08 | shared/api-docs/sapp.md:1337-1353 §Step 3 | Response bước 3 là báo cáo {created, skipped, failed} | FE khai báo và dùng như session snapshot | fe/src/data/services/sapp/sapp-24395-products.service.ts:61-71; fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:329-331 |
| DR-09 | Chú thích code be/apps/api/src/sapp/sapp.controller.ts:1724-1728 | "These endpoints take no request body… separate prod / dev route" | Contact và deal đều nhận body; có thêm route staging | be/apps/api/src/sapp/sapp.controller.ts:2198-2200, :2261 |
| DR-10 | Chú thích code be/libs/modules/src/lib/sapp/common.service.ts:236-237 | Session lưu dưới sessionKey "client-supplied" | sessionKey do server sinh (UUID) | be/libs/modules/src/lib/sapp/common.service.ts:2619 |
| DR-11 | Swagger be/apps/api/src/sapp/sapp.controller.ts:2302, :2342, :2406 | "resolved from the … table (currently identical per case)"; staging "the same fixed per-case property set" | Như DR-01 / DR-02 | như DR-01 |
| DR-12 | Chú thích FE fe/src/data/services/sapp/sapp-24395.service.ts:17-18 | Mỗi thao tác nhắm portal prod / dev | Có 3 môi trường, staging dùng portal prod | fe/src/data/constants/api.ts:222-227 |
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 | Ý nghĩa nghiệp vụ của từng deal case (14 case) và luồng "tự động ghi nhận doanh thu" mà mỗi case dùng để kiểm thử là gì? Code chỉ có nhãn và pipeline/dealstage | Viết mục đích / BR cho từng case trong FS | Có |
| Q-02 | Chọn "Staging" trên màn thực chất ghi contact/deal vào portal HubSpot production (R-19); tab Prod bị tắt ở FE nhưng route prod BE vẫn mở (R-20). Đây là chủ đích (dùng pipeline test trong portal prod) hay rủi ro cần chặn? | Quyết định ghi BR hay OQ/rủi ro; cảnh báo cho người dùng | Có |
| Q-03 | Vì sao prod không gắn loai_phi_dich_vu trong khi staging (cùng portal) và dev có (R-14)? Cố ý hay thiếu sót? | Quyết định rule đúng cho prod | Có |
| Q-04 | Bulk product ghi vào SAPP Ops môi trường nào trên thực tế (giá trị SAPP_BASE_API_URL hiện tại)? Có từng chạy vào prod không? | Ghi rõ phạm vi ảnh hưởng dữ liệu | Có |
| Q-05 | Khi email đã tồn tại, tool cập nhật đè contact HubSpot có sẵn (R-04) — kể cả trên portal prod. Có chấp nhận được với contact học viên thật không? | Rủi ro dữ liệu khách hàng | Có |
| Q-06 | Tạo deal đồng thời có thể trùng số thứ tự trong tên và mất mục trong danh sách (R-09). Có cần đảm bảo tên duy nhất không? | Cố ý hay bug? | Không |
| Q-07 | properties không có whitelist (R-12) — người gọi API có thể ghi bất kỳ thuộc tính deal nào. Cố ý (linh hoạt cho tester) hay cần giới hạn như tool 26878? | Cố ý hay bug? | Không |
| Q-08 | Danh sách contact/deal của tool không hết hạn, không có chức năng xoá/reset (R-21). Có cần không? | Phạm vi tính năng | Không |
| Q-09 | Bước 3 trên FE có hiển thị đúng kết quả không (R-33)? Cần BA/QA chạy thử để xác nhận — nếu đúng như đọc code thì màn luôn báo "Tạo thành công 0, lỗi 0" và mất session | Cố ý hay bug? Ảnh hưởng mô tả hành vi as-built | Có |
| Q-10 | Hết hạn session có hiện popup "Session đã hết hạn" không (R-34)? Cần chạy thử xác nhận | Cố ý hay bug? | Không |
| Q-11 | Deal/contact có tên null có làm hỏng ô tìm kiếm lịch sử không (R-22)? | Cố ý hay bug? | Không |
| Q-12 | SAPP Ops có trường hợp trả thành công mà không có id không (R-32)? | Cố ý hay bug? | Không |
| Q-13 | Bước 1.5 (tạo category thiếu) và danh sách "skipped" không có trên FE (R-35, DR-07). Là chủ đích (chưa làm) hay thiếu? Nếu category thiếu thì người dùng xử lý thế nào? | Phạm vi FS as-built | Có |
| Q-14 | Cảnh báo CORS / ngrok trên màn bulk product (R-38) còn đúng không, vì BE gọi SAPP Ops phía server? | Nội dung hướng dẫn sử dụng | Không |
| Q-15 | Danh sách 102 product cứng được lấy từ CSV "Đồng bộ Hubspot ↔ Order" — ai là owner, bao giờ cần cập nhật, và quy tắc giá là của nghiệp vụ hay chỉ là dữ liệu test? | Viết BR nguồn dữ liệu | Không |
| Q-16 | Ai được dùng 2 tool này (role)? Code không phân quyền riêng (→ nền tảng) | Mục Actor trong FS | Không |
| Q-17 | Mục đích của tên deal có tiền tố [IT TEST] và product [SAPP-24395][AUTO]: để nhận diện/dọn dữ liệu test sau này? Có quy trình dọn không? | Vận hành | Không |
9. Nhật ký nguồn đã đọc ​
| File / section | Mục đích |
|---|---|
ba/_workflow/templates/feature-inventory/Template.md | Template |
ba/system-profile.md §3–§6 | Công thức truy vết, ranh giới ghi |
fe/app/works/sapp/tools/sapp-24395/page.tsx, …/sapp-24395-products/page.tsx | Route màn |
fe/src/ui/pages/works/sapp/tools/registry.ts:1-110 | Nhóm / nhãn tool |
fe/src/data/constants/tool-codes.ts:30-40 | Mã tool sapp.006 / sapp.007 |
fe/src/data/constants/api.ts:221-227, :439-449 | URL FE |
fe/src/data/constants/local-storage.ts:6 | Khoá localStorage |
fe/src/data/services/sapp/sapp-24395.service.ts (toàn bộ) | Service FE contact/deal |
fe/src/data/services/sapp/sapp-24395-products.service.ts (toàn bộ) | Service FE bulk product |
fe/src/data/models/sapp-24395-products.interface.ts:1-67 | Type FE session |
fe/src/data/services/api-service.ts:250-268 | Cách FE nhận body lỗi |
fe/src/data/interfaces/response-api.ts:16-24 | Kiểu lỗi FE |
fe/src/ui/components/works/sapp/sapp-24395-tool.tsx:40-1042 | Màn contact/deal |
fe/src/ui/components/works/sapp/sapp-24395-products-tool.tsx:100-580, grep phần còn lại | Màn bulk product |
be/apps/api/src/sapp/sapp.controller.ts:1-160, :1720-2690 | Route, bảng deal case, build properties |
be/apps/api/src/main.ts:98-112 | Timeout server |
be/libs/modules/src/lib/sapp/common.service.ts:115-302, :1535-1684, :2489-3008 | Service contact/deal/session/product |
be/libs/modules/src/lib/hubspot/common.service.ts:40-70, :530-700 | Token theo env, createDeal, createOrReuseContact |
be/libs/constants/src/lib/hubspot/hubspot.constants.ts:10-30 | Staging dùng credential prod |
be/libs/constants/src/lib/sapp/sapp.constants.ts:14-61, :220-315 | Khoá Redis, TTL, enum deal case, nhãn, hằng số |
be/libs/constants/src/lib/sapp/sapp-24395-products.constants.ts (toàn bộ, đếm bằng grep) | Danh sách 102 product |
be/libs/constants/src/lib/env-system/env-system.constants.ts:10-24 | Khoá env_systems |
be/libs/constants/src/lib/error-codes/error-codes.constants.ts:574-648 | Mã lỗi 13007–13021 |
be/libs/interface/src/lib/dto/sapp/sapp-instance.dto.ts:1-303 | DTO |
be/libs/services/src/lib/sapp/sapp.service.ts (grep các hàm, :36-45) | Client SAPP Ops, timeout |
be/libs/services/src/lib/sapp-ops/sapp-ops.service.ts:96-109 | Xác nhận tool 24395 dùng SappService cũ, không dùng auto-login |
be/libs/services/src/lib/redis/redis.module.ts:1-29 | Store Redis, không TTL mặc định |
be/libs/modules/src/lib/cache/cache.service.ts (grep) | Hàm set cache |
shared/api-docs/sapp.md:1-12, :716-1372, grep reused / TTL | Đối chiếu Drift |
Grep 24395 toàn repo (trừ ba/); grep ExceptionFilter trong be/ | Định vị điểm vào; xác nhận không có filter đổi shape lỗi |
be/.agents/maps/modules/ (liệt kê) | Không có map riêng cho sapp chứa 24395 |