Skip to content

US-D17: Flow P2 — DTTH đồng bộ ngược (story SAPP US-09 phase 2) ​

Thuộc FS: 00-overview.md — SAPP-26878 · Tạo dữ liệu test doanh thu (Ops → HubSpot → SAP) Phần chung (tổng quan, roles, flow tổng, related features, NFR, Open Questions, References) xem ở Overview. File này chỉ chứa nội dung riêng của US.

Mode B – As-built tại commit e77c47a · nguồn Fact: inventory 26878 §3.3 (A-127). Khung stepper → US-D01 · lịch sử / log SAP → US-D02 · loại bước → US-D03. U01:x = bước x của luồng US-01 ở US-D09 §4a. Dựng callback đã ký (dịch vụ dùng chung) → US-P06.

⏳ Đã triển khai — chưa chạy được end-to-end. Bộ ký callback dựng body mà webhook SAP từ chối (400, 7 điểm lệch), chờ SAPP — đo trên staging 2026-09-22 (shared/external/sapp/sapp-26878/README.md:222,224-244; shared/runbooks/sapp/sapp-26878/phase-2/staging/README.md §6i "🔴 Callback TỰ DỰNG bị chặn"). Các bước của chip vẫn bấm được; hai ca bắt buộc cần callback tự dựng thì chưa dựng được. Xem OQ-D-08.

FieldValue
US IDUS-D17
Role thực hiệnNgười dùng tool — Assumption (OQ về G-01 · inventory Q-05 — xem 00-overview §6)
PhasePhase 2 của SAPP-26878 (DTTH) · chip US-09 · DTTH đồng bộ ngược (id us09-p2)
PriorityN/A — as-built, đã triển khai (backlog R2: không xếp Priority)
StatusDraft · Đã triển khai — chưa chạy được end-to-end (⏳ chờ SAPP: body /sign-callback bị webhook từ chối)
NFR liên quanNFR-P-01 (quyền, FS nền tảng) · NFR của 26878 — xem 00-overview §5
OQ liên quanOQ-D-08, OQ-D-07, OQ-D-06, OQ-D-13, OQ-D-15 — xem 00-overview §6 · DR-D-10, DR-D-06 — xem 00-overview §6b

1. US Statement ​

text
As a Người dùng tool (Assumption — OQ về G-01)
I want có một dòng sổ cái đang chờ kết quả SAP, tự dựng callback SAP đã ký rồi bắn vào webhook, đọc
       kết quả và dựng các ca còn lại (thiếu trường, ngưỡng cảnh báo đối soát)
So that kiểm được rằng kết quả SAP trả về được ghi đúng vào sổ cái, và dòng đã có bút toán trên SAP
        luôn được đánh dấu đã post (kể cả khi thiếu trường) để không bị gửi lại sinh bút toán trùng
  • Nguồn "So that": shared/runbooks/sapp/sapp-26878/phase-2/us-09/README.md:8-10 ("Luồng duy nhất nói về dữ liệu đi về: SAP trả kết quả, LMS ghi vào sổ cái … Đường dự phòng là vòng đối soát hỏi lại bằng GET; quá 8 lần thì gửi email và ngừng hỏi"); hint luồng sapp-26878-flows.ts:6214. Tiêu chí nghiệm thu và phạm vi "đã nghiệm thu" của US này: [Assumption] (inventory Q-08, Q-13).

2. Dependencies & Definition Of Ready ​

N/A — đã triển khai (Mode B). Dependency runtime như US-D09 §2, thêm:

#Cần sẵn sàngỞ đâuTình trạng
D1Credential ký callback (clientId / secret) trong env_systemsDB mind_keep → US-P04Fact từ hint (sapp-26878-flows.ts:6008)
D2Route dựng callback đã ký (dev/staging, không có prod)BE → SAP service → US-P06Đã có (inventory E-26)
D3Bộ ký phía SAPP dựng body khớp DTO webhookHệ ngoài (SAPP)⏳ Chờ SAPP — shared/external/sapp/sapp-26878/README.md:222
D4Công cụ chạy curl ngoài toolMáy người dùngNgười dùng tự chuẩn bị

3. Business Rules ​

IDRuleÁp dụng ởĐã cover bởi (AC)Nguồn code (Mode B/C)
BR-01Luồng có 21 bước: U01:1 → U01:16 rồi 5 bước riêng theo thứ tự ở §4a; không có bước trả lại mã lớpM1AC-M1.1fe/src/ui/components/works/sapp/sapp-26878-flows.ts:6216-6250
BR-02"Đọc sổ cái và tracking" là bước đọc không có kiểm chứng tự động, lọc theo deal_idM2 - A17AC-M2.1sapp-26878-flows.ts:5958-5967
BR-03"Dựng callback đã ký": câu SQL đọc tự chạy và điền sẵn event_id + id dòng sổ cái (dòng đầu theo ngày buổi) vào hai ô bắt buộc; các ô còn lại có mặc định (trạng thái SUCCEEDED, vai POSITIVE, số chứng từ, khóa chứng từ, số tiền, số dư 3387); bấm lặp đượcM1 - A18AC-M1.2sapp-26878-flows.ts:5981-6069
BR-04Để trống ô số dư 3387 là cách dựng ca "thiếu trường" (ERR-05)M1 - A18AC-M1.3sapp-26878-flows.ts:6062-6066
BR-05"Bắn callback" là bước hướng dẫn: người dùng tự chạy lệnh curl đã ký ngoài tool (hiệu lực khoảng 300 giây, dùng được một lần); xong khi bấm "Đã bắn callback"M1 - A19AC-M1.4sapp-26878-flows.ts:6072-6086
BR-06"Đọc kết quả" lọc theo deal_id, tự chạy 1 kiểm chứng, luôn coi là xongM2 - A20AC-M2.2, AC-M2.3sapp-26878-flows.ts:6094-6115
BR-07"Dựng các ca còn lại" là bước hướng dẫn (8 ca), xong khi bấm xác nhận; panel SQL 3 câu đọc + 2 câu ghi (đặt bộ đếm đối soát đúng ngưỡng 8; đưa dòng sổ cái và bản ghi tracking về trạng thái ban đầu)M1 - A21AC-M1.5sapp-26878-flows.ts:6132-6200
BR-08Câu SQL ghi không tự chạy và phải qua hộp xác nhậnM1 - A21AC-M1.5fe/src/ui/components/works/sapp/sapp-26878-step-sql.tsx:200-213

4. Design ​

a. Mối liên hệ giữa các màn & User Flow ​

text
[Chip "US-09 · DTTH đồng bộ ngược", portal Dev | Staging]
  ↓
[M1] U01:1 → … → U01:16 (để có một dòng sổ cái đang SENT)
  ↓
[M2] Đọc sổ cái và tracking
  ↓
[M1] Dựng callback đã ký → Bắn callback (curl NGOÀI tool)   ⏳ webhook trả 400 với body tự dựng
  ↓
[M2] Đọc kết quả
  ↓
[M1] Dựng các ca còn lại

Bảng bước (Fact: sapp-26878-flows.ts:6216-6250).

#Tên bướcLoạiTái dùngGhi gì (hệ thống · môi trường)Điều kiện chuyển (xong)
1–16Kiểm tra kết nối Ops … Chạy lượt quétnhư US-D09U01:1 → U01:16 (đúng thứ tự)Như US-D09 §4a bước 1–16 (⚠️ HubSpot portal prod khi Staging)Như US-D09
17Đọc sổ cái và trackingtraceriêngKhông ghiLuôn xong
18Dựng callback đã kýops + sqlriêngKhông ghi dữ liệu nghiệp vụ: gọi route ký (SAP service dev/staging) trả lệnh curl đã ký. SQL us09.sign.1 chỉ đọc → bag entry_id, event_idGọi thành công
19Bắn callbackguideriêngTool không ghi. Người dùng chạy curl → webhook SAP của môi trường (ghi sổ cái nếu webhook nhận)Bấm "Đã bắn callback"
20Đọc kết quảtraceriêngKhông ghiLuôn xong
21Dựng các ca còn lạiguide + sqlriêngOps DB: us09.cases.2, .3 (chỉ khi người dùng chạy)Bấm "Đã đọc các ca còn lại"

⚠️ Ghi PROD — trên Staging, bước 8, 9, 11, 14, 15 ghi HubSpot portal production (như US-D09 §4a).

Tình trạng nghiệm thu gần nhất (nguồn ngoài code, chưa kiểm chứng ở R3): dev 2026-09-18 🟡 "chạy được một nửa" — phần đối soát ✅, phần bắn callback tự dựng 🔴 (shared/runbooks/sapp/sapp-26878/phase-2/us-09/README.md:3-5); staging 2026-09-22 — callback thật từ SAP ghi đúng sổ cái, callback tự dựng bị webhook trả 400; hai ca bắt buộc (ERR-05, FAILED không kèm artifact) chưa dựng được (.../phase-2/staging/README.md §6i).

b. Chi tiết từng màn ​

Màn M1 - Chuỗi bước dựng dữ liệu và callback (bước 1–16, 18, 19, 21) ​

FieldValue
Figma frameN/A — as-built, không có Figma
Trigger vào mànChọn chip US-09 · DTTH đồng bộ ngược (inventory E-07)
Mô tả ngắnDựng như US-01 để có dòng sổ cái đang chờ, rồi dựng + bắn callback SAP giả lập.
UI States ​
StateKhi nàoHiển thịFigma frame
DefaultSitting mớiBước 1 mở (→ US-D01)N/A
LoadingĐang gọi route ký / SQLNút chạy ở trạng thái xử lýN/A
Has dataBước 18 xong"Phản hồi đầy đủ" chứa lệnh curl đã kýN/A
EmptyCâu us09.sign.1 không có event_idGhi chú: lượt quét chưa gửi dòng này — quay lại bước 16N/A
ErrorRoute ký lỗiLỗi của bước (→ US-P06)N/A
DisabledBước khóa; bước 7 trên StagingNhư US-D09N/A
Fields ​

Nguồn sapp-26878-flows.ts:6009-6067.

FieldRequiredTypeDefaultFormatValidationError MessageVisibilityDepends On
B18 · event_idYesTextbag event_id———Alwaysus09.sign.1
B18 · external_reference (id dòng sổ cái)YesTextbag entry_ididHint: sai → webhook vẫn 200 mà không ghi—Alwaysus09.sign.1
B18 · statusNoDropdown SUCCEEDED / FAILEDSUCCEEDED———Always—
B18 · roleNoDropdown POSITIVE / REVERSALPOSITIVE———Always—
B18 · doc_numNoText12345———Always—
B18 · trans_idNoText67890———Always—
B18 · recognized_amountNoText1000000.00———Always—
B18 · baseline_3387NoText7000000.00—BR-04—Always—
B21 · id dòng sổ cái / event_id của dòng—Text— (nhập tay từ câu ①)———AlwaysCâu ①
Actions ​
A18 - Dựng callback đã ký (bước 18) ​
ItemDescription
TriggerMở bước (SQL tự chạy), kiểm các ô, bấm chạy (lặp được)
BehaviorGọi route ký của tool (→ US-P06); bí mật đọc ở BE, không qua trình duyệt (BR-03, BR-04).
Success ResultNhận lệnh curl đã ký trong "Phản hồi đầy đủ"

Error cases:

CaseExpected Handling
Lệnh đã hết hạn / đã dùngBấm lại bước để lấy lệnh mới (hint)
A19 - Bắn callback (bước 19) ​
ItemDescription
TriggerChép lệnh curl từ bước 18, chạy ngoài tool, bấm "Đã bắn callback"
BehaviorTool không gọi gì (BR-05).
Success ResultTheo guide: nhận 200 — nhưng 200 không có nghĩa sổ cái đã đổi

Error cases:

CaseExpected Handling
401Guide: chữ ký sai, ngoài cửa sổ thời gian, hoặc nonce đã dùng
⏳ 400 từ webhook với body tự dựngHành vi đo được ngoài tool (EC-M1.1) — tool không phát hiện, người dùng đọc ở bước 20
A21 - Dựng các ca còn lại (bước 21) ​
ItemDescription
TriggerĐọc checklist; chạy câu SQL tùy ca (câu ghi cần xác nhận); bấm "Đã đọc các ca còn lại"
Behavior8 ca: thiếu trường vẫn đánh dấu đã post; Deal ID lệch; thất bại một phần; ngưỡng cảnh báo gửi đúng một lần; thiếu trường không xóa giá trị cũ; callback trùng; đảo đến sau khi đã SUCCESS; bút toán giá trị 0 (BR-07). Câu ② ③ ghi Ops DB dev/staging (BR-08).
Success ResultBước xong khi xác nhận

Error cases:

CaseExpected Handling
Ca ngưỡng cảnh báoGuide: không bấm được gì, chờ ≤ 15 phút cho vòng đối soát (dịch vụ không có ingress)
Acceptance Criteria của màn M1 ​
AC-M1.1 - Chuỗi 21 bước ​
gherkin
Given một sitting mới của luồng US-09 phase 2
When người dùng xem stepper
Then bước 1–16 trùng US-01, bước 17–21 lần lượt "Đọc sổ cái và tracking", "Dựng callback đã ký", "Bắn callback", "Đọc kết quả", "Dựng các ca còn lại"
And không có bước "Trả lại mã lớp"

Maps to:

  • Business Rule: BR-01
AC-M1.2 - Điền sẵn khóa callback ​
gherkin
Given lượt quét đã gửi dòng sổ cái của lớp
When mở bước 18
Then câu SQL ① tự chạy, ô event_id và external_reference được điền từ dòng đầu theo ngày buổi
And bấm chạy trả về lệnh curl đã ký

Maps to:

  • Business Rule: BR-03
AC-M1.3 - Dựng ca thiếu trường ​
gherkin
Given bước 18 đang mở
When xóa ô baseline_3387 rồi bấm chạy
Then yêu cầu ký được gửi không kèm số dư 3387

Maps to:

  • Business Rule: BR-04
AC-M1.4 - Bắn callback ngoài tool ​
gherkin
When mở bước 19
Then tool chỉ hiện hướng dẫn chạy curl, không gọi API nào; bước xong khi bấm "Đã bắn callback"

Maps to:

  • Business Rule: BR-05
AC-M1.5 - Dựng các ca còn lại ​
gherkin
When mở bước 21
Then thấy checklist 8 ca và panel 5 câu SQL (câu ② ③ là câu ghi, cần xác nhận)
When chạy câu ② và xác nhận
Then bản ghi tracking của event_id đã nhập có bộ đếm = 8, chưa cảnh báo, trạng thái PROCESSING

Maps to:

  • Business Rule: BR-07, BR-08
Negative AC / Edge Cases của màn M1 ​
IDTình huốngHành vi mong đợi
EC-M1.1⏳ Callback tự dựng bị webhook từ chối (đo trên staging 2026-09-22, ngoài code)Hành vi hiện tại: bộ ký trả lệnh curl hợp lệ, callback tới được webhook nhưng bị trả 400 vì body thiếu 5 trường (request_id, dry_run, created_at, updated_at, steps) và doc_num / trans_id là chuỗi thay vì số nguyên. Không đi vòng được từ phía tool. Hệ quả: ca ERR-05 và ca FAILED không kèm artifact chưa dựng được. ⏳ Chờ SAPP — OQ-D-08 — xem 00-overview §6
EC-M1.2⚠️ Observed — comment trong code ghi thao tác ký "pinned to dev"Luồng đã chạy được trên staging (integration #40BE) — DR-D-10 — xem 00-overview §6b
EC-M1.3⚠️ Observed — chạy luồng trên StagingLuồng không có bước "Trả lại mã lớp" (chỉ US-01 có), trong khi runbook staging §0.1 ghi mọi lượt đều trả mã. Mỗi lượt giữ vĩnh viễn một mã của pool staging; cạn pool → bước Tạo lớp của mọi luồng phase 2 bị chặn. OQ-D-07 · DR-D-06 — xem 00-overview §6 / §6b
EC-M1.4Vòng đối soát định kỳ (đường dự phòng)Guide: tool chưa nối được đường này
EC-M1.5⚠️ Observed — "Dùng deal có sẵn" ở bước 8Như US-D09 EC-M1.3 — OQ-D-06

Màn M2 - Bước 17 "Đọc sổ cái và tracking" và bước 20 "Đọc kết quả" ​

FieldValue
Figma frameN/A — as-built, không có Figma
Trigger vào mànMở bước 17 / 20
Mô tả ngắnHai bước đọc lọc theo deal_id (→ US-P05); bước 17 không có kiểm chứng, bước 20 có 1.
UI States ​
StateKhi nàoHiển thịFigma frame
DefaultMở bướcHint + mã lớp + trace tự chạyN/A
LoadingĐang traTrace đang tảiN/A
Has dataCó trailBước 20: kết quả kiểm chứng "Dòng đã chốt kết quả"N/A
EmptyCallback chưa tới / bị từ chốiDòng vẫn SENT; kiểm chứng fail kèm ba nghi canN/A
ErrorTrace lỗi→ US-P05N/A
DisabledN/AN/AN/A
Fields ​

N/A — bước chỉ đọc.

Actions ​
A17 / A20 - Đọc sổ cái và tracking / Đọc kết quả ​
ItemDescription
TriggerMở bước (tự chạy)
BehaviorBước 17: trace, không kiểm chứng (BR-02). Bước 20: trace + 1 kiểm chứng (BR-06). Không ghi.
Success ResultBước luôn xong

Error cases:

CaseExpected Handling
Không đọc được trườngKiểm chứng = unknown
Acceptance Criteria của màn M2 ​

AC-M2.2 = kiểm chứng tự chấm duy nhất của bước 20 (sapp-26878-flows.ts:6094-6103).

AC-M2.1 - Đọc sổ cái không chấm ​
gherkin
When mở bước 17
Then trace tự chạy theo deal_id, không có kiểm chứng tự động

Maps to:

  • Business Rule: BR-02
AC-M2.2 - Dòng đã chốt kết quả ​
gherkin
Given callback đã được webhook nhận và xử lý
When bước 20 chạy kiểm chứng "Dòng đã chốt kết quả"
Then pass khi có ít nhất một dòng sổ cái của lượt ở status = SUCCESS
And fail kèm ba nghi can (sai id dòng; Deal ID lệch → ERR-01; callback thiếu trường → SENT + ERR-05, khi đó fail là đúng kỳ vọng của ca đó) khi còn SENT

Maps to:

  • Business Rule: BR-06
AC-M2.3 - Bước đọc không chặn ​
gherkin
Given kiểm chứng fail hoặc unknown
Then bước 20 vẫn "xong" và bước 21 mở

Maps to:

  • Business Rule: BR-06
Negative AC / Edge Cases của màn M2 ​
IDTình huốngHành vi mong đợi
EC-M2.1⏳ Callback tự dựng bị 400 (EC-M1.1)Bước 20 thấy dòng vẫn SENT, kiểm chứng fail — không phân biệt được với ca "thiếu trường" chỉ qua kiểm chứng; người dùng phải đọc log (có WEBHOOK_RECEIVED nhưng không có WEBHOOK_VERIFIED, theo runbook §6i)
EC-M2.2Ca thiếu trường chạy được (sau khi SAPP sửa)Theo hint: dòng SENT + ERR-05, mốc đã post có giá trị — kiểm chứng duy nhất fail là đúng kỳ vọng; không có kiểm chứng tự động cho mốc đã post — OQ-D-15

5. Cross-feature AC ​

AC-X.1 - Dùng lại bước US-01 ​

gherkin
Given bước U01:1–16 thay đổi ở US-01
When mở luồng US-09 phase 2
Then luồng dùng đúng định nghĩa mới

Maps to:

  • Liên quan nội bộ FS 26878 (không có mã REL) (US-D09)

AC-X.2 - Dòng thiếu trường không được gửi lại ở US-08 ​

gherkin
Given dòng sổ cái ở SENT + ERR-05 với mốc đã post (ca AC-M2.4 của story)
When sang luồng US-08 phase 2 và gửi lại dòng đó
Then theo guide, dòng bị chặn và đếm ở skipped.ALREADY_POSTED

Maps to:

  • Liên quan nội bộ FS 26878 (không có mã REL) (US-D16). ⏳ Chưa dựng được vì EC-M1.1.