Skip to content

12.11 · Scorecard + known bugs + magic numbers

บทสุดท้ายของ Volume 12 รวมภาพใหญ่ — scorecard ของแต่ละ domain, bug ที่พบ, magic numbers ที่ต้องระวัง, dead code, และแนวทางปรับปรุง


1. Domain scorecard

ให้คะแนนแต่ละ domain ตาม 6 ด้าน (5 = ดีมาก, 1 = แย่)

DomainLogic clarityTestabilityCoupling to CDValidationBug densityรวม/25
Leave3254216
Welfare (WDHospital)2133211
Payroll/Payslip3253316
Provident Fund4344318
Study Leave4323416
Auth/Registration3253316
Delegate/Role2222412
Notification3223313

สรุปคะแนน

  • ดีที่สุด: Provident Fund (18/25) — code ชัด, transaction ใช้ถูก
  • แย่ที่สุด: Welfare/WDHospital (11/25) — ไฟล์ใหญ่ 3,200 บรรทัด, magic numbers เยอะ, validation cap ถูกปิด
  • เสี่ยงสูง: Leave — มี bug สำคัญที่ hrmi.js:1266

2. Known bugs

Bug #1 — createLeaveDocument เงื่อนไขกลับด้าน (Leave)

ที่อยู่: hrmi.js:1266

1266:1275:vtrc-api/api/src/lib/controllers/hrmi/hrmi.js
    if (typeof args.approveStatus === "undefined") {
        mappingStatus = statusHrmi.filter(value => args.approveStatus === value.statusId)
        ...

ปัญหา: เงื่อนไข === "undefined" กลับด้าน — ควรเป็น !== "undefined"

ผลกระทบ: เมื่อ client ส่ง approveStatus มา — จะตก else branch และใช้ args.status (ซึ่งอาจเป็น undefined)

ความรุนแรง: สูง — ส่งผลต่อ status ทุกเอกสารลาที่ถูกสร้าง

แนวทางแก้: เปลี่ยน === เป็น !==


Bug #2 — status mapping hrmi.js:1266 ผิดพลาด

ปัญหา: เมื่อ client ส่ง approveStatus = 'W' จะ filter จาก args.approveStatus ไม่ใช่ค่าคงที่

js
mappingStatus = statusHrmi.filter(value => args.approveStatus === value.statusId)

ผลกระทบ: mappingStatus อาจว่าง → throw DATA_NOT_FOUND ทั้งที่ควรผ่าน


Bug #3 — PDF retention 5 วินาที (Payroll)

ที่อยู่: routes.js:82

js
res.sendFile(pdfPath)
setTimeout(() => deletePDF(pdfPath), 5000)

ปัญหา: ถ้า client download ช้ากว่า 5 วินาที — ไฟล์ถูกลบก่อน → 404

ผลกระทบ: ผู้ใช้ในเครือข่ายช้าอาจไม่ได้รับสลิป

แนวทางแก้: ใช้ streaming หรือเพิ่ม retention เป็น 5 นาที


Bug #4 — Welfare cap ถูกปิด

ที่อยู่: hospital.js:2748

js
// if (args.totalCurrentWithdraw > 8000) {
//   throw responError('AMOUNT_OVER_LIMIT', ...)
// }

ปัญหา: validation cap ยอดเบิก 8,000 บาทถูก comment ปิด — ปัจจุบัน client ส่งยอดมาเท่าไรระบบรับหมด

ความรุนแรง: สูง — พนักงานอาจเบิกเกินสิทธิ์


Bug #5 — Cron ไม่มี error handling

ที่อยู่: crons.js

js
schedule.scheduleJob('30 3 * * *', async () => {
    updateDelegatesExpire();
})

ปัญหา: ไม่มี try/catch — ถ้า throw จะเป็น unhandledRejection

ผลกระทบ: cron ล้มเหลวโดยไม่รู้ตัว


Bug #6 — transferChangeBenefitPersonCD ชื่อผิด

ปัญหา: ชื่อฟังก์ชันบอกว่า "BenefitPerson" แต่จริง ๆ ใช้สำหรับทั้ง benefit และ rate — ทำให้สับสน


Bug #7 — Race condition DocNo

ที่อยู่: hrmi.js:1424, hospital.js:2858

ปัญหา: การ generate docNo ไม่ได้ใช้ DB sequence หรือ lock — ถ้า 2 request เข้ามาพร้อมกันอาจได้เลขซ้ำ


3. Magic numbers ทั้งหมด

รวม magic numbers ที่ฝังใน controller ทั้งหมด — ควรย้ายไป config

Org codes

Valueความหมายที่มา
'02'โรงพยาบาลจุฬาhospital.js:107-120
'03'สภากาชาดhospital.js:107-120
'88'บำนาญhospital.js:1266, 1388
'0734'Plasma (skip validation)hrmi.js:1302
'07'Plasma branch prefixhrmi.js:210

Leave validation

Valueความหมายที่มา
earlyLeaveDayล่วงหน้ากี่วันhrmi.js:1302 (from CD)
backLeaveDayย้อนหลังได้กี่วันhrmi.js:1302 (from CD)
maxLeaveDayสูงสุดกี่วันhrmi.js:1283 (from CD)
minLeaveDayอย่างน้อยกี่วันhrmi.js:1283 (from CD)
reqAttachDayเกิน N วันต้องแนบใบรับรองแพทย์hrmi.js:1164 (from CD)

Welfare

Valueความหมายที่มา
1 ปี (slip)อายุใบเสร็จสูงสุดhospital.js:2784
30 วัน (resignation)ระยะเวลาเบิกหลังพ้นสภาพhospital.js:3158
20 ปี (child)อายุบุตรสูงสุดhospital.js:3126
8000 บาทcap ยอดเบิก (ปิด)hospital.js:2748
> 179, > 83threshold skip exportroutes.js:803, 807
3.15576e+10ms/year คำนวณอายุhospital.js:3144

Payroll

Valueความหมายที่มา
3600 * 1000 mssigned URL timeout 1 ชม.routes.js:55
5000 msPDF retentionroutes.js:82
+543แปลง พศ.formatdate.js:37,42
qpdf ... 40RC4-40 encryptionroutes.js:73
8 หลักpassword DDMMYYYYcontrollers/slip/slip.js:41

Auth/Notification

Valueความหมายที่มา
5 นาทีcaptcha expireauth.js:307
15 นาทีJWT vtrc-apienv
30 วันpublic device tokennotification.js:113

4. Dead code / disabled code

รายการที่อยู่สถานะ
Auto-cancel leave croncrons.js:29-70comment ปิด
Welfare cap 8,000 บาทhospital.js:2748comment ปิด
hospital.js:1690-1738block ที่ comment ปิดควรลบ
countApprover unused varhrmi.js:1794ประกาศแล้วไม่ใช้

5. Dead files / ไฟล์ที่น่าสงสัย

ไฟล์หมายเหตุ
lib/createslip/index.js vs lib/genSlip/index.jsสองไฟล์ทำงานคล้ายกัน — อาจซ้ำซ้อน ต้องตรวจว่าอันไหนใช้จริง
_bk*.js filesbackup ไฟล์เก่า — ไม่ควรแก้ แต่ควรลบในอนาคต
*_copy* filescopy ของไฟล์ — ลบได้

6. Coupling to cu-central-api — ตารางรวม

ทุก domain พึ่งพา cu-central-api ในระดับสูง — ถ้า CD ล่ม vtrc-api ใช้งานไม่ได้เกือบทั้งหมด

Domainจำนวน cross-service callฟังก์ชันสำคัญที่พึ่ง CD
Leave12+getLeaveDay, getWorkingDay, getApprover
Welfare2getMedical, employeeProfile
Payroll4+payrollSlipDetail, salaryCertDetail, pwdVerification
Fund4fetchEmployeeProvidentFund, hrBenefitPerson
Auth10+signin, signinbypass, token, signout, setupPasswordUser
Study2getEmpStudyLeaveRequest, personal
User2searchUser, employeeProfile
Notification0(vtrc-api ทำเอง)

ยกเว้น notification และ publication — domain อื่นทั้งหมดขึ้นกับ CD โดยตรง


7. Code quality issues

1. ไฟล์ใหญ่เกินไป

ไฟล์ขนาดปัญหา
hrmi.js~2,600 บรรทัดยากอ่าน
hospital.js~3,200 บรรทัดยากอ่าน + มี delegate logic ปน
routes.js~68 KBรวมทุก REST endpoint

แนวทางแก้: แยกตาม concern (validation, payment, notification, delegate)

2. Inline templates

ไม่มี template engine — title/body inline ใน controller

แนวทางแก้: สร้าง notifications/templates.js รวมไว้ที่เดียว

3. ไม่มี constant file

Magic numbers กระจาย — ควรรวมเป็น constants.js

4. ไม่มี test

ไม่พบ unit test สำหรับ business logic — ทุก domain ตรวจด้วย manual test


8. Long-term improvement recommendations

ระดับสั้น (1-2 สัปดาห์)

  1. แก้ bug hrmi.js:1266 — เปลี่ยนเงื่อนไข === เป็น !==
  2. เพิ่ม PDF retention เป็น 5 นาที (แทน 5 วินาที)
  3. เปิด welfare cap กลับมา (หลังยืนยันกับ HR)
  4. catch error ใน cron ทุก job

ระดับกลาง (1-2 เดือน)

  1. แยก hospital.js เป็น validation.js, payment.js, notification.js, delegate.js
  2. ย้าย delegate ไป delegate/delegate.js
  3. สร้าง constants.js รวม magic numbers
  4. เพิ่ม rate limit สำหรับ PDF generation
  5. เปลี่ยน PhantomJS เป็น Puppeteer

ระดับยาว (3-6 เดือน)

  1. เพิ่ม unit test สำหรับ workflow logic
  2. เพิ่ม transaction retry สำหรับ cross-service sync
  3. เพิ่ม cache สำหรับ lookup ที่ยิง CD บ่อย (getApprover, getLeaveType)
  4. สร้าง workflow abstraction — class/instance แทน procedural code
  5. เพิ่ม observability — log ทุก status transition พร้อม trace ID

9. ข้อแนะนำสำหรับวิศวกรใหม่

ถ้าเข้ามาแก้ business logic ของ domain ใด:

  1. อ่าน บท 11.1 ก่อน — เข้าใจแพทเทิร์นร่วม
  2. อ่านบทของ domain นั้น — เช่น บท 11.3 สำหรับ welfare
  3. เช็ค magic numbers ใน หัวข้อ 3 ของบทนี้
  4. เช็ค known bugs — อย่าซ้ำหรือแก้ผิดแบบเดิม
  5. ตรวจ cu-central-api dependency — logic สำคัญอยู่ที่ CD ไม่ใช่ vtrc-api
  6. ทดสอบใน UAT ก่อน deploy — เพราะไม่มี unit test คุ้มกัน

สรุป Volume 12

Volume นี้เจาะลึก business logic ของระบบ VTRC — state machine, workflow, calculation, validation ของทุก domain

ประเด็นสำคัญที่ควรจำ

  1. logic กระจาย 2 ระบบ — vtrc-api เป็น workflow engine, cu-central-api เป็น calculation + HR data
  2. ทุก domain ใช้แพทเทิร์นร่วม — parent + history, status enum, notification trigger
  3. ไม่มีการคำนวณใน vtrc-api — วันลา, สิทธิ์เบิก, ภาษี, เงินสมทบ ทั้งหมดอยู่ใน CD
  4. มี bug สำคัญที่ hrmi.js:1266 — ควรแก้โดยด่วน
  5. welfare cap ถูกปิด — พนักงานอาจเบิกเกินสิทธิ์
  6. ไฟล์ใหญ่ (hrmi.js, hospital.js) — ยากต่อการ maintain
  7. ไม่มี unit test — ต้องทดสอบ manual

Volume ถัดไป: Volume 13 · Infrastructure & Deployment (ยังไม่ได้เขียน)