From 5678f333dbd719597192390ff52c7b7fef941e49 Mon Sep 17 00:00:00 2001 From: Bright Date: Mon, 25 Aug 2025 18:59:33 +0700 Subject: [PATCH 01/21] =?UTF-8?q?api=20=E0=B8=82=E0=B9=89=E0=B8=AD?= =?UTF-8?q?=E0=B8=A1=E0=B8=B9=E0=B8=A5=E0=B8=95=E0=B8=B3=E0=B9=81=E0=B8=AB?= =?UTF-8?q?=E0=B8=99=E0=B9=88=E0=B8=87=E0=B9=81=E0=B8=A5=E0=B8=B0=E0=B9=80?= =?UTF-8?q?=E0=B8=87=E0=B8=B4=E0=B8=99=E0=B9=80=E0=B8=94=E0=B8=B7=E0=B8=AD?= =?UTF-8?q?=E0=B8=99=20(=E0=B9=83=E0=B8=8A=E0=B9=89=E0=B9=83=E0=B8=99?= =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=87=E0=B8=B2=E0=B8=99=E0=B8=9B?= =?UTF-8?q?=E0=B8=A3=E0=B8=B0=E0=B8=A7=E0=B8=B1=E0=B8=95=E0=B8=B4=E0=B8=AA?= =?UTF-8?q?=E0=B8=B3=E0=B8=AB=E0=B8=A3=E0=B8=B1=E0=B8=9A=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B9=80=E0=B8=AA=E0=B8=99=E0=B8=AD=E0=B8=82=E0=B8=AD?= =?UTF-8?q?=E0=B8=9E=E0=B8=A3=E0=B8=B0=E0=B8=A3=E0=B8=B2=E0=B8=8A=E0=B8=97?= =?UTF-8?q?=E0=B8=B2=E0=B8=99=E0=B9=80=E0=B8=AB=E0=B8=A3=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=8D=E0=B8=88=E0=B8=B1=E0=B8=81=E0=B8=A3=E0=B8=9E=E0=B8=A3?= =?UTF-8?q?=E0=B8=A3=E0=B8=94=E0=B8=B4=E0=B8=A1=E0=B8=B2=E0=B8=A5=E0=B8=B2?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/ProfileInsigniaController.ts | 74 ++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/controllers/ProfileInsigniaController.ts b/src/controllers/ProfileInsigniaController.ts index 4ed9c189..f71f1601 100644 --- a/src/controllers/ProfileInsigniaController.ts +++ b/src/controllers/ProfileInsigniaController.ts @@ -26,6 +26,8 @@ import { Profile } from "../entities/Profile"; import { Insignia } from "../entities/Insignia"; import permission from "../interfaces/permission"; import { setLogDataDiff } from "../interfaces/utils"; +import { ProfileSalary } from "../entities/ProfileSalary"; +import { In, IsNull } from "typeorm"; @Route("api/v1/org/profile/insignia") @Tags("ProfileInsignia") @Security("bearerAuth") @@ -34,6 +36,7 @@ export class ProfileInsigniaController extends Controller { private insigniaRepo = AppDataSource.getRepository(ProfileInsignia); private insigniaHistoryRepo = AppDataSource.getRepository(ProfileInsigniaHistory); private insigniaMetaRepo = AppDataSource.getRepository(Insignia); + private profileSalaryRepo = AppDataSource.getRepository(ProfileSalary); @Get("user") public async getInsigniaUser(@Request() request: { user: Record }) { @@ -258,4 +261,75 @@ export class ProfileInsigniaController extends Controller { return new HttpSuccess(); } + + /** + * @summary ข้อมูลตำแหน่งและเงินเดือน (ใช้ในรายงานประวัติสำหรับการเสนอขอพระราชทานเหรียญจักรพรรดิมาลา) + */ + @Post("position") + public async GetPprofileSalarys( + @Request() req: RequestWithUser, + @Body() body: { profileId: string }, + ) { + const profile = await this.profileRepo.findOneBy({ id: body.profileId }); + if (!profile) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); + } + const profileSalarys = await this.profileSalaryRepo.find({ + where: [ + { + profileId: body.profileId, + commandCode: In([ + "0", + "9", + "1", + "2", + "3", + "4", + "8", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + ]), + }, + { profileId: body.profileId, commandCode: IsNull() }, + ], + order: { order: "ASC" }, + }); + if (!profileSalarys) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบข้อมูลตำแหน่งและเงินเดือน"); + } + const birth = new Date(profile.birthDate); + const mapData = profileSalarys.map(x => { + // คำนวณอายุ + let age = null; + if (x.commandDateAffect && profile.birthDate) { + const affect = new Date(x.commandDateAffect); + age = affect.getFullYear() - birth.getFullYear(); + // เช็คเดือน/วัน ถ้าวันเกิดยังไม่มาถึงในปีนั้น ให้ลบ 1 + const monthDiff = affect.getMonth() - birth.getMonth(); + const dayDiff = affect.getDate() - birth.getDate(); + if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) { + age--; + } + } + return { + dateAffect: x.commandDateAffect, + position: x.positionName, + root: x.orgRoot, + child1: x.orgChild1, + child2: x.orgChild2, + child3: x.orgChild3, + child4: x.orgChild4, + age: age, + amount: x.amount, + remark: x.remark, + commandCode: x.commandCode + }; + }); + return new HttpSuccess(mapData); + } } From 0df264e900777115062f645f97e47f88a008613b Mon Sep 17 00:00:00 2001 From: adisak Date: Tue, 26 Aug 2025 12:07:03 +0700 Subject: [PATCH 02/21] update #1675 --- src/controllers/OrganizationController.ts | 70 ++++++++++------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index cd400837..37663cc2 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -124,20 +124,20 @@ export class OrganizationController extends Controller { @Request() request: RequestWithUser, ) { try { - // // CheckQueueInProgress - // const [isBusyDraft, isBusyPublish] = await Promise.all([ - // checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG_DRAFT}`), - // checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG}`), - // ]); - // // console.log("✅ ตรวจสอบแล้ว Draft Busy:", isBusyDraft); - // // console.log("✅ ตรวจสอบแล้ว Publish Busy:", isBusyPublish); - // if (isBusyDraft || isBusyPublish) { - // // console.log("🚫 พบว่ามีงานอยู่ในคิว — error") - // throw new HttpError( - // HttpStatusCode.CONFLICT, - // "ไม่สามารถดำเนินการได้ หากกำลังเผยแพร่หรือสร้างแบบร่างโครงสร้างหน่วยงาน", - // ); - // } + // CheckQueueInProgress + const [isBusyDraft, isBusyPublish] = await Promise.all([ + checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG_DRAFT}`), + checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG}`), + ]); + // console.log("✅ ตรวจสอบแล้ว Draft Busy:", isBusyDraft); + // console.log("✅ ตรวจสอบแล้ว Publish Busy:", isBusyPublish); + if (isBusyDraft || isBusyPublish) { + // console.log("🚫 พบว่ามีงานอยู่ในคิว — error") + throw new HttpError( + HttpStatusCode.CONFLICT, + "ไม่สามารถดำเนินการได้ หากกำลังเผยแพร่หรือสร้างแบบร่างโครงสร้างหน่วยงาน", + ); + } //new main revision const before = null; const revision = Object.assign(new OrgRevision(), requestBody) as OrgRevision; @@ -163,13 +163,7 @@ export class OrganizationController extends Controller { await sendToQueueOrgDraft(msg); return new HttpSuccess("Draft is being created... Processing in the background."); } catch (error: any) { - if (error?.status && error?.message) { - return error; - } - return new HttpError( - HttpStatusCode.INTERNAL_SERVER_ERROR, - "Failed to process the draft. Please try again later.", - ); + throw error; } } @@ -3209,19 +3203,19 @@ export class OrganizationController extends Controller { try { // CheckQueueInProgress // console.log("🚀 ตรวจสอบว่ามีงานอยู่ในคิว"); - // const [isBusyDraft, isBusyPublish] = await Promise.all([ - // checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG_DRAFT}`), - // checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG}`), - // ]); - // // console.log("✅ ตรวจสอบแล้ว Draft Busy:", isBusyDraft); - // // console.log("✅ ตรวจสอบแล้ว Publish Busy:", isBusyPublish); - // if (isBusyDraft || isBusyPublish) { - // // console.log("🚫 พบว่ามีงานอยู่ในคิว — error") - // throw new HttpError( - // HttpStatusCode.CONFLICT, - // "ไม่สามารถดำเนินการได้ หากกำลังเผยแพร่หรือสร้างแบบร่างโครงสร้างหน่วยงาน", - // ); - // } + const [isBusyDraft, isBusyPublish] = await Promise.all([ + checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG_DRAFT}`), + checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG}`), + ]); + // console.log("✅ ตรวจสอบแล้ว Draft Busy:", isBusyDraft); + // console.log("✅ ตรวจสอบแล้ว Publish Busy:", isBusyPublish); + if (isBusyDraft || isBusyPublish) { + // console.log("🚫 พบว่ามีงานอยู่ในคิว — error") + throw new HttpError( + HttpStatusCode.CONFLICT, + "ไม่สามารถดำเนินการได้ หากกำลังเผยแพร่หรือสร้างแบบร่างโครงสร้างหน่วยงาน", + ); + } const today = new Date(); today.setHours(0, 0, 0, 0); // Set time to the beginning of the day @@ -3258,13 +3252,7 @@ export class OrganizationController extends Controller { await sendToQueueOrg(msg); return new HttpSuccess(); } catch (error: any) { - if (error?.status && error?.message) { - return error; - } - return new HttpError( - HttpStatusCode.INTERNAL_SERVER_ERROR, - "Failed to process the publish. Please try again later.", - ); + throw error; } } From 910e56897359fc7b46c5cf4529c7a7048a0ec522 Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Tue, 26 Aug 2025 13:47:43 +0700 Subject: [PATCH 03/21] history update position --- src/controllers/CommandController.ts | 30 ++- src/controllers/EmployeePositionController.ts | 28 ++- .../EmployeeTempPositionController.ts | 25 +- src/controllers/PositionController.ts | 31 ++- src/database/data-source.ts | 4 +- src/entities/PosMasterEmployeeHistory.ts | 101 ++++++++ src/entities/PosMasterEmployeeTempHistory.ts | 101 ++++++++ src/entities/PosMasterHistory.ts | 101 ++++++++ src/services/PositionService.ts | 216 ++++++++++++++++++ src/services/rabbitmq.ts | 4 +- 10 files changed, 625 insertions(+), 16 deletions(-) create mode 100644 src/entities/PosMasterEmployeeHistory.ts create mode 100644 src/entities/PosMasterEmployeeTempHistory.ts create mode 100644 src/entities/PosMasterHistory.ts create mode 100644 src/services/PositionService.ts diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 5dfcf45d..78bb9be3 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -59,7 +59,6 @@ import { createUser, getRoles, deleteUser, - enableStatus, getUserByUsername, getRoleMappings, removeUserRoles, @@ -92,6 +91,10 @@ import { ProfileInsignia, CreateProfileInsignia } from "../entities/ProfileInsig import { ProfileInsigniaHistory } from "../entities/ProfileInsigniaHistory"; import { Gender } from "../entities/Gender"; import { ProfileAvatar } from "../entities/ProfileAvatar"; +import { + CreatePosMasterHistoryEmployee, + CreatePosMasterHistoryOfficer, +} from "../services/PositionService"; @Route("api/v1/org/command") @Tags("Command") @Security("bearerAuth") @@ -3154,8 +3157,12 @@ export class CommandController extends Controller { posMaster.lastUpdatedAt = new Date(); posMaster.conditionReason = _null; posMaster.isCondition = false; - if (posMasterOld != null) await this.posMasterRepository.save(posMasterOld); + if (posMasterOld != null) { + await this.posMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryOfficer(posMasterOld.id, req); + } await this.posMasterRepository.save(posMaster); + await CreatePosMasterHistoryOfficer(posMaster.id, req); const positionNew = await this.positionRepository.findOne({ where: { @@ -3342,8 +3349,12 @@ export class CommandController extends Controller { posMaster.current_holderId = item.profileId; posMaster.lastUpdatedAt = new Date(); posMaster.next_holderId = null; - if (posMasterOld != null) await this.employeePosMasterRepository.save(posMasterOld); + if (posMasterOld != null) { + await this.employeePosMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryEmployee(posMasterOld.id, req); + } await this.employeePosMasterRepository.save(posMaster); + await CreatePosMasterHistoryEmployee(posMaster.id, req); const positionNew = await this.employeePositionRepository.findOne({ where: { @@ -3573,6 +3584,7 @@ export class CommandController extends Controller { posMaster.conditionReason = _null; posMaster.isCondition = false; await this.posMasterRepository.save(posMaster); + await CreatePosMasterHistoryOfficer(posMaster.id, req); const positionNew = await this.positionRepository.findOne({ where: { posMasterId: posMaster.id, @@ -6030,8 +6042,12 @@ export class CommandController extends Controller { posMaster.lastUpdatedAt = new Date(); posMaster.conditionReason = _null; posMaster.isCondition = false; - if (posMasterOld != null) await this.posMasterRepository.save(posMasterOld); + if (posMasterOld != null) { + await this.posMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryOfficer(posMasterOld.id, req); + } await this.posMasterRepository.save(posMaster); + await CreatePosMasterHistoryOfficer(posMaster.id, req); const positionNew = await this.positionRepository.findOne({ where: { @@ -6411,8 +6427,12 @@ export class CommandController extends Controller { posMaster.current_holderId = profile.id; posMaster.lastUpdatedAt = new Date(); posMaster.next_holderId = null; - if (posMasterOld != null) await this.employeePosMasterRepository.save(posMasterOld); + if (posMasterOld != null) { + await this.employeePosMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryEmployee(posMasterOld.id, req); + } await this.employeePosMasterRepository.save(posMaster); + await CreatePosMasterHistoryEmployee(posMaster.id, req); const clsTempPosmaster = await this.employeeTempPosMasterRepository.find({ where: { diff --git a/src/controllers/EmployeePositionController.ts b/src/controllers/EmployeePositionController.ts index 6ef4ffaa..6622f78f 100644 --- a/src/controllers/EmployeePositionController.ts +++ b/src/controllers/EmployeePositionController.ts @@ -38,6 +38,11 @@ import { AuthRole } from "../entities/AuthRole"; import { RequestWithUser } from "../middlewares/user"; import permission from "../interfaces/permission"; import { setLogDataDiff } from "../interfaces/utils"; +import { + CreatePosMasterHistoryOfficer, + CreatePosMasterHistoryEmployee, +} from "../services/PositionService"; +import { PosMasterEmployeeHistory } from "../entities/PosMasterEmployeeHistory"; @Route("api/v1/org/employee/pos") @Tags("Employee") @Security("bearerAuth") @@ -50,6 +55,7 @@ export class EmployeePositionController extends Controller { private employeePosTypeRepository = AppDataSource.getRepository(EmployeePosType); private employeePosLevelRepository = AppDataSource.getRepository(EmployeePosLevel); private employeePosMasterRepository = AppDataSource.getRepository(EmployeePosMaster); + private posMasterHistoryRepository = AppDataSource.getRepository(PosMasterEmployeeHistory); private employeePositionRepository = AppDataSource.getRepository(EmployeePosition); private profileRepository = AppDataSource.getRepository(ProfileEmployee); private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); @@ -2265,6 +2271,7 @@ export class EmployeePositionController extends Controller { dataMaster.lastUpdatedAt = new Date(); // dataMaster.next_holderId = requestBody.profileId; await this.employeePosMasterRepository.save(dataMaster); + await CreatePosMasterHistoryEmployee(dataMaster.id, request); return new HttpSuccess(); } @@ -2439,8 +2446,12 @@ export class EmployeePositionController extends Controller { posMaster.current_holderId = body.profileId; posMaster.lastUpdatedAt = new Date(); // posMaster.next_holderId = body.profileId; - if (posMasterOld != null) await this.employeePosMasterRepository.save(posMasterOld); + if (posMasterOld != null) { + await this.employeePosMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryEmployee(posMasterOld.id, request); + } await this.employeePosMasterRepository.save(posMaster); + await CreatePosMasterHistoryEmployee(posMaster.id, request); const positionNew = await this.employeePositionRepository.findOne({ where: { @@ -2463,4 +2474,19 @@ export class EmployeePositionController extends Controller { } return new HttpSuccess(); } + + /** + * API ประวัติแก้ไขตำแหน่ง + * + * @summary ประวัติแก้ไขตำแหน่ง (ADMIN) + * + */ + @Get("history-update/{id}") + async listPosMasterHistory(@Path() id: string, @Request() request: RequestWithUser) { + const posMasterHistory = await this.posMasterHistoryRepository.find({ + where: { ancestorDNA: id }, + }); + + return new HttpSuccess(posMasterHistory); + } } diff --git a/src/controllers/EmployeeTempPositionController.ts b/src/controllers/EmployeeTempPositionController.ts index 7ad6b9c7..d4316e3c 100644 --- a/src/controllers/EmployeeTempPositionController.ts +++ b/src/controllers/EmployeeTempPositionController.ts @@ -41,6 +41,8 @@ import { AuthRole } from "../entities/AuthRole"; import { RequestWithUser } from "../middlewares/user"; import permission from "../interfaces/permission"; import { setLogDataDiff } from "../interfaces/utils"; +import { CreatePosMasterHistoryEmployeeTemp } from "../services/PositionService"; +import { PosMasterEmployeeTempHistory } from "../entities/PosMasterEmployeeTempHistory"; @Route("api/v1/org/employee-temp/pos") @Tags("Employee") @Security("bearerAuth") @@ -53,6 +55,7 @@ export class EmployeeTempPositionController extends Controller { private employeePosTypeRepository = AppDataSource.getRepository(EmployeePosType); private employeePosLevelRepository = AppDataSource.getRepository(EmployeePosLevel); private employeeTempPosMasterRepository = AppDataSource.getRepository(EmployeeTempPosMaster); + private posMasterHistoryRepository = AppDataSource.getRepository(PosMasterEmployeeTempHistory); private employeePositionRepository = AppDataSource.getRepository(EmployeePosition); private profileRepository = AppDataSource.getRepository(ProfileEmployee); private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); @@ -2004,6 +2007,7 @@ export class EmployeeTempPositionController extends Controller { dataMaster.lastUpdatedAt = new Date(); // dataMaster.next_holderId = requestBody.profileId; await this.employeeTempPosMasterRepository.save(dataMaster); + await CreatePosMasterHistoryEmployeeTemp(dataMaster.id, request); return new HttpSuccess(); } @@ -2178,8 +2182,12 @@ export class EmployeeTempPositionController extends Controller { posMaster.current_holderId = body.profileId; posMaster.lastUpdatedAt = new Date(); // posMaster.next_holderId = body.profileId; - if (posMasterOld != null) await this.employeeTempPosMasterRepository.save(posMasterOld); + if (posMasterOld != null) { + await this.employeeTempPosMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryEmployeeTemp(posMasterOld.id, request); + } await this.employeeTempPosMasterRepository.save(posMaster); + await CreatePosMasterHistoryEmployeeTemp(posMaster.id, request); const positionNew = await this.employeePositionRepository.findOne({ where: { @@ -2202,4 +2210,19 @@ export class EmployeeTempPositionController extends Controller { } return new HttpSuccess(); } + + /** + * API ประวัติแก้ไขตำแหน่ง + * + * @summary ประวัติแก้ไขตำแหน่ง (ADMIN) + * + */ + @Get("history-update/{id}") + async listPosMasterHistory(@Path() id: string, @Request() request: RequestWithUser) { + const posMasterHistory = await this.posMasterHistoryRepository.find({ + where: { ancestorDNA: id }, + }); + + return new HttpSuccess(posMasterHistory); + } } diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index b233b631..997f5627 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -42,6 +42,8 @@ import { setLogDataDiff } from "../interfaces/utils"; import { PosMasterAssign } from "../entities/PosMasterAssign"; import { Assign } from "../entities/Assign"; import { ProfileEmployee } from "../entities/ProfileEmployee"; +import { PosMasterHistory } from "../entities/PosMasterHistory"; +import { CreatePosMasterHistoryOfficer } from "../services/PositionService"; @Route("api/v1/org/pos") @Tags("Position") @Security("bearerAuth") @@ -57,6 +59,7 @@ export class PositionController extends Controller { private posLevelEmployeeRepository = AppDataSource.getRepository(EmployeePosLevel); private posDictRepository = AppDataSource.getRepository(PosDict); private posMasterRepository = AppDataSource.getRepository(PosMaster); + private posMasterHistoryRepository = AppDataSource.getRepository(PosMasterHistory); private employeePosMasterRepository = AppDataSource.getRepository(EmployeePosMaster); private positionRepository = AppDataSource.getRepository(Position); private profileRepository = AppDataSource.getRepository(Profile); @@ -1541,7 +1544,7 @@ export class PositionController extends Controller { // posLevelRank: "ASC", // }, orderNo: "ASC", - createdAt: "ASC" + createdAt: "ASC", }, }); const formattedData = { @@ -2381,7 +2384,7 @@ export class PositionController extends Controller { // posType: { posTypeRank: "ASC" }, // posLevel: { posLevelRank: "ASC" }, orderNo: "ASC", - createdAt: "ASC" + createdAt: "ASC", }, }); @@ -3654,6 +3657,7 @@ export class PositionController extends Controller { dataMaster.current_holderId = _null; } await this.posMasterRepository.save(dataMaster, { data: request }); + await CreatePosMasterHistoryOfficer(dataMaster.id, request); setLogDataDiff(request, { before, after: dataMaster }); return new HttpSuccess(); @@ -4826,6 +4830,7 @@ export class PositionController extends Controller { positionId: string; profileId: string; }, + @Request() request: RequestWithUser, ) { const posMaster = await this.posMasterRepository.findOne({ where: { id: body.posmasterId }, @@ -4879,8 +4884,12 @@ export class PositionController extends Controller { const _null: any = null; posMaster.conditionReason = _null; posMaster.isCondition = false; - if (posMasterOld != null) await this.posMasterRepository.save(posMasterOld); + if (posMasterOld != null) { + await this.posMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryOfficer(posMasterOld.id, request); + } await this.posMasterRepository.save(posMaster); + await CreatePosMasterHistoryOfficer(posMaster.id, request); const positionNew = await this.positionRepository.findOne({ where: { @@ -5432,7 +5441,6 @@ export class PositionController extends Controller { posMaster.lastUpdatedAt = new Date(); posMaster.lastUpdateUserId = request.user.sub; posMaster.lastUpdateFullName = request.user.name; - posMaster.lastUpdatedAt = new Date(); await this.posMasterRepository.save(posMaster); return new HttpSuccess(); } @@ -5617,4 +5625,19 @@ export class PositionController extends Controller { } return new HttpSuccess(); } + + /** + * API ประวัติแก้ไขตำแหน่ง + * + * @summary ประวัติแก้ไขตำแหน่ง (ADMIN) + * + */ + @Get("history-update/{id}") + async listPosMasterHistory(@Path() id: string, @Request() request: RequestWithUser) { + const posMasterHistory = await this.posMasterHistoryRepository.find({ + where: { ancestorDNA: id }, + }); + + return new HttpSuccess(posMasterHistory); + } } diff --git a/src/database/data-source.ts b/src/database/data-source.ts index 655d6386..9de306d7 100644 --- a/src/database/data-source.ts +++ b/src/database/data-source.ts @@ -47,9 +47,7 @@ export const AppDataSource = new DataSource({ logging: true, // timezone: "Z", entities: - process.env.NODE_ENV !== "production" - ? ["src/entities/**/*.ts"] - : ["dist/entities/**/*{.ts,.js}"], + process.env.NODE_ENV !== "production" ? ["src/entities/*.ts"] : ["dist/entities/*{.ts,.js}"], migrations: process.env.NODE_ENV !== "production" ? ["src/migration/**/*.ts"] diff --git a/src/entities/PosMasterEmployeeHistory.ts b/src/entities/PosMasterEmployeeHistory.ts new file mode 100644 index 00000000..7dfa3b0c --- /dev/null +++ b/src/entities/PosMasterEmployeeHistory.ts @@ -0,0 +1,101 @@ +import { Entity, Column } from "typeorm"; +import { EntityBase } from "./base/Base"; + +@Entity("posMasterEmployeeHistory") +export class PosMasterEmployeeHistory extends EntityBase { + @Column({ + nullable: true, + comment: "คำนำหน้า", + length: 255, + default: null, + }) + prefix: string; + + @Column({ + nullable: true, + comment: "ชื่อ", + length: 255, + default: null, + }) + firstName: string; + + @Column({ + nullable: true, + comment: "สกุล", + length: 255, + default: null, + }) + lastName: string; + + @Column({ + nullable: true, + comment: "อักษรย่อ", + length: 16, + default: null, + }) + shortName: string; + + @Column({ + nullable: true, + comment: "Prefix นำหน้าเลขที่ตำแหน่ง เป็น Optional (ไม่ใช่อักษรย่อของหน่วยงาน/ส่วนราชการ)", + length: 16, + default: null, + }) + posMasterNoPrefix: string; + + @Column({ + nullable: true, + comment: "เลขที่ตำแหน่ง เป็นตัวเลข", + default: null, + }) + posMasterNo: number; + + @Column({ + nullable: true, + comment: "Suffix หลังเลขที่ตำแหน่ง เช่น ช.", + length: 16, + default: null, + }) + posMasterNoSuffix: string; + + @Column({ + nullable: true, + comment: "ชื่อตำแหน่ง", + length: 255, + default: null, + }) + position: string; + + @Column({ + nullable: true, + comment: "ชื่อประเภทตำแหน่ง", + length: 255, + default: null, + }) + posType: string; + + @Column({ + nullable: true, + comment: "ชื่อระดับตำแหน่ง", + length: 255, + default: null, + }) + posLevel: string; + + @Column({ + nullable: true, + comment: "ชื่อตำแหน่งทางการบริหาร", + length: 255, + default: null, + }) + posExecutive: string; + + @Column({ + nullable: true, + comment: + "รหัส DNA ใช้ในกรณีที่มีการทำสำเนาโครงสร้างและตำแหน่ง ตำแหน่งที่ทำสำเนามากับตำแหน่งเก่าจะต้องมี DNA เดียวกัน เพื่อให้ track ประวัติการแก้ไขตำแหน่งย้อนหลังได้", + length: 40, + default: null, + }) + ancestorDNA: string; +} diff --git a/src/entities/PosMasterEmployeeTempHistory.ts b/src/entities/PosMasterEmployeeTempHistory.ts new file mode 100644 index 00000000..c664959d --- /dev/null +++ b/src/entities/PosMasterEmployeeTempHistory.ts @@ -0,0 +1,101 @@ +import { Entity, Column } from "typeorm"; +import { EntityBase } from "./base/Base"; + +@Entity("posMasterEmployeeTempHistory") +export class PosMasterEmployeeTempHistory extends EntityBase { + @Column({ + nullable: true, + comment: "คำนำหน้า", + length: 255, + default: null, + }) + prefix: string; + + @Column({ + nullable: true, + comment: "ชื่อ", + length: 255, + default: null, + }) + firstName: string; + + @Column({ + nullable: true, + comment: "สกุล", + length: 255, + default: null, + }) + lastName: string; + + @Column({ + nullable: true, + comment: "อักษรย่อ", + length: 16, + default: null, + }) + shortName: string; + + @Column({ + nullable: true, + comment: "Prefix นำหน้าเลขที่ตำแหน่ง เป็น Optional (ไม่ใช่อักษรย่อของหน่วยงาน/ส่วนราชการ)", + length: 16, + default: null, + }) + posMasterNoPrefix: string; + + @Column({ + nullable: true, + comment: "เลขที่ตำแหน่ง เป็นตัวเลข", + default: null, + }) + posMasterNo: number; + + @Column({ + nullable: true, + comment: "Suffix หลังเลขที่ตำแหน่ง เช่น ช.", + length: 16, + default: null, + }) + posMasterNoSuffix: string; + + @Column({ + nullable: true, + comment: "ชื่อตำแหน่ง", + length: 255, + default: null, + }) + position: string; + + @Column({ + nullable: true, + comment: "ชื่อประเภทตำแหน่ง", + length: 255, + default: null, + }) + posType: string; + + @Column({ + nullable: true, + comment: "ชื่อระดับตำแหน่ง", + length: 255, + default: null, + }) + posLevel: string; + + @Column({ + nullable: true, + comment: "ชื่อตำแหน่งทางการบริหาร", + length: 255, + default: null, + }) + posExecutive: string; + + @Column({ + nullable: true, + comment: + "รหัส DNA ใช้ในกรณีที่มีการทำสำเนาโครงสร้างและตำแหน่ง ตำแหน่งที่ทำสำเนามากับตำแหน่งเก่าจะต้องมี DNA เดียวกัน เพื่อให้ track ประวัติการแก้ไขตำแหน่งย้อนหลังได้", + length: 40, + default: null, + }) + ancestorDNA: string; +} diff --git a/src/entities/PosMasterHistory.ts b/src/entities/PosMasterHistory.ts new file mode 100644 index 00000000..4360cf1e --- /dev/null +++ b/src/entities/PosMasterHistory.ts @@ -0,0 +1,101 @@ +import { Entity, Column } from "typeorm"; +import { EntityBase } from "./base/Base"; + +@Entity("posMasterHistory") +export class PosMasterHistory extends EntityBase { + @Column({ + nullable: true, + comment: "คำนำหน้า", + length: 255, + default: null, + }) + prefix: string; + + @Column({ + nullable: true, + comment: "ชื่อ", + length: 255, + default: null, + }) + firstName: string; + + @Column({ + nullable: true, + comment: "สกุล", + length: 255, + default: null, + }) + lastName: string; + + @Column({ + nullable: true, + comment: "อักษรย่อ", + length: 16, + default: null, + }) + shortName: string; + + @Column({ + nullable: true, + comment: "Prefix นำหน้าเลขที่ตำแหน่ง เป็น Optional (ไม่ใช่อักษรย่อของหน่วยงาน/ส่วนราชการ)", + length: 16, + default: null, + }) + posMasterNoPrefix: string; + + @Column({ + nullable: true, + comment: "เลขที่ตำแหน่ง เป็นตัวเลข", + default: null, + }) + posMasterNo: number; + + @Column({ + nullable: true, + comment: "Suffix หลังเลขที่ตำแหน่ง เช่น ช.", + length: 16, + default: null, + }) + posMasterNoSuffix: string; + + @Column({ + nullable: true, + comment: "ชื่อตำแหน่ง", + length: 255, + default: null, + }) + position: string; + + @Column({ + nullable: true, + comment: "ชื่อประเภทตำแหน่ง", + length: 255, + default: null, + }) + posType: string; + + @Column({ + nullable: true, + comment: "ชื่อระดับตำแหน่ง", + length: 255, + default: null, + }) + posLevel: string; + + @Column({ + nullable: true, + comment: "ชื่อตำแหน่งทางการบริหาร", + length: 255, + default: null, + }) + posExecutive: string; + + @Column({ + nullable: true, + comment: + "รหัส DNA ใช้ในกรณีที่มีการทำสำเนาโครงสร้างและตำแหน่ง ตำแหน่งที่ทำสำเนามากับตำแหน่งเก่าจะต้องมี DNA เดียวกัน เพื่อให้ track ประวัติการแก้ไขตำแหน่งย้อนหลังได้", + length: 40, + default: null, + }) + ancestorDNA: string; +} diff --git a/src/services/PositionService.ts b/src/services/PositionService.ts new file mode 100644 index 00000000..cbf842bb --- /dev/null +++ b/src/services/PositionService.ts @@ -0,0 +1,216 @@ +import { AppDataSource } from "../database/data-source"; +import { EmployeePosMaster } from "../entities/EmployeePosMaster"; +import { EmployeeTempPosMaster } from "../entities/EmployeeTempPosMaster"; +import { PosMaster } from "../entities/PosMaster"; +import { PosMasterEmployeeHistory } from "../entities/PosMasterEmployeeHistory"; +import { PosMasterEmployeeTempHistory } from "../entities/PosMasterEmployeeTempHistory"; +import { PosMasterHistory } from "../entities/PosMasterHistory"; +import { RequestWithUser } from "../middlewares/user"; + +export async function CreatePosMasterHistoryOfficer( + posMasterId: string, + request: RequestWithUser | null, +): Promise { + try { + await AppDataSource.transaction(async (manager) => { + const repoPosmaster = manager.getRepository(PosMaster); + const repoHistory = manager.getRepository(PosMasterHistory); + + const pm = await repoPosmaster.findOne({ + where: { id: posMasterId }, + relations: [ + "positions", + "positions.posLevel", + "positions.posType", + "positions.posExecutive", + "orgRoot", + "orgChild1", + "orgChild2", + "orgChild3", + "orgChild4", + "current_holder", + ], + }); + + if (!pm) return false; + if (!pm.ancestorDNA) return false; + const _null: any = null; + const h = new PosMasterHistory(); + const selectedPosition = + pm.positions.length > 0 + ? pm.positions.find((p) => p.positionIsSelected === true) ?? null + : null; + h.ancestorDNA = pm.ancestorDNA; + h.prefix = pm.current_holder?.prefix || _null; + h.firstName = pm.current_holder?.firstName || _null; + h.lastName = pm.current_holder?.lastName || _null; + h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null; + h.posMasterNo = pm.posMasterNo ?? _null; + h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null; + h.position = selectedPosition?.positionName ?? _null; + h.posType = selectedPosition?.posType?.posTypeName ?? _null; + h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _null; + h.posExecutive = selectedPosition?.posExecutive?.posExecutiveName ?? _null; + h.shortName = + [ + pm.orgChild4?.orgChild4ShortName, + pm.orgChild3?.orgChild3ShortName, + pm.orgChild2?.orgChild2ShortName, + pm.orgChild1?.orgChild1ShortName, + pm.orgRoot?.orgRootShortName, + ].find((s) => typeof s === "string" && s.trim().length > 0) ?? _null; + const userId = request?.user?.sub ?? ""; + const userName = request?.user?.name ?? "system"; + h.createdUserId = userId; + h.createdFullName = userName; + h.lastUpdateUserId = userId; + h.lastUpdateFullName = userName; + h.createdAt = new Date(); + h.lastUpdatedAt = new Date(); + await repoHistory.save(h); + }); + + return true; + } catch (err) { + console.error("CreatePosMasterHistoryOfficer transaction error:", err); + return false; + } +} + +export async function CreatePosMasterHistoryEmployee( + posMasterId: string, + request: RequestWithUser | null, +): Promise { + try { + await AppDataSource.transaction(async (manager) => { + const repoPosmaster = manager.getRepository(EmployeePosMaster); + const repoHistory = manager.getRepository(PosMasterEmployeeHistory); + + const pm = await repoPosmaster.findOne({ + where: { id: posMasterId }, + relations: [ + "positions", + "positions.posLevel", + "positions.posType", + "positions.posExecutive", + "orgRoot", + "orgChild1", + "orgChild2", + "orgChild3", + "orgChild4", + "current_holder", + ], + }); + + if (!pm) return false; + if (!pm.ancestorDNA) return false; + const _null: any = null; + const h = new PosMasterEmployeeHistory(); + const selectedPosition = + pm.positions.length > 0 + ? pm.positions.find((p) => p.positionIsSelected === true) ?? null + : null; + h.ancestorDNA = pm.ancestorDNA; + h.prefix = pm.current_holder?.prefix || _null; + h.firstName = pm.current_holder?.firstName || _null; + h.lastName = pm.current_holder?.lastName || _null; + h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null; + h.posMasterNo = pm.posMasterNo ?? _null; + h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null; + h.position = selectedPosition?.positionName ?? _null; + h.posType = selectedPosition?.posType?.posTypeName ?? _null; + h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _null; + h.shortName = + [ + pm.orgChild4?.orgChild4ShortName, + pm.orgChild3?.orgChild3ShortName, + pm.orgChild2?.orgChild2ShortName, + pm.orgChild1?.orgChild1ShortName, + pm.orgRoot?.orgRootShortName, + ].find((s) => typeof s === "string" && s.trim().length > 0) ?? _null; + const userId = request?.user?.sub ?? ""; + const userName = request?.user?.name ?? "system"; + h.createdUserId = userId; + h.createdFullName = userName; + h.lastUpdateUserId = userId; + h.lastUpdateFullName = userName; + h.createdAt = new Date(); + h.lastUpdatedAt = new Date(); + await repoHistory.save(h); + }); + + return true; + } catch (err) { + console.error("CreatePosMasterHistoryEmployee transaction error:", err); + return false; + } +} + +export async function CreatePosMasterHistoryEmployeeTemp( + posMasterId: string, + request: RequestWithUser | null, +): Promise { + try { + await AppDataSource.transaction(async (manager) => { + const repoPosmaster = manager.getRepository(EmployeeTempPosMaster); + const repoHistory = manager.getRepository(PosMasterEmployeeTempHistory); + + const pm = await repoPosmaster.findOne({ + where: { id: posMasterId }, + relations: [ + "positions", + "positions.posLevel", + "positions.posType", + "positions.posExecutive", + "orgRoot", + "orgChild1", + "orgChild2", + "orgChild3", + "orgChild4", + "current_holder", + ], + }); + + if (!pm) return false; + if (!pm.ancestorDNA) return false; + const _null: any = null; + const h = new PosMasterEmployeeTempHistory(); + const selectedPosition = + pm.positions.length > 0 + ? pm.positions.find((p) => p.positionIsSelected === true) ?? null + : null; + h.ancestorDNA = pm.ancestorDNA; + h.prefix = pm.current_holder?.prefix || _null; + h.firstName = pm.current_holder?.firstName || _null; + h.lastName = pm.current_holder?.lastName || _null; + h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null; + h.posMasterNo = pm.posMasterNo ?? _null; + h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null; + h.position = selectedPosition?.positionName ?? _null; + h.posType = selectedPosition?.posType?.posTypeName ?? _null; + h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _null; + h.shortName = + [ + pm.orgChild4?.orgChild4ShortName, + pm.orgChild3?.orgChild3ShortName, + pm.orgChild2?.orgChild2ShortName, + pm.orgChild1?.orgChild1ShortName, + pm.orgRoot?.orgRootShortName, + ].find((s) => typeof s === "string" && s.trim().length > 0) ?? _null; + const userId = request?.user?.sub ?? ""; + const userName = request?.user?.name ?? "system"; + h.createdUserId = userId; + h.createdFullName = userName; + h.lastUpdateUserId = userId; + h.lastUpdateFullName = userName; + h.createdAt = new Date(); + h.lastUpdatedAt = new Date(); + await repoHistory.save(h); + }); + + return true; + } catch (err) { + console.error("CreatePosMasterHistoryEmployeeTemp transaction error:", err); + return false; + } +} diff --git a/src/services/rabbitmq.ts b/src/services/rabbitmq.ts index 183a0e94..8c19f8f7 100644 --- a/src/services/rabbitmq.ts +++ b/src/services/rabbitmq.ts @@ -5,14 +5,12 @@ import { chunkArray, commandTypePath } from "../interfaces/utils"; import CallAPI from "../interfaces/call-api"; import HttpError from "../interfaces/http-error"; import HttpStatusCode from "../interfaces/http-status"; -import { RequestWithUser } from "../middlewares/user"; import { PosMaster } from "../entities/PosMaster"; import { Profile } from "../entities/Profile"; import { EmployeePosMaster } from "../entities/EmployeePosMaster"; import { EmployeeTempPosMaster } from "../entities/EmployeeTempPosMaster"; import { ProfileEmployee } from "../entities/ProfileEmployee"; import { OrgRevision } from "../entities/OrgRevision"; -import { request } from "http"; import { EmployeePosition } from "../entities/EmployeePosition"; import { OrgChild1 } from "../entities/OrgChild1"; import { OrgChild2 } from "../entities/OrgChild2"; @@ -25,6 +23,7 @@ import { In, Not } from "typeorm"; import { PosMasterAct } from "../entities/PosMasterAct"; import { PermissionOrg } from "../entities/PermissionOrg"; import { sendWebSocket } from "./webSocket"; +import { CreatePosMasterHistoryOfficer } from "./PositionService"; export let sendToQueue: (payload: any) => void; export let sendToQueueOrg: (payload: any) => void; @@ -580,6 +579,7 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { item.lastUpdateFullName = lastUpdateFullName; item.lastUpdatedAt = lastUpdatedAt; await repoPosmaster.save(item).catch((e) => console.log(e)); + await CreatePosMasterHistoryOfficer(item.id, null); } if (orgRevisionPublish != null && orgRevisionDraft != null) { //new main revision From 1cd3876b3e276d82a3b240ca57094faebf15a20f Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Tue, 26 Aug 2025 14:08:08 +0700 Subject: [PATCH 04/21] sort date position history --- src/controllers/EmployeePositionController.ts | 1 + src/controllers/EmployeeTempPositionController.ts | 1 + src/controllers/PositionController.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/controllers/EmployeePositionController.ts b/src/controllers/EmployeePositionController.ts index 6622f78f..632261e0 100644 --- a/src/controllers/EmployeePositionController.ts +++ b/src/controllers/EmployeePositionController.ts @@ -2485,6 +2485,7 @@ export class EmployeePositionController extends Controller { async listPosMasterHistory(@Path() id: string, @Request() request: RequestWithUser) { const posMasterHistory = await this.posMasterHistoryRepository.find({ where: { ancestorDNA: id }, + order: { createdAt: "DESC" }, }); return new HttpSuccess(posMasterHistory); diff --git a/src/controllers/EmployeeTempPositionController.ts b/src/controllers/EmployeeTempPositionController.ts index d4316e3c..5d304de0 100644 --- a/src/controllers/EmployeeTempPositionController.ts +++ b/src/controllers/EmployeeTempPositionController.ts @@ -2221,6 +2221,7 @@ export class EmployeeTempPositionController extends Controller { async listPosMasterHistory(@Path() id: string, @Request() request: RequestWithUser) { const posMasterHistory = await this.posMasterHistoryRepository.find({ where: { ancestorDNA: id }, + order: { createdAt: "DESC" }, }); return new HttpSuccess(posMasterHistory); diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 997f5627..e8467d12 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -5636,6 +5636,7 @@ export class PositionController extends Controller { async listPosMasterHistory(@Path() id: string, @Request() request: RequestWithUser) { const posMasterHistory = await this.posMasterHistoryRepository.find({ where: { ancestorDNA: id }, + order: { createdAt: "DESC" }, }); return new HttpSuccess(posMasterHistory); From a2833c9cb4a339e0212f874ef5ed45062a544d9f Mon Sep 17 00:00:00 2001 From: adisak Date: Tue, 26 Aug 2025 16:09:55 +0700 Subject: [PATCH 05/21] #1779 --- src/controllers/ProfileController.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 5d1bf77c..1cfa4565 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -6268,6 +6268,7 @@ export class ProfileController extends Controller { "current_holders.orgRevisionId", "current_holders.posMasterNo", "orgRoot.id", + "orgRoot.ancestorDNA", "orgRoot.orgRootName", "orgRoot.orgRootShortName", "orgRoot.orgRootOrder", @@ -6414,6 +6415,7 @@ export class ProfileController extends Controller { posNo: shortName ?? null, rootId: holder?.orgRoot?.id ?? null, root: holder?.orgRoot?.orgRootName ?? null, + rootDnaId: holder?.orgRoot == null ? null : holder?.orgRoot?.ancestorDNA, orgRootShortName: holder?.orgRoot?.orgRootShortName ?? null, orgRevisionId: holder?.orgRoot?.orgRevisionId ?? null, org, From 8a7417edf9c1978f9c57f202dde9a21ff0175745 Mon Sep 17 00:00:00 2001 From: Bright Date: Wed, 27 Aug 2025 10:38:47 +0700 Subject: [PATCH 06/21] =?UTF-8?q?API=20=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=95=E0=B9=89=E0=B8=AD=E0=B8=87=E0=B8=9B=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B9=81=E0=B8=81=E0=B9=89=E0=B9=84=E0=B8=82=20?= =?UTF-8?q?=E0=B8=84=E0=B8=B3=E0=B8=AA=E0=B8=B1=E0=B9=88=E0=B8=87=E0=B9=82?= =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=94=E0=B9=80=E0=B8=81=E0=B8=A5=E0=B9=89?= =?UTF-8?q?=E0=B8=B2=E0=B8=AF=20=E0=B9=81=E0=B8=95=E0=B9=88=E0=B8=87?= =?UTF-8?q?=E0=B8=95=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B9=83=E0=B8=AB=E0=B9=89?= =?UTF-8?q?=E0=B8=94=E0=B8=B3=E0=B8=A3=E0=B8=87=E0=B8=95=E0=B8=B3=E0=B9=81?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=88=E0=B8=87=20#1780?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/CommandController.ts | 11 +++- src/controllers/PositionController.ts | 72 ++++++++++++++++++++------- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 78bb9be3..c1e98a6c 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -2169,7 +2169,16 @@ export class CommandController extends Controller { command.detailFooter = commandType.detailFooter; command.isAttachment = commandType.isAttachment; command.isUploadAttachment = commandType.isUploadAttachment; - command.status = "NEW"; + // ถ้าเป็นคำสั่งโปรดเกล้าฯ "C-PM-47" ให้เปิดถึงรออัปโหลดไฟล์ #1780 + if (commandCode === "C-PM-47") { + command.isSignature = true; + command.isDraft = true; + command.isSign = true; + command.status = "PENDING"; + } + else { + command.status = "NEW"; + } command.issue = commandType.name; (command.commandAffectDate = requestBody.commandAffectDate ? new Date(requestBody.commandAffectDate) diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index e8467d12..2eb86e64 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -4075,21 +4075,53 @@ export class PositionController extends Controller { }, ) { let typeCondition: any = {}; - let conditionA: any = null; + let conditionA: any = "1=1"; + let params: any = {}; + let posType: any = {}; + let posLevel: any = {}; - let posType = await this.posTypeRepository.findOne({ - where: { id: String(body.posType) }, - }); - let posLevel = await this.posLevelRepository.findOne({ - where: { id: String(body.posLevel) }, - }); + if (body.typeCommand === "ROYAL") { + posType = await this.posTypeRepository.find({ + where: { posTypeName: In(["วิชาการ", "บริหาร"]) }, + }); + posLevel = await this.posLevelRepository.find({ + where: { + posTypeId: In(posType.map((x: any) => x.id)), + posLevelName: In(["ทรงคุณวุฒิ", "สูง"]) + }, + }); + conditionA = "positions.posTypeId IN (:...posTypeIds) AND positions.posLevelId IN (:...posLevelIds)"; + params = { + posTypeIds: posType.map((x: any) => x.id), + posLevelIds: posLevel.map((x: any) => x.id), + }; + } + else { + posType = await this.posTypeRepository.findOne({ + where: { id: String(body.posType) }, + }); + posLevel = await this.posLevelRepository.findOne({ + where: { id: String(body.posLevel) }, + }); - if (body.typeCommand == "APPOINTED" || body.typeCommand == "MOVE") { - conditionA = "positions.posTypeId LIKE :posType AND positions.posLevelId LIKE :posLevel"; - } else if (body.typeCommand == "APPOINT") { - conditionA = "posType.posTypeRank > :posTypeRank"; - } else if (body.typeCommand == "SLIP") { - conditionA = "positions.posTypeId LIKE :posType AND posLevel.posLevelRank > :posLevelRank"; + if (body.typeCommand == "APPOINTED" || body.typeCommand == "MOVE") { + conditionA = "positions.posTypeId LIKE :posType AND positions.posLevelId LIKE :posLevel"; + params = { + posType: posType?.id, + posLevel: posLevel?.id, + }; + } else if (body.typeCommand == "APPOINT") { + conditionA = "posType.posTypeRank > :posTypeRank"; + params = { + posTypeRank: posType?.posTypeRank ?? 0, + }; + } else if (body.typeCommand == "SLIP") { + conditionA = "positions.posTypeId LIKE :posType AND posLevel.posLevelRank > :posLevelRank"; + params = { + posType: posType?.id, + posLevelRank: posLevel?.posLevelRank ?? 0, + }; + } } if (body.isAll == false) { @@ -4161,12 +4193,14 @@ export class PositionController extends Controller { .andWhere("posMaster.next_holderId IS NULL") .andWhere( new Brackets((qb) => { - qb.andWhere(typeCondition).andWhere(conditionA == null ? "1=1" : conditionA, { - posType: posType == null ? `%%` : `${posType.id}`, - posLevel: posLevel == null ? `%%` : `${posLevel.id}`, - posTypeRank: posType == null ? 0 : posType.posTypeRank, - posLevelRank: posLevel == null ? 0 : posLevel.posLevelRank, - }); + qb.andWhere(typeCondition) + // .andWhere(conditionA == null ? "1=1" : conditionA, { + // posType: posType == null ? `%%` : `${posType.id}`, + // posLevel: posLevel == null ? `%%` : `${posLevel.id}`, + // posTypeRank: posType == null ? 0 : posType.posTypeRank, + // posLevelRank: posLevel == null ? 0 : posLevel.posLevelRank, + // }); + .andWhere(conditionA, params); }), ) .orderBy("orgRoot.orgRootOrder", "ASC") From a9cfcfc6b94522074b5c6c08bb570512d6bb5097 Mon Sep 17 00:00:00 2001 From: Bright Date: Wed, 27 Aug 2025 13:01:34 +0700 Subject: [PATCH 07/21] revert datasouce --- src/database/data-source.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/database/data-source.ts b/src/database/data-source.ts index 9de306d7..655d6386 100644 --- a/src/database/data-source.ts +++ b/src/database/data-source.ts @@ -47,7 +47,9 @@ export const AppDataSource = new DataSource({ logging: true, // timezone: "Z", entities: - process.env.NODE_ENV !== "production" ? ["src/entities/*.ts"] : ["dist/entities/*{.ts,.js}"], + process.env.NODE_ENV !== "production" + ? ["src/entities/**/*.ts"] + : ["dist/entities/**/*{.ts,.js}"], migrations: process.env.NODE_ENV !== "production" ? ["src/migration/**/*.ts"] From f66e721fa4e78f2d5fc88cd082fc0bc3fc16c0ed Mon Sep 17 00:00:00 2001 From: AdisakKanthawilang Date: Wed, 27 Aug 2025 14:49:36 +0700 Subject: [PATCH 08/21] #1749 --- src/controllers/OrganizationDotnetController.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index f6e926c3..7453fa47 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -4054,7 +4054,7 @@ export class OrganizationDotnetController extends Controller { if (body.node === 0) { typeCondition = { orgRootId: body.nodeId, - orgChild1Id: IsNull(), + // orgChild1Id: IsNull(), }; } else if (body.node === 1) { typeCondition = { @@ -4112,7 +4112,10 @@ export class OrganizationDotnetController extends Controller { "current_holders.orgChild4", ], }); - + const startDate = new Date().getFullYear(); + const endDate = new Date().getFullYear(); + const startOfYear = new Date(startDate, 0, 1); // 1 ม.ค. ปีนี้ + const endOfYear = new Date(endDate, 11, 31, 23, 59, 59, 999); // 31 ธ.ค. ปีนี้ if (body.isRetirement) { profile = await this.profileRepo.find({ where: { @@ -4122,7 +4125,7 @@ export class OrganizationDotnetController extends Controller { // isRetirement: true, dateRetire: And( Not(IsNull()), - Between(body.startDate, body.endDate) + Between(startOfYear, endOfYear) ) }, relations: [ @@ -4137,6 +4140,7 @@ export class OrganizationDotnetController extends Controller { ], }); } + let findRevision = await this.orgRevisionRepo.findOne({ where: { orgRevisionIsCurrent: true }, }); From 6cd39da15f95e22f47fe480b31189d53d38697a1 Mon Sep 17 00:00:00 2001 From: AdisakKanthawilang Date: Wed, 27 Aug 2025 14:53:16 +0700 Subject: [PATCH 09/21] update --- .../OrganizationDotnetController.ts | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 7453fa47..01cec5dd 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -4050,33 +4050,33 @@ export class OrganizationDotnetController extends Controller { }, ) { let typeCondition: any = {}; - if (body.isAll == false) { - if (body.node === 0) { - typeCondition = { - orgRootId: body.nodeId, - // orgChild1Id: IsNull(), - }; - } else if (body.node === 1) { - typeCondition = { - orgChild1Id: body.nodeId, - orgChild2Id: IsNull(), - }; - } else if (body.node === 2) { - typeCondition = { - orgChild2Id: body.nodeId, - orgChild3Id: IsNull(), - }; - } else if (body.node === 3) { - typeCondition = { - orgChild3Id: body.nodeId, - orgChild4Id: IsNull(), - }; - } else if (body.node === 4) { - typeCondition = { - orgChild4Id: body.nodeId, - }; - } - } else { + // if (body.isAll == false) { + // if (body.node === 0) { + // typeCondition = { + // orgRootId: body.nodeId, + // orgChild1Id: IsNull(), + // }; + // } else if (body.node === 1) { + // typeCondition = { + // orgChild1Id: body.nodeId, + // orgChild2Id: IsNull(), + // }; + // } else if (body.node === 2) { + // typeCondition = { + // orgChild2Id: body.nodeId, + // orgChild3Id: IsNull(), + // }; + // } else if (body.node === 3) { + // typeCondition = { + // orgChild3Id: body.nodeId, + // orgChild4Id: IsNull(), + // }; + // } else if (body.node === 4) { + // typeCondition = { + // orgChild4Id: body.nodeId, + // }; + // } + // } else { if (body.node === 0) { typeCondition = { orgRootId: body.nodeId, @@ -4098,7 +4098,7 @@ export class OrganizationDotnetController extends Controller { orgChild4Id: body.nodeId, }; } - } + // } let profile = await this.profileRepo.find({ where: { keycloak: Not(IsNull()) || Not(""), isLeave: false, current_holders: typeCondition }, relations: [ From 52fca7813c66c3f23b853a15dea489dc3471a506 Mon Sep 17 00:00:00 2001 From: Bright Date: Wed, 27 Aug 2025 15:19:32 +0700 Subject: [PATCH 10/21] migrate (create table registry and registryEmployee) --- src/entities/Registry.ts | 417 ++++++++++++++++++ src/entities/RegistryEmployee.ts | 375 ++++++++++++++++ .../1756282384029-create_table_registry_.ts | 16 + 3 files changed, 808 insertions(+) create mode 100644 src/entities/Registry.ts create mode 100644 src/entities/RegistryEmployee.ts create mode 100644 src/migration/1756282384029-create_table_registry_.ts diff --git a/src/entities/Registry.ts b/src/entities/Registry.ts new file mode 100644 index 00000000..6bd03545 --- /dev/null +++ b/src/entities/Registry.ts @@ -0,0 +1,417 @@ +import { + Entity, + Column +} from "typeorm"; +import { EntityBase } from "./base/Base"; + +@Entity("registry") +export class Registry extends EntityBase { + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง profile", + default: null, + }) + profileId: string; + + @Column({ + nullable: true, + comment: "เลขประจำตัวประชาชน", + default: null, + length: 13, + }) + citizenId: string; + + @Column({ + nullable: true, + length: 20, + comment: "คำนำหน้าชื่อ เช่น นาย นาง นางสาว", + default: null, + }) + prefix: string; + + @Column({ + nullable: true, + comment: "ชื่อ", + length: 255, + default: null, + }) + firstName: string; + + @Column({ + nullable: true, + comment: "นามสกุล", + length: 255, + default: null, + }) + lastName: string; + + @Column({ + comment: "ทดลองปฏิบัติหน้าที่", + default: false, + }) + isProbation: boolean; + + @Column({ + comment: "พ้นราชการ", + default: false, + }) + isLeave: boolean; + + @Column({ + comment: "เกษียณ", + default: false, + }) + isRetirement: boolean; + + @Column({ + nullable: true, + length: 255, + comment: "ประเภทพ้นคำสั่งพ้นจากราชการ", + default: null, + }) + leaveType: string; + + @Column({ + nullable: true, + comment: "เลขที่ตำแหน่ง เป็นตัวเลข", + default: null, + }) + posMasterNo: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgRoot", + default: null, + }) + orgRootId?: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgChild1", + default: null, + }) + orgChild1Id?: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgChild2", + default: null, + }) + orgChild2Id?: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgChild3", + default: null, + }) + orgChild3Id?: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgChild4", + default: null, + }) + orgChild4Id?: string; + + @Column({ + nullable: true, + comment: "ชื่อหน่วยงาน", + length: 255, + default: null, + }) + orgRootName: string; + + @Column({ + nullable: true, + comment: "ชื่อส่วนราชการ Child1", + length: 255, + default: null, + }) + orgChild1Name: string; + + @Column({ + nullable: true, + comment: "ชื่อส่วนราชการ Child2", + length: 255, + default: null, + }) + orgChild2Name: string; + + @Column({ + nullable: true, + comment: "ชื่อส่วนราชการ Child3", + length: 255, + default: null, + }) + orgChild3Name: string; + + @Column({ + nullable: true, + comment: "ชื่อส่วนราชการ Child4", + length: 255, + default: null, + }) + orgChild4Name: string; + + @Column({ + nullable: true, + length: 255, + comment: "สังกัด", + default: null, + }) + org: string; + + @Column({ + nullable: true, + length: 255, + comment: "เลขที่ตำแหน่ง", + default: null, + }) + searchShortName: string; + + @Column({ + nullable: true, + comment: "ชื่อตำแหน่งทางการบริหาร", + length: 255, + default: null, + }) + posExecutiveName: string; + + @Column({ + nullable: true, + comment: "ชื่อตำแหน่งในสายงาน", + length: 255, + default: null, + }) + position: string; + + @Column({ + nullable: true, + length: 255, + comment: "ประเภทตำแหน่ง", + default: null, + }) + posTypeName: string; + + @Column({ + nullable: true, + length: 255, + comment: "ระดับตำแหน่ง", + default: null, + }) + posLevelName: string; + + @Column({ + nullable: true, + comment: "เพศ", + length: 40, + default: null, + }) + gender: string; + + @Column({ + nullable: true, + comment: "ความสัมพันธ์", + length: 40, + default: null, + }) + relationship: string; + + @Column({ + nullable: true, + type: "datetime", + comment: "วันที่บรรจุ", + default: null, + }) + dateAppoint: Date; + + @Column({ + nullable: true, + type: "datetime", + comment: "วันครบเกษียณอายุ", + default: null, + }) + dateRetire: Date; + + @Column({ + nullable: true, + type: "datetime", + comment: "วันที่เกษียณอายุราชการตามกฏหมาย", + default: null, + }) + dateRetireLaw: Date; + + @Column({ + nullable: true, + type: "datetime", + comment: "วันเกิด", + default: null, + }) + birthdate: Date; + + @Column({ + nullable: true, + comment: "วุฒิการศึกษา", + length: 255, + default: null, + }) + degrees: string; + + @Column({ + nullable: true, + comment: "อายุ", + default: null, + }) + age: number; + + @Column({ + nullable: true, + comment: "จำนวนปีระยะเวลาดำรงตำแหน่งในสายงาน", + default: null, + }) + Years: number; + + @Column({ + nullable: true, + comment: "จำนวนเดือนระยะเวลาดำรงตำแหน่งในสายงาน", + default: null, + }) + Months: number; + + @Column({ + nullable: true, + comment: "จำนวนวันระยะเวลาดำรงตำแหน่งในสายงาน", + default: null, + }) + Days: number; + + @Column({ + nullable: true, + comment: "จำนวนปีระยะเวลาดำรงตำแหน่งตามระดับ", + default: null, + }) + levelYears: number; + + @Column({ + nullable: true, + comment: "จำนวนเดือนระยะเวลาดำรงตำแหน่งตามระดับ", + default: null, + }) + levelMonths: number; + + @Column({ + nullable: true, + comment: "จำนวนวันระยะเวลาดำรงตำแหน่งตามระดับ", + default: null, + }) + levelDays: number; + + @Column({ + nullable: true, + comment: "จำนวนปีระยะเวลาดำรงตำแหน่งทางการบริหาร", + default: null, + }) + posExecutiveYears: number; + + @Column({ + nullable: true, + comment: "จำนวนเดือนระยะเวลาดำรงตำแหน่งทางการบริหาร", + default: null, + }) + posExecutiveMonths: number; + + @Column({ + nullable: true, + comment: "จำนวนวันระยะเวลาดำรงตำแหน่งทางการบริหาร", + default: null, + }) + posExecutiveDays: number; + + @Column({ + nullable: true, + comment: "ด้าน/สาขา", + length: 255, + default: null, + }) + positionArea: string; + + @Column({ + type: "text", + nullable: true, + comment: "วุฒิการศึกษา", + default: null, + }) + Educations: string; + + @Column({ + nullable: true, + comment: "ระดับศึกษา", + length: 255, + default: null, + }) + educationLevels: string; + + @Column({ + nullable: true, + comment: "สาขาวิชา/ทาง", + length: 255, + default: null, + }) + fields: string; +} + +export class RegistryCreateDto { + profileId: string; + citizenId?: string | null; + prefix?: string | null; + firstName?: string | null; + lastName?: string | null; + isProbation?: boolean | null; + isLeave?: boolean | null; + isRetirement?: boolean | null; + leaveType?: string | null; + posMasterNo?: string | null; + orgRootId?: string | null; + orgChild1Id?: string | null; + orgChild2Id?: string | null; + orgChild3Id?: string | null; + orgChild4Id?: string | null; + orgRootName?: string | null; + orgChild1Name?: string | null; + orgChild2Name?: string | null; + orgChild3Name?: string | null; + orgChild4Name?: string | null; + org?: string | null; + searchShortName?: string | null; + posExecutiveName?: string | null; + position?: string | null; + posTypeName?: string | null; + posLevelName?: string | null; + gender?: string | null; + relationship?: string | null; + dateAppoint?: Date | null; + dateRetire?: Date | null; + dateRetireLaw?: Date | null; + birthdate?: Date | null; + degrees?: string | null; + age?: number | null; + Years?: number | null; + Months?: number | null; + Days?: number | null; + levelYears?: number | null; + levelMonths?: number | null; + levelDays?: number | null; + posExecutiveYears?: number | null; + posExecutiveMonths?: number | null; + posExecutiveDays?: number | null; + positionArea?: string | null; + Educations?: string | null; + educationLevels?: string | null; + fields?: string | null; +} \ No newline at end of file diff --git a/src/entities/RegistryEmployee.ts b/src/entities/RegistryEmployee.ts new file mode 100644 index 00000000..d84acfde --- /dev/null +++ b/src/entities/RegistryEmployee.ts @@ -0,0 +1,375 @@ +import { + Entity, + Column +} from "typeorm"; +import { EntityBase } from "./base/Base"; + +@Entity("registryEmployee") +export class RegistryEmployee extends EntityBase { + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง profileEmployee", + default: null, + }) + profileEmployeeId: string; + + @Column({ + nullable: true, + comment: "เลขประจำตัวประชาชน", + default: null, + length: 13, + }) + citizenId: string; + + @Column({ + nullable: true, + length: 20, + comment: "คำนำหน้าชื่อ เช่น นาย นาง นางสาว", + default: null, + }) + prefix: string; + + @Column({ + nullable: true, + comment: "ชื่อ", + length: 255, + default: null, + }) + firstName: string; + + @Column({ + nullable: true, + comment: "นามสกุล", + length: 255, + default: null, + }) + lastName: string; + + @Column({ + comment: "ทดลองปฏิบัติหน้าที่", + default: false, + }) + isProbation: boolean; + + @Column({ + comment: "พ้นราชการ", + default: false, + }) + isLeave: boolean; + + @Column({ + comment: "เกษียณ", + default: false, + }) + isRetirement: boolean; + + @Column({ + nullable: true, + length: 255, + comment: "ประเภทพ้นคำสั่งพ้นจากราชการ", + default: null, + }) + leaveType: string; + + @Column({ + nullable: true, + comment: "เลขที่ตำแหน่ง เป็นตัวเลข", + default: null, + }) + posMasterNo: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgRoot", + default: null, + }) + orgRootId?: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgChild1", + default: null, + }) + orgChild1Id?: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgChild2", + default: null, + }) + orgChild2Id?: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgChild3", + default: null, + }) + orgChild3Id?: string; + + @Column({ + nullable: true, + length: 40, + comment: "คีย์นอก(FK)ของตาราง orgChild4", + default: null, + }) + orgChild4Id?: string; + + @Column({ + nullable: true, + comment: "ชื่อหน่วยงาน", + length: 255, + default: null, + }) + orgRootName: string; + + @Column({ + nullable: true, + comment: "ชื่อส่วนราชการ Child1", + length: 255, + default: null, + }) + orgChild1Name: string; + + @Column({ + nullable: true, + comment: "ชื่อส่วนราชการ Child2", + length: 255, + default: null, + }) + orgChild2Name: string; + + @Column({ + nullable: true, + comment: "ชื่อส่วนราชการ Child3", + length: 255, + default: null, + }) + orgChild3Name: string; + + @Column({ + nullable: true, + comment: "ชื่อส่วนราชการ Child4", + length: 255, + default: null, + }) + orgChild4Name: string; + + @Column({ + nullable: true, + length: 255, + comment: "สังกัด", + default: null, + }) + org: string; + + @Column({ + nullable: true, + length: 255, + comment: "เลขที่ตำแหน่ง", + default: null, + }) + searchShortName: string; + + @Column({ + nullable: true, + comment: "ชื่อตำแหน่งในสายงาน", + length: 255, + default: null, + }) + position: string; + + @Column({ + nullable: true, + length: 255, + comment: "ประเภทตำแหน่ง", + default: null, + }) + posTypeName: string; + + @Column({ + nullable: true, + length: 255, + comment: "ระดับตำแหน่ง", + default: null, + }) + posLevelName: string; + + @Column({ + nullable: true, + comment: "เพศ", + length: 40, + default: null, + }) + gender: string; + + @Column({ + nullable: true, + comment: "ความสัมพันธ์", + length: 40, + default: null, + }) + relationship: string; + + @Column({ + nullable: true, + type: "datetime", + comment: "วันที่บรรจุ", + default: null, + }) + dateAppoint: Date; + + @Column({ + nullable: true, + type: "datetime", + comment: "วันครบเกษียณอายุ", + default: null, + }) + dateRetire: Date; + + @Column({ + nullable: true, + type: "datetime", + comment: "วันที่เกษียณอายุราชการตามกฏหมาย", + default: null, + }) + dateRetireLaw: Date; + + @Column({ + nullable: true, + type: "datetime", + comment: "วันเกิด", + default: null, + }) + birthdate: Date; + + @Column({ + nullable: true, + comment: "วุฒิการศึกษา", + length: 255, + default: null, + }) + degrees: string; + + @Column({ + nullable: true, + comment: "อายุ", + default: null, + }) + age: number; + + @Column({ + nullable: true, + comment: "จำนวนปีระยะเวลาดำรงตำแหน่งในสายงาน", + default: null, + }) + Years: number; + + @Column({ + nullable: true, + comment: "จำนวนเดือนระยะเวลาดำรงตำแหน่งในสายงาน", + default: null, + }) + Months: number; + + @Column({ + nullable: true, + comment: "จำนวนวันระยะเวลาดำรงตำแหน่งในสายงาน", + default: null, + }) + Days: number; + + @Column({ + nullable: true, + comment: "จำนวนปีระยะเวลาดำรงตำแหน่งตามระดับ", + default: null, + }) + levelYears: number; + + @Column({ + nullable: true, + comment: "จำนวนเดือนระยะเวลาดำรงตำแหน่งตามระดับ", + default: null, + }) + levelMonths: number; + + @Column({ + nullable: true, + comment: "จำนวนวันระยะเวลาดำรงตำแหน่งตามระดับ", + default: null, + }) + levelDays: number; + + @Column({ + type: "text", + nullable: true, + comment: "วุฒิการศึกษา", + default: null, + }) + Educations: string; + + @Column({ + nullable: true, + comment: "ระดับศึกษา", + length: 255, + default: null, + }) + educationLevels: string; + + @Column({ + nullable: true, + comment: "สาขาวิชา/ทาง", + length: 255, + default: null, + }) + fields: string; +} + +export class RegistryEmployeeCreateDto { + profileEmployeeId: string; + citizenId?: string | null; + prefix?: string | null; + firstName?: string | null; + lastName?: string | null; + isProbation?: boolean | null; + isLeave?: boolean | null; + isRetirement?: boolean | null; + leaveType?: string | null; + posMasterNo?: string | null; + // orgRootId?: string | null; + orgChild1Id?: string | null; + orgChild2Id?: string | null; + orgChild3Id?: string | null; + orgChild4Id?: string | null; + orgRootName?: string | null; + orgChild1Name?: string | null; + orgChild2Name?: string | null; + orgChild3Name?: string | null; + orgChild4Name?: string | null; + org?: string | null; + searchShortName?: string | null; + position?: string | null; + posTypeName?: string | null; + posLevelName?: string | null; + gender?: string | null; + relationship?: string | null; + dateAppoint?: Date | null; + dateRetire?: Date | null; + dateRetireLaw?: Date | null; + birthdate?: Date | null; + degrees?: string | null; + age?: number | null; + Years?: number | null; + Months?: number | null; + Days?: number | null; + levelYears?: number | null; + levelMonths?: number | null; + levelDays?: number | null; + Educations?: string | null; + educationLevels?: string | null; + fields?: string | null; +} \ No newline at end of file diff --git a/src/migration/1756282384029-create_table_registry_.ts b/src/migration/1756282384029-create_table_registry_.ts new file mode 100644 index 00000000..fe3b8d02 --- /dev/null +++ b/src/migration/1756282384029-create_table_registry_.ts @@ -0,0 +1,16 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class CreateTableRegistry_1756282384029 implements MigrationInterface { + name = 'CreateTableRegistry_1756282384029' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE \`registryEmployee\` (\`id\` varchar(36) NOT NULL, \`createdAt\` datetime(6) NOT NULL COMMENT 'สร้างข้อมูลเมื่อ' DEFAULT CURRENT_TIMESTAMP(6), \`createdUserId\` varchar(40) NOT NULL COMMENT 'User Id ที่สร้างข้อมูล' DEFAULT '00000000-0000-0000-0000-000000000000', \`lastUpdatedAt\` datetime(6) NOT NULL COMMENT 'แก้ไขข้อมูลล่าสุดเมื่อ' DEFAULT CURRENT_TIMESTAMP(6), \`lastUpdateUserId\` varchar(40) NOT NULL COMMENT 'User Id ที่แก้ไขข้อมูล' DEFAULT '00000000-0000-0000-0000-000000000000', \`createdFullName\` varchar(200) NOT NULL COMMENT 'ชื่อ User ที่สร้างข้อมูล' DEFAULT 'System Administrator', \`lastUpdateFullName\` varchar(200) NOT NULL COMMENT 'ชื่อ User ที่แก้ไขข้อมูลล่าสุด' DEFAULT 'System Administrator', \`profileEmployeeId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง profileEmployee', \`citizenId\` varchar(13) NULL COMMENT 'เลขประจำตัวประชาชน', \`prefix\` varchar(20) NULL COMMENT 'คำนำหน้าชื่อ เช่น นาย นาง นางสาว', \`firstName\` varchar(255) NULL COMMENT 'ชื่อ', \`lastName\` varchar(255) NULL COMMENT 'นามสกุล', \`isProbation\` tinyint NOT NULL COMMENT 'ทดลองปฏิบัติหน้าที่' DEFAULT 0, \`isLeave\` tinyint NOT NULL COMMENT 'พ้นราชการ' DEFAULT 0, \`isRetirement\` tinyint NOT NULL COMMENT 'เกษียณ' DEFAULT 0, \`leaveType\` varchar(255) NULL COMMENT 'ประเภทพ้นคำสั่งพ้นจากราชการ', \`posMasterNo\` varchar(255) NULL COMMENT 'เลขที่ตำแหน่ง เป็นตัวเลข', \`orgRootId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgRoot', \`orgChild1Id\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgChild1', \`orgChild2Id\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgChild2', \`orgChild3Id\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgChild3', \`orgChild4Id\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgChild4', \`orgRootName\` varchar(255) NULL COMMENT 'ชื่อหน่วยงาน', \`orgChild1Name\` varchar(255) NULL COMMENT 'ชื่อส่วนราชการ Child1', \`orgChild2Name\` varchar(255) NULL COMMENT 'ชื่อส่วนราชการ Child2', \`orgChild3Name\` varchar(255) NULL COMMENT 'ชื่อส่วนราชการ Child3', \`orgChild4Name\` varchar(255) NULL COMMENT 'ชื่อส่วนราชการ Child4', \`org\` varchar(255) NULL COMMENT 'สังกัด', \`searchShortName\` varchar(255) NULL COMMENT 'เลขที่ตำแหน่ง', \`position\` varchar(255) NULL COMMENT 'ชื่อตำแหน่งในสายงาน', \`posTypeName\` varchar(255) NULL COMMENT 'ประเภทตำแหน่ง', \`posLevelName\` varchar(255) NULL COMMENT 'ระดับตำแหน่ง', \`gender\` varchar(40) NULL COMMENT 'เพศ', \`relationship\` varchar(40) NULL COMMENT 'ความสัมพันธ์', \`dateAppoint\` datetime NULL COMMENT 'วันที่บรรจุ', \`dateRetire\` datetime NULL COMMENT 'วันครบเกษียณอายุ', \`dateRetireLaw\` datetime NULL COMMENT 'วันที่เกษียณอายุราชการตามกฏหมาย', \`birthdate\` datetime NULL COMMENT 'วันเกิด', \`degrees\` varchar(255) NULL COMMENT 'วุฒิการศึกษา', \`age\` int NULL COMMENT 'อายุ', \`Years\` int NULL COMMENT 'จำนวนปีระยะเวลาดำรงตำแหน่งในสายงาน', \`Months\` int NULL COMMENT 'จำนวนเดือนระยะเวลาดำรงตำแหน่งในสายงาน', \`Days\` int NULL COMMENT 'จำนวนวันระยะเวลาดำรงตำแหน่งในสายงาน', \`levelYears\` int NULL COMMENT 'จำนวนปีระยะเวลาดำรงตำแหน่งตามระดับ', \`levelMonths\` int NULL COMMENT 'จำนวนเดือนระยะเวลาดำรงตำแหน่งตามระดับ', \`levelDays\` int NULL COMMENT 'จำนวนวันระยะเวลาดำรงตำแหน่งตามระดับ', \`Educations\` text NULL COMMENT 'วุฒิการศึกษา', \`educationLevels\` varchar(255) NULL COMMENT 'ระดับศึกษา', \`fields\` varchar(255) NULL COMMENT 'สาขาวิชา/ทาง', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + await queryRunner.query(`CREATE TABLE \`registry\` (\`id\` varchar(36) NOT NULL, \`createdAt\` datetime(6) NOT NULL COMMENT 'สร้างข้อมูลเมื่อ' DEFAULT CURRENT_TIMESTAMP(6), \`createdUserId\` varchar(40) NOT NULL COMMENT 'User Id ที่สร้างข้อมูล' DEFAULT '00000000-0000-0000-0000-000000000000', \`lastUpdatedAt\` datetime(6) NOT NULL COMMENT 'แก้ไขข้อมูลล่าสุดเมื่อ' DEFAULT CURRENT_TIMESTAMP(6), \`lastUpdateUserId\` varchar(40) NOT NULL COMMENT 'User Id ที่แก้ไขข้อมูล' DEFAULT '00000000-0000-0000-0000-000000000000', \`createdFullName\` varchar(200) NOT NULL COMMENT 'ชื่อ User ที่สร้างข้อมูล' DEFAULT 'System Administrator', \`lastUpdateFullName\` varchar(200) NOT NULL COMMENT 'ชื่อ User ที่แก้ไขข้อมูลล่าสุด' DEFAULT 'System Administrator', \`profileId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง profile', \`citizenId\` varchar(13) NULL COMMENT 'เลขประจำตัวประชาชน', \`prefix\` varchar(20) NULL COMMENT 'คำนำหน้าชื่อ เช่น นาย นาง นางสาว', \`firstName\` varchar(255) NULL COMMENT 'ชื่อ', \`lastName\` varchar(255) NULL COMMENT 'นามสกุล', \`isProbation\` tinyint NOT NULL COMMENT 'ทดลองปฏิบัติหน้าที่' DEFAULT 0, \`isLeave\` tinyint NOT NULL COMMENT 'พ้นราชการ' DEFAULT 0, \`isRetirement\` tinyint NOT NULL COMMENT 'เกษียณ' DEFAULT 0, \`leaveType\` varchar(255) NULL COMMENT 'ประเภทพ้นคำสั่งพ้นจากราชการ', \`posMasterNo\` varchar(255) NULL COMMENT 'เลขที่ตำแหน่ง เป็นตัวเลข', \`orgRootId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgRoot', \`orgChild1Id\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgChild1', \`orgChild2Id\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgChild2', \`orgChild3Id\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgChild3', \`orgChild4Id\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง orgChild4', \`orgRootName\` varchar(255) NULL COMMENT 'ชื่อหน่วยงาน', \`orgChild1Name\` varchar(255) NULL COMMENT 'ชื่อส่วนราชการ Child1', \`orgChild2Name\` varchar(255) NULL COMMENT 'ชื่อส่วนราชการ Child2', \`orgChild3Name\` varchar(255) NULL COMMENT 'ชื่อส่วนราชการ Child3', \`orgChild4Name\` varchar(255) NULL COMMENT 'ชื่อส่วนราชการ Child4', \`org\` varchar(255) NULL COMMENT 'สังกัด', \`searchShortName\` varchar(255) NULL COMMENT 'เลขที่ตำแหน่ง', \`posExecutiveName\` varchar(255) NULL COMMENT 'ชื่อตำแหน่งทางการบริหาร', \`position\` varchar(255) NULL COMMENT 'ชื่อตำแหน่งในสายงาน', \`posTypeName\` varchar(255) NULL COMMENT 'ประเภทตำแหน่ง', \`posLevelName\` varchar(255) NULL COMMENT 'ระดับตำแหน่ง', \`gender\` varchar(40) NULL COMMENT 'เพศ', \`relationship\` varchar(40) NULL COMMENT 'ความสัมพันธ์', \`dateAppoint\` datetime NULL COMMENT 'วันที่บรรจุ', \`dateRetire\` datetime NULL COMMENT 'วันครบเกษียณอายุ', \`dateRetireLaw\` datetime NULL COMMENT 'วันที่เกษียณอายุราชการตามกฏหมาย', \`birthdate\` datetime NULL COMMENT 'วันเกิด', \`degrees\` varchar(255) NULL COMMENT 'วุฒิการศึกษา', \`age\` int NULL COMMENT 'อายุ', \`Years\` int NULL COMMENT 'จำนวนปีระยะเวลาดำรงตำแหน่งในสายงาน', \`Months\` int NULL COMMENT 'จำนวนเดือนระยะเวลาดำรงตำแหน่งในสายงาน', \`Days\` int NULL COMMENT 'จำนวนวันระยะเวลาดำรงตำแหน่งในสายงาน', \`levelYears\` int NULL COMMENT 'จำนวนปีระยะเวลาดำรงตำแหน่งตามระดับ', \`levelMonths\` int NULL COMMENT 'จำนวนเดือนระยะเวลาดำรงตำแหน่งตามระดับ', \`levelDays\` int NULL COMMENT 'จำนวนวันระยะเวลาดำรงตำแหน่งตามระดับ', \`posExecutiveYears\` int NULL COMMENT 'จำนวนปีระยะเวลาดำรงตำแหน่งทางการบริหาร', \`posExecutiveMonths\` int NULL COMMENT 'จำนวนเดือนระยะเวลาดำรงตำแหน่งทางการบริหาร', \`posExecutiveDays\` int NULL COMMENT 'จำนวนวันระยะเวลาดำรงตำแหน่งทางการบริหาร', \`positionArea\` varchar(255) NULL COMMENT 'ด้าน/สาขา', \`Educations\` text NULL COMMENT 'วุฒิการศึกษา', \`educationLevels\` varchar(255) NULL COMMENT 'ระดับศึกษา', \`fields\` varchar(255) NULL COMMENT 'สาขาวิชา/ทาง', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE \`registry\``); + await queryRunner.query(`DROP TABLE \`registryEmployee\``); + } + +} From b5e534413aa92e10f840cc548ecd38cec5cd2487 Mon Sep 17 00:00:00 2001 From: Bright Date: Wed, 27 Aug 2025 18:21:04 +0700 Subject: [PATCH 11/21] =?UTF-8?q?Job=20insert=20=E0=B8=82=E0=B9=89?= =?UTF-8?q?=E0=B8=AD=E0=B8=A1=E0=B8=B9=E0=B8=A5=E0=B8=A3=E0=B8=B2=E0=B8=8A?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3&=E0=B8=A5=E0=B8=B9=E0=B8=81?= =?UTF-8?q?=E0=B8=88=E0=B9=89=E0=B8=B2=E0=B8=87=20#1406?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.ts | 2 + src/controllers/ProfileInsigniaController.ts | 3 +- src/controllers/ProfileSalaryController.ts | 47 +++++++++++++++++++- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/app.ts b/src/app.ts index 565cb46f..236fb105 100644 --- a/src/app.ts +++ b/src/app.ts @@ -81,6 +81,8 @@ async function main() { await profileSalaryController.cronjobTenureExecutivePositionOfficer(); await profileSalaryController.cronjobTenurePositionEmployee(); await profileSalaryController.cronjobTenureLevelEmployee(); + await profileSalaryController.Registry(); + await profileSalaryController.RegistryEmployee(); } catch (error) { console.error("Error executing function from controller:", error); } diff --git a/src/controllers/ProfileInsigniaController.ts b/src/controllers/ProfileInsigniaController.ts index f71f1601..93c792f5 100644 --- a/src/controllers/ProfileInsigniaController.ts +++ b/src/controllers/ProfileInsigniaController.ts @@ -326,8 +326,7 @@ export class ProfileInsigniaController extends Controller { child4: x.orgChild4, age: age, amount: x.amount, - remark: x.remark, - commandCode: x.commandCode + remark: x.remark }; }); return new HttpSuccess(mapData); diff --git a/src/controllers/ProfileSalaryController.ts b/src/controllers/ProfileSalaryController.ts index f99c96bd..e40c8b0e 100644 --- a/src/controllers/ProfileSalaryController.ts +++ b/src/controllers/ProfileSalaryController.ts @@ -33,7 +33,10 @@ import { OrgRoot } from "../entities/OrgRoot"; import { OrgRevision } from "../entities/OrgRevision"; import { Position } from "../entities/Position"; import Extension from "../interfaces/extension"; - +import { viewRegistryOfficer } from "../entities/view/viewRegistryOfficer"; +import { viewRegistryEmployee } from "../entities/view/viewRegistryEmployee"; +import { Registry } from "../entities/Registry"; +import { RegistryEmployee } from "../entities/RegistryEmployee"; @Route("api/v1/org/profile/salary") @Tags("ProfileSalary") @Security("bearerAuth") @@ -51,7 +54,9 @@ export class ProfileSalaryController extends Controller { private orgRootRepository = AppDataSource.getRepository(OrgRoot); private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); private positionRepo = AppDataSource.getRepository(Position); - + private registryRepo = AppDataSource.getRepository(Registry); + private registryEmployeeRepo = AppDataSource.getRepository(RegistryEmployee); + @Get("TenurePositionOfficer") public async cronjobTenurePositionOfficer() { let data: any = []; @@ -332,6 +337,44 @@ export class ProfileSalaryController extends Controller { return new HttpSuccess(); } + @Get("Registry") + public async Registry() { + await this.registryRepo.clear(); + const profile = await this.profileRepo.find(); + for await (const x of profile) { + const _regis = await AppDataSource.getRepository(viewRegistryOfficer) + .createQueryBuilder("registryOfficer") + .where("registryOfficer.profileId IN (:id)", { id: x.id }) + .getMany(); + + const mapData = _regis.map(x => ({ + ...x, + Educations: x.Educations ? JSON.stringify(x.Educations) : "", + })); + await this.registryRepo.save(mapData); + } + return new HttpSuccess(); + } + + @Get("RegistryEmployee") + public async RegistryEmployee() { + await this.registryEmployeeRepo.clear(); + const profileEmp = await this.profileEmployeeRepo.find(); + for await (const x of profileEmp) { + const _regisEmp = await AppDataSource.getRepository(viewRegistryEmployee) + .createQueryBuilder("registryEmployee") + .where("registryEmployee.profileEmployeeId IN (:id)", { id: x.id }) + .getMany(); + + const mapData = _regisEmp.map(x => ({ + ...x, + Educations: x.Educations ? JSON.stringify(x.Educations) : "", + })); + await this.registryEmployeeRepo.save(mapData); + } + return new HttpSuccess(); + } + @Get("user") public async getSalaryUser(@Request() request: { user: Record }) { const profile = await this.profileRepo.findOneBy({ keycloak: request.user.sub }); From 8d010cd389994d07c7daa5df3dd536c9fc3034bb Mon Sep 17 00:00:00 2001 From: Bright Date: Thu, 28 Aug 2025 12:54:09 +0700 Subject: [PATCH 12/21] migrate && optimize --- src/controllers/ProfileSalaryController.ts | 32 +++++++++---------- src/entities/Registry.ts | 2 +- src/entities/RegistryEmployee.ts | 2 +- ...65498-update_registry_fix_length_prefix.ts | 20 ++++++++++++ 4 files changed, 38 insertions(+), 18 deletions(-) create mode 100644 src/migration/1756357065498-update_registry_fix_length_prefix.ts diff --git a/src/controllers/ProfileSalaryController.ts b/src/controllers/ProfileSalaryController.ts index e40c8b0e..1f3c614b 100644 --- a/src/controllers/ProfileSalaryController.ts +++ b/src/controllers/ProfileSalaryController.ts @@ -340,17 +340,17 @@ export class ProfileSalaryController extends Controller { @Get("Registry") public async Registry() { await this.registryRepo.clear(); - const profile = await this.profileRepo.find(); - for await (const x of profile) { - const _regis = await AppDataSource.getRepository(viewRegistryOfficer) - .createQueryBuilder("registryOfficer") - .where("registryOfficer.profileId IN (:id)", { id: x.id }) - .getMany(); - - const mapData = _regis.map(x => ({ + const allRegis = await AppDataSource.getRepository(viewRegistryOfficer) + .createQueryBuilder("registryOfficer") + .getMany(); + const profileIds = new Set((await this.profileRepo.find()).map(p => p.id)); + const mapData = allRegis + .filter(x => profileIds.has(x.profileId)) + .map(x => ({ ...x, Educations: x.Educations ? JSON.stringify(x.Educations) : "", })); + if (mapData.length > 0) { await this.registryRepo.save(mapData); } return new HttpSuccess(); @@ -359,17 +359,17 @@ export class ProfileSalaryController extends Controller { @Get("RegistryEmployee") public async RegistryEmployee() { await this.registryEmployeeRepo.clear(); - const profileEmp = await this.profileEmployeeRepo.find(); - for await (const x of profileEmp) { - const _regisEmp = await AppDataSource.getRepository(viewRegistryEmployee) - .createQueryBuilder("registryEmployee") - .where("registryEmployee.profileEmployeeId IN (:id)", { id: x.id }) - .getMany(); - - const mapData = _regisEmp.map(x => ({ + const allRegis = await AppDataSource.getRepository(viewRegistryEmployee) + .createQueryBuilder("registryEmployee") + .getMany(); + const profileEmpIds = new Set((await this.profileEmployeeRepo.find()).map(p => p.id)); + const mapData = allRegis + .filter(x => profileEmpIds.has(x.profileEmployeeId)) + .map(x => ({ ...x, Educations: x.Educations ? JSON.stringify(x.Educations) : "", })); + if (mapData.length > 0) { await this.registryEmployeeRepo.save(mapData); } return new HttpSuccess(); diff --git a/src/entities/Registry.ts b/src/entities/Registry.ts index 6bd03545..95a4d63e 100644 --- a/src/entities/Registry.ts +++ b/src/entities/Registry.ts @@ -24,7 +24,7 @@ export class Registry extends EntityBase { @Column({ nullable: true, - length: 20, + length: 40, comment: "คำนำหน้าชื่อ เช่น นาย นาง นางสาว", default: null, }) diff --git a/src/entities/RegistryEmployee.ts b/src/entities/RegistryEmployee.ts index d84acfde..e4b9dff6 100644 --- a/src/entities/RegistryEmployee.ts +++ b/src/entities/RegistryEmployee.ts @@ -24,7 +24,7 @@ export class RegistryEmployee extends EntityBase { @Column({ nullable: true, - length: 20, + length: 40, comment: "คำนำหน้าชื่อ เช่น นาย นาง นางสาว", default: null, }) diff --git a/src/migration/1756357065498-update_registry_fix_length_prefix.ts b/src/migration/1756357065498-update_registry_fix_length_prefix.ts new file mode 100644 index 00000000..564e3329 --- /dev/null +++ b/src/migration/1756357065498-update_registry_fix_length_prefix.ts @@ -0,0 +1,20 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class UpdateRegistryFixLengthPrefix1756357065498 implements MigrationInterface { + name = 'UpdateRegistryFixLengthPrefix1756357065498' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`registryEmployee\` DROP COLUMN \`prefix\``); + await queryRunner.query(`ALTER TABLE \`registryEmployee\` ADD \`prefix\` varchar(40) NULL COMMENT 'คำนำหน้าชื่อ เช่น นาย นาง นางสาว'`); + await queryRunner.query(`ALTER TABLE \`registry\` DROP COLUMN \`prefix\``); + await queryRunner.query(`ALTER TABLE \`registry\` ADD \`prefix\` varchar(40) NULL COMMENT 'คำนำหน้าชื่อ เช่น นาย นาง นางสาว'`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`registry\` DROP COLUMN \`prefix\``); + await queryRunner.query(`ALTER TABLE \`registry\` ADD \`prefix\` varchar(20) NULL COMMENT 'คำนำหน้าชื่อ เช่น นาย นาง นางสาว'`); + await queryRunner.query(`ALTER TABLE \`registryEmployee\` DROP COLUMN \`prefix\``); + await queryRunner.query(`ALTER TABLE \`registryEmployee\` ADD \`prefix\` varchar(20) NULL COMMENT 'คำนำหน้าชื่อ เช่น นาย นาง นางสาว'`); + } + +} From 8079693f19aa12051e0eef1dbc482eeea1d95fd0 Mon Sep 17 00:00:00 2001 From: Bright Date: Thu, 28 Aug 2025 13:32:26 +0700 Subject: [PATCH 13/21] =?UTF-8?q?=E0=B8=97=E0=B8=B0=E0=B9=80=E0=B8=9A?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=99=E0=B8=9B=E0=B8=A3=E0=B8=B0=E0=B8=A7?= =?UTF-8?q?=E0=B8=B1=E0=B8=95=E0=B8=B4=E0=B8=84=E0=B9=89=E0=B8=99=E0=B8=AB?= =?UTF-8?q?=E0=B8=B2=E0=B8=82=E0=B8=B1=E0=B9=89=E0=B8=99=E0=B8=AA=E0=B8=B9?= =?UTF-8?q?=E0=B8=87=20#1406?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/ReportController.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/controllers/ReportController.ts b/src/controllers/ReportController.ts index cedd3105..d3f947f7 100644 --- a/src/controllers/ReportController.ts +++ b/src/controllers/ReportController.ts @@ -32,7 +32,8 @@ import { viewRegistryOfficer } from "../entities/view/viewRegistryOfficer"; import { viewRegistryEmployee } from "../entities/view/viewRegistryEmployee"; import { EmployeeTempPosMaster } from "../entities/EmployeeTempPosMaster"; // import { sendWebSocket } from "../services/webSocket"; - +import { Registry } from "../entities/Registry"; +import { RegistryEmployee } from "../entities/RegistryEmployee"; @Route("api/v1/org/report") @Tags("Report") @Security("bearerAuth") @@ -153,8 +154,9 @@ export class ReportController extends Controller { } else if (tenureType != "" && tenureType == "posExecutive") { tenureTypeCondition = "registryOfficer.posExecutiveYears BETWEEN :tenureMin AND :tenureMax"; } - - const [lists, total] = await AppDataSource.getRepository(viewRegistryOfficer) + // ดึงผ่าน Table แทน View + // const [lists, total] = await AppDataSource.getRepository(viewRegistryOfficer) + const [lists, total] = await AppDataSource.getRepository(Registry) .createQueryBuilder("registryOfficer") .where(nodeCondition, { nodeId: nodeId, @@ -493,8 +495,9 @@ export class ReportController extends Controller { retireLawCondition = "DATE(registryEmployee.dateRetireLaw) >= :startDateRetireLaw AND DATE(registryEmployee.dateRetireLaw) <= :endDateRetireLaw"; } - - const [lists, total] = await AppDataSource.getRepository(viewRegistryEmployee) + // ดึงผ่าน Table แทน View + // const [lists, total] = await AppDataSource.getRepository(viewRegistryEmployee) + const [lists, total] = await AppDataSource.getRepository(RegistryEmployee) .createQueryBuilder("registryEmployee") .where(nodeCondition, { nodeId: nodeId, From bbb8eeb278d17e99d74ae1ad140439a11398344b Mon Sep 17 00:00:00 2001 From: Bright Date: Thu, 28 Aug 2025 14:14:37 +0700 Subject: [PATCH 14/21] migrate (add registryEmployee.employeeClass) --- src/entities/RegistryEmployee.ts | 11 ++++++++++- ...ate_registrymployere_add_field_employeeClass.ts | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/migration/1756364862810-update_registrymployere_add_field_employeeClass.ts diff --git a/src/entities/RegistryEmployee.ts b/src/entities/RegistryEmployee.ts index e4b9dff6..31d0a8b2 100644 --- a/src/entities/RegistryEmployee.ts +++ b/src/entities/RegistryEmployee.ts @@ -327,6 +327,14 @@ export class RegistryEmployee extends EntityBase { default: null, }) fields: string; + + @Column({ + nullable: true, + comment: "ประเภทลูกจ้าง (perm->ลูกจ้างประจำ temp->ลูกจ้างชั่วคราว)", + length: 40, + default: null, + }) + employeeClass: string; } export class RegistryEmployeeCreateDto { @@ -340,7 +348,7 @@ export class RegistryEmployeeCreateDto { isRetirement?: boolean | null; leaveType?: string | null; posMasterNo?: string | null; - // orgRootId?: string | null; + orgRootId?: string | null; orgChild1Id?: string | null; orgChild2Id?: string | null; orgChild3Id?: string | null; @@ -372,4 +380,5 @@ export class RegistryEmployeeCreateDto { Educations?: string | null; educationLevels?: string | null; fields?: string | null; + employeeClass?: string | null; } \ No newline at end of file diff --git a/src/migration/1756364862810-update_registrymployere_add_field_employeeClass.ts b/src/migration/1756364862810-update_registrymployere_add_field_employeeClass.ts new file mode 100644 index 00000000..997ad8c5 --- /dev/null +++ b/src/migration/1756364862810-update_registrymployere_add_field_employeeClass.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class UpdateRegistrymployereAddFieldEmployeeClass1756364862810 implements MigrationInterface { + name = 'UpdateRegistrymployereAddFieldEmployeeClass1756364862810' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`registryEmployee\` ADD \`employeeClass\` varchar(40) NULL COMMENT 'ประเภทลูกจ้าง (perm->ลูกจ้างประจำ temp->ลูกจ้างชั่วคราว)'`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`registryEmployee\` DROP COLUMN \`employeeClass\``); + } + +} From ef7a8bc0e827482d5acb6ae3d603a9601edc9799 Mon Sep 17 00:00:00 2001 From: Bright Date: Thu, 28 Aug 2025 17:07:45 +0700 Subject: [PATCH 15/21] update #1406 --- src/controllers/ProfileSalaryController.ts | 6 ++++++ src/entities/view/viewRegistryEmployee.ts | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/controllers/ProfileSalaryController.ts b/src/controllers/ProfileSalaryController.ts index 1f3c614b..7f9615af 100644 --- a/src/controllers/ProfileSalaryController.ts +++ b/src/controllers/ProfileSalaryController.ts @@ -348,6 +348,9 @@ export class ProfileSalaryController extends Controller { .filter(x => profileIds.has(x.profileId)) .map(x => ({ ...x, + isProbation: Boolean(x.isProbation), + isLeave: Boolean(x.isLeave), + isRetirement: Boolean(x.isRetirement), Educations: x.Educations ? JSON.stringify(x.Educations) : "", })); if (mapData.length > 0) { @@ -367,6 +370,9 @@ export class ProfileSalaryController extends Controller { .filter(x => profileEmpIds.has(x.profileEmployeeId)) .map(x => ({ ...x, + isProbation: Boolean(x.isProbation), + isLeave: Boolean(x.isLeave), + isRetirement: Boolean(x.isRetirement), Educations: x.Educations ? JSON.stringify(x.Educations) : "", })); if (mapData.length > 0) { diff --git a/src/entities/view/viewRegistryEmployee.ts b/src/entities/view/viewRegistryEmployee.ts index a26e20f0..807d08bc 100644 --- a/src/entities/view/viewRegistryEmployee.ts +++ b/src/entities/view/viewRegistryEmployee.ts @@ -298,4 +298,7 @@ export class viewRegistryEmployee { @ViewColumn() fields: string; + + @ViewColumn() + employeeClass: string; } From 35eec3a5f5e64fd0703bb822ec8554ba630da0a4 Mon Sep 17 00:00:00 2001 From: Bright Date: Fri, 29 Aug 2025 11:29:31 +0700 Subject: [PATCH 16/21] =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B9=81=E0=B8=99=E0=B8=9A=E0=B9=84=E0=B8=9F=E0=B8=A5?= =?UTF-8?q?=E0=B9=8C=E0=B8=84=E0=B8=B3=E0=B8=AA=E0=B8=B1=E0=B9=88=E0=B8=87?= =?UTF-8?q?=E0=B9=83=E0=B8=99=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81=E0=B8=B2?= =?UTF-8?q?=E0=B8=A3=E0=B9=81=E0=B8=88=E0=B9=89=E0=B8=87=E0=B9=80=E0=B8=95?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=99=20#1784?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interfaces/utils.ts | 21 +++++++++++++++++++++ src/services/rabbitmq.ts | 6 ++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index 69b31e58..86668e38 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -561,6 +561,27 @@ export function chunkArray(array: any, size: number) { return result; } +export async function PayloadSendNoti(commandId: string) { + if (!commandId) + return ""; + const commandRepository = AppDataSource.getRepository(Command); + const _command = await commandRepository.findOne({ + where: { + id: commandId, + }, + relations: ["commandType"], + }); + const _payload = { + name: _command && _command.commandType + ? `คำสั่ง${_command.commandType.name}` + : "", + url: `${process.env.API_URL}/salary/file/ระบบออกคำสั่ง/คำสั่ง/${commandId}/คำสั่ง`, + isReport: true, + isTemplate: false, + } + return JSON.stringify(_payload); +} + export function commandTypePath(commandCode: string): string | null { switch (commandCode) { case "C-PM-01": diff --git a/src/services/rabbitmq.ts b/src/services/rabbitmq.ts index 8c19f8f7..d55667a6 100644 --- a/src/services/rabbitmq.ts +++ b/src/services/rabbitmq.ts @@ -24,6 +24,7 @@ import { PosMasterAct } from "../entities/PosMasterAct"; import { PermissionOrg } from "../entities/PermissionOrg"; import { sendWebSocket } from "./webSocket"; import { CreatePosMasterHistoryOfficer } from "./PositionService"; +import { PayloadSendNoti } from "../interfaces/utils"; export let sendToQueue: (payload: any) => void; export let sendToQueueOrg: (payload: any) => void; @@ -83,7 +84,7 @@ export async function init() { console.log("[AMQ] Listening for message..."); createConsumer(queue, channel, handler), //----> (3) Process Consumer - createConsumer(queue_org, channel, handler_org); + createConsumer(queue_org, channel, handler_org); createConsumer(queue_org_draft, channel, handler_org_draft); createConsumer(queue_command_noti, channel, handler_command_noti); // createConsumer(queue2, channel, handler2); @@ -445,6 +446,7 @@ async function handler_command_noti(msg: amqp.ConsumeMessage): Promise })) : []; + const payloadStr = await PayloadSendNoti(command.id); const profilesSendRequest = new CallAPI() .PostData( { headers: { authorization: token } }, @@ -453,7 +455,7 @@ async function handler_command_noti(msg: amqp.ConsumeMessage): Promise subject: `${command.issue}`, body: `${command.issue}`, receiverUserIds: profilesSend, - payload: "", // แนบไฟล์ (ถ้าจำเป็น) + payload: payloadStr, // แนบไฟล์ (ถ้าจำเป็น) }, false, ) From 51443fd4f2fded8ae8f8889e7f07e59abe8d567b Mon Sep 17 00:00:00 2001 From: Bright Date: Fri, 29 Aug 2025 13:27:07 +0700 Subject: [PATCH 17/21] =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A=20pa?= =?UTF-8?q?yload=20#1784?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interfaces/utils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index 86668e38..b473366d 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -579,7 +579,10 @@ export async function PayloadSendNoti(commandId: string) { isReport: true, isTemplate: false, } - return JSON.stringify(_payload); + const attachments = { + attachments: [_payload] + }; + return JSON.stringify(attachments); } export function commandTypePath(commandCode: string): string | null { From a143bc862e33b5d415dd0095693326b8a85cb27c Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Fri, 29 Aug 2025 13:35:52 +0700 Subject: [PATCH 18/21] add dna --- src/controllers/PositionController.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 2eb86e64..6ffa9708 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -2458,6 +2458,7 @@ export class PositionController extends Controller { return { id: posMaster.id, + ancestorDNA: posMaster.ancestorDNA, current_holderId: posMaster.current_holderId, next_holderId: posMaster.next_holderId, isDirector: posMaster.isDirector, From 9aab92ee3f93193706ac7ba6880e27c6f3bdc2f8 Mon Sep 17 00:00:00 2001 From: Bright Date: Fri, 29 Aug 2025 15:12:29 +0700 Subject: [PATCH 19/21] no message --- src/interfaces/utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index b473366d..cbd1b00b 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -571,6 +571,8 @@ export async function PayloadSendNoti(commandId: string) { }, relations: ["commandType"], }); + if (!_command || !_command.commandType) + return ""; const _payload = { name: _command && _command.commandType ? `คำสั่ง${_command.commandType.name}` From 7e474f6652406325b137a6231555252a248fbc15 Mon Sep 17 00:00:00 2001 From: Bright Date: Mon, 1 Sep 2025 11:15:30 +0700 Subject: [PATCH 20/21] Test --- src/controllers/ProfileSalaryController.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/controllers/ProfileSalaryController.ts b/src/controllers/ProfileSalaryController.ts index 7f9615af..6809f326 100644 --- a/src/controllers/ProfileSalaryController.ts +++ b/src/controllers/ProfileSalaryController.ts @@ -93,9 +93,12 @@ export class ProfileSalaryController extends Controller { profileId: x.id, positionName: calDayDiff.positionName, days_diff: calDayDiff.days_diff, - 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: (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); await this.positionOfficerRepo.save(mapData); @@ -143,9 +146,12 @@ export class ProfileSalaryController extends Controller { profileEmployeeId: x.id, positionName: calDayDiff.positionName, days_diff: calDayDiff.days_diff, - 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: (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); await this.positionEmployeeRepo.save(mapData); From 2bb44003c01c019430ea805b9f41ac020cff3ea0 Mon Sep 17 00:00:00 2001 From: Bright Date: Mon, 1 Sep 2025 13:47:27 +0700 Subject: [PATCH 21/21] =?UTF-8?q?=E0=B8=A3=E0=B8=B2=E0=B8=A2=E0=B8=81?= =?UTF-8?q?=E0=B8=B2=E0=B8=A3=E0=B9=81=E0=B8=88=E0=B9=89=E0=B8=87=E0=B9=80?= =?UTF-8?q?=E0=B8=95=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B9=80=E0=B8=9E=E0=B8=B4?= =?UTF-8?q?=E0=B9=88=E0=B8=A1=E0=B9=81=E0=B8=99=E0=B8=9A=E0=B9=80=E0=B8=AD?= =?UTF-8?q?=E0=B8=81=E0=B8=AA=E0=B8=B2=E0=B8=A3=E0=B9=81=E0=B8=99=E0=B8=9A?= =?UTF-8?q?=E0=B8=97=E0=B9=89=E0=B8=B2=E0=B8=A2=E0=B8=84=E0=B8=B3=E0=B8=AA?= =?UTF-8?q?=E0=B8=B1=E0=B9=88=E0=B8=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interfaces/utils.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index cbd1b00b..cf86f369 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -581,9 +581,25 @@ export async function PayloadSendNoti(commandId: string) { isReport: true, isTemplate: false, } - const attachments = { - attachments: [_payload] - }; + let attachments = {} + if (_command.commandType.isUploadAttachment === true) { + const _payloadAtt = { + name: _command && _command.commandType + ? `เอกสารแนบท้ายคำสั่ง${_command.commandType.name}` + : "", + url: `${process.env.API_URL}/salary/file/ระบบออกคำสั่ง/แนบท้าย/${commandId}/แนบท้าย`, + isReport: true, + isTemplate: false, + } + attachments = { + attachments: [_payload, _payloadAtt] + }; + } + else { + attachments = { + attachments: [_payload] + }; + } return JSON.stringify(attachments); }