# Module `student` — Học viên & hồ sơ

> 07/7/2026 · Giai đoạn: **MVP** · Bản HTML: [student.html](student.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 học viên (port legacy) — cần thêm feature `hoso`, `giayto`, `lichsu`.
> Chi tiết quy trình: [WS1 quy trình hồ sơ](../giai-doan-1-mvp/WS1-quy-trinh-ho-so-2026-07-06.md) · Schema: [§1, §2](../giai-doan-1-mvp/schema-du-lieu-2026-07-06.md)

## 1. Vai trò
Module trung tâm của MVP — quản lý **học viên** và **vòng đời hồ sơ theo khóa** (tiếp nhận → báo Sở → đang học). Đây là màn hình đầu tiên được dựng.

## 2. Feature / BO / Action

| Feature | BO chính | Action |
|---|---|---|
| `student` | Học viên (danh bạ người) | view·create·edit·delete·export |
| `hoso` | Hồ sơ ghi danh theo khóa (state machine) | view·create·edit·**approve**·**transfer** |
| `giayto` | Giấy tờ hồ sơ (scan, hạn) | view·create(upload)·**verify**·delete |
| `lichsu` | Lịch sử trạng thái (audit) | view·export |

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

### Học viên (`student`) — *1 dòng = 1 lượt đăng ký theo khóa (theo legacy); định danh người qua CCCD*
Trường chính: mã đăng ký (`ma_dk`), họ tên, ngày sinh, giới tính, CCCD + ngày cấp, SĐT, nơi thường trú/cư trú, hạng đào tạo, hạng GPLX, khóa (`ma_kh`), giáo viên, xe tập, thẻ RFID, ảnh, cờ đào tạo/kết quả lý thuyết.
UNIQUE `(ma_dk, ma_kh, ma_csdt)`.

### Hồ sơ (`hoso`) — *cột bổ sung trên bảng học viên*
| Trường | Kiểu | Ghi chú |
|---|---|---|
| Trạng thái hồ sơ | enum | `tu_van → tiep_nhan ⇄ cho_bo_sung → hop_le → da_thu_dot1 → xep_khoa → da_bao_so → dang_hoc` + `huy`/`chuyen_khoa` |
| Loại hồ sơ | enum | mới / nâng hạng — quyết định checklist |
| Phòng phụ trách | ref | khi dùng bàn giao liên phòng |
| Học phí thỏa thuận | money | nền cho công nợ đợt (tuition) |

### Giấy tờ (`giayto`)
| Trường | Kiểu | Ghi chú |
|---|---|---|
| Loại giấy tờ | ref catalog | checklist động theo hạng + loại hồ sơ |
| File scan | file | |
| Trạng thái | enum | đã nộp / đã duyệt / thiếu |
| Ngày hết hạn | date | giấy khám SK |
| Người duyệt / thời gian | ref/dt | |

### Lịch sử (`lichsu`) — *append-only, thay audit chung ở MVP*
Trạng thái cũ → mới, phòng từ → đến, người thao tác, lý do, thời gian.

## 4. Quy tắc nghiệp vụ
1. **Chống trùng CCCD** trong cùng khóa; đủ tuổi theo hạng (đọc catalog).
2. Thiếu giấy tờ theo checklist → tự chuyển `cho_bo_sung`, cảnh báo đúng mục thiếu.
3. Mỗi lần đổi trạng thái/bàn giao **bắt buộc ghi lịch sử** (căn cứ pháp lý, không sửa xóa).
4. `da_thu_dot1` do tuition xác nhận; `da_bao_so` do report xác nhận — student không tự đổi 2 trạng thái này.
5. Data scope theo vai trò: tuyển sinh thấy khâu tiếp nhận, kế toán từ `hop_le` trở đi, GV chỉ học viên phụ trách, học viên chỉ chính mình (xem [plan data-scope](../giai-doan-1-mvp/plan-data-scope-2026-07-06.md) §3b).

## 5. Liên kết chéo module (khóa nghiệp vụ)
| Module | Khóa | Chiều |
|---|---|---|
| course | `ma_kh` | hồ sơ thuộc khóa; xếp lớp/phân công ở course |
| teacher / vehicle | `ma_gv` / `ma_xe` | phân công GV–xe |
| catalog | mã hạng, mã giấy tờ | validate + checklist |
| tuition | `(ma_dk, ma_kh, ma_csdt)` | công nợ đợt → callback trạng thái `da_thu_dot1` |
| dat | `ma_dk` | phiên học + đối soát km/giờ |
| report | `ma_kh` | danh sách học viên vào báo cáo Sở |
| rfcard | `rfid` | thẻ điểm danh |

## 6. Câu hỏi mở
- Migrate: sinh `trang_thai_ho_so` từ cờ legacy (`is_dao_tao`, kết quả lý thuyết) — cần bảng ánh xạ khi có DB V2.
- Có dùng bàn giao liên phòng không (chờ danh sách phòng ban Vĩnh An)?

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

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

```json
[
  {
    "group": "main",
    "feature": "student",
    "label": "Học viên",
    "actions": [
      "view",
      "create",
      "edit",
      "delete",
      "export"
    ]
  },
  {
    "group": "main",
    "feature": "hoso",
    "label": "Hồ sơ ghi danh",
    "actions": [
      "view",
      "create",
      "edit",
      "approve",
      "transfer"
    ]
  },
  {
    "group": "main",
    "feature": "giayto",
    "label": "Giấy tờ hồ sơ",
    "actions": [
      "view",
      "create",
      "verify",
      "delete"
    ]
  },
  {
    "group": "main",
    "feature": "lichsu",
    "label": "Lịch sử trạng thái",
    "actions": [
      "view",
      "export"
    ]
  }
]
```

> ⚠️ Action đặc thù (`transfer`, `verify`) 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` | `TUYEN_SINH` | `GIAO_VU` | `GIAO_VIEN` | `KE_TOAN` | `HOC_VIEN` |
|---|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| `student` | R | R | W | W | W | R¹ | R³ | R² |
| `hoso` | R | R | W·A | W³ | W | R¹ | R³ | R² |
| `giayto` | — | — | R·A | W·A | W·A | — | — | R² |
| `lichsu` | R | R | R | — | R | — | — | — |

> **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 đủ: `student.main.<feature>.<action>`.
> Đặc thù: hoso +transfer (QL_DAO_TAO, TUYEN_SINH, GIAO_VU) · hủy hồ sơ = bước workflow (không phải action quyền) · A ở giayto = verify (duyệt giấy tờ)

### 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": "student",
    "fields": [
      "giao_vien_id",
      "nguoi_lxid",
      "trang_thai_ho_so",
      "ma_csdt"
    ],
    "scopeVars": [
      "current_giao_vien_id",
      "current_nguoi_lxid"
    ]
  },
  {
    "group": "main",
    "feature": "hoso",
    "fields": [
      "trang_thai_ho_so",
      "giao_vien_id",
      "nguoi_lxid",
      "khoa_hoc_id",
      "ma_csdt"
    ],
    "scopeVars": [
      "current_giao_vien_id",
      "current_nguoi_lxid"
    ]
  },
  {
    "group": "main",
    "feature": "giayto",
    "fields": [
      "nguoi_lxid"
    ],
    "scopeVars": [
      "current_nguoi_lxid"
    ]
  }
]
```

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

| Vai trò | Feature (action) | Điều kiện record |
|---|---|---|
| `TUYEN_SINH` | hoso (view, edit) | trạng thái ∈ {tu_van, tiep_nhan, cho_bo_sung, hop_le} |
| `KE_TOAN` | student, hoso (view) | trạng thái ∈ {hop_le, da_thu_dot1, xep_khoa, da_bao_so, dang_hoc} |
| `GIAO_VIEN` | student, hoso (view) | trang_thai_ho_so = 'dang_hoc' **và** giao_vien_id = ctx.current_giao_vien_id |
| `HOC_VIEN` | student, hoso, giayto (view) | nguoi_lxid = ctx.current_nguoi_lxid |

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

```json
[
  {
    "kind": "rule",
    "module": "student",
    "feature": "hoso",
    "actions": [
      "view",
      "edit"
    ],
    "predicate": {
      "op": "in",
      "field": "trang_thai_ho_so",
      "values": [
        {
          "lit": "tu_van"
        },
        {
          "lit": "tiep_nhan"
        },
        {
          "lit": "cho_bo_sung"
        },
        {
          "lit": "hop_le"
        }
      ]
    }
  },
  {
    "kind": "rule",
    "module": "student",
    "feature": "hoso",
    "actions": [
      "view"
    ],
    "predicate": {
      "op": "in",
      "field": "trang_thai_ho_so",
      "values": [
        {
          "lit": "hop_le"
        },
        {
          "lit": "da_thu_dot1"
        },
        {
          "lit": "xep_khoa"
        },
        {
          "lit": "da_bao_so"
        },
        {
          "lit": "dang_hoc"
        }
      ]
    }
  },
  {
    "kind": "rule",
    "module": "student",
    "feature": "hoso",
    "actions": [
      "view"
    ],
    "predicate": {
      "op": "and",
      "of": [
        {
          "op": "cmp",
          "field": "trang_thai_ho_so",
          "cmp": "=",
          "value": {
            "lit": "dang_hoc"
          }
        },
        {
          "op": "cmp",
          "field": "giao_vien_id",
          "cmp": "=",
          "value": {
            "ctx": "current_giao_vien_id"
          }
        }
      ]
    }
  },
  {
    "kind": "rule",
    "module": "student",
    "feature": "hoso",
    "actions": [
      "view"
    ],
    "predicate": {
      "op": "cmp",
      "field": "nguoi_lxid",
      "cmp": "=",
      "value": {
        "ctx": "current_nguoi_lxid"
      }
    }
  }
]
```

> 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.
> Rule trên viết cho feature `hoso` — lặp tương tự cho `student`/`giayto`. QL_DAO_TAO/GIAO_VU/GIAM_DOC/QTHT không rule = thấy toàn bộ.
