1.4 · การจ่ายเงินเดือนและสลิป
บทนี้อธิบาย flow ที่พนักงาน "ดูสลิปเงินเดือน" และ "ดาวน์โหลด PDF" — ฟีเจอร์ที่ใช้บ่อยที่สุดในระบบ
VTRC ไม่ได้คำนวณเงินเดือน — payroll ทำในระบบ HRMI ต้นน้ำ VTRC แค่อ่านมาแสดงและ render PDF ให้ดาวน์โหลด
แผนภาพ flow ดูสลิป
พนักงานเปิดหน้าสลิป → เลือกเดือน
│
▼
vtrc-web ส่ง GraphQL: fetchSlip(month, year)
│
▼
vtrc-api @auth(USER) → verify JWT
│
▼
centralize bridge → slip controller
(GraphqlCDWithToken → END_POINT_CENTRALIZE)
│
▼
cu-central-api (GraphQL) → MSSQL / HRMI views
│
▼
ส่งข้อมูลกลับ vtrc-api (ไม่มี Redis บน path นี้)
│
▼
ส่ง Slip { paymentDate, income[], expense[], baseTax406 }
│
▼
vtrc-web render ตารางรายได้/รายจ่ายหมายเหตุ — live path คือ vtrc-api → cu-central-api ผ่าน END_POINT_CENTRALIZE + API_CENTRALIZE_KEY (centralize/controllers/slip.js, centralize/controllers/graphql.js) — ไม่ใช่ NestJS centralize-api และ ไม่มี Redis บน active payslip path
Source หลัก — vtrc-api/api/src/typeDefs/slip.js:1-129
ประเภทเอกสารเงินเดือน
Slip ในระบบไม่ใช่แค่ "สลิปเดือนนี้" แต่มีหลายประเภท (lib/controllers/slip/slip.js:52-77)
| Code | ชื่อไทย | ใช้ตอนไหน |
|---|---|---|
0 | สลิปเงินเดือน | ประจำเดือน |
1 | ภ.ง.ด. 1ก (50 ทวิ) | ภาษีรายเดือน |
2 | ค่าประกอบวิชาชีพอิสระ 40(6) ประจำเดือน | กรณีเป็น 40(6) |
3 | ค่าประกอบวิชาชีพอิสระ 40(6) ม.ค.–มิ.ย. | ภาษี 6 เดือนแรก |
4 | ค่าประกอบวิชาชีพอิสระ 40(6) ก.ค.–ธ.ค. | ภาษี 6 เดือนหลัง |
5 | หนังสือรับรองเงินเดือน | ยืนยันรายได้ (เช่น ขอวีซ่า) |
typeSlip parameter ใน fetchSlip รับ code จาก list นี้ — frontend แสดง dropdown ให้เลือก
Slip object — โครงสร้างข้อมูล
type Slip {
paymentDate: String # วันที่จ่ายจริง (จาก HRMI)
income: [SlipLine] # รายการรายได้ (เงินเดือน, โอที, โบนัส)
expense: [SlipLine] # รายการรายจ่าย (ภาษี, ประกันสังคม, กองทุน)
baseTax406: Boolean # true = ถูกประเมินภาษี 40(6)
}
type SlipLine {
incomeCode: String
incomeName: String
income: Float # จำนวนเงิน (ใช้สำหรับทั้งรายได้และรายจ่าย)
sourceDB: String # ระบุ HRMI replica (RC หรือ CU)
}sourceDB เป็นกุญแจสำคัญ — slip ของพนักงาน CU จะมี sourceDB = 'dbHRMI_CU_SC_rep' ส่วน RC จะมีค่า RC variant
PDF Generation — flow ที่ต้องระวัง
เมื่อพนักงานกดดาวน์โหลด PDF เป็น flow คนละแบบ — ใช้ REST endpoint ไม่ใช่ GraphQL
1. vtrc-web เรียก fetchUrlSlip(month, year, profileKey)
→ ได้ URL ที่มี hash (valid 1 ชั่วโมง)
2. vtrc-web เปิด URL → ไป REST endpoint ใน vtrc-api
3. vtrc-api (routes.js):
- อ่าน hash → หา slip data
- render EJS template เป็น HTML
- html-pdf (PhantomJS) → PDF ต้นฉบับ
- qpdf --encrypt <password> ใส่ password ให้ PDF
- ส่ง PDF กลับ browser
4. หลัง 5 วินาที vtrc-api ลบไฟล์ทิ้ง (cleanup)password ของ PDF คือวันเดือนปีเกิด ในรูปแบบ DDMMYYYY ปี พศ.
ตัวอย่างเช่น เกิด 1 ม.ค. 2532 → password คือ 01012532
Source — lib/controllers/slip/slip.js:40-45 (function howToOpenPayrollSlip คืนคำอธิบายนี้ให้ frontend แสดง)
ความเสี่ยงด้าน security ของ PDF flow
execSync(qpdf --encrypt ${password} ...) ใน routes.js เป็น command injection vector — ถ้า password (ที่มาจาก HRMI) มี shell metacharacter จะรัน command อะไรก็ได้ใน container
รายละเอียดและวิธีแก้ — ดู Volume 15 · Technical Debt SEC-1
ในทางปฏิบัติ password เป็นวันเดือนปีเกิดที่ HRMI validate แล้ว แต่ code ยังไม่ defensive — ถ้าแก้ระบบนี้ต้องระวัง
หนังสือรับรองเงินเดือน (Salary Certificate)
พนักงานขอหนังสือรับรองเงินเดือนได้ผ่าน fetchSalaryCert / fetchUrlSalaryCert — slip.js:13-20, 50-55
ระบบกำหนดประเภทหนังสือรับรองตาม 3 ปัจจัย
| Factor | Field | ผลกระทบ |
|---|---|---|
| หน่วยงาน | orgCode prefix | เลือก variant ของ template |
| กลุ่มพนักงาน | emplGrupCode | เปลี่ยน label (เช่น 04 บน dbHRMI_RC_B_rep → "แสดงเฉพาะเงินบำนาญ") |
| ประเภทหนังสือ | docType 0..3 | เลือก format ของใบรับรอง |
Source — lib/controllers/slip/slip.js:159-310 (dropDownTypeSalaryCert)
map เก็บในตาราง MapGroupDocument — key ด้วย (orgCode prefix, emplGrupCode, docType)
กรณีพิเศษ — พนักงานจุฬา (CU SC profile)
พนักงาน SC profile (profileKey = dbHRMI_CU_SC_rep) ถูกบังคับ orgUnitCode = '02' (slip.js:175-177) — เพราะ CU treat สาขานี้แยกใน HRMI
ผลคือ slip ของพนักงานกลุ่มนี้ใช้ template CU variant ไม่ใช่ RC variant แม้จะเข้าผ่านระบบเดียวกัน
วันจ่ายเงิน (paymentDate)
fetchSlipPayDate(month, year) — slip.js:59-64 — คืนวันที่จ่ายจริงของเดือนนั้น
ใช้ตรวจว่า slip ของเดือนนี้ "มีแล้วหรือยัง" — frontend อาจใช้เพื่อ disable เดือนที่ยังไม่ปิดบัญชี
ถ้า HRMI ยังไม่ mark วันจ่าย → พนักงานเห็น "ยังไม่พบข้อมูล" แม้จะถึงเดือนนั้นแล้ว
คำถามที่ HR ถามบ่อย
| คำถาม | คำตอบจากมุม VTRC |
|---|---|
| "สลิปเดือนนี้หาย" | ดูก่อนว่า paymentDate ตั้งใน HRMI หรือยัง ถ้ายัง → ปัญหาที่ HRMI |
| "สลิป CU ไม่ตรงกับ RC" | คนละ sourceDB — slip เดือนเดียวกันอาจมี 2 record |
| "เปิด PDF ไม่ได้" | ลอง DDMMYYYY ปี พศ. ก่อน ถ้าไม่ได้ → reset password ผ่าน HRMI |
| "เปลี่ยนเลขบัญชีในสลิป" | VTRC แก้ไม่ได้ ต้องแก้ที่ HRMI |
| "สลิปหลังลาออก" | HRMI ยังเก็บ record — VTRC ดึงได้ ถ้า session ยัง active |
ข้อควรระวังตอน debug
- live slip path ไม่มี Redis cache — ถ้าข้อมูลไม่ตรง ให้ตรวจ HRMI / cu-central-api ก่อน (อย่าไล่ Redis key)
- slip ที่ render ผ่าน EJS → html-pdf อาจมี font Thai พัง ถ้า container ลง font ไม่ครบ
- ถ้าแก้ template EJS ต้อง rebuild Docker image (ไม่ใช่ hot-reload เพราะอยู่ใน
routes.js)