import { RoleKeycloak } from "./../entities/RoleKeycloak"; 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 { Controller, Get, Post, Put, Route, Security, Tags, Body, Path, Request, Response, } from "tsoa"; import { CreateOrgRevision, OrgRevision } from "../entities/OrgRevision"; import { AppDataSource } from "../database/data-source"; import HttpSuccess from "../interfaces/http-success"; import { OrgChild1 } from "../entities/OrgChild1"; import HttpError from "../interfaces/http-error"; import HttpStatusCode from "../interfaces/http-status"; import { In, IsNull, Not, Like } from "typeorm"; import { OrgRoot } from "../entities/OrgRoot"; import { OrgChild2 } from "../entities/OrgChild2"; import { OrgChild3 } from "../entities/OrgChild3"; import { OrgChild4 } from "../entities/OrgChild4"; import { PosMaster } from "../entities/PosMaster"; import { Profile } from "../entities/Profile"; import { RequestWithUser } from "../middlewares/user"; import permission from "../interfaces/permission"; import { checkQueueInProgress, resolveNodeId, resolveNodeLevel, setLogDataDiff, } from "../interfaces/utils"; import { sendToQueueOrg, sendToQueueOrgDraft } from "../services/rabbitmq"; import { PosType } from "../entities/PosType"; import { PosLevel } from "../entities/PosLevel"; import { PermissionOrg } from "../entities/PermissionOrg"; import { deleteUser, getToken, createUser, getUserByUsername, getRoles, addUserRoles, updateUserAttributes, } from "../keycloak"; import { getPositionCountsAggregated, getPositionCount, PositionCountsByNode, } from "../services/OrganizationService"; import { BatchSavePosMasterHistoryOfficer, CreatePosMasterHistoryEmployee, CreatePosMasterHistoryOfficer, SavePosMasterHistoryOfficer, } from "../services/PositionService"; import { orgStructureCache } from "../utils/OrgStructureCache"; import { OrgIdMapping, AllOrgMappings, SavePosMasterHistory } from "../interfaces/OrgMapping"; import { OrgPermissionData, NodeLevel } from "../interfaces/OrgTypes"; import { formatPosMaster, generateLabelName, filterPosMasters, getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; @Route("api/v1/org") @Tags("Organization") @Security("bearerAuth") @Response( HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", ) export class OrganizationController extends Controller { private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); private orgRootRepository = AppDataSource.getRepository(OrgRoot); private child1Repository = AppDataSource.getRepository(OrgChild1); private child2Repository = AppDataSource.getRepository(OrgChild2); private child3Repository = AppDataSource.getRepository(OrgChild3); private child4Repository = AppDataSource.getRepository(OrgChild4); private posMasterRepository = AppDataSource.getRepository(PosMaster); private profileRepo = AppDataSource.getRepository(Profile); 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 positionRepository = AppDataSource.getRepository(Position); private profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); private employeePosMasterRepository = AppDataSource.getRepository(EmployeePosMaster); private employeePositionRepository = AppDataSource.getRepository(EmployeePosition); private roleKeycloakRepo = AppDataSource.getRepository(RoleKeycloak); /** * API ล้างข้อมูล * * @summary ล้างข้อมูล * */ @Get("clear-db") async ClearDb() { return new HttpSuccess(); } /** * API รายการประวัติโครงสร้าง * * @summary ORG_020 - รายการประวัติโครงสร้าง #21 * */ @Get("history") async GetHistory() { const orgRevision = await this.orgRevisionRepository.find({ select: ["id", "orgRevisionName", "orgRevisionIsCurrent", "createdAt", "orgRevisionIsDraft"], order: { createdAt: "DESC" }, }); // if (!orgRevision) { // return new HttpSuccess([]); // } const mapOrgRevisions = orgRevision.map((revision) => ({ orgRevisionId: revision.id, orgRevisionName: revision.orgRevisionName, orgRevisionIsCurrent: revision.orgRevisionIsCurrent, orgRevisionCreatedAt: revision.createdAt, orgRevisionIsDraft: revision.orgRevisionIsDraft, })); return new HttpSuccess(mapOrgRevisions); } /** * API โครงสร้างปัจจุบันที่ใช้อยู่ * * @summary ORG_021 - โครงสร้างปัจจุบันที่ใช้อยู่ #22 * */ @Get("active") async GetActive() { const orgRevisionActive = await this.orgRevisionRepository.findOne({ where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); const orgRevisionDraf = await this.orgRevisionRepository.findOne({ where: { orgRevisionIsCurrent: false, orgRevisionIsDraft: true }, }); const mapData = { activeId: orgRevisionActive == null ? null : orgRevisionActive.id, activeName: orgRevisionActive == null ? null : orgRevisionActive.orgRevisionName, draftId: orgRevisionDraf == null ? null : orgRevisionDraf.id, draftName: orgRevisionDraf == null ? null : orgRevisionDraf.orgRevisionName, orgPublishDate: orgRevisionDraf == null ? null : orgRevisionDraf.orgPublishDate, isPublic: orgRevisionDraf == null || orgRevisionDraf.orgRevisionName == null ? false : true, }; return new HttpSuccess(mapData); } /** * API สร้างแบบร่างโครงสร้าง * * @summary ORG_022 - สร้างโครงสร้างใหม่ #23 * */ @Post("draft") async CreateOrgRevision( @Body() requestBody: CreateOrgRevision, @Request() request: RequestWithUser, ) { try { // CheckQueueInProgress const [isBusyDraft, isBusyPublish] = await Promise.all([ checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG_DRAFT}`), checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG}`), ]); // console.log("✅ ตรวจสอบแล้ว Draft Busy:", isBusyDraft); // console.log("✅ ตรวจสอบแล้ว Publish Busy:", isBusyPublish); if (isBusyDraft || isBusyPublish) { // console.log("🚫 พบว่ามีงานอยู่ในคิว — error") throw new HttpError( HttpStatusCode.CONFLICT, "ไม่สามารถดำเนินการได้ หากกำลังเผยแพร่หรือสร้างแบบร่างโครงสร้างหน่วยงาน", ); } //new main revision const before = null; const revision = Object.assign(new OrgRevision(), requestBody) as OrgRevision; revision.orgRevisionIsDraft = true; revision.orgRevisionIsCurrent = false; revision.createdUserId = request.user.sub; revision.createdFullName = request.user.name; revision.lastUpdateUserId = request.user.sub; revision.lastUpdateFullName = request.user.name; revision.createdAt = new Date(); revision.lastUpdatedAt = new Date(); await this.orgRevisionRepository.save(revision, { data: request }); setLogDataDiff(request, { before, after: revision }); const msg = { data: { requestBody: requestBody, request: request.user, revision: revision, }, user: request.user, }; await sendToQueueOrgDraft(msg); return new HttpSuccess("Draft is being created... Processing in the background."); } catch (error: any) { throw error; } } /** * API สร้างแบบร่างโครงสร้าง * * @summary ORG_022 - สร้างโครงสร้างใหม่ #23 * */ @Post("finddna") async FindDnaOrg( @Body() requestBody: { root: string[] | null; child1: string[] | null; child2: string[] | null; child3: string[] | null; child4: string[] | null; privilege: string; }, @Request() request: RequestWithUser, ) { const _data = []; if (requestBody.root != null && requestBody.root.length > 0) { for (const x of requestBody.root) { const data = await this.orgRootRepository.findOne({ where: { id: x }, }); if (data) { _data.push(data.ancestorDNA); } } requestBody.root = _data; } const _data1 = []; if (requestBody.child1 != null && requestBody.child1.length > 0) { for (const x of requestBody.child1) { const data = await this.child1Repository.findOne({ where: { id: x }, }); if (data) { _data1.push(data.ancestorDNA); } } requestBody.child1 = _data1; } const _data2 = []; if (requestBody.child2 != null && requestBody.child2.length > 0) { for (const x of requestBody.child2) { const data = await this.child2Repository.findOne({ where: { id: x }, }); if (data) { _data2.push(data.ancestorDNA); } } requestBody.child2 = _data2; } const _data3 = []; if (requestBody.child3 != null && requestBody.child3.length > 0) { for (const x of requestBody.child3) { const data = await this.child3Repository.findOne({ where: { id: x }, }); if (data) { _data3.push(data.ancestorDNA); } } requestBody.child3 = _data3; } const _data4 = []; if (requestBody.child4 != null && requestBody.child4.length > 0) { for (const x of requestBody.child4) { const data = await this.child4Repository.findOne({ where: { id: x }, }); if (data) { _data4.push(data.ancestorDNA); } } requestBody.child4 = _data4; } return new HttpSuccess(requestBody); } /** * API หา ORG DNA ID * * @summary หา ORG DNA ID * */ @Get("finddna-by-keycloak/{keycloakId}") async FindDnaOrgByKeycloakId(@Path() keycloakId: string) { let reply: any; const profileByKeycloak: any = await this.profileRepo.findOne({ where: { keycloak: keycloakId }, select: ["id", "keycloak"], }); const orgRevision = await this.orgRevisionRepository.findOne({ select: ["id"], where: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true, }, }); const posMaster = await AppDataSource.getRepository(PosMaster) .createQueryBuilder("pos") .leftJoin("pos.orgRoot", "orgRoot") .leftJoin("pos.orgChild1", "orgChild1") .leftJoin("pos.orgChild2", "orgChild2") .leftJoin("pos.orgChild3", "orgChild3") .leftJoin("pos.orgChild4", "orgChild4") .where("pos.current_holderId = :holderId", { holderId: profileByKeycloak.id, }) .andWhere("pos.orgRevisionId = :revId", { revId: orgRevision?.id, }) .select([ "pos.id", "orgRoot.ancestorDNA as rootDnaId", "orgChild1.ancestorDNA as child1DnaId", "orgChild2.ancestorDNA as child2DnaId", "orgChild3.ancestorDNA as child3DnaId", "orgChild4.ancestorDNA as child4DnaId", ]) .getRawOne(); if (!posMaster) { reply = { rootDnaId: null, child1DnaId: null, child2DnaId: null, child3DnaId: null, child4DnaId: null, }; } else { reply = { rootDnaId: posMaster.rootDnaId, child1DnaId: posMaster.child1DnaId, child2DnaId: posMaster.child2DnaId, child3DnaId: posMaster.child3DnaId, child4DnaId: posMaster.child4DnaId, }; } return new HttpSuccess(reply); } /** * API เช็คสถานะโครงสร้างว่าส่งคนไปออกคำสั่งหรือไม่ * * @summary API เช็คสถานะโครงสร้างว่าส่งคนไปออกคำสั่งหรือไม่ * * @param {string} id Id OrgRevison */ @Get("lock/{id}") async GetById(@Request() request: RequestWithUser, @Path() id: string) { //add check permission const orgRevision = await this.orgRevisionRepository.findOne({ where: { id: id }, }); if (!orgRevision) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้าง"); } return new HttpSuccess(orgRevision.isLock); } /** * API รายละเอียดโครงสร้าง * * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN Menu) #25 * */ @Get("admin/{id}") async detailForAdmin(@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, "ไม่พบข้อมูล"); } // let attrOwnership = null; if ( orgRevision.orgRevisionIsDraft == true && orgRevision.orgRevisionIsCurrent == false && request.user.role.includes("SUPER_ADMIN") // attrOwnership == false ) { const profile = await this.profileRepo.findOne({ where: { keycloak: request.user.sub }, // relations: ["permissionProfiles"], }); if (!profile) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งานในทะเบียนประวัติ"); } // _data = { // root: profile.permissionProfiles.map((x) => x.orgRootId), // child1: null, // child2: null, // child3: null, // child4: null, // }; } const orgRootData = await AppDataSource.getRepository(OrgRoot) .createQueryBuilder("orgRoot") .where("orgRoot.orgRevisionId = :id", { id }) // .andWhere( // _data.root != undefined && _data.root != null // ? _data.root[0] != null // ? `orgRoot.id IN (:...node)` // : `orgRoot.id is null` // : "1=1", // { // node: _data.root, // }, // ) .select([ "orgRoot.id", "orgRoot.isDeputy", "orgRoot.isCommission", "orgRoot.orgRootName", "orgRoot.orgRootShortName", "orgRoot.orgRootCode", "orgRoot.orgRootOrder", "orgRoot.orgRootPhoneEx", "orgRoot.orgRootPhoneIn", "orgRoot.orgRootFax", "orgRoot.orgRevisionId", "orgRoot.orgRootRank", "orgRoot.orgRootRankSub", "orgRoot.DEPARTMENT_CODE", "orgRoot.DIVISION_CODE", "orgRoot.SECTION_CODE", "orgRoot.JOB_CODE", "orgRoot.responsibility", ]) .orderBy("orgRoot.orgRootOrder", "ASC") .getMany(); const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null; const orgChild1Data = orgRootIds && orgRootIds.length > 0 ? await AppDataSource.getRepository(OrgChild1) .createQueryBuilder("orgChild1") .where("orgChild1.orgRootId IN (:...ids)", { ids: orgRootIds }) // .andWhere( // _data.child1 != undefined && _data.child1 != null // ? _data.child1[0] != null // ? `orgChild1.id IN (:...node)` // : `orgChild1.id is null` // : "1=1", // { // node: _data.child1, // }, // ) .select([ "orgChild1.id", "orgChild1.isOfficer", "orgChild1.isInformation", "orgChild1.orgChild1Name", "orgChild1.orgChild1ShortName", "orgChild1.orgChild1Code", "orgChild1.orgChild1Order", "orgChild1.orgChild1PhoneEx", "orgChild1.orgChild1PhoneIn", "orgChild1.orgChild1Fax", "orgChild1.orgRootId", "orgChild1.orgChild1Rank", "orgChild1.orgChild1RankSub", "orgChild1.DEPARTMENT_CODE", "orgChild1.DIVISION_CODE", "orgChild1.SECTION_CODE", "orgChild1.JOB_CODE", "orgChild1.responsibility", ]) .orderBy("orgChild1.orgChild1Order", "ASC") .getMany() : []; const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id) || null; const orgChild2Data = orgChild1Ids && orgChild1Ids.length > 0 ? await AppDataSource.getRepository(OrgChild2) .createQueryBuilder("orgChild2") .where("orgChild2.orgChild1Id IN (:...ids)", { ids: orgChild1Ids }) // .andWhere( // _data.child2 != undefined && _data.child2 != null // ? _data.child2[0] != null // ? `orgChild2.id IN (:...node)` // : `orgChild2.id is null` // : "1=1", // { // node: _data.child2, // }, // ) .select([ "orgChild2.id", "orgChild2.orgChild2Name", "orgChild2.orgChild2ShortName", "orgChild2.orgChild2Code", "orgChild2.orgChild2Order", "orgChild2.orgChild2PhoneEx", "orgChild2.orgChild2PhoneIn", "orgChild2.orgChild2Fax", "orgChild2.orgRootId", "orgChild2.orgChild2Rank", "orgChild2.orgChild2RankSub", "orgChild2.DEPARTMENT_CODE", "orgChild2.DIVISION_CODE", "orgChild2.SECTION_CODE", "orgChild2.JOB_CODE", "orgChild2.orgChild1Id", "orgChild2.responsibility", ]) .orderBy("orgChild2.orgChild2Order", "ASC") .getMany() : []; const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id) || null; const orgChild3Data = orgChild2Ids && orgChild2Ids.length > 0 ? await AppDataSource.getRepository(OrgChild3) .createQueryBuilder("orgChild3") .where("orgChild3.orgChild2Id IN (:...ids)", { ids: orgChild2Ids }) // .andWhere( // _data.child3 != undefined && _data.child3 != null // ? _data.child3[0] != null // ? `orgChild3.id IN (:...node)` // : `orgChild3.id is null` // : "1=1", // { // node: _data.child3, // }, // ) .select([ "orgChild3.id", "orgChild3.orgChild3Name", "orgChild3.orgChild3ShortName", "orgChild3.orgChild3Code", "orgChild3.orgChild3Order", "orgChild3.orgChild3PhoneEx", "orgChild3.orgChild3PhoneIn", "orgChild3.orgChild3Fax", "orgChild3.orgRootId", "orgChild3.orgChild3Rank", "orgChild3.orgChild3RankSub", "orgChild3.DEPARTMENT_CODE", "orgChild3.DIVISION_CODE", "orgChild3.SECTION_CODE", "orgChild3.JOB_CODE", "orgChild3.orgChild2Id", "orgChild3.responsibility", ]) .orderBy("orgChild3.orgChild3Order", "ASC") .getMany() : []; const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id) || null; const orgChild4Data = orgChild3Ids && orgChild3Ids.length > 0 ? await AppDataSource.getRepository(OrgChild4) .createQueryBuilder("orgChild4") .where("orgChild4.orgChild3Id IN (:...ids)", { ids: orgChild3Ids }) // .andWhere( // _data.child4 != undefined && _data.child4 != null // ? _data.child4[0] != null // ? `orgChild4.id IN (:...node)` // : `orgChild4.id is null` // : "1=1", // { // node: _data.child4, // }, // ) .select([ "orgChild4.id", "orgChild4.orgChild4Name", "orgChild4.orgChild4ShortName", "orgChild4.orgChild4Code", "orgChild4.orgChild4Order", "orgChild4.orgChild4PhoneEx", "orgChild4.orgChild4PhoneIn", "orgChild4.orgChild4Fax", "orgChild4.orgRootId", "orgChild4.orgChild4Rank", "orgChild4.orgChild4RankSub", "orgChild4.DEPARTMENT_CODE", "orgChild4.DIVISION_CODE", "orgChild4.SECTION_CODE", "orgChild4.JOB_CODE", "orgChild4.orgChild3Id", "orgChild4.responsibility", ]) .orderBy("orgChild4.orgChild4Order", "ASC") .getMany() : []; const formattedData = await Promise.all( orgRootData.map(async (orgRoot) => { return { orgTreeId: orgRoot.id, orgLevel: 0, orgName: orgRoot.orgRootName, orgTreeName: orgRoot.orgRootName, orgTreeShortName: orgRoot.orgRootShortName, orgTreeCode: orgRoot.orgRootCode, orgCode: orgRoot.orgRootCode + "00", orgTreeRank: orgRoot.orgRootRank, orgTreeRankSub: orgRoot.orgRootRankSub, DEPARTMENT_CODE: orgRoot.DEPARTMENT_CODE, DIVISION_CODE: orgRoot.DIVISION_CODE, SECTION_CODE: orgRoot.SECTION_CODE, JOB_CODE: orgRoot.JOB_CODE, orgTreeOrder: orgRoot.orgRootOrder, orgTreePhoneEx: orgRoot.orgRootPhoneEx, orgTreePhoneIn: orgRoot.orgRootPhoneIn, orgTreeFax: orgRoot.orgRootFax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgRoot.responsibility, isOfficer: false, isDeputy: orgRoot.isDeputy, isCommission: orgRoot.isCommission, labelName: orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, totalPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id }, }), totalPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, current_holderId: Not(IsNull()) || Not(""), }, }), totalPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, current_holderId: IsNull() || "", }, }), totalPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, next_holderId: Not(IsNull()) || Not(""), }, }), totalPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, next_holderId: IsNull() || "", }, }), totalRootPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", }, }), totalRootPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", current_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", current_holderId: IsNull() || "", }, }), totalRootPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", next_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", next_holderId: IsNull() || "", }, }), children: await Promise.all( orgChild1Data .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id) .map(async (orgChild1) => ({ orgTreeId: orgChild1.id, orgRootId: orgRoot.id, orgLevel: 1, orgName: `${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild1.orgChild1Name, orgTreeShortName: orgChild1.orgChild1ShortName, orgTreeCode: orgChild1.orgChild1Code, orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code, orgTreeRank: orgChild1.orgChild1Rank, orgTreeRankSub: orgChild1.orgChild1RankSub, DEPARTMENT_CODE: orgChild1.DEPARTMENT_CODE, DIVISION_CODE: orgChild1.DIVISION_CODE, SECTION_CODE: orgChild1.SECTION_CODE, JOB_CODE: orgChild1.JOB_CODE, orgTreeOrder: orgChild1.orgChild1Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild1.orgChild1PhoneEx, orgTreePhoneIn: orgChild1.orgChild1PhoneIn, orgTreeFax: orgChild1.orgChild1Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild1.responsibility, isOfficer: orgChild1.isOfficer, isInformation: orgChild1.isInformation, labelName: orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName, totalPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild1Id: orgChild1.id }, }), totalPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild1Id: orgChild1.id, current_holderId: Not(IsNull()) || Not(""), }, }), totalPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild1Id: orgChild1.id, current_holderId: IsNull() || "", }, }), totalPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild1Id: orgChild1.id, next_holderId: Not(IsNull()) || Not(""), }, }), totalPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild1Id: orgChild1.id, next_holderId: IsNull() || "", }, }), totalRootPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", }, }), totalRootPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", current_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", current_holderId: IsNull() || "", }, }), totalRootPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", next_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", next_holderId: IsNull() || "", }, }), children: await Promise.all( orgChild2Data .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id) .map(async (orgChild2) => ({ orgTreeId: orgChild2.id, orgRootId: orgChild1.id, orgLevel: 2, orgName: `${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild2.orgChild2Name, orgTreeShortName: orgChild2.orgChild2ShortName, orgTreeCode: orgChild2.orgChild2Code, orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code, orgTreeRank: orgChild2.orgChild2Rank, orgTreeRankSub: orgChild2.orgChild2RankSub, DEPARTMENT_CODE: orgChild2.DEPARTMENT_CODE, DIVISION_CODE: orgChild2.DIVISION_CODE, SECTION_CODE: orgChild2.SECTION_CODE, JOB_CODE: orgChild2.JOB_CODE, orgTreeOrder: orgChild2.orgChild2Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild2.orgChild2PhoneEx, orgTreePhoneIn: orgChild2.orgChild2PhoneIn, orgTreeFax: orgChild2.orgChild2Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild2.responsibility, isOfficer: orgChild1.isOfficer, labelName: orgChild2.orgChild2Name + " " + orgRoot.orgRootCode + orgChild2.orgChild2Code + " " + orgChild2.orgChild2ShortName, totalPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild2Id: orgChild2.id, }, }), totalPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild2Id: orgChild2.id, current_holderId: Not(IsNull()) || Not(""), }, }), totalPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild2Id: orgChild2.id, current_holderId: IsNull() || "", }, }), totalPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild2Id: orgChild2.id, next_holderId: Not(IsNull()) || Not(""), }, }), totalPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild2Id: orgChild2.id, next_holderId: IsNull() || "", }, }), totalRootPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", }, }), totalRootPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", current_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", current_holderId: IsNull() || "", }, }), totalRootPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", next_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", next_holderId: IsNull() || "", }, }), children: await Promise.all( orgChild3Data .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id) .map(async (orgChild3) => ({ orgTreeId: orgChild3.id, orgRootId: orgChild2.id, orgLevel: 3, orgName: `${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild3.orgChild3Name, orgTreeShortName: orgChild3.orgChild3ShortName, orgTreeCode: orgChild3.orgChild3Code, orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code, orgTreeRank: orgChild3.orgChild3Rank, orgTreeRankSub: orgChild3.orgChild3RankSub, DEPARTMENT_CODE: orgChild3.DEPARTMENT_CODE, DIVISION_CODE: orgChild3.DIVISION_CODE, SECTION_CODE: orgChild3.SECTION_CODE, JOB_CODE: orgChild3.JOB_CODE, orgTreeOrder: orgChild3.orgChild3Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild3.orgChild3PhoneEx, orgTreePhoneIn: orgChild3.orgChild3PhoneIn, orgTreeFax: orgChild3.orgChild3Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild3.responsibility, isOfficer: orgChild1.isOfficer, labelName: orgChild3.orgChild3Name + " " + orgRoot.orgRootCode + orgChild3.orgChild3Code + " " + orgChild3.orgChild3ShortName, totalPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild3Id: orgChild3.id, }, }), totalPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild3Id: orgChild3.id, current_holderId: Not(IsNull()) || Not(""), }, }), totalPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild3Id: orgChild3.id, current_holderId: IsNull() || "", }, }), totalPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild3Id: orgChild3.id, next_holderId: Not(IsNull()) || Not(""), }, }), totalPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild3Id: orgChild3.id, next_holderId: IsNull() || "", }, }), totalRootPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: IsNull() || "", }, }), totalRootPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: IsNull() || "", current_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: IsNull() || "", current_holderId: IsNull() || "", }, }), totalRootPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: IsNull() || "", next_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: IsNull() || "", next_holderId: IsNull() || "", }, }), children: await Promise.all( orgChild4Data .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id) .map(async (orgChild4) => ({ orgTreeId: orgChild4.id, orgRootId: orgChild3.id, orgLevel: 4, orgName: `${orgChild4.orgChild4Name}/${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild4.orgChild4Name, orgTreeShortName: orgChild4.orgChild4ShortName, orgTreeCode: orgChild4.orgChild4Code, orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code, orgTreeRank: orgChild4.orgChild4Rank, orgTreeRankSub: orgChild4.orgChild4RankSub, DEPARTMENT_CODE: orgChild4.DEPARTMENT_CODE, DIVISION_CODE: orgChild4.DIVISION_CODE, SECTION_CODE: orgChild4.SECTION_CODE, JOB_CODE: orgChild4.JOB_CODE, orgTreeOrder: orgChild4.orgChild4Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild4.orgChild4PhoneEx, orgTreePhoneIn: orgChild4.orgChild4PhoneIn, orgTreeFax: orgChild4.orgChild4Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild4.responsibility, isOfficer: orgChild1.isOfficer, labelName: orgChild4.orgChild4Name + " " + orgRoot.orgRootCode + orgChild4.orgChild4Code + " " + orgChild4.orgChild4ShortName, totalPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild4Id: orgChild4.id, }, }), totalPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild4Id: orgChild4.id, current_holderId: Not(IsNull()) || Not(""), }, }), totalPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild4Id: orgChild4.id, current_holderId: IsNull() || "", }, }), totalPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild4Id: orgChild4.id, next_holderId: Not(IsNull()) || Not(""), }, }), totalPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgChild4Id: orgChild4.id, next_holderId: IsNull() || "", }, }), totalRootPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: orgChild4.id, }, }), totalRootPositionCurrentUse: await this.posMasterRepository.count( { where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: orgChild4.id, current_holderId: Not(IsNull()) || Not(""), }, }, ), totalRootPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: orgChild4.id, current_holderId: IsNull() || "", }, }), totalRootPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: orgChild4.id, next_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionNextVacant: await this.posMasterRepository.count( { where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: orgChild1.id, orgChild2Id: orgChild2.id, orgChild3Id: orgChild3.id, orgChild4Id: orgChild4.id, next_holderId: IsNull() || "", }, }, ), })), ), })), ), })), ), })), ), }; }), ); return new HttpSuccess(formattedData); } /** * API รายละเอียดโครงสร้าง * * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN) #25 * */ @Get("super-admin/{id}") async detailSuperAdmin(@Path() id: string, @Request() request: RequestWithUser) { const orgRevision = await this.orgRevisionRepository.findOne({ where: { id: id }, }); if (!orgRevision) return new HttpSuccess([]); let rootId: any = null; if (!request.user.role.includes("SUPER_ADMIN")) { const profile = await this.profileRepo.findOne({ where: { keycloak: request.user.sub, }, select: ["id"], }); if (profile == null) return new HttpSuccess([]); const posMaster = await this.posMasterRepository.findOne({ where: orgRevision.orgRevisionIsCurrent && !orgRevision.orgRevisionIsDraft ? { orgRevisionId: id, current_holderId: profile.id, } : { orgRevisionId: id, next_holderId: profile.id, }, }); if (!posMaster) return new HttpSuccess([]); rootId = posMaster.orgRootId; } // OPTIMIZED: Check cache first const cachedResponse = await orgStructureCache.get(id, rootId); if (cachedResponse) { return new HttpSuccess(cachedResponse); } // OPTIMIZED: Get all position counts in ONE query (closed) // const { orgRootMap, orgChild1Map, orgChild2Map, orgChild3Map, orgChild4Map, rootPosMap } = // await getPositionCounts(id); // OPTIMIZED: Fetch orgRoot first, then fetch all child levels in parallel const orgRootData = await AppDataSource.getRepository(OrgRoot) .createQueryBuilder("orgRoot") .where("orgRoot.orgRevisionId = :id", { id }) .andWhere(rootId != null ? `orgRoot.id = :rootId` : "1=1", { rootId: rootId, }) .orderBy("orgRoot.orgRootOrder", "ASC") .getMany(); const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id); // OPTIMIZED: Fetch all child levels in parallel using orgRevisionId // This is faster than sequential queries that depend on parent IDs const [orgChild1AllData, orgChild2AllData, orgChild3AllData, orgChild4AllData] = await Promise.all([ AppDataSource.getRepository(OrgChild1) .createQueryBuilder("orgChild1") .where("orgChild1.orgRevisionId = :id", { id }) .orderBy("orgChild1.orgChild1Order", "ASC") .getMany(), AppDataSource.getRepository(OrgChild2) .createQueryBuilder("orgChild2") .where("orgChild2.orgRevisionId = :id", { id }) .orderBy("orgChild2.orgChild2Order", "ASC") .getMany(), AppDataSource.getRepository(OrgChild3) .createQueryBuilder("orgChild3") .where("orgChild3.orgRevisionId = :id", { id }) .orderBy("orgChild3.orgChild3Order", "ASC") .getMany(), AppDataSource.getRepository(OrgChild4) .createQueryBuilder("orgChild4") .where("orgChild4.orgRevisionId = :id", { id }) .orderBy("orgChild4.orgChild4Order", "ASC") .getMany(), ]); // Filter child1 data by orgRootIds (maintains backward compatibility) const orgChild1Data = orgRootIds && orgRootIds.length > 0 ? orgChild1AllData.filter((orgChild1) => orgRootIds.includes(orgChild1.orgRootId)) : []; // Build maps for efficient filtering of deeper levels const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id); const orgChild2Data = orgChild1Ids && orgChild1Ids.length > 0 ? orgChild2AllData.filter((orgChild2) => orgChild1Ids.includes(orgChild2.orgChild1Id)) : []; const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id); const orgChild3Data = orgChild2Ids && orgChild2Ids.length > 0 ? orgChild3AllData.filter((orgChild3) => orgChild2Ids.includes(orgChild3.orgChild2Id)) : []; const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id); const orgChild4Data = orgChild3Ids && orgChild3Ids.length > 0 ? orgChild4AllData.filter((orgChild4) => orgChild3Ids.includes(orgChild4.orgChild3Id)) : []; // OPTIMIZED: Build formatted data using pre-calculated counts (no nested queries!) const formattedData = orgRootData.map((orgRoot) => { // const rootCounts = getCounts(orgRootMap, orgRoot.id); // const rootPosCounts = getRootCounts(rootPosMap, orgRoot.id); return { orgTreeId: orgRoot.id, orgLevel: 0, orgName: orgRoot.orgRootName, orgTreeName: orgRoot.orgRootName, orgTreeShortName: orgRoot.orgRootShortName, orgTreeCode: orgRoot.orgRootCode, orgCode: orgRoot.orgRootCode + "00", orgTreeRank: orgRoot.orgRootRank, orgTreeRankSub: orgRoot.orgRootRankSub, orgRootDnaId: orgRoot.ancestorDNA, DEPARTMENT_CODE: orgRoot.DEPARTMENT_CODE, DIVISION_CODE: orgRoot.DIVISION_CODE, SECTION_CODE: orgRoot.SECTION_CODE, JOB_CODE: orgRoot.JOB_CODE, orgTreeOrder: orgRoot.orgRootOrder, orgTreePhoneEx: orgRoot.orgRootPhoneEx, orgTreePhoneIn: orgRoot.orgRootPhoneIn, orgTreeFax: orgRoot.orgRootFax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, isDeputy: orgRoot.isDeputy, isCommission: orgRoot.isCommission, responsibility: orgRoot.responsibility, labelName: orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, // totalPosition: rootCounts.totalPosition, // totalPositionCurrentUse: rootCounts.totalPositionCurrentUse, // totalPositionCurrentVacant: rootCounts.totalPositionCurrentVacant, // totalPositionNextUse: rootCounts.totalPositionNextUse, // totalPositionNextVacant: rootCounts.totalPositionNextVacant, // totalRootPosition: rootPosCounts.totalRootPosition, // totalRootPositionCurrentUse: rootPosCounts.totalRootPositionCurrentUse, // totalRootPositionCurrentVacant: rootPosCounts.totalRootPositionCurrentVacant, // totalRootPositionNextUse: rootPosCounts.totalRootPositionNextUse, // totalRootPositionNextVacant: rootPosCounts.totalRootPositionNextVacant, children: orgChild1Data .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id) .map((orgChild1) => { // const child1Counts = getCounts(orgChild1Map, orgChild1.id); // const child1PosKey = `${orgRoot.id}-${orgChild1.id}`; // const child1PosCounts = getRootCounts(rootPosMap, child1PosKey); return { orgTreeId: orgChild1.id, orgRootId: orgRoot.id, orgLevel: 1, orgName: `${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild1.orgChild1Name, orgTreeShortName: orgChild1.orgChild1ShortName, orgTreeCode: orgChild1.orgChild1Code, orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code, orgTreeRank: orgChild1.orgChild1Rank, orgTreeRankSub: orgChild1.orgChild1RankSub, orgRootDnaId: orgRoot.ancestorDNA, orgChild1DnaId: orgChild1.ancestorDNA, DEPARTMENT_CODE: orgChild1.DEPARTMENT_CODE, DIVISION_CODE: orgChild1.DIVISION_CODE, SECTION_CODE: orgChild1.SECTION_CODE, JOB_CODE: orgChild1.JOB_CODE, orgTreeOrder: orgChild1.orgChild1Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild1.orgChild1PhoneEx, orgTreePhoneIn: orgChild1.orgChild1PhoneIn, orgTreeFax: orgChild1.orgChild1Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild1.responsibility, isOfficer: orgChild1.isOfficer, isInformation: orgChild1.isInformation, labelName: orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName, // totalPosition: child1Counts.totalPosition, // totalPositionCurrentUse: child1Counts.totalPositionCurrentUse, // totalPositionCurrentVacant: child1Counts.totalPositionCurrentVacant, // totalPositionNextUse: child1Counts.totalPositionNextUse, // totalPositionNextVacant: child1Counts.totalPositionNextVacant, // totalRootPosition: child1PosCounts.totalRootPosition, // totalRootPositionCurrentUse: child1PosCounts.totalRootPositionCurrentUse, // totalRootPositionCurrentVacant: child1PosCounts.totalRootPositionCurrentVacant, // totalRootPositionNextUse: child1PosCounts.totalRootPositionNextUse, // totalRootPositionNextVacant: child1PosCounts.totalRootPositionNextVacant, children: orgChild2Data .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id) .map((orgChild2) => { // const child2Counts = getCounts(orgChild2Map, orgChild2.id); // const child2PosKey = `${orgRoot.id}-${orgChild1.id}-${orgChild2.id}`; // const child2PosCounts = getRootCounts(rootPosMap, child2PosKey); return { orgTreeId: orgChild2.id, orgRootId: orgChild1.id, orgLevel: 2, orgName: `${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild2.orgChild2Name, orgTreeShortName: orgChild2.orgChild2ShortName, orgTreeCode: orgChild2.orgChild2Code, orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code, orgTreeRank: orgChild2.orgChild2Rank, orgTreeRankSub: orgChild2.orgChild2RankSub, orgRootDnaId: orgRoot.ancestorDNA, orgChild1DnaId: orgChild1.ancestorDNA, orgChild2DnaId: orgChild2.ancestorDNA, DEPARTMENT_CODE: orgChild2.DEPARTMENT_CODE, DIVISION_CODE: orgChild2.DIVISION_CODE, SECTION_CODE: orgChild2.SECTION_CODE, JOB_CODE: orgChild2.JOB_CODE, orgTreeOrder: orgChild2.orgChild2Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild2.orgChild2PhoneEx, orgTreePhoneIn: orgChild2.orgChild2PhoneIn, orgTreeFax: orgChild2.orgChild2Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild2.responsibility, labelName: orgChild2.orgChild2Name + " " + orgRoot.orgRootCode + orgChild2.orgChild2Code + " " + orgChild2.orgChild2ShortName, // totalPosition: child2Counts.totalPosition, // totalPositionCurrentUse: child2Counts.totalPositionCurrentUse, // totalPositionCurrentVacant: child2Counts.totalPositionCurrentVacant, // totalPositionNextUse: child2Counts.totalPositionNextUse, // totalPositionNextVacant: child2Counts.totalPositionNextVacant, // totalRootPosition: child2PosCounts.totalRootPosition, // totalRootPositionCurrentUse: child2PosCounts.totalRootPositionCurrentUse, // totalRootPositionCurrentVacant: child2PosCounts.totalRootPositionCurrentVacant, // totalRootPositionNextUse: child2PosCounts.totalRootPositionNextUse, // totalRootPositionNextVacant: child2PosCounts.totalRootPositionNextVacant, children: orgChild3Data .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id) .map((orgChild3) => { // const child3Counts = getCounts(orgChild3Map, orgChild3.id); // const child3PosKey = `${orgRoot.id}-${orgChild1.id}-${orgChild2.id}-${orgChild3.id}`; // const child3PosCounts = getRootCounts(rootPosMap, child3PosKey); return { orgTreeId: orgChild3.id, orgRootId: orgChild2.id, orgLevel: 3, orgName: `${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild3.orgChild3Name, orgTreeShortName: orgChild3.orgChild3ShortName, orgTreeCode: orgChild3.orgChild3Code, orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code, orgTreeRank: orgChild3.orgChild3Rank, orgTreeRankSub: orgChild3.orgChild3RankSub, orgRootDnaId: orgRoot.ancestorDNA, orgChild1DnaId: orgChild1.ancestorDNA, orgChild2DnaId: orgChild2.ancestorDNA, orgChild3DnaId: orgChild3.ancestorDNA, DEPARTMENT_CODE: orgChild3.DEPARTMENT_CODE, DIVISION_CODE: orgChild3.DIVISION_CODE, SECTION_CODE: orgChild3.SECTION_CODE, JOB_CODE: orgChild3.JOB_CODE, orgTreeOrder: orgChild3.orgChild3Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild3.orgChild3PhoneEx, orgTreePhoneIn: orgChild3.orgChild3PhoneIn, orgTreeFax: orgChild3.orgChild3Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild3.responsibility, labelName: orgChild3.orgChild3Name + " " + orgRoot.orgRootCode + orgChild3.orgChild3Code + " " + orgChild3.orgChild3ShortName, // totalPosition: child3Counts.totalPosition, // totalPositionCurrentUse: child3Counts.totalPositionCurrentUse, // totalPositionCurrentVacant: child3Counts.totalPositionCurrentVacant, // totalPositionNextUse: child3Counts.totalPositionNextUse, // totalPositionNextVacant: child3Counts.totalPositionNextVacant, // totalRootPosition: child3PosCounts.totalRootPosition, // totalRootPositionCurrentUse: child3PosCounts.totalRootPositionCurrentUse, // totalRootPositionCurrentVacant: // child3PosCounts.totalRootPositionCurrentVacant, // totalRootPositionNextUse: child3PosCounts.totalRootPositionNextUse, // totalRootPositionNextVacant: child3PosCounts.totalRootPositionNextVacant, children: orgChild4Data .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id) .map((orgChild4) => { // const child4Counts = getCounts(orgChild4Map, orgChild4.id); // const child4PosKey = `${orgRoot.id}-${orgChild1.id}-${orgChild2.id}-${orgChild3.id}-${orgChild4.id}`; // const child4PosCounts = getRootCounts(rootPosMap, child4PosKey); return { orgTreeId: orgChild4.id, orgRootId: orgChild3.id, orgLevel: 4, orgName: `${orgChild4.orgChild4Name}/${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild4.orgChild4Name, orgTreeShortName: orgChild4.orgChild4ShortName, orgTreeCode: orgChild4.orgChild4Code, orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code, orgTreeRank: orgChild4.orgChild4Rank, orgTreeRankSub: orgChild4.orgChild4RankSub, orgRootDnaId: orgRoot.ancestorDNA, orgChild1DnaId: orgChild1.ancestorDNA, orgChild2DnaId: orgChild2.ancestorDNA, orgChild3DnaId: orgChild3.ancestorDNA, orgChild4DnaId: orgChild4.ancestorDNA, DEPARTMENT_CODE: orgChild4.DEPARTMENT_CODE, DIVISION_CODE: orgChild4.DIVISION_CODE, SECTION_CODE: orgChild4.SECTION_CODE, JOB_CODE: orgChild4.JOB_CODE, orgTreeOrder: orgChild4.orgChild4Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild4.orgChild4PhoneEx, orgTreePhoneIn: orgChild4.orgChild4PhoneIn, orgTreeFax: orgChild4.orgChild4Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild4.responsibility, labelName: orgChild4.orgChild4Name + " " + orgRoot.orgRootCode + orgChild4.orgChild4Code + " " + orgChild4.orgChild4ShortName, // totalPosition: child4Counts.totalPosition, // totalPositionCurrentUse: child4Counts.totalPositionCurrentUse, // totalPositionCurrentVacant: child4Counts.totalPositionCurrentVacant, // totalPositionNextUse: child4Counts.totalPositionNextUse, // totalPositionNextVacant: child4Counts.totalPositionNextVacant, // totalRootPosition: child4PosCounts.totalRootPosition, // totalRootPositionCurrentUse: // child4PosCounts.totalRootPositionCurrentUse, // totalRootPositionCurrentVacant: // child4PosCounts.totalRootPositionCurrentVacant, // totalRootPositionNextUse: child4PosCounts.totalRootPositionNextUse, // totalRootPositionNextVacant: // child4PosCounts.totalRootPositionNextVacant, children: [], }; }), }; }), }; }), }; }), }; }); // OPTIMIZED: Cache the result await orgStructureCache.set(id, rootId, formattedData); return new HttpSuccess(formattedData); } /** * API รายละเอียดโครงสร้าง * * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN) #25 * */ @Get("{id}") async detail(@Path() id: string, @Request() request: RequestWithUser) { let _data: any = { root: null, child1: null, child2: null, child3: null, child4: null, }; let _data1: any = { root: null, }; const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } }); if (!orgRevision) { 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({ where: { keycloak: request.user.sub }, relations: ["permissionProfiles", "current_holders", "current_holders.posMasterAssigns"], }); if (!profile) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งานในทะเบียนประวัติ"); } let profileAssign = profile.current_holders ?.find((x) => x.orgRevisionId === id) ?.posMasterAssigns.find((x) => x.assignId === "SYS_ORG"); if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) { 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; if (isCurrentActive) { if (profileAssign && _privilege.privilege !== "OWNER" && _privilege.privilege !== "PARENT") { 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" || _privilege.privilege == "BROTHER") { 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]; } } else { if (!attrOwnership) _data = _privilege; } } const _revision = await this.orgRevisionRepository.findOne({ where: { id: id }, }); if (!_revision) throw new HttpError(HttpStatusCode.NOT_FOUND, "not found."); const orgRootData = await AppDataSource.getRepository(OrgRoot) .createQueryBuilder("orgRoot") .where("orgRoot.orgRevisionId = :id", { id }) .andWhere( _data.root !== undefined && _data.root !== null ? _data.root[0] !== null ? `orgRoot.id IN (:...node)` : `orgRoot.id is null` : "1=1", { node: _data.root, }, ) .select([ "orgRoot.ancestorDNA", "orgRoot.id", "orgRoot.misId", "orgRoot.isDeputy", "orgRoot.isCommission", "orgRoot.orgRootName", "orgRoot.orgRootShortName", "orgRoot.orgRootCode", "orgRoot.orgRootOrder", "orgRoot.orgRootPhoneEx", "orgRoot.orgRootPhoneIn", "orgRoot.orgRootFax", "orgRoot.orgRevisionId", "orgRoot.orgRootRank", "orgRoot.orgRootRankSub", "orgRoot.DEPARTMENT_CODE", "orgRoot.DIVISION_CODE", "orgRoot.SECTION_CODE", "orgRoot.JOB_CODE", "orgRoot.responsibility", ]) .orderBy("orgRoot.orgRootOrder", "ASC") .getMany(); const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null; const orgChild1Data = orgRootIds && orgRootIds.length > 0 ? await AppDataSource.getRepository(OrgChild1) .createQueryBuilder("orgChild1") .where("orgChild1.orgRootId IN (:...ids)", { ids: orgRootIds }) .andWhere( _data.child1 !== undefined && _data.child1 !== null ? _data.child1[0] !== null ? `orgChild1.id IN (:...node)` : `orgChild1.id is null` : "1=1", { node: _data.child1, }, ) .select([ "orgChild1.id", "orgChild1.misId", "orgChild1.isOfficer", "orgChild1.isInformation", "orgChild1.orgChild1Name", "orgChild1.orgChild1ShortName", "orgChild1.orgChild1Code", "orgChild1.orgChild1Order", "orgChild1.orgChild1PhoneEx", "orgChild1.orgChild1PhoneIn", "orgChild1.orgChild1Fax", "orgChild1.orgRootId", "orgChild1.orgChild1Rank", "orgChild1.orgChild1RankSub", "orgChild1.DEPARTMENT_CODE", "orgChild1.DIVISION_CODE", "orgChild1.SECTION_CODE", "orgChild1.JOB_CODE", "orgChild1.responsibility", ]) .orderBy("orgChild1.orgChild1Order", "ASC") .getMany() : []; const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id) || null; const orgChild2Data = orgChild1Ids && orgChild1Ids.length > 0 ? await AppDataSource.getRepository(OrgChild2) .createQueryBuilder("orgChild2") .where("orgChild2.orgChild1Id IN (:...ids)", { ids: orgChild1Ids }) .andWhere( _data.child2 !== undefined && _data.child2 !== null ? _data.child2[0] !== null ? `orgChild2.id IN (:...node)` : `orgChild2.id is null` : "1=1", { node: _data.child2, }, ) .select([ "orgChild2.id", "orgChild2.misId", "orgChild2.orgChild2Name", "orgChild2.orgChild2ShortName", "orgChild2.orgChild2Code", "orgChild2.orgChild2Order", "orgChild2.orgChild2PhoneEx", "orgChild2.orgChild2PhoneIn", "orgChild2.orgChild2Fax", "orgChild2.orgRootId", "orgChild2.orgChild2Rank", "orgChild2.orgChild2RankSub", "orgChild2.DEPARTMENT_CODE", "orgChild2.DIVISION_CODE", "orgChild2.SECTION_CODE", "orgChild2.JOB_CODE", "orgChild2.orgChild1Id", "orgChild2.responsibility", ]) .orderBy("orgChild2.orgChild2Order", "ASC") .getMany() : []; const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id) || null; const orgChild3Data = orgChild2Ids && orgChild2Ids.length > 0 ? await AppDataSource.getRepository(OrgChild3) .createQueryBuilder("orgChild3") .where("orgChild3.orgChild2Id IN (:...ids)", { ids: orgChild2Ids }) .andWhere( _data.child3 !== undefined && _data.child3 !== null ? _data.child3[0] !== null ? `orgChild3.id IN (:...node)` : `orgChild3.id is null` : "1=1", { node: _data.child3, }, ) .select([ "orgChild3.id", "orgChild3.misId", "orgChild3.orgChild3Name", "orgChild3.orgChild3ShortName", "orgChild3.orgChild3Code", "orgChild3.orgChild3Order", "orgChild3.orgChild3PhoneEx", "orgChild3.orgChild3PhoneIn", "orgChild3.orgChild3Fax", "orgChild3.orgRootId", "orgChild3.orgChild3Rank", "orgChild3.orgChild3RankSub", "orgChild3.DEPARTMENT_CODE", "orgChild3.DIVISION_CODE", "orgChild3.SECTION_CODE", "orgChild3.JOB_CODE", "orgChild3.orgChild2Id", "orgChild3.responsibility", ]) .orderBy("orgChild3.orgChild3Order", "ASC") .getMany() : []; const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id) || null; const orgChild4Data = orgChild3Ids && orgChild3Ids.length > 0 ? await AppDataSource.getRepository(OrgChild4) .createQueryBuilder("orgChild4") .where("orgChild4.orgChild3Id IN (:...ids)", { ids: orgChild3Ids }) .andWhere( _data.child4 !== undefined && _data.child4 !== null ? _data.child4[0] !== null ? `orgChild4.id IN (:...node)` : `orgChild4.id is null` : "1=1", { node: _data.child4, }, ) .select([ "orgChild4.id", "orgChild4.misId", "orgChild4.orgChild4Name", "orgChild4.orgChild4ShortName", "orgChild4.orgChild4Code", "orgChild4.orgChild4Order", "orgChild4.orgChild4PhoneEx", "orgChild4.orgChild4PhoneIn", "orgChild4.orgChild4Fax", "orgChild4.orgRootId", "orgChild4.orgChild4Rank", "orgChild4.orgChild4RankSub", "orgChild4.DEPARTMENT_CODE", "orgChild4.DIVISION_CODE", "orgChild4.SECTION_CODE", "orgChild4.JOB_CODE", "orgChild4.orgChild3Id", "orgChild4.responsibility", ]) .orderBy("orgChild4.orgChild4Order", "ASC") .getMany() : []; const formattedData = await Promise.all( orgRootData.map(async (orgRoot) => { return { orgDnaId: orgRoot.ancestorDNA, orgTreeId: orgRoot.id, orgLevel: 0, misId: orgRoot.misId, orgName: orgRoot.orgRootName, orgTreeName: orgRoot.orgRootName, orgTreeShortName: orgRoot.orgRootShortName, orgTreeCode: orgRoot.orgRootCode, orgCode: orgRoot.orgRootCode + "00", orgTreeRank: orgRoot.orgRootRank, orgTreeRankSub: orgRoot.orgRootRankSub, DEPARTMENT_CODE: orgRoot.DEPARTMENT_CODE, DIVISION_CODE: orgRoot.DIVISION_CODE, SECTION_CODE: orgRoot.SECTION_CODE, JOB_CODE: orgRoot.JOB_CODE, orgTreeOrder: orgRoot.orgRootOrder, orgTreePhoneEx: orgRoot.orgRootPhoneEx, orgTreePhoneIn: orgRoot.orgRootPhoneIn, orgTreeFax: orgRoot.orgRootFax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, isDeputy: orgRoot.isDeputy, isCommission: orgRoot.isCommission, responsibility: orgRoot.responsibility, labelName: orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, children: await Promise.all( orgChild1Data .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id) .map(async (orgChild1) => ({ orgTreeId: orgChild1.id, orgRootId: orgRoot.id, orgLevel: 1, misId: orgChild1.misId, orgName: `${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild1.orgChild1Name, orgTreeShortName: orgChild1.orgChild1ShortName, orgTreeCode: orgChild1.orgChild1Code, orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code, orgTreeRank: orgChild1.orgChild1Rank, orgTreeRankSub: orgChild1.orgChild1RankSub, DEPARTMENT_CODE: orgChild1.DEPARTMENT_CODE, DIVISION_CODE: orgChild1.DIVISION_CODE, SECTION_CODE: orgChild1.SECTION_CODE, JOB_CODE: orgChild1.JOB_CODE, orgTreeOrder: orgChild1.orgChild1Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild1.orgChild1PhoneEx, orgTreePhoneIn: orgChild1.orgChild1PhoneIn, orgTreeFax: orgChild1.orgChild1Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild1.responsibility, isOfficer: orgChild1.isOfficer, isInformation: orgChild1.isInformation, labelName: orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName + "/" + orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, children: await Promise.all( orgChild2Data .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id) .map(async (orgChild2) => ({ orgTreeId: orgChild2.id, orgRootId: orgChild1.id, orgLevel: 2, misId: orgChild2.misId, orgName: `${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild2.orgChild2Name, orgTreeShortName: orgChild2.orgChild2ShortName, orgTreeCode: orgChild2.orgChild2Code, orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code, orgTreeRank: orgChild2.orgChild2Rank, orgTreeRankSub: orgChild2.orgChild2RankSub, DEPARTMENT_CODE: orgChild2.DEPARTMENT_CODE, DIVISION_CODE: orgChild2.DIVISION_CODE, SECTION_CODE: orgChild2.SECTION_CODE, JOB_CODE: orgChild2.JOB_CODE, orgTreeOrder: orgChild2.orgChild2Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild2.orgChild2PhoneEx, orgTreePhoneIn: orgChild2.orgChild2PhoneIn, orgTreeFax: orgChild2.orgChild2Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild2.responsibility, labelName: orgChild2.orgChild2Name + " " + orgRoot.orgRootCode + orgChild2.orgChild2Code + " " + orgChild2.orgChild2ShortName + "/" + orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName + "/" + orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, children: await Promise.all( orgChild3Data .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id) .map(async (orgChild3) => ({ orgTreeId: orgChild3.id, orgRootId: orgChild2.id, orgLevel: 3, misId: orgChild3.misId, orgName: `${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild3.orgChild3Name, orgTreeShortName: orgChild3.orgChild3ShortName, orgTreeCode: orgChild3.orgChild3Code, orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code, orgTreeRank: orgChild3.orgChild3Rank, orgTreeRankSub: orgChild3.orgChild3RankSub, DEPARTMENT_CODE: orgChild3.DEPARTMENT_CODE, DIVISION_CODE: orgChild3.DIVISION_CODE, SECTION_CODE: orgChild3.SECTION_CODE, JOB_CODE: orgChild3.JOB_CODE, orgTreeOrder: orgChild3.orgChild3Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild3.orgChild3PhoneEx, orgTreePhoneIn: orgChild3.orgChild3PhoneIn, orgTreeFax: orgChild3.orgChild3Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild3.responsibility, labelName: orgChild3.orgChild3Name + " " + orgRoot.orgRootCode + orgChild3.orgChild3Code + " " + orgChild3.orgChild3ShortName + "/" + orgChild2.orgChild2Name + " " + orgRoot.orgRootCode + orgChild2.orgChild2Code + " " + orgChild2.orgChild2ShortName + "/" + orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName + "/" + orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, children: await Promise.all( orgChild4Data .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id) .map(async (orgChild4) => ({ orgTreeId: orgChild4.id, orgRootId: orgChild3.id, orgLevel: 4, misId: orgChild4.misId, orgName: `${orgChild4.orgChild4Name}/${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild4.orgChild4Name, orgTreeShortName: orgChild4.orgChild4ShortName, orgTreeCode: orgChild4.orgChild4Code, orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code, orgTreeRank: orgChild4.orgChild4Rank, orgTreeRankSub: orgChild4.orgChild4RankSub, DEPARTMENT_CODE: orgChild4.DEPARTMENT_CODE, DIVISION_CODE: orgChild4.DIVISION_CODE, SECTION_CODE: orgChild4.SECTION_CODE, JOB_CODE: orgChild4.JOB_CODE, orgTreeOrder: orgChild4.orgChild4Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild4.orgChild4PhoneEx, orgTreePhoneIn: orgChild4.orgChild4PhoneIn, orgTreeFax: orgChild4.orgChild4Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild4.responsibility, labelName: orgChild4.orgChild4Name + " " + orgRoot.orgRootCode + orgChild4.orgChild4Code + " " + orgChild4.orgChild4ShortName + "/" + orgChild3.orgChild3Name + " " + orgRoot.orgRootCode + orgChild3.orgChild3Code + " " + orgChild3.orgChild3ShortName + "/" + orgChild2.orgChild2Name + " " + orgRoot.orgRootCode + orgChild2.orgChild2Code + " " + orgChild2.orgChild2ShortName + "/" + orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName + "/" + orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, })), ), })), ), })), ), })), ), }; }), ); // return new HttpSuccess({ remark: _revision.remark, data: formattedData }); return new HttpSuccess({ remark: _revision.remark, data: formattedData }); } /** * API ตั้งเวลาเผยแพร่ * * @summary ORG_025 - ตั้งเวลาเผยแพร่ (ADMIN) #27 * * @param {string} id Id revison */ @Put("/set/publish/{id}") async Edit( @Path() id: string, @Body() requestBody: { orgPublishDate: Date }, @Request() request: RequestWithUser, ) { // await new permission().PermissionUpdate(request, "SYS_ORG");//ไม่แน่ใจOFFปิดไว้ก่อน const orgRevision = await this.orgRevisionRepository.findOne({ where: { id: id, orgRevisionIsDraft: true, orgRevisionIsCurrent: false, }, }); if (!orgRevision) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. RevisionId"); } const before = structuredClone(orgRevision); orgRevision.lastUpdateUserId = request.user.sub; orgRevision.lastUpdateFullName = request.user.name; orgRevision.lastUpdatedAt = new Date(); orgRevision.orgPublishDate = requestBody.orgPublishDate; this.orgRevisionRepository.merge(orgRevision, requestBody); await this.orgRevisionRepository.save(orgRevision, { data: request }); setLogDataDiff(request, { before, after: orgRevision }); return new HttpSuccess(); } /** * API ประวัติหน่วยงาน * * @summary ORG_039 - ประวัติหน่วยงาน (ADMIN) #42 * */ @Post("/history/publish") async GetHistoryPublish( @Body() requestBody: { id: string; type: number; }, @Request() request: RequestWithUser, ) { if (requestBody.type == 1) { const orgChild1 = await this.child1Repository.findOne({ where: { id: requestBody.id }, }); if (!orgChild1) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Child1"); } const datas = await this.child1Repository .createQueryBuilder("child1") .where("child1.ancestorDNA = :ancestorDNA", { ancestorDNA: orgChild1.ancestorDNA }) .andWhere("child1.ancestorDNA <> :nullUUID", { nullUUID: "00000000-0000-0000-0000-000000000000", }) .andWhere("child1.ancestorDNA IS NOT NULL") .leftJoinAndSelect("child1.orgRevision", "orgRevision") .orderBy("child1.lastUpdatedAt", "DESC") .getMany(); const _data = datas.map((item) => ({ id: item.id, orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName, name: item.orgChild1Name, lastUpdatedAt: item.lastUpdatedAt, })); return new HttpSuccess(_data); } else if (requestBody.type == 2) { const orgChild2 = await this.child2Repository.findOne({ where: { id: requestBody.id }, }); if (!orgChild2) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Child2"); } const datas = await this.child2Repository .createQueryBuilder("child2") .where("child2.ancestorDNA = :ancestorDNA", { ancestorDNA: orgChild2.ancestorDNA }) .andWhere("child2.ancestorDNA <> :nullUUID", { nullUUID: "00000000-0000-0000-0000-000000000000", }) .andWhere("child2.ancestorDNA IS NOT NULL") .leftJoinAndSelect("child2.orgRevision", "orgRevision") .orderBy("child2.lastUpdatedAt", "DESC") .getMany(); const _data = datas.map((item) => ({ id: item.id, orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName, name: item.orgChild2Name, lastUpdatedAt: item.lastUpdatedAt, })); return new HttpSuccess(_data); } else if (requestBody.type == 3) { const orgChild3 = await this.child3Repository.findOne({ where: { id: requestBody.id }, }); if (!orgChild3) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Child3"); } const datas = await this.child3Repository .createQueryBuilder("child3") .where("child3.ancestorDNA = :ancestorDNA", { ancestorDNA: orgChild3.ancestorDNA }) .andWhere("child3.ancestorDNA <> :nullUUID", { nullUUID: "00000000-0000-0000-0000-000000000000", }) .andWhere("child3.ancestorDNA IS NOT NULL") .leftJoinAndSelect("child3.orgRevision", "orgRevision") .orderBy("child3.lastUpdatedAt", "DESC") .getMany(); const _data = datas.map((item) => ({ id: item.id, orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName, name: item.orgChild3Name, lastUpdatedAt: item.lastUpdatedAt, })); return new HttpSuccess(_data); } else if (requestBody.type == 4) { const orgChild4 = await this.child4Repository.findOne({ where: { id: requestBody.id }, }); if (!orgChild4) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Child4"); } const datas = await this.child4Repository .createQueryBuilder("child4") .where("child4.ancestorDNA = :ancestorDNA", { ancestorDNA: orgChild4.ancestorDNA }) .andWhere("child4.ancestorDNA <> :nullUUID", { nullUUID: "00000000-0000-0000-0000-000000000000", }) .andWhere("child4.ancestorDNA IS NOT NULL") .leftJoinAndSelect("child4.orgRevision", "orgRevision") .orderBy("child4.lastUpdatedAt", "DESC") .getMany(); const _data = datas.map((item) => ({ id: item.id, orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName, name: item.orgChild4Name, lastUpdatedAt: item.lastUpdatedAt, })); return new HttpSuccess(_data); } else { const orgRoot = await this.orgRootRepository.findOne({ where: { id: requestBody.id }, }); if (!orgRoot) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Root"); } const datas = await this.orgRootRepository .createQueryBuilder("root") .where("root.ancestorDNA = :ancestorDNA", { ancestorDNA: orgRoot.ancestorDNA }) .andWhere("root.ancestorDNA <> :nullUUID", { nullUUID: "00000000-0000-0000-0000-000000000000", }) .andWhere("root.ancestorDNA IS NOT NULL") .leftJoinAndSelect("root.orgRevision", "orgRevision") .orderBy("root.lastUpdatedAt", "DESC") .getMany(); const _data = datas.map((item) => ({ id: item.id, orgRevisionName: item.orgRevision == null ? null : item.orgRevision.orgRevisionName, name: item.orgRootName, lastUpdatedAt: item.lastUpdatedAt, })); return new HttpSuccess(_data); } } /** * API จัดลำดับโครงสร้าง * * @summary ORG_038 - จัดลำดับโครงสร้าง (ADMIN) #41 * */ @Post("sort") async Sort( @Body() requestBody: { id: string; type: number; sortId: string[] }, @Request() request: RequestWithUser, ) { await new permission().PermissionUpdate(request, "SYS_ORG"); const before = null; switch (requestBody.type) { case 0: { const revisionId = await this.orgRevisionRepository.findOne({ where: { id: requestBody.id }, }); if (!revisionId?.id) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found revisionId: " + requestBody.id); } const listRootId = await this.orgRootRepository.find({ where: { orgRevisionId: requestBody.id }, select: ["id", "orgRootOrder"], }); if (!listRootId) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId."); } const sortData = listRootId.map((data) => ({ id: data.id, orgRootOrder: requestBody.sortId.indexOf(data.id) + 1, })); await this.orgRootRepository.save(sortData, { data: request }); setLogDataDiff(request, { before, after: sortData }); break; } case 1: { const rootId = await this.orgRootRepository.findOne({ where: { id: requestBody.id } }); if (!rootId?.id) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId: " + requestBody.id); } const listChild1Id = await this.child1Repository.find({ where: { orgRootId: requestBody.id }, select: ["id", "orgChild1Order"], }); if (!listChild1Id) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1Id."); } const sortData = listChild1Id.map((data) => ({ id: data.id, orgChild1Order: requestBody.sortId.indexOf(data.id) + 1, })); await this.child1Repository.save(sortData, { data: request }); setLogDataDiff(request, { before, after: sortData }); break; } case 2: { const child1Id = await this.child1Repository.findOne({ where: { id: requestBody.id } }); if (!child1Id?.id) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1Id: " + requestBody.id); } const listChild2Id = await this.child2Repository.find({ where: { orgChild1Id: requestBody.id }, select: ["id", "orgChild2Order"], }); if (!listChild2Id) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2Id."); } const sortData = listChild2Id.map((data) => ({ id: data.id, orgChild2Order: requestBody.sortId.indexOf(data.id) + 1, })); await this.child2Repository.save(sortData, { data: request }); setLogDataDiff(request, { before, after: sortData }); break; } case 3: { const child2Id = await this.child2Repository.findOne({ where: { id: requestBody.id } }); if (!child2Id?.id) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2Id: " + requestBody.id); } const listChild3Id = await this.child3Repository.find({ where: { orgChild2Id: requestBody.id }, select: ["id", "orgChild3Order"], }); if (!listChild3Id) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3Id."); } const sortData = listChild3Id.map((data) => ({ id: data.id, orgChild3Order: requestBody.sortId.indexOf(data.id) + 1, })); await this.child3Repository.save(sortData, { data: request }); setLogDataDiff(request, { before, after: sortData }); break; } case 4: { const child3Id = await this.child3Repository.findOne({ where: { id: requestBody.id } }); if (!child3Id?.id) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3Id: " + requestBody.id); } const listChild4Id = await this.child4Repository.find({ where: { orgChild3Id: requestBody.id }, select: ["id", "orgChild4Order"], }); if (!listChild4Id) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4Id."); } const sortData = listChild4Id.map((data) => ({ id: data.id, orgChild4Order: requestBody.sortId.indexOf(data.id) + 1, })); await this.child4Repository.save(sortData, { data: request }); setLogDataDiff(request, { before, after: sortData }); break; } default: throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: " + requestBody.type); } return new HttpSuccess(); } /** * API เผยแพร่ข้อมูล * * @summary ORG_071 - เผยแพร่ข้อมูล (ADMIN) #57 * * @param {string} id Id revison */ @Get("get/publish") async runPublish(@Request() request: RequestWithUser) { try { // CheckQueueInProgress // console.log("🚀 ตรวจสอบว่ามีงานอยู่ในคิว"); const [isBusyDraft, isBusyPublish] = await Promise.all([ checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG_DRAFT}`), checkQueueInProgress(`${process.env.AMQ_QUEUE_ORG}`), ]); // console.log("✅ ตรวจสอบแล้ว Draft Busy:", isBusyDraft); // console.log("✅ ตรวจสอบแล้ว Publish Busy:", isBusyPublish); if (isBusyDraft || isBusyPublish) { // console.log("🚫 พบว่ามีงานอยู่ในคิว — error") throw new HttpError( HttpStatusCode.CONFLICT, "ไม่สามารถดำเนินการได้ หากกำลังเผยแพร่หรือสร้างแบบร่างโครงสร้างหน่วยงาน", ); } const today = new Date(); today.setHours(0, 0, 0, 0); // Set time to the beginning of the day const orgRevisionDraft = await this.orgRevisionRepository .createQueryBuilder("orgRevision") .where("orgRevision.orgRevisionIsDraft = true") .andWhere("orgRevision.orgRevisionIsCurrent = false") // .andWhere("DATE(orgRevision.orgPublishDate) = :today", { today }) .getOne(); if (!orgRevisionDraft) { return new HttpSuccess(); // throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่มีข้อมูลเผยแพร่"); } // if (orgRevisionPublish) { // orgRevisionPublish.orgRevisionIsDraft = false; // orgRevisionPublish.orgRevisionIsCurrent = false; // await this.orgRevisionRepository.save(orgRevisionPublish); // } // orgRevisionDraft.orgRevisionIsCurrent = true; // orgRevisionDraft.orgRevisionIsDraft = false; // await this.orgRevisionRepository.save(orgRevisionDraft); const msg = { data: { id: orgRevisionDraft.id, status: "NOW", lastUpdateUserId: request.user.sub, lastUpdateFullName: request.user.name, lastUpdatedAt: new Date(), }, user: request.user, token: request.headers["authorization"], }; await sendToQueueOrg(msg); return new HttpSuccess(); } catch (error: any) { throw error; } } /** * Cronjob */ async cronjobRevision() { const today = new Date(); today.setUTCHours(0, 0, 0, 0); // Set time to the beginning of the day const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1); const orgRevisionDraft = await this.orgRevisionRepository .createQueryBuilder("orgRevision") .where("orgRevision.orgRevisionIsDraft = true") .andWhere("orgRevision.orgRevisionIsCurrent = false") .andWhere("orgRevision.orgPublishDate BETWEEN :today AND :tomorrow", { today, tomorrow }) .getOne(); if (!orgRevisionDraft) { return new HttpSuccess(); } // if (orgRevisionPublish) { // orgRevisionPublish.orgRevisionIsDraft = false; // orgRevisionPublish.orgRevisionIsCurrent = false; // await this.orgRevisionRepository.save(orgRevisionPublish); // } // orgRevisionDraft.orgRevisionIsCurrent = true; // orgRevisionDraft.orgRevisionIsDraft = false; // await this.orgRevisionRepository.save(orgRevisionDraft); // const posMaster = await this.posMasterRepository.find({ // where: { orgRevisionId: orgRevisionDraft.id }, // }); // posMaster.forEach(async (item) => { // // if(item.next_holderId != null){ // item.current_holderId = item.next_holderId; // item.next_holderId = null; // await this.posMasterRepository.save(item); // // } // }); const msg = { data: { id: orgRevisionDraft.id, status: "ON_SCHEDULE", lastUpdateUserId: "system", lastUpdateFullName: "system", lastUpdatedAt: new Date(), }, }; sendToQueueOrg(msg); return new HttpSuccess(); } /** * API Organizational Chart * * @summary Organizational Chart * * @param {string} revisionId Id revison */ @Get("org-chart/{revisionId}") async orgchart(@Path() revisionId: string) { const data = await this.orgRevisionRepository.findOne({ where: { id: revisionId }, }); if (!data) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้าง"); } let posMasterRoot: any; let posMasterChild1: any; let posMasterChild2: any; let posMasterChild3: any; let posMasterChild4: any; if (data.orgRevisionIsCurrent == true && data.orgRevisionIsDraft == false) { // ใช้ query เดียวแทน 5 queries แยก เพื่อความเร็วในการทำงาน const allPosMasters = await this.posMasterRepository.find({ where: { orgRevisionId: data.id, }, relations: [ "current_holder", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4", ], order: { orgRoot: { orgRootOrder: "ASC", }, posMasterOrder: "ASC", posMasterNo: "ASC", }, }); // แยกข้อมูลด้วย JavaScript แทนการใช้ database queries หลาย ๆ ครั้ง posMasterRoot = allPosMasters.filter((item) => item.orgChild1Id === null); posMasterChild1 = allPosMasters.filter( (item) => item.orgChild2Id === null && item.orgChild1Id !== null, ); posMasterChild2 = allPosMasters.filter( (item) => item.orgChild3Id === null && item.orgChild2Id !== null, ); posMasterChild3 = allPosMasters.filter( (item) => item.orgChild4Id === null && item.orgChild3Id !== null, ); posMasterChild4 = allPosMasters.filter((item) => item.orgChild4Id !== null); // สร้าง Maps เพื่อ lookup ที่เร็วขึ้น แทนการใช้ filter หลายรอบ const rootByOrgRootId = new Map(); const child1ByOrgRootId = new Map(); const child1ByOrgChild1Id = new Map(); const child2ByOrgChild1Id = new Map(); const child2ByOrgChild2Id = new Map(); const child3ByOrgChild2Id = new Map(); const child3ByOrgChild3Id = new Map(); const child4ByOrgChild3Id = new Map(); // Pre-compute all lookups เพื่อหลีกเลี่ยงการ filter ซ้ำๆ posMasterRoot.forEach((item: PosMaster) => { if (item.current_holderId) { if (item.isDirector) { // Root directors will be processed in main loop } else { // Group root non-directors by orgRootId const key = item.orgRootId || ""; if (!rootByOrgRootId.has(key)) rootByOrgRootId.set(key, []); rootByOrgRootId.get(key)!.push(item); } } }); posMasterChild1.forEach((item: PosMaster) => { if (item.isDirector) { // Group child1 directors by orgRootId const key = item.orgRootId || ""; if (!child1ByOrgRootId.has(key)) child1ByOrgRootId.set(key, []); child1ByOrgRootId.get(key)!.push(item); } else if (item.current_holderId) { // Group child1 non-directors by orgChild1Id const key = item.orgChild1Id || ""; if (!child1ByOrgChild1Id.has(key)) child1ByOrgChild1Id.set(key, []); child1ByOrgChild1Id.get(key)!.push(item); } }); posMasterChild2.forEach((item: PosMaster) => { if (item.isDirector) { // Group child2 directors by orgChild1Id const key = item.orgChild1Id || ""; if (!child2ByOrgChild1Id.has(key)) child2ByOrgChild1Id.set(key, []); child2ByOrgChild1Id.get(key)!.push(item); } else if (item.current_holderId) { // Group child2 non-directors by orgChild2Id const key = item.orgChild2Id || ""; if (!child2ByOrgChild2Id.has(key)) child2ByOrgChild2Id.set(key, []); child2ByOrgChild2Id.get(key)!.push(item); } }); posMasterChild3.forEach((item: PosMaster) => { if (item.isDirector) { // Group child3 directors by orgChild2Id const key = item.orgChild2Id || ""; if (!child3ByOrgChild2Id.has(key)) child3ByOrgChild2Id.set(key, []); child3ByOrgChild2Id.get(key)!.push(item); } else if (item.current_holderId) { // Group child3 non-directors by orgChild3Id const key = item.orgChild3Id || ""; if (!child3ByOrgChild3Id.has(key)) child3ByOrgChild3Id.set(key, []); child3ByOrgChild3Id.get(key)!.push(item); } }); posMasterChild4.forEach((item: PosMaster) => { if (item.isDirector) { // Group child4 directors by orgChild3Id const key = item.orgChild3Id || ""; if (!child4ByOrgChild3Id.has(key)) child4ByOrgChild3Id.set(key, []); child4ByOrgChild3Id.get(key)!.push(item); } }); // Helper function เพื่อสร้าง node object const createNode = (item: PosMaster, level: number, holder: any, orgInfo: any) => ({ level, personID: holder?.id ?? "", name: holder ? `${holder.firstName} ${holder.lastName}` : "ว่าง", avatar: holder?.avatar && holder?.avatarName ? `${holder.avatar}/${holder.avatarName}` : null, positionName: holder?.position ?? "", positionNum: `${orgInfo.shortName} ${item.posMasterNo}`, positionNumInt: item.posMasterNo, departmentName: orgInfo.name, organizationId: orgInfo.id, children: [] as any[], }); let formattedData = posMasterRoot .filter((x: any) => x.current_holderId != null && x.isDirector) .map((x0: PosMaster) => { const childLevel1: any[] = []; // Add root non-directors (Level 2) const rootNonDirectors = rootByOrgRootId.get(x0.orgRootId || "") || []; rootNonDirectors.forEach((x00: PosMaster) => { childLevel1.push( createNode(x00, 2, x00.current_holder, { shortName: x00.orgRoot.orgRootShortName, name: x00.orgRoot.orgRootName, id: x00.orgRoot.id, }), ); }); // Add child1 directors (Level 2) const child1Directors = child1ByOrgRootId.get(x0.orgRootId || "") || []; child1Directors.forEach((x1: PosMaster) => { const childLevel2: any[] = []; // Add child1 non-directors (Level 3) const child1NonDirectors = child1ByOrgChild1Id.get(x1.orgChild1Id || "") || []; child1NonDirectors.forEach((x11: PosMaster) => { childLevel2.push( createNode(x11, 3, x11.current_holder, { shortName: x11.orgChild1.orgChild1ShortName, name: x11.orgChild1.orgChild1Name, id: x11.orgChild1.id, }), ); }); // Add child2 directors (Level 3) const child2Directors = child2ByOrgChild1Id.get(x1.orgChild1Id || "") || []; child2Directors.forEach((x2: PosMaster) => { const childLevel3: any[] = []; // Add child2 non-directors (Level 4) const child2NonDirectors = child2ByOrgChild2Id.get(x2.orgChild2Id || "") || []; child2NonDirectors.forEach((x22: PosMaster) => { childLevel3.push( createNode(x22, 4, x22.current_holder, { shortName: x22.orgChild2.orgChild2ShortName, name: x22.orgChild2.orgChild2Name, id: x22.orgChild2.id, }), ); }); // Add child3 directors (Level 4) const child3Directors = child3ByOrgChild2Id.get(x2.orgChild2Id || "") || []; child3Directors.forEach((x3: PosMaster) => { const childLevel4: any[] = []; // Add child3 non-directors (Level 5) const child3NonDirectors = child3ByOrgChild3Id.get(x3.orgChild3Id || "") || []; child3NonDirectors.forEach((x33: PosMaster) => { childLevel4.push( createNode(x33, 5, x33.current_holder, { shortName: x33.orgChild3.orgChild3ShortName, name: x33.orgChild3.orgChild3Name, id: x33.orgChild3.id, }), ); }); // Add child4 directors (Level 5) const child4Directors = child4ByOrgChild3Id.get(x3.orgChild3Id || "") || []; child4Directors.forEach((x4: PosMaster) => { const childLevel5: any[] = []; // Add child4 non-directors (Level 5) posMasterChild4 .filter( (x: PosMaster) => !x.isDirector && x.current_holderId && x.orgChild3Id === x4.orgChild3Id, ) .forEach((x44: PosMaster) => { childLevel5.push( createNode(x44, 5, x44.current_holder, { shortName: x44.orgChild4.orgChild4ShortName, name: x44.orgChild4.orgChild4Name, id: x44.orgChild4.id, }), ); }); const child4Node = createNode(x4, 4, x4.current_holder, { shortName: x4.orgChild4.orgChild4ShortName, name: x4.orgChild4.orgChild4Name, id: x4.orgChild4.id, }); child4Node.children = childLevel5; childLevel4.push(child4Node); }); const child3Node = createNode(x3, 4, x3.current_holder, { shortName: x3.orgChild3.orgChild3ShortName, name: x3.orgChild3.orgChild3Name, id: x3.orgChild3.id, }); child3Node.children = childLevel4; childLevel3.push(child3Node); }); const child2Node = createNode(x2, 3, x2.current_holder, { shortName: x2.orgChild2.orgChild2ShortName, name: x2.orgChild2.orgChild2Name, id: x2.orgChild2.id, }); child2Node.children = childLevel3; childLevel2.push(child2Node); }); const child1Node = createNode(x1, 2, x1.current_holder, { shortName: x1.orgChild1.orgChild1ShortName, name: x1.orgChild1.orgChild1Name, id: x1.orgChild1.id, }); child1Node.children = childLevel2; childLevel1.push(child1Node); }); // Root Level 1 const rootNode = createNode(x0, 1, x0.current_holder, { shortName: x0.orgRoot.orgRootShortName, name: x0.orgRoot.orgRootName, id: x0.orgRoot.id, }); rootNode.children = childLevel1; return rootNode; }); const formattedData_ = { personID: "", name: "", avatar: "", positionName: "", positionNum: "", positionNumInt: null, departmentName: data.orgRevisionName, organizationId: data.id, children: formattedData, }; return new HttpSuccess([formattedData_]); } else if (data.orgRevisionIsCurrent == false && data.orgRevisionIsDraft == true) { // ใช้ query เดียวแทน 5 queries แยก เพื่อความเร็วในการทำงาน const allPosMasters = await this.posMasterRepository.find({ where: { orgRevisionId: data.id, // next_holderId: Not(IsNull()), }, relations: ["next_holder", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"], }); // แยกข้อมูลด้วย JavaScript แทนการใช้ database queries หลาย ๆ ครั้ง posMasterRoot = allPosMasters.filter((item) => item.orgChild1Id === null); posMasterChild1 = allPosMasters.filter( (item) => item.orgChild2Id === null && item.orgChild1Id !== null, ); posMasterChild2 = allPosMasters.filter( (item) => item.orgChild3Id === null && item.orgChild2Id !== null, ); posMasterChild3 = allPosMasters.filter( (item) => item.orgChild4Id === null && item.orgChild3Id !== null, ); posMasterChild4 = allPosMasters.filter((item) => item.orgChild4Id !== null); // สร้าง Maps เพื่อ lookup ที่เร็วขึ้น แทนการใช้ filter หลายรอบ สำหรับ draft version const rootByOrgRootIdDraft = new Map(); const child1ByOrgRootIdDraft = new Map(); const child1ByOrgChild1IdDraft = new Map(); const child2ByOrgChild1IdDraft = new Map(); const child2ByOrgChild2IdDraft = new Map(); const child3ByOrgChild2IdDraft = new Map(); const child3ByOrgChild3IdDraft = new Map(); const child4ByOrgChild3IdDraft = new Map(); // Pre-compute all lookups เพื่อหลีกเลี่ยงการ filter ซ้ำๆ สำหรับ draft version posMasterRoot.forEach((item: PosMaster) => { if (item.next_holderId) { if (item.isDirector) { // Root directors will be processed in main loop } else { // Group root non-directors by orgRootId const key = item.orgRootId || ""; if (!rootByOrgRootIdDraft.has(key)) rootByOrgRootIdDraft.set(key, []); rootByOrgRootIdDraft.get(key)!.push(item); } } }); posMasterChild1.forEach((item: PosMaster) => { if (item.isDirector) { // Group child1 directors by orgRootId const key = item.orgRootId || ""; if (!child1ByOrgRootIdDraft.has(key)) child1ByOrgRootIdDraft.set(key, []); child1ByOrgRootIdDraft.get(key)!.push(item); } else if (item.next_holderId) { // Group child1 non-directors by orgChild1Id const key = item.orgChild1Id || ""; if (!child1ByOrgChild1IdDraft.has(key)) child1ByOrgChild1IdDraft.set(key, []); child1ByOrgChild1IdDraft.get(key)!.push(item); } }); posMasterChild2.forEach((item: PosMaster) => { if (item.isDirector) { // Group child2 directors by orgChild1Id const key = item.orgChild1Id || ""; if (!child2ByOrgChild1IdDraft.has(key)) child2ByOrgChild1IdDraft.set(key, []); child2ByOrgChild1IdDraft.get(key)!.push(item); } else if (item.next_holderId) { // Group child2 non-directors by orgChild2Id const key = item.orgChild2Id || ""; if (!child2ByOrgChild2IdDraft.has(key)) child2ByOrgChild2IdDraft.set(key, []); child2ByOrgChild2IdDraft.get(key)!.push(item); } }); posMasterChild3.forEach((item: PosMaster) => { if (item.isDirector) { // Group child3 directors by orgChild2Id const key = item.orgChild2Id || ""; if (!child3ByOrgChild2IdDraft.has(key)) child3ByOrgChild2IdDraft.set(key, []); child3ByOrgChild2IdDraft.get(key)!.push(item); } else if (item.next_holderId) { // Group child3 non-directors by orgChild3Id const key = item.orgChild3Id || ""; if (!child3ByOrgChild3IdDraft.has(key)) child3ByOrgChild3IdDraft.set(key, []); child3ByOrgChild3IdDraft.get(key)!.push(item); } }); posMasterChild4.forEach((item: PosMaster) => { if (item.isDirector) { // Group child4 directors by orgChild3Id const key = item.orgChild3Id || ""; if (!child4ByOrgChild3IdDraft.has(key)) child4ByOrgChild3IdDraft.set(key, []); child4ByOrgChild3IdDraft.get(key)!.push(item); } }); // Helper function เพื่อสร้าง node object สำหรับ draft version const createNodeDraft = (item: PosMaster, level: number, holder: any, orgInfo: any) => ({ level, personID: holder?.id ?? "", name: holder ? `${holder.firstName} ${holder.lastName}` : "ว่าง", avatar: holder?.avatar && holder?.avatarName ? `${holder.avatar}/${holder.avatarName}` : null, positionName: holder?.position ?? "", positionNum: `${orgInfo.shortName} ${item.posMasterNo}`, positionNumInt: item.posMasterNo, departmentName: orgInfo.name, organizationId: orgInfo.id, children: [] as any[], }); let formattedData = posMasterRoot .filter((x: any) => x.next_holderId != null && x.isDirector) .map((x0: PosMaster) => { const childLevel1: any[] = []; // Add root non-directors (Level 2) const rootNonDirectors = rootByOrgRootIdDraft.get(x0.orgRootId || "") || []; rootNonDirectors.forEach((x00: PosMaster) => { childLevel1.push( createNodeDraft(x00, 2, x00.next_holder, { shortName: x00.orgRoot.orgRootShortName, name: x00.orgRoot.orgRootName, id: x00.orgRoot.id, }), ); }); // Add child1 directors (Level 2) const child1Directors = child1ByOrgRootIdDraft.get(x0.orgRootId || "") || []; child1Directors.forEach((x1: PosMaster) => { const childLevel2: any[] = []; // Add child1 non-directors (Level 3) const child1NonDirectors = child1ByOrgChild1IdDraft.get(x1.orgChild1Id || "") || []; child1NonDirectors.forEach((x11: PosMaster) => { childLevel2.push( createNodeDraft(x11, 3, x11.next_holder, { shortName: x11.orgChild1.orgChild1ShortName, name: x11.orgChild1.orgChild1Name, id: x11.orgChild1.id, }), ); }); // Add child2 directors (Level 3) const child2Directors = child2ByOrgChild1IdDraft.get(x1.orgChild1Id || "") || []; child2Directors.forEach((x2: PosMaster) => { const childLevel3: any[] = []; // Add child2 non-directors (Level 4) const child2NonDirectors = child2ByOrgChild2IdDraft.get(x2.orgChild2Id || "") || []; child2NonDirectors.forEach((x22: PosMaster) => { childLevel3.push( createNodeDraft(x22, 4, x22.next_holder, { shortName: x22.orgChild2.orgChild2ShortName, name: x22.orgChild2.orgChild2Name, id: x22.orgChild2.id, }), ); }); // Add child3 directors (Level 4) const child3Directors = child3ByOrgChild2IdDraft.get(x2.orgChild2Id || "") || []; child3Directors.forEach((x3: PosMaster) => { const childLevel4: any[] = []; // Add child3 non-directors (Level 5) const child3NonDirectors = child3ByOrgChild3IdDraft.get(x3.orgChild3Id || "") || []; child3NonDirectors.forEach((x33: PosMaster) => { childLevel4.push( createNodeDraft(x33, 5, x33.next_holder, { shortName: x33.orgChild3.orgChild3ShortName, name: x33.orgChild3.orgChild3Name, id: x33.orgChild3.id, }), ); }); // Add child4 directors (Level 5) const child4Directors = child4ByOrgChild3IdDraft.get(x3.orgChild3Id || "") || []; child4Directors.forEach((x4: PosMaster) => { const childLevel5: any[] = []; // Add child4 non-directors (Level 5) posMasterChild4 .filter( (x: PosMaster) => !x.isDirector && x.next_holderId && x.orgChild3Id === x4.orgChild3Id, ) .forEach((x44: PosMaster) => { childLevel5.push( createNodeDraft(x44, 5, x44.next_holder, { shortName: x44.orgChild4.orgChild4ShortName, name: x44.orgChild4.orgChild4Name, id: x44.orgChild4.id, }), ); }); const child4Node = createNodeDraft(x4, 4, x4.next_holder, { shortName: x4.orgChild4.orgChild4ShortName, name: x4.orgChild4.orgChild4Name, id: x4.orgChild4.id, }); child4Node.children = childLevel5; childLevel4.push(child4Node); }); const child3Node = createNodeDraft(x3, 4, x3.next_holder, { shortName: x3.orgChild3.orgChild3ShortName, name: x3.orgChild3.orgChild3Name, id: x3.orgChild3.id, }); child3Node.children = childLevel4; childLevel3.push(child3Node); }); const child2Node = createNodeDraft(x2, 3, x2.next_holder, { shortName: x2.orgChild2.orgChild2ShortName, name: x2.orgChild2.orgChild2Name, id: x2.orgChild2.id, }); child2Node.children = childLevel3; childLevel2.push(child2Node); }); const child1Node = createNodeDraft(x1, 2, x1.next_holder, { shortName: x1.orgChild1.orgChild1ShortName, name: x1.orgChild1.orgChild1Name, id: x1.orgChild1.id, }); child1Node.children = childLevel2; childLevel1.push(child1Node); }); // Root Level 1 const rootNode = createNodeDraft(x0, 1, x0.next_holder, { shortName: x0.orgRoot.orgRootShortName, name: x0.orgRoot.orgRootName, id: x0.orgRoot.id, }); rootNode.children = childLevel1; return rootNode; }); const formattedData_ = { personID: "", name: "", avatar: "", positionName: "", positionNum: "", positionNumInt: null, departmentName: data.orgRevisionName, organizationId: data.id, children: formattedData, }; return new HttpSuccess([formattedData_]); } else { return new HttpSuccess([ { personID: "", name: "", avatar: "", positionName: "", positionNum: "", positionNumInt: null, departmentName: data.orgRevisionName, organizationId: data.id, children: [], }, ]); } } /** * API Organizational Chart * * @summary Organizational Chart * * @param {string} revisionId Id revison */ @Get("org-chart/{revisionId}/{rootId}") async orgChartByRoot(@Path() revisionId: string, @Path() rootId: string) { // get revision data const data = await this.orgRevisionRepository.findOne({ where: { id: revisionId } }); if (!data) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้าง"); } type OrgInfo = { shortName: string; name: string; id: string }; let fieldId: "current_holderId" | "next_holderId" = "current_holderId"; let relations: string[] = rootId === "root" ? ["current_holder", "orgRoot"] : ["current_holder", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"]; if (data.orgRevisionIsCurrent === false && data.orgRevisionIsDraft === true) { fieldId = "next_holderId"; relations = rootId === "root" ? ["next_holder", "orgRoot"] : ["next_holder", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"]; } const where = rootId === "root" ? { orgRevisionId: data.id, posMasterNo: 1 } : { orgRevisionId: data.id, orgRootId: rootId }; const allPosMasters = await this.posMasterRepository.find({ where, relations, order: { orgRoot: { orgRootOrder: "ASC" }, posMasterOrder: "ASC", posMasterNo: "ASC", }, }); // Split positions by level const posMasterRoot = allPosMasters.filter((item) => item.orgChild1Id === null); const posMasterChild1 = rootId !== "root" ? allPosMasters.filter((item) => item.orgChild2Id === null && item.orgChild1Id !== null) : []; const posMasterChild2 = rootId !== "root" ? allPosMasters.filter((item) => item.orgChild3Id === null && item.orgChild2Id !== null) : []; const posMasterChild3 = rootId !== "root" ? allPosMasters.filter((item) => item.orgChild4Id === null && item.orgChild3Id !== null) : []; const posMasterChild4 = rootId !== "root" ? allPosMasters.filter((item) => item.orgChild4Id !== null) : []; // Find the minimum posMasterNo for each orgRootId const minPosMasterNoByOrgRootId = new Map(); posMasterRoot.forEach((x) => { const orgRootId = x.orgRootId ?? ""; const currentMin = minPosMasterNoByOrgRootId.get(orgRootId); if ( (currentMin === undefined || x.posMasterNo < currentMin) && (x as any)[fieldId] != null && x.isDirector ) { minPosMasterNoByOrgRootId.set(orgRootId, x.posMasterNo); } }); // Utility to group by key function groupBy( arr: T[], keyFn: (item: T) => string | number | undefined | null, ): Map { const map = new Map(); for (const item of arr) { let key = keyFn(item); if (key === undefined || key === null) key = ""; key = String(key); if (!map.has(key)) map.set(key, []); map.get(key)!.push(item); } return map; } // Build lookup maps const rootByOrgRootId = groupBy( posMasterRoot.filter( (item) => ((item as any)[fieldId] && !item.isDirector) || (item.isDirector && minPosMasterNoByOrgRootId.get(item.orgRootId ?? "") !== item.posMasterNo), ), (item) => item.orgRootId ?? "", ); const child1ByOrgRootId = groupBy( posMasterChild1.filter((item) => item.isDirector), (item) => item.orgRootId ?? "", ); const child1ByOrgChild1Id = groupBy( posMasterChild1.filter((item) => (item as any)[fieldId] && !item.isDirector), (item) => item.orgChild1Id ?? "", ); const child2ByOrgChild1Id = groupBy( posMasterChild2.filter((item) => item.isDirector), (item) => item.orgChild1Id ?? "", ); const child2ByOrgChild2Id = groupBy( posMasterChild2.filter((item) => (item as any)[fieldId] && !item.isDirector), (item) => item.orgChild2Id ?? "", ); const child3ByOrgChild2Id = groupBy( posMasterChild3.filter((item) => item.isDirector), (item) => item.orgChild2Id ?? "", ); const child3ByOrgChild3Id = groupBy( posMasterChild3.filter((item) => (item as any)[fieldId] && !item.isDirector), (item) => item.orgChild3Id ?? "", ); const child4ByOrgChild3Id = groupBy( posMasterChild4.filter((item) => item.isDirector), (item) => item.orgChild3Id ?? "", ); // Helper to create node function createNode(item: any, level: number, orgInfo: OrgInfo): any { const holder = fieldId === "current_holderId" ? item.current_holder : item.next_holder; return { level, personID: holder?.id ?? "", name: holder ? `${holder.firstName} ${holder.lastName}` : "ว่าง", avatar: holder?.avatar && holder?.avatarName ? `${holder.avatar}/${holder.avatarName}` : null, positionName: holder?.position ?? "", positionNum: `${orgInfo.shortName} ${item.posMasterNo}`, positionNumInt: item.posMasterNo, departmentName: orgInfo.name, organizationId: orgInfo.id, children: [], }; } // Recursive builder for children function buildChildren(level: number, parent: any, orgInfo: OrgInfo): any[] { if (level === 2) { // Level 2: child1 non-directors and child2 directors const children: any[] = []; // Root non-directors ( rootByOrgRootId .get(parent.orgRootId) ?.filter( (x00) => !x00.isDirector || (x00.isDirector && minPosMasterNoByOrgRootId.get(x00.orgRootId ?? "") !== x00.posMasterNo), ) || [] ).forEach((x00) => { children.push( createNode(x00, 2, { shortName: x00.orgRoot.orgRootShortName, name: x00.orgRoot.orgRootName, id: x00.orgRoot.id, }), ); }); if (rootId !== "root") { // Child1 directors (child1ByOrgRootId.get(parent.orgRootId) || []).forEach((x1) => { const childLevel2 = buildChildren(3, x1, { shortName: x1.orgChild1.orgChild1ShortName, name: x1.orgChild1.orgChild1Name, id: x1.orgChild1.id, }); const node = createNode(x1, 2, { shortName: x1.orgChild1.orgChild1ShortName, name: x1.orgChild1.orgChild1Name, id: x1.orgChild1.id, }); node.children = childLevel2; children.push(node); }); } return children; } else if (level === 3) { // Level 3: child1 non-directors and child2 directors const children: any[] = []; (child1ByOrgChild1Id.get(parent.orgChild1Id) || []).forEach((x11) => { children.push( createNode(x11, 3, { shortName: x11.orgChild1.orgChild1ShortName, name: x11.orgChild1.orgChild1Name, id: x11.orgChild1.id, }), ); }); (child2ByOrgChild1Id.get(parent.orgChild1Id) || []).forEach((x2) => { const childLevel3 = buildChildren(4, x2, { shortName: x2.orgChild2.orgChild2ShortName, name: x2.orgChild2.orgChild2Name, id: x2.orgChild2.id, }); const node = createNode(x2, 3, { shortName: x2.orgChild2.orgChild2ShortName, name: x2.orgChild2.orgChild2Name, id: x2.orgChild2.id, }); node.children = childLevel3; children.push(node); }); return children; } else if (level === 4) { // Level 4: child2 non-directors and child3 directors const children: any[] = []; (child2ByOrgChild2Id.get(parent.orgChild2Id) || []).forEach((x22) => { children.push( createNode(x22, 4, { shortName: x22.orgChild2.orgChild2ShortName, name: x22.orgChild2.orgChild2Name, id: x22.orgChild2.id, }), ); }); (child3ByOrgChild2Id.get(parent.orgChild2Id) || []).forEach((x3) => { const childLevel4 = buildChildren(5, x3, { shortName: x3.orgChild3.orgChild3ShortName, name: x3.orgChild3.orgChild3Name, id: x3.orgChild3.id, }); const node = createNode(x3, 4, { shortName: x3.orgChild3.orgChild3ShortName, name: x3.orgChild3.orgChild3Name, id: x3.orgChild3.id, }); node.children = childLevel4; children.push(node); }); return children; } else if (level === 5) { // Level 5: child3 non-directors and child4 directors const children: any[] = []; (child3ByOrgChild3Id.get(parent.orgChild3Id) || []).forEach((x33) => { children.push( createNode(x33, 5, { shortName: x33.orgChild3.orgChild3ShortName, name: x33.orgChild3.orgChild3Name, id: x33.orgChild3.id, }), ); }); (child4ByOrgChild3Id.get(parent.orgChild3Id) || []).forEach((x4) => { // Level 5: child4 directors and their non-directors const childLevel5: any[] = []; posMasterChild4 .filter((x) => !x.isDirector && (x as any)[fieldId] && x.orgChild3Id === x4.orgChild3Id) .forEach((x44) => { childLevel5.push( createNode(x44, 5, { shortName: x44.orgChild4.orgChild4ShortName, name: x44.orgChild4.orgChild4Name, id: x44.orgChild4.id, }), ); }); const node = createNode(x4, 4, { shortName: x4.orgChild4.orgChild4ShortName, name: x4.orgChild4.orgChild4Name, id: x4.orgChild4.id, }); node.children = childLevel5; children.push(node); }); return children; } return []; } // Build root nodes const formattedData = posMasterRoot .filter( (x: any) => x.isDirector && minPosMasterNoByOrgRootId.get(x.orgRootId ?? "") === x.posMasterNo, ) .map((x0) => { const rootNode = createNode(x0, 1, { shortName: x0.orgRoot.orgRootShortName, name: x0.orgRoot.orgRootName, id: x0.orgRoot.id, }); rootNode.children = buildChildren(2, x0, { shortName: x0.orgRoot.orgRootShortName, name: x0.orgRoot.orgRootName, id: x0.orgRoot.id, }); return rootNode; }); const formattedData_ = rootId === "root" ? [ { personID: "", name: "", avatar: "", positionName: "", positionNum: "", positionNumInt: null, departmentName: data.orgRevisionName, organizationId: data.id, children: formattedData, }, ] : formattedData; return new HttpSuccess(formattedData_); } // /** // * API Organizational StructChart // * // * @summary Organizational StructChart // * // */ // @Get("struct-chart/{idNode}/{type}") // async structchart(@Path() idNode: string, type: number) { // switch (type) { // case 0: { // const data = await this.orgRevisionRepository.findOne({ // where: { id: idNode }, // relations: [ // "orgRoots", // "orgRoots.orgChild1s", // "orgRoots.orgChild1s.orgChild2s", // "orgRoots.orgChild1s.orgChild2s.orgChild3s", // "orgRoots.orgChild1s.orgChild2s.orgChild3s.orgChild4s", // ], // }); // if (!data) { // throw new HttpError(HttpStatusCode.NOT_FOUND, "not found revision"); // } // const formattedData = { // departmentName: data.orgRevisionName, // deptID: data.id, // type: 0, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgRevisionId: data.id }, // }), // totalPositionVacant: // data.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // data.orgRoots // .sort((a, b) => a.orgRootOrder - b.orgRootOrder) // .map(async (orgRoot: OrgRoot) => { // return { // departmentName: orgRoot.orgRootName, // deptID: orgRoot.id, // type: 1, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgRevisionId: data.id, orgRootId: orgRoot.id }, // }), // totalPositionVacant: // data.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // orgRoot.orgChild1s // .sort((a, b) => a.orgChild1Order - b.orgChild1Order) // .map(async (orgChild1) => ({ // departmentName: orgChild1.orgChild1Name, // deptID: orgChild1.id, // type: 2, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // }, // }), // totalPositionVacant: // data.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // orgChild1Id: orgChild1.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // orgChild1Id: orgChild1.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // orgChild1.orgChild2s // .sort((a, b) => a.orgChild2Order - b.orgChild2Order) // .map(async (orgChild2) => ({ // departmentName: orgChild2.orgChild2Name, // deptID: orgChild2.id, // type: 3, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // }, // }), // totalPositionVacant: // data.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // orgChild2.orgChild3s // .sort((a, b) => a.orgChild3Order - b.orgChild3Order) // .map(async (orgChild3) => ({ // departmentName: orgChild3.orgChild3Name, // deptID: orgChild3.id, // type: 4, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // }, // }), // totalPositionVacant: // data.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: // // await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count( // // { // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // next_holderId: IsNull() || "", // // }, // // }, // // ), // children: await Promise.all( // orgChild3.orgChild4s // .sort((a, b) => a.orgChild4Order - b.orgChild4Order) // .map(async (orgChild4) => ({ // departmentName: orgChild4.orgChild4Name, // deptID: orgChild4.id, // type: 5, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // }, // }), // totalPositionVacant: // data.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: // // await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // orgChild4Id: orgChild4.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: // // await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgRootId: orgRoot.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // orgChild4Id: orgChild4.id, // // next_holderId: IsNull() || "", // // }, // // }), // })), // ), // })), // ), // })), // ), // })), // ), // }; // }), // ), // }; // return new HttpSuccess([formattedData]); // } // case 1: { // const data = await this.orgRootRepository.findOne({ // where: { id: idNode }, // relations: [ // "orgRevision", // "orgChild1s", // "orgChild1s.orgChild2s", // "orgChild1s.orgChild2s.orgChild3s", // "orgChild1s.orgChild2s.orgChild3s.orgChild4s", // ], // }); // if (!data) { // throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId"); // } // const formattedData = { // departmentName: data.orgRootName, // deptID: data.id, // type: 1, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgRootId: data.id }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // data.orgChild1s // .sort((a, b) => a.orgChild1Order - b.orgChild1Order) // .map(async (orgChild1) => ({ // departmentName: orgChild1.orgChild1Name, // deptID: orgChild1.id, // type: 2, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgRootId: data.id, orgChild1Id: orgChild1.id }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // orgChild1Id: orgChild1.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // orgChild1Id: orgChild1.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // orgChild1.orgChild2s // .sort((a, b) => a.orgChild2Order - b.orgChild2Order) // .map(async (orgChild2) => ({ // departmentName: orgChild2.orgChild2Name, // deptID: orgChild2.id, // type: 3, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // }, // }), // totalPositionCurrentVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // orgChild2.orgChild3s // .sort((a, b) => a.orgChild3Order - b.orgChild3Order) // .map(async (orgChild3) => ({ // departmentName: orgChild3.orgChild3Name, // deptID: orgChild3.id, // type: 4, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // orgChild3.orgChild4s // .sort((a, b) => a.orgChild4Order - b.orgChild4Order) // .map(async (orgChild4) => ({ // departmentName: orgChild4.orgChild4Name, // deptID: orgChild4.id, // type: 5, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRootId: data.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: // // await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // orgChild4Id: orgChild4.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRootId: data.id, // // orgChild1Id: orgChild1.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // orgChild4Id: orgChild4.id, // // next_holderId: IsNull() || "", // // }, // // }), // })), // ), // })), // ), // })), // ), // })), // ), // }; // return new HttpSuccess([formattedData]); // } // case 2: { // const data = await this.child1Repository.findOne({ // where: { id: idNode }, // relations: [ // "orgRevision", // "orgChild2s", // "orgChild2s.orgChild3s", // "orgChild2s.orgChild3s.orgChild4s", // ], // }); // if (!data) { // throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1Id"); // } // const formattedData = { // departmentName: data.orgChild1Name, // deptID: data.id, // type: 2, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgChild1Id: data.id }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgChild1Id: data.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgChild1Id: data.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgChild1Id: data.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgChild1Id: data.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // data.orgChild2s // .sort((a, b) => a.orgChild2Order - b.orgChild2Order) // .map(async (orgChild2) => ({ // departmentName: orgChild2.orgChild2Name, // deptID: orgChild2.id, // type: 3, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgChild1Id: data.id, orgChild2Id: orgChild2.id }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgChild1Id: data.id, // orgChild2Id: orgChild2.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgChild1Id: data.id, // orgChild2Id: orgChild2.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgChild1Id: data.id, // // orgChild2Id: orgChild2.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgChild1Id: data.id, // // orgChild2Id: orgChild2.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // orgChild2.orgChild3s // .sort((a, b) => a.orgChild3Order - b.orgChild3Order) // .map(async (orgChild3) => ({ // departmentName: orgChild3.orgChild3Name, // deptID: orgChild3.id, // type: 4, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // orgChild3.orgChild4s // .sort((a, b) => a.orgChild4Order - b.orgChild4Order) // .map(async (orgChild4) => ({ // departmentName: orgChild4.orgChild4Name, // deptID: orgChild4.id, // type: 5, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgRevisionId: data.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // orgChild4Id: orgChild4.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgRevisionId: data.id, // // orgChild2Id: orgChild2.id, // // orgChild3Id: orgChild3.id, // // orgChild4Id: orgChild4.id, // // next_holderId: IsNull() || "", // // }, // // }), // })), // ), // })), // ), // })), // ), // }; // return new HttpSuccess([formattedData]); // } // case 3: { // const data = await this.child2Repository.findOne({ // where: { id: idNode }, // relations: ["orgRevision", "orgChild3s", "orgChild3s.orgChild4s"], // }); // if (!data) { // throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2Id"); // } // const formattedData = { // departmentName: data.orgChild2Name, // deptID: data.id, // type: 3, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgChild2Id: data.id }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgChild2Id: data.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgChild2Id: data.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgChild2Id: data.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgChild2Id: data.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // data.orgChild3s // .sort((a, b) => a.orgChild3Order - b.orgChild3Order) // .map(async (orgChild3) => ({ // departmentName: orgChild3.orgChild3Name, // deptID: orgChild3.id, // type: 4, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgChild2Id: data.id, orgChild3Id: orgChild3.id }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgChild2Id: data.id, // orgChild3Id: orgChild3.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgChild2Id: data.id, // orgChild3Id: orgChild3.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgChild2Id: data.id, // // orgChild3Id: orgChild3.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgChild2Id: data.id, // // orgChild3Id: orgChild3.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // orgChild3.orgChild4s // .sort((a, b) => a.orgChild4Order - b.orgChild4Order) // .map(async (orgChild4) => ({ // departmentName: orgChild4.orgChild4Name, // deptID: orgChild4.id, // type: 5, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { // orgChild2Id: data.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgChild2Id: data.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgChild2Id: data.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgChild2Id: data.id, // // orgChild3Id: orgChild3.id, // // orgChild4Id: orgChild4.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgChild2Id: data.id, // // orgChild3Id: orgChild3.id, // // orgChild4Id: orgChild4.id, // // next_holderId: IsNull() || "", // // }, // // }), // })), // ), // })), // ), // }; // return new HttpSuccess([formattedData]); // } // case 4: { // const data = await this.child3Repository.findOne({ // where: { id: idNode }, // relations: ["orgRevision", "orgChild4s"], // }); // if (!data) { // throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3Id"); // } // const formattedData = { // departmentName: data.orgChild3Name, // deptID: data.id, // type: 4, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgChild3Id: data.id }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgChild3Id: data.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgChild3Id: data.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgChild3Id: data.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgChild3Id: data.id, // // next_holderId: IsNull() || "", // // }, // // }), // children: await Promise.all( // data.orgChild4s // .sort((a, b) => a.orgChild4Order - b.orgChild4Order) // .map(async (orgChild4) => ({ // departmentName: orgChild4.orgChild4Name, // deptID: orgChild4.id, // type: 5, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgChild3Id: data.id, orgChild4Id: orgChild4.id }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgChild3Id: data.id, // orgChild4Id: orgChild4.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgChild3Id: data.id, // orgChild4Id: orgChild4.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgChild3Id: data.id, // // orgChild4Id: orgChild4.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgChild3Id: data.id, // // orgChild4Id: orgChild4.id, // // next_holderId: IsNull() || "", // // }, // // }), // })), // ), // }; // return new HttpSuccess([formattedData]); // } // case 5: { // const data = await this.child4Repository.findOne({ // where: { id: idNode }, // relations: ["orgRevision"], // }); // if (!data) { // throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4Id"); // } // const formattedData = { // departmentName: data.orgChild4Name, // deptID: data.id, // type: 5, // // heads: // totalPositionCount: await this.posMasterRepository.count({ // where: { orgChild4Id: data.id }, // }), // totalPositionVacant: // data.orgRevision.orgRevisionIsDraft == true // ? await this.posMasterRepository.count({ // where: { // orgChild4Id: data.id, // next_holderId: IsNull() || "", // }, // }) // : await this.posMasterRepository.count({ // where: { // orgChild4Id: data.id, // current_holderId: IsNull() || "", // }, // }), // // totalPositionCurrentVacant: await this.posMasterRepository.count({ // // where: { // // orgChild4Id: data.id, // // current_holderId: IsNull() || "", // // }, // // }), // // totalPositionNextVacant: await this.posMasterRepository.count({ // // where: { // // orgChild4Id: data.id, // // next_holderId: IsNull() || "", // // }, // // }), // }; // return new HttpSuccess([formattedData]); // } // default: // throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: "); // } // } /** * API Organizational StructChart V2 (Optimized) * * @summary Organizational StructChart - Optimized with batch queries to prevent N+1 problem * */ @Get("struct-chart/{idNode}/{type}") async structchartV2(@Path() idNode: string, type: number) { // Fetch orgRevisionId and isDraft status first let orgRevisionId: string | null = null; let isDraft = false; switch (type) { case 0: const revision = await this.orgRevisionRepository.findOne({ where: { id: idNode } }); orgRevisionId = revision?.id || null; isDraft = revision?.orgRevisionIsDraft || false; break; case 1: { const root = await this.orgRootRepository.findOne({ where: { id: idNode }, relations: ["orgRevision"], }); orgRevisionId = root?.orgRevision?.id || null; isDraft = root?.orgRevision?.orgRevisionIsDraft || false; break; } case 2: { const child1 = await this.child1Repository.findOne({ where: { id: idNode }, relations: ["orgRevision"], }); orgRevisionId = child1?.orgRevision?.id || null; isDraft = child1?.orgRevision?.orgRevisionIsDraft || false; break; } case 3: { const child2 = await this.child2Repository.findOne({ where: { id: idNode }, relations: ["orgRevision"], }); orgRevisionId = child2?.orgRevision?.id || null; isDraft = child2?.orgRevision?.orgRevisionIsDraft || false; break; } case 4: { const child3 = await this.child3Repository.findOne({ where: { id: idNode }, relations: ["orgRevision"], }); orgRevisionId = child3?.orgRevision?.id || null; isDraft = child3?.orgRevision?.orgRevisionIsDraft || false; break; } case 5: { const child4 = await this.child4Repository.findOne({ where: { id: idNode }, relations: ["orgRevision"], }); orgRevisionId = child4?.orgRevision?.id || null; isDraft = child4?.orgRevision?.orgRevisionIsDraft || false; break; } default: throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: "); } if (!orgRevisionId) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found revision"); } // Fetch all position counts in a single batch query (optimized) const positionCounts = await getPositionCountsAggregated(orgRevisionId); switch (type) { case 0: { const data = await this.orgRevisionRepository.findOne({ where: { id: idNode }, relations: [ "orgRoots", "orgRoots.orgChild1s", "orgRoots.orgChild1s.orgChild2s", "orgRoots.orgChild1s.orgChild2s.orgChild3s", "orgRoots.orgChild1s.orgChild2s.orgChild3s.orgChild4s", ], }); if (!data) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found revision"); } const formattedData = { departmentName: data.orgRevisionName, deptID: data.id, type: 0, totalPositionCount: this.sumAllCounts(positionCounts.orgRootMap), totalPositionVacant: this.sumAllVacantCounts(positionCounts.orgRootMap, isDraft), children: this.buildOrgRoots(data.orgRoots, positionCounts, isDraft), }; return new HttpSuccess([formattedData]); } case 1: { const data = await this.orgRootRepository.findOne({ where: { id: idNode }, relations: [ "orgRevision", "orgChild1s", "orgChild1s.orgChild2s", "orgChild1s.orgChild2s.orgChild3s", "orgChild1s.orgChild2s.orgChild3s.orgChild4s", ], }); if (!data) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId"); } const rootCounts = getPositionCount(positionCounts.orgRootMap, data.id); const formattedData = { departmentName: data.orgRootName, deptID: data.id, type: 1, totalPositionCount: rootCounts.totalCount, totalPositionVacant: isDraft ? rootCounts.nextVacantCount : rootCounts.currentVacantCount, children: this.buildOrgChild1s(data.orgChild1s, positionCounts, isDraft), }; return new HttpSuccess([formattedData]); } case 2: { const data = await this.child1Repository.findOne({ where: { id: idNode }, relations: [ "orgRevision", "orgChild2s", "orgChild2s.orgChild3s", "orgChild2s.orgChild3s.orgChild4s", ], }); if (!data) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1Id"); } const child1Counts = getPositionCount(positionCounts.orgChild1Map, data.id); const formattedData = { departmentName: data.orgChild1Name, deptID: data.id, type: 2, totalPositionCount: child1Counts.totalCount, totalPositionVacant: isDraft ? child1Counts.nextVacantCount : child1Counts.currentVacantCount, children: this.buildOrgChild2s(data.orgChild2s, positionCounts, isDraft), }; return new HttpSuccess([formattedData]); } case 3: { const data = await this.child2Repository.findOne({ where: { id: idNode }, relations: ["orgRevision", "orgChild3s", "orgChild3s.orgChild4s"], }); if (!data) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2Id"); } const child2Counts = getPositionCount(positionCounts.orgChild2Map, data.id); const formattedData = { departmentName: data.orgChild2Name, deptID: data.id, type: 3, totalPositionCount: child2Counts.totalCount, totalPositionVacant: isDraft ? child2Counts.nextVacantCount : child2Counts.currentVacantCount, children: this.buildOrgChild3s(data.orgChild3s, positionCounts, isDraft), }; return new HttpSuccess([formattedData]); } case 4: { const data = await this.child3Repository.findOne({ where: { id: idNode }, relations: ["orgRevision", "orgChild4s"], }); if (!data) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3Id"); } const child3Counts = getPositionCount(positionCounts.orgChild3Map, data.id); const formattedData = { departmentName: data.orgChild3Name, deptID: data.id, type: 4, totalPositionCount: child3Counts.totalCount, totalPositionVacant: isDraft ? child3Counts.nextVacantCount : child3Counts.currentVacantCount, children: this.buildOrgChild4s(data.orgChild4s, positionCounts, isDraft), }; return new HttpSuccess([formattedData]); } case 5: { const data = await this.child4Repository.findOne({ where: { id: idNode }, relations: ["orgRevision"], }); if (!data) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4Id"); } const child4Counts = getPositionCount(positionCounts.orgChild4Map, data.id); const formattedData = { departmentName: data.orgChild4Name, deptID: data.id, type: 5, totalPositionCount: child4Counts.totalCount, totalPositionVacant: isDraft ? child4Counts.nextVacantCount : child4Counts.currentVacantCount, }; return new HttpSuccess([formattedData]); } default: throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: "); } } /** * API เช็ค node * * @summary เช็ค node (ADMIN) * */ @Post("find/node") async findNodeAll(@Body() requestBody: { node: number; nodeId: string }) { switch (requestBody.node) { case 0: { const data = await this.orgRootRepository.findOne({ where: { id: requestBody.nodeId }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId."); } return new HttpSuccess([data.id]); } case 1: { const data = await this.child1Repository.findOne({ where: { id: requestBody.nodeId }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1."); } return new HttpSuccess([data.orgRootId, data.id]); } case 2: { const data = await this.child2Repository.findOne({ where: { id: requestBody.nodeId }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2."); } return new HttpSuccess([data.orgRootId, data.orgChild1Id, data.id]); } case 3: { const data = await this.child3Repository.findOne({ where: { id: requestBody.nodeId }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3."); } return new HttpSuccess([data.orgRootId, data.orgChild1Id, data.orgChild2Id, data.id]); } case 4: { const data = await this.child4Repository.findOne({ where: { id: requestBody.nodeId }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4."); } return new HttpSuccess([ data.orgRootId, data.orgChild1Id, data.orgChild2Id, data.orgChild3Id, data.id, ]); } default: throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: " + requestBody.node); } } /** * API เช็ค node * * @summary เช็ค node (ADMIN) * */ @Post("find/node-all") async findNodeAllOrg(@Body() requestBody: { node: number | null; nodeId: string | null }) { let orgRootRankSub1 = ["BUREAU", "OFFICE"]; let orgRootRankSub2 = ["DISTRICT"]; let data1: any; let data2: any; switch (requestBody.node) { case 0: { const _data1 = await this.orgRootRepository.find({ where: { id: requestBody.nodeId ?? "", orgRootRankSub: In(orgRootRankSub1), }, order: { orgRootOrder: "ASC" }, }); const _data2 = await this.orgRootRepository.find({ where: [ { id: requestBody.nodeId ?? "", orgRootRankSub: In(orgRootRankSub2), }, { id: requestBody.nodeId ?? "", orgRootRankSub: IsNull(), }, ], order: { orgRootOrder: "ASC" }, }); data1 = _data1.map((y) => ({ name: y.orgRootName, rootId: y.id, rootDnaId: y.ancestorDNA, child1Id: null, child1DnaId: null, child2Id: null, child2DnaId: null, child3Id: null, child3DnaId: null, child4Id: null, child4DnaId: null, })); data2 = _data2.map((y) => ({ name: y.orgRootName, rootId: y.id, rootDnaId: y.ancestorDNA, child1Id: null, child1DnaId: null, child2Id: null, child2DnaId: null, child3Id: null, child3DnaId: null, child4Id: null, child4DnaId: null, })); break; } case 1: { const _data1 = await this.child1Repository.find({ where: { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: In(orgRootRankSub1), }, }, order: { orgRoot: { orgRootOrder: "ASC" }, }, relations: ["orgRoot"], }); const _data2 = await this.child1Repository.find({ where: [ { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: In(orgRootRankSub2), }, }, { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: IsNull(), }, }, ], order: { orgRoot: { orgRootOrder: "ASC" }, }, relations: ["orgRoot"], }); data1 = _data1.map((y) => ({ name: y.orgRoot.orgRootName, rootId: y.orgRoot.id, rootDnaId: y.orgRoot.ancestorDNA, child1Id: y.id, child1DnaId: y.ancestorDNA, child2Id: null, child2DnaId: null, child3Id: null, child3DnaId: null, child4Id: null, child4DnaId: null, })); data2 = _data2.map((y) => ({ name: y.orgRoot.orgRootName, rootId: y.orgRoot.id, rootDnaId: y.orgRoot.ancestorDNA, child1Id: y.id, child1DnaId: y.ancestorDNA, child2Id: null, child2DnaId: null, child3Id: null, child3DnaId: null, child4Id: null, child4DnaId: null, })); break; } case 2: { const _data1 = await this.child2Repository.find({ where: { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: In(orgRootRankSub1), }, }, order: { orgRoot: { orgRootOrder: "ASC" }, }, relations: ["orgRoot", "orgChild1"], }); const _data2 = await this.child2Repository.find({ where: [ { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: In(orgRootRankSub2), }, }, { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: IsNull(), }, }, ], order: { orgRoot: { orgRootOrder: "ASC" }, }, relations: ["orgRoot", "orgChild1"], }); data1 = _data1.map((y) => ({ name: y.orgRoot.orgRootName, rootId: y.orgRoot.id, rootDnaId: y.orgRoot.ancestorDNA, child1Id: y.orgChild1.id, child1DnaId: y.orgChild1.ancestorDNA, child2Id: y.id, child2DnaId: y.ancestorDNA, child3Id: null, child3DnaId: null, child4Id: null, child4DnaId: null, })); data2 = _data2.map((y) => ({ name: y.orgRoot.orgRootName, rootId: y.orgRoot.id, rootDnaId: y.orgRoot.ancestorDNA, child1Id: y.orgChild1.id, child1DnaId: y.orgChild1.ancestorDNA, child2Id: y.id, child2DnaId: y.ancestorDNA, child3Id: null, child3DnaId: null, child4Id: null, child4DnaId: null, })); break; } case 3: { const _data1 = await this.child3Repository.find({ where: { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: In(orgRootRankSub1), }, }, order: { orgRoot: { orgRootOrder: "ASC" }, }, relations: ["orgRoot", "orgChild1", "orgChild2"], }); const _data2 = await this.child3Repository.find({ where: [ { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: In(orgRootRankSub2), }, }, { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: IsNull(), }, }, ], order: { orgRoot: { orgRootOrder: "ASC" }, }, relations: ["orgRoot", "orgChild1", "orgChild2"], }); data1 = _data1.map((y) => ({ name: y.orgRoot.orgRootName, rootId: y.orgRoot.id, rootDnaId: y.orgRoot.ancestorDNA, child1Id: y.orgChild1.id, child1DnaId: y.orgChild1.ancestorDNA, child2Id: y.orgChild2.id, child2DnaId: y.orgChild2.ancestorDNA, child3Id: y.id, child3DnaId: y.ancestorDNA, child4Id: null, child4DnaId: null, })); data2 = _data2.map((y) => ({ name: y.orgRoot.orgRootName, rootId: y.orgRoot.id, rootDnaId: y.orgRoot.ancestorDNA, child1Id: y.orgChild1.id, child1DnaId: y.orgChild1.ancestorDNA, child2Id: y.orgChild2.id, child2DnaId: y.orgChild2.ancestorDNA, child3Id: y.id, child3DnaId: y.ancestorDNA, child4Id: null, child4DnaId: null, })); break; } case 4: { const _data1 = await this.child4Repository.find({ where: { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: In(orgRootRankSub1), }, }, order: { orgRoot: { orgRootOrder: "ASC" }, }, relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3"], }); const _data2 = await this.child4Repository.find({ where: [ { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: In(orgRootRankSub2), }, }, { id: requestBody.nodeId ?? "", orgRoot: { orgRootRankSub: IsNull(), }, }, ], order: { orgRoot: { orgRootOrder: "ASC" }, }, relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3"], }); data1 = _data1.map((y) => ({ name: y.orgRoot.orgRootName, rootId: y.orgRoot.id, rootDnaId: y.orgRoot.ancestorDNA, child1Id: y.orgChild1.id, child1DnaId: y.orgChild1.ancestorDNA, child2Id: y.orgChild2.id, child2DnaId: y.orgChild2.ancestorDNA, child3Id: y.orgChild3.id, child3DnaId: y.orgChild3.ancestorDNA, child4Id: y.id, child4DnaId: y.ancestorDNA, })); data2 = _data2.map((y) => ({ name: y.orgRoot.orgRootName, rootId: y.orgRoot.id, rootDnaId: y.orgRoot.ancestorDNA, child1Id: y.orgChild1.id, child1DnaId: y.orgChild1.ancestorDNA, child2Id: y.orgChild2.id, child2DnaId: y.orgChild2.ancestorDNA, child3Id: y.orgChild3.id, child3DnaId: y.orgChild3.ancestorDNA, child4Id: y.id, child4DnaId: y.ancestorDNA, })); break; } default: { const orgRevision = await this.orgRevisionRepository.findOne({ where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false, }, }); const _data1 = await this.orgRootRepository.find({ where: { orgRevisionId: orgRevision?.id, orgRootRankSub: In(orgRootRankSub1), }, order: { orgRootOrder: "ASC" }, }); const _data2 = await this.orgRootRepository.find({ where: [ { orgRevisionId: orgRevision?.id, orgRootRankSub: In(orgRootRankSub2), }, { orgRevisionId: orgRevision?.id, orgRootRankSub: IsNull(), }, ], order: { orgRootOrder: "ASC" }, }); data1 = _data1.map((y) => ({ name: y.orgRootName, rootId: y.id, rootDnaId: y.ancestorDNA, child1Id: null, child2Id: null, child3Id: null, child4Id: null, })); data2 = _data2.map((y) => ({ name: y.orgRootName, rootId: y.id, rootDnaId: y.ancestorDNA, child1Id: null, child2Id: null, child3Id: null, child4Id: null, })); } } return new HttpSuccess({ isRootTrue: data1, isRootFalse: data2 }); } /** * API เช็ค node detail * * @summary เช็ค node detail (ADMIN) * */ @Post("find/all") async findNodeAllDetail(@Body() requestBody: { node: number; nodeId: string }) { switch (requestBody.node) { case 0: { const data = await this.orgRootRepository.findOne({ where: { id: requestBody.nodeId }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId."); } return new HttpSuccess({ rootId: data.id, rootDnaId: data.ancestorDNA, root: data.orgRootName, rootShortName: data.orgRootShortName, }); } case 1: { const data = await this.child1Repository.findOne({ where: { id: requestBody.nodeId }, relations: { orgRoot: true, }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1."); } return new HttpSuccess({ rootId: data.orgRootId, rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA, root: data.orgRoot == null ? null : data.orgRoot.orgRootName, rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName, child1Id: data.id, child1DnaId: data.ancestorDNA, child1: data.orgChild1Name, child1ShortName: data.orgChild1ShortName, }); } case 2: { const data = await this.child2Repository.findOne({ where: { id: requestBody.nodeId }, relations: { orgRoot: true, orgChild1: true, }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2."); } return new HttpSuccess({ rootId: data.orgRootId, rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA, root: data.orgRoot == null ? null : data.orgRoot.orgRootName, rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName, child1Id: data.orgChild1Id, child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA, child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name, child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName, child2Id: data.id, child2DnaId: data.ancestorDNA, child2: data.orgChild2Name, child2ShortName: data.orgChild2ShortName, }); } case 3: { const data = await this.child3Repository.findOne({ where: { id: requestBody.nodeId }, relations: { orgRoot: true, orgChild1: true, orgChild2: true, }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3."); } return new HttpSuccess({ rootId: data.orgRootId, rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA, root: data.orgRoot == null ? null : data.orgRoot.orgRootName, rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName, child1Id: data.orgChild1Id, child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA, child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name, child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName, child2Id: data.orgChild2Id, child2DnaId: data.orgChild2 == null ? null : data.orgChild2.ancestorDNA, child2: data.orgChild2 == null ? null : data.orgChild2.orgChild2Name, child2ShortName: data.orgChild2 == null ? null : data.orgChild2.orgChild2ShortName, child3Id: data.id, child3DnaId: data.ancestorDNA, child3: data.orgChild3Name, child3ShortName: data.orgChild3ShortName, }); } case 4: { const data = await this.child4Repository.findOne({ where: { id: requestBody.nodeId }, relations: { orgRoot: true, orgChild1: true, orgChild2: true, orgChild3: true, }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4."); } return new HttpSuccess({ rootId: data.orgRootId, rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA, root: data.orgRoot == null ? null : data.orgRoot.orgRootName, rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName, child1Id: data.orgChild1Id, child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA, child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name, child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName, child2Id: data.orgChild2Id, child2DnaId: data.orgChild2 == null ? null : data.orgChild2.ancestorDNA, child2: data.orgChild2 == null ? null : data.orgChild2.orgChild2Name, child2ShortName: data.orgChild2 == null ? null : data.orgChild2.orgChild2ShortName, child3Id: data.orgChild3Id, child3DnaId: data.orgChild3 == null ? null : data.orgChild3.ancestorDNA, child3: data.orgChild3 == null ? null : data.orgChild3.orgChild3Name, child3ShortName: data.orgChild3 == null ? null : data.orgChild3.orgChild3ShortName, child4Id: data.id, child4DnaId: data.ancestorDNA, child4: data.orgChild4Name, child4ShortName: data.orgChild4ShortName, }); } default: throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: " + requestBody.node); } } /** * API เช็ค node detail * * @summary เช็ค node detail (ADMIN) * */ @Post("find/allv2") async findNodeAllDetailV2(@Body() requestBody: { node: number; nodeId: string }) { switch (requestBody.node) { case 0: { const data = await this.orgRootRepository.findOne({ where: { ancestorDNA: requestBody.nodeId }, order: { createdAt: "DESC" }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found rootId."); } return new HttpSuccess({ rootId: data.id, rootDnaId: data.ancestorDNA, root: data.orgRootName, rootShortName: data.orgRootShortName, }); } case 1: { const data = await this.child1Repository.findOne({ where: { ancestorDNA: requestBody.nodeId }, relations: { orgRoot: true, }, order: { createdAt: "DESC" }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child1."); } return new HttpSuccess({ rootId: data.orgRootId, rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA, root: data.orgRoot == null ? null : data.orgRoot.orgRootName, rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName, child1Id: data.id, child1DnaId: data.ancestorDNA, child1: data.orgChild1Name, child1ShortName: data.orgChild1ShortName, }); } case 2: { const data = await this.child2Repository.findOne({ where: { ancestorDNA: requestBody.nodeId }, relations: { orgRoot: true, orgChild1: true, }, order: { createdAt: "DESC" }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child2."); } return new HttpSuccess({ rootId: data.orgRootId, rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA, root: data.orgRoot == null ? null : data.orgRoot.orgRootName, rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName, child1Id: data.orgChild1Id, child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA, child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name, child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName, child2Id: data.id, child2DnaId: data.ancestorDNA, child2: data.orgChild2Name, child2ShortName: data.orgChild2ShortName, }); } case 3: { const data = await this.child3Repository.findOne({ where: { ancestorDNA: requestBody.nodeId }, relations: { orgRoot: true, orgChild1: true, orgChild2: true, }, order: { createdAt: "DESC" }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child3."); } return new HttpSuccess({ rootId: data.orgRootId, rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA, root: data.orgRoot == null ? null : data.orgRoot.orgRootName, rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName, child1Id: data.orgChild1Id, child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA, child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name, child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName, child2Id: data.orgChild2Id, child2DnaId: data.orgChild2 == null ? null : data.orgChild2.ancestorDNA, child2: data.orgChild2 == null ? null : data.orgChild2.orgChild2Name, child2ShortName: data.orgChild2 == null ? null : data.orgChild2.orgChild2ShortName, child3Id: data.id, child3DnaId: data.ancestorDNA, child3: data.orgChild3Name, child3ShortName: data.orgChild3ShortName, }); } case 4: { const data = await this.child4Repository.findOne({ where: { ancestorDNA: requestBody.nodeId }, relations: { orgRoot: true, orgChild1: true, orgChild2: true, orgChild3: true, }, order: { createdAt: "DESC" }, }); if (data == null) { throw new HttpError(HttpStatusCode.NOT_FOUND, "not found child4."); } return new HttpSuccess({ rootId: data.orgRootId, rootDnaId: data.orgRoot == null ? null : data.orgRoot.ancestorDNA, root: data.orgRoot == null ? null : data.orgRoot.orgRootName, rootShortName: data.orgRoot == null ? null : data.orgRoot.orgRootShortName, child1Id: data.orgChild1Id, child1DnaId: data.orgChild1 == null ? null : data.orgChild1.ancestorDNA, child1: data.orgChild1 == null ? null : data.orgChild1.orgChild1Name, child1ShortName: data.orgChild1 == null ? null : data.orgChild1.orgChild1ShortName, child2Id: data.orgChild2Id, child2DnaId: data.orgChild2 == null ? null : data.orgChild2.ancestorDNA, child2: data.orgChild2 == null ? null : data.orgChild2.orgChild2Name, child2ShortName: data.orgChild2 == null ? null : data.orgChild2.orgChild2ShortName, child3Id: data.orgChild3Id, child3DnaId: data.orgChild3 == null ? null : data.orgChild3.ancestorDNA, child3: data.orgChild3 == null ? null : data.orgChild3.orgChild3Name, child3ShortName: data.orgChild3 == null ? null : data.orgChild3.orgChild3ShortName, child4Id: data.id, child4DnaId: data.ancestorDNA, child4: data.orgChild4Name, child4ShortName: data.orgChild4ShortName, }); } default: throw new HttpError(HttpStatusCode.NOT_FOUND, "not found type: " + requestBody.node); } } /** * API หาสำนักทั้งหมด * * @summary หาสำนักทั้งหมด * */ @Get("active/root") async GetActiveRoot() { const orgRevisionActive = await this.orgRevisionRepository.findOne({ where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); if (!orgRevisionActive) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างที่เผยแพร่อยู่ตอนนี้"); } const data = await this.orgRootRepository.find({ where: { orgRevisionId: orgRevisionActive.id }, relations: [ "orgRevision", "orgChild1s", "orgChild1s.orgChild2s", "orgChild1s.orgChild2s.orgChild3s", "orgChild1s.orgChild2s.orgChild3s.orgChild4s", ], order: { orgChild1s: { orgChild1Order: "ASC", orgChild2s: { orgChild2Order: "ASC", orgChild3s: { orgChild3Order: "ASC", orgChild4s: { orgChild4Order: "ASC", }, }, }, }, }, }); return new HttpSuccess(data); } /** * API หาสำนักทั้งหมด * * @summary หาสำนักทั้งหมด * */ @Get("active/root/id") async GetActiveRootId() { const orgRevisionActive = await this.orgRevisionRepository.findOne({ where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); if (!orgRevisionActive) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างที่เผยแพร่อยู่ตอนนี้"); } const data = await this.orgRootRepository.find({ where: { orgRevisionId: orgRevisionActive.id }, order: { orgRootOrder: "ASC", }, }); return new HttpSuccess(data.map((x) => x.id)); } /** * API หาสำนักทั้งหมด * * @summary หาสำนักทั้งหมด * */ @Get("active/root/all") async GetActiveRootAll() { const orgRevisionActive = await this.orgRevisionRepository.findOne({ where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); if (!orgRevisionActive) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างที่เผยแพร่อยู่ตอนนี้"); } const data = await this.orgRootRepository.find({ where: { orgRevisionId: orgRevisionActive.id, id: In([ "d7e98989-b5ce-47d6-93c3-ab63ed486348", "6f9b30e1-757a-40d5-b053-61eb1b91c0f0", "eaf65f33-25e9-4956-9dba-5d909f5eb595", "87c5bc89-300c-4b6a-99e4-26371436caa2", "982d33af-4eb5-4cc8-9c9f-b3ccadbb66d7", "e0545eca-5d0a-4a1c-8bbd-e3e25c2521db", ]), }, order: { orgRootOrder: "ASC", }, }); return new HttpSuccess(data); } /** * API * * @summary * */ @Get("active/root/latest") async GetActiveRootIdLatest() { const orgRevisionActive = await this.orgRevisionRepository.findOne({ where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); if (!orgRevisionActive) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างที่เผยแพร่อยู่ตอนนี้"); } return new HttpSuccess(orgRevisionActive.id); } /** * API หาสำนักทั้งหมด by revision * * @summary หาสำนักทั้งหมด by revision * */ @Get("active/root/{revisionId}") async GetActiveRootByRevision(@Path() revisionId: string) { const rawData = await this.orgRootRepository.find({ where: { orgRevisionId: revisionId }, relations: [ "orgRevision", "orgChild1s", "orgChild1s.orgChild2s", "orgChild1s.orgChild2s.orgChild3s", "orgChild1s.orgChild2s.orgChild3s.orgChild4s", ], order: { orgRootOrder: "ASC", orgChild1s: { orgChild1Order: "ASC", orgChild2s: { orgChild2Order: "ASC", orgChild3s: { orgChild3Order: "ASC", orgChild4s: { orgChild4Order: "ASC", }, }, }, }, }, }); const data = rawData.map((item) => ({ ...item, orgRootCode: item.orgRootCode + "00", })); return new HttpSuccess(data); } /** * API หา revision ล่าสุด * * @summary หา revision ล่าสุด * */ @Get("revision/latest") async salaryGen() { const findRevision = await this.orgRevisionRepository.findOne({ where: { orgRevisionIsCurrent: true }, }); if (!findRevision) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบโครงสร้างล่าสุด"); } return new HttpSuccess(findRevision.id); } /** * API รายละเอียดโครงสร้าง * * @summary รายละเอียดโครงสร้าง (ADMIN) * */ @Get("act/{id}") async detailAct(@Path() id: string, @Request() request: RequestWithUser) { let _data: OrgPermissionData = { root: null, child1: null, child2: null, child3: null, child4: null, }; // if (!request.user.role.includes("SUPER_ADMIN")) { // _data = await new permission().PermissionOrgList(request, "SYS_ACTING"); // } const _privilege = await new permission().PermissionOrgList(request, "SYS_ACTING"); const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } }); if (!orgRevision) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); } const attrOwnership = _privilege.root === null ? true : false; const profile = await this.profileRepo.findOne({ where: { keycloak: request.user.sub }, relations: ["permissionProfiles", "current_holders", "current_holders.posMasterAssigns"], }); if (!profile) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งานในทะเบียนประวัติ"); } let profileAssign = profile.current_holders ?.find((x) => x.orgRevisionId === id) ?.posMasterAssigns.find((x) => x.assignId === "SYS_ORG"); if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) { 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; if (isCurrentActive) { if (profileAssign && _privilege.privilege !== "OWNER" && _privilege.privilege !== "PARENT") { if (_privilege.privilege == "NORMAL") { const holder = profile.current_holders.find((x) => x.orgRevisionId === id); if (!holder) return new HttpSuccess({ remark: "", data: [] }); _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" || _privilege.privilege == "BROTHER") { const holder = profile.current_holders.find((x) => x.orgRevisionId === id); if (!holder) return new HttpSuccess({ remark: "", data: [] }); _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]; } } else { if (!attrOwnership) _data = _privilege; } } const orgDna = await new permission().checkDna(request, request.user.sub); let level: NodeLevel = resolveNodeLevel(orgDna); const orgRootData = await AppDataSource.getRepository(OrgRoot) .createQueryBuilder("orgRoot") .select([ "orgRoot.id", "orgRoot.orgRootName", "orgRoot.orgRootShortName", "orgRoot.orgRootCode", "orgRoot.orgRootOrder", ]) .addSelect([ "posMasters.id", "posMasters.posMasterNo", "posMasters.orgChild1Id", "posMasters.isDirector", ]) .addSelect(["current_holder.prefix", "current_holder.firstName", "current_holder.lastName"]) .where("orgRoot.orgRevisionId = :id", { id }) .andWhere( _data.root != undefined && _data.root != null ? _data.root[0] != null ? `orgRoot.id IN (:...node)` : `orgRoot.id is null` : "1=1", { node: _data.root, }, ) .leftJoin("orgRoot.posMasters", "posMasters") .leftJoin("posMasters.current_holder", "current_holder") .orderBy("orgRoot.orgRootOrder", "ASC") .getMany(); const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null; const orgChild1Data = orgRootIds && orgRootIds.length > 0 ? await AppDataSource.getRepository(OrgChild1) .createQueryBuilder("orgChild1") .select([ "orgChild1.id", "orgChild1.orgRootId", "orgChild1.orgChild1Name", "orgChild1.orgChild1ShortName", "orgChild1.orgChild1Code", "orgChild1.orgChild1Order", ]) .addSelect([ "posMasters.id", "posMasters.posMasterNo", "posMasters.orgChild2Id", "posMasters.isDirector", ]) .addSelect([ "current_holder.prefix", "current_holder.firstName", "current_holder.lastName", ]) .where("orgChild1.orgRootId IN (:...ids)", { ids: orgRootIds }) .andWhere( _data.child1 != undefined && _data.child1 != null ? _data.child1[0] != null ? `orgChild1.id IN (:...node)` : `orgChild1.id is null` : "1=1", { node: _data.child1, }, ) .leftJoin("orgChild1.posMasters", "posMasters") .leftJoin("posMasters.current_holder", "current_holder") .orderBy("orgChild1.orgChild1Order", "ASC") .getMany() : []; const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id) || null; const orgChild2Data = orgChild1Ids && orgChild1Ids.length > 0 ? await AppDataSource.getRepository(OrgChild2) .createQueryBuilder("orgChild2") .select([ "orgChild2.id", "orgChild2.orgChild1Id", "orgChild2.orgChild2Name", "orgChild2.orgChild2ShortName", "orgChild2.orgChild2Code", "orgChild2.orgChild2Order", ]) .addSelect([ "posMasters.id", "posMasters.posMasterNo", "posMasters.orgChild3Id", "posMasters.isDirector", ]) .addSelect([ "current_holder.prefix", "current_holder.firstName", "current_holder.lastName", ]) .where("orgChild2.orgChild1Id IN (:...ids)", { ids: orgChild1Ids }) .andWhere( _data.child2 != undefined && _data.child2 != null ? _data.child2[0] != null ? `orgChild2.id IN (:...node)` : `orgChild2.id is null` : "1=1", { node: _data.child2, }, ) .leftJoin("orgChild2.posMasters", "posMasters") .leftJoin("posMasters.current_holder", "current_holder") .orderBy("orgChild2.orgChild2Order", "ASC") .getMany() : []; const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id) || null; const orgChild3Data = orgChild2Ids && orgChild2Ids.length > 0 ? await AppDataSource.getRepository(OrgChild3) .createQueryBuilder("orgChild3") .select([ "orgChild3.id", "orgChild3.orgChild2Id", "orgChild3.orgChild3Name", "orgChild3.orgChild3ShortName", "orgChild3.orgChild3Code", "orgChild3.orgChild3Order", ]) .addSelect([ "posMasters.id", "posMasters.posMasterNo", "posMasters.orgChild4Id", "posMasters.isDirector", ]) .addSelect([ "current_holder.prefix", "current_holder.firstName", "current_holder.lastName", ]) .where("orgChild3.orgChild2Id IN (:...ids)", { ids: orgChild2Ids }) .andWhere( _data.child3 != undefined && _data.child3 != null ? _data.child3[0] != null ? `orgChild3.id IN (:...node)` : `orgChild3.id is null` : "1=1", { node: _data.child3, }, ) .leftJoin("orgChild3.posMasters", "posMasters") .leftJoin("posMasters.current_holder", "current_holder") .orderBy("orgChild3.orgChild3Order", "ASC") .getMany() : []; const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id) || null; const orgChild4Data = orgChild3Ids && orgChild3Ids.length > 0 ? await AppDataSource.getRepository(OrgChild4) .createQueryBuilder("orgChild4") .select([ "orgChild4.id", "orgChild4.orgChild3Id", "orgChild4.orgChild4Name", "orgChild4.orgChild4ShortName", "orgChild4.orgChild4Code", "orgChild4.orgChild4Order", ]) .addSelect(["posMasters.id", "posMasters.posMasterNo", "posMasters.isDirector"]) .addSelect([ "current_holder.prefix", "current_holder.firstName", "current_holder.lastName", ]) .where("orgChild4.orgChild3Id IN (:...ids)", { ids: orgChild3Ids }) .andWhere( _data.child4 != undefined && _data.child4 != null ? _data.child4[0] != null ? `orgChild4.id IN (:...node)` : `orgChild4.id is null` : "1=1", { node: _data.child4, }, ) .leftJoin("orgChild4.posMasters", "posMasters") .leftJoin("posMasters.current_holder", "current_holder") .orderBy("orgChild4.orgChild4Order", "ASC") .getMany() : []; const cannotViewRootPosMaster = _privilege.privilege === "PARENT" || (_privilege.privilege === "BROTHER" && level !== null && level > 1) || (_privilege.privilege === "CHILD" && level !== null && level > 0) || (_privilege.privilege === "NORMAL" && level !== null && level !== 0); const cannotViewChild1PosMaster = (_privilege.privilege === "PARENT" && level !== null && level > 1) || (_privilege.privilege === "BROTHER" && level !== null && level > 2) || (_privilege.privilege === "CHILD" && level !== null && level > 1) || (_privilege.privilege === "NORMAL" && level !== 1); const cannotViewChild2PosMaster = (_privilege.privilege === "PARENT" && level !== null && level > 2) || (_privilege.privilege === "BROTHER" && level !== null && level > 3) || (_privilege.privilege === "CHILD" && level !== null && level > 2) || (_privilege.privilege === "NORMAL" && level !== 2); const cannotViewChild3PosMaster = (_privilege.privilege === "PARENT" && level !== null && level > 3) || (_privilege.privilege === "BROTHER" && level !== null && level > 4) || (_privilege.privilege === "CHILD" && level !== null && level > 3) || (_privilege.privilege === "NORMAL" && level !== 3); const cannotViewChild4PosMaster = (_privilege.privilege === "PARENT" && level !== null && level > 4) || (_privilege.privilege === "CHILD" && level !== null && level > 4) || (_privilege.privilege === "NORMAL" && level !== 4); const formattedData = orgRootData.map((orgRoot) => ({ orgTreeId: orgRoot.id, orgLevel: 0, orgName: orgRoot.orgRootName, orgTreeName: orgRoot.orgRootName, orgTreeShortName: orgRoot.orgRootShortName, orgTreeCode: orgRoot.orgRootCode, orgCode: orgRoot.orgRootCode + "00", orgRootName: orgRoot.orgRootName, labelName: generateLabelName( orgRoot.orgRootName, orgRoot.orgRootCode + "00", orgRoot.orgRootShortName, orgRoot.orgRootName, orgRoot.orgRootCode, orgRoot.orgRootShortName, ), posMaster: cannotViewRootPosMaster ? [] : filterPosMasters(orgRoot.posMasters, "orgChild1Id").map((x) => formatPosMaster(x, orgRoot.orgRootShortName, orgRoot.id, 0), ), children: orgChild1Data .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id) .map((orgChild1) => ({ orgTreeId: orgChild1.id, orgRootId: orgRoot.id, orgLevel: 1, orgName: `${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild1.orgChild1Name, orgTreeShortName: orgChild1.orgChild1ShortName, orgTreeCode: orgChild1.orgChild1Code, orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code, orgRootName: orgRoot.orgRootName, labelName: generateLabelName( orgChild1.orgChild1Name, orgChild1.orgChild1Code, orgChild1.orgChild1ShortName, orgRoot.orgRootName, orgRoot.orgRootCode, orgRoot.orgRootShortName, ), posMaster: cannotViewChild1PosMaster ? [] : filterPosMasters(orgChild1.posMasters, "orgChild2Id").map((x) => formatPosMaster(x, orgChild1.orgChild1ShortName, orgChild1.id, 1), ), children: orgChild2Data .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id) .map((orgChild2) => ({ orgTreeId: orgChild2.id, orgRootId: orgChild1.id, orgLevel: 2, orgName: `${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild2.orgChild2Name, orgTreeShortName: orgChild2.orgChild2ShortName, orgTreeCode: orgChild2.orgChild2Code, orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code, orgRootName: orgRoot.orgRootName, labelName: generateLabelName( orgChild2.orgChild2Name, orgChild2.orgChild2Code, orgChild2.orgChild2ShortName, orgRoot.orgRootName, orgRoot.orgRootCode, orgRoot.orgRootShortName, [orgChild1.orgChild1Name], [orgChild1.orgChild1Code], [orgChild1.orgChild1ShortName], ), posMaster: cannotViewChild2PosMaster ? [] : filterPosMasters(orgChild2.posMasters, "orgChild3Id").map((x) => formatPosMaster(x, orgChild2.orgChild2ShortName, orgChild2.id, 2), ), children: orgChild3Data .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id) .map((orgChild3) => ({ orgTreeId: orgChild3.id, orgRootId: orgChild2.id, orgLevel: 3, orgName: `${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild3.orgChild3Name, orgTreeShortName: orgChild3.orgChild3ShortName, orgTreeCode: orgChild3.orgChild3Code, orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code, orgRootName: orgRoot.orgRootName, labelName: generateLabelName( orgChild3.orgChild3Name, orgChild3.orgChild3Code, orgChild3.orgChild3ShortName, orgRoot.orgRootName, orgRoot.orgRootCode, orgRoot.orgRootShortName, [orgChild2.orgChild2Name, orgChild1.orgChild1Name], [orgChild2.orgChild2Code, orgChild1.orgChild1Code], [orgChild2.orgChild2ShortName, orgChild1.orgChild1ShortName], ), posMaster: cannotViewChild3PosMaster ? [] : filterPosMasters(orgChild3.posMasters, "orgChild4Id").map((x) => formatPosMaster(x, orgChild3.orgChild3ShortName, orgChild3.id, 3), ), children: orgChild4Data .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id) .map((orgChild4) => ({ orgTreeId: orgChild4.id, orgRootId: orgChild3.id, orgLevel: 4, orgName: `${orgChild4.orgChild4Name}/${orgChild3.orgChild3Name}/${orgChild2.orgChild2Name}/${orgChild1.orgChild1Name}/${orgRoot.orgRootName}`, orgTreeName: orgChild4.orgChild4Name, orgTreeShortName: orgChild4.orgChild4ShortName, orgTreeCode: orgChild4.orgChild4Code, orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code, orgRootName: orgRoot.orgRootName, labelName: generateLabelName( orgChild4.orgChild4Name, orgChild4.orgChild4Code, orgChild4.orgChild4ShortName, orgRoot.orgRootName, orgRoot.orgRootCode, orgRoot.orgRootShortName, [orgChild3.orgChild3Name, orgChild2.orgChild2Name, orgChild1.orgChild1Name], [orgChild3.orgChild3Code, orgChild2.orgChild2Code, orgChild1.orgChild1Code], [ orgChild3.orgChild3ShortName, orgChild2.orgChild2ShortName, orgChild1.orgChild1ShortName, ], ), posMaster: cannotViewChild4PosMaster ? [] : orgChild4.posMasters .filter((x) => x.isDirector === true) .map((x) => formatPosMaster(x, orgChild4.orgChild4ShortName, orgChild4.id, 4), ), })), })), })), })), })); return new HttpSuccess(formattedData); } /** * API * * @summary (ADMIN) * * @param {string} id */ @Get("approver/{id}") async getUserRootOrg(@Path() id: string, @Request() request: RequestWithUser) { if (id == "00000000-0000-0000-0000-000000000000") { const maps = { id: "00000000-0000-0000-0000-000000000000", name: "", positionName: "ปลัดกรุงเทพมหานคร", }; return new HttpSuccess(maps); } const root = await this.orgRootRepository.findOne({ where: { id: id }, }); if (!root) throw new HttpError(HttpStatusCode.NOT_FOUND, "not found. Root"); const posMaster = await this.posMasterRepository.find({ where: { orgRootId: root.id, orgChild1Id: IsNull(), current_holder: Not(IsNull()) }, relations: ["current_holder"], }); if (!posMaster) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่ง"); const maps = posMaster.map((posMaster) => ({ id: posMaster?.current_holder?.id, name: `${posMaster?.current_holder?.prefix}${posMaster?.current_holder?.firstName} ${posMaster?.current_holder?.lastName}`, positionName: posMaster?.current_holder?.position, })); return new HttpSuccess(maps); } /** * API รายละเอียดโครงสร้าง * * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN) #25 * */ @Get("system/{id}/{system}") async detailBySystem( @Path() id: string, @Path() system: string, @Request() request: RequestWithUser, ) { const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } }); if (!orgRevision) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); } let _data: any = { root: null, child1: null, child2: null, child3: null, child4: null, }; if ( (orgRevision.orgRevisionIsDraft == true && orgRevision.orgRevisionIsCurrent == false) || system != "SYS_ORG" ) { _data = await new permission().PermissionOrgList(request, system.trim().toUpperCase()); } const profile = await this.profileRepo.findOne({ where: { keycloak: request.user.sub }, relations: ["permissionProfiles", "current_holders"], }); if (!profile) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งานในทะเบียนประวัติ"); } let _privilege = await new permission().PermissionOrgList(request, system); const attrOwnership = _privilege.root === null ? true : false; if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) { 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; if (isCurrentActive) { if (_privilege.privilege !== "OWNER" && _privilege.privilege !== "PARENT") { 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" || _privilege.privilege == "BROTHER") { 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]; } } else { if (!attrOwnership) _data = _privilege; } } const orgRootData = await AppDataSource.getRepository(OrgRoot) .createQueryBuilder("orgRoot") .where("orgRoot.orgRevisionId = :id", { id }) .andWhere( _data.root != undefined && _data.root != null ? _data.root[0] != null ? `orgRoot.id IN (:...node)` : `orgRoot.id is null` : "1=1", { node: _data.root, }, ) .select([ "orgRoot.id", "orgRoot.ancestorDNA", "orgRoot.orgRootName", "orgRoot.orgRootShortName", "orgRoot.orgRootCode", "orgRoot.orgRootOrder", "orgRoot.orgRootPhoneEx", "orgRoot.orgRootPhoneIn", "orgRoot.orgRootFax", "orgRoot.orgRevisionId", "orgRoot.orgRootRank", "orgRoot.orgRootRankSub", "orgRoot.DEPARTMENT_CODE", "orgRoot.DIVISION_CODE", "orgRoot.SECTION_CODE", "orgRoot.JOB_CODE", "orgRoot.responsibility", ]) .orderBy("orgRoot.orgRootOrder", "ASC") .getMany(); const orgRootIds = orgRootData.map((orgRoot) => orgRoot.id) || null; const orgChild1Data = orgRootIds && orgRootIds.length > 0 ? await AppDataSource.getRepository(OrgChild1) .createQueryBuilder("orgChild1") .where("orgChild1.orgRootId IN (:...ids)", { ids: orgRootIds }) .andWhere( _data.child1 != undefined && _data.child1 != null ? _data.child1[0] != null ? `orgChild1.id IN (:...node)` : `orgChild1.id is ${_data.privilege == "PARENT" ? "not null" : "null"}` : "1=1", { node: _data.child1, }, ) .select([ "orgChild1.id", "orgChild1.ancestorDNA", "orgChild1.orgChild1Name", "orgChild1.orgChild1ShortName", "orgChild1.orgChild1Code", "orgChild1.orgChild1Order", "orgChild1.orgChild1PhoneEx", "orgChild1.orgChild1PhoneIn", "orgChild1.orgChild1Fax", "orgChild1.orgRootId", "orgChild1.orgChild1Rank", "orgChild1.orgChild1RankSub", "orgChild1.DEPARTMENT_CODE", "orgChild1.DIVISION_CODE", "orgChild1.SECTION_CODE", "orgChild1.JOB_CODE", "orgChild1.responsibility", ]) .orderBy("orgChild1.orgChild1Order", "ASC") .getMany() : []; const orgChild1Ids = orgChild1Data.map((orgChild1) => orgChild1.id) || null; const orgChild2Data = orgChild1Ids && orgChild1Ids.length > 0 ? await AppDataSource.getRepository(OrgChild2) .createQueryBuilder("orgChild2") .where("orgChild2.orgChild1Id IN (:...ids)", { ids: orgChild1Ids }) .andWhere( _data.child2 != undefined && _data.child2 != null ? _data.child2[0] != null ? `orgChild2.id IN (:...node)` : `orgChild2.id is null` : "1=1", { node: _data.child2, }, ) .select([ "orgChild2.id", "orgChild2.ancestorDNA", "orgChild2.orgChild2Name", "orgChild2.orgChild2ShortName", "orgChild2.orgChild2Code", "orgChild2.orgChild2Order", "orgChild2.orgChild2PhoneEx", "orgChild2.orgChild2PhoneIn", "orgChild2.orgChild2Fax", "orgChild2.orgRootId", "orgChild2.orgChild2Rank", "orgChild2.orgChild2RankSub", "orgChild2.DEPARTMENT_CODE", "orgChild2.DIVISION_CODE", "orgChild2.SECTION_CODE", "orgChild2.JOB_CODE", "orgChild2.orgChild1Id", "orgChild2.responsibility", ]) .orderBy("orgChild2.orgChild2Order", "ASC") .getMany() : []; const orgChild2Ids = orgChild2Data.map((orgChild2) => orgChild2.id) || null; const orgChild3Data = orgChild2Ids && orgChild2Ids.length > 0 ? await AppDataSource.getRepository(OrgChild3) .createQueryBuilder("orgChild3") .where("orgChild3.orgChild2Id IN (:...ids)", { ids: orgChild2Ids }) .andWhere( _data.child3 != undefined && _data.child3 != null ? _data.child3[0] != null ? `orgChild3.id IN (:...node)` : `orgChild3.id is null` : "1=1", { node: _data.child3, }, ) .select([ "orgChild3.id", "orgChild3.ancestorDNA", "orgChild3.orgChild3Name", "orgChild3.orgChild3ShortName", "orgChild3.orgChild3Code", "orgChild3.orgChild3Order", "orgChild3.orgChild3PhoneEx", "orgChild3.orgChild3PhoneIn", "orgChild3.orgChild3Fax", "orgChild3.orgRootId", "orgChild3.orgChild3Rank", "orgChild3.orgChild3RankSub", "orgChild3.DEPARTMENT_CODE", "orgChild3.DIVISION_CODE", "orgChild3.SECTION_CODE", "orgChild3.JOB_CODE", "orgChild3.orgChild2Id", "orgChild3.responsibility", ]) .orderBy("orgChild3.orgChild3Order", "ASC") .getMany() : []; const orgChild3Ids = orgChild3Data.map((orgChild3) => orgChild3.id) || null; const orgChild4Data = orgChild3Ids && orgChild3Ids.length > 0 ? await AppDataSource.getRepository(OrgChild4) .createQueryBuilder("orgChild4") .where("orgChild4.orgChild3Id IN (:...ids)", { ids: orgChild3Ids }) .andWhere( _data.child4 != undefined && _data.child4 != null ? _data.child4[0] != null ? `orgChild4.id IN (:...node)` : `orgChild4.id is null` : "1=1", { node: _data.child4, }, ) .select([ "orgChild4.id", "orgChild4.ancestorDNA", "orgChild4.orgChild4Name", "orgChild4.orgChild4ShortName", "orgChild4.orgChild4Code", "orgChild4.orgChild4Order", "orgChild4.orgChild4PhoneEx", "orgChild4.orgChild4PhoneIn", "orgChild4.orgChild4Fax", "orgChild4.orgRootId", "orgChild4.orgChild4Rank", "orgChild4.orgChild4RankSub", "orgChild4.DEPARTMENT_CODE", "orgChild4.DIVISION_CODE", "orgChild4.SECTION_CODE", "orgChild4.JOB_CODE", "orgChild4.orgChild3Id", "orgChild4.responsibility", ]) .orderBy("orgChild4.orgChild4Order", "ASC") .getMany() : []; // const formattedData = orgRootData.map((orgRoot) => { const formattedData = await Promise.all( orgRootData.map(async (orgRoot) => { // const sum0 = await this.posMasterRepository.find({ // where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id }, // select: [ // "current_holderId", // "next_holderId", // "orgRootId", // "orgChild1Id", // "orgChild2Id", // "orgChild3Id", // "orgChild4Id", // ], // }); return { orgTreeId: orgRoot.id, orgTreeDnaId: orgRoot.ancestorDNA, orgLevel: 0, orgName: orgRoot.orgRootName, orgTreeName: orgRoot.orgRootName, orgTreeShortName: orgRoot.orgRootShortName, orgTreeCode: orgRoot.orgRootCode, orgCode: orgRoot.orgRootCode + "00", orgTreeRank: orgRoot.orgRootRank, orgTreeRankSub: orgRoot.orgRootRankSub, DEPARTMENT_CODE: orgRoot.DEPARTMENT_CODE, DIVISION_CODE: orgRoot.DIVISION_CODE, SECTION_CODE: orgRoot.SECTION_CODE, JOB_CODE: orgRoot.JOB_CODE, orgTreeOrder: orgRoot.orgRootOrder, orgTreePhoneEx: orgRoot.orgRootPhoneEx, orgTreePhoneIn: orgRoot.orgRootPhoneIn, orgTreeFax: orgRoot.orgRootFax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgRoot.responsibility, labelName: orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, // totalPosition: sum0.length + 1, // totalPositionCurrentUse: // sum0.filter((x) => x.current_holderId != null && x.current_holderId != "").length + 1, // totalPositionCurrentVacant: // sum0.filter((x) => x.current_holderId == null || x.current_holderId == "").length + 1, // totalPositionNextUse: // sum0.filter((x) => x.next_holderId != null && x.next_holderId != "").length + 1, // totalPositionNextVacant: // sum0.filter((x) => x.next_holderId == null || x.next_holderId == "").length + 1, // totalRootPosition: // sum0.filter( // (x) => // (x.orgChild1Id == null || x.orgChild1Id == "") && // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == ""), // ).length + 1, // totalRootPositionCurrentUse: // sum0.filter( // (x) => // (x.orgChild1Id == null || x.orgChild1Id == "") && // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // x.current_holderId != null && // x.current_holderId != "", // ).length + 1, // totalRootPositionCurrentVacant: // sum0.filter( // (x) => // (x.orgChild1Id == null || x.orgChild1Id == "") && // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // (x.current_holderId == null || x.current_holderId == ""), // ).length + 1, // totalRootPositionNextUse: // sum0.filter( // (x) => // (x.orgChild1Id == null || x.orgChild1Id == "") && // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // x.next_holderId != null && // x.next_holderId != "", // ).length + 1, // totalRootPositionNextVacant: // sum0.filter( // (x) => // (x.orgChild1Id == null || x.orgChild1Id == "") && // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // (x.next_holderId == null || x.next_holderId == ""), // ).length + 1, children: await Promise.all( orgChild1Data .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id) .map(async (orgChild1) => { // const sum1 = await this.posMasterRepository.find({ // where: { // orgRevisionId: orgRoot.orgRevisionId, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // }, // select: [ // "current_holderId", // "next_holderId", // "orgRootId", // "orgChild1Id", // "orgChild2Id", // "orgChild3Id", // "orgChild4Id", // ], // }); return { orgTreeId: orgChild1.id, orgTreeDnaId: orgChild1.ancestorDNA, orgRootId: orgRoot.id, orgRootDnaId: orgRoot.ancestorDNA, orgLevel: 1, orgName: `${orgChild1.orgChild1Name} ${orgRoot.orgRootName}`, orgTreeName: orgChild1.orgChild1Name, orgTreeShortName: orgChild1.orgChild1ShortName, orgTreeCode: orgChild1.orgChild1Code, orgCode: orgRoot.orgRootCode + orgChild1.orgChild1Code, orgTreeRank: orgChild1.orgChild1Rank, orgTreeRankSub: orgChild1.orgChild1RankSub, DEPARTMENT_CODE: orgChild1.DEPARTMENT_CODE, DIVISION_CODE: orgChild1.DIVISION_CODE, SECTION_CODE: orgChild1.SECTION_CODE, JOB_CODE: orgChild1.JOB_CODE, orgTreeOrder: orgChild1.orgChild1Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild1.orgChild1PhoneEx, orgTreePhoneIn: orgChild1.orgChild1PhoneIn, orgTreeFax: orgChild1.orgChild1Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild1.responsibility, labelName: orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName + "/" + orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, // totalPosition: sum1.length + 1, // totalPositionCurrentUse: // sum1.filter((x) => x.current_holderId != null && x.current_holderId != "") // .length + 1, // totalPositionCurrentVacant: // sum1.filter((x) => x.current_holderId == null || x.current_holderId == "") // .length + 1, // totalPositionNextUse: // sum1.filter((x) => x.next_holderId != null && x.next_holderId != "").length + 1, // totalPositionNextVacant: // sum1.filter((x) => x.next_holderId == null || x.next_holderId == "").length + 1, // totalRootPosition: // sum1.filter( // (x) => // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == ""), // ).length + 1, // totalRootPositionCurrentUse: // sum1.filter( // (x) => // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // x.current_holderId != null && // x.current_holderId != "", // ).length + 1, // totalRootPositionCurrentVacant: // sum1.filter( // (x) => // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // (x.current_holderId == null || x.current_holderId == ""), // ).length + 1, // totalRootPositionNextUse: // sum1.filter( // (x) => // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // x.next_holderId != null && // x.next_holderId != "", // ).length + 1, // totalRootPositionNextVacant: // sum1.filter( // (x) => // (x.orgChild2Id == null || x.orgChild2Id == "") && // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // (x.next_holderId == null || x.next_holderId == ""), // ).length + 1, children: await Promise.all( orgChild2Data .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id) .map(async (orgChild2) => { // const sum2 = await this.posMasterRepository.find({ // where: { // orgRevisionId: orgRoot.orgRevisionId, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // }, // select: [ // "current_holderId", // "next_holderId", // "orgRootId", // "orgChild1Id", // "orgChild2Id", // "orgChild3Id", // "orgChild4Id", // ], // }); return { orgTreeId: orgChild2.id, orgTreeDnaId: orgChild2.ancestorDNA, orgRootId: orgChild1.id, orgRootDnaId: orgChild1.ancestorDNA, orgLevel: 2, orgName: `${orgChild2.orgChild2Name} ${orgChild1.orgChild1Name} ${orgRoot.orgRootName}`, orgTreeName: orgChild2.orgChild2Name, orgTreeShortName: orgChild2.orgChild2ShortName, orgTreeCode: orgChild2.orgChild2Code, orgCode: orgRoot.orgRootCode + orgChild2.orgChild2Code, orgTreeRank: orgChild2.orgChild2Rank, orgTreeRankSub: orgChild2.orgChild2RankSub, DEPARTMENT_CODE: orgChild2.DEPARTMENT_CODE, DIVISION_CODE: orgChild2.DIVISION_CODE, SECTION_CODE: orgChild2.SECTION_CODE, JOB_CODE: orgChild2.JOB_CODE, orgTreeOrder: orgChild2.orgChild2Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild2.orgChild2PhoneEx, orgTreePhoneIn: orgChild2.orgChild2PhoneIn, orgTreeFax: orgChild2.orgChild2Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild2.responsibility, labelName: orgChild2.orgChild2Name + " " + orgRoot.orgRootCode + orgChild2.orgChild2Code + " " + orgChild2.orgChild2ShortName + "/" + orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName + "/" + orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, // totalPosition: sum2.length + 1, // totalPositionCurrentUse: // sum2.filter( // (x) => x.current_holderId != null && x.current_holderId != "", // ).length + 1, // totalPositionCurrentVacant: // sum2.filter( // (x) => x.current_holderId == null || x.current_holderId == "", // ).length + 1, // totalPositionNextUse: // sum2.filter((x) => x.next_holderId != null && x.next_holderId != "") // .length + 1, // totalPositionNextVacant: // sum2.filter((x) => x.next_holderId == null || x.next_holderId == "") // .length + 1, // totalRootPosition: // sum2.filter( // (x) => // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == ""), // ).length + 1, // totalRootPositionCurrentUse: // sum2.filter( // (x) => // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // x.current_holderId != null && // x.current_holderId != "", // ).length + 1, // totalRootPositionCurrentVacant: // sum2.filter( // (x) => // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // (x.current_holderId == null || x.current_holderId == ""), // ).length + 1, // totalRootPositionNextUse: // sum2.filter( // (x) => // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // x.next_holderId != null && // x.next_holderId != "", // ).length + 1, // totalRootPositionNextVacant: // sum2.filter( // (x) => // (x.orgChild3Id == null || x.orgChild3Id == "") && // (x.orgChild4Id == null || x.orgChild4Id == "") && // (x.next_holderId == null || x.next_holderId == ""), // ).length + 1, children: await Promise.all( orgChild3Data .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id) .map(async (orgChild3) => { // const sum3 = await this.posMasterRepository.find({ // where: { // orgRevisionId: orgRoot.orgRevisionId, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // }, // select: [ // "current_holderId", // "next_holderId", // "orgRootId", // "orgChild1Id", // "orgChild2Id", // "orgChild3Id", // "orgChild4Id", // ], // }); return { orgTreeId: orgChild3.id, orgTreeDnaId: orgChild3.ancestorDNA, orgRootId: orgChild2.id, orgRootDnaId: orgChild2.ancestorDNA, orgLevel: 3, orgName: `${orgChild3.orgChild3Name} ${orgChild2.orgChild2Name} ${orgChild1.orgChild1Name} ${orgRoot.orgRootName}`, orgTreeName: orgChild3.orgChild3Name, orgTreeShortName: orgChild3.orgChild3ShortName, orgTreeCode: orgChild3.orgChild3Code, orgCode: orgRoot.orgRootCode + orgChild3.orgChild3Code, orgTreeRank: orgChild3.orgChild3Rank, orgTreeRankSub: orgChild3.orgChild3RankSub, DEPARTMENT_CODE: orgChild3.DEPARTMENT_CODE, DIVISION_CODE: orgChild3.DIVISION_CODE, SECTION_CODE: orgChild3.SECTION_CODE, JOB_CODE: orgChild3.JOB_CODE, orgTreeOrder: orgChild3.orgChild3Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild3.orgChild3PhoneEx, orgTreePhoneIn: orgChild3.orgChild3PhoneIn, orgTreeFax: orgChild3.orgChild3Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild3.responsibility, labelName: orgChild3.orgChild3Name + " " + orgRoot.orgRootCode + orgChild3.orgChild3Code + " " + orgChild3.orgChild3ShortName + "/" + orgChild2.orgChild2Name + " " + orgRoot.orgRootCode + orgChild2.orgChild2Code + " " + orgChild2.orgChild2ShortName + "/" + orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName + "/" + orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, // totalPosition: sum3.length + 1, // totalPositionCurrentUse: // sum3.filter( // (x) => x.current_holderId != null && x.current_holderId != "", // ).length + 1, // totalPositionCurrentVacant: // sum3.filter( // (x) => x.current_holderId == null || x.current_holderId == "", // ).length + 1, // totalPositionNextUse: // sum3.filter( // (x) => x.next_holderId != null && x.next_holderId != "", // ).length + 1, // totalPositionNextVacant: // sum3.filter( // (x) => x.next_holderId == null || x.next_holderId == "", // ).length + 1, // totalRootPosition: // sum3.filter((x) => x.orgChild4Id == null || x.orgChild4Id == "") // .length + 1, // totalRootPositionCurrentUse: // sum3.filter( // (x) => // (x.orgChild4Id == null || x.orgChild4Id == "") && // x.current_holderId != null && // x.current_holderId != "", // ).length + 1, // totalRootPositionCurrentVacant: // sum3.filter( // (x) => // (x.orgChild4Id == null || x.orgChild4Id == "") && // (x.current_holderId == null || x.current_holderId == ""), // ).length + 1, // totalRootPositionNextUse: // sum3.filter( // (x) => // (x.orgChild4Id == null || x.orgChild4Id == "") && // x.next_holderId != null && // x.next_holderId != "", // ).length + 1, // totalRootPositionNextVacant: // sum3.filter( // (x) => // (x.orgChild4Id == null || x.orgChild4Id == "") && // (x.next_holderId == null || x.next_holderId == ""), // ).length + 1, children: await Promise.all( orgChild4Data .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id) .map(async (orgChild4) => { // const sum4 = await this.posMasterRepository.find({ // where: { // orgRevisionId: orgRoot.orgRevisionId, // orgRootId: orgRoot.id, // orgChild1Id: orgChild1.id, // orgChild2Id: orgChild2.id, // orgChild3Id: orgChild3.id, // orgChild4Id: orgChild4.id, // }, // select: [ // "current_holderId", // "next_holderId", // "orgRootId", // "orgChild1Id", // "orgChild2Id", // "orgChild3Id", // "orgChild4Id", // ], // }); return { orgTreeId: orgChild4.id, orgTreeDnaId: orgChild4.ancestorDNA, orgRootId: orgChild3.id, orgRootDnaId: orgChild3.ancestorDNA, orgLevel: 4, orgName: `${orgChild4.orgChild4Name} ${orgChild3.orgChild3Name} ${orgChild2.orgChild2Name} ${orgChild1.orgChild1Name} ${orgRoot.orgRootName}`, orgTreeName: orgChild4.orgChild4Name, orgTreeShortName: orgChild4.orgChild4ShortName, orgTreeCode: orgChild4.orgChild4Code, orgCode: orgRoot.orgRootCode + orgChild4.orgChild4Code, orgTreeRank: orgChild4.orgChild4Rank, orgTreeRankSub: orgChild4.orgChild4RankSub, DEPARTMENT_CODE: orgChild4.DEPARTMENT_CODE, DIVISION_CODE: orgChild4.DIVISION_CODE, SECTION_CODE: orgChild4.SECTION_CODE, JOB_CODE: orgChild4.JOB_CODE, orgTreeOrder: orgChild4.orgChild4Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild4.orgChild4PhoneEx, orgTreePhoneIn: orgChild4.orgChild4PhoneIn, orgTreeFax: orgChild4.orgChild4Fax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgChild4.responsibility, labelName: orgChild4.orgChild4Name + " " + orgRoot.orgRootCode + orgChild4.orgChild4Code + " " + orgChild4.orgChild4ShortName + "/" + orgChild3.orgChild3Name + " " + orgRoot.orgRootCode + orgChild3.orgChild3Code + " " + orgChild3.orgChild3ShortName + "/" + orgChild2.orgChild2Name + " " + orgRoot.orgRootCode + orgChild2.orgChild2Code + " " + orgChild2.orgChild2ShortName + "/" + orgChild1.orgChild1Name + " " + orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + orgChild1.orgChild1ShortName + "/" + orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, // totalPosition: sum4.length + 1, // totalPositionCurrentUse: // sum4.filter( // (x) => // x.current_holderId != null && // x.current_holderId != "", // ).length + 1, // totalPositionCurrentVacant: // sum4.filter( // (x) => // x.current_holderId == null || // x.current_holderId == "", // ).length + 1, // totalPositionNextUse: // sum4.filter( // (x) => // x.next_holderId != null && x.next_holderId != "", // ).length + 1, // totalPositionNextVacant: // sum4.filter( // (x) => // x.next_holderId == null || x.next_holderId == "", // ).length + 1, // totalRootPosition: sum4.length + 1, // totalRootPositionCurrentUse: // sum4.filter( // (x) => // x.current_holderId != null && // x.current_holderId != "", // ).length + 1, // totalRootPositionCurrentVacant: // sum4.filter( // (x) => // x.current_holderId == null || // x.current_holderId == "", // ).length + 1, // totalRootPositionNextUse: // sum4.filter( // (x) => // x.next_holderId != null && x.next_holderId != "", // ).length + 1, // totalRootPositionNextVacant: // sum4.filter( // (x) => // x.next_holderId == null || x.next_holderId == "", // ).length + 1, }; }), ), }; }), ), }; }), ), }; }), ), }; }), ); return new HttpSuccess(formattedData); } /** * API รายละเอียดโครงสร้าง * * @summary ORG_023 - รายละเอียดโครงสร้าง (ADMIN) #25 * */ @Get("system-root/{id}/{system}") async detailBySystemRoot( @Path() id: string, @Path() system: string, @Request() request: RequestWithUser, ) { const orgRevision = await this.orgRevisionRepository.findOne({ where: { id } }); if (!orgRevision) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); } let _data = { root: null, child1: null, child2: null, child3: null, child4: null, }; if (orgRevision.orgRevisionIsDraft == true && orgRevision.orgRevisionIsCurrent == false) { _data = await new permission().PermissionOrgList(request, system.trim().toUpperCase()); } const orgRootData = await AppDataSource.getRepository(OrgRoot) .createQueryBuilder("orgRoot") .where("orgRoot.orgRevisionId = :id", { id }) .andWhere( _data.root != undefined && _data.root != null ? _data.root[0] != null ? `orgRoot.id IN (:...node)` : `orgRoot.id is null` : "1=1", { node: _data.root, }, ) .select([ "orgRoot.id", "orgRoot.orgRootName", "orgRoot.orgRootShortName", "orgRoot.orgRootCode", "orgRoot.orgRootOrder", "orgRoot.orgRootPhoneEx", "orgRoot.orgRootPhoneIn", "orgRoot.orgRootFax", "orgRoot.orgRevisionId", "orgRoot.orgRootRank", "orgRoot.orgRootRankSub", "orgRoot.responsibility", "orgRoot.isDeputy", ]) .orderBy("orgRoot.orgRootOrder", "ASC") .getMany(); const formattedData = await Promise.all( orgRootData.map(async (orgRoot) => { return { orgTreeId: orgRoot.id, orgLevel: 0, isDeputy: orgRoot.isDeputy, orgName: orgRoot.orgRootName, orgTreeName: orgRoot.orgRootName, orgTreeShortName: orgRoot.orgRootShortName, orgTreeCode: orgRoot.orgRootCode, orgCode: orgRoot.orgRootCode + "00", orgTreeRank: orgRoot.orgRootRank, orgTreeRankSub: orgRoot.orgRootRankSub, orgTreeOrder: orgRoot.orgRootOrder, orgTreePhoneEx: orgRoot.orgRootPhoneEx, orgTreePhoneIn: orgRoot.orgRootPhoneIn, orgTreeFax: orgRoot.orgRootFax, orgRevisionId: orgRoot.orgRevisionId, orgRootName: orgRoot.orgRootName, responsibility: orgRoot.responsibility, labelName: orgRoot.orgRootName + " " + orgRoot.orgRootCode + "00" + " " + orgRoot.orgRootShortName, totalPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id }, }), totalPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, current_holderId: Not(IsNull()) || Not(""), }, }), totalPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, current_holderId: IsNull() || "", }, }), totalPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, next_holderId: Not(IsNull()) || Not(""), }, }), totalPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, next_holderId: IsNull() || "", }, }), totalRootPosition: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", }, }), totalRootPositionCurrentUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", current_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionCurrentVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", current_holderId: IsNull() || "", }, }), totalRootPositionNextUse: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", next_holderId: Not(IsNull()) || Not(""), }, }), totalRootPositionNextVacant: await this.posMasterRepository.count({ where: { orgRevisionId: orgRoot.orgRevisionId, orgRootId: orgRoot.id, orgChild1Id: IsNull() || "", orgChild2Id: IsNull() || "", orgChild3Id: IsNull() || "", orgChild4Id: IsNull() || "", next_holderId: IsNull() || "", }, }), }; }), ); return new HttpSuccess(formattedData); } /** * API เช็ค org ในระบบ * * @summary - เช็ค org ในระบบ (ADMIN) * */ @Get("check/child1/{id}") async findIsOfficerChild1(@Path() id: string, @Request() request: RequestWithUser) { const orgRevision = await this.orgRevisionRepository.findOne({ where: { id }, relations: ["orgChild1s"], }); if (!orgRevision) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); } const check = orgRevision.orgChild1s.find((x) => x.isOfficer == true); return new HttpSuccess(check != null); } /** * API เช็ค org ในระบบ * * @summary - เช็ค org ในระบบ (ADMIN) * */ @Get("check/root/{id}") async findIsDeputyRoot(@Path() id: string, @Request() request: RequestWithUser) { const orgRevision = await this.orgRevisionRepository.findOne({ where: { id }, relations: ["orgRoots"], }); if (!orgRevision) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); } const check = orgRevision.orgRoots.find((x) => x.isDeputy == true); return new HttpSuccess(check != null); } /** * API หา สกก1 * * @summary - หา สกก1 (ADMIN) * */ @Get("find/head/officer") async findIsHeadOfficer(@Request() request: RequestWithUser) { const posMaster = await this.posMasterRepository.find({ where: { orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, orgRoot: { isCommission: true }, orgChild1Id: IsNull(), isDirector: true, current_holderId: Not(IsNull()), // posMasterActs: { statusReport: "DONE" }, // posMasterActChilds: { statusReport: "DONE" }, }, order: { posMasterOrder: "ASC", posMasterActChilds: { posMasterOrder: "ASC" } }, relations: [ "current_holder", // "posMasterActChilds", // "posMasterActChilds.posMasterChild", // "posMasterActChilds.posMasterChild.current_holder", ], }); if (posMaster.length <= 0) { return new HttpSuccess({ name: "......................................................", position: "......................................................", }); } if (posMaster[0].current_holder == null) { const posMaster = await this.posMasterRepository.find({ where: { orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, orgRoot: { isCommission: true }, orgChild1Id: IsNull(), isDirector: true, posMasterActs: { statusReport: "DONE" }, posMasterActChilds: { statusReport: "DONE" }, }, order: { posMasterOrder: "ASC", posMasterActChilds: { posMasterOrder: "ASC" } }, relations: [ "current_holder", "posMasterActChilds", "posMasterActChilds.posMasterChild", "posMasterActChilds.posMasterChild.current_holder", ], }); if ( posMaster[0].posMasterActChilds.length <= 0 || posMaster[0].posMasterActChilds[0].posMasterChild == null || posMaster[0].posMasterActChilds[0].posMasterChild.current_holder == null ) { return new HttpSuccess({ name: "......................................................", position: "......................................................", }); } return new HttpSuccess({ name: `${posMaster[0].posMasterActChilds[0].posMasterChild.current_holder.prefix}${posMaster[0].posMasterActChilds[0].posMasterChild.current_holder.firstName} ${posMaster[0].posMasterActChilds[0].posMasterChild.current_holder.lastName}`, position: posMaster[0].posMasterActChilds[0].posMasterChild.current_holder.position, }); } else { return new HttpSuccess({ name: `${posMaster[0].current_holder.prefix}${posMaster[0].current_holder.firstName} ${posMaster[0].current_holder.lastName}`, position: posMaster[0].current_holder.position, }); } } /** * API ลำดับโครงสร้าง * * @summary - ลำดับโครงสร้าง (ADMIN) * */ @Get("root/search/sort") async searchSortRootLevelType(@Request() request: RequestWithUser) { // const root1 = await this.orgRootRepository.find({ // where: { // orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, // DEPARTMENT_CODE: Not("50"), // }, // order: { isDeputy: "DESC", orgRootOrder: "ASC" }, // select: ["orgRootName"], // }); // const root2 = await this.orgRootRepository.find({ // where: { // orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, // DEPARTMENT_CODE: "50", // }, // order: { orgRootName: "ASC" }, // select: ["orgRootName"], // }); // const root = [...root1, ...root2]; // const child1 = await this.child1Repository.find({ // where: { // orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, // }, // order: { orgChild1Order: "ASC" }, // select: ["orgChild1Name"], // }); // const child2 = await this.child2Repository.find({ // where: { // orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, // }, // order: { orgChild2Order: "ASC" }, // select: ["orgChild2Name"], // }); // const child3 = await this.child3Repository.find({ // where: { // orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, // }, // order: { orgChild3Order: "ASC" }, // select: ["orgChild3Name"], // }); // const child4 = await this.child4Repository.find({ // where: { // orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, // }, // order: { orgChild4Order: "ASC" }, // select: ["orgChild4Name"], // }); // const hospital = await this.child1Repository.find({ // where: [ // { // orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, // orgRoot: { isDeputy: true }, // }, // { // orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, // orgChild1RankSub: "HOSPITAL", // }, // ], // select: ["orgChild1Name"], // }); // const posType = await this.posTypeRepository.find({ // order: { posTypeRank: "DESC" }, // select: ["posTypeName"], // }); // const posLevel = await this.posLevelRepository.find({ // order: { posLevelRank: "DESC" }, // select: ["posLevelName"], // }); const [roots, child1, child2, child3, child4, hospital, posType, posLevel] = await Promise.all([ // ===== ROOT ===== this.orgRootRepository .createQueryBuilder("root") .innerJoin("root.orgRevision", "rev") .select(["root.orgRootName AS orgRootName"]) .where("rev.orgRevisionIsDraft = false") .andWhere("rev.orgRevisionIsCurrent = true") .orderBy("CASE WHEN root.DEPARTMENT_CODE = '50' THEN 1 ELSE 0 END", "ASC") .addOrderBy("root.isDeputy", "DESC") .addOrderBy("root.orgRootOrder", "ASC") .addOrderBy("root.orgRootName", "ASC") .getRawMany(), // ===== CHILD 1 ===== this.child1Repository .createQueryBuilder("c1") .innerJoin("c1.orgRevision", "rev") .select("c1.orgChild1Name", "orgChild1Name") .where("rev.orgRevisionIsDraft = false") .andWhere("rev.orgRevisionIsCurrent = true") .orderBy("c1.orgChild1Order", "ASC") .getRawMany(), // ===== CHILD 2 ===== this.child2Repository .createQueryBuilder("c2") .innerJoin("c2.orgRevision", "rev") .select("c2.orgChild2Name", "orgChild2Name") .where("rev.orgRevisionIsDraft = false") .andWhere("rev.orgRevisionIsCurrent = true") .orderBy("c2.orgChild2Order", "ASC") .getRawMany(), // ===== CHILD 3 ===== this.child3Repository .createQueryBuilder("c3") .innerJoin("c3.orgRevision", "rev") .select("c3.orgChild3Name", "orgChild3Name") .where("rev.orgRevisionIsDraft = false") .andWhere("rev.orgRevisionIsCurrent = true") .orderBy("c3.orgChild3Order", "ASC") .getRawMany(), // ===== CHILD 4 ===== this.child4Repository .createQueryBuilder("c4") .innerJoin("c4.orgRevision", "rev") .select("c4.orgChild4Name", "orgChild4Name") .where("rev.orgRevisionIsDraft = false") .andWhere("rev.orgRevisionIsCurrent = true") .orderBy("c4.orgChild4Order", "ASC") .getRawMany(), // ===== HOSPITAL ===== this.child1Repository .createQueryBuilder("c1") .innerJoin("c1.orgRevision", "rev") .leftJoin("c1.orgRoot", "root") .select("c1.orgChild1Name", "orgChild1Name") .where("rev.orgRevisionIsDraft = false") .andWhere("rev.orgRevisionIsCurrent = true") .andWhere("(root.isDeputy = true OR c1.orgChild1RankSub = :rank)", { rank: "HOSPITAL" }) .getRawMany(), // ===== POSITION TYPE ===== this.posTypeRepository .createQueryBuilder("pt") .select("pt.posTypeName", "posTypeName") .orderBy("pt.posTypeRank", "DESC") .getRawMany(), // ===== POSITION LEVEL ===== this.posLevelRepository .createQueryBuilder("pl") .select("pl.posLevelName", "posLevelName") .orderBy("pl.posLevelRank", "DESC") .getRawMany(), ]); return new HttpSuccess({ root: roots.map((x) => x.orgRootName), child1: child1.map((x) => x.orgChild1Name), child2: child2.map((x) => x.orgChild2Name), child3: child3.map((x) => x.orgChild3Name), child4: child4.map((x) => x.orgChild4Name), hospital: hospital.map((x) => x.orgChild1Name), posTypeNameOrder: posType.map((x) => x.posTypeName), posLevelNameOrder: posLevel.map((x) => x.posLevelName), }); } /** * API เพิ่มสิทธิ์โครงสร้าง * * @summary - เพิ่มสิทธิ์โครงสร้าง (ADMIN) * */ @Get("root/add/permission/{child1Id}") async addRootPermission(@Path() child1Id: string, @Request() request: RequestWithUser) { const profiles = await this.profileRepo.find({ where: { keycloak: Not(IsNull()), current_holders: { orgChild1Id: child1Id, }, }, }); const orgRoots = await this.orgRootRepository.find({ where: { orgRevision: { orgRevisionIsDraft: true, orgRevisionIsCurrent: false, }, }, }); for await (const root of orgRoots) { const _permissionOrg = profiles.map((profile) => { const permission = new PermissionOrg(); permission.orgRootId = root.id; permission.profileId = profile.id; permission.createdUserId = request.user.sub; permission.createdFullName = request.user.name; permission.lastUpdateUserId = request.user.sub; permission.lastUpdateFullName = request.user.name; permission.createdAt = new Date(); permission.lastUpdatedAt = new Date(); return permission; }); await this.permissionOrgRepository.save(_permissionOrg); } return new HttpSuccess(); } /** * API ลบข้าราชการในโครงสร้าง * * @summary - ลบข้าราชการในโครงสร้าง (ADMIN) * */ @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, }, // positions: { positionIsSelected: true }, }, relations: ["positions"], }); let checkOfficer = 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); }), ]); 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; if (posMaster.positions) { for (const position of posMaster.positions) { position.positionIsSelected = false; await this.employeePositionRepository.save(position); checkEmployee += 1; } } await this.employeePosMasterRepository.save(posMaster); await CreatePosMasterHistoryEmployee(posMaster.id, null); }), ); return new HttpSuccess({ totalEmployee: posMastersEmployee.length, employeeSuccessAmount: checkEmployee, }); } /** * API บันทึกลงประวัติตำแหน่ง * * @summary - บันทึกลงประวัติตำแหน่ง (ADMIN) * */ @Get("save/profile/position-history") async saveRetireToPositionHistory(@Request() request: RequestWithUser) { 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 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" }, }); const data: any = { order: dest_item == null ? 1 : dest_item.order + 1, amount: null, positionSalaryAmount: null, mouthSalaryAmount: null, profileEmployeeId: 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); checkEmployee += 1; }), ); // จำนวนคนที่บันทึกลงประวัติตำแหน่ง const totalOfficer = profileLeave.length; const totalEmployee = profileEmployeeLeave.length; // จำนวนคนที่ถูกบันทึกลงประวัติตำแหน่งสำเร็จ return new HttpSuccess({ totalOfficer, totalEmployee, officerSuccessAmount: checkOfficer, successEmployeeAmount: checkEmployee, }); } /** * API แก้ไขเหตุผลการลาออก และปลดจาก keycloak * * @summary - แก้ไขเหตุผลการลาออก และปลดจาก keycloak (ADMIN) * */ @Get("update/profile/leave-reason") async updateRetireReason() { const [profileLeave, profileEmployeeLeave, token] = await Promise.all([ this.profileRepo.find({ where: { isLeave: true, isRetirement: true, leaveType: IsNull(), }, }), this.profileEmployeeRepo.find({ where: { isLeave: true, isRetirement: true, leaveType: IsNull(), }, }), getToken(), ]); if (!token) throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "ไม่สามารถเชื่อมต่อ Keycloak"); let checkOfficer: number = 0; let notDeleteOfficer: string[] = []; await Promise.all( profileLeave.map(async (profile) => { profile.leaveReason = "เกษียณอายุราชการ"; profile.leaveType = "RETIRE"; profile.isActive = false; if (profile.keycloak != null && profile.keycloak != "" && profile.isDelete === false) { const delUserKeycloak = await deleteUser(profile.keycloak, token); if (delUserKeycloak) { // profile.keycloak = ""; profile.isDelete = true; profile.roleKeycloaks = []; checkOfficer += 1; } else { // push array not delete notDeleteOfficer.push(profile.keycloak); } } await this.profileRepo.save(profile); }), ); 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 != "" && profileEmp.isDelete === false) { const delUserKeycloak = await deleteUser(profileEmp.keycloak, token); if (delUserKeycloak) { // profileEmp.keycloak = ""; profileEmp.isDelete = true; 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) { let check: number = 0; const profiles = await this.profileEmployeeRepo.find({ where: { keycloak: IsNull(), isLeave: false, isRetirement: false, }, order: { citizenId: "ASC" }, relations: ["roleKeycloaks"], }); // ดึงข้อมูลที่ใช้บ่อยก่อน (cache) const rolesList = await getRoles(); if (!Array.isArray(rolesList)) throw new Error("Failed. Cannot get role(s) data from the server."); 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; _item = await this.profileEmployeeRepo.save(_item); // update user attribute in keycloak await updateUserAttributes(_item.keycloak, { profileId: [_item.id], prefix: [_item.prefix || ""], }); } } catch (error) { console.error(`Error processing ${_item.citizenId}:`, error); } }), ); } const total = profiles.length; return new HttpSuccess({ total, successAmount: check }); } /** * API ย้ายโครงสร้างแบบร่างไปโครงสร้างปัจจุบัน โดยอ้างอิงตาม rootId * * @summary - ย้ายโครงสร้างและตำแหน่งจากแบบร่างไปโครงสร้างปัจจุบัน โดยอ้างอิงตาม rootId * */ @Post("move-draft-to-current/{rootDnaId}") async moveDraftToCurrent(@Path() rootDnaId: string, @Request() request: RequestWithUser) { const queryRunner = AppDataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { // permission owner only ?? // this code check... // part 1 ข้อมูลโครงสร้าง const [drafRevision, currentRevision] = await Promise.all([ this.orgRevisionRepository.findOne({ where: { orgRevisionIsDraft: true, orgRevisionIsCurrent: false, }, select: ["id"], }), this.orgRevisionRepository.findOne({ where: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true, }, select: ["id"], }), ]); if (!drafRevision || !currentRevision) return new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); const drafRevisionId = drafRevision.id; const currentRevisionId = currentRevision.id; // ตรวจสอบว่ามี rootDnaId ในโครงสร้างร่าง และในโครงสร้างปัจจุบันหรือไม่ let [orgRootDraft, orgRootCurrent] = await Promise.all([ this.orgRootRepository.findOne({ where: { ancestorDNA: rootDnaId, orgRevisionId: drafRevisionId, }, // select: ["id"], }), this.orgRootRepository.findOne({ where: { ancestorDNA: rootDnaId, orgRevisionId: currentRevisionId, }, select: ["id"], }), ]); if (!orgRootDraft) return new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโครงสร้างร่าง"); // if current record not found, create new one if (!orgRootCurrent) { // Create new current record using draft's ID const newCurrentRoot = queryRunner.manager.create(OrgRoot, { ...orgRootDraft, id: undefined, // Let database generate new ID orgRevisionId: currentRevisionId, // Change to current revision }); const savedRoot = await queryRunner.manager.save(OrgRoot, newCurrentRoot); orgRootCurrent = savedRoot; // Use saved record for sync } // Part 1: Differential sync of organization structure (bottom-up) // Build mapping incrementally as we process each level const allMappings: AllOrgMappings = { orgRoot: { byAncestorDNA: new Map(), byDraftId: new Map() }, orgChild1: { byAncestorDNA: new Map(), byDraftId: new Map() }, orgChild2: { byAncestorDNA: new Map(), byDraftId: new Map() }, orgChild3: { byAncestorDNA: new Map(), byDraftId: new Map() }, orgChild4: { byAncestorDNA: new Map(), byDraftId: new Map() }, }; // Track sync statistics for organization nodes const orgSyncStats: Record = {}; // Process from top (Root) to bottom (Child4) to handle foreign key constraints // OrgRoot (sync first - no parent dependencies) // If we manually created orgRootCurrent, skip syncOrgLevel and set up mapping directly // to avoid double insert (syncOrgLevel would try to insert again because IDs don't match) let orgRootResult: { mapping: OrgIdMapping; counts: { deleted: number; updated: number; inserted: number }; }; if ( orgRootCurrent && orgRootDraft && orgRootCurrent.ancestorDNA === orgRootDraft.ancestorDNA ) { // Manually created - set up mapping directly const rootMapping: OrgIdMapping = { byAncestorDNA: new Map([[orgRootDraft.ancestorDNA, orgRootCurrent.id]]), byDraftId: new Map([[orgRootDraft.id, orgRootCurrent.id]]), }; orgRootResult = { mapping: rootMapping, counts: { deleted: 0, updated: 0, inserted: 1 }, // Count as insert since we created it }; } else { // Not manually created - use normal syncOrgLevel flow orgRootResult = await this.syncOrgLevel( queryRunner, OrgRoot, this.orgRootRepository, drafRevisionId, currentRevisionId, allMappings, orgRootDraft?.id, orgRootCurrent?.id, ); } allMappings.orgRoot = orgRootResult.mapping; orgSyncStats.orgRoot = orgRootResult.counts; // Child1 (parent OrgRoot already synced) const child1Result = await this.syncOrgLevel( queryRunner, OrgChild1, this.child1Repository, drafRevisionId, currentRevisionId, allMappings, orgRootDraft?.id, orgRootCurrent?.id, ); allMappings.orgChild1 = child1Result.mapping; orgSyncStats.orgChild1 = child1Result.counts; // Child2 (parents OrgRoot and Child1 already synced) const child2Result = await this.syncOrgLevel( queryRunner, OrgChild2, this.child2Repository, drafRevisionId, currentRevisionId, allMappings, orgRootDraft?.id, orgRootCurrent?.id, ); allMappings.orgChild2 = child2Result.mapping; orgSyncStats.orgChild2 = child2Result.counts; // Child3 (parents OrgRoot, Child1, Child2 already synced) const child3Result = await this.syncOrgLevel( queryRunner, OrgChild3, this.child3Repository, drafRevisionId, currentRevisionId, allMappings, orgRootDraft?.id, orgRootCurrent?.id, ); allMappings.orgChild3 = child3Result.mapping; orgSyncStats.orgChild3 = child3Result.counts; // Child4 (parents OrgRoot, Child1, Child2, Child3 already synced) const child4Result = await this.syncOrgLevel( queryRunner, OrgChild4, this.child4Repository, drafRevisionId, currentRevisionId, allMappings, orgRootDraft?.id, orgRootCurrent?.id, ); allMappings.orgChild4 = child4Result.mapping; orgSyncStats.orgChild4 = child4Result.counts; // Part 2: Sync position data using new org IDs from Part 1 // 2.1 Clear current_holderId for affected positions (keep existing logic) // Get draft organization IDs under the given rootDnaId to find positions to clear const draftOrgIds = { orgRoot: [...allMappings.orgRoot.byDraftId.keys()], orgChild1: [...allMappings.orgChild1.byDraftId.keys()], orgChild2: [...allMappings.orgChild2.byDraftId.keys()], orgChild3: [...allMappings.orgChild3.byDraftId.keys()], orgChild4: [...allMappings.orgChild4.byDraftId.keys()], }; // Get current organization IDs from the mappings (moved up for reuse) const currentOrgIds = { orgRoot: [...allMappings.orgRoot.byDraftId.values()], orgChild1: [...allMappings.orgChild1.byDraftId.values()], orgChild2: [...allMappings.orgChild2.byDraftId.values()], orgChild3: [...allMappings.orgChild3.byDraftId.values()], orgChild4: [...allMappings.orgChild4.byDraftId.values()], }; // Get draft positions that belong to any org under the rootDnaId const posMasterDraft = await this.posMasterRepository.find({ where: [ { orgRevisionId: drafRevisionId, orgRootId: In(draftOrgIds.orgRoot) }, { orgRevisionId: drafRevisionId, orgChild1Id: In(draftOrgIds.orgChild1) }, { orgRevisionId: drafRevisionId, orgChild2Id: In(draftOrgIds.orgChild2) }, { orgRevisionId: drafRevisionId, orgChild3Id: In(draftOrgIds.orgChild3) }, { orgRevisionId: drafRevisionId, orgChild4Id: In(draftOrgIds.orgChild4) }, ], }); // Special case: If draft has no positions, delete all current positions if (posMasterDraft.length <= 0) { // Fetch current positions const posMasterCurrent = await this.posMasterRepository.find({ where: [ { orgRevisionId: currentRevisionId, orgRootId: In(currentOrgIds.orgRoot) }, { orgRevisionId: currentRevisionId, orgChild1Id: In(currentOrgIds.orgChild1) }, { orgRevisionId: currentRevisionId, orgChild2Id: In(currentOrgIds.orgChild2) }, { orgRevisionId: currentRevisionId, orgChild3Id: In(currentOrgIds.orgChild3) }, { orgRevisionId: currentRevisionId, orgChild4Id: In(currentOrgIds.orgChild4) }, ], }); if (posMasterCurrent.length > 0) { const toDeleteIds = posMasterCurrent.map((p) => p.id); // Cascade delete positions first await queryRunner.manager.delete(Position, { posMasterId: In(toDeleteIds) }); // Then delete posMaster records await queryRunner.manager.delete(PosMaster, toDeleteIds); // Save history const deleteHistoryOps = posMasterCurrent.map((pos) => ({ posMasterDnaId: pos.ancestorDNA, profileId: null, pm: null, })); await BatchSavePosMasterHistoryOfficer(queryRunner, deleteHistoryOps); } // Build summary for this special case const summary = { message: "ย้ายโครงสร้างสำเร็จ", organization: { orgRoot: orgSyncStats.orgRoot, orgChild1: orgSyncStats.orgChild1, orgChild2: orgSyncStats.orgChild2, orgChild3: orgSyncStats.orgChild3, orgChild4: orgSyncStats.orgChild4, }, positionMaster: { deleted: posMasterCurrent.length, updated: 0, inserted: 0, }, position: { deleted: 0, updated: 0, inserted: 0 }, }; await queryRunner.commitTransaction(); return new HttpSuccess(summary); } // Clear current_holderId for positions that will have new holders const nextHolderIds = posMasterDraft .filter((x) => x.next_holderId != null) .map((x) => x.next_holderId) as string[]; if (nextHolderIds.length > 0) { // FIX: Fetch positions first before updating (to avoid race condition) const posMastersToUpdate = await queryRunner.manager.find(PosMaster, { where: { orgRevisionId: currentRevisionId, current_holderId: In(nextHolderIds), }, }); // Save history BEFORE clearing current_holderId const historyOps = posMastersToUpdate .filter((x) => x.orgRootId != orgRootCurrent?.id) .map((pos) => ({ posMasterDnaId: pos.ancestorDNA, profileId: null, pm: null, })); await BatchSavePosMasterHistoryOfficer(queryRunner, historyOps); // Now clear current_holderId await queryRunner.manager.update( PosMaster, { orgRevisionId: currentRevisionId, current_holderId: In(nextHolderIds), }, { current_holderId: null, isSit: false }, ); } // 2.2 Fetch current positions for comparison const posMasterCurrent = await this.posMasterRepository.find({ where: [ { orgRevisionId: currentRevisionId, orgRootId: In(currentOrgIds.orgRoot) }, { orgRevisionId: currentRevisionId, orgChild1Id: In(currentOrgIds.orgChild1) }, { orgRevisionId: currentRevisionId, orgChild2Id: In(currentOrgIds.orgChild2) }, { orgRevisionId: currentRevisionId, orgChild3Id: In(currentOrgIds.orgChild3) }, { orgRevisionId: currentRevisionId, orgChild4Id: In(currentOrgIds.orgChild4) }, ], }); // Build lookup map const currentByDNA = new Map(posMasterCurrent.map((p) => [p.ancestorDNA, p])); // 2.3 Batch DELETE: positions in current but not in draft const toDelete = posMasterCurrent.filter( (curr) => !posMasterDraft.some((d) => d.ancestorDNA === curr.ancestorDNA), ); if (toDelete.length > 0) { const toDeleteIds = toDelete.map((p) => p.id); // Cascade delete positions first await queryRunner.manager.delete(Position, { posMasterId: In(toDeleteIds) }); // Then delete posMaster records await queryRunner.manager.delete(PosMaster, toDeleteIds); const deleteHistoryOps = toDelete.map((pos) => ({ posMasterDnaId: pos.ancestorDNA, profileId: null, pm: null, })); await BatchSavePosMasterHistoryOfficer(queryRunner, deleteHistoryOps); } // 2.4 Process draft positions (UPDATE or INSERT) const toUpdate: PosMaster[] = []; const toInsert: any[] = []; // Track draft PosMaster ID to current PosMaster ID mapping for position sync // Type: Map const posMasterMapping: Map = new Map(); for (const draftPos of posMasterDraft) { const current = currentByDNA.get(draftPos.ancestorDNA); // Map organization IDs using new IDs from Part 1 const orgRootId = this.resolveOrgId(draftPos.orgRootId ?? null, allMappings.orgRoot); const orgChild1Id = this.resolveOrgId(draftPos.orgChild1Id ?? null, allMappings.orgChild1); const orgChild2Id = this.resolveOrgId(draftPos.orgChild2Id ?? null, allMappings.orgChild2); const orgChild3Id = this.resolveOrgId(draftPos.orgChild3Id ?? null, allMappings.orgChild3); const orgChild4Id = this.resolveOrgId(draftPos.orgChild4Id ?? null, allMappings.orgChild4); if (current) { // UPDATE existing position Object.assign(current, { createdAt: draftPos.createdAt, createdUserId: draftPos.createdUserId, createdFullName: draftPos.createdFullName, lastUpdatedAt: new Date(), lastUpdateUserId: request.user.sub, lastUpdateFullName: request.user.name, posMasterNoPrefix: draftPos.posMasterNoPrefix, posMasterNoSuffix: draftPos.posMasterNoSuffix, posMasterNo: draftPos.posMasterNo, posMasterOrder: draftPos.posMasterOrder, orgRootId, orgChild1Id, orgChild2Id, orgChild3Id, orgChild4Id, current_holderId: draftPos.next_holderId, isSit: draftPos.isSit, reason: draftPos.reason, isDirector: draftPos.isDirector, isStaff: draftPos.isStaff, positionSign: draftPos.positionSign, statusReport: "DONE", isCondition: draftPos.isCondition, conditionReason: draftPos.conditionReason, }); toUpdate.push(current); if (draftPos.next_holderId === null) { await SavePosMasterHistoryOfficer(queryRunner, draftPos.ancestorDNA, null, null); } // Track mapping for position sync posMasterMapping.set(draftPos.id, [current.id, draftPos.next_holderId]); } else { // INSERT new position const newPosMaster = queryRunner.manager.create(PosMaster, { ...draftPos, id: undefined, orgRevisionId: currentRevisionId, orgRootId, orgChild1Id, orgChild2Id, orgChild3Id, orgChild4Id, current_holderId: draftPos.next_holderId, statusReport: "DONE", }); toInsert.push(newPosMaster); } } // Batch save updates and inserts if (toUpdate.length > 0) { await queryRunner.manager.save(toUpdate); } if (toInsert.length > 0) { const saved = await queryRunner.manager.save(toInsert); // Track mapping for newly inserted posMasters // saved is an array, map each to its draft ID if (Array.isArray(saved)) { for (let i = 0; i < saved.length; i++) { const draftPos = posMasterDraft.filter((d) => !currentByDNA.has(d.ancestorDNA))[i]; if (draftPos && saved[i]) { posMasterMapping.set(draftPos.id, [saved[i].id, draftPos.next_holderId]); } } } } // 2.5 Sync positions table for all affected posMasters (BATCH operation for performance) const positionSyncStats = await this.syncAllPositionsBatch( queryRunner, posMasterMapping, drafRevisionId, currentRevisionId, ); // Build comprehensive summary const summary = { message: "ย้ายโครงสร้างสำเร็จ", organization: { orgRoot: orgSyncStats.orgRoot, orgChild1: orgSyncStats.orgChild1, orgChild2: orgSyncStats.orgChild2, orgChild3: orgSyncStats.orgChild3, orgChild4: orgSyncStats.orgChild4, }, positionMaster: { deleted: toDelete.length, updated: toUpdate.length, inserted: toInsert.length, }, position: positionSyncStats, }; await queryRunner.commitTransaction(); return new HttpSuccess(summary); } catch (error) { console.error("Error moving draft to current:", error); await queryRunner.rollbackTransaction(); throw new HttpError(HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาดในการย้ายโครงสร้าง"); } finally { if (queryRunner.isTransactionActive) { await queryRunner.rollbackTransaction(); } await queryRunner.release(); } } /** * Helper function: Map draft ID to current ID using the mapping */ private resolveOrgId(draftId: string | null, mapping: OrgIdMapping): string | null { if (!draftId) return null; return mapping.byDraftId.get(draftId) ?? null; } /** * Helper function: Cascade delete positions before deleting org node */ private async cascadeDeletePositions( queryRunner: any, node: any, entityClass: any, ): Promise { const whereClause: any = { orgRevisionId: node.orgRevisionId, }; // Determine which FK field to use based on entity type if (entityClass === OrgRoot) { whereClause.orgRootId = node.id; } else if (entityClass === OrgChild1) { whereClause.orgChild1Id = node.id; } else if (entityClass === OrgChild2) { whereClause.orgChild2Id = node.id; } else if (entityClass === OrgChild3) { whereClause.orgChild3Id = node.id; } else if (entityClass === OrgChild4) { whereClause.orgChild4Id = node.id; } await queryRunner.manager.delete(PosMaster, whereClause); } /** * Helper function: Generic differential sync for each org level * Performs DELETE (nodes not in draft), UPDATE (existing nodes), INSERT (new nodes) */ private async syncOrgLevel( queryRunner: any, entityClass: any, repository: any, draftRevisionId: string, currentRevisionId: string, parentMappings?: AllOrgMappings, draftOrgRootId?: string, currentOrgRootId?: string, ): Promise<{ mapping: OrgIdMapping; counts: { deleted: number; updated: number; inserted: number }; }> { // 1. Fetch draft and current nodes under the given rootDnaId // Build WHERE condition for draft nodes const draftWhere: any = { orgRevisionId: draftRevisionId, }; if ( draftOrgRootId && (entityClass === OrgChild1 || entityClass === OrgChild2 || entityClass === OrgChild3 || entityClass === OrgChild4) ) { draftWhere.orgRootId = draftOrgRootId; } else if (entityClass === OrgRoot) { draftWhere.id = draftOrgRootId; } // Build WHERE condition for current nodes const currentWhere: any = { orgRevisionId: currentRevisionId, }; if ( currentOrgRootId && (entityClass === OrgChild1 || entityClass === OrgChild2 || entityClass === OrgChild3 || entityClass === OrgChild4) ) { currentWhere.orgRootId = currentOrgRootId; } else if (entityClass === OrgRoot) { currentWhere.id = currentOrgRootId; } const [draftNodes, currentNodes] = await Promise.all([ repository.find({ where: draftWhere }), repository.find({ where: currentWhere }), ]); // 2. Build lookup maps for efficient matching by ancestorDNA const draftByDNA = new Map(draftNodes.map((n: any) => [n.ancestorDNA, n])); const currentByDNA = new Map(currentNodes.map((n: any) => [n.ancestorDNA, n])); const mapping: OrgIdMapping = { byAncestorDNA: new Map(), byDraftId: new Map(), }; // 3. DELETE: Current nodes not in draft (cascade delete positions first) const toDelete = currentNodes.filter((curr: any) => !draftByDNA.has(curr.ancestorDNA)); for (const node of toDelete) { // Cascade delete positions first await this.cascadeDeletePositions(queryRunner, node, entityClass); await queryRunner.manager.delete(entityClass, node.id); } // 4. UPDATE: Nodes that exist in both draft and current (matched by ancestorDNA) const toUpdate = draftNodes.filter((draft: any) => currentByDNA.has(draft.ancestorDNA)); for (const draft of toUpdate) { const current: any = currentByDNA.get(draft.ancestorDNA)!; // Build update data with mapped parent IDs const updateData: any = { ...draft, id: current.id, orgRevisionId: currentRevisionId, }; // Map parent IDs based on entity level if (entityClass === OrgChild1 && draft.orgRootId && parentMappings) { updateData.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId) ?? draft.orgRootId; } else if (entityClass === OrgChild2) { if (draft.orgRootId && parentMappings) { updateData.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId) ?? draft.orgRootId; } if (draft.orgChild1Id && parentMappings) { updateData.orgChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id) ?? draft.orgChild1Id; } } else if (entityClass === OrgChild3) { if (draft.orgRootId && parentMappings) { updateData.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId) ?? draft.orgRootId; } if (draft.orgChild1Id && parentMappings) { updateData.orgChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id) ?? draft.orgChild1Id; } if (draft.orgChild2Id && parentMappings) { updateData.orgChild2Id = parentMappings.orgChild2.byDraftId.get(draft.orgChild2Id) ?? draft.orgChild2Id; } } else if (entityClass === OrgChild4) { if (draft.orgRootId && parentMappings) { updateData.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId) ?? draft.orgRootId; } if (draft.orgChild1Id && parentMappings) { updateData.orgChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id) ?? draft.orgChild1Id; } if (draft.orgChild2Id && parentMappings) { updateData.orgChild2Id = parentMappings.orgChild2.byDraftId.get(draft.orgChild2Id) ?? draft.orgChild2Id; } if (draft.orgChild3Id && parentMappings) { updateData.orgChild3Id = parentMappings.orgChild3.byDraftId.get(draft.orgChild3Id) ?? draft.orgChild3Id; } } await queryRunner.manager.update(entityClass, current.id, updateData); mapping.byAncestorDNA.set(draft.ancestorDNA, current.id); mapping.byDraftId.set(draft.id, current.id); } // 5. INSERT: Draft nodes not in current const toInsert = draftNodes.filter((draft: any) => !currentByDNA.has(draft.ancestorDNA)); for (const draft of toInsert) { const newNode: any = queryRunner.manager.create(entityClass, { ...draft, id: undefined, orgRevisionId: currentRevisionId, }); // Map parent IDs based on entity level if (entityClass === OrgChild1 && draft.orgRootId) { if (draft.orgRootId === draftOrgRootId) { newNode.orgRootId = currentOrgRootId; } else if (parentMappings) { newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); } } else if (entityClass === OrgChild2) { if (draft.orgRootId) { if (draft.orgRootId === draftOrgRootId) { newNode.orgRootId = currentOrgRootId; } else if (parentMappings) { newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); } } if (draft.orgChild1Id && parentMappings) { const mappedChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id); if (mappedChild1Id) { newNode.orgChild1Id = mappedChild1Id; } } } else if (entityClass === OrgChild3) { if (draft.orgRootId) { if (draft.orgRootId === draftOrgRootId) { newNode.orgRootId = currentOrgRootId; } else if (parentMappings) { newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); } } if (draft.orgChild1Id && parentMappings) { const mappedChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id); if (mappedChild1Id) { newNode.orgChild1Id = mappedChild1Id; } } if (draft.orgChild2Id && parentMappings) { const mappedChild2Id = parentMappings.orgChild2.byDraftId.get(draft.orgChild2Id); if (mappedChild2Id) { newNode.orgChild2Id = mappedChild2Id; } } } else if (entityClass === OrgChild4) { if (draft.orgRootId) { if (draft.orgRootId === draftOrgRootId) { newNode.orgRootId = currentOrgRootId; } else if (parentMappings) { newNode.orgRootId = parentMappings.orgRoot.byDraftId.get(draft.orgRootId); } } if (draft.orgChild1Id && parentMappings) { const mappedChild1Id = parentMappings.orgChild1.byDraftId.get(draft.orgChild1Id); if (mappedChild1Id) { newNode.orgChild1Id = mappedChild1Id; } } if (draft.orgChild2Id && parentMappings) { const mappedChild2Id = parentMappings.orgChild2.byDraftId.get(draft.orgChild2Id); if (mappedChild2Id) { newNode.orgChild2Id = mappedChild2Id; } } if (draft.orgChild3Id && parentMappings) { const mappedChild3Id = parentMappings.orgChild3.byDraftId.get(draft.orgChild3Id); if (mappedChild3Id) { newNode.orgChild3Id = mappedChild3Id; } } } const saved = await queryRunner.manager.save(newNode); mapping.byAncestorDNA.set(draft.ancestorDNA, saved.id); mapping.byDraftId.set(draft.id, saved.id); } return { mapping, counts: { deleted: toDelete.length, updated: toUpdate.length, inserted: toInsert.length, }, }; } /** * Batch version: Sync positions for ALL posMasters in a single operation * This significantly reduces database round trips for large organizations */ private async syncAllPositionsBatch( queryRunner: any, posMasterMapping: Map, _draftRevisionId: string, _currentRevisionId: string, ): Promise<{ deleted: number; updated: number; inserted: number }> { // Extract draft and current posMaster IDs const draftPosMasterIds = Array.from(posMasterMapping.keys()); const currentPosMasterIds = Array.from(posMasterMapping.values()).map( ([currentId]) => currentId, ); if (draftPosMasterIds.length === 0) { return { deleted: 0, updated: 0, inserted: 0 }; } // Fetch draft PosMasters with relations for history tracking const draftPosMasters = await queryRunner.manager.find(PosMaster, { where: { id: In(draftPosMasterIds) }, relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4", "next_holder"], }); // Fetch ALL positions for ALL posMasters in just 2 queries const [allDraftPositions, allCurrentPositions] = await Promise.all([ queryRunner.manager.find(Position, { where: { posMasterId: In(draftPosMasterIds), }, relations: ["posType", "posLevel", "posExecutive"], order: { orderNo: "ASC" }, }), queryRunner.manager.find(Position, { where: { posMasterId: In(currentPosMasterIds), }, }), ]); // Group positions by posMasterId for processing const draftPositionsByMaster = new Map(); for (const pos of allDraftPositions) { if (!draftPositionsByMaster.has(pos.posMasterId)) { draftPositionsByMaster.set(pos.posMasterId, []); } draftPositionsByMaster.get(pos.posMasterId)!.push(pos); } const currentPositionsByMaster = new Map(); for (const pos of allCurrentPositions) { if (!currentPositionsByMaster.has(pos.posMasterId)) { currentPositionsByMaster.set(pos.posMasterId, []); } currentPositionsByMaster.get(pos.posMasterId)!.push(pos); } // Collect all operations const allToDelete: string[] = []; const allToDeleteHistory: string[] = []; const allToUpdate: Array<{ id: string; data: any }> = []; const allToInsert: Array = []; const profileUpdates: Map = new Map(); // Create a map for quick lookup of draft PosMasters with relations const draftPosMasterMap = new Map(draftPosMasters.map((pm: PosMaster) => [pm.id, pm])); // Collect PosMasterHistory calls for selected positions const historyCalls: Array<{ ancestorDNA: string; profileId: string | null; historyData: SavePosMasterHistory; }> = []; // Process each posMaster mapping for (const [draftPosMasterId, [currentPosMasterId, nextHolderId]] of posMasterMapping) { const draftPositions = draftPositionsByMaster.get(draftPosMasterId) || []; const currentPositions = currentPositionsByMaster.get(currentPosMasterId) || []; // If no draft positions, mark all current positions for deletion if (draftPositions.length === 0) { allToDelete.push(...currentPositions.map((p: any) => p.id)); allToDeleteHistory.push(...currentPositions.map((p: any) => p.ancestorDNA)); continue; } // Build map for tracking const currentByOrderNo = new Map(currentPositions.map((p: any) => [p.orderNo, p])); const draftOrderNos = new Set(draftPositions.map((p: any) => p.orderNo)); // Mark for deletion: current positions not in draft (by orderNo) for (const currentPos of currentPositions) { if (!draftOrderNos.has(currentPos.orderNo)) { allToDelete.push(currentPos.id); allToDeleteHistory.push(currentPos.ancestorDNA); } } // Process UPDATE and INSERT for (const draftPos of draftPositions) { const current = currentByOrderNo.get(draftPos.orderNo); if (current) { // UPDATE existing position - collect for batch update allToUpdate.push({ id: current.id, data: { positionName: draftPos.positionName, positionField: draftPos.positionField, posTypeId: draftPos.posTypeId, posLevelId: draftPos.posLevelId, posExecutiveId: draftPos.posExecutiveId, positionExecutiveField: draftPos.positionExecutiveField, positionArea: draftPos.positionArea, isSpecial: draftPos.isSpecial, orderNo: draftPos.orderNo, positionIsSelected: draftPos.positionIsSelected, lastUpdateFullName: draftPos.lastUpdateFullName, lastUpdatedAt: new Date(), }, }); } else { // INSERT new position - collect for batch insert allToInsert.push({ ...draftPos, id: undefined, posMasterId: currentPosMasterId, }); } // Collect history data for the selected position const draftPosMaster = draftPosMasterMap.get(draftPosMasterId) as any; // Collect profile update for the selected position // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit if (nextHolderId != null && draftPos.positionIsSelected) { const _null: any = null; profileUpdates.set(nextHolderId, { posMasterNo: draftPosMaster ? (getPosMasterNo(draftPosMaster as PosMaster) ?? _null) : _null, org: draftPosMaster ? (getOrgFullName(draftPosMaster as PosMaster) ?? _null) : _null, }); } // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ if (nextHolderId != null && draftPos.positionIsSelected && !draftPosMaster?.isSit) { const existing = profileUpdates.get(nextHolderId) || {}; existing.position = draftPos.positionName; existing.posTypeId = draftPos.posTypeId; existing.posLevelId = draftPos.posLevelId; existing.positionField = draftPos.positionField ?? null; existing.posExecutive = (draftPos as any).posExecutive?.posExecutiveName ?? null; existing.positionArea = draftPos.positionArea ?? null; existing.positionExecutiveField = draftPos.positionExecutiveField ?? null; profileUpdates.set(nextHolderId, existing); if (draftPosMaster && draftPosMaster.ancestorDNA) { // Find the selected position from draft positions const selectedPos = draftPositions.find((p) => p.positionIsSelected === true) || draftPos; historyCalls.push({ ancestorDNA: draftPosMaster.ancestorDNA, profileId: nextHolderId, historyData: { prefix: draftPosMaster.next_holder?.prefix ?? null, firstName: draftPosMaster.next_holder?.firstName ?? null, lastName: draftPosMaster.next_holder?.lastName ?? null, position: selectedPos.positionName ?? null, posType: (selectedPos as any).posType?.posTypeName ?? null, posLevel: (selectedPos as any).posLevel?.posLevelName ?? null, posExecutive: (selectedPos as any).posExecutive?.posExecutiveName ?? null, profileId: nextHolderId, rootDnaId: draftPosMaster.orgRoot?.ancestorDNA ?? null, child1DnaId: draftPosMaster.orgChild1?.ancestorDNA ?? null, child2DnaId: draftPosMaster.orgChild2?.ancestorDNA ?? null, child3DnaId: draftPosMaster.orgChild3?.ancestorDNA ?? null, child4DnaId: draftPosMaster.orgChild4?.ancestorDNA ?? null, shortName: [ draftPosMaster.orgChild4?.orgChild4ShortName, draftPosMaster.orgChild3?.orgChild3ShortName, draftPosMaster.orgChild2?.orgChild2ShortName, draftPosMaster.orgChild1?.orgChild1ShortName, draftPosMaster.orgRoot?.orgRootShortName, ].find((s) => typeof s === "string" && s.trim().length > 0) ?? null, posMasterNoPrefix: draftPosMaster.posMasterNoPrefix ?? null, posMasterNo: draftPosMaster.posMasterNo ?? null, posMasterNoSuffix: draftPosMaster.posMasterNoSuffix ?? null, }, }); } } } } // Execute bulk operations let deletedCount = 0; let updatedCount = 0; let insertedCount = 0; // Bulk DELETE if (allToDelete.length > 0) { await queryRunner.manager.delete(Position, allToDelete); const deleteOps = allToDeleteHistory.map((ancestorDNA) => ({ posMasterDnaId: ancestorDNA, profileId: null, pm: null, })); await BatchSavePosMasterHistoryOfficer(queryRunner, deleteOps); deletedCount = allToDelete.length; } // Bulk UPDATE (batch by 100 to avoid query size limits) if (allToUpdate.length > 0) { const batchSize = 100; for (let i = 0; i < allToUpdate.length; i += batchSize) { const batch = allToUpdate.slice(i, i + batchSize); await Promise.all( batch.map(({ id, data }) => queryRunner.manager.update(Position, id, data)), ); } updatedCount = allToUpdate.length; } // Bulk INSERT if (allToInsert.length > 0) { const batchSize = 100; for (let i = 0; i < allToInsert.length; i += batchSize) { const batch = allToInsert.slice(i, i + batchSize); await queryRunner.manager.save(Position, batch); } insertedCount = allToInsert.length; } // Bulk UPDATE profiles if (profileUpdates.size > 0) { const profileUpdateEntries = Array.from(profileUpdates.entries()); await Promise.all( profileUpdateEntries.map(([profileId, data]) => queryRunner.manager.update(Profile, profileId, data), ), ); } // Save PosMasterHistory for updated positions if (historyCalls.length > 0) { await BatchSavePosMasterHistoryOfficer( queryRunner, historyCalls.map(({ ancestorDNA, profileId, historyData }) => ({ posMasterDnaId: ancestorDNA, profileId, pm: historyData, })), ); } return { deleted: deletedCount, updated: updatedCount, inserted: insertedCount }; } /** * Helper: Sum all counts from a map (for root level total) */ private sumAllCounts(map: Map): number { let sum = 0; for (const value of map.values()) { sum += value.totalCount; } return sum; } /** * Helper: Sum all vacant counts from a map */ private sumAllVacantCounts( map: Map, isDraft: boolean, ): number { let sum = 0; for (const value of map.values()) { sum += isDraft ? value.nextVacantCount : value.currentVacantCount; } return sum; } /** * Helper: Build orgRoots children array with pre-fetched counts */ private buildOrgRoots( orgRoots: OrgRoot[], positionCounts: PositionCountsByNode, isDraft: boolean, ) { if (!orgRoots) return []; return orgRoots .sort((a, b) => a.orgRootOrder - b.orgRootOrder) .map((orgRoot) => { const counts = getPositionCount(positionCounts.orgRootMap, orgRoot.id); return { departmentName: orgRoot.orgRootName, deptID: orgRoot.id, type: 1, totalPositionCount: counts.totalCount, totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount, children: this.buildOrgChild1s(orgRoot.orgChild1s, positionCounts, isDraft), }; }); } /** * Helper: Build orgChild1s children array with pre-fetched counts */ private buildOrgChild1s( orgChild1s: OrgChild1[], positionCounts: PositionCountsByNode, isDraft: boolean, ) { if (!orgChild1s) return []; return orgChild1s .sort((a, b) => a.orgChild1Order - b.orgChild1Order) .map((orgChild1) => { const counts = getPositionCount(positionCounts.orgChild1Map, orgChild1.id); return { departmentName: orgChild1.orgChild1Name, deptID: orgChild1.id, type: 2, totalPositionCount: counts.totalCount, totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount, children: this.buildOrgChild2s(orgChild1.orgChild2s, positionCounts, isDraft), }; }); } /** * Helper: Build orgChild2s children array with pre-fetched counts */ private buildOrgChild2s( orgChild2s: OrgChild2[], positionCounts: PositionCountsByNode, isDraft: boolean, ) { if (!orgChild2s) return []; return orgChild2s .sort((a, b) => a.orgChild2Order - b.orgChild2Order) .map((orgChild2) => { const counts = getPositionCount(positionCounts.orgChild2Map, orgChild2.id); return { departmentName: orgChild2.orgChild2Name, deptID: orgChild2.id, type: 3, totalPositionCount: counts.totalCount, totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount, children: this.buildOrgChild3s(orgChild2.orgChild3s, positionCounts, isDraft), }; }); } /** * Helper: Build orgChild3s children array with pre-fetched counts */ private buildOrgChild3s( orgChild3s: OrgChild3[], positionCounts: PositionCountsByNode, isDraft: boolean, ) { if (!orgChild3s) return []; return orgChild3s .sort((a, b) => a.orgChild3Order - b.orgChild3Order) .map((orgChild3) => { const counts = getPositionCount(positionCounts.orgChild3Map, orgChild3.id); return { departmentName: orgChild3.orgChild3Name, deptID: orgChild3.id, type: 4, totalPositionCount: counts.totalCount, totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount, children: this.buildOrgChild4s(orgChild3.orgChild4s, positionCounts, isDraft), }; }); } /** * Helper: Build orgChild4s children array with pre-fetched counts (leaf nodes) */ private buildOrgChild4s( orgChild4s: OrgChild4[], positionCounts: PositionCountsByNode, isDraft: boolean, ) { if (!orgChild4s) return []; return orgChild4s .sort((a, b) => a.orgChild4Order - b.orgChild4Order) .map((orgChild4) => { const counts = getPositionCount(positionCounts.orgChild4Map, orgChild4.id); return { departmentName: orgChild4.orgChild4Name, deptID: orgChild4.id, type: 5, totalPositionCount: counts.totalCount, totalPositionVacant: isDraft ? counts.nextVacantCount : counts.currentVacantCount, }; }); } }