Skip to content

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 ​

  1. Sidebar → Đào tạo → Học viên.
  2. Tìm học viên → Xem.
  3. Tab Ghi danh (BatchEnrollments).
  4. Row enrollment ở status Active → action Chuyển lớp.

Cách 2 — Từ trang lớp ​

  1. Sidebar → Đào tạo → Lớp học → mở Batch nguồn.
  2. Edit page (/admin/batches/{id}/edit) — không phải View.
  3. 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ườngMô 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:

CheckLỗ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:

  1. Update source enrollment:
    • status = transferred_out
    • exited_at = now()
    • exit_reason = lý do bạn nhập
  2. Dispatch BatchEnrollmentStatusChanged event (cho source).
  3. Create new BatchUser ở dest:
    • batch_id = dest
    • user_id = user
    • joined_at = now()
    • status = active
  4. Trigger BatchUser::booted callbacks (auto-sync enrollments):
    • Tạo Enrollment cho mọi khoá thuộc Program của Batch đích.
    • Dispatch UserJoinedBatch event.
  5. Create BatchTransfer record (audit log):
    • student_id, source_batch_id, dest_batch_id
    • transferred_by = user thực hiện (auth)
    • transferred_at = now()
    • reason
  6. Dispatch StudentTransferred event.

→ 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ộtMô tả
student_idStudent được chuyển
source_batch_idLớp đi
dest_batch_idLớp đến
transferred_byUser thực hiện (auth()->id())
transferred_atThời điểm
reasonLý 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_at desc.
  • 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:

  1. Resume trước (status → active).
  2. Transfer.

Hoặc:

  1. Drop (status → dropped) — đóng enrollment hiện tại.
  2. 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:

  1. Resume trước.
  2. 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.

LMS Documentation