From 7eae9d2c8d264e07f2a93e6ccf6bbffbd2054e22 Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Wed, 1 Oct 2025 08:59:18 +0700 Subject: [PATCH 01/50] =?UTF-8?q?=E0=B8=A5=E0=B8=9A=E0=B8=84=E0=B8=99?= =?UTF-8?q?=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B8=88=E0=B8=B2=E0=B8=81=E0=B9=82?= =?UTF-8?q?=E0=B8=84=E0=B8=A3=E0=B8=87=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/OrganizationController.ts | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 5ad7c11c..83b407a6 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -7915,4 +7915,31 @@ export class OrganizationController extends Controller { return new HttpSuccess(); } + + /** + * API ลบคนในโครงสร้าง + * + * @summary - ลบคนในโครงสร้าง (ADMIN) + * + */ + @Get("delete/profile/org/{orgRevisionId}") + async deleteRetireInOrg(@Path() orgRevisionId: string, @Request() request: RequestWithUser) { + const posMasters = await this.posMasterRepository.find({ + where: { + orgRevisionId: orgRevisionId, + current_holder: { + isLeave: true, + }, + }, + }); + + await Promise.all( + posMasters.map(async (posMaster) => { + posMaster.current_holderId = null; + await this.posMasterRepository.save(posMaster); + }), + ); + + return new HttpSuccess(); + } } From a366e5ca0d9ea51ba57f797b7bee225a0e2feb43 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 1 Oct 2025 11:33:00 +0700 Subject: [PATCH 02/50] update scrip update retire --- src/controllers/OrganizationController.ts | 125 ++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 83b407a6..e6ceb790 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -1,3 +1,5 @@ +import { ProfileSalaryHistory } from "./../entities/ProfileSalaryHistory"; +import { ProfileSalary } from "./../entities/ProfileSalary"; import { Controller, Get, @@ -31,6 +33,7 @@ import { sendToQueueOrg, sendToQueueOrgDraft } from "../services/rabbitmq"; import { PosType } from "../entities/PosType"; import { PosLevel } from "../entities/PosLevel"; import { PermissionOrg } from "../entities/PermissionOrg"; +import { deleteUser } from "../keycloak"; @Route("api/v1/org") @Tags("Organization") @@ -51,6 +54,9 @@ export class OrganizationController extends Controller { private posTypeRepository = AppDataSource.getRepository(PosType); private posLevelRepository = AppDataSource.getRepository(PosLevel); private permissionOrgRepository = AppDataSource.getRepository(PermissionOrg); + private profileSalaryRepository = AppDataSource.getRepository(ProfileSalary); + private salaryHistoryRepo = AppDataSource.getRepository(ProfileSalaryHistory); + private orgRevisionRepo = AppDataSource.getRepository(OrgRevision); /** * API ล้างข้อมูล @@ -7942,4 +7948,123 @@ export class OrganizationController extends Controller { return new HttpSuccess(); } + + /** + * API บันทึกลงประวัติตำแหน่ง + * + * @summary - แก้ไขเหตุผลการลาออก และปลดจาก keycloak (ADMIN) + * + */ + @Get("save/profile/position-history") + async saveRetireToPositionHistory(request: RequestWithUser) { + const profileLeave = await this.profileRepo.find({ + where: { + isLeave: true, + leaveType: "RETIRE", + }, + relations: [ + "posType", + "posLevel", + "current_holders", + "current_holders.orgRoot", + "current_holders.orgChild1", + "current_holders.orgChild2", + "current_holders.orgChild3", + "current_holders.orgChild4", + "current_holders.positions", + "current_holders.positions.posExecutive", + ], + }); + + const batchSize = 1000; + for (let i = 0; i < profileLeave.length; i += batchSize) { + const batch = profileLeave.slice(i, i + batchSize); + await Promise.all( + batch.map(async (profile: any) => { + const dest_item = await this.profileSalaryRepository.findOne({ + where: { profileId: profile.id }, + order: { order: "DESC" }, + }); + const data: any = { + order: dest_item == null ? 1 : dest_item.order + 1, + amount: null, + positionSalaryAmount: null, + mouthSalaryAmount: null, + profileId: profile.id, + posNo: "-", + positionExecutive: "-", + positionType: "-", + positionLevel: "-", + amountSpecial: null, + orgRoot: null, + orgChild1: null, + orgChild2: null, + orgChild3: null, + orgChild4: null, + commandYear: new Date().getFullYear() + 543, + commandDateAffect: profile.dateLeave, + commandCode: "16", + commandName: "พ้นจากราชการ", + posNoAbb: "-", + isEntry: false, + positionName: "เกษียณอายุราชการ", + createdUserId: request.user.sub, + createdFullName: request.user.name, + lastUpdateUserId: request.user.sub, + lastUpdateFullName: request.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + remark: "ประกาศคณะอนุกรรมการสามัญข้าราชการกรุงเทพมหานครสามัญ ลว. 31 มี.ค. 68", // script เกษียณจริง ๆ ให้เอา “วันที่ประกาศเกษียณฉบับแรก” มาลงในเอกสารอ้างอิง + isGovernment: false, + }; + + delete data.id; + + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + data.dateGovernment = profile.dateLeave; + await this.profileSalaryRepository.save(data); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history); + }), + ); + } + + return new HttpSuccess(); + } + + /** + * API แก้ไขเหตุผลการลาออก และปลดจาก keycloak + * + * @summary - แก้ไขเหตุผลการลาออก และปลดจาก keycloak (ADMIN) + * + */ + @Get("update/profile/leave-reason") + async updateRetireReason() { + const profileLeave = await this.profileRepo.find({ + where: { + isLeave: true, + leaveType: "RETIRE", + }, + }); + + const batchSize = 1000; + for (let i = 0; i < profileLeave.length; i += batchSize) { + const batch = profileLeave.slice(i, i + batchSize); + await Promise.all( + batch.map(async (profile) => { + const delUserKeycloak = await deleteUser(profile.keycloak); + if (delUserKeycloak) { + profile.leaveReason = "เกษียณอายุราชการ"; + profile.keycloak = ""; + profile.isActive = false; + profile.roleKeycloaks = []; + await this.profileRepo.save(profile); + } + }), + ); + } + + return new HttpSuccess(); + } } From aff6958149d2da76632f8b598306fd2c0ff6f8b4 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 1 Oct 2025 12:10:27 +0700 Subject: [PATCH 03/50] fix error script retire --- src/controllers/OrganizationController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index e6ceb790..702e857f 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -7956,7 +7956,7 @@ export class OrganizationController extends Controller { * */ @Get("save/profile/position-history") - async saveRetireToPositionHistory(request: RequestWithUser) { + async saveRetireToPositionHistory(@Request() request: RequestWithUser) { const profileLeave = await this.profileRepo.find({ where: { isLeave: true, From 08af004d4acde11710b5f11c4a2e83c507f57260 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 1 Oct 2025 13:01:54 +0700 Subject: [PATCH 04/50] fix script update retire of 2569 --- src/controllers/OrganizationController.ts | 152 +++++++++++----------- 1 file changed, 74 insertions(+), 78 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 702e857f..068170f6 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -56,7 +56,6 @@ export class OrganizationController extends Controller { private permissionOrgRepository = AppDataSource.getRepository(PermissionOrg); private profileSalaryRepository = AppDataSource.getRepository(ProfileSalary); private salaryHistoryRepo = AppDataSource.getRepository(ProfileSalaryHistory); - private orgRevisionRepo = AppDataSource.getRepository(OrgRevision); /** * API ล้างข้อมูล @@ -7939,14 +7938,18 @@ export class OrganizationController extends Controller { }, }); + let check = 0; await Promise.all( posMasters.map(async (posMaster) => { posMaster.current_holderId = null; await this.posMasterRepository.save(posMaster); + check += 1; }), ); - return new HttpSuccess(); + // จำนวนคนที่ถูกลบออกจากโครงสร้าง + const total = posMasters.length; + return new HttpSuccess({ total, successAmount: check }); } /** @@ -7962,75 +7965,64 @@ export class OrganizationController extends Controller { isLeave: true, leaveType: "RETIRE", }, - relations: [ - "posType", - "posLevel", - "current_holders", - "current_holders.orgRoot", - "current_holders.orgChild1", - "current_holders.orgChild2", - "current_holders.orgChild3", - "current_holders.orgChild4", - "current_holders.positions", - "current_holders.positions.posExecutive", - ], }); - const batchSize = 1000; - for (let i = 0; i < profileLeave.length; i += batchSize) { - const batch = profileLeave.slice(i, i + batchSize); - await Promise.all( - batch.map(async (profile: any) => { - const dest_item = await this.profileSalaryRepository.findOne({ - where: { profileId: profile.id }, - order: { order: "DESC" }, - }); - const data: any = { - order: dest_item == null ? 1 : dest_item.order + 1, - amount: null, - positionSalaryAmount: null, - mouthSalaryAmount: null, - profileId: profile.id, - posNo: "-", - positionExecutive: "-", - positionType: "-", - positionLevel: "-", - amountSpecial: null, - orgRoot: null, - orgChild1: null, - orgChild2: null, - orgChild3: null, - orgChild4: null, - commandYear: new Date().getFullYear() + 543, - commandDateAffect: profile.dateLeave, - commandCode: "16", - commandName: "พ้นจากราชการ", - posNoAbb: "-", - isEntry: false, - positionName: "เกษียณอายุราชการ", - createdUserId: request.user.sub, - createdFullName: request.user.name, - lastUpdateUserId: request.user.sub, - lastUpdateFullName: request.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - remark: "ประกาศคณะอนุกรรมการสามัญข้าราชการกรุงเทพมหานครสามัญ ลว. 31 มี.ค. 68", // script เกษียณจริง ๆ ให้เอา “วันที่ประกาศเกษียณฉบับแรก” มาลงในเอกสารอ้างอิง - isGovernment: false, - }; + let check: number = 0; + await Promise.all( + profileLeave.map(async (profile: any) => { + const dest_item = await this.profileSalaryRepository.findOne({ + where: { profileId: profile.id }, + order: { order: "DESC" }, + }); + const data: any = { + order: dest_item == null ? 1 : dest_item.order + 1, + amount: null, + positionSalaryAmount: null, + mouthSalaryAmount: null, + profileId: profile.id, + posNo: null, + positionExecutive: null, + positionType: null, + positionLevel: null, + amountSpecial: null, + orgRoot: null, + orgChild1: null, + orgChild2: null, + orgChild3: null, + orgChild4: null, + commandYear: new Date().getFullYear() + 543, + commandDateAffect: profile.dateLeave, + commandCode: "16", + commandName: "พ้นจากราชการ", + posNoAbb: null, + isEntry: false, + positionName: "เกษียณอายุราชการ", + createdUserId: request.user.sub, + createdFullName: request.user.name, + lastUpdateUserId: request.user.sub, + lastUpdateFullName: request.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + remark: "ประกาศคณะอนุกรรมการสามัญข้าราชการกรุงเทพมหานครสามัญ ลว. 31 มี.ค. 68", // script เกษียณจริง ๆ ให้เอา “วันที่ประกาศเกษียณฉบับแรก” มาลงในเอกสารอ้างอิง + isGovernment: false, + }; - delete data.id; + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + data.dateGovernment = profile.dateLeave; + const savedData = await this.profileSalaryRepository.save(data); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...data, id: undefined }); - data.dateGovernment = profile.dateLeave; - await this.profileSalaryRepository.save(data); - history.profileSalaryId = data.id; - await this.salaryHistoryRepo.save(history); - }), - ); - } + history.profileSalaryId = savedData.id; + await this.salaryHistoryRepo.save(history); - return new HttpSuccess(); + check += 1; + }), + ); + + // จำนวนคนที่บันทึกลงประวัติตำแหน่ง + const total = profileLeave.length; + // จำนวนคนที่ถูกบันทึกลงประวัติตำแหน่งสำเร็จ + return new HttpSuccess({ total, successAmount: check }); } /** @@ -8048,23 +8040,27 @@ export class OrganizationController extends Controller { }, }); - const batchSize = 1000; - for (let i = 0; i < profileLeave.length; i += batchSize) { - const batch = profileLeave.slice(i, i + batchSize); - await Promise.all( - batch.map(async (profile) => { + let check: number = 0; + await Promise.all( + profileLeave.map(async (profile) => { + profile.leaveReason = "เกษียณอายุราชการ"; + profile.isActive = false; + + if (profile.keycloak != null) { const delUserKeycloak = await deleteUser(profile.keycloak); if (delUserKeycloak) { - profile.leaveReason = "เกษียณอายุราชการ"; profile.keycloak = ""; - profile.isActive = false; profile.roleKeycloaks = []; - await this.profileRepo.save(profile); + check += 1; } - }), - ); - } + } - return new HttpSuccess(); + await this.profileRepo.save(profile); + }), + ); + + // จำนวนคนที่ถูกแก้ไขเหตุผลการลาออก + const total = profileLeave.length; + return new HttpSuccess({ total, successAmount: check }); } } From 18aa974c3d23078c7468fa023634409d7e588f18 Mon Sep 17 00:00:00 2001 From: harid Date: Wed, 1 Oct 2025 16:31:40 +0700 Subject: [PATCH 05/50] update --- src/controllers/ProfileController.ts | 2 +- src/controllers/ProfileEmployeeController.ts | 2 +- .../ProfileGovernmentController.ts | 74 ++++++++++++++----- .../ProfileGovernmentEmployeeController.ts | 74 ++++++++++++++----- 4 files changed, 114 insertions(+), 38 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 33d33227..636a4e48 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -5888,7 +5888,7 @@ export class ProfileController extends Controller { ) // .andWhere("profile.leaveCommandId Is NOT NULL") .andWhere( - "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id)", + "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id and profileSalary.positionName != 'เกษียณอายุราชการ')", ) .andWhere( diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 04b43f87..4bc85898 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -2786,7 +2786,7 @@ export class ProfileEmployeeController extends Controller { ) // .andWhere("profileEmployee.leaveCommandId Is NOT NULL") .andWhere( - "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id)", + "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id and profileSalary.positionName != 'เกษียณอายุราชการ')", ) // .andWhere( diff --git a/src/controllers/ProfileGovernmentController.ts b/src/controllers/ProfileGovernmentController.ts index 6f63bd0e..8abe6617 100644 --- a/src/controllers/ProfileGovernmentController.ts +++ b/src/controllers/ProfileGovernmentController.ts @@ -237,14 +237,33 @@ export class ProfileGovernmentHistoryController extends Controller { } } let _OrgLeave:any = [] + let _profileSalary:any = null; if (record?.isLeave && record?.profileSalary.length > 0) { - _OrgLeave = [ - record?.profileSalary[0].orgChild4 ? record?.profileSalary[0].orgChild4 : null, - record?.profileSalary[0].orgChild3 ? record?.profileSalary[0].orgChild3 : null, - record?.profileSalary[0].orgChild2 ? record?.profileSalary[0].orgChild2 : null, - record?.profileSalary[0].orgChild1 ? record?.profileSalary[0].orgChild1 : null, - record?.profileSalary[0].orgRoot ? record?.profileSalary[0].orgRoot : null, - ]; + // _OrgLeave = [ + // record?.profileSalary[0].orgChild4 ? record?.profileSalary[0].orgChild4 : null, + // record?.profileSalary[0].orgChild3 ? record?.profileSalary[0].orgChild3 : null, + // record?.profileSalary[0].orgChild2 ? record?.profileSalary[0].orgChild2 : null, + // record?.profileSalary[0].orgChild1 ? record?.profileSalary[0].orgChild1 : null, + // record?.profileSalary[0].orgRoot ? record?.profileSalary[0].orgRoot : null, + // ]; + if (record.leaveType == "RETIRE") { + _profileSalary = record?.profileSalary.length > 1 + ? record?.profileSalary[1] + : null; + } else { + _profileSalary = record?.profileSalary[0]; + } + if (_profileSalary) { + _OrgLeave = [ + _profileSalary.orgChild4 ?? null, + _profileSalary.orgChild3 ?? null, + _profileSalary.orgChild2 ?? null, + _profileSalary.orgChild1 ?? null, + _profileSalary.orgRoot ?? null, + ]; + } else { + _OrgLeave = []; + } } const orgLeave = _OrgLeave.filter((x:any) => x !== undefined && x !== null).join("\n"); const data = { @@ -254,8 +273,8 @@ export class ProfileGovernmentHistoryController extends Controller { posLevel: record?.posLevel == null ? null : record?.posLevel.posLevelName, //ระดับ posMasterNo: record?.isLeave == false ? posMaster == null ? null : `${orgShortName} ${posMaster.posMasterNo}` - : record && record?.profileSalary.length > 0 - ? `${record?.profileSalary[0].posNoAbb} ${record?.profileSalary[0].posNo}` + : _profileSalary != null + ? `${_profileSalary.posNoAbb} ${_profileSalary.posNo}` : null, //เลขที่ตำแหน่ง posType: record?.posType == null ? null : record?.posType.posTypeName, //ประเภท posExecutive: @@ -373,14 +392,33 @@ export class ProfileGovernmentHistoryController extends Controller { } } let _OrgLeave:any = [] + let _profileSalary:any = null; if (record?.isLeave && record?.profileSalary.length > 0) { - _OrgLeave = [ - record?.profileSalary[0].orgChild4 ? record?.profileSalary[0].orgChild4 : null, - record?.profileSalary[0].orgChild3 ? record?.profileSalary[0].orgChild3 : null, - record?.profileSalary[0].orgChild2 ? record?.profileSalary[0].orgChild2 : null, - record?.profileSalary[0].orgChild1 ? record?.profileSalary[0].orgChild1 : null, - record?.profileSalary[0].orgRoot ? record?.profileSalary[0].orgRoot : null, - ]; + // _OrgLeave = [ + // record?.profileSalary[0].orgChild4 ? record?.profileSalary[0].orgChild4 : null, + // record?.profileSalary[0].orgChild3 ? record?.profileSalary[0].orgChild3 : null, + // record?.profileSalary[0].orgChild2 ? record?.profileSalary[0].orgChild2 : null, + // record?.profileSalary[0].orgChild1 ? record?.profileSalary[0].orgChild1 : null, + // record?.profileSalary[0].orgRoot ? record?.profileSalary[0].orgRoot : null, + // ]; + if (record.leaveType == "RETIRE") { + _profileSalary = record?.profileSalary.length > 1 + ? record?.profileSalary[1] + : null; + } else { + _profileSalary = record?.profileSalary[0]; + } + if (_profileSalary) { + _OrgLeave = [ + _profileSalary.orgChild4 ?? null, + _profileSalary.orgChild3 ?? null, + _profileSalary.orgChild2 ?? null, + _profileSalary.orgChild1 ?? null, + _profileSalary.orgRoot ?? null, + ]; + } else { + _OrgLeave = []; + } } const orgLeave = _OrgLeave.filter((x:any) => x !== undefined && x !== null).join("\n"); const data = { @@ -393,8 +431,8 @@ export class ProfileGovernmentHistoryController extends Controller { ? posMaster == null ? null : `${orgShortName} ${posMaster.posMasterNo}` - : record && record.profileSalary.length > 0 - ? `${record?.profileSalary[0].posNoAbb} ${record?.profileSalary[0].posNo}` + : _profileSalary != null + ? `${_profileSalary.posNoAbb} ${_profileSalary.posNo}` : null, //เลขที่ตำแหน่ง posType: record?.posType == null ? null : record?.posType.posTypeName, //ประเภท posExecutive: diff --git a/src/controllers/ProfileGovernmentEmployeeController.ts b/src/controllers/ProfileGovernmentEmployeeController.ts index 0cfd43e3..9191eb2e 100644 --- a/src/controllers/ProfileGovernmentEmployeeController.ts +++ b/src/controllers/ProfileGovernmentEmployeeController.ts @@ -220,6 +220,7 @@ export class ProfileGovernmentEmployeeController extends Controller { let _OrgLeave:any = [] let orgLeave:string = "" let posNoLeave:string = "" + let _profileSalary:any = null; if (record?.isLeave /*&& record?.profileSalary.length > 0*/) { const profileSalary = await this.salaryRepo.find({ select: [ @@ -255,16 +256,34 @@ export class ProfileGovernmentEmployeeController extends Controller { createdAt: "DESC" } }); - _OrgLeave = [ - profileSalary.length > 0 && profileSalary[0].orgChild4 ? profileSalary[0].orgChild4 : null, - profileSalary.length > 0 && profileSalary[0].orgChild3 ? profileSalary[0].orgChild3 : null, - profileSalary.length > 0 && profileSalary[0].orgChild2 ? profileSalary[0].orgChild2 : null, - profileSalary.length > 0 && profileSalary[0].orgChild1 ? profileSalary[0].orgChild1 : null, - profileSalary.length > 0 && profileSalary[0].orgRoot ? profileSalary[0].orgRoot : null, - ]; + // _OrgLeave = [ + // profileSalary.length > 0 && profileSalary[0].orgChild4 ? profileSalary[0].orgChild4 : null, + // profileSalary.length > 0 && profileSalary[0].orgChild3 ? profileSalary[0].orgChild3 : null, + // profileSalary.length > 0 && profileSalary[0].orgChild2 ? profileSalary[0].orgChild2 : null, + // profileSalary.length > 0 && profileSalary[0].orgChild1 ? profileSalary[0].orgChild1 : null, + // profileSalary.length > 0 && profileSalary[0].orgRoot ? profileSalary[0].orgRoot : null, + // ]; + if (record.leaveType == "RETIRE") { + _profileSalary = record?.profileSalary.length > 1 + ? record?.profileSalary[1] + : null; + } else { + _profileSalary = record?.profileSalary[0]; + } + if (_profileSalary) { + _OrgLeave = [ + _profileSalary.orgChild4 ?? null, + _profileSalary.orgChild3 ?? null, + _profileSalary.orgChild2 ?? null, + _profileSalary.orgChild1 ?? null, + _profileSalary.orgRoot ?? null, + ]; + } else { + _OrgLeave = []; + } orgLeave = _OrgLeave.filter((x:any) => x !== undefined && x !== null).join("\n"); - posNoLeave = profileSalary.length > 0 - ? `${profileSalary[0].posNoAbb} ${profileSalary[0].posNo}` + posNoLeave = _profileSalary != null + ? `${_profileSalary.posNoAbb} ${_profileSalary.posNo}` : "" } const data = { @@ -372,6 +391,7 @@ export class ProfileGovernmentEmployeeController extends Controller { let _OrgLeave:any = [] let orgLeave:string = "" let posNoLeave:string = "" + let _profileSalary:any = null; if (record?.isLeave /*&& record?.profileSalary.length > 0*/) { const profileSalary = await this.salaryRepo.find({ select: [ @@ -407,16 +427,34 @@ export class ProfileGovernmentEmployeeController extends Controller { createdAt: "DESC" } }); - _OrgLeave = [ - profileSalary.length > 0 && profileSalary[0].orgChild4 ? profileSalary[0].orgChild4 : null, - profileSalary.length > 0 && profileSalary[0].orgChild3 ? profileSalary[0].orgChild3 : null, - profileSalary.length > 0 && profileSalary[0].orgChild2 ? profileSalary[0].orgChild2 : null, - profileSalary.length > 0 && profileSalary[0].orgChild1 ? profileSalary[0].orgChild1 : null, - profileSalary.length > 0 && profileSalary[0].orgRoot ? profileSalary[0].orgRoot : null, - ]; + // _OrgLeave = [ + // profileSalary.length > 0 && profileSalary[0].orgChild4 ? profileSalary[0].orgChild4 : null, + // profileSalary.length > 0 && profileSalary[0].orgChild3 ? profileSalary[0].orgChild3 : null, + // profileSalary.length > 0 && profileSalary[0].orgChild2 ? profileSalary[0].orgChild2 : null, + // profileSalary.length > 0 && profileSalary[0].orgChild1 ? profileSalary[0].orgChild1 : null, + // profileSalary.length > 0 && profileSalary[0].orgRoot ? profileSalary[0].orgRoot : null, + // ]; + if (record.leaveType == "RETIRE") { + _profileSalary = record?.profileSalary.length > 1 + ? record?.profileSalary[1] + : null; + } else { + _profileSalary = record?.profileSalary[0]; + } + if (_profileSalary) { + _OrgLeave = [ + _profileSalary.orgChild4 ?? null, + _profileSalary.orgChild3 ?? null, + _profileSalary.orgChild2 ?? null, + _profileSalary.orgChild1 ?? null, + _profileSalary.orgRoot ?? null, + ]; + } else { + _OrgLeave = []; + } orgLeave = _OrgLeave.filter((x:any) => x !== undefined && x !== null).join("\n"); - posNoLeave = profileSalary.length > 0 - ? `${profileSalary[0].posNoAbb} ${profileSalary[0].posNo}` + posNoLeave = _profileSalary != null + ? `${_profileSalary.posNoAbb} ${_profileSalary.posNo}` : "" } const data = { From d8d376386d9fc39ccdc03e888ffc8b8ad16909bc Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 1 Oct 2025 16:37:23 +0700 Subject: [PATCH 06/50] fix --- src/controllers/OrganizationController.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 068170f6..c12b1a72 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -8041,17 +8041,21 @@ export class OrganizationController extends Controller { }); let check: number = 0; + let notDelete: string[] = []; await Promise.all( profileLeave.map(async (profile) => { profile.leaveReason = "เกษียณอายุราชการ"; profile.isActive = false; - if (profile.keycloak != null) { + if (profile.keycloak != null && profile.keycloak != "") { const delUserKeycloak = await deleteUser(profile.keycloak); if (delUserKeycloak) { profile.keycloak = ""; profile.roleKeycloaks = []; check += 1; + } else { + // push array not delete + notDelete.push(profile.keycloak); } } @@ -8061,6 +8065,6 @@ export class OrganizationController extends Controller { // จำนวนคนที่ถูกแก้ไขเหตุผลการลาออก const total = profileLeave.length; - return new HttpSuccess({ total, successAmount: check }); + return new HttpSuccess({ total, successAmount: check, notDelete }); } } From 77918a4b6b0c5836066a4919a764877958b56d77 Mon Sep 17 00:00:00 2001 From: Adisak Date: Wed, 1 Oct 2025 17:41:27 +0700 Subject: [PATCH 07/50] sort --- src/controllers/ProfileController.ts | 36 ++++++++++++++++++-- src/controllers/ProfileEmployeeController.ts | 36 ++++++++++++++++++-- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 33d33227..76950695 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -9781,6 +9781,8 @@ export class ProfileController extends Controller { rootId?: string; year: number; period: string; + sortBy?: string; + descending?: boolean; }, ) { const findRevision = await this.orgRevisionRepo.findOne({ @@ -9790,7 +9792,7 @@ export class ProfileController extends Controller { throw new HttpError(HttpStatus.NOT_FOUND, "not found. OrgRevision"); } - const [findPosMaster, total] = await AppDataSource.getRepository(PosMaster) + let query = await AppDataSource.getRepository(PosMaster) .createQueryBuilder("posMaster") .leftJoinAndSelect("posMaster.current_holder", "current_holder") .leftJoinAndSelect("posMaster.orgRoot", "orgRoot") @@ -9923,7 +9925,37 @@ export class ProfileController extends Controller { ); }), ) - .orderBy("current_holder.citizenId", "ASC") + + if (body.sortBy) { + if(body.sortBy === "posType"){ + query = query.orderBy( + `posType.posTypeName`, + body.descending ? "DESC" : "ASC" + ); + }else if(body.sortBy === "posLevel"){ + query = query.orderBy( + `posLevel.posLevelName`, + body.descending ? "DESC" : "ASC" + ); + }else if(body.sortBy === "orgShortName" || body.sortBy === "posMasterNo"){ + query = query + .orderBy(`orgRoot.orgRootShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild1.orgChild1ShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild2.orgChild2ShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild3.orgChild3ShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild4.orgChild4ShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`posMaster.posMasterNo`,body.descending ? "DESC" : "ASC") + }else{ + query = query.orderBy( + `current_holder.${body.sortBy}`, + body.descending ? "DESC" : "ASC" + ); + } + }else{ + query = query.orderBy("current_holder.citizenId", "ASC") + } + + const [findPosMaster, total] = await query .skip((body.page - 1) * body.pageSize) .take(body.pageSize) .getManyAndCount(); diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 04b43f87..6b1ec5f4 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -4981,6 +4981,8 @@ export class ProfileEmployeeController extends Controller { rootId?: string; year: number; period: string; + sortBy?: string; + descending?: boolean; }, ) { const findRevision = await this.orgRevisionRepo.findOne({ @@ -4990,7 +4992,7 @@ export class ProfileEmployeeController extends Controller { throw new HttpError(HttpStatus.NOT_FOUND, "not found. OrgRevision"); } - const [findPosMaster, total] = await AppDataSource.getRepository(EmployeePosMaster) + let query = await AppDataSource.getRepository(EmployeePosMaster) .createQueryBuilder("employeePosMaster") .leftJoinAndSelect("employeePosMaster.current_holder", "current_holder") .leftJoinAndSelect("employeePosMaster.orgRoot", "orgRoot") @@ -5122,10 +5124,40 @@ export class ProfileEmployeeController extends Controller { ); }), ) - .orderBy("current_holder.citizenId", "ASC") + + if (body.sortBy) { + if(body.sortBy === "posType"){ + query = query.orderBy( + `posType.posTypeName`, + body.descending ? "DESC" : "ASC" + ); + }else if(body.sortBy === "posLevel"){ + query = query + .orderBy(`posType.posTypeShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`posLevel.posLevelName`,body.descending ? "DESC" : "ASC"); + }else if(body.sortBy === "orgShortName" || body.sortBy === "posMasterNo"){ + query = query + .orderBy(`orgRoot.orgRootShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild1.orgChild1ShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild2.orgChild2ShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild3.orgChild3ShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild4.orgChild4ShortName`,body.descending ? "DESC" : "ASC") + .addOrderBy(`employeePosMaster.posMasterNo`,body.descending ? "DESC" : "ASC") + }else{ + query = query.orderBy( + `current_holder.${body.sortBy}`, + body.descending ? "DESC" : "ASC" + ); + } + }else{ + query = query.orderBy("current_holder.citizenId", "ASC") + } + + const [findPosMaster, total] = await query .skip((body.page - 1) * body.pageSize) .take(body.pageSize) .getManyAndCount(); + if (!findPosMaster) { throw new HttpError(HttpStatus.NOT_FOUND, "not found. PosMaster"); } From e5dfed9312d32ac3d7d2a0d9c8599fce8ba82858 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 1 Oct 2025 17:58:36 +0700 Subject: [PATCH 08/50] fix query --- src/controllers/ProfileController.ts | 2 +- src/controllers/ProfileEmployeeController.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index bc02a392..e6f4c1e7 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -5888,7 +5888,7 @@ export class ProfileController extends Controller { ) // .andWhere("profile.leaveCommandId Is NOT NULL") .andWhere( - "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id and profileSalary.positionName != 'เกษียณอายุราชการ')", + "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id and ps.positionName != 'เกษียณอายุราชการ')", ) .andWhere( diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 076881de..7ee64703 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -2786,7 +2786,7 @@ export class ProfileEmployeeController extends Controller { ) // .andWhere("profileEmployee.leaveCommandId Is NOT NULL") .andWhere( - "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id and profileSalary.positionName != 'เกษียณอายุราชการ')", + "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id and ps.positionName != 'เกษียณอายุราชการ')", ) // .andWhere( From 7ffb698252593136ddee6de15df901697c99c682 Mon Sep 17 00:00:00 2001 From: harid Date: Wed, 1 Oct 2025 22:21:16 +0700 Subject: [PATCH 09/50] =?UTF-8?q?test=20job=20=E0=B9=80=E0=B8=81=E0=B8=A9?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.ts | 3 +- src/controllers/CommandController.ts | 162 ++++++++++++++++++++++++--- 2 files changed, 149 insertions(+), 16 deletions(-) diff --git a/src/app.ts b/src/app.ts index 236fb105..fb2c4d5b 100644 --- a/src/app.ts +++ b/src/app.ts @@ -62,7 +62,8 @@ async function main() { } }); - const cronTime_Oct = "0 0 1 10 *"; + // const cronTime_Oct = "0 0 1 10 *"; + const cronTime_Oct = "0 0 2 10 *"; // test 2 ตุลาคม cron.schedule(cronTime_Oct, async () => { try { const commandController = new CommandController(); diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 4e5f898e..2f72f709 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1499,6 +1499,7 @@ export class CommandController extends Controller { const today = new Date(); today.setUTCHours(0, 0, 0, 0); let type: string = "OFFICER"; + let _Date = new Date() try { const response_ = await axios.get( process.env.API_URL + `/retirement/update-status/${type}/${today.getFullYear()}`, @@ -1510,23 +1511,46 @@ export class CommandController extends Controller { }, }, ); - if (response && response_.data.result.length > 0) { - let profiles: Profile[] = []; + if (response_ && response_.data.result) { + let signDate:string = "" + if (response_.data.result.signDate != null) { + signDate = Extension.ToThaiShortDate_noPrefix(new Date(response_.data.result.signDate)) + } + else { + signDate = Extension.ToThaiShortDate_noPrefix(_Date) + } + // let profiles: Profile[] = []; await Promise.all( - response_.data.result.map(async (x: any) => { + response_.data.result.profiles.map(async (x: any) => { const _profile = await this.profileRepository.findOneBy({ id: x.profileId }); if (_profile) { + // console.log("1. บันทึกลงประวัติตำแหน่ง") + await this.profileSalaryRetire(x.profileId, type, signDate, _Date); + // console.log("2. ปลดออกจากโครงสร้าง") + await this.posMasterRetire(x.profileId, type, _Date); + // console.log("3. แก้ไขสถานะในทะเบียนประวัติ") _profile.isRetirement = true; _profile.isLeave = true; + _profile.isActive = false _profile.leaveType = "RETIRE"; - _profile.leaveDate = new Date(); - _profile.dateLeave = new Date(); - _profile.lastUpdatedAt = new Date(); - profiles.push(_profile); + _profile.leaveReason = "เกษียณอายุราชการ"; + _profile.leaveDate = _Date; + _profile.dateLeave = _Date; + _profile.lastUpdatedAt = _Date; + if (_profile.keycloak != null && _profile.keycloak != "") { + // console.log("4. disable keycloak/authen") + const delUserKeycloak = await deleteUser(_profile.keycloak); + if (delUserKeycloak) { + _profile.keycloak = ""; + _profile.roleKeycloaks = []; + } + } + // profiles.push(_profile); + // console.log("5. save profile ",_profile) + await this.profileRepository.save(_profile); } }), ); - await this.profileRepository.save(profiles); } } catch {} @@ -1542,29 +1566,137 @@ export class CommandController extends Controller { }, }, ); - if (response && response_.data.result.length > 0) { - let profiles: ProfileEmployee[] = []; + if (response_ && response_.data.result) { + let signDate:string = "" + if (response_.data.result.signDate != null) { + signDate = Extension.ToThaiShortDate_noPrefix(new Date(response_.data.result.signDate)) + } + else { + signDate = Extension.ToThaiShortDate_noPrefix(_Date) + } + // let profiles: ProfileEmployee[] = []; await Promise.all( response_.data.result.map(async (x: any) => { const _profileEmp = await this.profileEmployeeRepository.findOneBy({ id: x.profileId }); if (_profileEmp) { + // บันทึกลงประวัติตำแหน่ง + await this.profileSalaryRetire(x.profileId, type, signDate, _Date); + // ปลดออกจากโครงสร้าง + await this.posMasterRetire(x.profileId, type, _Date); + // แก้ไขสถานะในทะเบียนประวัติ _profileEmp.isRetirement = true; _profileEmp.isLeave = true; + _profileEmp.isActive = false _profileEmp.leaveType = "RETIRE"; - _profileEmp.leaveDate = new Date(); - _profileEmp.dateLeave = new Date(); - _profileEmp.lastUpdatedAt = new Date(); - profiles.push(_profileEmp); + _profileEmp.leaveReason = "เกษียณอายุราชการ"; + _profileEmp.leaveDate = _Date; + _profileEmp.dateLeave = _Date; + _profileEmp.lastUpdatedAt = _Date; + if (_profileEmp.keycloak != null && _profileEmp.keycloak != "") { + // disable keycloak/authen + const delUserKeycloak = await deleteUser(_profileEmp.keycloak); + if (delUserKeycloak) { + _profileEmp.keycloak = ""; + _profileEmp.roleKeycloaks = []; + } + } + // profiles.push(_profileEmp); + await this.profileEmployeeRepository.save(_profileEmp); } }), ); - await this.profileEmployeeRepository.save(profiles); } } catch {} return new HttpSuccess(); } + async profileSalaryRetire(profileId: string, type: string, signDate: string, _Date: Date) { + const whereKey = type == "OFFICER" + ? { profileId: profileId } + : { profileEmployeeId: profileId }; + const maxOrder = await this.salaryRepo.findOne({ + select: { order: true }, + where: whereKey, + order: { order: "DESC" }, + }); + const data: any = { + order: maxOrder ? maxOrder.order + 1 : 1, + amount: null, + positionSalaryAmount: null, + mouthSalaryAmount: null, + profileId: profileId, + posNo: null, + positionExecutive: null, + positionType: null, + positionLevel: null, + amountSpecial: null, + orgRoot: null, + orgChild1: null, + orgChild2: null, + orgChild3: null, + orgChild4: null, + commandYear: _Date.getFullYear() + 543, + commandDateAffect: _Date, + commandCode: "16", + commandName: "พ้นจากราชการ", + posNoAbb: null, + isEntry: false, + positionName: "เกษียณอายุราชการ", + createdUserId: "", + createdFullName: "System Administrator", + lastUpdateUserId: "", + lastUpdateFullName: "System Administrator", + createdAt: _Date, + lastUpdatedAt: _Date, + remark: `ประกาศคณะอนุกรรมการสามัญข้าราชการกรุงเทพมหานครสามัญ ลว. ${signDate}`, // script เกษียณจริง ๆ ให้เอา “วันที่ประกาศเกษียณฉบับแรก” มาลงในเอกสารอ้างอิง + isGovernment: false, + }; + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + data.dateGovernment = _Date; + const savedData = await this.salaryRepo.save(data); + history.profileSalaryId = savedData.id; + await this.salaryHistoryRepo.save(history); + } + + async posMasterRetire(profileId: string, type: string, _Date: Date) { + const orgRevision = await this.orgRevisionRepo.findOne({ + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false } + }); + if (orgRevision) { + let _posMaster:any = null; + if (type == "OFFICER") { + _posMaster = await this.posMasterRepository.findOne({ + where: { + orgRevisionId: orgRevision.id, + current_holderId: profileId, + }, + }); + if (_posMaster) { + _posMaster.current_holderId = null; + _posMaster.lastUpdateFullName = "System Administrator"; + _posMaster.lastUpdatedAt = _Date; + await this.posMasterRepository.save(_posMaster); + } + } + else if (type == "EMPLOYEE") { + _posMaster = await this.employeePosMasterRepository.findOne({ + where: { + orgRevisionId: orgRevision.id, + current_holderId: profileId, + }, + }); + if (_posMaster) { + _posMaster.current_holderId = null; + _posMaster.lastUpdateFullName = "System Administrator"; + _posMaster.lastUpdatedAt = _Date; + await this.employeePosMasterRepository.save(_posMaster); + } + } + } + } + @Get("cornjob/cronjobUpdateRetirementStatus") async runCronjobUpdateRetirementStatus() { await this.cronjobUpdateRetirementStatus(); From 6b719049b09ead7b541b09db57728d08926c7157 Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 2 Oct 2025 11:40:01 +0700 Subject: [PATCH 10/50] =?UTF-8?q?update=20script=20=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B8=99=E0=B9=80=E0=B8=81=E0=B8=A9=E0=B8=B5=E0=B8=A2=E0=B8=93?= =?UTF-8?q?=20#169?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/CommandController.ts | 13 +++++++--- .../ProfileGovernmentController.ts | 16 +++++++++---- .../ProfileGovernmentEmployeeController.ts | 24 ++++++++++++------- 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 2f72f709..68bd0588 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1478,7 +1478,7 @@ export class CommandController extends Controller { return new HttpSuccess(); } - async cronjobUpdateRetirementStatus() { + async cronjobUpdateRetirementStatus(/*@Request() request: RequestWithUser*/) { let body = { client_id: "gettoken", client_secret: process.env.AUTH_ACCOUNT_SECRET, @@ -1576,7 +1576,7 @@ export class CommandController extends Controller { } // let profiles: ProfileEmployee[] = []; await Promise.all( - response_.data.result.map(async (x: any) => { + response_.data.result.profiles.map(async (x: any) => { const _profileEmp = await this.profileEmployeeRepository.findOneBy({ id: x.profileId }); if (_profileEmp) { // บันทึกลงประวัติตำแหน่ง @@ -1625,7 +1625,6 @@ export class CommandController extends Controller { amount: null, positionSalaryAmount: null, mouthSalaryAmount: null, - profileId: profileId, posNo: null, positionExecutive: null, positionType: null, @@ -1655,6 +1654,14 @@ export class CommandController extends Controller { const history = new ProfileSalaryHistory(); Object.assign(history, { ...data, id: undefined }); data.dateGovernment = _Date; + if (type == "OFFICER") { + data.profileId = profileId; + data.profileEmployeeId = null; + } + else if (type == "EMPLOYEE"){ + data.profileEmployeeId = profileId; + data.profileId = null; + } const savedData = await this.salaryRepo.save(data); history.profileSalaryId = savedData.id; await this.salaryHistoryRepo.save(history); diff --git a/src/controllers/ProfileGovernmentController.ts b/src/controllers/ProfileGovernmentController.ts index 8abe6617..f2acbaba 100644 --- a/src/controllers/ProfileGovernmentController.ts +++ b/src/controllers/ProfileGovernmentController.ts @@ -249,9 +249,13 @@ export class ProfileGovernmentHistoryController extends Controller { if (record.leaveType == "RETIRE") { _profileSalary = record?.profileSalary.length > 1 ? record?.profileSalary[1] - : null; + : record?.profileSalary.length > 0 + ? record?.profileSalary[0] + : null; } else { - _profileSalary = record?.profileSalary[0]; + _profileSalary = record?.profileSalary.length > 0 + ? record?.profileSalary[0] + : null; } if (_profileSalary) { _OrgLeave = [ @@ -404,9 +408,13 @@ export class ProfileGovernmentHistoryController extends Controller { if (record.leaveType == "RETIRE") { _profileSalary = record?.profileSalary.length > 1 ? record?.profileSalary[1] - : null; + : record?.profileSalary.length > 0 + ? record?.profileSalary[0] + : null; } else { - _profileSalary = record?.profileSalary[0]; + _profileSalary = record?.profileSalary.length > 0 + ? record?.profileSalary[0] + : null; } if (_profileSalary) { _OrgLeave = [ diff --git a/src/controllers/ProfileGovernmentEmployeeController.ts b/src/controllers/ProfileGovernmentEmployeeController.ts index 9191eb2e..ce170d17 100644 --- a/src/controllers/ProfileGovernmentEmployeeController.ts +++ b/src/controllers/ProfileGovernmentEmployeeController.ts @@ -264,11 +264,15 @@ export class ProfileGovernmentEmployeeController extends Controller { // profileSalary.length > 0 && profileSalary[0].orgRoot ? profileSalary[0].orgRoot : null, // ]; if (record.leaveType == "RETIRE") { - _profileSalary = record?.profileSalary.length > 1 - ? record?.profileSalary[1] - : null; + _profileSalary = profileSalary.length > 1 + ? profileSalary[1] + : profileSalary.length > 0 + ? profileSalary[0] + : null; } else { - _profileSalary = record?.profileSalary[0]; + _profileSalary = profileSalary.length > 0 + ? profileSalary[0] + : null } if (_profileSalary) { _OrgLeave = [ @@ -435,11 +439,15 @@ export class ProfileGovernmentEmployeeController extends Controller { // profileSalary.length > 0 && profileSalary[0].orgRoot ? profileSalary[0].orgRoot : null, // ]; if (record.leaveType == "RETIRE") { - _profileSalary = record?.profileSalary.length > 1 - ? record?.profileSalary[1] - : null; + _profileSalary = profileSalary.length > 1 + ? profileSalary[1] + : profileSalary.length > 0 + ? profileSalary[0] + : null; } else { - _profileSalary = record?.profileSalary[0]; + _profileSalary = profileSalary.length > 0 + ? profileSalary[0] + : null; } if (_profileSalary) { _OrgLeave = [ From 364e69729319a51676551b515c691b69acd66b25 Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 2 Oct 2025 11:42:01 +0700 Subject: [PATCH 11/50] revert --- src/app.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app.ts b/src/app.ts index fb2c4d5b..236fb105 100644 --- a/src/app.ts +++ b/src/app.ts @@ -62,8 +62,7 @@ async function main() { } }); - // const cronTime_Oct = "0 0 1 10 *"; - const cronTime_Oct = "0 0 2 10 *"; // test 2 ตุลาคม + const cronTime_Oct = "0 0 1 10 *"; cron.schedule(cronTime_Oct, async () => { try { const commandController = new CommandController(); From 4be0b068bcc0030b7e2e12a4448b05af8ffaa853 Mon Sep 17 00:00:00 2001 From: Adisak Date: Thu, 2 Oct 2025 14:37:25 +0700 Subject: [PATCH 12/50] #170 and calAge --- src/controllers/OrganizationDotnetController.ts | 4 ++-- src/controllers/ProfileEmployeeController.ts | 6 +++--- src/controllers/ProfileEmployeeTempController.ts | 4 ++-- src/controllers/ProfileSalaryTempController.ts | 2 +- src/interfaces/extension.ts | 6 ++++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 415759af..38e17b06 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -5521,7 +5521,7 @@ export class OrganizationDotnetController extends Controller { govAge: profile.dateAppoint ? `${Extension.CalculateGovAge(profile.dateAppoint, 0, 0)} ปี` : "-", - age: profile.birthDate ? Extension.CalculateAgeStrV2(profile.birthDate, 0, 0) : "-", + age: profile.birthDate ? Extension.CalculateAgeStrV2(profile.birthDate, 0, 0, "GET") : "-", dateAppoint: profile.dateAppoint, dateCurrent: new Date(), amount: profile.amount ?? "-", @@ -5636,7 +5636,7 @@ export class OrganizationDotnetController extends Controller { govAge: profile.dateAppoint ? `${Extension.CalculateGovAge(profile.dateAppoint, 0, 0)} ปี` : "-", - age: profile.birthDate ? Extension.CalculateAgeStrV2(profile.birthDate, 0, 0) : "-", + age: profile.birthDate ? Extension.CalculateAgeStrV2(profile.birthDate, 0, 0, "GET") : "-", dateAppoint: profile.dateAppoint, dateCurrent: new Date(), amount: profile.amount ?? "-", diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 076881de..c1f1db7a 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -2127,7 +2127,7 @@ export class ProfileEmployeeController extends Controller { posNo: _data.employeeClass == "TEMP" ? _data.posMasterNoTemp : shortName, employeeClass: _data.employeeClass == null ? null : _data.employeeClass, govAge: Extension.CalculateGovAge(_data.dateAppoint, 0, 0), - age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0), + age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0, "GET"), dateEmployment: dateEmployment, dateAppoint: _data.dateAppoint, dateStart: _data.dateStart, @@ -2995,7 +2995,7 @@ export class ProfileEmployeeController extends Controller { : _data.profileSalary[0].posNo || "", employeeClass: _data.employeeClass == null ? null : _data.employeeClass, govAge: Extension.CalculateGovAge(_data.dateAppoint, 0, 0), - age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0), + age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0, "GET"), dateEmployment: dateEmployment, dateAppoint: _data.dateAppoint, dateStart: _data.dateStart, @@ -3417,7 +3417,7 @@ export class ProfileEmployeeController extends Controller { posNo: _data.employeeClass == "TEMP" ? _data.posMasterNoTemp : shortName, employeeClass: _data.employeeClass == null ? null : _data.employeeClass, govAge: Extension.CalculateGovAge(_data.dateAppoint, 0, 0), - age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0), + age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0, "GET"), dateEmployment: dateEmployment, dateAppoint: _data.dateAppoint, dateStart: _data.dateStart, diff --git a/src/controllers/ProfileEmployeeTempController.ts b/src/controllers/ProfileEmployeeTempController.ts index e5d03e4a..12c65e10 100644 --- a/src/controllers/ProfileEmployeeTempController.ts +++ b/src/controllers/ProfileEmployeeTempController.ts @@ -1189,7 +1189,7 @@ export class ProfileEmployeeTempController extends Controller { posNo: _data.employeeClass == "TEMP" ? _data.posMasterNoTemp : shortName, employeeClass: _data.employeeClass == null ? null : _data.employeeClass, govAge: Extension.CalculateGovAge(_data.dateAppoint, 0, 0), - age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0), + age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0, "GET"), dateEmployment: dateEmployment, dateAppoint: _data.dateAppoint, dateStart: _data.dateStart, @@ -1724,7 +1724,7 @@ export class ProfileEmployeeTempController extends Controller { posNo: _data.employeeClass == "TEMP" ? _data.posMasterNoTemp : shortName, employeeClass: _data.employeeClass == null ? null : _data.employeeClass, govAge: Extension.CalculateGovAge(_data.dateAppoint, 0, 0), - age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0), + age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0, "GET"), dateEmployment: dateEmployment, dateAppoint: _data.dateAppoint, dateStart: _data.dateStart, diff --git a/src/controllers/ProfileSalaryTempController.ts b/src/controllers/ProfileSalaryTempController.ts index b160f98d..5da8860e 100644 --- a/src/controllers/ProfileSalaryTempController.ts +++ b/src/controllers/ProfileSalaryTempController.ts @@ -878,7 +878,7 @@ export class ProfileSalaryTempController extends Controller { posNo: _data.employeeClass == "TEMP" ? _data.posMasterNoTemp : shortName, employeeClass: _data.employeeClass == null ? null : _data.employeeClass, govAge: Extension.CalculateGovAge(_data.dateAppoint, 0, 0), - age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0), + age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0, "GET"), dateEmployment: dateEmployment, dateAppoint: _data.dateAppoint, dateStart: _data.dateStart, diff --git a/src/interfaces/extension.ts b/src/interfaces/extension.ts index 26a12465..c81e66c0 100644 --- a/src/interfaces/extension.ts +++ b/src/interfaces/extension.ts @@ -309,11 +309,13 @@ class Extension { return years; } - public static CalculateAgeStrV2(date: Date, plusYear: number = 0, subtractYear: number = 0) { + public static CalculateAgeStrV2(date: Date, plusYear: number = 0, subtractYear: number = 0, method?:string) { if (date == null || date == undefined) return ""; const currentDate = new Date(); - if (date > currentDate) { + if (date > currentDate && method !== "GET") { throw new Error("วันเกิดต้องไม่มากกว่าวันที่ปัจจุบัน"); + }else if(date > currentDate && method === "GET"){ + return "" } let years = currentDate.getFullYear() - date.getFullYear(); From b2fafc68e84e75379163fedcc8405c4432e48a80 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Thu, 2 Oct 2025 14:44:12 +0700 Subject: [PATCH 13/50] add retirement --- src/controllers/ExRetirementController.ts | 126 ++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/controllers/ExRetirementController.ts diff --git a/src/controllers/ExRetirementController.ts b/src/controllers/ExRetirementController.ts new file mode 100644 index 00000000..232f9a8c --- /dev/null +++ b/src/controllers/ExRetirementController.ts @@ -0,0 +1,126 @@ +import axios from "axios"; +import { + Controller, + Post, + Delete, + Route, + Security, + Tags, + Body, + Path, + Request, + Response, + Get, +} from "tsoa"; +import HttpError from "../interfaces/http-error"; +import HttpStatusCode from "../interfaces/http-status"; + +interface CachedToken { + token: string; + expiry: Date; +} +const API_URL_BANGKOK = "https://exprofile.bangkok.go.th/API"; +const clientId = "e5f6ad6ce374177eef023bf5d0c018b6"; +const clientSecret = "5EhOvN5DwHOKakupqT9FmCk7MOwpT3zLqLPkPh4ZhJpxBN2nMG@2022"; + +class TokenCache { + private static cache: Map = new Map(); + + static get(key: string): string | null { + const cached = this.cache.get(key); + if (!cached) return null; + return cached.token; + } + + static set(key: string, token: string): void { + this.cache.set(key, { token, expiry: new Date(Date.now() + 50 * 60 * 1000) }); + } + + static delete(key: string): void { + this.cache.delete(key); + } +} + +@Route("api/v1/org/ex/retirement") +@Tags("ExRetirement") +@Security("bearerAuth") +export class ExRetirementController extends Controller { + @Post() + async getExRetirement( + @Body() + requestBody: { + type: string; //ประเภท + retireYear: string; //ปีที่เกษียณ + citizenID: string; //เลขบัตรประชาชน + firstNameTH: string; //ชื่อ + lastNameTH: string; //นามสกุล + page: number; //หน้า + }, + ) { + let retryCount = 0; + const maxRetries = 2; + + while (retryCount < maxRetries) { + try { + const token = await getToken(clientId, clientSecret); + + if (!token) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่สามารถขอ Token ได้"); + } + + const scope = requestBody.type === "officer" ? "getOfficerRetireData" : ""; + const startRecord = requestBody.page !== 1 ? (requestBody.page - 1) * 25 : 0; + + const formData = new FormData(); + formData.append("scope", scope); + formData.append("startRecord", startRecord.toString()); + formData.append("retireYear", requestBody.retireYear); + formData.append("citizenID", requestBody.citizenID); + formData.append("firstNameTH", requestBody.firstNameTH); + formData.append("lastNameTH", requestBody.lastNameTH); + + const res = await axios.post(API_URL_BANGKOK + "/getData", formData, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return res.data; + } catch (error: any) { + if (error.response?.status === 500 && retryCount < maxRetries - 1) { + TokenCache.delete(`${clientId}:${clientSecret}`); + retryCount++; + continue; + } + throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถติดต่อ API ได้"); + } + } + } +} + +async function getToken(ClientID: string, ClientSecret: string): Promise { + const cacheKey = `${ClientID}:${ClientSecret}`; + + // ลองหา token ใน cache ก่อน + const cachedToken = TokenCache.get(cacheKey); + if (cachedToken) { + return cachedToken; + } + + // ถ้าไม่มีใน cache ให้ขอใหม่ + try { + const formData = new FormData(); + formData.append("ClientID", ClientID); + formData.append("ClientSecret", ClientSecret); + const res = await axios.post(API_URL_BANGKOK + "/authorize", formData, { + headers: { + "Content-Type": "application/json", + }, + }); + const token = res.data.token; + TokenCache.set(cacheKey, token); + return token; + } catch (error) { + return Promise.reject({ message: "Error occurred", error }); + } +} From 1a1da7b5846cb42e13dcb2a63af8761604ea83c2 Mon Sep 17 00:00:00 2001 From: "Warunee.T" Date: Thu, 2 Oct 2025 14:49:59 +0700 Subject: [PATCH 14/50] Delete .forgejo/workflows/build.yml --- .forgejo/workflows/build.yml | 49 ------------------------------------ 1 file changed, 49 deletions(-) delete mode 100644 .forgejo/workflows/build.yml diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml deleted file mode 100644 index ba3556e0..00000000 --- a/.forgejo/workflows/build.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Build - -on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+" - - "v[0-9]+.[0-9]+.[0-9]+*" - workflow_dispatch: - -env: - REGISTRY: ${{ vars.CONTAINER_REGISTRY }} - REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}/${{ vars.CONTAINER_IMAGE_NAME }} - IMAGE_VERSION: build - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - with: - config-inline: | - [registry."${{ env.REGISTRY }}"] - ca=["/etc/ssl/certs/ca-certificates.crt"] - - name: Tag Version - run: | - if [[ "${{ github.event_name }}" == "push" ]]; then - echo "IMAGE_VERSION=${{ github.ref_name }}" | sed 's/v//g' >> $GITHUB_ENV - else - echo "IMAGE_VERSION=${{ env.IMAGE_VERSION }}-${{ github.run_number }}" >> $GITHUB_ENV - fi - - name: Login in to registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USERNAME }} - password: ${{ env.REGISTRY_PASSWORD }} - - name: Build and push docker image - uses: docker/build-push-action@v3 - with: - platforms: linux/amd64 - context: . - file: ./docker/Dockerfile - tags: ${{ env.CONTAINER_IMAGE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}:${{ env.IMAGE_VERSION }} - push: true From 58c9833be932cec3d971bd7fb9e44fe451d0b7b6 Mon Sep 17 00:00:00 2001 From: "Warunee.T" Date: Thu, 2 Oct 2025 14:50:25 +0700 Subject: [PATCH 15/50] Delete .forgejo/workflows/ci-cd.yml --- .forgejo/workflows/ci-cd.yml | 79 ------------------------------------ 1 file changed, 79 deletions(-) delete mode 100644 .forgejo/workflows/ci-cd.yml diff --git a/.forgejo/workflows/ci-cd.yml b/.forgejo/workflows/ci-cd.yml deleted file mode 100644 index 0f913ddc..00000000 --- a/.forgejo/workflows/ci-cd.yml +++ /dev/null @@ -1,79 +0,0 @@ -# /.forgejo/workflows/ci-cd.yml -name: Build & Deploy on Dev - -on: - push: - branches: - - dev - workflow_dispatch: - -env: - REGISTRY: ${{ vars.CONTAINER_REGISTRY }} - REGISTRY_USERNAME: ${{ vars.CONTAINER_REGISTRY_USERNAME }} - REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_IMAGE_NAME: ${{ vars.CONTAINER_REGISTRY }}/${{ vars.CONTAINER_IMAGE_OWNER }}/${{ vars.CONTAINER_IMAGE_NAME }} - IMAGE_VERSION: latest - DISCORD_WEBHOOK: ${{ vars.DISCORD_WEBHOOK }} - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - with: - config-inline: | - [registry."${{ env.REGISTRY }}"] - ca=["/etc/ssl/certs/ca-certificates.crt"] - - name: Tag Version - run: | - echo "IMAGE_VERSION=latest" - - name: Login in to registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ env.REGISTRY_USERNAME }} - password: ${{ env.REGISTRY_PASSWORD }} - - name: Build and push docker image - uses: docker/build-push-action@v3 - with: - platforms: linux/amd64 - context: . - file: ./docker/Dockerfile - tags: ${{ env.CONTAINER_IMAGE_NAME }}:latest,${{ env.CONTAINER_IMAGE_NAME }}:${{ env.IMAGE_VERSION }} - push: true - - name: Remote Deploy - uses: appleboy/ssh-action@v1.2.1 - with: - host: ${{ vars.SSH_DEPLOY_HOST }} - port: ${{ vars.SSH_DEPLOY_PORT }} - username: ${{ secrets.SSH_DEPLOY_USER }} - password: ${{ secrets.SSH_DEPLOY_PASSWORD }} - script: | - cd ~/repo - ./replace-env.sh API_ORG "${{ env.IMAGE_VERSION }}" - ./deploy.sh hrms-api-org - - - name: Discord Notification - if: always() - run: | - STATUS="${{ job.status == 'success' && '✅ Success' || '❌ Failed' }}" - COLOR="${{ job.status == 'success' && '3066993' || '15158332' }}" - TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ) - curl -H "Content-Type: application/json" \ - -X POST \ - -d "{ - \"embeds\": [{ - \"title\": \"$STATUS\", - \"description\": \"**Build & Deploy**\\n- Image: \`${{ env.CONTAINER_IMAGE_NAME }}\`\\n- Version: \`${{ env.IMAGE_VERSION }}\`\\n- By: \`${{ github.actor }}\`\", - \"color\": $COLOR, - \"footer\": { - \"text\": \"Release Notification\", - \"icon_url\": \"https://example.com/success-icon.png\" - }, - \"timestamp\": \"$TIMESTAMP\" - }] - }" \ - ${{ env.DISCORD_WEBHOOK }} From b7a609dc798fa911cd5cc54eaa078b0d1cc77d1a Mon Sep 17 00:00:00 2001 From: "Warunee.T" Date: Thu, 2 Oct 2025 14:50:56 +0700 Subject: [PATCH 16/50] Delete .forgejo/workflows/deploy.yml --- .forgejo/workflows/deploy.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .forgejo/workflows/deploy.yml diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml deleted file mode 100644 index 0dc28bce..00000000 --- a/.forgejo/workflows/deploy.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Build - -on: - workflow_dispatch: - inputs: - version: - description: "Version to deploy" - type: string - required: false - default: "latest" - -env: - IMAGE_VERSION: build - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Remote Deploy - uses: appleboy/ssh-action@v1.2.1 - with: - host: ${{ vars.SSH_DEPLOY_HOST }} - port: ${{ vars.SSH_DEPLOY_PORT }} - username: ${{ secrets.SSH_DEPLOY_USER }} - password: ${{ secrets.SSH_DEPLOY_PASSWORD }} - script: | - cd ~/repo - ./replace-env.sh API_ORG "${{ inputs.version }}" - ./deploy.sh hrms-api-org From 342c7dde679979c95c2389bdeb3cce22af045a1c Mon Sep 17 00:00:00 2001 From: Adisak Date: Thu, 2 Oct 2025 15:18:48 +0700 Subject: [PATCH 17/50] =?UTF-8?q?comment=20=E0=B9=80=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=B5=E0=B8=A2=E0=B8=A3=E0=B9=8C=E0=B8=95=E0=B8=B3=E0=B9=81?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=88=E0=B8=87=E0=B8=A5=E0=B8=B9=E0=B8=81?= =?UTF-8?q?=E0=B8=88=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B8=AB=E0=B8=A5=E0=B8=B1?= =?UTF-8?q?=E0=B8=87=E0=B8=A5=E0=B8=9A=E0=B8=AD=E0=B8=AD=E0=B8=81=E0=B8=88?= =?UTF-8?q?=E0=B8=B2=E0=B8=81=E0=B8=84=E0=B8=A3=E0=B8=AD=E0=B8=87=E0=B8=95?= =?UTF-8?q?=E0=B8=B3=E0=B9=81=E0=B8=AB=E0=B8=99=E0=B9=88=E0=B8=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/EmployeePositionController.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/controllers/EmployeePositionController.ts b/src/controllers/EmployeePositionController.ts index b0091494..86bf8bde 100644 --- a/src/controllers/EmployeePositionController.ts +++ b/src/controllers/EmployeePositionController.ts @@ -2293,22 +2293,22 @@ export class EmployeePositionController extends Controller { if (!dataMaster) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); } - if (dataMaster.current_holderId != null) { - const profile = await this.profileRepository.findOne({ - where: { - id: dataMaster.current_holderId, - }, - }); - const _null: any = null; - if (profile != null) { - profile.posLevelId = _null; - profile.posTypeId = _null; - profile.position = _null; - profile.employeeOc = _null; - profile.positionEmployeePositionId = _null; - await this.profileRepository.save(profile); - } - } + // if (dataMaster.current_holderId != null) { + // const profile = await this.profileRepository.findOne({ + // where: { + // id: dataMaster.current_holderId, + // }, + // }); + // const _null: any = null; + // if (profile != null) { + // profile.posLevelId = _null; + // profile.posTypeId = _null; + // profile.position = _null; + // profile.employeeOc = _null; + // profile.positionEmployeePositionId = _null; + // await this.profileRepository.save(profile); + // } + // } await this.employeePosMasterRepository.update(id, { isSit: false, From 7a25e8fedaf77abefba6d8dd84f0d5ea8a4bbbb6 Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 2 Oct 2025 17:16:36 +0700 Subject: [PATCH 18/50] Fix Error #1815 --- .../OrganizationDotnetController.ts | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 38e17b06..b3cc030f 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -4506,12 +4506,13 @@ export class OrganizationDotnetController extends Controller { role: string; isRetirement?: boolean; revisionId?: string; + reqNode?: number; + reqNodeId?: string; }, ) { let typeCondition: any = {}; - const node = body.role === "OWNER" ? null : body.node; - if (body.role === "OWNER" || body.role === "CHILD") { - switch (node) { + if (body.role === "CHILD") { + switch (body.node) { case 0: typeCondition = { orgRoot: { @@ -4547,21 +4548,53 @@ export class OrganizationDotnetController extends Controller { } }; break; - case null: + default: typeCondition = {}; break; + } + } else if (body.role === "ROOT" || body.role === "OWNER") { + switch (body.reqNode) { + case 0: + typeCondition = { + orgRoot: { + id: body.reqNodeId + } + }; + break; + case 1: + typeCondition = { + orgChild1: { + id: body.reqNodeId + } + }; + break; + case 2: + typeCondition = { + orgChild2: { + id: body.reqNodeId + } + }; + break; + case 3: + typeCondition = { + orgChild3: { + id: body.reqNodeId + } + }; + break; + case 4: + typeCondition = { + orgChild4: { + id: body.reqNodeId + } + }; + break; default: typeCondition = {}; break; } - } else if (body.role === "ROOT") { - typeCondition = { - orgRoot: { - ancestorDNA: body.nodeId - }, - }; } else if (body.role === "NORMAL") { - switch (node) { + switch (body.node) { case 0: typeCondition = { orgRoot: { From b5823fdf2831d55be8a4ae564502012ce8f9f712 Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 2 Oct 2025 18:17:19 +0700 Subject: [PATCH 19/50] emp --- .../OrganizationDotnetController.ts | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index b3cc030f..a5e04d3d 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -5101,12 +5101,13 @@ export class OrganizationDotnetController extends Controller { role: string; isRetirement?: boolean; revisionId?: string; + reqNode?: number; + reqNodeId?: string; }, ) { let typeCondition: any = {}; - const node = body.role === "OWNER" ? null : body.node; - if (body.role === "OWNER" || body.role === "CHILD") { - switch (node) { + if (body.role === "CHILD") { + switch (body.node) { case 0: typeCondition = { orgRoot: { @@ -5149,14 +5150,49 @@ export class OrganizationDotnetController extends Controller { typeCondition = {}; break; } - } else if (body.role === "ROOT") { - typeCondition = { - orgRoot: { - ancestorDNA: body.nodeId - }, - }; + } else if (body.role === "ROOT" || body.role === "OWNER") { + switch (body.reqNode) { + case 0: + typeCondition = { + orgRoot: { + id: body.reqNodeId + } + }; + break; + case 1: + typeCondition = { + orgChild1: { + id: body.reqNodeId + } + }; + break; + case 2: + typeCondition = { + orgChild2: { + id: body.reqNodeId + } + }; + break; + case 3: + typeCondition = { + orgChild3: { + id: body.reqNodeId + } + }; + break; + case 4: + typeCondition = { + orgChild4: { + id: body.reqNodeId + } + }; + break; + default: + typeCondition = {}; + break; + } } else if (body.role === "NORMAL") { - switch (node) { + switch (body.node) { case 0: typeCondition = { orgRoot: { From 4994c829b5236e4540c594fa18c1966c39b8e6d9 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Thu, 2 Oct 2025 18:42:14 +0700 Subject: [PATCH 20/50] fix query registry retire & sort registry of employee --- src/controllers/ProfileEmployeeController.ts | 527 +++---------------- src/services/ProfileLeaveService.ts | 433 +++++++++++++++ 2 files changed, 498 insertions(+), 462 deletions(-) create mode 100644 src/services/ProfileLeaveService.ts diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index b217b3b3..89b6644d 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -13,6 +13,7 @@ import { Response, Get, Query, + Example, } from "tsoa"; import { AppDataSource } from "../database/data-source"; import HttpSuccess from "../interfaces/http-success"; @@ -79,6 +80,7 @@ import { ProfileChangeName } from "../entities/ProfileChangeName"; import { ProfileChildren } from "../entities/ProfileChildren"; import { ProfileDuty } from "../entities/ProfileDuty"; import { getTopDegrees } from "../services/PositionService"; +import { ProfileLeaveService } from "../services/ProfileLeaveService"; @Route("api/v1/org/profile-employee") @Tags("ProfileEmployee") @Security("bearerAuth") @@ -131,6 +133,9 @@ export class ProfileEmployeeController extends Controller { private profileAbilityRepo = AppDataSource.getRepository(ProfileAbility); private profileAssistanceRepository = AppDataSource.getRepository(ProfileAssistance); + // Services + private profileLeaveService = new ProfileLeaveService(); + /** * report ประวัติแบบย่อ ลูกจ้าง * @@ -2629,6 +2634,29 @@ export class ProfileEmployeeController extends Controller { * */ @Get("profileLeave") + @Example({ + status: 200, + message: "สำเร็จ", + result: { + data: [ + { + page: 1, + pageSize: 12, + posLevel: "บ 1", + posType: "บริการพื้นฐาน", + isProbation: false, + isRetire: true, + node: 0, + nodeId: "8349b6b6-d005-4eb7-9960-ceb5b96e1962", + isAll: true, + sortBy: "profile.dateAppoint", + sort: "DESC", + retireType: "RETIRE_DECEASED", + }, + ], + total: 1, + }, + }) async listProfileLeave( @Request() request: RequestWithUser, @Query("page") page: number = 1, @@ -2638,443 +2666,31 @@ export class ProfileEmployeeController extends Controller { @Query() searchKeyword: string = "", @Query() posType?: string, @Query() posLevel?: string, - @Query() yearLeave?: number, @Query() isProbation?: boolean, - // @Query() isRetire?: boolean, - @Query() type?: string, @Query() node?: number, @Query() nodeId?: string, @Query() isAll?: boolean, @Query() retireType?: string, - @Query() sortBy: string = "current_holders.posMasterNo", - @Query() sort: "ASC" | "DESC" = "ASC", + @Query() sortBy: string = "profileEmployee.dateLeave", + @Query() sort: "ASC" | "DESC" = "DESC", ) { - let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_EMP"); - let queryLike = - "CONCAT(profileEmployee.prefix, profileEmployee.firstName, ' ', profileEmployee.lastName) LIKE :keyword"; - if (searchField == "citizenId") { - queryLike = "profileEmployee.citizenId LIKE :keyword"; - } else if (searchField == "position") { - queryLike = "profileEmployee.position LIKE :keyword"; - } - // else if (searchField == "posNo") { - // queryLike = ` - // CASE - // WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, current_holders.posMasterNo) - // WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, current_holders.posMasterNo) - // WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, current_holders.posMasterNo) - // WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, current_holders.posMasterNo) - // ELSE CONCAT(orgRoot.orgRootShortName, current_holders.posMasterNo) - // END LIKE :keyword - // `; - // } - else if (searchField == "posNo") { - queryLike = ` - CONCAT(profileSalary.posNoAbb, profileSalary.posNo) LIKE :keyword - OR CONCAT(profileSalary.posNoAbb, " ", profileSalary.posNo) LIKE :keyword - OR profileSalary.posNo LIKE :keyword - `; - } - let nodeCondition = "1=1"; - let nodeAll = ""; - let orgRoot = null; - let orgChild1 = null; - let orgChild2 = null; - let orgChild3 = null; - let orgChild4 = null; + const { data, total } = await this.profileLeaveService.getLeaveEmployees(request, { + page, + pageSize, + searchField, + searchKeyword, + posType, + posLevel, + isProbation, + node, + nodeId, + isAll, + retireType, + sortBy, + sort, + }); - let pmsCondition = "1=1"; - let orgRootPms = null; - let orgChild1Pms = null; - let orgChild2Pms = null; - let orgChild3Pms = null; - let orgChild4Pms = null; - - if (node === 0 && nodeId) { - orgRoot = await this.orgRootRepository.findOne({ where: { id: nodeId } }); - if (orgRoot) { - nodeCondition = "profileSalary.orgRoot = :orgRoot"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild1 IS NULL"; - } else if (node === 1 && nodeId) { - orgChild1 = await this.child1Repository.findOne({ where: { id: nodeId } }); - if (orgChild1) { - nodeCondition = "profileSalary.orgChild1 = :orgChild1"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild2 IS NULL"; - } else if (node === 2 && nodeId) { - orgChild2 = await this.child2Repository.findOne({ where: { id: nodeId } }); - if (orgChild2) { - nodeCondition = "profileSalary.orgChild2 = :orgChild2"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild3 IS NULL"; - } else if (node === 3 && nodeId) { - orgChild3 = await this.child3Repository.findOne({ where: { id: nodeId } }); - if (orgChild3) { - nodeCondition = "profileSalary.orgChild3 = :orgChild3"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild4 IS NULL"; - } else if (node === 4 && nodeId) { - orgChild4 = await this.child4Repository.findOne({ where: { id: nodeId } }); - if (orgChild4) { - nodeCondition = "profileSalary.orgChild4 = :orgChild4"; - } - } - nodeCondition = nodeCondition + nodeAll; - - if (_data.root) { - orgRootPms = await this.orgRootRepository.findOne({ where: { id: _data.root } }); - if (orgRootPms) { - pmsCondition = "profileSalary.orgRoot = :orgRootPms"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild1 IS NULL"; - } else if (_data.child1) { - orgChild1Pms = await this.child1Repository.findOne({ where: { id: _data.child1 } }); - if (orgChild1Pms) { - pmsCondition = "profileSalary.orgChild1 = :orgChild1Pms"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild2 IS NULL"; - } else if (_data.child2) { - orgChild2Pms = await this.child2Repository.findOne({ where: { id: _data.child2 } }); - if (orgChild2Pms) { - pmsCondition = "profileSalary.orgChild2 = :orgChild2Pms"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild3 IS NULL"; - } else if (_data.child3) { - orgChild3Pms = await this.child3Repository.findOne({ where: { id: _data.child3 } }); - if (orgChild3Pms) { - pmsCondition = "profileSalary.orgChild3 = :orgChild3Pms"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild4 IS NULL"; - } else if (_data.child4) { - orgChild4Pms = await this.child4Repository.findOne({ where: { id: _data.child4 } }); - if (orgChild4Pms) { - pmsCondition = "profileSalary.orgChild4 = :orgChild4Pms"; - } - } - pmsCondition = pmsCondition + nodeAll; - // const findRevision = await this.orgRevisionRepo.findOne({ - // where: { orgRevisionIsCurrent: true }, - // }); - // if (!findRevision) { - // throw new HttpError(HttpStatus.NOT_FOUND, "not found. OrgRevision"); - // } - const [record, total] = await this.profileRepo - .createQueryBuilder("profileEmployee") - .leftJoinAndSelect("profileEmployee.posLevel", "posLevel") - .leftJoinAndSelect("profileEmployee.posType", "posType") - // .leftJoinAndSelect("profileEmployee.current_holders", "current_holders") - .leftJoinAndSelect("profileEmployee.profileEmployeeEmployment", "profileEmployeeEmployment") - // .leftJoinAndSelect("current_holders.positions", "positions") - .leftJoinAndSelect("profileEmployee.profileSalary", "profileSalary") - // .leftJoinAndSelect("current_holders.orgRevision", "orgRevision") - // .leftJoinAndSelect("current_holders.orgRoot", "orgRoot") - // .leftJoinAndSelect("current_holders.orgChild1", "orgChild1") - // .leftJoinAndSelect("current_holders.orgChild2", "orgChild2") - // .leftJoinAndSelect("current_holders.orgChild3", "orgChild3") - // .leftJoinAndSelect("current_holders.orgChild4", "orgChild4") - // .where(node && nodeId ? "current_holders.orgRevisionId = :orgRevisionId" : "1=1", { - // orgRevisionId: node && nodeId ? findRevision.id : undefined, - // }) - .where( - new Brackets((qb) => { - qb.where("profileEmployee.isLeave = :isLeave", { isLeave: true }).orWhere( - "profileEmployee.isRetirement = :isRetirement", - { isRetirement: true }, - ); - }), - ) - // .andWhere("profileEmployee.leaveCommandId Is NOT NULL") - .andWhere( - "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id and ps.positionName != 'เกษียณอายุราชการ')", - ) - - // .andWhere( - // _data.root != undefined && _data.root != null - // ? _data.root[0] != null - // ? `current_holders.orgRootId IN (:...root)` - // : `current_holders.orgRootId is null` - // : "1=1", - // { - // root: _data.root, - // }, - // ) - // .andWhere( - // _data.child1 != undefined && _data.child1 != null - // ? _data.child1[0] != null - // ? `current_holders.orgChild1Id IN (:...child1)` - // : `current_holders.orgChild1Id is null` - // : "1=1", - // { - // child1: _data.child1, - // }, - // ) - // .andWhere( - // _data.child2 != undefined && _data.child2 != null - // ? _data.child2[0] != null - // ? `current_holders.orgChild2Id IN (:...child2)` - // : `current_holders.orgChild2Id is null` - // : "1=1", - // { - // child2: _data.child2, - // }, - // ) - // .andWhere( - // _data.child3 != undefined && _data.child3 != null - // ? _data.child3[0] != null - // ? `current_holders.orgChild3Id IN (:...child3)` - // : `current_holders.orgChild3Id is null` - // : "1=1", - // { - // child3: _data.child3, - // }, - // ) - // .andWhere( - // _data.child4 != undefined && _data.child4 != null - // ? _data.child4[0] != null - // ? `current_holders.orgChild4Id IN (:...child4)` - // : `current_holders.orgChild4Id is null` - // : "1=1", - // { - // child4: _data.child4, - // }, - // ) - .andWhere( - posType != undefined && posType != null && posType != "" - ? "posType.posTypeName LIKE :keyword1" - : "1=1", - { - keyword1: `${posType}`, - }, - ) - .andWhere( - posLevel != undefined && posLevel != null && posLevel != "" - ? `CONCAT(posType.posTypeShortName,' ',posLevel.posLevelName) LIKE :keyword2` - : "1=1", - { - keyword2: `${posLevel}`, - }, - ) - .andWhere( - isProbation != undefined && isProbation != null - ? `profileEmployee.isProbation = ${isProbation}` - : "1=1", - ) - .andWhere( - retireType != undefined && retireType != null - ? `profileEmployee.leaveType = :retireType` - : "1=1", - { retireType: retireType }, - ) - .andWhere("profileEmployee.employeeClass LIKE :type", { - type: "PERM", - }) - .andWhere( - searchKeyword != undefined && searchKeyword != null && searchKeyword != "" - ? queryLike - : "1=1", - { - keyword: `%${searchKeyword}%`, - }, - ) - .andWhere(pmsCondition, { - orgRootPms: orgRootPms ? orgRootPms.orgRootName : "", - orgChild1Pms: orgChild1Pms ? orgChild1Pms.orgChild1Name : "", - orgChild2Pms: orgChild2Pms ? orgChild2Pms.orgChild2Name : "", - orgChild3Pms: orgChild3Pms ? orgChild3Pms.orgChild3Name : "", - orgChild4Pms: orgChild4Pms ? orgChild4Pms.orgChild4Name : "", - }) - - .andWhere(nodeCondition, { - orgRoot: orgRoot ? orgRoot.orgRootName : "", - orgChild1: orgChild1 ? orgChild1.orgChild1Name : "", - orgChild2: orgChild2 ? orgChild2.orgChild2Name : "", - orgChild3: orgChild3 ? orgChild3.orgChild3Name : "", - orgChild4: orgChild4 ? orgChild4.orgChild4Name : "", - }) - // .andWhere(`current_holders.orgRevisionId LIKE :orgRevisionId`, { - // orgRevisionId: findRevision.id, - // }) - // .orderBy("current_holders.posMasterNo", "ASC") - // .orderBy(`${sortBy}`, sort) - .skip((page - 1) * pageSize) - .take(pageSize) - .getManyAndCount(); - const data = await Promise.all( - record.map((_data) => { - // const shortName = - // _data.current_holders.length == 0 - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != - // null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - // ?.orgChild3 != null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - // ?.orgChild2 != null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - // ?.orgChild1 != null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - // null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - // ?.orgRoot != null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : null; - const dateEmployment = - _data.profileEmployeeEmployment.length == 0 - ? null - : _data.profileEmployeeEmployment.reduce((latest, current) => { - return latest.date > current.date ? latest : current; - }).date; - // const root = - // _data.current_holders.length == 0 || - // (_data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot; - - // const child1 = - // _data.current_holders == null || - // _data.current_holders.length == 0 || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1; - - // const child2 = - // _data.current_holders == null || - // _data.current_holders.length == 0 || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2; - - // const child3 = - // _data.current_holders == null || - // _data.current_holders.length == 0 || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3; - - // const child4 = - // _data.current_holders == null || - // _data.current_holders.length == 0 || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4; - - // let _child1 = child1?.orgChild1Name; - // let _child2 = child2?.orgChild2Name; - // let _child3 = child3?.orgChild3Name; - // let _child4 = child4?.orgChild4Name; - return { - id: _data.id, - avatar: _data.avatar, - avatarName: _data.avatarName, - prefix: _data.prefix, - rank: _data.rank, - firstName: _data.firstName, - lastName: _data.lastName, - citizenId: _data.citizenId, - posLevel: _data.posLevel == null ? null : _data.posLevel.posLevelName, - posType: _data.posType == null ? null : _data.posType.posTypeName, - posTypeShortName: _data.posType == null ? null : _data.posType.posTypeShortName, - posLevelId: _data.posLevel == null ? null : _data.posLevel.id, - posTypeId: _data.posType == null ? null : _data.posType.id, - positionId: _data.positionIdTemp, - posmasterId: _data.posmasterIdTemp, - position: _data.position, - posNo: - _data.profileSalary[0].posNoAbb && _data.profileSalary[0].posNo - ? `${_data.profileSalary[0].posNoAbb} ${_data.profileSalary[0].posNo}` - : _data.profileSalary[0].posNo || "", - employeeClass: _data.employeeClass == null ? null : _data.employeeClass, - govAge: Extension.CalculateGovAge(_data.dateAppoint, 0, 0), - age: Extension.CalculateAgeStrV2(_data.birthDate, 0, 0, "GET"), - dateEmployment: dateEmployment, - dateAppoint: _data.dateAppoint, - dateStart: _data.dateStart, - createdAt: _data.createdAt, - dateRetireLaw: _data.dateRetireLaw, - draftOrganizationOrganization: - _data.nodeTemp == "0" - ? _data.rootTemp - : _data.nodeTemp == "1" - ? _data.child1Temp - : _data.nodeTemp == "2" - ? _data.child2Temp - : _data.nodeTemp == "3" - ? _data.child3Temp - : _data.nodeTemp == "4" - ? _data.child4Temp - : null, - draftPositionEmployee: _data.positionTemp, - draftOrgEmployeeStatus: _data.statusTemp, - node: _data.nodeTemp, - nodeId: _data.nodeIdTemp, - nodeName: - _data.nodeTemp == "0" - ? _data.rootTemp - : _data.nodeTemp == "1" - ? _data.child1Temp - : _data.nodeTemp == "2" - ? _data.child2Temp - : _data.nodeTemp == "3" - ? _data.child3Temp - : _data.nodeTemp == "4" - ? _data.child4Temp - : null, - nodeShortName: - _data.nodeTemp == "0" - ? _data.rootShortNameTemp - : _data.nodeTemp == "1" - ? _data.child1ShortNameTemp - : _data.nodeTemp == "2" - ? _data.child1ShortNameTemp - : _data.nodeTemp == "3" - ? _data.child3ShortNameTemp - : _data.nodeTemp == "4" - ? _data.child4ShortNameTemp - : null, - root: _data.rootTemp ? _data.rootTemp : null, - rootId: _data.rootIdTemp ? _data.rootIdTemp : null, - rootShortName: _data.rootShortNameTemp ? _data.rootShortNameTemp : null, - child1: _data.child1Temp ? _data.child1Temp : null, - child1Id: _data.child1IdTemp ? _data.child1IdTemp : null, - child1ShortName: _data.child1ShortNameTemp ? _data.child1ShortNameTemp : null, - child2: _data.child2Temp ? _data.child2Temp : null, - child2Id: _data.child2IdTemp ? _data.child2IdTemp : null, - child2ShortName: _data.child2ShortNameTemp ? _data.child2ShortNameTemp : null, - child3: _data.child3Temp ? _data.child3Temp : null, - child3Id: _data.child3IdTemp ? _data.child3IdTemp : null, - child3ShortName: _data.child3ShortNameTemp ? _data.child3ShortNameTemp : null, - child4: _data.child4Temp ? _data.child4Temp : null, - child4Id: _data.child4IdTemp ? _data.child4IdTemp : null, - child4ShortName: _data.child4ShortNameTemp ? _data.child4ShortNameTemp : null, - org: - (_data.profileSalary[0].orgChild4 == null - ? "" - : _data.profileSalary[0].orgChild4 + "\n") + - (_data.profileSalary[0].orgChild3 == null - ? "" - : _data.profileSalary[0].orgChild3 + "\n") + - (_data.profileSalary[0].orgChild2 == null - ? "" - : _data.profileSalary[0].orgChild2 + "\n") + - (_data.profileSalary[0].orgChild1 == null - ? "" - : _data.profileSalary[0].orgChild1 + "\n") + - (_data.profileSalary[0].orgRoot == null ? "" : _data.profileSalary[0].orgRoot), - }; - }), - ); - - return new HttpSuccess({ data: data, total }); + return new HttpSuccess({ data, total }); } /** @@ -3137,15 +2753,13 @@ export class ProfileEmployeeController extends Controller { @Query() searchKeyword: string = "", @Query() posType?: string, @Query() posLevel?: string, - @Query() yearLeave?: number, @Query() isProbation?: boolean, @Query() isRetire?: boolean, - @Query() type?: string, @Query() node?: number, @Query() nodeId?: string, @Query() isAll?: boolean, @Query() retireType?: string, - @Query() sortBy: string = "current_holders.posMasterNo", + @Query() sortBy?: string, @Query() sort: "ASC" | "DESC" = "ASC", ) { let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_EMP"); @@ -3300,13 +2914,8 @@ export class ProfileEmployeeController extends Controller { .andWhere(nodeCondition, { nodeId: nodeId, }) - // .andWhere(`current_holders.orgRevisionId LIKE :orgRevisionId`, { - // orgRevisionId: findRevision.id, - // }) - // .orderBy("current_holders.posMasterNo", "ASC") - // .orderBy(`${sortBy}`, sort) .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") - .orderBy("sort_order", "ASC") + .orderBy(`${sortBy ? sortBy : "sort_order"}`, `${sort}`) .addOrderBy("orgRoot.orgRootOrder", sort) .addOrderBy("orgChild1.orgChild1Order", sort) .addOrderBy("orgChild2.orgChild2Order", sort) @@ -5123,34 +4732,28 @@ export class ProfileEmployeeController extends Controller { }, ); }), - ) + ); if (body.sortBy) { - if(body.sortBy === "posType"){ - query = query.orderBy( - `posType.posTypeName`, - body.descending ? "DESC" : "ASC" - ); - }else if(body.sortBy === "posLevel"){ + if (body.sortBy === "posType") { + query = query.orderBy(`posType.posTypeName`, body.descending ? "DESC" : "ASC"); + } else if (body.sortBy === "posLevel") { query = query - .orderBy(`posType.posTypeShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`posLevel.posLevelName`,body.descending ? "DESC" : "ASC"); - }else if(body.sortBy === "orgShortName" || body.sortBy === "posMasterNo"){ + .orderBy(`posType.posTypeShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`posLevel.posLevelName`, body.descending ? "DESC" : "ASC"); + } else if (body.sortBy === "orgShortName" || body.sortBy === "posMasterNo") { query = query - .orderBy(`orgRoot.orgRootShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`orgChild1.orgChild1ShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`orgChild2.orgChild2ShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`orgChild3.orgChild3ShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`orgChild4.orgChild4ShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`employeePosMaster.posMasterNo`,body.descending ? "DESC" : "ASC") - }else{ - query = query.orderBy( - `current_holder.${body.sortBy}`, - body.descending ? "DESC" : "ASC" - ); + .orderBy(`orgRoot.orgRootShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild1.orgChild1ShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild2.orgChild2ShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild3.orgChild3ShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild4.orgChild4ShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`employeePosMaster.posMasterNo`, body.descending ? "DESC" : "ASC"); + } else { + query = query.orderBy(`current_holder.${body.sortBy}`, body.descending ? "DESC" : "ASC"); } - }else{ - query = query.orderBy("current_holder.citizenId", "ASC") + } else { + query = query.orderBy("current_holder.citizenId", "ASC"); } const [findPosMaster, total] = await query diff --git a/src/services/ProfileLeaveService.ts b/src/services/ProfileLeaveService.ts new file mode 100644 index 00000000..f187675d --- /dev/null +++ b/src/services/ProfileLeaveService.ts @@ -0,0 +1,433 @@ +import { AppDataSource } from "../database/data-source"; +import { ProfileEmployee } from "../entities/ProfileEmployee"; +import { OrgRoot } from "../entities/OrgRoot"; +import { OrgChild1 } from "../entities/OrgChild1"; +import { OrgChild2 } from "../entities/OrgChild2"; +import { OrgChild3 } from "../entities/OrgChild3"; +import { OrgChild4 } from "../entities/OrgChild4"; +import { Brackets, Repository } from "typeorm"; +import Extension from "../interfaces/extension"; +import permission from "../interfaces/permission"; +import { RequestWithUser } from "../middlewares/user"; + +export interface LeaveEmployeeFilter { + page: number; + pageSize: number; + searchField?: "firstName" | "lastName" | "fullName" | "citizenId" | "position" | "posNo"; + searchKeyword?: string; + posType?: string; + posLevel?: string; + isProbation?: boolean; + node?: number; + nodeId?: string; + isAll?: boolean; + retireType?: string; + sortBy?: string; + sort: "ASC" | "DESC"; +} + +export interface OrganizationCondition { + condition: string; + params: Record; +} + +export class ProfileLeaveService { + private profileRepo: Repository; + private orgRootRepository: Repository; + private child1Repository: Repository; + private child2Repository: Repository; + private child3Repository: Repository; + private child4Repository: Repository; + + constructor() { + this.profileRepo = AppDataSource.getRepository(ProfileEmployee); + this.orgRootRepository = AppDataSource.getRepository(OrgRoot); + this.child1Repository = AppDataSource.getRepository(OrgChild1); + this.child2Repository = AppDataSource.getRepository(OrgChild2); + this.child3Repository = AppDataSource.getRepository(OrgChild3); + this.child4Repository = AppDataSource.getRepository(OrgChild4); + } + + /** + * สร้าง query สำหรับการค้นหาตามฟิลด์ต่างๆ + */ + buildSearchQuery(searchField?: string): string { + switch (searchField) { + case "citizenId": + return "profileEmployee.citizenId LIKE :keyword"; + case "position": + return "profileEmployee.position LIKE :keyword"; + case "posNo": + return ` + (profileSalary.posNoAbb IS NOT NULL AND CONCAT(profileSalary.posNoAbb, profileSalary.posNo) LIKE :keyword) + OR (profileSalary.posNoAbb IS NOT NULL AND CONCAT(profileSalary.posNoAbb, " ", profileSalary.posNo) LIKE :keyword) + OR (profileSalary.posNo IS NOT NULL AND profileSalary.posNo LIKE :keyword) + `; + default: + return "CONCAT(profileEmployee.prefix, profileEmployee.firstName, ' ', profileEmployee.lastName) LIKE :keyword"; + } + } + + /** + * สร้างเงื่อนไขการค้นหาตาม node และ nodeId + */ + async buildNodeCondition( + node?: number, + nodeId?: string, + isAll?: boolean, + ): Promise { + let condition = "1=1"; + let nodeAll = ""; + const params: Record = {}; + + if (!node || !nodeId) { + return { condition, params }; + } + + // สร้าง nodeAll condition - เพิ่มการตรวจสอบ IS NULL + if (isAll === false && node < 4) { + const nextLevels = ["orgChild1", "orgChild2", "orgChild3", "orgChild4"]; + nodeAll = ` AND (profileSalary.${nextLevels[node]} IS NULL OR profileSalary.id IS NULL)`; + } + + try { + switch (node) { + case 0: { + const orgRoot = await this.orgRootRepository.findOne({ where: { id: nodeId } }); + if (orgRoot) { + condition = "(profileSalary.orgRoot = :orgRoot OR profileSalary.id IS NULL)"; + params.orgRoot = orgRoot.orgRootName; + } + break; + } + case 1: { + const orgChild1 = await this.child1Repository.findOne({ where: { id: nodeId } }); + if (orgChild1) { + condition = "(profileSalary.orgChild1 = :orgChild1 OR profileSalary.id IS NULL)"; + params.orgChild1 = orgChild1.orgChild1Name; + } + break; + } + case 2: { + const orgChild2 = await this.child2Repository.findOne({ where: { id: nodeId } }); + if (orgChild2) { + condition = "(profileSalary.orgChild2 = :orgChild2 OR profileSalary.id IS NULL)"; + params.orgChild2 = orgChild2.orgChild2Name; + } + break; + } + case 3: { + const orgChild3 = await this.child3Repository.findOne({ where: { id: nodeId } }); + if (orgChild3) { + condition = "(profileSalary.orgChild3 = :orgChild3 OR profileSalary.id IS NULL)"; + params.orgChild3 = orgChild3.orgChild3Name; + } + break; + } + case 4: { + const orgChild4 = await this.child4Repository.findOne({ where: { id: nodeId } }); + if (orgChild4) { + condition = "(profileSalary.orgChild4 = :orgChild4 OR profileSalary.id IS NULL)"; + params.orgChild4 = orgChild4.orgChild4Name; + } + break; + } + } + } catch (error) { + console.error("Error building node condition:", error); + } + + return { condition: condition + nodeAll, params }; + } + + /** + * สร้างเงื่อนไขการค้นหาตาม permission + */ + async buildPermissionCondition( + request: RequestWithUser, + isAll?: boolean, + ): Promise { + const _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_EMP"); + let condition = "1=1"; + let nodeAll = ""; + const params: Record = {}; + + try { + if (_data.root) { + const orgRootPms = await this.orgRootRepository.findOne({ where: { id: _data.root } }); + if (orgRootPms) { + condition = "(profileSalary.orgRoot = :orgRootPms OR profileSalary.id IS NULL)"; + params.orgRootPms = orgRootPms.orgRootName; + } + if (isAll === false) + nodeAll = " AND (profileSalary.orgChild1 IS NULL OR profileSalary.id IS NULL)"; + } else if (_data.child1) { + const orgChild1Pms = await this.child1Repository.findOne({ where: { id: _data.child1 } }); + if (orgChild1Pms) { + condition = "(profileSalary.orgChild1 = :orgChild1Pms OR profileSalary.id IS NULL)"; + params.orgChild1Pms = orgChild1Pms.orgChild1Name; + } + if (isAll === false) + nodeAll = " AND (profileSalary.orgChild2 IS NULL OR profileSalary.id IS NULL)"; + } else if (_data.child2) { + const orgChild2Pms = await this.child2Repository.findOne({ where: { id: _data.child2 } }); + if (orgChild2Pms) { + condition = "(profileSalary.orgChild2 = :orgChild2Pms OR profileSalary.id IS NULL)"; + params.orgChild2Pms = orgChild2Pms.orgChild2Name; + } + if (isAll === false) + nodeAll = " AND (profileSalary.orgChild3 IS NULL OR profileSalary.id IS NULL)"; + } else if (_data.child3) { + const orgChild3Pms = await this.child3Repository.findOne({ where: { id: _data.child3 } }); + if (orgChild3Pms) { + condition = "(profileSalary.orgChild3 = :orgChild3Pms OR profileSalary.id IS NULL)"; + params.orgChild3Pms = orgChild3Pms.orgChild3Name; + } + if (isAll === false) + nodeAll = " AND (profileSalary.orgChild4 IS NULL OR profileSalary.id IS NULL)"; + } else if (_data.child4) { + const orgChild4Pms = await this.child4Repository.findOne({ where: { id: _data.child4 } }); + if (orgChild4Pms) { + condition = "(profileSalary.orgChild4 = :orgChild4Pms OR profileSalary.id IS NULL)"; + params.orgChild4Pms = orgChild4Pms.orgChild4Name; + } + } + } catch (error) { + console.error("Error building permission condition:", error); + } + + return { condition: condition + nodeAll, params }; + } + + /** + * แปลงข้อมูลพนักงานเป็น format ที่ต้องการ + */ + transformEmployeeData(employee: ProfileEmployee): any { + const dateEmployment = + employee.profileEmployeeEmployment?.length === 0 || !employee.profileEmployeeEmployment + ? null + : employee.profileEmployeeEmployment.reduce((latest, current) => { + return latest.date > current.date ? latest : current; + }).date; + + // ตรวจสอบว่า profileSalary มีข้อมูลหรือไม่ + const salary = + employee.profileSalary && employee.profileSalary.length > 0 + ? employee.profileSalary[0] + : null; + + const posNo = + salary?.posNoAbb && salary?.posNo + ? `${salary.posNoAbb} ${salary.posNo}` + : salary?.posNo || ""; + + // สร้าง organization hierarchy - ใช้ข้อมูลจาก temp fields ถ้า salary ไม่มี + const org = salary + ? [salary.orgChild4, salary.orgChild3, salary.orgChild2, salary.orgChild1, salary.orgRoot] + .filter(Boolean) + .join("\n") + : [ + employee.child4Temp, + employee.child3Temp, + employee.child2Temp, + employee.child1Temp, + employee.rootTemp, + ] + .filter(Boolean) + .join("\n"); + + // สร้าง node information + const getNodeInfo = (nodeTemp: string) => { + switch (nodeTemp) { + case "0": + return { + name: employee.rootTemp, + shortName: employee.rootShortNameTemp, + }; + case "1": + return { + name: employee.child1Temp, + shortName: employee.child1ShortNameTemp, + }; + case "2": + return { + name: employee.child2Temp, + shortName: employee.child2ShortNameTemp, + }; + case "3": + return { + name: employee.child3Temp, + shortName: employee.child3ShortNameTemp, + }; + case "4": + return { + name: employee.child4Temp, + shortName: employee.child4ShortNameTemp, + }; + default: + return { name: null, shortName: null }; + } + }; + + const nodeInfo = getNodeInfo(employee.nodeTemp || "0"); + + return { + id: employee.id, + avatar: employee.avatar, + avatarName: employee.avatarName, + prefix: employee.prefix, + rank: employee.rank, + firstName: employee.firstName, + lastName: employee.lastName, + citizenId: employee.citizenId, + posLevel: employee.posLevel?.posLevelName || null, + posType: employee.posType?.posTypeName || null, + posTypeShortName: employee.posType?.posTypeShortName || null, + posLevelId: employee.posLevel?.id || null, + posTypeId: employee.posType?.id || null, + positionId: employee.positionIdTemp, + posmasterId: employee.posmasterIdTemp, + position: employee.position, + posNo, + employeeClass: employee.employeeClass, + govAge: Extension.CalculateGovAge(employee.dateAppoint, 0, 0), + age: Extension.CalculateAgeStrV2(employee.birthDate, 0, 0, "GET"), + dateEmployment, + dateAppoint: employee.dateAppoint, + dateStart: employee.dateStart, + createdAt: employee.createdAt, + dateRetireLaw: employee.dateRetireLaw, + draftOrganizationOrganization: nodeInfo.name, + draftPositionEmployee: employee.positionTemp, + draftOrgEmployeeStatus: employee.statusTemp, + node: employee.nodeTemp, + nodeId: employee.nodeIdTemp, + nodeName: nodeInfo.name, + nodeShortName: nodeInfo.shortName, + root: employee.rootTemp || null, + rootId: employee.rootIdTemp || null, + rootShortName: employee.rootShortNameTemp || null, + child1: employee.child1Temp || null, + child1Id: employee.child1IdTemp || null, + child1ShortName: employee.child1ShortNameTemp || null, + child2: employee.child2Temp || null, + child2Id: employee.child2IdTemp || null, + child2ShortName: employee.child2ShortNameTemp || null, + child3: employee.child3Temp || null, + child3Id: employee.child3IdTemp || null, + child3ShortName: employee.child3ShortNameTemp || null, + child4: employee.child4Temp || null, + child4Id: employee.child4IdTemp || null, + child4ShortName: employee.child4ShortNameTemp || null, + org, + }; + } + + /** + * ค้นหาพนักงานที่พ้นจากราชการ + */ + async getLeaveEmployees( + request: RequestWithUser, + filter: LeaveEmployeeFilter, + ): Promise<{ data: any[]; total: number }> { + const { + page, + pageSize, + searchField, + searchKeyword = "", + posType, + posLevel, + isProbation, + node, + nodeId, + isAll, + retireType, + sortBy = "profileEmployee.dateLeave", + sort, + } = filter; + + // สร้าง query conditions แบบ parallel + const [nodeCondition, permissionCondition] = await Promise.all([ + this.buildNodeCondition(node, nodeId, isAll), + this.buildPermissionCondition(request, isAll), + ]); + + const searchQuery = this.buildSearchQuery(searchField); + + // สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary + const queryBuilder = this.profileRepo + .createQueryBuilder("profileEmployee") + .leftJoinAndSelect("profileEmployee.posLevel", "posLevel") + .leftJoinAndSelect("profileEmployee.posType", "posType") + .leftJoinAndSelect("profileEmployee.profileEmployeeEmployment", "profileEmployeeEmployment") + .leftJoin( + "profileEmployee.profileSalary", + "profileSalary", + "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id and ps.positionName != 'เกษียณอายุราชการ')", + ) + .addSelect([ + "profileSalary.id", + "profileSalary.order", + "profileSalary.posNo", + "profileSalary.posNoAbb", + "profileSalary.orgRoot", + "profileSalary.orgChild1", + "profileSalary.orgChild2", + "profileSalary.orgChild3", + "profileSalary.orgChild4", + ]) + .where( + new Brackets((qb) => { + qb.where("profileEmployee.isLeave = :isLeave", { isLeave: true }).orWhere( + "profileEmployee.isRetirement = :isRetirement", + { isRetirement: true }, + ); + }), + ) + .andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" }); + + // เพิ่มเงื่อนไขการค้นหา + if (posType) { + queryBuilder.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` }); + } + + if (posLevel) { + queryBuilder.andWhere( + "CONCAT(posType.posTypeShortName,' ',posLevel.posLevelName) LIKE :keyword2", + { keyword2: `${posLevel}` }, + ); + } + + if (isProbation !== undefined && isProbation !== null) { + queryBuilder.andWhere(`profileEmployee.isProbation = ${isProbation}`); + } + + if (retireType) { + queryBuilder.andWhere("profileEmployee.leaveType = :retireType", { retireType }); + } + + if (searchKeyword) { + queryBuilder.andWhere(searchQuery, { keyword: `%${searchKeyword}%` }); + } + + // เพิ่ม permission และ node conditions + queryBuilder + .andWhere(permissionCondition.condition, permissionCondition.params) + .andWhere(nodeCondition.condition, nodeCondition.params); + + // เพิ่ม sorting และ pagination + queryBuilder + .orderBy(sortBy, sort) + .skip((page - 1) * pageSize) + .take(pageSize); + + const [records, total] = await queryBuilder.getManyAndCount(); + + // แปลงข้อมูลแบบ parallel + const data = await Promise.all( + records.map((record) => Promise.resolve(this.transformEmployeeData(record))), + ); + + return { data, total }; + } +} From 0172aa9496f5e7941a971d88b7857d4491a372ad Mon Sep 17 00:00:00 2001 From: Adisak Date: Fri, 3 Oct 2025 10:28:51 +0700 Subject: [PATCH 21/50] sort --- src/controllers/ProfileController.ts | 59 ++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index e6f4c1e7..db52598d 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -6220,8 +6220,10 @@ export class ProfileController extends Controller { @Query() nodeId?: string, @Query() isAll?: boolean, @Query() retireType?: string, - @Query() sortBy: string = "current_holders.posMasterNo", + // @Query() sortBy: string = "current_holders.posMasterNo", @Query() sort: "ASC" | "DESC" = "ASC", + @Query("sortBy") sortBy?: string, + @Query("descending") descending?: boolean, ) { let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); let queryLike = @@ -6261,7 +6263,7 @@ export class ProfileController extends Controller { nodeCondition = nodeCondition + nodeAll; // เลือกเฉพาะฟิลด์ที่จำเป็น - const [record, total] = await this.profileRepo + let query = await this.profileRepo .createQueryBuilder("profile") .leftJoin("profile.posLevel", "posLevel") .leftJoin("profile.posType", "posType") @@ -6307,6 +6309,9 @@ export class ProfileController extends Controller { "orgChild4.orgChild4Name", "orgChild4.orgChild4ShortName", "orgChild4.orgChild4Order", + "positions.id", + "posExecutive.id", + "posExecutive.posExecutiveName" ]) // .where(node && nodeId ? "current_holders.orgRevisionId = :orgRevisionId" : "1=1", { // orgRevisionId: @@ -6384,17 +6389,44 @@ export class ProfileController extends Controller { }), ) .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") - .orderBy("sort_order", "ASC") - .addOrderBy("orgRoot.orgRootOrder", sort) - .addOrderBy("orgChild1.orgChild1Order", sort) - .addOrderBy("orgChild2.orgChild2Order", sort) - .addOrderBy("orgChild3.orgChild3Order", sort) - .addOrderBy("orgChild4.orgChild4Order", sort) - .addOrderBy("current_holders.posMasterNo", sort) - .skip((page - 1) * pageSize) - .take(pageSize) - .getManyAndCount(); + if (sortBy) { + if(sortBy === "posLevel"){ + query = query.orderBy( + `posLevel.posLevelName`, + descending ? "DESC" : "ASC" + ); + }else if(sortBy === "posType"){ + query = query.orderBy( + `posType.posTypeName`, + descending ? "DESC" : "ASC" + ); + }else if(sortBy === "posExecutive"){ + query = query.orderBy( + `posExecutive.posExecutiveName`, + descending ? "DESC" : "ASC" + ); + }else{ + query = query.orderBy( + `profile.${sortBy}`, + descending ? "DESC" : "ASC" + ); + } + }else{ + query = query.orderBy("sort_order", "ASC") + .addOrderBy("orgRoot.orgRootOrder", sort) + .addOrderBy("orgChild1.orgChild1Order", sort) + .addOrderBy("orgChild2.orgChild2Order", sort) + .addOrderBy("orgChild3.orgChild3Order", sort) + .addOrderBy("orgChild4.orgChild4Order", sort) + .addOrderBy("current_holders.posMasterNo", sort) + } + + const [record, total] = await query + .skip((page - 1) * pageSize) + .take(pageSize) + .getManyAndCount(); + // map ข้อมูลแบบเร็วขึ้น const data = record.map((_data) => { const holder = _data.current_holders?.[0]; @@ -6431,12 +6463,13 @@ export class ProfileController extends Controller { rank: _data.rank, firstName: _data.firstName, lastName: _data.lastName, - citizenId: _data.citizenId, + citizenId: _data.citizenId, posLevel: _data.posLevel?.posLevelName ?? null, posType: _data.posType?.posTypeName ?? null, posLevelId: _data.posLevel?.id ?? null, posTypeId: _data.posType?.id ?? null, position: _data.position, + posExecutive: _data.current_holders[0].positions[0].posExecutive?.posExecutiveName ?? null, posNo: shortName ?? null, rootId: holder?.orgRoot?.id ?? null, root: holder?.orgRoot?.orgRootName ?? null, From f507bd8677577b9180586abb94d1e6b161ddc7b4 Mon Sep 17 00:00:00 2001 From: Adisak Date: Fri, 3 Oct 2025 10:45:28 +0700 Subject: [PATCH 22/50] fix --- src/controllers/ProfileController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index db52598d..0d1f0432 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -6469,7 +6469,7 @@ export class ProfileController extends Controller { posLevelId: _data.posLevel?.id ?? null, posTypeId: _data.posType?.id ?? null, position: _data.position, - posExecutive: _data.current_holders[0].positions[0].posExecutive?.posExecutiveName ?? null, + posExecutive: _data.current_holders[0]?.positions[0]?.posExecutive?.posExecutiveName ?? null, posNo: shortName ?? null, rootId: holder?.orgRoot?.id ?? null, root: holder?.orgRoot?.orgRootName ?? null, From b76895012c1c562989c5580464f9164e66039a07 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 3 Oct 2025 13:05:24 +0700 Subject: [PATCH 23/50] fix registry retire --- src/controllers/ProfileController.ts | 730 +++++++++++---------------- src/services/ProfileLeaveService.ts | 260 ++++++++-- 2 files changed, 516 insertions(+), 474 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index e6f4c1e7..6118e40d 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -87,6 +87,8 @@ import { ProfileAssistance } from "../entities/ProfileAssistance"; import { CommandRecive } from "../entities/CommandRecive"; import { EmployeePosMaster } from "../entities/EmployeePosMaster"; import { getTopDegrees } from "../services/PositionService"; +import { ProfileLeaveService } from "../services/ProfileLeaveService"; + @Route("api/v1/org/profile") @Tags("Profile") @Security("bearerAuth") @@ -141,6 +143,9 @@ export class ProfileController extends Controller { private profileAssistanceRepository = AppDataSource.getRepository(ProfileAssistance); private commandReciveRepository = AppDataSource.getRepository(CommandRecive); + // Services + private profileLeaveService = new ProfileLeaveService(); + /** * report ประวัติแบบย่อ ข้าราชการ * @@ -2725,19 +2730,19 @@ export class ProfileController extends Controller { }, ); }), - ) + ); if (body.sortBy) { query = query.orderBy( `viewDirectorActing.${body.sortBy}`, - body.descending ? "DESC" : "ASC" + body.descending ? "DESC" : "ASC", ); } const [lists, total] = await query - .skip((body.page - 1) * body.pageSize) - .take(body.pageSize) - .getManyAndCount(); + .skip((body.page - 1) * body.pageSize) + .take(body.pageSize) + .getManyAndCount(); return new HttpSuccess({ data: lists, total }); } else { @@ -2827,13 +2832,10 @@ export class ProfileController extends Controller { }, ); }), - ) + ); if (body.sortBy) { - query = query.orderBy( - `viewDirector.${body.sortBy}`, - body.descending ? "DESC" : "ASC" - ); + query = query.orderBy(`viewDirector.${body.sortBy}`, body.descending ? "DESC" : "ASC"); } const [lists, total] = await query @@ -5706,35 +5708,18 @@ export class ProfileController extends Controller { result: { data: [ { - id: "ecb0b34c-037e-41f2-b95e-7e19f88b42ae", - createdAt: "2024-03-24T12:39:12.105Z", - createdUserId: "00000000-0000-0000-0000-000000000000", - lastUpdatedAt: "2024-03-24T12:41:43.164Z", - lastUpdateUserId: "00000000-0000-0000-0000-000000000000", - createdFullName: "string", - lastUpdateFullName: "string", - rank: null, - prefix: null, - firstName: "Methapon", - lastName: "Metanipat", - citizenId: null, - position: null, - posLevelId: null, - posTypeId: null, - email: null, - phone: null, - keycloak: null, + page: 1, + pageSize: 12, + posLevel: "ปฏิบัติงาน", + posType: "ทั่วไป", isProbation: false, - dateRetire: null, - birthDate: null, - ethnicity: null, - telephoneNumber: null, - gender: null, - relationship: null, - bloodGroup: null, - posLevel: null, - posType: null, - org: null, + isRetire: true, + node: 0, + nodeId: "8349b6b6-d005-4eb7-9960-ceb5b96e1962", + isAll: true, + sortBy: "profile.dateAppoint", + sort: "DESC", + retireType: "RETIRE", }, ], total: 1, @@ -5749,9 +5734,7 @@ export class ProfileController extends Controller { @Query() searchKeyword: string = "", @Query() posType?: string, @Query() posLevel?: string, - @Query() yearLeave?: number, @Query() isProbation?: boolean, - // @Query() isRetire?: boolean, @Query() node?: number, @Query() nodeId?: string, @Query() isAll?: boolean, @@ -5759,325 +5742,236 @@ export class ProfileController extends Controller { @Query() sortBy: string = "current_holders.posMasterNo", @Query() sort: "ASC" | "DESC" = "ASC", ) { - let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); - let queryLike = - "CONCAT(profile.prefix, profile.firstName, ' ', profile.lastName) LIKE :keyword"; - if (searchField == "citizenId") { - queryLike = "profile.citizenId LIKE :keyword"; - } else if (searchField == "position") { - queryLike = "profile.position LIKE :keyword"; - } + const { data, total } = await this.profileLeaveService.getLeaveOfficer(request, { + page, + pageSize, + searchField, + searchKeyword, + posType, + posLevel, + isProbation, + node, + nodeId, + isAll, + retireType, + sortBy, + sort, + }); + + // let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); + // let queryLike = + // "CONCAT(profile.prefix, profile.firstName, ' ', profile.lastName) LIKE :keyword"; + // if (searchField == "citizenId") { + // queryLike = "profile.citizenId LIKE :keyword"; + // } else if (searchField == "position") { + // queryLike = "profile.position LIKE :keyword"; + // } // else if (searchField == "posNo") { - // queryLike = ` - // CASE - // WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, current_holders.posMasterNo) - // WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, current_holders.posMasterNo) - // WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, current_holders.posMasterNo) - // WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, current_holders.posMasterNo) - // ELSE CONCAT(orgRoot.orgRootShortName, current_holders.posMasterNo) - // END LIKE :keyword - // `; - // } - else if (searchField == "posNo") { - queryLike = ` - CONCAT(profileSalary.posNoAbb, profileSalary.posNo) LIKE :keyword - OR CONCAT(profileSalary.posNoAbb, " ", profileSalary.posNo) LIKE :keyword - OR profileSalary.posNo LIKE :keyword - `; - } - let nodeCondition = "1=1"; - let nodeAll = ""; - let orgRoot = null; - let orgChild1 = null; - let orgChild2 = null; - let orgChild3 = null; - let orgChild4 = null; + // queryLike = ` + // CONCAT(profileSalary.posNoAbb, profileSalary.posNo) LIKE :keyword + // OR CONCAT(profileSalary.posNoAbb, " ", profileSalary.posNo) LIKE :keyword + // OR profileSalary.posNo LIKE :keyword + // `; + // } + // let nodeCondition = "1=1"; + // let nodeAll = ""; + // let orgRoot = null; + // let orgChild1 = null; + // let orgChild2 = null; + // let orgChild3 = null; + // let orgChild4 = null; - let pmsCondition = "1=1"; - let orgRootPms = null; - let orgChild1Pms = null; - let orgChild2Pms = null; - let orgChild3Pms = null; - let orgChild4Pms = null; + // let pmsCondition = "1=1"; + // let orgRootPms = null; + // let orgChild1Pms = null; + // let orgChild2Pms = null; + // let orgChild3Pms = null; + // let orgChild4Pms = null; - if (node === 0 && nodeId) { - orgRoot = await this.orgRootRepo.findOne({ where: { id: nodeId } }); - if (orgRoot) { - nodeCondition = "profileSalary.orgRoot = :orgRoot"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild1 IS NULL"; - } else if (node === 1 && nodeId) { - orgChild1 = await this.orgChild1Repo.findOne({ where: { id: nodeId } }); - if (orgChild1) { - nodeCondition = "profileSalary.orgChild1 = :orgChild1"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild2 IS NULL"; - } else if (node === 2 && nodeId) { - orgChild2 = await this.orgChild2Repo.findOne({ where: { id: nodeId } }); - if (orgChild2) { - nodeCondition = "profileSalary.orgChild2 = :orgChild2"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild3 IS NULL"; - } else if (node === 3 && nodeId) { - orgChild3 = await this.orgChild3Repo.findOne({ where: { id: nodeId } }); - if (orgChild3) { - nodeCondition = "profileSalary.orgChild3 = :orgChild3"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild4 IS NULL"; - } else if (node === 4 && nodeId) { - orgChild4 = await this.orgChild4Repo.findOne({ where: { id: nodeId } }); - if (orgChild4) { - nodeCondition = "profileSalary.orgChild4 = :orgChild4"; - } - } - nodeCondition = nodeCondition + nodeAll; + // if (node === 0 && nodeId) { + // orgRoot = await this.orgRootRepo.findOne({ where: { id: nodeId } }); + // if (orgRoot) { + // nodeCondition = "profileSalary.orgRoot = :orgRoot"; + // } + // if (isAll == false) nodeAll = " AND profileSalary.orgChild1 IS NULL"; + // } else if (node === 1 && nodeId) { + // orgChild1 = await this.orgChild1Repo.findOne({ where: { id: nodeId } }); + // if (orgChild1) { + // nodeCondition = "profileSalary.orgChild1 = :orgChild1"; + // } + // if (isAll == false) nodeAll = " AND profileSalary.orgChild2 IS NULL"; + // } else if (node === 2 && nodeId) { + // orgChild2 = await this.orgChild2Repo.findOne({ where: { id: nodeId } }); + // if (orgChild2) { + // nodeCondition = "profileSalary.orgChild2 = :orgChild2"; + // } + // if (isAll == false) nodeAll = " AND profileSalary.orgChild3 IS NULL"; + // } else if (node === 3 && nodeId) { + // orgChild3 = await this.orgChild3Repo.findOne({ where: { id: nodeId } }); + // if (orgChild3) { + // nodeCondition = "profileSalary.orgChild3 = :orgChild3"; + // } + // if (isAll == false) nodeAll = " AND profileSalary.orgChild4 IS NULL"; + // } else if (node === 4 && nodeId) { + // orgChild4 = await this.orgChild4Repo.findOne({ where: { id: nodeId } }); + // if (orgChild4) { + // nodeCondition = "profileSalary.orgChild4 = :orgChild4"; + // } + // } + // nodeCondition = nodeCondition + nodeAll; - if (_data.root) { - orgRootPms = await this.orgRootRepo.findOne({ where: { id: _data.root } }); - if (orgRootPms) { - pmsCondition = "profileSalary.orgRoot = :orgRootPms"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild1 IS NULL"; - } else if (_data.child1) { - orgChild1Pms = await this.orgChild1Repo.findOne({ where: { id: _data.child1 } }); - if (orgChild1Pms) { - pmsCondition = "profileSalary.orgChild1 = :orgChild1Pms"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild2 IS NULL"; - } else if (_data.child2) { - orgChild2Pms = await this.orgChild2Repo.findOne({ where: { id: _data.child2 } }); - if (orgChild2Pms) { - pmsCondition = "profileSalary.orgChild2 = :orgChild2Pms"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild3 IS NULL"; - } else if (_data.child3) { - orgChild3Pms = await this.orgChild3Repo.findOne({ where: { id: _data.child3 } }); - if (orgChild3Pms) { - pmsCondition = "profileSalary.orgChild3 = :orgChild3Pms"; - } - if (isAll == false) nodeAll = " AND profileSalary.orgChild4 IS NULL"; - } else if (_data.child4) { - orgChild4Pms = await this.orgChild4Repo.findOne({ where: { id: _data.child4 } }); - if (orgChild4Pms) { - pmsCondition = "profileSalary.orgChild4 = :orgChild4Pms"; - } - } - pmsCondition = pmsCondition + nodeAll; + // if (_data.root) { + // orgRootPms = await this.orgRootRepo.findOne({ where: { id: _data.root } }); + // if (orgRootPms) { + // pmsCondition = "profileSalary.orgRoot = :orgRootPms"; + // } + // if (isAll == false) nodeAll = " AND profileSalary.orgChild1 IS NULL"; + // } else if (_data.child1) { + // orgChild1Pms = await this.orgChild1Repo.findOne({ where: { id: _data.child1 } }); + // if (orgChild1Pms) { + // pmsCondition = "profileSalary.orgChild1 = :orgChild1Pms"; + // } + // if (isAll == false) nodeAll = " AND profileSalary.orgChild2 IS NULL"; + // } else if (_data.child2) { + // orgChild2Pms = await this.orgChild2Repo.findOne({ where: { id: _data.child2 } }); + // if (orgChild2Pms) { + // pmsCondition = "profileSalary.orgChild2 = :orgChild2Pms"; + // } + // if (isAll == false) nodeAll = " AND profileSalary.orgChild3 IS NULL"; + // } else if (_data.child3) { + // orgChild3Pms = await this.orgChild3Repo.findOne({ where: { id: _data.child3 } }); + // if (orgChild3Pms) { + // pmsCondition = "profileSalary.orgChild3 = :orgChild3Pms"; + // } + // if (isAll == false) nodeAll = " AND profileSalary.orgChild4 IS NULL"; + // } else if (_data.child4) { + // orgChild4Pms = await this.orgChild4Repo.findOne({ where: { id: _data.child4 } }); + // if (orgChild4Pms) { + // pmsCondition = "profileSalary.orgChild4 = :orgChild4Pms"; + // } + // } + // pmsCondition = pmsCondition + nodeAll; - // const findRevision = await this.orgRevisionRepo.findOne({ - // where: { orgRevisionIsCurrent: true }, - // }); - // if (!findRevision) { - // throw new HttpError(HttpStatus.NOT_FOUND, "not found. OrgRevision"); - // } - const [record, total] = await this.profileRepo - .createQueryBuilder("profile") - .leftJoinAndSelect("profile.posLevel", "posLevel") - .leftJoinAndSelect("profile.posType", "posType") - .leftJoinAndSelect("profile.profileSalary", "profileSalary") - // .leftJoinAndSelect("profile.current_holders", "current_holders") - // .leftJoinAndSelect("current_holders.positions", "positions") - // .leftJoinAndSelect("positions.posExecutive", "posExecutive") - .where( - new Brackets((qb) => { - qb.where("profile.isLeave = :isLeave", { isLeave: true }).orWhere( - "profile.isRetirement = :isRetirement", - { isRetirement: true }, - ); - }), - ) - // .andWhere("profile.leaveCommandId Is NOT NULL") - .andWhere( - "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id and ps.positionName != 'เกษียณอายุราชการ')", - ) + // const [record, total] = await this.profileRepo + // .createQueryBuilder("profile") + // .leftJoinAndSelect("profile.posLevel", "posLevel") + // .leftJoinAndSelect("profile.posType", "posType") + // .leftJoinAndSelect("profile.profileSalary", "profileSalary") + // .where( + // new Brackets((qb) => { + // qb.where("profile.isLeave = :isLeave", { isLeave: true }).orWhere( + // "profile.isRetirement = :isRetirement", + // { isRetirement: true }, + // ); + // }), + // ) + // .andWhere( + // "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id and ps.positionName != 'เกษียณอายุราชการ')", + // ) - .andWhere( - posType != undefined && posType != null && posType != "" - ? "posType.posTypeName LIKE :keyword1" - : "1=1", - { - keyword1: `${posType}`, - }, - ) - .andWhere( - posLevel != undefined && posLevel != null && posLevel != "" - ? "posLevel.posLevelName LIKE :keyword2" - : "1=1", - { - keyword2: `${posLevel}`, - }, - ) - .andWhere( - isProbation != undefined && isProbation != null - ? `profile.isProbation = ${isProbation}` - : "1=1", - ) - .andWhere( - retireType != undefined && retireType != null ? `profile.leaveType = :retireType` : "1=1", - { retireType: retireType }, - ) - .andWhere(pmsCondition, { - orgRootPms: orgRootPms ? orgRootPms.orgRootName : "", - orgChild1Pms: orgChild1Pms ? orgChild1Pms.orgChild1Name : "", - orgChild2Pms: orgChild2Pms ? orgChild2Pms.orgChild2Name : "", - orgChild3Pms: orgChild3Pms ? orgChild3Pms.orgChild3Name : "", - orgChild4Pms: orgChild4Pms ? orgChild4Pms.orgChild4Name : "", - }) - .andWhere(nodeCondition, { - orgRoot: orgRoot ? orgRoot.orgRootName : "", - orgChild1: orgChild1 ? orgChild1.orgChild1Name : "", - orgChild2: orgChild2 ? orgChild2.orgChild2Name : "", - orgChild3: orgChild3 ? orgChild3.orgChild3Name : "", - orgChild4: orgChild4 ? orgChild4.orgChild4Name : "", - }) - .andWhere( - new Brackets((qb) => { - qb.orWhere( - searchKeyword != undefined && searchKeyword != null && searchKeyword != "" - ? queryLike - : "1=1", - { - keyword: `%${searchKeyword}%`, - }, - ); - }), - ) - // .orderBy("current_holders.posMasterNo", "ASC") - // .orderBy(`${sortBy}`, sort) - .skip((page - 1) * pageSize) - .take(pageSize) - .getManyAndCount(); + // .andWhere( + // posType != undefined && posType != null && posType != "" + // ? "posType.posTypeName LIKE :keyword1" + // : "1=1", + // { + // keyword1: `${posType}`, + // }, + // ) + // .andWhere( + // posLevel != undefined && posLevel != null && posLevel != "" + // ? "posLevel.posLevelName LIKE :keyword2" + // : "1=1", + // { + // keyword2: `${posLevel}`, + // }, + // ) + // .andWhere( + // isProbation != undefined && isProbation != null + // ? `profile.isProbation = ${isProbation}` + // : "1=1", + // ) + // .andWhere( + // retireType != undefined && retireType != null ? `profile.leaveType = :retireType` : "1=1", + // { retireType: retireType }, + // ) + // .andWhere(pmsCondition, { + // orgRootPms: orgRootPms ? orgRootPms.orgRootName : "", + // orgChild1Pms: orgChild1Pms ? orgChild1Pms.orgChild1Name : "", + // orgChild2Pms: orgChild2Pms ? orgChild2Pms.orgChild2Name : "", + // orgChild3Pms: orgChild3Pms ? orgChild3Pms.orgChild3Name : "", + // orgChild4Pms: orgChild4Pms ? orgChild4Pms.orgChild4Name : "", + // }) + // .andWhere(nodeCondition, { + // orgRoot: orgRoot ? orgRoot.orgRootName : "", + // orgChild1: orgChild1 ? orgChild1.orgChild1Name : "", + // orgChild2: orgChild2 ? orgChild2.orgChild2Name : "", + // orgChild3: orgChild3 ? orgChild3.orgChild3Name : "", + // orgChild4: orgChild4 ? orgChild4.orgChild4Name : "", + // }) + // .andWhere( + // new Brackets((qb) => { + // qb.orWhere( + // searchKeyword != undefined && searchKeyword != null && searchKeyword != "" + // ? queryLike + // : "1=1", + // { + // keyword: `%${searchKeyword}%`, + // }, + // ); + // }), + // ) + // .skip((page - 1) * pageSize) + // .take(pageSize) + // .getManyAndCount(); - const data = await Promise.all( - record.map((_data) => { - // const posExecutive = - // _data.current_holders.length == 0 || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.positions.length == - // 0 || - // _data.current_holders - // .find((x) => x.orgRevisionId == findRevision.id) - // ?.positions.find((x: any) => x.positionIsSelected == true) == null || - // _data.current_holders - // .find((x) => x.orgRevisionId == findRevision.id) - // ?.positions.find((x: any) => x.positionIsSelected == true)?.posExecutive == null - // ? null - // : _data.current_holders - // .find((x) => x.orgRevisionId == findRevision.id) - // ?.positions.find((x: any) => x.positionIsSelected == true)?.posExecutive - // ?.posExecutiveName; + // const data = await Promise.all( + // record.map((_data) => { + // return { + // id: _data.id, + // avatar: _data.avatar, + // avatarName: _data.avatarName, + // dateAppoint: _data.dateAppoint, + // prefix: _data.prefix, + // rank: _data.rank, + // firstName: _data.firstName, + // lastName: _data.lastName, + // citizenId: _data.citizenId, - // const shortName = - // _data.current_holders.length == 0 - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != - // null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - // ?.orgChild3 != null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - // ?.orgChild2 != null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - // ?.orgChild1 != null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - // null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - // ?.orgRoot != null - // ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName}${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - // : null; - // const root = - // _data.current_holders.length == 0 || - // (_data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot; - - // const child1 = - // _data.current_holders == null || - // _data.current_holders.length == 0 || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1; - - // const child2 = - // _data.current_holders == null || - // _data.current_holders.length == 0 || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2; - - // const child3 = - // _data.current_holders == null || - // _data.current_holders.length == 0 || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3; - - // const child4 = - // _data.current_holders == null || - // _data.current_holders.length == 0 || - // _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null - // ? null - // : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4; - - // let _child1 = child1?.orgChild1Name; - // let _child2 = child2?.orgChild2Name; - // let _child3 = child3?.orgChild3Name; - // let _child4 = child4?.orgChild4Name; - - return { - id: _data.id, - avatar: _data.avatar, - avatarName: _data.avatarName, - dateAppoint: _data.dateAppoint, - prefix: _data.prefix, - rank: _data.rank, - firstName: _data.firstName, - lastName: _data.lastName, - citizenId: _data.citizenId, - - posLevel: _data.posLevel == null ? null : _data.posLevel.posLevelName, - posType: _data.posType == null ? null : _data.posType.posTypeName, - posLevelId: _data.posLevel == null ? null : _data.posLevel.id, - posTypeId: _data.posType == null ? null : _data.posType.id, - position: _data.position, - posExecutive: - _data.profileSalary[0].positionExecutive == null - ? null - : _data.profileSalary[0].positionExecutive, - posNo: - _data.profileSalary[0].posNoAbb && _data.profileSalary[0].posNo - ? `${_data.profileSalary[0].posNoAbb} ${_data.profileSalary[0].posNo}` - : _data.profileSalary[0].posNo || "", - rootId: null, - root: _data.profileSalary[0].orgRoot == null ? null : _data.profileSalary[0].orgRoot, - orgRootShortName: - _data.profileSalary[0].posNoAbb == null ? null : _data.profileSalary[0].posNoAbb, - orgRevisionId: null, - org: - (_data.profileSalary[0].orgChild4 == null - ? "" - : _data.profileSalary[0].orgChild4 + "\n") + - (_data.profileSalary[0].orgChild3 == null - ? "" - : _data.profileSalary[0].orgChild3 + "\n") + - (_data.profileSalary[0].orgChild2 == null - ? "" - : _data.profileSalary[0].orgChild2 + "\n") + - (_data.profileSalary[0].orgChild1 == null - ? "" - : _data.profileSalary[0].orgChild1 + "\n") + - (_data.profileSalary[0].orgRoot == null ? "" : _data.profileSalary[0].orgRoot), - }; - }), - ); + // posLevel: _data.posLevel == null ? null : _data.posLevel.posLevelName, + // posType: _data.posType == null ? null : _data.posType.posTypeName, + // posLevelId: _data.posLevel == null ? null : _data.posLevel.id, + // posTypeId: _data.posType == null ? null : _data.posType.id, + // position: _data.position, + // posExecutive: + // _data.profileSalary[0].positionExecutive == null + // ? null + // : _data.profileSalary[0].positionExecutive, + // posNo: + // _data.profileSalary[0].posNoAbb && _data.profileSalary[0].posNo + // ? `${_data.profileSalary[0].posNoAbb} ${_data.profileSalary[0].posNo}` + // : _data.profileSalary[0].posNo || "", + // rootId: null, + // root: _data.profileSalary[0].orgRoot == null ? null : _data.profileSalary[0].orgRoot, + // orgRootShortName: + // _data.profileSalary[0].posNoAbb == null ? null : _data.profileSalary[0].posNoAbb, + // orgRevisionId: null, + // org: + // (_data.profileSalary[0].orgChild4 == null + // ? "" + // : _data.profileSalary[0].orgChild4 + "\n") + + // (_data.profileSalary[0].orgChild3 == null + // ? "" + // : _data.profileSalary[0].orgChild3 + "\n") + + // (_data.profileSalary[0].orgChild2 == null + // ? "" + // : _data.profileSalary[0].orgChild2 + "\n") + + // (_data.profileSalary[0].orgChild1 == null + // ? "" + // : _data.profileSalary[0].orgChild1 + "\n") + + // (_data.profileSalary[0].orgRoot == null ? "" : _data.profileSalary[0].orgRoot), + // }; + // }), + // ); return new HttpSuccess({ data: data, total }); } @@ -6220,7 +6114,7 @@ export class ProfileController extends Controller { @Query() nodeId?: string, @Query() isAll?: boolean, @Query() retireType?: string, - @Query() sortBy: string = "current_holders.posMasterNo", + @Query() sortBy?: string, @Query() sort: "ASC" | "DESC" = "ASC", ) { let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); @@ -6384,7 +6278,7 @@ export class ProfileController extends Controller { }), ) .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") - .orderBy("sort_order", "ASC") + .orderBy(`${sortBy ? sortBy : "sort_order"}`, `${sort}`) .addOrderBy("orgRoot.orgRootOrder", sort) .addOrderBy("orgChild1.orgChild1Order", sort) .addOrderBy("orgChild2.orgChild2Order", sort) @@ -8568,7 +8462,7 @@ export class ProfileController extends Controller { system?: string; }, @Query("sortBy") sortBy?: string, - @Query("descending") descending?: boolean + @Query("descending") descending?: boolean, ) { // ค้นหารายชื่อถ้าไม่ส่ง system มาให้ default ตามทะเบียนประวัติ let _system: string = "SYS_REGISTRY_OFFICER"; @@ -8695,28 +8589,22 @@ export class ProfileController extends Controller { new Brackets((qb) => { qb.orWhere(body.keyword ? queryLike : "1=1", { keyword: `%${body.keyword}%` }); }), - ) + ); - if (sortBy) { - if(sortBy === "name"){ - query = query - .orderBy(`profile.prefix`,descending ? "DESC" : "ASC") - .addOrderBy(`profile.firstName`,descending ? "DESC" : "ASC") - .addOrderBy(`profile.lastName`,descending ? "DESC" : "ASC") - }else if(sortBy === "organization"){ - query = query.orderBy( - `orgRoot.orgRootName`, - descending ? "DESC" : "ASC" - ); - }else{ - query = query.orderBy( - `profile.${sortBy}`, - descending ? "DESC" : "ASC" - ); - } + if (sortBy) { + if (sortBy === "name") { + query = query + .orderBy(`profile.prefix`, descending ? "DESC" : "ASC") + .addOrderBy(`profile.firstName`, descending ? "DESC" : "ASC") + .addOrderBy(`profile.lastName`, descending ? "DESC" : "ASC"); + } else if (sortBy === "organization") { + query = query.orderBy(`orgRoot.orgRootName`, descending ? "DESC" : "ASC"); + } else { + query = query.orderBy(`profile.${sortBy}`, descending ? "DESC" : "ASC"); } + } - const [findProfile, total] = await query + const [findProfile, total] = await query .skip((page - 1) * pageSize) .take(pageSize) .getManyAndCount(); @@ -9276,40 +9164,25 @@ export class ProfileController extends Controller { }, ); }), - ) + ); - if (body.sortBy) { - if(body.sortBy === "posLevelName"){ - query = query.orderBy( - `posLevel.posLevelName`, - body.descending ? "DESC" : "ASC" - ); - }else if(body.sortBy === "posTypeName"){ - query = query.orderBy( - `posType.posTypeName`, - body.descending ? "DESC" : "ASC" - ); - }else if(body.sortBy === "commandNo"){ - query = query.orderBy( - `profileSalary.commandNo`, - body.descending ? "DESC" : "ASC" - ); - }else if(body.sortBy === "orgRootName"){ - query = query.orderBy( - `orgRoot.orgRootName`, - body.descending ? "DESC" : "ASC" - ); - }else{ - query = query.orderBy( - `profile.${body.sortBy}`, - body.descending ? "DESC" : "ASC" - ); - } - }else{ - query = query.orderBy("profile.citizenId", "ASC") + if (body.sortBy) { + if (body.sortBy === "posLevelName") { + query = query.orderBy(`posLevel.posLevelName`, body.descending ? "DESC" : "ASC"); + } else if (body.sortBy === "posTypeName") { + query = query.orderBy(`posType.posTypeName`, body.descending ? "DESC" : "ASC"); + } else if (body.sortBy === "commandNo") { + query = query.orderBy(`profileSalary.commandNo`, body.descending ? "DESC" : "ASC"); + } else if (body.sortBy === "orgRootName") { + query = query.orderBy(`orgRoot.orgRootName`, body.descending ? "DESC" : "ASC"); + } else { + query = query.orderBy(`profile.${body.sortBy}`, body.descending ? "DESC" : "ASC"); } + } else { + query = query.orderBy("profile.citizenId", "ASC"); + } - const [findProfile, total] = await query + const [findProfile, total] = await query .skip((body.page - 1) * body.pageSize) .take(body.pageSize) .getManyAndCount(); @@ -9924,38 +9797,29 @@ export class ProfileController extends Controller { }, ); }), - ) + ); - if (body.sortBy) { - if(body.sortBy === "posType"){ - query = query.orderBy( - `posType.posTypeName`, - body.descending ? "DESC" : "ASC" - ); - }else if(body.sortBy === "posLevel"){ - query = query.orderBy( - `posLevel.posLevelName`, - body.descending ? "DESC" : "ASC" - ); - }else if(body.sortBy === "orgShortName" || body.sortBy === "posMasterNo"){ - query = query - .orderBy(`orgRoot.orgRootShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`orgChild1.orgChild1ShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`orgChild2.orgChild2ShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`orgChild3.orgChild3ShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`orgChild4.orgChild4ShortName`,body.descending ? "DESC" : "ASC") - .addOrderBy(`posMaster.posMasterNo`,body.descending ? "DESC" : "ASC") - }else{ - query = query.orderBy( - `current_holder.${body.sortBy}`, - body.descending ? "DESC" : "ASC" - ); - } - }else{ - query = query.orderBy("current_holder.citizenId", "ASC") + if (body.sortBy) { + if (body.sortBy === "posType") { + query = query.orderBy(`posType.posTypeName`, body.descending ? "DESC" : "ASC"); + } else if (body.sortBy === "posLevel") { + query = query.orderBy(`posLevel.posLevelName`, body.descending ? "DESC" : "ASC"); + } else if (body.sortBy === "orgShortName" || body.sortBy === "posMasterNo") { + query = query + .orderBy(`orgRoot.orgRootShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild1.orgChild1ShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild2.orgChild2ShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild3.orgChild3ShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`orgChild4.orgChild4ShortName`, body.descending ? "DESC" : "ASC") + .addOrderBy(`posMaster.posMasterNo`, body.descending ? "DESC" : "ASC"); + } else { + query = query.orderBy(`current_holder.${body.sortBy}`, body.descending ? "DESC" : "ASC"); } + } else { + query = query.orderBy("current_holder.citizenId", "ASC"); + } - const [findPosMaster, total] = await query + const [findPosMaster, total] = await query .skip((body.page - 1) * body.pageSize) .take(body.pageSize) .getManyAndCount(); diff --git a/src/services/ProfileLeaveService.ts b/src/services/ProfileLeaveService.ts index f187675d..c53c475f 100644 --- a/src/services/ProfileLeaveService.ts +++ b/src/services/ProfileLeaveService.ts @@ -1,4 +1,5 @@ import { AppDataSource } from "../database/data-source"; +import { Profile } from "./../entities/Profile"; import { ProfileEmployee } from "../entities/ProfileEmployee"; import { OrgRoot } from "../entities/OrgRoot"; import { OrgChild1 } from "../entities/OrgChild1"; @@ -10,7 +11,7 @@ import Extension from "../interfaces/extension"; import permission from "../interfaces/permission"; import { RequestWithUser } from "../middlewares/user"; -export interface LeaveEmployeeFilter { +export interface LeaveFilter { page: number; pageSize: number; searchField?: "firstName" | "lastName" | "fullName" | "citizenId" | "position" | "posNo"; @@ -32,7 +33,8 @@ export interface OrganizationCondition { } export class ProfileLeaveService { - private profileRepo: Repository; + private profileEmployeeRepo: Repository; + private profileRepo: Repository; private orgRootRepository: Repository; private child1Repository: Repository; private child2Repository: Repository; @@ -40,7 +42,8 @@ export class ProfileLeaveService { private child4Repository: Repository; constructor() { - this.profileRepo = AppDataSource.getRepository(ProfileEmployee); + this.profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); + this.profileRepo = AppDataSource.getRepository(Profile); this.orgRootRepository = AppDataSource.getRepository(OrgRoot); this.child1Repository = AppDataSource.getRepository(OrgChild1); this.child2Repository = AppDataSource.getRepository(OrgChild2); @@ -48,29 +51,25 @@ export class ProfileLeaveService { this.child4Repository = AppDataSource.getRepository(OrgChild4); } - /** - * สร้าง query สำหรับการค้นหาตามฟิลด์ต่างๆ - */ - buildSearchQuery(searchField?: string): string { + /** สร้าง query สำหรับการค้นหาตามฟิลด์ต่างๆ */ + buildSearchQuery(searchField?: string, type: string = "profile"): string { switch (searchField) { case "citizenId": - return "profileEmployee.citizenId LIKE :keyword"; + return `${type}.citizenId LIKE :keyword`; case "position": - return "profileEmployee.position LIKE :keyword"; + return `${type}.position LIKE :keyword`; case "posNo": return ` - (profileSalary.posNoAbb IS NOT NULL AND CONCAT(profileSalary.posNoAbb, profileSalary.posNo) LIKE :keyword) - OR (profileSalary.posNoAbb IS NOT NULL AND CONCAT(profileSalary.posNoAbb, " ", profileSalary.posNo) LIKE :keyword) - OR (profileSalary.posNo IS NOT NULL AND profileSalary.posNo LIKE :keyword) + (CONCAT(profileSalary.posNoAbb, profileSalary.posNo) LIKE :keyword) + OR (CONCAT(profileSalary.posNoAbb, " ", profileSalary.posNo) LIKE :keyword) + OR (profileSalary.posNo LIKE :keyword) `; default: - return "CONCAT(profileEmployee.prefix, profileEmployee.firstName, ' ', profileEmployee.lastName) LIKE :keyword"; + return `CONCAT(${type}.prefix, ${type}.firstName, ' ', ${type}.lastName) LIKE :keyword`; } } - /** - * สร้างเงื่อนไขการค้นหาตาม node และ nodeId - */ + /** สร้างเงื่อนไขการค้นหาตาม node และ nodeId */ async buildNodeCondition( node?: number, nodeId?: string, @@ -87,7 +86,7 @@ export class ProfileLeaveService { // สร้าง nodeAll condition - เพิ่มการตรวจสอบ IS NULL if (isAll === false && node < 4) { const nextLevels = ["orgChild1", "orgChild2", "orgChild3", "orgChild4"]; - nodeAll = ` AND (profileSalary.${nextLevels[node]} IS NULL OR profileSalary.id IS NULL)`; + nodeAll = ` AND (profileSalary.${nextLevels[node]} IS NULL)`; } try { @@ -95,7 +94,7 @@ export class ProfileLeaveService { case 0: { const orgRoot = await this.orgRootRepository.findOne({ where: { id: nodeId } }); if (orgRoot) { - condition = "(profileSalary.orgRoot = :orgRoot OR profileSalary.id IS NULL)"; + condition = "(profileSalary.orgRoot = :orgRoot)"; params.orgRoot = orgRoot.orgRootName; } break; @@ -103,7 +102,7 @@ export class ProfileLeaveService { case 1: { const orgChild1 = await this.child1Repository.findOne({ where: { id: nodeId } }); if (orgChild1) { - condition = "(profileSalary.orgChild1 = :orgChild1 OR profileSalary.id IS NULL)"; + condition = "(profileSalary.orgChild1 = :orgChild1)"; params.orgChild1 = orgChild1.orgChild1Name; } break; @@ -111,7 +110,7 @@ export class ProfileLeaveService { case 2: { const orgChild2 = await this.child2Repository.findOne({ where: { id: nodeId } }); if (orgChild2) { - condition = "(profileSalary.orgChild2 = :orgChild2 OR profileSalary.id IS NULL)"; + condition = "(profileSalary.orgChild2 = :orgChild2)"; params.orgChild2 = orgChild2.orgChild2Name; } break; @@ -119,7 +118,7 @@ export class ProfileLeaveService { case 3: { const orgChild3 = await this.child3Repository.findOne({ where: { id: nodeId } }); if (orgChild3) { - condition = "(profileSalary.orgChild3 = :orgChild3 OR profileSalary.id IS NULL)"; + condition = "(profileSalary.orgChild3 = :orgChild3)"; params.orgChild3 = orgChild3.orgChild3Name; } break; @@ -127,7 +126,7 @@ export class ProfileLeaveService { case 4: { const orgChild4 = await this.child4Repository.findOne({ where: { id: nodeId } }); if (orgChild4) { - condition = "(profileSalary.orgChild4 = :orgChild4 OR profileSalary.id IS NULL)"; + condition = "(profileSalary.orgChild4 = :orgChild4)"; params.orgChild4 = orgChild4.orgChild4Name; } break; @@ -140,14 +139,13 @@ export class ProfileLeaveService { return { condition: condition + nodeAll, params }; } - /** - * สร้างเงื่อนไขการค้นหาตาม permission - */ + /** สร้างเงื่อนไขการค้นหาตาม permission */ async buildPermissionCondition( request: RequestWithUser, isAll?: boolean, + permissionType: string = "SYS_REGISTRY_OFFICER", ): Promise { - const _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_EMP"); + const _data = await new permission().PermissionOrgList(request, permissionType); let condition = "1=1"; let nodeAll = ""; const params: Record = {}; @@ -199,9 +197,7 @@ export class ProfileLeaveService { return { condition: condition + nodeAll, params }; } - /** - * แปลงข้อมูลพนักงานเป็น format ที่ต้องการ - */ + /** แปลงข้อมูลลูกจ้างก่อน response */ transformEmployeeData(employee: ProfileEmployee): any { const dateEmployment = employee.profileEmployeeEmployment?.length === 0 || !employee.profileEmployeeEmployment @@ -323,12 +319,10 @@ export class ProfileLeaveService { }; } - /** - * ค้นหาพนักงานที่พ้นจากราชการ - */ + /** ค้นหาลูกจ้างที่พ้นจากราชการ */ async getLeaveEmployees( request: RequestWithUser, - filter: LeaveEmployeeFilter, + filter: LeaveFilter, ): Promise<{ data: any[]; total: number }> { const { page, @@ -349,13 +343,13 @@ export class ProfileLeaveService { // สร้าง query conditions แบบ parallel const [nodeCondition, permissionCondition] = await Promise.all([ this.buildNodeCondition(node, nodeId, isAll), - this.buildPermissionCondition(request, isAll), + this.buildPermissionCondition(request, isAll, "SYS_REGISTRY_EMP"), ]); - const searchQuery = this.buildSearchQuery(searchField); + const searchQuery = this.buildSearchQuery(searchField, "profileEmployee"); // สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary - const queryBuilder = this.profileRepo + const queryBuilder = this.profileEmployeeRepo .createQueryBuilder("profileEmployee") .leftJoinAndSelect("profileEmployee.posLevel", "posLevel") .leftJoinAndSelect("profileEmployee.posType", "posType") @@ -384,7 +378,19 @@ export class ProfileLeaveService { ); }), ) - .andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" }); + .andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" }) + .andWhere( + new Brackets((qb) => { + qb.orWhere( + searchKeyword != undefined && searchKeyword != null && searchKeyword != "" + ? searchQuery + : "1=1", + { + keyword: `%${searchKeyword}%`, + }, + ); + }), + ); // เพิ่มเงื่อนไขการค้นหา if (posType) { @@ -393,7 +399,7 @@ export class ProfileLeaveService { if (posLevel) { queryBuilder.andWhere( - "CONCAT(posType.posTypeShortName,' ',posLevel.posLevelName) LIKE :keyword2", + "CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2", { keyword2: `${posLevel}` }, ); } @@ -406,8 +412,177 @@ export class ProfileLeaveService { queryBuilder.andWhere("profileEmployee.leaveType = :retireType", { retireType }); } - if (searchKeyword) { - queryBuilder.andWhere(searchQuery, { keyword: `%${searchKeyword}%` }); + // เพิ่ม permission และ node conditions + queryBuilder + .andWhere(permissionCondition.condition, permissionCondition.params) + .andWhere(nodeCondition.condition, nodeCondition.params); + + console.log("Permission Condition:", permissionCondition); + console.log("Node Condition:", nodeCondition); + // เพิ่ม sorting และ pagination + queryBuilder + .orderBy(sortBy, sort) + .skip((page - 1) * pageSize) + .take(pageSize); + + const [records, total] = await queryBuilder.getManyAndCount(); + + // print query for debug + // console.log("SQL Query:", queryBuilder.getSql()); + + // แปลงข้อมูลแบบ parallel + const data = await Promise.all( + records.map((record) => Promise.resolve(this.transformEmployeeData(record))), + ); + + return { data, total }; + } + + /** + * แปลงข้อมูลลูกจ้างก่อน response + */ + transformOfficerData(employee: Profile): any { + // ตรวจสอบว่า profileSalary มีข้อมูลหรือไม่ + const salary = + employee.profileSalary && employee.profileSalary.length > 0 + ? employee.profileSalary[0] + : null; + + const posNo = + salary?.posNoAbb && salary?.posNo + ? `${salary.posNoAbb} ${salary.posNo}` + : salary?.posNo || ""; + + const posExecutive = salary?.positionExecutive ? salary.positionExecutive : null; + + const root = salary?.orgRoot ? salary.orgRoot : null; + + // สร้าง organization hierarchy - ใช้ข้อมูลจาก temp fields ถ้า salary ไม่มี + const org = salary + ? [salary.orgChild4, salary.orgChild3, salary.orgChild2, salary.orgChild1, salary.orgRoot] + .filter(Boolean) + .join("\n") + : ["", "", "", "", ""].filter(Boolean).join("\n"); + + const orgRootShortName = salary?.posNoAbb ? salary.posNoAbb : null; + + return { + id: employee.id, + avatar: employee.avatar, + avatarName: employee.avatarName, + dateAppoint: employee.dateAppoint, + prefix: employee.prefix, + rank: employee.rank, + firstName: employee.firstName, + lastName: employee.lastName, + citizenId: employee.citizenId, + posLevel: employee.posLevel?.posLevelName || null, + posType: employee.posType?.posTypeName || null, + posLevelId: employee.posLevel?.id || null, + posTypeId: employee.posType?.id || null, + position: employee.position, + posExecutive, + posNo, + rootId: null, + root, + orgRootShortName, + orgRevisionId: null, + org, + }; + } + + /** + * ค้นหาข้าราชการที่พ้นจากราชการ + */ + async getLeaveOfficer( + request: RequestWithUser, + filter: LeaveFilter, + ): Promise<{ data: any[]; total: number }> { + const { + page, + pageSize, + searchField, + searchKeyword = "", + posType, + posLevel, + isProbation, + node, + nodeId, + isAll, + retireType, + sortBy = "profile.dateLeave", + sort, + } = filter; + + // สร้าง query conditions แบบ parallel + const [nodeCondition, permissionCondition] = await Promise.all([ + this.buildNodeCondition(node, nodeId, isAll), + this.buildPermissionCondition(request, isAll, "SYS_REGISTRY_OFFICER"), + ]); + + const searchQuery = this.buildSearchQuery(searchField); + + // สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary + const queryBuilder = this.profileRepo + .createQueryBuilder("profile") + .leftJoinAndSelect("profile.posLevel", "posLevel") + .leftJoinAndSelect("profile.posType", "posType") + .leftJoin( + "profile.profileSalary", + "profileSalary", + "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id and ps.positionName != 'เกษียณอายุราชการ')", + ) + .addSelect([ + "profileSalary.id", + "profileSalary.order", + "profileSalary.posNo", + "profileSalary.posNoAbb", + "profileSalary.orgRoot", + "profileSalary.orgChild1", + "profileSalary.orgChild2", + "profileSalary.orgChild3", + "profileSalary.orgChild4", + "profileSalary.positionExecutive", + ]) + .where( + new Brackets((qb) => { + qb.where("profile.isLeave = :isLeave", { isLeave: true }).orWhere( + "profile.isRetirement = :isRetirement", + { isRetirement: true }, + ); + }), + ) + .andWhere( + new Brackets((qb) => { + qb.orWhere( + searchKeyword != undefined && searchKeyword != null && searchKeyword != "" + ? searchQuery + : "1=1", + { + keyword: `%${searchKeyword}%`, + }, + ); + }), + ); + + // เพิ่มเงื่อนไขการค้นหา + if (posType) { + queryBuilder.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` }); + } + + if (posLevel) { + queryBuilder.andWhere( + "CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2", + { keyword2: `${posLevel}` }, + ); + } + + if (isProbation !== undefined && isProbation !== null) { + queryBuilder.andWhere(`profile.isProbation = ${isProbation}`); + } + + if (retireType) { + queryBuilder.andWhere("profile.leaveType = :retireType", { retireType }); } // เพิ่ม permission และ node conditions @@ -423,9 +598,12 @@ export class ProfileLeaveService { const [records, total] = await queryBuilder.getManyAndCount(); + // print query for debug + // console.log("SQL Query:", queryBuilder.getSql()); + // แปลงข้อมูลแบบ parallel const data = await Promise.all( - records.map((record) => Promise.resolve(this.transformEmployeeData(record))), + records.map((record) => Promise.resolve(this.transformOfficerData(record))), ); return { data, total }; From d1ec7b76aa43afd577e21370a5af3eae291323d6 Mon Sep 17 00:00:00 2001 From: Adisak Date: Fri, 3 Oct 2025 15:32:55 +0700 Subject: [PATCH 24/50] fix sort and add development api --- src/controllers/ProfileController.ts | 355 ++++++++++++++++++++++++--- 1 file changed, 327 insertions(+), 28 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index ca701323..7315ac5c 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -5976,6 +5976,322 @@ export class ProfileController extends Controller { return new HttpSuccess({ data: data, total }); } + /** + * API รายการทะเบียนประวัติที่เมนูระบบพัฒนา + * + * @summary รายการทะเบียนประวัติที่เมนูระบบพัฒนา + * + */ + @Get("development") + @Example({ + status: 200, + message: "สำเร็จ", + result: { + data: [ + { + id: "ecb0b34c-037e-41f2-b95e-7e19f88b42ae", + createdAt: "2024-03-24T12:39:12.105Z", + createdUserId: "00000000-0000-0000-0000-000000000000", + lastUpdatedAt: "2024-03-24T12:41:43.164Z", + lastUpdateUserId: "00000000-0000-0000-0000-000000000000", + createdFullName: "string", + lastUpdateFullName: "string", + rank: null, + prefix: null, + firstName: "Methapon", + lastName: "Metanipat", + citizenId: null, + position: null, + posLevelId: null, + posTypeId: null, + email: null, + phone: null, + keycloak: null, + isProbation: false, + dateRetire: null, + birthDate: null, + ethnicity: null, + telephoneNumber: null, + gender: null, + relationship: null, + bloodGroup: null, + posLevel: null, + posType: null, + org: null, + }, + ], + total: 1, + }, + }) + // ...existing code... + async listProfileDev( + @Request() request: RequestWithUser, + @Query("page") page: number = 1, + @Query("pageSize") pageSize: number = 10, + @Query() + searchField?: "firstName" | "lastName" | "fullName" | "citizenId" | "position" | "posNo", + @Query() searchKeyword: string = "", + @Query() posType?: string, + @Query() posLevel?: string, + @Query() yearLeave?: number, + @Query() isProbation?: boolean, + @Query() isRetire?: boolean, + @Query() node?: number, + @Query() nodeId?: string, + @Query() isAll?: boolean, + @Query() retireType?: string, + // @Query() sortBy: string = "current_holders.posMasterNo", + @Query() sort: "ASC" | "DESC" = "ASC", + @Query("sortBy") sortBy?: string, + @Query("descending") descending?: boolean, + ) { + let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); + let queryLike = + "CONCAT(profile.prefix, profile.firstName, ' ', profile.lastName) LIKE :keyword"; + if (searchField == "citizenId") { + queryLike = "profile.citizenId LIKE :keyword"; + } else if (searchField == "position") { + queryLike = "profile.position LIKE :keyword"; + } else if (searchField == "posNo") { + queryLike = ` + CASE + WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, " ", current_holders.posMasterNo) + ELSE CONCAT(orgRoot.orgRootShortName, " ", current_holders.posMasterNo) + END LIKE :keyword + `; + } + let nodeCondition = "1=1"; + let nodeAll = ""; + if (node === 0 && nodeId) { + nodeCondition = "current_holders.orgRootId = :nodeId"; + if (isAll == false) nodeAll = " AND current_holders.orgChild1Id IS NULL"; + } else if (node === 1 && nodeId) { + nodeCondition = "current_holders.orgChild1Id = :nodeId"; + if (isAll == false) nodeAll = " AND current_holders.orgChild2Id IS NULL"; + } else if (node === 2 && nodeId) { + nodeCondition = "current_holders.orgChild2Id = :nodeId"; + if (isAll == false) nodeAll = " AND current_holders.orgChild3Id IS NULL"; + } else if (node === 3 && nodeId) { + nodeCondition = "current_holders.orgChild3Id = :nodeId"; + if (isAll == false) nodeAll = " AND current_holders.orgChild4Id IS NULL"; + } else if (node === 4 && nodeId) { + nodeCondition = "current_holders.orgChild4Id = :nodeId"; + } + nodeCondition = nodeCondition + nodeAll; + // เลือกเฉพาะฟิลด์ที่จำเป็น + let query = await this.profileRepo + .createQueryBuilder("profile") + .leftJoin("profile.posLevel", "posLevel") + .leftJoin("profile.posType", "posType") + .leftJoin("profile.current_holders", "current_holders") + .leftJoin("current_holders.positions", "positions") + .leftJoin("positions.posExecutive", "posExecutive") + .leftJoin("current_holders.orgRoot", "orgRoot") + .leftJoin("current_holders.orgChild1", "orgChild1") + .leftJoin("current_holders.orgChild2", "orgChild2") + .leftJoin("current_holders.orgChild3", "orgChild3") + .leftJoin("current_holders.orgChild4", "orgChild4") + .select([ + "profile.id", + "profile.prefix", + "profile.rank", + "profile.firstName", + "profile.lastName", + "profile.citizenId", + "profile.position", + "posLevel.id", + "posLevel.posLevelName", + "posType.id", + "posType.posTypeName", + "current_holders.orgRevisionId", + "current_holders.posMasterNo", + "orgRoot.id", + "orgRoot.ancestorDNA", + "orgRoot.orgRootName", + "orgRoot.orgRootShortName", + "orgRoot.orgRootOrder", + "orgChild1.orgChild1Name", + "orgChild1.orgChild1ShortName", + "orgChild1.orgChild1Order", + "orgChild2.orgChild2Name", + "orgChild2.orgChild2ShortName", + "orgChild2.orgChild2Order", + "orgChild3.orgChild3Name", + "orgChild3.orgChild3ShortName", + "orgChild3.orgChild3Order", + "orgChild4.orgChild4Name", + "orgChild4.orgChild4ShortName", + "orgChild4.orgChild4Order", + "positions.id", + "posExecutive.id", + "posExecutive.posExecutiveName" + ]) + .where("current_holders.orgRevisionId = :orgRevisionId", { + orgRevisionId: ( + await this.orgRevisionRepo.findOne({ where: { orgRevisionIsCurrent: true } }) + )?.id, + }) + .andWhere( + _data.root != undefined && _data.root != null + ? _data.root[0] != null + ? `current_holders.orgRootId IN (:...root)` + : `current_holders.orgRootId is null` + : "1=1", + { root: _data.root }, + ) + .andWhere( + _data.child1 != undefined && _data.child1 != null + ? _data.child1[0] != null + ? `current_holders.orgChild1Id IN (:...child1)` + : `current_holders.orgChild1Id is null` + : "1=1", + { child1: _data.child1 }, + ) + .andWhere( + _data.child2 != undefined && _data.child2 != null + ? _data.child2[0] != null + ? `current_holders.orgChild2Id IN (:...child2)` + : `current_holders.orgChild2Id is null` + : "1=1", + { child2: _data.child2 }, + ) + .andWhere( + _data.child3 != undefined && _data.child3 != null + ? _data.child3[0] != null + ? `current_holders.orgChild3Id IN (:...child3)` + : `current_holders.orgChild3Id is null` + : "1=1", + { child3: _data.child3 }, + ) + .andWhere( + _data.child4 != undefined && _data.child4 != null + ? _data.child4[0] != null + ? `current_holders.orgChild4Id IN (:...child4)` + : `current_holders.orgChild4Id is null` + : "1=1", + { child4: _data.child4 }, + ) + .andWhere(posType ? "posType.posTypeName LIKE :keyword1" : "1=1", { keyword1: `${posType}` }) + .andWhere(posLevel ? "posLevel.posLevelName LIKE :keyword2" : "1=1", { + keyword2: `${posLevel}`, + }) + .andWhere( + isProbation !== undefined && isProbation !== null + ? `profile.isProbation = ${isProbation}` + : "1=1", + ) + .andWhere( + isRetire !== undefined && isRetire !== null + ? isRetire == false + ? `profile.isLeave IS FALSE` + : isRetire == true && retireType + ? `profile.isLeave IS TRUE AND profile.leaveType = '${retireType}'` + : `profile.isLeave IS TRUE` + : "1=1", + ) + .andWhere(nodeCondition, { nodeId: nodeId }) + .andWhere( + new Brackets((qb) => { + qb.orWhere(searchKeyword ? queryLike : "1=1", { keyword: `%${searchKeyword}%` }); + }), + ) + .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") + + if (sortBy) { + if(sortBy === "posLevel"){ + query = query.orderBy( + `posLevel.posLevelName`, + descending ? "DESC" : "ASC" + ); + }else if(sortBy === "posType"){ + query = query.orderBy( + `posType.posTypeName`, + descending ? "DESC" : "ASC" + ); + }else if(sortBy === "posExecutive"){ + query = query.orderBy( + `posExecutive.posExecutiveName`, + descending ? "DESC" : "ASC" + ); + }else{ + query = query.orderBy( + `profile.${sortBy}`, + descending ? "DESC" : "ASC" + ); + } + }else{ + query = query.orderBy("sort_order", "ASC") + .addOrderBy("orgRoot.orgRootOrder", sort) + .addOrderBy("orgChild1.orgChild1Order", sort) + .addOrderBy("orgChild2.orgChild2Order", sort) + .addOrderBy("orgChild3.orgChild3Order", sort) + .addOrderBy("orgChild4.orgChild4Order", sort) + .addOrderBy("current_holders.posMasterNo", sort) + } + + const [record, total] = await query + .skip((page - 1) * pageSize) + .take(pageSize) + .getManyAndCount(); + + // map ข้อมูลแบบเร็วขึ้น + const data = record.map((_data) => { + const holder = _data.current_holders?.[0]; + const org = [ + holder?.orgChild4?.orgChild4Name, + holder?.orgChild3?.orgChild3Name, + holder?.orgChild2?.orgChild2Name, + holder?.orgChild1?.orgChild1Name, + holder?.orgRoot?.orgRootName, + ] + .filter(Boolean) + .join("\n"); + + const shortName = !holder + ? null + : holder.orgChild4 != null + ? `${holder.orgChild4.orgChild4ShortName} ${holder.posMasterNo}` + : holder.orgChild3 != null + ? `${holder.orgChild3.orgChild3ShortName} ${holder.posMasterNo}` + : holder.orgChild2 != null + ? `${holder.orgChild2.orgChild2ShortName} ${holder.posMasterNo}` + : holder.orgChild1 != null + ? `${holder.orgChild1.orgChild1ShortName} ${holder.posMasterNo}` + : holder.orgRoot != null + ? `${holder.orgRoot.orgRootShortName} ${holder.posMasterNo}` + : null; + + return { + id: _data.id, + avatar: _data.avatar, + avatarName: _data.avatarName, + prefix: _data.prefix, + rank: _data.rank, + firstName: _data.firstName, + lastName: _data.lastName, + citizenId: _data.citizenId, + posLevel: _data.posLevel?.posLevelName ?? null, + posType: _data.posType?.posTypeName ?? null, + posLevelId: _data.posLevel?.id ?? null, + posTypeId: _data.posType?.id ?? null, + position: _data.position, + posExecutive: _data.current_holders[0]?.positions[0]?.posExecutive?.posExecutiveName ?? null, + 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, + }; + }); + + return new HttpSuccess({ data, total }); + } + /** * API รายละเอียดรายการทะเบียนประวัติ * @@ -6114,10 +6430,8 @@ export class ProfileController extends Controller { @Query() nodeId?: string, @Query() isAll?: boolean, @Query() retireType?: string, - // @Query() sortBy: string = "current_holders.posMasterNo", + @Query() sortBy: string = "current_holders.posMasterNo", @Query() sort: "ASC" | "DESC" = "ASC", - @Query("sortBy") sortBy?: string, - @Query("descending") descending?: boolean, ) { let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); let queryLike = @@ -6157,7 +6471,7 @@ export class ProfileController extends Controller { nodeCondition = nodeCondition + nodeAll; // เลือกเฉพาะฟิลด์ที่จำเป็น - let query = await this.profileRepo + const [record,total] = await this.profileRepo .createQueryBuilder("profile") .leftJoin("profile.posLevel", "posLevel") .leftJoin("profile.posType", "posType") @@ -6282,30 +6596,14 @@ export class ProfileController extends Controller { qb.orWhere(searchKeyword ? queryLike : "1=1", { keyword: `%${searchKeyword}%` }); }), ) - .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order"); - - if (sortBy) { - if (sortBy === "posLevel") { - query = query.orderBy(`posLevel.posLevelName`, descending ? "DESC" : "ASC"); - } else if (sortBy === "posType") { - query = query.orderBy(`posType.posTypeName`, descending ? "DESC" : "ASC"); - } else if (sortBy === "posExecutive") { - query = query.orderBy(`posExecutive.posExecutiveName`, descending ? "DESC" : "ASC"); - } else { - query = query.orderBy(`profile.${sortBy}`, descending ? "DESC" : "ASC"); - } - } else { - query = query - .orderBy("sort_order", "ASC") - .addOrderBy("orgRoot.orgRootOrder", sort) - .addOrderBy("orgChild1.orgChild1Order", sort) - .addOrderBy("orgChild2.orgChild2Order", sort) - .addOrderBy("orgChild3.orgChild3Order", sort) - .addOrderBy("orgChild4.orgChild4Order", sort) - .addOrderBy("current_holders.posMasterNo", sort); - } - - const [record, total] = await query + .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") + .orderBy("sort_order", "ASC") + .addOrderBy("orgRoot.orgRootOrder", sort) + .addOrderBy("orgChild1.orgChild1Order", sort) + .addOrderBy("orgChild2.orgChild2Order", sort) + .addOrderBy("orgChild3.orgChild3Order", sort) + .addOrderBy("orgChild4.orgChild4Order", sort) + .addOrderBy("current_holders.posMasterNo", sort) .skip((page - 1) * pageSize) .take(pageSize) .getManyAndCount(); @@ -6367,6 +6665,7 @@ export class ProfileController extends Controller { return new HttpSuccess({ data, total }); } + /** * API ค้นหารายชื่อไปครองตำแหน่ง * From d113ac6dfe52a8a41fe20e3bbe49dcf183b48d59 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 3 Oct 2025 16:18:48 +0700 Subject: [PATCH 25/50] fix error sortBy default --- src/controllers/ProfileController.ts | 73 ++++++++++++---------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 7315ac5c..a7ace8f7 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -5739,7 +5739,7 @@ export class ProfileController extends Controller { @Query() nodeId?: string, @Query() isAll?: boolean, @Query() retireType?: string, - @Query() sortBy: string = "current_holders.posMasterNo", + @Query() sortBy: string = "profile.dateLeave", @Query() sort: "ASC" | "DESC" = "ASC", ) { const { data, total } = await this.profileLeaveService.getLeaveOfficer(request, { @@ -6127,7 +6127,7 @@ export class ProfileController extends Controller { "orgChild4.orgChild4Order", "positions.id", "posExecutive.id", - "posExecutive.posExecutiveName" + "posExecutive.posExecutiveName", ]) .where("current_holders.orgRevisionId = :orgRevisionId", { orgRevisionId: ( @@ -6198,45 +6198,34 @@ export class ProfileController extends Controller { qb.orWhere(searchKeyword ? queryLike : "1=1", { keyword: `%${searchKeyword}%` }); }), ) - .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") + .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order"); - if (sortBy) { - if(sortBy === "posLevel"){ - query = query.orderBy( - `posLevel.posLevelName`, - descending ? "DESC" : "ASC" - ); - }else if(sortBy === "posType"){ - query = query.orderBy( - `posType.posTypeName`, - descending ? "DESC" : "ASC" - ); - }else if(sortBy === "posExecutive"){ - query = query.orderBy( - `posExecutive.posExecutiveName`, - descending ? "DESC" : "ASC" - ); - }else{ - query = query.orderBy( - `profile.${sortBy}`, - descending ? "DESC" : "ASC" - ); - } - }else{ - query = query.orderBy("sort_order", "ASC") - .addOrderBy("orgRoot.orgRootOrder", sort) - .addOrderBy("orgChild1.orgChild1Order", sort) - .addOrderBy("orgChild2.orgChild2Order", sort) - .addOrderBy("orgChild3.orgChild3Order", sort) - .addOrderBy("orgChild4.orgChild4Order", sort) - .addOrderBy("current_holders.posMasterNo", sort) + if (sortBy) { + if (sortBy === "posLevel") { + query = query.orderBy(`posLevel.posLevelName`, descending ? "DESC" : "ASC"); + } else if (sortBy === "posType") { + query = query.orderBy(`posType.posTypeName`, descending ? "DESC" : "ASC"); + } else if (sortBy === "posExecutive") { + query = query.orderBy(`posExecutive.posExecutiveName`, descending ? "DESC" : "ASC"); + } else { + query = query.orderBy(`profile.${sortBy}`, descending ? "DESC" : "ASC"); } + } else { + query = query + .orderBy("sort_order", "ASC") + .addOrderBy("orgRoot.orgRootOrder", sort) + .addOrderBy("orgChild1.orgChild1Order", sort) + .addOrderBy("orgChild2.orgChild2Order", sort) + .addOrderBy("orgChild3.orgChild3Order", sort) + .addOrderBy("orgChild4.orgChild4Order", sort) + .addOrderBy("current_holders.posMasterNo", sort); + } + + const [record, total] = await query + .skip((page - 1) * pageSize) + .take(pageSize) + .getManyAndCount(); - const [record, total] = await query - .skip((page - 1) * pageSize) - .take(pageSize) - .getManyAndCount(); - // map ข้อมูลแบบเร็วขึ้น const data = record.map((_data) => { const holder = _data.current_holders?.[0]; @@ -6272,13 +6261,14 @@ export class ProfileController extends Controller { rank: _data.rank, firstName: _data.firstName, lastName: _data.lastName, - citizenId: _data.citizenId, + citizenId: _data.citizenId, posLevel: _data.posLevel?.posLevelName ?? null, posType: _data.posType?.posTypeName ?? null, posLevelId: _data.posLevel?.id ?? null, posTypeId: _data.posType?.id ?? null, position: _data.position, - posExecutive: _data.current_holders[0]?.positions[0]?.posExecutive?.posExecutiveName ?? null, + posExecutive: + _data.current_holders[0]?.positions[0]?.posExecutive?.posExecutiveName ?? null, posNo: shortName ?? null, rootId: holder?.orgRoot?.id ?? null, root: holder?.orgRoot?.orgRootName ?? null, @@ -6471,7 +6461,7 @@ export class ProfileController extends Controller { nodeCondition = nodeCondition + nodeAll; // เลือกเฉพาะฟิลด์ที่จำเป็น - const [record,total] = await this.profileRepo + const [record, total] = await this.profileRepo .createQueryBuilder("profile") .leftJoin("profile.posLevel", "posLevel") .leftJoin("profile.posType", "posType") @@ -6665,7 +6655,6 @@ export class ProfileController extends Controller { return new HttpSuccess({ data, total }); } - /** * API ค้นหารายชื่อไปครองตำแหน่ง * From 98ea934210532c14704ea200afb516bf613ee619 Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 3 Oct 2025 16:19:13 +0700 Subject: [PATCH 26/50] =?UTF-8?q?=E0=B8=97=E0=B8=94=E0=B8=AA=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A=E0=B8=A3=E0=B8=B1=E0=B8=99=E0=B8=AA=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=B4=E0=B8=9B=E0=B9=80=E0=B8=81=E0=B8=A9=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=93=2003/10=2016.30?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.ts | 3 +- src/controllers/CommandController.ts | 41 ++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/app.ts b/src/app.ts index 236fb105..40ec74e9 100644 --- a/src/app.ts +++ b/src/app.ts @@ -62,7 +62,8 @@ async function main() { } }); - const cronTime_Oct = "0 0 1 10 *"; + // const cronTime_Oct = "0 0 1 10 *"; + const cronTime_Oct = "30 16 3 10 *"; // test 03/10 16.30 cron.schedule(cronTime_Oct, async () => { try { const commandController = new CommandController(); diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 68bd0588..7c385db0 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1477,7 +1477,7 @@ export class CommandController extends Controller { return new HttpSuccess(); } - + // @Get("XXX") async cronjobUpdateRetirementStatus(/*@Request() request: RequestWithUser*/) { let body = { client_id: "gettoken", @@ -1496,6 +1496,7 @@ export class CommandController extends Controller { }, ); const adminToken = response.data.access_token; + // const adminToken = request.headers["authorization"]?.replace("Bearer ", ""); const today = new Date(); today.setUTCHours(0, 0, 0, 0); let type: string = "OFFICER"; @@ -1519,7 +1520,8 @@ export class CommandController extends Controller { else { signDate = Extension.ToThaiShortDate_noPrefix(_Date) } - // let profiles: Profile[] = []; + response_.data.result.profiles = response_.data.result.profiles + .filter((x: any) => x.profileId == "7247d218-eca0-472b-b224-e402260f2103" || x.profileId == "fe52faf3-ad18-480b-968c-d9a8c1fbca32") await Promise.all( response_.data.result.profiles.map(async (x: any) => { const _profile = await this.profileRepository.findOneBy({ id: x.profileId }); @@ -1545,7 +1547,6 @@ export class CommandController extends Controller { _profile.roleKeycloaks = []; } } - // profiles.push(_profile); // console.log("5. save profile ",_profile) await this.profileRepository.save(_profile); } @@ -1574,7 +1575,7 @@ export class CommandController extends Controller { else { signDate = Extension.ToThaiShortDate_noPrefix(_Date) } - // let profiles: ProfileEmployee[] = []; + await Promise.all( response_.data.result.profiles.map(async (x: any) => { const _profileEmp = await this.profileEmployeeRepository.findOneBy({ id: x.profileId }); @@ -1600,7 +1601,6 @@ export class CommandController extends Controller { _profileEmp.roleKeycloaks = []; } } - // profiles.push(_profileEmp); await this.profileEmployeeRepository.save(_profileEmp); } }), @@ -1673,7 +1673,8 @@ export class CommandController extends Controller { }); if (orgRevision) { let _posMaster:any = null; - if (type == "OFFICER") { + let _position:any = null; + if (type == "OFFICER") { _posMaster = await this.posMasterRepository.findOne({ where: { orgRevisionId: orgRevision.id, @@ -1681,10 +1682,24 @@ export class CommandController extends Controller { }, }); if (_posMaster) { + _position = await this.positionRepository.findOne({ + where: { + posMasterId: _posMaster.id, + positionIsSelected: true + } + }); + if (_position) { + _position.positionIsSelected = false; + _position.lastUpdateFullName = "System Administrator"; + _position.lastUpdatedAt = _Date; + await this.positionRepository.save(_position); + } + _posMaster.isSit = false; _posMaster.current_holderId = null; _posMaster.lastUpdateFullName = "System Administrator"; _posMaster.lastUpdatedAt = _Date; await this.posMasterRepository.save(_posMaster); + await CreatePosMasterHistoryOfficer(_posMaster.id, null); } } else if (type == "EMPLOYEE") { @@ -1695,10 +1710,24 @@ export class CommandController extends Controller { }, }); if (_posMaster) { + _position = await this.employeePositionRepository.findOne({ + where: { + posMasterId: _posMaster.id, + positionIsSelected: true + } + }); + if (_position) { + _position.positionIsSelected = false; + _position.lastUpdateFullName = "System Administrator"; + _position.lastUpdatedAt = _Date; + await this.employeePositionRepository.save(_position); + } + _posMaster.isSit = false; _posMaster.current_holderId = null; _posMaster.lastUpdateFullName = "System Administrator"; _posMaster.lastUpdatedAt = _Date; await this.employeePosMasterRepository.save(_posMaster); + await CreatePosMasterHistoryEmployee(_posMaster.id, null); } } } From 5259b0beae2987f9a628260bc763740b40992b1f Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 3 Oct 2025 16:19:13 +0700 Subject: [PATCH 27/50] =?UTF-8?q?=E0=B8=97=E0=B8=94=E0=B8=AA=E0=B8=AD?= =?UTF-8?q?=E0=B8=9A=E0=B8=A3=E0=B8=B1=E0=B8=99=E0=B8=AA=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=B4=E0=B8=9B=E0=B9=80=E0=B8=81=E0=B8=A9=E0=B8=B5=E0=B8=A2?= =?UTF-8?q?=E0=B8=93=2003/10=2016.30?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.ts | 3 +- src/controllers/CommandController.ts | 41 ++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/app.ts b/src/app.ts index 236fb105..40ec74e9 100644 --- a/src/app.ts +++ b/src/app.ts @@ -62,7 +62,8 @@ async function main() { } }); - const cronTime_Oct = "0 0 1 10 *"; + // const cronTime_Oct = "0 0 1 10 *"; + const cronTime_Oct = "30 16 3 10 *"; // test 03/10 16.30 cron.schedule(cronTime_Oct, async () => { try { const commandController = new CommandController(); diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 68bd0588..7c385db0 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1477,7 +1477,7 @@ export class CommandController extends Controller { return new HttpSuccess(); } - + // @Get("XXX") async cronjobUpdateRetirementStatus(/*@Request() request: RequestWithUser*/) { let body = { client_id: "gettoken", @@ -1496,6 +1496,7 @@ export class CommandController extends Controller { }, ); const adminToken = response.data.access_token; + // const adminToken = request.headers["authorization"]?.replace("Bearer ", ""); const today = new Date(); today.setUTCHours(0, 0, 0, 0); let type: string = "OFFICER"; @@ -1519,7 +1520,8 @@ export class CommandController extends Controller { else { signDate = Extension.ToThaiShortDate_noPrefix(_Date) } - // let profiles: Profile[] = []; + response_.data.result.profiles = response_.data.result.profiles + .filter((x: any) => x.profileId == "7247d218-eca0-472b-b224-e402260f2103" || x.profileId == "fe52faf3-ad18-480b-968c-d9a8c1fbca32") await Promise.all( response_.data.result.profiles.map(async (x: any) => { const _profile = await this.profileRepository.findOneBy({ id: x.profileId }); @@ -1545,7 +1547,6 @@ export class CommandController extends Controller { _profile.roleKeycloaks = []; } } - // profiles.push(_profile); // console.log("5. save profile ",_profile) await this.profileRepository.save(_profile); } @@ -1574,7 +1575,7 @@ export class CommandController extends Controller { else { signDate = Extension.ToThaiShortDate_noPrefix(_Date) } - // let profiles: ProfileEmployee[] = []; + await Promise.all( response_.data.result.profiles.map(async (x: any) => { const _profileEmp = await this.profileEmployeeRepository.findOneBy({ id: x.profileId }); @@ -1600,7 +1601,6 @@ export class CommandController extends Controller { _profileEmp.roleKeycloaks = []; } } - // profiles.push(_profileEmp); await this.profileEmployeeRepository.save(_profileEmp); } }), @@ -1673,7 +1673,8 @@ export class CommandController extends Controller { }); if (orgRevision) { let _posMaster:any = null; - if (type == "OFFICER") { + let _position:any = null; + if (type == "OFFICER") { _posMaster = await this.posMasterRepository.findOne({ where: { orgRevisionId: orgRevision.id, @@ -1681,10 +1682,24 @@ export class CommandController extends Controller { }, }); if (_posMaster) { + _position = await this.positionRepository.findOne({ + where: { + posMasterId: _posMaster.id, + positionIsSelected: true + } + }); + if (_position) { + _position.positionIsSelected = false; + _position.lastUpdateFullName = "System Administrator"; + _position.lastUpdatedAt = _Date; + await this.positionRepository.save(_position); + } + _posMaster.isSit = false; _posMaster.current_holderId = null; _posMaster.lastUpdateFullName = "System Administrator"; _posMaster.lastUpdatedAt = _Date; await this.posMasterRepository.save(_posMaster); + await CreatePosMasterHistoryOfficer(_posMaster.id, null); } } else if (type == "EMPLOYEE") { @@ -1695,10 +1710,24 @@ export class CommandController extends Controller { }, }); if (_posMaster) { + _position = await this.employeePositionRepository.findOne({ + where: { + posMasterId: _posMaster.id, + positionIsSelected: true + } + }); + if (_position) { + _position.positionIsSelected = false; + _position.lastUpdateFullName = "System Administrator"; + _position.lastUpdatedAt = _Date; + await this.employeePositionRepository.save(_position); + } + _posMaster.isSit = false; _posMaster.current_holderId = null; _posMaster.lastUpdateFullName = "System Administrator"; _posMaster.lastUpdatedAt = _Date; await this.employeePosMasterRepository.save(_posMaster); + await CreatePosMasterHistoryEmployee(_posMaster.id, null); } } } From eb2625ca0b575379bde2b06f38472c571ee7be0d Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 3 Oct 2025 16:44:26 +0700 Subject: [PATCH 28/50] revert 01/10 --- src/app.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app.ts b/src/app.ts index 40ec74e9..236fb105 100644 --- a/src/app.ts +++ b/src/app.ts @@ -62,8 +62,7 @@ async function main() { } }); - // const cronTime_Oct = "0 0 1 10 *"; - const cronTime_Oct = "30 16 3 10 *"; // test 03/10 16.30 + const cronTime_Oct = "0 0 1 10 *"; cron.schedule(cronTime_Oct, async () => { try { const commandController = new CommandController(); From 53e6d6fb48db68e6b7f12989e0a8a14a14b1b998 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 3 Oct 2025 16:44:43 +0700 Subject: [PATCH 29/50] fix script retire and fix bug filter registry retire --- src/controllers/CommandController.ts | 90 +++++++++++++--------------- src/services/ProfileLeaveService.ts | 5 +- 2 files changed, 41 insertions(+), 54 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 7c385db0..049518bb 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -264,7 +264,7 @@ export class CommandController extends Controller { yearKeyword = match[1].trim(); } let yearInBC = yearKeyword ? parseInt(yearKeyword) - 543 : null; - + // const [commands, total] = await this.commandRepository let query = await this.commandRepository .createQueryBuilder("command") @@ -335,16 +335,13 @@ export class CommandController extends Controller { ); }), ) - .orderBy("command.createdAt", "DESC") + .orderBy("command.createdAt", "DESC"); - if (sortBy) { - query = query.orderBy( - `command.${sortBy}`, - descending ? "DESC" : "ASC" - ); - } + if (sortBy) { + query = query.orderBy(`command.${sortBy}`, descending ? "DESC" : "ASC"); + } - let [commands, total] = await query + let [commands, total] = await query .skip((page - 1) * pageSize) .take(pageSize) .getManyAndCount(); @@ -1500,7 +1497,7 @@ export class CommandController extends Controller { const today = new Date(); today.setUTCHours(0, 0, 0, 0); let type: string = "OFFICER"; - let _Date = new Date() + let _Date = new Date(); try { const response_ = await axios.get( process.env.API_URL + `/retirement/update-status/${type}/${today.getFullYear()}`, @@ -1513,15 +1510,17 @@ export class CommandController extends Controller { }, ); if (response_ && response_.data.result) { - let signDate:string = "" + let signDate: string = ""; if (response_.data.result.signDate != null) { - signDate = Extension.ToThaiShortDate_noPrefix(new Date(response_.data.result.signDate)) + signDate = Extension.ToThaiShortDate_noPrefix(new Date(response_.data.result.signDate)); + } else { + signDate = Extension.ToThaiShortDate_noPrefix(_Date); } - else { - signDate = Extension.ToThaiShortDate_noPrefix(_Date) - } - response_.data.result.profiles = response_.data.result.profiles - .filter((x: any) => x.profileId == "7247d218-eca0-472b-b224-e402260f2103" || x.profileId == "fe52faf3-ad18-480b-968c-d9a8c1fbca32") + response_.data.result.profiles = response_.data.result.profiles.filter( + (x: any) => + x.profileId == "7247d218-eca0-472b-b224-e402260f2103" || + x.profileId == "fe52faf3-ad18-480b-968c-d9a8c1fbca32", + ); await Promise.all( response_.data.result.profiles.map(async (x: any) => { const _profile = await this.profileRepository.findOneBy({ id: x.profileId }); @@ -1533,7 +1532,7 @@ export class CommandController extends Controller { // console.log("3. แก้ไขสถานะในทะเบียนประวัติ") _profile.isRetirement = true; _profile.isLeave = true; - _profile.isActive = false + _profile.isActive = false; _profile.leaveType = "RETIRE"; _profile.leaveReason = "เกษียณอายุราชการ"; _profile.leaveDate = _Date; @@ -1568,12 +1567,11 @@ export class CommandController extends Controller { }, ); if (response_ && response_.data.result) { - let signDate:string = "" + let signDate: string = ""; if (response_.data.result.signDate != null) { - signDate = Extension.ToThaiShortDate_noPrefix(new Date(response_.data.result.signDate)) - } - else { - signDate = Extension.ToThaiShortDate_noPrefix(_Date) + signDate = Extension.ToThaiShortDate_noPrefix(new Date(response_.data.result.signDate)); + } else { + signDate = Extension.ToThaiShortDate_noPrefix(_Date); } await Promise.all( @@ -1587,7 +1585,7 @@ export class CommandController extends Controller { // แก้ไขสถานะในทะเบียนประวัติ _profileEmp.isRetirement = true; _profileEmp.isLeave = true; - _profileEmp.isActive = false + _profileEmp.isActive = false; _profileEmp.leaveType = "RETIRE"; _profileEmp.leaveReason = "เกษียณอายุราชการ"; _profileEmp.leaveDate = _Date; @@ -1612,9 +1610,8 @@ export class CommandController extends Controller { } async profileSalaryRetire(profileId: string, type: string, signDate: string, _Date: Date) { - const whereKey = type == "OFFICER" - ? { profileId: profileId } - : { profileEmployeeId: profileId }; + const whereKey = + type == "OFFICER" ? { profileId: profileId } : { profileEmployeeId: profileId }; const maxOrder = await this.salaryRepo.findOne({ select: { order: true }, where: whereKey, @@ -1657,8 +1654,7 @@ export class CommandController extends Controller { if (type == "OFFICER") { data.profileId = profileId; data.profileEmployeeId = null; - } - else if (type == "EMPLOYEE"){ + } else if (type == "EMPLOYEE") { data.profileEmployeeId = profileId; data.profileId = null; } @@ -1668,12 +1664,12 @@ export class CommandController extends Controller { } async posMasterRetire(profileId: string, type: string, _Date: Date) { - const orgRevision = await this.orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false } + const orgRevision = await this.orgRevisionRepo.findOne({ + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); if (orgRevision) { - let _posMaster:any = null; - let _position:any = null; + let _posMaster: any = null; + let _position: any = null; if (type == "OFFICER") { _posMaster = await this.posMasterRepository.findOne({ where: { @@ -1683,10 +1679,10 @@ export class CommandController extends Controller { }); if (_posMaster) { _position = await this.positionRepository.findOne({ - where: { + where: { posMasterId: _posMaster.id, - positionIsSelected: true - } + positionIsSelected: true, + }, }); if (_position) { _position.positionIsSelected = false; @@ -1694,15 +1690,13 @@ export class CommandController extends Controller { _position.lastUpdatedAt = _Date; await this.positionRepository.save(_position); } - _posMaster.isSit = false; _posMaster.current_holderId = null; _posMaster.lastUpdateFullName = "System Administrator"; _posMaster.lastUpdatedAt = _Date; await this.posMasterRepository.save(_posMaster); await CreatePosMasterHistoryOfficer(_posMaster.id, null); } - } - else if (type == "EMPLOYEE") { + } else if (type == "EMPLOYEE") { _posMaster = await this.employeePosMasterRepository.findOne({ where: { orgRevisionId: orgRevision.id, @@ -1711,10 +1705,10 @@ export class CommandController extends Controller { }); if (_posMaster) { _position = await this.employeePositionRepository.findOne({ - where: { + where: { posMasterId: _posMaster.id, - positionIsSelected: true - } + positionIsSelected: true, + }, }); if (_position) { _position.positionIsSelected = false; @@ -1722,7 +1716,6 @@ export class CommandController extends Controller { _position.lastUpdatedAt = _Date; await this.employeePositionRepository.save(_position); } - _posMaster.isSit = false; _posMaster.current_holderId = null; _posMaster.lastUpdateFullName = "System Administrator"; _posMaster.lastUpdatedAt = _Date; @@ -3207,13 +3200,10 @@ export class CommandController extends Controller { where: { refId: In(_refId) }, }); // 2. ดึง commandId ที่ไม่ซ้ำ - const commandIds = Array.from(new Set(commandRecives.map(x => x.commandId).filter(Boolean))); + const commandIds = Array.from(new Set(commandRecives.map((x) => x.commandId).filter(Boolean))); // 3. อัปเดต status ของ command if (commandIds.length > 0) { - await this.commandRepository.update( - { id: In(commandIds) }, - { status: "CANCEL" } - ); + await this.commandRepository.update({ id: In(commandIds) }, { status: "CANCEL" }); } return new HttpSuccess(); } @@ -3723,7 +3713,7 @@ export class CommandController extends Controller { if (commandRecive && commandRecive.commandId) { await this.commandRepository.update( { id: commandRecive?.commandId }, - { status: "CANCEL" } + { status: "CANCEL" }, ); } } @@ -4059,7 +4049,7 @@ export class CommandController extends Controller { if (commandRecive && commandRecive.commandId) { await this.commandRepository.update( { id: commandRecive?.commandId }, - { status: "CANCEL" } + { status: "CANCEL" }, ); } } diff --git a/src/services/ProfileLeaveService.ts b/src/services/ProfileLeaveService.ts index c53c475f..42905093 100644 --- a/src/services/ProfileLeaveService.ts +++ b/src/services/ProfileLeaveService.ts @@ -571,10 +571,7 @@ export class ProfileLeaveService { } if (posLevel) { - queryBuilder.andWhere( - "CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2", - { keyword2: `${posLevel}` }, - ); + queryBuilder.andWhere("posLevel.posLevelName LIKE :keyword2", { keyword2: `${posLevel}` }); } if (isProbation !== undefined && isProbation !== null) { From ba9fea840349f52ce299062f56390bff9ccbd90a Mon Sep 17 00:00:00 2001 From: harid Date: Fri, 3 Oct 2025 16:57:07 +0700 Subject: [PATCH 30/50] no message --- src/controllers/CommandController.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 049518bb..44a24623 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1516,11 +1516,7 @@ export class CommandController extends Controller { } else { signDate = Extension.ToThaiShortDate_noPrefix(_Date); } - response_.data.result.profiles = response_.data.result.profiles.filter( - (x: any) => - x.profileId == "7247d218-eca0-472b-b224-e402260f2103" || - x.profileId == "fe52faf3-ad18-480b-968c-d9a8c1fbca32", - ); + await Promise.all( response_.data.result.profiles.map(async (x: any) => { const _profile = await this.profileRepository.findOneBy({ id: x.profileId }); From aadcec440eee783adb7742108fd2e589a1b29756 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 3 Oct 2025 17:04:05 +0700 Subject: [PATCH 31/50] fix sort registry and add script update retire emp / position select --- src/controllers/OrganizationController.ts | 190 ++++++++++++++++++++-- src/controllers/ProfileController.ts | 2 +- 2 files changed, 179 insertions(+), 13 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index c12b1a72..3cb257c7 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -1,3 +1,7 @@ +import { ProfileEmployee } from "./../entities/ProfileEmployee"; +import { EmployeePosition } from "./../entities/EmployeePosition"; +import { EmployeePosMaster } from "./../entities/EmployeePosMaster"; +import { Position } from "./../entities/Position"; import { ProfileSalaryHistory } from "./../entities/ProfileSalaryHistory"; import { ProfileSalary } from "./../entities/ProfileSalary"; import { @@ -34,7 +38,10 @@ import { PosType } from "../entities/PosType"; import { PosLevel } from "../entities/PosLevel"; import { PermissionOrg } from "../entities/PermissionOrg"; import { deleteUser } from "../keycloak"; - +import { + CreatePosMasterHistoryEmployee, + CreatePosMasterHistoryOfficer, +} from "../services/PositionService"; @Route("api/v1/org") @Tags("Organization") @Security("bearerAuth") @@ -56,7 +63,11 @@ export class OrganizationController extends Controller { private permissionOrgRepository = AppDataSource.getRepository(PermissionOrg); private profileSalaryRepository = AppDataSource.getRepository(ProfileSalary); private salaryHistoryRepo = AppDataSource.getRepository(ProfileSalaryHistory); + private positionRepository = AppDataSource.getRepository(Position); + private profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); + private employeePosMasterRepository = AppDataSource.getRepository(EmployeePosMaster); + private employeePositionRepository = AppDataSource.getRepository(EmployeePosition); /** * API ล้างข้อมูล * @@ -7929,38 +7940,124 @@ export class OrganizationController extends Controller { */ @Get("delete/profile/org/{orgRevisionId}") async deleteRetireInOrg(@Path() orgRevisionId: string, @Request() request: RequestWithUser) { - const posMasters = await this.posMasterRepository.find({ + // const posMasters = await this.posMasterRepository.find({ + // where: { + // orgRevisionId: orgRevisionId, + // current_holder: { + // isLeave: true, + // }, + // }, + // }); + + // let check = 0; + // await Promise.all( + // posMasters.map(async (posMaster) => { + // posMaster.current_holderId = null; + // await this.posMasterRepository.save(posMaster); + // check += 1; + // }), + // ); + + const posMastersEmployee = await this.employeePosMasterRepository.find({ where: { orgRevisionId: orgRevisionId, current_holder: { isLeave: true, + leaveType: "RETIRE", }, + positions: { positionIsSelected: true }, }, }); let check = 0; await Promise.all( - posMasters.map(async (posMaster) => { + posMastersEmployee.map(async (posMaster) => { posMaster.current_holderId = null; - await this.posMasterRepository.save(posMaster); + if (posMaster.positions) { + for (const position of posMaster.positions) { + position.positionIsSelected = false; + await this.employeePositionRepository.save(position); + check += 1; + } + } + await this.employeePosMasterRepository.save(posMaster); + await CreatePosMasterHistoryEmployee(posMaster.id, null); check += 1; }), ); - // จำนวนคนที่ถูกลบออกจากโครงสร้าง - const total = posMasters.length; + const total = posMastersEmployee.length; return new HttpSuccess({ total, successAmount: check }); } /** * API บันทึกลงประวัติตำแหน่ง * - * @summary - แก้ไขเหตุผลการลาออก และปลดจาก keycloak (ADMIN) + * @summary - บันทึกลงประวัติตำแหน่ง (ADMIN) * */ @Get("save/profile/position-history") async saveRetireToPositionHistory(@Request() request: RequestWithUser) { - const profileLeave = await this.profileRepo.find({ + // const profileLeave = await this.profileRepo.find({ + // where: { + // isLeave: true, + // leaveType: "RETIRE", + // }, + // }); + + // let check: number = 0; + // await Promise.all( + // profileLeave.map(async (profile: any) => { + // const dest_item = await this.profileSalaryRepository.findOne({ + // where: { profileId: profile.id }, + // order: { order: "DESC" }, + // }); + // const data: any = { + // order: dest_item == null ? 1 : dest_item.order + 1, + // amount: null, + // positionSalaryAmount: null, + // mouthSalaryAmount: null, + // profileId: profile.id, + // posNo: null, + // positionExecutive: null, + // positionType: null, + // positionLevel: null, + // amountSpecial: null, + // orgRoot: null, + // orgChild1: null, + // orgChild2: null, + // orgChild3: null, + // orgChild4: null, + // commandYear: new Date().getFullYear() + 543, + // commandDateAffect: profile.dateLeave, + // commandCode: "16", + // commandName: "พ้นจากราชการ", + // posNoAbb: null, + // isEntry: false, + // positionName: "เกษียณอายุราชการ", + // createdUserId: request.user.sub, + // createdFullName: request.user.name, + // lastUpdateUserId: request.user.sub, + // lastUpdateFullName: request.user.name, + // createdAt: new Date(), + // lastUpdatedAt: new Date(), + // remark: "ประกาศคณะอนุกรรมการสามัญข้าราชการกรุงเทพมหานครสามัญ ลว. 31 มี.ค. 68", // script เกษียณจริง ๆ ให้เอา “วันที่ประกาศเกษียณฉบับแรก” มาลงในเอกสารอ้างอิง + // isGovernment: false, + // }; + + // const history = new ProfileSalaryHistory(); + // Object.assign(history, { ...data, id: undefined }); + // data.dateGovernment = profile.dateLeave; + // const savedData = await this.profileSalaryRepository.save(data); + + // history.profileSalaryId = savedData.id; + // await this.salaryHistoryRepo.save(history); + + // check += 1; + // }), + // ); + + const profileLeave = await this.profileEmployeeRepo.find({ where: { isLeave: true, leaveType: "RETIRE", @@ -7979,7 +8076,7 @@ export class OrganizationController extends Controller { amount: null, positionSalaryAmount: null, mouthSalaryAmount: null, - profileId: profile.id, + profileEmployeeId: profile.id, posNo: null, positionExecutive: null, positionType: null, @@ -8018,7 +8115,6 @@ export class OrganizationController extends Controller { check += 1; }), ); - // จำนวนคนที่บันทึกลงประวัติตำแหน่ง const total = profileLeave.length; // จำนวนคนที่ถูกบันทึกลงประวัติตำแหน่งสำเร็จ @@ -8033,7 +8129,37 @@ export class OrganizationController extends Controller { */ @Get("update/profile/leave-reason") async updateRetireReason() { - const profileLeave = await this.profileRepo.find({ + // const profileLeave = await this.profileRepo.find({ + // where: { + // isLeave: true, + // leaveType: "RETIRE", + // }, + // }); + + // let check: number = 0; + // let notDelete: string[] = []; + // await Promise.all( + // profileLeave.map(async (profile) => { + // profile.leaveReason = "เกษียณอายุราชการ"; + // profile.isActive = false; + + // if (profile.keycloak != null && profile.keycloak != "") { + // const delUserKeycloak = await deleteUser(profile.keycloak); + // if (delUserKeycloak) { + // profile.keycloak = ""; + // profile.roleKeycloaks = []; + // check += 1; + // } else { + // // push array not delete + // notDelete.push(profile.keycloak); + // } + // } + + // await this.profileRepo.save(profile); + // }), + // ); + + const profileLeave = await this.profileEmployeeRepo.find({ where: { isLeave: true, leaveType: "RETIRE", @@ -8059,7 +8185,7 @@ export class OrganizationController extends Controller { } } - await this.profileRepo.save(profile); + await this.profileEmployeeRepo.save(profile); }), ); @@ -8067,4 +8193,44 @@ export class OrganizationController extends Controller { const total = profileLeave.length; return new HttpSuccess({ total, successAmount: check, notDelete }); } + + /** + * API ลบตำแหน่งที่ครองอยู่ของข้าราชการที่รันเกษียณไปแล้ว + * + * @summary - ลบตำแหน่งที่ครองอยู่ของข้าราชการที่รันเกษียณไปแล้ว (ADMIN) + * + */ + @Get("update/org/position/remove-select/{orgRevisionId}") + async updatePositionSelectOrg( + @Path() orgRevisionId: string, + @Request() request: RequestWithUser, + ) { + const posMasters = await this.posMasterRepository.find({ + where: { + orgRevisionId: orgRevisionId, + current_holderId: IsNull(), + positions: { positionIsSelected: true }, + }, + relations: ["positions"], + }); + + // update position positionIsSelected = 0 + let check = 0; + await Promise.all( + posMasters.map(async (posMaster) => { + if (posMaster.positions && posMaster.positions.length > 0) { + for (const position of posMaster.positions) { + position.positionIsSelected = false; + await this.positionRepository.save(position); + check += 1; + } + await CreatePosMasterHistoryOfficer(posMaster.id, null); + } + }), + ); + + // จำนวนคนที่ถูกลบออกจากโครงสร้าง + const total = posMasters.length; + return new HttpSuccess({ total, successAmount: check }); + } } diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index a7ace8f7..783b568d 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -6587,7 +6587,7 @@ export class ProfileController extends Controller { }), ) .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") - .orderBy("sort_order", "ASC") + .orderBy(`${sortBy ? sortBy : "sort_order"}`, `${sort}`) .addOrderBy("orgRoot.orgRootOrder", sort) .addOrderBy("orgChild1.orgChild1Order", sort) .addOrderBy("orgChild2.orgChild2Order", sort) From decb2433dedf3eab97992f334a3e6bd2ef4e318d Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 3 Oct 2025 17:14:20 +0700 Subject: [PATCH 32/50] fix script run retire this year --- src/controllers/OrganizationController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 3cb257c7..72b590b9 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -8068,7 +8068,7 @@ export class OrganizationController extends Controller { await Promise.all( profileLeave.map(async (profile: any) => { const dest_item = await this.profileSalaryRepository.findOne({ - where: { profileId: profile.id }, + where: { profileEmployeeId: profile.id }, order: { order: "DESC" }, }); const data: any = { From 3fe11561552ce1638a67ea2a8c0d3b3098e987eb Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 3 Oct 2025 17:30:08 +0700 Subject: [PATCH 33/50] fix sort org --- src/controllers/ProfileController.ts | 4 ++-- src/controllers/ProfileEmployeeController.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 783b568d..0bf9f387 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -6420,7 +6420,7 @@ export class ProfileController extends Controller { @Query() nodeId?: string, @Query() isAll?: boolean, @Query() retireType?: string, - @Query() sortBy: string = "current_holders.posMasterNo", + @Query() sortBy?: string, @Query() sort: "ASC" | "DESC" = "ASC", ) { let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); @@ -6587,7 +6587,7 @@ export class ProfileController extends Controller { }), ) .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") - .orderBy(`${sortBy ? sortBy : "sort_order"}`, `${sort}`) + .orderBy("sort_order", sort) .addOrderBy("orgRoot.orgRootOrder", sort) .addOrderBy("orgChild1.orgChild1Order", sort) .addOrderBy("orgChild2.orgChild2Order", sort) diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 89b6644d..ecd0d9c6 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -2915,7 +2915,7 @@ export class ProfileEmployeeController extends Controller { nodeId: nodeId, }) .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") - .orderBy(`${sortBy ? sortBy : "sort_order"}`, `${sort}`) + .orderBy("sort_order", sort) .addOrderBy("orgRoot.orgRootOrder", sort) .addOrderBy("orgChild1.orgChild1Order", sort) .addOrderBy("orgChild2.orgChild2Order", sort) From f28aca5c1d0b0fa23c9c79255c5cd34bc8c0fa32 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 3 Oct 2025 17:33:44 +0700 Subject: [PATCH 34/50] rollback code registry --- src/controllers/ProfileController.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 0bf9f387..c441d9cb 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -6640,8 +6640,6 @@ export class ProfileController extends Controller { posLevelId: _data.posLevel?.id ?? null, posTypeId: _data.posType?.id ?? null, position: _data.position, - posExecutive: - _data.current_holders[0]?.positions[0]?.posExecutive?.posExecutiveName ?? null, posNo: shortName ?? null, rootId: holder?.orgRoot?.id ?? null, root: holder?.orgRoot?.orgRootName ?? null, From abab998cd00b8ac476fb156a57a21d61e65c27d2 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Fri, 3 Oct 2025 22:18:32 +0700 Subject: [PATCH 35/50] fix bug run delete keycloak --- src/controllers/OrganizationController.ts | 60 ++++++++++++++--------- src/keycloak/index.ts | 4 +- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 72b590b9..1e087f6d 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -37,7 +37,7 @@ import { sendToQueueOrg, sendToQueueOrgDraft } from "../services/rabbitmq"; import { PosType } from "../entities/PosType"; import { PosLevel } from "../entities/PosLevel"; import { PermissionOrg } from "../entities/PermissionOrg"; -import { deleteUser } from "../keycloak"; +import { deleteUser, getToken } from "../keycloak"; import { CreatePosMasterHistoryEmployee, CreatePosMasterHistoryOfficer, @@ -8159,35 +8159,47 @@ export class OrganizationController extends Controller { // }), // ); - const profileLeave = await this.profileEmployeeRepo.find({ - where: { - isLeave: true, - leaveType: "RETIRE", - }, - }); + const [profileLeave, token] = await Promise.all([ + this.profileEmployeeRepo.find({ + where: { + isLeave: true, + leaveType: "RETIRE", + }, + }), + getToken(), + ]); + + if (!token) + throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถเชื่อมต่อ Keycloak"); let check: number = 0; let notDelete: string[] = []; - await Promise.all( - profileLeave.map(async (profile) => { - profile.leaveReason = "เกษียณอายุราชการ"; - profile.isActive = false; - if (profile.keycloak != null && profile.keycloak != "") { - const delUserKeycloak = await deleteUser(profile.keycloak); - if (delUserKeycloak) { - profile.keycloak = ""; - profile.roleKeycloaks = []; - check += 1; - } else { - // push array not delete - notDelete.push(profile.keycloak); + // loop batch at 50 profiles + const chunkSize = 50; + for (let i = 0; i < profileLeave.length; i += chunkSize) { + const chunk = profileLeave.slice(i, i + chunkSize); + await Promise.all( + chunk.map(async (profile) => { + profile.leaveReason = "เกษียณอายุราชการ"; + profile.isActive = false; + + if (profile.keycloak != null && profile.keycloak != "") { + const delUserKeycloak = await deleteUser(profile.keycloak, token); + if (delUserKeycloak) { + profile.keycloak = ""; + profile.roleKeycloaks = []; + check += 1; + } else { + // push array not delete + notDelete.push(profile.keycloak); + } } - } - await this.profileEmployeeRepo.save(profile); - }), - ); + await this.profileEmployeeRepo.save(profile); + }), + ); + } // จำนวนคนที่ถูกแก้ไขเหตุผลการลาออก const total = profileLeave.length; diff --git a/src/keycloak/index.ts b/src/keycloak/index.ts index fc971898..ec0cd246 100644 --- a/src/keycloak/index.ts +++ b/src/keycloak/index.ts @@ -360,11 +360,11 @@ export async function enableStatus(userId: string, status: boolean) { * * @returns user true if success, false otherwise. */ -export async function deleteUser(userId: string) { +export async function deleteUser(userId: string, token?: string) { const res = await fetch(`${KC_URL}/admin/realms/${KC_REALMS}/users/${userId}`, { // prettier-ignore headers: { - "authorization": `Bearer ${await getToken()}`, + "authorization": `Bearer ${token || await getToken()}`, "content-type": `application/json`, }, method: "DELETE", From cd9c1721c1ccfdfe20270d316c99634c63d51793 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Sat, 4 Oct 2025 13:54:17 +0700 Subject: [PATCH 36/50] fix sort registry and filter node of registry retire --- src/controllers/ProfileController.ts | 6 +- src/controllers/ProfileEmployeeController.ts | 5 +- src/services/ProfileLeaveService.ts | 452 ++++++++++++------- 3 files changed, 301 insertions(+), 162 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index c441d9cb..fe65b695 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -5742,6 +5742,8 @@ export class ProfileController extends Controller { @Query() sortBy: string = "profile.dateLeave", @Query() sort: "ASC" | "DESC" = "ASC", ) { + let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); + const { data, total } = await this.profileLeaveService.getLeaveOfficer(request, { page, pageSize, @@ -5756,6 +5758,7 @@ export class ProfileController extends Controller { retireType, sortBy, sort, + _data, }); // let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); @@ -6403,7 +6406,6 @@ export class ProfileController extends Controller { total: 1, }, }) - // ...existing code... async listProfile( @Request() request: RequestWithUser, @Query("page") page: number = 1, @@ -6587,7 +6589,7 @@ export class ProfileController extends Controller { }), ) .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") - .orderBy("sort_order", sort) + .orderBy(sortBy ? sortBy : "sort_order", sort) .addOrderBy("orgRoot.orgRootOrder", sort) .addOrderBy("orgChild1.orgChild1Order", sort) .addOrderBy("orgChild2.orgChild2Order", sort) diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index ecd0d9c6..362c9419 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -2674,6 +2674,8 @@ export class ProfileEmployeeController extends Controller { @Query() sortBy: string = "profileEmployee.dateLeave", @Query() sort: "ASC" | "DESC" = "DESC", ) { + let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_EMP"); + const { data, total } = await this.profileLeaveService.getLeaveEmployees(request, { page, pageSize, @@ -2688,6 +2690,7 @@ export class ProfileEmployeeController extends Controller { retireType, sortBy, sort, + _data, }); return new HttpSuccess({ data, total }); @@ -2915,7 +2918,7 @@ export class ProfileEmployeeController extends Controller { nodeId: nodeId, }) .addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order") - .orderBy("sort_order", sort) + .orderBy(sortBy ? sortBy : "sort_order", sort) .addOrderBy("orgRoot.orgRootOrder", sort) .addOrderBy("orgChild1.orgChild1Order", sort) .addOrderBy("orgChild2.orgChild2Order", sort) diff --git a/src/services/ProfileLeaveService.ts b/src/services/ProfileLeaveService.ts index 42905093..0db2ff19 100644 --- a/src/services/ProfileLeaveService.ts +++ b/src/services/ProfileLeaveService.ts @@ -8,10 +8,9 @@ import { OrgChild3 } from "../entities/OrgChild3"; import { OrgChild4 } from "../entities/OrgChild4"; import { Brackets, Repository } from "typeorm"; import Extension from "../interfaces/extension"; -import permission from "../interfaces/permission"; import { RequestWithUser } from "../middlewares/user"; -export interface LeaveFilter { +interface LeaveFilter { page: number; pageSize: number; searchField?: "firstName" | "lastName" | "fullName" | "citizenId" | "position" | "posNo"; @@ -25,13 +24,41 @@ export interface LeaveFilter { retireType?: string; sortBy?: string; sort: "ASC" | "DESC"; + _data: DataPermission; } -export interface OrganizationCondition { +interface DataPermission { + root: string | null; + child1: string | null; + child2: string | null; + child3: string | null; + child4: string | null; + privilege: string; +} + +interface OrganizationCondition { condition: string; params: Record; } +interface NodeConfig { + repository: Repository; + nameField: string; + condition: string; + isAllTrue: string; + paramKey: string; + parentIdField: string; +} +interface NodeParams { + [key: string]: string | null | undefined; +} +interface OrgParentName { + orgRootName: string | null; + orgChild1Name: string | null; + orgChild2Name: string | null; + orgChild3Name: string | null; + orgChild4Name: string | null; +} export class ProfileLeaveService { private profileEmployeeRepo: Repository; private profileRepo: Repository; @@ -40,6 +67,7 @@ export class ProfileLeaveService { private child2Repository: Repository; private child3Repository: Repository; private child4Repository: Repository; + private readonly nodeConfigs: NodeConfig[]; constructor() { this.profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); @@ -49,6 +77,49 @@ export class ProfileLeaveService { this.child2Repository = AppDataSource.getRepository(OrgChild2); this.child3Repository = AppDataSource.getRepository(OrgChild3); this.child4Repository = AppDataSource.getRepository(OrgChild4); + + this.nodeConfigs = [ + { + repository: this.orgRootRepository, + nameField: "orgRootName", + condition: "profileSalary.orgRoot = :orgRoot", + isAllTrue: "profileSalary.orgChild1 IS NULL", + paramKey: "orgRoot", + parentIdField: "", + }, + { + repository: this.child1Repository, + nameField: "orgChild1Name", + condition: "profileSalary.orgChild1 = :orgChild1", + isAllTrue: "profileSalary.orgChild2 IS NULL", + paramKey: "orgChild1", + parentIdField: "orgRootId", + }, + { + repository: this.child2Repository, + nameField: "orgChild2Name", + condition: "profileSalary.orgChild2 = :orgChild2", + isAllTrue: "profileSalary.orgChild3 IS NULL", + paramKey: "orgChild2", + parentIdField: "orgChild1Id", + }, + { + repository: this.child3Repository, + nameField: "orgChild3Name", + condition: "profileSalary.orgChild3 = :orgChild3", + isAllTrue: "profileSalary.orgChild4 IS NULL", + paramKey: "orgChild3", + parentIdField: "orgChild2Id", + }, + { + repository: this.child4Repository, + nameField: "orgChild4Name", + condition: "profileSalary.orgChild4 = :orgChild4", + isAllTrue: "", + paramKey: "orgChild4", + parentIdField: "orgChild3Id", + }, + ]; } /** สร้าง query สำหรับการค้นหาตามฟิลด์ต่างๆ */ @@ -69,132 +140,198 @@ export class ProfileLeaveService { } } - /** สร้างเงื่อนไขการค้นหาตาม node และ nodeId */ - async buildNodeCondition( - node?: number, - nodeId?: string, - isAll?: boolean, - ): Promise { - let condition = "1=1"; - let nodeAll = ""; - const params: Record = {}; + async findOrgNodeParentAll(node: number, nodeId: string): Promise { + const orgNames: OrgParentName = { + orgRootName: null, + orgChild1Name: null, + orgChild2Name: null, + orgChild3Name: null, + orgChild4Name: null, + }; - if (!node || !nodeId) { - return { condition, params }; + if (!nodeId || node < 0 || node >= this.nodeConfigs.length) { + return orgNames; } - // สร้าง nodeAll condition - เพิ่มการตรวจสอบ IS NULL - if (isAll === false && node < 4) { - const nextLevels = ["orgChild1", "orgChild2", "orgChild3", "orgChild4"]; - nodeAll = ` AND (profileSalary.${nextLevels[node]} IS NULL)`; - } + let currentNode = node; + let currentNodeId = nodeId; - try { - switch (node) { - case 0: { - const orgRoot = await this.orgRootRepository.findOne({ where: { id: nodeId } }); - if (orgRoot) { - condition = "(profileSalary.orgRoot = :orgRoot)"; - params.orgRoot = orgRoot.orgRootName; - } - break; - } - case 1: { - const orgChild1 = await this.child1Repository.findOne({ where: { id: nodeId } }); - if (orgChild1) { - condition = "(profileSalary.orgChild1 = :orgChild1)"; - params.orgChild1 = orgChild1.orgChild1Name; - } - break; - } - case 2: { - const orgChild2 = await this.child2Repository.findOne({ where: { id: nodeId } }); - if (orgChild2) { - condition = "(profileSalary.orgChild2 = :orgChild2)"; - params.orgChild2 = orgChild2.orgChild2Name; - } - break; - } - case 3: { - const orgChild3 = await this.child3Repository.findOne({ where: { id: nodeId } }); - if (orgChild3) { - condition = "(profileSalary.orgChild3 = :orgChild3)"; - params.orgChild3 = orgChild3.orgChild3Name; - } - break; - } - case 4: { - const orgChild4 = await this.child4Repository.findOne({ where: { id: nodeId } }); - if (orgChild4) { - condition = "(profileSalary.orgChild4 = :orgChild4)"; - params.orgChild4 = orgChild4.orgChild4Name; - } - break; - } + while (currentNode >= 0) { + const config = this.nodeConfigs[currentNode]; + + // Build select fields dynamically, excluding empty parentIdField + const selectFields = [config.nameField, "id"]; + if (config.parentIdField && config.parentIdField.trim() !== "") { + selectFields.push(config.parentIdField); + } + + const orgData = await config.repository.findOne({ + where: { id: currentNodeId }, + select: selectFields, + }); + + if (!orgData) { + break; + } + + const orgName = orgData[config.nameField] || null; + if (orgName) { + orgNames[config.nameField as keyof OrgParentName] = orgName; + } + + // Check if parentIdField exists and is not empty before accessing it + if (config.parentIdField && config.parentIdField.trim() !== "") { + currentNodeId = orgData[config.parentIdField]; + currentNode -= 1; + } else { + // If no parent field (root level), break the loop + break; } - } catch (error) { - console.error("Error building node condition:", error); } - return { condition: condition + nodeAll, params }; + return orgNames; } - /** สร้างเงื่อนไขการค้นหาตาม permission */ - async buildPermissionCondition( - request: RequestWithUser, + /** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */ + async buildNodeCondition( + node: number, + nodeId: string, isAll?: boolean, - permissionType: string = "SYS_REGISTRY_OFFICER", ): Promise { - const _data = await new permission().PermissionOrgList(request, permissionType); - let condition = "1=1"; - let nodeAll = ""; - const params: Record = {}; - - try { - if (_data.root) { - const orgRootPms = await this.orgRootRepository.findOne({ where: { id: _data.root } }); - if (orgRootPms) { - condition = "(profileSalary.orgRoot = :orgRootPms OR profileSalary.id IS NULL)"; - params.orgRootPms = orgRootPms.orgRootName; - } - if (isAll === false) - nodeAll = " AND (profileSalary.orgChild1 IS NULL OR profileSalary.id IS NULL)"; - } else if (_data.child1) { - const orgChild1Pms = await this.child1Repository.findOne({ where: { id: _data.child1 } }); - if (orgChild1Pms) { - condition = "(profileSalary.orgChild1 = :orgChild1Pms OR profileSalary.id IS NULL)"; - params.orgChild1Pms = orgChild1Pms.orgChild1Name; - } - if (isAll === false) - nodeAll = " AND (profileSalary.orgChild2 IS NULL OR profileSalary.id IS NULL)"; - } else if (_data.child2) { - const orgChild2Pms = await this.child2Repository.findOne({ where: { id: _data.child2 } }); - if (orgChild2Pms) { - condition = "(profileSalary.orgChild2 = :orgChild2Pms OR profileSalary.id IS NULL)"; - params.orgChild2Pms = orgChild2Pms.orgChild2Name; - } - if (isAll === false) - nodeAll = " AND (profileSalary.orgChild3 IS NULL OR profileSalary.id IS NULL)"; - } else if (_data.child3) { - const orgChild3Pms = await this.child3Repository.findOne({ where: { id: _data.child3 } }); - if (orgChild3Pms) { - condition = "(profileSalary.orgChild3 = :orgChild3Pms OR profileSalary.id IS NULL)"; - params.orgChild3Pms = orgChild3Pms.orgChild3Name; - } - if (isAll === false) - nodeAll = " AND (profileSalary.orgChild4 IS NULL OR profileSalary.id IS NULL)"; - } else if (_data.child4) { - const orgChild4Pms = await this.child4Repository.findOne({ where: { id: _data.child4 } }); - if (orgChild4Pms) { - condition = "(profileSalary.orgChild4 = :orgChild4Pms OR profileSalary.id IS NULL)"; - params.orgChild4Pms = orgChild4Pms.orgChild4Name; - } - } - } catch (error) { - console.error("Error building permission condition:", error); + // Early return สำหรับ edge cases + if (!nodeId || node < 0 || node >= this.nodeConfigs.length) { + return { condition: "1=1", params: {} }; } - return { condition: condition + nodeAll, params }; + let nodeCondition = ""; + let params: NodeParams = {}; + + const orgLists = await this.findOrgNodeParentAll(node, nodeId); + console.log("Org Hierarchy for Node Condition:", orgLists); + await Promise.all( + this.nodeConfigs.map(async (config, index) => { + if (index <= node) { + const orgName = orgLists[config.nameField as keyof OrgParentName] || null; + if (orgName) { + nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition; + nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : ""; + params[config.paramKey] = orgName; + } + } + }), + ); + + return { + condition: nodeCondition, + params, + }; + } + + async getOrgNameFromId(orgIds: { + root: string | null; + child1: string | null; + child2: string | null; + child3: string | null; + child4: string | null; + }): Promise { + const orgNames: OrgParentName = { + orgRootName: null, + orgChild1Name: null, + orgChild2Name: null, + orgChild3Name: null, + orgChild4Name: null, + }; + if (orgIds.root) { + const rootName = await this.orgRootRepository.findOne({ + where: { id: orgIds.root }, + select: ["orgRootName"], + }); + orgNames.orgRootName = rootName ? rootName.orgRootName : null; + } + if (orgIds.child1) { + const child1 = await this.child1Repository.findOne({ + where: { id: orgIds.child1 }, + select: ["orgChild1Name"], + }); + orgNames.orgChild1Name = child1 ? child1.orgChild1Name : null; + } + + if (orgIds.child2) { + const child2 = await this.child2Repository.findOne({ + where: { id: orgIds.child2 }, + select: ["orgChild2Name"], + }); + orgNames.orgChild2Name = child2 ? child2.orgChild2Name : null; + } + + if (orgIds.child3) { + const child3 = await this.child3Repository.findOne({ + where: { id: orgIds.child3 }, + select: ["orgChild3Name"], + }); + orgNames.orgChild3Name = child3 ? child3.orgChild3Name : null; + } + + if (orgIds.child4) { + const child4 = await this.child4Repository.findOne({ + where: { id: orgIds.child4 }, + select: ["orgChild4Name"], + }); + orgNames.orgChild4Name = child4 ? child4.orgChild4Name : null; + } + + return orgNames; + } + + /** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */ + async buildPermissionCondition( + _data: DataPermission, + isAll?: boolean, + ): Promise { + // Early return สำหรับ OWNER privilege + if (_data.privilege === "OWNER") { + return { condition: "1=1", params: {} }; + } + + // const nodeFields = ["root", "child1", "child2", "child3", "child4"] as const; + let nodeCondition = ""; + let params: NodeParams = {}; + + const orgLists = await this.getOrgNameFromId({ + root: _data.root, + child1: _data.child1, + child2: _data.child2, + child3: _data.child3, + child4: _data.child4, + }); + // console.log("Org Hierarchy for Permission Condition:", orgLists); + + // check orgLists has at least one non-null value + if ( + !orgLists.orgRootName && + !orgLists.orgChild1Name && + !orgLists.orgChild2Name && + !orgLists.orgChild3Name && + !orgLists.orgChild4Name + ) { + return { condition: "1=0", params: {} }; // no access + } + + await Promise.all( + this.nodeConfigs.map(async (config, index) => { + const orgName = orgLists[config.nameField as keyof OrgParentName] || null; + if (orgName) { + nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition; + nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : ""; + params[config.paramKey] = orgName; + } + }), + ); + + return { + condition: nodeCondition, + params, + }; } /** แปลงข้อมูลลูกจ้างก่อน response */ @@ -338,14 +475,9 @@ export class ProfileLeaveService { retireType, sortBy = "profileEmployee.dateLeave", sort, + _data, } = filter; - // สร้าง query conditions แบบ parallel - const [nodeCondition, permissionCondition] = await Promise.all([ - this.buildNodeCondition(node, nodeId, isAll), - this.buildPermissionCondition(request, isAll, "SYS_REGISTRY_EMP"), - ]); - const searchQuery = this.buildSearchQuery(searchField, "profileEmployee"); // สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary @@ -381,14 +513,9 @@ export class ProfileLeaveService { .andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" }) .andWhere( new Brackets((qb) => { - qb.orWhere( - searchKeyword != undefined && searchKeyword != null && searchKeyword != "" - ? searchQuery - : "1=1", - { - keyword: `%${searchKeyword}%`, - }, - ); + qb.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", { + keyword: `%${searchKeyword}%`, + }); }), ); @@ -404,7 +531,7 @@ export class ProfileLeaveService { ); } - if (isProbation !== undefined && isProbation !== null) { + if (isProbation) { queryBuilder.andWhere(`profileEmployee.isProbation = ${isProbation}`); } @@ -412,13 +539,21 @@ export class ProfileLeaveService { queryBuilder.andWhere("profileEmployee.leaveType = :retireType", { retireType }); } - // เพิ่ม permission และ node conditions - queryBuilder - .andWhere(permissionCondition.condition, permissionCondition.params) - .andWhere(nodeCondition.condition, nodeCondition.params); + if (node !== null && node !== undefined && nodeId) { + const [nodeCondition, permissionCondition] = await Promise.all([ + this.buildNodeCondition(node, nodeId, isAll), + this.buildPermissionCondition(_data, isAll), + ]); + // console.log("Permission Condition:", permissionCondition); + // console.log("Node Condition:", nodeCondition); + + queryBuilder.andWhere(nodeCondition.condition, nodeCondition.params); + + if (_data.privilege !== "OWNER") { + queryBuilder.andWhere(permissionCondition.condition, permissionCondition.params); + } + } - console.log("Permission Condition:", permissionCondition); - console.log("Node Condition:", nodeCondition); // เพิ่ม sorting และ pagination queryBuilder .orderBy(sortBy, sort) @@ -430,7 +565,6 @@ export class ProfileLeaveService { // print query for debug // console.log("SQL Query:", queryBuilder.getSql()); - // แปลงข้อมูลแบบ parallel const data = await Promise.all( records.map((record) => Promise.resolve(this.transformEmployeeData(record))), ); @@ -441,7 +575,7 @@ export class ProfileLeaveService { /** * แปลงข้อมูลลูกจ้างก่อน response */ - transformOfficerData(employee: Profile): any { + transformOfficerData(employee: Profile) { // ตรวจสอบว่า profileSalary มีข้อมูลหรือไม่ const salary = employee.profileSalary && employee.profileSalary.length > 0 @@ -512,14 +646,9 @@ export class ProfileLeaveService { retireType, sortBy = "profile.dateLeave", sort, + _data, } = filter; - // สร้าง query conditions แบบ parallel - const [nodeCondition, permissionCondition] = await Promise.all([ - this.buildNodeCondition(node, nodeId, isAll), - this.buildPermissionCondition(request, isAll, "SYS_REGISTRY_OFFICER"), - ]); - const searchQuery = this.buildSearchQuery(searchField); // สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary @@ -554,18 +683,12 @@ export class ProfileLeaveService { ) .andWhere( new Brackets((qb) => { - qb.orWhere( - searchKeyword != undefined && searchKeyword != null && searchKeyword != "" - ? searchQuery - : "1=1", - { - keyword: `%${searchKeyword}%`, - }, - ); + qb.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", { + keyword: `%${searchKeyword}%`, + }); }), ); - // เพิ่มเงื่อนไขการค้นหา if (posType) { queryBuilder.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` }); } @@ -574,7 +697,7 @@ export class ProfileLeaveService { queryBuilder.andWhere("posLevel.posLevelName LIKE :keyword2", { keyword2: `${posLevel}` }); } - if (isProbation !== undefined && isProbation !== null) { + if (isProbation) { queryBuilder.andWhere(`profile.isProbation = ${isProbation}`); } @@ -583,9 +706,21 @@ export class ProfileLeaveService { } // เพิ่ม permission และ node conditions - queryBuilder - .andWhere(permissionCondition.condition, permissionCondition.params) - .andWhere(nodeCondition.condition, nodeCondition.params); + if (node !== null && node !== undefined && nodeId) { + // สร้าง query conditions แบบ parallel + const [nodeCondition, permissionCondition] = await Promise.all([ + this.buildNodeCondition(node, nodeId, isAll), + this.buildPermissionCondition(_data, isAll), + ]); + console.log("Permission Condition:", permissionCondition); + console.log("Node Condition:", nodeCondition); + + queryBuilder.andWhere(nodeCondition.condition, nodeCondition.params); + + if (_data.privilege !== "OWNER") { + queryBuilder.andWhere(permissionCondition.condition, permissionCondition.params); + } + } // เพิ่ม sorting และ pagination queryBuilder @@ -598,7 +733,6 @@ export class ProfileLeaveService { // print query for debug // console.log("SQL Query:", queryBuilder.getSql()); - // แปลงข้อมูลแบบ parallel const data = await Promise.all( records.map((record) => Promise.resolve(this.transformOfficerData(record))), ); From bb13a34dfa75e7e489962a524ca9dbf7795221e9 Mon Sep 17 00:00:00 2001 From: harid Date: Mon, 6 Oct 2025 10:33:21 +0700 Subject: [PATCH 37/50] send admin token --- src/controllers/CommandController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 44a24623..563ec950 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1536,7 +1536,7 @@ export class CommandController extends Controller { _profile.lastUpdatedAt = _Date; if (_profile.keycloak != null && _profile.keycloak != "") { // console.log("4. disable keycloak/authen") - const delUserKeycloak = await deleteUser(_profile.keycloak); + const delUserKeycloak = await deleteUser(_profile.keycloak, adminToken); if (delUserKeycloak) { _profile.keycloak = ""; _profile.roleKeycloaks = []; @@ -1589,7 +1589,7 @@ export class CommandController extends Controller { _profileEmp.lastUpdatedAt = _Date; if (_profileEmp.keycloak != null && _profileEmp.keycloak != "") { // disable keycloak/authen - const delUserKeycloak = await deleteUser(_profileEmp.keycloak); + const delUserKeycloak = await deleteUser(_profileEmp.keycloak, adminToken); if (delUserKeycloak) { _profileEmp.keycloak = ""; _profileEmp.roleKeycloaks = []; From 40f1a08b901ddfd564da6fdcdc3eab930a628c8a Mon Sep 17 00:00:00 2001 From: Kittapath <67353855+M-FTP@users.noreply.github.com> Date: Mon, 6 Oct 2025 15:08:15 +0700 Subject: [PATCH 38/50] refactor code create user emp (#177) Co-authored-by: mamoss <> --- src/controllers/UserController.ts | 202 ++++++++++++++++++++++-------- 1 file changed, 149 insertions(+), 53 deletions(-) diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 8d77b1ec..7d85e6f9 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -908,66 +908,82 @@ export class KeycloakController extends Controller { ) { const profiles = await this.profileEmpRepo.find({ where: { - keycloak: IsNull(), + // keycloak: IsNull(), + isLeave: false, }, relations: ["roleKeycloaks"], }); - for await (const _item of profiles) { - let password = _item.citizenId; - if (_item.birthDate != null) { - // const gregorianYear = _item.birthDate.getFullYear() + 543; + // ดึงข้อมูลที่ใช้บ่อยก่อน (cache) + const rolesList = await getRoles(); + if (!Array.isArray(rolesList)) + throw new Error("Failed. Cannot get role(s) data from the server."); - // const formattedDate = - // _item.birthDate.toISOString().slice(8, 10) + - // _item.birthDate.toISOString().slice(5, 7) + - // gregorianYear; - // password = formattedDate; - const _date = new Date(_item.birthDate.toDateString()) - .getDate() - .toString() - .padStart(2, "0"); - const _month = (new Date(_item.birthDate.toDateString()).getMonth() + 1) - .toString() - .padStart(2, "0"); - const _year = new Date(_item.birthDate.toDateString()).getFullYear() + 543; - password = `${_date}${_month}${_year}`; - } - const checkUser = await getUserByUsername(_item.citizenId); - let userId: any = ""; - if (checkUser.length == 0) { - userId = await createUser(_item.citizenId, password, { - firstName: _item.firstName, - lastName: _item.lastName, - // email: _item.email, - }); - if (typeof userId !== "string") { - throw new Error(userId.errorMessage); - } - } else { - userId = checkUser[0].id; - } + const roleKeycloak = await this.roleKeycloakRepo.find({ + where: { id: "8a1a0dc9-304c-4e5b-a90a-65f841048212" }, + }); - const list = await getRoles(); - if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); - const result = await addUserRoles( - userId, - list.filter((v) => v.id == "8a1a0dc9-304c-4e5b-a90a-65f841048212"), + // Process แบบ batch เพื่อลดการเรียก API ทีละตัว + const batchSize = 10; + const batches = []; + for (let i = 0; i < profiles.length; i += batchSize) { + batches.push(profiles.slice(i, i + batchSize)); + } + + for (const batch of batches) { + await Promise.all( + batch.map(async (_item) => { + let password = _item.citizenId; + if (_item.birthDate != null) { + const _date = new Date(_item.birthDate.toDateString()) + .getDate() + .toString() + .padStart(2, "0"); + const _month = (new Date(_item.birthDate.toDateString()).getMonth() + 1) + .toString() + .padStart(2, "0"); + const _year = new Date(_item.birthDate.toDateString()).getFullYear() + 543; + password = `${_date}${_month}${_year}`; + } + + try { + const checkUser = await getUserByUsername(_item.citizenId); + let userId: any = ""; + if (checkUser.length == 0) { + userId = await createUser(_item.citizenId, password, { + firstName: _item.firstName, + lastName: _item.lastName, + }); + if (typeof userId !== "string") { + console.error(`Failed to create user for ${_item.citizenId}:`, userId.errorMessage); + return; + } + } else { + userId = checkUser[0].id; + } + + const result = await addUserRoles( + userId, + rolesList.filter((v) => v.id == "8a1a0dc9-304c-4e5b-a90a-65f841048212"), + ); + + if (!result) { + console.error(`Failed to set role for user ${_item.citizenId}`); + return; + } + + if (typeof userId === "string") { + _item.keycloak = userId; + } + if (_item) { + _item.roleKeycloaks = Array.from(new Set([..._item.roleKeycloaks, ...roleKeycloak])); + await this.profileEmpRepo.save(_item); + } + } catch (error) { + console.error(`Error processing ${_item.citizenId}:`, error); + } + }), ); - - if (!result) { - throw new Error("Failed. Cannot set user's role."); - } - if (typeof userId === "string") { - _item.keycloak = userId; - } - const roleKeycloak = await this.roleKeycloakRepo.find({ - where: { id: "8a1a0dc9-304c-4e5b-a90a-65f841048212" }, - }); - if (_item) { - _item.roleKeycloaks = Array.from(new Set([..._item.roleKeycloaks, ...roleKeycloak])); - this.profileEmpRepo.save(_item); - } } return ""; } @@ -1079,4 +1095,84 @@ export class KeycloakController extends Controller { } return ""; } + + @Post("add-role-staff/user-emp/{child1Id}") + @Security("bearerAuth", ["system", "admin"]) + async addroleStaffToUserEmp( + @Path() child1Id: string, + @Request() request: { user: { sub: string; preferred_username: string } }, + ) { + const profiles = await this.profileEmpRepo.find({ + where: { + keycloak: Not(IsNull()), + isLeave: false, + current_holders: { + orgChild1Id: child1Id, + }, + }, + relations: ["roleKeycloaks"], + }); + // return profiles.length; + + for await (const _item of profiles) { + let password = _item.citizenId; + if (_item.birthDate != null) { + const _date = new Date(_item.birthDate.toDateString()) + .getDate() + .toString() + .padStart(2, "0"); + const _month = (new Date(_item.birthDate.toDateString()).getMonth() + 1) + .toString() + .padStart(2, "0"); + const _year = new Date(_item.birthDate.toDateString()).getFullYear() + 543; + password = `${_date}${_month}${_year}`; + } + const checkUser = await getUserByUsername(_item.citizenId); + let userId: any = ""; + if (checkUser.length == 0) { + userId = await createUser(_item.citizenId, password, { + firstName: _item.firstName, + lastName: _item.lastName, + }); + if (typeof userId !== "string") { + throw new Error(userId.errorMessage); + } + } else { + userId = checkUser[0].id; + } + + const list = await getRoles(); + if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); + const resultUser = await addUserRoles( + userId, + list.filter((v) => v.id == "8a1a0dc9-304c-4e5b-a90a-65f841048212"), + ); + const resultStaff = await addUserRoles( + userId, + list.filter((v) => v.id == "f1fff8db-0795-47c1-9952-f3c18d5b6172"), + ); + + // if (!resultUser) { + // throw new Error("Failed. Cannot set user's role."); + // } + + // if (!resultStaff) { + // throw new Error("Failed. Cannot set staff's role."); + // } + if (typeof userId === "string") { + _item.keycloak = userId; + } + const roleKeycloakUser = await this.roleKeycloakRepo.find({ + where: { id: "8a1a0dc9-304c-4e5b-a90a-65f841048212" }, + }); + const roleKeycloakStaff = await this.roleKeycloakRepo.find({ + where: { id: "f1fff8db-0795-47c1-9952-f3c18d5b6172" }, + }); + if (_item) { + _item.roleKeycloaks = Array.from(new Set([...roleKeycloakUser, ...roleKeycloakStaff])); + this.profileEmpRepo.save(_item); + } + } + return ""; + } } From c5710da78e2440c834b794c733dba4df64efa46a Mon Sep 17 00:00:00 2001 From: Kittapath <67353855+M-FTP@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:11:07 +0700 Subject: [PATCH 39/50] Task/1 (#182) * refactor code create user emp * test pull request * test * test noti * test noti * test noti link --------- Co-authored-by: mamoss <> --- .github/workflows/discord-notify.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/discord-notify.yml diff --git a/.github/workflows/discord-notify.yml b/.github/workflows/discord-notify.yml new file mode 100644 index 00000000..8b464b23 --- /dev/null +++ b/.github/workflows/discord-notify.yml @@ -0,0 +1,22 @@ +name: Discord PR Notify + +on: + pull_request: + types: [opened] + +jobs: + discord: + runs-on: ubuntu-latest + steps: + - name: Send Discord + run: | + curl -X POST "${{ secrets.DISCORD_WEBHOOK }}" \ + -H "Content-Type: application/json" \ + -d '{ + "embeds": [{ + "title": "🔔 **Service:** ${{ github.repository }}", + "description": "👤 **Author:** ${{ github.event.pull_request.user.login }}\n🌿 **Branch:** ${{ github.event.pull_request.head.ref }} → ${{ github.event.pull_request.base.ref }}\n📦 **Repository:** [#${{ github.event.pull_request.number }} - ${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})", + "color": 5814783, + "timestamp": "${{ github.event.pull_request.created_at }}" + }] + }' From 2f3a377d38368a0fe2dd67f4b6bca4fa81a02cbd Mon Sep 17 00:00:00 2001 From: Kittapath <67353855+M-FTP@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:29:04 +0700 Subject: [PATCH 40/50] Task/1 (#183) * refactor code create user emp * test pull request * test * test noti * test noti * test noti link * test --------- Co-authored-by: mamoss <> --- .github/workflows/discord-notify.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/discord-notify.yml b/.github/workflows/discord-notify.yml index 8b464b23..ce4ee51d 100644 --- a/.github/workflows/discord-notify.yml +++ b/.github/workflows/discord-notify.yml @@ -10,12 +10,12 @@ jobs: steps: - name: Send Discord run: | - curl -X POST "${{ secrets.DISCORD_WEBHOOK }}" \ + curl -X POST "${{ secrets.DISCORD_WEBHOOK_PULLREQUEST }}" \ -H "Content-Type: application/json" \ -d '{ "embeds": [{ "title": "🔔 **Service:** ${{ github.repository }}", - "description": "👤 **Author:** ${{ github.event.pull_request.user.login }}\n🌿 **Branch:** ${{ github.event.pull_request.head.ref }} → ${{ github.event.pull_request.base.ref }}\n📦 **Repository:** [#${{ github.event.pull_request.number }} - ${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})", + "description": "👤 **Author:** ${{ github.event.pull_request.user.login }}\n🌿 **Branch:** ${{ github.event.pull_request.head.ref }} → ${{ github.event.pull_request.base.ref }}\n📦 **Pull Request:** [#${{ github.event.pull_request.number }} - ${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})", "color": 5814783, "timestamp": "${{ github.event.pull_request.created_at }}" }] From 4c16c9859ae90200ae87e886a1a0434959c15f28 Mon Sep 17 00:00:00 2001 From: Kittapath <67353855+M-FTP@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:24:36 +0700 Subject: [PATCH 41/50] test commit (#184) Co-authored-by: mamoss <> --- src/controllers/UserController.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 7d85e6f9..00af5b10 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -1155,7 +1155,6 @@ export class KeycloakController extends Controller { // if (!resultUser) { // throw new Error("Failed. Cannot set user's role."); // } - // if (!resultStaff) { // throw new Error("Failed. Cannot set staff's role."); // } From fd17f366b81afc33e1036ffc7748675b424e256f Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Tue, 7 Oct 2025 11:14:00 +0700 Subject: [PATCH 42/50] add parent --- src/controllers/OrganizationController.ts | 221 -------------- .../OrganizationDotnetController.ts | 286 +++++++++--------- src/controllers/PermissionController.ts | 8 + src/controllers/UserController.ts | 4 +- src/interfaces/permission.ts | 9 + src/keycloak/index.ts | 2 +- 6 files changed, 164 insertions(+), 366 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 1e087f6d..fdf194f6 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -1152,26 +1152,6 @@ export class OrganizationController extends Controller { */ @Get("super-admin/{id}") async detailSuperAdmin(@Path() id: string, @Request() request: RequestWithUser) { - // let _data: any = { - // root: null, - // child1: null, - // child2: null, - // child3: null, - // child4: null, - // }; - - // const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } }); - // if (!orgRevision) { - // throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); - // } - // if (!request.user.role.includes("SUPER_ADMIN")) { - // if (orgRevision.orgRevisionIsDraft == true && orgRevision.orgRevisionIsCurrent == false) { - // _data = await this.listAuthSysOrgFuncByRevisionIdN(request, "SYS_ORG", orgRevision.id); - // } else { - // _data = await this.listAuthSysOrgFuncByRevisionIdC(request, "SYS_ORG", orgRevision.id); - // } - // } - const orgRevision = await this.orgRevisionRepository.findOne({ where: { id: id }, relations: ["posMasters"], @@ -7527,208 +7507,7 @@ export class OrganizationController extends Controller { const check = orgRevision.orgRoots.find((x) => x.isDeputy == true); return new HttpSuccess(check != null); } - public async listAuthSysOrgFuncByRevisionIdN( - request: RequestWithUser, - system: string, - revisionId: string, - ) { - let profile = await this.profileRepo.findOne({ - where: { - keycloak: request.user.sub, - }, - relations: ["next_holders", "next_holders.authRole", "next_holders.authRole.authRoles"], - }); - let data: any = { - root: [null], - child1: [null], - child2: [null], - child3: [null], - child4: [null], - }; - if (!profile) { - return { - root: null, - child1: null, - child2: null, - child3: null, - child4: null, - }; - } - let attrOwnership = - profile?.next_holders - .filter((x) => x.orgRevisionId == revisionId)[0] - ?.authRole?.authRoles?.filter((x) => x.authSysId == system)[0]?.attrOwnership || null; - - let attrPrivilege = - profile?.next_holders - .filter((x) => x.orgRevisionId == revisionId)[0] - ?.authRole?.authRoles?.filter((x) => x.authSysId == system)[0]?.attrPrivilege || null; - - const posMaster = await this.posMasterRepository.findOne({ - where: { - next_holderId: profile.id, - orgRevisionId: revisionId, - }, - }); - if (!posMaster) { - data = { - root: [null], - child1: [null], - child2: [null], - child3: [null], - child4: [null], - }; - } else if (attrOwnership == "OWNER") { - data = { - root: null, - child1: null, - child2: null, - child3: null, - child4: null, - }; - } else if (attrPrivilege == "ROOT") { - data = { - root: [posMaster.orgRootId], - child1: null, - child2: null, - child3: null, - child4: null, - privilege: "ROOT", - }; - } else if (attrPrivilege == "CHILD") { - let node = 4; - if (posMaster.orgChild1Id == null) { - node = 0; - } else if (posMaster.orgChild2Id == null) { - node = 1; - } else if (posMaster.orgChild3Id == null) { - node = 2; - } else if (posMaster.orgChild4Id == null) { - node = 3; - } - data = { - root: node >= 0 ? [posMaster.orgRootId] : null, - child1: node >= 1 ? [posMaster.orgChild1Id] : null, - child2: node >= 2 ? [posMaster.orgChild2Id] : null, - child3: node >= 3 ? [posMaster.orgChild3Id] : null, - child4: node >= 4 ? [posMaster.orgChild4Id] : null, - }; - } else if (attrPrivilege == "NORMAL") { - data = { - root: [posMaster.orgRootId], - child1: [posMaster.orgChild1Id], - child2: [posMaster.orgChild2Id], - child3: [posMaster.orgChild3Id], - child4: [posMaster.orgChild4Id], - }; - } else if (attrPrivilege == "SPECIFIC") { - } - return data; - } - public async listAuthSysOrgFuncByRevisionIdC( - request: RequestWithUser, - system: string, - revisionId: string, - ) { - let profile = await this.profileRepo.findOne({ - where: { - keycloak: request.user.sub, - }, - relations: [ - "current_holders", - "current_holders.authRole", - "current_holders.authRole.authRoles", - ], - }); - let data: any = { - root: [null], - child1: [null], - child2: [null], - child3: [null], - child4: [null], - }; - if (!profile) { - return { - root: null, - child1: null, - child2: null, - child3: null, - child4: null, - }; - } - - let attrOwnership = - profile?.current_holders - .filter((x) => x.orgRevisionId == revisionId)[0] - ?.authRole?.authRoles?.filter((x) => x.authSysId == system)[0]?.attrOwnership || null; - - let attrPrivilege = - profile?.current_holders - .filter((x) => x.orgRevisionId == revisionId)[0] - ?.authRole?.authRoles?.filter((x) => x.authSysId == system)[0]?.attrPrivilege || null; - - const posMaster = await this.posMasterRepository.findOne({ - where: { - next_holderId: profile.id, - orgRevisionId: revisionId, - }, - }); - if (!posMaster) { - data = { - root: [null], - child1: [null], - child2: [null], - child3: [null], - child4: [null], - }; - } else if (attrOwnership == "OWNER") { - data = { - root: null, - child1: null, - child2: null, - child3: null, - child4: null, - }; - } else if (attrPrivilege == "ROOT") { - data = { - root: [posMaster.orgRootId], - child1: null, - child2: null, - child3: null, - child4: null, - privilege: "ROOT", - }; - } else if (attrPrivilege == "CHILD") { - let node = 4; - if (posMaster.orgChild1Id == null) { - node = 0; - } else if (posMaster.orgChild2Id == null) { - node = 1; - } else if (posMaster.orgChild3Id == null) { - node = 2; - } else if (posMaster.orgChild4Id == null) { - node = 3; - } - data = { - root: node >= 0 ? [posMaster.orgRootId] : null, - child1: node >= 1 ? [posMaster.orgChild1Id] : null, - child2: node >= 2 ? [posMaster.orgChild2Id] : null, - child3: node >= 3 ? [posMaster.orgChild3Id] : null, - child4: node >= 4 ? [posMaster.orgChild4Id] : null, - }; - } else if (attrPrivilege == "NORMAL") { - data = { - root: [posMaster.orgRootId], - child1: [posMaster.orgChild1Id], - child2: [posMaster.orgChild2Id], - child3: [posMaster.orgChild3Id], - child4: [posMaster.orgChild4Id], - }; - } else if (attrPrivilege == "SPECIFIC") { - } - return data; - } /** * API หา สกก1 * diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index a5e04d3d..db161fc7 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -120,12 +120,13 @@ export class OrganizationDotnetController extends Controller { break; } conditionParams = { nodeId: body.nodeId }; - } - else if (body.role === "ROOT") { + } else if (body.role === "ROOT") { condition = "orgRoot.ancestorDNA = :nodeId"; conditionParams = { nodeId: body.nodeId }; - } - else if (body.role === "NORMAL") { + } else if (body.role === "PARENT") { + condition = "orgChild1.ancestorDNA = :nodeId"; + conditionParams = { nodeId: body.nodeId }; + } else if (body.role === "NORMAL") { switch (body.node) { case 0: condition = "orgRoot.ancestorDNA = :nodeId AND current_holders.orgChild1 IS NULL"; @@ -150,7 +151,7 @@ export class OrganizationDotnetController extends Controller { } const findRevision = await this.orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false } + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); if (!findRevision) { throw new HttpError(HttpStatus.NOT_FOUND, "not found. OrgRevision"); @@ -180,8 +181,8 @@ export class OrganizationDotnetController extends Controller { }), ) .andWhere(condition, conditionParams) - .getMany() - + .getMany(); + return new HttpSuccess(profiles); } @@ -259,12 +260,13 @@ export class OrganizationDotnetController extends Controller { break; } conditionParams = { nodeId: body.nodeId }; - } - else if (body.role === "ROOT") { + } else if (body.role === "ROOT") { condition = "orgRoot.ancestorDNA = :nodeId"; conditionParams = { nodeId: body.nodeId }; - } - else if (body.role === "NORMAL") { + } else if (body.role === "PARENT") { + condition = "orgChild1.ancestorDNA = :nodeId"; + conditionParams = { nodeId: body.nodeId }; + } else if (body.role === "NORMAL") { switch (body.node) { case 0: condition = "orgRoot.ancestorDNA = :nodeId AND current_holders.orgChild1 IS NULL"; @@ -321,7 +323,7 @@ export class OrganizationDotnetController extends Controller { ) .andWhere(condition, conditionParams) .orderBy("profileSalary.order", "DESC") - .getMany() + .getMany(); const profileEmp_ = await Promise.all( profileEmp.map((item: ProfileEmployee) => { @@ -693,10 +695,10 @@ export class OrganizationDotnetController extends Controller { } } } - let positionLeaveName = + let positionLeaveName = profile.posType == null && profile.posLevel == null - ? "" - : `${profile.posType?.posTypeShortName ?? ""} ${profile.posLevel?.posLevelName ?? ""}` + ? "" + : `${profile.posType?.posTypeShortName ?? ""} ${profile.posLevel?.posLevelName ?? ""}`; const _profileCurrent = profile?.current_holders?.find( (x) => @@ -4077,27 +4079,27 @@ export class OrganizationDotnetController extends Controller { // }; // } // } else { - if (body.node === 0) { - typeCondition = { - orgRootId: body.nodeId, - }; - } else if (body.node === 1) { - typeCondition = { - orgChild1Id: body.nodeId, - }; - } else if (body.node === 2) { - typeCondition = { - orgChild2Id: body.nodeId, - }; - } else if (body.node === 3) { - typeCondition = { - orgChild3Id: body.nodeId, - }; - } else if (body.node === 4) { - typeCondition = { - orgChild4Id: body.nodeId, - }; - } + if (body.node === 0) { + typeCondition = { + orgRootId: body.nodeId, + }; + } else if (body.node === 1) { + typeCondition = { + orgChild1Id: body.nodeId, + }; + } else if (body.node === 2) { + typeCondition = { + orgChild2Id: body.nodeId, + }; + } else if (body.node === 3) { + typeCondition = { + orgChild3Id: body.nodeId, + }; + } else if (body.node === 4) { + typeCondition = { + orgChild4Id: body.nodeId, + }; + } // } let profile = await this.profileRepo.find({ where: { keycloak: Not(IsNull()) || Not(""), isLeave: false, current_holders: typeCondition }, @@ -4123,10 +4125,7 @@ export class OrganizationDotnetController extends Controller { // isLeave: false, current_holders: typeCondition, // isRetirement: true, - dateRetire: And( - Not(IsNull()), - Between(startOfYear, endOfYear) - ) + dateRetire: And(Not(IsNull()), Between(startOfYear, endOfYear)), }, relations: [ "posType", @@ -4397,15 +4396,15 @@ export class OrganizationDotnetController extends Controller { }, ) { let findRevision = await this.orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false } + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); switch (body.node) { case 0: let orgRoot = await this.orgRootRepo.findOne({ - where: { + where: { id: body.nodeId, - orgRevisionId: findRevision?.id - } + orgRevisionId: findRevision?.id, + }, }); return new HttpSuccess({ rootName: orgRoot?.orgRootName ?? null, @@ -4418,10 +4417,10 @@ export class OrganizationDotnetController extends Controller { case 1: let orgChild1 = await this.orgChild1Repo.findOne({ relations: ["orgRoot"], - where: { + where: { id: body.nodeId, - orgRevisionId: findRevision?.id - } + orgRevisionId: findRevision?.id, + }, }); return new HttpSuccess({ rootName: orgChild1?.orgRoot.orgRootName ?? null, @@ -4434,10 +4433,10 @@ export class OrganizationDotnetController extends Controller { case 2: let orgChild2 = await this.orgChild2Repo.findOne({ relations: ["orgRoot", "orgChild1"], - where: { + where: { id: body.nodeId, - orgRevisionId: findRevision?.id - } + orgRevisionId: findRevision?.id, + }, }); return new HttpSuccess({ rootName: orgChild2?.orgRoot.orgRootName ?? null, @@ -4450,10 +4449,10 @@ export class OrganizationDotnetController extends Controller { case 3: let orgChild3 = await this.orgChild3Repo.findOne({ relations: ["orgRoot", "orgChild1", "orgChild2"], - where: { + where: { id: body.nodeId, - orgRevisionId: findRevision?.id - } + orgRevisionId: findRevision?.id, + }, }); return new HttpSuccess({ rootName: orgChild3?.orgRoot.orgRootName ?? null, @@ -4466,10 +4465,10 @@ export class OrganizationDotnetController extends Controller { case 4: let orgChild4 = await this.orgChild4Repo.findOne({ relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3"], - where: { + where: { id: body.nodeId, - orgRevisionId: findRevision?.id - } + orgRevisionId: findRevision?.id, + }, }); return new HttpSuccess({ rootName: orgChild4?.orgRoot.orgRootName ?? null, @@ -4486,7 +4485,7 @@ export class OrganizationDotnetController extends Controller { child2Name: null, child3Name: null, child4Name: null, - });; + }); } } @@ -4506,87 +4505,87 @@ export class OrganizationDotnetController extends Controller { role: string; isRetirement?: boolean; revisionId?: string; - reqNode?: number; + reqNode?: number; reqNodeId?: string; }, ) { let typeCondition: any = {}; - if (body.role === "CHILD") { + if (body.role === "CHILD" || body.role === "PARENT" || body.role === "ROOT") { switch (body.node) { case 0: typeCondition = { orgRoot: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; case 1: typeCondition = { orgChild1: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; case 2: typeCondition = { orgChild2: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; case 3: typeCondition = { orgChild3: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; case 4: typeCondition = { orgChild4: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; default: typeCondition = {}; break; } - } else if (body.role === "ROOT" || body.role === "OWNER") { + } else if (body.role === "OWNER") { switch (body.reqNode) { case 0: typeCondition = { orgRoot: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; case 1: typeCondition = { orgChild1: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; case 2: typeCondition = { orgChild2: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; case 3: typeCondition = { orgChild3: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; case 4: typeCondition = { orgChild4: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; default: @@ -4598,39 +4597,39 @@ export class OrganizationDotnetController extends Controller { case 0: typeCondition = { orgRoot: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, - orgChild1: IsNull() + orgChild1: IsNull(), }; break; case 1: typeCondition = { orgChild1: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, - orgChild2: IsNull() + orgChild2: IsNull(), }; break; case 2: typeCondition = { orgChild2: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, - orgChild3: IsNull() + orgChild3: IsNull(), }; break; case 3: typeCondition = { orgChild3: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, - orgChild4: IsNull() + orgChild4: IsNull(), }; break; case 4: typeCondition = { orgChild4: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, }; break; @@ -4670,8 +4669,8 @@ export class OrganizationDotnetController extends Controller { orgChild4Order: "ASC", }, posMasterNo: "ASC", - } - } + }, + }, }); if (body.isRetirement) { profile = await this.profileRepo.find({ @@ -4693,7 +4692,7 @@ export class OrganizationDotnetController extends Controller { }); } let findRevision = await this.orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false } + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); if (body.revisionId) { @@ -4703,7 +4702,7 @@ export class OrganizationDotnetController extends Controller { } const profile_ = await Promise.all( - profile.map(async(item: Profile) => { + profile.map(async (item: Profile) => { const shortName = item.current_holders.length == 0 ? null @@ -4747,8 +4746,8 @@ export class OrganizationDotnetController extends Controller { let _posMaster = await this.posMasterRepository.findOne({ where: { orgRevisionId: findRevision?.id, - current_holderId: item.id - } + current_holderId: item.id, + }, }); return { @@ -4769,7 +4768,7 @@ export class OrganizationDotnetController extends Controller { orgChild1Id: _posMaster?.orgChild1Id, orgChild2Id: _posMaster?.orgChild2Id, orgChild3Id: _posMaster?.orgChild3Id, - orgChild4Id: _posMaster?.orgChild4Id + orgChild4Id: _posMaster?.orgChild4Id, }; }), ); @@ -5101,46 +5100,46 @@ export class OrganizationDotnetController extends Controller { role: string; isRetirement?: boolean; revisionId?: string; - reqNode?: number; + reqNode?: number; reqNodeId?: string; }, ) { let typeCondition: any = {}; - if (body.role === "CHILD") { + if (body.role === "CHILD" || body.role === "PARENT" || body.role === "ROOT") { switch (body.node) { case 0: typeCondition = { orgRoot: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; case 1: typeCondition = { orgChild1: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; case 2: typeCondition = { orgChild2: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; case 3: typeCondition = { orgChild3: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; case 4: typeCondition = { orgChild4: { - ancestorDNA: body.nodeId - } + ancestorDNA: body.nodeId, + }, }; break; case null: @@ -5150,41 +5149,41 @@ export class OrganizationDotnetController extends Controller { typeCondition = {}; break; } - } else if (body.role === "ROOT" || body.role === "OWNER") { + } else if (body.role === "OWNER") { switch (body.reqNode) { case 0: typeCondition = { orgRoot: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; case 1: typeCondition = { orgChild1: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; case 2: typeCondition = { orgChild2: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; case 3: typeCondition = { orgChild3: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; case 4: typeCondition = { orgChild4: { - id: body.reqNodeId - } + id: body.reqNodeId, + }, }; break; default: @@ -5196,39 +5195,39 @@ export class OrganizationDotnetController extends Controller { case 0: typeCondition = { orgRoot: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, - orgChild1: IsNull() + orgChild1: IsNull(), }; break; case 1: typeCondition = { orgChild1: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, - orgChild2: IsNull() + orgChild2: IsNull(), }; break; case 2: typeCondition = { orgChild2: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, - orgChild3: IsNull() + orgChild3: IsNull(), }; break; case 3: typeCondition = { orgChild3: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, - orgChild4: IsNull() + orgChild4: IsNull(), }; break; case 4: typeCondition = { orgChild4: { - ancestorDNA: body.nodeId + ancestorDNA: body.nodeId, }, }; break; @@ -5268,8 +5267,8 @@ export class OrganizationDotnetController extends Controller { orgChild4Order: "ASC", }, posMasterNo: "ASC", - } - } + }, + }, }); if (body.isRetirement) { profile = await this.profileEmpRepo.find({ @@ -5291,7 +5290,7 @@ export class OrganizationDotnetController extends Controller { }); } let findRevision = await this.orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false } + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); if (body.revisionId) { @@ -5301,7 +5300,7 @@ export class OrganizationDotnetController extends Controller { } const profile_ = await Promise.all( - profile.map(async(item: ProfileEmployee) => { + profile.map(async (item: ProfileEmployee) => { const shortName = item.current_holders.length == 0 ? null @@ -5345,8 +5344,8 @@ export class OrganizationDotnetController extends Controller { let _posMaster = await this.empPosMasterRepository.findOne({ where: { orgRevisionId: findRevision?.id, - current_holderId: item.id - } + current_holderId: item.id, + }, }); return { @@ -5360,9 +5359,10 @@ export class OrganizationDotnetController extends Controller { keycloak: item.keycloak, posNo: shortName, position: item.position, - positionLevel: item.posType?.posTypeShortName && item.posLevel?.posLevelName - ? `${item.posType?.posTypeShortName} ${item.posLevel?.posLevelName}` - : null, + positionLevel: + item.posType?.posTypeShortName && item.posLevel?.posLevelName + ? `${item.posType?.posTypeShortName} ${item.posLevel?.posLevelName}` + : null, positionType: item.posType?.posTypeName ?? null, oc: Oc, orgRootId: _posMaster?.orgRootId, @@ -5580,7 +5580,7 @@ export class OrganizationDotnetController extends Controller { } const position = await AppDataSource.query("CALL GetProfileEmployeeSalaryPosition(?, ?)", [ profile.id, - _currentDate + _currentDate, ]); const _position = position.length > 0 ? position[0] : []; @@ -5692,11 +5692,13 @@ export class OrganizationDotnetController extends Controller { } } if (profile && profile?.isLeave) { - _currentDate = profile && profile.leaveDate - ? Extension.toDateOnlyString(profile.leaveDate) - : _currentDate + _currentDate = + profile && profile.leaveDate ? Extension.toDateOnlyString(profile.leaveDate) : _currentDate; } - const position = await AppDataSource.query("CALL GetProfileSalaryPosition(?, ?)", [profile.id, _currentDate]); + const position = await AppDataSource.query("CALL GetProfileSalaryPosition(?, ?)", [ + profile.id, + _currentDate, + ]); const _position = position.length > 0 ? position[0] : []; const mapProfile = { diff --git a/src/controllers/PermissionController.ts b/src/controllers/PermissionController.ts index 72b7d671..54dea336 100644 --- a/src/controllers/PermissionController.ts +++ b/src/controllers/PermissionController.ts @@ -732,6 +732,14 @@ export class PermissionController extends Controller { child3: null, child4: null, }; + } else if (privilege == "PARENT") { + data = { + root: [x.orgRootId], + child1: [x.orgChild1Id], + child2: null, + child3: null, + child4: null, + }; } else if (privilege == "CHILD") { data = { root: node >= 0 ? [x.orgRootId] : null, diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 00af5b10..09a83c57 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -911,6 +911,7 @@ export class KeycloakController extends Controller { // keycloak: IsNull(), isLeave: false, }, + order: { citizenId: "ASC" }, relations: ["roleKeycloaks"], }); @@ -924,12 +925,11 @@ export class KeycloakController extends Controller { }); // Process แบบ batch เพื่อลดการเรียก API ทีละตัว - const batchSize = 10; + const batchSize = 100; const batches = []; for (let i = 0; i < profiles.length; i += batchSize) { batches.push(profiles.slice(i, i + batchSize)); } - for (const batch of batches) { await Promise.all( batch.map(async (_item) => { diff --git a/src/interfaces/permission.ts b/src/interfaces/permission.ts index cfe76e6d..1fb12f00 100644 --- a/src/interfaces/permission.ts +++ b/src/interfaces/permission.ts @@ -94,6 +94,15 @@ class CheckAuth { child4: null, privilege: "ROOT", }; + } else if (privilege == "PARENT") { + data = { + root: [x.orgRootId], + child1: [x.orgChild1Id], + child2: null, + child3: null, + child4: null, + privilege: "PARENT", + }; } else if (privilege == "CHILD") { data = { root: node >= 0 ? [x.orgRootId] : null, diff --git a/src/keycloak/index.ts b/src/keycloak/index.ts index ec0cd246..c14a6b62 100644 --- a/src/keycloak/index.ts +++ b/src/keycloak/index.ts @@ -56,7 +56,7 @@ export async function getToken() { if (data && data.access_token) { token = data.access_token; } - + console.log(`token: ${token}`); return token; } From 564d7d96c3de5ff82e5b3e95de87aaf88cf7fac7 Mon Sep 17 00:00:00 2001 From: AdisakKanthawilang <153157069+AdisakKanthawilang@users.noreply.github.com> Date: Tue, 7 Oct 2025 14:59:11 +0700 Subject: [PATCH 43/50] #176 (#185) * #176 * delete log --- src/controllers/UserController.ts | 104 +++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 30 deletions(-) diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 00af5b10..38daaeae 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -470,15 +470,21 @@ export class KeycloakController extends Controller { throw new Error("Failed. Cannot get user list."); }*/ - @Get("user") + @Post("user/admin") async listUserKeycloak( - @Query("page") page: number = 1, - @Query("pageSize") pageSize: number = 10, - @Query() keyword: string = "", - @Query() type: string = "", - @Request() req: RequestWithUser, + @Request() req: RequestWithUser, + @Body() + body:{ + page: number, + pageSize: number, + keyword: string | null, + type: string, + isAll: boolean, + node: number | null, + nodeId: string | null, + } ) { - let condition: any = {}; + let checkChildFromRole: any = {}; if (req.user.role.includes("ADMIN") && !req.user.role.includes("SUPER_ADMIN")) { const profile = await this.profileRepo.findOne({ @@ -498,7 +504,7 @@ export class KeycloakController extends Controller { profile?.current_holders[0]?.orgRootId && profile?.current_holders[0]?.orgChild1Id == null ) { - condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' + checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id IS NULL and current_holders.orgChild2Id IS NULL and current_holders.orgChild3Id IS NULL @@ -507,7 +513,7 @@ export class KeycloakController extends Controller { profile?.current_holders[0]?.orgChild1Id && profile?.current_holders[0]?.orgChild2Id == null ) { - condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' + checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' and current_holders.orgChild2Id IS NULL and current_holders.orgChild3Id IS NULL @@ -516,7 +522,7 @@ export class KeycloakController extends Controller { profile?.current_holders[0]?.orgChild2Id && profile?.current_holders[0]?.orgChild3Id == null ) { - condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' + checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' and current_holders.orgChild2Id = '${profile?.current_holders[0]?.orgChild2Id}' and current_holders.orgChild3Id IS NULL @@ -525,77 +531,115 @@ export class KeycloakController extends Controller { profile?.current_holders[0]?.orgChild3Id && profile?.current_holders[0]?.orgChild4Id == null ) { - condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' + checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' and current_holders.orgChild2Id = '${profile?.current_holders[0]?.orgChild2Id}' and current_holders.orgChild3Id = '${profile?.current_holders[0]?.orgChild3Id}' and current_holders.orgChild4Id IS NULL`; } else if (profile?.current_holders[0]?.orgChild4Id) { - condition = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' + checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' and current_holders.orgChild2Id = '${profile?.current_holders[0]?.orgChild2Id}' and current_holders.orgChild3Id = '${profile?.current_holders[0]?.orgChild3Id}' and current_holders.orgChild4Id = '${profile?.current_holders[0]?.orgChild4Id}'`; } } + + let typeCondition = ""; + let checkChildConditions = ""; + let conditions:any = {}; + + if(body.nodeId !== null && body.nodeId !== "" && body.nodeId !== undefined){ + if (body.node === 0) { + typeCondition = `current_holders.orgRootId = '${body.nodeId}'`; + if (!body.isAll) { + checkChildConditions = `and current_holders.orgChild1Id IS NULL`; + } + } else if (body.node === 1) { + typeCondition = `current_holders.orgChild1Id = '${body.nodeId}'`; + if (!body.isAll) { + checkChildConditions = `and current_holders.orgChild2Id IS NULL`; + } + } else if (body.node === 2) { + typeCondition = `current_holders.orgChild2Id = '${body.nodeId}'`; + if (!body.isAll) { + checkChildConditions = `and current_holders.orgChild3Id IS NULL`; + } + } else if (body.node === 3) { + typeCondition = `current_holders.orgChild3Id = '${body.nodeId}'`; + if (!body.isAll) { + checkChildConditions = `and current_holders.orgChild4Id IS NULL`; + } + } else if (body.node === 4) { + typeCondition = `current_holders.orgChild4Id = '${body.nodeId}'`; + } + + conditions = typeCondition; + if(checkChildConditions){ + conditions += checkChildConditions; + } + } + let profiles: any = []; let total: any; - if (type.trim().toUpperCase() == "OFFICER") { + if (body.type.trim().toUpperCase() == "OFFICER") { [profiles, total] = await this.profileRepo .createQueryBuilder("profile") .leftJoinAndSelect("profile.roleKeycloaks", "roleKeycloaks") .leftJoinAndSelect("profile.current_holders", "current_holders") .where("profile.keycloak IS NOT NULL AND profile.keycloak != ''") - .andWhere(condition) + .andWhere(checkChildFromRole) + .andWhere(conditions) .andWhere( new Brackets((qb) => { qb.orWhere( - keyword != null && keyword != "" ? `profile.citizenId like '%${keyword}%'` : "1=1", + body.keyword != null && body.keyword != "" ? `profile.citizenId like '%${body.keyword}%'` : "1=1", ) .orWhere( - keyword != null && keyword != "" ? `profile.email like '%${keyword}%'` : "1=1", + body.keyword != null && body.keyword != "" ? `profile.email like '%${body.keyword}%'` : "1=1", ) .orWhere( - keyword != null && keyword != "" - ? `CONCAT(profile.prefix, profile.firstName," ",profile.lastName) like '%${keyword}%'` + body.keyword != null && body.keyword != "" + ? `CONCAT(profile.prefix, profile.firstName," ",profile.lastName) like '%${body.keyword}%'` : "1=1", ); }), ) .orderBy("profile.citizenId", "ASC") - .skip((page - 1) * pageSize) - .take(pageSize) + .skip((body.page - 1) * body.pageSize) + .take(body.pageSize) .getManyAndCount(); - } else if (type.trim().toUpperCase() == "EMPLOYEE") { + } else if (body.type.trim().toUpperCase() == "EMPLOYEE") { [profiles, total] = await this.profileEmpRepo .createQueryBuilder("profileEmployee") .leftJoinAndSelect("profileEmployee.roleKeycloaks", "roleKeycloaks") .leftJoinAndSelect("profileEmployee.current_holders", "current_holders") .where("profileEmployee.keycloak IS NOT NULL AND profileEmployee.keycloak != ''") - .andWhere(condition) + .andWhere(checkChildFromRole) + .andWhere(conditions) .andWhere({ employeeClass: "PERM" }) .andWhere( new Brackets((qb) => { qb.orWhere( - keyword != null && keyword != "" - ? `profileEmployee.citizenId like '%${keyword}%'` + body.keyword != null && body.keyword != "" + ? `profileEmployee.citizenId like '%${body.keyword}%'` : "1=1", ) .orWhere( - keyword != null && keyword != "" - ? `profileEmployee.email like '%${keyword}%'` + body.keyword != null && body.keyword != "" + ? `profileEmployee.email like '%${body.keyword}%'` : "1=1", ) .orWhere( - keyword != null && keyword != "" - ? `CONCAT(profileEmployee.prefix, profileEmployee.firstName," ",profileEmployee.lastName) like '%${keyword}%'` + body.keyword != null && body.keyword != "" + ? `CONCAT(profileEmployee.prefix, profileEmployee.firstName," ",profileEmployee.lastName) like '%${body.keyword}%'` : "1=1", ); }), ) .orderBy("profileEmployee.citizenId", "ASC") - .skip((page - 1) * pageSize) - .take(pageSize) + .skip((body.page - 1) * body.pageSize) + .take(body.pageSize) .getManyAndCount(); } From e0e850b4c782d5b6f3a5c67fcffb7313c516168a Mon Sep 17 00:00:00 2001 From: AdisakKanthawilang <153157069+AdisakKanthawilang@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:18:54 +0700 Subject: [PATCH 44/50] =?UTF-8?q?=E0=B8=9B=E0=B8=A3=E0=B8=B1=E0=B8=9A?= =?UTF-8?q?=E0=B9=80=E0=B8=87=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=99=E0=B9=84?= =?UTF-8?q?=E0=B8=82=20admin=20=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B9=80?= =?UTF-8?q?=E0=B8=AB=E0=B9=87=E0=B8=99=E0=B8=82=E0=B9=89=E0=B8=AD=E0=B8=A1?= =?UTF-8?q?=E0=B8=B9=E0=B8=A5=E0=B8=97=E0=B8=B1=E0=B9=89=E0=B8=87=E0=B8=AA?= =?UTF-8?q?=E0=B8=B1=E0=B8=87=E0=B8=81=E0=B8=B1=E0=B8=94=20(#186)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/UserController.ts | 46 ++----------------------------- 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 38daaeae..4f90afeb 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -499,49 +499,9 @@ export class KeycloakController extends Controller { }, }, }); - - if ( - profile?.current_holders[0]?.orgRootId && - profile?.current_holders[0]?.orgChild1Id == null - ) { - checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' - and current_holders.orgChild1Id IS NULL - and current_holders.orgChild2Id IS NULL - and current_holders.orgChild3Id IS NULL - and current_holders.orgChild4Id IS NULL`; - } else if ( - profile?.current_holders[0]?.orgChild1Id && - profile?.current_holders[0]?.orgChild2Id == null - ) { - checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' - and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' - and current_holders.orgChild2Id IS NULL - and current_holders.orgChild3Id IS NULL - and current_holders.orgChild4Id IS NULL`; - } else if ( - profile?.current_holders[0]?.orgChild2Id && - profile?.current_holders[0]?.orgChild3Id == null - ) { - checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' - and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' - and current_holders.orgChild2Id = '${profile?.current_holders[0]?.orgChild2Id}' - and current_holders.orgChild3Id IS NULL - and current_holders.orgChild4Id IS NULL`; - } else if ( - profile?.current_holders[0]?.orgChild3Id && - profile?.current_holders[0]?.orgChild4Id == null - ) { - checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' - and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' - and current_holders.orgChild2Id = '${profile?.current_holders[0]?.orgChild2Id}' - and current_holders.orgChild3Id = '${profile?.current_holders[0]?.orgChild3Id}' - and current_holders.orgChild4Id IS NULL`; - } else if (profile?.current_holders[0]?.orgChild4Id) { - checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}' - and current_holders.orgChild1Id = '${profile?.current_holders[0]?.orgChild1Id}' - and current_holders.orgChild2Id = '${profile?.current_holders[0]?.orgChild2Id}' - and current_holders.orgChild3Id = '${profile?.current_holders[0]?.orgChild3Id}' - and current_holders.orgChild4Id = '${profile?.current_holders[0]?.orgChild4Id}'`; + //ADMIN เห็นทั้งหน่วยงานที่สังกัด + if (profile?.current_holders[0]?.orgRootId) { + checkChildFromRole = `current_holders.orgRootId = '${profile?.current_holders[0]?.orgRootId}'`; } } From 6328de1a5c2c92aa3014fab6e1719fdd753c3466 Mon Sep 17 00:00:00 2001 From: Kittapath <67353855+M-FTP@users.noreply.github.com> Date: Wed, 8 Oct 2025 23:03:02 +0700 Subject: [PATCH 45/50] remove role add relation (#191) Co-authored-by: mamoss <> --- src/controllers/CommandController.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 563ec950..22ebd41d 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1519,7 +1519,10 @@ export class CommandController extends Controller { await Promise.all( response_.data.result.profiles.map(async (x: any) => { - const _profile = await this.profileRepository.findOneBy({ id: x.profileId }); + const _profile = await this.profileRepository.findOne({ + where: { id: x.profileId }, + relations: ["roleKeycloaks"], + }); if (_profile) { // console.log("1. บันทึกลงประวัติตำแหน่ง") await this.profileSalaryRetire(x.profileId, type, signDate, _Date); @@ -1572,7 +1575,10 @@ export class CommandController extends Controller { await Promise.all( response_.data.result.profiles.map(async (x: any) => { - const _profileEmp = await this.profileEmployeeRepository.findOneBy({ id: x.profileId }); + const _profileEmp = await this.profileEmployeeRepository.findOne({ + where: { id: x.profileId }, + relations: ["roleKeycloaks"], + }); if (_profileEmp) { // บันทึกลงประวัติตำแหน่ง await this.profileSalaryRetire(x.profileId, type, signDate, _Date); @@ -4694,6 +4700,7 @@ export class CommandController extends Controller { if (item.isLeave != null) { const _profile = await this.profileRepository.findOne({ where: { id: item.profileId }, + relations: ["roleKeycloaks"], }); if (!_profile) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); @@ -4896,6 +4903,7 @@ export class CommandController extends Controller { if (item.isLeave != null) { const _profile = await this.profileEmployeeRepository.findOne({ where: { id: item.profileId }, + relations: ["roleKeycloaks"], }); if (!_profile) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); @@ -5188,6 +5196,7 @@ export class CommandController extends Controller { if (item.isLeave != null) { const _profile = await this.profileEmployeeRepository.findOne({ where: { id: item.profileId }, + relations: ["roleKeycloaks"], }); if (!_profile) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); @@ -5889,7 +5898,7 @@ export class CommandController extends Controller { //กรณีลูกจ้างประจำมาสอบเป็นข้าราชการ ต้อง update สถานะโปรไฟล์เดิม let profileEmployee: any = await this.profileEmployeeRepository.findOne({ where: { citizenId: item.bodyProfile.citizenId }, - relations: ["profileInsignias"], + relations: ["profileInsignias", "roleKeycloaks"], }); if (profileEmployee) { const _order = await this.salaryRepo.findOne({ From c500ff4b7ad9d1d6232de60c212fe0935a65fcfd Mon Sep 17 00:00:00 2001 From: harid Date: Thu, 9 Oct 2025 14:49:55 +0700 Subject: [PATCH 46/50] =?UTF-8?q?fix=20#1836=20=E0=B8=9B=E0=B8=A3=E0=B8=B1?= =?UTF-8?q?=E0=B8=9A=E0=B9=83=E0=B8=AB=E0=B9=89=E0=B9=81=E0=B8=AA=E0=B8=94?= =?UTF-8?q?=E0=B8=87=E0=B8=95=E0=B8=B2=E0=B8=A1=E0=B8=95=E0=B8=B3=E0=B9=81?= =?UTF-8?q?=E0=B8=AB=E0=B8=99=E0=B9=88=E0=B8=87=E0=B8=97=E0=B8=B5=E0=B9=88?= =?UTF-8?q?=E0=B8=9A=E0=B8=A3=E0=B8=A3=E0=B8=88=E0=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/CommandController.ts | 32 +++++++++------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 22ebd41d..2c18fd52 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1954,20 +1954,13 @@ export class CommandController extends Controller { const orgLeave = _OrgLeave.filter((x: any) => x !== undefined && x !== null).join("\n"); return { no: Extension.ToThaiNumber((idx + 1).toString()), - org: - commandCode != "C-PM-21" - ? profile?.isLeave == false - ? (_child4 == null ? "" : _child4 + "\n") + - (_child3 == null ? "" : _child3 + "\n") + - (_child2 == null ? "" : _child2 + "\n") + - (_child1 == null ? "" : _child1 + "\n") + - (_root == null ? "" : _root) - : orgLeave - : (profile?.child4Temp == null ? "" : profile?.child4Temp + "\n") + - (profile?.child3Temp == null ? "" : profile?.child3Temp + "\n") + - (profile?.child2Temp == null ? "" : profile?.child2Temp + "\n") + - (profile?.child1Temp == null ? "" : profile?.child1Temp + "\n") + - (profile?.rootTemp == null ? "" : profile?.rootTemp), + org: profile?.isLeave == false + ? (_child4 == null ? "" : _child4 + "\n") + + (_child3 == null ? "" : _child3 + "\n") + + (_child2 == null ? "" : _child2 + "\n") + + (_child1 == null ? "" : _child1 + "\n") + + (_root == null ? "" : _root) + : orgLeave, fullName: `${x.prefix}${x.firstName} ${x.lastName}`, citizenId: Extension.ToThaiNumber(x.citizenId), position: profile?.position ? profile?.position : "-", @@ -1977,14 +1970,9 @@ export class CommandController extends Controller { `${profile?.posType.posTypeShortName} ${profile?.posLevel.posLevelName}`, ) : "-", - posNo: - commandCode != "C-PM-21" - ? shortName - ? Extension.ToThaiNumber(shortName) - : "-" - : Extension.ToThaiNumber( - `${profile?.rootShortNameTemp} ${profile?.posMasterNoTemp}`, - ), + posNo: shortName + ? Extension.ToThaiNumber(shortName) + : "-", amount: x.amount ? Extension.ToThaiNumber(x.amount.toLocaleString()) : "-", dateRetire: profile?.dateRetire ? Extension.ToThaiNumber(Extension.ToThaiShortDate_monthYear(profile?.dateRetire)) From 847d9c302fa88ee112707a09d56b893d7e16a3ac Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Thu, 9 Oct 2025 23:24:04 +0700 Subject: [PATCH 47/50] =?UTF-8?q?cdh=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=84?= =?UTF-8?q?=E0=B8=B4=E0=B8=A7=E0=B8=A3=E0=B8=B5=E0=B9=88=E0=B9=83=E0=B8=AB?= =?UTF-8?q?=E0=B9=89=E0=B9=80=E0=B8=A3=E0=B9=87=E0=B8=A7=E0=B8=82=E0=B8=B6?= =?UTF-8?q?=E0=B9=89=E0=B8=99=20=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OrganizationDotnetController.ts | 263 ++++++++---------- src/controllers/WorkflowController.ts | 243 ++++++++-------- 2 files changed, 241 insertions(+), 265 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index db161fc7..cd4bad67 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -73,29 +73,10 @@ export class OrganizationDotnetController extends Controller { role?: string | null; nodeId?: string | null; node?: number | null; + page: number; + pageSize: number; }, ) { - // const profileRepository = AppDataSource.getRepository(Profile); - // const queryBuilder = profileRepository - // .createQueryBuilder("profile") - // .leftJoinAndSelect("profile.posLevel", "posLevel") - // .leftJoinAndSelect("profile.posType", "posType") - // if (body.citizenId || body.firstName || body.lastName) { - // queryBuilder.where( - // new Brackets((qb) => { - // if (body.citizenId) { - // qb.orWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); - // } - // if (body.firstName) { - // qb.orWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); - // } - // if (body.lastName) { - // qb.orWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); - // } - // }), - // ); - // } - // const profiles = await queryBuilder.getMany(); let condition = "1=1"; let conditionParams = {}; if (body.role === "CHILD") { @@ -156,7 +137,7 @@ export class OrganizationDotnetController extends Controller { if (!findRevision) { throw new HttpError(HttpStatus.NOT_FOUND, "not found. OrgRevision"); } - const profiles = await this.profileRepo + const [profiles, total] = await this.profileRepo .createQueryBuilder("profile") .leftJoinAndSelect("profile.posLevel", "posLevel") .leftJoinAndSelect("profile.posType", "posType") @@ -170,20 +151,22 @@ export class OrganizationDotnetController extends Controller { .andWhere( new Brackets((qb) => { if (body.citizenId) { - qb.orWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); + qb.andWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); } if (body.firstName) { - qb.orWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); + qb.andWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); } if (body.lastName) { - qb.orWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); + qb.andWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); } }), ) .andWhere(condition, conditionParams) - .getMany(); + .skip((body.page - 1) * body.pageSize) + .take(body.pageSize) + .getManyAndCount(); - return new HttpSuccess(profiles); + return new HttpSuccess({ data: profiles, total: total }); } /** @@ -202,40 +185,10 @@ export class OrganizationDotnetController extends Controller { role?: string | null; nodeId?: string | null; node?: number | null; + page: number; + pageSize: number; }, ) { - // const profileRepository = AppDataSource.getRepository(ProfileEmployee); - // const queryBuilder = profileRepository - // .createQueryBuilder("profile") - // .leftJoinAndSelect("profile.posLevel", "posLevel") - // .leftJoinAndSelect("profile.posType", "posType") - // .leftJoinAndSelect("profile.profileSalary", "profileSalary") - // .leftJoinAndSelect("profile.current_holders", "current_holders") - // .leftJoinAndSelect("current_holders.orgRoot", "orgRoot") - // .leftJoinAndSelect("current_holders.orgChild1", "orgChild1") - // .leftJoinAndSelect("current_holders.orgChild2", "orgChild2") - // .leftJoinAndSelect("current_holders.orgChild3", "orgChild3") - // .leftJoinAndSelect("current_holders.orgChild4", "orgChild4") - // .orderBy("profileSalary.order", "DESC"); - - // if (body.citizenId || body.firstName || body.lastName) { - // queryBuilder.where( - // new Brackets((qb) => { - // if (body.citizenId) { - // qb.orWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); - // } - // if (body.firstName) { - // qb.orWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); - // } - // if (body.lastName) { - // qb.orWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); - // } - // }), - // ); - // } - - // const profileEmp = await queryBuilder.getMany(); - let condition = "1=1"; let conditionParams = {}; if (body.role === "CHILD") { @@ -296,7 +249,7 @@ export class OrganizationDotnetController extends Controller { if (!findRevision) { throw new HttpError(HttpStatus.NOT_FOUND, "not found. OrgRevision"); } - const profileEmp = await this.profileEmpRepo + const [profileEmp, total] = await this.profileEmpRepo .createQueryBuilder("profile") .leftJoinAndSelect("profile.posLevel", "posLevel") .leftJoinAndSelect("profile.posType", "posType") @@ -311,110 +264,112 @@ export class OrganizationDotnetController extends Controller { .andWhere( new Brackets((qb) => { if (body.citizenId) { - qb.orWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); + qb.andWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); } if (body.firstName) { - qb.orWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); + qb.andWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); } if (body.lastName) { - qb.orWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); + qb.andWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); } }), ) .andWhere(condition, conditionParams) .orderBy("profileSalary.order", "DESC") - .getMany(); + .skip((body.page - 1) * body.pageSize) + .take(body.pageSize) + .getManyAndCount(); - const profileEmp_ = await Promise.all( - profileEmp.map((item: ProfileEmployee) => { - const rootName = - item.current_holders.length == 0 - ? null - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot - ?.orgRootName; - const shortName = - item.current_holders.length == 0 - ? null - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4 != - null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - ?.orgChild3 != null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - ?.orgChild2 != null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - ?.orgChild1 != null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != - null && - item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - ?.orgRoot != null - ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - : null; - return { - oc: rootName, - id: item.id, - createdAt: item.createdAt, - createdUserId: item.createdUserId, - lastUpdatedAt: item.lastUpdatedAt, - lastUpdateUserId: item.lastUpdateUserId, - createdFullName: item.createdFullName, - lastUpdateFullName: item.lastUpdateFullName, - avatar: item.avatar, - avatarName: item.avatarName, - rank: item.rank, - prefix: item.prefix, - firstName: item.firstName, - lastName: item.lastName, - citizenId: item.citizenId, - position: item.position, - posLevelId: item.posLevelId, - posTypeId: item.posTypeId, - email: item.email, - phone: item.phone, - keycloak: item.keycloak, - isProbation: item.isProbation, - isLeave: item.isLeave, - leaveReason: item.leaveReason, - dateLeave: item.dateLeave, - dateRetire: item.dateRetire, - dateAppoint: item.dateAppoint, - dateRetireLaw: item.dateRetireLaw, - dateStart: item.dateStart, - govAgeAbsent: item.govAgeAbsent, - govAgePlus: item.govAgePlus, - birthDate: item.birthDate ?? new Date(), - reasonSameDate: item.reasonSameDate, - ethnicity: item.ethnicity, - telephoneNumber: item.phone, - nationality: item.nationality, - gender: item.gender, - relationship: item.relationship, - religion: item.religion, - bloodGroup: item.bloodGroup, - registrationAddress: item.registrationAddress, - registrationProvinceId: item.registrationProvinceId, - registrationDistrictId: item.registrationDistrictId, - registrationSubDistrictId: item.registrationSubDistrictId, - registrationZipCode: item.registrationZipCode, - currentAddress: item.currentAddress, - currentProvinceId: item.currentProvinceId, - currentDistrictId: item.currentDistrictId, - currentSubDistrictId: item.currentSubDistrictId, - currentZipCode: item.currentZipCode, - posLevel: item.posLevel, - posType: item.posType, - posNo: shortName, - }; - }), - ); - return new HttpSuccess(profileEmp_); + // const profileEmp_ = await Promise.all( + // profileEmp.map((item: ProfileEmployee) => { + // const rootName = + // item.current_holders.length == 0 + // ? null + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot + // ?.orgRootName; + // const shortName = + // item.current_holders.length == 0 + // ? null + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4 != + // null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) + // ?.orgChild3 != null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) + // ?.orgChild2 != null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) + // ?.orgChild1 != null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != + // null && + // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) + // ?.orgRoot != null + // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` + // : null; + // return { + // oc: rootName, + // id: item.id, + // createdAt: item.createdAt, + // createdUserId: item.createdUserId, + // lastUpdatedAt: item.lastUpdatedAt, + // lastUpdateUserId: item.lastUpdateUserId, + // createdFullName: item.createdFullName, + // lastUpdateFullName: item.lastUpdateFullName, + // avatar: item.avatar, + // avatarName: item.avatarName, + // rank: item.rank, + // prefix: item.prefix, + // firstName: item.firstName, + // lastName: item.lastName, + // citizenId: item.citizenId, + // position: item.position, + // posLevelId: item.posLevelId, + // posTypeId: item.posTypeId, + // email: item.email, + // phone: item.phone, + // keycloak: item.keycloak, + // isProbation: item.isProbation, + // isLeave: item.isLeave, + // leaveReason: item.leaveReason, + // dateLeave: item.dateLeave, + // dateRetire: item.dateRetire, + // dateAppoint: item.dateAppoint, + // dateRetireLaw: item.dateRetireLaw, + // dateStart: item.dateStart, + // govAgeAbsent: item.govAgeAbsent, + // govAgePlus: item.govAgePlus, + // birthDate: item.birthDate ?? new Date(), + // reasonSameDate: item.reasonSameDate, + // ethnicity: item.ethnicity, + // telephoneNumber: item.phone, + // nationality: item.nationality, + // gender: item.gender, + // relationship: item.relationship, + // religion: item.religion, + // bloodGroup: item.bloodGroup, + // registrationAddress: item.registrationAddress, + // registrationProvinceId: item.registrationProvinceId, + // registrationDistrictId: item.registrationDistrictId, + // registrationSubDistrictId: item.registrationSubDistrictId, + // registrationZipCode: item.registrationZipCode, + // currentAddress: item.currentAddress, + // currentProvinceId: item.currentProvinceId, + // currentDistrictId: item.currentDistrictId, + // currentSubDistrictId: item.currentSubDistrictId, + // currentZipCode: item.currentZipCode, + // posLevel: item.posLevel, + // posType: item.posType, + // posNo: shortName, + // }; + // }), + // ); + return new HttpSuccess({ data: profileEmp, total: total }); } /** diff --git a/src/controllers/WorkflowController.ts b/src/controllers/WorkflowController.ts index 0938b267..e12cdd0a 100644 --- a/src/controllers/WorkflowController.ts +++ b/src/controllers/WorkflowController.ts @@ -55,29 +55,35 @@ export class WorkflowController extends Controller { fullName?: string | null; }, ) { + // ขั้นที่ 1: ทำการค้นหา profile และ metaWorkflow แบบ parallel + const [userProfileOfficer, userProfileEmployee, metaWorkflow] = await Promise.all([ + this.profileRepo.findOne({ + where: { keycloak: req.user.sub }, + select: ["id", "keycloak"], + }), + this.profileEmployeeRepo.findOne({ + where: { keycloak: req.user.sub }, + select: ["id", "keycloak"], + }), + this.metaWorkflowRepo.findOne({ + where: { + sysName: body.sysName, + posLevelName: body.posLevelName, + posTypeName: body.posTypeName, + }, + }), + ]); + + // กำหนด profile type และ profile let profileType = "OFFICER"; - let profile: any = await this.profileRepo.findOne({ - where: { - keycloak: req.user.sub, - }, - }); + let profile: any = userProfileOfficer; + if (!profile) { profileType = "EMPLOYEE"; - profile = await this.profileEmployeeRepo.findOne({ - where: { - keycloak: req.user.sub, - }, - }); + profile = userProfileEmployee; if (!profile) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งาน"); } - const metaWorkflow = await this.metaWorkflowRepo.findOne({ - where: { - sysName: body.sysName, - posLevelName: body.posLevelName, - posTypeName: body.posTypeName, - }, - }); if (!metaWorkflow) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบกระบวนการนี้ได้"); const meta = { @@ -88,6 +94,8 @@ export class WorkflowController extends Controller { createdAt: new Date(), lastUpdatedAt: new Date(), }; + + // ขั้นที่ 2: สร้าง workflow และดึง metaState แบบ parallel const workflow = new Workflow(); Object.assign(workflow, { ...metaWorkflow, @@ -97,117 +105,130 @@ export class WorkflowController extends Controller { profileType: profileType, system: body.sysName, }); - await this.workflowRepo.save(workflow); - const metaState = await this.metaStateRepo.find({ - where: { - metaWorkflowId: metaWorkflow.id, - }, - order: { order: "ASC" }, + + const [savedWorkflow, metaStates] = await Promise.all([ + this.workflowRepo.save(workflow), + this.metaStateRepo.find({ + where: { metaWorkflowId: metaWorkflow.id }, + order: { order: "ASC" }, + }), + ]); + + // ขั้นที่ 3: สร้าง states ทั้งหมดในครั้งเดียว + const statesToCreate = metaStates.map((item) => { + const state = new State(); + Object.assign(state, { ...item, id: undefined, workflowId: savedWorkflow.id, ...meta }); + return state; }); - await Promise.all( - metaState.map(async (item) => { - const state = new State(); - Object.assign(state, { ...item, id: undefined, workflowId: workflow.id, ...meta }); - await this.stateRepo.save(state); - if (state.order == 1) { - workflow.stateId = state.id; - await this.workflowRepo.save(workflow); - } - const metaStateOperator = await this.metaStateOperatorRepo.find({ - where: { - metaStateId: item.id, - }, + const savedStates = await this.stateRepo.save(statesToCreate); + + // ขั้นที่ 4: อัปเดต workflow.stateId กับ state แรก + const firstState = savedStates.find((state) => state.order === 1); + if (firstState) { + savedWorkflow.stateId = firstState.id; + await this.workflowRepo.save(savedWorkflow); + } + + // ขั้นที่ 5: ดึง metaStateOperators ทั้งหมดและสร้าง stateOperators + const metaStateIds = metaStates.map((item) => item.id); + const allMetaStateOperators = await this.metaStateOperatorRepo.find({ + where: { metaStateId: In(metaStateIds) }, + }); + + // สร้าง stateOperators ทั้งหมดในครั้งเดียว + const stateOperatorsToCreate: StateOperator[] = []; + allMetaStateOperators.forEach((metaStateOp) => { + const correspondingState = savedStates.find( + (state) => + metaStates.find((metaState) => metaState.id === metaStateOp.metaStateId)?.order === + state.order, + ); + + if (correspondingState) { + const stateOperator = new StateOperator(); + Object.assign(stateOperator, { + ...metaStateOp, + id: undefined, + stateId: correspondingState.id, + ...meta, }); - await Promise.all( - metaStateOperator.map(async (item1) => { - const stateOperator = new StateOperator(); - Object.assign(stateOperator, { ...item1, id: undefined, stateId: state.id, ...meta }); - await this.stateOperatorRepo.save(stateOperator); - }), - ); - }), - ); - let num = 1; - const stateOperatorUser = new StateOperatorUser(); + stateOperatorsToCreate.push(stateOperator); + } + }); + + await this.stateOperatorRepo.save(stateOperatorsToCreate); + // ขั้นที่ 6: สร้าง StateOperatorUsers แบบ bulk + const stateOperatorUsersToCreate: StateOperatorUser[] = []; + let orderNum = 1; + + // เพิ่ม Owner ก่อน if (profile) { - Object.assign(stateOperatorUser, { - profileId: profileType == "OFFICER" ? profile.id : null, - profileEmployeeId: profileType != "OFFICER" ? profile.id : null, + const ownerStateOperatorUser = new StateOperatorUser(); + Object.assign(ownerStateOperatorUser, { + profileId: profileType === "OFFICER" ? profile.id : null, + profileEmployeeId: profileType !== "OFFICER" ? profile.id : null, profileType: profileType, operator: "Owner", - order: num, - workflowId: workflow.id, + order: orderNum, + workflowId: savedWorkflow.id, ...meta, }); + stateOperatorUsersToCreate.push(ownerStateOperatorUser); } - await this.stateOperatorUserRepo.save(stateOperatorUser); - const profileOfficer = await this.posMasterRepo.find({ + // ดึงข้อมูล profileOfficers และสร้าง StateOperatorUsers + const profileOfficers = await this.posMasterRepo.find({ where: { posMasterAssigns: { assignId: body.sysName }, orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, + current_holderId: Not(IsNull()), // เพิ่มเงื่อนไขนี้เพื่อกรองเฉพาะที่มี current_holder }, relations: ["orgChild1"], + select: ["current_holderId", "orgChild1"], // เลือกเฉพาะ field ที่จำเป็น }); - await Promise.all( - profileOfficer.map(async (item, i) => { - if (item.current_holderId) { - num = num + 1; - if (item.orgChild1 == null || item.orgChild1.isOfficer == false) { - const stateOperatorUser = new StateOperatorUser(); - Object.assign(stateOperatorUser, { - profileId: item.current_holderId, - operator: "Officer", - profileType: "OFFICER", - order: num, - workflowId: workflow.id, - ...meta, - }); - await this.stateOperatorUserRepo.save(stateOperatorUser); - } else { - const stateOperatorUser = new StateOperatorUser(); - Object.assign(stateOperatorUser, { - profileId: item.current_holderId, - operator: "PersonnelOfficer", - profileType: "OFFICER", - order: num, - workflowId: workflow.id, - ...meta, - }); - await this.stateOperatorUserRepo.save(stateOperatorUser); - } - } - }), + + // สร้าง StateOperatorUsers สำหรับ officers + profileOfficers.forEach((item) => { + if (item.current_holderId) { + orderNum += 1; + const isPersonnelOfficer = item.orgChild1?.isOfficer === true; + + const officerStateOperatorUser = new StateOperatorUser(); + Object.assign(officerStateOperatorUser, { + profileId: item.current_holderId, + operator: isPersonnelOfficer ? "PersonnelOfficer" : "Officer", + profileType: "OFFICER", + order: orderNum, + workflowId: savedWorkflow.id, + ...meta, + }); + stateOperatorUsersToCreate.push(officerStateOperatorUser); + } + }); + + // บันทึก StateOperatorUsers ทั้งหมดในครั้งเดียว + await this.stateOperatorUserRepo.save(stateOperatorUsersToCreate); + + // ขั้นที่ 7: ส่ง notification (ใช้ข้อมูลที่มีอยู่แล้วแทนการ query ใหม่) + const firstStateOperators = stateOperatorsToCreate.filter((so) => + savedStates.find((state) => state.id === so.stateId && state.order === 1), ); - const _workflow = await this.workflowRepo.findOne({ - where: { id: workflow.id }, - relations: ["stateOperatorUsers"], - }); - if (!_workflow) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่สามารถดำเนินการกระบวนการนี้ได้"); - - const _state = await this.stateRepo.findOne({ - where: { - id: _workflow.stateId, - }, - relations: ["stateOperators"], - }); - if (!_state) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลขั้นตอนการอนุมัติ"); - let profileNow = _workflow.stateOperatorUsers - .filter((x) => _state.stateOperators.map((s) => s.operator).includes(x.operator)) - .map((x) => ({ - receiverUserId: x.profileType == "OFFICER" ? x.profileId : x.profileEmployeeId, + const notificationReceivers = stateOperatorUsersToCreate + .filter((user) => firstStateOperators.some((op) => op.operator === user.operator)) + .map((user) => ({ + receiverUserId: user.profileType === "OFFICER" ? user.profileId : user.profileEmployeeId, notiLink: "", })); - await new CallAPI() + + // ส่ง notification แบบ fire-and-forget + new CallAPI() .PostData(req, "/placement/noti/profiles", { - // subject: `รายการถูกส่ง`, - // body: `รายการถูกส่ง`, - subject: `แจ้ง${workflow.name}ของ ${body.fullName}`, - body: `แจ้ง${workflow.name}ของ ${body.fullName}`, - receiverUserIds: profileNow, - payload: "", //แนบไฟล์ + subject: `แจ้ง${savedWorkflow.name}ของ ${body.fullName}`, + body: `แจ้ง${savedWorkflow.name}ของ ${body.fullName}`, + receiverUserIds: notificationReceivers, + payload: "", isSendMail: true, isSendInbox: true, isSendNotification: true, @@ -809,8 +830,8 @@ export class WorkflowController extends Controller { pageSize: number; keycloakId?: string | null; type?: string | null; - sortBy?: string | null, - descending?: boolean, + sortBy?: string | null; + descending?: boolean; }, ) { const userKeycloak = body.keycloakId ?? request.user.sub; @@ -990,10 +1011,10 @@ export class WorkflowController extends Controller { if (body.sortBy) { queryBuilder = queryBuilder.orderBy( `entity.${body.sortBy}`, - body.descending ? "DESC" : "ASC" + body.descending ? "DESC" : "ASC", ); } - + // 7. Execute พร้อมกัน - ใช้ Promise.all const [data, total] = await Promise.all([ queryBuilder From c1f3de5619299e86c3f54d2760204341823fdd17 Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Fri, 10 Oct 2025 00:21:44 +0700 Subject: [PATCH 48/50] delete comment --- .../OrganizationDotnetController.ts | 103 +++--------------- 1 file changed, 14 insertions(+), 89 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index cd4bad67..88cff766 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -162,6 +162,13 @@ export class OrganizationDotnetController extends Controller { }), ) .andWhere(condition, conditionParams) + .select([ + "profile.id", + "profile.citizenId", + "profile.prefix", + "profile.firstName", + "profile.lastName", + ]) .skip((body.page - 1) * body.pageSize) .take(body.pageSize) .getManyAndCount(); @@ -276,99 +283,17 @@ export class OrganizationDotnetController extends Controller { ) .andWhere(condition, conditionParams) .orderBy("profileSalary.order", "DESC") + .select([ + "profile.id", + "profile.citizenId", + "profile.prefix", + "profile.firstName", + "profile.lastName", + ]) .skip((body.page - 1) * body.pageSize) .take(body.pageSize) .getManyAndCount(); - // const profileEmp_ = await Promise.all( - // profileEmp.map((item: ProfileEmployee) => { - // const rootName = - // item.current_holders.length == 0 - // ? null - // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot - // ?.orgRootName; - // const shortName = - // item.current_holders.length == 0 - // ? null - // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4 != - // null - // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - // ?.orgChild3 != null - // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - // ?.orgChild2 != null - // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != null && - // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - // ?.orgChild1 != null - // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - // : item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) != - // null && - // item.current_holders.find((x) => x.orgRevisionId == findRevision?.id) - // ?.orgRoot != null - // ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision?.id)?.posMasterNo}` - // : null; - // return { - // oc: rootName, - // id: item.id, - // createdAt: item.createdAt, - // createdUserId: item.createdUserId, - // lastUpdatedAt: item.lastUpdatedAt, - // lastUpdateUserId: item.lastUpdateUserId, - // createdFullName: item.createdFullName, - // lastUpdateFullName: item.lastUpdateFullName, - // avatar: item.avatar, - // avatarName: item.avatarName, - // rank: item.rank, - // prefix: item.prefix, - // firstName: item.firstName, - // lastName: item.lastName, - // citizenId: item.citizenId, - // position: item.position, - // posLevelId: item.posLevelId, - // posTypeId: item.posTypeId, - // email: item.email, - // phone: item.phone, - // keycloak: item.keycloak, - // isProbation: item.isProbation, - // isLeave: item.isLeave, - // leaveReason: item.leaveReason, - // dateLeave: item.dateLeave, - // dateRetire: item.dateRetire, - // dateAppoint: item.dateAppoint, - // dateRetireLaw: item.dateRetireLaw, - // dateStart: item.dateStart, - // govAgeAbsent: item.govAgeAbsent, - // govAgePlus: item.govAgePlus, - // birthDate: item.birthDate ?? new Date(), - // reasonSameDate: item.reasonSameDate, - // ethnicity: item.ethnicity, - // telephoneNumber: item.phone, - // nationality: item.nationality, - // gender: item.gender, - // relationship: item.relationship, - // religion: item.religion, - // bloodGroup: item.bloodGroup, - // registrationAddress: item.registrationAddress, - // registrationProvinceId: item.registrationProvinceId, - // registrationDistrictId: item.registrationDistrictId, - // registrationSubDistrictId: item.registrationSubDistrictId, - // registrationZipCode: item.registrationZipCode, - // currentAddress: item.currentAddress, - // currentProvinceId: item.currentProvinceId, - // currentDistrictId: item.currentDistrictId, - // currentSubDistrictId: item.currentSubDistrictId, - // currentZipCode: item.currentZipCode, - // posLevel: item.posLevel, - // posType: item.posType, - // posNo: shortName, - // }; - // }), - // ); return new HttpSuccess({ data: profileEmp, total: total }); } From bfea0438f8ebf0391bf411be9e90e2e8995f2247 Mon Sep 17 00:00:00 2001 From: mamoss <> Date: Sun, 12 Oct 2025 16:51:21 +0700 Subject: [PATCH 49/50] test parent --- src/controllers/OrganizationDotnetController.ts | 4 ++-- src/controllers/PermissionController.ts | 2 +- src/interfaces/permission.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 88cff766..84d88bbc 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -105,7 +105,7 @@ export class OrganizationDotnetController extends Controller { condition = "orgRoot.ancestorDNA = :nodeId"; conditionParams = { nodeId: body.nodeId }; } else if (body.role === "PARENT") { - condition = "orgChild1.ancestorDNA = :nodeId"; + condition = "orgRoot.ancestorDNA = :nodeId AND current_holders.orgChild1 IS NOT NULL"; conditionParams = { nodeId: body.nodeId }; } else if (body.role === "NORMAL") { switch (body.node) { @@ -224,7 +224,7 @@ export class OrganizationDotnetController extends Controller { condition = "orgRoot.ancestorDNA = :nodeId"; conditionParams = { nodeId: body.nodeId }; } else if (body.role === "PARENT") { - condition = "orgChild1.ancestorDNA = :nodeId"; + condition = "orgRoot.ancestorDNA = :nodeId AND current_holders.orgChild1 IS NOT NULL"; conditionParams = { nodeId: body.nodeId }; } else if (body.role === "NORMAL") { switch (body.node) { diff --git a/src/controllers/PermissionController.ts b/src/controllers/PermissionController.ts index 54dea336..46f37a73 100644 --- a/src/controllers/PermissionController.ts +++ b/src/controllers/PermissionController.ts @@ -735,7 +735,7 @@ export class PermissionController extends Controller { } else if (privilege == "PARENT") { data = { root: [x.orgRootId], - child1: [x.orgChild1Id], + child1: null, child2: null, child3: null, child4: null, diff --git a/src/interfaces/permission.ts b/src/interfaces/permission.ts index 1fb12f00..1819b647 100644 --- a/src/interfaces/permission.ts +++ b/src/interfaces/permission.ts @@ -97,7 +97,7 @@ class CheckAuth { } else if (privilege == "PARENT") { data = { root: [x.orgRootId], - child1: [x.orgChild1Id], + child1: null, child2: null, child3: null, child4: null, From 1a50a62b7a30ede3335daf59982ab612b07fec8f Mon Sep 17 00:00:00 2001 From: harid Date: Tue, 14 Oct 2025 09:13:44 +0700 Subject: [PATCH 50/50] =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=84=E0=B8=A5=E0=B8=B5=E0=B8=A2=20isSit?= =?UTF-8?q?=20=E0=B9=80=E0=B8=9B=E0=B9=87=E0=B8=99=200=20#193?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/CommandController.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 2c18fd52..7cce510c 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1692,6 +1692,7 @@ export class CommandController extends Controller { _position.lastUpdatedAt = _Date; await this.positionRepository.save(_position); } + _posMaster.isSit = false; _posMaster.current_holderId = null; _posMaster.lastUpdateFullName = "System Administrator"; _posMaster.lastUpdatedAt = _Date; @@ -1718,6 +1719,7 @@ export class CommandController extends Controller { _position.lastUpdatedAt = _Date; await this.employeePositionRepository.save(_position); } + _posMaster.isSit = false; _posMaster.current_holderId = null; _posMaster.lastUpdateFullName = "System Administrator"; _posMaster.lastUpdatedAt = _Date;