Built-in module · WS runtime · 08/7/2026

hrm — Nhân sự & lương (built-in — thay hr + payroll) BUILT-IN

Nhân sự + lương trọn gói: hồ sơ nhân viên, hợp đồng, chấm công, nghỉ phép, bảng lương (tùy chọn: tuyển dụng, KPI, đánh giá). Đặc thù trường lái: GV ↔ Employee 1-1timesheet — lương GV từ giờ dạy thực tế (DAT + LMS), không khai tay.

🎯 Thay 2 module kế hoạch v1.5 (`hr` + `payroll`). Hiện trạng: 18 model đầy đủ nhưng Payroll tổng quát (chưa có nguồn giờ DAT), chưa nối teacher. Đích = hrm + 2 phần đặc thù, KHÔNG scaffold mới.
✏️ Cập nhật 08/7 (chiều): chuẩn hóa quy ước đặt permission key + bảng rename key hiện có → key đích (mục 2). Doc là source of truth — code rename theo bảng.
1

Feature / BO / Action

FeatureBO chínhAction
personnel.employeeNhân viên (1 phòng + 1 chức danh + user)viewcreateeditimportexport
personnel.contractHợp đồng lao động + hạnviewcreateeditconfigure
time.attendanceChấm công (văn phòng + nguồn ngoài)viewimporteditexportcheckin
time.leaveNghỉ phép (quota + đơn)viewcreateeditapprove
payroll.timesheetBảng công GV từ giờ dạy thực tế ★đặc thù (nguồn: DAT, LMS)viewexportgenerate
payroll.salaryBảng lương (công thức giờ/khoán)viewexportgenerateapprovelock
recruitment.* · training.* · performance.* · analytics.*Tuyển dụng · Đào tạo nội bộ · KPI/đánh giá · Dashboard (tùy chọn — ẩn qua config per-WS, key giữ trong manifest)như manifest mục 2
2

Manifest — khai báo permissions

Permission key theo convention hrm.<group>.<feature>.<action>. fields/scopeVars theo plan data-scope (manifest v1.1). Action ngoài core-12 (checkin, generate, lock) bắt buộc khai customActions.

▸ 2.1 Quy ước đặt key (chuẩn hóa — áp toàn nền tảng)

Key là định danh máy (như AWS service:Action, K8s verb×resource); label là tên hiển thị.

#Quy tắcVí dụ vi phạm → sửa
K1Key bất biến sau phát hành; label đổi tự do. Muốn đổi key → qua cơ chế rename có khai báo (manifest renames + grant-rewrite + bump epoch khi upgrade), không sửa tay rời rạc
K2Không lặp ngữ cảnh cha: feature không chứa lại tên module/groupemployment-contractcontract · training-programprogram
K3Không để group.feature trùng tên — dấu hiệu nhóm sai hoặc tên quá chungattendance.attendance, leave.leave → group time · payroll.payrollpayroll.salary
K4Key không mã hóa nguồn dữ liệu/công nghệ — nguồn là chi tiết của action generatetimesheet-dattimesheet (nguồn DAT LMS ngay trong thiết kế)
K5Danh từ số ít, kebab-case chỉ khi cần phân biệt/ghép nghĩajob-position giữ (tránh nhầm org.position — chức danh)
K6Group = section nghiệp vụ ổn định chuẩn HRIS: personnel · time · payroll · recruitment · training · performance · analyticsgộp group attendance + leavetime (Time & Attendance)

▸ 2.2 Bảng rename — key hiện có (code hrm v1.0.0) → key đích (v2.0.0)

Tham chiếu khi rename code. Grant kind:'module' (owner) không bị ảnh hưởng; grant theo feature/action phải rewrite.

Key hiện tạiKey đíchQuy tắcAction thay đổi kèm theo
personnel.employee(giữ)bỏ delete (nghỉ việc = đổi status — giữ audit); giữ import (kit migrate WS3)
personnel.employment-contractpersonnel.contractK2bỏ delete; approve → bỏ (trạng thái đủ); thêm configure (nhắc hạn)
attendance.attendancetime.attendanceK3·K6create → checkin/import; bỏ delete (sửa bằng edit có audit); bỏ report/approve
leave.leavetime.leaveK3·K6bỏ delete (hủy đơn = trạng thái); giữ edit
payroll.payrollpayroll.salaryK3bỏ create/edit/delete tay → generate từ công thức; thêm lock
— (mới)payroll.timesheetK4view·generate·export
recruitment.job-position · recruitment.candidate(giữ)K5
training.training-program / training-sessiontraining.program / training.sessionK2— (training.enrollment giữ)
performance.kpi-okr · performance.appraisal · analytics.dashboard(giữ)
🔎 Full code cũ → mới (chuỗi đang hardcode ở FE portal/AppGrid):
hrm.personnel.employment-contract.view → hrm.personnel.contract.view
hrm.attendance.attendance.view → hrm.time.attendance.view · hrm.leave.leave.view → hrm.time.leave.view
hrm.payroll.payroll.view → hrm.payroll.salary.view · hrm.training.training-program.view → hrm.training.program.view
🛠 Checklist rename code (thứ tự): ① manifest hrm (key mới + customActions + version 2.0.0) → ② BE enforce: grep code cũ trong routes/controllers → ③ grant-rewrite rt_role (JSONB) từng DB ws + bump epoch (làm thành cơ chế renames chạy lúc upgrade, không SQL tay) → ④ CP catalog + rbacExportService rà lại → ⑤ FE AppGrid/permPrefix (dài hạn: constants sinh từ manifest — rename vỡ lúc compile, không vỡ âm thầm) → ⑥ docs khác có ghi code cũ (plan data-scope, WS0).

▸ 2.3 Manifest đích (v2.0.0 — JSON hợp lệ)

⚠️ Điều kiện schema: manifest-schema hiện tại đã có customActions nhưng chưa có fields/scopeVars (chờ manifest v1.1 — plan data-scope) và renames (cơ chế K1). Thứ tự: nâng schema trước → npm run catalog:validate pass rồi mới code.
{ "key": "hrm", "name": "Nhân sự & Lương", "version": "2.0.0", "customActions": [ { "key": "generate", "label": "Sinh bảng", "desc": "Sinh bảng công/bảng lương từ dữ liệu nguồn (DAT/LMS) — không nhập tay." }, { "key": "checkin", "label": "Điểm danh", "desc": "Ghi nhận chấm công tại chỗ — sinh bản ghi từ sự kiện thiết bị/thao tác nhanh, khác create." }, { "key": "lock", "label": "Khóa sổ", "desc": "Khóa kỳ lương đã duyệt — bất biến; sai thì điều chỉnh kỳ sau." } ], "permissions": [ { "group": "personnel", "feature": "employee", "label": "Nhân viên", "actions": ["view", "create", "edit", "import", "export"], "fields": ["phong_ban_id", "chuc_danh_id", "user_id"], "scopeVars": ["current_employee_id", "current_dept_id"] }, { "group": "personnel", "feature": "contract", "label": "Hợp đồng lao động", "actions": ["view", "create", "edit", "configure"] }, { "group": "time", "feature": "attendance", "label": "Chấm công", "actions": ["view", "import", "edit", "export", "checkin"] }, { "group": "time", "feature": "leave", "label": "Nghỉ phép", "actions": ["view", "create", "edit", "approve"] }, { "group": "payroll", "feature": "timesheet", "label": "Bảng công GV (giờ DAT/LMS)", "actions": ["view", "generate", "export"], "fields": ["ma_gv", "ky"], "scopeVars": ["current_giao_vien_id"] }, { "group": "payroll", "feature": "salary", "label": "Bảng lương", "actions": ["view", "generate", "approve", "lock", "export"], "fields": ["ma_nv", "ky"], "scopeVars": ["current_employee_id"] }, { "group": "recruitment", "feature": "job-position", "label": "Vị trí tuyển dụng", "actions": ["view", "create", "edit", "delete"] }, { "group": "recruitment", "feature": "candidate", "label": "Ứng viên", "actions": ["view", "create", "edit", "delete"] }, { "group": "training", "feature": "program", "label": "Chương trình đào tạo", "actions": ["view", "create", "edit", "delete"] }, { "group": "training", "feature": "session", "label": "Lớp/buổi đào tạo", "actions": ["view", "create", "edit", "delete"] }, { "group": "training", "feature": "enrollment", "label": "Ghi danh đào tạo", "actions": ["view", "create", "edit", "delete"] }, { "group": "performance", "feature": "kpi-okr", "label": "KPI / OKR", "actions": ["view", "create", "edit", "delete", "report"] }, { "group": "performance", "feature": "appraisal", "label": "Đánh giá", "actions": ["view", "create", "edit", "delete", "approve", "report"] }, { "group": "analytics", "feature": "dashboard", "label": "Dashboard nhân sự", "actions": ["view", "export"] } ] }

recruitment.* / training.* / performance.* / analytics.* = tùy chọn: ẩn/hiện qua config per-WS, không xoá key khỏi manifest — training tái dùng cho tập huấn định kỳ GV.

3

Dữ liệu chính

▸ Nhân viên (khớp WS0 §4.3)

TrườngKiểuBắt buộcGhi chú
Mã nhân viêntextGV: đồng bộ với ma_gv (teacher)
Họ têntext
CCCDtextĐối chiếu hồ sơ GV với Sở
SĐT / emailtext✔/—
Phòng banref orgMột phòng chính
Chức danhref orgMột chức danh chính
Tài khoản đăng nhậpref iamuser_id — NV có đăng nhập
Ngày vào làmdate
Trạng tháibool

▸ Hợp đồng

TrườngKiểuBắt buộcGhi chú
Nhân viênref
Loại hợp đồngenumThử việc / xác định / không xác định thời hạn
Ngày hiệu lực / hết hạndateHết hạn → notify 30/15/7 ngày
File scan / phụ lụcfile

▸ Bảng công GV (payroll.timesheet) ★

TrườngKiểuBắt buộcGhi chú
KỳtextTháng hoặc theo khóa (config)
Giáo viênref (ma_gv)
Σ giờ DAT hợp lệnumber (generate)Từ phiên hop_le theo ma_gv — không nhập tay
Giờ lý thuyếtnumberTừ lms (v1.5)
Điều chỉnhnumber + lý do + người duyệtBắt buộc lý do — audit

▸ Bảng lương (payroll.salary)

TrườngKiểuBắt buộcGhi chú
Kỳ × nhân viênref
Đơn giá giờ / khoán khóamoneyCông thức config theo trung tâm
Phụ cấp / khấu trừmoneyModel hiện có sẵn BHXH/BHYT/BHTN/PIT — giữ (NV văn phòng lương đầy đủ)
Thực lãnhmoney
Trạng tháienumnháp → duyệt → đã khóa (không sửa)
4

Ma trận quyền — vai trò × feature

Vai trò theo WS0. R xem · W tạo/sửa · A duyệt · — không có quyền.

FeatureQTHTGIAM_DOCQL_DAO_TAOTUYEN_SINHGIAO_VUGIAO_VIENKE_TOANNHAN_SU
personnel.employeeRRRRW
personnel.contractRRW
time.attendanceRRW
payroll.timesheetRRRWR
time.leaveRAA
payroll.salaryRRRW·AR

² chỉ của chính mình · W·A = tạo/sửa + duyệt

5

Quy tắc nghiệp vụ & liên kết

Quy tắc

  • GV = Employee: teacher.ma_gv ↔ mã NV (1-1); teacher chỉ giữ hồ sơ nghiệp vụ dạy — không trùng dữ liệu
  • Nguồn giờ GV duy nhất = phiên DAT hợp lệ + buổi lms; điều chỉnh tay phải có lý do + duyệt
  • Kỳ lương đã lock không sửa — sai thì điều chỉnh kỳ sau
  • Hết hạn hợp đồng/chứng chỉ tập huấn → notify. Ranh giới: hrm.personnel.contract = HĐLĐ + giấy tờ lao động; teacher.cert = chứng chỉ nghiệp vụ dạy (đăng ký Sở) — không nhắc trùng
  • Data scope: NV/GV xem hồ sơ + lương của chính mình; quản lý theo phòng (org). ⚠️ Cần thêm cột user_id + cccd vào model Employee hiện có — điều kiện của scopeVar current_employee_id
  • payroll.timesheet phụ thuộc module dat (.ogapp cài/gỡ được): mount check view dat_v_phien_hop_le — vắng thì ẩn feature (degrade), không crash

Liên kết chéo

  • teacher — 1-1 Employee (mã NV)
  • dat — nguồn giờ timesheet (degrade khi vắng)
  • lms — giờ dạy lý thuyết
  • org — phòng/chức danh
  • iam — user đăng nhập
  • notify — nhắc hạn HĐ
  • finance — chi lương vào sổ

⚠ Câu hỏi mở

  • Công thức lương GV Vĩnh An (giờ/khoán/hỗn hợp)? Kỳ theo tháng hay khóa?
  • Bật recruitment/training/KPI/dashboard hay ẩn (config theo WS)?
  • Quy tắc mã NV hiện dùng (đồng bộ ma_gv)?
  • Nguồn chấm công văn phòng: máy chấm công riêng hay dùng chung face terminal LMS? (quyết time.attendance.import nối với gì)