Appearance
Chuyển lớp (Batch Transfer)
Batch Transfer là quy trình chuyển học viên từ lớp này sang lớp khác — atomic, có audit trail, đảm bảo không mất tiến độ học. Đây là kênh chính thức để học viên đổi cohort khi cần (vd: kẹt lịch, cohort cũ kết thúc, học viên xin hoãn).
Một dòng tóm tắt
Action Chuyển lớp trên BatchEnrollment → đóng enrollment cũ với status transferred_out + tạo enrollment mới ở lớp đích status active → log vào batch_transfers table với người thực hiện + lý do. Toàn bộ trong 1 transaction → atomic.
Khi nào dùng?
- Học viên kẹt lịch cohort hiện tại → chuyển sang cohort sau.
- Cohort hiện tại kết thúc đột ngột (huỷ) → di chuyển toàn bộ học viên sang cohort khác.
- Học viên xin chuyển từ lớp morning sang evening → cùng program, lịch khác.
- Doanh nghiệp khách hàng đổi nhóm nhân sự → chuyển batch.
Khi nào KHÔNG dùng Transfer?
- Học viên rời lớp + nghỉ học hẳn → dùng Drop, không phải Transfer.
- Học viên xin tạm nghỉ → dùng Suspend, không phải Transfer.
- Học viên thi xong + tốt nghiệp → dùng Complete + Graduate.
- Đổi Program (curriculum khác) → Transfer chỉ giữa 2 Batch; nếu Program khác = curriculum khác = transfer được nhưng tiến độ khoá có thể không tương thích.
Vào quy trình Transfer
Có 2 cách mở action Chuyển lớp:
Cách 1 — Từ hồ sơ học viên
- Sidebar → Đào tạo → Học viên.
- Tìm học viên → Xem.
- Tab Ghi danh (BatchEnrollments).
- Row enrollment ở status
Active→ action Chuyển lớp.
Cách 2 — Từ trang lớp
- Sidebar → Đào tạo → Lớp học → mở Batch nguồn.
- Edit page (
/admin/batches/{id}/edit) — không phải View. - Tab Học viên → row học viên → action Chuyển lớp.
Chỉ ở Edit page
Lifecycle actions (Suspend, Resume, Drop, Complete, Transfer) chỉ hiện ở Edit page — View page giữ read-only. Cần edit Batch → click Edit ở góc trên.
Form Transfer
Modal Chuyển sang lớp khác:
| Trường | Mô tả |
|---|---|
Lớp đến (dest_batch_id) | Dropdown — chỉ hiện Batch khác và đang ở trạng thái cho phép enrollment (Active, Draft) |
Lý do (reason) | Text mô tả — sẽ lưu vào audit log |
Click Chuyển → confirm → service chạy.
Validation trước transfer
BatchTransferService::transfer() kiểm tra:
| Check | Lỗi nếu fail |
|---|---|
| Source ≠ Dest | "Lớp đi và lớp đến phải khác nhau." |
| Dest cho phép enrollment | "Lớp đến không cho phép ghi danh mới (trạng thái: :status)." |
Source enrollment status = active | "Học viên không có ghi danh hoạt động ở lớp đi." |
| User chưa có enrollment ở Dest | "Học viên đã có ghi danh ở lớp đến." |
Học viên đã có enrollment cũ ở Dest
Nếu học viên trước đây đã thuộc lớp đích (vd: đã dropped rồi quay lại) → không thể tạo enrollment mới cùng lớp đó. Phải hoặc:
- Reactivate enrollment cũ (chưa có UI — chỉnh DB).
- Hoặc không transfer, dùng cách khác.
Constraint UNIQUE(batch_id, user_id) trên batch_user.
Atomic flow
Trong 1 DB transaction:
- Update source enrollment:
status=transferred_outexited_at=now()exit_reason= lý do bạn nhập
- Dispatch
BatchEnrollmentStatusChangedevent (cho source). - Create new BatchUser ở dest:
batch_id= destuser_id= userjoined_at=now()status=active
- Trigger BatchUser::booted callbacks (auto-sync enrollments):
- Tạo Enrollment cho mọi khoá thuộc Program của Batch đích.
- Dispatch
UserJoinedBatchevent.
- Create BatchTransfer record (audit log):
student_id,source_batch_id,dest_batch_idtransferred_by= user thực hiện (auth)transferred_at=now()reason
- Dispatch
StudentTransferredevent.
→ Nếu bất kỳ bước nào fail → rollback toàn bộ. Không có trạng thái lưng chừng.
Sau khi transfer
Phía học viên
- Lớp cũ biến mất khỏi "Lớp đang học" trong Learnspace.
- Lớp mới xuất hiện ngay (hoặc sau vài giây nếu queue ùn).
- Notification + email "Bạn đã được chuyển sang lớp X".
- Tiến độ khoá học được giữ nếu Program lớp mới chứa cùng khoá đó (FK qua user_id, không qua batch_user.id).
Tiến độ khoá ≠ tiến độ lớp
- Tiến độ khoá (Enrollment.progress) — gắn với cặp
(user, course). Giữ qua transfer. - Tiến độ lớp (BatchEnrollment.progress) — gắn với cặp
(batch_user). Không transfer được — lớp mới bắt đầu fresh.
Hệ quả thực tế: học viên transfer giữa 2 cohort cùng Program → các khoá học đã hoàn thành không phải làm lại. Mất nhịp lớp (catch-up plan, attendance) là tất yếu.
Phía trainer
- Trainer lớp cũ thấy student rời danh sách Học viên (status =
Transferred Out). - Trainer lớp mới thấy student xuất hiện (status =
Active). - Cả 2 trainer có thể @mention nhau trong nhật ký lớp để bàn giao.
Phía gradebook
- Điểm trong lớp cũ vẫn lưu (vì gắn với cặp grade_item × user).
- Lớp mới có cột điểm riêng — bắt đầu trống. Nếu muốn carry-over, nhập tay.
Phía attendance
- Buổi lớp cũ trước transfer: attendance giữ nguyên trong cross-batch summary của student profile.
- Buổi lớp cũ sau transfer: học viên không có mặt (đã rời) → không bị tính.
- Buổi lớp mới: học viên có mặt từ thời điểm
joined_at.
Audit trail
Mỗi lần transfer ghi 1 row vào batch_transfers:
| Cột | Mô tả |
|---|---|
student_id | Student được chuyển |
source_batch_id | Lớp đi |
dest_batch_id | Lớp đến |
transferred_by | User thực hiện (auth()->id()) |
transferred_at | Thời điểm |
reason | Lý do (text) |
Không sửa được record này (read-only — chỉ tạo qua action).
Xem lịch sử transfer của 1 student
Hồ sơ Student → tab Lịch sử chuyển lớp (TransfersRelationManager):
- Bảng tất cả transfers — sort theo
transferred_atdesc. - Read-only — không action.
Xem ai đã làm transfer trong tổ chức
Hiện chưa có "global transfer log" trong UI. Cách tạm:
- DB query
SELECT * FROM batch_transfers WHERE transferred_at > ?. - Đề xuất Tenant Admin / Central Admin tạo audit page nếu nhu cầu.
Trường hợp đặc biệt
Transfer học viên đang suspended
Action Transfer chỉ hoạt động trên enrollment active. Học viên đang suspended:
- Resume trước (status →
active). - Transfer.
Hoặc:
- Drop (status →
dropped) — đóng enrollment hiện tại. - Enroll vào lớp mới (qua tab Ghi danh của student). Note: action này không tạo BatchTransfer record (vì là 2 thao tác riêng, không phải transfer atomic).
Transfer giữa 2 Batch khác Program
Service không chặn — bạn được phép transfer giữa Program A và Program B. Hệ quả:
- Khoá thuộc Program A nhưng không thuộc B → enrollment khoá đó bị huỷ.
- Khoá thuộc Program B nhưng không thuộc A → enrollment mới được tạo (tiến độ 0%).
- Khoá chung (cả A và B) → tiến độ giữ.
Cẩn thận với khác Program
Nếu curriculum khác nhau hoàn toàn, học viên có thể "mất" việc đã học (về mặt UI). Đề xuất:
- Tránh transfer khác Program nếu có thể.
- Nếu phải, ghi note + thông báo học viên trước.
- Hoặc không transfer mà Drop + Enroll thủ công với confirmation.
Transfer học viên đang Suspend
Hệ thống chặn — service yêu cầu source enrollment ở active.
Cách tạm:
- Resume trước.
- Transfer.
Lưu ý audit: 2 events BatchEnrollmentStatusChanged (resume + transfer-out).
Transfer cho học viên khi Batch đã ở Completed
Source batch có thể ở mọi status (chỉ enrollment cần active). Tuy nhiên dest batch phải ở status cho phép enrollment (Active / Draft / Paused tuỳ cấu hình allowsEnrollment()).
Bulk transfer
Hiện không có UI bulk action "Transfer 30 học viên cùng lúc". Phải transfer từng người.
Bulk transfer khi cohort huỷ
Tình huống: Batch A bị huỷ, cần move 30 học viên sang Batch B.
Cách 1 — Manual loop: Vào Batch A → Edit → tab Học viên → từng row click Transfer. Mất ~5 phút.
Cách 2 — Script: Đề nghị Central Admin chạy script BatchTransferService::transfer() cho 30 user.
Cách 3 — DB direct (KHÔNG khuyến nghị): UPDATE batch_user SET batch_id = B; thiếu audit log + skip listeners.
Cách 1 an toàn nhất. Cách 2 nhanh nhất.
Phạm vi
Training Manager mặc định có toàn quyền chuyển lớp + xem lịch sử chuyển lớp. Không có UI tạo BatchTransfer riêng — chỉ thông qua Transfer action.
Quan hệ với các module khác
Với Notifications
Sự kiện StudentTransferred có thể được wire lên email template:
- Học viên: "Bạn đã được chuyển từ lớp A sang lớp B."
- Trainer mới: "Học viên X vừa transfer vào lớp của bạn."
Cấu hình ở Tenant Admin → Notifications.
Với Gamification
Hiện không có rule "cộng điểm khi transfer" — không phải hành vi positive cần khích lệ.
Với Catch-up Plan
Sau transfer, học viên thường chậm hơn cohort mới. Trainer có thể áp Catch-up plan ngay sau transfer để hiệu chỉnh nhịp.
Câu hỏi thường gặp
Học viên transfer sang lớp đầy (đã đạt max_students)? Hệ thống không chặn ở UI hiện tại. Tự kiểm trước khi confirm. Nếu cần enforce, đề xuất Tenant Admin thêm validation.
Tôi transfer nhầm lớp — undo được không? Không có nút undo. Cách tạm: transfer ngược lại (lớp mới → lớp cũ) — sẽ tạo BatchTransfer thứ 2 trong audit log. Hoặc:
- Drop enrollment vừa tạo ở dest.
- Reactivate enrollment cũ ở source (manual DB).
Liên hệ Tenant Admin nếu phức tạp.
Học viên có nhận thông báo khi tôi transfer họ? Tuỳ config Notifications. Mặc định template chưa active — kiểm tra với Tenant Admin.
Trainer lớp cũ vẫn thấy nhật ký + comment cũ của học viên? Có. Nhật ký lớp + activity comment không transfer. Chỉ enrollment status đổi. Lịch sử cũ giữ trong context lớp cũ.
Có rate limit cho transfer? Không có. Service được thiết kế để run nhanh + atomic. Vẫn cẩn thận với bulk — mỗi transfer trigger background jobs (BatchCourseEnrollmentSync).
Tiến độ khoá học có "merge" giữa 2 lớp? Không. Tiến độ là per (user, course). Học viên đã hoàn thành khoá X trong lớp A → vào lớp B (cũng có khoá X) → hệ thống thấy enrollment.progress đã đạt → không phải học lại.
Audit log có thể export? DB query thôi. UI hiện không có export riêng cho batch_transfers. Nhờ Tenant Admin trích nếu cần báo cáo.
Học viên transfer cùng group (Subscription)? Group (subscription) gắn với User, không gắn Batch — transfer batch không ảnh hưởng group/subscription.
Có thể transfer học viên đã Withdraw từ trường? Không nên — học viên đã rời. Nếu thực sự cần, Reactivate student trước, sau đó transfer enrollment.