Skip to content

US-D13: Flow P2 — DTTH rời lớp, A rời / B nhận (story SAPP US-05 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-123). 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. Khối 8 bước học viên = như US-D12 BR-03.

⚠ "US-05" ở đây là story phase 2 (chip id us05-p2), khác "US-05 · Gia hạn / Bảo lưu" phase 1 (US-D05).

FieldValue
US IDUS-D13
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-05 · DTTH rời lớp (id us05-p2)
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 — 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ó hai học viên, cho học viên A được ghi nhận vài buổi, rồi chuyển nhượng suất học
       của A cho B trong cùng lớp và đọc kết quả
So that kiểm được rằng khi học viên rời lớp, phần doanh thu đã ghi cho họ bị đảo bằng một dòng sổ cái
        mới mang số âm, ngay trong ngày, và cặp (Deal + môn) của người rời ngừng được xử lý
  • Nguồn "So that": shared/runbooks/sapp/sapp-26878/phase-2/us-05/README.md:6 ("Học viên rời lớp thì phần doanh thu đã ghi cho họ bị đảo bằng một dòng sổ cái MỚI mang số âm"); chuyển nhượng không có lớp đích — shared/external/sapp/sapp-26878/README.md:184; hint luồng sapp-26878-flows.ts:4874. 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 (hint): ngày khai giảng lớp phải trước hôm nay, trong [go-live, hôm qua] (dev từ 2026-09-14, staging từ 2026-09-17) (sapp-26878-flows.ts:4599,4881).

3. Business Rules ​

IDRuleÁp dụng ởĐã cover bởi (AC)Nguồn code (Mode B/C)
BR-01Luồng có 30 bước chia hai hàng (A: 1–17, B: 18–30) 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:4876-4906
BR-02Luồng có hai Deal: học viên A "người RỜI lớp", học viên B "người NHẬN suất", cùng bộ property Deal phase 2 của US-01, vai contact khác nhau; mỗi học viên đi qua khối 8 bước học viênM1 - A9…A16, A18…A25AC-M1.2sapp-26878-flows.ts:4514-4530,4897-4900
BR-03"Kéo hai buổi về quá khứ": người dùng nhập một ngày và hai id lịch; câu ghi cập nhật ngày bắt đầu và kết thúc của hai buổi đầu, các buổi còn lại giữ nguyênM1 - A6AC-M1.3sapp-26878-flows.ts:4550-4605
BR-04Sau khối 8 bước của A có một lượt quét (U01:16) để dựng nền ghi nhận cho A, trước khi B bắt đầuM1 - A17AC-M1.1sapp-26878-flows.ts:4898-4899
BR-05"Đọc học viên trong lớp" đọc danh sách học viên của lớp trên Ops và điền sẵn id người dùng của hai dòng đầu vào hai ô của bước chuyển nhượng (thứ tự không đảm bảo)M1 - A26AC-M1.4sapp-26878-flows.ts:4615-4627
BR-06"Chuyển nhượng suất học" gửi đúng hai trường người rời / người nhận (id người dùng), trong cùng lớp; cả hai bắt buộcM1 - A27AC-M1.4sapp-26878-flows.ts:4635-4661
BR-07Đọc kết quả lọc log SAP theo mã lớp, tự chạy 7 kiểm chứng (§M2), luôn coi là xong; panel SQL có 4 câu đọc và 2 câu ghi (đổi loại ghi danh để dựng nhãn "bảo lưu" / "chuyển đổi")M2AC-M2.1 → AC-M2.8sapp-26878-flows.ts:4670-4802
BR-08"Quét lại sau khi đảo" là một lượt quét thủ công; "Kiểm dừng xử lý" là bước đọc không có kiểm chứng tự động, lọc theo mã lớp, kèm 1 câu đọc và 1 câu ghi dựng lại ca (xóa dòng đảo → trả loại ghi danh về bình thường → xóa lượt chạy sự kiện rời lớp, theo đúng thứ tự trong một lệnh)M1 - A29, M2 - A30AC-M1.5, AC-M2.9sapp-26878-flows.ts:4812-4858
BR-09Mọi câu SQL ghi không tự chạy và phải qua hộp xác nhậnM1 - A6, M2AC-M1.3, AC-M2.9fe/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-05 · DTTH rời lớp", portal Dev | Staging]
  ↓
[M1 · hàng A] U01:1 → U01:2 → U01:3 (sửa ngày khai giảng) → U01:5 → U01:4 → Kéo hai buổi về quá khứ
     → U01:6 → U01:7 → 8 bước học viên A → U01:16 (nền của A)
  ↓
[M1 · hàng B] 8 bước học viên B → Đọc học viên trong lớp → Chuyển nhượng suất học
  ↓
[M2] Đọc kết quả → (M1) Quét lại sau khi đảo → (M2) Kiểm dừng xử lý

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

#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 hai buổi về quá khứguide + sqlriêngOps DB: us05.backdate.2 cập nhật ngày 2 buổiBấm "Đã kéo ngày xong"
7Mở lớp (PUBLIC)opsU01:6OpsGọi thành công
8Thêm option mã lớpoptionU01:7HubSpot dev (staging: bỏ qua)Đủ 3 property / bỏ qua
9–168 bước học viên A (Tạo Deal … Hoàn thiện hồ sơ)deal / classcode / ops / stagetương đương U01:8 → U01:15, trên Deal AHubSpot (⚠️ portal prod khi Staging) + Ops; Redis run historyNhư U01:8 → U01:15
17Chạy lượt quétscanU01:16SAP service dev/stagingGọi thành công
18–258 bước học viên Bnhư 9–16tương đương U01:8 → U01:15, trên Deal BNhư 9–16Như 9–16
26Đọc học viên trong lớpopsriêngKhông ghi (đọc Ops) → bag class_user_0, class_user_1Gọi thành công
27Chuyển nhượng suất họcopsriêngOps: chuyển nhượng trong lớp (theo hint: bắn sự kiện rời lớp, bút toán âm chạy ngay)Gọi thành công
28Đọc kết quảtrace + sqlriêngKhông ghi, trừ câu SQL ⑤ ⑥ (Ops DB) nếu người dùng chạyLuôn xong
29Quét lại sau khi đảoscanriêngSAP serviceGọi thành công
30Kiểm dừng xử lýtrace + sqlriêngKhông ghi, trừ câu SQL ② (Ops DB) nếu chạyLuôn xong

⚠️ Ghi PROD — trên Staging, các bước Deal / Gán mã lớp / chuyển stage của hai học viên (9, 10, 12, 15, 16, 18, 19, 21, 24, 25) ghi HubSpot portal production.

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 ✅ 7/7 trên dev 2026-09-18, lớp F103.216 (shared/runbooks/sapp/sapp-26878/phase-2/us-05/README.md:3-4) và một lượt trọn vẹn trên staging 2026-09-22 (.../phase-2/staging/README.md §6g).

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

Màn M1 - Chuỗi bước dựng dữ liệu và kích hoạt (bước 1–27, 29) ​

FieldValue
Figma frameN/A — as-built, không có Figma
Trigger vào mànChọn chip US-05 · DTTH rời lớp (inventory E-07)
Mô tả ngắnHai hàng "Học viên A · người RỜI lớp" / "Học viên B · người NHẬN suất".
UI States ​
StateKhi nàoHiển thịFigma frame
DefaultSitting mớiHai hàng bước; bước 1 mở (→ 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 26 xongResponse đầy đủ + hai id điền sẵn vào bước 27N/A
EmptyStaging, pool mã lớp rỗngNhư US-D09 EC-M1.2N/A
ErrorOps từ chối chuyển nhượngLỗi Ops (vd theo hint: học viên không ở trạng thái NORMAL)N/A
DisabledBước khóa; bước 8 trên StagingNhư US-D09N/A
Fields ​

Chỉ field riêng (Nguồn sapp-26878-flows.ts:4550-4661).

FieldRequiredTypeDefaultFormatValidationError MessageVisibilityDepends On
B3 · started_atYesDatetime ISO00:00 hôm nay giờ VNISOHint: sửa về go-live; ngày rời lớp phải sau ngày khai giảng—Always—
B6 · Ngày kéo về (quá khứ)—Date—YYYY-MM-DDHint: sau khai giảng, ≤ hôm nay, ≥ go-live—Always—
B6 · schedule_1, schedule_2—Text— (nhập tay từ câu ①)id lịchHint: cột schedule_id—AlwaysCâu ①
B27 · transfer_from (người RỜI)YesTextbag class_user_0id người dùngBR-06Lỗi OpsAlwaysBước 26
B27 · transfer_to (người NHẬN)YesTextbag class_user_1id người dùngBR-06Lỗi OpsAlwaysBước 26
Actions ​
A6 - Kéo hai buổi về quá khứ (bước 6) ​
ItemDescription
TriggerChạy câu ①, nhập ngày + 2 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-03, BR-09). Guide: kéo đúng hai buổi; kéo hết thì lớp bị chốt sổ và ca hỏng.
Success ResultĐúng hai buổi ở quá khứ, phần còn lại ở tương lai

Error cases:

CaseExpected Handling
Nhập nhầm cột idTheo ghi chú: không cập nhật dòng nào, không báo lỗi
A26–A27 - Đọc học viên · Chuyển nhượng suất học (bước 26–27) ​
ItemDescription
TriggerBấm chạy bước 26; kiểm lại hai ô rồi bấm chạy bước 27
BehaviorBước 26 đọc học viên của lớp (BR-05). Bước 27 gọi chuyển nhượng trên Ops dev/staging (BR-06). Hint: phải đối chiếu tên/email trong "Phản hồi đầy đủ" để chắc ai là người rời — đặt ngược là đảo nhầm học viên, không có lỗi.
Success ResultChuyển nhượng thành công; không có lượt quét nào cần bấm cho bút toán âm

Error cases:

CaseExpected Handling
Hai ô bị điền ngược thứ tựTool không phát hiện; bút toán âm rơi vào nhầm người
Học viên không ở trạng thái NORMALOps trả lỗi (hint sapp-26878-flows.ts:4622)
A29 - Quét lại sau khi đảo (bước 29) ​
ItemDescription
TriggerBấm chạy
BehaviorLượt quét thủ công (→ US-P05) để chứng minh cặp (Deal + môn) của người rời không sinh thêm dòng (BR-08).
Success ResultLượt quét vào hàng đợi

Error cases:

CaseExpected Handling
Bấm ngay sau lượt trướcTheo hint: 429, đợi rồi bấm lại
Acceptance Criteria của màn M1 ​
AC-M1.1 - Chuỗi 30 bước, hai hàng ​
gherkin
Given một sitting mới của luồng US-05 phase 2
When người dùng xem stepper
Then thấy hàng A (bước 1–17, kết thúc bằng "Chạy lượt quét") và hàng B (bước 18–30) theo §4a
And không có bước "Trả lại mã lớp"

Maps to:

  • Business Rule: BR-01, BR-04
AC-M1.2 - Hai Deal ​
gherkin
When đi hết khối học viên A và B
Then có hai Deal riêng (tên chứa "US-05 Rời lớp · Học viên A" và "… Học viên B")

Maps to:

  • Business Rule: BR-02
AC-M1.3 - Kéo hai buổi đầu ​
gherkin
Given đã nhập ngày và hai schedule_id
When chạy câu ② và xác nhận
Then ngày bắt đầu và kết thúc của đúng hai buổi đó đổi sang ngày đã chọn

Maps to:

  • Business Rule: BR-03, BR-09
AC-M1.4 - Chuyển nhượng trong cùng lớp ​
gherkin
Given bước 26 trả về danh sách học viên của lớp
When mở bước 27
Then transfer_from = user_id của dòng thứ nhất, transfer_to = user_id của dòng thứ hai, cả hai sửa được
When bấm chạy
Then Ops nhận yêu cầu chuyển nhượng chỉ gồm hai trường đó trên lớp của luồng

Maps to:

  • Business Rule: BR-05, BR-06
AC-M1.5 - Quét lại ​
gherkin
Given bước 28 đã đọc xong
When bấm chạy bước 29
Then một lượt quét thủ công được bắn; bước 30 mở

Maps to:

  • Business Rule: BR-08
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 §6g 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 (OQ-D-18) — guide bước 6 ghi "⚠ Chỉ làm trên dev"Bước không bị khóa trên Staging; runbook §6g ghi đã chạy trên staging. Đề xuất OQ — điều phối viên cấp mã
EC-M1.3⚠️ 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 28 "Đọc kết quả" và bước 30 "Kiểm dừng xử lý" ​

FieldValue
Figma frameN/A — as-built, không có Figma
Trigger vào mànMở bước 28 / bước 30
Mô tả ngắnHai bước đọc cùng lọc theo mã lớp (→ US-P05); bước 28 có 7 kiểm chứng, bước 30 không có kiểm chứng tự động.
UI States ​
StateKhi nàoHiển thịFigma frame
DefaultMở bướcHint + mã lớp + trace tự chạy + panel SQL (us05.trace / us05.stop)N/A
LoadingĐang traTrace đang tảiN/A
Has dataCó trailBước 28: kết quả 7 kiểm chứng. Bước 30: chỉ trailN/A
EmptyChưa có dòng đảoKiểm chứng 1 fail kèm giải thích các lý do skippedN/A
ErrorTrace / SQL lỗi→ US-P05 / US-P09N/A
DisabledN/AN/AN/A
Fields ​
FieldRequiredTypeDefaultFormatValidationError MessageVisibilityDepends On
B28 · Mã sản phẩm SAP (item_code)—Text——chỉ cần cho câu ②—AlwaysCâu ①
B28 / B30 · id bản ghi ghi danh (class_user_id)—Text—idHint: id bản ghi ghi danh, không phải id người dùng—AlwaysCâu ①
Actions ​
A28 - Đọc kết quả (bước 28) ​
ItemDescription
TriggerMở bước (tự chạy); chạy tay SQL
BehaviorTrace theo mã lớp + 7 kiểm chứng (BR-07). SQL ①–④ chỉ đọc (sổ cái deal người rời, giờ lũy kế, lượt EVENT_LEAVE, dấu vết sự kiện rời lớp trong log). ⑤ ⑥ ghi Ops DB: đổi loại ghi danh sang RESERVED / MOVED_OUT để dựng hai nhãn lý do còn lại (sau đó người dùng tự gọi gửi lại dòng — ngoài bước này).
Success ResultBước luôn xong

Error cases:

CaseExpected Handling
Không đọc được trườngKiểm chứng = unknown
A30 - Kiểm dừng xử lý (bước 30) ​
ItemDescription
TriggerMở bước (tự chạy); chạy tay SQL
BehaviorTrace theo mã lớp, không có kiểm chứng tự động — theo code, bước chứng minh một điều phủ định nên người dùng so tay (BR-08). SQL ① đọc 5 dòng sổ cái gần nhất của deal người rời; ② ghi Ops DB để dựng lại ca.
Success ResultBước luôn xong

Error cases:

CaseExpected Handling
Trả loại ghi danh về bình thường mà chưa xóa dòng đảo (chạy tay ngoài câu ②)Theo ghi chú: lượt quét sau gửi mail ERR-01 thật cho kế toán — câu ② gói đúng thứ tự để tránh việc này
Acceptance Criteria của màn M2 ​

AC-M2.1 → AC-M2.7 = kiểm chứng tự chấm của bước 28 (sapp-26878-flows.ts:4670-4731).

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

Maps to:

  • Business Rule: BR-07
AC-M2.2 - Payload mang recognition_type = LEAVE_REVERSAL ​
gherkin
When chạy kiểm chứng tương ứng
Then pass khi phần tử cuối của payload có recognition_type = LEAVE_REVERSAL

Maps to:

  • Business Rule: BR-07
AC-M2.3 - Đảo gửi accumulated_hours ​
gherkin
When chạy kiểm chứng "Đảo gửi accumulated_hours"
Then pass khi phần tử cuối của payload CÓ accumulated_hours

Maps to:

  • Business Rule: BR-07
AC-M2.4 - Đảo gửi standard_hours_remaining ​
gherkin
When chạy kiểm chứng "Đảo gửi standard_hours_remaining"
Then pass khi phần tử cuối của payload CÓ standard_hours_remaining

Maps to:

  • Business Rule: BR-07
AC-M2.5 - Đảo KHÔNG gửi lesson_hours ​
gherkin
When chạy kiểm chứng "Đảo KHÔNG gửi lesson_hours"
Then pass khi phần tử cuối của payload KHÔNG có lesson_hours

Maps to:

  • Business Rule: BR-07
AC-M2.6 - document_date = u_lesson_date ​
gherkin
When chạy kiểm chứng "document_date = u_lesson_date"
Then pass khi ở phần tử cuối, hai trường ngày này bằng nhau

Maps to:

  • Business Rule: BR-07
AC-M2.7 - document_date = posting_date ​
gherkin
When chạy kiểm chứng "document_date = posting_date"
Then pass khi ở phần tử cuối, hai trường ngày này bằng nhau

Maps to:

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

Maps to:

  • Business Rule: BR-07
AC-M2.9 - Dựng lại ca rời lớp ​
gherkin
Given người dùng muốn chạy lại ca
When chạy câu ② của bước 30 và xác nhận
Then trong một lệnh: dòng LEAVE_REVERSAL của deal người rời bị xóa, loại ghi danh trở về NORMAL, các lượt EVENT_LEAVE bị xóa — theo đúng thứ tự đó

Maps to:

  • Business Rule: BR-08, BR-09
Negative AC / Edge Cases của màn M2 ​
IDTình huốngHành vi mong đợi
EC-M2.1Nhãn lý do trong remarksTheo hint: phải so khớp toàn chuỗi bằng tay (Giảm trừ doanh thu thực hiện chuyển nhượng <Deal ID>_<Tên lớp>); không có kiểm chứng tự động
EC-M2.2Bước 30 — không có dòng mớiNgười dùng tự so với ảnh chụp ở bước 28; tool không tự kết luận — OQ-D-15
EC-M2.3⚠️ Observed (OQ-D-19) — câu ② bước 30 xóa mọi lượt EVENT_LEAVE của môi trườngHành vi hiện tại: câu xóa lượt chạy không lọc theo lớp (sapp-26878-flows.ts:4853) — trên môi trường dùng chung có thể xóa cả lượt của người khác. Đề xuất OQ "cố ý hay bug?" — điều phối viên cấp mã

5. Cross-feature AC &ZeroWidthSpace;

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

gherkin
Given khối 8 bước học viên thay đổi
When mở luồng US-04
Then luồng đó dùng cùng khối bước (US-D12 bước 10–17 / 20–27)

Maps to:

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