Giao diện
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.
| Field | Value |
|---|---|
| US ID | US-S03 |
| Role thực hiện | R1 - Người dùng tool — Assumption (OQ về G-01 → OQ-S-01) |
| Phase | As-built (đã triển khai @ e77c47a) |
| Priority | N/A — as-built |
| Status | Draft |
| NFR liên quan | NFR-S-01, NFR-S-02 — xem Overview §5 |
| OQ liên quan | OQ-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àng | Ai cung cấp | Trạng thái |
|---|---|---|---|
| D1 | Case 0.1 đạt (US-S02); fixture có L1, L2, F1, subject, A, B, C | Tester | — |
| D2 | Ops 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 ​
| ID | Rule | Áp dụng ở | Đã cover bởi (AC) | Nguồn code (Mode B/C) |
|---|---|---|---|---|
| BR-01 | Bướ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ống | M1 - bước chuyển nhượng | AC-M1.1 → AC-M1.6 | fe/src/ui/components/works/sapp/sapp-26297-flows.ts:351-385 |
| BR-02 | Case 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 trang | M1 - case 1.A | AC-M1.1 | sapp-26297-flows.ts:606-647 · fe/src/ui/components/works/sapp/sapp-26297-step-call.tsx:351-370 |
| BR-03 | Case 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 đọc | M1 - case 1.A | AC-M1.1 | sapp-26297-flows.ts:657-705,469-480 |
| BR-04 | Case 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ẹn | M1 - case 1.2, 1.3, 1.4, AC9 | AC-M1.3 | sapp-26297-flows.ts:721-758,848-863 |
| BR-05 | Case 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_code | M1 - case 1.5 | AC-M1.4 | sapp-26297-flows.ts:760-783 |
| BR-06 | Case 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.6 | AC-M1.2 | sapp-26297-flows.ts:707-719,785-795 |
| BR-07 | Case 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 = 422 | M1 - case 1.10, 1.10b | AC-M1.5, AC-M1.6 | sapp-26297-flows.ts:815-840 |
| BR-08 | Case 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.B | M1 - case 1.7, 1.8, 1.9, 1.11 | AC-M1.7 | sapp-26297-flows.ts:797-813,842-846 |
| BR-09 | Biế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.A | AC-M1.8 | sapp-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--> TRANSFERREDb. Chi tiết từng màn ​
Màn M1 - Tab "1 · Chuyển nhượng" ​
| Field | Value |
|---|---|
| Figma frame | N/A |
| Trigger vào màn | Bấm tab "1 · Chuyển nhượng" |
| Mô tả ngắn | Tó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 ​
| State | Khi nào | Hiển thị | Figma frame |
|---|---|---|---|
| Default | Vào tab | Tóm tắt + 14 case đóng; fixture hiện ô lớp / môn / học viên A, B, C | N/A |
| Loading | Bước đang chạy | Theo US-S01 M2 / M3 | N/A |
| Has data | Bước chạy xong | ✓/✗ + "x/y đạt" (US-S01) | N/A |
| Empty | N/A — case cố định | — | N/A |
| Error | Không gọi được Ops | Hộp đỏ 13042 (US-S01 AC-M2.6) | N/A |
| Disabled | Fixture thiếu L1 / L2 / A / B | Nút Chạy khoá "Chưa điền: …" (US-S01 BR-10) | N/A |
Fields ​
| Field | Required | Type | Default | Format | Validation | Error Message | Visibility | Depends On |
|---|---|---|---|---|---|---|---|---|
class_id (đường dẫn, lớp của A) | Yes | Text | {class_id_l1} | id Ops | US-S01 BR-10 | "Chưa điền: …" | Bước chuyển nhượng, ứng viên | Fixture |
transfer_from | Theo case | Text | {user_a} (3.5 dùng {user_c}) | id Ops | US-S01 BR-11 | — | Bước chuyển nhượng, ứng viên | Fixture |
transfer_to | Theo case | Text | {user_b} (1.10: {user_a}) | id Ops | US-S01 BR-11 | — | Bước chuyển nhượng | Fixture |
transfer_to_class_id | Theo case | Text | {class_id_l2} (1.1: {class_id_l1}; 1.10b: trống) | id Ops | Trống ⇒ không gửi (US-S01 BR-11) | — | Bước chuyển nhượng | Fixture |
page_index, page_size, text, filter_class_id, sort_type | No | Text | 1, 20, trống, trống, trống | sort_type: LATEST (mặc định phía Ops) / A_Z | Trống ⇒ không gửi | — | Bước ứng viên | — |
page_index, page_size (danh sách học viên) | No | Text | 1, 50 | — | — | — | Bước cờ nút | — |
Actions ​
A1 - Chạy các bước của một case ​
| Item | Description |
|---|---|
| Trigger | Mở case, bấm Chạy / Chạy (GHI) từng bước theo thứ tự (US-S01) |
| Behavior | Thao 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 Result | Mọi check của case đạt theo bảng case dưới |
Error cases:
| Case | Expected Handling |
|---|---|
| Ops trả mã khác kỳ vọng | Check ✗ kèm giá trị thực; tool không coi là lỗi mind_keep (US-S01 BR-14) |
| Không gọi được Ops | Hộp đỏ 13042 (US-S01 BR-15) |
Bảng case (14 case):
| Mã case | Tên | Bước | Kỳ vọng tool chấm |
|---|---|---|---|
| 1.A | Happy 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 Kafka | BR-02, BR-03 |
| 1.1 | Lớp nhận = chính L1 | chuyển nhượng (lớp nhận = L1) | Cập nhật thành công |
| 1.2 | A đã có tiến độ tại L1 | SQL c12 (GHI 3/10) · chuyển nhượng · SQL int | 400|100332 transfer_condition_no_longer_valid |
| 1.3 | B đã có tiến độ tại L2 | SQL c33 · chuyển nhượng · SQL int | 400|100332 |
| 1.4 | B đang RESERVED tại L2 | SQL c34 · chuyển nhượng · SQL int | 400|100332 |
| 1.5 | A có mặt tại F1 nhưng đang RESERVED | SQL c36 · chuyển nhượng · SQL int | 400|100333 transfer_linked_class_invalid_status + đủ 5 replacements |
| 1.6 | A không có mặt tại F1 | hướng dẫn · chuyển nhượng | Cập nhật thành công (lớp đi kèm không có học viên thì bỏ qua) |
| 1.7 | B đã 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.8 | Lớp nhận khác môn | hướng dẫn → 3.1 | (văn bản) 400|100331 — chấm ở 3.1 |
| 1.9 | Lớp nhận là lớp Revision | hướng dẫn → 3.2 | (văn bản) 400|100331 — chấm ở 3.2 |
| 1.10 | transfer_from = transfer_to | chuyển nhượng A → A | success = false (mã chưa chốt — OQ-S-13) |
| 1.10b | Thiếu transfer_to_class_id | chuyển nhượng (ô lớp nhận trống) | HTTP Ops 422 |
| 1.11 | Chuyể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 |
| AC9 | Kiểm lại tại thời điểm xác nhận | hướng dẫn thao tác UI Ops · chuyển nhượng | 400|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 = falseMaps 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ệ ​
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 ​
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_codeMaps to:
- Business Rule: BR-05
AC-M1.5 - Người cho trùng người nhận ​
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 ​
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" đạtMaps to:
- Business Rule: BR-07
AC-M1.7 - Case trỏ ​
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 ​
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 ​
| ID | Tình huống | Hành vi mong đợi |
|---|---|---|
| EC-M1.1 | Chạy 1.A lần hai khi A→B đang hiệu lực | Tool 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.2 | Chạy case lỗi khi chưa dọn dữ liệu case trước | Kết quả chấm có thể sai do dữ liệu dựng chồng; tool không tự dọn |
| EC-M1.3 | Sự kiện Kafka HubSpot sau chuyển nhượng | Chỉ 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.4 | A / B nằm ngoài 50 dòng đầu của lớp | Check cờ nút fail "không thấy học viên ở trang 1" (US-S01 EC-M2.4) |
5. Cross-feature AC ​
AC-X.1 - Kết quả phụ thuộc hệ SAPP ​
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 ứngMaps to:
- REL-07 (Affects - Hệ thống SAPP)
- REL-03 (Affects - Ops passthrough)