diff --git a/sql_seed/update_profile_position_fields.sql b/sql_seed/update_profile_position_fields.sql deleted file mode 100644 index e9e999dc..00000000 --- a/sql_seed/update_profile_position_fields.sql +++ /dev/null @@ -1,154 +0,0 @@ --- ===================================================== --- Update position fields in profile table --- อัพเดทฟิลด์ตำแหน่งในตาราง profile --- --- Fields: --- - positionField (สายงาน) --- - posExecutive (ตำแหน่งทางการบริหาร) --- - positionArea (ด้าน/สาขา) --- - positionExecutiveField (ด้านทางการบริหาร) --- - posMasterNo (เลขที่ตำแหน่ง) - format: orgShortName + space + number --- - org (สังกัด) --- --- Run each query separately to verify results --- ===================================================== -USE hrms_organization; --- 1. Update positionField (สายงาน) -UPDATE profile p -INNER JOIN posMaster pm ON pm.current_holderId = p.id -INNER JOIN orgRevision oRev ON pm.orgRevisionId = oRev.id AND oRev.orgRevisionIsCurrent = 1 AND oRev.orgRevisionIsDraft = 0 -INNER JOIN position pos ON pos.posMasterId = pm.id AND pos.positionIsSelected = 1 -SET p.positionField = pos.positionField -WHERE p.positionField IS NULL; - --- 2. Update posExecutive (ตำแหน่งทางการบริหาร) -UPDATE profile p -INNER JOIN posMaster pm ON pm.current_holderId = p.id -INNER JOIN orgRevision oRev ON pm.orgRevisionId = oRev.id AND oRev.orgRevisionIsCurrent = 1 AND oRev.orgRevisionIsDraft = 0 -INNER JOIN position pos ON pos.posMasterId = pm.id AND pos.positionIsSelected = 1 -INNER JOIN posExecutive pe ON pos.posExecutiveId = pe.id -SET p.posExecutive = pe.posExecutiveName -WHERE p.posExecutive IS NULL; - --- 3. Update positionArea (ด้าน/สาขา) -UPDATE profile p -INNER JOIN posMaster pm ON pm.current_holderId = p.id -INNER JOIN orgRevision oRev ON pm.orgRevisionId = oRev.id AND oRev.orgRevisionIsCurrent = 1 AND oRev.orgRevisionIsDraft = 0 -INNER JOIN position pos ON pos.posMasterId = pm.id AND pos.positionIsSelected = 1 -SET p.positionArea = pos.positionArea -WHERE p.positionArea IS NULL; - --- 4. Update positionExecutiveField (ด้านทางการบริหาร) -UPDATE profile p -INNER JOIN posMaster pm ON pm.current_holderId = p.id -INNER JOIN orgRevision oRev ON pm.orgRevisionId = oRev.id AND oRev.orgRevisionIsCurrent = 1 AND oRev.orgRevisionIsDraft = 0 -INNER JOIN position pos ON pos.posMasterId = pm.id AND pos.positionIsSelected = 1 -SET p.positionExecutiveField = pos.positionExecutiveField -WHERE p.positionExecutiveField IS NULL; - --- 5. Update posMasterNo (เลขที่ตำแหน่ง) - format: orgShortName + space + number -UPDATE profile p -INNER JOIN posMaster pm ON pm.current_holderId = p.id -INNER JOIN orgRevision oRev ON pm.orgRevisionId = oRev.id AND oRev.orgRevisionIsCurrent = 1 AND oRev.orgRevisionIsDraft = 0 -LEFT JOIN orgRoot r ON pm.orgRootId = r.id -LEFT JOIN orgChild1 c1 ON pm.orgChild1Id = c1.id -LEFT JOIN orgChild2 c2 ON pm.orgChild2Id = c2.id -LEFT JOIN orgChild3 c3 ON pm.orgChild3Id = c3.id -LEFT JOIN orgChild4 c4 ON pm.orgChild4Id = c4.id -SET p.posMasterNo = TRIM(CONCAT( - CASE - WHEN pm.orgChild1Id IS NULL THEN r.orgRootShortName - WHEN pm.orgChild2Id IS NULL THEN c1.orgChild1ShortName - WHEN pm.orgChild3Id IS NULL THEN c2.orgChild2ShortName - WHEN pm.orgChild4Id IS NULL THEN c3.orgChild3ShortName - ELSE c4.orgChild4ShortName - END, - ' ', - CONCAT_WS('', pm.posMasterNoPrefix, pm.posMasterNo, pm.posMasterNoSuffix) -)) -WHERE p.posMasterNo IS NULL; - --- 6. Update org (สังกัด) - combine all org levels -UPDATE profile p -INNER JOIN posMaster pm ON pm.current_holderId = p.id -INNER JOIN orgRevision oRev ON pm.orgRevisionId = oRev.id AND oRev.orgRevisionIsCurrent = 1 AND oRev.orgRevisionIsDraft = 0 -LEFT JOIN orgRoot r ON pm.orgRootId = r.id -LEFT JOIN orgChild1 c1 ON pm.orgChild1Id = c1.id -LEFT JOIN orgChild2 c2 ON pm.orgChild2Id = c2.id -LEFT JOIN orgChild3 c3 ON pm.orgChild3Id = c3.id -LEFT JOIN orgChild4 c4 ON pm.orgChild4Id = c4.id -SET p.org = TRIM(CONCAT_WS( - CHAR(10), - c4.orgChild4Name, - c3.orgChild3Name, - c2.orgChild2Name, - c1.orgChild1Name, - r.orgRootName -)) -WHERE p.org IS NULL; - --- ===================================================== --- เช็คผลลัพธ์ (Check results) --- ===================================================== - --- เช็คจำนวนที่ update ได้ -SELECT - COUNT(CASE WHEN positionField IS NOT NULL THEN 1 END) AS has_positionField, - COUNT(CASE WHEN posExecutive IS NOT NULL THEN 1 END) AS has_posExecutive, - COUNT(CASE WHEN positionArea IS NOT NULL THEN 1 END) AS has_positionArea, - COUNT(CASE WHEN positionExecutiveField IS NOT NULL THEN 1 END) AS has_positionExecutiveField, - COUNT(CASE WHEN posMasterNo IS NOT NULL THEN 1 END) AS has_posMasterNo, - COUNT(CASE WHEN org IS NOT NULL THEN 1 END) AS has_org -FROM profile; - --- ===================================================== --- SELECT query สำหรับทดสอบก่อนรัน (Test before run) --- ===================================================== - -SELECT - p.id, - p.firstName, - p.lastName, - p.citizenId, - - p.positionField as old_positionField, - p.posExecutive as old_posExecutive, - p.positionArea as old_positionArea, - p.positionExecutiveField as old_positionExecutiveField, - p.posMasterNo as old_posMasterNo, - p.org as old_org, - - pos.positionField as new_positionField, - pe.posExecutiveName as new_posExecutive, - pos.positionArea as new_positionArea, - pos.positionExecutiveField as new_positionExecutiveField, - - TRIM(CONCAT( - CASE - WHEN pm.orgChild1Id IS NULL THEN r.orgRootShortName - WHEN pm.orgChild2Id IS NULL THEN c1.orgChild1ShortName - WHEN pm.orgChild3Id IS NULL THEN c2.orgChild2ShortName - WHEN pm.orgChild4Id IS NULL THEN c3.orgChild3ShortName - ELSE c4.orgChild4ShortName - END, - ' ', - pm.posMasterNo - )) as new_posMasterNo, - - TRIM(CONCAT_WS(CHAR(10), c4.orgChild4Name, c3.orgChild3Name, c2.orgChild2Name, c1.orgChild1Name, r.orgRootName)) as new_org - -FROM profile p -INNER JOIN posMaster pm ON pm.current_holderId = p.id -INNER JOIN orgRevision oRev ON pm.orgRevisionId = oRev.id AND oRev.orgRevisionIsCurrent = 1 AND oRev.orgRevisionIsDraft = 0 -INNER JOIN position pos ON pos.posMasterId = pm.id AND pos.positionIsSelected = 1 -LEFT JOIN posExecutive pe ON pos.posExecutiveId = pe.id -LEFT JOIN orgRoot r ON pm.orgRootId = r.id -LEFT JOIN orgChild1 c1 ON pm.orgChild1Id = c1.id -LEFT JOIN orgChild2 c2 ON pm.orgChild2Id = c2.id -LEFT JOIN orgChild3 c3 ON pm.orgChild3Id = c3.id -LEFT JOIN orgChild4 c4 ON pm.orgChild4Id = c4.id - --- ใส่ WHERE ทดสอบ 1 คน (Test 1 person) -WHERE p.id = 'ใส่ profile_id ที่ต้องการทดสอบ' --- หรือทดสอบ 10 คน (Test 10 persons) --- LIMIT 10; diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index e97abce5..da0ed3fe 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -48,7 +48,6 @@ import { import { Position } from "../entities/Position"; import { PosMaster } from "../entities/PosMaster"; import { EmployeePosition } from "../entities/EmployeePosition"; -import { getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; import { EmployeePosMaster } from "../entities/EmployeePosMaster"; import { ProfileDiscipline } from "../entities/ProfileDiscipline"; import { ProfileDisciplineHistory } from "../entities/ProfileDisciplineHistory"; @@ -3661,7 +3660,6 @@ export class CommandController extends Controller { const posMaster = await this.posMasterRepository.findOne({ where: { id: item.posmasterId }, - relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"], }); if (posMaster == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); @@ -3717,22 +3715,14 @@ export class CommandController extends Controller { id: item.positionId, posMasterId: item.posmasterId, }, - relations: ["posExecutive"], }); // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ if (positionNew != null) { positionNew.positionIsSelected = true; - // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit - profile.posMasterNo = getPosMasterNo(posMaster); - profile.org = getOrgFullName(posMaster); if(!posMaster.isSit){ profile.posLevelId = positionNew.posLevelId; profile.posTypeId = positionNew.posTypeId; profile.position = positionNew.positionName; - profile.positionField = positionNew.positionField ?? null; - profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; - profile.positionArea = positionNew.positionArea ?? null; - profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; } profile.amount = item.amount ?? null; profile.amountSpecial = item.amountSpecial ?? null; @@ -6886,7 +6876,7 @@ export class CommandController extends Controller { where: { id: item.bodyPosition.posmasterId, }, - relations: { orgRevision: true, orgRoot: true, orgChild1: true, orgChild2: true, orgChild3: true, orgChild4: true } + relations: { orgRevision: true } }); // เช็คว่า posMaster ที่หามาอยู่ในโครงสร้างปัจจุบันหรือไม่ @@ -6903,8 +6893,9 @@ export class CommandController extends Controller { orgRevisionIsDraft: false } }, - relations: { orgRevision: true, orgRoot: true, orgChild1: true, orgChild2: true, orgChild3: true, orgChild4: true } - }); } + relations: { orgRevision: true } + }); + } if (posMaster == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); @@ -6992,28 +6983,20 @@ export class CommandController extends Controller { id: item.bodyPosition.positionId, posMasterId: posMaster.id, }, - relations: ["posExecutive"], }); } // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ if (positionNew != null) { positionNew.positionIsSelected = true; - // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit - profile.posMasterNo = getPosMasterNo(posMaster); - profile.org = getOrgFullName(posMaster); if(!posMaster.isSit){ profile.posLevelId = positionNew.posLevelId; profile.posTypeId = positionNew.posTypeId; profile.position = positionNew.positionName; - profile.positionField = positionNew.positionField ?? null; - profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; - profile.positionArea = positionNew.positionArea ?? null; - profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; // profile.dateStart = new Date(); + await this.profileRepository.save(profile, { data: req }); + setLogDataDiff(req, { before, after: profile }); } - await this.profileRepository.save(profile, { data: req }); - setLogDataDiff(req, { before, after: profile }); await this.positionRepository.save(positionNew, { data: req }); } // await CreatePosMasterHistoryOfficer(posMaster.id, req); diff --git a/src/controllers/ExRetirementController.ts b/src/controllers/ExRetirementController.ts index c8ffe5da..128cb4d1 100644 --- a/src/controllers/ExRetirementController.ts +++ b/src/controllers/ExRetirementController.ts @@ -15,7 +15,6 @@ import { import HttpError from "../interfaces/http-error"; import HttpStatusCode from "../interfaces/http-status"; import { addLogSequence } from "../interfaces/utils"; -import HttpSuccess from "../interfaces/http-success"; interface CachedToken { token: string; @@ -89,8 +88,7 @@ export class ExRetirementController extends Controller { }, }); - // return res.data; - return new HttpSuccess(res.data.data); + return res.data; } catch (error: any) { if (error.response?.status === 500 && retryCount < maxRetries - 1) { TokenCache.delete(`${clientId}:${clientSecret}`); diff --git a/src/controllers/ImportDataController.ts b/src/controllers/ImportDataController.ts index 3e1a1b2b..5b8ca808 100644 --- a/src/controllers/ImportDataController.ts +++ b/src/controllers/ImportDataController.ts @@ -1,4 +1,4 @@ -import { Controller, Post, Route, Security, Tags, Request, UploadedFile, Path } from "tsoa"; +import { Controller, Post, Route, Security, Tags, Request, UploadedFile } from "tsoa"; import { AppDataSource } from "../database/data-source"; import { In, IsNull, LessThanOrEqual, Not, Between } from "typeorm"; import HttpSuccess from "../interfaces/http-success"; @@ -105,7 +105,6 @@ import { positionOfficer } from "../entities/mis/positionOfficer"; import { ProvinceMaster } from "../entities/ProvinceMaster"; import { SubDistrictMaster } from "../entities/SubDistrictMaster"; import { DistrictMaster } from "../entities/DistrictMaster"; -import { RequestWithUser } from "../middlewares/user"; @Route("api/v1/org/upload") @Tags("UPLOAD") @Security("bearerAuth") @@ -6816,502 +6815,4 @@ export class ImportDataController extends Controller { // await repo.save(entities); // return entities; // } - - /** - * @summary Import ข้อมูลประวัติตำแหน่งเงินเดือนของข้าราชการเข้าตาราง ProfileSalaryTemp - * @param profileId Id โปรไฟล์ข้าราชการ - * @param file Excel file with salary history data - */ - @Post("office-profileSalaryTemp/{profileId}") - @UseInterceptors(FileInterceptor("file")) - async UploadProfileSalaryTemp( - @Path() profileId: string, - @Request() req: RequestWithUser, - @UploadedFile() file: Express.Multer.File, - ) { - if (!profileId) { - throw new Error("profileId is required"); - } - - // อ่านไฟล์ Excel ก่อน (นอก transaction) - const workbook = xlsx.read(file.buffer, { type: "buffer" }); - const sheetName = workbook.SheetNames[0]; - const sheet = workbook.Sheets[sheetName]; - const getExcel = xlsx.utils.sheet_to_json(sheet, { header: 1 }) as any[][]; - - let salaryTemps: ProfileSalaryTemp[] = []; - let dateTime = new Date(); - - // เริ่มจาก index 1 เพื่อข้าม header row - for (let i = 1; i < getExcel.length; i++) { - const row = getExcel[i]; - - // ข้าม empty rows - if (!row || row.length === 0) { - continue; - } - - // ข้ามแถวที่ไม่มีลำดับ (row[0] เป็น null, undefined หรือค่าว่าง) - if (!row[0]) { - continue; - } - - const salaryTemp = new ProfileSalaryTemp(); - - // ฟังก์ชันแปลงวันที่จาก Excel รองรับทั้ง string format และ serial number - const parseExcelDate = (value: any): Date | null => { - if (!value) return null; - - // กรณี 1: Excel serial number (ตัวเลข) - if (typeof value === "number") { - // Excel serial number = จำนวนวันตั้งแต่ 1 ม.ค. 1900 - // แปลงเป็น JavaScript Date (epoch 1970) - let jsDate = new Date(Math.round((value - 25569) * 86400 * 1000)); - - // ตรวจสอบและแปลงปี พ.ศ. เป็น ค.ศ. (ถ้าปี > 2500) - if (jsDate.getFullYear() > 2500) { - const newYear = jsDate.getFullYear() - 543; - jsDate = new Date( - newYear, - jsDate.getMonth(), - jsDate.getDate(), - jsDate.getHours(), - jsDate.getMinutes(), - jsDate.getSeconds(), - jsDate.getMilliseconds() - ); - } - return jsDate; - } - - // กรณี 2: String format (dd/mm/yyyy หรือ d/m/yyyy) - const dateStr = value.toString().trim(); - - // ตรวจสอบว่าเป็น serial number ที่เป็น string หรือไม่ - if (/^\d+$/.test(dateStr)) { - const serialNum = parseInt(dateStr); - let jsDate = new Date(Math.round((serialNum - 25569) * 86400 * 1000)); - - // ตรวจสอบและแปลงปี พ.ศ. เป็น ค.ศ. (ถ้าปี > 2500) - if (jsDate.getFullYear() > 2500) { - const newYear = jsDate.getFullYear() - 543; - jsDate = new Date( - newYear, - jsDate.getMonth(), - jsDate.getDate(), - jsDate.getHours(), - jsDate.getMinutes(), - jsDate.getSeconds(), - jsDate.getMilliseconds() - ); - } - return jsDate; - } - - // String format ปกติ (dd/mm/yyyy) - const dateParts = dateStr.split("/"); - if (dateParts.length === 3) { - // แปลงเป็นตัวเลขแล้วค่อยจัดรูปแบบใหม่ เพื่อรองรับทั้ง 1 หลักและ 2 หลัก - const day = parseInt(dateParts[0].trim()).toString().padStart(2, "0"); - const month = parseInt(dateParts[1].trim()).toString().padStart(2, "0"); - let year = parseInt(dateParts[2].trim()); - if (year > 2500) { - year -= 543; - } - const result = new Date(`${year}-${month}-${day}`); - return result; - } - - return null; - }; - - // Index 1: วันที่คำสั่งมีผล - let commandDateAffect: Date | null = null; - if (row[1]) { - commandDateAffect = parseExcelDate(row[1]); - } - - // Index 25: วันที่ลงนาม - let commandDateSign: Date | null = null; - if (row[25]) { - commandDateSign = parseExcelDate(row[25]); - } - - // Map ข้อมูลจาก Excel ไปยัง ProfileSalaryTemp ตามลำดับ column - // ข้อมูลระบบ - salaryTemp.profileId = profileId; - salaryTemp.profileEmployeeId = null as any; - - // Index 0: ลำดับ - salaryTemp.order = row[0] ? parseInt(row[0].toString()) : (null as any); - - // Index 1: วันที่คำสั่งมีผล - salaryTemp.commandDateAffect = commandDateAffect as any; - - // Index 2: ตำแหน่งในสายงาน - salaryTemp.positionName = row[2] || null; - - // Index 3: ตำแหน่งประเภท - salaryTemp.positionType = row[3] || null; - - // Index 4: ระดับ - salaryTemp.positionLevel = row[4] || null; - - // Index 5: ระดับซี - salaryTemp.positionCee = row[5] || null; - - // Index 6: สายงาน - salaryTemp.positionLine = row[6] || null; - - // Index 7: ด้าน/สาขา - salaryTemp.positionPathSide = row[7] || null; - - // Index 8: ตำแหน่งทางการบริหาร - salaryTemp.positionExecutive = row[8] || null; - - // Index 9: ด้านทางการบริหาร - salaryTemp.positionExecutiveField = row[9] || null; - - // Index 10: เงินเดือน - salaryTemp.amount = row[10] || 0; - - // Index 11: เงินค่าตอบแทนรายเดือน - salaryTemp.mouthSalaryAmount = row[11] || 0; - - // Index 12: เงินประจำตำแหน่ง - salaryTemp.positionSalaryAmount = row[12] || 0; - - // Index 13: เงินค่าตอบแทนพิเศษ - salaryTemp.amountSpecial = row[13] || 0; - - // Index 14: หน่วยงาน - salaryTemp.orgRoot = row[14] || null; - - // Index 15: ส่วนราชการระดับ 1 - salaryTemp.orgChild1 = row[15] || null; - - // Index 16: ส่วนราชการระดับ 2 - salaryTemp.orgChild2 = row[16] || null; - - // Index 17: ส่วนราชการระดับ 3 - salaryTemp.orgChild3 = row[17] || null; - - // Index 18: ส่วนราชการระดับ 4 - salaryTemp.orgChild4 = row[18] || null; - - // Index 19: ตัวย่อเลขที่ตำแหน่ง - salaryTemp.posNoAbb = row[19] || null; - - // Index 20: เลขที่ตำแหน่ง - salaryTemp.posNo = row[20] ? row[20].toString() : null; - - // Index 21: หน่วยงานที่ออกคำสั่ง - salaryTemp.posNumCodeSit = row[21] || null; - - // Index 22: ตัวย่อหน่วยงานที่ออกคำสั่ง - salaryTemp.posNumCodeSitAbb = row[22] || null; - - // Index 23: เลขที่คำสั่ง - salaryTemp.commandNo = row[23] || null; - - // Index 24: ปีเลขที่คำสั่ง (แปลงเป็น ค.ศ.) - let commandYearValue: number | null = null; - if (row[24]) { - commandYearValue = parseInt(row[24].toString()); - // ถ้าปีเป็น พ.ศ. (มากกว่า 2500) ให้แปลงเป็น ค.ศ. - if (commandYearValue > 2500) { - commandYearValue -= 543; - } - } - salaryTemp.commandYear = commandYearValue as any; - - // Index 25: วันที่ลงนาม (แปลงแล้ว) - salaryTemp.commandDateSign = commandDateSign as any; - - // Index 26: ประเภทคำสั่ง - salaryTemp.commandName = row[26] || null; - - // Index 27: หมายเหตุ - salaryTemp.remark = row[27] || null; - - // Index 28: commandId - salaryTemp.commandId = row[28] || null; - - // Index 29: commandCode - salaryTemp.commandCode = row[29] || null; - - // ข้อมูลระบบ - salaryTemp.isDelete = false; - salaryTemp.isEdit = false; - salaryTemp.isGovernment = false; - salaryTemp.isEntry = false; - salaryTemp.createdAt = dateTime; - salaryTemp.createdUserId = req.user?.sub || ""; - salaryTemp.createdFullName = req.user?.name || "System Administrator"; - salaryTemp.lastUpdatedAt = dateTime; - salaryTemp.lastUpdateUserId = req.user?.sub || ""; - salaryTemp.lastUpdateFullName = req.user?.name || "System Administrator"; - - salaryTemps.push(salaryTemp); - } - - // ใช้ Transaction เพื่อความปลอดภัย - await AppDataSource.transaction(async (transactionalEntityManager) => { - // ล้างข้อมูลทั้งหมดในตาราง profileSalaryTemp ของ profileId นั้น - await transactionalEntityManager.delete(ProfileSalaryTemp, { profileId }); - // Insert ข้อมูลใหม่ - await transactionalEntityManager.save(ProfileSalaryTemp, salaryTemps); - }); - - return new HttpSuccess({ message: "Import ข้อมูลเรียบร้อย", count: salaryTemps.length }); - } - - /** - * @summary Import ข้อมูลประวัติตำแหน่งเงินเดือนของลูกจ้างประจำเข้าตาราง ProfileSalaryTemp - * @param profileEmployeeId Id โปรไฟล์ลูกจ้างประจำ - * @param file Excel file with salary history data - */ - @Post("employee-profileSalaryTemp/{profileEmployeeId}") - @UseInterceptors(FileInterceptor("file")) - async UploadProfileEmployeeSalaryTemp( - @Path() profileEmployeeId: string, - @Request() req: RequestWithUser, - @UploadedFile() file: Express.Multer.File, - ) { - if (!profileEmployeeId) { - throw new Error("profileEmployeeId is required"); - } - - // อ่านไฟล์ Excel ก่อน (นอก transaction) - const workbook = xlsx.read(file.buffer, { type: "buffer" }); - const sheetName = workbook.SheetNames[0]; - const sheet = workbook.Sheets[sheetName]; - const getExcel = xlsx.utils.sheet_to_json(sheet, { header: 1 }) as any[][]; - - let salaryTemps: ProfileSalaryTemp[] = []; - let dateTime = new Date(); - - // เริ่มจาก index 1 เพื่อข้าม header row - for (let i = 1; i < getExcel.length; i++) { - const row = getExcel[i]; - - // ข้าม empty rows - if (!row || row.length === 0) { - continue; - } - - // ข้ามแถวที่ไม่มีลำดับ (row[0] เป็น null, undefined หรือค่าว่าง) - if (!row[0]) { - continue; - } - - const salaryTemp = new ProfileSalaryTemp(); - - // ฟังก์ชันแปลงวันที่จาก Excel รองรับทั้ง string format และ serial number - const parseExcelDate = (value: any): Date | null => { - if (!value) return null; - - // กรณี 1: Excel serial number (ตัวเลข) - if (typeof value === "number") { - // Excel serial number = จำนวนวันตั้งแต่ 1 ม.ค. 1900 - // แปลงเป็น JavaScript Date (epoch 1970) - let jsDate = new Date(Math.round((value - 25569) * 86400 * 1000)); - - // ตรวจสอบและแปลงปี พ.ศ. เป็น ค.ศ. (ถ้าปี > 2500) - if (jsDate.getFullYear() > 2500) { - const newYear = jsDate.getFullYear() - 543; - jsDate = new Date( - newYear, - jsDate.getMonth(), - jsDate.getDate(), - jsDate.getHours(), - jsDate.getMinutes(), - jsDate.getSeconds(), - jsDate.getMilliseconds() - ); - } - return jsDate; - } - - // กรณี 2: String format (dd/mm/yyyy หรือ d/m/yyyy) - const dateStr = value.toString().trim(); - - // ตรวจสอบว่าเป็น serial number ที่เป็น string หรือไม่ - if (/^\d+$/.test(dateStr)) { - const serialNum = parseInt(dateStr); - let jsDate = new Date(Math.round((serialNum - 25569) * 86400 * 1000)); - - // ตรวจสอบและแปลงปี พ.ศ. เป็น ค.ศ. (ถ้าปี > 2500) - if (jsDate.getFullYear() > 2500) { - const newYear = jsDate.getFullYear() - 543; - jsDate = new Date( - newYear, - jsDate.getMonth(), - jsDate.getDate(), - jsDate.getHours(), - jsDate.getMinutes(), - jsDate.getSeconds(), - jsDate.getMilliseconds() - ); - } - return jsDate; - } - - // String format ปกติ (dd/mm/yyyy) - const dateParts = dateStr.split("/"); - if (dateParts.length === 3) { - // แปลงเป็นตัวเลขแล้วค่อยจัดรูปแบบใหม่ เพื่อรองรับทั้ง 1 หลักและ 2 หลัก - const day = parseInt(dateParts[0].trim()).toString().padStart(2, "0"); - const month = parseInt(dateParts[1].trim()).toString().padStart(2, "0"); - let year = parseInt(dateParts[2].trim()); - if (year > 2500) { - year -= 543; - } - const result = new Date(`${year}-${month}-${day}`); - return result; - } - - return null; - }; - - // Index 1: วันที่คำสั่งมีผล - let commandDateAffect: Date | null = null; - if (row[1]) { - commandDateAffect = parseExcelDate(row[1]); - } - - // Index 25: วันที่ลงนาม - let commandDateSign: Date | null = null; - if (row[25]) { - commandDateSign = parseExcelDate(row[25]); - } - - // Map ข้อมูลจาก Excel ไปยัง ProfileSalaryTemp ตามลำดับ column - // ข้อมูลระบบ - salaryTemp.profileEmployeeId = profileEmployeeId; - salaryTemp.profileId = null as any; - - // Index 0: ลำดับ - salaryTemp.order = row[0] ? parseInt(row[0].toString()) : (null as any); - - // Index 1: วันที่คำสั่งมีผล - salaryTemp.commandDateAffect = commandDateAffect as any; - - // Index 2: ตำแหน่งในสายงาน - salaryTemp.positionName = row[2] || null; - - // Index 3: ตำแหน่งประเภท - salaryTemp.positionType = row[3] || null; - - // Index 4: ระดับ - salaryTemp.positionLevel = row[4] || null; - - // Index 5: ระดับซี - salaryTemp.positionCee = row[5] || null; - - // Index 6: สายงาน - salaryTemp.positionLine = row[6] || null; - - // Index 7: ด้าน/สาขา - salaryTemp.positionPathSide = row[7] || null; - - // Index 8: ตำแหน่งทางการบริหาร - salaryTemp.positionExecutive = row[8] || null; - - // Index 9: ด้านทางการบริหาร - salaryTemp.positionExecutiveField = row[9] || null; - - // Index 10: เงินเดือน - salaryTemp.amount = row[10] || 0; - - // Index 11: เงินค่าตอบแทนรายเดือน - salaryTemp.mouthSalaryAmount = row[11] || 0; - - // Index 12: เงินประจำตำแหน่ง - salaryTemp.positionSalaryAmount = row[12] || 0; - - // Index 13: เงินค่าตอบแทนพิเศษ - salaryTemp.amountSpecial = row[13] || 0; - - // Index 14: หน่วยงาน - salaryTemp.orgRoot = row[14] || null; - - // Index 15: ส่วนราชการระดับ 1 - salaryTemp.orgChild1 = row[15] || null; - - // Index 16: ส่วนราชการระดับ 2 - salaryTemp.orgChild2 = row[16] || null; - - // Index 17: ส่วนราชการระดับ 3 - salaryTemp.orgChild3 = row[17] || null; - - // Index 18: ส่วนราชการระดับ 4 - salaryTemp.orgChild4 = row[18] || null; - - // Index 19: ตัวย่อเลขที่ตำแหน่ง - salaryTemp.posNoAbb = row[19] || null; - - // Index 20: เลขที่ตำแหน่ง - salaryTemp.posNo = row[20] ? row[20].toString() : null; - - // Index 21: หน่วยงานที่ออกคำสั่ง - salaryTemp.posNumCodeSit = row[21] || null; - - // Index 22: ตัวย่อหน่วยงานที่ออกคำสั่ง - salaryTemp.posNumCodeSitAbb = row[22] || null; - - // Index 23: เลขที่คำสั่ง - salaryTemp.commandNo = row[23] || null; - - // Index 24: ปีเลขที่คำสั่ง (แปลงเป็น ค.ศ.) - let commandYearValue: number | null = null; - if (row[24]) { - commandYearValue = parseInt(row[24].toString()); - // ถ้าปีเป็น พ.ศ. (มากกว่า 2500) ให้แปลงเป็น ค.ศ. - if (commandYearValue > 2500) { - commandYearValue -= 543; - } - } - salaryTemp.commandYear = commandYearValue as any; - - // Index 25: วันที่ลงนาม (แปลงแล้ว) - salaryTemp.commandDateSign = commandDateSign as any; - - // Index 26: ประเภทคำสั่ง - salaryTemp.commandName = row[26] || null; - - // Index 27: หมายเหตุ - salaryTemp.remark = row[27] || null; - - // Index 28: commandId - salaryTemp.commandId = row[28] || null; - - // Index 29: commandCode - salaryTemp.commandCode = row[29] || null; - - // ข้อมูลระบบ - salaryTemp.isDelete = false; - salaryTemp.isEdit = false; - salaryTemp.isGovernment = false; - salaryTemp.isEntry = false; - salaryTemp.createdAt = dateTime; - salaryTemp.createdUserId = req.user?.sub || ""; - salaryTemp.createdFullName = req.user?.name || "System Administrator"; - salaryTemp.lastUpdatedAt = dateTime; - salaryTemp.lastUpdateUserId = req.user?.sub || ""; - salaryTemp.lastUpdateFullName = req.user?.name || "System Administrator"; - - salaryTemps.push(salaryTemp); - } - - // ใช้ Transaction เพื่อความปลอดภัย - await AppDataSource.transaction(async (transactionalEntityManager) => { - // ล้างข้อมูลทั้งหมดในตาราง profileSalaryTemp ของ profileEmployeeId นั้น - await transactionalEntityManager.delete(ProfileSalaryTemp, { profileEmployeeId }); - // Insert ข้อมูลใหม่ - await transactionalEntityManager.save(ProfileSalaryTemp, salaryTemps); - }); - - return new HttpSuccess({ message: "Import ข้อมูลเรียบร้อย", count: salaryTemps.length }); - } } diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index ee8f3413..39752b7e 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -66,7 +66,7 @@ import { import { orgStructureCache } from "../utils/OrgStructureCache"; import { OrgIdMapping, AllOrgMappings, SavePosMasterHistory } from "../interfaces/OrgMapping"; import { OrgPermissionData, NodeLevel } from "../interfaces/OrgTypes"; -import { formatPosMaster, generateLabelName, filterPosMasters, getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; +import { formatPosMaster, generateLabelName, filterPosMasters } from "../utils/org-formatting"; @Route("api/v1/org") @Tags("Organization") @@ -8933,25 +8933,13 @@ export class OrganizationController extends Controller { const draftPosMaster = draftPosMasterMap.get(draftPosMasterId) as any; // Collect profile update for the selected position - // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit - if (nextHolderId != null && draftPos.positionIsSelected) { - const _null: any = null; - profileUpdates.set(nextHolderId, { - posMasterNo: draftPosMaster ? (getPosMasterNo(draftPosMaster as PosMaster) ?? _null) : _null, - org: draftPosMaster ? (getOrgFullName(draftPosMaster as PosMaster) ?? _null) : _null, - }); - } // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ if (nextHolderId != null && draftPos.positionIsSelected && !draftPosMaster?.isSit) { - const existing = profileUpdates.get(nextHolderId) || {}; - existing.position = draftPos.positionName; - existing.posTypeId = draftPos.posTypeId; - existing.posLevelId = draftPos.posLevelId; - existing.positionField = draftPos.positionField ?? null; - existing.posExecutive = (draftPos as any).posExecutive?.posExecutiveName ?? null; - existing.positionArea = draftPos.positionArea ?? null; - existing.positionExecutiveField = draftPos.positionExecutiveField ?? null; - profileUpdates.set(nextHolderId, existing); + profileUpdates.set(nextHolderId, { + position: draftPos.positionName, + posTypeId: draftPos.posTypeId, + posLevelId: draftPos.posLevelId, + }); if (draftPosMaster && draftPosMaster.ancestorDNA) { // Find the selected position from draft positions const selectedPos = diff --git a/src/controllers/PermissionController.ts b/src/controllers/PermissionController.ts index ed8fc343..801d4b97 100644 --- a/src/controllers/PermissionController.ts +++ b/src/controllers/PermissionController.ts @@ -15,7 +15,6 @@ import permission from "../interfaces/permission"; import { ProfileEmployee } from "../entities/ProfileEmployee"; import { EmployeePosMaster } from "../entities/EmployeePosMaster"; import { OrgRevision } from "../entities/OrgRevision"; -import { actingPositionService } from "../services/ActingPositionService"; const REDIS_HOST = process.env.REDIS_HOST; const REDIS_PORT = process.env.REDIS_PORT; @@ -255,64 +254,6 @@ export class PermissionController extends Controller { return new HttpSuccess(res); } - /** - * API permission with acting positions - * @summary permission with acting positions (dotnet api) - * @param {string} action action - * @param {string} system authSysId - */ - @Get("dotnet-acting/{action}/{system}") - public async dotnetActing( - @Request() req: RequestWithUser, - @Path() action: string, - @Path() system: string, - ) { - if (!["CREATE", "DELETE", "GET", "LIST", "UPDATE"].includes(action)) { - throw new HttpError(HttpStatus.NOT_FOUND, "Action ไม่ถูกต้อง"); - } - // ดึง privilege ตามปกติ - let privilege = await new permission().Permission(req, system.toLocaleUpperCase(), action); - - // ดึงข้อมูล profile และ orgRevision - let profile: any = await this.profileRepo.findOne({ - select: ["id"], - where: { keycloak: req.user.sub }, - }); - - if (!profile) { - profile = await this.profileEmployeeRepo.findOne({ - select: ["id"], - where: { keycloak: req.user.sub }, - }); - if (!profile) { - throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลบุคคลนี้ในระบบ"); - } - } - - const orgRevision = await this.orgRevisionRepository.findOne({ - select: ["id"], - where: { - orgRevisionIsDraft: false, - orgRevisionIsCurrent: true, - }, - }); - - // ดึงข้อมูลตำแหน่งที่รักษาการ - const actingData = await actingPositionService.getActingPositionsWithPrivilege( - profile.id, - orgRevision?.id, - action, - system.toLocaleUpperCase() - ); - - // ส่งค่ากลับเหมือน dotnet endpoint แต่เพิ่ม isAct และ posMasterActs - return new HttpSuccess({ - privilege, - isAct: actingData.isAct, - posMasterActs: actingData.posMasterActs, - }); - } - /** * API permission (dotnet api) * @summary permission (dotnet api) diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 37393e14..75d6c2a0 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -39,7 +39,6 @@ import { AuthRole } from "../entities/AuthRole"; import { RequestWithUser } from "../middlewares/user"; import permission from "../interfaces/permission"; import { resolveNodeLevel, setLogDataDiff } from "../interfaces/utils"; -import { getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; import { PosMasterAssign } from "../entities/PosMasterAssign"; import { Assign } from "../entities/Assign"; import { ProfileEmployee } from "../entities/ProfileEmployee"; @@ -1257,15 +1256,7 @@ export class PositionController extends Controller { ) { await new permission().PermissionUpdate(request, "SYS_ORG"); const posMaster = await this.posMasterRepository.findOne({ - relations: [ - "positions", - "orgRevision", - "orgRoot", - "orgChild1", - "orgChild2", - "orgChild3", - "orgChild4", - ], + relations: ["positions", "orgRevision"], where: { id: id }, }); if (!posMaster) { @@ -1460,17 +1451,6 @@ export class PositionController extends Controller { }), ); - // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit - if (posMaster.orgRevision?.orgRevisionIsCurrent == true && posMaster.current_holderId) { - const _profile = await this.profileRepository.findOne({ - where: { id: posMaster.current_holderId }, - }); - if (_profile) { - _profile.posMasterNo = getPosMasterNo(posMaster); - _profile.org = getOrgFullName(posMaster); - await this.profileRepository.save(_profile); - } - } // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ if (posMaster.orgRevision?.orgRevisionIsCurrent == true && !posMaster.isSit) { const _position = requestBody.positions.find((p) => p.positionIsSelected == true); @@ -1483,10 +1463,6 @@ export class PositionController extends Controller { _profile.position = _position.posDictName ?? _null; _profile.posTypeId = _position.posTypeId; _profile.posLevelId = _position.posLevelId; - _profile.positionField = _position.posDictField ?? _null; - _profile.posExecutive = _position.posExecutiveId ?? _null; - _profile.positionArea = _position.posDictArea ?? _null; - _profile.positionExecutiveField = _position.posDictExecutiveField ?? _null; await this.profileRepository.save(_profile); } } @@ -2411,16 +2387,16 @@ export class PositionController extends Controller { ? "posMaster.orgRootId IN (:...root)" : "posMaster.orgRootId is null" : "1=1", - { root: _data.root }, + { root: _data.root } ) .andWhere( _data.child1 != undefined && _data.child1 != null ? _data.child1[0] != null ? "posMaster.orgChild1Id IN (:...child1)" - : // : `posMaster.orgChild1Id is ${_data.privilege == "PARENT" ? "not null" : "null"}` - `posMaster.orgChild1Id is null` + // : `posMaster.orgChild1Id is ${_data.privilege == "PARENT" ? "not null" : "null"}` + : `posMaster.orgChild1Id is null` : "1=1", - { child1: _data.child1 }, + { child1: _data.child1 } ) .andWhere( _data.child2 != undefined && _data.child2 != null @@ -2451,27 +2427,26 @@ export class PositionController extends Controller { { child4: _data.child4, }, - ); + ) // .andWhere(checkChildConditions) // .andWhere(typeCondition) // .andWhere(revisionCondition); if (body.keyword != null && body.keyword != "") { - query - .orWhere( - new Brackets((qb) => { - qb.andWhere( - body.keyword != null && body.keyword != "" - ? body.isAll == false - ? searchShortName - : `CASE WHEN posMaster.orgChild1 is null THEN ${searchShortName0} WHEN posMaster.orgChild2 is null THEN ${searchShortName1} WHEN posMaster.orgChild3 is null THEN ${searchShortName2} WHEN posMaster.orgChild4 is null THEN ${searchShortName3} ELSE ${searchShortName4} END LIKE '%${body.keyword}%'` - : "1=1", - ) - .andWhere(checkChildConditions) - .andWhere(typeCondition) - .andWhere(revisionCondition); - }), - ) + query.orWhere( + new Brackets((qb) => { + qb.andWhere( + body.keyword != null && body.keyword != "" + ? body.isAll == false + ? searchShortName + : `CASE WHEN posMaster.orgChild1 is null THEN ${searchShortName0} WHEN posMaster.orgChild2 is null THEN ${searchShortName1} WHEN posMaster.orgChild3 is null THEN ${searchShortName2} WHEN posMaster.orgChild4 is null THEN ${searchShortName3} ELSE ${searchShortName4} END LIKE '%${body.keyword}%'` + : "1=1", + ) + .andWhere(checkChildConditions) + .andWhere(typeCondition) + .andWhere(revisionCondition); + }), + ) .orWhere( new Brackets((qb) => { qb.andWhere( @@ -2980,50 +2955,50 @@ export class PositionController extends Controller { const type0LastPosMasterNo = requestBody.type == 0 ? await this.posMasterRepository.find({ - where: { - orgRootId: requestBody.id, - orgChild1Id: IsNull(), - }, - }) + where: { + orgRootId: requestBody.id, + orgChild1Id: IsNull(), + }, + }) : []; const type1LastPosMasterNo = requestBody.type == 1 ? await this.posMasterRepository.find({ - where: { - orgChild1Id: requestBody.id, - orgChild2Id: IsNull(), - }, - }) + where: { + orgChild1Id: requestBody.id, + orgChild2Id: IsNull(), + }, + }) : []; const type2LastPosMasterNo = requestBody.type == 2 ? await this.posMasterRepository.find({ - where: { - orgChild2Id: requestBody.id, - orgChild3Id: IsNull(), - }, - }) + where: { + orgChild2Id: requestBody.id, + orgChild3Id: IsNull(), + }, + }) : []; const type3LastPosMasterNo = requestBody.type == 3 ? await this.posMasterRepository.find({ - where: { - orgChild3Id: requestBody.id, - orgChild4Id: IsNull(), - }, - }) + where: { + orgChild3Id: requestBody.id, + orgChild4Id: IsNull(), + }, + }) : []; const type4LastPosMasterNo = requestBody.type == 4 ? await this.posMasterRepository.find({ - where: { - orgChild4Id: requestBody.id, - }, - }) + where: { + orgChild4Id: requestBody.id, + }, + }) : []; const allLastPosMasterNo = [ @@ -3818,7 +3793,7 @@ export class PositionController extends Controller { await new permission().PermissionUpdate(request, "SYS_ORG"); const dataMaster = await this.posMasterRepository.findOne({ where: { id: requestBody.posMaster }, - relations: ["positions", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"], + relations: ["positions"], }); if (!dataMaster) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); @@ -3850,24 +3825,16 @@ export class PositionController extends Controller { if (_profile) { let _position = await this.positionRepository.findOne({ where: { id: requestBody.position, posMasterId: requestBody.posMaster }, - relations: ["posExecutive"], }); if (_position) { - // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit - _profile.posMasterNo = getPosMasterNo(dataMaster); - _profile.org = getOrgFullName(dataMaster); // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ - if (!dataMaster.isSit) { + if(!dataMaster.isSit){ _profile.position = _position.positionName; _profile.posTypeId = _position.posTypeId; _profile.posLevelId = _position.posLevelId; - _profile.positionField = _position.positionField ?? _null; - _profile.posExecutive = _position.posExecutive?.posExecutiveName ?? _null; - _profile.positionArea = _position.positionArea ?? _null; - _profile.positionExecutiveField = _position.positionExecutiveField ?? _null; + await this.profileRepository.save(_profile); + setLogDataDiff(request, { before, after: _profile }); } - await this.profileRepository.save(_profile); - setLogDataDiff(request, { before, after: _profile }); } } dataMaster.current_holderId = requestBody.profileId; @@ -5202,9 +5169,9 @@ export class PositionController extends Controller { } /** - * API รายการตำแหน่งติดเงื่อนไข + * API รายการอัตรากำลัง * - * @summary รายการตำแหน่งติดเงื่อนไข + * @summary ORG_070 - รายการอัตรากำลัง (ADMIN) #56 * */ @Post("master/position-condition") @@ -5215,7 +5182,7 @@ export class PositionController extends Controller { id: string; revisionId: string; type: number; - isAll: boolean; // true คือเลือกเฉพาะตำแหน่งติดเงื่อนไข / false คือเลือกตำแหน่งทั้งหมด + isAll: boolean; page: number; pageSize: number; keyword?: string; @@ -5235,7 +5202,7 @@ export class PositionController extends Controller { let level: any = resolveNodeLevel(orgDna); const cannotViewRootPosMaster = - _data.privilege === "PARENT" || + (_data.privilege === "PARENT") || (_data.privilege === "BROTHER" && level > 1) || (_data.privilege === "CHILD" && level > 0) || (_data.privilege === "NORMAL" && level != 0); @@ -5267,46 +5234,46 @@ export class PositionController extends Controller { typeCondition = { ...(cannotViewRootPosMaster ? { orgRootId: null } : { orgRootId: body.id }), }; - // if (!body.isAll) { - // checkChildConditions = { - // orgChild1Id: IsNull(), - // }; - // searchShortName = `CONCAT(orgRoot.orgRootShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; - // } else { - // } + if (!body.isAll) { + checkChildConditions = { + orgChild1Id: IsNull(), + }; + searchShortName = `CONCAT(orgRoot.orgRootShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; + } else { + } } else if (body.type === 1) { typeCondition = { ...(cannotViewChild1PosMaster ? { orgChild1Id: null } : { orgChild1Id: body.id }), }; - // if (!body.isAll) { - // checkChildConditions = { - // orgChild2Id: IsNull(), - // }; - // searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; - // } else { - // } + if (!body.isAll) { + checkChildConditions = { + orgChild2Id: IsNull(), + }; + searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; + } else { + } } else if (body.type === 2) { typeCondition = { ...(cannotViewChild2PosMaster ? { orgChild2Id: null } : { orgChild2Id: body.id }), }; - // if (!body.isAll) { - // checkChildConditions = { - // orgChild3Id: IsNull(), - // }; - // searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; - // } else { - // } + if (!body.isAll) { + checkChildConditions = { + orgChild3Id: IsNull(), + }; + searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; + } else { + } } else if (body.type === 3) { typeCondition = { ...(cannotViewChild3PosMaster ? { orgChild3Id: null } : { orgChild3Id: body.id }), }; - // if (!body.isAll) { - // checkChildConditions = { - // orgChild4Id: IsNull(), - // }; - // searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; - // } else { - // } + if (!body.isAll) { + checkChildConditions = { + orgChild4Id: IsNull(), + }; + searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; + } else { + } } else if (body.type === 4) { typeCondition = { ...(cannotViewChild4PosMaster ? { orgChild4Id: null } : { orgChild4Id: body.id }), @@ -5379,7 +5346,7 @@ export class PositionController extends Controller { (masterId.length > 0 ? { id: In(masterId) } : { posMasterNo: Like(`%${body.keyword}%`) })), - ...(!body.isAll && { isCondition: true }), + current_holderId: IsNull(), }, ]; let [posMaster, total] = await AppDataSource.getRepository(PosMaster) @@ -5448,15 +5415,15 @@ export class PositionController extends Controller { new Brackets((qb) => { qb.andWhere( body.keyword != null && body.keyword != "" - ? `CASE WHEN posMaster.orgChild1 is null THEN ${searchShortName0} WHEN posMaster.orgChild2 is null THEN ${searchShortName1} WHEN posMaster.orgChild3 is null THEN ${searchShortName2} WHEN posMaster.orgChild4 is null THEN ${searchShortName3} ELSE ${searchShortName4} END LIKE '%${body.keyword}%'` + ? body.isAll == false + ? searchShortName + : `CASE WHEN posMaster.orgChild1 is null THEN ${searchShortName0} WHEN posMaster.orgChild2 is null THEN ${searchShortName1} WHEN posMaster.orgChild3 is null THEN ${searchShortName2} WHEN posMaster.orgChild4 is null THEN ${searchShortName3} ELSE ${searchShortName4} END LIKE '%${body.keyword}%'` : "1=1", ) .andWhere(checkChildConditions) .andWhere(typeCondition) - .andWhere(revisionCondition); - if (!body.isAll) { - qb.andWhere({ isCondition: true }); - } + .andWhere(revisionCondition) + .andWhere({ current_holderId: IsNull() }); }), ) .orWhere( @@ -5466,10 +5433,8 @@ export class PositionController extends Controller { ) .andWhere(checkChildConditions) .andWhere(typeCondition) - .andWhere(revisionCondition); - if (!body.isAll) { - qb.andWhere({ isCondition: true }); - } + .andWhere(revisionCondition) + .andWhere({ current_holderId: IsNull() }); }), ) .orderBy("orgRoot.orgRootOrder", "ASC") diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index dbbecb80..15461d31 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -1679,84 +1679,35 @@ export class ProfileController extends Controller { // ประวัติพ้นจากราชการ let retires = []; const currentDate = new Date(); + // todo: รอข้อสรุป + // const retire_raw = await this.salaryRepo.findOne({ + // where: { + // profileId: id, + // commandCode: In(["12", "15", "16"]), + // }, + // order: { order: "desc" }, + // }); - // commandCode ที่ถือว่าออกจากราชการ - const retireCommandCodes = ["12", "15", "16"]; + // if (retire_raw) { + // const startDate = retire_raw.commandDateAffect; - // ดึงข้อมูล profileSalary ทั้งหมดเพื่อหาประวัติพ้นจากราชการ - const salaries = await this.salaryRepo.find({ - where: { profileId: id }, - order: { order: "ASC" }, - }); + // // คำนวณจำนวนวันจากวันพ้นสภาพถึงปัจจุบัน + // let daysCount = 0; + // if (startDate) { + // const start = new Date(startDate); + // daysCount = Math.ceil((currentDate.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); + // } - // มีคำสั่งพ้นราชการหรือไม่ - if ( - salaries.length > 0 && - salaries.some((s) => s.commandCode && retireCommandCodes.includes(s.commandCode)) - ) { - // กรองข้อมูลซ้ำตาม commandDateAffect - const uniqueSalaries = salaries.filter( - (item, index, self) => - index === - self.findIndex( - (t) => t.commandDateAffect?.getTime() === item.commandDateAffect?.getTime(), - ), - ); + // const startDateStr = startDate + // ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(startDate)) + // : "-"; - // วนลูปหาคู่ของ "ออกราชการ" และ "กลับเข้าราชการ" - for (let i = 0; i < uniqueSalaries.length; i++) { - const current = uniqueSalaries[i]; - - // เป็นคำสั่งออกจากราชการหรือไม่ - if (current.commandCode && retireCommandCodes.includes(current.commandCode)) { - const startDate = current.commandDateAffect; - let endDate: Date | null = null; - let endRecord = null; - - // หาคำสั่งถัดไปที่ไม่ใช่การออกจากราชการ (ถือว่ากลับเข้าราชการ) - for (let j = i + 1; j < uniqueSalaries.length; j++) { - const next = uniqueSalaries[j]; - if (next.commandCode && !retireCommandCodes.includes(next.commandCode)) { - endDate = next.commandDateAffect; - endRecord = next; - break; - } - } - - // ถ้าไม่เจอคำสั่งกลับเข้า ให้ใช้วันปัจจุบัน - if (!endDate) { - endDate = currentDate; - } - - // คำนวณจำนวนวัน - let daysCount = 0; - if (startDate && endDate) { - const start = new Date(startDate); - const end = new Date(endDate); - daysCount = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); - } - - // สร้าง detail จาก commandName + remark - const commandName = current.commandName || ""; - const remark = current.remark || ""; - const detail = `${commandName} ${remark}`.trim(); - - // แปลงวันที่เป็น format ไทย - const startDateStr = startDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(startDate)) - : "-"; - const endDateStr = endDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(endDate)) - : "-"; - - retires.push({ - date: `${startDateStr} - ${endDateStr}`, - detail: detail || "-", - day: daysCount > 0 ? Extension.ToThaiNumber(daysCount.toLocaleString()) : "-", - }); - } - } - } + // retires.push({ + // date: `${startDateStr}`, + // detail: retire_raw.commandName ?? "-", + // day: daysCount > 0 ? Extension.ToThaiNumber(daysCount.toLocaleString()) : "-" + // }); + // } // กรณีไม่มีข้อมูล if (retires.length === 0) { @@ -12014,90 +11965,4 @@ export class ProfileController extends Controller { return new HttpSuccess(); } - - /** - * API ข้อมูลทะเบียนประวัติตาม keycloak สำหรับเช็คอินเข้าใช้งานระบบ - * - * @summary ข้อมูลทะเบียนประวัติตาม keycloak สำหรับเช็คอินเข้าใช้งานระบบ - * - */ - @Get("keycloak/position-checkin") - async getProfileByKeycloakForCheckin(@Request() request: { user: Record }) { - const userSub = request.user.sub; - const relations = [ - "current_holders", - "current_holders.orgRoot", - "current_holders.orgChild1", - "current_holders.orgChild2", - "current_holders.orgChild3", - "current_holders.orgChild4", - ]; - - const [officerProfile, orgRevisionPublish] = await Promise.all([ - this.profileRepo.findOne({ - where: { keycloak: userSub }, - relations, - }), - this.orgRevisionRepo.findOne({ - where: { - orgRevisionIsDraft: false, - orgRevisionIsCurrent: true, - }, - }), - ]); - - if (!orgRevisionPublish) { - throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบแบบร่างโครงสร้าง"); - } - - let profile: any = officerProfile; - let profileType: "OFFICER" | "EMPLOYEE" = "OFFICER"; - - if (!profile) { - profile = await this.profileEmpRepo.findOne({ - where: { keycloak: userSub }, - relations, - }); - profileType = "EMPLOYEE"; - } - - if (!profile) { - if (request.user.role.includes("SUPER_ADMIN")) { - return new HttpSuccess(null); - } - throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลบุคคลนี้ในระบบ"); - } - - const currentHolder = - profile.current_holders?.find((x: any) => x.orgRevisionId == orgRevisionPublish.id) ?? null; - const root = currentHolder?.orgRoot ?? null; - const child1 = currentHolder?.orgChild1 ?? null; - const child2 = currentHolder?.orgChild2 ?? null; - const child3 = currentHolder?.orgChild3 ?? null; - const child4 = currentHolder?.orgChild4 ?? null; - - const _profile: any = { - profileId: profile.id, - keycloak: profile.keycloak, - prefix: profile.prefix, - avatar: profile.avatar, - profileType, - isProbation: profile.isProbation, - avatarName: profile.avatarName, - firstName: profile.firstName, - lastName: profile.lastName, - citizenId: profile.citizenId, - root: root?.orgRootName ?? null, - child1: child1?.orgChild1Name ?? null, - child2: child2?.orgChild2Name ?? null, - child3: child3?.orgChild3Name ?? null, - child4: child4?.orgChild4Name ?? null, - privacyCheckin: profile.privacyCheckin, - privacyUser: profile.privacyUser, - privacyMgt: profile.privacyMgt, - ...(profileType !== "OFFICER" ? { type: profile.employeeClass } : {}), - }; - - return new HttpSuccess(_profile); - } } diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 8ae134c1..2a7fba38 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -1950,78 +1950,35 @@ export class ProfileEmployeeController extends Controller { // ประวัติพ้นจากราชการ let retires = []; const currentDate = new Date(); + // todo: รอข้อสรุป + // const retire_raw = await this.salaryRepo.findOne({ + // where: { + // profileEmployeeId: id, + // commandCode: In(["12", "15", "16"]), + // }, + // order: { order: "desc" }, + // }); - // commandCode ที่ถือว่าออกจากราชการ - const retireCommandCodes = ["12", "15", "16"]; + // if (retire_raw) { + // const startDate = retire_raw.commandDateAffect; - // ดึงข้อมูล profileSalary ทั้งหมดเพื่อหาประวัติพ้นจากราชการ - const salaries = await this.salaryRepo.find({ - where: { profileEmployeeId: id }, - order: { order: "ASC" }, - }); + // // คำนวณจำนวนวันจากวันพ้นสภาพถึงปัจจุบัน + // let daysCount = 0; + // if (startDate) { + // const start = new Date(startDate); + // daysCount = Math.ceil((currentDate.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); + // } - // มีคำสั่งพ้นราชการหรือไม่ - if (salaries.length > 0 && salaries.some((s) => s.commandCode && - retireCommandCodes.includes(s.commandCode))) { - // กรองข้อมูลซ้ำตาม commandDateAffect - const uniqueSalaries = salaries.filter((item, index, self) => - index === self.findIndex((t) => t.commandDateAffect?.getTime() === item.commandDateAffect?.getTime()) - ); + // const startDateStr = startDate + // ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(startDate)) + // : "-"; - // วนลูปหาคู่ของ "ออกราชการ" และ "กลับเข้าราชการ" - for (let i = 0; i < uniqueSalaries.length; i++) { - const current = uniqueSalaries[i]; - - // เป็นคำสั่งออกจากราชการหรือไม่ - if (current.commandCode && retireCommandCodes.includes(current.commandCode)) { - const startDate = current.commandDateAffect; - let endDate: Date | null = null; - let endRecord = null; - - // หาคำสั่งถัดไปที่ไม่ใช่การออกจากราชการ (ถือว่ากลับเข้าราชการ) - for (let j = i + 1; j < uniqueSalaries.length; j++) { - const next = uniqueSalaries[j]; - if (next.commandCode && !retireCommandCodes.includes(next.commandCode)) { - endDate = next.commandDateAffect; - endRecord = next; - break; - } - } - - // ถ้าไม่เจอคำสั่งกลับเข้า ให้ใช้วันปัจจุบัน - if (!endDate) { - endDate = currentDate; - } - - // คำนวณจำนวนวัน - let daysCount = 0; - if (startDate && endDate) { - const start = new Date(startDate); - const end = new Date(endDate); - daysCount = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); - } - - // สร้าง detail จาก commandName + remark - const commandName = current.commandName || ""; - const remark = current.remark || ""; - const detail = `${commandName} ${remark}`.trim(); - - // แปลงวันที่เป็น format ไทย - const startDateStr = startDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(startDate)) - : "-"; - const endDateStr = endDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(endDate)) - : "-"; - - retires.push({ - date: `${startDateStr} - ${endDateStr}`, - detail: detail || "-", - day: daysCount > 0 ? Extension.ToThaiNumber(daysCount.toLocaleString()) : "-" - }); - } - } - } + // retires.push({ + // date: `${startDateStr} - ปัจจุบัน`, + // detail: retire_raw.commandName ?? "-", + // day: daysCount > 0 ? Extension.ToThaiNumber(daysCount.toLocaleString()) : "-" + // }); + // } // กรณีไม่มีข้อมูล if (retires.length === 0) { diff --git a/src/controllers/ProfileGovernmentController.ts b/src/controllers/ProfileGovernmentController.ts index 9af40339..8caaff28 100644 --- a/src/controllers/ProfileGovernmentController.ts +++ b/src/controllers/ProfileGovernmentController.ts @@ -6,6 +6,8 @@ import HttpError from "../interfaces/http-error"; import { RequestWithUser } from "../middlewares/user"; import { Profile } from "../entities/Profile"; import { ProfileGovernment, UpdateProfileGovernment } from "../entities/ProfileGovernment"; +import { Position } from "../entities/Position"; +import { PosMaster } from "../entities/PosMaster"; import { calculateAge, calculateGovAge, @@ -13,6 +15,7 @@ import { setLogDataDiff, } from "../interfaces/utils"; import permission from "../interfaces/permission"; +import { OrgRevision } from "../entities/OrgRevision"; import { In } from "typeorm"; @Route("api/v1/org/profile/government") @Tags("ProfileGovernment") @@ -20,6 +23,9 @@ import { In } from "typeorm"; export class ProfileGovernmentHistoryController extends Controller { private profileRepo = AppDataSource.getRepository(Profile); private govRepo = AppDataSource.getRepository(ProfileGovernment); + private positionRepo = AppDataSource.getRepository(Position); + private posMasterRepo = AppDataSource.getRepository(PosMaster); + private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); /** * * @summary ข้อมูลราชการ @@ -27,6 +33,13 @@ export class ProfileGovernmentHistoryController extends Controller { */ @Get("user") public async getGovHistoryUser(@Request() request: { user: Record }) { + const orgRevision = await this.orgRevisionRepository.findOne({ + select: ["id"], + where: { + orgRevisionIsDraft: false, + orgRevisionIsCurrent: true, + }, + }); const profile = await this.profileRepo.findOneBy({ keycloak: request.user.sub }); if (!profile) { throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); @@ -38,19 +51,79 @@ export class ProfileGovernmentHistoryController extends Controller { posLevel: true, }, }); + const posMaster = await this.posMasterRepo.findOne({ + where: { + // orgRevision: { + // orgRevisionIsCurrent: true, + // orgRevisionIsDraft: false, + // }, + orgRevisionId: orgRevision?.id, + current_holderId: profile.id, + }, + order: { createdAt: "DESC" }, + relations: { + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + const position = await this.positionRepo.findOne({ + where: { + positionIsSelected: true, + posMaster: { + // orgRevision: { + // orgRevisionIsCurrent: true, + // orgRevisionIsDraft: false, + // }, + orgRevisionId: orgRevision?.id, + current_holderId: profile.id, + }, + }, + order: { createdAt: "DESC" }, + relations: { + posExecutive: true, + }, + }); + if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); - - // ดึงข้อมูลจาก profile ที่เก็บไว้แล้ว + const fullNameParts = [ + posMaster == null || posMaster.orgChild4 == null ? null : posMaster.orgChild4.orgChild4Name, + posMaster == null || posMaster.orgChild3 == null ? null : posMaster.orgChild3.orgChild3Name, + posMaster == null || posMaster.orgChild2 == null ? null : posMaster.orgChild2.orgChild2Name, + posMaster == null || posMaster.orgChild1 == null ? null : posMaster.orgChild1.orgChild1Name, + posMaster == null || posMaster.orgRoot == null ? null : posMaster.orgRoot.orgRootName, + ]; + const org = fullNameParts.filter((part) => part !== undefined && part !== null).join("\n"); + let orgShortName = ""; + if (posMaster != null) { + if (posMaster.orgChild1Id === null) { + orgShortName = posMaster.orgRoot?.orgRootShortName; + } else if (posMaster.orgChild2Id === null) { + orgShortName = posMaster.orgChild1?.orgChild1ShortName; + } else if (posMaster.orgChild3Id === null) { + orgShortName = posMaster.orgChild2?.orgChild2ShortName; + } else if (posMaster.orgChild4Id === null) { + orgShortName = posMaster.orgChild3?.orgChild3ShortName; + } else { + orgShortName = posMaster.orgChild4?.orgChild4ShortName; + } + } + //posMaster?.isSit แก้ไขชั่วคราว const data = { - org: record.org ?? null, //สังกัด - positionField: record.positionField ?? null, //สายงาน + org: org, //สังกัด + positionField: position == null || posMaster?.isSit ? null : position.positionField, //สายงาน position: record.position, //ตำแหน่ง posLevel: record.posLevel == null ? null : record.posLevel.posLevelName, //ระดับ - posMasterNo: record.posMasterNo ?? null, //เลขที่ตำแหน่ง + posMasterNo: posMaster == null ? null : `${orgShortName} ${posMaster.posMasterNo}`, //เลขที่ตำแหน่ง posType: record.posType == null ? null : record.posType.posTypeName, //ประเภท - posExecutive: record.posExecutive ?? null, //ตำแหน่งทางการบริหาร - positionArea: record.positionArea ?? null, //ด้าน/สาขา - positionExecutiveField: record.positionExecutiveField ?? null, //ด้านทางการบริหาร + posExecutive: + position == null || position.posExecutive == null || posMaster?.isSit + ? null + : position.posExecutive.posExecutiveName, //ตำแหน่งทางการบริหาร + positionArea: position == null || posMaster?.isSit ? null : position.positionArea, //ด้าน/สาขา + positionExecutiveField: position == null || posMaster?.isSit ? null : position.positionExecutiveField, //ด้านทางการบริหาร dateLeave: record.birthDate == null ? null : calculateRetireDate(record.birthDate), dateRetireLaw: record.dateRetireLaw ?? null, // govAge: record.dateStart == null ? null : calculateAge(record.dateStart), @@ -62,10 +135,10 @@ export class ProfileGovernmentHistoryController extends Controller { govAgePlus: record.govAgePlus, reasonSameDate: record.reasonSameDate, }; - + return new HttpSuccess(data); } - + /** * * @summary ข้อมูลราชการ @@ -77,17 +150,25 @@ export class ProfileGovernmentHistoryController extends Controller { let _workflow = await new permission().Workflow(req, profileId, "SYS_REGISTRY_OFFICER"); if (_workflow == false) await new permission().PermissionOrgUserGet(req, "SYS_REGISTRY_OFFICER", profileId); - + + const orgRevision = await this.orgRevisionRepository.findOne({ + select: ["id"], + where: { + orgRevisionIsDraft: false, + orgRevisionIsCurrent: true, + }, + }); + // ค้นหา profile ก่อน const record = await this.profileRepo.findOne({ where: { id: profileId }, relations: ["posType", "posLevel"], }); - + if (!record) { throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล profile"); } - + // ค้นหา profileSalary แยกต่างหาก const profileWithSalary = await this.profileRepo.findOne({ where: { @@ -120,13 +201,70 @@ export class ProfileGovernmentHistoryController extends Controller { }, }, }); - + // ใช้ profileSalary จาก query ที่สอง หรือ [] ถ้าไม่เจอ record.profileSalary = profileWithSalary?.profileSalary || []; - + const posMaster = await this.posMasterRepo.findOne({ + where: { + orgRevisionId: orgRevision?.id, + current_holderId: profileId, + }, + order: { createdAt: "DESC" }, + relations: { + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + const position = await this.positionRepo.findOne({ + where: { + positionIsSelected: true, + posMaster: { + orgRevisionId: orgRevision?.id, + current_holderId: profileId, + }, + }, + order: { createdAt: "DESC" }, + relations: { + posExecutive: true, + }, + }); + + // if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); + const fullNameParts = [ + posMaster == null || posMaster.orgChild4 == null ? null : posMaster.orgChild4.orgChild4Name, + posMaster == null || posMaster.orgChild3 == null ? null : posMaster.orgChild3.orgChild3Name, + posMaster == null || posMaster.orgChild2 == null ? null : posMaster.orgChild2.orgChild2Name, + posMaster == null || posMaster.orgChild1 == null ? null : posMaster.orgChild1.orgChild1Name, + posMaster == null || posMaster.orgRoot == null ? null : posMaster.orgRoot.orgRootName, + ]; + const org = fullNameParts.filter((part) => part !== undefined && part !== null).join("\n"); + let orgShortName = ""; + if (posMaster != null) { + if (posMaster.orgChild1Id === null) { + orgShortName = posMaster.orgRoot?.orgRootShortName ?? ""; + } else if (posMaster.orgChild2Id === null) { + orgShortName = posMaster.orgChild1?.orgChild1ShortName ?? ""; + } else if (posMaster.orgChild3Id === null) { + orgShortName = posMaster.orgChild2?.orgChild2ShortName ?? ""; + } else if (posMaster.orgChild4Id === null) { + orgShortName = posMaster.orgChild3?.orgChild3ShortName ?? ""; + } else { + orgShortName = posMaster.orgChild4?.orgChild4ShortName ?? ""; + } + } let _OrgLeave: any = []; let _profileSalary: any = null; if (record?.isLeave && record?.profileSalary.length > 0) { + // _OrgLeave = [ + // record?.profileSalary[0].orgChild4 ? record?.profileSalary[0].orgChild4 : null, + // record?.profileSalary[0].orgChild3 ? record?.profileSalary[0].orgChild3 : null, + // record?.profileSalary[0].orgChild2 ? record?.profileSalary[0].orgChild2 : null, + // record?.profileSalary[0].orgChild1 ? record?.profileSalary[0].orgChild1 : null, + // record?.profileSalary[0].orgRoot ? record?.profileSalary[0].orgRoot : null, + // ]; if (record.leaveType == "RETIRE") { _profileSalary = record?.profileSalary.length > 1 @@ -150,23 +288,27 @@ export class ProfileGovernmentHistoryController extends Controller { } } const orgLeave = _OrgLeave.filter((x: any) => x !== undefined && x !== null).join("\n"); - - // ดึงข้อมูลจาก profile ที่เก็บไว้แล้ว + //posMaster?.isSit แก้ไขชั่วคราว const data = { - org: record?.isLeave == false ? (record.org ?? null) : orgLeave, //สังกัด - positionField: record.positionField ?? null, //สายงาน + org: record?.isLeave == false ? org : orgLeave, //สังกัด + positionField: position == null || posMaster?.isSit ? null : position.positionField, //สายงาน position: record?.position, //ตำแหน่ง posLevel: record?.posLevel == null ? null : record?.posLevel.posLevelName, //ระดับ posMasterNo: record?.isLeave == false - ? record.posMasterNo ?? null + ? posMaster == null + ? null + : `${orgShortName} ${posMaster.posMasterNo}` : _profileSalary != null ? `${_profileSalary.posNoAbb} ${_profileSalary.posNo}` : null, //เลขที่ตำแหน่ง posType: record?.posType == null ? null : record?.posType.posTypeName, //ประเภท - posExecutive: record.posExecutive ?? null, //ตำแหน่งทางการบริหาร - positionArea: record.positionArea ?? null, //ด้าน/สาขา - positionExecutiveField: record.positionExecutiveField ?? null, //ด้านทางการบริหาร + posExecutive: + position == null || position.posExecutive == null || posMaster?.isSit + ? null + : position.posExecutive.posExecutiveName, //ตำแหน่งทางการบริหาร + positionArea: position == null || posMaster?.isSit ? null : position.positionArea, //ด้าน/สาขา + positionExecutiveField: position == null || posMaster?.isSit ? null : position.positionExecutiveField, //ด้านทางการบริหาร dateLeave: record?.birthDate == null ? null : calculateRetireDate(record?.birthDate), dateRetireLaw: record?.dateRetireLaw ?? null, // govAge: record?.dateStart == null ? null : calculateAge(record?.dateStart), @@ -178,22 +320,30 @@ export class ProfileGovernmentHistoryController extends Controller { govAgePlus: record?.govAgePlus, reasonSameDate: record?.reasonSameDate, }; - + return new HttpSuccess(data); } - + @Get("admin/{profileId}") public async getGovHistoryAdmin(@Path() profileId: string) { + const orgRevision = await this.orgRevisionRepository.findOne({ + select: ["id"], + where: { + orgRevisionIsDraft: false, + orgRevisionIsCurrent: true, + }, + }); + // ค้นหา profile ก่อน const record = await this.profileRepo.findOne({ where: { id: profileId }, relations: ["posType", "posLevel"], }); - + if (!record) { throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล profile"); } - + // ค้นหา profileSalary แยกต่างหาก const profileWithSalary = await this.profileRepo.findOne({ where: { @@ -226,13 +376,70 @@ export class ProfileGovernmentHistoryController extends Controller { }, }, }); - + // ใช้ profileSalary จาก query ที่สอง หรือ [] ถ้าไม่เจอ record.profileSalary = profileWithSalary?.profileSalary || []; - + const posMaster = await this.posMasterRepo.findOne({ + where: { + orgRevisionId: orgRevision?.id, + current_holderId: profileId, + }, + order: { createdAt: "DESC" }, + relations: { + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + const position = await this.positionRepo.findOne({ + where: { + positionIsSelected: true, + posMaster: { + orgRevisionId: orgRevision?.id, + current_holderId: profileId, + }, + }, + order: { createdAt: "DESC" }, + relations: { + posExecutive: true, + }, + }); + + // if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); + const fullNameParts = [ + posMaster == null || posMaster.orgChild4 == null ? null : posMaster.orgChild4.orgChild4Name, + posMaster == null || posMaster.orgChild3 == null ? null : posMaster.orgChild3.orgChild3Name, + posMaster == null || posMaster.orgChild2 == null ? null : posMaster.orgChild2.orgChild2Name, + posMaster == null || posMaster.orgChild1 == null ? null : posMaster.orgChild1.orgChild1Name, + posMaster == null || posMaster.orgRoot == null ? null : posMaster.orgRoot.orgRootName, + ]; + const org = fullNameParts.filter((part) => part !== undefined && part !== null).join("\n"); + let orgShortName = ""; + if (posMaster != null) { + if (posMaster.orgChild1Id === null) { + orgShortName = posMaster.orgRoot?.orgRootShortName; + } else if (posMaster.orgChild2Id === null) { + orgShortName = posMaster.orgChild1?.orgChild1ShortName; + } else if (posMaster.orgChild3Id === null) { + orgShortName = posMaster.orgChild2?.orgChild2ShortName; + } else if (posMaster.orgChild4Id === null) { + orgShortName = posMaster.orgChild3?.orgChild3ShortName; + } else { + orgShortName = posMaster.orgChild4?.orgChild4ShortName; + } + } let _OrgLeave: any = []; let _profileSalary: any = null; if (record?.isLeave && record?.profileSalary.length > 0) { + // _OrgLeave = [ + // record?.profileSalary[0].orgChild4 ? record?.profileSalary[0].orgChild4 : null, + // record?.profileSalary[0].orgChild3 ? record?.profileSalary[0].orgChild3 : null, + // record?.profileSalary[0].orgChild2 ? record?.profileSalary[0].orgChild2 : null, + // record?.profileSalary[0].orgChild1 ? record?.profileSalary[0].orgChild1 : null, + // record?.profileSalary[0].orgRoot ? record?.profileSalary[0].orgRoot : null, + // ]; if (record.leaveType == "RETIRE") { _profileSalary = record?.profileSalary.length > 1 @@ -256,23 +463,27 @@ export class ProfileGovernmentHistoryController extends Controller { } } const orgLeave = _OrgLeave.filter((x: any) => x !== undefined && x !== null).join("\n"); - - // ดึงข้อมูลจาก profile ที่เก็บไว้แล้ว + //posMaster?.isSit แก้ไขชั่วคราว const data = { - org: record?.isLeave == false ? (record.org ?? null) : orgLeave, //สังกัด - positionField: record.positionField ?? null, //สายงาน + org: record?.isLeave == false ? org : orgLeave, //สังกัด + positionField: position == null || posMaster?.isSit ? null : position.positionField, //สายงาน position: record?.position, //ตำแหน่ง posLevel: record?.posLevel == null ? null : record?.posLevel.posLevelName, //ระดับ posMasterNo: record?.isLeave == false - ? record.posMasterNo ?? null + ? posMaster == null + ? null + : `${orgShortName} ${posMaster.posMasterNo}` : _profileSalary != null ? `${_profileSalary.posNoAbb} ${_profileSalary.posNo}` : null, //เลขที่ตำแหน่ง posType: record?.posType == null ? null : record?.posType.posTypeName, //ประเภท - posExecutive: record.posExecutive ?? null, //ตำแหน่งทางการบริหาร - positionArea: record.positionArea ?? null, //ด้าน/สาขา - positionExecutiveField: record.positionExecutiveField ?? null, //ด้านทางการบริหาร + posExecutive: + position == null || position.posExecutive == null || posMaster?.isSit + ? null + : position.posExecutive.posExecutiveName, //ตำแหน่งทางการบริหาร + positionArea: position == null || posMaster?.isSit ? null : position.positionArea, //ด้าน/สาขา + positionExecutiveField: position == null || posMaster?.isSit ? null : position.positionExecutiveField, //ด้านทางการบริหาร dateLeave: record?.birthDate == null ? null : calculateRetireDate(record?.birthDate), dateRetireLaw: record?.dateRetireLaw ?? null, // govAge: record?.dateStart == null ? null : calculateAge(record?.dateStart), @@ -285,10 +496,10 @@ export class ProfileGovernmentHistoryController extends Controller { reasonSameDate: record?.reasonSameDate, isLeave: record?.isLeave, }; - + return new HttpSuccess(data); } - + /** * * @summary ประวัติข้อมูลราชการ by keycloak @@ -306,7 +517,7 @@ export class ProfileGovernmentHistoryController extends Controller { }); return new HttpSuccess(record); } - + /** * * @summary ประวัติข้อมูลราชการ @@ -322,12 +533,12 @@ export class ProfileGovernmentHistoryController extends Controller { order: { lastUpdatedAt: "DESC" }, where: { profileId: profileId }, }); - + // record.pop(); - + return new HttpSuccess(record); } - + /** * * @summary แก้ไขข้อมูลราชการ @@ -343,14 +554,14 @@ export class ProfileGovernmentHistoryController extends Controller { const record = await this.profileRepo.findOne({ where: { id: profileId }, }); - + if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); const before = structuredClone(record); const history = new ProfileGovernment(); - + Object.assign(record, body); Object.assign(history, { ...record, id: undefined }); - + history.profileId = profileId; record.lastUpdateUserId = req.user.sub; record.lastUpdateFullName = req.user.name; @@ -361,14 +572,13 @@ export class ProfileGovernmentHistoryController extends Controller { history.createdFullName = req.user.name; history.createdAt = new Date(); history.lastUpdatedAt = new Date(); - + await Promise.all([ this.profileRepo.save(record, { data: req }), setLogDataDiff(req, { before, after: record }), this.govRepo.save(history, { data: req }), ]); - + return new HttpSuccess(); } } - \ No newline at end of file diff --git a/src/controllers/ProfileSalaryController.ts b/src/controllers/ProfileSalaryController.ts index 4e3e9e3b..c8193750 100644 --- a/src/controllers/ProfileSalaryController.ts +++ b/src/controllers/ProfileSalaryController.ts @@ -23,7 +23,6 @@ import { ProfileEmployee } from "../entities/ProfileEmployee"; import { In, IsNull, LessThan, MoreThan, Not } from "typeorm"; import permission from "../interfaces/permission"; import { setLogDataDiff } from "../interfaces/utils"; -import { calculateTenure } from "../utils/tenure"; import { TenurePositionOfficer } from "../entities/TenurePositionOfficer"; import { TenureLevelOfficer } from "../entities/TenureLevelOfficer"; import { TenurePositionEmployee } from "../entities/TenurePositionEmployee"; @@ -66,12 +65,10 @@ export class ProfileSalaryController extends Controller { await this.positionOfficerRepo.clear(); const profile = await this.profileRepo.find(); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); - const baseCurrentDate = CURRENT_DATE[0].today; + let _currentDate = CURRENT_DATE[0].today; for await (const x of profile) { - // Use leave date if available and valid, otherwise use current date - let _currentDate = baseCurrentDate; - if (x.isLeave && x.leaveDate) { - _currentDate = Extension.toDateOnlyString(x.leaveDate); + if (x.isLeave) { + _currentDate = x.leaveDate ? Extension.toDateOnlyString(x.leaveDate) : _currentDate; } const position = await AppDataSource.query("CALL GetProfileSalaryPosition(?, ?)", [ x.id, @@ -95,18 +92,21 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionName: null }, ); - const { year, month, day } = calculateTenure(calDayDiff.days_diff); const mapData: any = { profileId: x.id, positionName: calDayDiff.positionName, days_diff: calDayDiff.days_diff, - Years: year, - Months: month, - Days: day, + // Years: (calDayDiff.days_diff / 365.2524).toFixed(4), + // Months: ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), + // Days: (calDayDiff.days_diff % 30.4375).toFixed(4), + Years: Math.floor(calDayDiff.days_diff / 365.2524), + Months: Math.floor((calDayDiff.days_diff / 30.4375) % 12), + Days: Math.floor(calDayDiff.days_diff % 30.4375), }; - data.push(mapData); + // data.push(_mapData); + await this.positionOfficerRepo.save(mapData); } - await this.positionOfficerRepo.save(data); + // await this.positionOfficerRepo.save(data); return new HttpSuccess(); } @@ -115,13 +115,11 @@ export class ProfileSalaryController extends Controller { let data: any = []; await this.positionEmployeeRepo.clear(); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); - const baseCurrentDate = CURRENT_DATE[0].today; + let _currentDate = CURRENT_DATE[0].today; const profile = await this.profileEmployeeRepo.find(); for await (const x of profile) { - // Use leave date if available and valid, otherwise use current date - let _currentDate = baseCurrentDate; - if (x?.isLeave && x.leaveDate) { - _currentDate = Extension.toDateOnlyString(x.leaveDate); + if (x?.isLeave) { + _currentDate = x.leaveDate ? Extension.toDateOnlyString(x.leaveDate) : _currentDate; } const position = await AppDataSource.query("CALL GetProfileEmployeeSalaryPosition(?, ?)", [ x.id, @@ -145,18 +143,21 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionName: null }, ); - const { year, month, day } = calculateTenure(calDayDiff.days_diff); const mapData: any = { profileEmployeeId: x.id, positionName: calDayDiff.positionName, days_diff: calDayDiff.days_diff, - Years: year, - Months: month, - Days: day, + // Years: (calDayDiff.days_diff / 365.2524).toFixed(4), + // Months: ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), + // Days: (calDayDiff.days_diff % 30.4375).toFixed(4), + Years: Math.floor(calDayDiff.days_diff / 365.2524), + Months: Math.floor((calDayDiff.days_diff / 30.4375) % 12), + Days: Math.floor(calDayDiff.days_diff % 30.4375), }; - data.push(mapData); + // data.push(_mapData); + await this.positionEmployeeRepo.save(mapData); } - await this.positionEmployeeRepo.save(data); + // await this.positionEmployeeRepo.save(data); return new HttpSuccess(); } @@ -166,12 +167,10 @@ export class ProfileSalaryController extends Controller { await this.levelOfficerRepo.clear(); const profile = await this.profileRepo.find({ relations: ["posLevel", "posType"] }); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); - const baseCurrentDate = CURRENT_DATE[0].today; + let _currentDate = CURRENT_DATE[0].today; for await (const x of profile) { - // Use leave date if available and valid, otherwise use current date - let _currentDate = baseCurrentDate; - if (x?.isLeave && x.leaveDate) { - _currentDate = Extension.toDateOnlyString(x.leaveDate); + if (x?.isLeave) { + _currentDate = x.leaveDate ? Extension.toDateOnlyString(x.leaveDate) : _currentDate; } const positionLevel = await AppDataSource.query("CALL GetProfileSalaryLevel(?, ?)", [ x.id, @@ -203,20 +202,20 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionType: null, positionLevel: null, positionCee: null }, ); - const { year, month, day } = calculateTenure(calDayDiff.days_diff); const mapData: any = { profileId: x.id, positionType: calDayDiff.positionType, positionLevel: calDayDiff.positionLevel, positionCee: calDayDiff.positionCee, days_diff: calDayDiff.days_diff, - Years: x.posLevel == null ? 0 : year.toFixed(4), - Months: x.posLevel == null ? 0 : month.toFixed(4), - Days: x.posLevel == null ? 0 : day.toFixed(4), + Years: x.posLevel == null ? 0 : (calDayDiff.days_diff / 365.2524).toFixed(4), + Months: x.posLevel == null ? 0 : ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), + Days: x.posLevel == null ? 0 : (calDayDiff.days_diff % 30.4375).toFixed(4), }; - data.push(mapData); + // data.push(_mapData); + await this.levelOfficerRepo.save(mapData); } - await this.levelOfficerRepo.save(data); + // await this.levelOfficerRepo.save(data); return new HttpSuccess(); } @@ -226,12 +225,10 @@ export class ProfileSalaryController extends Controller { await this.levelEmployeeRepo.clear(); const profile = await this.profileEmployeeRepo.find({ relations: ["posLevel", "posType"] }); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); - const baseCurrentDate = CURRENT_DATE[0].today; + let _currentDate = CURRENT_DATE[0].today; for await (const x of profile) { - // Use leave date if available and valid, otherwise use current date - let _currentDate = baseCurrentDate; - if (x?.isLeave && x.leaveDate) { - _currentDate = Extension.toDateOnlyString(x.leaveDate); + if (x?.isLeave) { + _currentDate = x.leaveDate ? Extension.toDateOnlyString(x.leaveDate) : _currentDate; } const positionLevel = await AppDataSource.query("CALL GetProfileEmployeeSalaryLevel(?, ?)", [ x.id, @@ -263,27 +260,26 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionType: null, positionLevel: null, positionCee: null }, ); - const { year, month, day } = calculateTenure(calDayDiff.days_diff); const mapData: any = { profileEmployeeId: x.id, positionType: calDayDiff.positionType, positionLevel: calDayDiff.positionLevel, positionCee: calDayDiff.positionCee, days_diff: calDayDiff.days_diff, - Years: x.posLevel == null ? 0 : year.toFixed(4), - Months: x.posLevel == null ? 0 : month.toFixed(4), - Days: x.posLevel == null ? 0 : day.toFixed(4), + Years: x.posLevel == null ? 0 : (calDayDiff.days_diff / 365.2524).toFixed(4), + Months: x.posLevel == null ? 0 : ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), + Days: x.posLevel == null ? 0 : (calDayDiff.days_diff % 30.4375).toFixed(4), }; - data.push(mapData); + // data.push(_mapData); + await this.levelEmployeeRepo.save(mapData); } - await this.levelEmployeeRepo.save(data); + // await this.levelEmployeeRepo.save(data); return new HttpSuccess(); } @Get("TenurePositionExecutiveOfficer") public async cronjobTenureExecutivePositionOfficer() { - let data: any = []; await this.positionExecutiveOfficerRepo.clear(); const profile = await this.profileRepo.find(); const orgRevision = await this.orgRevisionRepository.findOne({ @@ -294,12 +290,10 @@ export class ProfileSalaryController extends Controller { }, }); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); - const baseCurrentDate = CURRENT_DATE[0].today; + let _currentDate = CURRENT_DATE[0].today; for await (const x of profile) { - // Use leave date if available and valid, otherwise use current date - let _currentDate = baseCurrentDate; - if (x?.isLeave && x.leaveDate) { - _currentDate = Extension.toDateOnlyString(x.leaveDate); + if (x?.isLeave) { + _currentDate = x.leaveDate ? Extension.toDateOnlyString(x.leaveDate) : _currentDate; } const position = await this.positionRepo.findOne({ where: { @@ -337,18 +331,16 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionExecutive: null }, ); - const { year, month, day } = calculateTenure(calDayDiff.days_diff); const mapData: any = { profileId: x.id, positionExecutiveName: calDayDiff.positionExecutive, days_diff: calDayDiff.days_diff, - Years: year.toFixed(4), - Months: month.toFixed(4), - Days: day.toFixed(4), + Years: (calDayDiff.days_diff / 365.2524).toFixed(4), + Months: ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), + Days: (calDayDiff.days_diff % 30.4375).toFixed(4), }; - data.push(mapData); + await this.positionExecutiveOfficerRepo.save(mapData); } - await this.positionExecutiveOfficerRepo.save(data); return new HttpSuccess(); } @@ -610,10 +602,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + // Recalculate year, month, and day + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, @@ -649,10 +641,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + // Recalculate year, month, and day + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, @@ -683,10 +675,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + // Recalculate year, month, and day + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, @@ -747,10 +739,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + // Recalculate year, month, and day + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, @@ -790,10 +782,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + // Recalculate year, month, and day + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, @@ -827,10 +819,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + // Recalculate year, month, and day + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, diff --git a/src/controllers/ProfileSalaryEmployeeController.ts b/src/controllers/ProfileSalaryEmployeeController.ts index 7428e913..5b87003c 100644 --- a/src/controllers/ProfileSalaryEmployeeController.ts +++ b/src/controllers/ProfileSalaryEmployeeController.ts @@ -27,7 +27,6 @@ import { Profile } from "../entities/Profile"; import { In, LessThan, IsNull, MoreThan } from "typeorm"; import permission from "../interfaces/permission"; import { setLogDataDiff } from "../interfaces/utils"; -import { calculateTenure } from "../utils/tenure"; import { Command } from "../entities/Command"; import { OrgRoot } from "../entities/OrgRoot"; import Extension from "../interfaces/extension"; @@ -176,10 +175,9 @@ export class ProfileSalaryEmployeeController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, @@ -213,10 +211,9 @@ export class ProfileSalaryEmployeeController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, @@ -269,10 +266,9 @@ export class ProfileSalaryEmployeeController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, @@ -306,10 +302,9 @@ export class ProfileSalaryEmployeeController extends Controller { acc.push(existing); } - const { year, month, day } = calculateTenure(existing.days); - existing.year = year; - existing.month = month; - existing.day = day; + existing.year = Math.floor(existing.days / 365.2524); + existing.month = Math.floor((existing.days / 30.4375) % 12); + existing.day = Math.ceil(existing.days % 30.4375); return acc; }, diff --git a/src/controllers/ProfileSalaryTempController.ts b/src/controllers/ProfileSalaryTempController.ts index 49c757dc..fc6a9df5 100644 --- a/src/controllers/ProfileSalaryTempController.ts +++ b/src/controllers/ProfileSalaryTempController.ts @@ -1433,10 +1433,10 @@ export class ProfileSalaryTempController extends Controller { profileEmployeeId: x.profileEmployeeId, dateStart: x.commandDateAffect, dateEnd: null, - posNo: `${x.posNoAbb ?? ""} ${x.posNo ?? ""}`.trim(), + posNo: `${x.posNoAbb} ${x.posNo}`, position: x.positionName, commandId: x.commandId, - refCommandNo: [x.commandNo, x.commandYear].filter(Boolean).join("/") || undefined, + refCommandNo: `${x.commandNo}/${x.commandYear}`, refCommandDate: x.commandDateAffect, status: false, isDeleted: false, @@ -1456,7 +1456,7 @@ export class ProfileSalaryTempController extends Controller { dateStart: x.commandDateAffect, dateEnd: null, commandId: x.commandId, - commandNo: [x.commandNo, x.commandYear].filter(Boolean).join("/") || undefined, + commandNo: `${x.commandNo}/${x.commandYear}`, commandName: x.commandName ?? "ให้ช่วยราชการ", refCommandDate: x.commandDateSign, refId: x.refId, diff --git a/src/controllers/WorkflowController.ts b/src/controllers/WorkflowController.ts index bc1d37c4..0609c932 100644 --- a/src/controllers/WorkflowController.ts +++ b/src/controllers/WorkflowController.ts @@ -237,18 +237,11 @@ export class WorkflowController extends Controller { savedStates.find((state) => state.id === so.stateId && state.order === 1), ); - // add link sysName = REGISTRY_PROFILE or REGISTRY_PROFILE_EMP - let notiLink = ''; - if (body.sysName === 'REGISTRY_PROFILE') { - notiLink = `${process.env.VITE_URL_MGT}/registry-officer/request-edit/personal/${body.refId}`; - } else if (body.sysName === 'REGISTRY_PROFILE_EMP') { - notiLink = `${process.env.VITE_URL_MGT}/registry-employee/request-edit/personal/${body.refId}`; - } const notificationReceivers = stateOperatorUsersToCreate .filter((user) => firstStateOperators.some((op) => op.operator === user.operator)) .map((user) => ({ receiverUserId: user.profileType === "OFFICER" ? user.profileId : user.profileEmployeeId, - notiLink: notiLink, + notiLink: "", })); // ส่ง notification แบบ fire-and-forget diff --git a/src/entities/Profile.ts b/src/entities/Profile.ts index a875a969..72a1d505 100644 --- a/src/entities/Profile.ts +++ b/src/entities/Profile.ts @@ -140,54 +140,6 @@ export class Profile extends EntityBase { }) posTypeId: string | null; - @Column({ - nullable: true, - comment: "สายงาน", - length: 45, - default: null, - }) - positionField: string; - - @Column({ - nullable: true, - comment: "ตำแหน่งทางการบริหาร", - length: 255, - default: null, - }) - posExecutive?: string; - - @Column({ - nullable: true, - comment: "ด้าน/สาขา", - length: 255, - default: null, - }) - positionArea?: string; - - @Column({ - nullable: true, - comment: "ด้านทางการบริหาร", - length: 255, - default: null, - }) - positionExecutiveField?: string; - - @Column({ - nullable: true, - comment: "เลขที่ตำแหน่ง", - length: 255, - default: null, - }) - posMasterNo?: string; - - @Column({ - nullable: true, - comment: "สังกัด", - type: "text", - default: null, - }) - org?: string; - @Column({ nullable: true, length: 255, diff --git a/src/migration/1776308026834-add_position_fields_to_profile.ts b/src/migration/1776308026834-add_position_fields_to_profile.ts deleted file mode 100644 index 9b214460..00000000 --- a/src/migration/1776308026834-add_position_fields_to_profile.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class AddPositionFieldsToProfile1776308026834 implements MigrationInterface { - name = 'AddPositionFieldsToProfile1776308026834' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`profile\` ADD \`positionField\` varchar(45) NULL COMMENT 'สายงาน'`); - await queryRunner.query(`ALTER TABLE \`profile\` ADD \`posExecutive\` varchar(255) NULL COMMENT 'ตำแหน่งทางการบริหาร'`); - await queryRunner.query(`ALTER TABLE \`profile\` ADD \`positionArea\` varchar(255) NULL COMMENT 'ด้าน/สาขา'`); - await queryRunner.query(`ALTER TABLE \`profile\` ADD \`positionExecutiveField\` varchar(255) NULL COMMENT 'ด้านทางการบริหาร'`); - await queryRunner.query(`ALTER TABLE \`profile\` ADD \`posMasterNo\` varchar(255) NULL COMMENT 'เลขที่ตำแหน่ง'`); - await queryRunner.query(`ALTER TABLE \`profile\` ADD \`org\` text NULL COMMENT 'สังกัด'`); - - await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`positionField\` varchar(45) NULL COMMENT 'สายงาน'`); - await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`posExecutive\` varchar(255) NULL COMMENT 'ตำแหน่งทางการบริหาร'`); - await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`positionArea\` varchar(255) NULL COMMENT 'ด้าน/สาขา'`); - await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`positionExecutiveField\` varchar(255) NULL COMMENT 'ด้านทางการบริหาร'`); - await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`posMasterNo\` varchar(255) NULL COMMENT 'เลขที่ตำแหน่ง'`); - await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`org\` text NULL COMMENT 'สังกัด'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`org\``); - await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`posMasterNo\``); - await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`positionExecutiveField\``); - await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`positionArea\``); - await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`posExecutive\``); - await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`positionField\``); - - await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`org\``); - await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`posMasterNo\``); - await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`positionExecutiveField\``); - await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`positionArea\``); - await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`posExecutive\``); - await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`positionField\``); - } -} diff --git a/src/services/ActingPositionService.ts b/src/services/ActingPositionService.ts deleted file mode 100644 index e5a0b601..00000000 --- a/src/services/ActingPositionService.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { AppDataSource } from "../database/data-source"; -import { AuthRoleAttr } from "../entities/AuthRoleAttr"; -import { PosMasterAct } from "../entities/PosMasterAct"; - -export interface ActingPositionData { - isAct: boolean; - posMasterActs: Array<{ - privilege: string | null; - posNo: string | null; - rootDnaId: string | null; - child1DnaId: string | null; - child2DnaId: string | null; - child3DnaId: string | null; - child4DnaId: string | null; - }>; -} - -export interface ActingPositionWithPrivilegeData extends ActingPositionData { - privilege?: string | null; -} - -/** - * Service สำหรับจัดการข้อมูลตำแหน่งที่รักษาการและ privilege - */ -export class ActingPositionService { - private posMasterActRepo = AppDataSource.getRepository(PosMasterAct); - private authRoleAttrRepo = AppDataSource.getRepository(AuthRoleAttr); - - /** - * ดึงข้อมูลตำแหน่งที่รักษาการและ privilege - * - * @param profileId - ID ของ profile ที่ต้องการตรวจสอบ - * @param orgRevisionId - ID ของ orgRevision ปัจจุบัน - * @param action - Action ที่ต้องการตรวจสอบสิทธิ์ (CREATE, DELETE, GET, LIST, UPDATE) - * @param system - System ID ที่ต้องการตรวจสอบสิทธิ์ (authSysId) - * @returns ข้อมูลตำแหน่งที่รักษาการและ privilege - */ - async getActingPositionsWithPrivilege( - profileId: string, - orgRevisionId: string | undefined, - action?: string, - system?: string - ): Promise { - // ดึงข้อมูล posMasterAct โดย join กับ posMaster (ตำแหน่งที่ถูกรักษาการ) - const posMasterActs = await this.posMasterActRepo - .createQueryBuilder("posMasterAct") - .leftJoinAndSelect("posMasterAct.posMaster", "posMaster") - .addSelect([ - "posMaster.authRoleId", // เพิ่มการดึง authRoleId จากตำแหน่งที่ถูกรักษาการ - "posMaster.posMasterNo", // เพิ่มการดึงเลขที่ตำแหน่ง - "posMaster.posMasterNoPrefix", // เพิ่มการดึง prefix ของเลขที่ตำแหน่ง - "posMaster.posMasterNoSuffix" // เพิ่มการดึง suffix ของเลขที่ตำแหน่ง - ]) - .leftJoinAndSelect("posMaster.orgRoot", "orgRoot") - .leftJoinAndSelect("posMaster.orgChild1", "orgChild1") - .leftJoinAndSelect("posMaster.orgChild2", "orgChild2") - .leftJoinAndSelect("posMaster.orgChild3", "orgChild3") - .leftJoinAndSelect("posMaster.orgChild4", "orgChild4") - .leftJoinAndSelect("posMaster.orgRevision", "orgRevision") - .leftJoinAndSelect("posMasterAct.posMasterChild", "posMasterChild") - .leftJoinAndSelect("posMasterChild.current_holder", "profileChild") - .where("profileChild.id = :profileId", { profileId }) - .andWhere("posMaster.orgRevisionId = :orgRevisionId", { orgRevisionId }) - .andWhere("orgRevision.orgRevisionIsCurrent = true") - .andWhere("orgRevision.orgRevisionIsDraft = false") - .getMany(); - - if (posMasterActs.length === 0) { - return { - isAct: false, - posMasterActs: [], - }; - } - - // วนลูปแต่ละ posMasterAct เพื่อดึง privilege ของตำแหน่งที่รักษาการ - const posMasterActsResponse = await Promise.all( - posMasterActs.map(async (act) => { - let privilege: string | null = null; - let privileges: Record = {}; - - if (act.posMaster?.authRoleId) { - // ถ้าระบุ action และ system มา ให้ดึงเฉพาะ privilege ของระบบนั้นๆ - if (action && system) { - const roleAttr = await this.authRoleAttrRepo - .createQueryBuilder("authRoleAttr") - .select(["authRoleAttr.attrPrivilege", "authRoleAttr.attrIsCreate", "authRoleAttr.attrIsDelete", "authRoleAttr.attrIsGet", "authRoleAttr.attrIsList", "authRoleAttr.attrIsUpdate"]) - .where("authRoleAttr.authRoleId = :authRoleId", { - authRoleId: act.posMaster.authRoleId, - }) - .andWhere("authRoleAttr.authSysId = :system", { system }) - .getOne(); - - if (roleAttr) { - // ตรวจสอบสิทธิ์ตาม action - let hasPermission = false; - const actionUpper = action.trim().toUpperCase(); - - switch (actionUpper) { - case "CREATE": - hasPermission = roleAttr.attrIsCreate; - break; - case "DELETE": - hasPermission = roleAttr.attrIsDelete; - break; - case "GET": - hasPermission = roleAttr.attrIsGet; - break; - case "LIST": - hasPermission = roleAttr.attrIsList; - break; - case "UPDATE": - hasPermission = roleAttr.attrIsUpdate; - break; - } - - if (hasPermission) { - privilege = roleAttr.attrPrivilege; - } - } - } else { - // ดึงข้อมูล AuthRoleAttr สำหรับทุกระบบ - const roleAttrs = await this.authRoleAttrRepo - .createQueryBuilder("authRoleAttr") - .select(["authRoleAttr.authSysId", "authRoleAttr.attrPrivilege"]) - .where("authRoleAttr.authRoleId = :authRoleId", { - authRoleId: act.posMaster.authRoleId, - }) - .getMany(); - - privileges = roleAttrs.reduce((acc, attr) => { - acc[attr.authSysId] = attr.attrPrivilege; - return acc; - }, {} as Record); - } - } - - // จัดรูปแบบเลขที่ตำแหน่งตามรูปแบบ shortName ที่ใช้ในระบบ - const holder = act.posMaster; - const posNo = !holder - ? null - : holder.orgChild4 != null - ? `${holder.orgChild4.orgChild4ShortName} ${holder.posMasterNo}` - : holder.orgChild3 != null - ? `${holder.orgChild3.orgChild3ShortName} ${holder.posMasterNo}` - : holder.orgChild2 != null - ? `${holder.orgChild2.orgChild2ShortName} ${holder.posMasterNo}` - : holder.orgChild1 != null - ? `${holder.orgChild1.orgChild1ShortName} ${holder.posMasterNo}` - : holder.orgRoot != null - ? `${holder.orgRoot.orgRootShortName} ${holder.posMasterNo}` - : null; - - return { - posNo: posNo, - privilege: action && system ? privilege : JSON.stringify(privileges), - rootDnaId: act.posMaster?.orgRoot?.ancestorDNA ?? null, - child1DnaId: act.posMaster?.orgChild1?.ancestorDNA ?? null, - child2DnaId: act.posMaster?.orgChild2?.ancestorDNA ?? null, - child3DnaId: act.posMaster?.orgChild3?.ancestorDNA ?? null, - child4DnaId: act.posMaster?.orgChild4?.ancestorDNA ?? null, - }; - }) - ); - - // ถ้าระบุ action และ system มา ให้ดึง privilege ของตำแหน่งแรก - let specificPrivilege: string | null = null; - if (action && system && posMasterActsResponse.length > 0) { - specificPrivilege = posMasterActsResponse[0].privilege; - } - - const response: ActingPositionWithPrivilegeData = { - isAct: true, - posMasterActs: posMasterActsResponse, - }; - - // ถ้าระบุ action และ system มา ให้เพิ่ม privilege เข้าไปใน response ด้วย - if (action && system) { - response.privilege = specificPrivilege ?? null; - } - - return response; - } -} - -// Export singleton instance -export const actingPositionService = new ActingPositionService(); diff --git a/src/services/rabbitmq.ts b/src/services/rabbitmq.ts index 6ba40258..a8011900 100644 --- a/src/services/rabbitmq.ts +++ b/src/services/rabbitmq.ts @@ -3,7 +3,6 @@ import { AppDataSource } from "../database/data-source"; import { Command } from "../entities/Command"; import { chunkArray, commandTypePath } from "../interfaces/utils"; import CallAPI from "../interfaces/call-api"; -import { getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; import HttpError from "../interfaces/http-error"; import HttpStatusCode from "../interfaces/http-status"; import { PosMaster } from "../entities/PosMaster"; @@ -652,33 +651,23 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { await posMasterAssignRepository.save(newAssigns); } - // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit - if (item.next_holderId != null) { + // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ + if (item.next_holderId != null && !item.isSit) { const profile = await repoProfile.findOne({ where: { id: item.next_holderId == null ? "" : item.next_holderId }, }); - if (profile != null) { - profile.posMasterNo = getPosMasterNo(item) ?? _null; - profile.org = getOrgFullName(item) ?? _null; - - // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ - if (!item.isSit && item.positions.length > 0) { - let position = await item.positions.find((x) => x.positionIsSelected == true); + if (profile != null && item.positions.length > 0) { + let position = await item.positions.find((x) => x.positionIsSelected == true); + if (position == null) { + position = await item.positions.find((x) => x.posLevelId == profile?.posLevelId); if (position == null) { - position = await item.positions.find((x) => x.posLevelId == profile?.posLevelId); - if (position == null) { - position = await item.positions.sort((a, b) => a.orderNo - b.orderNo)[0]; - } + position = await item.positions.sort((a, b) => a.orderNo - b.orderNo)[0]; } - - profile.posLevelId = position?.posLevelId ?? _null; - profile.posTypeId = position?.posTypeId ?? _null; - profile.position = position?.positionName ?? _null; - profile.positionField = position?.positionField ?? _null; - profile.posExecutive = position?.posExecutive?.posExecutiveName ?? _null; - profile.positionArea = position?.positionArea ?? _null; - profile.positionExecutiveField = position?.positionExecutiveField ?? _null; } + + profile.posLevelId = position?.posLevelId ?? _null; + profile.posTypeId = position?.posTypeId ?? _null; + profile.position = position?.positionName ?? _null; await repoProfile.save(profile); } } diff --git a/src/utils/org-formatting.ts b/src/utils/org-formatting.ts index fd61f33b..701fb478 100644 --- a/src/utils/org-formatting.ts +++ b/src/utils/org-formatting.ts @@ -68,47 +68,3 @@ export function filterPosMasters( ): PosMaster[] { return posMasters.filter((x) => x[childLevelIdKey] == null && x.isDirector === true); } - -/** - * สร้าง orgShortName จาก posMaster (ต้อง load org relations มาก่อน) - */ -export function getOrgShortName(posMaster: PosMaster): string { - if (posMaster.orgChild1Id === null) { - return posMaster.orgRoot?.orgRootShortName ?? ""; - } else if (posMaster.orgChild2Id === null) { - return posMaster.orgChild1?.orgChild1ShortName ?? ""; - } else if (posMaster.orgChild3Id === null) { - return posMaster.orgChild2?.orgChild2ShortName ?? ""; - } else if (posMaster.orgChild4Id === null) { - return posMaster.orgChild3?.orgChild3ShortName ?? ""; - } else { - return posMaster.orgChild4?.orgChild4ShortName ?? ""; - } -} - -/** - * สร้างชื่อสังกัดเต็ม จาก posMaster (join ด้วย \n) - */ -export function getOrgFullName(posMaster: PosMaster): string { - const parts = [ - posMaster.orgChild4?.orgChild4Name, - posMaster.orgChild3?.orgChild3Name, - posMaster.orgChild2?.orgChild2Name, - posMaster.orgChild1?.orgChild1Name, - posMaster.orgRoot?.orgRootName, - ]; - return parts.filter((part) => part !== undefined && part !== null).join("\n"); -} - -/** - * สร้างเลขที่ตำแหน่ง เช่น "กทม. กบ.1234ช" - */ -export function getPosMasterNo(posMaster: PosMaster): string { - const orgShortName = getOrgShortName(posMaster); - const parts = [ - posMaster.posMasterNoPrefix, - posMaster.posMasterNo, - posMaster.posMasterNoSuffix, - ].filter((part) => part !== null && part !== undefined); - return `${orgShortName} ${parts.join('')}`; -} diff --git a/src/utils/tenure.ts b/src/utils/tenure.ts deleted file mode 100644 index 577d314b..00000000 --- a/src/utils/tenure.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * คำนวณอายุงานจากจำนวนวันรวม - * @param totalDays จำนวนวันรวม - * @returns { year, month, day } ปี เดือน วัน - */ -export function calculateTenure(totalDays: number) { - // 1. แปลงเป็น year เต็ม - const year = Math.floor(totalDays / 365.2524); - - // 2. วันที่เหลือหลังหัก year ออก - const remainAfterYear = totalDays - year * 365.2524; - - // 3. แปลงเป็น month เต็ม - const month = Math.floor(remainAfterYear / 30.4375); - - // 4. วันที่เหลือหลังหัก month ออก - const remainAfterMonth = remainAfterYear - month * 30.4375; - - // 5. ปัดลง เฉพาะวัน - const day = Math.floor(remainAfterMonth); - - return { year, month, day }; -}