Skip to content

US-D12: Flow P2 — DTTH ghi bù, học viên A/B (story SAPP US-04) ​

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-122). Khung stepper (kể cả hiển thị một hàng cho mỗi deal) → 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.

FieldValue
US IDUS-D12
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-04 · DTTH ghi bù (id us04)
PriorityN/A — as-built, đã triển khai (backlog R2: không xếp Priority)
StatusDraft
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-07, OQ-D-06, OQ-D-13, OQ-D-15 — xem 00-overview §6 · DR-D-06, DR-D-07 — xem 00-overview §6b

1. US Statement ​

text
As a Người dùng tool (Assumption — OQ về G-01)
I want dựng một lớp có buổi ở quá khứ, cho học viên A vào lớp và quét trước, dựng mốc "cửa sổ đã quét",
       rồi cho học viên B vào lớp muộn và quét lần hai
So that kiểm được rằng những buổi đã qua trước khi học viên vào lớp vẫn được ghi nhận bù cho học viên
        vào muộn (ghi bù / CATCH_UP), còn học viên có mặt từ đầu không bị ghi lại
  • Nguồn "So that": shared/runbooks/sapp/sapp-26878/phase-2/us-04/README.md:5-6 ("Học viên vào lớp sau khi lớp đã học vài buổi thì những buổi đã qua phải được ghi bù, với ngày chứng từ dồn về ngày quét để phiếu vào được kỳ đang mở"); hint luồng sapp-26878-flows.ts:4457. Tiêu chí nghiệm thu của tool: [Assumption] (OQ-D-13).

2. Dependencies & Definition Of Ready ​

N/A — đã triển khai (Mode B). Dependency runtime như US-D09 §2. Thêm: trên Staging, ngày khai giảng lớp phải nằm trong [2026-09-17, hôm qua] thì mới còn chỗ kéo buổi về quá khứ (hint sapp-26878-flows.ts:4251,4457).

3. Business Rules ​

IDRuleÁp dụng ởĐã cover bởi (AC)Nguồn code (Mode B/C)
BR-01Luồng có 29 bước chia hai hàng theo học viên (A: bước 1–18, B: bước 19–29) 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:4459-4496
BR-02Luồng có hai Deal (học viên A "có mặt từ đầu", học viên B "vào lớp muộn"), cả hai dùng bộ property Deal phase 2 của US-01 và mang vai contact khác nhauM1 - A10, A20AC-M1.2sapp-26878-flows.ts:4109-4125; nhãn tên deal be/libs/constants/src/lib/sapp/sapp-26878.constants.ts:515-529
BR-03Mỗi học viên đi qua cùng một khối 8 bước: Tạo Deal → Gán mã lớp → Tạo đơn → Soạn thảo hợp đồng → Tạo giao dịch → Xác nhận giao dịch → Kéo sang Won → Hoàn thiện hồ sơ, gắn với Deal của học viên đóM1 - A10…A17, A20…A27AC-M1.2sapp-26878-flows.ts:4140-4176
BR-04Bước Tạo lớp dùng lại bước của US-01; chỉ hint ô ngày khai giảng đổi (yêu cầu sửa về mốc go-live của môi trường), giá trị mặc định không đổiM1 - A3AC-M1.3sapp-26878-flows.ts:4463-4468
BR-05"Kéo buổi về quá khứ": người dùng tự nhập hai ngày (buổi 1–2 ≤ mốc S, buổi 3–4 > S) và bốn id lịch; câu ghi cập nhật ngày bắt đầu và ngày kết thúc của 4 buổiM1 - A6AC-M1.4sapp-26878-flows.ts:4192-4259
BR-06"Quét lần 1 (nền của A)" là một lượt quét thủ công nằm sau khối 8 bước của A và trước "Dựng mốc cửa sổ"M1 - A18AC-M1.5sapp-26878-flows.ts:4271-4279,4482-4492
BR-07"Dựng mốc cửa sổ": câu ghi chèn một lượt quét WEEKLY · SUCCESS có ngày cuối cửa sổ = S, đánh dấu seeded_by = test-us04; câu dọn chỉ xóa đúng các lượt mang dấu đóM1 - A19AC-M1.6sapp-26878-flows.ts:4325-4377
BR-08Đọc kết quả lọc log SAP theo mã lớp (không theo deal), tự chạy 4 kiểm chứng (§M2), luôn coi là xongM2AC-M2.1 → AC-M2.5sapp-26878-flows.ts:4390-4440; fe/src/ui/components/works/sapp/sapp-26878-tool.tsx:1479-1498
BR-09Mọi câu SQL ghi không tự chạy và phải qua hộp xác nhậnM1 - A6, A19AC-M1.4, AC-M1.6fe/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-04 · DTTH ghi bù", portal Dev | Staging]
  ↓
[M1 · hàng Học viên A] U01:1 → U01:2 → U01:3 (sửa ngày khai giảng) → U01:5 → U01:4
     → Kéo buổi về quá khứ → Xóa buổi đuôi (tùy chọn) → U01:6 → U01:7
     → 8 bước học viên A → Quét lần 1 (nền của A)
  ↓
[M1 · hàng Học viên B] Dựng mốc cửa sổ → 8 bước học viên B → U01:16
  ↓
[M2] Đọc kết quả (lọc theo mã lớp, luôn "xong")

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

#Tên bướcLoạiTái dùngGhi gì (hệ thống · môi trường)Điều kiện chuyển (xong)
1Kiểm tra kết nối Opsops + sqlU01:1Không ghiPing thành công
2Đếm lớp của mônopsU01:2Không ghiGọi thành công
3Tạo lớpops + sqlU01:3 (đổi hint started_at)Ops dev/stagingGọi thành công
4Gán phòng họcopsU01:5OpsGọi thành công
5Tạo buổi họcopsU01:4OpsGọi thành công
6Kéo buổi về quá khứguide + sqlriêngOps DB: us04.backdate.2 cập nhật ngày 4 buổiBấm "Đã kéo ngày xong"
7Xóa buổi đuôi (tùy chọn)ops + sqlriêng (dùng chung với US-D11 bước 16)Ops: xóa buổi (lặp được)Xóa thành công hoặc bỏ qua
8Mở lớp (PUBLIC)opsU01:6OpsGọi thành công
9Thêm option mã lớpoptionU01:7HubSpot dev (staging: bỏ qua)Đủ 3 property / bỏ qua
10Tạo Deal (A)dealtương đương U01:8, Deal học viên AHubSpot (⚠️ portal prod khi Staging); Redis run historyCó deal A
11Gán mã lớp (A)classcodeU01:9HubSpotGọi thành công
12Tạo đơn hàng (A)opsU01:10OpsThành công, số tiền ≠ 0
13Soạn thảo hợp đồng (A)stagetương đương U01:11, trên deal AHubSpotChuyển stage thành công
14Tạo giao dịch (A)opsU01:12OpsGọi thành công
15Xác nhận giao dịch (A)opsU01:13Ops → SAP (theo hint)Gọi thành công
16Kéo sang Won (A)stagetương đương U01:14, trên deal AHubSpotChuyển stage thành công
17Hoàn thiện hồ sơ (A)stagetương đương U01:15, trên deal A (thêm câu hint)HubSpotChuyển stage thành công
18Quét lần 1 (nền của A)scanriêngSAP service dev/stagingGọi thành công
19Dựng mốc cửa sổguide + sqlriêngOps DB: us04.window.1 chèn lượt quét mẫu (câu ③ xóa khi chạy lại)Bấm "Đã dựng mốc cửa sổ"
20–278 bước học viên B (Tạo Deal … Hoàn thiện hồ sơ)deal / classcode / ops / stagenhư bước 10–17, trên deal BNhư bước 10–17Như bước 10–17
28Chạy lượt quétscanU01:16SAP serviceGọi thành công
29Đọc kết quảtraceriêng (lọc theo mã lớp)Không ghiLuôn xong

⚠️ Ghi PROD — trên Staging, bước 10, 11, 13, 16, 17 và 20, 21, 23, 26, 27 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): runbook ghi ✅ 4/4 trên dev 2026-09-18 (shared/runbooks/sapp/sapp-26878/phase-2/us-04/README.md:3) và một lượt trọn vẹn trên staging 2026-09-22 (.../phase-2/staging/README.md §6f).

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

Màn M1 - Chuỗi bước dựng dữ liệu (bước 1–28, hai hàng) ​

FieldValue
Figma frameN/A — as-built, không có Figma
Trigger vào mànChọn chip US-04 · DTTH ghi bù (inventory E-07)
Mô tả ngắnStepper chia hai hàng "Học viên A · có mặt từ đầu" / "Học viên B · vào lớp muộn"; các bước dựng lớp đứng ở hàng A.
UI States ​
StateKhi nàoHiển thịFigma frame
DefaultSitting mớiHai hàng bước; bước 1 mở, còn lại khóa (→ US-D01)N/A
LoadingĐang gọi hệ ngoài / SQLNút chạy ở trạng thái xử lýN/A
Has dataBước xongResponse + id vào bag, tự sang bước kếN/A
EmptyStaging, pool mã lớp rỗngNhư US-D09 EC-M1.2N/A
ErrorHệ ngoài trả lỗi (vd 429 khi quét dồn)Lỗi của bướcN/A
DisabledBước khóa; bước 9 trên StagingNhư US-D09N/A
Fields ​

Chỉ field riêng của luồng (Nguồn sapp-26878-flows.ts:4192-4356).

FieldRequiredTypeDefaultFormatValidationError MessageVisibilityDepends On
B3 · started_atYesDatetime ISO00:00 hôm nay giờ VN (như U01:3)ISOHint: sửa về go-live (dev 2026-09-14, staging 2026-09-17), không sớm hơn go-live—Always—
B6 · Ngày cho buổi 1–2 (≤ S)—Date—YYYY-MM-DDHint: ≥ go-live và ≥ ngày khai giảng—Always—
B6 · Ngày cho buổi 3–4 (> S)—Date—YYYY-MM-DDHint: > S, ≤ hôm nay—Always—
B6 · schedule_1 … schedule_4—Text— (nhập tay từ câu ①)id lịchHint: cột schedule_id, không phải cột id—AlwaysCâu ①
B7 · class_schedule_idYesTextid buổi cuối từ p2.deletetail.1idNhư US-D11 BR-03class_schedule_not_foundAlwaysclass_id
B19 · Mốc S—Date—YYYY-MM-DDHint: cùng giá trị đã dùng ở bước 6—AlwaysBước 6
Actions ​
A6 - Kéo buổi về quá khứ (bước 6) ​
ItemDescription
TriggerChạy câu ① lấy id, nhập 2 ngày + 4 id, chạy câu ② (xác nhận), chạy câu ③, bấm "Đã kéo ngày xong"
BehaviorGhi Ops DB dev/staging (BR-05, BR-09). Guide: phải chừa ít nhất một buổi ở tương lai, nếu không buổi ghi bù cuối cùng ra TRUE_UP thay vì CATCH_UP.
Success Result4 buổi nằm đúng hai phía S, còn ít nhất một buổi tương lai

Error cases:

CaseExpected Handling
Nhập nhầm cột id thay cho schedule_idTheo ghi chú: câu UPDATE không cập nhật dòng nào, không báo lỗi
A18 - Quét lần 1 (bước 18) ​
ItemDescription
TriggerBấm chạy
BehaviorLượt quét thủ công (→ US-P05) để học viên A có lịch sử ghi nhận (BR-06).
Success ResultLượt quét vào hàng đợi

Error cases:

CaseExpected Handling
Bấm dồn hai lượtTheo hint: SAP service trả 429 (nhịp chặn 1000 ms) — đợi rồi bấm lại
A19 - Dựng mốc cửa sổ (bước 19) ​
ItemDescription
TriggerNhập S, chạy câu ① (xác nhận), chạy câu ② kiểm, bấm "Đã dựng mốc cửa sổ"
BehaviorGhi Ops DB dev/staging: chèn lượt quét mẫu (BR-07). Guide: thứ tự cố định quét lần 1 → dựng mốc → thêm B → quét lần 2.
Success ResultDòng mới nhất là WEEKLY · SUCCESS với ngày cuối cửa sổ = S

Error cases:

CaseExpected Handling
Chạy lại caCâu ③ xóa đúng các mốc seeded_by = test-us04
A10–A17, A20–A27 - Khối 8 bước học viên ​
ItemDescription
TriggerNhư U01:8 → U01:15
BehaviorNhư US-D09 A8–A15 nhưng trên Deal A / Deal B (BR-02, BR-03). Hint Deal B: B phải là người khác A (email, SĐT, CCCD khác). ⚠️ Ghi PROD (HubSpot) khi Staging.
Success ResultHai Deal ở Won - Hoàn thiện hồ sơ

Error cases:

CaseExpected Handling
Contact của B trùng ATheo hint Deal B: Ops coi là cùng một học viên, không có người vào muộn; tool chỉ cảnh báo contact trùng theo vai (→ US-D03)
Acceptance Criteria của màn M1 ​
AC-M1.1 - Chuỗi 29 bước, hai hàng ​
gherkin
Given một sitting mới của luồng US-04
When người dùng xem stepper
Then thấy hai hàng "Học viên A · có mặt từ đầu" (bước 1–18) và "Học viên B · vào lớp muộn" (bước 19–29) theo §4a
And không có bước "Trả lại mã lớp"

Maps to:

  • Business Rule: BR-01
AC-M1.2 - Hai Deal, hai khối học viên ​
gherkin
Given luồng US-04
When đi hết bước 10–17 và 20–27
Then có hai Deal riêng (tên chứa "US-04 Ghi bù · Học viên A" và "… Học viên B"), mỗi khối 8 bước tác động đúng Deal của nó

Maps to:

  • Business Rule: BR-02, BR-03
AC-M1.3 - Hint ngày khai giảng ​
gherkin
When mở bước 3 "Tạo lớp" của US-04
Then ô started_at vẫn mặc định 00:00 hôm nay giờ VN, và hint yêu cầu sửa về mốc go-live của môi trường

Maps to:

  • Business Rule: BR-04
AC-M1.4 - Kéo 4 buổi quanh mốc S ​
gherkin
Given đã nhập hai ngày và bốn schedule_id
When chạy câu ② và xác nhận hộp thoại
Then ngày bắt đầu và kết thúc của buổi 1–2 = ngày ≤ S, của buổi 3–4 = ngày > S

Maps to:

  • Business Rule: BR-05, BR-09
AC-M1.5 - Quét lần 1 trước khi dựng mốc ​
gherkin
Given khối 8 bước của A đã xong
When người dùng xem bước kế tiếp
Then đó là "Quét lần 1 (nền của A)", và "Dựng mốc cửa sổ" chỉ mở sau khi bước này xong

Maps to:

  • Business Rule: BR-06
AC-M1.6 - Dựng và dọn mốc cửa sổ ​
gherkin
Given đã nhập mốc S
When chạy câu ① và xác nhận
Then có thêm một lượt quét WEEKLY · SUCCESS với ngày cuối cửa sổ = S và counters.seeded_by = "test-us04"
When chạy câu ③ và xác nhận
Then chỉ các lượt mang seeded_by = "test-us04" bị xóa

Maps to:

  • Business Rule: BR-07, BR-09
Negative AC / Edge Cases của màn M1 ​
IDTình huốngHành vi mong đợi
EC-M1.1⚠️ 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 và lượt §6f 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.2⚠️ Observed — sổ cái trao đổi với SAPP ghi "chip US-04 (28 bước)"Code hiện có 29 bước (có thể do thay đổi sau ngày ghi) — DR-D-07 — xem 00-overview §6b
EC-M1.3Đảo thứ tự (dựng mốc trước quét lần 1, hoặc thêm B trước khi dựng mốc)Gate stepper giữ đúng thứ tự khi chạy tuần tự; nếu bỏ qua bước thì tool không chặn. Theo guide: A cũng nhận CATCH_UP hoặc B không còn là người vào muộn
EC-M1.4Kéo hết buổi về quá khứTool không chặn; theo guide, buổi cuối ra TRUE_UP (thành ca EC-M2.5 của story)
EC-M1.5⚠️ Observed (OQ-D-18) — guide bước 6 ghi "⚠ Chỉ làm trên dev"Bước không bị khóa trên Staging, flow hint mô tả khung staging, và runbook §6f ghi đã chạy trên staging. Đề xuất OQ "hướng dẫn cũ hay giới hạn thật?" — điều phối viên cấp mã
EC-M1.6⚠️ Observed — "Dùng deal có sẵn" cho Deal A hoặc BMọi bước còn lại thuộc deal đó bị đánh dấu xong — OQ-D-06

Màn M2 - Bước 29 "Đọc kết quả" ​

FieldValue
Figma frameN/A — as-built, không có Figma
Trigger vào mànMở bước 29
Mô tả ngắnTrace log SAP lọc theo mã lớp (cả hai học viên) + 4 kiểm chứng.
UI States ​
StateKhi nàoHiển thịFigma frame
DefaultMở bướcHint + mã lớp + trace tự chạy (lọc class_code)N/A
LoadingĐang traTrace đang tảiN/A
Has dataCó trailKết quả 4 kiểm chứngN/A
EmptyChưa có bút toán ghi bùKiểm chứng 1 fail kèm hai nghi can (chưa có mốc cửa sổ; B vào lớp trước mốc)N/A
ErrorTrace lỗi→ US-P05N/A
DisabledN/AN/AN/A
Fields ​

N/A — bộ lọc class_code lấy từ bag.

Actions ​
A29 - Đọc kết quả ​
ItemDescription
TriggerMở bước (tự chạy)
BehaviorTra log theo mã lớp, chạy 4 kiểm chứng (BR-08). Không ghi. Theo code: hai khẳng định quan trọng nhất của story (A không bị ghi lại; ngày chứng từ của dòng ghi bù) cần so từng dòng giữa hai học viên, không được tự động hóa — chỉ nêu trong hint (sapp-26878-flows.ts:4379-4389).
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 = kiểm chứng tự chấm (sapp-26878-flows.ts:4390-4426).

AC-M2.1 - Có bút toán ghi bù ​
gherkin
When chạy kiểm chứng "Có bút toán ghi bù"
Then pass khi có ít nhất một dòng sổ cái của lớp mang entry_type = CATCH_UP

Maps to:

  • Business Rule: BR-08
AC-M2.2 - Ghi bù dùng chung công thức buổi thường ​
gherkin
When chạy kiểm chứng "Ghi bù dùng chung công thức buổi thường"
Then pass khi recognition_type trong payload = PER_LESSON

Maps to:

  • Business Rule: BR-08
AC-M2.3 - Ghi bù mang ngày quét, giữ nguyên ngày buổi ​
gherkin
When chạy kiểm chứng "Ghi bù mang ngày quét, giữ nguyên ngày buổi"
Then pass khi có ít nhất một phần tử payload có document_date khác u_lesson_date

Maps to:

  • Business Rule: BR-08
AC-M2.4 - Ghi bù và buổi thường là một chuỗi ​
gherkin
When chạy kiểm chứng "Ghi bù và buổi thường là MỘT chuỗi"
Then pass khi standard_hours_remaining giảm dần qua các phần tử payload

Maps to:

  • Business Rule: BR-08
AC-M2.5 - Bước đọc không chặn ​
gherkin
Given một kiểm chứng fail hoặc unknown
Then bước 29 vẫn là "xong"

Maps to:

  • Business Rule: BR-08
Negative AC / Edge Cases của màn M2 ​
IDTình huốngHành vi mong đợi
EC-M2.14/4 kiểm chứng passChưa đủ để kết luận story đạt: đếm "A đúng 4 dòng" và so ngày của dòng ghi bù phải đọc tay (A29) — OQ-D-15
EC-M2.2Lượt quét tuần thật hỏng hoặc mốc dựng saiTheo hint: buổi lẽ ra ghi bù ra PER_LESSON, tiền vẫn đúng, không lỗi — người dùng phải tự đọc window_from

5. Cross-feature AC ​

AC-X.1 - Khối 8 bước học viên dùng chung với US-05 ​

gherkin
Given khối 8 bước học viên (bước 10–17 / 20–27) thay đổi
When mở luồng US-05 phase 2
Then luồng đó dùng cùng khối bước (US-D13 bước 9–16 / 18–25)

Maps to:

  • Liên quan nội bộ FS 26878 (không có mã REL) (US-D09, US-D11 bước "Xóa buổi đuôi", US-D13)