# Module `course` — Khóa học & xếp lớp

> 07/7/2026 · Giai đoạn: **MVP** · Bản HTML: [course.html](course.html) · Sơ đồ tổng thể: [../so-do-ws-module-feature-2026-07-06.md](../so-do-ws-module-feature-2026-07-06.md)
> Trạng thái code: **đã scaffold** bảng khóa học (port legacy) — cần thêm `enrollment`, `plan`.

## 1. Vai trò
Quản lý **khóa học** theo hạng đào tạo, **ghi danh + phân công GV–xe**, và **kế hoạch đào tạo trình Sở** — mắt xích giữa hồ sơ (student) và báo cáo tuân thủ (report).

## 2. Feature / BO / Action

| Feature | BO chính | Action |
|---|---|---|
| `course` | Khóa học | view·create·edit·**close**·export |
| `enrollment` | Ghi danh + phân công GV–xe | view·create·edit·**assign** |
| `plan` | Kế hoạch đào tạo (trình Sở) | view·create·**approve**·report |

## 3. Dữ liệu chính

### Khóa học (`course`) — *bảng đã có*
| Trường | Kiểu | Bắt buộc | Ghi chú |
|---|---|---|---|
| Mã khóa (`ma_kh`) / tên | text | ✔ | theo quy tắc mã (catalog code-rule) |
| Hạng đào tạo | ref catalog | ✔ | |
| Ngày khai giảng / bế giảng | date | ✔ | |
| Ngày đào tạo / ngày sát hạch dự kiến | date | | |
| Đào tạo nội bộ | bool | | |
| Trạng thái | enum | ✔ | mở / **đã báo Sở** / đang học / đã đóng |

### Ghi danh (`enrollment`) — *mới*
| Trường | Kiểu | Ghi chú |
|---|---|---|
| Hồ sơ (`ma_dk`) | ref student | 1 hồ sơ chỉ thuộc 1 khóa |
| Giáo viên (`ma_gv`) | ref teacher | phải khớp đăng ký với Sở |
| Xe tập (`ma_xe`) | ref vehicle | phải khớp đăng ký; xe gắn DAT |

### Kế hoạch đào tạo (`plan`) — *mới*
| Trường | Kiểu | Ghi chú |
|---|---|---|
| Khóa | ref | |
| Nội dung kế hoạch | jsonb/file | theo mẫu Sở (đọc từ report template) |
| Trạng thái | enum | nháp → trình duyệt → **đã báo Sở** (`reported_at`) |

## 4. Quy tắc nghiệp vụ
1. Khóa chỉ được **khai giảng sau khi kế hoạch đã báo Sở** — trạng thái hồ sơ `da_bao_so` phụ thuộc mốc này.
2. Phân công GV–xe phải **khớp thông tin đã đăng ký với Sở XD** (siết theo NĐ 94/2026) — lệch → cảnh báo, và phiên DAT tương ứng bị đánh dấu không hợp lệ (module dat).
3. Đóng khóa (`close`) chỉ khi mọi hồ sơ đã ra trạng thái cuối (tốt nghiệp/hủy/chuyển).
4. Sĩ số theo hạng/xe tuân giới hạn quy định (cấu hình trong catalog).

## 5. Liên kết chéo module
| Module | Khóa | Ghi chú |
|---|---|---|
| student | `ma_kh` | hồ sơ gắn khóa; enrollment đọc hồ sơ hợp lệ |
| teacher / vehicle | `ma_gv` / `ma_xe` | phân công; đối chiếu đăng ký Sở |
| dat | `ma_kh` | phiên học đối soát theo khóa |
| report | `ma_kh` | kế hoạch + DS học viên vào báo cáo |
| catalog | hạng đào tạo, quy tắc mã | |

## 6. Câu hỏi mở
- Quy tắc `ma_kh` hiện tại của Vĩnh An (13 ký tự theo legacy?) — giữ khi migrate.
- Sĩ số/giới hạn học viên trên GV/xe theo quy định mới → cấu hình catalog.

## 7. Ma trận phân quyền (→ manifest & seed role)

### 7.1 Khai báo `permissions[]` trong `manifest.json`

```json
[
  {
    "group": "main",
    "feature": "course",
    "label": "Khóa học",
    "actions": [
      "view",
      "create",
      "edit",
      "close",
      "export"
    ]
  },
  {
    "group": "main",
    "feature": "enrollment",
    "label": "Ghi danh & phân công",
    "actions": [
      "view",
      "create",
      "edit",
      "assign"
    ]
  },
  {
    "group": "main",
    "feature": "plan",
    "label": "Kế hoạch đào tạo",
    "actions": [
      "view",
      "create",
      "approve",
      "report"
    ]
  }
]
```

> ⚠️ Action đặc thù (`close`) cần bổ sung vào validator manifest — xem [plan data-scope P1](../giai-doan-1-mvp/plan-data-scope-2026-07-06.md).

### 7.2 Ma trận vai trò × feature (seed `rt_role`)

| Feature | `QTHT` | `GIAM_DOC` | `QL_DAO_TAO` | `GIAO_VU` | `TUYEN_SINH` | `GIAO_VIEN` | `HOC_VIEN` |
|---|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| `course` | R | R | W | W | R | R¹ | R² |
| `enrollment` | R | R | W | W | — | R¹ | — |
| `plan` | R | R | W·A | W | — | — | — |

> **R** xem · **W** tạo/sửa · **A** duyệt · **—** không có quyền · ¹ dữ liệu **phân công** · ² **của chính mình** · ³ **theo trạng thái** hồ sơ. Vai trò không liệt kê = không có quyền. Permission key đầy đủ: `course.main.<feature>.<action>`.
> Đặc thù: course +close · enrollment +assign (QL_DAO_TAO)

### 7.3 Quyền mức record (row-level)

**(a) Manifest — bổ sung `fields` + `scopeVars` vào các feature bị scope** *(mở rộng schema theo [plan data-scope P1](../giai-doan-1-mvp/plan-data-scope-2026-07-06.md) — manifest chỉ khai "cái gì scope được", KHÔNG chứa rule)*:

```json
[
  {
    "group": "main",
    "feature": "course",
    "fields": [
      "khoa_hoc_id",
      "hang_dao_tao",
      "ma_csdt"
    ],
    "scopeVars": [
      "current_assigned_khoa_ids",
      "current_khoa_ids"
    ]
  },
  {
    "group": "main",
    "feature": "enrollment",
    "fields": [
      "ma_gv",
      "ma_dk"
    ],
    "scopeVars": [
      "current_ma_gv"
    ]
  }
]
```

**(b) Điều kiện record theo vai trò:**

| Vai trò | Feature (action) | Điều kiện record |
|---|---|---|
| `GIAO_VIEN` | enrollment (view) | ma_gv = ctx.current_ma_gv |
| `GIAO_VIEN` | course (view) | khoa_hoc_id ∈ ctx.current_assigned_khoa_ids (khóa mình dạy) |
| `HOC_VIEN` | course (view) | khoa_hoc_id ∈ ctx.current_khoa_ids (khóa của mình) |

**(c) Seed `rt_role.grants` (kind `rule` — nhiều rule cùng resource+action = OR):**

```json
[
  {
    "kind": "rule",
    "module": "course",
    "feature": "enrollment",
    "actions": [
      "view"
    ],
    "predicate": {
      "op": "cmp",
      "field": "ma_gv",
      "cmp": "=",
      "value": {
        "ctx": "current_ma_gv"
      }
    }
  },
  {
    "kind": "rule",
    "module": "course",
    "feature": "course",
    "actions": [
      "view"
    ],
    "predicate": {
      "op": "in",
      "field": "khoa_hoc_id",
      "values": [
        {
          "ctx": "current_assigned_khoa_ids"
        }
      ]
    }
  },
  {
    "kind": "rule",
    "module": "course",
    "feature": "course",
    "actions": [
      "view"
    ],
    "predicate": {
      "op": "in",
      "field": "khoa_hoc_id",
      "values": [
        {
          "ctx": "current_khoa_ids"
        }
      ]
    }
  }
]
```

> Biến ngữ cảnh: `current_user_id` — engine có sẵn · `current_giao_vien_id`/`current_ma_gv` — module teacher tra từ user đăng nhập (ctxVars, plan P3) · `current_nguoi_lxid`/`current_ma_dk_list` — module student tra (1 người nhiều lượt đăng ký) · `current_assigned_*` — danh sách phụ trách/được phân công.
