Skip to content

1.6 · การลา (Leave)

บทนี้อธิบาย flow การลาในเชิง business — ตั้งแต่ประเภทการลา, validation ก่อนยื่น, การอนุมัติ multi-level, จนถึง cancel

flow นี้เป็น flow ที่ซับซ้อนที่สุดใน VTRC เพราะ validation หนักและ approval chain มาจาก HRMI ไม่ใช่ VTRC


ประเภทการลา — ไม่ได้ hardcode

จำนวนประเภทการลาและกฎของแต่ละประเภท ไม่ได้เก็บใน VTRC แต่ดึง live จาก HRMI

Query — fetchLeaveType / dropdownLeaveType (typeDefs/hrmi.js:4-15)

แต่ละประเภทมี field ที่กำหนดเงื่อนไขการลา (hrmi.js:436-451)

Fieldหมายถึง
leaveTypeCodeรหัสประเภท (string)
leaveTypeNameชื่อไทย เช่น "ลาป่วย"
isFullDayLeaveลาเต็มวันเท่านั้น
isCanLeaveOverลาข้ามปีได้ไหม
maxLeaveDayจำนวนวันสูงสุดต่อครั้ง
minLeaveDayจำนวนวันขั้นต่ำต่อครั้ง
earlyLeaveDayจองล่วงหน้ากี่วันขึ้นไป
backLeaveDayลาย้อนหลังได้กี่วัน
reqAttachDayลาต่อเนื่องกี่วันขึ้นไปต้องแนบใบรับรองแพทย์
isAutoApproveอนุมัติอัตโนมัติ
isCancelByReqยกเลิกได้ผ่าน request

ประเภทที่ใช้กันจริงมี ~12 ประเภท — ลาป่วย, ลากิจ, ลาพักผ่อน, ลาคลอด, ลาทหาร, ลาไปศึกษา, ลาอุปสมบท, ลาช่วยภริยาคลอด, ลาดูแลบุตร, ลาประจำปี, ลาอื่นๆ

ในโค้ด VTRC มี code เดียวที่ถูก hard-reference — "1" คือลาป่วย (hrmi.js:1169 ใน continuous-leave check)


Validation ก่อนยื่นคำขอ

ก่อน createLeaveDocument ผ่าน — VTRC ตรวจ 5 อย่าง (lib/controllers/hrmi/hrmi.js:1277-1333)

ลำดับตรวจอะไรError message ถ้าไม่ผ่าน
1maxLeaveDay${leaveTypeLabel} ไม่เกิน ${maxLeaveDay} วัน ต่อครั้ง
2minLeaveDayต้องลาอย่างน้อย ${minLeaveDay} วัน
3backLeaveDay (ย้อนหลัง)ไม่อยู่ในเงื่อนไข ย้อนหลัง ${backLeaveDay} วัน
4earlyLeaveDay (ล่วงหน้า)ไม่อยู่ในเงื่อนไข ล่วงหน้า ${earlyLeaveDay} วัน
5วันเริ่ม/สิ้นสุด sanitySTART_DATE_LEAVE_TYPE_MISMATCH ฯลฯ

Bypass rules — 2 กรณี

  1. HR HQ (0734)hrmi.js:1302 skip ทั้ง 4 validation ถ้า admin สังกัด orgUnitCode ขึ้นต้นด้วย 0734
  2. adminCanPass === true — flag นี้ส่งจาก HR admin UI เพื่อบังคับผ่าน

ทั้งสองเป็น policy ของ HR HQ — ไม่ใช่ bug


การลา Custom (รายชั่วโมง)

durationType: 'Custom' — ลาเป็นช่วงเวลาในวัน ไม่ใช่ทั้งวัน

hrmi.js:1335-1385 — ตรวจเพิ่ม

  • ดึง shift ของพนักงานผ่าน fetchCDLeaveShiftTime
  • จัดการ overnight shift (ที่ shiftTimeIn > shiftTimeOut เช่น 22:00–06:00)
  • ตรวจว่า leaveStartTime และ leaveEndTime อยู่ใน [shiftTimeIn, shiftTimeOut]
  • ถ้า leaveStartTime > leaveEndTime → reject "เวลาเริ่มต้นต้องไม่มากกว่าเวลาสิ้นสุด"

durationType enum (hrmi.js:584-589)

Codeความหมาย
H1ครึ่งวันเช้า
H2ครึ่งวันบ่าย
Fullเต็มวัน
Customรายชั่วโมง

กฎ "ลาต่อเนื่องต้องมีใบแพทย์"

hrmi.js:1164-1216findLeaveContinuos(args, empCode, reqAttachDay)

กฎ — ถ้าลาป่วย (type "1") ต่อเนื่องกัน >= reqAttachDay วัน ต้องแนบใบรับรองแพทย์

algorithm

  1. เริ่มจาก leaveStartDate
  2. เดินหลังไปวันก่อนหน้า เช็คว่ามีลาป่วยที่ PENDING หรือ APPROVE ไหม
  3. เดินหน้าไปวันถัดไป เช็คแบบเดียวกัน
  4. รวมวันต่อเนื่อง — ถ้า >= reqAttachDay และไม่มี attach → reject

Error — พบการลาต่อเนื่อง กรุณาแนบเอกสารใบรับรองแพทย์

ตรวจที่ hrmi.js:1473-1480


ตรวจวันทับซ้อน (Duplicate Leave)

durationTypefunctionError
FullvalidateDuplicateDateวันลาทับซ้อนกับเอกสารลาอื่น
CustomvalidateDuplicateTimeเวลาทับซ้อนกับเอกสารลาอื่น

ตรวจที่ hrmi.js:1458-1471


Document Number Format

hrmi.js:1424-1452 — generate docNo ใน format

LEV{YYMM}-NNNNNN

ตัวอย่าง:
  LEV2501-000123    (ม.ค. 2025, เลขที่ 123 ของเดือน)
  LEV2512-000004    (ธ.ค. 2025, เลขที่ 4 ของเดือน)
  • prefix เปลี่ยนตามเดือน (2501, 2502, ...)
  • running number zero-pad 6 หลัก
  • reset เป็น 1 เมื่อเดือนเปลี่ยน

LeaveDocument Status

models/core/leave/leave.js:58-61 — status enum ทั้งหมด 6 ค่า

Statusหมายถึงเมื่อไร
PENDINGรออนุมัติหลัง create ใหม่
MOREINFOขอข้อมูลเพิ่มapprover ตั้ง
APPROVEอนุมัติแล้วlevel สุดท้าย
REJECTไม่อนุมัติapprover ตั้ง
CANCELยกเลิกหลังอนุมัติแล้ว ขอยกเลิก
PEDDING_CANCLEรอยกเลิกพนักงานขอ cancel หลังอนุมัติ

จุดสำคัญ — มี typo PEDDING_CANCLE (ควรเป็น PENDING_CANCEL) ที่ติดมาตั้งแต่ต้นโปรเจกต์และอยู่ใน production database ทุก consumer ต้องสะกดผิดเหมือนกัน — อย่า "แก้ให้ถูก" ในไฟล์เดียวเพราะจะพัง


Approval Chain — Multi-Level

ผู้อนุมัติการลา ไม่ได้เก็บใน VTRC — ดึง live จาก HRMI

fetchApproverLeave(empCode, leaveTypeId, leaveDay) (hrmi.js:9-15) เรียก hrmiGetLeaveApproverCD (centralize/controllers/hrmi.js:44-64) → ส่ง GraphQL query getLeaveApprover ไป HRMI

ผลลัพธ์ — list ของ [ApproverMedical] (ชื่อ type ซ้ำกับ hospital แต่คนละ context)

graphql
type ApproverMedical {
  users: [User]            # ผู้อนุมัติใน level นี้
  level: Int               # ลำดับขั้น (1, 2, 3...)
  canEdit: Boolean         # แก้คำขอแทนได้ไหม
  approveType: String      # ประเภทการอนุมัติ
  approveLabel: String     # label ไทย เช่น "ผู้บังคับบัญชาระดับ..."
}

chain อาจมีหลาย level — แต่ละ level ต้อง approve ก่อนส่ง level ถัดไป

ถ้า HRMI ล่ม — VTRC ไม่รู้ว่าใครเป็นผู้อนุมัติ → การลาทั้งระบบไม่สามารถอนุมัติได้


เส้นทางของ Leave Document

พนักงาน create


status = PENDING (level 1)

    ├── ผู้อนุมัติ level 1 → approve → ส่งต่อ level 2
    │       │
    │       ▼
    │   ผู้อนุมัติ level 2 → approve
    │       │
    │       ▼ (ถ้าเป็น level สุดท้าย)
    │   status = APPROVE

    ├── ผู้อนุมัติ → reject
    │       │
    │       ▼
    │   status = REJECT

    └── ผู้อนุมัติ → moreinfo


        status = MOREINFO → พนักงานแก้ → resubmit → PENDING

หลัง APPROVE:
  - พนักงานขอ cancel → status = PEDDING_CANCLE
  - ผู้อนุมัติ approve cancel → status = CANCEL

Cancel flow — แยกจาก reject

checkRemoveLeaveDocumentAsFlow (hrmi.js:140) — ฟังก์ชันที่แยก "ลบ draft" กับ "ขอ cancel หลังอนุมัติ"

  • ถ้าเอกสารยัง PENDING → ลบได้ทันที (removeLeaveDocumentDraf)
  • ถ้า APPROVE แล้ว → ต้องผ่าน cancel request (PEDDING_CANCLE) แล้วรอ approver approve cancel

Field สำคัญใน LeaveDocument

models/core/leave/leave.js:1-164 — field ที่ใช้บ่อย

Fieldหน้าที่
wdLeaveId (UUID)PK
leaveType / leaveTypeLabelรหัสและชื่อประเภท
leaveStartDate / leaveEndDateวันที่เริ่ม/สิ้นสุด
leaveDayจำนวนวันรวม
durationTypeFull / H1 / H2 / Custom
empCodeรหัสพนักงาน
createLeaveByEmpCode ของคนสร้าง (อาจเป็น admin แทน)
docIdเลขที่เอกสาร (LEV{YYMM}-NNNNNN)
statusenum ด้านบน
levellevel ปัจจุบันใน approval chain
noteApproveหมายเหตุของผู้อนุมัติ
clientTypeWEB_CLIENT (พนักงาน) / WEB_ADMIN (HR แทน)
orgCode / orgNameหน่วยงานตอนสร้างเอกสาร
sourceDBHRMI replica ที่ดึงข้อมูล
leaveTotalUse / leaveUsed / leaveDayAllวันลาคงเหลือ/ใช้ไป/ทั้งหมด

clientType สำคัญ — ใช้แยกว่าเอกสารนี้ "พนักงานยื่นเอง" หรือ "HR ยื่นแทน" เพราะ validation ต่างกัน (HR สามารถ bypass ได้)


Attachment — ไฟล์แนบ

hrmi.js:138-237 — mutations สำหรับ attach file

Mutationหน้าที่
createAttachmentFileอัปโหลดไฟล์แนบ
updateAttachmentFileแก้ไข
deleteAttachmentFileลบ

fetchAttachmentListFile และ fetchAttachmentDetailFile ใช้อ่าน

ใช้สำหรับ

  • ใบรับรองแพทย์ (เมื่อลาป่วยต่อเนื่อง)
  • เอกสารประกอบลาคลอด (ใบรับรองแพทย์เกี่ยวกับการตั้งครรภ์)
  • หนังสือลางาน (ในกรณีเฉพาะ)

History — ประวัติการเปลี่ยนแปลง

fetchLeaveDocumentHistory (hrmi.js) — คืน action trail ของแต่ละเอกสาร

แต่ละ row บอก

  • ใคร (empCode)
  • ทำอะไร (approve / reject / moreinfo / edit)
  • เมื่อไร (timestamp)
  • note อะไร

ใช้สำหรับ audit + debug "ทำไมสถานะเป็นแบบนี้"


สรุป — checklist ตอน debug

อาการสาเหตุที่เป็นไปได้ที่ตรวจ
"ลาไม่ผ่าน" + error max dayเกิน maxLeaveDay (เว้นแต่ 0734 / adminCanPass)hrmi.js:1304
"ลาไม่ผ่าน" + error ล่วงหน้าไม่จองล่วงหน้าพอhrmi.js:1319, 1323
"ลาไม่ผ่าน" + error ย้อนหลังเลย backLeaveDayhrmi.js:1317, 1321
"ลาไม่ผ่าน" + error ทับซ้อนมีเอกสารเดิมวันทับซ้อนhrmi.js:1458-1471
"ลาไม่ผ่าน" + error ใบแพทย์ลาป่วยต่อเนื่อง >= reqAttachDay วันhrmi.js:1473-1480
อนุมัติไม่ได้HRMI ล่ม → ดึง approver chain ไม่ได้centralize/controllers/hrmi.js:44-64
Cancel ไม่ผ่านสถานะไม่ใช่ APPROVEhrmi.js:140
status ภาษาไทยดูแปลกtypo PEDDING_CANCLEmodels/core/leave/leave.js:59

ขั้นตอนถัดไป

ไป บท 1.7 สวัสดิการรักษาพยาบาล (WDHospital) →