From a80fe85032c375e8be4848d68e9567f0fbd29724 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Thu, 19 Feb 2026 15:41:49 +0700 Subject: [PATCH] fix: bug query --- src/controllers/OrganizationController.ts | 2308 ++++++++++++--------- src/services/OrganizationService.ts | 131 +- 2 files changed, 1461 insertions(+), 978 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index d1b47518..efb0f679 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -51,7 +51,7 @@ import { getRoles, addUserRoles, } from "../keycloak"; -// import { getPositionCounts, getCounts, getRootCounts } from "../services/OrganizationService"; +import { getPositionCountsAggregated, getPositionCount, PositionCountsByNode } from "../services/OrganizationService"; import { BatchSavePosMasterHistoryOfficer, CreatePosMasterHistoryEmployee, @@ -3465,14 +3465,1174 @@ export class OrganizationController extends Controller { 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 + * API Organizational StructChart V2 (Optimized) * - * @summary Organizational StructChart + * @summary Organizational StructChart - Optimized with batch queries to prevent N+1 problem * */ @Get("struct-chart/{idNode}/{type}") - async structchart(@Path() idNode: string, type: number) { + 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({ @@ -3493,320 +4653,9 @@ export class OrganizationController extends Controller { 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() || "", - // }, - // }), - })), - ), - })), - ), - })), - ), - })), - ), - }; - }), - ), + totalPositionCount: this.sumAllCounts(positionCounts.orgRootMap), + totalPositionVacant: this.sumAllVacantCounts(positionCounts.orgRootMap, isDraft), + children: this.buildOrgRoots(data.orgRoots, positionCounts, isDraft), }; return new HttpSuccess([formattedData]); } @@ -3825,455 +4674,34 @@ export class OrganizationController extends Controller { 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, - // 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() || "", - // }, - // }), - })), - ), - })), - ), - })), - ), - })), - ), + 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", - ], + 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, - // 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() || "", - // }, - // }), - })), - ), - })), - ), - })), - ), + totalPositionCount: child1Counts.totalCount, + totalPositionVacant: isDraft ? child1Counts.nextVacantCount : child1Counts.currentVacantCount, + children: this.buildOrgChild2s(data.orgChild2s, positionCounts, isDraft), }; return new HttpSuccess([formattedData]); } @@ -4286,135 +4714,14 @@ export class OrganizationController extends Controller { 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, - // 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() || "", - // }, - // }), - })), - ), - })), - ), + totalPositionCount: child2Counts.totalCount, + totalPositionVacant: isDraft ? child2Counts.nextVacantCount : child2Counts.currentVacantCount, + children: this.buildOrgChild3s(data.orgChild3s, positionCounts, isDraft), }; return new HttpSuccess([formattedData]); } @@ -4427,84 +4734,14 @@ export class OrganizationController extends Controller { 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, - // 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() || "", - // }, - // }), - })), - ), + totalPositionCount: child3Counts.totalCount, + totalPositionVacant: isDraft ? child3Counts.nextVacantCount : child3Counts.currentVacantCount, + children: this.buildOrgChild4s(data.orgChild4s, positionCounts, isDraft), }; return new HttpSuccess([formattedData]); } @@ -4517,40 +4754,13 @@ export class OrganizationController extends Controller { 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, - // 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() || "", - // }, - // }), + totalPositionCount: child4Counts.totalCount, + totalPositionVacant: isDraft ? child4Counts.nextVacantCount : child4Counts.currentVacantCount, }; return new HttpSuccess([formattedData]); } @@ -8819,4 +9029,148 @@ export class OrganizationController extends Controller { 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, + }; + }); + } } diff --git a/src/services/OrganizationService.ts b/src/services/OrganizationService.ts index a9b2b796..85cc2220 100644 --- a/src/services/OrganizationService.ts +++ b/src/services/OrganizationService.ts @@ -1,7 +1,121 @@ import { AppDataSource } from "../database/data-source"; import { PosMaster } from "../entities/PosMaster"; -// Helper function to get aggregated position counts +// Type definitions for position counts +export interface PositionCountData { + totalCount: number; + currentVacantCount: number; + nextVacantCount: number; +} + +export interface PositionCountsByNode { + orgRootMap: Map; + orgChild1Map: Map; + orgChild2Map: Map; + orgChild3Map: Map; + orgChild4Map: Map; +} + +// Optimized function using SQL aggregation with GROUP BY +// This is much more efficient than loading all records into memory +export async function getPositionCountsAggregated(orgRevisionId: string): Promise { + const posRepo = AppDataSource.getRepository(PosMaster); + + // Helper to build map from query results + const buildMap = (results: any[]) => { + const map = new Map(); + for (const row of results) { + if (row.nodeId) { + map.set(row.nodeId, { + totalCount: parseInt(row.totalCount) || 0, + currentVacantCount: parseInt(row.currentVacantCount) || 0, + nextVacantCount: parseInt(row.nextVacantCount) || 0, + }); + } + } + return map; + }; + + // Execute all aggregation queries in parallel + const [ + orgRootResults, + orgChild1Results, + orgChild2Results, + orgChild3Results, + orgChild4Results, + ] = await Promise.all([ + // Level 0: orgRoot + posRepo + .createQueryBuilder("pos") + .select("pos.orgRootId", "nodeId") + .addSelect("COUNT(*)", "totalCount") + .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount") + .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount") + .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId }) + .andWhere("pos.orgRootId IS NOT NULL") + .groupBy("pos.orgRootId") + .getRawMany(), + + // Level 1: orgChild1 + posRepo + .createQueryBuilder("pos") + .select("pos.orgChild1Id", "nodeId") + .addSelect("COUNT(*)", "totalCount") + .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount") + .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount") + .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId }) + .andWhere("pos.orgChild1Id IS NOT NULL") + .groupBy("pos.orgChild1Id") + .getRawMany(), + + // Level 2: orgChild2 + posRepo + .createQueryBuilder("pos") + .select("pos.orgChild2Id", "nodeId") + .addSelect("COUNT(*)", "totalCount") + .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount") + .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount") + .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId }) + .andWhere("pos.orgChild2Id IS NOT NULL") + .groupBy("pos.orgChild2Id") + .getRawMany(), + + // Level 3: orgChild3 + posRepo + .createQueryBuilder("pos") + .select("pos.orgChild3Id", "nodeId") + .addSelect("COUNT(*)", "totalCount") + .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount") + .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount") + .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId }) + .andWhere("pos.orgChild3Id IS NOT NULL") + .groupBy("pos.orgChild3Id") + .getRawMany(), + + // Level 4: orgChild4 + posRepo + .createQueryBuilder("pos") + .select("pos.orgChild4Id", "nodeId") + .addSelect("COUNT(*)", "totalCount") + .addSelect("SUM(CASE WHEN pos.current_holderId IS NULL OR pos.current_holderId = '' THEN 1 ELSE 0 END)", "currentVacantCount") + .addSelect("SUM(CASE WHEN pos.next_holderId IS NULL OR pos.next_holderId = '' THEN 1 ELSE 0 END)", "nextVacantCount") + .where("pos.orgRevisionId = :orgRevisionId", { orgRevisionId }) + .andWhere("pos.orgChild4Id IS NOT NULL") + .groupBy("pos.orgChild4Id") + .getRawMany(), + ]); + + return { + orgRootMap: buildMap(orgRootResults), + orgChild1Map: buildMap(orgChild1Results), + orgChild2Map: buildMap(orgChild2Results), + orgChild3Map: buildMap(orgChild3Results), + orgChild4Map: buildMap(orgChild4Results), + }; +} + +// Legacy function - kept for backward compatibility +// DEPRECATED: Use getPositionCountsAggregated instead export async function getPositionCounts(orgRevisionId: string) { // Query all posMaster data for this revision with aggregation const rawData = await AppDataSource.getRepository(PosMaster) @@ -276,3 +390,18 @@ export function getRootCounts(map: Map, key: string) { } ); } + +// Helper function to get position counts from aggregated maps with defaults +export function getPositionCount( + map: Map, + key: string | null | undefined +): PositionCountData { + if (!key) return { totalCount: 0, currentVacantCount: 0, nextVacantCount: 0 }; + return ( + map.get(key) || { + totalCount: 0, + currentVacantCount: 0, + nextVacantCount: 0, + } + ); +}