Skip to content

US-S03: Chuyển nhượng suất học cho học viên khác (luồng 1) ​

Thuộc FS: 00-overview.md — SAPP-26297 Chuyển nhượng học viên (tool sapp.011) 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.

Cơ chế chạy bước, tự chấm, xác nhận GHI, loại check → US-S01. Câu SQL → catalogue ở US-S02 §4. "Kỳ vọng" là văn bản tool hiển thị theo guide QA — nghiệp vụ chuyển nhượng thật do SAPP Ops cài, không phải rule của mind_keep.

FieldValue
US IDUS-S03
Role thực hiệnR1 - Người dùng tool — Assumption (OQ về G-01 → OQ-S-01)
PhaseAs-built (đã triển khai @ e77c47a)
PriorityN/A — as-built
StatusDraft
NFR liên quanNFR-S-01, NFR-S-02 — xem Overview §5
OQ liên quanOQ-S-01, OQ-S-12, OQ-S-13, OQ-S-14, OQ-S-16 — xem Overview §6

1. US Statement ​

text
As a Người dùng tool  [Assumption — OQ-S-01]
I want chạy 14 case của luồng 1: học viên A nhường suất tại lớp L1 cho học viên B học tại lớp L2 (cùng môn),
       kèm các trường hợp bị từ chối, và xem tool tự chấm kết quả Ops
So that kiểm được AC1, AC2, AC4, AC5, AC6, AC8, AC9 của SAPP-26297 trên dev/staging
        (nguồn phạm vi: tóm tắt luồng trên màn; mục tiêu nghiệp vụ rộng hơn là [Assumption] — OQ-S-18)

2. Dependencies & Definition Of Ready ​

N/A — đã triển khai. Dependency runtime:

#Cần sẵn sàngAi cung cấpTrạng thái
D1Case 0.1 đạt (US-S02); fixture có L1, L2, F1, subject, A, B, CTester—
D2Ops dev/staging chạy nhánh có tính năng chuyển nhượng (sapp-sprint-49)SAPP❓ OQ-S-15

3. Business Rules ​

IDRuleÁp dụng ởĐã cover bởi (AC)Nguồn code (Mode B/C)
BR-01Bước chuyển nhượng mặc định: lớp đường dẫn = L1, người cho = A, người nhận = B, lớp nhận = L2 (điền từ fixture); case thay đổi đúng một giá trị để dựng tình huốngM1 - bước chuyển nhượngAC-M1.1 → AC-M1.6fe/src/ui/components/works/sapp/sapp-26297-flows.ts:351-385
BR-02Case 1.A bước ứng viên đạt khi: HTTP 200; B có trong danh sách ứng viên; B có L2 trong danh sách lớp hợp lệ với tiến độ 0; A không có trong danh sách; có tên môn; danh sách lớp trong môn chứa L1 và L2; có thông tin phân trangM1 - case 1.AAC-M1.1sapp-26297-flows.ts:606-647 · fe/src/ui/components/works/sapp/sapp-26297-step-call.tsx:351-370
BR-03Case 1.A sau chuyển nhượng đạt khi: "Cập nhật thành công"; tại L1 A không còn nút chuyển nhượng và có nút hủy; tại L2 B không có cả hai nút; tại F1 (lớp đi kèm) A không có nút hủy. Verify DB (trạng thái hai phía, bản ghi lịch sử, đúng 1 bản ghi) là câu SQL tester đọcM1 - case 1.AAC-M1.1sapp-26297-flows.ts:657-705,469-480
BR-04Case từ chối vì điều kiện không còn hợp lệ (1.2 A có tiến độ, 1.3 B có tiến độ, 1.4 B đang RESERVED, AC9 đổi trạng thái lúc xác nhận): dựng bằng SQL GHI rồi chấm "Từ chối đúng" 400|100332 · exceptions.transfer_condition_no_longer_valid, sau đó chạy câu kiểm toàn vẹnM1 - case 1.2, 1.3, 1.4, AC9AC-M1.3sapp-26297-flows.ts:721-758,848-863
BR-05Case 1.5 (A tại F1 đang RESERVED) đạt khi "Từ chối đúng" 400|100333 · exceptions.transfer_linked_class_invalid_status và error.replacements có đủ 5 khoá: student_name, status, class_code, linked_class_type, lesson_class_codeM1 - case 1.5AC-M1.4sapp-26297-flows.ts:760-783
BR-06Case thành công 1.1 (lớp nhận = chính L1) và 1.6 (A không có mặt tại F1) đạt khi "Cập nhật thành công"M1 - case 1.1, 1.6AC-M1.2sapp-26297-flows.ts:707-719,785-795
BR-07Case 1.10 (người cho = người nhận) chỉ chấm success = false — mã lỗi chưa chốt; case 1.10b (để trống lớp nhận) chấm HTTP Ops = 422M1 - case 1.10, 1.10bAC-M1.5, AC-M1.6sapp-26297-flows.ts:815-840
BR-08Case trỏ sang nơi khác chỉ có bước hướng dẫn, không gọi API: 1.7 → 3.5, 1.8 → 3.1, 1.9 → 3.2, 1.11 → 2.BM1 - case 1.7, 1.8, 1.9, 1.11AC-M1.7sapp-26297-flows.ts:797-813,842-846
BR-09Biến thể bước ứng viên (tìm theo tên / mã / email, lọc theo lớp, sắp A_Z, bỏ trống page_index ⇒ 422) là hướng dẫn tay: tester sửa ô rồi chạy lại, tool không có check riêng cho biến thểM1 - case 1.AAC-M1.8sapp-26297-flows.ts:649-656 · be/libs/interface/src/lib/dto/sapp/sapp-26297.dto.ts:47-70

4. Design ​

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

text
[Tab "1 · Chuyển nhượng"]  (tiền đề: case 0.1 đạt)
  ↓ mở 1.A
[ứng viên (đọc)] → [chuyển nhượng (GHI Ops)] → [SQL verify f1v] → [cờ nút L1, L2, F1 (đọc)] → [ghi chú Kafka]
  ↓ các case lỗi
[SQL dựng điều kiện (GHI)] → [chuyển nhượng (GHI Ops) — kỳ vọng bị từ chối] → [SQL kiểm toàn vẹn int]

Trạng thái kỳ vọng phía Ops (máy trạng thái thuộc Ops, không cài ở mind_keep):
A@L1: NORMAL 0% --chuyển nhượng--> TRANSFERED_TO   (lớp đi kèm của A cũng đổi)
B@L2: NORMAL 0% --chuyển nhượng--> BE_TRANSFERRED
Lịch sử A→B: (không có) --chuyển nhượng--> TRANSFERRED

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

Màn M1 - Tab "1 · Chuyển nhượng" ​

FieldValue
Figma frameN/A
Trigger vào mànBấm tab "1 · Chuyển nhượng"
Mô tả ngắnTóm tắt: "AC1, AC2, AC4, AC5, AC6, AC8, AC9. A nhường suất tại L1 cho B, B học tại L2 (cùng môn). Cascade lớp Foundation/Revision cả hai phía, sinh đúng 1 bản ghi lịch sử." 14 case
UI States ​
StateKhi nàoHiển thịFigma frame
DefaultVào tabTóm tắt + 14 case đóng; fixture hiện ô lớp / môn / học viên A, B, CN/A
LoadingBước đang chạyTheo US-S01 M2 / M3N/A
Has dataBước chạy xong✓/✗ + "x/y đạt" (US-S01)N/A
EmptyN/A — case cố định—N/A
ErrorKhông gọi được OpsHộp đỏ 13042 (US-S01 AC-M2.6)N/A
DisabledFixture thiếu L1 / L2 / A / BNút Chạy khoá "Chưa điền: …" (US-S01 BR-10)N/A
Fields ​
FieldRequiredTypeDefaultFormatValidationError MessageVisibilityDepends On
class_id (đường dẫn, lớp của A)YesText{class_id_l1}id OpsUS-S01 BR-10"Chưa điền: …"Bước chuyển nhượng, ứng viênFixture
transfer_fromTheo caseText{user_a} (3.5 dùng {user_c})id OpsUS-S01 BR-11—Bước chuyển nhượng, ứng viênFixture
transfer_toTheo caseText{user_b} (1.10: {user_a})id OpsUS-S01 BR-11—Bước chuyển nhượngFixture
transfer_to_class_idTheo caseText{class_id_l2} (1.1: {class_id_l1}; 1.10b: trống)id OpsTrống ⇒ không gửi (US-S01 BR-11)—Bước chuyển nhượngFixture
page_index, page_size, text, filter_class_id, sort_typeNoText1, 20, trống, trống, trốngsort_type: LATEST (mặc định phía Ops) / A_ZTrống ⇒ không gửi—Bước ứng viên—
page_index, page_size (danh sách học viên)NoText1, 50———Bước cờ nút—
Actions ​
A1 - Chạy các bước của một case ​
ItemDescription
TriggerMở case, bấm Chạy / Chạy (GHI) từng bước theo thứ tự (US-S01)
BehaviorThao tác dùng: ứng viên (đọc), chuyển nhượng (GHI), danh sách học viên lớp (đọc); câu SQL f1v, c12, c33, c34, c36, int (US-S02). Ghi vào: Ops dev/staging (chuyển nhượng) và DB Ops dev/staging (SQL dựng điều kiện). Không có đường PROD (NFR-S-02)
Success ResultMọi check của case đạt theo bảng case dưới

Error cases:

CaseExpected Handling
Ops trả mã khác kỳ vọngCheck ✗ kèm giá trị thực; tool không coi là lỗi mind_keep (US-S01 BR-14)
Không gọi được OpsHộp đỏ 13042 (US-S01 BR-15)

Bảng case (14 case):

Mã caseTênBướcKỳ vọng tool chấm
1.AHappy path: ứng viên → chuyển nhượng → verifyứng viên · hướng dẫn biến thể · chuyển nhượng (GHI) · SQL f1v · học viên L1 · học viên L2 · học viên F1 · hướng dẫn KafkaBR-02, BR-03
1.1Lớp nhận = chính L1chuyển nhượng (lớp nhận = L1)Cập nhật thành công
1.2A đã có tiến độ tại L1SQL c12 (GHI 3/10) · chuyển nhượng · SQL int400|100332 transfer_condition_no_longer_valid
1.3B đã có tiến độ tại L2SQL c33 · chuyển nhượng · SQL int400|100332
1.4B đang RESERVED tại L2SQL c34 · chuyển nhượng · SQL int400|100332
1.5A có mặt tại F1 nhưng đang RESERVEDSQL c36 · chuyển nhượng · SQL int400|100333 transfer_linked_class_invalid_status + đủ 5 replacements
1.6A không có mặt tại F1hướng dẫn · chuyển nhượngCập nhật thành công (lớp đi kèm không có học viên thì bỏ qua)
1.7B đã nhận chuyển nhượng môn này (AC8)hướng dẫn → 3.5(văn bản) 400|100332 — chấm ở 3.5
1.8Lớp nhận khác mônhướng dẫn → 3.1(văn bản) 400|100331 — chấm ở 3.1
1.9Lớp nhận là lớp Revisionhướng dẫn → 3.2(văn bản) 400|100331 — chấm ở 3.2
1.10transfer_from = transfer_tochuyển nhượng A → Asuccess = false (mã chưa chốt — OQ-S-13)
1.10bThiếu transfer_to_class_idchuyển nhượng (ô lớp nhận trống)HTTP Ops 422
1.11Chuyển nhượng lại sau hủy (AC12)hướng dẫn → 2.B(văn bản) thành công — chấm ở 2.B
AC9Kiểm lại tại thời điểm xác nhậnhướng dẫn thao tác UI Ops · chuyển nhượng400|100332, không phải thành công
Acceptance Criteria của màn M1 ​
AC-M1.1 - Case 1.A happy path ​
gherkin
Given case 0.1 đạt và fixture đủ L1, L2, F1, A, B
When người dùng chạy bước ứng viên
Then HTTP 200, B có trong data.data, B.eligible_classes chứa L2 với progress = 0, A không có trong data.data,
     có data.subject_name, data.classes_in_subject chứa L1 và L2, có data.metadata
When người dùng chạy "Bước 2 — Chuyển nhượng A → B tại L2" và xác nhận GHI
Then HTTP 200, success = true, data.message = "Update successfully"
When người dùng chạy 3 bước cờ nút
Then tại L1: A can_transfer = false, can_revert_transfer = true
And tại L2: B can_transfer = false, can_revert_transfer = false
And tại F1: A can_revert_transfer = false

Maps to:

  • Business Rule: BR-01, BR-02, BR-03
AC-M1.2 - Case chuyển nhượng thành công ​
gherkin
Scenario Outline: chuyển nhượng được chấp nhận
  Given <dựng>
  When người dùng chạy bước chuyển nhượng với lớp nhận = <lớp nhận>
  Then HTTP 200, success = true, data.message = "Update successfully"

  Examples:
    | case | dựng                          | lớp nhận |
    | 1.1  | A, B NORMAL 0%                | L1       |
    | 1.6  | A không có mặt tại lớp F1     | L2       |

Maps to:

  • Business Rule: BR-06
AC-M1.3 - Từ chối vì điều kiện không còn hợp lệ &ZeroWidthSpace;
gherkin
Scenario Outline: 100332
  Given đã chạy <dựng>
  When người dùng chạy bước chuyển nhượng
  Then HTTP 400, error.code = "400|100332", error.message = "exceptions.transfer_condition_no_longer_valid"
  And câu kiểm toàn vẹn cho thấy không type nào đổi, số bản ghi A→B không tăng

  Examples:
    | case | dựng                                                  |
    | 1.2  | SQL c12: A tại L1 tiến độ 3/10                        |
    | 1.3  | SQL c33: B tại L2 tiến độ 3/10                        |
    | 1.4  | SQL c34: B tại L2 → RESERVED                          |
    | AC9  | mở popup trên UI Ops, đổi B sang RESERVED ở tab khác  |

Maps to:

  • Business Rule: BR-04
AC-M1.4 - Lớp đi kèm sai trạng thái &ZeroWidthSpace;
gherkin
Given đã chạy SQL c36: A tại F1 → RESERVED
When người dùng chạy bước chuyển nhượng
Then HTTP 400, error.code = "400|100333", error.message = "exceptions.transfer_linked_class_invalid_status"
And error.replacements có đủ student_name, status, class_code, linked_class_type, lesson_class_code

Maps to:

  • Business Rule: BR-05
AC-M1.5 - Người cho trùng người nhận &ZeroWidthSpace;
gherkin
Given ô transfer_to = user_a
When người dùng chạy bước chuyển nhượng
Then check "success = false" đạt; tester ghi lại error.code thực tế

Maps to:

  • Business Rule: BR-07
AC-M1.6 - Thiếu lớp nhận &ZeroWidthSpace;
gherkin
Given ô transfer_to_class_id để trống
When người dùng chạy bước chuyển nhượng
Then body gửi đi không có transfer_to_class_id
And check "HTTP Ops = 422" đạt

Maps to:

  • Business Rule: BR-07
AC-M1.7 - Case trỏ &ZeroWidthSpace;
gherkin
Scenario Outline: case chỉ hướng dẫn
  When người dùng mở case <case>
  Then chỉ hiện bước hướng dẫn trỏ sang <đích> và không có request nào

  Examples:
    | case | đích |
    | 1.7  | 3.5  |
    | 1.8  | 3.1  |
    | 1.9  | 3.2  |
    | 1.11 | 2.B  |

Maps to:

  • Business Rule: BR-08
AC-M1.8 - Biến thể bước ứng viên &ZeroWidthSpace;
gherkin
Given case 1.A đang mở
When người dùng sửa ô sort_type = "A_Z" rồi chạy lại bước ứng viên
Then query gửi đi có sort_type=A_Z và các check của bước được chấm lại
When người dùng xoá ô page_index rồi chạy lại
Then query không có page_index; kỳ vọng theo hướng dẫn là 422 (tester tự đọc HTTP trong response)

Maps to:

  • Business Rule: BR-09
Negative AC / Edge Cases của màn M1 &ZeroWidthSpace;
IDTình huốngHành vi mong đợi
EC-M1.1Chạy 1.A lần hai khi A→B đang hiệu lựcTool gửi lại và hiển thị nguyên văn câu trả lời Ops (có thể là từ chối) — tester chạy 0.2 hoặc luồng 2 trước
EC-M1.2Chạy case lỗi khi chưa dọn dữ liệu case trướcKết quả chấm có thể sai do dữ liệu dựng chồng; tool không tự dọn
EC-M1.3Sự kiện Kafka HubSpot sau chuyển nhượngChỉ có ghi chú: BE chỉ bắn cho A, không cho B — "nếu BA muốn bắn cho cả B thì đây là bug" (OQ-S-16)
EC-M1.4A / B nằm ngoài 50 dòng đầu của lớpCheck cờ nút fail "không thấy học viên ở trang 1" (US-S01 EC-M2.4)

5. Cross-feature AC &ZeroWidthSpace;

AC-X.1 - Kết quả phụ thuộc hệ SAPP &ZeroWidthSpace;

gherkin
Given Ops dev/staging chưa chạy nhánh có tính năng chuyển nhượng
When người dùng chạy case 1.A
Then tool vẫn hiển thị nguyên văn câu trả lời Ops và check ✗ tương ứng

Maps to:

  • REL-07 (Affects - Hệ thống SAPP)
  • REL-03 (Affects - Ops passthrough)