Skip to content

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-apicu-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 — โครงสร้างข้อมูล

graphql
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 / fetchUrlSalaryCertslip.js:13-20, 50-55

ระบบกำหนดประเภทหนังสือรับรองตาม 3 ปัจจัย

FactorFieldผลกระทบ
หน่วยงาน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)

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

ไป บท 1.5 ภาษีเงินได้ →