From 645f9760f08998c837e7ef8b2d624fa02e2a7949 Mon Sep 17 00:00:00 2001 From: Adisak Date: Fri, 17 Oct 2025 13:51:43 +0700 Subject: [PATCH 01/13] update privilege validate (OWNER) --- src/controllers/OrganizationController.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 18a65e8d..da712e14 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -1973,6 +1973,8 @@ export class OrganizationController extends Controller { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); } let _privilege = await new permission().PermissionOrgList(request, "SYS_ORG"); + // console.log("privilege>>>>",_privilege); + const attrOwnership = _privilege.root === null ? true : false; const profile = await this.profileRepo.findOne({ @@ -1986,6 +1988,8 @@ export class OrganizationController extends Controller { ?.find((x) => x.orgRevisionId === id) ?.posMasterAssigns.find((x) => x.assignId === "SYS_ORG"); + // console.log("profileAssign>>>>",profileAssign); + if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) { _data.root = profile.permissionProfiles.map((x) => x.orgRootId); } @@ -1993,7 +1997,7 @@ export class OrganizationController extends Controller { const isCurrentActive = !orgRevision.orgRevisionIsDraft && orgRevision.orgRevisionIsCurrent; if (isCurrentActive) { - if (profileAssign) { + if (profileAssign && _privilege.privilege !== "OWNER") { _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId]; } else { if (!attrOwnership) _data = _privilege; From 5cdcc2a91ee4dab0a0a54b775ef3824aa892ac36 Mon Sep 17 00:00:00 2001 From: Adisak Date: Fri, 17 Oct 2025 16:05:42 +0700 Subject: [PATCH 02/13] update privilage validate (NORMAL) and update calRetireLaw --- src/controllers/OrganizationController.ts | 14 +++++++++----- src/controllers/ProfileController.ts | 1 + src/controllers/ProfileEmployeeController.ts | 1 + src/controllers/ProfileEmployeeTempController.ts | 1 + 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index da712e14..71156c48 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -1973,7 +1973,6 @@ export class OrganizationController extends Controller { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); } let _privilege = await new permission().PermissionOrgList(request, "SYS_ORG"); - // console.log("privilege>>>>",_privilege); const attrOwnership = _privilege.root === null ? true : false; @@ -1988,17 +1987,22 @@ export class OrganizationController extends Controller { ?.find((x) => x.orgRevisionId === id) ?.posMasterAssigns.find((x) => x.assignId === "SYS_ORG"); - // console.log("profileAssign>>>>",profileAssign); - if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) { _data.root = profile.permissionProfiles.map((x) => x.orgRootId); } // กำหนดการเข้าถึงข้อมูลตามสถานะและสิทธิ์ const isCurrentActive = !orgRevision.orgRevisionIsDraft && orgRevision.orgRevisionIsCurrent; - if (isCurrentActive) { if (profileAssign && _privilege.privilege !== "OWNER") { - _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId]; + if(_privilege.privilege == "NORMAL"){ + _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId]; + _data.child1 = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgChild1Id]; + _data.child2 = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgChild2Id]; + _data.child3 = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgChild3Id]; + _data.child4 = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgChild4Id]; + }else{ + _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId]; + } } else { if (!attrOwnership) _data = _privilege; } diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index d4e65f29..aebd8fa6 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -5262,6 +5262,7 @@ export class ProfileController extends Controller { if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์นี้"); Object.assign(record, body); + record.dateRetireLaw = calculateRetireLaw(record.birthDate); record.prefixMain = record.prefix; record.prefix = record.rank && record.rank.length > 0 ? record.rank : record.prefixMain; record.createdUserId = request.user.sub; diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index a7d905ad..52de0922 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -2002,6 +2002,7 @@ export class ProfileEmployeeController extends Controller { } Object.assign(record, body); + record.dateRetireLaw = calculateRetireLaw(record.birthDate); record.prefixMain = record.prefix; record.prefix = record.rank && record.rank.length > 0 ? record.rank : record.prefixMain; record.createdUserId = request.user.sub; diff --git a/src/controllers/ProfileEmployeeTempController.ts b/src/controllers/ProfileEmployeeTempController.ts index 46ef525b..6dc49b1a 100644 --- a/src/controllers/ProfileEmployeeTempController.ts +++ b/src/controllers/ProfileEmployeeTempController.ts @@ -994,6 +994,7 @@ export class ProfileEmployeeTempController extends Controller { } Object.assign(record, body); + record.dateRetireLaw = calculateRetireLaw(record.birthDate); record.prefixMain = record.prefix; record.createdUserId = request.user.sub; record.createdFullName = request.user.name; From 6e9015b1e6b977a6ea81edbe7b85c9bfbf42818b Mon Sep 17 00:00:00 2001 From: Harid Promsri <52228846+Harid-999@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:02:19 +0700 Subject: [PATCH 03/13] Fix #1836 (#197) Co-authored-by: harid --- src/controllers/CommandController.ts | 72 ++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 7cce510c..c9302286 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -1954,27 +1954,71 @@ export class CommandController extends Controller { ]; } const orgLeave = _OrgLeave.filter((x: any) => x !== undefined && x !== null).join("\n"); + let profileTemp = { + org: "-", + position: "-", + posLevel: "-", + posNo: "-", + }; + if (commandCode == "C-PM-21") { + profileTemp.position = profile?.positionTemp ?? "-"; + profileTemp.posLevel = profile?.posLevelNameTemp ?? "-"; + profileTemp.org = (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) + if (profile?.nodeTemp) { + switch (profile?.nodeTemp) { + case "4": + profileTemp.posNo = `${profile.child4ShortNameTemp} ${profile?.posMasterNoTemp}`; + break + case "3": + profileTemp.posNo = `${profile.child3ShortNameTemp} ${profile?.posMasterNoTemp}`; + break + case "2": + profileTemp.posNo = `${profile.child2ShortNameTemp} ${profile?.posMasterNoTemp}`; + break + case "1": + profileTemp.posNo = `${profile.child1ShortNameTemp} ${profile?.posMasterNoTemp}`; + break + case "0": + profileTemp.posNo = `${profile.rootShortNameTemp} ${profile?.posMasterNoTemp}`; + break + default: break; + } + } + } return { no: Extension.ToThaiNumber((idx + 1).toString()), - org: profile?.isLeave == false - ? (_child4 == null ? "" : _child4 + "\n") + - (_child3 == null ? "" : _child3 + "\n") + - (_child2 == null ? "" : _child2 + "\n") + - (_child1 == null ? "" : _child1 + "\n") + - (_root == null ? "" : _root) - : orgLeave, + 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 + : profileTemp.org, fullName: `${x.prefix}${x.firstName} ${x.lastName}`, citizenId: Extension.ToThaiNumber(x.citizenId), - position: profile?.position ? profile?.position : "-", - posLevel: - profile?.posType && profile?.posLevel + position: commandCode != "C-PM-21" + ? profile?.position + ? profile?.position + : "-" + : profileTemp.position, + posLevel: commandCode != "C-PM-21" + ? profile?.posType && profile?.posLevel ? Extension.ToThaiNumber( `${profile?.posType.posTypeShortName} ${profile?.posLevel.posLevelName}`, ) - : "-", - posNo: shortName - ? Extension.ToThaiNumber(shortName) - : "-", + : "-" + : Extension.ToThaiNumber(profileTemp.posLevel), + posNo: commandCode != "C-PM-21" + ? shortName + ? Extension.ToThaiNumber(shortName) + : "-" + : Extension.ToThaiNumber(profileTemp.posNo), amount: x.amount ? Extension.ToThaiNumber(x.amount.toLocaleString()) : "-", dateRetire: profile?.dateRetire ? Extension.ToThaiNumber(Extension.ToThaiShortDate_monthYear(profile?.dateRetire)) From 0d059f24ea0f0b1382a422c4f4d6cce311a7369c Mon Sep 17 00:00:00 2001 From: Adisak Date: Fri, 17 Oct 2025 17:38:54 +0700 Subject: [PATCH 04/13] =?UTF-8?q?update=20validate=20=E0=B8=AA=E0=B8=B4?= =?UTF-8?q?=E0=B8=97=E0=B8=98=E0=B8=B4=E0=B9=8C=E0=B8=88=E0=B8=B1=E0=B8=94?= =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B9=82=E0=B8=84=E0=B8=A3=E0=B8=87?= =?UTF-8?q?=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B9=83=E0=B8=99?= =?UTF-8?q?=E0=B9=80=E0=B8=A1=E0=B8=99=E0=B8=B9=E0=B9=82=E0=B8=84=E0=B8=A3?= =?UTF-8?q?=E0=B8=87=E0=B8=AA=E0=B8=A3=E0=B9=89=E0=B8=B2=E0=B8=87=E0=B8=AD?= =?UTF-8?q?=E0=B8=B1=E0=B8=95=E0=B8=95=E0=B8=A3=E0=B8=B2=E0=B8=81=E0=B8=B3?= =?UTF-8?q?=E0=B8=A5=E0=B8=B1=E0=B8=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/OrganizationController.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 71156c48..e163ade8 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -1988,7 +1988,11 @@ export class OrganizationController extends Controller { ?.posMasterAssigns.find((x) => x.assignId === "SYS_ORG"); if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) { - _data.root = profile.permissionProfiles.map((x) => x.orgRootId); + if(Array.isArray(profile.permissionProfiles) && profile.permissionProfiles.length > 0){ + _data.root = profile.permissionProfiles.map((x) => x.orgRootId); + }else{ + return new HttpSuccess({ remark: "", data: [] }); + } } // กำหนดการเข้าถึงข้อมูลตามสถานะและสิทธิ์ const isCurrentActive = !orgRevision.orgRevisionIsDraft && orgRevision.orgRevisionIsCurrent; From 25f3a6535f872f98b49a93782cfbaa03e6d2a06b Mon Sep 17 00:00:00 2001 From: Harid Promsri <52228846+Harid-999@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:38:44 +0700 Subject: [PATCH 05/13] =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=E0=B8=84?= =?UTF-8?q?=E0=B9=89=E0=B8=99=E0=B8=AB=E0=B8=B2=E0=B8=8A=E0=B8=B7=E0=B9=88?= =?UTF-8?q?=E0=B8=AD=E0=B8=A5=E0=B8=B9=E0=B8=81=E0=B8=88=E0=B9=89=E0=B8=B2?= =?UTF-8?q?=E0=B8=87=20(#199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: harid --- src/controllers/OrganizationDotnetController.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 84d88bbc..fb615566 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -282,7 +282,6 @@ export class OrganizationDotnetController extends Controller { }), ) .andWhere(condition, conditionParams) - .orderBy("profileSalary.order", "DESC") .select([ "profile.id", "profile.citizenId", From 45cc074e81b9a366f2586adb6dd3b97a69c87f18 Mon Sep 17 00:00:00 2001 From: AdisakKanthawilang <153157069+AdisakKanthawilang@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:45:43 +0700 Subject: [PATCH 06/13] update validate privilege (#201) --- src/controllers/OrganizationController.ts | 28 ++++++++++++++--- src/controllers/PositionController.ts | 38 ++++++++++++++++++++--- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index e163ade8..64ae57b1 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -1999,11 +1999,29 @@ export class OrganizationController extends Controller { if (isCurrentActive) { if (profileAssign && _privilege.privilege !== "OWNER") { if(_privilege.privilege == "NORMAL"){ - _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId]; - _data.child1 = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgChild1Id]; - _data.child2 = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgChild2Id]; - _data.child3 = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgChild3Id]; - _data.child4 = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgChild4Id]; + const holder = profile.current_holders.find(x => x.orgRevisionId === id); + if (!holder) return; + _data.root = [holder.orgRootId]; + _data.child1 = [holder.orgChild1Id]; + _data.child2 = [holder.orgChild2Id]; + _data.child3 = [holder.orgChild3Id]; + _data.child4 = [holder.orgChild4Id]; + }else if(_privilege.privilege == "CHILD"){ + const holder = profile.current_holders.find(x => x.orgRevisionId === id); + if (!holder) return; + _data.root = [holder.orgRootId]; + if (_privilege.root && _privilege.child1 === null) { + } else if (_privilege.child1 && _privilege.child2 === null) { + _data.child1 = [holder.orgChild1Id]; + } else if (_privilege.child2 && _privilege.child3 === null) { + _data.child1 = [holder.orgChild1Id]; + _data.child2 = [holder.orgChild2Id]; + } else if (_privilege.child3 && _privilege.child4 === null) { + _data.child1 = [holder.orgChild1Id]; + _data.child2 = [holder.orgChild2Id]; + _data.child3 = [holder.orgChild3Id]; + _data.child4 = [holder.orgChild4Id]; + } }else{ _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId]; } diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 4deffbaa..2963e0b3 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -2134,7 +2134,6 @@ export class PositionController extends Controller { orgChild1Id: IsNull(), }; searchShortName = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; - } else { } } else if (body.type === 1) { typeCondition = { @@ -2145,7 +2144,6 @@ export class PositionController extends Controller { orgChild2Id: IsNull(), }; searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; - } else { } } else if (body.type === 2) { typeCondition = { @@ -2156,7 +2154,6 @@ export class PositionController extends Controller { orgChild3Id: IsNull(), }; searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; - } else { } } else if (body.type === 3) { typeCondition = { @@ -2167,7 +2164,6 @@ export class PositionController extends Controller { orgChild4Id: IsNull(), }; searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; - } else { } } else if (body.type === 4) { typeCondition = { @@ -2524,6 +2520,40 @@ export class PositionController extends Controller { }; }), ); + + if(_data.privilege === 'NORMAL'|| _data.privilege === 'PARENT'|| _data.privilege === 'CHILD'){ //PARENT จะไม่มีทางเห็น ROOT , CHILD ยึดจาก CHILD ที่อยู่ลงไปข้างล่างและจะไม่เห็น CHILD ที่อยู่เหนือกว่า + const nextChildMap:any = { //เอาไวเช็ค CHILD ถัดไป + 0: _data.child1, + 1: _data.child2, + 2: _data.child3, + 3: _data.child4, + }; + const childValue = nextChildMap[body.type]; + if(_data.privilege === 'NORMAL'){ + if (Array.isArray(childValue) && childValue.some(item => item != null)) { + return new HttpSuccess({ data: {}, total: 0 }); + } + }else if(_data.privilege === 'PARENT'){ + if (body.type == 0){ + return new HttpSuccess({ data: {}, total: 0 }); + } + } else if (_data.privilege === 'CHILD') { + const higherChildChecks = [ + { type: [0], child: _data.child1, next: _data.child2 }, + { type: [0, 1], child: _data.child2, next: _data.child3 }, + { type: [0, 1, 2], child: _data.child3, next: _data.child4 }, + { type: [0, 1, 2, 3], child: _data.child4, next: true }, + ]; + + for (const check of higherChildChecks) { + if (Array.isArray(check.child) && check.next == null) { + if (check.type.includes(body.type)) { + return new HttpSuccess({ data: {}, total: 0 }); + } + } + } + } + } return new HttpSuccess({ data: formattedData, total }); } From c14de472504b542a7f0b19c17d491a43aaa93951 Mon Sep 17 00:00:00 2001 From: Adisak Date: Mon, 20 Oct 2025 18:04:19 +0700 Subject: [PATCH 07/13] fix --- src/controllers/PositionController.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 2963e0b3..07473ca6 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -2531,11 +2531,11 @@ export class PositionController extends Controller { const childValue = nextChildMap[body.type]; if(_data.privilege === 'NORMAL'){ if (Array.isArray(childValue) && childValue.some(item => item != null)) { - return new HttpSuccess({ data: {}, total: 0 }); + return new HttpSuccess({ data: [{}], total: 0 }); } }else if(_data.privilege === 'PARENT'){ if (body.type == 0){ - return new HttpSuccess({ data: {}, total: 0 }); + return new HttpSuccess({ data: [{}], total: 0 }); } } else if (_data.privilege === 'CHILD') { const higherChildChecks = [ @@ -2548,7 +2548,7 @@ export class PositionController extends Controller { for (const check of higherChildChecks) { if (Array.isArray(check.child) && check.next == null) { if (check.type.includes(body.type)) { - return new HttpSuccess({ data: {}, total: 0 }); + return new HttpSuccess({ data: [{}], total: 0 }); } } } From 0f22605784298a231b4ac9ea99f17675d3964f6b Mon Sep 17 00:00:00 2001 From: Adisak Date: Mon, 20 Oct 2025 18:08:59 +0700 Subject: [PATCH 08/13] fix --- src/controllers/PositionController.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 07473ca6..e9f8d99e 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -2531,11 +2531,11 @@ export class PositionController extends Controller { const childValue = nextChildMap[body.type]; if(_data.privilege === 'NORMAL'){ if (Array.isArray(childValue) && childValue.some(item => item != null)) { - return new HttpSuccess({ data: [{}], total: 0 }); + return new HttpSuccess({ data: [], total: 0 }); } }else if(_data.privilege === 'PARENT'){ if (body.type == 0){ - return new HttpSuccess({ data: [{}], total: 0 }); + return new HttpSuccess({ data: [], total: 0 }); } } else if (_data.privilege === 'CHILD') { const higherChildChecks = [ @@ -2548,7 +2548,7 @@ export class PositionController extends Controller { for (const check of higherChildChecks) { if (Array.isArray(check.child) && check.next == null) { if (check.type.includes(body.type)) { - return new HttpSuccess({ data: [{}], total: 0 }); + return new HttpSuccess({ data: [], total: 0 }); } } } From 2429159020bd33aba207ecbc1b514c9f2c07ed14 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Mon, 20 Oct 2025 23:04:04 +0700 Subject: [PATCH 09/13] fix: send token in keycloak function & change script fix retire --- src/controllers/OrganizationController.ts | 620 ++++++++++++++-------- src/keycloak/index.ts | 29 +- 2 files changed, 429 insertions(+), 220 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 64ae57b1..4cb17e6d 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -1,3 +1,4 @@ +import { RoleKeycloak } from "./../entities/RoleKeycloak"; import { ProfileEmployee } from "./../entities/ProfileEmployee"; import { EmployeePosition } from "./../entities/EmployeePosition"; import { EmployeePosMaster } from "./../entities/EmployeePosMaster"; @@ -37,7 +38,14 @@ import { sendToQueueOrg, sendToQueueOrgDraft } from "../services/rabbitmq"; import { PosType } from "../entities/PosType"; import { PosLevel } from "../entities/PosLevel"; import { PermissionOrg } from "../entities/PermissionOrg"; -import { deleteUser, getToken } from "../keycloak"; +import { + deleteUser, + getToken, + createUser, + getUserByUsername, + getRoles, + addUserRoles, +} from "../keycloak"; import { CreatePosMasterHistoryEmployee, CreatePosMasterHistoryOfficer, @@ -69,6 +77,8 @@ export class OrganizationController extends Controller { private profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); private employeePosMasterRepository = AppDataSource.getRepository(EmployeePosMaster); private employeePositionRepository = AppDataSource.getRepository(EmployeePosition); + private roleKeycloakRepo = AppDataSource.getRepository(RoleKeycloak); + /** * API ล้างข้อมูล * @@ -1973,7 +1983,7 @@ export class OrganizationController extends Controller { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); } let _privilege = await new permission().PermissionOrgList(request, "SYS_ORG"); - + const attrOwnership = _privilege.root === null ? true : false; const profile = await this.profileRepo.findOne({ @@ -1988,26 +1998,26 @@ export class OrganizationController extends Controller { ?.posMasterAssigns.find((x) => x.assignId === "SYS_ORG"); if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) { - if(Array.isArray(profile.permissionProfiles) && profile.permissionProfiles.length > 0){ + if (Array.isArray(profile.permissionProfiles) && profile.permissionProfiles.length > 0) { _data.root = profile.permissionProfiles.map((x) => x.orgRootId); - }else{ + } else { return new HttpSuccess({ remark: "", data: [] }); - } + } } // กำหนดการเข้าถึงข้อมูลตามสถานะและสิทธิ์ const isCurrentActive = !orgRevision.orgRevisionIsDraft && orgRevision.orgRevisionIsCurrent; if (isCurrentActive) { if (profileAssign && _privilege.privilege !== "OWNER") { - if(_privilege.privilege == "NORMAL"){ - const holder = profile.current_holders.find(x => x.orgRevisionId === id); + if (_privilege.privilege == "NORMAL") { + const holder = profile.current_holders.find((x) => x.orgRevisionId === id); if (!holder) return; _data.root = [holder.orgRootId]; _data.child1 = [holder.orgChild1Id]; _data.child2 = [holder.orgChild2Id]; _data.child3 = [holder.orgChild3Id]; _data.child4 = [holder.orgChild4Id]; - }else if(_privilege.privilege == "CHILD"){ - const holder = profile.current_holders.find(x => x.orgRevisionId === id); + } else if (_privilege.privilege == "CHILD") { + const holder = profile.current_holders.find((x) => x.orgRevisionId === id); if (!holder) return; _data.root = [holder.orgRootId]; if (_privilege.root && _privilege.child1 === null) { @@ -2022,7 +2032,7 @@ export class OrganizationController extends Controller { _data.child3 = [holder.orgChild3Id]; _data.child4 = [holder.orgChild4Id]; } - }else{ + } else { _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId]; } } else { @@ -7318,54 +7328,70 @@ export class OrganizationController extends Controller { */ @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, - // }, - // }, - // }); - - // 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", + const [posMasters, posMastersEmployee] = await Promise.all([ + this.posMasterRepository.find({ + where: { + orgRevisionId, + current_holder: { + isLeave: true, + isRetirement: true, + leaveType: IsNull(), + }, + positions: { positionIsSelected: true }, }, - positions: { positionIsSelected: true }, - }, - }); + relations: ["positions"], + }), + this.employeePosMasterRepository.find({ + where: { + orgRevisionId, + current_holder: { + isLeave: true, + isRetirement: true, + leaveType: IsNull(), + }, + }, + relations: ["positions"], + }), + ]); - let check = 0; - await Promise.all( + let checkOfficer = 0; + let checkEmployee = 0; + + await Promise.all([ + posMasters.map(async (posMaster) => { + posMaster.current_holderId = null; + posMaster.isSit = false; + if (posMaster.positions) { + for (const position of posMaster.positions) { + position.positionIsSelected = false; + await this.positionRepository.save(position); + checkOfficer += 1; + } + } + await this.posMasterRepository.save(posMaster); + await CreatePosMasterHistoryOfficer(posMaster.id, null); + }), posMastersEmployee.map(async (posMaster) => { posMaster.current_holderId = null; + posMaster.isSit = false; if (posMaster.positions) { for (const position of posMaster.positions) { position.positionIsSelected = false; await this.employeePositionRepository.save(position); - check += 1; + checkEmployee += 1; } } await this.employeePosMasterRepository.save(posMaster); await CreatePosMasterHistoryEmployee(posMaster.id, null); - check += 1; }), - ); - // จำนวนคนที่ถูกลบออกจากโครงสร้าง - const total = posMastersEmployee.length; - return new HttpSuccess({ total, successAmount: check }); + ]); + + return new HttpSuccess({ + totalOfficer: posMasters.length, + totalEmployee: posMastersEmployee.length, + officerSuccessAmount: checkOfficer, + employeeSuccessAmount: checkEmployee, + }); } /** @@ -7376,75 +7402,78 @@ export class OrganizationController extends Controller { */ @Get("save/profile/position-history") async saveRetireToPositionHistory(@Request() request: RequestWithUser) { - // const profileLeave = await this.profileRepo.find({ - // where: { - // isLeave: true, - // leaveType: "RETIRE", - // }, - // }); + const [profileLeave, profileEmployeeLeave] = await Promise.all([ + this.profileRepo.find({ + where: { + isLeave: true, + isRetirement: true, + leaveType: IsNull(), + }, + }), + this.profileEmployeeRepo.find({ + where: { + isLeave: true, + isRetirement: true, + leaveType: IsNull(), + }, + }), + ]); - // 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", - }, - }); - - let check: number = 0; + let checkOfficer: 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); + + checkOfficer += 1; + }), + ); + + let checkEmployee: number = 0; + await Promise.all( + profileEmployeeLeave.map(async (profile: any) => { const dest_item = await this.profileSalaryRepository.findOne({ where: { profileEmployeeId: profile.id }, order: { order: "DESC" }, @@ -7490,13 +7519,20 @@ export class OrganizationController extends Controller { history.profileSalaryId = savedData.id; await this.salaryHistoryRepo.save(history); - check += 1; + checkEmployee += 1; }), ); + // จำนวนคนที่บันทึกลงประวัติตำแหน่ง - const total = profileLeave.length; + const totalOfficer = profileLeave.length; + const totalEmployee = profileEmployeeLeave.length; // จำนวนคนที่ถูกบันทึกลงประวัติตำแหน่งสำเร็จ - return new HttpSuccess({ total, successAmount: check }); + return new HttpSuccess({ + totalOfficer, + totalEmployee, + officerSuccessAmount: checkOfficer, + successEmployeeAmount: checkEmployee, + }); } /** @@ -7507,41 +7543,19 @@ export class OrganizationController extends Controller { */ @Get("update/profile/leave-reason") async updateRetireReason() { - // 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, token] = await Promise.all([ + const [profileLeave, profileEmployeeLeave, token] = await Promise.all([ + this.profileRepo.find({ + where: { + isLeave: true, + isRetirement: true, + leaveType: IsNull(), + }, + }), this.profileEmployeeRepo.find({ where: { isLeave: true, - leaveType: "RETIRE", + isRetirement: true, + leaveType: IsNull(), }, }), getToken(), @@ -7550,77 +7564,263 @@ export class OrganizationController extends Controller { if (!token) throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถเชื่อมต่อ Keycloak"); - let check: number = 0; - let notDelete: string[] = []; + let checkOfficer: number = 0; + let notDeleteOfficer: string[] = []; - // 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); - }), - ); - } - - // จำนวนคนที่ถูกแก้ไขเหตุผลการลาออก - 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; + profileLeave.map(async (profile) => { + profile.leaveReason = "เกษียณอายุราชการ"; + profile.leaveType = "RETIRE"; + profile.isActive = false; + + if (profile.keycloak != null && profile.keycloak != "") { + const delUserKeycloak = await deleteUser(profile.keycloak, token); + if (delUserKeycloak) { + profile.keycloak = ""; + profile.roleKeycloaks = []; + checkOfficer += 1; + } else { + // push array not delete + notDeleteOfficer.push(profile.keycloak); } - await CreatePosMasterHistoryOfficer(posMaster.id, null); } + + await this.profileRepo.save(profile); }), ); - // จำนวนคนที่ถูกลบออกจากโครงสร้าง - const total = posMasters.length; + let checkEmployee: number = 0; + let notDeleteEmployee: string[] = []; + + await Promise.all( + profileEmployeeLeave.map(async (profileEmp) => { + profileEmp.leaveReason = "เกษียณอายุราชการ"; + profileEmp.leaveType = "RETIRE"; + profileEmp.leaveDate = new Date("2025-09-30"); + profileEmp.dateLeave = new Date("2025-09-30"); + profileEmp.lastUpdatedAt = new Date(); + profileEmp.isActive = false; + + if (profileEmp.keycloak != null && profileEmp.keycloak != "") { + const delUserKeycloak = await deleteUser(profileEmp.keycloak, token); + if (delUserKeycloak) { + profileEmp.keycloak = ""; + profileEmp.roleKeycloaks = []; + checkEmployee += 1; + } else { + // push array not delete + notDeleteEmployee.push(profileEmp.keycloak); + } + } + + await this.profileEmployeeRepo.save(profileEmp); + }), + ); + + // 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); + // }), + // ); + // } + + // จำนวนคนที่ถูกแก้ไขเหตุผลการลาออก + const total = profileLeave.length; + return new HttpSuccess({ + total, + successOfficerAmount: checkOfficer, + notDeleteOfficer, + successEmployeeAmount: checkEmployee, + notDeleteEmployee, + }); + } + + /** + * API สร้าง keycloak ใหม่สำหรับข้าราชการที่รันเกษียณผิด + * + * @summary - สร้าง keycloak ใหม่สำหรับข้าราชการที่รันเกษียณผิด (ADMIN) + * + */ + @Get("update/org/profile-officer/create-keycloak") + async createKeycloakForOfficer(@Request() request: RequestWithUser) { + const [profiles, token] = await Promise.all([ + this.profileRepo.find({ + where: { + keycloak: IsNull(), + isActive: true, + }, + relations: ["roleKeycloaks"], + }), + getToken(), + ]); + + if (!token) + throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถเชื่อมต่อ Keycloak"); + + let check: number = 0; + 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, token); + let userId: any = ""; + if (checkUser.length == 0) { + userId = await createUser( + _item.citizenId, + password, + { + firstName: _item.firstName, + lastName: _item.lastName, + // email: _item.email, + }, + token, + ); + if (typeof userId !== "string") { + throw new Error(userId.errorMessage); + } + } else { + userId = checkUser[0].id; + } + + const list = await getRoles("", token); + 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"), + token, + ); + + 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.profileRepo.save(_item); + check += 1; + } + } + + const total = profiles.length; + + return new HttpSuccess({ total, successAmount: check }); + } + + /** + * API สร้าง keycloak ใหม่สำหรับลูกจ้างที่รันเกษียณผิด + * + * @summary - สร้าง keycloak ใหม่สำหรับลูกจ้างที่รันเกษียณผิด (ADMIN) + * + */ + @Get("update/org/profile-employee/create-keycloak") + async createKeycloakForEmployee(@Request() request: RequestWithUser) { + const [profiles, token] = await Promise.all([ + this.profileEmployeeRepo.find({ + where: { + keycloak: IsNull(), + isActive: true, + }, + relations: ["roleKeycloaks"], + }), + getToken(), + ]); + + if (!token) + throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถเชื่อมต่อ Keycloak"); + + let check: number = 0; + 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, token); + let userId: any = ""; + if (checkUser.length == 0) { + userId = await createUser( + _item.citizenId, + password, + { + firstName: _item.firstName, + lastName: _item.lastName, + // email: _item.email, + }, + token, + ); + if (typeof userId !== "string") { + throw new Error(userId.errorMessage); + } + } else { + userId = checkUser[0].id; + } + + const list = await getRoles("", token); + 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"), + token, + ); + + 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.profileEmployeeRepo.save(_item); + check += 1; + } + } + + const total = profiles.length; + return new HttpSuccess({ total, successAmount: check }); } } diff --git a/src/keycloak/index.ts b/src/keycloak/index.ts index c14a6b62..a81e2af9 100644 --- a/src/keycloak/index.ts +++ b/src/keycloak/index.ts @@ -67,11 +67,16 @@ export async function getToken() { * * @returns user uuid or true if success, false otherwise. */ -export async function createUser(username: string, password: string, opts?: Record) { +export async function createUser( + username: string, + password: string, + opts?: Record, + token?: string, +) { const res = await fetch(`${KC_URL}/admin/realms/${KC_REALMS}/users`, { // prettier-ignore headers: { - "authorization": `Bearer ${await getToken()}`, + "authorization": `Bearer ${token || await getToken()}`, "content-type": `application/json`, }, method: "POST", @@ -101,11 +106,11 @@ export async function createUser(username: string, password: string, opts?: Reco * * @returns user if success, false otherwise. */ -export async function getUser(userId: string) { +export async function getUser(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`, }, }).catch((e) => console.log("Keycloak Error: ", e)); @@ -123,11 +128,11 @@ export async function getUser(userId: string) { * * @returns user if success, false otherwise. */ -export async function getUserByUsername(citizenId: string) { +export async function getUserByUsername(citizenId: string, token?: string) { const res = await fetch(`${KC_URL}/admin/realms/${KC_REALMS}/users?username=${citizenId}`, { // prettier-ignore headers: { - "authorization": `Bearer ${await getToken()}`, + "authorization": `Bearer ${token || await getToken()}`, "content-type": `application/json`, }, }).catch((e) => console.log("Keycloak Error: ", e)); @@ -409,13 +414,13 @@ export async function getRoleMappings(userId: string) { * * @returns role's info (array if not specify name) if success, null if not found, false otherwise. */ -export async function getRoles(name?: string) { +export async function getRoles(name?: string, token?: string) { const res = await fetch( `${KC_URL}/admin/realms/${KC_REALMS}/roles`.concat((name && `/${name}`) || ""), { // prettier-ignore headers: { - "authorization": `Bearer ${await getToken()}`, + "authorization": `Bearer ${token || await getToken()}`, }, }, ).catch((e) => console.log(e)); @@ -491,13 +496,17 @@ export async function getUserRoles(userId: string) { * * @returns true if success, false otherwise. */ -export async function addUserRoles(userId: string, roles: { id: string; name: string }[]) { +export async function addUserRoles( + userId: string, + roles: { id: string; name: string }[], + token?: string, +) { const res = await fetch( `${KC_URL}/admin/realms/${KC_REALMS}/users/${userId}/role-mappings/realm`, { // prettier-ignore headers: { - "authorization": `Bearer ${await getToken()}`, + "authorization": `Bearer ${token || await getToken()}`, "content-type": `application/json`, }, method: "POST", From e59ccf88b366a8e2e2bff8fab838a23765432321 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Tue, 21 Oct 2025 00:11:18 +0700 Subject: [PATCH 10/13] fix: script create keycloak --- src/controllers/OrganizationController.ts | 145 ++++++++++++---------- 1 file changed, 77 insertions(+), 68 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 4cb17e6d..47e97934 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -7747,80 +7747,89 @@ export class OrganizationController extends Controller { */ @Get("update/org/profile-employee/create-keycloak") async createKeycloakForEmployee(@Request() request: RequestWithUser) { - const [profiles, token] = await Promise.all([ - this.profileEmployeeRepo.find({ - where: { - keycloak: IsNull(), - isActive: true, - }, - relations: ["roleKeycloaks"], - }), - getToken(), - ]); - - if (!token) - throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถเชื่อมต่อ Keycloak"); - let check: number = 0; - 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, token); - let userId: any = ""; - if (checkUser.length == 0) { - userId = await createUser( - _item.citizenId, - password, - { - firstName: _item.firstName, - lastName: _item.lastName, - // email: _item.email, - }, - token, - ); - if (typeof userId !== "string") { - throw new Error(userId.errorMessage); - } - } else { - userId = checkUser[0].id; - } + const profiles = await this.profileEmployeeRepo.find({ + where: { + keycloak: IsNull(), + isLeave: false, + isRetirement: false, + }, + order: { citizenId: "ASC" }, + relations: ["roleKeycloaks"], + }); - const list = await getRoles("", token); - 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"), - token, - ); + // ดึงข้อมูลที่ใช้บ่อยก่อน (cache) + const rolesList = await getRoles(); + if (!Array.isArray(rolesList)) + throw new Error("Failed. Cannot get role(s) data from the server."); - 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.profileEmployeeRepo.save(_item); - check += 1; - } + const roleKeycloak = await this.roleKeycloakRepo.find({ + where: { id: "8a1a0dc9-304c-4e5b-a90a-65f841048212" }, + }); + + // Process แบบ batch เพื่อลดการเรียก API ทีละตัว + 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) => { + 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])); + check += 1; + await this.profileEmployeeRepo.save(_item); + } + } catch (error) { + console.error(`Error processing ${_item.citizenId}:`, error); + } + }), + ); + } const total = profiles.length; - return new HttpSuccess({ total, successAmount: check }); } } From 42c8f34f1510483d430cb29f99d8e538d8cbd7d6 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Tue, 21 Oct 2025 10:12:23 +0700 Subject: [PATCH 11/13] fix script: retire remove from org --- src/controllers/OrganizationController.ts | 83 ++++++++++++++--------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 47e97934..8544c1f3 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -7321,41 +7321,30 @@ export class OrganizationController extends Controller { } /** - * API ลบคนในโครงสร้าง + * API ลบข้าราชการในโครงสร้าง * - * @summary - ลบคนในโครงสร้าง (ADMIN) + * @summary - ลบข้าราชการในโครงสร้าง (ADMIN) * */ - @Get("delete/profile/org/{orgRevisionId}") - async deleteRetireInOrg(@Path() orgRevisionId: string, @Request() request: RequestWithUser) { - const [posMasters, posMastersEmployee] = await Promise.all([ - this.posMasterRepository.find({ - where: { - orgRevisionId, - current_holder: { - isLeave: true, - isRetirement: true, - leaveType: IsNull(), - }, - positions: { positionIsSelected: true }, + @Get("delete/profile-officer/org/{orgRevisionId}") + async deleteOfficerRetireInOrg( + @Path() orgRevisionId: string, + @Request() request: RequestWithUser, + ) { + const posMasters = await this.posMasterRepository.find({ + where: { + orgRevisionId, + current_holderId: Not(IsNull()), + current_holder: { + isLeave: true, + isRetirement: true, }, - relations: ["positions"], - }), - this.employeePosMasterRepository.find({ - where: { - orgRevisionId, - current_holder: { - isLeave: true, - isRetirement: true, - leaveType: IsNull(), - }, - }, - relations: ["positions"], - }), - ]); + // positions: { positionIsSelected: true }, + }, + relations: ["positions"], + }); let checkOfficer = 0; - let checkEmployee = 0; await Promise.all([ posMasters.map(async (posMaster) => { @@ -7371,6 +7360,38 @@ export class OrganizationController extends Controller { await this.posMasterRepository.save(posMaster); await CreatePosMasterHistoryOfficer(posMaster.id, null); }), + ]); + + return new HttpSuccess({ + totalOfficer: posMasters.length, + officerSuccessAmount: checkOfficer, + }); + } + + /** + * API ลบลูกจ้างในโครงสร้าง + * + * @summary - ลบลูกจ้างในโครงสร้าง (ADMIN) + * + */ + @Get("delete/profile-emp/org/{orgRevisionId}") + async deleteRetireEmpInOrg(@Path() orgRevisionId: string, @Request() request: RequestWithUser) { + const posMastersEmployee = await this.employeePosMasterRepository.find({ + where: { + orgRevisionId, + current_holderId: Not(IsNull()), + current_holder: { + isLeave: true, + isRetirement: true, + }, + // positions: { positionIsSelected: true }, + }, + relations: ["positions"], + }); + + let checkEmployee = 0; + + await Promise.all( posMastersEmployee.map(async (posMaster) => { posMaster.current_holderId = null; posMaster.isSit = false; @@ -7384,12 +7405,10 @@ export class OrganizationController extends Controller { await this.employeePosMasterRepository.save(posMaster); await CreatePosMasterHistoryEmployee(posMaster.id, null); }), - ]); + ); return new HttpSuccess({ - totalOfficer: posMasters.length, totalEmployee: posMastersEmployee.length, - officerSuccessAmount: checkOfficer, employeeSuccessAmount: checkEmployee, }); } From 9ba31d40eb9f9d44fc60bbd6a70d5fd5aa5b5277 Mon Sep 17 00:00:00 2001 From: adisak Date: Tue, 21 Oct 2025 15:27:41 +0700 Subject: [PATCH 12/13] #1888 --- src/controllers/OrganizationDotnetController.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index fb615566..67d39ed8 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -4389,7 +4389,7 @@ export class OrganizationDotnetController extends Controller { }, ) { let typeCondition: any = {}; - if (body.role === "CHILD" || body.role === "PARENT" || body.role === "ROOT") { + if (body.role === "CHILD" || body.role === "PARENT") { switch (body.node) { case 0: typeCondition = { @@ -4516,6 +4516,12 @@ export class OrganizationDotnetController extends Controller { typeCondition = {}; break; } + } else if (body.role === "ROOT") { + typeCondition = { + orgRoot: { + ancestorDNA: body.nodeId, + }, + }; } let profile = await this.profileRepo.find({ From 783bb95e16bc0af19ae74785e818d34039d8c78d Mon Sep 17 00:00:00 2001 From: adisak Date: Tue, 21 Oct 2025 17:42:21 +0700 Subject: [PATCH 13/13] fix --- src/controllers/OrganizationDotnetController.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 67d39ed8..52acf7f0 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -4430,7 +4430,7 @@ export class OrganizationDotnetController extends Controller { typeCondition = {}; break; } - } else if (body.role === "OWNER") { + } else if (body.role === "OWNER" || body.role === "ROOT") { switch (body.reqNode) { case 0: typeCondition = { @@ -4516,12 +4516,6 @@ export class OrganizationDotnetController extends Controller { typeCondition = {}; break; } - } else if (body.role === "ROOT") { - typeCondition = { - orgRoot: { - ancestorDNA: body.nodeId, - }, - }; } let profile = await this.profileRepo.find({ @@ -4990,7 +4984,7 @@ export class OrganizationDotnetController extends Controller { }, ) { let typeCondition: any = {}; - if (body.role === "CHILD" || body.role === "PARENT" || body.role === "ROOT") { + if (body.role === "CHILD" || body.role === "PARENT") { switch (body.node) { case 0: typeCondition = { @@ -5034,7 +5028,7 @@ export class OrganizationDotnetController extends Controller { typeCondition = {}; break; } - } else if (body.role === "OWNER") { + } else if (body.role === "OWNER" || body.role === "ROOT") { switch (body.reqNode) { case 0: typeCondition = {