diff --git a/docs/migrations/fix_GetProfileEmployeeSalaryLevel_calendar_arithmetic.sql b/docs/migrations/fix_GetProfileEmployeeSalaryLevel_calendar_arithmetic.sql deleted file mode 100644 index bca30538..00000000 --- a/docs/migrations/fix_GetProfileEmployeeSalaryLevel_calendar_arithmetic.sql +++ /dev/null @@ -1,140 +0,0 @@ --- ==================================================================== --- Fix GetProfileEmployeeSalaryLevel to use calendar arithmetic --- This changes from fixed formulas to actual calendar arithmetic, --- matching calculateGovAge and GetProfileSalaryLevel behavior --- ==================================================================== - -DELIMITER $$ - -DROP PROCEDURE IF EXISTS `GetProfileEmployeeSalaryLevel`$$ - -CREATE DEFINER=`root`@`%` PROCEDURE `GetProfileEmployeeSalaryLevel`( - IN personId VARCHAR(36), - IN _date DATE -) -BEGIN -WITH ordered AS ( - SELECT * - FROM profileSalary - WHERE profileEmployeeId = personId - AND commandCode IN ('0','1','2','3','4','8','9','10','11','12','13','14','15','16','20') -), -work_session AS ( - SELECT *, - COALESCE( - SUM(CASE WHEN commandCode IN (12,15,16) THEN 1 ELSE 0 END) - OVER (ORDER BY commandDateAffect, commandDateSign - ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), - 0) AS sessionId - FROM ordered -), -session_end AS ( - SELECT sessionId, MAX(commandDateAffect) AS sessionEndDate - FROM work_session - GROUP BY sessionId -), -level_change AS ( - SELECT *, - CASE - WHEN LAG(positionCee) OVER (ORDER BY commandDateAffect, commandDateSign) <=> positionCee - AND LAG(positionType) OVER (ORDER BY commandDateAffect, commandDateSign) <=> positionType - AND LAG(positionLevel) OVER (ORDER BY commandDateAffect, commandDateSign) <=> positionLevel - AND LAG(sessionId) OVER (ORDER BY commandDateAffect, commandDateSign) = sessionId - THEN 0 - ELSE 1 - END AS isNewLevel - FROM work_session -), -level_group AS ( - SELECT *, - SUM(isNewLevel) OVER (ORDER BY commandDateAffect, commandDateSign) AS levelGroup - FROM level_change -), -first_rows AS ( - SELECT * FROM ( - SELECT *, - ROW_NUMBER() OVER (PARTITION BY levelGroup ORDER BY commandDateAffect, commandDateSign) AS rnLevel - FROM level_group - ) t WHERE rnLevel = 1 -), -rows_with_duration AS ( - SELECT - fr.*, - CASE - WHEN LEAD(fr.commandDateAffect) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) IS NULL - THEN NULL - WHEN LEAD(fr.sessionId) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) <> fr.sessionId - THEN TIMESTAMPDIFF(DAY, fr.commandDateAffect, se.sessionEndDate) + 1 - ELSE - TIMESTAMPDIFF(DAY, fr.commandDateAffect, - LEAD(fr.commandDateAffect) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign)) - END AS duration_days - FROM first_rows fr - LEFT JOIN session_end se ON se.sessionId = fr.sessionId -), -resultWithDiff AS ( - SELECT - *, - LAG(duration_days) OVER (ORDER BY commandDateAffect, commandDateSign) AS days_diff - FROM rows_with_duration -) -SELECT - r.commandDateAffect, - r.positionType, - r.positionLevel, - r.positionCee, - r.days_diff, - CASE - WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN - TIMESTAMPDIFF(YEAR, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) - ELSE 0 - END AS Years, - CASE - WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN - TIMESTAMPDIFF(MONTH, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) % 12 - ELSE 0 - END AS Months, - CASE - WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN - DATEDIFF(r.commandDateAffect, - DATE_ADD( - DATE_ADD(LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), - INTERVAL TIMESTAMPDIFF(YEAR, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) % 12 MONTH) - ) - ELSE 0 - END AS Days, - r.posNo, - r.positionExecutive, - r.orgRoot, - r.orgChild1, - r.orgChild2, - r.orgChild3, - r.orgChild4, - r.commandCode, - r.commandName, - r.commandNo, - r.commandYear, - r.remark -FROM resultWithDiff r - -UNION ALL - -SELECT - _date, NULL, NULL, NULL, - TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1, - TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date), - TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12, - DATEDIFF(_date, - DATE_ADD( - DATE_ADD(MAX(commandDateAffect), - INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH) - ), - NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, - NULL,NULL,NULL,NULL -FROM resultWithDiff; - -END$$ - -DELIMITER ; diff --git a/docs/migrations/fix_GetProfileEmployeeSalaryPosition_calendar_arithmetic.sql b/docs/migrations/fix_GetProfileEmployeeSalaryPosition_calendar_arithmetic.sql deleted file mode 100644 index fa53b467..00000000 --- a/docs/migrations/fix_GetProfileEmployeeSalaryPosition_calendar_arithmetic.sql +++ /dev/null @@ -1,137 +0,0 @@ --- ==================================================================== --- Fix GetProfileEmployeeSalaryPosition to use calendar arithmetic --- This changes from fixed formulas to actual calendar arithmetic, --- matching calculateGovAge and GetProfileSalaryPosition behavior --- ==================================================================== - -DELIMITER $$ - -DROP PROCEDURE IF EXISTS `GetProfileEmployeeSalaryPosition`$$ - -CREATE DEFINER=`root`@`%` PROCEDURE `GetProfileEmployeeSalaryPosition`( - IN personId VARCHAR(36), - IN _date DATE -) -BEGIN -WITH ordered AS ( - SELECT * FROM profileSalary WHERE profileEmployeeId = personId AND commandCode IN ('0','1','2','3','4','8','9','10','11','12','13','14','15','16','20') -), -work_session AS ( - SELECT *, - COALESCE( - SUM(CASE WHEN commandCode IN (12,15,16) THEN 1 ELSE 0 END) - OVER (ORDER BY commandDateAffect, commandDateSign - ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), - 0) AS sessionId - FROM ordered -), -session_end AS ( - SELECT sessionId, MAX(commandDateAffect) AS sessionEndDate - FROM work_session - GROUP BY sessionId -), -position_change AS ( - SELECT *, - CASE - WHEN LAG(positionName) OVER (ORDER BY commandDateAffect, commandDateSign) = positionName - AND LAG(sessionId) OVER (ORDER BY commandDateAffect, commandDateSign) = sessionId - THEN 0 - ELSE 1 - END AS isNewPosition - FROM work_session -), -position_group AS ( - SELECT *, - SUM(isNewPosition) OVER (ORDER BY commandDateAffect, commandDateSign) AS posGroup - FROM position_change -), -first_rows AS ( - SELECT * FROM ( - SELECT *, - ROW_NUMBER() OVER (PARTITION BY posGroup ORDER BY commandDateAffect, commandDateSign) AS rnPos - FROM position_group - ) t WHERE rnPos = 1 -), -rows_with_duration AS ( - SELECT - fr.*, - LEAD(fr.sessionId) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) AS nextSessionId, - CASE - WHEN LEAD(fr.commandDateAffect) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) IS NULL - THEN NULL - WHEN LEAD(fr.sessionId) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign) <> fr.sessionId - THEN TIMESTAMPDIFF(DAY, fr.commandDateAffect, se.sessionEndDate) + 1 - ELSE - TIMESTAMPDIFF(DAY, fr.commandDateAffect, - LEAD(fr.commandDateAffect) OVER (ORDER BY fr.commandDateAffect, fr.commandDateSign)) - END AS duration_days - FROM first_rows fr - LEFT JOIN session_end se ON se.sessionId = fr.sessionId -), -resultWithDiff AS ( - SELECT - *, - LAG(duration_days) OVER (ORDER BY commandDateAffect, commandDateSign) AS days_diff - FROM rows_with_duration -) -SELECT - r.commandDateAffect, - r.positionName, - r.positionCee, - r.days_diff, - CASE - WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN - TIMESTAMPDIFF(YEAR, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) - ELSE 0 - END AS Years, - CASE - WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN - TIMESTAMPDIFF(MONTH, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) % 12 - ELSE 0 - END AS Months, - CASE - WHEN LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign) IS NOT NULL THEN - TIMESTAMPDIFF(DAY, - DATE_ADD( - DATE_ADD(LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), - INTERVAL TIMESTAMPDIFF(YEAR, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, LAG(commandDateAffect) OVER (ORDER BY commandDateAffect, commandDateSign), r.commandDateAffect) % 12 MONTH), - r.commandDateAffect) - ELSE 0 - END AS Days, - r.posNo, - r.positionExecutive, - r.positionType, - r.positionLevel, - r.orgRoot, - r.orgChild1, - r.orgChild2, - r.orgChild3, - r.orgChild4, - r.commandCode, - r.commandName, - r.commandNo, - r.commandYear, - r.remark -FROM resultWithDiff r - -UNION ALL - -SELECT - _date, NULL, NULL, - TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1, - TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date), - TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12, - DATEDIFF(_date, - DATE_ADD( - DATE_ADD(MAX(commandDateAffect), - INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH) - ), - NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, - NULL,NULL,NULL,NULL,NULL -FROM resultWithDiff; - -END$$ - -DELIMITER ; diff --git a/docs/migrations/fix_GetProfileSalaryExecutive_calendar_arithmetic.sql b/docs/migrations/fix_GetProfileSalaryExecutive_calendar_arithmetic.sql index 9b1d5d50..e3585c9f 100644 --- a/docs/migrations/fix_GetProfileSalaryExecutive_calendar_arithmetic.sql +++ b/docs/migrations/fix_GetProfileSalaryExecutive_calendar_arithmetic.sql @@ -14,7 +14,7 @@ CREATE DEFINER=`root`@`%` PROCEDURE `GetProfileSalaryExecutive`( ) BEGIN WITH ordered AS ( - SELECT * FROM profileSalary WHERE profileId = personId AND commandCode IN ('0','1','2','3','4','8','9','10','11','12','13','14','15','16','20') AND positionExecutive <> '' + SELECT * FROM profileSalary WHERE profileId = personId AND commandCode IN ('0','1','2','3','4','8','9','10','11','12','13','14','15','16','20') ), work_session AS ( SELECT *, @@ -90,12 +90,12 @@ SELECT END AS Months, CASE WHEN LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign) IS NOT NULL THEN - DATEDIFF(r.commandDateAffect, + TIMESTAMPDIFF(DAY, DATE_ADD( DATE_ADD(LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), INTERVAL TIMESTAMPDIFF(YEAR, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) % 12 MONTH) - ) + INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) MONTH), + r.commandDateAffect) + 1 ELSE 0 END AS Days, r.posNo, @@ -121,12 +121,12 @@ SELECT TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1, TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date), TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12, - DATEDIFF(_date, + TIMESTAMPDIFF(DAY, DATE_ADD( DATE_ADD(MAX(commandDateAffect), INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH) - ), + INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) MONTH), + _date) + 1, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL FROM resultWithDiff; diff --git a/docs/migrations/fix_GetProfileSalaryLevel_calendar_arithmetic.sql b/docs/migrations/fix_GetProfileSalaryLevel_calendar_arithmetic.sql index 0ce8bbb5..ca811a23 100644 --- a/docs/migrations/fix_GetProfileSalaryLevel_calendar_arithmetic.sql +++ b/docs/migrations/fix_GetProfileSalaryLevel_calendar_arithmetic.sql @@ -94,12 +94,12 @@ SELECT END AS Months, CASE WHEN LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign) IS NOT NULL THEN - DATEDIFF(r.commandDateAffect, + TIMESTAMPDIFF(DAY, DATE_ADD( DATE_ADD(LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), INTERVAL TIMESTAMPDIFF(YEAR, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) % 12 MONTH) - ) + INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) MONTH), + r.commandDateAffect) + 1 ELSE 0 END AS Days, r.posNo, @@ -123,12 +123,12 @@ SELECT TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1, TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date), TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12, - DATEDIFF(_date, + TIMESTAMPDIFF(DAY, DATE_ADD( DATE_ADD(MAX(commandDateAffect), INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH) - ), + INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) MONTH), + _date) + 1, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL FROM resultWithDiff; diff --git a/docs/migrations/fix_GetProfileSalaryPosition_calendar_arithmetic.sql b/docs/migrations/fix_GetProfileSalaryPosition_calendar_arithmetic.sql index aed2e9e7..a546ad97 100644 --- a/docs/migrations/fix_GetProfileSalaryPosition_calendar_arithmetic.sql +++ b/docs/migrations/fix_GetProfileSalaryPosition_calendar_arithmetic.sql @@ -96,8 +96,8 @@ SELECT DATE_ADD( DATE_ADD(LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), INTERVAL TIMESTAMPDIFF(YEAR, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) % 12 MONTH), - r.commandDateAffect) + INTERVAL TIMESTAMPDIFF(MONTH, LAG(r.commandDateAffect) OVER (ORDER BY r.commandDateAffect, r.commandDateSign), r.commandDateAffect) MONTH), + r.commandDateAffect) + 1 ELSE 0 END AS Days, r.posNo, @@ -124,12 +124,12 @@ SELECT TIMESTAMPDIFF(DAY, MAX(commandDateAffect), _date) + 1, TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date), TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12, - DATEDIFF(_date, + TIMESTAMPDIFF(DAY, DATE_ADD( DATE_ADD(MAX(commandDateAffect), INTERVAL TIMESTAMPDIFF(YEAR, MAX(commandDateAffect), _date) YEAR), - INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) % 12 MONTH) - ), + INTERVAL TIMESTAMPDIFF(MONTH, MAX(commandDateAffect), _date) MONTH), + _date) + 1, NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL FROM resultWithDiff; diff --git a/src/controllers/ApiKeyController.ts b/src/controllers/ApiKeyController.ts index 7de6e415..4c9664d7 100644 --- a/src/controllers/ApiKeyController.ts +++ b/src/controllers/ApiKeyController.ts @@ -20,12 +20,6 @@ import { In } from "typeorm"; import { RequestWithUser } from "../middlewares/user"; import { ApiName } from "../entities/ApiName"; import { ApiHistory } from "../entities/ApiHistory"; -import { OrgRoot } from "../entities/OrgRoot"; -import { OrgChild1 } from "../entities/OrgChild1"; -import { OrgChild2 } from "../entities/OrgChild2"; -import { OrgChild3 } from "../entities/OrgChild3"; -import { OrgChild4 } from "../entities/OrgChild4"; -import { OrgRevision } from "../entities/OrgRevision"; const jwt = require("jsonwebtoken"); @Route("api/v1/org/apiKey") @@ -39,12 +33,6 @@ export class ApiKeyController extends Controller { private apiKeyRepository = AppDataSource.getRepository(ApiKey); private apiNameRepository = AppDataSource.getRepository(ApiName); private apiHistoryRepository = AppDataSource.getRepository(ApiHistory); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - private orgChild1Repository = AppDataSource.getRepository(OrgChild1); - private orgChild2Repository = AppDataSource.getRepository(OrgChild2); - private orgChild3Repository = AppDataSource.getRepository(OrgChild3); - private orgChild4Repository = AppDataSource.getRepository(OrgChild4); - private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); /** * API ตรวจสอบและถอดรหัส JWT token @@ -163,9 +151,6 @@ export class ApiKeyController extends Controller { relations: ["apiNames", "apiHistorys"], order: { createdAt: "DESC", apiNames: { createdAt: "DESC" } }, }); - - const orgNames = await this.buildOrgNameBatch(apiKey); - const data = apiKey.map((_data) => ({ id: _data.id, createdAt: _data.createdAt, @@ -178,7 +163,6 @@ export class ApiKeyController extends Controller { dnaChild2Id: _data.dnaChild2Id, dnaChild3Id: _data.dnaChild3Id, dnaChild4Id: _data.dnaChild4Id, - orgName: orgNames.get(_data.id), apiNames: _data.apiNames.map((x) => ({ id: x.id, name: x.name, @@ -190,139 +174,10 @@ export class ApiKeyController extends Controller { return new HttpSuccess(data); } - private async buildOrgNameBatch(apiKeys: ApiKey[]): Promise> { - const currentRevision = await this.orgRevisionRepository.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - - if (!currentRevision) { - return new Map(apiKeys.map((k) => [k.id, null])); - } - - const currentRevisionId = currentRevision.id; - - const rootIds = [...new Set(apiKeys.map((k) => k.dnaRootId).filter(Boolean))]; - const child1Ids = [...new Set(apiKeys.map((k) => k.dnaChild1Id).filter(Boolean))]; - const child2Ids = [...new Set(apiKeys.map((k) => k.dnaChild2Id).filter(Boolean))]; - const child3Ids = [...new Set(apiKeys.map((k) => k.dnaChild3Id).filter(Boolean))]; - const child4Ids = [...new Set(apiKeys.map((k) => k.dnaChild4Id).filter(Boolean))]; - - const [roots, child1s, child2s, child3s, child4s] = await Promise.all([ - rootIds.length > 0 - ? this.orgRootRepository.find({ - where: [ - { id: In(rootIds), orgRevisionId: currentRevisionId }, - { ancestorDNA: In(rootIds), orgRevisionId: currentRevisionId }, - ], - select: ["id", "ancestorDNA", "orgRootName"], - }) - : [], - child1Ids.length > 0 - ? this.orgChild1Repository.find({ - where: [ - { id: In(child1Ids), orgRevisionId: currentRevisionId }, - { ancestorDNA: In(child1Ids), orgRevisionId: currentRevisionId }, - ], - select: ["id", "ancestorDNA", "orgChild1Name"], - }) - : [], - child2Ids.length > 0 - ? this.orgChild2Repository.find({ - where: [ - { id: In(child2Ids), orgRevisionId: currentRevisionId }, - { ancestorDNA: In(child2Ids), orgRevisionId: currentRevisionId }, - ], - select: ["id", "ancestorDNA", "orgChild2Name"], - }) - : [], - child3Ids.length > 0 - ? this.orgChild3Repository.find({ - where: [ - { id: In(child3Ids), orgRevisionId: currentRevisionId }, - { ancestorDNA: In(child3Ids), orgRevisionId: currentRevisionId }, - ], - select: ["id", "ancestorDNA", "orgChild3Name"], - }) - : [], - child4Ids.length > 0 - ? this.orgChild4Repository.find({ - where: [ - { id: In(child4Ids), orgRevisionId: currentRevisionId }, - { ancestorDNA: In(child4Ids), orgRevisionId: currentRevisionId }, - ], - select: ["id", "ancestorDNA", "orgChild4Name"], - }) - : [], - ]); - - const rootMap = new Map( - roots.map((r) => [r.id, { name: r.orgRootName, ancestorDNA: r.ancestorDNA }]), - ); - const child1Map = new Map( - child1s.map((c) => [c.id, { name: c.orgChild1Name, ancestorDNA: c.ancestorDNA }]), - ); - const child2Map = new Map( - child2s.map((c) => [c.id, { name: c.orgChild2Name, ancestorDNA: c.ancestorDNA }]), - ); - const child3Map = new Map( - child3s.map((c) => [c.id, { name: c.orgChild3Name, ancestorDNA: c.ancestorDNA }]), - ); - const child4Map = new Map( - child4s.map((c) => [c.id, { name: c.orgChild4Name, ancestorDNA: c.ancestorDNA }]), - ); - - const result = new Map(); - for (const apiKey of apiKeys) { - if (apiKey.accessType === "ALL") { - result.set(apiKey.id, null); - continue; - } - - const parts: string[] = []; - - const getOrgName = ( - dnaId: string, - orgMap: Map, - ): string | null => { - const byId = orgMap.get(dnaId); - if (byId) return byId.name; - for (const [, value] of orgMap) { - if (value.ancestorDNA === dnaId) return value.name; - } - return null; - }; - - if (apiKey.dnaChild4Id) { - const name = getOrgName(apiKey.dnaChild4Id, child4Map); - if (name) parts.push(name); - } - if (apiKey.dnaChild3Id) { - const name = getOrgName(apiKey.dnaChild3Id, child3Map); - if (name) parts.push(name); - } - if (apiKey.dnaChild2Id) { - const name = getOrgName(apiKey.dnaChild2Id, child2Map); - if (name) parts.push(name); - } - if (apiKey.dnaChild1Id) { - const name = getOrgName(apiKey.dnaChild1Id, child1Map); - if (name) parts.push(name); - } - if (apiKey.dnaRootId) { - const name = getOrgName(apiKey.dnaRootId, rootMap); - if (name) parts.push(name); - } - - result.set(apiKey.id, parts.length > 0 ? parts.join(" ") : null); - } - - return result; - } - /** - * API รายการ Api Name + * API รายการ Api Key * - * @summary รายการ Api Name (ADMIN) + * @summary รายการ Api Key (ADMIN) * */ @Get("name") diff --git a/src/controllers/ApiManageController.ts b/src/controllers/ApiManageController.ts index 17f14050..01a27eb5 100644 --- a/src/controllers/ApiManageController.ts +++ b/src/controllers/ApiManageController.ts @@ -106,10 +106,10 @@ export class ApiManageController extends Controller { code: "organization", name: "ข้อมูลโครงสร้าง", }, - // { - // code: "position", - // name: "ข้อมูลอัตรากำลัง", - // }, + { + code: "position", + name: "ข้อมูลอัตรากำลัง", + }, ]; // รายการเอนทิตีทั้งหมด @@ -273,240 +273,59 @@ export class ApiManageController extends Controller { description: "ข้อมูลส่วนราชการ ระดับที่ 4", system: ["organization"], }, - // { - // name: "PosMaster", - // repository: this.posMasterRepository, - // description: "ข้อมูลอัตรากำลัง", - // isMain: true, - // system: ["position"], - // }, - // { - // name: "Position", - // repository: this.positionRepository, - // description: "ข้อมูลตำแหน่ง", - // system: ["position"], - // }, - // { - // name: "OrgRoot", - // repository: this.orgRootRepository, - // description: "ข้อมูลหน่วยงาน", - // system: ["position"], - // }, - // { - // name: "OrgChild1", - // repository: this.orgChild1Repository, - // description: "ข้อมูลส่วนราชการ ระดับที่ 1", - // system: ["position"], - // }, - // { - // name: "OrgChild2", - // repository: this.orgChild2Repository, - // description: "ข้อมูลส่วนราชการ ระดับที่ 2", - // system: ["position"], - // }, - // { - // name: "OrgChild3", - // repository: this.orgChild3Repository, - // description: "ข้อมูลส่วนราชการ ระดับที่ 3", - // system: ["position"], - // }, - // { - // name: "OrgChild4", - // repository: this.orgChild4Repository, - // description: "ข้อมูลส่วนราชการ ระดับที่ 4", - // system: ["position"], - // }, - // { - // name: "Profile", - // repository: this.profileRepository, - // description: "ข้อมูลคนครอง", - // system: ["position"], - // }, + { + name: "PosMaster", + repository: this.posMasterRepository, + description: "ข้อมูลอัตรากำลัง", + isMain: true, + system: ["position"], + }, + { + name: "Position", + repository: this.positionRepository, + description: "ข้อมูลตำแหน่ง", + system: ["position"], + }, + { + name: "OrgRoot", + repository: this.orgRootRepository, + description: "ข้อมูลหน่วยงาน", + system: ["position"], + }, + { + name: "OrgChild1", + repository: this.orgChild1Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 1", + system: ["position"], + }, + { + name: "OrgChild2", + repository: this.orgChild2Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 2", + system: ["position"], + }, + { + name: "OrgChild3", + repository: this.orgChild3Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 3", + system: ["position"], + }, + { + name: "OrgChild4", + repository: this.orgChild4Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 4", + system: ["position"], + }, + { + name: "Profile", + repository: this.profileRepository, + description: "ข้อมูลคนครอง", + system: ["position"], + }, ]; private readonly DEFAULT_PAGE_SIZE = 10; // ขนาดหน้าเริ่มต้น - private readonly EXCLUDED_COLUMNS = [ - "createdUserId", - "lastUpdateUserId", - "createdAt", - "createdFullName", - "lastUpdateFullName", - "avatarName", - "profileId", - "prefixId", - "profileEmployeeId", - "documentId", - "orgRevisionId", - "posMasterId", - "orgRootId", - "orgChild1Id", - "orgChild2Id", - "orgChild3Id", - "orgChild4Id", - "keycloak", - "commandId", - "prefixMain", - "authRoleId", - "next_holderId", - "current_holderId", - "ancestorDNA", - "leaveCommandId", - "posLevelId", - "posTypeId", - "posExecutiveId", - "registrationProvinceId", - "registrationDistrictId", - "registrationSubDistrictId", - "currentProvinceId", - "currentDistrictId", - "currentSubDistrictId", - "isDelete", - "keycloak", - "statusCheckEdit", - "privacyCheckin", - "privacyUser", - "privacyMgt", - "dutyTimeId", - "dutyTimeEffectiveDate", - "profileId", - "profileEmployeeId", - "orgRevisionId", - "rank", - "isUpload", - "isDeleted", - "isEntry", - "prefixId", - "leaveId", - "leaveTypeId", - "isDeputy", - "isCommission", - ]; // ฟิลด์ที่ไม่ต้องการแสดงในผลลัพธ์ - - // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ Profile entity - private readonly PROFILE_FIELD_REPLACEMENTS: Record< - string, - { propertyName: string; type: string; comment: string; joinTable: string; joinField: string } - > = { - posLevelId: { - propertyName: "posLevelName", - type: "string", - comment: "ระดับตำแหน่ง", - joinTable: "PosLevel", - joinField: "posLevelName", - }, - posTypeId: { - propertyName: "posTypeName", - type: "string", - comment: "ประเภทตำแหน่ง", - joinTable: "PosType", - joinField: "posTypeName", - }, - registrationProvinceId: { - propertyName: "registrationProvinceName", - type: "string", - comment: "จังหวัดตามทะเบียนบ้าน", - joinTable: "Province", - joinField: "name", - }, - registrationDistrictId: { - propertyName: "registrationDistrictName", - type: "string", - comment: "เขตตามทะเบียนบ้าน", - joinTable: "District", - joinField: "name", - }, - registrationSubDistrictId: { - propertyName: "registrationSubDistrictName", - type: "string", - comment: "แขวงตามทะเบียนบ้าน", - joinTable: "SubDistrict", - joinField: "name", - }, - currentProvinceId: { - propertyName: "currentProvinceName", - type: "string", - comment: "จังหวัดตามปัจจุบัน", - joinTable: "Province", - joinField: "name", - }, - currentDistrictId: { - propertyName: "currentDistrictName", - type: "string", - comment: "เขตตามปัจจุบัน", - joinTable: "District", - joinField: "name", - }, - currentSubDistrictId: { - propertyName: "currentSubDistrictName", - type: "string", - comment: "แขวงตามปัจจุบัน", - joinTable: "SubDistrict", - joinField: "name", - }, - }; - - // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ Position entity - private readonly POSITION_FIELD_REPLACEMENTS: Record< - string, - { propertyName: string; type: string; comment: string; joinTable: string; joinField: string } - > = { - posTypeId: { - propertyName: "posTypeName", - type: "string", - comment: "ประเภทตำแหน่ง", - joinTable: "PosType", - joinField: "posTypeName", - }, - posLevelId: { - propertyName: "posLevelName", - type: "string", - comment: "ระดับตำแหน่ง", - joinTable: "PosLevel", - joinField: "posLevelName", - }, - posExecutiveId: { - propertyName: "posExecutiveName", - type: "string", - comment: "ตำแหน่งทางการบริหาร", - joinTable: "PosExecutive", - joinField: "posExecutiveName", - }, - }; - - // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ ProfileEmployee entity - private readonly PROFILEEMPLOYEE_FIELD_REPLACEMENTS: Record< - string, - { propertyName: string; type: string; comment: string; joinTable: string; joinField: string } - > = { - posLevelId: { - propertyName: "posLevelName", - type: "string", - comment: "ระดับชั้นงาน", - joinTable: "EmployeePosLevel", - joinField: "posLevelName", - }, - posTypeId: { - propertyName: "posTypeName", - type: "string", - comment: "กลุ่มงาน", - joinTable: "EmployeePosType", - joinField: "posTypeName", - }, - }; - - // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ ProfileLeave entity - private readonly PROFILELEAVE_FIELD_REPLACEMENTS: Record< - string, - { propertyName: string; type: string; comment: string; joinTable: string; joinField: string } - > = { - leaveTypeId: { - propertyName: "leaveTypeName", - type: "string", - comment: "ประเภทการลา", - joinTable: "LeaveType", - joinField: "name", - }, - }; + private readonly EXCLUDED_COLUMNS = ["createdUserId", "lastUpdateUserId"]; // ฟิลด์ที่ไม่ต้องการแสดงในผลลัพธ์ private validateSuperAdminRole(user: any): void { if (!user.role.includes("SUPER_ADMIN")) { @@ -545,8 +364,11 @@ export class ApiManageController extends Controller { const result = this.entities .filter((entity) => entity.system.includes(system)) - .map(({ name, repository, description, isMain }) => { - let columns = repository.metadata.columns + .map(({ name, repository, description, isMain }) => ({ + tb: name, + description, + isMain: isMain || false, + propertys: repository.metadata.columns .filter( (column: any) => !column.isPrimary && !this.EXCLUDED_COLUMNS.includes(column.propertyName), @@ -556,114 +378,8 @@ export class ApiManageController extends Controller { type: typeof column.type === "string" ? column.type : "string", comment: column.comment, key: column.propertyName, - })); - - // Special handling for Profile entity - replace ID fields with name fields - if (name === "Profile") { - const replacementKeys = Object.keys(this.PROFILE_FIELD_REPLACEMENTS); - - // Remove ID fields that should be replaced - columns = columns.filter( - (col: { propertyName: string }) => !replacementKeys.includes(col.propertyName), - ); - - // Add the corresponding name fields - const nameFields = replacementKeys.map((key) => ({ - propertyName: this.PROFILE_FIELD_REPLACEMENTS[key].propertyName, - type: "string", - comment: this.PROFILE_FIELD_REPLACEMENTS[key].comment, - key: this.PROFILE_FIELD_REPLACEMENTS[key].propertyName, - })); - - columns = [...columns, ...nameFields]; - } - - // Special handling for Position entity - replace ID fields with name fields - if (name === "Position") { - const replacementKeys = Object.keys(this.POSITION_FIELD_REPLACEMENTS); - - // Remove ID fields that should be replaced - columns = columns.filter( - (col: { propertyName: string }) => !replacementKeys.includes(col.propertyName), - ); - - // Add the corresponding name fields - const nameFields = replacementKeys.map((key) => ({ - propertyName: this.POSITION_FIELD_REPLACEMENTS[key].propertyName, - type: "string", - comment: this.POSITION_FIELD_REPLACEMENTS[key].comment, - key: this.POSITION_FIELD_REPLACEMENTS[key].propertyName, - })); - - columns = [...columns, ...nameFields]; - } - - // Special handling for ProfileEmployee entity - replace ID fields with name fields - if (name === "ProfileEmployee") { - const replacementKeys = Object.keys(this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS); - - // Remove ID fields that should be replaced - columns = columns.filter( - (col: { propertyName: string }) => !replacementKeys.includes(col.propertyName), - ); - - // Add the corresponding name fields - const nameFields = replacementKeys.map((key) => ({ - propertyName: this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS[key].propertyName, - type: "string", - comment: this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS[key].comment, - key: this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS[key].propertyName, - })); - - columns = [...columns, ...nameFields]; - } - - // Special handling for ProfileLeave entity - replace ID fields with name fields - if (name === "ProfileLeave") { - const replacementKeys = Object.keys(this.PROFILELEAVE_FIELD_REPLACEMENTS); - - // Remove ID fields that should be replaced - columns = columns.filter( - (col: { propertyName: string }) => !replacementKeys.includes(col.propertyName), - ); - - // Add the corresponding name fields - const nameFields = replacementKeys.map((key) => ({ - propertyName: this.PROFILELEAVE_FIELD_REPLACEMENTS[key].propertyName, - type: "string", - comment: this.PROFILELEAVE_FIELD_REPLACEMENTS[key].comment, - key: this.PROFILELEAVE_FIELD_REPLACEMENTS[key].propertyName, - })); - - columns = [...columns, ...nameFields]; - } - - // Special handling for PosMaster entity - add Profile fields for holder information - if (name === "PosMaster") { - // Add Profile fields that are accessible via current_holder relation - const profileFields = ["prefix", "rank", "firstName", "lastName", "citizenId"]; - const profileRepository = AppDataSource.getRepository(Profile); - const profileColumns = profileRepository.metadata.columns - .filter( - (column: any) => !column.isPrimary && profileFields.includes(column.propertyName), - ) - .map((column: any) => ({ - propertyName: `Profile.${column.propertyName}`, - type: typeof column.type === "string" ? column.type : "string", - comment: column.comment, - key: `Profile.${column.propertyName}`, - })); - - columns = [...columns, ...profileColumns]; - } - - return { - tb: name, - description, - isMain: isMain || false, - propertys: columns, - }; - }); + })), + })); return new HttpSuccess(result); } catch (error) { diff --git a/src/controllers/ApiWebServiceController.ts b/src/controllers/ApiWebServiceController.ts index 52fcb3a3..61f3d54a 100644 --- a/src/controllers/ApiWebServiceController.ts +++ b/src/controllers/ApiWebServiceController.ts @@ -8,11 +8,6 @@ import { isPermissionRequest } from "../middlewares/authWebService"; import { RequestWithUserWebService } from "../middlewares/user"; import { OrgRevision } from "../entities/OrgRevision"; import { ApiHistory } from "../entities/ApiHistory"; -import { OrgRoot } from "../entities/OrgRoot"; -import { OrgChild1 } from "../entities/OrgChild1"; -import { OrgChild2 } from "../entities/OrgChild2"; -import { OrgChild3 } from "../entities/OrgChild3"; -import { OrgChild4 } from "../entities/OrgChild4"; import { SystemCode } from "./../interfaces/api-type"; @Route("api/v1/org/api-service") @Tags("ApiKey") @@ -25,206 +20,6 @@ export class ApiWebServiceController extends Controller { private apiNameRepository = AppDataSource.getRepository(ApiName); private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); private apiHistoryRepository = AppDataSource.getRepository(ApiHistory); - private currentRevisionId: string = ""; - - // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ Profile entity - private readonly PROFILE_FIELD_REPLACEMENTS: Record< - string, - { propertyName: string; joinRelation: string; joinField: string } - > = { - posLevelName: { - propertyName: "posLevelId", - joinRelation: "posLevel", - joinField: "posLevelName", - }, - posTypeName: { - propertyName: "posTypeId", - joinRelation: "posType", - joinField: "posTypeName", - }, - registrationProvinceName: { - propertyName: "registrationProvinceId", - joinRelation: "registrationProvince", - joinField: "name", - }, - registrationDistrictName: { - propertyName: "registrationDistrictId", - joinRelation: "registrationDistrict", - joinField: "name", - }, - registrationSubDistrictName: { - propertyName: "registrationSubDistrictId", - joinRelation: "registrationSubDistrict", - joinField: "name", - }, - currentProvinceName: { - propertyName: "currentProvinceId", - joinRelation: "currentProvince", - joinField: "name", - }, - currentDistrictName: { - propertyName: "currentDistrictId", - joinRelation: "currentDistrict", - joinField: "name", - }, - currentSubDistrictName: { - propertyName: "currentSubDistrictId", - joinRelation: "currentSubDistrict", - joinField: "name", - }, - }; - - // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ ProfileLeave entity - private readonly PROFILELEAVE_FIELD_REPLACEMENTS: Record< - string, - { propertyName: string; joinRelation: string; joinField: string } - > = { - leaveTypeName: { - propertyName: "leaveTypeId", - joinRelation: "leaveType", - joinField: "name", - }, - }; - - // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ Position entity - private readonly POSITION_FIELD_REPLACEMENTS: Record< - string, - { propertyName: string; joinRelation: string; joinField: string } - > = { - posTypeName: { - propertyName: "posTypeId", - joinRelation: "posType", - joinField: "posTypeName", - }, - posLevelName: { - propertyName: "posLevelId", - joinRelation: "posLevel", - joinField: "posLevelName", - }, - posExecutiveName: { - propertyName: "posExecutiveId", - joinRelation: "posExecutive", - joinField: "posExecutiveName", - }, - }; - - // การแทนที่ฟิลด์ ID ด้วยฟิลด์ Name สำหรับ ProfileEmployee entity - private readonly PROFILEEMPLOYEE_FIELD_REPLACEMENTS: Record< - string, - { propertyName: string; joinRelation: string; joinField: string } - > = { - posTypeName: { - propertyName: "posTypeId", - joinRelation: "posType", - joinField: "posTypeName", - }, - posLevelName: { - propertyName: "posLevelId", - joinRelation: "posLevel", - joinField: "posLevelName", - }, - }; - - /** - * build posMaster permission condition - * @summary สร้างเงื่อนไขการกรองข้อมูลตามสิทธิ์การเข้าถึง - */ - private buildPosMasterPermissionCondition( - accessType: string | undefined, - dnaIds: { - dnaRootId?: string | null; - dnaChild1Id?: string | null; - dnaChild2Id?: string | null; - dnaChild3Id?: string | null; - dnaChild4Id?: string | null; - }, - tableAlias: string = "posMaster", - ): string { - // ALL - no filtering - if (accessType === "ALL") { - return "1=1"; - } - - // No access type specified but has DNA IDs - default to NORMAL behavior - const conditions: string[] = []; - - if (accessType === "ROOT" && dnaIds.dnaRootId) { - // All organizations under this root - conditions.push( - `${tableAlias}.orgRootId IN (SELECT id FROM orgRoot WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaRootId}%")`, - ); - } else if (accessType === "CHILD" || accessType === "NORMAL") { - // Build conditions based on which DNA level is specified - if (dnaIds.dnaChild4Id) { - conditions.push( - `${tableAlias}.orgChild4Id IN (SELECT id FROM orgChild4 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild4Id}")`, - ); - } else if (dnaIds.dnaChild3Id) { - conditions.push( - accessType === "NORMAL" - ? `(${tableAlias}.orgChild3Id IN (SELECT id FROM orgChild3 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild3Id}") AND ${tableAlias}.orgChild4Id IS NULL)` - : `${tableAlias}.orgChild3Id IN (SELECT id FROM orgChild3 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild3Id}")`, - ); - // For CHILD type, include all descendants - if (accessType === "CHILD") { - conditions.push( - `(${tableAlias}.orgChild3Id IN (SELECT id FROM orgChild3 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild3Id}%") OR ${tableAlias}.orgChild4Id IN (SELECT id FROM orgChild4 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild3Id}%"))`, - ); - } - } else if (dnaIds.dnaChild2Id) { - conditions.push( - accessType === "NORMAL" - ? `(${tableAlias}.orgChild2Id IN (SELECT id FROM orgChild2 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild2Id}") AND ${tableAlias}.orgChild3Id IS NULL AND ${tableAlias}.orgChild4Id IS NULL)` - : `${tableAlias}.orgChild2Id IN (SELECT id FROM orgChild2 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild2Id}")`, - ); - if (accessType === "CHILD") { - conditions.push( - `(${tableAlias}.orgChild2Id IN (SELECT id FROM orgChild2 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild2Id}%") OR ${tableAlias}.orgChild3Id IN (SELECT id FROM orgChild3 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild2Id}%") OR ${tableAlias}.orgChild4Id IN (SELECT id FROM orgChild4 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild2Id}%"))`, - ); - } - } else if (dnaIds.dnaChild1Id) { - conditions.push( - accessType === "NORMAL" - ? `(${tableAlias}.orgChild1Id IN (SELECT id FROM orgChild1 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild1Id}") AND ${tableAlias}.orgChild2Id IS NULL AND ${tableAlias}.orgChild3Id IS NULL AND ${tableAlias}.orgChild4Id IS NULL)` - : `${tableAlias}.orgChild1Id IN (SELECT id FROM orgChild1 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaChild1Id}")`, - ); - if (accessType === "CHILD") { - conditions.push( - `(${tableAlias}.orgChild1Id IN (SELECT id FROM orgChild1 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild1Id}%") OR ${tableAlias}.orgChild2Id IN (SELECT id FROM orgChild2 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild1Id}%") OR ${tableAlias}.orgChild3Id IN (SELECT id FROM orgChild3 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild1Id}%") OR ${tableAlias}.orgChild4Id IN (SELECT id FROM orgChild4 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaChild1Id}%"))`, - ); - } - } else if (dnaIds.dnaRootId) { - conditions.push( - accessType === "NORMAL" - ? `(${tableAlias}.orgRootId IN (SELECT id FROM orgRoot WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaRootId}") AND ${tableAlias}.orgChild1Id IS NULL AND ${tableAlias}.orgChild2Id IS NULL AND ${tableAlias}.orgChild3Id IS NULL AND ${tableAlias}.orgChild4Id IS NULL)` - : `${tableAlias}.orgRootId IN (SELECT id FROM orgRoot WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA = "${dnaIds.dnaRootId}")`, - ); - if (accessType === "CHILD") { - conditions.push( - `(${tableAlias}.orgRootId IN (SELECT id FROM orgRoot WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaRootId}%") OR ${tableAlias}.orgChild1Id IN (SELECT id FROM orgChild1 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaRootId}%") OR ${tableAlias}.orgChild2Id IN (SELECT id FROM orgChild2 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaRootId}%") OR ${tableAlias}.orgChild3Id IN (SELECT id FROM orgChild3 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaRootId}%") OR ${tableAlias}.orgChild4Id IN (SELECT id FROM orgChild4 WHERE orgRevisionId = "${this.currentRevisionId}" AND ancestorDNA LIKE "${dnaIds.dnaRootId}%"))`, - ); - } - } - } - - return conditions.length > 0 ? `(${conditions.join(" OR ")})` : "1=1"; - } - - /** - * rename ancestorDNA to id - * @summary เปลี่ยนชื่อฟิลด์ ancestorDNA เป็น id - */ - private renameAncestorDnaToId(obj: any): any { - if (!obj || typeof obj !== "object") { - return obj; - } - const result = { ...obj }; - if (result.ancestorDNA !== undefined) { - result.id = result.ancestorDNA; - delete result.ancestorDNA; - } - return result; - } /** * list fields by systems @@ -255,42 +50,12 @@ export class ApiWebServiceController extends Controller { } await isPermissionRequest(request, apiName.id); const offset = (page - 1) * pageSize; - let propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`); - const selectedFieldsByTable: Record> = {}; - apiName.apiAttributes.forEach((attr) => { - if (!selectedFieldsByTable[attr.tbName]) { - selectedFieldsByTable[attr.tbName] = new Set(); - } - selectedFieldsByTable[attr.tbName].add(attr.propertyKey); - }); - - // สำหรับ Organization: ให้รวม ancestorDNA เสมอ เพื่อแสดงเป็น id - if (system === "organization") { - // สำหรับ OrgRoot - const ancestorDnaField = "OrgRoot.ancestorDNA"; - if (!propertyKey.includes(ancestorDnaField)) { - propertyKey.push(ancestorDnaField); - } - if (!selectedFieldsByTable["OrgRoot"]) { - selectedFieldsByTable["OrgRoot"] = new Set(); - } - selectedFieldsByTable["OrgRoot"].add("ancestorDNA"); - - // สำหรับ OrgChild1, OrgChild2, OrgChild3, OrgChild4 - const childTables = ["OrgChild1", "OrgChild2", "OrgChild3", "OrgChild4"]; - childTables.forEach((table) => { - if (!selectedFieldsByTable[table]) { - selectedFieldsByTable[table] = new Set(); - } - selectedFieldsByTable[table].add("ancestorDNA"); - }); - } + const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`); let tbMain: string = ""; let condition: string = "1=1"; if (system == "registry") { tbMain = "Profile"; - condition = `Profile.isActive = true AND Profile.isDelete = false`; } else if (system == "registry_emp") { tbMain = "ProfileEmployee"; condition = `ProfileEmployee.employeeClass = "PERM"`; @@ -313,152 +78,6 @@ export class ApiWebServiceController extends Controller { condition = `PosMaster.orgRevisionId = "${revision?.id}"`; } - let posMasterCondition: string = "1=1"; - let posMasterAlias: string = ""; - - // Add isDeleted filtering for entities that have this field - // Profile.ts uses isDelete (singular) instead of isDeleted - if (tbMain === "Profile") { - // Already handled above in the registry system condition - } else if ( - [ - "ProfileAbility", - "ProfileAbilityHistory", - "ProfileAbsentLate", - "ProfileActposition", - "ProfileActpositionHistory", - "ProfileAssistance", - "ProfileAssistanceHistory", - "ProfileAssessment", - "ProfileAssessmentHistory", - "ProfileCertificate", - "ProfileCertificateHistory", - "ProfileChangeName", - "ProfileChangeNameHistory", - "ProfileChildren", - "ProfileChildrenHistory", - "ProfileDiscipline", - "ProfileDisciplineHistory", - "ProfileDevelopment", - "ProfileDevelopmentHistory", - "ProfileDuty", - "ProfileDutyHistory", - "ProfileEducation", - "ProfileEducationHistory", - "ProfileHonor", - "ProfileHonorHistory", - "ProfileInsignia", - "ProfileInsigniaHistory", - "ProfileLeave", - "ProfileNopaid", - "ProfileNopaidHistory", - "ProfileOther", - "ProfileOtherHistory", - "ProfileSalary", - "ProfileSalaryHistory", - "ProfileSalaryTemp", - "ProfileTraining", - "ProfileTrainingHistory", - ].includes(tbMain) - ) { - condition = `${tbMain}.isDeleted = false`; - } - - // Special handling for Profile and ProfileEmployee systems with permission filtering - if (system == "registry") { - // Get current revision - const revision = await this.orgRevisionRepository.findOne({ - select: ["id"], - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - - // Store for use in permission building - this.currentRevisionId = revision?.id || ""; - posMasterAlias = "posMaster"; - - // Build permission condition - posMasterCondition = this.buildPosMasterPermissionCondition( - request.user.accessType, - { - dnaRootId: request.user.dnaRootId, - dnaChild1Id: request.user.dnaChild1Id, - dnaChild2Id: request.user.dnaChild2Id, - dnaChild3Id: request.user.dnaChild3Id, - dnaChild4Id: request.user.dnaChild4Id, - }, - posMasterAlias, - ); - } else if (system == "registry_emp") { - // Get current revision - const revision = await this.orgRevisionRepository.findOne({ - select: ["id"], - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - - // Store for use in permission building - this.currentRevisionId = revision?.id || ""; - posMasterAlias = "employeePosMaster"; - - // Build permission condition - posMasterCondition = this.buildPosMasterPermissionCondition( - request.user.accessType, - { - dnaRootId: request.user.dnaRootId, - dnaChild1Id: request.user.dnaChild1Id, - dnaChild2Id: request.user.dnaChild2Id, - dnaChild3Id: request.user.dnaChild3Id, - dnaChild4Id: request.user.dnaChild4Id, - }, - posMasterAlias, - ); - } else if (system == "registry_temp") { - // Get current revision - const revision = await this.orgRevisionRepository.findOne({ - select: ["id"], - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - - // Store for use in permission building - this.currentRevisionId = revision?.id || ""; - posMasterAlias = "employeeTempPosMaster"; - - // Build permission condition - posMasterCondition = this.buildPosMasterPermissionCondition( - request.user.accessType, - { - dnaRootId: request.user.dnaRootId, - dnaChild1Id: request.user.dnaChild1Id, - dnaChild2Id: request.user.dnaChild2Id, - dnaChild3Id: request.user.dnaChild3Id, - dnaChild4Id: request.user.dnaChild4Id, - }, - posMasterAlias, - ); - } else if (system == "position") { - // Get current revision - const revision = await this.orgRevisionRepository.findOne({ - select: ["id"], - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - - // Store for use in permission building - this.currentRevisionId = revision?.id || ""; - posMasterAlias = "PosMaster"; // Note: Uses PascalCase to match tbMain alias - - // Build permission condition - posMasterCondition = this.buildPosMasterPermissionCondition( - request.user.accessType, - { - dnaRootId: request.user.dnaRootId, - dnaChild1Id: request.user.dnaChild1Id, - dnaChild2Id: request.user.dnaChild2Id, - dnaChild3Id: request.user.dnaChild3Id, - dnaChild4Id: request.user.dnaChild4Id, - }, - posMasterAlias, - ); - } - const repo = AppDataSource.getRepository(tbMain); const metadata = repo.metadata; @@ -473,271 +92,27 @@ export class ApiWebServiceController extends Controller { ...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== tbMain)), ]; - // Organization hierarchy is assembled in a separate step; keep main query focused on OrgRoot only. - if (tbMain === "OrgRoot") { - const orgChildTables = new Set(["OrgChild1", "OrgChild2", "OrgChild3", "OrgChild4"]); - propertyKey = propertyKey.filter((key) => !orgChildTables.has(key.split(".")[0])); - propertyOtherKey = propertyOtherKey.filter((tb) => !orgChildTables.has(tb)); - } - - // สำหรับ Profile: ตรวจสอบฟิลด์ที่ต้องการ join และแปลง propertyKey - const profileFieldJoins: Record = {}; // alias -> relationName - if (tbMain === "Profile") { - propertyKey = propertyKey.map((key) => { - const [table, field] = key.split("."); - if (table === "Profile") { - const replacement = this.PROFILE_FIELD_REPLACEMENTS[field]; - if (replacement) { - const alias = `${table}_${replacement.joinRelation}`; - profileFieldJoins[alias] = replacement.joinRelation; - return `${alias}.${replacement.joinField}`; - } - } - return key; - }); - } - - // สำหรับ Position: ตรวจสอบฟิลด์ที่ต้องการ join และแปลง propertyKey - const positionFieldJoins: Record = {}; // alias -> relationName - if (tbMain === "Position" || tbMain === "PosMaster") { - propertyKey = propertyKey.map((key) => { - const [table, field] = key.split("."); - if (table === "Position") { - const replacement = this.POSITION_FIELD_REPLACEMENTS[field]; - if (replacement) { - const alias = `${table}_${replacement.joinRelation}`; - positionFieldJoins[alias] = replacement.joinRelation; - return `${alias}.${replacement.joinField}`; - } - } - return key; - }); - } - - // สำหรับ ProfileEmployee: ตรวจสอบฟิลด์ที่ต้องการ join และแปลง propertyKey - const profileEmployeeFieldJoins: Record = {}; // alias -> relationName - if (tbMain === "ProfileEmployee") { - propertyKey = propertyKey.map((key) => { - const [table, field] = key.split("."); - if (table === "ProfileEmployee") { - const replacement = this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS[field]; - if (replacement) { - const alias = `${table}_${replacement.joinRelation}`; - profileEmployeeFieldJoins[alias] = replacement.joinRelation; - return `${alias}.${replacement.joinField}`; - } - } - return key; - }); - } - - // สำหรับ ProfileLeave: ตรวจสอบฟิลด์ที่ต้องการ join และแปลง propertyKey - const profileLeaveFieldJoins: Record = {}; // alias -> relationName - // Process ProfileLeave fields regardless of main table - propertyKey = propertyKey.map((key) => { - const [table, field] = key.split("."); - if (table === "ProfileLeave") { - const replacement = this.PROFILELEAVE_FIELD_REPLACEMENTS[field]; - if (replacement) { - const alias = `${table}_${replacement.joinRelation}`; - profileLeaveFieldJoins[alias] = replacement.joinRelation; - return `${alias}.${replacement.joinField}`; - } - } - return key; - }); - const queryBuilder = repo.createQueryBuilder(tbMain); // join กับตารารอง if (propertyOtherKey.length > 0) { propertyOtherKey.forEach((tb) => { - // Skip Profile join for PosMaster - it's handled separately below - if (tbMain === "PosMaster" && tb === "Profile") { - return; - } - - // Skip Position join for PosMaster - it's handled separately below - if (tbMain === "PosMaster" && tb === "Position") { - return; - } - const relationName = relationMap[tb]; if (relationName) { - queryBuilder.leftJoin(`${tbMain}.${relationName}`, tb); - } else { - // Remove fields from this table from propertyKey - propertyKey = propertyKey.filter((key) => !key.startsWith(`${tb}.`)); - } - }); - } - - // Check if propertyKey is empty after filtering - if (propertyKey.length === 0) { - throw new HttpError( - HttpStatusCode.BAD_REQUEST, - "ไม่พบฟิลด์ที่ต้องการแสดงผล กรุณาตรวจสอบการตั้งค่า API (ไม่สามารถ join ตารางลูกได้)", - ); - } - - // join สำหรับฟิลด์ Profile ที่ต้องการดึงค่าจากตารางอื่น - if (tbMain === "Profile" && Object.keys(profileFieldJoins).length > 0) { - Object.entries(profileFieldJoins).forEach(([alias, relationName]) => { - queryBuilder.leftJoin(`${tbMain}.${relationName}`, alias); - }); - } - - // join สำหรับฟิลด์ Position ที่ต้องการดึงค่าจากตารางอื่น - if ( - (tbMain === "Position" || tbMain === "PosMaster") && - Object.keys(positionFieldJoins).length > 0 - ) { - if (tbMain === "PosMaster") { - const posMasterPositionRelation = relationMap["Position"]; - if (!posMasterPositionRelation) { - throw new HttpError( - HttpStatusCode.BAD_REQUEST, - "ไม่พบความสัมพันธ์ระหว่าง PosMaster กับ Position กรุณาตรวจสอบการตั้งค่า API", + queryBuilder.leftJoin( + `${tbMain}.${relationName === "next_holder" ? "current_holder" : relationName}`, // เช็คว่าถ้าเป็น next_holder ให้ใช้ current_holder แทน + tb, ); } - - // Join PosMaster -> Position once using actual relation name from metadata - queryBuilder.leftJoin(`PosMaster.${posMasterPositionRelation}`, "Position"); - } - - Object.entries(positionFieldJoins).forEach(([alias, relationName]) => { - if (tbMain === "PosMaster") { - queryBuilder.leftJoin(`Position.${relationName}`, alias); - } else { - queryBuilder.leftJoin(`${tbMain}.${relationName}`, alias); - } }); } - // join สำหรับฟิลด์ ProfileEmployee ที่ต้องการดึงค่าจากตารางอื่น - if (tbMain === "ProfileEmployee" && Object.keys(profileEmployeeFieldJoins).length > 0) { - Object.entries(profileEmployeeFieldJoins).forEach(([alias, relationName]) => { - queryBuilder.leftJoin(`${tbMain}.${relationName}`, alias); - }); - } - - // join สำหรับฟิลด์ ProfileLeave ที่ต้องการดึงค่าจากตารางอื่น - if (Object.keys(profileLeaveFieldJoins).length > 0) { - if (tbMain === "ProfileLeave") { - // ProfileLeave is the main table - direct join - Object.entries(profileLeaveFieldJoins).forEach(([alias, relationName]) => { - queryBuilder.leftJoin(`${tbMain}.${relationName}`, alias); - }); - } else { - // ProfileLeave is a related table - the base join is already created by propertyOtherKey logic - // Join from the ProfileLeave alias (not from nested path) - Object.entries(profileLeaveFieldJoins).forEach(([alias, relationName]) => { - queryBuilder.leftJoin(`ProfileLeave.${relationName}`, alias); - }); - } - } - - // join สำหรับ PosMaster เมื่อต้องการดึงค่าจาก Profile (ข้อมูลคนครอง) - const posMasterProfileFields: string[] = []; - if (tbMain === "PosMaster") { - // Collect Profile fields from both formats: "Profile.xxx" and "PosMaster.Profile.xxx" - const extractedProfileFields = propertyKey - .filter((key) => key.startsWith("Profile.") || key.startsWith("PosMaster.Profile.")) - .map((key) => key.replace(/^PosMaster\.Profile\./, "Profile.")); - - posMasterProfileFields.push(...new Set(extractedProfileFields)); - - // Remove Profile fields then add back normalized "Profile.xxx" form - propertyKey = propertyKey.filter( - (key) => !key.startsWith("Profile.") && !key.startsWith("PosMaster.Profile."), - ); - propertyKey.push(...posMasterProfileFields); - } - - // join PosMaster กับ Profile เมื่อมีการขอ Profile fields - if (tbMain === "PosMaster" && posMasterProfileFields.length > 0) { - // Always join via current_holder (not next_holder) because PosMaster has two relations - // to Profile and relationMap["Profile"] would resolve to next_holder (last defined in entity) - queryBuilder.leftJoin("PosMaster.current_holder", "Profile"); - } - - // สำหรับ registry system: เก็บ posMaster เพื่อดึง org IDs แล้วค่อย query ancestorDNA - let includeOrgAncestorDna = false; - if (system === "registry" || system === "registry_emp" || system === "registry_temp") { - // Always join posMaster for registry systems (inner join to exclude profiles without current posMaster) - // Only include posMaster from current revision - if (tbMain === "Profile") { - queryBuilder.innerJoin("Profile.current_holders", "posMaster", "posMaster.orgRevisionId = :currentRevisionId"); - queryBuilder.setParameter("currentRevisionId", this.currentRevisionId); - - // Add org ID fields from posMaster to propertyKey - propertyKey.push("posMaster.orgRootId"); - propertyKey.push("posMaster.orgChild1Id"); - propertyKey.push("posMaster.orgChild2Id"); - propertyKey.push("posMaster.orgChild3Id"); - propertyKey.push("posMaster.orgChild4Id"); - } else if (tbMain === "ProfileEmployee") { - // For registry_emp and registry_temp, also use inner join with current revision - if (system === "registry_temp") { - queryBuilder.innerJoin("ProfileEmployee.current_holderTemps", "employeeTempPosMaster", "employeeTempPosMaster.orgRevisionId = :currentRevisionId"); - queryBuilder.setParameter("currentRevisionId", this.currentRevisionId); - - // Add org ID fields from employeeTempPosMaster to propertyKey - propertyKey.push("employeeTempPosMaster.orgRootId"); - propertyKey.push("employeeTempPosMaster.orgChild1Id"); - propertyKey.push("employeeTempPosMaster.orgChild2Id"); - propertyKey.push("employeeTempPosMaster.orgChild3Id"); - propertyKey.push("employeeTempPosMaster.orgChild4Id"); - } else { - // registry_emp - queryBuilder.innerJoin("ProfileEmployee.current_holders", "employeePosMaster", "employeePosMaster.orgRevisionId = :currentRevisionId"); - queryBuilder.setParameter("currentRevisionId", this.currentRevisionId); - - // Add org ID fields from employeePosMaster to propertyKey - propertyKey.push("employeePosMaster.orgRootId"); - propertyKey.push("employeePosMaster.orgChild1Id"); - propertyKey.push("employeePosMaster.orgChild2Id"); - propertyKey.push("employeePosMaster.orgChild3Id"); - propertyKey.push("employeePosMaster.orgChild4Id"); - } - } - - // Mark that we need to include ancestorDNA fields - includeOrgAncestorDna = true; - } - - // join กับ posMaster/employeePosMaster/employeeTempPosMaster เพื่อกรองตามสิทธิ์การเข้าถึง - // Skip duplicate join - posMaster already joined for registry systems at lines 569-571 - // Permission condition will use the existing alias - const posMasterAlreadyJoined = (system === "registry" || system === "registry_emp" || system === "registry_temp") && tbMain === "Profile"; - - if ((tbMain === "Profile" || tbMain === "ProfileEmployee") && posMasterCondition !== "1=1") { - if (tbMain === "Profile") { - // Only join if not already joined for registry systems - if (!posMasterAlreadyJoined) { - queryBuilder.innerJoin("Profile.current_holders", "posMaster", "posMaster.orgRevisionId = :currentRevisionIdPerm"); - queryBuilder.setParameter("currentRevisionIdPerm", this.currentRevisionId); - } - } else if (tbMain === "ProfileEmployee") { - // Check if already joined for registry_emp systems - const alreadyJoined = (system === "registry_emp" || system === "registry_temp"); - // Use the correct relation based on posMasterAlias - if (posMasterAlias === "employeeTempPosMaster" && !alreadyJoined) { - queryBuilder.innerJoin("ProfileEmployee.current_holderTemps", "employeeTempPosMaster", "employeeTempPosMaster.orgRevisionId = :currentRevisionIdPerm"); - queryBuilder.setParameter("currentRevisionIdPerm", this.currentRevisionId); - } else if (posMasterAlias === "employeePosMaster" && !alreadyJoined) { - queryBuilder.innerJoin("ProfileEmployee.current_holders", "employeePosMaster", "employeePosMaster.orgRevisionId = :currentRevisionIdPerm"); - queryBuilder.setParameter("currentRevisionIdPerm", this.currentRevisionId); - } - } - } - // // เพิ่ม Main.id เพราะจะใช้ pk ในการแมบและนับจำนวน // if (!propertyKey.includes(`${Main}.id`)) { // propertyKey.push(`${Main}.id`); // } - // add PK - ensure propertyKey is never empty + // add FK let pk: string = ""; const primaryColumns = metadata.primaryColumns; primaryColumns.forEach((col) => { @@ -747,89 +122,13 @@ export class ApiWebServiceController extends Controller { } }); - let items: any[] = []; - let total = 0; - - if (tbMain === "OrgRoot") { - // Organization API should always return full hierarchy regardless of page/pageSize. - [items, total] = await queryBuilder - .select(propertyKey) - .where(condition) - .andWhere(posMasterCondition) - .orderBy(propertyKey[0] || `${tbMain}.${pk}`, "ASC") - .getManyAndCount(); - } else { - [items, total] = await queryBuilder - .select(propertyKey) - .where(condition) - .andWhere(posMasterCondition) - .orderBy(propertyKey[0] || `${tbMain}.${pk}`, "ASC") - .skip(offset) - .take(pageSize) - .getManyAndCount(); - } - - // สำหรับ registry systems: ดึง ancestorDNA จาก org tables - let orgRootAncestorMap: Record = {}; - let orgChild1AncestorMap: Record = {}; - let orgChild2AncestorMap: Record = {}; - let orgChild3AncestorMap: Record = {}; - let orgChild4AncestorMap: Record = {}; - - if (includeOrgAncestorDna && items.length > 0) { - // Collect all unique org IDs - const orgRootIds = new Set(); - const orgChild1Ids = new Set(); - const orgChild2Ids = new Set(); - const orgChild3Ids = new Set(); - const orgChild4Ids = new Set(); - - items.forEach((item) => { - // Handle Profile (registry) - current_holders - if (item["current_holders"] && Array.isArray(item["current_holders"]) && item["current_holders"].length > 0) { - const posMaster = item["current_holders"][0]; - if (posMaster.orgRootId) orgRootIds.add(posMaster.orgRootId); - if (posMaster.orgChild1Id) orgChild1Ids.add(posMaster.orgChild1Id); - if (posMaster.orgChild2Id) orgChild2Ids.add(posMaster.orgChild2Id); - if (posMaster.orgChild3Id) orgChild3Ids.add(posMaster.orgChild3Id); - if (posMaster.orgChild4Id) orgChild4Ids.add(posMaster.orgChild4Id); - } - // Handle ProfileEmployee (registry_emp) - current_holders - if (item["current_holders"] && Array.isArray(item["current_holders"]) && item["current_holders"].length > 0) { - const posMaster = item["current_holders"][0]; - if (posMaster.orgRootId) orgRootIds.add(posMaster.orgRootId); - if (posMaster.orgChild1Id) orgChild1Ids.add(posMaster.orgChild1Id); - if (posMaster.orgChild2Id) orgChild2Ids.add(posMaster.orgChild2Id); - if (posMaster.orgChild3Id) orgChild3Ids.add(posMaster.orgChild3Id); - if (posMaster.orgChild4Id) orgChild4Ids.add(posMaster.orgChild4Id); - } - // Handle ProfileEmployee (registry_temp) - current_holderTemps - if (item["current_holderTemps"] && Array.isArray(item["current_holderTemps"]) && item["current_holderTemps"].length > 0) { - const posMaster = item["current_holderTemps"][0]; - if (posMaster.orgRootId) orgRootIds.add(posMaster.orgRootId); - if (posMaster.orgChild1Id) orgChild1Ids.add(posMaster.orgChild1Id); - if (posMaster.orgChild2Id) orgChild2Ids.add(posMaster.orgChild2Id); - if (posMaster.orgChild3Id) orgChild3Ids.add(posMaster.orgChild3Id); - if (posMaster.orgChild4Id) orgChild4Ids.add(posMaster.orgChild4Id); - } - }); - - // Query org tables to get ancestorDNA - const [orgRoots, orgChild1s, orgChild2s, orgChild3s, orgChild4s] = await Promise.all([ - orgRootIds.size > 0 ? AppDataSource.getRepository(OrgRoot).createQueryBuilder("orgRoot").select(["orgRoot.id", "orgRoot.ancestorDNA"]).where("orgRoot.id IN (:...ids)", { ids: Array.from(orgRootIds) }).getMany() : [], - orgChild1Ids.size > 0 ? AppDataSource.getRepository(OrgChild1).createQueryBuilder("orgChild1").select(["orgChild1.id", "orgChild1.ancestorDNA"]).where("orgChild1.id IN (:...ids)", { ids: Array.from(orgChild1Ids) }).getMany() : [], - orgChild2Ids.size > 0 ? AppDataSource.getRepository(OrgChild2).createQueryBuilder("orgChild2").select(["orgChild2.id", "orgChild2.ancestorDNA"]).where("orgChild2.id IN (:...ids)", { ids: Array.from(orgChild2Ids) }).getMany() : [], - orgChild3Ids.size > 0 ? AppDataSource.getRepository(OrgChild3).createQueryBuilder("orgChild3").select(["orgChild3.id", "orgChild3.ancestorDNA"]).where("orgChild3.id IN (:...ids)", { ids: Array.from(orgChild3Ids) }).getMany() : [], - orgChild4Ids.size > 0 ? AppDataSource.getRepository(OrgChild4).createQueryBuilder("orgChild4").select(["orgChild4.id", "orgChild4.ancestorDNA"]).where("orgChild4.id IN (:...ids)", { ids: Array.from(orgChild4Ids) }).getMany() : [], - ]); - - // Create separate maps for each org level - orgRoots.forEach((org: any) => { orgRootAncestorMap[org.id] = org.ancestorDNA; }); - orgChild1s.forEach((org: any) => { orgChild1AncestorMap[org.id] = org.ancestorDNA; }); - orgChild2s.forEach((org: any) => { orgChild2AncestorMap[org.id] = org.ancestorDNA; }); - orgChild3s.forEach((org: any) => { orgChild3AncestorMap[org.id] = org.ancestorDNA; }); - orgChild4s.forEach((org: any) => { orgChild4AncestorMap[org.id] = org.ancestorDNA; }); - } + const [items, total] = await queryBuilder + .select(propertyKey) + .where(condition) + .orderBy(propertyKey[0], "ASC") + .skip(offset) + .take(pageSize) + .getManyAndCount(); // ลบ Main.id // const results = items.map(({ id, ...x }) => x); @@ -842,330 +141,11 @@ export class ApiWebServiceController extends Controller { // split object id ออกก่อน return const data = items.map((item) => { - const { [pk]: removedPk, ...rest } = item; - - // สำหรับ Profile: แปลงฟิลด์ที่มาจาก join กลับเป็นชื่อเดิม - if (tbMain === "Profile") { - const flattened: any = { ...rest }; - Object.entries(this.PROFILE_FIELD_REPLACEMENTS).forEach(([nameField, config]) => { - const alias = `${tbMain}_${config.joinRelation}`; - if (rest[alias] && rest[alias][config.joinField] !== undefined) { - flattened[nameField] = rest[alias][config.joinField]; - delete flattened[alias]; - } - // Also handle nested relation objects (e.g., "posLevel": { "posLevelName": "..." }) - if ( - rest[config.joinRelation] && - rest[config.joinRelation][config.joinField] !== undefined - ) { - flattened[nameField] = rest[config.joinRelation][config.joinField]; - delete flattened[config.joinRelation]; - } - }); - - // แปลง ancestorDNA เป็น orgRootId, orgChild1Id, etc. - if (includeOrgAncestorDna) { - if (rest["current_holders"] && Array.isArray(rest["current_holders"]) && rest["current_holders"].length > 0) { - const posMaster = rest["current_holders"][0]; - - // Get ancestorDNA from separate maps using org IDs - // Always set the fields, use null if no value - flattened.orgRootId = (posMaster.orgRootId && orgRootAncestorMap[posMaster.orgRootId]) ? orgRootAncestorMap[posMaster.orgRootId] : null; - flattened.orgChild1Id = (posMaster.orgChild1Id && orgChild1AncestorMap[posMaster.orgChild1Id]) ? orgChild1AncestorMap[posMaster.orgChild1Id] : null; - flattened.orgChild2Id = (posMaster.orgChild2Id && orgChild2AncestorMap[posMaster.orgChild2Id]) ? orgChild2AncestorMap[posMaster.orgChild2Id] : null; - flattened.orgChild3Id = (posMaster.orgChild3Id && orgChild3AncestorMap[posMaster.orgChild3Id]) ? orgChild3AncestorMap[posMaster.orgChild3Id] : null; - flattened.orgChild4Id = (posMaster.orgChild4Id && orgChild4AncestorMap[posMaster.orgChild4Id]) ? orgChild4AncestorMap[posMaster.orgChild4Id] : null; - } else { - // No current_holders, set all to null - flattened.orgRootId = null; - flattened.orgChild1Id = null; - flattened.orgChild2Id = null; - flattened.orgChild3Id = null; - flattened.orgChild4Id = null; - } - - // Delete current_holders array - delete flattened["current_holders"]; - } - return flattened; - } - - // สำหรับ Position: แปลงฟิลด์ที่มาจาก join กลับเป็นชื่อเดิม - if (tbMain === "Position" || tbMain === "PosMaster") { - const flattened: any = { ...rest }; - Object.entries(this.POSITION_FIELD_REPLACEMENTS).forEach(([nameField, config]) => { - // Remove the original ID field - delete flattened[config.propertyName]; - // Add the name field from joined table - const alias = `Position_${config.joinRelation}`; - if (rest[alias] && rest[alias][config.joinField] !== undefined) { - flattened[nameField] = rest[alias][config.joinField]; - } - // Remove the joined table object - delete flattened[alias]; - }); - // Remove Position object if exists - if (flattened["Position"]) { - delete flattened["Position"]; - } - return flattened; - } - - // สำหรับ ProfileEmployee: แปลงฟิลด์ที่มาจาก join กลับเป็นชื่อเดิม - if (tbMain === "ProfileEmployee") { - const flattened: any = { ...rest }; - Object.entries(this.PROFILEEMPLOYEE_FIELD_REPLACEMENTS).forEach(([nameField, config]) => { - // Remove the original ID field - delete flattened[config.propertyName]; - // Add the name field from joined table - const alias = `${tbMain}_${config.joinRelation}`; - if (rest[alias] && rest[alias][config.joinField] !== undefined) { - flattened[nameField] = rest[alias][config.joinField]; - } - // Remove the joined table object - delete flattened[alias]; - // Also handle nested relation objects (e.g., "posType": { "posTypeName": "..." }) - if ( - rest[config.joinRelation] && - rest[config.joinRelation][config.joinField] !== undefined - ) { - flattened[nameField] = rest[config.joinRelation][config.joinField]; - delete flattened[config.joinRelation]; - } - }); - - // แปลง ancestorDNA เป็น orgRootId, orgChild1Id, etc. (สำหรับ registry_emp และ registry_temp) - if (includeOrgAncestorDna) { - const posMasterKey = system === "registry_temp" ? "current_holderTemps" : "current_holders"; - if (rest[posMasterKey] && Array.isArray(rest[posMasterKey]) && rest[posMasterKey].length > 0) { - const posMaster = rest[posMasterKey][0]; - - // Always set the fields, use null if no value - flattened.orgRootId = (posMaster.orgRootId && orgRootAncestorMap[posMaster.orgRootId]) ? orgRootAncestorMap[posMaster.orgRootId] : null; - flattened.orgChild1Id = (posMaster.orgChild1Id && orgChild1AncestorMap[posMaster.orgChild1Id]) ? orgChild1AncestorMap[posMaster.orgChild1Id] : null; - flattened.orgChild2Id = (posMaster.orgChild2Id && orgChild2AncestorMap[posMaster.orgChild2Id]) ? orgChild2AncestorMap[posMaster.orgChild2Id] : null; - flattened.orgChild3Id = (posMaster.orgChild3Id && orgChild3AncestorMap[posMaster.orgChild3Id]) ? orgChild3AncestorMap[posMaster.orgChild3Id] : null; - flattened.orgChild4Id = (posMaster.orgChild4Id && orgChild4AncestorMap[posMaster.orgChild4Id]) ? orgChild4AncestorMap[posMaster.orgChild4Id] : null; - } else { - // No posMaster data, set all to null - flattened.orgRootId = null; - flattened.orgChild1Id = null; - flattened.orgChild2Id = null; - flattened.orgChild3Id = null; - flattened.orgChild4Id = null; - } - - // Delete the posMaster array - delete flattened[posMasterKey]; - } - return flattened; - } - - // สำหรับ PosMaster: แปลงฟิลด์ Profile ที่มาจาก join กลับเป็นฟิลด์ระดับบน - if (tbMain === "PosMaster" && posMasterProfileFields.length > 0) { - const flattened: any = { ...rest }; - const profileFieldNames = posMasterProfileFields - .filter((field) => field.startsWith("Profile.")) - .map((field) => field.replace("Profile.", "")); - - // Extract only requested Profile fields and add top-level aliases - if (rest["Profile"]) { - profileFieldNames.forEach((fieldName) => { - if (rest["Profile"][fieldName] !== undefined) { - flattened[`profile_${fieldName}`] = rest["Profile"][fieldName]; - } - }); - // Remove the nested Profile object - delete flattened["Profile"]; - } - return flattened; - } - - // สำหรับ OrgRoot: เก็บ primary key ไว้ใช้ group ข้อมูล แล้วแยก children ภายหลัง - if (tbMain === "OrgRoot") { - return { __rootPk: removedPk, ...rest }; - } - - return rest; + const { [pk]: removedPk, ...x } = item; + return x; }); - let responseData: any[] = data; - let responseTotal = total; - - // สำหรับ Organization: รวมข้อมูลให้เหลือ 1 root ต่อ 1 object และจัด children ตาม hierarchy - if (tbMain === "OrgRoot") { - const rootVisibleFields = Array.from(selectedFieldsByTable["OrgRoot"] || []); - const child1VisibleFields = Array.from(selectedFieldsByTable["OrgChild1"] || []); - const child2VisibleFields = Array.from(selectedFieldsByTable["OrgChild2"] || []); - const child3VisibleFields = Array.from(selectedFieldsByTable["OrgChild3"] || []); - const child4VisibleFields = Array.from(selectedFieldsByTable["OrgChild4"] || []); - - const pickVisibleFields = (obj: any, fields: string[]) => { - const out: any = {}; - fields.forEach((field) => { - if (obj[field] !== undefined) { - // ถ้าเป็น ancestorDNA ให้เปลี่ยนชื่อเป็น id - if (field === "ancestorDNA") { - out.id = obj[field]; - } else { - out[field] = obj[field]; - } - } - }); - return out; - }; - - const rootMap = new Map(); - data.forEach((row: any) => { - if (!row.__rootPk || rootMap.has(row.__rootPk)) { - return; - } - - const rootNode = { - ...pickVisibleFields(row, rootVisibleFields), - children: [], - }; - rootMap.set(row.__rootPk, rootNode); - }); - - const rootIds = Array.from(rootMap.keys()); - - if (rootIds.length > 0) { - const buildSelect = (alias: string, required: string[], visible: string[]) => - Array.from(new Set([...required, ...visible])).map((field) => `${alias}.${field}`); - - const [child1Rows, child2Rows, child3Rows, child4Rows] = await Promise.all([ - AppDataSource.getRepository(OrgChild1) - .createQueryBuilder("OrgChild1") - .select(buildSelect("OrgChild1", ["id", "orgRootId", "ancestorDNA"], child1VisibleFields)) - .where("OrgChild1.orgRootId IN (:...rootIds)", { rootIds }) - .orderBy("OrgChild1.id", "ASC") - .getMany(), - AppDataSource.getRepository(OrgChild2) - .createQueryBuilder("OrgChild2") - .select( - buildSelect("OrgChild2", ["id", "orgRootId", "orgChild1Id", "ancestorDNA"], child2VisibleFields), - ) - .where("OrgChild2.orgRootId IN (:...rootIds)", { rootIds }) - .orderBy("OrgChild2.id", "ASC") - .getMany(), - AppDataSource.getRepository(OrgChild3) - .createQueryBuilder("OrgChild3") - .select( - buildSelect( - "OrgChild3", - ["id", "orgRootId", "orgChild1Id", "orgChild2Id", "ancestorDNA"], - child3VisibleFields, - ), - ) - .where("OrgChild3.orgRootId IN (:...rootIds)", { rootIds }) - .orderBy("OrgChild3.id", "ASC") - .getMany(), - AppDataSource.getRepository(OrgChild4) - .createQueryBuilder("OrgChild4") - .select( - buildSelect( - "OrgChild4", - ["id", "orgRootId", "orgChild1Id", "orgChild2Id", "orgChild3Id", "ancestorDNA"], - child4VisibleFields, - ), - ) - .where("OrgChild4.orgRootId IN (:...rootIds)", { rootIds }) - .orderBy("OrgChild4.id", "ASC") - .getMany(), - ]); - - const child1Map = new Map(); - const child2Map = new Map(); - const child3Map = new Map(); - - child1Rows.forEach((row) => { - const node = { - ...pickVisibleFields(row, child1VisibleFields), - children: [], - }; - child1Map.set(row.id, node); - - const rootNode = rootMap.get(row.orgRootId); - if (rootNode) { - rootNode.children.push(node); - } - }); - - child2Rows.forEach((row) => { - const node = { - ...pickVisibleFields(row, child2VisibleFields), - children: [], - }; - child2Map.set(row.id, node); - - const parent = child1Map.get(row.orgChild1Id); - if (parent) { - parent.children.push(node); - } - }); - - child3Rows.forEach((row) => { - const node = { - ...pickVisibleFields(row, child3VisibleFields), - children: [], - }; - child3Map.set(row.id, node); - - const parent = child2Map.get(row.orgChild2Id); - if (parent) { - parent.children.push(node); - } - }); - - child4Rows.forEach((row) => { - const node = { - ...pickVisibleFields(row, child4VisibleFields), - }; - - const parent = child3Map.get(row.orgChild3Id); - if (parent) { - if (!Array.isArray(parent.children)) { - parent.children = []; - } - parent.children.push(node); - } - }); - } - - responseData = Array.from(rootMap.values()); - - // สำหรับ Organization: เปลี่ยนชื่อ ancestorDNA เป็น id - responseData = responseData.map((root: any) => { - const renamed = this.renameAncestorDnaToId(root); - if (Array.isArray(renamed.children)) { - renamed.children = renamed.children.map((child1: any) => { - const renamedChild1 = this.renameAncestorDnaToId(child1); - if (Array.isArray(renamedChild1.children)) { - renamedChild1.children = renamedChild1.children.map((child2: any) => { - const renamedChild2 = this.renameAncestorDnaToId(child2); - if (Array.isArray(renamedChild2.children)) { - renamedChild2.children = renamedChild2.children.map((child3: any) => { - const renamedChild3 = this.renameAncestorDnaToId(child3); - if (Array.isArray(renamedChild3.children)) { - renamedChild3.children = renamedChild3.children.map((child4: any) => - this.renameAncestorDnaToId(child4), - ); - } - return renamedChild3; - }); - } - return renamedChild2; - }); - } - return renamedChild1; - }); - } - return renamed; - }); - - responseTotal = responseData.length; - } + // console.log("queryBuilder ===> ", queryBuilder.getQuery()); // save api history after query success const history = { @@ -1215,6 +195,6 @@ export class ApiWebServiceController extends Controller { // return flattenedItem; // }); - return new HttpSuccess({ data: responseData, total: responseTotal }); + return new HttpSuccess({ data: data, total }); } } diff --git a/src/controllers/AuthRoleController.ts b/src/controllers/AuthRoleController.ts index 13f5ce04..4159c5ec 100644 --- a/src/controllers/AuthRoleController.ts +++ b/src/controllers/AuthRoleController.ts @@ -123,25 +123,18 @@ export class AuthRoleController extends Controller { // เช็คว่าถ้ามีค่า current_holderId ให้ลบ key สิทธิ์ใน redis if (posMaster.current_holderId) { - let redisClient; - try { - redisClient = await this.redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); - redisClient.del("role_" + posMaster.current_holderId, (err: Error) => { - if (err) console.error("Redis delete role error:", err); - }); + redisClient.del("role_" + posMaster.current_holderId, (err: Error, response: Response) => { + if (err) throw err; + }); - redisClient.del("menu_" + posMaster.current_holderId, (err: Error) => { - if (err) console.error("Redis delete menu error:", err); - }); - } finally { - if (redisClient) { - redisClient.quit(); - } - } + redisClient.del("menu_" + posMaster.current_holderId, (err: Error, response: Response) => { + if (err) throw err; + }); } return new HttpSuccess(); @@ -267,45 +260,20 @@ export class AuthRoleController extends Controller { return newAttr; }); const before = structuredClone(record); + await Promise.all([ + this.authRoleRepo.save(record, { data: req }), + setLogDataDiff(req, { before, after: record }), + ...newAttrs.map((attr) => this.authRoleAttrRepo.save(attr)), + ]); - const queryRunner = AppDataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); - try { - await queryRunner.manager.save(AuthRole, record); - await Promise.all( - newAttrs.map((attr) => queryRunner.manager.save(AuthRoleAttr, attr)) - ); - await queryRunner.commitTransaction(); - - setLogDataDiff(req, { before, after: record }); - } catch (error) { - await queryRunner.rollbackTransaction(); - console.error("Error saving auth role:", error); - throw new HttpError( - HttpStatusCode.INTERNAL_SERVER_ERROR, - "เกิดข้อผิดพลาดในการบันทึกข้อมูลบทบาท กรุณาลองใหม่ในภายหลัง" - ); - } finally { - await queryRunner.release(); - } - - let redisClient; - try { - redisClient = await this.redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); - - await redisClient.flushdb(function (err: any, succeeded: any) { - console.log(succeeded); // will be true if successfull - }); - } finally { - if (redisClient) { - redisClient.quit(); - } - } + await redisClient.flushdb(function (err: any, succeeded: any) { + console.log(succeeded); // will be true if successfull + }); return new HttpSuccess(); } diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index b401aa31..27d2671c 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -41,9 +41,9 @@ import { removeProfileInOrganize, setLogDataDiff, checkReturnCommandType, + checkExceptCommandType, checkCommandType, removePostMasterAct, - logPositionIsSelectedChange, } from "../interfaces/utils"; import { Position } from "../entities/Position"; import { PosMaster } from "../entities/PosMaster"; @@ -81,6 +81,8 @@ import { District } from "../entities/District"; import { Province } from "../entities/Province"; import { ProfileAssistance } from "../entities/ProfileAssistance"; import { ProfileAssistanceHistory } from "../entities/ProfileAssistanceHistory"; +import { ProfileActposition } from "../entities/ProfileActposition"; +import { ProfileActpositionHistory } from "../entities/ProfileActpositionHistory"; import { ProfileFamilyFather } from "../entities/ProfileFamilyFather"; import { ProfileFamilyFatherHistory } from "../entities/ProfileFamilyFatherHistory"; import { ProfileFamilyCouple } from "../entities/ProfileFamilyCouple"; @@ -100,17 +102,11 @@ import { } from "../services/PositionService"; import { LeaveType } from "../entities/LeaveType"; import { KeycloakAttributeService } from "../services/KeycloakAttributeService"; -import { reOrderCommandRecivesAndDelete, ensureCommandOperator } from "../services/CommandService"; +import { reOrderCommandRecivesAndDelete } from "../services/CommandService"; import { RetirementService } from "../services/RetirementService"; -import { ExecuteOfficerProfileService } from "../services/ExecuteOfficerProfileService"; -import { ExecuteSalaryService } from "../services/ExecuteSalaryService"; -import { ExecuteSalaryCurrentService } from "../services/ExecuteSalaryCurrentService"; -import { ExecuteSalaryEmployeeCurrentService } from "../services/ExecuteSalaryEmployeeCurrentService"; -import { ExecuteSalaryLeaveService } from "../services/ExecuteSalaryLeaveService"; -import { ExecuteSalaryEmployeeLeaveService } from "../services/ExecuteSalaryEmployeeLeaveService"; -import { ExecuteSalaryLeaveDisciplineService } from "../services/ExecuteSalaryLeaveDisciplineService"; -import { ExecuteSalaryProbationService } from "../services/ExecuteSalaryProbationService"; -import { ExecuteOrgCommandService } from "../services/ExecuteOrgCommandService"; +import { promisify } from "util"; +const REDIS_HOST = process.env.REDIS_HOST; +const REDIS_PORT = process.env.REDIS_PORT; @Route("api/v1/org/command") @Tags("Command") @@ -120,6 +116,7 @@ import { ExecuteOrgCommandService } from "../services/ExecuteOrgCommandService"; "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", ) export class CommandController extends Controller { + private redis = require("redis"); private commandRepository = AppDataSource.getRepository(Command); private commandTypeRepository = AppDataSource.getRepository(CommandType); private commandSendRepository = AppDataSource.getRepository(CommandSend); @@ -154,6 +151,8 @@ export class CommandController extends Controller { private subDistrictRepo = AppDataSource.getRepository(SubDistrict); private assistanceRepository = AppDataSource.getRepository(ProfileAssistance); private assistanceHistoryRepository = AppDataSource.getRepository(ProfileAssistanceHistory); + private actpositionRepository = AppDataSource.getRepository(ProfileActposition); + private actpositionHistoryRepository = AppDataSource.getRepository(ProfileActpositionHistory); private profileFamilyCoupleRepo = AppDataSource.getRepository(ProfileFamilyCouple); private profileFamilyCoupleHistoryRepo = AppDataSource.getRepository(ProfileFamilyCoupleHistory); private profileFamilyMotherRepo = AppDataSource.getRepository(ProfileFamilyMother); @@ -206,13 +205,7 @@ export class CommandController extends Controller { child4: null, }; if (request.user.role.includes("STAFF")) { - // #2523 STAFF + isDirector ให้ล้อสิทธิ์เหมือน CHILD - if (!isDirector) { - _data = await new permission().PermissionOrgList(request, "COMMAND"); - - } else { - _data = await new permission().PermissionIsDirectorOrgList(request, "COMMAND", isDirector); - } + _data = await new permission().PermissionOrgList(request, "COMMAND"); } if (isDirector || _data.privilege == "OWNER") { const profiles = await this.profileRepository @@ -343,11 +336,9 @@ export class CommandController extends Controller { .andWhere( new Brackets((qb) => { qb.where( - keyword != null && keyword != "" - ? "TRIM(CONCAT(COALESCE(command.shortName, ''), ' ', command.commandNo, '/', command.commandYear + 543)) LIKE :keyword" - : "1=1", + keyword != null && keyword != "" ? "command.commandNo LIKE :baseKeyword" : "1=1", { - keyword: `%${keyword}%`, + baseKeyword: `%${baseKeyword}%`, }, ) .orWhere(keyword != null && keyword != "" ? "command.issue LIKE :keyword" : "1=1", { @@ -374,7 +365,7 @@ export class CommandController extends Controller { const data = commands.map((_data) => ({ id: _data.id, - commandNo: `${_data.shortName ?? ""} ${_data.commandNo}`.trim(), + commandNo: _data.commandNo, commandYear: _data.commandYear, commandAffectDate: _data.commandAffectDate, commandExcecuteDate: _data.commandExcecuteDate, @@ -415,7 +406,6 @@ export class CommandController extends Controller { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทคำสั่งนี้ในระบบ"); } const now = new Date(); - let userProfile: any = null; command.detailHeader = commandType.detailHeader; command.detailBody = commandType.detailBody; command.detailFooter = commandType.detailFooter; @@ -429,39 +419,78 @@ export class CommandController extends Controller { command.lastUpdateUserId = request.user.sub; command.lastUpdateFullName = request.user.name; command.lastUpdatedAt = now; - // Query profile ครั้งเดียว ใช้ร่วมกันทั้ง shortName และ CommandOperator - userProfile = await this.profileRepository.findOne({ - where: { keycloak: request.user.sub }, - relations: { - posLevel: true, - posType: true, - current_holders: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }, - }); - - // เช็คถ้าไม่ใช่ กสจ. ดึง root.shortName มาปั๊ม - if (userProfile) { - const currentHolder = userProfile.current_holders?.find( - (x: any) => - x.orgRevision?.orgRevisionIsDraft === false && - x.orgRevision?.orgRevisionIsCurrent === true, - ); - - if (currentHolder && !currentHolder.orgChild1?.isOfficer) { - command.shortName = currentHolder.orgRoot?.orgRootShortName ?? null; - } - } - await this.commandRepository.save(command); // insert commandOperator - await ensureCommandOperator(userProfile, command.id, request, now); + if (request.user.sub) { + const profile = await this.profileRepository.findOne({ + where: { keycloak: request.user.sub }, + relations: { + posLevel: true, + posType: true, + current_holders: { + orgRevision: true, + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }, + }); + if (profile) { + const currentHolder = profile!.current_holders?.find( + (x) => + x.orgRevision?.orgRevisionIsDraft === false && + x.orgRevision?.orgRevisionIsCurrent === true, + ); + + const posNo = + currentHolder != null && currentHolder.orgChild4 != null + ? `${currentHolder.orgChild4.orgChild4ShortName} ${currentHolder.posMasterNo}` + : currentHolder != null && currentHolder.orgChild3 != null + ? `${currentHolder.orgChild3.orgChild3ShortName} ${currentHolder.posMasterNo}` + : currentHolder != null && currentHolder.orgChild2 != null + ? `${currentHolder.orgChild2.orgChild2ShortName} ${currentHolder.posMasterNo}` + : currentHolder != null && currentHolder.orgChild1 != null + ? `${currentHolder.orgChild1.orgChild1ShortName} ${currentHolder.posMasterNo}` + : currentHolder != null && currentHolder?.orgRoot != null + ? `${currentHolder.orgRoot.orgRootShortName} ${currentHolder.posMasterNo}` + : null; + + const position = await this.positionRepository.findOne({ + where: { + positionIsSelected: true, + posMaster: { + orgRevisionId: currentHolder?.orgRevisionId, + current_holderId: profile!.id, + }, + }, + order: { createdAt: "DESC" }, + relations: { posExecutive: true }, + }); + const operator = Object.assign(new CommandOperator(), { + profileId: profile?.id, + prefix: profile?.prefix, + firstName: profile?.firstName, + lastName: profile?.lastName, + posNo: posNo, + posType: profile?.posType?.posTypeName ?? null, + posLevel: profile?.posLevel?.posLevelName ?? null, + position: position?.positionName ?? null, + positionExecutive: position?.posExecutive?.posExecutiveName ?? null, + roleName: "เจ้าหน้าที่ดำเนินการ", + orderNo: 1, + commandId: command.id, + createdUserId: request.user.sub, + createdFullName: request.user.name, + createdAt: now, + lastUpdateUserId: request.user.sub, + lastUpdateFullName: request.user.name, + lastUpdatedAt: now, + }); + await this.commandOperatorRepository.save(operator); + } + } return new HttpSuccess(command.id); } @@ -486,7 +515,6 @@ export class CommandController extends Controller { const _command = { id: command.id, status: command.status, - shortName: command.shortName ?? "", commandNo: command.commandNo, commandYear: command.commandYear, issue: command.issue, @@ -537,34 +565,6 @@ export class CommandController extends Controller { } const data = new Command(); Object.assign(data, { ...command, ...requestBody }); - - // ถ้าเป็น officer (isOfficer == true) ดึง orgRoot.shortName มาใช้ - const userProfile = await this.profileRepository.findOne({ - where: { keycloak: request.user.sub }, - relations: { - current_holders: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - }, - }, - }); - - if (userProfile) { - const currentHolder = userProfile.current_holders?.find( - (x: any) => - x.orgRevision?.orgRevisionIsDraft === false && - x.orgRevision?.orgRevisionIsCurrent === true, - ); - - if (currentHolder && currentHolder.orgChild1?.isOfficer) { - data.shortName = - requestBody.isBangkok && requestBody.isBangkok === "BANGKOK" - ? "กทม." - : currentHolder.orgRoot?.orgRootShortName ?? "สนป."; - } - } - data.lastUpdateUserId = request.user.sub; data.lastUpdateFullName = request.user.name; data.lastUpdatedAt = new Date(); @@ -2373,9 +2373,9 @@ export class CommandController extends Controller { ? "" : Extension.ToThaiNumber(Extension.ToThaiYear(command.commandYear).toString()), commandExcecuteDate: - command.commandAffectDate == null + command.commandExcecuteDate == null ? "" - : Extension.ToThaiNumber(Extension.ToThaiFullDate2(command.commandAffectDate)), + : Extension.ToThaiNumber(Extension.ToThaiFullDate2(command.commandExcecuteDate)), operators: operators.length > 0 ? operators.map((x) => ({ @@ -2580,9 +2580,7 @@ export class CommandController extends Controller { const now = new Date(); let command = new Command(); let commandCode: string = ""; - let commandSysId: string = ""; let _null: any = null; - let userProfile: any = null; if ( requestBody.commandId != undefined && requestBody.commandId != null && @@ -2600,7 +2598,6 @@ export class CommandController extends Controller { if (!_command) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบคำสั่งนี้ในระบบ"); } - commandSysId = _command.commandType.commandSysId; commandCode = _command.commandType.code; command = _command; } else { @@ -2615,7 +2612,6 @@ export class CommandController extends Controller { if (!commandType) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทคำสั่งนี้ในระบบ"); } - commandSysId = commandType.commandSysId; commandCode = commandType.code; command.detailHeader = commandType.detailHeader; command.detailBody = commandType.detailBody; @@ -2644,37 +2640,6 @@ export class CommandController extends Controller { command.lastUpdateUserId = request.user.sub; command.lastUpdateFullName = request.user.name; command.lastUpdatedAt = now; - - // Query profile ครั้งเดียว ใช้ร่วมกันทั้ง shortName และ CommandOperator - userProfile = await this.profileRepository.findOne({ - where: { keycloak: request.user.sub }, - relations: { - posLevel: true, - posType: true, - current_holders: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }, - }); - - // เช็คถ้าไม่ใช่ กสจ. ดึง root.shortName มาปั๊ม - if (userProfile) { - const currentHolder = userProfile.current_holders?.find( - (x: any) => - x.orgRevision?.orgRevisionIsDraft === false && - x.orgRevision?.orgRevisionIsCurrent === true, - ); - - if (currentHolder && !currentHolder.orgChild1?.isOfficer) { - command.shortName = currentHolder.orgRoot?.orgRootShortName ?? null; - } - } - await this.commandRepository.save(command); } // insert commandOperator @@ -2682,12 +2647,81 @@ export class CommandController extends Controller { where: { commandId: command.id, roleName: "เจ้าหน้าที่ดำเนินการ" }, }); if (!checkCommandOperator) { - await ensureCommandOperator(userProfile, command.id, request, now); + if (request.user.sub) { + const profile = await this.profileRepository.findOne({ + where: { keycloak: request.user.sub }, + relations: { + posLevel: true, + posType: true, + current_holders: { + orgRevision: true, + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }, + }); + if (profile) { + const currentHolder = profile!.current_holders?.find( + (x) => + x.orgRevision?.orgRevisionIsDraft === false && + x.orgRevision?.orgRevisionIsCurrent === true, + ); + + const posNo = + currentHolder != null && currentHolder.orgChild4 != null + ? `${currentHolder.orgChild4.orgChild4ShortName} ${currentHolder.posMasterNo}` + : currentHolder != null && currentHolder.orgChild3 != null + ? `${currentHolder.orgChild3.orgChild3ShortName} ${currentHolder.posMasterNo}` + : currentHolder != null && currentHolder.orgChild2 != null + ? `${currentHolder.orgChild2.orgChild2ShortName} ${currentHolder.posMasterNo}` + : currentHolder != null && currentHolder.orgChild1 != null + ? `${currentHolder.orgChild1.orgChild1ShortName} ${currentHolder.posMasterNo}` + : currentHolder != null && currentHolder?.orgRoot != null + ? `${currentHolder.orgRoot.orgRootShortName} ${currentHolder.posMasterNo}` + : null; + + const position = await this.positionRepository.findOne({ + where: { + positionIsSelected: true, + posMaster: { + orgRevisionId: currentHolder?.orgRevisionId, + current_holderId: profile!.id, + }, + }, + order: { createdAt: "DESC" }, + relations: { posExecutive: true }, + }); + const operator = Object.assign(new CommandOperator(), { + profileId: profile?.id, + prefix: profile?.prefix, + firstName: profile?.firstName, + lastName: profile?.lastName, + posNo: posNo, + posType: profile?.posType?.posTypeName ?? null, + posLevel: profile?.posLevel?.posLevelName ?? null, + position: position?.positionName ?? null, + positionExecutive: position?.posExecutive?.posExecutiveName ?? null, + roleName: "เจ้าหน้าที่ดำเนินการ", + orderNo: 1, + commandId: command.id, + createdUserId: request.user.sub, + createdFullName: request.user.name, + createdAt: now, + lastUpdateUserId: request.user.sub, + lastUpdateFullName: request.user.name, + lastUpdatedAt: now, + }); + await this.commandOperatorRepository.save(operator); + } + } } const path = commandTypePath(commandCode); if (path == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทคำสั่งนี้ในระบบ"); - if (commandSysId && commandSysId.toLocaleUpperCase().trim() !== "DISCIPLINE") { + if (!["C-PM-26", "C-PM-25"].includes(commandCode)) { await new CallAPI() .PostData(request, path, { refIds: requestBody.persons.filter((x) => x.refId != null).map((x) => x.refId), @@ -3537,14 +3571,6 @@ export class CommandController extends Controller { return new HttpSuccess(); } - /** - * API สร้าง ProfileSalary ข้าราชการ + อัปเดตตำแหน่งปัจจุบัน (เปลี่ยนตำแหน่ง) - * - * Thin wrapper — เรียก ExecuteSalaryCurrentService (C-PM-03, 04, 05, 06, 07, 39, 47) - * ทั้ง endpoint นี้และ consumer ใน rabbitmq ใช้ service ตัวเดียวกัน (Linear Flow) - * - * @summary API สร้าง ProfileSalary ข้าราชการ + เปลี่ยนตำแหน่ง - */ @Post("excexute/salary-current") public async newSalaryAndUpdateCurrent( @Request() req: RequestWithUser, @@ -3561,12 +3587,8 @@ export class CommandController extends Controller { positionArea?: string | null; positionType: string | null; positionLevel: string | null; - positionTypeId?: string | null; - positionLevelId?: string | null; posmasterId: string; positionId: string; - posExecutiveId?: string | null; - positionField?: string | null; commandId?: string | null; orgRoot?: string | null; orgChild1?: string | null; @@ -3586,10 +3608,167 @@ export class CommandController extends Controller { }[]; }, ) { - await new ExecuteSalaryCurrentService().executeSalaryCurrent(body.data, { - user: { sub: req.user.sub, name: req.user.name }, - req, + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.data.find((x) => x.commandId)?.commandId ?? "" }, }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + await Promise.all( + body.data.map(async (item) => { + const profile: any = await this.profileRepository.findOneBy({ id: item.profileId }); + if (!profile) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); + } + let _null: any = null; + const dest_item = await this.salaryRepo.findOne({ + where: { profileId: item.profileId }, + order: { order: "DESC" }, + }); + const before = null; + const data = new ProfileSalary(); + + const meta = { + order: dest_item == null ? 1 : dest_item.order + 1, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + Object.assign(data, { ...item, ...meta }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + await this.salaryRepo.save(data, { data: req }); + setLogDataDiff(req, { before, after: data }); + history.commandId = item.commandId ?? _null; + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + const posMaster = await this.posMasterRepository.findOne({ + where: { id: item.posmasterId }, + relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"], + }); + if (posMaster == null) + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); + + const posMasterOld = await this.posMasterRepository.findOne({ + where: { + current_holderId: item.profileId, + orgRevisionId: posMaster.orgRevisionId, + }, + }); + if (posMasterOld != null) { + posMasterOld.current_holderId = null; + posMasterOld.lastUpdatedAt = new Date(); + } + + const positionOld = await this.positionRepository.findOne({ + where: { + posMasterId: posMasterOld?.id, + positionIsSelected: true, + }, + }); + if (positionOld != null) { + positionOld.positionIsSelected = false; + await this.positionRepository.save(positionOld); + } + + const checkPosition = await this.positionRepository.find({ + where: { + posMasterId: item.posmasterId, + positionIsSelected: true, + }, + }); + if (checkPosition.length > 0) { + const clearPosition = checkPosition.map((positions) => ({ + ...positions, + positionIsSelected: false, + })); + await this.positionRepository.save(clearPosition); + } + + posMaster.current_holderId = item.profileId; + posMaster.lastUpdatedAt = new Date(); + // posMaster.conditionReason = _null; + // posMaster.isCondition = false; + if (posMasterOld != null) { + await this.posMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryOfficer(posMasterOld.id, req); + } + await this.posMasterRepository.save(posMaster); + + const positionNew = await this.positionRepository.findOne({ + where: { + id: item.positionId, + posMasterId: item.posmasterId, + }, + relations: ["posExecutive"], + }); + // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ + if (positionNew != null) { + positionNew.positionIsSelected = true; + // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit + profile.posMasterNo = getPosMasterNo(posMaster); + profile.org = getOrgFullName(posMaster); + if (!posMaster.isSit) { + profile.posLevelId = positionNew.posLevelId; + profile.posTypeId = positionNew.posTypeId; + profile.position = positionNew.positionName; + profile.positionField = positionNew.positionField ?? null; + profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; + profile.positionArea = positionNew.positionArea ?? null; + profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; + } + profile.amount = item.amount ?? null; + profile.amountSpecial = item.amountSpecial ?? null; + await this.profileRepository.save(profile); + await this.positionRepository.save(positionNew); + } + await CreatePosMasterHistoryOfficer(posMaster.id, req); + }), + ); + return new HttpSuccess(); } @@ -3627,21 +3806,165 @@ export class CommandController extends Controller { }[]; }, ) { - await new ExecuteSalaryEmployeeCurrentService().executeSalaryEmployeeCurrent(body.data, { - user: { sub: req.user.sub, name: req.user.name }, - req, + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.data.find((x) => x.commandId)?.commandId ?? "" }, }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + await Promise.all( + body.data.map(async (item) => { + const profile: any = await this.profileEmployeeRepository.findOneBy({ id: item.profileId }); + if (!profile) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); + } + + const dest_item = await this.salaryRepo.findOne({ + where: { profileEmployeeId: item.profileId }, + order: { order: "DESC" }, + }); + const before = null; + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + order: dest_item == null ? 1 : dest_item.order + 1, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + + Object.assign(data, { + ...item, + ...meta, + profileEmployeeId: item.profileId, + profileId: undefined, + }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + + await this.salaryRepo.save(data, { data: req }); + setLogDataDiff(req, { before, after: data }); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + const posMaster = await this.employeePosMasterRepository.findOne({ + where: { id: item.posmasterId }, + relations: ["orgRoot"], + }); + if (posMaster == null) + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); + + const posMasterOld = await this.employeePosMasterRepository.findOne({ + where: { + current_holderId: item.profileId, + orgRevisionId: posMaster.orgRevisionId, + }, + }); + if (posMasterOld != null) { + posMasterOld.current_holderId = null; + posMasterOld.lastUpdatedAt = new Date(); + } + // if (posMasterOld != null) posMasterOld.next_holderId = null; + + const positionOld = await this.employeePositionRepository.findOne({ + where: { + posMasterId: posMasterOld?.id, + positionIsSelected: true, + }, + }); + if (positionOld != null) { + positionOld.positionIsSelected = false; + await this.employeePositionRepository.save(positionOld); + } + + const checkPosition = await this.employeePositionRepository.find({ + where: { + posMasterId: item.posmasterId, + positionIsSelected: true, + }, + }); + if (checkPosition.length > 0) { + const clearPosition = checkPosition.map((positions) => ({ + ...positions, + positionIsSelected: false, + })); + await this.employeePositionRepository.save(clearPosition); + } + + posMaster.current_holderId = item.profileId; + posMaster.lastUpdatedAt = new Date(); + posMaster.next_holderId = null; + if (posMasterOld != null) { + await this.employeePosMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryEmployee(posMasterOld.id, req); + } + await this.employeePosMasterRepository.save(posMaster); + const positionNew = await this.employeePositionRepository.findOne({ + where: { + id: item.positionId, + posMasterId: item.posmasterId, + }, + }); + if (positionNew != null) { + positionNew.positionIsSelected = true; + profile.posLevelId = positionNew.posLevelId; + profile.posTypeId = positionNew.posTypeId; + profile.position = positionNew.positionName; + profile.employeeOc = posMaster?.orgRoot?.orgRootName ?? null; + profile.positionEmployeePositionId = positionNew.positionName; + profile.amount = item.amount ?? null; + profile.amountSpecial = item.amountSpecial ?? null; + await this.profileEmployeeRepository.save(profile); + await this.employeePositionRepository.save(positionNew); + } + await CreatePosMasterHistoryEmployee(posMaster.id, req); + }), + ); + return new HttpSuccess(); } - /** - * API สร้าง ProfileSalary ข้าราชการ + handle leave/กลับเข้าราชการ - * - * Thin wrapper — เรียก ExecuteSalaryLeaveService (C-PM-08, 09, 17, 18, 41, 48) - * ทั้ง endpoint นี้และ consumer ใน rabbitmq ใช้ service ตัวเดียวกัน (Linear Flow) - * - * @summary API สร้าง ProfileSalary ข้าราชการ + leave/กลับเข้าราชการ - */ @Post("excexute/salary-leave") public async newSalaryAndUpdateLeave( @Request() req: RequestWithUser, @@ -3661,8 +3984,6 @@ export class CommandController extends Controller { isLeave: boolean; leaveReason?: string | null; dateLeave?: Date | null; - posExecutiveId?: string | null; - positionField?: string | null; commandId?: string | null; isGovernment?: boolean | null; orgRoot?: string | null; @@ -3680,7 +4001,6 @@ export class CommandController extends Controller { commandCode?: string | null; commandName?: string | null; remark: string | null; - positionId?: string | null; positionTypeNew?: string | null; positionLevelNew?: string | null; positionNameNew?: string | null; @@ -3698,10 +4018,365 @@ export class CommandController extends Controller { }[]; }, ) { - await new ExecuteSalaryLeaveService().executeSalaryLeave(body.data, { - user: { sub: req.user.sub, name: req.user.name }, - req, + const roleKeycloak = await this.roleKeycloakRepo.findOne({ + where: { name: Like("USER") }, }); + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + relations: ["commandType"], + where: { id: body.data.find((x) => x.commandId)?.commandId ?? "" }, + }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + const today = new Date().setHours(0, 0, 0, 0); + await Promise.all( + body.data.map(async (item) => { + const profile = await this.profileRepository.findOne({ + where: { id: item.profileId }, + // relations: ["roleKeycloaks"], + relations: { + roleKeycloaks: true, + posType: true, + posLevel: true, + }, + }); + if (!profile) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); + } + //ลบตำแหน่งที่รักษาการแทน + const code = _command?.commandType?.code; + if (code && ["C-PM-08", "C-PM-17", "C-PM-18"].includes(code)) { + removePostMasterAct(profile.id); + } + //ออกคำสั่งยกเลิกลาออก ลบเฉพาะคนที่ขอยกเลิกลาออก + else if (item.resignId && code && ["C-PM-41"].includes(code)) { + const commandResign = await this.commandReciveRepository.findOne({ + where: { refId: item.resignId }, + relations: { command: true }, + }); + const executeDate = commandResign + ? new Date(commandResign.command.commandExcecuteDate).setHours(0, 0, 0, 0) + : today; + if ( + commandResign && + _command.status !== "REPORTED" && + (_command.status !== "WAITING" || today < executeDate) + ) { + await reOrderCommandRecivesAndDelete(commandResign!.id); + } + } + let _commandYear = item.commandYear; + if (item.commandYear) { + _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; + } + const returnWork = await checkReturnCommandType(String(item.commandId)); + const dest_item = await this.salaryRepo.findOne({ + where: { profileId: item.profileId }, + order: { order: "DESC" }, + }); + const before = null; + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + data.dateGovernment = item.commandDateAffect ?? new Date(); + data.order = dest_item == null ? 1 : dest_item.order + 1; + const meta = { + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + if (!returnWork) { + Object.assign(data, { ...item, ...meta }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + await this.salaryRepo.save(data, { data: req }); + setLogDataDiff(req, { before, after: data }); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history, { data: req }); + } + const _null: any = null; + profile.isLeave = item.isLeave; + profile.leaveReason = item.leaveReason ?? _null; + profile.dateLeave = item.dateLeave ?? _null; + profile.lastUpdateUserId = req.user.sub; + profile.lastUpdateFullName = req.user.name; + profile.lastUpdatedAt = new Date(); + const clearProfile = await checkCommandType(String(item.commandId)); + + //ปั๊มประวัติก่อนลบตำแหน่ง + const curRevision = await this.orgRevisionRepo.findOne({ + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, + }); + let orgRootRef = null; + let orgChild1Ref = null; + let orgChild2Ref = null; + let orgChild3Ref = null; + let orgChild4Ref = null; + if (curRevision) { + const curPosMaster = await this.posMasterRepository.findOne({ + where: { + current_holderId: profile.id, + orgRevisionId: curRevision.id, + }, + relations: { + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + orgRootRef = curPosMaster?.orgRoot ?? null; + orgChild1Ref = curPosMaster?.orgChild1 ?? null; + orgChild2Ref = curPosMaster?.orgChild2 ?? null; + orgChild3Ref = curPosMaster?.orgChild3 ?? null; + orgChild4Ref = curPosMaster?.orgChild4 ?? null; + if (curPosMaster && clearProfile.LeaveType != "RETIRE_OUT_EMP") { + await CreatePosMasterHistoryOfficer(curPosMaster.id, req, "DELETE"); + } + } + + //ลบตำแหน่ง + if (item.isLeave == true) { + await removeProfileInOrganize(profile.id, "OFFICER"); + } + if (clearProfile.status) { + if (profile.keycloak != null && profile.keycloak != "" && profile.isDelete === false) { + const delUserKeycloak = await deleteUser(profile.keycloak); + if (delUserKeycloak) { + // Task #228 + // profile.keycloak = _null; + profile.roleKeycloaks = []; + profile.isActive = false; + profile.isDelete = true; + } + } + profile.leaveCommandId = item.commandId ?? _null; + profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; + profile.leaveRemark = clearProfile.leaveRemark ?? _null; + profile.leaveDate = item.commandDateAffect ?? _null; + profile.leaveType = clearProfile.LeaveType ?? _null; + //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) + // profile.position = _null; + // profile.posTypeId = _null; + // profile.posLevelId = _null; + } + + if (item.isGovernment == true) { + if (returnWork) { + //ปลดตำแหน่งเดิมที่ไม่ถูกปลดออกจากกิ่งครั้งเมื่อออกคำสั่งพักราชการหรือออกราชการไว้ + await removeProfileInOrganize(profile.id, "OFFICER"); + //ปั๊มตำแหน่งใหม่ + const posMaster = await this.posMasterRepository.findOne({ + where: { id: item.posmasterId?.toString() }, + }); + + if (posMaster) { + const checkPosition = await this.positionRepository.find({ + where: { + posMasterId: posMaster.id, + positionIsSelected: true, + }, + }); + if (checkPosition.length > 0) { + const clearPosition = checkPosition.map((positions) => ({ + ...positions, + positionIsSelected: false, + })); + await this.positionRepository.save(clearPosition); + } + posMaster.current_holderId = profile.id; + posMaster.lastUpdatedAt = new Date(); + // posMaster.conditionReason = _null; + // posMaster.isCondition = false; + await this.posMasterRepository.save(posMaster); + const positionNew = await this.positionRepository.findOne({ + where: { + posMasterId: posMaster.id, + }, + }); + if (positionNew) { + positionNew.positionIsSelected = true; + await this.positionRepository.save(positionNew, { data: req }); + } + await CreatePosMasterHistoryOfficer(posMaster.id, req); + } + const newMapProfileSalary = { + profileId: profile.id, + commandId: item.commandId, + positionName: item.positionNameNew ?? null, + positionType: item.posTypeNameNew ?? null, + positionLevel: item.posLevelNameNew ?? null, + amount: item.amount ? item.amount : null, + positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, + amountSpecial: item.amountSpecial ? item.amountSpecial : null, + mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, + posNo: item.posNoNew, + posNoAbb: item.posNoAbbNew, + orgRoot: item.orgRootNew, + orgChild1: item.orgChild1New, + orgChild2: item.orgChild2New, + orgChild3: item.orgChild3New, + orgChild4: item.orgChild4New, + isGovernment: item.isGovernment, + commandNo: item.commandNo, + commandYear: item.commandYear, + commandDateAffect: item.commandDateAffect, + commandDateSign: item.commandDateSign, + commandCode: item.commandCode, + commandName: item.commandName, + remark: item.remark, + }; + Object.assign(data, { ...newMapProfileSalary, ...meta }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + await this.salaryRepo.save(data); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history); + profile.leaveReason = _null; + profile.leaveCommandId = _null; + profile.leaveCommandNo = _null; + profile.leaveRemark = _null; + profile.leaveDate = _null; + profile.leaveType = _null; + profile.position = item.positionNameNew ?? _null; + profile.posTypeId = item.positionTypeNew ?? _null; + profile.posLevelId = item.positionLevelNew ?? _null; + } + let userKeycloakId; + const checkUser = await getUserByUsername(profile.citizenId); + //ถ้ายังไม่มี user keycloak ให้สร้างใหม่ + if (checkUser.length == 0) { + let password = profile.citizenId; + if (profile.birthDate != null) { + // const gregorianYear = profile.birthDate.getFullYear() + 543; + + // const formattedDate = + // profile.birthDate.toISOString().slice(8, 10) + + // profile.birthDate.toISOString().slice(5, 7) + + // gregorianYear; + // password = formattedDate; + const _date = new Date(profile.birthDate.toDateString()) + .getDate() + .toString() + .padStart(2, "0"); + const _month = (new Date(profile.birthDate.toDateString()).getMonth() + 1) + .toString() + .padStart(2, "0"); + const _year = new Date(profile.birthDate.toDateString()).getFullYear() + 543; + password = `${_date}${_month}${_year}`; + } + userKeycloakId = await createUser(profile.citizenId, password, { + firstName: profile.firstName, + lastName: profile.lastName, + }); + const list = await getRoles(); + let result = false; + if (Array.isArray(list) && userKeycloakId) { + result = await addUserRoles( + userKeycloakId, + list + .filter((v) => v.name === "USER") + .map((x) => ({ + id: x.id, + name: x.name, + })), + ); + } + profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; + profile.keycloak = + userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; + } + //ถ้ามีอยู่แล้วให้ใช้อันเดิม + else { + const rolesData = await getRoleMappings(checkUser[0].id); + if (rolesData) { + const _roleKeycloak = await this.roleKeycloakRepo.find({ + where: { name: In(rolesData.map((x: any) => x.name)) }, + }); + profile.roleKeycloaks = + _roleKeycloak && _roleKeycloak.length > 0 ? _roleKeycloak : []; + } + profile.keycloak = checkUser[0].id; + } + profile.amount = item.amount ?? _null; + profile.amountSpecial = item.amountSpecial ?? _null; + profile.isActive = true; + profile.isDelete = false; + } + await this.profileRepository.save(profile); + + // if (profile.id) { + // await this.keycloakAttributeService.clearOrgDnaAttributes( + // [profile.id], + // "PROFILE", + // ); + // } + + // update user attribute in keycloak + await updateUserAttributes(profile.keycloak ?? "", { + profileId: [profile.id], + prefix: [profile.prefix || ""], + }); + + // Task #2190 + if (code && ["C-PM-17", "C-PM-18"].includes(code)) { + let organizeName = ""; + if (orgRootRef) { + const names = [ + orgChild4Ref?.orgChild4Name, + orgChild3Ref?.orgChild3Name, + orgChild2Ref?.orgChild2Name, + orgChild1Ref?.orgChild1Name, + orgRootRef?.orgRootName, + ].filter(Boolean); + organizeName = names.join(" "); + } + + } + }), + ); + return new HttpSuccess(); } @@ -3745,10 +4420,212 @@ export class CommandController extends Controller { }[]; }, ) { - await new ExecuteSalaryEmployeeLeaveService().executeSalaryEmployeeLeave(body.data, { - user: { sub: req.user.sub, name: req.user.name }, - req, + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.data.find((x) => x.commandId)?.commandId ?? "" }, + relations: { commandType: true }, }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + const today = new Date().setHours(0, 0, 0, 0); + await Promise.all( + body.data.map(async (item) => { + const profile = await this.profileEmployeeRepository.findOne({ + where: { id: item.profileId }, + // relations: ["roleKeycloaks"], + relations: { + roleKeycloaks: true, + posType: true, + posLevel: true, + }, + }); + if (!profile) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); + } + const code = _command?.commandType?.code; + //ออกคำสั่งยกเลิกลาออก ลบเฉพาะคนที่ขอยกเลิกลาออก + if (item.resignId && code && ["C-PM-42"].includes(code)) { + const commandResign = await this.commandReciveRepository.findOne({ + where: { refId: item.resignId }, + relations: { command: true }, + }); + const executeDate = commandResign + ? new Date(commandResign.command.commandExcecuteDate).setHours(0, 0, 0, 0) + : today; + if ( + commandResign && + _command.status !== "REPORTED" && + (_command.status !== "WAITING" || today < executeDate) + ) { + await reOrderCommandRecivesAndDelete(commandResign!.id); + } + } + let _commandYear = item.commandYear; + if (item.commandYear) { + _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; + } + const dest_item = await this.salaryRepo.findOne({ + where: { profileEmployeeId: item.profileId }, + order: { order: "DESC" }, + }); + const before = null; + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + order: dest_item == null ? 1 : dest_item.order + 1, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + + Object.assign(data, { + ...item, + ...meta, + profileEmployeeId: item.profileId, + profileId: undefined, + }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + data.dateGovernment = item.commandDateAffect ?? meta.createdAt; + await this.salaryRepo.save(data, { data: req }); + setLogDataDiff(req, { before, after: data }); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + const _null: any = null; + profile.isLeave = item.isLeave; + profile.leaveReason = item.leaveReason ?? _null; + profile.dateLeave = item.dateLeave ?? _null; + profile.lastUpdateUserId = req.user.sub; + profile.lastUpdateFullName = req.user.name; + profile.lastUpdatedAt = new Date(); + // บันทึกประวัติก่อนลบตำแหน่ง + const clearProfile = await checkCommandType(String(item.commandId)); + const curRevision = await this.orgRevisionRepo.findOne({ + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, + }); + let orgRootRef = null; + let orgChild1Ref = null; + let orgChild2Ref = null; + let orgChild3Ref = null; + let orgChild4Ref = null; + if (curRevision) { + const curPosMaster = await this.employeePosMasterRepository.findOne({ + where: { + current_holderId: profile.id, + orgRevisionId: curRevision.id, + }, + relations: { + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + orgRootRef = curPosMaster?.orgRoot ?? null; + orgChild1Ref = curPosMaster?.orgChild1 ?? null; + orgChild2Ref = curPosMaster?.orgChild2 ?? null; + orgChild3Ref = curPosMaster?.orgChild3 ?? null; + orgChild4Ref = curPosMaster?.orgChild4 ?? null; + if (curPosMaster) { + await CreatePosMasterHistoryEmployee(curPosMaster.id, req, "DELETE"); + } + } + + // ลบตำแหน่ง + if (item.isLeave == true) { + await removeProfileInOrganize(profile.id, "EMPLOYEE"); + } + + if (clearProfile.status) { + if (profile.keycloak != null && profile.keycloak != "" && profile.isDelete === false) { + const delUserKeycloak = await deleteUser(profile.keycloak); + if (delUserKeycloak) { + // Task #228 + // profile.keycloak = _null; + profile.roleKeycloaks = []; + profile.isActive = false; + profile.isDelete = true; + } + } + profile.leaveCommandId = item.commandId ?? _null; + profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; + profile.leaveRemark = clearProfile.leaveRemark ?? _null; + profile.leaveDate = item.commandDateAffect ?? _null; + profile.leaveType = clearProfile.LeaveType ?? _null; + //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) + // profile.position = _null; + // profile.posTypeId = _null; + // profile.posLevelId = _null; + } + await this.profileEmployeeRepository.save(profile); + + // if (profile.id) { + // await this.keycloakAttributeService.clearOrgDnaAttributes( + // [profile.id], + // "PROFILE_EMPLOYEE", + // ); + // } + + // Task #2190 + if (code && ["C-PM-23", "C-PM-43"].includes(code)) { + let organizeName = ""; + if (orgRootRef) { + const names = [ + orgChild4Ref?.orgChild4Name, + orgChild3Ref?.orgChild3Name, + orgChild2Ref?.orgChild2Name, + orgChild1Ref?.orgChild1Name, + orgRootRef?.orgRootName, + ].filter(Boolean); + organizeName = names.join(" "); + } + } + }), + ); + return new HttpSuccess(); } @@ -3794,10 +4671,221 @@ export class CommandController extends Controller { }[]; }, ) { - await new ExecuteSalaryService().executeSalary(body.data, { - user: { sub: req.user.sub, name: req.user.name }, - req, + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + relations: ["commandType"], + where: { id: body.data.find((x) => x.commandId)?.commandId ?? "" }, }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + await Promise.all( + body.data.map(async (item) => { + const profile: any = await this.profileRepository.findOne({ + where: { id: item.profileId }, + // relations: ["roleKeycloaks"], + relations: { + roleKeycloaks: true, + posType: true, + posLevel: true, + }, + }); + if (!profile) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); + } + const posMaster: any = await this.posMasterRepository.findOne({ + where: { + current_holderId: item.profileId, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: { + orgRevision: true, + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + + const orgRevisionRef = posMaster ? posMaster.id : null; + const orgRootRef = orgRevisionRef?.orgRoot ?? null; + const orgChild1Ref = orgRevisionRef?.orgChild1 ?? null; + const orgChild2Ref = orgRevisionRef?.orgChild2 ?? null; + const orgChild3Ref = orgRevisionRef?.orgChild3 ?? null; + const orgChild4Ref = orgRevisionRef?.orgChild4 ?? null; + + //ลบตำแหน่งที่รักษาการแทน + const code = _command?.commandType?.code; + if (code && ["C-PM-13"].includes(code)) { + removePostMasterAct(profile.id); + } + + let _commandYear = item.commandYear; + if (item.commandYear) { + _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; + } + const dest_item = await this.salaryRepo.findOne({ + where: { profileId: item.profileId }, + order: { order: "DESC" }, + }); + const before = null; + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + order: dest_item == null ? 1 : dest_item.order + 1, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + if (item.isLeave != undefined && item.isLeave == true) { + await CreatePosMasterHistoryOfficer(orgRevisionRef, req, "DELETE"); + await removeProfileInOrganize(profile.id, "OFFICER"); + } + const clearProfile = await checkCommandType(String(item.commandId)); + const _null: any = null; + if (clearProfile.status) { + if (profile.keycloak != null && profile.keycloak != "" && profile.isDelete === false) { + const delUserKeycloak = await deleteUser(profile.keycloak); + if (delUserKeycloak) { + // Task #228 + // profile.keycloak = _null; + profile.roleKeycloaks = []; + profile.isActive = false; + profile.isDelete = true; + } + } + profile.isLeave = item.isLeave; + profile.leaveCommandId = item.commandId ?? _null; + profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; + profile.leaveRemark = clearProfile.leaveRemark ?? _null; + profile.leaveDate = item.commandDateAffect ?? _null; + profile.leaveType = clearProfile.LeaveType ?? _null; + //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) + // profile.position = _null; + // profile.posTypeId = _null; + // profile.posLevelId = _null; + profile.leaveReason = item.leaveReason ?? _null; + profile.dateLeave = item.dateLeave ?? _null; + profile.amount = item.amount ?? _null; + profile.amountSpecial = item.amountSpecial ?? _null; + await this.profileRepository.save(profile, { data: req }); + + // if (profile.id) { + // await this.keycloakAttributeService.clearOrgDnaAttributes( + // [profile.id], + // "PROFILE", + // ); + // } + } + Object.assign(data, { ...item, ...meta }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + + await this.salaryRepo.save(data, { data: req }); + setLogDataDiff(req, { before, after: data }); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + if (_command) { + /* + const command = await this.commandRepository.findOne({ + where: { id: item.commandId }, + relations: ["commandType"], + }); + */ + if (["C-PM-15", "C-PM-16"].includes(_command.commandType.code)) { + // ประวัติคำสั่งให้ช่วยราชการ + const dataAssis = new ProfileAssistance(); + + const metaAssis = { + profileId: item.profileId, + agency: item.officerOrg, + dateStart: item.dateStart, + dateEnd: item.dateEnd, + commandNo: `${item.commandNo}/${_commandYear}`, + commandName: item.commandName, + refId: item.refId, + refCommandDate: new Date(), + commandId: item.commandId, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + status: _command.commandType.code == "C-PM-15" ? "PENDING" : "DONE", + }; + + Object.assign(dataAssis, metaAssis); + const historyAssis = new ProfileAssistanceHistory(); + Object.assign(historyAssis, { ...dataAssis, id: undefined }); + + await this.assistanceRepository.save(dataAssis); + historyAssis.profileAssistanceId = dataAssis.id; + await this.assistanceHistoryRepository.save(historyAssis); + } + // Task #2190 + else if (_command.commandType.code == "C-PM-13") { + let organizeName = ""; + if (orgRootRef) { + const names = [ + orgChild4Ref?.orgChild4Name, + orgChild3Ref?.orgChild3Name, + orgChild2Ref?.orgChild2Name, + orgChild1Ref?.orgChild1Name, + orgRootRef?.orgRootName, + ].filter(Boolean); + organizeName = names.join(" "); + } + } + } + }), + ); + return new HttpSuccess(); } @@ -3919,14 +5007,6 @@ export class CommandController extends Controller { return new HttpSuccess(); } - /** - * API สร้าง ProfileSalary + ProfileDiscipline + handle leave ของคำสั่งวินัย - * - * Thin wrapper — เรียก ExecuteSalaryLeaveDisciplineService (C-PM-19, 20, 25, 26, 27, 28, 29, 30, 31, 32) - * ทั้ง endpoint นี้และ consumer ใน rabbitmq ใช้ service ตัวเดียวกัน (Linear Flow) - * - * @summary API คำสั่งวินัย (ข้าราชการ/ลูกจ้าง) + leave - */ @Post("excexute/salary-leave-discipline") public async newSalaryAndUpdateLeaveDiscipline( @Request() req: RequestWithUser, @@ -3964,10 +5044,491 @@ export class CommandController extends Controller { }[]; }, ) { - await new ExecuteSalaryLeaveDisciplineService().executeSalaryLeaveDiscipline(body.data, { - user: { sub: req.user.sub, name: req.user.name }, - req, + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + relations: ["commandType"], + where: { id: body.data.find((x) => x.commandId)?.commandId ?? "" }, }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + await Promise.all( + body.data.map(async (item) => { + let _commandYear = item.commandYear; + if (item.commandYear) { + _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; + } + const orgRevision = await this.orgRevisionRepo.findOne({ + where: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }); + let orgRootRef = null; + let orgChild1Ref = null; + let orgChild2Ref = null; + let orgChild3Ref = null; + let orgChild4Ref = null; + let profile; + let isEmployee: boolean = false; + let retireTypeName: string = ""; + // ขรก. + if (item.profileType && item.profileType.trim().toUpperCase() == "OFFICER") { + profile = await this.profileRepository.findOne({ + relations: [ + // "profileSalary", + "posLevel", + "posType", + "current_holders", + "current_holders.orgRoot", + "current_holders.orgChild1", + "current_holders.orgChild2", + "current_holders.orgChild3", + "current_holders.orgChild4", + "current_holders.positions", + "current_holders.positions.posExecutive", + "roleKeycloaks", + ], + where: { id: item.profileId }, + // order: { + // profileSalary: { + // order: "DESC", + // }, + // }, + }); + if (!profile) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); + } + const lastSalary = await this.salaryRepo.findOne({ + where: { profileId: item.profileId }, + select: ["order"], + order: { order: "DESC" }, + }); + const nextOrder = lastSalary ? lastSalary.order + 1 : 1; + //ลบตำแหน่งที่รักษาการแทน + const code = _command?.commandType?.code; + if (code && ["C-PM-19", "C-PM-20"].includes(code)) { + removePostMasterAct(profile.id); + } + + const orgRevisionRef = + profile?.current_holders?.find((x) => x.orgRevisionId == orgRevision?.id) ?? null; + orgRootRef = orgRevisionRef?.orgRoot ?? null; + orgChild1Ref = orgRevisionRef?.orgChild1 ?? null; + orgChild2Ref = orgRevisionRef?.orgChild2 ?? null; + orgChild3Ref = orgRevisionRef?.orgChild3 ?? null; + orgChild4Ref = orgRevisionRef?.orgChild4 ?? null; + + let position = + profile.current_holders + .filter((x) => x.orgRevisionId == orgRevision?.id)[0] + ?.positions?.filter((pos) => pos.positionIsSelected === true)[0] ?? null; + // ประวัติตำแหน่ง + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + profileId: profile.id, + commandId: item.commandId, + position: profile.position, + positionName: profile.position, + positionType: profile?.posType?.posTypeName ?? null, + positionLevel: profile?.posLevel?.posLevelName ?? null, + positionExecutive: position?.posExecutive?.posExecutiveName ?? null, + amount: item.amount ? item.amount : null, + positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, + amountSpecial: item.amountSpecial ? item.amountSpecial : null, + mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, + // order: + // profile.profileSalary.length >= 0 + // ? profile.profileSalary.length > 0 + // ? profile.profileSalary[0].order + 1 + // : 1 + // : null, + order: nextOrder, + orgRoot: item.orgRoot, + orgChild1: item.orgChild1, + orgChild2: item.orgChild2, + orgChild3: item.orgChild3, + orgChild4: item.orgChild4, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + dateGovernment: item.commandDateAffect ?? new Date(), + isGovernment: item.isGovernment, + commandNo: item.commandNo, + commandYear: item.commandYear, + posNo: item.posNo, + posNoAbb: item.posNoAbb, + commandDateAffect: item.commandDateAffect, + commandDateSign: item.commandDateSign, + commandCode: item.commandCode, + commandName: item.commandName, + remark: item.remark, + }; + + Object.assign(data, meta); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + + await this.salaryRepo.save(data); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history); + + // ประวัติวินัย + const dataDis = new ProfileDiscipline(); + + const metaDis = { + date: item.commandDateAffect, + refCommandDate: item.commandDateSign, + refCommandNo: `${item.commandNo}/${item.commandYear}`, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + + Object.assign(dataDis, { ...item, ...metaDis }); + const historyDis = new ProfileDisciplineHistory(); + Object.assign(historyDis, { ...dataDis, id: undefined }); + + await this.disciplineRepository.save(dataDis); + historyDis.profileDisciplineId = dataDis.id; + await this.disciplineHistoryRepository.save(historyDis); + + // ทะเบียนประวัติ + if (item.isLeave != null) { + const _profile = await this.profileRepository.findOne({ + where: { id: item.profileId }, + relations: ["roleKeycloaks"], + }); + if (!_profile) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); + } + const _null: any = null; + _profile.isLeave = item.isLeave; + _profile.leaveReason = item.leaveReason ?? _null; + _profile.dateLeave = item.dateLeave ?? _null; + _profile.lastUpdateUserId = req.user.sub; + _profile.lastUpdateFullName = req.user.name; + _profile.lastUpdatedAt = new Date(); + if (item.isLeave == true) { + const exceptClear = await checkExceptCommandType(String(item.commandId)); + if (exceptClear.status) { + _profile.leaveReason = item.leaveReason ?? _null; + _profile.leaveCommandId = item.commandId ?? _null; + _profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; + _profile.leaveRemark = exceptClear.leaveRemark ?? _null; + _profile.leaveDate = item.commandDateAffect ?? _null; + _profile.leaveType = exceptClear.LeaveType ?? _null; + } else { + if (orgRevisionRef) { + await CreatePosMasterHistoryOfficer(orgRevisionRef.id, req, "DELETE"); + } + await removeProfileInOrganize(_profile.id, "OFFICER"); + } + } + const clearProfile = await checkCommandType(String(item.commandId)); + if (clearProfile.status) { + retireTypeName = clearProfile.retireTypeName ?? ""; + if ( + _profile.keycloak != null && + _profile.keycloak != "" && + _profile.isDelete === false + ) { + const delUserKeycloak = await deleteUser(_profile.keycloak); + if (delUserKeycloak) { + // Task #228 + // _profile.keycloak = _null; + _profile.roleKeycloaks = []; + _profile.isActive = false; + _profile.isDelete = true; + } + } + _profile.leaveCommandId = item.commandId ?? _null; + _profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; + _profile.leaveRemark = clearProfile.leaveRemark ?? _null; + _profile.leaveDate = item.commandDateAffect ?? _null; + _profile.leaveType = clearProfile.LeaveType ?? _null; + //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) + // _profile.position = _null; + // _profile.posTypeId = _null; + // _profile.posLevelId = _null; + } + await this.profileRepository.save(_profile); + // if (_profile.id) { + // await this.keycloakAttributeService.clearOrgDnaAttributes( + // [_profile.id], + // "PROFILE", + // ); + // } + } + } + // ลูกจ้าง + else { + isEmployee = true; + profile = await this.profileEmployeeRepository.findOne({ + relations: [ + // "profileSalary", + "posLevel", + "posType", + "current_holders", + "current_holders.orgRoot", + "current_holders.orgChild1", + "current_holders.orgChild2", + "current_holders.orgChild3", + "current_holders.orgChild4", + "roleKeycloaks", + ], + where: { id: item.profileId }, + // order: { + // profileSalary: { + // order: "DESC", + // }, + // }, + }); + if (!profile) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); + } + const lastSalary = await this.salaryRepo.findOne({ + where: { profileEmployeeId: item.profileId }, + select: ["order"], + order: { order: "DESC" }, + }); + const nextOrder = lastSalary ? lastSalary.order + 1 : 1; + const orgRevisionRef = + profile?.current_holders?.find((x) => x.orgRevisionId == orgRevision?.id) ?? null; + orgRootRef = orgRevisionRef?.orgRoot ?? null; + orgChild1Ref = orgRevisionRef?.orgChild1 ?? null; + orgChild2Ref = orgRevisionRef?.orgChild2 ?? null; + orgChild3Ref = orgRevisionRef?.orgChild3 ?? null; + orgChild4Ref = orgRevisionRef?.orgChild4 ?? null; + + // ประวัติตำแหน่ง + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + profileEmployeeId: profile.id, + commandId: item.commandId, + position: profile.position, + positionName: profile.position, + positionType: profile?.posType?.posTypeName ?? null, + positionLevel: + profile?.posType && profile?.posLevel + ? `${profile?.posType?.posTypeShortName} ${profile?.posLevel?.posLevelName}` + : null, + amount: item.amount ? item.amount : null, + positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, + mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, + // order: + // profile.profileSalary.length >= 0 + // ? profile.profileSalary.length > 0 + // ? profile.profileSalary[0].order + 1 + // : 1 + // : null, + order: nextOrder, + orgRoot: item.orgRoot, + orgChild1: item.orgChild1, + orgChild2: item.orgChild2, + orgChild3: item.orgChild3, + orgChild4: item.orgChild4, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + dateGovernment: item.commandDateAffect ?? new Date(), + isGovernment: item.isGovernment, + commandNo: item.commandNo, + commandYear: item.commandYear, + posNo: item.posNo, + posNoAbb: item.posNoAbb, + commandDateAffect: item.commandDateAffect, + commandDateSign: item.commandDateSign, + commandCode: item.commandCode, + commandName: item.commandName, + remark: item.remark, + }; + + Object.assign(data, meta); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + + await this.salaryRepo.save(data); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history); + + // ประวัติวินัย + const dataDis = new ProfileDiscipline(); + + const metaDis = { + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + + Object.assign(dataDis, { + ...item, + ...metaDis, + date: item.commandDateAffect, + refCommandDate: item.commandDateSign, + refCommandNo: item.commandNo, + profileEmployeeId: item.profileId, + profileId: undefined, + }); + const historyDis = new ProfileDisciplineHistory(); + Object.assign(historyDis, { ...dataDis, id: undefined }); + + await this.disciplineRepository.save(dataDis); + historyDis.profileDisciplineId = dataDis.id; + await this.disciplineHistoryRepository.save(historyDis); + + // ทะเบียนประวัติ + if (item.isLeave != null) { + const _profile = await this.profileEmployeeRepository.findOne({ + where: { id: item.profileId }, + relations: ["roleKeycloaks"], + }); + if (!_profile) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); + } + const _null: any = null; + _profile.isLeave = item.isLeave; + _profile.leaveReason = item.leaveReason ?? _null; + _profile.dateLeave = item.dateLeave ?? _null; + _profile.lastUpdateUserId = req.user.sub; + _profile.lastUpdateFullName = req.user.name; + _profile.lastUpdatedAt = new Date(); + if (item.isLeave == true) { + const exceptClear = await checkExceptCommandType(String(item.commandId)); + if (exceptClear.status) { + _profile.leaveReason = item.leaveReason ?? _null; + _profile.leaveCommandId = item.commandId ?? _null; + _profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; + _profile.leaveRemark = exceptClear.leaveRemark ?? _null; + _profile.leaveDate = item.commandDateAffect ?? _null; + _profile.leaveType = exceptClear.LeaveType ?? _null; + } else { + // บันทึกประวัติก่อนลบตำแหน่ง + const curRevision = await this.orgRevisionRepo.findOne({ + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, + }); + if (curRevision) { + const curPosMaster = await this.employeePosMasterRepository.findOne({ + where: { + current_holderId: _profile.id, + orgRevisionId: curRevision.id, + }, + }); + if (curPosMaster) { + await CreatePosMasterHistoryEmployee(curPosMaster.id, req, "DELETE"); + } + } + await removeProfileInOrganize(_profile.id, "EMPLOYEE"); + } + } + const clearProfile = await checkCommandType(String(item.commandId)); + if (clearProfile.status) { + retireTypeName = clearProfile.retireTypeName ?? ""; + if ( + _profile.keycloak != null && + _profile.keycloak != "" && + _profile.isDelete === false + ) { + const delUserKeycloak = await deleteUser(_profile.keycloak); + if (delUserKeycloak) { + // Task #228 + // _profile.keycloak = _null; + _profile.roleKeycloaks = []; + _profile.isActive = false; + _profile.isDelete = true; + } + } + _profile.leaveCommandId = item.commandId ?? _null; + _profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; + _profile.leaveRemark = clearProfile.leaveRemark ?? _null; + _profile.leaveDate = item.commandDateAffect ?? _null; + _profile.leaveType = clearProfile.LeaveType ?? _null; + //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) + // _profile.position = _null; + // _profile.posTypeId = _null; + // _profile.posLevelId = _null; + } + await this.profileEmployeeRepository.save(_profile); + // if (_profile.id) { + // await this.keycloakAttributeService.clearOrgDnaAttributes( + // [_profile.id], + // "PROFILE_EMPLOYEE", + // ); + // } + } + } + // Task #2190 + if (_command && ["C-PM-19", "C-PM-20"].includes(_command.commandType.code)) { + let organizeName = ""; + if (orgRootRef) { + const names = [ + orgChild4Ref?.orgChild4Name, + orgChild3Ref?.orgChild3Name, + orgChild2Ref?.orgChild2Name, + orgChild1Ref?.orgChild1Name, + orgRootRef?.orgRootName, + ].filter(Boolean); + organizeName = names.join(" "); + } + let _posLevelName: string = !isEmployee + ? `${profile.posLevel?.posLevelName}` + : `${profile.posType?.posTypeName} ${profile.posLevel?.posLevelName}`; + } + }), + ); + return new HttpSuccess(); } @@ -4203,7 +5764,6 @@ export class CommandController extends Controller { date: item.commandDateAffect, refCommandDate: item.commandDateSign, refCommandNo: `${item.commandNo}/${item.commandYear}`, - refCommandId: item.commandId, profileEmployeeId: item.profileId, profileId: undefined, }); @@ -4231,22 +5791,32 @@ export class CommandController extends Controller { _profile.lastUpdateFullName = req.user.name; _profile.lastUpdatedAt = new Date(); if (item.isLeave == true) { - // บันทึกประวัติก่อนลบตำแหน่ง - const curRevision = await this.orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - if (curRevision) { - const curPosMaster = await this.employeePosMasterRepository.findOne({ - where: { - current_holderId: _profile.id, - orgRevisionId: curRevision.id, - }, + const exceptClear = await checkExceptCommandType(String(item.commandId)); + if (exceptClear.status) { + _profile.leaveReason = item.leaveReason ?? _null; + _profile.leaveCommandId = item.commandId ?? _null; + _profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; + _profile.leaveRemark = exceptClear.leaveRemark ?? _null; + _profile.leaveDate = item.commandDateAffect ?? _null; + _profile.leaveType = exceptClear.LeaveType ?? _null; + } else { + // บันทึกประวัติก่อนลบตำแหน่ง + const curRevision = await this.orgRevisionRepo.findOne({ + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, }); - if (curPosMaster) { - await CreatePosMasterHistoryEmployee(curPosMaster.id, req, "DELETE"); + if (curRevision) { + const curPosMaster = await this.employeePosMasterRepository.findOne({ + where: { + current_holderId: _profile.id, + orgRevisionId: curRevision.id, + }, + }); + if (curPosMaster) { + await CreatePosMasterHistoryEmployee(curPosMaster.id, req, "DELETE"); + } } + await removeProfileInOrganize(_profile.id, "EMPLOYEE"); } - await removeProfileInOrganize(_profile.id, "EMPLOYEE"); } const clearProfile = await checkCommandType(String(item.commandId)); if (clearProfile.status) { @@ -4314,10 +5884,170 @@ export class CommandController extends Controller { }[]; }, ) { - await new ExecuteSalaryProbationService().executeProbationPass(body.data, { - user: { sub: req.user.sub, name: req.user.name }, - req, + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + let commandType: any = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.data.find((x) => x.commandId)?.commandId ?? "" }, }); + if (_command) { + commandType = await this.commandTypeRepository.findOne({ + select: { code: true }, + where: { id: _command.commandTypeId }, + }); + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + // const leaveType = await this.leaveType.findOne({ + // select: { id: true, limit: true, code: true }, + // where: { code: "LV-005" } + // }); + await Promise.all( + body.data.map(async (item) => { + const profile = await this.profileRepository.findOne({ + relations: [ + "posType", + "posLevel", + "current_holders", + "current_holders.orgRoot", + "current_holders.orgChild1", + "current_holders.orgChild2", + "current_holders.orgChild3", + "current_holders.orgChild4", + ], + where: { id: item.profileId }, + }); + if (!profile) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); + } + const lastSalary = await this.salaryRepo.findOne({ + where: { profileId: item.profileId }, + select: ["order"], + order: { order: "DESC" }, + }); + const nextOrder = lastSalary ? lastSalary.order + 1 : 1; + const orgRevision = await this.orgRevisionRepo.findOne({ + where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, + }); + + const orgRevisionRef = + profile?.current_holders?.find((x) => x.orgRevisionId == orgRevision?.id) ?? null; + const shortName = + orgRevisionRef?.orgChild4?.orgChild4ShortName ?? + orgRevisionRef?.orgChild3?.orgChild3ShortName ?? + orgRevisionRef?.orgChild2?.orgChild2ShortName ?? + orgRevisionRef?.orgChild1?.orgChild1ShortName ?? + orgRevisionRef?.orgRoot?.orgRootShortName ?? + null; + const posNo = orgRevisionRef?.posMasterNo?.toString() ?? null; + let position = + profile.current_holders + .filter((x) => x.orgRevisionId == orgRevision?.id)[0] + ?.positions?.filter((pos) => pos.positionIsSelected === true)[0] ?? null; + // ประวัติตำแหน่ง + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + profileId: item.profileId, + commandId: item.commandId, + positionName: profile.position, + positionType: profile?.posType?.posTypeName ?? null, + positionLevel: profile?.posLevel?.posLevelName ?? null, + positionExecutive: position?.posExecutive?.posExecutiveName ?? null, + amount: item.amount ? item.amount : null, + amountSpecial: item.amountSpecial ? item.amountSpecial : null, + positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, + mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, + order: nextOrder, + orgRoot: orgRevisionRef?.orgRoot?.orgRootName ?? null, + orgChild1: orgRevisionRef?.orgChild1?.orgChild1Name ?? null, + orgChild2: orgRevisionRef?.orgChild2?.orgChild2Name ?? null, + orgChild3: orgRevisionRef?.orgChild3?.orgChild3Name ?? null, + orgChild4: orgRevisionRef?.orgChild4?.orgChild4Name ?? null, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + commandNo: item.commandNo, + commandYear: item.commandYear, + posNo: posNo, + posNoAbb: shortName, + commandDateAffect: item.commandDateAffect, + commandDateSign: item.commandDateSign, + commandCode: item.commandCode, + commandName: item.commandName, + remark: item.remark, + }; + Object.assign(data, meta); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + + await this.salaryRepo.save(data); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history); + }), + ); + + if (commandType && String(commandType.code) == "C-PM-11") { + const profileIds = body.data.map((x) => x.profileId); + await this.profileRepository.update({ id: In(profileIds) }, { isProbation: false }); + // // Task #2304 อัปเดตจำนวนสิทธิ์การลา เมื่อผ่านทดลองงานฯ + // if (leaveType != null) { + // await Promise.all( + // body.data.map((item) => + // new CallAPI().PutData(req, `/leave-beginning/schedule`, { + // profileId: item.profileId, + // leaveTypeId: leaveType.id, + // leaveYear: item.commandYear, + // leaveDays: leaveType.limit, + // leaveDaysUsed: 0, + // leaveCount: 0, + // beginningLeaveDays: 0, + // beginningLeaveCount: 0, + // }) + // .then(() => {}) + // .catch(() => {}) + // ) + // ); + // } + } return new HttpSuccess(); } @@ -4344,17 +6074,248 @@ export class CommandController extends Controller { }[]; }, ) { - await new ExecuteSalaryProbationService().executeProbationLeave(body.data, { - user: { sub: req.user.sub, name: req.user.name }, - req, + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.data.find((x) => x.commandId)?.commandId ?? "" }, }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + await Promise.all( + body.data.map(async (item) => { + const profile = await this.profileRepository.findOne({ + relations: [ + // "profileSalary", + "posType", + "posLevel", + "current_holders", + "current_holders.orgRoot", + "current_holders.orgChild1", + "current_holders.orgChild2", + "current_holders.orgChild3", + "current_holders.orgChild4", + "current_holders.positions", + "current_holders.positions.posExecutive", + ], + where: { id: item.profileId }, + // order: { + // profileSalary: { + // order: "DESC", + // }, + // }, + }); + if (!profile) { + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์"); + } + const lastSalary = await this.salaryRepo.findOne({ + where: { profileId: item.profileId }, + select: ["order"], + order: { order: "DESC" }, + }); + const nextOrder = lastSalary ? lastSalary.order + 1 : 1; + let _commandYear = item.commandYear; + if (item.commandYear) { + _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; + } + const _profile = await this.profileRepository.findOne({ + where: { id: item.profileId }, + relations: ["roleKeycloaks"], + }); + if (!_profile) { + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์"); + } + let dateLeave_: any = item.commandDateAffect; + _profile.isLeave = true; + _profile.leaveReason = + "คำสั่งให้ข้าราชการออกจากราชการเพราะผลการทดลองปฏิบัติหน้าที่ราชการต่ำกว่ามาตรฐานที่กำหนด"; + _profile.dateLeave = dateLeave_; + _profile.lastUpdateUserId = req.user.sub; + _profile.lastUpdateFullName = req.user.name; + _profile.lastUpdatedAt = new Date(); + + const orgRevision = await this.orgRevisionRepo.findOne({ + where: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }); + const orgRevisionRef = + profile?.current_holders?.find((x) => x.orgRevisionId == orgRevision?.id) ?? null; + const orgRootRef = orgRevisionRef?.orgRoot ?? null; + const orgChild1Ref = orgRevisionRef?.orgChild1 ?? null; + const orgChild2Ref = orgRevisionRef?.orgChild2 ?? null; + const orgChild3Ref = orgRevisionRef?.orgChild3 ?? null; + const orgChild4Ref = orgRevisionRef?.orgChild4 ?? null; + const shortName = + !profile.current_holders || profile.current_holders.length == 0 + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) + ?.orgChild4 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild4.orgChild4ShortName}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) + ?.orgChild3 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild3.orgChild3ShortName}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) + ?.orgChild2 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild2.orgChild2ShortName}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != + null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) + ?.orgChild1 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild1.orgChild1ShortName}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != + null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) + ?.orgRoot != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgRoot.orgRootShortName}` + : null; + const posNo = `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.posMasterNo}`; + let position = + profile.current_holders + .filter((x) => x.orgRevisionId == orgRevision?.id)[0] + ?.positions?.filter((pos) => pos.positionIsSelected === true)[0] ?? null; + const profileSalary: ProfileSalary = Object.assign(new ProfileSalary(), { + profileId: item.profileId, + commandId: item.commandId, + positionName: profile.position, + positionType: profile?.posType?.posTypeName ?? null, + positionLevel: profile?.posLevel?.posLevelName ?? null, + positionExecutive: position?.posExecutive?.posExecutiveName ?? null, + amount: item.amount ? item.amount : null, + amountSpecial: item.amountSpecial ? item.amountSpecial : null, + positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, + mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, + // order: + // profile.profileSalary.length >= 0 + // ? profile.profileSalary.length > 0 + // ? profile.profileSalary[0].order + 1 + // : 1 + // : null, + order: nextOrder, + orgRoot: orgRootRef?.orgRootName ?? null, + orgChild1: orgChild1Ref?.orgChild1Name ?? null, + orgChild2: orgChild2Ref?.orgChild2Name ?? null, + orgChild3: orgChild3Ref?.orgChild3Name ?? null, + orgChild4: orgChild4Ref?.orgChild4Name ?? null, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + dateGovernment: item.commandDateAffect ?? new Date(), + isGovernment: item.isGovernment, + commandNo: item.commandNo, + commandYear: item.commandYear, + posNo: posNo, + posNoAbb: shortName, + commandDateAffect: item.commandDateAffect, + commandDateSign: item.commandDateSign, + commandCode: item.commandCode, + commandName: item.commandName, + remark: item.remark, + posNumCodeSit: _posNumCodeSit, + posNumCodeSitAbb: _posNumCodeSitAbb, + }); + if (orgRevisionRef) { + await CreatePosMasterHistoryOfficer(orgRevisionRef.id, req, "DELETE"); + } + await removeProfileInOrganize(profile.id, "OFFICER"); + const clearProfile = await checkCommandType(String(item.commandId)); + const _null: any = null; + if (clearProfile.status) { + if (_profile.keycloak != null && _profile.keycloak != "" && _profile.isDelete === false) { + const delUserKeycloak = await deleteUser(_profile.keycloak); + if (delUserKeycloak) { + // Task #228 + // _profile.keycloak = _null; + _profile.roleKeycloaks = []; + _profile.isActive = false; + _profile.isDelete = true; + } + } + _profile.leaveCommandId = item.commandId ?? _null; + _profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; + _profile.leaveRemark = clearProfile.leaveRemark ?? _null; + _profile.leaveDate = item.commandDateAffect ?? _null; + _profile.leaveType = clearProfile.LeaveType ?? _null; + //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) + // _profile.position = _null; + // _profile.posTypeId = _null; + // _profile.posLevelId = _null; + } + await Promise.all([ + this.profileRepository.save(_profile), + this.salaryRepo.save(profileSalary), + ]); + + // if (profile.id) { + // await this.keycloakAttributeService.clearOrgDnaAttributes( + // [profile.id], + // "PROFILE", + // ); + // } + + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...profileSalary, id: undefined }); + history.profileSalaryId = profileSalary.id; + await this.salaryHistoryRepo.save(history); + // Task #2190 + let organizeName = ""; + if (orgRootRef) { + const names = [ + orgChild4Ref?.orgChild4Name, + orgChild3Ref?.orgChild3Name, + orgChild2Ref?.orgChild2Name, + orgChild1Ref?.orgChild1Name, + orgRootRef?.orgRootName, + ].filter(Boolean); + organizeName = names.join(" "); + } + }), + ); return new HttpSuccess(); } - /** - * API สร้างทะเบียนประวัติข้าราชการ หลังออกคำสั่งบรรจุสอบ หรือ รับโอน - * @summary API สร้างทะเบียนประวัติข้าราชการ หลังออกคำสั่งบรรจุสอบ หรือ รับโอน - */ @Post("excexute/create-officer-profile") public async CreateOfficeProfileExcecute( @Request() req: RequestWithUser, @@ -4368,13 +6329,6 @@ export class CommandController extends Controller { bodyPosition?: { posmasterId: string; positionId: string; - positionName: string; - posTypeId: string; - posLevelId: string; - posExecutiveId: string | null; - positionField: string | null; - positionExecutiveField: string | null; - positionArea: string | null; } | null; bodyMarry?: { marry?: boolean | null; @@ -4401,10 +6355,793 @@ export class CommandController extends Controller { }[]; }, ) { - await new ExecuteOfficerProfileService().executeCreateOfficerProfile(body.data, { - user: { sub: req.user.sub, name: req.user.name }, - req, + const roleKeycloak = await this.roleKeycloakRepo.findOne({ + where: { name: Like("USER") }, }); + const list = await getRoles(); + if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.data.find((x) => x.bodySalarys?.commandId)?.bodySalarys?.commandId ?? "" }, + }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + const before = null; + const meta = { + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + await Promise.all( + body.data.map(async (item) => { + const _null: any = null; + if (item.bodyProfile.posLevelId === "") item.bodyProfile.posLevelId = null; + if (item.bodyProfile.posTypeId === "") item.bodyProfile.posTypeId = null; + if ( + item.bodyProfile.posLevelId && + !(await this.posLevelRepo.findOneBy({ id: item.bodyProfile.posLevelId })) + ) { + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลระดับตำแหน่งนี้"); + } + if ( + item.bodyProfile.posTypeId && + !(await this.posTypeRepo.findOneBy({ id: item.bodyProfile.posTypeId })) + ) { + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลประเภทตำแหน่งนี้"); + } + + let registrationProvinceId = await this.provinceRepo.findOneBy({ + id: item.bodyProfile.registrationProvinceId ?? "", + }); + let registrationDistrictId = await this.districtRepo.findOneBy({ + id: item.bodyProfile.registrationDistrictId ?? "", + }); + let registrationSubDistrictId = await this.subDistrictRepo.findOneBy({ + id: item.bodyProfile.registrationSubDistrictId ?? "", + }); + let currentProvinceId = await this.provinceRepo.findOneBy({ + id: item.bodyProfile.currentProvinceId ?? "", + }); + let currentDistrictId = await this.districtRepo.findOneBy({ + id: item.bodyProfile.currentDistrictId ?? "", + }); + let currentSubDistrictId = await this.subDistrictRepo.findOneBy({ + id: item.bodyProfile.currentSubDistrictId ?? "", + }); + + let _dateRetire = + item.bodyProfile.birthDate == null + ? _null + : calculateRetireDate(item.bodyProfile.birthDate); + let _dateRetireLaw = + item.bodyProfile.birthDate == null + ? _null + : calculateRetireLaw(item.bodyProfile.birthDate); + + let userKeycloakId: any; + let result: any; + const checkUser = await getUserByUsername(item.bodyProfile.citizenId); + if (checkUser.length == 0) { + let password = item.bodyProfile.citizenId; + if (item.bodyProfile.birthDate != null) { + const _date = new Date(item.bodyProfile.birthDate.toDateString()) + .getDate() + .toString() + .padStart(2, "0"); + const _month = (new Date(item.bodyProfile.birthDate.toDateString()).getMonth() + 1) + .toString() + .padStart(2, "0"); + const _year = new Date(item.bodyProfile.birthDate.toDateString()).getFullYear() + 543; + password = `${_date}${_month}${_year}`; + } + userKeycloakId = await createUser(item.bodyProfile.citizenId, password, { + firstName: item.bodyProfile.firstName, + lastName: item.bodyProfile.lastName, + }); + result = await addUserRoles( + userKeycloakId, + list + .filter((v) => v.name === "USER") + .map((x) => ({ + id: x.id, + name: x.name, + })), + ); + } else { + userKeycloakId = checkUser[0].id; + const rolesData = await getRoleMappings(userKeycloakId); + if (rolesData) { + const _delRole = rolesData.map((x: any) => ({ + id: x.id, + name: x.name, + })); + await removeUserRoles(userKeycloakId, _delRole); + } + result = await addUserRoles( + userKeycloakId, + list + .filter((v) => v.name === "USER") + .map((x) => ({ + id: x.id, + name: x.name, + })), + ); + } + + let profile: any = await this.profileRepository.findOne({ + where: { citizenId: item.bodyProfile.citizenId /*, isActive: true */ }, + relations: ["roleKeycloaks", "profileInsignias", "profileAvatars"], + }); + let _oldInsigniaIds: string[] = []; + let _oldSalaries: any[] = []; + //ลูกจ้างประจำ หรือ บุคคลภายนอก + if (!profile) { + //กรณีลูกจ้างประจำมาสอบเป็นข้าราชการ ต้อง update สถานะโปรไฟล์เดิม + let profileEmployee: any = await this.profileEmployeeRepository.findOne({ + where: { citizenId: item.bodyProfile.citizenId }, + relations: ["profileInsignias", "roleKeycloaks"], + }); + if (profileEmployee) { + const _order = await this.salaryRepo.findOne({ + where: { profileEmployeeId: profileEmployee.id }, + order: { order: "DESC" }, + }); + const profileEmpSalary = new ProfileSalary(); + profileEmpSalary.posNumCodeSit = _posNumCodeSit; + profileEmpSalary.posNumCodeSitAbb = _posNumCodeSitAbb; + profileEmpSalary.order = _order == null ? 1 : _order.order + 1; + Object.assign(profileEmpSalary, { + ...item.bodySalarys, + ...meta, + profileEmployeeId: profileEmployee.id, + profileId: undefined, + }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...profileEmpSalary, id: undefined }); + profileEmpSalary.dateGovernment = item.bodySalarys?.commandDateAffect ?? meta.createdAt; + (profileEmpSalary.profileId = _null), + await this.salaryRepo.save(profileEmpSalary, { data: req }); + setLogDataDiff(req, { before, after: profileEmpSalary }); + history.profileSalaryId = profileEmpSalary.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + if (profileEmployee.profileInsignias.length > 0) { + _oldInsigniaIds = profileEmployee.profileInsignias?.map((x: any) => x.id) ?? []; + } + await removeProfileInOrganize(profileEmployee.id, "EMPLOYEE"); + if (profileEmployee.keycloak != null) { + // const delUserKeycloak = await deleteUser(profileEmployee.keycloak); + // if (delUserKeycloak) { + // Task #228 + // profileEmployee.keycloak = _null; + profileEmployee.roleKeycloaks = []; + profileEmployee.isActive = false; + // } + } + profileEmployee.isLeave = true; + profileEmployee.leaveReason = "บรรจุข้าราชการ"; + profileEmployee.lastUpdateUserId = req.user.sub; + profileEmployee.lastUpdateFullName = req.user.name; + profileEmployee.lastUpdatedAt = new Date(); + await this.profileEmployeeRepository.save(profileEmployee); + setLogDataDiff(req, { before, after: profileEmployee }); + } + profile = Object.assign({ ...item.bodyProfile, ...meta }); + profile.dateRetire = _dateRetire; + profile.dateRetireLaw = _dateRetireLaw; + profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; + profile.keycloak = + userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; + profile.registrationAddress = item.bodyProfile.registrationAddress; + profile.registrationProvinceId = registrationProvinceId + ? registrationProvinceId.id + : _null; + profile.registrationDistrictId = registrationDistrictId + ? registrationDistrictId.id + : _null; + profile.registrationSubDistrictId = registrationSubDistrictId + ? registrationSubDistrictId.id + : _null; + profile.registrationZipCode = item.bodyProfile.registrationZipCode; + profile.currentAddress = item.bodyProfile.currentAddress; + profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; + profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; + profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; + profile.currentZipCode = item.bodyProfile.currentZipCode; + profile.email = item.bodyProfile.email; + profile.dateStart = item.bodyProfile.dateStart; + profile.amount = item.bodyProfile.amount ?? null; + profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; + profile.isProbation = item.bodyProfile.isProbation; + //เพิ่มใหม่จากรับโอน + profile.prefix = item.bodyProfile.prefix ?? null; + profile.prefixMain = item.bodyProfile.prefix ?? null; + profile.firstName = item.bodyProfile.firstName ?? null; + profile.lastName = item.bodyProfile.lastName ?? null; + profile.birthDate = item.bodyProfile.birthDate ?? null; + profile.gender = item.bodyProfile.gender ?? null; + profile.relationship = item.bodyProfile.relationship ?? null; + profile.religion = item.bodyProfile.religion ?? null; + profile.ethnicity = item.bodyProfile.ethnicity; + profile.nationality = item.bodyProfile.nationality ?? null; + profile.bloodGroup = item.bodyProfile.bloodGroup ?? null; + profile.phone = item.bodyProfile.phone ?? null; + + await this.profileRepository.save(profile); + // update user attribute in keycloak + await updateUserAttributes(profile.keycloak ?? "", { + profileId: [profile.id], + prefix: [profile.prefix || ""], + }); + setLogDataDiff(req, { before, after: profile }); + } + //ขรก.ในระบบ หรือ ขรก.ในระบบที่สถานะพ้นจากราชการ + else { + //สร้างโปรไฟล์ใหม่ ถ้าสถานะพ้นราชการ คำสั่งโอนออกหรือคำสั่งขอลาออก + if ( + profile.isLeave && + ["PLACEMENT_TRANSFER", "RETIRE_RESIGN"].includes(profile.leaveType) + ) { + //ดึง profileSalary เดิม + _oldSalaries = await this.salaryRepo.find({ + where: { profileId: profile.id }, + order: { order: "ASC" }, + }); + if (profile.profileInsignias.length > 0) { + _oldInsigniaIds = profile.profileInsignias?.map((x: any) => x.id) ?? []; + } + profile = Object.assign({ ...item.bodyProfile, ...meta }); + profile.dateRetire = _dateRetire; + profile.dateRetireLaw = _dateRetireLaw; + profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; + profile.keycloak = + userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; + profile.registrationAddress = item.bodyProfile.registrationAddress; + profile.registrationProvinceId = registrationProvinceId + ? registrationProvinceId.id + : _null; + profile.registrationDistrictId = registrationDistrictId + ? registrationDistrictId.id + : _null; + profile.registrationSubDistrictId = registrationSubDistrictId + ? registrationSubDistrictId.id + : _null; + profile.registrationZipCode = item.bodyProfile.registrationZipCode; + profile.currentAddress = item.bodyProfile.currentAddress; + profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; + profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; + profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; + profile.currentZipCode = item.bodyProfile.currentZipCode; + profile.email = item.bodyProfile.email; + profile.dateStart = item.bodyProfile.dateStart; + profile.amount = item.bodyProfile.amount ?? null; + profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; + profile.isProbation = item.bodyProfile.isProbation; + profile.prefix = item.bodyProfile.prefix ?? null; + profile.prefixMain = item.bodyProfile.prefix ?? null; + profile.firstName = item.bodyProfile.firstName ?? null; + profile.lastName = item.bodyProfile.lastName ?? null; + profile.birthDate = item.bodyProfile.birthDate ?? null; + profile.gender = item.bodyProfile.gender ?? null; + profile.relationship = item.bodyProfile.relationship ?? null; + profile.religion = item.bodyProfile.religion ?? null; + profile.ethnicity = item.bodyProfile.ethnicity; + profile.nationality = item.bodyProfile.nationality ?? null; + profile.bloodGroup = item.bodyProfile.bloodGroup ?? null; + profile.phone = item.bodyProfile.phone ?? null; + await this.profileRepository.save(profile); + setLogDataDiff(req, { before, after: profile }); + } else { + profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; + profile.keycloak = + userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; + profile.isProbation = item.bodyProfile.isProbation; + profile.isLeave = item.bodyProfile.isLeave; + profile.isRetirement = false; + profile.isActive = true; + profile.isDelete = false; + profile.dateLeave = _null; + profile.dateRetire = _dateRetire; + profile.dateRetireLaw = _dateRetireLaw; + profile.registrationAddress = item.bodyProfile.registrationAddress; + profile.registrationProvinceId = registrationProvinceId + ? registrationProvinceId.id + : _null; + profile.registrationDistrictId = registrationDistrictId + ? registrationDistrictId.id + : _null; + profile.registrationSubDistrictId = registrationSubDistrictId + ? registrationSubDistrictId.id + : _null; + profile.registrationZipCode = item.bodyProfile.registrationZipCode; + profile.currentAddress = item.bodyProfile.currentAddress; + profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; + profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; + profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; + profile.currentZipCode = item.bodyProfile.currentZipCode; + profile.email = item.bodyProfile.email; + profile.telephoneNumber = item.bodyProfile.telephoneNumber; + profile.phone = item.bodyProfile.phone; + profile.dateStart = item.bodyProfile.dateStart; + profile.amount = item.bodyProfile.amount ?? null; + profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; + profile.leaveCommandId = _null; + profile.leaveCommandNo = _null; + profile.leaveRemark = _null; + profile.leaveDate = _null; + profile.leaveType = _null; + profile.leaveReason = _null; + profile.lastUpdateUserId = req.user.sub; + profile.lastUpdateFullName = req.user.name; + profile.lastUpdatedAt = new Date(); + //เพิ่มใหม่จากรับโอน + profile.prefix = + item.bodyProfile.prefix && item.bodyProfile.prefix != "" + ? item.bodyProfile.prefix + : profile.prefix; + profile.prefixMain = item.bodyProfile.prefix ?? null; + profile.firstName = + item.bodyProfile.firstName && item.bodyProfile.firstName != "" + ? item.bodyProfile.firstName + : profile.firstName; + profile.lastName = + item.bodyProfile.lastName && item.bodyProfile.lastName != "" + ? item.bodyProfile.lastName + : profile.lastName; + profile.birthDate = item.bodyProfile.birthDate + ? item.bodyProfile.birthDate + : profile.birthDate; + profile.gender = + item.bodyProfile.gender && item.bodyProfile.gender != "" + ? item.bodyProfile.gender + : profile.gender; + profile.relationship = + item.bodyProfile.relationship && item.bodyProfile.relationship != "" + ? item.bodyProfile.relationship + : profile.relationship; + profile.religion = + item.bodyProfile.religion && item.bodyProfile.religion != "" + ? item.bodyProfile.religion + : profile.religion; + profile.ethnicity = + item.bodyProfile.ethnicity && item.bodyProfile.ethnicity != "" + ? item.bodyProfile.ethnicity + : profile.ethnicity; + profile.nationality = + item.bodyProfile.nationality && item.bodyProfile.nationality != "" + ? item.bodyProfile.nationality + : profile.nationality; + profile.bloodGroup = + item.bodyProfile.bloodGroup && item.bodyProfile.bloodGroup != "" + ? item.bodyProfile.bloodGroup + : profile.bloodGroup; + profile.phone = + item.bodyProfile.phone && item.bodyProfile.phone != "" + ? item.bodyProfile.phone + : profile.phone; + await this.profileRepository.save(profile); + setLogDataDiff(req, { before, after: profile }); + } + } + + if (profile && profile.id) { + //Educations + if (item.bodyEducations && item.bodyEducations.length > 0) { + await Promise.all( + item.bodyEducations.map(async (education) => { + const profileEdu = new ProfileEducation(); + Object.assign(profileEdu, { ...education, ...meta }); + const eduHistory = new ProfileEducationHistory(); + Object.assign(eduHistory, { ...profileEdu, id: undefined }); + profileEdu.profileId = profile.id; + const educationLevel = await this.profileEducationRepo.findOne({ + select: ["id", "level", "profileId"], + where: { profileId: profile.id, isDeleted: false }, + order: { level: "DESC" }, + }); + profileEdu.level = educationLevel == null ? 1 : educationLevel.level + 1; + await this.profileEducationRepo.save(profileEdu, { data: req }); + setLogDataDiff(req, { before, after: profileEdu }); + eduHistory.profileEducationId = profileEdu.id; + await this.profileEducationHistoryRepo.save(eduHistory, { data: req }); + }), + ); + } + //Certificates + if (item.bodyCertificates && item.bodyCertificates.length > 0) { + await Promise.all( + item.bodyCertificates.map(async (cer) => { + const profileCer = new ProfileCertificate(); + Object.assign(profileCer, { ...cer, ...meta }); + const cerHistory = new ProfileCertificateHistory(); + Object.assign(cerHistory, { ...profileCer, id: undefined }); + profileCer.profileId = profile.id; + await this.certificateRepo.save(profileCer, { data: req }); + setLogDataDiff(req, { before, after: profileCer }); + cerHistory.profileCertificateId = profileCer.id; + await this.certificateHistoryRepo.save(cerHistory, { data: req }); + }), + ); + } + //FamilyCouple + if (item.bodyMarry != null) { + const profileCouple = new ProfileFamilyCouple(); + const data = { + profileId: profile.id, + couple: item.bodyMarry.marry, + couplePrefix: item.bodyMarry.marryPrefix, + coupleFirstName: item.bodyMarry.marryFirstName, + coupleLastName: item.bodyMarry.marryLastName, + coupleCareer: item.bodyMarry.marryOccupation, + coupleLive: true, + }; + Object.assign(profileCouple, { ...data, ...meta }); + const coupleHistory = new ProfileFamilyCoupleHistory(); + Object.assign(coupleHistory, { ...profileCouple, id: undefined }); + profileCouple.profileId = profile.id; + await this.profileFamilyCoupleRepo.save(profileCouple, { data: req }); + setLogDataDiff(req, { before, after: profileCouple }); + coupleHistory.profileFamilyCoupleId = profileCouple.id; + await this.profileFamilyCoupleHistoryRepo.save(coupleHistory, { data: req }); + } + //FamilyFather + if (item.bodyFather != null) { + const profileFather = new ProfileFamilyFather(); + const data = { + profileId: profile.id, + fatherPrefix: item.bodyFather.fatherPrefix, + fatherFirstName: item.bodyFather.fatherFirstName, + fatherLastName: item.bodyFather.fatherLastName, + fatherCareer: item.bodyFather.fatherOccupation, + fatherLive: true, + }; + Object.assign(profileFather, { ...data, ...meta }); + const fatherHistory = new ProfileFamilyFatherHistory(); + Object.assign(fatherHistory, { ...profileFather, id: undefined }); + profileFather.profileId = profile.id; + await this.profileFamilyFatherRepo.save(profileFather, { data: req }); + setLogDataDiff(req, { before, after: profileFather }); + fatherHistory.profileFamilyFatherId = profileFather.id; + await this.profileFamilyFatherHistoryRepo.save(fatherHistory, { data: req }); + } + //FamilyMother + if (item.bodyMother != null) { + const profileMother = new ProfileFamilyMother(); + const data = { + profileId: profile.id, + motherPrefix: item.bodyMother.motherPrefix, + motherFirstName: item.bodyMother.motherFirstName, + motherLastName: item.bodyMother.motherLastName, + motherCareer: item.bodyMother.motherOccupation, + motherLive: true, + }; + Object.assign(profileMother, { ...data, ...meta }); + const motherHistory = new ProfileFamilyMotherHistory(); + Object.assign(motherHistory, { ...profileMother, id: undefined }); + profileMother.profileId = profile.id; + await this.profileFamilyMotherRepo.save(profileMother, { data: req }); + setLogDataDiff(req, { before, after: profileMother }); + motherHistory.profileFamilyMotherId = profileMother.id; + await this.profileFamilyMotherHistoryRepo.save(motherHistory, { data: req }); + } + //Salary + //insert profileSalary อันเก่า กรณีพ้นราชการแล้วกลับมาบรรจุ + if (_oldSalaries.length > 0) { + await Promise.all( + _oldSalaries.map(async (oldSal) => { + const profileSal: any = new ProfileSalary(); + Object.assign(profileSal, { ...oldSal, ...meta }); + const salaryHistory = new ProfileSalaryHistory(); + Object.assign(salaryHistory, { ...profileSal, id: undefined }); + profileSal.profileId = profile.id; + await this.salaryRepo.save(profileSal, { data: req }); + setLogDataDiff(req, { before, after: profileSal }); + salaryHistory.profileSalaryId = profileSal.id; + await this.salaryHistoryRepo.save(salaryHistory, { data: req }); + }), + ); + } + //insert item.bodySalarys ต่อจากที่ insert เดิมไปแล้ว + if (item.bodySalarys && item.bodySalarys != null) { + const dest_item = await this.salaryRepo.findOne({ + where: { profileId: profile.id }, + order: { order: "DESC" }, + }); + const profileSal: any = new ProfileSalary(); + profileSal.posNumCodeSit = _posNumCodeSit; + profileSal.posNumCodeSitAbb = _posNumCodeSitAbb; + Object.assign(profileSal, { ...item.bodySalarys, ...meta }); + const salaryHistory = new ProfileSalaryHistory(); + Object.assign(salaryHistory, { ...profileSal, id: undefined }); + profileSal.order = dest_item == null ? 1 : dest_item.order + 1; + profileSal.profileId = profile.id; + profileSal.dateGovernment = item.bodySalarys.commandDateAffect ?? meta.createdAt; + profileSal.amount = item.bodySalarys.amount ?? null; + profileSal.amountSpecial = item.bodySalarys.amountSpecial ?? null; + profileSal.positionSalaryAmount = item.bodySalarys.positionSalaryAmount ?? null; + profileSal.mouthSalaryAmount = item.bodySalarys.mouthSalaryAmount ?? null; + await this.salaryRepo.save(profileSal, { data: req }); + setLogDataDiff(req, { before, after: profileSal }); + salaryHistory.profileSalaryId = profileSal.id; + await this.salaryHistoryRepo.save(salaryHistory, { data: req }); + } + //Position + if (item.bodyPosition && item.bodyPosition != null) { + // STEP 1: หา posMaster ที่จะใช้งานตาม id ที่ส่งมา (อาจเป็นตำแหน่งเก่าหรือใหม่ก็ได้) + let posMaster = await this.posMasterRepository.findOne({ + where: { + id: item.bodyPosition.posmasterId, + }, + relations: { + orgRevision: true, + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + + // เช็คว่า posMaster ที่หามาอยู่ในโครงสร้างปัจจุบันหรือไม่ + const isCurrent = + posMaster?.orgRevision?.orgRevisionIsCurrent === true && + posMaster?.orgRevision?.orgRevisionIsDraft === false; + + // ถ้าไม่อยู่ในโครงสร้างปัจจุบัน ให้หาตัวใหม่จาก ancestorDNA + if (!isCurrent && posMaster?.ancestorDNA) { + posMaster = await this.posMasterRepository.findOne({ + where: { + ancestorDNA: posMaster.ancestorDNA, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: { + orgRevision: true, + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + } + + if (posMaster == null) + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); + + // STEP 2: เคลียร์ข้อมูลตำแหน่งเก่าที่ครองอยู่ ในโครงสร้างปัจจุบัน + const posMasterOld = await this.posMasterRepository.findOne({ + where: { + current_holderId: profile.id, + orgRevisionId: posMaster.orgRevisionId, + }, + }); + if (posMasterOld != null) { + // เคลียร์คนครองเก่าออกจากตำแหน่งเดิม + posMasterOld.current_holderId = null; + posMasterOld.lastUpdatedAt = new Date(); + } + + // หา position เก่าที่เลือกไว้ แล้วเคลียร์การเลือก + const positionOld = await this.positionRepository.findOne({ + where: { + posMasterId: posMasterOld?.id, + positionIsSelected: true, + }, + }); + if (positionOld != null) { + positionOld.positionIsSelected = false; + await this.positionRepository.save(positionOld); + } + + // STEP 3: เคลียร์ position ที่เลือกไว้อื่นๆ ใน posMaster ตัวใหม่ + const checkPosition = await this.positionRepository.find({ + where: { + posMasterId: posMaster.id, + positionIsSelected: true, + }, + }); + if (checkPosition.length > 0) { + const clearPosition = checkPosition.map((positions) => ({ + ...positions, + positionIsSelected: false, + })); + await this.positionRepository.save(clearPosition); + } + + // STEP 4: กำหนดคนครองใหม่ให้กับ posMaster + posMaster.current_holderId = profile.id; + posMaster.lastUpdatedAt = new Date(); + // posMaster.conditionReason = _null; + // posMaster.isCondition = false; + if (posMasterOld != null) { + await this.posMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryOfficer(posMasterOld.id, req); + } + await this.posMasterRepository.save(posMaster); + + // STEP 5: กำหนด position ใหม่ + // เช็คว่า posMaster เปลี่ยนจากเก่าเป็นใหม่หรือไม่ + const originalPosMasterId = item.bodyPosition.posmasterId; + const isPosMasterChanged = originalPosMasterId !== posMaster.id; + + let positionNew = null; + + if (isPosMasterChanged) { + // posMaster เปลี่ยน ต้องหา position ใหม่จากคุณสมบัติของ position เก่า + // 1. หา position เก่าจาก id ที่ส่งมา + const positionOld = await this.positionRepository.findOne({ + where: { id: item.bodyPosition.positionId }, + }); + + if (positionOld) { + // 2. ใช้ posTypeId + posLevelId + positionName หา position ใหม่ใน posMaster ตัวใหม่ + positionNew = await this.positionRepository.findOne({ + where: { + posMasterId: posMaster.id, // ใช้ posMaster ตัวใหม่ + posTypeId: positionOld.posTypeId, + posLevelId: positionOld.posLevelId, + positionName: positionOld.positionName, + }, + }); + } + } else { + // posMaster ไม่เปลี่ยน - ใช้วิธีเดิม + positionNew = await this.positionRepository.findOne({ + where: { + id: item.bodyPosition.positionId, + posMasterId: posMaster.id, + }, + relations: ["posExecutive"], + }); + } + + // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ + if (positionNew != null) { + positionNew.positionIsSelected = true; + // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit + profile.posMasterNo = getPosMasterNo(posMaster); + profile.org = getOrgFullName(posMaster); + if (!posMaster.isSit) { + profile.posLevelId = positionNew.posLevelId; + profile.posTypeId = positionNew.posTypeId; + profile.position = positionNew.positionName; + profile.positionField = positionNew.positionField ?? null; + profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; + profile.positionArea = positionNew.positionArea ?? null; + profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; + // profile.dateStart = new Date(); + } + await this.profileRepository.save(profile, { data: req }); + setLogDataDiff(req, { before, after: profile }); + await this.positionRepository.save(positionNew, { data: req }); + } + // await CreatePosMasterHistoryOfficer(posMaster.id, req); + await CreatePosMasterHistoryOfficer(posMaster.id, req, null, { + positionId: positionNew?.id, + }); + } + // Insignia + if (_oldInsigniaIds.length > 0) { + const _insignias = await this.insigniaRepo.find({ + where: { id: In(_oldInsigniaIds), isDeleted: false }, + order: { createdAt: "ASC" }, + }); + for (const oldInsignia of _insignias) { + const newInsigniaData: CreateProfileInsignia = { + profileId: profile.id, + year: oldInsignia.year, + no: oldInsignia.no, + volume: oldInsignia.volume, + section: oldInsignia.section, + page: oldInsignia.page, + receiveDate: oldInsignia.receiveDate, + insigniaId: oldInsignia.insigniaId, + dateAnnounce: oldInsignia.dateAnnounce, + issue: oldInsignia.issue, + volumeNo: oldInsignia.volumeNo, + refCommandDate: oldInsignia.refCommandDate, + refCommandNo: oldInsignia.refCommandNo, + note: oldInsignia.note, + isUpload: oldInsignia.isUpload, + }; + const insignia = new ProfileInsignia(); + Object.assign(insignia, { ...newInsigniaData, ...meta }); + const history = new ProfileInsigniaHistory(); + Object.assign(history, { ...insignia, id: undefined }); + await this.insigniaRepo.save(insignia, { data: req }); + setLogDataDiff(req, { before, after: insignia }); + history.profileInsigniaId = insignia.id; + await this.insigniaHistoryRepo.save(history, { data: req }); + } + } + // เพิ่มรูปภาพโปรไฟล์ + if (item.bodyProfile.objectRefId) { + const _profileAvatar = new ProfileAvatar(); + Object.assign(_profileAvatar, { + ...meta, + profileId: profile.id, + profileEmployeeId: undefined, + }); + if (profile.profileAvatars && profile.profileAvatars.length > 0) { + await Promise.all( + profile.profileAvatars.map(async (item: any) => { + item.isActive = false; + await this.avatarRepository.save(item); + }), + ); + } + await this.avatarRepository.save(_profileAvatar); + let avatar = `ทะเบียนประวัติ/โปรไฟล์/${profile.id}`; + let fileName = `profile-${_profileAvatar.id}`; + _profileAvatar.isActive = true; + _profileAvatar.avatar = avatar; + _profileAvatar.avatarName = fileName; + await this.avatarRepository.save(_profileAvatar, { data: req }); + profile.avatar = avatar; + profile.avatarName = fileName; + await this.profileRepository.save(profile, { data: req }); + const checkAvatar = await this.avatarRepository.findOne({ + where: { avatar: avatar, avatarName: fileName }, + }); + if (checkAvatar && checkAvatar.profileId == null) { + checkAvatar.profileId = profile.id; + await this.avatarRepository.save(checkAvatar); + } + //duplicate รูปภาพโปรไฟล์โดยอิงจากรูปภาพเดิม + await new CallAPI() + .PostData(req, `/salary/file/avatar/${item.bodyProfile.objectRefId}`, { + prefix: avatar, + fileName: fileName, + }) + .then(() => {}) + .catch(() => {}); + } + } + }), + ); return new HttpSuccess(); } @@ -4494,14 +7231,363 @@ export class CommandController extends Controller { }[]; }, ) { - /** - * Thin wrapper — เรียก ExecuteOrgCommandService.executeCommand21Employee (C-PM-21) - * ทั้ง endpoint นี้และ consumer ใน rabbitmq ใช้ service ตัวเดียวกัน (Linear Flow) - */ - await new ExecuteOrgCommandService().executeCommand21Employee(body.refIds, { - user: { sub: req.user.sub, name: req.user.name }, - req, + let _reqBody: any[] = []; + const roleKeycloak = await this.roleKeycloakRepo.findOne({ + where: { name: Like("USER") }, }); + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.refIds.find((x) => x.commandId)?.commandId ?? "" }, + }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + await Promise.all( + body.refIds.map(async (item) => { + const profile = await this.profileEmployeeRepository.findOne({ + where: { id: item.refId }, + relations: ["roleKeycloaks"], + }); + if (!profile) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); + } + const orgRevision = await this.orgRevisionRepository.findOne({ + where: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }); + const _posMaster = await this.employeePosMasterRepository.findOne({ + where: { + orgRevisionId: orgRevision?.id, + id: profile.posmasterIdTemp, + // current_holderId: profile.id + }, + relations: { + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + const orgRootRef = _posMaster?.orgRoot ?? null; + const orgChild1Ref = _posMaster?.orgChild1 ?? null; + const orgChild2Ref = _posMaster?.orgChild2 ?? null; + const orgChild3Ref = _posMaster?.orgChild3 ?? null; + const orgChild4Ref = _posMaster?.orgChild4 ?? null; + let orgShortName = ""; + if (_posMaster != null) { + if (_posMaster.orgChild1Id === null) { + orgShortName = _posMaster.orgRoot?.orgRootShortName; + } else if (_posMaster.orgChild2Id === null) { + orgShortName = _posMaster.orgChild1?.orgChild1ShortName; + } else if (_posMaster.orgChild3Id === null) { + orgShortName = _posMaster.orgChild2?.orgChild2ShortName; + } else if (_posMaster.orgChild4Id === null) { + orgShortName = _posMaster.orgChild3?.orgChild3ShortName; + } else { + orgShortName = _posMaster.orgChild4?.orgChild4ShortName; + } + } + const dest_item = await this.salaryRepo.findOne({ + where: { profileEmployeeId: item.refId }, + order: { order: "DESC" }, + }); + const before = null; + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + profileEmployeeId: profile.id, + amount: item.amount, + amountSpecial: item.amountSpecial, + commandId: item.commandId, + positionSalaryAmount: item.positionSalaryAmount, + mouthSalaryAmount: item.mouthSalaryAmount, + position: profile.positionTemp, + positionName: profile.positionTemp, + positionType: profile.posTypeNameTemp, + positionLevel: profile.posLevelNameTemp, + order: dest_item == null ? 1 : dest_item.order + 1, + orgRoot: orgRootRef?.orgRootName ?? null, + orgChild1: orgChild1Ref?.orgChild1Name ?? null, + orgChild2: orgChild2Ref?.orgChild2Name ?? null, + orgChild3: orgChild3Ref?.orgChild3Name ?? null, + orgChild4: orgChild4Ref?.orgChild4Name ?? null, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + commandNo: item.commandNo, + commandYear: item.commandYear, + posNo: profile.posMasterNoTemp ?? "", + posNoAbb: orgShortName, + commandDateAffect: item.commandDateAffect, + commandDateSign: item.commandDateSign, + commandCode: item.commandCode, + commandName: item.commandName, + remark: item.remark, + }; + + Object.assign(data, meta); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + + await this.salaryRepo.save(data, { data: req }); + setLogDataDiff(req, { before, after: data }); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + const posMaster = await this.employeePosMasterRepository.findOne({ + where: { id: profile.posmasterIdTemp }, + relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"], + }); + if (posMaster == null) + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); + + const posMasterOld = await this.employeePosMasterRepository.findOne({ + where: { + current_holderId: profile.id, + orgRevisionId: posMaster.orgRevisionId, + }, + }); + if (posMasterOld != null) { + posMasterOld.current_holderId = null; + posMasterOld.lastUpdatedAt = new Date(); + } + // if (posMasterOld != null) posMasterOld.next_holderId = null; + + const positionOld = await this.employeePositionRepository.findOne({ + where: { + posMasterId: posMasterOld?.id, + positionIsSelected: true, + }, + }); + if (positionOld != null) { + positionOld.positionIsSelected = false; + await this.employeePositionRepository.save(positionOld); + } + + const checkPosition = await this.employeePositionRepository.find({ + where: { + posMasterId: profile.posmasterIdTemp, + positionIsSelected: true, + }, + }); + if (checkPosition.length > 0) { + const clearPosition = checkPosition.map((positions) => ({ + ...positions, + positionIsSelected: false, + })); + await this.employeePositionRepository.save(clearPosition); + } + + posMaster.current_holderId = profile.id; + posMaster.lastUpdatedAt = new Date(); + posMaster.next_holderId = null; + if (posMasterOld != null) { + await this.employeePosMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryEmployee(posMasterOld.id, req); + } + await this.employeePosMasterRepository.save(posMaster); + await CreatePosMasterHistoryEmployee(posMaster.id, req); + + const clsTempPosmaster = await this.employeeTempPosMasterRepository.find({ + where: { + current_holderId: profile.id, + orgRevisionId: posMaster.orgRevisionId, + }, + }); + + if (clsTempPosmaster.length > 0) { + const clearTempPosmaster = clsTempPosmaster.map((posMasterTemp) => ({ + ...posMasterTemp, + current_holderId: null, + next_holderId: null, + })); + await this.employeeTempPosMasterRepository.save(clearTempPosmaster); + + const checkTempPosition = await this.employeePositionRepository.find({ + where: { + posMasterTempId: In(clearTempPosmaster.map((x) => x.id)), + positionIsSelected: true, + }, + }); + if (checkTempPosition.length > 0) { + const clearTempPosition = checkTempPosition.map((positions) => ({ + ...positions, + positionIsSelected: false, + })); + await this.employeePositionRepository.save(clearTempPosition); + } + await Promise.all( + clsTempPosmaster.map( + async (posMasterTemp) => + await CreatePosMasterHistoryEmployeeTemp(posMasterTemp.id, req), + ), + ); + } + + const positionNew = await this.employeePositionRepository.findOne({ + where: { + id: profile.positionIdTemp, + posMasterId: profile.posmasterIdTemp, + }, + }); + + if (positionNew != null) { + // Create Keycloak + const checkUser = await getUserByUsername(profile.citizenId); + if (checkUser.length == 0) { + let password = profile.citizenId; + if (profile.birthDate != null) { + // const gregorianYear = profile.birthDate.getFullYear() + 543; + + // const formattedDate = + // profile.birthDate.toISOString().slice(8, 10) + + // profile.birthDate.toISOString().slice(5, 7) + + // gregorianYear; + // password = formattedDate; + const _date = new Date(profile.birthDate.toDateString()) + .getDate() + .toString() + .padStart(2, "0"); + const _month = (new Date(profile.birthDate.toDateString()).getMonth() + 1) + .toString() + .padStart(2, "0"); + const _year = new Date(profile.birthDate.toDateString()).getFullYear() + 543; + password = `${_date}${_month}${_year}`; + } + const userKeycloakId = await createUser(profile.citizenId, password, { + firstName: profile.firstName, + lastName: profile.lastName, + // email: profile.email, + }); + // if (typeof userKeycloakId !== "string") { + // throw new Error(userKeycloakId.errorMessage); + // } + const list = await getRoles(); + if (!Array.isArray(list)) + throw new Error("Failed. Cannot get role(s) data from the server."); + const result = await addUserRoles( + userKeycloakId, + list + .filter((v) => v.name === "USER") + .map((x) => ({ + id: x.id, + name: x.name, + })), + ); + // if (!result) throw new Error("Failed. Cannot set user's role."); + profile.keycloak = + userKeycloakId && typeof userKeycloakId == "string" ? userKeycloakId : ""; + profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; + // End Create Keycloak + } else { + const rolesData = await getRoleMappings(checkUser[0].id); + if (rolesData) { + const _roleKeycloak = await this.roleKeycloakRepo.find({ + where: { name: In(rolesData.map((x: any) => x.name)) }, + }); + profile.roleKeycloaks = + _roleKeycloak && _roleKeycloak.length > 0 ? _roleKeycloak : []; + } + profile.keycloak = checkUser[0].id; + } + positionNew.positionIsSelected = true; + profile.posLevelId = positionNew.posLevelId; + profile.posTypeId = positionNew.posTypeId; + profile.position = positionNew.positionName; + profile.employeeOc = posMaster?.orgRoot?.orgRootName ?? null; + profile.positionEmployeePositionId = positionNew.positionName; + profile.statusTemp = "DONE"; + profile.employeeClass = "PERM"; + const _null: any = null; + profile.employeeWage = item.amount == null ? _null : item.amount.toString(); + profile.dateStart = _command ? _command.commandExcecuteDate : new Date(); + profile.dateAppoint = _command ? _command.commandExcecuteDate : new Date(); + profile.amount = item.amount == null ? _null : item.amount; + profile.amountSpecial = item.amountSpecial == null ? _null : item.amountSpecial; + _reqBody.push({ + profileId: profile.id, + prefix: profile.prefix, + firstName: profile.firstName, + lastName: profile.lastName, + citizenId: profile.citizenId, + root: posMaster.orgRoot.orgRootName, + rootId: posMaster.orgRootId, + rootShortName: posMaster.orgRoot.orgRootShortName, + rootDnaId: posMaster.orgRoot?.ancestorDNA ?? _null, + child1DnaId: posMaster.orgChild1?.ancestorDNA ?? _null, + child2DnaId: posMaster.orgChild2?.ancestorDNA ?? _null, + child3DnaId: posMaster.orgChild3?.ancestorDNA ?? _null, + child4DnaId: posMaster.orgChild4?.ancestorDNA ?? _null, + }); + await this.profileEmployeeRepository.save(profile); + await this.employeePositionRepository.save(positionNew); + await CreatePosMasterHistoryEmployee(posMaster.id, req); + //ลบออกคนออกจากโครงสร้างลูกจ้างชั่วคราว + const posMasterTemp = await this.employeeTempPosMasterRepository.findOne({ + where: { + orgRevisionId: orgRevision?.id, + current_holderId: profile.id, + }, + }); + if (posMasterTemp) { + await this.employeeTempPosMasterRepository.update(posMasterTemp.id, { + current_holderId: _null, + }); + await CreatePosMasterHistoryEmployeeTemp(posMasterTemp.id, req); + } + } + }), + ); + await new CallAPI() + .PostData(req, "/placement/appointment/employee-appoint-21/report/excecute", { + profileEmps: _reqBody, + }) + .catch((error) => { + throw new Error("Failed. Cannot update status. ", error); + }); return new HttpSuccess(); } @@ -4544,14 +7630,153 @@ export class CommandController extends Controller { }[]; }, ) { - /** - * Thin wrapper — เรียก ExecuteOrgCommandService.executeCommand40Officer (C-PM-40) - * ทั้ง endpoint นี้และ consumer ใน rabbitmq ใช้ service ตัวเดียวกัน (Linear Flow) - */ - await new ExecuteOrgCommandService().executeCommand40Officer(body.refIds, { - user: { sub: req.user.sub, name: req.user.name }, - req, + // 1. Bulk update status + await this.posMasterActRepository.update( + { id: In(body.refIds.map((x) => x.refId)) }, + { statusReport: "DONE" }, + ); + + // 2. ดึงข้อมูลครบทุก relation ที่จำเป็น + const posMasters = await this.posMasterActRepository.find({ + where: { id: In(body.refIds.map((x) => x.refId)) }, + relations: [ + "posMasterChild", + "posMasterChild.current_holder", + "posMaster", + "posMaster.current_holder", + "posMaster.positions", + "posMaster.orgRoot", + "posMaster.orgChild1", + "posMaster.orgChild2", + "posMaster.orgChild3", + "posMaster.orgChild4", + ], }); + + // 3. ตรวจสอบว่ามี body.refIds[0] หรือไม่ + const firstRef = body.refIds[0]; + if (!firstRef) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบข้อมูล refIds"); + } + + const profileIdsToClearCache = new Set(); + + await Promise.all( + posMasters.map(async (item) => { + // 4. ตรวจสอบข้อมูลที่จำเป็นทั้งหมด + if (!item.posMasterChild?.current_holderId || !item.posMaster) { + console.warn(`ข้ามรายการ ${item.id}: ข้อมูลไม่ครบ`); + return; + } + + if (item.posMasterChild.current_holderId) { + profileIdsToClearCache.add(item.posMasterChild.current_holderId); + } + + // 5. สร้าง orgShortName แบบปลอดภัย + const orgShortName = + [ + item.posMaster?.orgChild4?.orgChild4ShortName, + item.posMaster?.orgChild3?.orgChild3ShortName, + item.posMaster?.orgChild2?.orgChild2ShortName, + item.posMaster?.orgChild1?.orgChild1ShortName, + item.posMaster?.orgRoot?.orgRootShortName, + ].find(Boolean) ?? ""; + + // 6. หา position ที่ถูกเลือกแบบปลอดภัย + const selectedPosition = item.posMaster?.positions; + const positionName = + selectedPosition + ?.map((pos) => pos.positionName) + .filter(Boolean) + .join(", ") ?? "-"; + + // 7. สร้าง metaAct แบบปลอดภัย + const metaAct = { + profileId: item.posMasterChild.current_holderId, + dateStart: firstRef.commandDateAffect ?? null, + dateEnd: null, + position: positionName, + status: true, + commandId: firstRef.commandId ?? null, + createdUserId: req.user?.sub ?? null, + createdFullName: req.user?.name ?? null, + lastUpdateUserId: req.user?.sub ?? null, + lastUpdateFullName: req.user?.name ?? null, + createdAt: new Date(), + lastUpdatedAt: new Date(), + commandNo: firstRef.commandNo ?? null, + refCommandNo: `${firstRef.commandNo ?? ""}/${firstRef.commandYear ? Extension.ToThaiYear(firstRef.commandYear) : ""}`, + commandYear: firstRef.commandYear ? Extension.ToThaiYear(firstRef.commandYear) : null, + posNo: + orgShortName && item.posMaster?.posMasterNo + ? `${orgShortName} ${item.posMaster.posMasterNo}` + : item.posMaster?.posMasterNo ?? "-", + posNoAbb: orgShortName, + commandDateAffect: firstRef.commandDateAffect ?? null, + commandDateSign: firstRef.commandDateSign ?? null, + commandCode: firstRef.commandCode ?? null, + commandName: firstRef.commandName ?? null, + remark: firstRef.remark ?? null, + }; + + try { + // 8. ปิดสถานะรักษาการ + const existingActPositions = await this.actpositionRepository.find({ + where: { + profileId: item.posMasterChild.current_holderId, + status: true, + isDeleted: false, + }, + }); + + if (existingActPositions.length > 0) { + const updatedActPositions = existingActPositions.map((_data) => ({ + ..._data, + status: false, + dateEnd: new Date(), + })); + + await this.actpositionRepository.save(updatedActPositions); + } + + // 9. บันทึกข้อมูลใหม่ + const dataAct = new ProfileActposition(); + Object.assign(dataAct, metaAct); + + const historyAct = new ProfileActpositionHistory(); + Object.assign(historyAct, { ...dataAct, id: undefined }); + + await this.actpositionRepository.save(dataAct); + historyAct.profileActpositionId = dataAct.id; + await this.actpositionHistoryRepository.save(historyAct); + } catch (error) { + console.error(`Error processing item ${item.id}:`, error); + throw new HttpError( + HttpStatus.INTERNAL_SERVER_ERROR, + `เกิดข้อผิดพลาดในการประมวลผลรายการ ${item.id}`, + ); + } + }), + ); + + if (profileIdsToClearCache.size > 0) { + await Promise.all( + Array.from(profileIdsToClearCache).map(async (profileId) => { + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); + + const delAsync = promisify(redisClient.del).bind(redisClient); + await delAsync("role_" + profileId); + await delAsync("menu_" + profileId); + + redisClient.quit(); + }), + ); + } + return new HttpSuccess(); } @@ -4702,14 +7927,166 @@ export class CommandController extends Controller { }[]; }, ) { - /** - * Thin wrapper — เรียก ExecuteOrgCommandService.executeCommand38Officer (C-PM-38) - * ทั้ง endpoint นี้และ consumer ใน rabbitmq ใช้ service ตัวเดียวกัน (Linear Flow) - */ - await new ExecuteOrgCommandService().executeCommand38Officer(body.refIds, { - user: { sub: req.user.sub, name: req.user.name }, - req, + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.refIds.find((x) => x.commandId)?.commandId ?? "" }, }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + await Promise.all( + body.refIds.map(async (item) => { + const posMaster = await this.posMasterRepository.findOne({ + where: { id: item.refId }, + relations: [ + "orgRoot", + "orgChild1", + "orgChild2", + "orgChild3", + "orgChild4", + "current_holder", + "current_holder.posLevel", + "current_holder.posType", + ], + }); + if (!posMaster) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบตำแหน่งดังกล่าว"); + } + if (posMaster.next_holderId != null) { + const orgRootRef = posMaster?.orgRoot ?? null; + const orgChild1Ref = posMaster?.orgChild1 ?? null; + const orgChild2Ref = posMaster?.orgChild2 ?? null; + const orgChild3Ref = posMaster?.orgChild3 ?? null; + const orgChild4Ref = posMaster?.orgChild4 ?? null; + const shortName = + posMaster != null && posMaster.orgChild4 != null + ? `${posMaster.orgChild4.orgChild4ShortName}` + : posMaster != null && posMaster.orgChild3 != null + ? `${posMaster.orgChild3.orgChild3ShortName}` + : posMaster != null && posMaster.orgChild2 != null + ? `${posMaster.orgChild2.orgChild2ShortName}` + : posMaster != null && posMaster.orgChild1 != null + ? `${posMaster.orgChild1.orgChild1ShortName}` + : posMaster != null && posMaster?.orgRoot != null + ? `${posMaster.orgRoot.orgRootShortName}` + : null; + const profile = await this.profileRepository.findOne({ + where: { id: posMaster.next_holderId }, + }); + const position = await this.positionRepository.findOne({ + where: { + posMasterId: posMaster.id, + positionIsSelected: true, + }, + relations: ["posType", "posLevel"], + }); + const dest_item = await this.salaryRepo.findOne({ + where: { profileId: profile?.id }, + order: { order: "DESC" }, + }); + const before = null; + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + profileId: profile?.id, + date: new Date(), + amount: item.amount, + commandId: item.commandId, + positionSalaryAmount: item.positionSalaryAmount, + mouthSalaryAmount: item.mouthSalaryAmount, + position: position?.positionName ?? null, + positionType: position?.posType?.posTypeName ?? null, + positionLevel: position?.posLevel?.posLevelName ?? null, + order: dest_item == null ? 1 : dest_item.order + 1, + orgRoot: orgRootRef?.orgRootName ?? null, + orgChild1: orgChild1Ref?.orgChild1Name ?? null, + orgChild2: orgChild2Ref?.orgChild2Name ?? null, + orgChild3: orgChild3Ref?.orgChild3Name ?? null, + orgChild4: orgChild4Ref?.orgChild4Name ?? null, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + commandNo: item.commandNo, + commandYear: item.commandYear, + posNo: posMaster.posMasterNo, + posNoAbb: shortName, + commandDateAffect: item.commandDateAffect, + commandDateSign: item.commandDateSign, + commandCode: item.commandCode, + commandName: item.commandName, + remark: item.remark, + }; + Object.assign(data, meta); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + + await this.salaryRepo.save(data, { data: req }); + setLogDataDiff(req, { before, after: data }); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + // if (profile != null) { + // profile.position = position?.positionName ?? ""; + // profile.posTypeId = position?.posTypeId ?? ""; + // profile.posLevelId = position?.posLevelId ?? ""; + // profile.lastUpdateUserId = req.user.sub; + // profile.lastUpdateFullName = req.user.name; + // profile.lastUpdatedAt = new Date(); + // await this.profileRepository.save(profile); + // } + } + }), + ); + // const posMasters = await this.posMasterRepository.find({ + // where: { id: In(body.refIds.map((x) => x.refId)) }, + // }); + // const data = posMasters.map((_data) => ({ + // ..._data, + // current_holderId: _data.next_holderId, + // next_holderId: null, + // statusReport: "PENDING", + // })); + // await this.posMasterRepository.save(data); return new HttpSuccess(); } diff --git a/src/controllers/CommandOperatorController.ts b/src/controllers/CommandOperatorController.ts index 18393f53..1a461ab3 100644 --- a/src/controllers/CommandOperatorController.ts +++ b/src/controllers/CommandOperatorController.ts @@ -9,7 +9,7 @@ import { Path, Request, Response, - Get, + Get } from "tsoa"; import { LessThan, MoreThan } from "typeorm"; import { AppDataSource } from "../database/data-source"; @@ -37,7 +37,9 @@ export class CommandOperatorController extends Controller { * @param commandId คีย์คำสั่ง */ @Get("{commandId}") - async getCommandOperatorByCommandId(@Path() commandId: string) { + async getCommandOperatorByCommandId( + @Path() commandId: string + ) { const command = await this.commandRepo.findOne({ where: { id: commandId }, select: { id: true }, @@ -59,7 +61,10 @@ export class CommandOperatorController extends Controller { * @param operatorId คีย์เจ้าหน้าที่ดำเนินการ */ @Get("swap/{direction}/{operatorId}") - async swapCommandOperator(@Path() direction: string, @Path() operatorId: string) { + async swapCommandOperator( + @Path() direction: string, + @Path() operatorId: string, + ) { const source = await this.commandOperatorRepo.findOne({ where: { id: operatorId }, }); @@ -101,7 +106,10 @@ export class CommandOperatorController extends Controller { source.orderNo = dest.orderNo; dest.orderNo = temp; - await Promise.all([this.commandOperatorRepo.save(source), this.commandOperatorRepo.save(dest)]); + await Promise.all([ + this.commandOperatorRepo.save(source), + this.commandOperatorRepo.save(dest), + ]); return new HttpSuccess(); } @@ -133,29 +141,35 @@ export class CommandOperatorController extends Controller { const nextOrderNo = (lastOrderNo?.orderNo ?? 1) + 1; const now = new Date(); - const operator = Object.assign(new CommandOperator(), { - ...body, - commandId: command.id, - orderNo: nextOrderNo, - createdUserId: request.user.sub, - createdFullName: request.user.name, - createdAt: now, - lastUpdateUserId: request.user.sub, - lastUpdateFullName: request.user.name, - lastUpdatedAt: now, - }); + const operator = Object.assign( + new CommandOperator(), + { + ...body, + commandId: command.id, + orderNo: nextOrderNo, + createdUserId: request.user.sub, + createdFullName: request.user.name, + createdAt: now, + lastUpdateUserId: request.user.sub, + lastUpdateFullName: request.user.name, + lastUpdatedAt: now, + } + ); await this.commandOperatorRepo.save(operator); return new HttpSuccess(); } - /** + /** * API ลบเจ้าหน้าที่ดำเนินการที่คำสั่ง * @summary API ลบเจ้าหน้าที่ดำเนินการที่คำสั่ง * @param commandId คีย์คำสั่ง * @param operatorId คีย์เจ้าหน้าที่ดำเนินการ */ @Delete("{commandId}/{operatorId}") - public async deleteCommandOperator(@Path() commandId: string, @Path() operatorId: string) { + public async deleteCommandOperator( + @Path() commandId: string, + @Path() operatorId: string, + ) { const queryRunner = AppDataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); @@ -201,9 +215,10 @@ export class CommandOperatorController extends Controller { return new HttpSuccess(true); } catch (error) { await queryRunner.rollbackTransaction(); - console.error("Delete command operator error:", error); + throw error; } finally { await queryRunner.release(); } } + } diff --git a/src/controllers/DevTestController.ts b/src/controllers/DevTestController.ts deleted file mode 100644 index e3edfaa5..00000000 --- a/src/controllers/DevTestController.ts +++ /dev/null @@ -1,576 +0,0 @@ -import { - Controller, - Post, - Put, - Patch, - Delete, - Route, - Security, - Tags, - Body, - Path, - Request, - Response, - Get, - Query, -} from "tsoa"; -import { AppDataSource } from "../database/data-source"; -import HttpStatus from "../interfaces/http-status"; -import HttpSuccess from "../interfaces/http-success"; -import HttpStatusCode from "../interfaces/http-status"; -import HttpError from "../interfaces/http-error"; -import { Command } from "../entities/Command"; -import { Brackets, LessThan, MoreThan, Double, In, Between, IsNull, Not, Any } from "typeorm"; -import { CommandType } from "../entities/CommandType"; -import { Profile, CreateProfileAllFields } from "../entities/Profile"; -import { RequestWithUser, RequestWithUserWebService } from "../middlewares/user"; -import { OrgRevision } from "../entities/OrgRevision"; -import { ProfileEmployee } from "../entities/ProfileEmployee"; -import { PosMaster } from "../entities/PosMaster"; -import permission from "../interfaces/permission"; -import { viewCurrentTenureOfficer } from "../entities/view/viewCurrentTenureOfficer"; -import { CommandController } from "./CommandController"; -import Extension from "../interfaces/extension"; -import { viewRegistryOfficer } from "../entities/view/viewRegistryOfficer"; -import { viewRegistryEmployee } from "../entities/view/viewRegistryEmployee"; -import { Registry } from "../entities/Registry"; -import { RegistryEmployee } from "../entities/RegistryEmployee"; -import { TenurePositionOfficer } from "../entities/TenurePositionOfficer"; -import { PosMasterAssign, PosMasterAssignDTO } from "../entities/PosMasterAssign"; -import { PermissionProfile } from "../entities/PermissionProfile"; -import { OrgRoot } from "../entities/OrgRoot"; -import { MetaWorkflow } from "../entities/MetaWorkflow"; -import { MetaState } from "../entities/MetaState"; -import { MetaStateOperator } from "../entities/MetaStateOperator"; -import { Workflow } from "../entities/Workflow"; -import { State } from "../entities/State"; -import { StateOperator } from "../entities/StateOperator"; -import { StateOperatorUser } from "../entities/StateOperatorUser"; -import { - commandTypePath, - calculateGovAge, - calculateAge, - calculateRetireDate, - calculateRetireLaw, - removeProfileInOrganize, - setLogDataDiff, -} from "../interfaces/utils"; -import CallAPI from "../interfaces/call-api"; -import { PostRetireToExprofile } from "./ExRetirementController" -import { Position } from "../entities/Position"; -import { PosLevel } from "../entities/PosLevel"; -import { TenureLevelOfficer } from "../entities/TenureLevelOfficer"; -import { TenurePositionEmployee } from "../entities/TenurePositionEmployee"; -import { TenureLevelEmployee } from "../entities/TenureLevelEmployee"; -import { TenurePositionExecutiveOfficer } from "../entities/TenurePositionExecutiveOfficer"; - -@Route("api/v1/org/DevTest") -@Tags("DevTest") -@Security("bearerAuth") -@Response( - HttpStatusCode.INTERNAL_SERVER_ERROR, - "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", -) -export class DevTestController extends Controller { - private commandRepository = AppDataSource.getRepository(Command); - private commandTypeRepository = AppDataSource.getRepository(CommandType); - private orgRevisionRepo = AppDataSource.getRepository(OrgRevision); - private orgRootRepo = AppDataSource.getRepository(OrgRoot); - private posMasterRepo = AppDataSource.getRepository(PosMaster); - private profileRepo = AppDataSource.getRepository(Profile); - private profileEmpRepo = AppDataSource.getRepository(ProfileEmployee); - private registryRepo = AppDataSource.getRepository(Registry); - private registryEmployeeRepo = AppDataSource.getRepository(RegistryEmployee); - private posMasterAssignRepository = AppDataSource.getRepository(PosMasterAssign); - private permissionProfilesRepository = AppDataSource.getRepository(PermissionProfile); - private profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); - private metaWorkflowRepo = AppDataSource.getRepository(MetaWorkflow); - private metaStateRepo = AppDataSource.getRepository(MetaState); - private metaStateOperatorRepo = AppDataSource.getRepository(MetaStateOperator); - private workflowRepo = AppDataSource.getRepository(Workflow); - private stateRepo = AppDataSource.getRepository(State); - private stateOperatorRepo = AppDataSource.getRepository(StateOperator); - private stateOperatorUserRepo = AppDataSource.getRepository(StateOperatorUser); - private positionRepository = AppDataSource.getRepository(Position); - private positionOfficerRepo = AppDataSource.getRepository(TenurePositionOfficer); - private positionEmployeeRepo = AppDataSource.getRepository(TenurePositionEmployee); - private levelOfficerRepo = AppDataSource.getRepository(TenureLevelOfficer); - private levelEmployeeRepo = AppDataSource.getRepository(TenureLevelEmployee); - private positionExecutiveOfficerRepo = AppDataSource.getRepository( - TenurePositionExecutiveOfficer, - ); - - @Patch("tick-officer-registry") - public async calculateOfficerPosition( - @Request() req: RequestWithUser, - @Body() - body: { - profileIds: string[]; - }, - ) { - - console.log("1.") - /** - * =============================== - * PREPARE DATA - * =============================== - */ - const profile = await this.profileRepo.find({ - where: { id: In(body.profileIds) }, - relations: { - posLevel: true, - posType: true, - }, - }); - - if (!profile.length) return; - - const [{ today }] = await AppDataSource.query( - "SELECT CURRENT_DATE() as today", - ); - - const orgRevision = await this.orgRevisionRepo.findOne({ - select: ["id"], - where: { - orgRevisionIsDraft: false, - orgRevisionIsCurrent: true, - }, - }); - - /** - * =============================== - * TRANSACTION - * =============================== - */ - const queryRunner = AppDataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); - console.log("2.") - try { - /** - * =============================== - * RESULT BUFFERS (SAVE ARRAY) - * =============================== - */ - const positionOfficerBulk: any[] = []; - const levelOfficerBulk: any[] = []; - const executiveOfficerBulk: any[] = []; - console.log("3.") - /** - * =============================== - * MAIN LOOP (SINGLE LOOP) - * =============================== - */ - for (const x of profile) { - const currentDate = - x.isLeave && x.leaveDate - ? Extension.toDateOnlyString(x.leaveDate) - : today; - /** - * ==================================== - * PARALLEL STORED PROCEDURES - * ==================================== - */ - const [ - positionResult, - levelResult, - executiveResult, - ] = await Promise.all([ - AppDataSource.query("CALL GetProfileSalaryPosition(?, ?)", [ - x.id, - currentDate, - ]), - AppDataSource.query("CALL GetProfileSalaryLevel(?, ?)", [ - x.id, - currentDate, - ]), - AppDataSource.query("CALL GetProfileSalaryExecutive(?, ?)", [ - x.id, - currentDate, - ]), - ]); - console.log("4.",x.id) - /** - * ==================================== - * POSITION - * ==================================== - */ - const posRows = positionResult?.[0] ?? []; - const posMap = - posRows.length > 1 - ? posRows.slice(1).map((r: any, i: number) => ({ - days_diff: Number(r.days_diff) || 0, - positionName: posRows[i]?.positionName, - })) - : []; - - const posCal = posMap - .filter((p:any) => p.positionName === x.position) - .reduce( - (a:any, c:any) => ({ - days_diff: a.days_diff + c.days_diff, - positionName: c.positionName, - }), - { days_diff: 0, positionName: null }, - ); - - positionOfficerBulk.push({ - profileId: x.id, - positionName: posCal.positionName, - days_diff: posCal.days_diff, - Years: Math.floor(posCal.days_diff / 365.2524), - Months: Math.floor((posCal.days_diff / 30.4375) % 12), - Days: Math.floor(posCal.days_diff % 30.4375), - }); - console.log("5.",x.id) - /** - * ==================================== - * 2️⃣ POSITION LEVEL - * ==================================== - */ - const lvlRows = levelResult?.[0] ?? []; - const lvlMap = - lvlRows.length > 1 - ? lvlRows.slice(1).map((r: any, i: number) => ({ - days_diff: Number(r.days_diff) || 0, - positionType: lvlRows[i]?.positionType, - positionLevel: lvlRows[i]?.positionLevel, - positionCee: lvlRows[i]?.positionCee, - })) - : []; - - const lvlCal = lvlMap - .filter( - (l:any) => - l.positionLevel === x.posLevel?.posLevelName && - l.positionType === x.posType?.posTypeName, - ) - .reduce( - (a:any, c:any) => ({ - days_diff: a.days_diff + c.days_diff, - positionType: c.positionType, - positionLevel: c.positionLevel, - positionCee: c.positionCee, - }), - { - days_diff: 0, - positionType: null, - positionLevel: null, - positionCee: null, - }, - ); - - levelOfficerBulk.push({ - profileId: x.id, - positionType: lvlCal.positionType, - positionLevel: lvlCal.positionLevel, - positionCee: lvlCal.positionCee, - days_diff: lvlCal.days_diff, - Years: x.posLevel ? (lvlCal.days_diff / 365.2524).toFixed(4) : 0, - Months: x.posLevel ? ((lvlCal.days_diff / 30.4375) % 12).toFixed(4) : 0, - Days: x.posLevel ? (lvlCal.days_diff % 30.4375).toFixed(4) : 0, - }); - console.log("6.",x.id) - /** - * ==================================== - * 3️⃣ POSITION EXECUTIVE - * ==================================== - */ - const exeRows = executiveResult?.[0] ?? []; - const exeMap = - exeRows.length > 1 - ? exeRows.slice(1).map((r: any, i: number) => ({ - days_diff: Number(r.days_diff) || 0, - positionExecutive: exeRows[i]?.positionExecutive, - })) - : []; - - const position = await this.positionRepository.findOne({ - where: { - positionIsSelected: true, - posMaster: { - orgRevisionId: orgRevision?.id, - current_holderId: x.id, - }, - }, - order: { createdAt: "DESC" }, - relations: { - posExecutive: true, - }, - }); - - const exeName = position?.posExecutive?.posExecutiveName; - - const exeCal = exeMap - .filter((e:any) => exeName && e.positionExecutive === exeName) - .reduce( - (a:any, c:any) => ({ - days_diff: a.days_diff + c.days_diff, - positionExecutive: c.positionExecutive, - }), - { days_diff: 0, positionExecutive: null }, - ); - - executiveOfficerBulk.push({ - profileId: x.id, - positionExecutiveName: exeCal.positionExecutive, - days_diff: exeCal.days_diff, - Years: (exeCal.days_diff / 365.2524).toFixed(4), - Months: ((exeCal.days_diff / 30.4375) % 12).toFixed(4), - Days: (exeCal.days_diff % 30.4375).toFixed(4), - }); - } - console.log("7.") - /** - * =============================== - * CLEAR ALL DATA AND SAVE ARRAY (BULK) - * =============================== - */ - await queryRunner.manager - .createQueryBuilder() - .delete() - .from(this.positionOfficerRepo.target) - .execute(); - - await queryRunner.manager - .createQueryBuilder() - .delete() - .from(this.levelOfficerRepo.target) - .execute(); - - await queryRunner.manager - .createQueryBuilder() - .delete() - .from(this.positionExecutiveOfficerRepo.target) - .execute(); - console.log("8.") - await queryRunner.manager.save(this.positionOfficerRepo.target, positionOfficerBulk); - await queryRunner.manager.save(this.levelOfficerRepo.target, levelOfficerBulk); - await queryRunner.manager.save(this.positionExecutiveOfficerRepo.target,executiveOfficerBulk); - console.log("9.") - /** - * =============================== - * REGISTRY OFFICER (SYNC VIEW) - * =============================== - */ - const allRegis = await queryRunner.manager - .getRepository(viewRegistryOfficer) - .createQueryBuilder("registryOfficer") - .where("registryOfficer.profileId IN (:...profileIds)", { - profileIds: new Set(profile.map((p) => p.id)) - }) - .getMany(); - - const mapRegistryData = allRegis.map((x) => ({ - ...x, - isProbation: Boolean(x.isProbation), - isLeave: Boolean(x.isLeave), - isRetirement: Boolean(x.isRetirement), - Educations: x.Educations ? JSON.stringify(x.Educations) : "", - })); - console.log("10.") - - await queryRunner.manager - .createQueryBuilder() - .delete() - .from(this.registryRepo.target) - .execute(); - - if (mapRegistryData.length > 0) { - await queryRunner.manager.save(this.registryRepo.target, mapRegistryData); - } - console.log("11.") - /** - * =============================== - * COMMIT - * =============================== - */ - await queryRunner.commitTransaction(); - } catch (error) { - await queryRunner.rollbackTransaction(); - throw error; - } finally { - await queryRunner.release(); - } - } - - @Post("getDNA") - public async GetData( - @Request() req: RequestWithUser - ){ - let _data: any = { - root: null, - child1: null, - child2: null, - child3: null, - child4: null, - }; - - _data = await new permission().PermissionOrgList(req, "COMMAND"); - return new HttpSuccess(_data); - } - - @Post("calculateGovAge") - public async calculateGovAge( - @Request() req: RequestWithUser, - @Body() - body: { - profileId: string; - }, - ){ - return new HttpSuccess(await calculateGovAge(body.profileId, "OFFICER")); - } - - /** - * @summary Test Job กวาดออกคำสั่ง ทำงานทุกๆตี2 - */ - @Post("cronjobCommand") - async CronjobCommand() { - const commandController = new CommandController(); - await commandController.cronjobCommand(); - } - - /** - * @summary payload & Endpoint ออกคำสั่ง - */ - @Put("path-excec/{id}") - async Bright( - @Path() id: string, - @Request() request: RequestWithUser, - ) { - const command = await this.commandRepository.findOne({ - where: { id: id }, - relations: ["commandType", "commandRecives", "commandSends", "commandSends.commandSendCCs"], - }); - if (!command) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลคำสั่งนี้"); - } - const path = commandTypePath(command.commandType.code); - return new HttpSuccess({ - path: path + "/excecute", - refIds: command.commandRecives - .filter((x) => x.refId != null) - .map((x) => ({ - refId: x.refId, - commandNo: command.commandNo, - commandYear: command.commandYear, - commandId: command.id, - remark: command.positionDetail, - amount: x.amount, - amountSpecial: x.amountSpecial, - positionSalaryAmount: x.positionSalaryAmount, - mouthSalaryAmount: x.mouthSalaryAmount, - commandCode: command.commandType.commandCode, - commandName: command.commandType.name, - commandDateAffect: command.commandExcecuteDate, - commandDateSign: command.commandAffectDate, - })), - }); - } - - /** - * API รายละเอียดรายการคำสั่ง tab4 แนบท้าย - * @summary API รายละเอียดรายการคำสั่ง tab4 แนบท้าย - * @param {string} id Id คำสั่ง - * @param {string} profileId profileId - */ - @Get("tab4/attachment/{id}/{profileId}") - async GetByIdTab4Attachment( - @Path() id: string, - @Path() profileId: string, - @Request() request: RequestWithUser - ) { - await new permission().PermissionGet(request, "COMMAND"); - - let profile: Profile | ProfileEmployee | null = null; - profile = await this.profileRepo.findOne({ where: { id: profileId } }); - if (!profile) { - profile = await this.profileEmpRepo.findOne({ where: { id: profileId } }); - if (!profile) - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลบุคคลากรนี้"); - } - - const command = await this.commandRepository.findOne({ - where: { id }, - relations: ["commandType", "commandRecives"], - }); - if (!command) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลคำสั่งนี้"); - } - - let _command: any = []; - const path = commandTypePath(command.commandType.code); - if (path == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทคำสั่งนี้ในระบบ"); - await new CallAPI() - .PostData(request, path + "/attachment", { - refIds: command.commandRecives - .filter((x) => - x.refId != null && - x.profileId != null && x.profileId == profileId - ) - .map((x) => ({ - refId: x.refId, - Sequence: x.order, - CitizenId: x.citizenId, - Prefix: x.prefix, - FirstName: x.firstName, - LastName: x.lastName, - Amount: x.amount, - PositionSalaryAmount: x.positionSalaryAmount, - MouthSalaryAmount: x.mouthSalaryAmount, - RemarkHorizontal: x.remarkHorizontal, - RemarkVertical: x.remarkVertical, - CommandYear: command.commandYear, - CommandExcecuteDate: command.commandExcecuteDate, - })), - }) - .then(async (res) => { - _command = res; - }) - .catch(() => {}); - - let issue = - command.isBangkok == "OFFICE" - ? "สำนักปลัดกรุงเทพมหานคร" - : command.isBangkok == "BANGKOK" - ? "กรุงเทพมหานคร" - : null; - if (issue == null) { - const orgRevisionActive = await this.orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - relations: ["posMasters", "posMasters.orgRoot"], - }); - if (orgRevisionActive != null) { - const profile = await this.profileRepo.findOne({ - where: { - keycloak: command.createdUserId.toString(), - }, - }); - if (profile != null) { - issue = - orgRevisionActive?.posMasters?.filter((x) => x.current_holderId == profile.id)[0] - ?.orgRoot?.orgRootName || null; - } - } - } - if (issue == null) issue = "..................................."; - return new HttpSuccess({ - template: command.commandType.fileAttachment, - reportName: "xlsx-report", - data: { - data: _command, - issuerOrganizationName: issue, - commandNo: command.commandNo == null ? "" : Extension.ToThaiNumber(command.commandNo), - commandYear: - command.commandYear == null - ? "" - : Extension.ToThaiNumber(Extension.ToThaiYear(command.commandYear).toString()), - commandExcecuteDate: - command.commandExcecuteDate == null - ? "" - : Extension.ToThaiNumber(Extension.ToThaiFullDate2(command.commandExcecuteDate)), - }, - }); - } - -} diff --git a/src/controllers/EmployeePositionController.ts b/src/controllers/EmployeePositionController.ts index 5809841c..6ead5522 100644 --- a/src/controllers/EmployeePositionController.ts +++ b/src/controllers/EmployeePositionController.ts @@ -39,7 +39,8 @@ import { RequestWithUser } from "../middlewares/user"; import permission from "../interfaces/permission"; import { setLogDataDiff } from "../interfaces/utils"; import { - CreatePosMasterHistoryEmployee + CreatePosMasterHistoryEmployee, + CreatePosMasterHistoryOfficer, } from "../services/PositionService"; import { PosMasterEmployeeHistory } from "../entities/PosMasterEmployeeHistory"; import { KeycloakAttributeService } from "../services/KeycloakAttributeService"; @@ -1057,11 +1058,11 @@ export class EmployeePositionController extends Controller { let checkChildConditions: any = {}; let keywordAsInt: any; let searchShortName = "1=1"; - let searchShortName0 = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName1 = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName2 = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName3 = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName4 = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; + let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo)`; + let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo)`; + let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo)`; + let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo)`; + let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo)`; let _data = await new permission().PermissionOrgList(request, "SYS_ORG_EMP"); if (body.type === 0) { typeCondition = { @@ -1071,7 +1072,7 @@ export class EmployeePositionController extends Controller { checkChildConditions = { orgChild1Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } else { } } else if (body.type === 1) { @@ -1082,7 +1083,7 @@ export class EmployeePositionController extends Controller { checkChildConditions = { orgChild2Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } else { } } else if (body.type === 2) { @@ -1093,7 +1094,7 @@ export class EmployeePositionController extends Controller { checkChildConditions = { orgChild3Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } else { } } else if (body.type === 3) { @@ -1104,14 +1105,14 @@ export class EmployeePositionController extends Controller { checkChildConditions = { orgChild4Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } else { } } else if (body.type === 4) { typeCondition = { orgChild4Id: body.id, }; - searchShortName = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } let findPosition: any; let masterId = new Array(); @@ -1139,8 +1140,10 @@ export class EmployeePositionController extends Controller { select: ["posMasterId"], }); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId)); - const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); - keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; + keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10); + if (isNaN(keywordAsInt)) { + keywordAsInt = "P@ssw0rd!z"; + } masterId = [...new Set(masterId)]; } @@ -1155,7 +1158,7 @@ export class EmployeePositionController extends Controller { ...(body.keyword && (masterId.length > 0 ? { id: In(masterId) } - : /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), + : { posMasterNo: Like(`%${body.keyword}%`) })), }, ]; @@ -2376,47 +2379,26 @@ export class EmployeePositionController extends Controller { dataMaster.positions.forEach(async (position) => { if (position.id === requestBody.position) { position.positionIsSelected = true; + const profile = await this.profileRepository.findOne({ + where: { id: requestBody.profileId }, + }); + if (profile != null) { + const _null: any = null; + profile.posLevelId = position?.posLevelId ?? _null; + profile.posTypeId = position?.posTypeId ?? _null; + profile.position = position?.positionName ?? _null; + await this.profileRepository.save(profile); + } } else { position.positionIsSelected = false; } await this.employeePositionRepository.save(position); }); - const _null: any = null; - const before = null; + dataMaster.isSit = requestBody.isSit; + dataMaster.current_holderId = requestBody.profileId; dataMaster.lastUpdatedAt = new Date(); - - //เช็คถ้า revision ปัจจุบันให้ปั๊มที่ profile - const chkRevision = await this.orgRevisionRepository.findOne({ - where: { id: dataMaster.orgRevisionId }, - }); - if (chkRevision?.orgRevisionIsCurrent) { - const _profile = await this.profileRepository.findOne({ - where: { id: requestBody.profileId }, - }); - if (_profile) { - let _position = await this.employeePositionRepository.findOne({ - where: { id: requestBody.position, posMasterId: requestBody.posMaster }, - }); - if (_position) { - - // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ - if (!dataMaster.isSit) { - _profile.position = _position.positionName; - _profile.posTypeId = _position.posTypeId; - _profile.posLevelId = _position.posLevelId; - } - await this.profileRepository.save(_profile); - setLogDataDiff(request, { before, after: _profile }); - } - } - dataMaster.current_holderId = requestBody.profileId; - dataMaster.next_holderId = _null; - } else { - dataMaster.next_holderId = requestBody.profileId; - dataMaster.current_holderId = _null; - } - + // dataMaster.next_holderId = requestBody.profileId; await this.employeePosMasterRepository.save(dataMaster); await CreatePosMasterHistoryEmployee(dataMaster.id, request); diff --git a/src/controllers/EmployeeTempPositionController.ts b/src/controllers/EmployeeTempPositionController.ts index ec17bef5..e5229e67 100644 --- a/src/controllers/EmployeeTempPositionController.ts +++ b/src/controllers/EmployeeTempPositionController.ts @@ -777,11 +777,11 @@ export class EmployeeTempPositionController extends Controller { let checkChildConditions: any = {}; let keywordAsInt: any; let searchShortName = "1=1"; - let searchShortName0 = `CONCAT(orgRoot.orgRootShortName,' ',posMaster.posMasterNo)`; - let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName,' ',posMaster.posMasterNo)`; - let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName,' ',posMaster.posMasterNo)`; - let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName,' ',posMaster.posMasterNo)`; - let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName,' ',posMaster.posMasterNo)`; + let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo)`; + let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo)`; + let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo)`; + let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo)`; + let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo)`; let _data = await new permission().PermissionOrgList(request, "SYS_ORG_TEMP"); if (body.type === 0) { typeCondition = { @@ -791,7 +791,7 @@ export class EmployeeTempPositionController extends Controller { checkChildConditions = { orgChild1Id: IsNull(), }; - searchShortName = `CONCAT(orgRoot.orgRootShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } else { } } else if (body.type === 1) { @@ -802,7 +802,7 @@ export class EmployeeTempPositionController extends Controller { checkChildConditions = { orgChild2Id: IsNull(), }; - searchShortName = `CONCAT(orgChild1.orgChild1ShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } else { } } else if (body.type === 2) { @@ -813,7 +813,7 @@ export class EmployeeTempPositionController extends Controller { checkChildConditions = { orgChild3Id: IsNull(), }; - searchShortName = `CONCAT(orgChild2.orgChild2ShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } else { } } else if (body.type === 3) { @@ -824,14 +824,14 @@ export class EmployeeTempPositionController extends Controller { checkChildConditions = { orgChild4Id: IsNull(), }; - searchShortName = `CONCAT(orgChild3.orgChild3ShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } else { } } else if (body.type === 4) { typeCondition = { orgChild4Id: body.id, }; - searchShortName = `CONCAT(orgChild4.orgChild4ShortName,' ',posMaster.posMasterNo) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } let findPosition: any; let masterId = new Array(); @@ -859,8 +859,10 @@ export class EmployeeTempPositionController extends Controller { select: ["posMasterTempId"], }); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterTempId)); - const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); - keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; + keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10); + if (isNaN(keywordAsInt)) { + keywordAsInt = "P@ssw0rd!z"; + } masterId = [...new Set(masterId)]; } @@ -875,7 +877,7 @@ export class EmployeeTempPositionController extends Controller { ...(body.keyword && (masterId.length > 0 ? { id: In(masterId) } - : /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), + : { posMasterNo: Like(`%${body.keyword}%`) })), }, ]; let query = AppDataSource.getRepository(EmployeeTempPosMaster) diff --git a/src/controllers/OrgChild1Controller.ts b/src/controllers/OrgChild1Controller.ts index 6817d6fd..6e44af05 100644 --- a/src/controllers/OrgChild1Controller.ts +++ b/src/controllers/OrgChild1Controller.ts @@ -74,11 +74,6 @@ export class OrgChild1Controller { DIVISION_CODE: orgChild1.DIVISION_CODE, SECTION_CODE: orgChild1.SECTION_CODE, JOB_CODE: orgChild1.JOB_CODE, - ROOT_CODE: orgChild1.ROOT_CODE, - CHILD1_CODE: orgChild1.CHILD1_CODE, - CHILD2_CODE: orgChild1.CHILD2_CODE, - CHILD3_CODE: orgChild1.CHILD3_CODE, - CHILD4_CODE: orgChild1.CHILD4_CODE, orgCode: orgChild1.orgRoot.orgRootCode + orgChild1.orgChild1Code, }; return new HttpSuccess(getOrgChild1); @@ -351,11 +346,6 @@ export class OrgChild1Controller { DIVISION_CODE: requestBody.DIVISION_CODE != null ? requestBody.DIVISION_CODE : _null, SECTION_CODE: requestBody.SECTION_CODE != null ? requestBody.SECTION_CODE : _null, JOB_CODE: requestBody.JOB_CODE != null ? requestBody.JOB_CODE : _null, - ROOT_CODE: requestBody.ROOT_CODE != null ? requestBody.ROOT_CODE : _null, - CHILD1_CODE: requestBody.CHILD1_CODE != null ? requestBody.CHILD1_CODE : _null, - CHILD2_CODE: requestBody.CHILD2_CODE != null ? requestBody.CHILD2_CODE : _null, - CHILD3_CODE: requestBody.CHILD3_CODE != null ? requestBody.CHILD3_CODE : _null, - CHILD4_CODE: requestBody.CHILD4_CODE != null ? requestBody.CHILD4_CODE : _null, isOfficer: requestBody.isOfficer, isInformation: requestBody.isInformation, orgChild1PhoneEx: requestBody.orgChild1PhoneEx, diff --git a/src/controllers/OrgChild2Controller.ts b/src/controllers/OrgChild2Controller.ts index 650cc96e..28ce564f 100644 --- a/src/controllers/OrgChild2Controller.ts +++ b/src/controllers/OrgChild2Controller.ts @@ -85,11 +85,6 @@ export class OrgChild2Controller extends Controller { DIVISION_CODE: orgChild2.DIVISION_CODE, SECTION_CODE: orgChild2.SECTION_CODE, JOB_CODE: orgChild2.JOB_CODE, - ROOT_CODE: orgChild2.ROOT_CODE, - CHILD1_CODE: orgChild2.CHILD1_CODE, - CHILD2_CODE: orgChild2.CHILD2_CODE, - CHILD3_CODE: orgChild2.CHILD3_CODE, - CHILD4_CODE: orgChild2.CHILD4_CODE, orgCode: orgChild2.orgRoot.orgRootCode + orgChild2.orgChild2Code, }; return new HttpSuccess(getOrgChild2); @@ -257,11 +252,6 @@ export class OrgChild2Controller extends Controller { DIVISION_CODE: requestBody.DIVISION_CODE != null ? requestBody.DIVISION_CODE : _null, SECTION_CODE: requestBody.SECTION_CODE != null ? requestBody.SECTION_CODE : _null, JOB_CODE: requestBody.JOB_CODE != null ? requestBody.JOB_CODE : _null, - ROOT_CODE: requestBody.ROOT_CODE != null ? requestBody.ROOT_CODE : _null, - CHILD1_CODE: requestBody.CHILD1_CODE != null ? requestBody.CHILD1_CODE : _null, - CHILD2_CODE: requestBody.CHILD2_CODE != null ? requestBody.CHILD2_CODE : _null, - CHILD3_CODE: requestBody.CHILD3_CODE != null ? requestBody.CHILD3_CODE : _null, - CHILD4_CODE: requestBody.CHILD4_CODE != null ? requestBody.CHILD4_CODE : _null, orgChild2PhoneEx: requestBody.orgChild2PhoneEx, orgChild2PhoneIn: requestBody.orgChild2PhoneIn, orgChild2Fax: requestBody.orgChild2Fax, diff --git a/src/controllers/OrgChild3Controller.ts b/src/controllers/OrgChild3Controller.ts index d8cc5939..4ed10804 100644 --- a/src/controllers/OrgChild3Controller.ts +++ b/src/controllers/OrgChild3Controller.ts @@ -69,11 +69,6 @@ export class OrgChild3Controller { DIVISION_CODE: orgChild3.DIVISION_CODE, SECTION_CODE: orgChild3.SECTION_CODE, JOB_CODE: orgChild3.JOB_CODE, - ROOT_CODE: orgChild3.ROOT_CODE, - CHILD1_CODE: orgChild3.CHILD1_CODE, - CHILD2_CODE: orgChild3.CHILD2_CODE, - CHILD3_CODE: orgChild3.CHILD3_CODE, - CHILD4_CODE: orgChild3.CHILD4_CODE, orgCode: orgChild3.orgRoot.orgRootCode + orgChild3.orgChild3Code, }; return new HttpSuccess(getOrgChild3); @@ -212,11 +207,6 @@ export class OrgChild3Controller { DIVISION_CODE: requestBody.DIVISION_CODE != null ? requestBody.DIVISION_CODE : _null, SECTION_CODE: requestBody.SECTION_CODE != null ? requestBody.SECTION_CODE : _null, JOB_CODE: requestBody.JOB_CODE != null ? requestBody.JOB_CODE : _null, - ROOT_CODE: requestBody.ROOT_CODE != null ? requestBody.ROOT_CODE : _null, - CHILD1_CODE: requestBody.CHILD1_CODE != null ? requestBody.CHILD1_CODE : _null, - CHILD2_CODE: requestBody.CHILD2_CODE != null ? requestBody.CHILD2_CODE : _null, - CHILD3_CODE: requestBody.CHILD3_CODE != null ? requestBody.CHILD3_CODE : _null, - CHILD4_CODE: requestBody.CHILD4_CODE != null ? requestBody.CHILD4_CODE : _null, orgChild3PhoneEx: requestBody.orgChild3PhoneEx, orgChild3PhoneIn: requestBody.orgChild3PhoneIn, orgChild3Fax: requestBody.orgChild3Fax, diff --git a/src/controllers/OrgChild4Controller.ts b/src/controllers/OrgChild4Controller.ts index 4dac9e82..e18c15f9 100644 --- a/src/controllers/OrgChild4Controller.ts +++ b/src/controllers/OrgChild4Controller.ts @@ -82,11 +82,6 @@ export class OrgChild4Controller extends Controller { DIVISION_CODE: orgChild4.DIVISION_CODE, SECTION_CODE: orgChild4.SECTION_CODE, JOB_CODE: orgChild4.JOB_CODE, - ROOT_CODE: orgChild4.ROOT_CODE, - CHILD1_CODE: orgChild4.CHILD1_CODE, - CHILD2_CODE: orgChild4.CHILD2_CODE, - CHILD3_CODE: orgChild4.CHILD3_CODE, - CHILD4_CODE: orgChild4.CHILD4_CODE, orgCode: orgChild4.orgRoot.orgRootCode + orgChild4.orgChild4Code, }; return new HttpSuccess(getOrgChild4); @@ -259,11 +254,6 @@ export class OrgChild4Controller extends Controller { DIVISION_CODE: requestBody.DIVISION_CODE != null ? requestBody.DIVISION_CODE : _null, SECTION_CODE: requestBody.SECTION_CODE != null ? requestBody.SECTION_CODE : _null, JOB_CODE: requestBody.JOB_CODE != null ? requestBody.JOB_CODE : _null, - ROOT_CODE: requestBody.ROOT_CODE != null ? requestBody.ROOT_CODE : _null, - CHILD1_CODE: requestBody.CHILD1_CODE != null ? requestBody.CHILD1_CODE : _null, - CHILD2_CODE: requestBody.CHILD2_CODE != null ? requestBody.CHILD2_CODE : _null, - CHILD3_CODE: requestBody.CHILD3_CODE != null ? requestBody.CHILD3_CODE : _null, - CHILD4_CODE: requestBody.CHILD4_CODE != null ? requestBody.CHILD4_CODE : _null, orgChild4PhoneEx: requestBody.orgChild4PhoneEx, orgChild4PhoneIn: requestBody.orgChild4PhoneIn, orgChild4Fax: requestBody.orgChild4Fax, diff --git a/src/controllers/OrgRootController.ts b/src/controllers/OrgRootController.ts index 97f3f6a3..45bf1436 100644 --- a/src/controllers/OrgRootController.ts +++ b/src/controllers/OrgRootController.ts @@ -83,11 +83,6 @@ export class OrgRootController extends Controller { DIVISION_CODE: orgRoot.DIVISION_CODE, SECTION_CODE: orgRoot.SECTION_CODE, JOB_CODE: orgRoot.JOB_CODE, - ROOT_CODE: orgRoot.ROOT_CODE, - CHILD1_CODE: orgRoot.CHILD1_CODE, - CHILD2_CODE: orgRoot.CHILD2_CODE, - CHILD3_CODE: orgRoot.CHILD3_CODE, - CHILD4_CODE: orgRoot.CHILD4_CODE, orgCode: orgRoot.orgRootCode + "00", }; return new HttpSuccess(getOrgRoot); @@ -355,11 +350,6 @@ export class OrgRootController extends Controller { DIVISION_CODE: requestBody.DIVISION_CODE != null ? requestBody.DIVISION_CODE : _null, SECTION_CODE: requestBody.SECTION_CODE != null ? requestBody.SECTION_CODE : _null, JOB_CODE: requestBody.JOB_CODE != null ? requestBody.JOB_CODE : _null, - ROOT_CODE: requestBody.ROOT_CODE != null ? requestBody.ROOT_CODE : _null, - CHILD1_CODE: requestBody.CHILD1_CODE != null ? requestBody.CHILD1_CODE : _null, - CHILD2_CODE: requestBody.CHILD2_CODE != null ? requestBody.CHILD2_CODE : _null, - CHILD3_CODE: requestBody.CHILD3_CODE != null ? requestBody.CHILD3_CODE : _null, - CHILD4_CODE: requestBody.CHILD4_CODE != null ? requestBody.CHILD4_CODE : _null, }); await this.orgRootRepository.save(orgRoot, { data: request }); setLogDataDiff(request, { before, after: orgRoot }); diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 8358313b..eb91c408 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -214,7 +214,6 @@ export class OrganizationController extends Controller { await sendToQueueOrgDraft(msg); return new HttpSuccess("Draft is being created... Processing in the background."); } catch (error: any) { - console.error("Error creating draft organization:", error); throw error; } } @@ -468,157 +467,157 @@ export class OrganizationController extends Controller { 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() + .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() + .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() + .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() + .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( @@ -1271,13 +1270,13 @@ export class OrganizationController extends Controller { where: orgRevision.orgRevisionIsCurrent && !orgRevision.orgRevisionIsDraft ? { - orgRevisionId: id, - current_holderId: profile.id, - } + orgRevisionId: id, + current_holderId: profile.id, + } : { - orgRevisionId: id, - next_holderId: profile.id, - }, + orgRevisionId: id, + next_holderId: profile.id, + }, }); if (!posMaster) return new HttpSuccess([]); @@ -1441,14 +1440,7 @@ export class OrganizationController extends Controller { orgRoot.orgRootCode + orgChild1.orgChild1Code + " " + - orgChild1.orgChild1ShortName + - "/" + - orgRoot.orgRootName + - " " + - orgRoot.orgRootCode + - "00" + - " " + - orgRoot.orgRootShortName, + orgChild1.orgChild1ShortName, // totalPosition: child1Counts.totalPosition, // totalPositionCurrentUse: child1Counts.totalPositionCurrentUse, // totalPositionCurrentVacant: child1Counts.totalPositionCurrentVacant, @@ -1498,21 +1490,7 @@ export class OrganizationController extends Controller { orgRoot.orgRootCode + orgChild2.orgChild2Code + " " + - orgChild2.orgChild2ShortName + - "/" + - orgChild1.orgChild1Name + - " " + - orgRoot.orgRootCode + - orgChild1.orgChild1Code + - " " + - orgChild1.orgChild1ShortName + - "/" + - orgRoot.orgRootName + - " " + - orgRoot.orgRootCode + - "00" + - " " + - orgRoot.orgRootShortName, + orgChild2.orgChild2ShortName, // totalPosition: child2Counts.totalPosition, // totalPositionCurrentUse: child2Counts.totalPositionCurrentUse, // totalPositionCurrentVacant: child2Counts.totalPositionCurrentVacant, @@ -1563,28 +1541,7 @@ export class OrganizationController extends Controller { 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, + orgChild3.orgChild3ShortName, // totalPosition: child3Counts.totalPosition, // totalPositionCurrentUse: child3Counts.totalPositionCurrentUse, // totalPositionCurrentVacant: child3Counts.totalPositionCurrentVacant, @@ -1637,35 +1594,7 @@ export class OrganizationController extends Controller { 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, + orgChild4.orgChild4ShortName, // totalPosition: child4Counts.totalPosition, // totalPositionCurrentUse: child4Counts.totalPositionCurrentUse, // totalPositionCurrentVacant: child4Counts.totalPositionCurrentVacant, @@ -1817,11 +1746,6 @@ export class OrganizationController extends Controller { "orgRoot.DIVISION_CODE", "orgRoot.SECTION_CODE", "orgRoot.JOB_CODE", - "orgRoot.ROOT_CODE", - "orgRoot.CHILD1_CODE", - "orgRoot.CHILD2_CODE", - "orgRoot.CHILD3_CODE", - "orgRoot.CHILD4_CODE", "orgRoot.responsibility", ]) .orderBy("orgRoot.orgRootOrder", "ASC") @@ -1830,181 +1754,161 @@ export class OrganizationController extends Controller { 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.ROOT_CODE", - "orgChild1.CHILD1_CODE", - "orgChild1.CHILD2_CODE", - "orgChild1.CHILD3_CODE", - "orgChild1.CHILD4_CODE", - "orgChild1.responsibility", - ]) - .orderBy("orgChild1.orgChild1Order", "ASC") - .getMany() + .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.ROOT_CODE", - "orgChild2.CHILD1_CODE", - "orgChild2.CHILD2_CODE", - "orgChild2.CHILD3_CODE", - "orgChild2.CHILD4_CODE", - "orgChild2.orgChild1Id", - "orgChild2.responsibility", - ]) - .orderBy("orgChild2.orgChild2Order", "ASC") - .getMany() + .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.ROOT_CODE", - "orgChild3.CHILD1_CODE", - "orgChild3.CHILD2_CODE", - "orgChild3.CHILD3_CODE", - "orgChild3.CHILD4_CODE", - "orgChild3.orgChild2Id", - "orgChild3.responsibility", - ]) - .orderBy("orgChild3.orgChild3Order", "ASC") - .getMany() + .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.ROOT_CODE", - "orgChild4.CHILD1_CODE", - "orgChild4.CHILD2_CODE", - "orgChild4.CHILD3_CODE", - "orgChild4.CHILD4_CODE", - "orgChild4.orgChild3Id", - "orgChild4.responsibility", - ]) - .orderBy("orgChild4.orgChild4Order", "ASC") - .getMany() + .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( @@ -2025,11 +1929,6 @@ export class OrganizationController extends Controller { DIVISION_CODE: orgRoot.DIVISION_CODE, SECTION_CODE: orgRoot.SECTION_CODE, JOB_CODE: orgRoot.JOB_CODE, - ROOT_CODE: orgRoot.ROOT_CODE, - CHILD1_CODE: orgRoot.CHILD1_CODE, - CHILD2_CODE: orgRoot.CHILD2_CODE, - CHILD3_CODE: orgRoot.CHILD3_CODE, - CHILD4_CODE: orgRoot.CHILD4_CODE, orgTreeOrder: orgRoot.orgRootOrder, orgTreePhoneEx: orgRoot.orgRootPhoneEx, orgTreePhoneIn: orgRoot.orgRootPhoneIn, @@ -2061,11 +1960,6 @@ export class OrganizationController extends Controller { DIVISION_CODE: orgChild1.DIVISION_CODE, SECTION_CODE: orgChild1.SECTION_CODE, JOB_CODE: orgChild1.JOB_CODE, - ROOT_CODE: orgChild1.ROOT_CODE, - CHILD1_CODE: orgChild1.CHILD1_CODE, - CHILD2_CODE: orgChild1.CHILD2_CODE, - CHILD3_CODE: orgChild1.CHILD3_CODE, - CHILD4_CODE: orgChild1.CHILD4_CODE, orgTreeOrder: orgChild1.orgChild1Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild1.orgChild1PhoneEx, @@ -2110,11 +2004,6 @@ export class OrganizationController extends Controller { DIVISION_CODE: orgChild2.DIVISION_CODE, SECTION_CODE: orgChild2.SECTION_CODE, JOB_CODE: orgChild2.JOB_CODE, - ROOT_CODE: orgChild2.ROOT_CODE, - CHILD1_CODE: orgChild2.CHILD1_CODE, - CHILD2_CODE: orgChild2.CHILD2_CODE, - CHILD3_CODE: orgChild2.CHILD3_CODE, - CHILD4_CODE: orgChild2.CHILD4_CODE, orgTreeOrder: orgChild2.orgChild2Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild2.orgChild2PhoneEx, @@ -2164,11 +2053,6 @@ export class OrganizationController extends Controller { DIVISION_CODE: orgChild3.DIVISION_CODE, SECTION_CODE: orgChild3.SECTION_CODE, JOB_CODE: orgChild3.JOB_CODE, - ROOT_CODE: orgChild3.ROOT_CODE, - CHILD1_CODE: orgChild3.CHILD1_CODE, - CHILD2_CODE: orgChild3.CHILD2_CODE, - CHILD3_CODE: orgChild3.CHILD3_CODE, - CHILD4_CODE: orgChild3.CHILD4_CODE, orgTreeOrder: orgChild3.orgChild3Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild3.orgChild3PhoneEx, @@ -2225,11 +2109,6 @@ export class OrganizationController extends Controller { DIVISION_CODE: orgChild4.DIVISION_CODE, SECTION_CODE: orgChild4.SECTION_CODE, JOB_CODE: orgChild4.JOB_CODE, - ROOT_CODE: orgChild4.ROOT_CODE, - CHILD1_CODE: orgChild4.CHILD1_CODE, - CHILD2_CODE: orgChild4.CHILD2_CODE, - CHILD3_CODE: orgChild4.CHILD3_CODE, - CHILD4_CODE: orgChild4.CHILD4_CODE, orgTreeOrder: orgChild4.orgChild4Order, orgRootCode: orgRoot.orgRootCode, orgTreePhoneEx: orgChild4.orgChild4PhoneEx, @@ -2650,7 +2529,6 @@ export class OrganizationController extends Controller { await sendToQueueOrg(msg); return new HttpSuccess(); } catch (error: any) { - console.error("Error publishing draft organization:", error); throw error; } } @@ -3599,18 +3477,18 @@ export class OrganizationController extends Controller { const formattedData_ = rootId === "root" ? [ - { - personID: "", - name: "", - avatar: "", - positionName: "", - positionNum: "", - positionNumInt: null, - departmentName: data.orgRevisionName, - organizationId: data.id, - children: formattedData, - }, - ] + { + personID: "", + name: "", + avatar: "", + positionName: "", + positionNum: "", + positionNumInt: null, + departmentName: data.orgRevisionName, + organizationId: data.id, + children: formattedData, + }, + ] : formattedData; return new HttpSuccess(formattedData_); } @@ -5929,170 +5807,165 @@ export class OrganizationController extends Controller { .leftJoin("orgRoot.posMasters", "posMasters") .leftJoin("posMasters.current_holder", "current_holder") .orderBy("orgRoot.orgRootOrder", "ASC") - .addOrderBy("posMasters.posMasterOrder", "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") - .addOrderBy("posMasters.posMasterOrder", "ASC") - .getMany() + .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") - .addOrderBy("posMasters.posMasterOrder", "ASC") - .getMany() + .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") - .addOrderBy("posMasters.posMasterOrder", "ASC") - .getMany() + .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") - .addOrderBy("posMasters.posMasterOrder", "ASC") - .getMany() + .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 = @@ -6144,8 +6017,8 @@ export class OrganizationController extends Controller { posMaster: cannotViewRootPosMaster ? [] : filterPosMasters(orgRoot.posMasters, "orgChild1Id").map((x) => - formatPosMaster(x, orgRoot.orgRootShortName, orgRoot.id, 0), - ), + formatPosMaster(x, orgRoot.orgRootShortName, orgRoot.id, 0), + ), children: orgChild1Data .filter((orgChild1) => orgChild1.orgRootId === orgRoot.id) .map((orgChild1) => ({ @@ -6169,8 +6042,8 @@ export class OrganizationController extends Controller { posMaster: cannotViewChild1PosMaster ? [] : filterPosMasters(orgChild1.posMasters, "orgChild2Id").map((x) => - formatPosMaster(x, orgChild1.orgChild1ShortName, orgChild1.id, 1), - ), + formatPosMaster(x, orgChild1.orgChild1ShortName, orgChild1.id, 1), + ), children: orgChild2Data .filter((orgChild2) => orgChild2.orgChild1Id === orgChild1.id) .map((orgChild2) => ({ @@ -6197,8 +6070,8 @@ export class OrganizationController extends Controller { posMaster: cannotViewChild2PosMaster ? [] : filterPosMasters(orgChild2.posMasters, "orgChild3Id").map((x) => - formatPosMaster(x, orgChild2.orgChild2ShortName, orgChild2.id, 2), - ), + formatPosMaster(x, orgChild2.orgChild2ShortName, orgChild2.id, 2), + ), children: orgChild3Data .filter((orgChild3) => orgChild3.orgChild2Id === orgChild2.id) .map((orgChild3) => ({ @@ -6225,8 +6098,8 @@ export class OrganizationController extends Controller { posMaster: cannotViewChild3PosMaster ? [] : filterPosMasters(orgChild3.posMasters, "orgChild4Id").map((x) => - formatPosMaster(x, orgChild3.orgChild3ShortName, orgChild3.id, 3), - ), + formatPosMaster(x, orgChild3.orgChild3ShortName, orgChild3.id, 3), + ), children: orgChild4Data .filter((orgChild4) => orgChild4.orgChild3Id === orgChild3.id) .map((orgChild4) => ({ @@ -6257,10 +6130,10 @@ export class OrganizationController extends Controller { posMaster: cannotViewChild4PosMaster ? [] : orgChild4.posMasters - .filter((x) => x.isDirector === true) - .map((x) => - formatPosMaster(x, orgChild4.orgChild4ShortName, orgChild4.id, 4), - ), + .filter((x) => x.isDirector === true) + .map((x) => + formatPosMaster(x, orgChild4.orgChild4ShortName, orgChild4.id, 4), + ), })), })), })), @@ -6431,159 +6304,159 @@ export class OrganizationController extends Controller { 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() + .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() + .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() + .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() + .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) => { @@ -8470,13 +8343,13 @@ export class OrganizationController extends Controller { profileId: null, shortName: pos ? [ - pos.orgChild4?.orgChild4ShortName, - pos.orgChild3?.orgChild3ShortName, - pos.orgChild2?.orgChild2ShortName, - pos.orgChild1?.orgChild1ShortName, - pos.orgRoot?.orgRootShortName, - ].find((s: string | undefined) => typeof s === "string" && s.trim().length > 0) ?? - null + pos.orgChild4?.orgChild4ShortName, + pos.orgChild3?.orgChild3ShortName, + pos.orgChild2?.orgChild2ShortName, + pos.orgChild1?.orgChild1ShortName, + pos.orgRoot?.orgRootShortName, + ].find((s: string | undefined) => typeof s === "string" && s.trim().length > 0) ?? + null : null, posMasterNoPrefix: pos.posMasterNoPrefix ?? null, posMasterNo: pos.posMasterNo != null ? String(pos.posMasterNo) : null, @@ -8545,13 +8418,13 @@ export class OrganizationController extends Controller { profileId: null, shortName: pos ? [ - pos.orgChild4?.orgChild4ShortName, - pos.orgChild3?.orgChild3ShortName, - pos.orgChild2?.orgChild2ShortName, - pos.orgChild1?.orgChild1ShortName, - pos.orgRoot?.orgRootShortName, - ].find((s: string | undefined) => typeof s === "string" && s.trim().length > 0) ?? - null + pos.orgChild4?.orgChild4ShortName, + pos.orgChild3?.orgChild3ShortName, + pos.orgChild2?.orgChild2ShortName, + pos.orgChild1?.orgChild1ShortName, + pos.orgRoot?.orgRootShortName, + ].find((s: string | undefined) => typeof s === "string" && s.trim().length > 0) ?? + null : null, posMasterNoPrefix: pos.posMasterNoPrefix ?? null, posMasterNo: pos.posMasterNo != null ? String(pos.posMasterNo) : null, @@ -8619,13 +8492,13 @@ export class OrganizationController extends Controller { profileId: null, shortName: pos ? [ - pos.orgChild4?.orgChild4ShortName, - pos.orgChild3?.orgChild3ShortName, - pos.orgChild2?.orgChild2ShortName, - pos.orgChild1?.orgChild1ShortName, - pos.orgRoot?.orgRootShortName, - ].find((s: string | undefined) => typeof s === "string" && s.trim().length > 0) ?? - null + pos.orgChild4?.orgChild4ShortName, + pos.orgChild3?.orgChild3ShortName, + pos.orgChild2?.orgChild2ShortName, + pos.orgChild1?.orgChild1ShortName, + pos.orgRoot?.orgRootShortName, + ].find((s: string | undefined) => typeof s === "string" && s.trim().length > 0) ?? + null : null, posMasterNoPrefix: pos.posMasterNoPrefix ?? null, posMasterNo: pos.posMasterNo != null ? String(pos.posMasterNo) : null, @@ -8682,9 +8555,8 @@ export class OrganizationController extends Controller { orgChild2Id, orgChild3Id, orgChild4Id, - authRoleId: draftPos.authRoleId, current_holderId: draftPos.next_holderId, - next_holderId: null, + next_holderId: draftPos.next_holderId, isSit: draftPos.isSit, reason: draftPos.reason, isDirector: draftPos.isDirector, @@ -8713,7 +8585,6 @@ export class OrganizationController extends Controller { orgChild3Id, orgChild4Id, current_holderId: draftPos.next_holderId, - next_holderId: null, statusReport: "DONE", }); @@ -8772,14 +8643,14 @@ export class OrganizationController extends Controller { profileId: null, shortName: pmWithRelations ? [ - pmWithRelations.orgChild4?.orgChild4ShortName, - pmWithRelations.orgChild3?.orgChild3ShortName, - pmWithRelations.orgChild2?.orgChild2ShortName, - pmWithRelations.orgChild1?.orgChild1ShortName, - pmWithRelations.orgRoot?.orgRootShortName, - ].find( - (s: string | undefined) => typeof s === "string" && s.trim().length > 0, - ) ?? null + pmWithRelations.orgChild4?.orgChild4ShortName, + pmWithRelations.orgChild3?.orgChild3ShortName, + pmWithRelations.orgChild2?.orgChild2ShortName, + pmWithRelations.orgChild1?.orgChild1ShortName, + pmWithRelations.orgRoot?.orgRootShortName, + ].find( + (s: string | undefined) => typeof s === "string" && s.trim().length > 0, + ) ?? null : null, posMasterNoPrefix: draftPos.posMasterNoPrefix ?? null, posMasterNo: draftPos.posMasterNo != null ? String(draftPos.posMasterNo) : null, @@ -8823,45 +8694,6 @@ export class OrganizationController extends Controller { }; await queryRunner.commitTransaction(); - - // Clear Redis cache after successful publish (only menu and role for menu display) - const redis = require("redis"); - const { promisify } = require("util"); - const redisClient = redis.createClient({ - host: process.env.REDIS_HOST || "localhost", - port: parseInt(process.env.REDIS_PORT || "6379"), - }); - const keysAsync = promisify(redisClient.keys).bind(redisClient); - const delAsync = promisify(redisClient.del).bind(redisClient); - - try { - // Clear only menu and role cache (affects menu display) - const menuRolePatterns = ["menu_*", "role_*"]; - let totalCleared = 0; - - for (const pattern of menuRolePatterns) { - const keys = await keysAsync(pattern); - if (keys.length > 0) { - // Delete in chunks of 1000 to avoid argument limit - const chunkSize = 1000; - for (let i = 0; i < keys.length; i += chunkSize) { - const chunk = keys.slice(i, i + chunkSize); - await delAsync(...chunk); - } - totalCleared += keys.length; - console.log(`[moveDraftToCurrent] Cleared ${keys.length} cache keys for pattern: ${pattern}`); - } - } - console.log(`[moveDraftToCurrent] Total cache cleared: ${totalCleared} keys`); - } catch (err) { - console.error("[moveDraftToCurrent] Error clearing cache:", err); - } finally { - redisClient.quit(); - } - - // Invalidate memory cache - orgStructureCache.invalidate(currentRevisionId); - return new HttpSuccess(summary); } catch (error) { console.error("Error moving draft to current:", error); @@ -9145,16 +8977,7 @@ export class OrganizationController extends Controller { // 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", - "next_holder.posType", - "next_holder.posLevel", - ], + relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4", "next_holder"], }); // Fetch ALL positions for ALL posMasters in just 2 queries @@ -9292,22 +9115,17 @@ export class OrganizationController extends Controller { org: draftPosMaster ? getOrgFullName(draftPosMaster as PosMaster) ?? _null : _null, }); } - - if (nextHolderId != null && draftPos.positionIsSelected) { - // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ - if (!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); - } - - // ยังบันทึกประวัติคนครอง + // ถ้าไม่ใช่ตำแหน่งนั่งทับ (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 = @@ -9319,11 +9137,10 @@ export class OrganizationController extends Controller { prefix: draftPosMaster.next_holder?.prefix ?? null, firstName: draftPosMaster.next_holder?.firstName ?? null, lastName: draftPosMaster.next_holder?.lastName ?? null, - // isSit = true ดึงค่าจาก profile - position: draftPosMaster?.isSit ? draftPosMaster.next_holder?.position ?? null : selectedPos.positionName ?? null, - posType: draftPosMaster?.isSit ? draftPosMaster.next_holder?.posType?.posTypeName ?? null : (selectedPos as any).posType?.posTypeName ?? null, - posLevel: draftPosMaster?.isSit ? draftPosMaster.next_holder?.posLevel?.posLevelName ?? null : (selectedPos as any).posLevel?.posLevelName ?? null, - posExecutive: draftPosMaster?.isSit ? draftPosMaster.next_holder?.posExecutive ?? null : (selectedPos as any).posExecutive?.posExecutiveName ?? 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, @@ -9375,12 +9192,12 @@ export class OrganizationController extends Controller { child4DnaId: posMaster?.orgChild4?.ancestorDNA ?? null, shortName: posMaster ? [ - posMaster.orgChild4?.orgChild4ShortName, - posMaster.orgChild3?.orgChild3ShortName, - posMaster.orgChild2?.orgChild2ShortName, - posMaster.orgChild1?.orgChild1ShortName, - posMaster.orgRoot?.orgRootShortName, - ].find((s) => typeof s === "string" && s.trim().length > 0) ?? null + posMaster.orgChild4?.orgChild4ShortName, + posMaster.orgChild3?.orgChild3ShortName, + posMaster.orgChild2?.orgChild2ShortName, + posMaster.orgChild1?.orgChild1ShortName, + posMaster.orgRoot?.orgRootShortName, + ].find((s) => typeof s === "string" && s.trim().length > 0) ?? null : null, posMasterNoPrefix: posMaster?.posMasterNoPrefix ?? null, posMasterNo: posMaster?.posMasterNo != null ? String(posMaster.posMasterNo) : null, diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 0814d8a2..1af5c723 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -26,7 +26,6 @@ import { OrgRoot } from "../entities/OrgRoot"; import { Position } from "../entities/Position"; import { PosMaster } from "../entities/PosMaster"; import { PosMasterHistory } from "../entities/PosMasterHistory"; -import { PosMasterEmployeeHistory } from "../entities/PosMasterEmployeeHistory"; import { Profile } from "../entities/Profile"; import { ProfileEducation } from "../entities/ProfileEducation"; import { ProfileEmployee } from "../entities/ProfileEmployee"; @@ -40,7 +39,7 @@ import { calculateRetireLaw } from "../interfaces/utils"; import { RequestWithUser } from "../middlewares/user"; @Route("api/v1/org/dotnet") @Tags("Dotnet") -// @Security("bearerAuth") +@Security("bearerAuth") @Response( HttpStatus.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", @@ -58,7 +57,6 @@ export class OrganizationDotnetController extends Controller { private positionRepository = AppDataSource.getRepository(Position); private posMasterRepository = AppDataSource.getRepository(PosMaster); private posMasterHistoryRepository = AppDataSource.getRepository(PosMasterHistory); - private posMasterEmployeeHistoryRepository = AppDataSource.getRepository(PosMasterEmployeeHistory); private empPosMasterRepository = AppDataSource.getRepository(EmployeePosMaster); private insigniaRepo = AppDataSource.getRepository(ProfileInsignia); private employeePosDictRepository = AppDataSource.getRepository(EmployeePosDict); @@ -73,7 +71,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("check-citizen") - @Security("internalAuth") public async CheckCitizen( @Body() body: { @@ -91,7 +88,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("search") - @Security("internalAuth") public async SearchProfile( @Body() body: { @@ -306,7 +302,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("search-employee") - @Security("internalAuth") public async SearchProfileEmployee( @Body() body: { @@ -491,7 +486,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} id Id หน่วยงาน */ @Get("org/{id}") - @Security("internalAuth") async GetOrganizationById(@Path() id: string) { const orgRoot = await this.orgRootRepo.findOne({ where: { id: id }, @@ -501,7 +495,6 @@ export class OrganizationDotnetController extends Controller { } @Get("agency/{id}") - @Security("internalAuth") async GetOrgAgencyById(@Path() id: string) { const orgRoot = await this.orgRootRepo.findOne({ where: { id: id }, @@ -511,7 +504,6 @@ export class OrganizationDotnetController extends Controller { } @Get("go-agency/{id}") - @Security("internalAuth") async GetOrgGoAgencyById(@Path() id: string) { const orgRoot = await this.orgRootRepo.findOne({ where: { id: id }, @@ -527,7 +519,6 @@ export class OrganizationDotnetController extends Controller { * */ @Get("get-profileId") - @Security("bearerAuth") async getProfileInbox(@Request() request: { user: Record }) { let profile: any; //OFF @@ -563,7 +554,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} keycloakId Id keycloak */ @Get("keycloak-old/{keycloakId}") - @Security("internalAuth") async GetProfileByKeycloakIdAsyncOld(@Path() keycloakId: string) { const profile = await this.profileRepo.findOne({ relations: [ @@ -1363,7 +1353,6 @@ export class OrganizationDotnetController extends Controller { } @Get("keycloak/{keycloakId}") - @Security("internalAuth") async GetProfileByKeycloakIdAsync(@Path() keycloakId: string) { /* ========================= * 1. Load profile @@ -1792,7 +1781,6 @@ export class OrganizationDotnetController extends Controller { } @Get("by-keycloak/{keycloakId}") - @Security("internalAuth") async NewGetProfileByKeycloakIdAsync(@Path() keycloakId: string) { /* ========================= * 1. Load profile @@ -2027,7 +2015,6 @@ export class OrganizationDotnetController extends Controller { // เพิ่มที่อยู่ปัจจุบัน + ตำแหน่งหัวหน้า @Get("by-keycloak2/{keycloakId}") - @Security("internalAuth") async NewGetProfileByKeycloak2IdAsync(@Path() keycloakId: string) { /* ========================= * 1. Load profile @@ -2363,151 +2350,6 @@ export class OrganizationDotnetController extends Controller { return new HttpSuccess(mapProfile); } - /** - * API Get Profile For Process Check In - * @summary API Get Profile For Process Check In - * @param {string} keycloakId keycloakId profile - */ - @Get("check-keycloak/{keycloakId}") - @Security("internalAuth") - async GetProfileForProcessCheckInAsync(@Path() keycloakId: string) { - try { - // console.log(`[check-keycloak] START - keycloakId=${keycloakId}`); - - /* ========================= - * 1. Load profile (Officer) - * ========================= */ - const profile = await this.profileRepo.findOne({ - where: { keycloak: keycloakId }, - relations: { - current_holders: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }, - }); - - // Employee - if (!profile) { - console.log(`[check-keycloak] OFFICER_NOT_FOUND - keycloakId=${keycloakId}, checking EMPLOYEE`); - - const empProfile = await this.profileEmpRepo.findOne({ - where: { keycloak: keycloakId }, - relations: { - current_holders: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }, - }); - - if (!empProfile) { - console.log(`[check-keycloak] EMPLOYEE_NOT_FOUND - keycloakId=${keycloakId}`); - throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); - } - - const currentHolder = empProfile.current_holders?.find( - (x) => - x.orgRevision?.orgRevisionIsDraft === false && - x.orgRevision?.orgRevisionIsCurrent === true, - ); - - const mapProfile = { - profileType: "EMPLOYEE", - id: empProfile.id, - keycloak: empProfile.keycloak, - prefix: empProfile.prefix, - firstName: empProfile.firstName, - lastName: empProfile.lastName, - citizenId: empProfile.citizenId, - gender: empProfile.gender, - - root: currentHolder?.orgRoot?.orgRootName ?? null, - rootId: currentHolder?.orgRootId ?? null, - rootDnaId: currentHolder?.orgRoot?.ancestorDNA ?? null, - child1: currentHolder?.orgChild1?.orgChild1Name ?? null, - child1Id: currentHolder?.orgChild1Id ?? null, - child1DnaId: currentHolder?.orgChild1?.ancestorDNA ?? null, - child2: currentHolder?.orgChild2?.orgChild2Name ?? null, - child2Id: currentHolder?.orgChild2Id ?? null, - child2DnaId: currentHolder?.orgChild2?.ancestorDNA ?? null, - child3: currentHolder?.orgChild3?.orgChild3Name ?? null, - child3Id: currentHolder?.orgChild3Id ?? null, - child3DnaId: currentHolder?.orgChild3?.ancestorDNA ?? null, - child4: currentHolder?.orgChild4?.orgChild4Name ?? null, - child4Id: currentHolder?.orgChild4Id ?? null, - child4DnaId: currentHolder?.orgChild4?.ancestorDNA ?? null, - }; - - // console.log( - // `[check-keycloak] SUCCESS_EMPLOYEE - keycloakId=${keycloakId}, profileType=EMPLOYEE`, - // ); - - return new HttpSuccess(mapProfile); - } - - // console.log(`[check-keycloak] OFFICER_FOUND - keycloakId=${keycloakId}`); - - /* ========================================= - * 2. current holder (Officer) - * ========================================= */ - const currentHolder = profile.current_holders?.find( - (x) => - x.orgRevision?.orgRevisionIsDraft === false && x.orgRevision?.orgRevisionIsCurrent === true, - ); - - /* ========================================= - * 3. map response - * ========================================= */ - const mapProfile = { - profileType: "OFFICER", - id: profile.id, - keycloak: profile.keycloak, - prefix: profile.prefix, - firstName: profile.firstName, - lastName: profile.lastName, - citizenId: profile.citizenId, - gender: profile.gender, - - root: currentHolder?.orgRoot?.orgRootName ?? null, - rootId: currentHolder?.orgRootId ?? null, - rootDnaId: currentHolder?.orgRoot?.ancestorDNA ?? null, - child1: currentHolder?.orgChild1?.orgChild1Name ?? null, - child1Id: currentHolder?.orgChild1Id ?? null, - child1DnaId: currentHolder?.orgChild1?.ancestorDNA ?? null, - child2: currentHolder?.orgChild2?.orgChild2Name ?? null, - child2Id: currentHolder?.orgChild2Id ?? null, - child2DnaId: currentHolder?.orgChild2?.ancestorDNA ?? null, - child3: currentHolder?.orgChild3?.orgChild3Name ?? null, - child3Id: currentHolder?.orgChild3Id ?? null, - child3DnaId: currentHolder?.orgChild3?.ancestorDNA ?? null, - child4: currentHolder?.orgChild4?.orgChild4Name ?? null, - child4Id: currentHolder?.orgChild4Id ?? null, - child4DnaId: currentHolder?.orgChild4?.ancestorDNA ?? null, - }; - - // console.log( - // `[check-keycloak] SUCCESS_OFFICER - keycloakId=${keycloakId}, profileType=OFFICER`, - // ); - - return new HttpSuccess(mapProfile); - } catch (error: any) { - // Log เฉพาะ unexpected errors (ไม่ใช่ HttpError) - if (!(error instanceof HttpError)) { - console.error(`[check-keycloak] Unexpected error: keycloakId=${keycloakId}`, error); - } - throw error; - } - } - /** * API Get Profile For Logs * @@ -2516,7 +2358,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} keycloakId keycloakId profile */ @Get("user-logs/{keycloakId}") - @Security("internalAuth") async UserLogs(@Path() keycloakId: string) { /* ========================= * 1. Load profile @@ -2596,7 +2437,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} profileId Id profile */ @Get("profile/{profileId}") - @Security("internalAuth") async GetProfileByProfileIdAsync(@Path() profileId: string) { const profile = await this.profileRepo.findOne({ relations: [ @@ -3266,7 +3106,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} citizenId citizen Id */ @Get("citizenId/{citizenId}") - @Security("internalAuth") async GetProfileByCitizenIdAsync(@Path() citizenId: string) { const profile = await this.profileRepo.findOne({ relations: [ @@ -3921,7 +3760,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} keycloakId Id keycloak */ @Get("root/officer/{rootId}") - @Security("internalAuth") async GetProfileByRootIdAsync(@Path() rootId: string) { const profiles = await this.profileRepo.find({ relations: [ @@ -4234,7 +4072,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} keycloakId Id keycloak */ @Get("root/employee/{rootId}") - @Security("internalAuth") async GetProfileByRootIdEmpAsync(@Path() rootId: string) { const profiles = await this.profileEmpRepo.find({ relations: [ @@ -4439,7 +4276,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} keycloakId Id keycloak */ @Post("find/employee/position") - @Security("internalAuth") async GetProfileByPositionEmpAsync( @Body() body: { @@ -4769,7 +4605,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} keycloakId Id keycloak */ @Get("user-fullname/{keycloakId}") - @Security("internalAuth") async GetUserFullName(@Path() keycloakId: string) { const profile = await this.profileRepo.findOne({ where: { keycloak: keycloakId }, @@ -4788,7 +4623,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} keycloakId Id keycloak */ @Get("user-oc/{keycloakId}") - @Security("internalAuth") async getProfileByKeycloak(@Path() keycloakId: string) { const profile = await this.profileRepo.findOne({ where: { keycloak: keycloakId }, @@ -4840,7 +4674,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} keycloakId Id keycloak */ @Get("user-oc-all/{keycloakId}") - @Security("internalAuth") async getAllProfileByKeycloak(@Path() keycloakId: string) { const profile = await this.profileRepo.findOne({ where: { keycloak: keycloakId }, @@ -5008,7 +4841,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} ocId Id หน่วยงาน */ @Get("root-oc/{ocId}") - @Security("internalAuth") async GetRootOcId(@Path() ocId: string) { const orgRoot = await this.orgRootRepo.findOne({ where: { id: ocId }, @@ -5025,7 +4857,6 @@ export class OrganizationDotnetController extends Controller { * */ @Get("keycloak") - @Security("internalAuth") async GetProfileWithKeycloak() { const profile = await this.profileRepo.find({ where: { keycloak: Not(IsNull()) }, @@ -5235,7 +5066,6 @@ export class OrganizationDotnetController extends Controller { * */ @Get("keycloak-employee") - @Security("internalAuth") async GetProfileWithKeycloakEmployee() { const profile = await this.profileEmpRepo.find({ where: { keycloak: Not(IsNull()) || Not("") }, @@ -5364,7 +5194,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("keycloak-all-officer") - @Security("internalAuth") async PostProfileWithKeycloakAllOfficer( @Body() body: { @@ -5535,7 +5364,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("keycloak-all-officer/date") - @Security("internalAuth") async PostProfileWithKeycloakAllOfficerDate( @Body() body: { @@ -5714,7 +5542,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("none-validate-keycloak-all-officer") - @Security("internalAuth") async PostProfileWithNoneValidateKeycloakAllOfficer( @Body() body: { @@ -5884,7 +5711,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("find-node-name") - @Security("internalAuth") async findNodeName( @Body() body: { @@ -5993,7 +5819,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("officer-by-admin-role") - @Security("internalAuth") async GetOfficersByAdminRole( @Body() body: { @@ -6331,7 +6156,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("keycloak-all-employee") - @Security("internalAuth") async PostProfileWithKeycloakAllEmployee( @Body() body: { @@ -6485,7 +6309,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("none-validate-keycloak-all-employee") - @Security("internalAuth") async PostProfileWithNoneValidateKeycloakAllEmployee( @Body() body: { @@ -6639,7 +6462,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("employee-by-admin-role") - @Security("internalAuth") async GetEmployeesByAdminRole( @Body() body: { @@ -6975,276 +6797,229 @@ export class OrganizationDotnetController extends Controller { return new HttpSuccess(profile_); } - /** - * รายชื่อลูกจ้างประจำ ตามสิทธิ์ admin - * @summary รายชื่อลูกจ้างประจำ ตามสิทธิ์ admin - */ - @Post("employee-by-admin-rolev2") - @Security("internalAuth") - async GetEmployeesByAdminRoleV2( - // @Request() req: RequestWithUser, // ไม่ได้ใช้ - @Body() - body: { - node: number; - nodeId: string; - role: string; - isRetirement?: boolean; - reqNode?: number; - reqNodeId?: string; - date: Date; - }, - ) { - let typeCondition: any = {}; - if (body.role === "CHILD" || body.role === "BROTHER") { - if (body.role === "CHILD") { - switch (body.node) { - case 0: - typeCondition = { - rootDnaId: body.nodeId, - }; - break; - case 1: - typeCondition = { - child1DnaId: body.nodeId, - }; - break; - case 2: - typeCondition = { - child2DnaId: body.nodeId, - }; - break; - case 3: - typeCondition = { - child3DnaId: body.nodeId, - }; - break; - case 4: - typeCondition = { - child4DnaId: body.nodeId, - }; - break; - default: - typeCondition = {}; - break; - } - } else if (body.role === "BROTHER") { - switch (body.node) { - case 0: - typeCondition = { - rootDnaId: body.nodeId, - }; - break; - case 1: - typeCondition = { - rootDnaId: body.nodeId, - }; - break; - case 2: - typeCondition = { - child1DnaId: body.nodeId, - }; - break; - case 3: - typeCondition = { - child2DnaId: body.nodeId, - }; - break; - case 4: - typeCondition = { - child3DnaId: body.nodeId, - }; - break; - default: - typeCondition = {}; - break; - } - } - } else if (body.role === "OWNER" || body.role === "ROOT" || body.role === "PARENT") { - switch (body.reqNode) { - case 0: - typeCondition = { - rootDnaId: body.reqNodeId, - }; - break; - case 1: - typeCondition = { - child1DnaId: body.reqNodeId, - }; - break; - case 2: - typeCondition = { - child2DnaId: body.reqNodeId, - }; - break; - case 3: - typeCondition = { - child3DnaId: body.reqNodeId, - }; - break; - case 4: - typeCondition = { - child4DnaId: body.reqNodeId, - }; - break; - default: - typeCondition = {}; - break; - } - } else if (body.role === "NORMAL") { - switch (body.node) { - case 0: - typeCondition = { - rootDnaId: body.nodeId, - child1DnaId: IsNull(), - }; - break; - case 1: - typeCondition = { - child1DnaId: body.nodeId, - child2DnaId: IsNull(), - }; - break; - case 2: - typeCondition = { - child2DnaId: body.nodeId, - child3DnaId: IsNull(), - }; - break; - case 3: - typeCondition = { - child3DnaId: body.nodeId, - child4DnaId: IsNull(), - }; - break; - case 4: - typeCondition = { - child4DnaId: body.nodeId, - }; - break; - default: - typeCondition = {}; - break; - } - } - // set เวลาเป็น 23:59:59 ของวันนั้น - const date = body.date ? new Date(body.date.toISOString().slice(0, 10)) : new Date(); - date.setHours(23, 59, 59, 999); + // /** + // * รายชื่อขรก. ตามสิทธิ์ admin + // * + // * @summary รายชื่อขรก. ตามสิทธิ์ admin + // * + // */ + // @Post("employee-by-admin-rolev2") + // async GetEmployeesByAdminRoleV2( + // @Request() req: RequestWithUser, + // @Body() + // body: { + // node: number; + // nodeId: string; + // role: string; + // isRetirement?: boolean; + // reqNode?: number; + // reqNodeId?: string; + // date?: Date; + // }, + // ) { + // let typeCondition: any = {}; + // if (body.role === "CHILD" || body.role === "PARENT" || body.role === "BROTHER") { + // if (body.role === "CHILD") { + // switch (body.node) { + // case 0: + // typeCondition = { + // rootDnaId: body.nodeId, + // }; + // break; + // case 1: + // typeCondition = { + // child1DnaId: body.nodeId, + // }; + // break; + // case 2: + // typeCondition = { + // child2DnaId: body.nodeId, + // }; + // break; + // case 3: + // typeCondition = { + // child3DnaId: body.nodeId, + // }; + // break; + // case 4: + // typeCondition = { + // child4DnaId: body.nodeId, + // }; + // break; + // default: + // typeCondition = {}; + // break; + // } + // } else if (body.role === "BROTHER") { + // switch (body.node) { + // case 0: + // typeCondition = { + // rootDnaId: body.nodeId, + // }; + // break; + // case 1: + // typeCondition = { + // rootDnaId: body.nodeId, + // }; + // break; + // case 2: + // typeCondition = { + // child1DnaId: body.nodeId, + // }; + // break; + // case 3: + // typeCondition = { + // child2DnaId: body.nodeId, + // }; + // break; + // case 4: + // typeCondition = { + // child3DnaId: body.nodeId, + // }; + // break; + // default: + // typeCondition = {}; + // break; + // } + // } else if (body.role === "PARENT") { + // typeCondition = { + // rootDnaId: body.nodeId, + // child1DnaId: Not(IsNull()), + // }; + // } + // } else if (body.role === "OWNER" || body.role === "ROOT") { + // switch (body.reqNode) { + // case 0: + // typeCondition = { + // rootDnaId: body.reqNodeId, + // }; + // break; + // case 1: + // typeCondition = { + // child1DnaId: body.reqNodeId, + // }; + // break; + // case 2: + // typeCondition = { + // child2DnaId: body.reqNodeId, + // }; + // break; + // case 3: + // typeCondition = { + // child3DnaId: body.reqNodeId, + // }; + // break; + // case 4: + // typeCondition = { + // child4DnaId: body.reqNodeId, + // }; + // break; + // default: + // typeCondition = {}; + // break; + // } + // } else if (body.role === "NORMAL") { + // switch (body.node) { + // case 0: + // typeCondition = { + // rootDnaId: body.nodeId, + // child1DnaId: IsNull(), + // }; + // break; + // case 1: + // typeCondition = { + // child1DnaId: body.nodeId, + // child2DnaId: IsNull(), + // }; + // break; + // case 2: + // typeCondition = { + // child2DnaId: body.nodeId, + // child3DnaId: IsNull(), + // }; + // break; + // case 3: + // typeCondition = { + // child3DnaId: body.nodeId, + // child4DnaId: IsNull(), + // }; + // break; + // case 4: + // typeCondition = { + // child4DnaId: body.nodeId, + // }; + // break; + // default: + // typeCondition = {}; + // break; + // } + // } + // const date = body.date ? new Date(body.date) : new Date(); + // // set เวลาเป็น 23:59:59 ของวันนั้น + // date.setHours(23, 59, 59, 999); - let posEmpHis = await this.posMasterEmployeeHistoryRepository.find({ - where: { - ...typeCondition, - createdAt: LessThanOrEqual(date), - }, - select: [ - "profileEmployeeId", - "prefix", - "firstName", - "lastName", - "shortName", - "posMasterNo", - "position", - "posType", - "posLevel", - "ancestorDNA", - "rootDnaId", - "child1DnaId", - "child2DnaId", - "child3DnaId", - "child4DnaId", - "createdAt", - ], - order: { - firstName: "ASC", - lastName: "ASC", - createdAt: "DESC", - }, - }); + // let profile = await this.posMasterEmployeeHistoryRepository.find({ + // where: { + // ...typeCondition, + // createdAt: LessThanOrEqual(date), + // // firstName: Not("") && Not(IsNull()), + // // lastName: Not("") && Not(IsNull()), + // }, + // order: { + // firstName: "ASC", + // lastName: "ASC", + // createdAt: "DESC", // ให้ createdAt ล่าสุดอยู่ข้างบน + // }, + // }); - // group1: group by ancestorDNA แล้วเลือก create_at ล่าสุด - const grouped1 = new Map(); - for (const item of posEmpHis) { - const key = `${item.ancestorDNA}`; - if (!grouped1.has(key)) { - grouped1.set(key, item); - } else { - // ถ้าเจอซ้ำ ให้เลือก createdAt ล่าสุด - const exist = grouped1.get(key); - if (exist && item.createdAt > exist.createdAt) { - grouped1.set(key, item); - } - } - } - // group2: group by shortName-posMasterNo จากค่าที่ได้จาก group1 - const grouped2 = new Map(); - for (const item of Array.from(grouped1.values())) { - const key = `${item.shortName}-${item.posMasterNo}`; - if (!grouped2.has(key)) { - grouped2.set(key, item); - } else { - // ถ้าเจอซ้ำ ให้เลือก createdAt ล่าสุด - const exist = grouped2.get(key); - if (exist && item.createdAt > exist.createdAt) { - grouped2.set(key, item); - } - } - } - // group3: group by firstName-lastName จากค่าที่ได้จาก group2 - const grouped3 = new Map(); - for (const item of Array.from(grouped2.values())) { - const key = `${item.firstName}-${item.lastName}`; - if (!grouped3.has(key)) { - grouped3.set(key, item); - } else { - // ถ้าเจอซ้ำ ให้เลือก createdAt ล่าสุด - const exist = grouped3.get(key); - if (exist && item.createdAt > exist.createdAt) { - grouped3.set(key, item); - } - } - } + // // group by ancestorDNA แล้วเลือก create_at ล่าสุด + // const grouped = new Map(); + // for (const item of profile) { + // const key = `${item.shortName}-${item.posMasterNo}`; + // if (!grouped.has(key)) { + // grouped.set(key, item); + // } else { + // // ถ้าเจอซ้ำ ให้เลือก createdAt ล่าสุด + // const exist = grouped.get(key); + // if (exist && item.createdAt > exist.createdAt) { + // grouped.set(key, item); + // } + // } + // } - const profileEmployeeIds = Array.from(grouped3.values()) - .filter((x) => x.profileEmployeeId != null) - .map((x) => x.profileEmployeeId); + // const profile_ = await Promise.all( + // Array.from(grouped.values()) + // .filter((x) => x.profileId != null) + // .map(async (item: PosMasterEmployeeHistory) => { + // let profile = await this.profileRepo.findOne({ + // where: { id: item.profileId }, + // }); - const profileEmployees = await this.profileEmpRepo.find({ - where: { id: In(profileEmployeeIds) }, - select: ["id", "citizenId", "dateStart", "dateAppoint", "keycloak"], - }); + // return { + // id: item.profileId, + // prefix: item.prefix, + // firstName: item.firstName, + // lastName: item.lastName, + // citizenId: profile?.citizenId ?? null, + // dateStart: profile?.dateStart ?? null, + // dateAppoint: profile?.dateAppoint ?? null, + // keycloak: profile?.keycloak ?? null, + // posNo: item.shortName, + // position: item.position, + // positionLevel: item.posLevel, + // positionType: item.posType, + // // oc: Oc, + // orgRootId: item.rootDnaId, + // orgChild1Id: item.child1DnaId, + // orgChild2Id: item.child2DnaId, + // orgChild3Id: item.child3DnaId, + // orgChild4Id: item.child4DnaId, + // }; + // }), + // ); - const profileEmployeeMap = new Map(profileEmployees.map((p) => [p.id, p])); - - const profile_ = Array.from(grouped3.values()) - .filter((x) => x.profileEmployeeId != null) - .map((item: PosMasterEmployeeHistory) => { - const profileEmp = profileEmployeeMap.get(item.profileEmployeeId); - return { - id: item.profileEmployeeId, - prefix: item.prefix, - firstName: item.firstName, - lastName: item.lastName, - citizenId: profileEmp?.citizenId ?? null, - dateStart: profileEmp?.dateStart ?? null, - dateAppoint: profileEmp?.dateAppoint ?? null, - keycloak: profileEmp?.keycloak ?? null, - posNo: `${item.shortName} ${[item.posMasterNoPrefix, item.posMasterNo, item.posMasterNoSuffix] - .filter((p) => p !== null && p !== undefined && p !== "") - .join(" ")}`, - position: item.position, - positionLevel: item.posLevel, - positionType: item.posType, - orgRootId: item.rootDnaId, - orgChild1Id: item.child1DnaId, - orgChild2Id: item.child2DnaId, - orgChild3Id: item.child3DnaId, - orgChild4Id: item.child4DnaId, - }; - }); - - return new HttpSuccess( - (profile_ ?? []).sort((a, b) => a.posNo.localeCompare(b.posNo, undefined, { numeric: true })), - ); - } + // return new HttpSuccess(profile_); + // } /** * 4. API Update รอบการลงเวลา ในตาราง profile @@ -7253,7 +7028,6 @@ export class OrganizationDotnetController extends Controller { * */ @Put("update-dutytime") - @Security("bearerAuth") async UpdateDutyTimeAsync( @Request() req: RequestWithUser, @Body() @@ -7296,7 +7070,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("insignia/Dumb") - @Security("bearerAuth") public async newInsignia(@Request() req: RequestWithUser, @Body() body: CreateProfileInsignia) { if (!body.profileId) { throw new HttpError(HttpStatus.BAD_REQUEST, "กรุณากรอก profileId"); @@ -7373,7 +7146,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} keycloakId Id keycloak */ @Get("profile-leave/keycloak/{keycloakId}") - @Security("internalAuth") async GetProfileLeaveByKeycloakIdAsync(@Path() keycloakId: string) { const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); let _currentDate = CURRENT_DATE[0].today; @@ -7661,7 +7433,6 @@ export class OrganizationDotnetController extends Controller { } @Post("profile-leave/keycloak") - @Security("internalAuth") async GetProfileLeaveReportByKeycloakIdAsync( @Body() body: { keycloakId: string; report?: string }, ) { @@ -7966,7 +7737,6 @@ export class OrganizationDotnetController extends Controller { * @param {string} type ประเภท (ข้าราชการ หรือ ลูกจ้าง) */ @Post("find/insignia-requests-profile/{type}") - @Security("internalAuth") async GetInsigniaRequestsProfileAsync( @Path() type: string, @Body() @@ -8096,7 +7866,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("officer-by-admin-rolev2") - @Security("internalAuth") async GetOfficersByAdminRoleV2( @Body() body: { @@ -8320,7 +8089,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("officer-by-admin-rolev3") - @Security("internalAuth") async GetOfficersByAdminRoleV3( @Body() body: { @@ -8667,7 +8435,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("officer-by-admin-rolev4") - @Security("internalAuth") async GetOfficersByAdminRoleV4( @Body() body: { @@ -8902,7 +8669,7 @@ export class OrganizationDotnetController extends Controller { const profiles = await this.profileRepo.find({ where: { id: In(profileIds) }, - select: ["id", "citizenId", "dateStart", "dateAppoint", "keycloak", "posMasterNo"], + select: ["id", "citizenId", "dateStart", "dateAppoint", "keycloak"], }); const profileMap = new Map(profiles.map((p) => [p.id, p])); @@ -8920,11 +8687,7 @@ export class OrganizationDotnetController extends Controller { dateStart: profile?.dateStart ?? null, dateAppoint: profile?.dateAppoint ?? null, keycloak: profile?.keycloak ?? null, - posNo: - profile?.posMasterNo ?? - `${item.shortName} ${[item.posMasterNoPrefix, item.posMasterNo, item.posMasterNoSuffix] - .filter((p) => p !== null && p !== undefined && p !== "") - .join(" ")}`, + posNo: `${item.shortName} ${item.posMasterNo}`, position: item.position, positionLevel: item.posLevel, positionType: item.posType, @@ -8948,7 +8711,6 @@ export class OrganizationDotnetController extends Controller { * */ @Post("find-staff") - @Security("internalAuth") async findHigher( @Body() requestBody: { @@ -9157,43 +8919,4 @@ export class OrganizationDotnetController extends Controller { }); return new HttpSuccess(filteredPosMasters); } - - /** - * API ตรวจสอบสถานะผู้สมัครสอบ - * @summary API ตรวจสอบสถานะผู้สมัครสอบ - */ - @Post("check-isLeave") - @Security("internalAuth") - async findProfileIsLeave( - @Body() - req: { citizenIds: string[] } - ) { - - const profiles = await this.profileRepo.find({ - select: { - id: true, - citizenId: true, - isLeave: true, - isActive: true - }, - where: { - citizenId: In(req.citizenIds) - } - }); - - if (profiles.length === 0) { - return new HttpSuccess([]); - } - - return new HttpSuccess( - profiles.map(p => ({ - citizenId: p.citizenId, - profileId: p.id, - isLeave: p.isLeave ?? false, - isActive: p.isActive ?? false - })) - ); - - } - } diff --git a/src/controllers/PermissionController.ts b/src/controllers/PermissionController.ts index 44747b09..8c713947 100644 --- a/src/controllers/PermissionController.ts +++ b/src/controllers/PermissionController.ts @@ -37,13 +37,11 @@ export class PermissionController extends Controller { @Get("") public async getPermission(@Request() request: RequestWithUser) { - let redisClient; - try { - redisClient = await this.redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); - const getAsync = promisify(redisClient.get).bind(redisClient); + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); + const getAsync = promisify(redisClient.get).bind(redisClient); let profile: any = await this.profileRepo.findOne({ select: ["id"], @@ -272,11 +270,6 @@ export class PermissionController extends Controller { redisClient.setex("role_" + profile.id, 86400, JSON.stringify(reply)); } return new HttpSuccess(reply); - } finally { - if (redisClient) { - redisClient.quit(); - } - } } @Get("menu") @@ -288,13 +281,11 @@ export class PermissionController extends Controller { orgRevisionIsCurrent: true, }, }); - let redisClient; - try { - redisClient = await this.redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); - const getAsync = promisify(redisClient.get).bind(redisClient); + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); + const getAsync = promisify(redisClient.get).bind(redisClient); let profileType = "OFFICER"; let profile: any = await this.profileRepo.findOne({ @@ -447,11 +438,6 @@ export class PermissionController extends Controller { } return new HttpSuccess(reply); - } finally { - if (redisClient) { - redisClient.quit(); - } - } } /** @@ -686,13 +672,11 @@ export class PermissionController extends Controller { @Path() system: string, @Path() action: string, ) { - let redisClient; - try { - redisClient = await this.redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); - const getAsync = promisify(redisClient.get).bind(redisClient); + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); + const getAsync = promisify(redisClient.get).bind(redisClient); let profileType = "OFFICER"; let profile: any = await this.profileRepo.findOne({ @@ -781,11 +765,6 @@ export class PermissionController extends Controller { } return new HttpSuccess(reply); - } finally { - if (redisClient) { - redisClient.quit(); - } - } } @Get("user/{system}/{action}/{id}") @@ -802,13 +781,11 @@ export class PermissionController extends Controller { orgRevisionIsCurrent: true, }, }); - let redisClient; - try { - redisClient = await this.redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); - const getAsync = promisify(redisClient.get).bind(redisClient); + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); + const getAsync = promisify(redisClient.get).bind(redisClient); let org = this.PermissionOrg(request, system, action); let reply = await getAsync("user_" + id); @@ -889,21 +866,14 @@ export class PermissionController extends Controller { } return new HttpSuccess(reply); - } finally { - if (redisClient) { - redisClient.quit(); - } - } } public async getPermissionFunc(@Request() request: RequestWithUser) { - let redisClient; - try { - redisClient = await this.redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); - const getAsync = promisify(redisClient.get).bind(redisClient); + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); + const getAsync = promisify(redisClient.get).bind(redisClient); let profile: any = await this.profileRepo.findOne({ select: ["id"], @@ -1120,11 +1090,6 @@ export class PermissionController extends Controller { redisClient.setex("role_" + profile.id, 86400, JSON.stringify(reply)); } return reply; - } finally { - if (redisClient) { - redisClient.quit(); - } - } } public async Permission(req: RequestWithUser, system: string, action: string) { @@ -1150,13 +1115,11 @@ export class PermissionController extends Controller { } public async listAuthSysOrgFunc(request: RequestWithUser, system: string, action: string) { - let redisClient; - try { - redisClient = await this.redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); - const getAsync = promisify(redisClient.get).bind(redisClient); + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); + const getAsync = promisify(redisClient.get).bind(redisClient); let profileType = "OFFICER"; let profile: any = await this.profileRepo.findOne({ @@ -1224,11 +1187,6 @@ export class PermissionController extends Controller { redisClient.setex("posMaster_" + profile.id, 86400, JSON.stringify(reply)); } return reply; - } finally { - if (redisClient) { - redisClient.quit(); - } - } } // Helper method: ดึง org scope จากตำแหน่งปกติ @@ -1408,13 +1366,11 @@ export class PermissionController extends Controller { @Get("checkOrg/{keycloakId}") public async checkOrg(@Path() keycloakId: string) { - let redisClient; - try { - redisClient = await this.redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); - // const getAsync = promisify(redisClient.get).bind(redisClient); + const redisClient = await this.redis.createClient({ + host: REDIS_HOST, + port: REDIS_PORT, + }); + // const getAsync = promisify(redisClient.get).bind(redisClient); // let profileType = "OFFICER"; let profile: any = await this.profileRepo.findOne({ @@ -1492,10 +1448,5 @@ export class PermissionController extends Controller { // } return new HttpSuccess(reply); - } finally { - if (redisClient) { - redisClient.quit(); - } - } } } diff --git a/src/controllers/PermissionProfileController.ts b/src/controllers/PermissionProfileController.ts index 0df114af..9f565828 100644 --- a/src/controllers/PermissionProfileController.ts +++ b/src/controllers/PermissionProfileController.ts @@ -65,7 +65,7 @@ export class PermissionProfileController extends Controller { if (!request.user.role.includes("SUPER_ADMIN")) { rootId = - orgRevisionActive?.posMasters?.filter((x) => x.current_holderId == profile.id)[0] + orgRevisionActive?.posMasters?.filter((x) => x.next_holderId == profile.id)[0] ?.orgRootId || null; if (!rootId) return new HttpSuccess([]); } @@ -165,59 +165,59 @@ export class PermissionProfileController extends Controller { _data.current_holders.length == 0 ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != - null + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != + null ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName} ${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild3 != null + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild3 != null ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName} ${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild2 != null + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild2 != null ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName} ${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild1 != null + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild1 != null ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName} ${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - null && - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgRoot != null + null && + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgRoot != null ? `${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName} ${_data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : null; const root = _data.current_holders.length == 0 || - (_data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) + (_data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot; const child1 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1; const child2 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2; const child3 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3; const child4 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4; @@ -363,78 +363,78 @@ export class PermissionProfileController extends Controller { _data.profileTree.current_holders.length == 0 ? null : _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - null && - _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild4 != null + null && + _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild4 != null ? `${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName} ${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - null && - _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild3 != null + null && + _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild3 != null ? `${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName} ${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : _data.profileTree.current_holders.find( - (x) => x.orgRevisionId == findRevision.id, - ) != null && - _data.profileTree.current_holders.find( - (x) => x.orgRevisionId == findRevision.id, - )?.orgChild2 != null - ? `${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName} ${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - : _data.profileTree.current_holders.find( - (x) => x.orgRevisionId == findRevision.id, - ) != null && - _data.profileTree.current_holders.find( - (x) => x.orgRevisionId == findRevision.id, - )?.orgChild1 != null - ? `${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName} ${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` - : _data.profileTree.current_holders.find( (x) => x.orgRevisionId == findRevision.id, ) != null && + _data.profileTree.current_holders.find( + (x) => x.orgRevisionId == findRevision.id, + )?.orgChild2 != null + ? `${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName} ${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` + : _data.profileTree.current_holders.find( + (x) => x.orgRevisionId == findRevision.id, + ) != null && _data.profileTree.current_holders.find( (x) => x.orgRevisionId == findRevision.id, - )?.orgRoot != null + )?.orgChild1 != null + ? `${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName} ${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` + : _data.profileTree.current_holders.find( + (x) => x.orgRevisionId == findRevision.id, + ) != null && + _data.profileTree.current_holders.find( + (x) => x.orgRevisionId == findRevision.id, + )?.orgRoot != null ? `${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName} ${_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : null; const root = _data.profileTree.current_holders.length == 0 || - (_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - null && - _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgRoot == null) + (_data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) != + null && + _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgRoot == null) ? null : _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgRoot; + ?.orgRoot; const child1 = _data.profileTree.current_holders == null || - _data.profileTree.current_holders.length == 0 || - _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.profileTree.current_holders.length == 0 || + _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild1; + ?.orgChild1; const child2 = _data.profileTree.current_holders == null || - _data.profileTree.current_holders.length == 0 || - _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.profileTree.current_holders.length == 0 || + _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild2; + ?.orgChild2; const child3 = _data.profileTree.current_holders == null || - _data.profileTree.current_holders.length == 0 || - _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.profileTree.current_holders.length == 0 || + _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild3; + ?.orgChild3; const child4 = _data.profileTree.current_holders == null || - _data.profileTree.current_holders.length == 0 || - _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.profileTree.current_holders.length == 0 || + _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.profileTree.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild4; + ?.orgChild4; let _root = root?.orgRootName; let _child1 = child1?.orgChild1Name; diff --git a/src/controllers/PosMasterActController.ts b/src/controllers/PosMasterActController.ts index 3b5d3196..fbc09201 100644 --- a/src/controllers/PosMasterActController.ts +++ b/src/controllers/PosMasterActController.ts @@ -621,13 +621,13 @@ export class PosMasterActController extends Controller { let conditionGroup = ""; if (body.type.trim().toUpperCase() == "GROUP1.1") { conditionGroup = - "(posType.posTypeName = 'ทั่วไป' AND posLevel.posLevelName = 'ชำนาญงาน') OR (posType.posTypeName = 'ทั่วไป' AND posLevel.posLevelName = 'ปฏิบัติงาน') OR (posType.posTypeName = 'วิชาการ' AND posLevel.posLevelName = 'ปฏิบัติการ') OR (posType.posTypeName = 'วิชาการ' AND posLevel.posLevelName = 'ชำนาญการ')"; + "(posTypeAct.posTypeName = 'ทั่วไป' AND posLevelAct.posLevelName = 'ชำนาญงาน') OR (posTypeAct.posTypeName = 'ทั่วไป' AND posLevelAct.posLevelName = 'ปฏิบัติงาน') OR (posTypeAct.posTypeName = 'วิชาการ' AND posLevelAct.posLevelName = 'ปฏิบัติการ') OR (posTypeAct.posTypeName = 'วิชาการ' AND posLevelAct.posLevelName = 'ชำนาญการ')"; } else if (body.type.trim().toUpperCase() == "GROUP1.2") { conditionGroup = - "(posType.posTypeName = 'ทั่วไป' AND posLevel.posLevelName = 'อาวุโส') OR (posType.posTypeName = 'วิชาการ' AND posLevel.posLevelName = 'ชำนาญการพิเศษ') OR (posType.posTypeName = 'อำนวยการ' AND posLevel.posLevelName = 'ต้น')"; + "(posTypeAct.posTypeName = 'ทั่วไป' AND posLevelAct.posLevelName = 'อาวุโส') OR (posTypeAct.posTypeName = 'วิชาการ' AND posLevelAct.posLevelName = 'ชำนาญการพิเศษ') OR (posTypeAct.posTypeName = 'อำนวยการ' AND posLevelAct.posLevelName = 'ต้น')"; } else if (body.type.trim().toUpperCase() == "GROUP2") { conditionGroup = - "(posType.posTypeName = 'ทั่วไป' AND posLevel.posLevelName = 'ทักษะพิเศษ') OR (posType.posTypeName = 'วิชาการ' AND posLevel.posLevelName = 'เชี่ยวชาญ') OR (posType.posTypeName = 'วิชาการ' AND posLevel.posLevelName = 'ทรงคุณวุฒิ') OR (posType.posTypeName = 'อำนวยการ' AND posLevel.posLevelName = 'สูง') OR (posType.posTypeName = 'บริหาร' AND posLevel.posLevelName = 'ต้น') OR (posType.posTypeName = 'บริหาร' AND posLevel.posLevelName = 'สูง')"; + "(posTypeAct.posTypeName = 'ทั่วไป' AND posLevelAct.posLevelName = 'ทักษะพิเศษ') OR (posTypeAct.posTypeName = 'วิชาการ' AND posLevelAct.posLevelName = 'เชี่ยวชาญ') OR (posTypeAct.posTypeName = 'วิชาการ' AND posLevelAct.posLevelName = 'ทรงคุณวุฒิ') OR (posTypeAct.posTypeName = 'อำนวยการ' AND posLevelAct.posLevelName = 'สูง') OR (posTypeAct.posTypeName = 'บริหาร' AND posLevelAct.posLevelName = 'ต้น') OR (posTypeAct.posTypeName = 'บริหาร' AND posLevelAct.posLevelName = 'สูง')"; } else { throw new HttpError(HttpStatusCode.NOT_FOUND, "กลุ่มเป้าหมายไม่ถูกต้อง"); } diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 96fa3db6..98120f30 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -38,7 +38,7 @@ import { EmployeePosLevel } from "../entities/EmployeePosLevel"; import { AuthRole } from "../entities/AuthRole"; import { RequestWithUser } from "../middlewares/user"; import permission from "../interfaces/permission"; -import { resolveNodeLevel, setLogDataDiff, logPositionIsSelectedChange } from "../interfaces/utils"; +import { resolveNodeLevel, setLogDataDiff } from "../interfaces/utils"; import { getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; import { PosMasterAssign } from "../entities/PosMasterAssign"; import { Assign } from "../entities/Assign"; @@ -1427,17 +1427,7 @@ export class PositionController extends Controller { requestBody.positions.map(async (x: any) => { const match = posMaster.positions.find((p: any) => p.id == x.id); if (match) { - const oldValue = match.positionIsSelected; - const newValue = x.positionIsSelected ?? false; - - logPositionIsSelectedChange(match.id, oldValue, newValue, { - posMasterId: posMaster.id, - userId: request.user.sub, - endpoint: "updateMaster", - action: "update_position", - }); - - match.positionIsSelected = newValue; + match.positionIsSelected = x.positionIsSelected ?? false; match.orderNo = x.orderNo ?? null; return match; } else { @@ -1688,11 +1678,11 @@ export class PositionController extends Controller { let checkChildConditions: any = {}; let keywordAsInt: any; let searchShortName = "1=1"; - let searchShortName0 = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName1 = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName2 = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName3 = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName4 = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; + let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; + let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; + let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; + let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; + let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; if (body.type != null && body.id != null) { if (body.type === 0) { typeCondition = { @@ -1702,7 +1692,7 @@ export class PositionController extends Controller { checkChildConditions = { orgChild1Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`; } else { } } else if (body.type === 1) { @@ -1713,7 +1703,7 @@ export class PositionController extends Controller { checkChildConditions = { orgChild2Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`; } else { } } else if (body.type === 2) { @@ -1724,7 +1714,7 @@ export class PositionController extends Controller { checkChildConditions = { orgChild3Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`; } else { } } else if (body.type === 3) { @@ -1735,14 +1725,14 @@ export class PositionController extends Controller { checkChildConditions = { orgChild4Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`; } else { } } else if (body.type === 4) { typeCondition = { orgChild4Id: body.id, }; - searchShortName = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix) like '%${body.keyword}%'`; } } else { body.isAll = true; @@ -1787,8 +1777,10 @@ export class PositionController extends Controller { select: ["posMasterId"], }); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId)); - const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); - keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; + keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10); + if (isNaN(keywordAsInt)) { + keywordAsInt = "P@ssw0rd!z"; + } masterId = [...new Set(masterId)]; //serch name สิทธิ์ @@ -1821,7 +1813,7 @@ export class PositionController extends Controller { ...(body.keyword && (masterId.length > 0 ? { id: In(masterId) } - : /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), + : { posMasterNo: Like(`%${body.keyword}%`) })), }, ]; let [posMaster, total] = await AppDataSource.getRepository(PosMaster) @@ -2162,11 +2154,11 @@ export class PositionController extends Controller { let checkChildConditions: any = {}; let keywordAsInt: any; let searchShortName = "1=1"; - let searchShortName0 = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName1 = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName2 = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName3 = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName4 = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; + let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo)`; + let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo)`; + let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo)`; + let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo)`; + let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo)`; let _data = await new permission().PermissionOrgList(request, "SYS_ORG"); if (body.type === 0) { typeCondition = { @@ -2176,7 +2168,7 @@ export class PositionController extends Controller { checkChildConditions = { orgChild1Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgRoot.orgRootShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } } else if (body.type === 1) { typeCondition = { @@ -2186,7 +2178,7 @@ export class PositionController extends Controller { checkChildConditions = { orgChild2Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } } else if (body.type === 2) { typeCondition = { @@ -2196,7 +2188,7 @@ export class PositionController extends Controller { checkChildConditions = { orgChild3Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } } else if (body.type === 3) { typeCondition = { @@ -2206,13 +2198,13 @@ export class PositionController extends Controller { checkChildConditions = { orgChild4Id: IsNull(), }; - searchShortName = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } } else if (body.type === 4) { typeCondition = { orgChild4Id: body.id, }; - searchShortName = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNo) like '%${body.keyword}%'`; } let findPosition: any; let masterId = new Array(); @@ -2249,8 +2241,10 @@ export class PositionController extends Controller { select: ["posMasterId"], }); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId)); - const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); - keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; + keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10); + if (isNaN(keywordAsInt)) { + keywordAsInt = "P@ssw0rd!z"; + } masterId = [...new Set(masterId)]; } @@ -2277,7 +2271,7 @@ export class PositionController extends Controller { ...(body.keyword && (masterId.length > 0 ? { id: In(masterId) } - : /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), + : { posMasterNo: Like(`%${body.keyword}%`) })), }, ]; @@ -2766,19 +2760,7 @@ export class PositionController extends Controller { id: data.id, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, })); - // Bulk update using CASE WHEN instead of save() per row - const caseClauses_0 = sortData_0 - .map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`) - .join(" "); - const ids_0 = sortData_0.map((d) => `'${d.id}'`).join(","); - await this.posMasterRepository - .createQueryBuilder() - .update(PosMaster) - .set({ - posMasterOrder: () => `CASE id ${caseClauses_0} END`, - }) - .where(`id IN (${ids_0})`) - .execute(); + await this.posMasterRepository.save(sortData_0, { data: request }); setLogDataDiff(request, { before, after: sortData_0 }); break; } @@ -2807,19 +2789,7 @@ export class PositionController extends Controller { id: data.id, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, })); - // Bulk update using CASE WHEN instead of save() per row - const caseClauses_1 = sortData_1 - .map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`) - .join(" "); - const ids_1 = sortData_1.map((d) => `'${d.id}'`).join(","); - await this.posMasterRepository - .createQueryBuilder() - .update(PosMaster) - .set({ - posMasterOrder: () => `CASE id ${caseClauses_1} END`, - }) - .where(`id IN (${ids_1})`) - .execute(); + await this.posMasterRepository.save(sortData_1, { data: request }); setLogDataDiff(request, { before, after: sortData_1 }); break; } @@ -2848,19 +2818,7 @@ export class PositionController extends Controller { id: data.id, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, })); - // Bulk update using CASE WHEN instead of save() per row - const caseClauses_2 = sortData_2 - .map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`) - .join(" "); - const ids_2 = sortData_2.map((d) => `'${d.id}'`).join(","); - await this.posMasterRepository - .createQueryBuilder() - .update(PosMaster) - .set({ - posMasterOrder: () => `CASE id ${caseClauses_2} END`, - }) - .where(`id IN (${ids_2})`) - .execute(); + await this.posMasterRepository.save(sortData_2, { data: request }); setLogDataDiff(request, { before, after: sortData_2 }); break; } @@ -2889,19 +2847,7 @@ export class PositionController extends Controller { id: data.id, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, })); - // Bulk update using CASE WHEN instead of save() per row - const caseClauses_3 = sortData_3 - .map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`) - .join(" "); - const ids_3 = sortData_3.map((d) => `'${d.id}'`).join(","); - await this.posMasterRepository - .createQueryBuilder() - .update(PosMaster) - .set({ - posMasterOrder: () => `CASE id ${caseClauses_3} END`, - }) - .where(`id IN (${ids_3})`) - .execute(); + await this.posMasterRepository.save(sortData_3, { data: request }); setLogDataDiff(request, { before, after: sortData_3 }); break; } @@ -2930,19 +2876,7 @@ export class PositionController extends Controller { id: data.id, posMasterOrder: requestBody.sortId.indexOf(data.id) + 1, })); - // Bulk update using CASE WHEN instead of save() per row - const caseClauses_4 = sortData_4 - .map((d) => `WHEN '${d.id}' THEN ${d.posMasterOrder}`) - .join(" "); - const ids_4 = sortData_4.map((d) => `'${d.id}'`).join(","); - await this.posMasterRepository - .createQueryBuilder() - .update(PosMaster) - .set({ - posMasterOrder: () => `CASE id ${caseClauses_4} END`, - }) - .where(`id IN (${ids_4})`) - .execute(); + await this.posMasterRepository.save(sortData_4, { data: request }); setLogDataDiff(request, { before, after: sortData_4 }); break; } @@ -4040,18 +3974,7 @@ export class PositionController extends Controller { statusReport: "PENDING", }); - console.log( - `[positionIsSelected-DEBUG] Deleting holder, resetting ALL positions to false (posMasterId: ${id}, userId: ${request.user.sub}, endpoint: deleteHolder)` - ); - dataMaster.positions.forEach(async (position) => { - logPositionIsSelectedChange(position.id, position.positionIsSelected, false, { - posMasterId: id, - userId: request.user.sub, - endpoint: "deleteHolder", - action: "delete_holder_reset_positions", - }); - await this.positionRepository.update(position.id, { positionIsSelected: false, }); @@ -4242,7 +4165,6 @@ export class PositionController extends Controller { const [posMaster, total] = await AppDataSource.getRepository(PosMaster) .createQueryBuilder("posMaster") - .leftJoinAndSelect("posMaster.orgRevision", "orgRevision") .leftJoinAndSelect("posMaster.orgRoot", "orgRoot") .leftJoinAndSelect("posMaster.orgChild1", "orgChild1") .leftJoinAndSelect("posMaster.orgChild2", "orgChild2") @@ -4254,8 +4176,6 @@ export class PositionController extends Controller { .leftJoinAndSelect("positions.posType", "posType") .leftJoinAndSelect("positions.posLevel", "posLevel") .leftJoinAndSelect("positions.posExecutive", "posExecutive") - .andWhere("orgRevision.orgRevisionIsCurrent = true") - .andWhere("orgRevision.orgRevisionIsDraft = false") .andWhere( new Brackets((qb) => { qb.andWhere(typeCondition).andWhere(conditionA == null ? "1=1" : conditionA, { @@ -4434,7 +4354,6 @@ export class PositionController extends Controller { typeCommand: string | null; posType?: string | null; posLevel?: string | null; - profileId?: string | null; isAll: boolean; isBlank: boolean; }, @@ -4476,13 +4395,9 @@ export class PositionController extends Controller { posLevel: posLevel?.id, }; } else if (body.typeCommand == "APPOINT") { - // เดิม : กรองเฉพาะ posTypeRank ที่สูงกว่า - // conditionA = "posType.posTypeRank > :posTypeRank"; - // ใหม่ : กรองเฉพาะ posType ที่สูงหรือต่ำกว่าก็ได้ - conditionA = "positions.posTypeId != :currentPosType"; + conditionA = "posType.posTypeRank > :posTypeRank"; params = { - // posTypeRank: posType?.posTypeRank ?? 0, - currentPosType: posType?.id, + posTypeRank: posType?.posTypeRank ?? 0, }; } else if (body.typeCommand == "SLIP") { conditionA = "positions.posTypeId LIKE :posType AND posLevel.posLevelRank > :posLevelRank"; @@ -4546,13 +4461,8 @@ export class PositionController extends Controller { typeCondition.current_holderId = IsNull(); } - if (body.typeCommand === "MOVE" && body.profileId && !body.isBlank) { - typeCondition.current_holderId = Not(body.profileId); - } - const [posMaster, total] = await AppDataSource.getRepository(PosMaster) .createQueryBuilder("posMaster") - .leftJoinAndSelect("posMaster.orgRevision", "orgRevision") .leftJoinAndSelect("posMaster.orgRoot", "orgRoot") .leftJoinAndSelect("posMaster.orgChild1", "orgChild1") .leftJoinAndSelect("posMaster.orgChild2", "orgChild2") @@ -4565,8 +4475,6 @@ export class PositionController extends Controller { .leftJoinAndSelect("positions.posLevel", "posLevel") .leftJoinAndSelect("positions.posExecutive", "posExecutive") .andWhere("posMaster.next_holderId IS NULL") - .andWhere("orgRevision.orgRevisionIsCurrent = true") - .andWhere("orgRevision.orgRevisionIsDraft = false") .andWhere( new Brackets((qb) => { qb.andWhere(typeCondition) @@ -5366,11 +5274,11 @@ export class PositionController extends Controller { let checkChildConditions: any = {}; let keywordAsInt: any; let searchShortName = "1=1"; - let searchShortName0 = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName1 = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName2 = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName3 = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; - let searchShortName4 = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,''))`; + let searchShortName0 = `CONCAT(orgRoot.orgRootShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`; + let searchShortName1 = `CONCAT(orgChild1.orgChild1ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`; + let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`; + let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`; + let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, ""))`; let _data = await new permission().PermissionOrgList(request, "SYS_POS_CONDITION"); const orgDna = await new permission().checkDna(request, request.user.sub); let level: any = resolveNodeLevel(orgDna); @@ -5412,7 +5320,7 @@ export class PositionController extends Controller { // checkChildConditions = { // orgChild1Id: IsNull(), // }; - // searchShortName = `CONCAT_WS(' ',orgRoot.orgRootShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + // searchShortName = `CONCAT(orgRoot.orgRootShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; // } else { // } } else if (body.type === 1) { @@ -5423,7 +5331,7 @@ export class PositionController extends Controller { // checkChildConditions = { // orgChild2Id: IsNull(), // }; - // searchShortName = `CONCAT_WS(' ',orgChild1.orgChild1ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + // searchShortName = `CONCAT(orgChild1.orgChild1ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; // } else { // } } else if (body.type === 2) { @@ -5434,7 +5342,7 @@ export class PositionController extends Controller { // checkChildConditions = { // orgChild3Id: IsNull(), // }; - // searchShortName = `CONCAT_WS(' ',orgChild2.orgChild2ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + // searchShortName = `CONCAT(orgChild2.orgChild2ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; // } else { // } } else if (body.type === 3) { @@ -5445,14 +5353,14 @@ export class PositionController extends Controller { // checkChildConditions = { // orgChild4Id: IsNull(), // }; - // searchShortName = `CONCAT_WS(' ',orgChild3.orgChild3ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + // searchShortName = `CONCAT(orgChild3.orgChild3ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; // } else { // } } else if (body.type === 4) { typeCondition = { ...(cannotViewChild4PosMaster ? { orgChild4Id: null } : { orgChild4Id: body.id }), }; - searchShortName = `CONCAT_WS(' ',orgChild4.orgChild4ShortName,NULLIF(posMaster.posMasterNoPrefix,''),posMaster.posMasterNo,NULLIF(posMaster.posMasterNoSuffix,'')) like '%${body.keyword}%'`; + searchShortName = `CONCAT(orgChild4.orgChild4ShortName," ",COALESCE(posMaster.posMasterNoPrefix, ""),posMaster.posMasterNo,COALESCE(posMaster.posMasterNoSuffix, "")) like '%${body.keyword}%'`; } let findPosition: any; let masterId = new Array(); @@ -5489,8 +5397,10 @@ export class PositionController extends Controller { select: ["posMasterId"], }); masterId = masterId.concat(findPosition.map((position: any) => position.posMasterId)); - const numericMatch = body.keyword == null ? null : body.keyword.match(/\d+/); - keywordAsInt = numericMatch ? parseInt(numericMatch[0], 10) : null; + keywordAsInt = body.keyword == null ? null : parseInt(body.keyword, 10); + if (isNaN(keywordAsInt)) { + keywordAsInt = "P@ssw0rd!z"; + } masterId = [...new Set(masterId)]; } @@ -5517,7 +5427,7 @@ export class PositionController extends Controller { ...(body.keyword && (masterId.length > 0 ? { id: In(masterId) } - : /^\d+$/.test(body.keyword) ? { posMasterNo: keywordAsInt } : { posMasterNo: Like(`%${body.keyword}%`) })), + : { posMasterNo: Like(`%${body.keyword}%`) })), ...(!body.isAll && { isCondition: true }), }, ]; diff --git a/src/controllers/ProfileChangeNameController.ts b/src/controllers/ProfileChangeNameController.ts index 22f147b8..77cff634 100644 --- a/src/controllers/ProfileChangeNameController.ts +++ b/src/controllers/ProfileChangeNameController.ts @@ -25,7 +25,6 @@ import { } from "../entities/ProfileChangeName"; import { updateName } from "../keycloak"; import permission from "../interfaces/permission"; -import { updateHolderProfileHistory } from "../services/PositionService"; import { setLogDataDiff } from "../interfaces/utils"; @Route("api/v1/org/profile/changeName") @Tags("ProfileChangeName") @@ -111,12 +110,8 @@ export class ProfileChangeNameController extends Controller { setLogDataDiff(req, { before, after: history }); profile.firstName = body.firstName ?? profile.firstName; profile.lastName = body.lastName ?? profile.lastName; - // profile.prefix = body.prefix ?? profile.prefix; //old - profile.rank = body.rank ?? profile.rank; - // profile.prefixMain = profile.rank ?? profile.prefix; // old - profile.prefixMain = body.prefix ?? profile.prefix; - profile.prefix = body.rank && body.rank.length > 0 ? body.rank : body.prefix ?? profile.prefix; - + profile.prefix = body.prefix ?? profile.prefix; + profile.prefixMain = profile.rank ?? profile.prefix; await this.profileRepository.save(profile, { data: req }); setLogDataDiff(req, { before, after: profile }); @@ -132,9 +127,6 @@ export class ProfileChangeNameController extends Controller { } } - // บันทึกประวัติคนครองตำแหน่ง (ถ้า profile นี้ครองตำแหน่งอยู่) - await updateHolderProfileHistory(profile.id, req); - return new HttpSuccess(data.id); } @@ -187,11 +179,8 @@ export class ProfileChangeNameController extends Controller { if (profile && chkLastRecord.id === record.id) { profile.firstName = body.firstName ?? profile.firstName; profile.lastName = body.lastName ?? profile.lastName; - // profile.prefix = body.prefix ?? profile.prefix; //old - profile.rank = body.rank ?? profile.rank; - // profile.prefixMain = profile.rank ?? profile.prefix; // old - profile.prefixMain = body.prefix ?? profile.prefix; - profile.prefix = body.rank && body.rank.length > 0 ? body.rank : body.prefix ?? profile.prefix; + profile.prefix = body.prefix ?? profile.prefix; + profile.prefixMain = profile.rank ?? profile.prefix; await this.profileRepository.save(profile, { data: req }); setLogDataDiff(req, { before: before_profile, after: profile }); } diff --git a/src/controllers/ProfileChangeNameEmployeeController.ts b/src/controllers/ProfileChangeNameEmployeeController.ts index a632e718..c2df9c8c 100644 --- a/src/controllers/ProfileChangeNameEmployeeController.ts +++ b/src/controllers/ProfileChangeNameEmployeeController.ts @@ -24,7 +24,6 @@ import { } from "../entities/ProfileChangeName"; import { ProfileEmployee } from "../entities/ProfileEmployee"; import permission from "../interfaces/permission"; -import { updateHolderProfileHistory } from "../services/PositionService"; import { updateName } from "../keycloak"; import { setLogDataDiff } from "../interfaces/utils"; @Route("api/v1/org/profile-employee/changeName") @@ -117,11 +116,8 @@ export class ProfileChangeNameEmployeeController extends Controller { profile.firstName = body.firstName ?? profile.firstName; profile.lastName = body.lastName ?? profile.lastName; - // profile.prefix = body.prefix ?? profile.prefix; //old - profile.rank = body.rank ?? profile.rank; - // profile.prefixMain = profile.rank ?? profile.prefix; // old - profile.prefixMain = body.prefix ?? profile.prefix; - profile.prefix = body.rank && body.rank.length > 0 ? body.rank : body.prefix ?? profile.prefix; + profile.prefix = body.prefix ?? profile.prefix; + profile.prefixMain = profile.rank ?? profile.prefix; await this.profileEmployeeRepo.save(profile, { data: req }); setLogDataDiff(req, { before, after: profile }); @@ -137,9 +133,6 @@ export class ProfileChangeNameEmployeeController extends Controller { } } - // บันทึกประวัติคนครองตำแหน่ง (ถ้า profile นี้ครองตำแหน่งอยู่) - await updateHolderProfileHistory(profile.id, req, "EMPLOYEE"); - return new HttpSuccess(data.id); } @@ -193,11 +186,8 @@ export class ProfileChangeNameEmployeeController extends Controller { if (profile && chkLastRecord.id === record.id) { profile.firstName = body.firstName ?? profile.firstName; profile.lastName = body.lastName ?? profile.lastName; - // profile.prefix = body.prefix ?? profile.prefix; //old - profile.rank = body.rank ?? profile.rank; - // profile.prefixMain = profile.rank ?? profile.prefix; // old - profile.prefixMain = body.prefix ?? profile.prefix; - profile.prefix = body.rank && body.rank.length > 0 ? body.rank : body.prefix ?? profile.prefix; + profile.prefix = body.prefix ?? profile.prefix; + profile.prefixMain = profile.rank ?? profile.prefix; await this.profileEmployeeRepo.save(profile); } diff --git a/src/controllers/ProfileChangeNameEmployeeTempController.ts b/src/controllers/ProfileChangeNameEmployeeTempController.ts index 86c6b352..92e1e2f3 100644 --- a/src/controllers/ProfileChangeNameEmployeeTempController.ts +++ b/src/controllers/ProfileChangeNameEmployeeTempController.ts @@ -108,11 +108,8 @@ export class ProfileChangeNameEmployeeTempController extends Controller { profile.firstName = body.firstName ?? profile.firstName; profile.lastName = body.lastName ?? profile.lastName; - // profile.prefix = body.prefix ?? profile.prefix; //old - profile.rank = body.rank ?? profile.rank; - // profile.prefixMain = profile.rank ?? profile.prefix; // old - profile.prefixMain = body.prefix ?? profile.prefix; - profile.prefix = body.rank && body.rank.length > 0 ? body.rank : body.prefix ?? profile.prefix; + profile.prefix = body.prefix ?? profile.prefix; + profile.prefixMain = profile.rank ?? profile.prefix; await this.profileEmployeeRepo.save(profile, { data: req }); setLogDataDiff(req, { before, after: profile }); @@ -181,11 +178,8 @@ export class ProfileChangeNameEmployeeTempController extends Controller { if (profile && chkLastRecord.id === record.id) { profile.firstName = body.firstName ?? profile.firstName; profile.lastName = body.lastName ?? profile.lastName; - // profile.prefix = body.prefix ?? profile.prefix; //old - profile.rank = body.rank ?? profile.rank; - // profile.prefixMain = profile.rank ?? profile.prefix; // old - profile.prefixMain = body.prefix ?? profile.prefix; - profile.prefix = body.rank && body.rank.length > 0 ? body.rank : body.prefix ?? profile.prefix; + profile.prefix = body.prefix ?? profile.prefix; + profile.prefixMain = profile.rank ?? profile.prefix; await this.profileEmployeeRepo.save(profile); } diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 05ab62f8..3c514b6b 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -93,7 +93,6 @@ import { CreatePosMasterHistoryOfficer, getTopDegrees, getPosMasterPositions } f import { ProfileLeaveService } from "../services/ProfileLeaveService"; // import { PostRetireToExprofile } from "./ExRetirementController"; import { getPosNumCodeSit } from "../services/CommandService"; -import { updateHolderProfileHistory } from "../services/PositionService"; @Route("api/v1/org/profile") @Tags("Profile") @Security("bearerAuth") @@ -408,8 +407,8 @@ export class ProfileController extends Controller { ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].positionExecutive)) : "", org: `${salary_raw.length > 0 && salary_raw[0].orgChild4 && salary_raw[0].orgChild4 != "-" - ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild4)) + " " - : "" + ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild4)) + " " + : "" }${salary_raw.length > 0 && salary_raw[0].orgChild3 && salary_raw[0].orgChild3 != "-" ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild3)) + " " : "" @@ -5775,34 +5774,36 @@ export class ProfileController extends Controller { } if (body.citizenId) { - Extension.CheckCitizen(body.citizenId); + const citizenIdDigits = body.citizenId.toString().split("").map(Number); + const cal = + citizenIdDigits[0] * 13 + + citizenIdDigits[1] * 12 + + citizenIdDigits[2] * 11 + + citizenIdDigits[3] * 10 + + citizenIdDigits[4] * 9 + + citizenIdDigits[5] * 8 + + citizenIdDigits[6] * 7 + + citizenIdDigits[7] * 6 + + citizenIdDigits[8] * 5 + + citizenIdDigits[9] * 4 + + citizenIdDigits[10] * 3 + + citizenIdDigits[11] * 2; + const calStp2 = cal % 11; + const chkDigit = (11 - calStp2) % 10; + + if (citizenIdDigits[12] !== chkDigit) { + throw new HttpError(HttpStatus.NOT_FOUND, "ข้อมูลรหัสบัตรประจำตัวประชาชนไม่ถูกต้อง"); + } } const record = await this.profileRepo.findOneBy({ id }); const before = structuredClone(record); - // เช็คว่ามี profileHistory ของ profile นี้หรือไม่ - const historyCount = await this.profileHistoryRepo.count({ - where: { profileId: id }, - }); - - // ถ้าไม่มีเลย ให้บันทึกข้อมูลเริ่มต้น (ก่อน update) ลงไปก่อน - if (historyCount === 0) { - await this.profileHistoryRepo.save( - Object.assign(new ProfileHistory(), { - ...before, - birthDateOld: before?.birthDate, - profileId: id, - id: undefined, - }), - ); - } if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์นี้"); Object.assign(record, body); record.dateRetireLaw = calculateRetireLaw(record.birthDate); record.prefixMain = record.prefix; - // record.prefix = record.rank && record.rank.length > 0 ? record.rank : record.prefixMain; - record.prefix = record.rank && record.rank.length > 0 ? record.rank : record.prefix; + record.prefix = record.rank && record.rank.length > 0 ? record.rank : record.prefixMain; record.createdUserId = request.user.sub; record.createdFullName = request.user.name; record.createdAt = new Date(); @@ -5832,9 +5833,6 @@ export class ProfileController extends Controller { } } - // บันทึกประวัติคนครองตำแหน่ง (ถ้า profile นี้ครองตำแหน่งอยู่) - await updateHolderProfileHistory(record.id, request); - return new HttpSuccess(); } @@ -6027,12 +6025,12 @@ export class ProfileController extends Controller { queryLike = "profile.position LIKE :keyword"; } else if (searchField == "posNo") { queryLike = ` - CASE - WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT_WS(' ', orgChild4.orgChild4ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT_WS(' ', orgChild3.orgChild3ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT_WS(' ', orgChild2.orgChild2ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT_WS(' ', orgChild1.orgChild1ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - ELSE CONCAT_WS(' ', orgRoot.orgRootShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) + CASE + WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, " ", current_holders.posMasterNo) + ELSE CONCAT(orgRoot.orgRootShortName, " ", current_holders.posMasterNo) END LIKE :keyword `; } @@ -6302,7 +6300,7 @@ export class ProfileController extends Controller { @Query() sortBy: string = "profile.dateLeave", @Query() sort: "ASC" | "DESC" = "ASC", ) { - let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_RETIRE_OFFICER"); + let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER"); const { data, total } = await this.profileLeaveService.getLeaveOfficer(request, { page, @@ -6617,12 +6615,12 @@ export class ProfileController extends Controller { queryLike = "profile.position LIKE :keyword"; } else if (searchField == "posNo") { queryLike = ` - CASE - WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT_WS(' ', orgChild4.orgChild4ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT_WS(' ', orgChild3.orgChild3ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT_WS(' ', orgChild2.orgChild2ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT_WS(' ', orgChild1.orgChild1ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - ELSE CONCAT_WS(' ', orgRoot.orgRootShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) + CASE + WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, " ", current_holders.posMasterNo) + ELSE CONCAT(orgRoot.orgRootShortName, " ", current_holders.posMasterNo) END LIKE :keyword `; } @@ -6805,19 +6803,18 @@ export class ProfileController extends Controller { .filter(Boolean) .join("\n"); - const numPart = holder ? [holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; const shortName = !holder ? null : holder.orgChild4 != null - ? `${holder.orgChild4.orgChild4ShortName} ${numPart}` + ? `${holder.orgChild4.orgChild4ShortName} ${holder.posMasterNo}` : holder.orgChild3 != null - ? `${holder.orgChild3.orgChild3ShortName} ${numPart}` + ? `${holder.orgChild3.orgChild3ShortName} ${holder.posMasterNo}` : holder.orgChild2 != null - ? `${holder.orgChild2.orgChild2ShortName} ${numPart}` + ? `${holder.orgChild2.orgChild2ShortName} ${holder.posMasterNo}` : holder.orgChild1 != null - ? `${holder.orgChild1.orgChild1ShortName} ${numPart}` + ? `${holder.orgChild1.orgChild1ShortName} ${holder.posMasterNo}` : holder.orgRoot != null - ? `${holder.orgRoot.orgRootShortName} ${numPart}` + ? `${holder.orgRoot.orgRootShortName} ${holder.posMasterNo}` : null; return { @@ -7011,12 +7008,12 @@ export class ProfileController extends Controller { queryLike = "profile.position LIKE :keyword"; } else if (searchField == "posNo") { queryLike = ` - CASE - WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT_WS(' ', orgChild4.orgChild4ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT_WS(' ', orgChild3.orgChild3ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT_WS(' ', orgChild2.orgChild2ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT_WS(' ', orgChild1.orgChild1ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - ELSE CONCAT_WS(' ', orgRoot.orgRootShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) + CASE + WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, " ", current_holders.posMasterNo) + ELSE CONCAT(orgRoot.orgRootShortName, " ", current_holders.posMasterNo) END LIKE :keyword `; } @@ -7162,7 +7159,6 @@ export class ProfileController extends Controller { : `profile.isLeave IS TRUE` : "1=1", ) - .andWhere("profile.isActive IS TRUE AND profile.isDelete IS FALSE") .andWhere(nodeCondition, { nodeId: nodeId }) .andWhere( new Brackets((qb) => { @@ -7194,7 +7190,7 @@ export class ProfileController extends Controller { .filter(Boolean) .join("\n"); - const numPart = holder ? [holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; + const numPart = holder ? `${holder.posMasterNoPrefix ?? ''}${holder.posMasterNo ?? ''}${holder.posMasterNoSuffix ?? ''}` : ''; const shortName = !holder ? null @@ -7953,38 +7949,40 @@ export class ProfileController extends Controller { privacyUser: profile.privacyUser, privacyMgt: profile.privacyMgt, isDeputy: root?.isDeputy ?? false, + // root?.orgRootShortName && posMaster?.posMasterNo + // ? `${root?.orgRootShortName} ${posMaster?.posMasterNo}` + // : "", }; - const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; if (_profile.child4Id != null) { _profile.node = 4; _profile.nodeId = _profile.child4Id; _profile.nodeDnaId = _profile.child4DnaId; _profile.nodeShortName = _profile.child4ShortName; - _profile.posNo = `${_profile.child4ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child4ShortName} ${_profile.posMasterNo}`; } else if (_profile.child3Id != null) { _profile.node = 3; _profile.nodeId = _profile.child3Id; _profile.nodeDnaId = _profile.child3DnaId; _profile.nodeShortName = _profile.child3ShortName; - _profile.posNo = `${_profile.child3ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child3ShortName} ${_profile.posMasterNo}`; } else if (_profile.child2Id != null) { _profile.node = 2; _profile.nodeId = _profile.child2Id; _profile.nodeDnaId = _profile.child2DnaId; _profile.nodeShortName = _profile.child2ShortName; - _profile.posNo = `${_profile.child2ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child2ShortName} ${_profile.posMasterNo}`; } else if (_profile.child1Id != null) { _profile.node = 1; _profile.nodeId = _profile.child1Id; _profile.nodeDnaId = _profile.child1DnaId; _profile.nodeShortName = _profile.child1ShortName; - _profile.posNo = `${_profile.child1ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child1ShortName} ${_profile.posMasterNo}`; } else if (_profile.rootId != null) { _profile.node = 0; _profile.nodeId = _profile.rootId; _profile.nodeDnaId = _profile.rootDnaId; _profile.nodeShortName = _profile.rootShortName; - _profile.posNo = `${_profile.rootShortName} ${_numPart}`; + _profile.posNo = `${_profile.rootShortName} ${_profile.posMasterNo}`; } return new HttpSuccess(_profile); } @@ -8124,39 +8122,41 @@ export class ProfileController extends Controller { privacyUser: profile.privacyUser, privacyMgt: profile.privacyMgt, isDeputy: root?.isDeputy ?? false, + // root?.orgRootShortName && posMaster?.posMasterNo + // ? `${root?.orgRootShortName} ${posMaster?.posMasterNo}` + // : "", }; - const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; if (_profile.child4Id != null) { _profile.node = 4; _profile.nodeId = _profile.child4Id; _profile.nodeDnaId = _profile.child4DnaId; _profile.nodeShortName = _profile.child4ShortName; - _profile.posNo = `${_profile.child4ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child4ShortName} ${posMaster?.posMasterNo}`; } else if (_profile.child3Id != null) { _profile.node = 3; _profile.nodeId = _profile.child3Id; _profile.nodeDnaId = _profile.child3DnaId; _profile.nodeShortName = _profile.child3ShortName; - _profile.posNo = `${_profile.child3ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child3ShortName} ${posMaster?.posMasterNo}`; } else if (_profile.child2Id != null) { _profile.node = 2; _profile.nodeId = _profile.child2Id; _profile.nodeDnaId = _profile.child2DnaId; _profile.nodeShortName = _profile.child2ShortName; - _profile.posNo = `${_profile.child2ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child2ShortName} ${posMaster?.posMasterNo}`; } else if (_profile.child1Id != null) { _profile.node = 1; _profile.nodeId = _profile.child1Id; _profile.nodeDnaId = _profile.child1DnaId; _profile.nodeShortName = _profile.child1ShortName; - _profile.posNo = `${_profile.child1ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child1ShortName} ${posMaster?.posMasterNo}`; } else if (_profile.rootId != null) { _profile.node = 0; _profile.nodeId = _profile.rootId; _profile.nodeDnaId = _profile.rootDnaId; _profile.nodeShortName = _profile.rootShortName; - _profile.posNo = `${_profile.rootShortName} ${_numPart}`; + _profile.posNo = `${_profile.rootShortName} ${posMaster?.posMasterNo}`; } return new HttpSuccess(_profile); } @@ -8726,10 +8726,14 @@ export class ProfileController extends Controller { "current_holders.orgChild2", "current_holders.orgChild3", "current_holders.orgChild4", + // "profileSalary", "profileEducations", "profileActpositions", ], order: { + // profileSalary: { + // order: "DESC", + // }, profileEducations: { level: "ASC", }, @@ -8792,8 +8796,85 @@ export class ProfileController extends Controller { posMasterId: posMaster?.id, }, }); - const holder = profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id); - const numPart = holder ? [holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; + const shortName = + profile.current_holders.length == 0 + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgChild4 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4.orgChild4ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgChild3 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3.orgChild3ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != + null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgChild2 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2.orgChild2ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != + null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgChild1 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1.orgChild1ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != + null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgRoot != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot.orgRootShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : null; + // const posMasterActs = await this.posMasterActRepository.find({ + // relations: [ + // "posMaster", + // "posMaster.orgRoot", + // "posMaster.orgChild1", + // "posMaster.orgChild2", + // "posMaster.orgChild3", + // "posMaster.orgChild4", + // "posMaster.current_holder", + // "posMaster.current_holder.posLevel", + // "posMaster.current_holder.posType", + // ], + // where: { + // posMaster: { + // orgRevisionId: orgRevisionPublish.id, + // }, + // posMasterChild: { + // current_holderId: profile.id, + // }, + // }, + // }); + // const data = await Promise.all( + // posMasterActs + // .sort((a, b) => a.posMaster.posMasterOrder - b.posMaster.posMasterOrder) + // .map((item) => { + // const shortName = + // item.posMaster != null && item.posMaster.orgChild4 != null + // ? `${item.posMaster.orgChild4.orgChild4ShortName} ${item.posMaster.posMasterNo}` + // : item.posMaster != null && item.posMaster?.orgChild3 != null + // ? `${item.posMaster.orgChild3.orgChild3ShortName} ${item.posMaster.posMasterNo}` + // : item.posMaster != null && item.posMaster?.orgChild2 != null + // ? `${item.posMaster.orgChild2.orgChild2ShortName} ${item.posMaster.posMasterNo}` + // : item.posMaster != null && item.posMaster?.orgChild1 != null + // ? `${item.posMaster.orgChild1.orgChild1ShortName} ${item.posMaster.posMasterNo}` + // : item.posMaster != null && item.posMaster?.orgRoot != null + // ? `${item.posMaster.orgRoot.orgRootShortName} ${item.posMaster.posMasterNo}` + // : null; + // return { + // id: item.id, + // posMasterOrder: item.posMasterOrder, + // profileId: item.posMaster?.current_holder?.id ?? null, + // citizenId: item.posMaster?.current_holder?.citizenId ?? null, + // prefix: item.posMaster?.current_holder?.prefix ?? null, + // firstName: item.posMaster?.current_holder?.firstName ?? null, + // lastName: item.posMaster?.current_holder?.lastName ?? null, + // posLevel: item.posMaster?.current_holder?.posLevel?.posLevelName ?? null, + // posType: item.posMaster?.current_holder?.posType?.posTypeName ?? null, + // position: item.posMaster?.current_holder?.position ?? null, + // posNo: shortName, + // }; + // }), + // ); const data = await Promise.all( profile.profileActpositions .filter((x) => x.status) @@ -8840,96 +8921,6 @@ export class ProfileController extends Controller { }, }); - let _Org = null; - let _PosNo = null; - if (!profile.org && !profile.posMasterNo) { - if (profile.isLeave) { - const profileWithSalary = await this.profileRepo.findOne({ - where: { - id: id, - profileSalary: { - commandCode: In([ - "0", - "9", - "1", - "2", - "3", - "4", - "8", - "10", - "11", - "12", - "13", - "14", - "15", - "16", - "20", - ]), - }, - }, - relations: { profileSalary: true }, - order: { - profileSalary: { - order: "DESC", - createdAt: "DESC", - }, - }, - }); - const profileSalaryList = profileWithSalary?.profileSalary || []; - - if (profileSalaryList.length > 0) { - const _profileSalary = - profile.leaveType == "RETIRE" - ? profileSalaryList.length > 1 - ? profileSalaryList[1] - : profileSalaryList[0] - : profileSalaryList[0]; - - if (_profileSalary) { - const orgLeaveParts = [ - _profileSalary.orgChild4 ?? null, - _profileSalary.orgChild3 ?? null, - _profileSalary.orgChild2 ?? null, - _profileSalary.orgChild1 ?? null, - _profileSalary.orgRoot ?? null, - ]; - _Org = orgLeaveParts - .filter((x: any) => x !== undefined && x !== null) - .join("\n"); - _PosNo = `${_profileSalary.posNoAbb} ${_profileSalary.posNo}`; - } - } - } else { - _Org = [ - child4?.orgChild4Name, - child3?.orgChild3Name, - child2?.orgChild2Name, - child1?.orgChild1Name, - root?.orgRootName, - ] - .filter((x) => x != null && x !== "") - .join("\n"); - - _PosNo = - holder == null - ? null - : holder.orgChild4 != null - ? `${holder.orgChild4.orgChild4ShortName} ${numPart}` - : holder.orgChild3 != null - ? `${holder.orgChild3.orgChild3ShortName} ${numPart}` - : holder.orgChild2 != null - ? `${holder.orgChild2.orgChild2ShortName} ${numPart}` - : holder.orgChild1 != null - ? `${holder.orgChild1.orgChild1ShortName} ${numPart}` - : holder.orgRoot != null - ? `${holder.orgRoot.orgRootShortName} ${numPart}` - : null; - } - } else { - _Org = profile.org; - _PosNo = profile.posMasterNo; - } - const _profile: any = { profileId: profile.id, prefix: profile.prefix, @@ -8981,8 +8972,7 @@ export class ProfileController extends Controller { node: null, nodeId: null, nodeDnaId: null, - posNo: _PosNo, - org: _Org, + posNo: shortName, isPosmasterAct: data.length > 0, posmasterAct: data, salary: profile ? profile.amount : null, @@ -9193,32 +9183,26 @@ export class ProfileController extends Controller { profile.avatar && profile.avatarName ? `${profile.avatar}/${profile.avatarName}` : null, }; - const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; if (_profile.child4Id != null) { _profile.node = 4; _profile.nodeId = _profile.child4Id; _profile.nodeShortName = _profile.child4ShortName; - _profile.posNo = `${_profile.child4ShortName} ${_numPart}`; } else if (_profile.child3Id != null) { _profile.node = 3; _profile.nodeId = _profile.child3Id; _profile.nodeShortName = _profile.child3ShortName; - _profile.posNo = `${_profile.child3ShortName} ${_numPart}`; } else if (_profile.child2Id != null) { _profile.node = 2; _profile.nodeId = _profile.child2Id; _profile.nodeShortName = _profile.child2ShortName; - _profile.posNo = `${_profile.child2ShortName} ${_numPart}`; } else if (_profile.child1Id != null) { _profile.node = 1; _profile.nodeId = _profile.child1Id; _profile.nodeShortName = _profile.child1ShortName; - _profile.posNo = `${_profile.child1ShortName} ${_numPart}`; } else if (_profile.rootId != null) { _profile.node = 0; _profile.nodeId = _profile.rootId; _profile.nodeShortName = _profile.rootShortName; - _profile.posNo = `${_profile.rootShortName} ${_numPart}`; } return new HttpSuccess(_profile); } @@ -9348,32 +9332,26 @@ export class ProfileController extends Controller { : "-", }; - const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; if (_profile.child4Id != null) { _profile.node = 4; _profile.nodeId = _profile.child4Id; _profile.nodeShortName = _profile.child4ShortName; - _profile.posNo = `${_profile.child4ShortName} ${_numPart}`; } else if (_profile.child3Id != null) { _profile.node = 3; _profile.nodeId = _profile.child3Id; _profile.nodeShortName = _profile.child3ShortName; - _profile.posNo = `${_profile.child3ShortName} ${_numPart}`; } else if (_profile.child2Id != null) { _profile.node = 2; _profile.nodeId = _profile.child2Id; _profile.nodeShortName = _profile.child2ShortName; - _profile.posNo = `${_profile.child2ShortName} ${_numPart}`; } else if (_profile.child1Id != null) { _profile.node = 1; _profile.nodeId = _profile.child1Id; _profile.nodeShortName = _profile.child1ShortName; - _profile.posNo = `${_profile.child1ShortName} ${_numPart}`; } else if (_profile.rootId != null) { _profile.node = 0; _profile.nodeId = _profile.rootId; _profile.nodeShortName = _profile.rootShortName; - _profile.posNo = `${_profile.rootShortName} ${_numPart}`; } return new HttpSuccess(_profile); } @@ -9554,28 +9532,38 @@ export class ProfileController extends Controller { const mapDataProfile = await Promise.all( findProfile.map(async (item: Profile) => { const fullName = `${item.prefix}${item.firstName} ${item.lastName}`; - const holder = item.current_holders?.find((x) => x.orgRevisionId == findRevision.id); - const _numPart = holder ? [holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; - const shortName = !holder - ? null - : holder.orgChild4 != null - ? `${holder.orgChild4.orgChild4ShortName} ${_numPart}` - : holder.orgChild3 != null - ? `${holder.orgChild3.orgChild3ShortName} ${_numPart}` - : holder.orgChild2 != null - ? `${holder.orgChild2.orgChild2ShortName} ${_numPart}` - : holder.orgChild1 != null - ? `${holder.orgChild1.orgChild1ShortName} ${_numPart}` - : holder.orgRoot != null - ? `${holder.orgRoot.orgRootShortName} ${_numPart}` - : null; + const shortName = + item.current_holders.length == 0 + ? null + : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != + null + ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` + : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3 != + null + ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` + : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild2 != null + ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` + : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild1 != null + ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` + : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != + null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgRoot != null + ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` + : null; const root = item.current_holders.length == 0 || - (holder != null && - holder?.orgRoot == null) + (item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) ? null - : holder?.orgRoot; + : item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot; const rootHolder = item.current_holders?.find( (x) => x.orgRevisionId == findRevision.id, diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 21eb9a5d..c0b36961 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -84,7 +84,6 @@ import { ProfileDuty } from "../entities/ProfileDuty"; import { CreatePosMasterHistoryEmployee, getTopDegrees } from "../services/PositionService"; import { ProfileLeaveService } from "../services/ProfileLeaveService"; import { CommandCode } from "../entities/CommandCode"; -import { updateHolderProfileHistory } from "../services/PositionService"; @Route("api/v1/org/profile-employee") @Tags("ProfileEmployee") @Security("bearerAuth") @@ -197,7 +196,7 @@ export class ProfileEmployeeController extends Controller { }, }); ImgUrl = response_.data.downloadUrl; - } catch { } + } catch {} } const province = await this.provinceRepository.findOneBy({ id: profile.registrationProvinceId, @@ -209,36 +208,36 @@ export class ProfileEmployeeController extends Controller { const root = profile.current_holders == null || - profile.current_holders.length == 0 || - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profile.current_holders.length == 0 || + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgRoot; const child1 = profile.current_holders == null || - profile.current_holders.length == 0 || - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profile.current_holders.length == 0 || + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild1; const child2 = profile.current_holders == null || - profile.current_holders.length == 0 || - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profile.current_holders.length == 0 || + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild2; const child3 = profile.current_holders == null || - profile.current_holders.length == 0 || - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profile.current_holders.length == 0 || + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild3; const child4 = profile.current_holders == null || - profile.current_holders.length == 0 || - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profile.current_holders.length == 0 || + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild4; @@ -287,38 +286,38 @@ export class ProfileEmployeeController extends Controller { const salarys = salary_raw.length > 1 ? salary_raw.slice(1).map((item) => ({ - date: item.commandDateAffect - ? Extension.ToThaiNumber(Extension.ToThaiShortDate(item.commandDateAffect)) - : null, - position: Extension.ToThaiNumber( - Extension.ToThaiNumber( - `${item.positionName != null ? item.positionName : "-"} ${item.positionType == null ? item.positionCee ?? "" : (item.positionType == "อำนวยการ" || item.positionType == "บริหาร" ? item.positionType : "") + item.positionLevel}`, + date: item.commandDateAffect + ? Extension.ToThaiNumber(Extension.ToThaiShortDate(item.commandDateAffect)) + : null, + position: Extension.ToThaiNumber( + Extension.ToThaiNumber( + `${item.positionName != null ? item.positionName : "-"} ${item.positionType == null ? item.positionCee ?? "" : (item.positionType == "อำนวยการ" || item.positionType == "บริหาร" ? item.positionType : "") + item.positionLevel}`, + ), ), - ), - posNo: item.posNo != null ? Extension.ToThaiNumber(item.posNo) : "", - orgRoot: item.orgRoot != null ? Extension.ToThaiNumber(item.orgRoot) : "", - orgChild1: item.orgChild1 != null ? Extension.ToThaiNumber(item.orgChild1) : "", - orgChild2: item.orgChild2 != null ? Extension.ToThaiNumber(item.orgChild2) : "", - orgChild3: item.orgChild3 != null ? Extension.ToThaiNumber(item.orgChild3) : "", - orgChild4: item.orgChild4 != null ? Extension.ToThaiNumber(item.orgChild4) : "", - positionCee: item.positionCee != null ? Extension.ToThaiNumber(item.positionCee) : "", - positionExecutive: - item.positionExecutive != null ? Extension.ToThaiNumber(item.positionExecutive) : "", - })) + posNo: item.posNo != null ? Extension.ToThaiNumber(item.posNo) : "", + orgRoot: item.orgRoot != null ? Extension.ToThaiNumber(item.orgRoot) : "", + orgChild1: item.orgChild1 != null ? Extension.ToThaiNumber(item.orgChild1) : "", + orgChild2: item.orgChild2 != null ? Extension.ToThaiNumber(item.orgChild2) : "", + orgChild3: item.orgChild3 != null ? Extension.ToThaiNumber(item.orgChild3) : "", + orgChild4: item.orgChild4 != null ? Extension.ToThaiNumber(item.orgChild4) : "", + positionCee: item.positionCee != null ? Extension.ToThaiNumber(item.positionCee) : "", + positionExecutive: + item.positionExecutive != null ? Extension.ToThaiNumber(item.positionExecutive) : "", + })) : [ - { - date: "-", - position: "-", - posNo: "-", - orgRoot: null, - orgChild1: null, - orgChild2: null, - orgChild3: null, - orgChild4: null, - positionCee: null, - positionExecutive: null, - }, - ]; + { + date: "-", + position: "-", + posNo: "-", + orgRoot: null, + orgChild1: null, + orgChild2: null, + orgChild3: null, + orgChild4: null, + positionCee: null, + positionExecutive: null, + }, + ]; const educations = await this.profileEducationRepo.find({ select: [ @@ -336,20 +335,20 @@ export class ProfileEmployeeController extends Controller { const Education = educations && educations.length > 0 ? educations.map((item) => ({ - institute: item.institute ? item.institute : "-", - date: - item.startDate && item.endDate - ? `${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate))} - ${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate))}` - : "-", - degree: item.degree && item.field ? `${item.degree} ${item.field}` : "-", - })) + institute: item.institute ? item.institute : "-", + date: + item.startDate && item.endDate + ? `${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate))} - ${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate))}` + : "-", + degree: item.degree && item.field ? `${item.degree} ${item.field}` : "-", + })) : [ - { - institute: "-", - date: "-", - degree: "-", - }, - ]; + { + institute: "-", + date: "-", + degree: "-", + }, + ]; const mapData = { // Id: profile.id, @@ -387,10 +386,10 @@ export class ProfileEmployeeController extends Controller { position: salary_raw.length > 0 && salary_raw[0].positionName != null ? Extension.ToThaiNumber( - Extension.ToThaiNumber( - `${salary_raw[0].positionName != null ? salary_raw[0].positionName : "-"} ${salary_raw[0].positionType == null ? salary_raw[0].positionCee ?? "" : (salary_raw[0].positionType == "อำนวยการ" || salary_raw[0].positionType == "บริหาร" ? salary_raw[0].positionType : "") + salary_raw[0].positionLevel}`, - ), - ) + Extension.ToThaiNumber( + `${salary_raw[0].positionName != null ? salary_raw[0].positionName : "-"} ${salary_raw[0].positionType == null ? salary_raw[0].positionCee ?? "" : (salary_raw[0].positionType == "อำนวยการ" || salary_raw[0].positionType == "บริหาร" ? salary_raw[0].positionType : "") + salary_raw[0].positionLevel}`, + ), + ) : "", positionCee: salary_raw.length > 0 && salary_raw[0].positionCee != null @@ -400,22 +399,27 @@ export class ProfileEmployeeController extends Controller { salary_raw.length > 0 && salary_raw[0].positionExecutive != null ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].positionExecutive)) : "", - org: `${salary_raw.length > 0 && salary_raw[0].orgChild4 && salary_raw[0].orgChild4 != "-" + org: `${ + salary_raw.length > 0 && salary_raw[0].orgChild4 && salary_raw[0].orgChild4 != "-" ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild4)) + " " : "" - }${salary_raw.length > 0 && salary_raw[0].orgChild3 && salary_raw[0].orgChild3 != "-" + }${ + salary_raw.length > 0 && salary_raw[0].orgChild3 && salary_raw[0].orgChild3 != "-" ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild3)) + " " : "" - }${salary_raw.length > 0 && salary_raw[0].orgChild2 && salary_raw[0].orgChild2 != "-" + }${ + salary_raw.length > 0 && salary_raw[0].orgChild2 && salary_raw[0].orgChild2 != "-" ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild2)) + " " : "" - }${salary_raw.length > 0 && salary_raw[0].orgChild1 && salary_raw[0].orgChild1 != "-" + }${ + salary_raw.length > 0 && salary_raw[0].orgChild1 && salary_raw[0].orgChild1 != "-" ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgChild1)) + " " : "" - }${salary_raw.length > 0 && salary_raw[0].orgRoot && salary_raw[0].orgRoot != "-" + }${ + salary_raw.length > 0 && salary_raw[0].orgRoot && salary_raw[0].orgRoot != "-" ? Extension.ToThaiNumber(Extension.ToThaiNumber(salary_raw[0].orgRoot)) : "" - }`, + }`, ocFullPath: (_child4 == null ? "" : _child4 + "\n") + (_child3 == null ? "" : _child3 + "\n") + @@ -484,7 +488,7 @@ export class ProfileEmployeeController extends Controller { }, }); _ImgUrl[i] = response_.data.downloadUrl; - } catch { } + } catch {} } }), ); @@ -498,7 +502,7 @@ export class ProfileEmployeeController extends Controller { }, }); ImgUrl = response_.data.downloadUrl; - } catch { } + } catch {} } const profileOc = await this.profileRepo.findOne({ relations: [ @@ -537,36 +541,36 @@ export class ProfileEmployeeController extends Controller { const root = profileOc.current_holders == null || - profileOc.current_holders.length == 0 || - profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profileOc.current_holders.length == 0 || + profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgRoot; const child1 = profileOc.current_holders == null || - profileOc.current_holders.length == 0 || - profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profileOc.current_holders.length == 0 || + profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild1; const child2 = profileOc.current_holders == null || - profileOc.current_holders.length == 0 || - profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profileOc.current_holders.length == 0 || + profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild2; const child3 = profileOc.current_holders == null || - profileOc.current_holders.length == 0 || - profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profileOc.current_holders.length == 0 || + profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild3; const child4 = profileOc.current_holders == null || - profileOc.current_holders.length == 0 || - profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profileOc.current_holders.length == 0 || + profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profileOc.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild4; @@ -585,19 +589,19 @@ export class ProfileEmployeeController extends Controller { const certs = cert_raw.length > 0 ? cert_raw.slice(-2).map((item) => ({ - CertificateType: item.certificateType ?? null, - Issuer: item.issuer ?? null, - CertificateNo: Extension.ToThaiNumber(item.certificateNo) ?? null, - IssueDate: Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.issueDate)) ?? null, - })) + CertificateType: item.certificateType ?? null, + Issuer: item.issuer ?? null, + CertificateNo: Extension.ToThaiNumber(item.certificateNo) ?? null, + IssueDate: Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.issueDate)) ?? null, + })) : [ - { - CertificateType: "-", - Issuer: "-", - CertificateNo: "-", - IssueDate: "-", - }, - ]; + { + CertificateType: "-", + Issuer: "-", + CertificateNo: "-", + IssueDate: "-", + }, + ]; const training_raw = await this.trainingRepository.find({ select: ["startDate", "endDate", "place", "department", "isDeleted"], where: { profileEmployeeId: id, isDeleted: false }, @@ -606,34 +610,34 @@ export class ProfileEmployeeController extends Controller { const trainings = training_raw.length > 0 ? training_raw.slice(-2).map((item) => ({ - Institute: item.department ?? "", - Start: - item.startDate == null - ? "" - : Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate)), - End: - item.endDate == null - ? "" - : Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate)), - Date: - item.startDate && item.endDate - ? `${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate))} - ${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate))}` - : "", - Level: "", - Degree: item.name, - Field: "", - })) + Institute: item.department ?? "", + Start: + item.startDate == null + ? "" + : Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate)), + End: + item.endDate == null + ? "" + : Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate)), + Date: + item.startDate && item.endDate + ? `${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate))} - ${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate))}` + : "", + Level: "", + Degree: item.name, + Field: "", + })) : [ - { - Institute: "-", - Start: "-", - End: "-", - Date: "-", - Level: "-", - Degree: "-", - Field: "-", - }, - ]; + { + Institute: "-", + Start: "-", + End: "-", + Date: "-", + Level: "-", + Degree: "-", + Field: "-", + }, + ]; const discipline_raw = await this.disciplineRepository.find({ select: ["refCommandDate", "refCommandNo", "detail", "isDeleted"], @@ -643,19 +647,19 @@ export class ProfileEmployeeController extends Controller { const disciplines = discipline_raw.length > 0 ? discipline_raw.slice(-2).map((item) => ({ - DisciplineYear: - Extension.ToThaiNumber(new Date(item.refCommandDate).getFullYear().toString()) ?? - null, - DisciplineDetail: item.detail ?? null, - RefNo: item.refCommandNo ? Extension.ToThaiNumber(item.refCommandNo) : null, - })) + DisciplineYear: + Extension.ToThaiNumber(new Date(item.refCommandDate).getFullYear().toString()) ?? + null, + DisciplineDetail: item.detail ?? null, + RefNo: item.refCommandNo ? Extension.ToThaiNumber(item.refCommandNo) : null, + })) : [ - { - DisciplineYear: "-", - DisciplineDetail: "-", - RefNo: "-", - }, - ]; + { + DisciplineYear: "-", + DisciplineDetail: "-", + RefNo: "-", + }, + ]; const education_raw = await this.profileEducationRepo.find({ select: [ @@ -674,34 +678,34 @@ export class ProfileEmployeeController extends Controller { const educations = education_raw.length > 0 ? education_raw.slice(-2).map((item) => ({ - Institute: item.institute, - Start: - item.startDate == null - ? "" - : Extension.ToThaiNumber(new Date(item.startDate).getFullYear().toString()), - End: - item.endDate == null - ? "" - : Extension.ToThaiNumber(new Date(item.endDate).getFullYear().toString()), - Date: - item.startDate && item.endDate - ? `${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate))} - ${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate))}` - : "", - Level: item.educationLevel ?? "", - Degree: item.degree ? `${item.degree} ${item.field ? item.field : ""}` : "", - Field: item.field ?? "-", - })) + Institute: item.institute, + Start: + item.startDate == null + ? "" + : Extension.ToThaiNumber(new Date(item.startDate).getFullYear().toString()), + End: + item.endDate == null + ? "" + : Extension.ToThaiNumber(new Date(item.endDate).getFullYear().toString()), + Date: + item.startDate && item.endDate + ? `${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate))} - ${Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate))}` + : "", + Level: item.educationLevel ?? "", + Degree: item.degree ? `${item.degree} ${item.field ? item.field : ""}` : "", + Field: item.field ?? "-", + })) : [ - { - Institute: "-", - Start: "-", - End: "-", - Date: "-", - Level: "-", - Degree: "-", - Field: "-", - }, - ]; + { + Institute: "-", + Start: "-", + End: "-", + Date: "-", + Level: "-", + Degree: "-", + Field: "-", + }, + ]; const salary_raw = await this.salaryRepo.find({ select: [ "commandDateAffect", @@ -721,45 +725,45 @@ export class ProfileEmployeeController extends Controller { const salarys = salary_raw.length > 0 ? salary_raw.map((item) => ({ - SalaryDate: item.commandDateAffect - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) - : null, - Position: item.positionName != null ? Extension.ToThaiNumber(item.positionName) : null, - PosNo: item.posNo != null ? Extension.ToThaiNumber(item.posNo) : null, - Salary: - item.amount != null ? Extension.ToThaiNumber(item.amount.toLocaleString()) : null, - Rank: item.positionLevel != null ? Extension.ToThaiNumber(item.positionLevel) : null, - RefAll: item.remark ? Extension.ToThaiNumber(item.remark) : null, - PositionLevel: - item.positionLevel != null ? Extension.ToThaiNumber(item.positionLevel) : null, - PositionType: item.positionType ?? null, - PositionAmount: - item.positionSalaryAmount == null - ? null - : Extension.ToThaiNumber(item.positionSalaryAmount.toLocaleString()), - FullName: `${profiles?.prefix}${profiles?.firstName} ${profiles?.lastName}`, - OcFullPath: - (_child4 == null ? "" : _child4 + "\n") + - (_child3 == null ? "" : _child3 + "\n") + - (_child2 == null ? "" : _child2 + "\n") + - (_child1 == null ? "" : _child1 + "\n") + - (_root == null ? "" : _root), - })) + SalaryDate: item.commandDateAffect + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) + : null, + Position: item.positionName != null ? Extension.ToThaiNumber(item.positionName) : null, + PosNo: item.posNo != null ? Extension.ToThaiNumber(item.posNo) : null, + Salary: + item.amount != null ? Extension.ToThaiNumber(item.amount.toLocaleString()) : null, + Rank: item.positionLevel != null ? Extension.ToThaiNumber(item.positionLevel) : null, + RefAll: item.remark ? Extension.ToThaiNumber(item.remark) : null, + PositionLevel: + item.positionLevel != null ? Extension.ToThaiNumber(item.positionLevel) : null, + PositionType: item.positionType ?? null, + PositionAmount: + item.positionSalaryAmount == null + ? null + : Extension.ToThaiNumber(item.positionSalaryAmount.toLocaleString()), + FullName: `${profiles?.prefix}${profiles?.firstName} ${profiles?.lastName}`, + OcFullPath: + (_child4 == null ? "" : _child4 + "\n") + + (_child3 == null ? "" : _child3 + "\n") + + (_child2 == null ? "" : _child2 + "\n") + + (_child1 == null ? "" : _child1 + "\n") + + (_root == null ? "" : _root), + })) : [ - { - SalaryDate: "-", - Position: "-", - PosNo: "-", - Salary: "-", - Rank: "-", - RefAll: "-", - PositionLevel: "-", - PositionType: "-", - PositionAmount: "-", - FullName: "-", - OcFullPath: "-", - }, - ]; + { + SalaryDate: "-", + Position: "-", + PosNo: "-", + Salary: "-", + Rank: "-", + RefAll: "-", + PositionLevel: "-", + PositionType: "-", + PositionAmount: "-", + FullName: "-", + OcFullPath: "-", + }, + ]; const insignia_raw = await this.profileInsigniaRepo.find({ relations: { @@ -773,37 +777,37 @@ export class ProfileEmployeeController extends Controller { const insignias = insignia_raw.length > 0 ? insignia_raw.map((item) => ({ - ReceiveDate: item.receiveDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.receiveDate)) - : "", - InsigniaName: item.insignia.name, - InsigniaShortName: item.insignia.shortName, - InsigniaTypeName: item.insignia.insigniaType.name, - No: item.no ? Extension.ToThaiNumber(item.no) : "", - Issue: item.issue ? item.issue : "", - VolumeNo: item.volumeNo ? Extension.ToThaiNumber(item.volumeNo) : "", - Volume: item.volume ? Extension.ToThaiNumber(item.volume) : "", - Section: item.section ? Extension.ToThaiNumber(item.section) : "", - Page: item.page ? Extension.ToThaiNumber(item.page) : "", - RefCommandDate: item.refCommandDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.refCommandDate)) - : "", - })) + ReceiveDate: item.receiveDate + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.receiveDate)) + : "", + InsigniaName: item.insignia.name, + InsigniaShortName: item.insignia.shortName, + InsigniaTypeName: item.insignia.insigniaType.name, + No: item.no ? Extension.ToThaiNumber(item.no) : "", + Issue: item.issue ? item.issue : "", + VolumeNo: item.volumeNo ? Extension.ToThaiNumber(item.volumeNo) : "", + Volume: item.volume ? Extension.ToThaiNumber(item.volume) : "", + Section: item.section ? Extension.ToThaiNumber(item.section) : "", + Page: item.page ? Extension.ToThaiNumber(item.page) : "", + RefCommandDate: item.refCommandDate + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.refCommandDate)) + : "", + })) : [ - { - ReceiveDate: "-", - InsigniaName: "-", - InsigniaShortName: "-", - InsigniaTypeName: "-", - No: "-", - Issue: "-", - VolumeNo: "-", - Volume: "-", - Section: "-", - Page: "-", - RefCommandDate: "-", - }, - ]; + { + ReceiveDate: "-", + InsigniaName: "-", + InsigniaShortName: "-", + InsigniaTypeName: "-", + No: "-", + Issue: "-", + VolumeNo: "-", + Volume: "-", + Section: "-", + Page: "-", + RefCommandDate: "-", + }, + ]; const leave_raw = await this.profileLeaveRepository .createQueryBuilder("profileLeave") @@ -893,20 +897,20 @@ export class ProfileEmployeeController extends Controller { profiles.citizenId != null ? Extension.ToThaiNumber(profiles.citizenId.toString()) : "", fatherFullName: profileFamilyFather?.fatherPrefix || - profileFamilyFather?.fatherFirstName || - profileFamilyFather?.fatherLastName + profileFamilyFather?.fatherFirstName || + profileFamilyFather?.fatherLastName ? `${profileFamilyFather?.fatherPrefix ?? ""}${profileFamilyFather?.fatherFirstName ?? ""} ${profileFamilyFather?.fatherLastName ?? ""}`.trim() : null, motherFullName: profileFamilyMother?.motherPrefix || - profileFamilyMother?.motherFirstName || - profileFamilyMother?.motherLastName + profileFamilyMother?.motherFirstName || + profileFamilyMother?.motherLastName ? `${profileFamilyMother?.motherPrefix ?? ""}${profileFamilyMother?.motherFirstName ?? ""} ${profileFamilyMother?.motherLastName ?? ""}`.trim() : null, coupleFullName: profileFamilyCouple?.couplePrefix || - profileFamilyCouple?.coupleFirstName || - profileFamilyCouple?.coupleLastNameOld + profileFamilyCouple?.coupleFirstName || + profileFamilyCouple?.coupleLastNameOld ? `${profileFamilyCouple?.couplePrefix ?? ""}${profileFamilyCouple?.coupleFirstName ?? ""} ${profileFamilyCouple?.coupleLastName ?? ""}`.trim() : null, coupleLastNameOld: profileFamilyCouple?.coupleLastNameOld ?? null, @@ -1024,7 +1028,7 @@ export class ProfileEmployeeController extends Controller { }, }); _ImgUrl[i] = response_.data.downloadUrl; - } catch { } + } catch {} } }), ); @@ -1038,7 +1042,7 @@ export class ProfileEmployeeController extends Controller { }, }); ImgUrl = response_.data.downloadUrl; - } catch { } + } catch {} } const orgRevision = await this.orgRevisionRepo.findOne({ @@ -1072,36 +1076,36 @@ export class ProfileEmployeeController extends Controller { const root = profiles.current_holders == null || - profiles.current_holders.length == 0 || - profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profiles.current_holders.length == 0 || + profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgRoot; const child1 = profiles.current_holders == null || - profiles.current_holders.length == 0 || - profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profiles.current_holders.length == 0 || + profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild1; const child2 = profiles.current_holders == null || - profiles.current_holders.length == 0 || - profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profiles.current_holders.length == 0 || + profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild2; const child3 = profiles.current_holders == null || - profiles.current_holders.length == 0 || - profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profiles.current_holders.length == 0 || + profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild3; const child4 = profiles.current_holders == null || - profiles.current_holders.length == 0 || - profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null + profiles.current_holders.length == 0 || + profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) == null ? null : profiles.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild4; @@ -1127,31 +1131,31 @@ export class ProfileEmployeeController extends Controller { const certs = cert_raw.length > 0 ? cert_raw.map((item) => ({ - certificateType: item.certificateType ?? null, - issuer: item.issuer ?? null, - certificateNo: item.certificateNo ? Extension.ToThaiNumber(item.certificateNo) : null, - detail: Extension.ToThaiNumber( - `${item.issuer ?? ""} ${item.certificateNo ?? ""}`.trim(), - ), - issueToExpireDate: item.issueDate - ? item.expireDate - ? Extension.ToThaiNumber( - `${Extension.ToThaiFullDate2(item.issueDate)} - ${Extension.ToThaiFullDate2(item.expireDate)}`, - ) - : Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.issueDate)) - : item.expireDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.expireDate)) - : "", - })) + certificateType: item.certificateType ?? null, + issuer: item.issuer ?? null, + certificateNo: item.certificateNo ? Extension.ToThaiNumber(item.certificateNo) : null, + detail: Extension.ToThaiNumber( + `${item.issuer ?? ""} ${item.certificateNo ?? ""}`.trim(), + ), + issueToExpireDate: item.issueDate + ? item.expireDate + ? Extension.ToThaiNumber( + `${Extension.ToThaiFullDate2(item.issueDate)} - ${Extension.ToThaiFullDate2(item.expireDate)}`, + ) + : Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.issueDate)) + : item.expireDate + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.expireDate)) + : "", + })) : [ - { - certificateType: "", - issuer: "", - certificateNo: "", - detail: "", - issueToExpireDate: "", - }, - ]; + { + certificateType: "", + issuer: "", + certificateNo: "", + detail: "", + issueToExpireDate: "", + }, + ]; const training_raw = await this.trainingRepository.find({ select: ["place", "department", "name", "duration", "isDeleted", "startDate", "endDate"], where: { profileEmployeeId: id, isDeleted: false }, @@ -1160,23 +1164,23 @@ export class ProfileEmployeeController extends Controller { const trainings = training_raw.length > 0 ? training_raw.map((item) => ({ - institute: item.department ?? "", - degree: item.name ? Extension.ToThaiNumber(item.name) : "", - place: item.place ? Extension.ToThaiNumber(item.place) : "", - duration: item.duration ? Extension.ToThaiNumber(item.duration) : "", - date: Extension.ToThaiNumber( - `${Extension.ToThaiFullDate2(item.startDate)} - ${Extension.ToThaiFullDate2(item.endDate)}`, - ), - })) + institute: item.department ?? "", + degree: item.name ? Extension.ToThaiNumber(item.name) : "", + place: item.place ? Extension.ToThaiNumber(item.place) : "", + duration: item.duration ? Extension.ToThaiNumber(item.duration) : "", + date: Extension.ToThaiNumber( + `${Extension.ToThaiFullDate2(item.startDate)} - ${Extension.ToThaiFullDate2(item.endDate)}`, + ), + })) : [ - { - institute: "", - degree: "", - place: "", - duration: "", - date: "", - }, - ]; + { + institute: "", + degree: "", + place: "", + duration: "", + date: "", + }, + ]; const discipline_raw = await this.disciplineRepository.find({ select: ["refCommandDate", "refCommandNo", "detail", "level", "isDeleted"], @@ -1186,21 +1190,21 @@ export class ProfileEmployeeController extends Controller { const disciplines = discipline_raw.length > 0 ? discipline_raw.map((item) => ({ - disciplineYear: item.refCommandDate - ? Extension.ToThaiNumber(Extension.ToThaiShortYear(new Date(item.refCommandDate))) - : null, - disciplineDetail: item.detail ?? null, - refNo: Extension.ToThaiNumber(item.refCommandNo) ?? null, - level: item.level ?? "", - })) + disciplineYear: item.refCommandDate + ? Extension.ToThaiNumber(Extension.ToThaiShortYear(new Date(item.refCommandDate))) + : null, + disciplineDetail: item.detail ?? null, + refNo: Extension.ToThaiNumber(item.refCommandNo) ?? null, + level: item.level ?? "", + })) : [ - { - disciplineYear: "", - disciplineDetail: "", - refNo: "", - level: "", - }, - ]; + { + disciplineYear: "", + disciplineDetail: "", + refNo: "", + level: "", + }, + ]; const education_raw = await this.profileEducationRepo .createQueryBuilder("education") @@ -1212,21 +1216,21 @@ export class ProfileEmployeeController extends Controller { const educations = education_raw.length > 0 ? education_raw.map((item) => ({ - institute: item.institute, - date: item.isDate - ? `${item.startDate ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate)) : ""} - ${item.endDate ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate)) : ""}` - : `${item.startDate ? Extension.ToThaiNumber(Extension.ToThaiShortYear(new Date(item.startDate))) : ""} - ${item.endDate ? Extension.ToThaiNumber(Extension.ToThaiShortYear(new Date(item.endDate))) : ""}`, - degree: `${item.degree ?? ""} ${item.field ?? ""}`.trim(), - level: item.educationLevel, - })) + institute: item.institute, + date: item.isDate + ? `${item.startDate ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.startDate)) : ""} - ${item.endDate ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.endDate)) : ""}` + : `${item.startDate ? Extension.ToThaiNumber(Extension.ToThaiShortYear(new Date(item.startDate))) : ""} - ${item.endDate ? Extension.ToThaiNumber(Extension.ToThaiShortYear(new Date(item.endDate))) : ""}`, + degree: `${item.degree ?? ""} ${item.field ?? ""}`.trim(), + level: item.educationLevel, + })) : [ - { - institute: "", - date: "", - degree: "", - level: "", - }, - ]; + { + institute: "", + date: "", + degree: "", + level: "", + }, + ]; const salary_raw = await this.salaryRepo.find({ select: [ "commandName", @@ -1254,58 +1258,58 @@ export class ProfileEmployeeController extends Controller { const salarys = salary_raw.length > 0 ? salary_raw.map((item) => ({ - commandName: item.commandName ?? "", - salaryDate: item.commandDateAffect - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) - : null, - position: item.positionName != null ? Extension.ToThaiNumber(item.positionName) : null, - posNo: - item.posNoAbb && item.posNo - ? Extension.ToThaiNumber(`${item.posNoAbb}${item.posNo}`) + commandName: item.commandName ?? "", + salaryDate: item.commandDateAffect + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) : null, - salary: - item.amount != null ? Extension.ToThaiNumber(item.amount.toLocaleString()) : null, - special: - item.amountSpecial != null - ? Extension.ToThaiNumber(item.amountSpecial.toLocaleString()) - : null, - rank: item.positionLevel != null ? Extension.ToThaiNumber(item.positionLevel) : null, - refAll: item.remark ? Extension.ToThaiNumber(item.remark) : null, - positionLevel: item.positionLevel - ? Extension.ToThaiNumber(item.positionLevel) - : item.positionCee - ? Extension.ToThaiNumber(item.positionCee) - : null, - positionType: item.positionType ?? null, - positionAmount: - item.positionSalaryAmount == null - ? null - : Extension.ToThaiNumber(item.positionSalaryAmount.toLocaleString()), - fullName: `${profiles?.prefix}${profiles?.firstName} ${profiles?.lastName}`, - ocFullPath: - (_child4 == null ? "" : _child4 + "\n") + - (_child3 == null ? "" : _child3 + "\n") + - (_child2 == null ? "" : _child2 + "\n") + - (_child1 == null ? "" : _child1 + "\n") + - (_root == null ? "" : _root), - })) + position: item.positionName != null ? Extension.ToThaiNumber(item.positionName) : null, + posNo: + item.posNoAbb && item.posNo + ? Extension.ToThaiNumber(`${item.posNoAbb}${item.posNo}`) + : null, + salary: + item.amount != null ? Extension.ToThaiNumber(item.amount.toLocaleString()) : null, + special: + item.amountSpecial != null + ? Extension.ToThaiNumber(item.amountSpecial.toLocaleString()) + : null, + rank: item.positionLevel != null ? Extension.ToThaiNumber(item.positionLevel) : null, + refAll: item.remark ? Extension.ToThaiNumber(item.remark) : null, + positionLevel: item.positionLevel + ? Extension.ToThaiNumber(item.positionLevel) + : item.positionCee + ? Extension.ToThaiNumber(item.positionCee) + : null, + positionType: item.positionType ?? null, + positionAmount: + item.positionSalaryAmount == null + ? null + : Extension.ToThaiNumber(item.positionSalaryAmount.toLocaleString()), + fullName: `${profiles?.prefix}${profiles?.firstName} ${profiles?.lastName}`, + ocFullPath: + (_child4 == null ? "" : _child4 + "\n") + + (_child3 == null ? "" : _child3 + "\n") + + (_child2 == null ? "" : _child2 + "\n") + + (_child1 == null ? "" : _child1 + "\n") + + (_root == null ? "" : _root), + })) : [ - { - commandName: "", - salaryDate: "", - position: "", - posNo: "", - salary: "", - special: "", - rank: "", - refAll: "", - positionLevel: "", - positionType: "", - positionAmount: "", - fullName: "", - ocFullPath: "", - }, - ]; + { + commandName: "", + salaryDate: "", + position: "", + posNo: "", + salary: "", + special: "", + rank: "", + refAll: "", + positionLevel: "", + positionType: "", + positionAmount: "", + fullName: "", + ocFullPath: "", + }, + ]; const insignia_raw = await this.profileInsigniaRepo.find({ select: [ @@ -1331,38 +1335,38 @@ export class ProfileEmployeeController extends Controller { const insignias = insignia_raw.length > 0 ? insignia_raw.map((item) => ({ - receiveDate: item.receiveDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.receiveDate)) - : "", - insigniaName: item.insignia?.name ?? "", - insigniaShortName: item.insignia?.shortName ?? "", - insigniaTypeName: item.insignia?.insigniaType?.name ?? "", - no: item.no ? Extension.ToThaiNumber(item.no) : "", - issue: item.issue ? Extension.ToThaiNumber(item.issue) : "", - volumeNo: item.volumeNo ? Extension.ToThaiNumber(item.volumeNo) : "", - volume: item.volume ? Extension.ToThaiNumber(item.volume) : "", - section: item.section ? Extension.ToThaiNumber(item.section) : "", - page: item.page ? Extension.ToThaiNumber(item.page) : "", - refCommandDate: item.refCommandDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.refCommandDate)) - : "", - note: item.note ? Extension.ToThaiNumber(item.note) : "", - })) + receiveDate: item.receiveDate + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.receiveDate)) + : "", + insigniaName: item.insignia?.name ?? "", + insigniaShortName: item.insignia?.shortName ?? "", + insigniaTypeName: item.insignia?.insigniaType?.name ?? "", + no: item.no ? Extension.ToThaiNumber(item.no) : "", + issue: item.issue ? Extension.ToThaiNumber(item.issue) : "", + volumeNo: item.volumeNo ? Extension.ToThaiNumber(item.volumeNo) : "", + volume: item.volume ? Extension.ToThaiNumber(item.volume) : "", + section: item.section ? Extension.ToThaiNumber(item.section) : "", + page: item.page ? Extension.ToThaiNumber(item.page) : "", + refCommandDate: item.refCommandDate + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.refCommandDate)) + : "", + note: item.note ? Extension.ToThaiNumber(item.note) : "", + })) : [ - { - receiveDate: "", - insigniaName: "", - insigniaShortName: "", - insigniaTypeName: "", - no: "", - issue: "", - volumeNo: "", - volume: "", - section: "", - page: "", - refCommandDate: "", - }, - ]; + { + receiveDate: "", + insigniaName: "", + insigniaShortName: "", + insigniaTypeName: "", + no: "", + issue: "", + volumeNo: "", + volume: "", + section: "", + page: "", + refCommandDate: "", + }, + ]; const leave_raw = await this.profileLeaveRepository .createQueryBuilder("profileLeave") @@ -1500,62 +1504,62 @@ export class ProfileEmployeeController extends Controller { const leaves2 = leave2_raw.length > 0 ? leave2_raw.map((item) => { - const leaveTypeCode = item.code ? item.code.trim().toUpperCase() : ""; + const leaveTypeCode = item.code ? item.code.trim().toUpperCase() : ""; - // ข้อที่ 1: LV-008 ให้ใช้ leaveSubTypeName (ประเภทย่อย) แทน name - const displayType = - leaveTypeCode === "LV-008" && item.leaveSubTypeName - ? item.leaveSubTypeName - : item.name || "-"; + // ข้อที่ 1: LV-008 ให้ใช้ leaveSubTypeName (ประเภทย่อย) แทน name + const displayType = + leaveTypeCode === "LV-008" && item.leaveSubTypeName + ? item.leaveSubTypeName + : item.name || "-"; - // ข้อที่ 2: แสดง reason ก่อนเสมอ ถ้ามี coupleDayLevelCountry ค่อยแสดงประเทศ - const displayReason = item.coupleDayLevelCountry - ? `${item.reason || ""} ลาไปประเทศ ${item.coupleDayLevelCountry}`.trim() - : item.reason || "-"; + // ข้อที่ 2: แสดง reason ก่อนเสมอ ถ้ามี coupleDayLevelCountry ค่อยแสดงประเทศ + const displayReason = item.coupleDayLevelCountry + ? `${item.reason || ""} ลาไปประเทศ ${item.coupleDayLevelCountry}`.trim() + : item.reason || "-"; - return { - date: - item.dateLeaveStart && item.dateLeaveEnd - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.dateLeaveStart)) + - " - " + - Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.dateLeaveEnd)) - : "-", - type: displayType, - leaveDays: item.leaveDays ? Extension.ToThaiNumber(item.leaveDays.toString()) : "-", - reason: displayReason, - }; - }) + return { + date: + item.dateLeaveStart && item.dateLeaveEnd + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.dateLeaveStart)) + + " - " + + Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.dateLeaveEnd)) + : "-", + type: displayType, + leaveDays: item.leaveDays ? Extension.ToThaiNumber(item.leaveDays.toString()) : "-", + reason: displayReason, + }; + }) : [ - { - date: "", - type: "", - leaveDays: "", - reason: "", - }, - ]; + { + date: "", + type: "", + leaveDays: "", + reason: "", + }, + ]; const children_raw = await this.profileChildrenRepository.find({ where: { profileEmployeeId: id, isDeleted: false }, }); const children = children_raw.length > 0 ? children_raw.map((item, index) => ({ - no: Extension.ToThaiNumber((index + 1).toString()), - childrenPrefix: item.childrenPrefix, - childrenFirstName: item.childrenFirstName, - childrenLastName: item.childrenLastName, - childrenFullName: `${item.childrenPrefix}${item.childrenFirstName} ${item.childrenLastName}`, - childrenLive: item.childrenLive == false ? "ถึงแก่กรรม" : "มีชีวิต", - })) + no: Extension.ToThaiNumber((index + 1).toString()), + childrenPrefix: item.childrenPrefix, + childrenFirstName: item.childrenFirstName, + childrenLastName: item.childrenLastName, + childrenFullName: `${item.childrenPrefix}${item.childrenFirstName} ${item.childrenLastName}`, + childrenLive: item.childrenLive == false ? "ถึงแก่กรรม" : "มีชีวิต", + })) : [ - { - no: "", - childrenPrefix: "", - childrenFirstName: "", - childrenLastName: "", - childrenFullName: "", - childrenLive: "", - }, - ]; + { + no: "", + childrenPrefix: "", + childrenFirstName: "", + childrenLastName: "", + childrenFullName: "", + childrenLive: "", + }, + ]; const changeName_raw = await this.changeNameRepository.find({ where: { profileEmployeeId: id, isDeleted: false }, order: { createdAt: "ASC" }, @@ -1563,23 +1567,23 @@ export class ProfileEmployeeController extends Controller { const changeName = changeName_raw.length > 0 ? changeName_raw.map((item) => ({ - createdAt: item.createdAt - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.createdAt)) - : null, - status: item.status, - prefix: item.prefix, - firstName: item.firstName, - lastName: item.lastName, - })) + createdAt: item.createdAt + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.createdAt)) + : null, + status: item.status, + prefix: item.prefix, + firstName: item.firstName, + lastName: item.lastName, + })) : [ - { - createdAt: "", - status: "", - prefix: "", - firstName: "", - lastName: "", - }, - ]; + { + createdAt: "", + status: "", + prefix: "", + firstName: "", + lastName: "", + }, + ]; const profileHistory = await this.profileHistoryRepo.find({ where: { profileEmployeeId: id }, @@ -1588,19 +1592,19 @@ export class ProfileEmployeeController extends Controller { const history = profileHistory.length > 0 ? profileHistory.map((item) => ({ - birthDateOld: item.birthDateOld - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.birthDateOld)) - : "", - birthDate: item.birthDate - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.birthDate)) - : "", - })) + birthDateOld: item.birthDateOld + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.birthDateOld)) + : "", + birthDate: item.birthDate + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.birthDate)) + : "", + })) : [ - { - birthDateOld: "", - birthDate: "", - }, - ]; + { + birthDateOld: "", + birthDate: "", + }, + ]; const position_raw = await this.salaryRepo.find({ where: [ @@ -1634,76 +1638,76 @@ export class ProfileEmployeeController extends Controller { const positionList = position_raw.length > 0 ? await Promise.all( - position_raw.map(async (item, idx, arr) => { - const isLast = idx === arr.length - 1; - if (isLast) { - _commandDateAffect = item.commandDateAffect - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) - : ""; - } - const _code = item.commandCode ? Number(item.commandCode) : null; - if (_code != null) { - _commandName = await this.commandCodeRepository.findOne({ - select: { name: true, code: true }, - where: { code: _code }, - }); - } - const codeSitAbb = item.posNumCodeSitAbb ?? "-"; - const commandNo = - item.commandNo && item.commandYear - ? item.commandNo + - "/" + - (item.commandYear > 2500 ? item.commandYear : item.commandYear + 543) + position_raw.map(async (item, idx, arr) => { + const isLast = idx === arr.length - 1; + if (isLast) { + _commandDateAffect = item.commandDateAffect + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) + : ""; + } + const _code = item.commandCode ? Number(item.commandCode) : null; + if (_code != null) { + _commandName = await this.commandCodeRepository.findOne({ + select: { name: true, code: true }, + where: { code: _code }, + }); + } + const codeSitAbb = item.posNumCodeSitAbb ?? "-"; + const commandNo = + item.commandNo && item.commandYear + ? item.commandNo + + "/" + + (item.commandYear > 2500 ? item.commandYear : item.commandYear + 543) + : "-"; + const dateAffect = item.commandDateAffect + ? `${Extension.ToThaiFullDate2(item.commandDateAffect)}` : "-"; - const dateAffect = item.commandDateAffect - ? `${Extension.ToThaiFullDate2(item.commandDateAffect)}` - : "-"; - return { - commandName: - _commandName && _commandName.name ? _commandName.name : item.commandName, - commandDateAffect: item.commandDateAffect - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) - : "", - commandDateSign: item.commandDateSign - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateSign)) - : "", - posNo: - item.posNoAbb && item.posNo - ? Extension.ToThaiNumber(`${item.posNoAbb} ${item.posNo}`) + return { + commandName: + _commandName && _commandName.name ? _commandName.name : item.commandName, + commandDateAffect: item.commandDateAffect + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) : "", - position: item.positionName, - posType: item.positionType, - posLevel: item.positionLevel - ? Extension.ToThaiNumber(item.positionLevel) - : item.positionCee - ? Extension.ToThaiNumber(item.positionCee) - : null, - amount: item.amount - ? Extension.ToThaiNumber(Number(item.amount).toLocaleString()) - : "", - positionSalaryAmount: item.positionSalaryAmount - ? Extension.ToThaiNumber(Number(item.positionSalaryAmount).toLocaleString()) - : "", - refDoc: Extension.ToThaiNumber( - `คำสั่ง ${codeSitAbb} ที่ ${commandNo} ลว. ${dateAffect}`, - ), - }; - }), - ) + commandDateSign: item.commandDateSign + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateSign)) + : "", + posNo: + item.posNoAbb && item.posNo + ? Extension.ToThaiNumber(`${item.posNoAbb} ${item.posNo}`) + : "", + position: item.positionName, + posType: item.positionType, + posLevel: item.positionLevel + ? Extension.ToThaiNumber(item.positionLevel) + : item.positionCee + ? Extension.ToThaiNumber(item.positionCee) + : null, + amount: item.amount + ? Extension.ToThaiNumber(Number(item.amount).toLocaleString()) + : "", + positionSalaryAmount: item.positionSalaryAmount + ? Extension.ToThaiNumber(Number(item.positionSalaryAmount).toLocaleString()) + : "", + refDoc: Extension.ToThaiNumber( + `คำสั่ง ${codeSitAbb} ที่ ${commandNo} ลว. ${dateAffect}`, + ), + }; + }), + ) : [ - { - commandName: "", - commandDateAffect: "", - commandDateSign: "", - posNo: "", - position: "", - posType: "", - posLevel: "", - amount: "", - positionSalaryAmount: "", - refDoc: "", - }, - ]; + { + commandName: "", + commandDateAffect: "", + commandDateSign: "", + posNo: "", + position: "", + posType: "", + posLevel: "", + amount: "", + positionSalaryAmount: "", + refDoc: "", + }, + ]; // ลูกจ้างยังไม่มีรักษาการและช่วยราชการ // const actposition_raw = await this.profileActpositionRepo.find({ @@ -1787,36 +1791,36 @@ export class ProfileEmployeeController extends Controller { const duty = duty_raw.length > 0 ? duty_raw.map((item) => ({ - date: - item.dateStart && item.dateEnd - ? Extension.ToThaiNumber( - `${Extension.ToThaiFullDate2(item.dateStart)} - ${Extension.ToThaiFullDate2(item.dateEnd)}`, - ) - : item.dateStart - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.dateStart)) - : item.dateEnd - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.dateEnd)) - : "", - type: "-", - detail: Extension.ToThaiNumber(item.detail), - agency: "-", - refCommandNo: item.refCommandNo - ? item.refCommandDate - ? Extension.ToThaiNumber( - `${item.refCommandNo} ลว. ${Extension.ToThaiFullDate2(item.refCommandDate)}`, - ) - : Extension.ToThaiNumber(item.refCommandNo) - : "-", - })) + date: + item.dateStart && item.dateEnd + ? Extension.ToThaiNumber( + `${Extension.ToThaiFullDate2(item.dateStart)} - ${Extension.ToThaiFullDate2(item.dateEnd)}`, + ) + : item.dateStart + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.dateStart)) + : item.dateEnd + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.dateEnd)) + : "", + type: "-", + detail: Extension.ToThaiNumber(item.detail), + agency: "-", + refCommandNo: item.refCommandNo + ? item.refCommandDate + ? Extension.ToThaiNumber( + `${item.refCommandNo} ลว. ${Extension.ToThaiFullDate2(item.refCommandDate)}`, + ) + : Extension.ToThaiNumber(item.refCommandNo) + : "-", + })) : [ - { - date: "", - type: "", - detail: "", - agency: "", - refCommandNo: "", - }, - ]; + { + date: "", + type: "", + detail: "", + agency: "", + refCommandNo: "", + }, + ]; const assessments_raw = await this.profileAssessmentsRepository.find({ where: { profileEmployeeId: id, isDeleted: false }, order: { createdAt: "ASC" }, @@ -1824,48 +1828,48 @@ export class ProfileEmployeeController extends Controller { const assessments = assessments_raw.length > 0 ? assessments_raw.map((item) => ({ - year: item.year ? Extension.ToThaiNumber((parseInt(item.year) + 543).toString()) : "", - period: - item.period && item.period == "APR" - ? Extension.ToThaiNumber( - `1 เม.ย. ${(parseInt(item.year) + 543 - 1).toString()} - 31 มี.ค. ${(parseInt(item.year) + 543).toString()}`, - ) - : Extension.ToThaiNumber( - `1 ต.ค. ${(parseInt(item.year) + 543 - 1).toString()} - 30 ก.ย. ${(parseInt(item.year) + 543).toString()}`, - ), - point1: item.point1 ? Extension.ToThaiNumber(item.point1.toString()) : "", - point1Total: item.point1Total - ? Extension.ToThaiNumber(item.point1Total.toString()) - : "", - point2: item.point2 ? Extension.ToThaiNumber(item.point2.toString()) : "", - point2Total: item.point2Total - ? Extension.ToThaiNumber(item.point2Total.toString()) - : "", - pointSum: item.pointSum - ? Extension.ToThaiNumber(`ร้อยละ ${item.pointSum.toString()}`) - : "", - pointSumTh: item.pointSum ? Extension.textPoint(item.pointSum) : "", - level: - item.pointSum < 60.0 - ? "ต้องปรับปรุง" - : item.pointSum <= 69.99 && item.pointSum >= 60.0 - ? "พอใช้" - : item.pointSum <= 79.99 && item.pointSum >= 70.0 - ? "ดี" - : item.pointSum <= 89.99 && item.pointSum >= 80.0 - ? "ดีมาก" - : "ดีเด่น", - })) + year: item.year ? Extension.ToThaiNumber((parseInt(item.year) + 543).toString()) : "", + period: + item.period && item.period == "APR" + ? Extension.ToThaiNumber( + `1 เม.ย. ${(parseInt(item.year) + 543 - 1).toString()} - 31 มี.ค. ${(parseInt(item.year) + 543).toString()}`, + ) + : Extension.ToThaiNumber( + `1 ต.ค. ${(parseInt(item.year) + 543 - 1).toString()} - 30 ก.ย. ${(parseInt(item.year) + 543).toString()}`, + ), + point1: item.point1 ? Extension.ToThaiNumber(item.point1.toString()) : "", + point1Total: item.point1Total + ? Extension.ToThaiNumber(item.point1Total.toString()) + : "", + point2: item.point2 ? Extension.ToThaiNumber(item.point2.toString()) : "", + point2Total: item.point2Total + ? Extension.ToThaiNumber(item.point2Total.toString()) + : "", + pointSum: item.pointSum + ? Extension.ToThaiNumber(`ร้อยละ ${item.pointSum.toString()}`) + : "", + pointSumTh: item.pointSum ? Extension.textPoint(item.pointSum) : "", + level: + item.pointSum < 60.0 + ? "ต้องปรับปรุง" + : item.pointSum <= 69.99 && item.pointSum >= 60.0 + ? "พอใช้" + : item.pointSum <= 79.99 && item.pointSum >= 70.0 + ? "ดี" + : item.pointSum <= 89.99 && item.pointSum >= 80.0 + ? "ดีมาก" + : "ดีเด่น", + })) : [ - { - year: "", - period: "", - point1: "", - point2: "", - pointSum: "", - pointSumTh: "", - }, - ]; + { + year: "", + period: "", + point1: "", + point2: "", + pointSum: "", + pointSumTh: "", + }, + ]; const profileAbility_raw = await this.profileAbilityRepo.find({ where: { profileEmployeeId: id }, order: { createdAt: "ASC" }, @@ -1873,15 +1877,15 @@ export class ProfileEmployeeController extends Controller { const profileAbility = profileAbility_raw.length > 0 ? profileAbility_raw.map((item) => ({ - field: item.field ? item.field : "", - detail: item.detail ? item.detail : "", - })) + field: item.field ? item.field : "", + detail: item.detail ? item.detail : "", + })) : [ - { - field: "", - detail: "", - }, - ]; + { + field: "", + detail: "", + }, + ]; const otherIncome_raw = await this.salaryRepo.find({ where: { @@ -1894,53 +1898,53 @@ export class ProfileEmployeeController extends Controller { const otherIncome = otherIncome_raw.length > 0 ? await Promise.all( - otherIncome_raw.map(async (item) => { - const codeSitAbb = item.posNumCodeSitAbb ?? "-"; - const commandNo = - item.commandNo && item.commandYear - ? item.commandNo + - "/" + - (item.commandYear > 2500 ? item.commandYear : item.commandYear + 543) + otherIncome_raw.map(async (item) => { + const codeSitAbb = item.posNumCodeSitAbb ?? "-"; + const commandNo = + item.commandNo && item.commandYear + ? item.commandNo + + "/" + + (item.commandYear > 2500 ? item.commandYear : item.commandYear + 543) + : "-"; + const dateAffect = item.commandDateAffect + ? `${Extension.ToThaiFullDate2(item.commandDateAffect)}` : "-"; - const dateAffect = item.commandDateAffect - ? `${Extension.ToThaiFullDate2(item.commandDateAffect)}` - : "-"; - return { - commandName: item.commandName ?? "", - commandDateAffect: item.commandDateAffect - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) - : "", - commandDateSign: item.commandDateSign - ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateSign)) - : "", - commandNo: item.commandNo ? Extension.ToThaiNumber(item.commandNo) : "", - position: item.positionName, - posLevel: item.positionLevel - ? Extension.ToThaiNumber(item.positionLevel) - : item.positionCee - ? Extension.ToThaiNumber(item.positionCee) - : null, - amount: item.amount - ? Extension.ToThaiNumber(Number(item.amount).toLocaleString()) - : "", - refDoc: Extension.ToThaiNumber( - `คำสั่ง ${codeSitAbb} ที่ ${commandNo} ลว. ${dateAffect}`, - ), - }; - }), - ) + return { + commandName: item.commandName ?? "", + commandDateAffect: item.commandDateAffect + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateAffect)) + : "", + commandDateSign: item.commandDateSign + ? Extension.ToThaiNumber(Extension.ToThaiFullDate2(item.commandDateSign)) + : "", + commandNo: item.commandNo ? Extension.ToThaiNumber(item.commandNo) : "", + position: item.positionName, + posLevel: item.positionLevel + ? Extension.ToThaiNumber(item.positionLevel) + : item.positionCee + ? Extension.ToThaiNumber(item.positionCee) + : null, + amount: item.amount + ? Extension.ToThaiNumber(Number(item.amount).toLocaleString()) + : "", + refDoc: Extension.ToThaiNumber( + `คำสั่ง ${codeSitAbb} ที่ ${commandNo} ลว. ${dateAffect}`, + ), + }; + }), + ) : [ - { - commandName: "", - commandDateAffect: "", - commandDateSign: "", - commandNo: "", - position: "", - posLevel: "", - amount: "", - refDoc: "", - }, - ]; + { + commandName: "", + commandDateAffect: "", + commandDateSign: "", + commandNo: "", + position: "", + posLevel: "", + amount: "", + refDoc: "", + }, + ]; // ประวัติพ้นจากราชการ let retires = []; @@ -2039,8 +2043,8 @@ export class ProfileEmployeeController extends Controller { profiles?.position != null ? profiles.posType != null && profiles?.posLevel != null ? Extension.ToThaiNumber( - `${profiles.position} ${profiles.posType.posTypeShortName} ${profiles.posLevel.posLevelName}`, - ) + `${profiles.position} ${profiles.posType.posTypeShortName} ${profiles.posLevel.posLevelName}`, + ) : profiles.position : "" ).trim(); @@ -2057,45 +2061,45 @@ export class ProfileEmployeeController extends Controller { const sum = profiles ? Extension.ToThaiNumber( - ( - Number(profiles.amount) + - Number(profiles.positionSalaryAmount) + - Number(profiles.mouthSalaryAmount) + - Number(profiles.amountSpecial) - ).toLocaleString(), - ) + ( + Number(profiles.amount) + + Number(profiles.positionSalaryAmount) + + Number(profiles.mouthSalaryAmount) + + Number(profiles.amountSpecial) + ).toLocaleString(), + ) : ""; const fullCurrentAddress = profiles && profiles.currentAddress ? Extension.ToThaiNumber( - profiles.currentAddress + - (profiles.currentSubDistrict && profiles.currentSubDistrict.name - ? " ตำบล/แขวง " + profiles.currentSubDistrict.name - : "") + - (profiles.currentDistrict && profiles.currentDistrict.name - ? " อำเภอ/เขต " + profiles.currentDistrict.name - : "") + - (profiles.currentProvince && profiles.currentProvince.name - ? " จังหวัด " + profiles.currentProvince.name - : "") + - (profiles.currentZipCode ? " " + profiles.currentZipCode : ""), - ) + profiles.currentAddress + + (profiles.currentSubDistrict && profiles.currentSubDistrict.name + ? " ตำบล/แขวง " + profiles.currentSubDistrict.name + : "") + + (profiles.currentDistrict && profiles.currentDistrict.name + ? " อำเภอ/เขต " + profiles.currentDistrict.name + : "") + + (profiles.currentProvince && profiles.currentProvince.name + ? " จังหวัด " + profiles.currentProvince.name + : "") + + (profiles.currentZipCode ? " " + profiles.currentZipCode : ""), + ) : ""; const fullRegistrationAddress = profiles && profiles.registrationAddress ? Extension.ToThaiNumber( - profiles.registrationAddress + - (profiles.registrationSubDistrict && profiles.registrationSubDistrict.name - ? " ตำบล/แขวง " + profiles.registrationSubDistrict.name - : "") + - (profiles.registrationDistrict && profiles.registrationDistrict.name - ? " อำเภอ/เขต " + profiles.registrationDistrict.name - : "") + - (profiles.registrationProvince && profiles.registrationProvince.name - ? " จังหวัด " + profiles.registrationProvince.name - : "") + - (profiles.currentZipCode ? " " + profiles.currentZipCode : ""), - ) + profiles.registrationAddress + + (profiles.registrationSubDistrict && profiles.registrationSubDistrict.name + ? " ตำบล/แขวง " + profiles.registrationSubDistrict.name + : "") + + (profiles.registrationDistrict && profiles.registrationDistrict.name + ? " อำเภอ/เขต " + profiles.registrationDistrict.name + : "") + + (profiles.registrationProvince && profiles.registrationProvince.name + ? " จังหวัด " + profiles.registrationProvince.name + : "") + + (profiles.currentZipCode ? " " + profiles.currentZipCode : ""), + ) : ""; const data = { currentDate: Extension.ToThaiNumber(Extension.ToThaiFullDate2(currentDate)), @@ -2143,24 +2147,24 @@ export class ProfileEmployeeController extends Controller { profiles.citizenId != null ? Extension.ToThaiNumber(profiles.citizenId.toString()) : "", fatherFullName: profileFamilyFather?.fatherPrefix || - profileFamilyFather?.fatherFirstName || - profileFamilyFather?.fatherLastName + profileFamilyFather?.fatherFirstName || + profileFamilyFather?.fatherLastName ? `${profileFamilyFather?.fatherPrefix ?? ""}${profileFamilyFather?.fatherFirstName ?? ""} ${profileFamilyFather?.fatherLastName ?? ""}`.trim() : null, fatherLive: profileFamilyFather && profileFamilyFather?.fatherLive == true ? "ถึงแก่กรรม" : "มีชีวิต", motherFullName: profileFamilyMother?.motherPrefix || - profileFamilyMother?.motherFirstName || - profileFamilyMother?.motherLastName + profileFamilyMother?.motherFirstName || + profileFamilyMother?.motherLastName ? `${profileFamilyMother?.motherPrefix ?? ""}${profileFamilyMother?.motherFirstName ?? ""} ${profileFamilyMother?.motherLastName ?? ""}`.trim() : null, motherLive: profileFamilyMother && profileFamilyMother?.motherLive == true ? "ถึงแก่กรรม" : "มีชีวิต", coupleFullName: profileFamilyCouple?.couplePrefix || - profileFamilyCouple?.coupleFirstName || - profileFamilyCouple?.coupleLastNameOld + profileFamilyCouple?.coupleFirstName || + profileFamilyCouple?.coupleLastNameOld ? `${profileFamilyCouple?.couplePrefix ?? ""}${profileFamilyCouple?.coupleFirstName ?? ""} ${profileFamilyCouple?.coupleLastName ?? ""}`.trim() : null, coupleLastNameOld: profileFamilyCouple?.coupleLastNameOld ?? null, @@ -2382,27 +2386,28 @@ export class ProfileEmployeeController extends Controller { } if (body.citizenId) { - Extension.CheckCitizen(body.citizenId); + const citizenIdDigits = body.citizenId.toString().split("").map(Number); + const cal = + citizenIdDigits[0] * 13 + + citizenIdDigits[1] * 12 + + citizenIdDigits[2] * 11 + + citizenIdDigits[3] * 10 + + citizenIdDigits[4] * 9 + + citizenIdDigits[5] * 8 + + citizenIdDigits[6] * 7 + + citizenIdDigits[7] * 6 + + citizenIdDigits[8] * 5 + + citizenIdDigits[9] * 4 + + citizenIdDigits[10] * 3 + + citizenIdDigits[11] * 2; + const calStp2 = cal % 11; + const chkDigit = (11 - calStp2) % 10; + + if (citizenIdDigits[12] !== chkDigit) { + throw new HttpError(HttpStatus.NOT_FOUND, "ข้อมูลรหัสบัตรประจำตัวประชาชนไม่ถูกต้อง"); + } } const record = await this.profileRepo.findOneBy({ id }); - const before = structuredClone(record); - // เช็คว่ามี profileHistory ของ profile นี้หรือไม่ - const historyCount = await this.profileHistoryRepo.count({ - where: { profileEmployeeId: id }, - }); - - // ถ้าไม่มีเลย ให้บันทึกข้อมูลเริ่มต้น (ก่อน update) ลงไปก่อน - if (historyCount === 0) { - await this.profileHistoryRepo.save( - Object.assign(new ProfileEmployeeHistory(), { - ...before, - birthDateOld: before?.birthDate, - profileEmployeeId: id, - id: undefined, - }), - ); - } - if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์นี้"); if (body.employeeClass == null || body.employeeClass == undefined || body.employeeClass == "") { @@ -2415,7 +2420,7 @@ export class ProfileEmployeeController extends Controller { Object.assign(record, body); record.dateRetireLaw = calculateRetireLaw(record.birthDate); record.prefixMain = record.prefix; - record.prefix = record.rank && record.rank.length > 0 ? record.rank : record.prefix; + record.prefix = record.rank && record.rank.length > 0 ? record.rank : record.prefixMain; record.createdUserId = request.user.sub; record.createdFullName = request.user.name; record.createdAt = new Date(); @@ -2434,8 +2439,6 @@ export class ProfileEmployeeController extends Controller { }), ); await this.profileRepo.save(record); - // บันทึกประวัติคนครองตำแหน่ง (ถ้า profile นี้ครองตำแหน่งอยู่) - await updateHolderProfileHistory(record.id, request, "EMPLOYEE"); return new HttpSuccess(); } @@ -2533,8 +2536,8 @@ export class ProfileEmployeeController extends Controller { _data.profileEmployeeEmployment.length == 0 ? null : _data.profileEmployeeEmployment.reduce((latest, current) => { - return latest.date > current.date ? latest : current; - }).date; + return latest.date > current.date ? latest : current; + }).date; return { id: _data.id, prefix: _data.prefix, @@ -2697,32 +2700,32 @@ export class ProfileEmployeeController extends Controller { profile.current_holders.length == 0 ? null : profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != - null + profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != + null ? `${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild3 != null + profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild3 != null ? `${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild2 != null + profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild2 != null ? `${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild1 != null + profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild1 != null ? `${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - null && - profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgRoot != null + null && + profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgRoot != null ? `${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName} ${profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : null; const root = profile.current_holders.length == 0 || - (profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) + (profile.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) ? null : profile.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot; @@ -2849,12 +2852,12 @@ export class ProfileEmployeeController extends Controller { queryLike = "profileEmployee.position LIKE :keyword"; } else if (searchField == "posNo") { queryLike = ` - CASE - WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT_WS(' ', orgChild4.orgChild4ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT_WS(' ', orgChild3.orgChild3ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT_WS(' ', orgChild2.orgChild2ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT_WS(' ', orgChild1.orgChild1ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - ELSE CONCAT_WS(' ', orgRoot.orgRootShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) + CASE + WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, " ", current_holders.posMasterNo) + ELSE CONCAT(orgRoot.orgRootShortName, " ", current_holders.posMasterNo) END LIKE :keyword `; } @@ -2947,57 +2950,57 @@ export class ProfileEmployeeController extends Controller { _data.current_holders.length == 0 ? null : _data.current_holders.find((x) => x.orgRevisionId == revisionId) != null && - _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4 != null + _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4 != null ? `${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4.orgChild4ShortName} ${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.posMasterNo}` : _data.current_holders.find((x) => x.orgRevisionId == revisionId) != null && - _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3 != - null + _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3 != + null ? `${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3.orgChild3ShortName} ${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.posMasterNo}` : _data.current_holders.find((x) => x.orgRevisionId == revisionId) != null && - _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2 != - null + _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2 != + null ? `${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2.orgChild2ShortName} ${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.posMasterNo}` : _data.current_holders.find((x) => x.orgRevisionId == revisionId) != null && - _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1 != - null + _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1 != + null ? `${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1.orgChild1ShortName} ${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.posMasterNo}` : _data.current_holders.find((x) => x.orgRevisionId == revisionId) != null && - _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot != - null + _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot != + null ? `${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot.orgRootShortName} ${_data.current_holders.find((x) => x.orgRevisionId == revisionId)?.posMasterNo}` : null; const root = _data.current_holders.length == 0 || - (_data.current_holders.find((x) => x.orgRevisionId == revisionId) != null && - _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot == null) + (_data.current_holders.find((x) => x.orgRevisionId == revisionId) != null && + _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot == null) ? null : _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot; const child1 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == revisionId) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == revisionId) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1; const child2 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == revisionId) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == revisionId) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2; const child3 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == revisionId) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == revisionId) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3; const child4 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == revisionId) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == revisionId) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4; @@ -3108,7 +3111,7 @@ export class ProfileEmployeeController extends Controller { @Query() sortBy: string = "profileEmployee.dateLeave", @Query() sort: "ASC" | "DESC" = "DESC", ) { - let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_RETIRE_EMP"); + let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_EMP"); const { data, total } = await this.profileLeaveService.getLeaveEmployees(request, { page, @@ -3208,12 +3211,12 @@ export class ProfileEmployeeController extends Controller { queryLike = "profileEmployee.position LIKE :keyword"; } else if (searchField == "posNo") { queryLike = ` - CASE - WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT_WS(' ', orgChild4.orgChild4ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT_WS(' ', orgChild3.orgChild3ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT_WS(' ', orgChild2.orgChild2ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT_WS(' ', orgChild1.orgChild1ShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) - ELSE CONCAT_WS(' ', orgRoot.orgRootShortName, NULLIF(current_holders.posMasterNoPrefix,''), current_holders.posMasterNo, NULLIF(current_holders.posMasterNoSuffix,'')) + CASE + WHEN current_holders.orgChild4Id IS NOT NULL THEN CONCAT(orgChild4.orgChild4ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild3Id IS NOT NULL THEN CONCAT(orgChild3.orgChild3ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild2Id IS NOT NULL THEN CONCAT(orgChild2.orgChild2ShortName, " ", current_holders.posMasterNo) + WHEN current_holders.orgChild1Id IS NOT NULL THEN CONCAT(orgChild1.orgChild1ShortName, " ", current_holders.posMasterNo) + ELSE CONCAT(orgRoot.orgRootShortName, " ", current_holders.posMasterNo) END LIKE :keyword `; } @@ -3254,8 +3257,8 @@ export class ProfileEmployeeController extends Controller { .leftJoinAndSelect("current_holders.orgChild2", "orgChild2") .leftJoinAndSelect("current_holders.orgChild3", "orgChild3") .leftJoinAndSelect("current_holders.orgChild4", "orgChild4") - .where("current_holders.orgRevisionId = :orgRevisionId", { - orgRevisionId: findRevision.id, + .where(node && nodeId ? "current_holders.orgRevisionId = :orgRevisionId" : "1=1", { + orgRevisionId: node && nodeId ? findRevision.id : undefined, }) .andWhere( _data.root != undefined && _data.root != null @@ -3272,7 +3275,7 @@ export class ProfileEmployeeController extends Controller { ? _data.child1[0] != null ? `current_holders.orgChild1Id IN (:...child1)` : // : `current_holders.orgChild1Id is ${_data.privilege == "PARENT" ? "not null" : "null"}` - `current_holders.orgChild1Id is null` + `current_holders.orgChild1Id is null` : "1=1", { child1: _data.child1, @@ -3338,7 +3341,6 @@ export class ProfileEmployeeController extends Controller { : `profileEmployee.isLeave IS TRUE` : "1=1", ) - .andWhere("profileEmployee.isActive IS TRUE AND profileEmployee.isDelete IS FALSE") .andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM", }) @@ -3367,7 +3369,7 @@ export class ProfileEmployeeController extends Controller { const data = await Promise.all( record.map((_data) => { const holder = _data.current_holders.find((x) => x.orgRevisionId == findRevision.id); - const numPart = holder ? [holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; + const numPart = holder ? `${holder.posMasterNoPrefix ?? ''}${holder.posMasterNo ?? ''}${holder.posMasterNoSuffix ?? ''}` : ''; const shortName = !holder ? null : holder.orgChild4 != null @@ -3385,40 +3387,40 @@ export class ProfileEmployeeController extends Controller { _data.profileEmployeeEmployment.length == 0 ? null : _data.profileEmployeeEmployment.reduce((latest, current) => { - return latest.date > current.date ? latest : current; - }).date; + return latest.date > current.date ? latest : current; + }).date; const root = _data.current_holders.length == 0 || - (_data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) + (_data.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot; const child1 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1; const child2 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2; const child3 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3; const child4 = _data.current_holders == null || - _data.current_holders.length == 0 || - _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + _data.current_holders.length == 0 || + _data.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : _data.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4; @@ -3686,8 +3688,8 @@ export class ProfileEmployeeController extends Controller { .map((x) => x.current_holderId).length == 0 ? ["zxc"] : orgRevision.employeePosMasters - .filter((x) => x.current_holderId != null) - .map((x) => x.current_holderId), + .filter((x) => x.current_holderId != null) + .map((x) => x.current_holderId), }); }), ) @@ -3844,7 +3846,7 @@ export class ProfileEmployeeController extends Controller { holder.orgChild2?.orgChild2ShortName || holder.orgChild1?.orgChild1ShortName || holder.orgRoot?.orgRootShortName; - return `${shortName || ""} ${[holder.posMasterNoPrefix, holder.posMasterNo, holder.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ')}`; + return `${shortName || ""} ${holder.posMasterNo || ""}`; }); return profile.current_holders.map((holder, index) => { const position = holder.positions.find((position) => position.posMasterId === holder.id); @@ -3925,37 +3927,37 @@ export class ProfileEmployeeController extends Controller { const posMaster = profile.current_holders == null || - profile.current_holders.length == 0 || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) == null + profile.current_holders.length == 0 || + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id); const root = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot == null + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot; const child1 = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1 == + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1; const child2 = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2 == + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2; const child3 = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3 == + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3; const child4 = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4 == + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4; @@ -4022,38 +4024,40 @@ export class ProfileEmployeeController extends Controller { salary: profile ? profile.amount : null, amountSpecial: profile ? profile.amountSpecial : null, posNo: null, + // root?.orgRootShortName && posMaster?.posMasterNo + // ? `${root?.orgRootShortName} ${posMaster?.posMasterNo}` + // : "", }; - const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; if (_profile.child4Id != null) { _profile.node = 4; _profile.nodeId = _profile.child4Id; _profile.nodeDnaId = _profile.child4DnaId; _profile.nodeShortName = _profile.child4ShortName; - _profile.posNo = `${_profile.child4ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child4ShortName} ${_profile.posMasterNo}`; } else if (_profile.child3Id != null) { _profile.node = 3; _profile.nodeId = _profile.child3Id; _profile.nodeDnaId = _profile.child3DnaId; _profile.nodeShortName = _profile.child3ShortName; - _profile.posNo = `${_profile.child3ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child3ShortName} ${_profile.posMasterNo}`; } else if (_profile.child2Id != null) { _profile.node = 2; _profile.nodeId = _profile.child2Id; _profile.nodeDnaId = _profile.child2DnaId; _profile.nodeShortName = _profile.child2ShortName; - _profile.posNo = `${_profile.child2ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child2ShortName} ${_profile.posMasterNo}`; } else if (_profile.child1Id != null) { _profile.node = 1; _profile.nodeId = _profile.child1Id; _profile.nodeDnaId = _profile.child1DnaId; _profile.nodeShortName = _profile.child1ShortName; - _profile.posNo = `${_profile.child1ShortName} ${_numPart}`; + _profile.posNo = `${_profile.child1ShortName} ${_profile.posMasterNo}`; } else if (_profile.rootId != null) { _profile.node = 0; _profile.nodeId = _profile.rootId; _profile.nodeDnaId = _profile.rootDnaId; _profile.nodeShortName = _profile.rootShortName; - _profile.posNo = `${_profile.rootShortName} ${_numPart}`; + _profile.posNo = `${_profile.rootShortName} ${_profile.posMasterNo}`; } return new HttpSuccess(_profile); } @@ -4154,7 +4158,7 @@ export class ProfileEmployeeController extends Controller { ? _data.child1[0] != null ? `current_holders.orgChild1Id IN (:...child1)` : // : `current_holders.orgChild1Id is ${_data.privilege == "PARENT" ? "not null" : "null"}` - `current_holders.orgChild1Id is null` + `current_holders.orgChild1Id is null` : "1=1", { child1: _data.child1 }, ) @@ -4198,32 +4202,32 @@ export class ProfileEmployeeController extends Controller { item.current_holders.length == 0 ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != - null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != + null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3 != - null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3 != + null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild2 != null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild2 != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild1 != null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild1 != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgRoot != null + null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgRoot != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : null; const root = item.current_holders.length == 0 || - (item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) + (item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot; @@ -4713,7 +4717,7 @@ export class ProfileEmployeeController extends Controller { ? _data.child1[0] != null ? `current_holders.orgChild1Id IN (:...child1)` : // : `current_holders.orgChild1Id is ${_data.privilege == "PARENT" ? "not null" : "null"}` - `current_holders.orgChild1Id is null` + `current_holders.orgChild1Id is null` : "1=1", { child1: _data.child1, @@ -4784,54 +4788,54 @@ export class ProfileEmployeeController extends Controller { isProbation: item.isProbation, orgRootName: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot - ?.orgRootName == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot + ?.orgRootName == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot - ?.orgRootName, + ?.orgRootName, orgChild1Name: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild1 == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild1 == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild1 - ?.orgChild1Name == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild1 + ?.orgChild1Name == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) - ?.orgChild1?.orgChild1Name, + ?.orgChild1?.orgChild1Name, orgChild2Name: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild2 == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild2 == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild2 - ?.orgChild2Name == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild2 + ?.orgChild2Name == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) - ?.orgChild2?.orgChild2Name, + ?.orgChild2?.orgChild2Name, orgChild3Name: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild3 == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild3 == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild3 - ?.orgChild3Name == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild3 + ?.orgChild3Name == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) - ?.orgChild3?.orgChild3Name, + ?.orgChild3?.orgChild3Name, orgChild4Name: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild4 == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild4 == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild4 - ?.orgChild4Name == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild4 + ?.orgChild4Name == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) - ?.orgChild4?.orgChild4Name, + ?.orgChild4?.orgChild4Name, }; }), ); @@ -4908,49 +4912,49 @@ export class ProfileEmployeeController extends Controller { findProfile.map(async (item: ProfileEmployee) => { const posMaster = item.current_holders == null || - item.current_holders.length == 0 || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) == null + item.current_holders.length == 0 || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id); const position = posMaster == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.positions == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.positions == null || - item.current_holders?.find((x) => x.orgRevisionId == orgRevisionActive.id)?.positions - .length == 0 || - item.current_holders - .find((x) => x.orgRevisionId == orgRevisionActive.id) - ?.positions?.find((position) => position.positionIsSelected == true) == null + item.current_holders?.find((x) => x.orgRevisionId == orgRevisionActive.id)?.positions + .length == 0 || + item.current_holders + .find((x) => x.orgRevisionId == orgRevisionActive.id) + ?.positions?.find((position) => position.positionIsSelected == true) == null ? null : item.current_holders - .find((x) => x.orgRevisionId == orgRevisionActive.id) - ?.positions?.find((position) => position.positionIsSelected == true); + .find((x) => x.orgRevisionId == orgRevisionActive.id) + ?.positions?.find((position) => position.positionIsSelected == true); const shortName = item.current_holders.length == 0 ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) != null && - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) - ?.orgChild4 != null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) + ?.orgChild4 != null ? `${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) != null && - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) - ?.orgChild3 != null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) + ?.orgChild3 != null ? `${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) != - null && - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) - ?.orgChild2 != null + null && + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) + ?.orgChild2 != null ? `${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) != - null && - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) - ?.orgChild1 != null + null && + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) + ?.orgChild1 != null ? `${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) != - null && - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) - ?.orgRoot != null + null && + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id) + ?.orgRoot != null ? `${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive.id)?.posMasterNo}` : null; @@ -4968,54 +4972,54 @@ export class ProfileEmployeeController extends Controller { isProbation: item.isProbation, orgRootName: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot - ?.orgRootName == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot + ?.orgRootName == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgRoot - ?.orgRootName, + ?.orgRootName, orgChild1Name: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild1 == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild1 == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild1 - ?.orgChild1Name == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild1 + ?.orgChild1Name == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) - ?.orgChild1?.orgChild1Name, + ?.orgChild1?.orgChild1Name, orgChild2Name: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild2 == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild2 == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild2 - ?.orgChild2Name == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild2 + ?.orgChild2Name == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) - ?.orgChild2?.orgChild2Name, + ?.orgChild2?.orgChild2Name, orgChild3Name: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild3 == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild3 == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild3 - ?.orgChild3Name == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild3 + ?.orgChild3Name == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) - ?.orgChild3?.orgChild3Name, + ?.orgChild3?.orgChild3Name, orgChild4Name: item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild4 == + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) == null || + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild4 == null || - item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild4 - ?.orgChild4Name == null + item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id)?.orgChild4 + ?.orgChild4Name == null ? null : item.current_holders.find((x) => x.orgRevisionId == orgRevisionActive?.id) - ?.orgChild4?.orgChild4Name, + ?.orgChild4?.orgChild4Name, }; }), ); @@ -5308,7 +5312,7 @@ export class ProfileEmployeeController extends Controller { isLeave: false, isRetired: item.current_holder.birthDate == null || - calculateRetireDate(item.current_holder.birthDate).getFullYear() != body.year + calculateRetireDate(item.current_holder.birthDate).getFullYear() != body.year ? false : true, isSpecial: false, @@ -5362,98 +5366,98 @@ export class ProfileEmployeeController extends Controller { posTypeId: profile.posType == null ? null : profile.posType.id, rootId: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRootId, rootDnaId: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.ancestorDNA, root: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgRoot.orgRootName, child1Id: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1Id, child1DnaId: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.ancestorDNA, child1: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild1 - .orgChild1Name, + .orgChild1Name, child2Id: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2Id, child2DnaId: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.ancestorDNA, child2: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild2 - .orgChild2Name, + .orgChild2Name, child3Id: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3Id, child3DnaId: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.ancestorDNA, child3: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild3 - .orgChild3Name, + .orgChild3Name, child4Id: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4Id, child4DnaId: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.ancestorDNA, child4: profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || - profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4 == null + profile.current_holders.find((x) => x.orgRevisionId == revisionId) == null || + profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == revisionId)?.orgChild4 - .orgChild4Name, + .orgChild4Name, }; return new HttpSuccess(_profile); } @@ -5505,8 +5509,8 @@ export class ProfileEmployeeController extends Controller { const formattedData = profiles.map((item) => { const posMaster = item.current_holders == null || - item.current_holders.length == 0 || - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null + item.current_holders.length == 0 || + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) == null ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id); @@ -5514,49 +5518,49 @@ export class ProfileEmployeeController extends Controller { item.current_holders.length == 0 ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != - null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != + null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3 != - null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3 != + null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2 != - null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2 != + null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild1 != null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild1 != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgRoot != null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgRoot != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : null; const root = item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot; const child1 = item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1 == null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1 == null ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1; const child2 = item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2 == null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2 == null ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2; const child3 = item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3 == null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3 == null ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3; const child4 = item.current_holders == null || - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 == null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 == null ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4; @@ -5679,24 +5683,24 @@ export class ProfileEmployeeController extends Controller { !profile.current_holders || profile.current_holders.length == 0 ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild4 != - null + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild4 != + null ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild4.orgChild4ShortName}` : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild3 != - null + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild3 != + null ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild3.orgChild3ShortName}` : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) - ?.orgChild2 != null + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) + ?.orgChild2 != null ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild2.orgChild2ShortName}` : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) - ?.orgChild1 != null + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) + ?.orgChild1 != null ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgChild1.orgChild1ShortName}` : profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) != null && - profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) - ?.orgRoot != null + profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id) + ?.orgRoot != null ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevision?.id)?.orgRoot.orgRootShortName}` : null; const dest_item = await this.salaryRepo.findOne({ @@ -6157,7 +6161,7 @@ export class ProfileEmployeeController extends Controller { positionId: profile.positionIdTemp, profileId: profile.id, }) - .then(async () => { }); + .then(async () => {}); } }), ); @@ -6251,33 +6255,33 @@ export class ProfileEmployeeController extends Controller { item.current_holders.length == 0 ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != - null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4 != + null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild4.orgChild4ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild3 != null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild3 != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild3.orgChild3ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild2 != null + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild2 != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild2.orgChild2ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgChild1 != null + null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgChild1 != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgChild1.orgChild1ShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != - null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id) - ?.orgRoot != null + null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id) + ?.orgRoot != null ? `${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot.orgRootShortName} ${item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.posMasterNo}` : null; root = item.current_holders.length == 0 || - (item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && - item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) + (item.current_holders.find((x) => x.orgRevisionId == findRevision.id) != null && + item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot == null) ? null : item.current_holders.find((x) => x.orgRevisionId == findRevision.id)?.orgRoot; root = root == null ? null : root.orgRootName; @@ -6428,126 +6432,66 @@ export class ProfileEmployeeController extends Controller { }); const posMaster = profile.current_holders == null || - profile.current_holders.length == 0 || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) == null + profile.current_holders.length == 0 || + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id); const root = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot == null + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot; const child1 = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1 == + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1; const child2 = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2 == + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2; const child3 = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3 == + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3; const child4 = profile.current_holders == null || - profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4 == + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4 == null ? null : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4; - const _numPart = posMaster ? [posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ') : ''; - - // org / posNo — ล้อ fallback ของ officer (ProfileController.getProfileByProfileid) - // employee ไม่มี profile.org / profile.posMasterNo จึงแยกตาม isLeave โดยตรง - let _Org: string | null = null; - let _PosNo: string | null = null; - if (profile.isLeave) { - const profileWithSalary = await this.profileRepo.findOne({ - where: { - id: id, - profileSalary: { - commandCode: In([ - "0", - "9", - "1", - "2", - "3", - "4", - "8", - "10", - "11", - "12", - "13", - "14", - "15", - "16", - "20", - ]), - }, - }, - relations: { profileSalary: true }, - order: { - profileSalary: { - order: "DESC", - createdAt: "DESC", - }, - }, - }); - const profileSalaryList = profileWithSalary?.profileSalary || []; - - if (profileSalaryList.length > 0) { - const _profileSalary = - profile.leaveType == "RETIRE" - ? profileSalaryList.length > 1 - ? profileSalaryList[1] - : profileSalaryList[0] - : profileSalaryList[0]; - - if (_profileSalary) { - _Org = [ - _profileSalary.orgChild4 ?? null, - _profileSalary.orgChild3 ?? null, - _profileSalary.orgChild2 ?? null, - _profileSalary.orgChild1 ?? null, - _profileSalary.orgRoot ?? null, - ] - .filter((x: any) => x !== undefined && x !== null) - .join("\n"); - _PosNo = `${_profileSalary.posNoAbb} ${_profileSalary.posNo}`; - } - } - } else { - _Org = [ - child4?.orgChild4Name, - child3?.orgChild3Name, - child2?.orgChild2Name, - child1?.orgChild1Name, - root?.orgRootName, - ] - .filter((x) => x != null && x !== "") - .join("\n"); - - _PosNo = - posMaster == null - ? null - : child4 != null - ? `${child4.orgChild4ShortName} ${_numPart}` - : child3 != null - ? `${child3.orgChild3ShortName} ${_numPart}` - : child2 != null - ? `${child2.orgChild2ShortName} ${_numPart}` - : child1 != null - ? `${child1.orgChild1ShortName} ${_numPart}` - : root != null - ? `${root.orgRootShortName} ${_numPart}` - : null; - } + const shortName = + profile.current_holders.length == 0 + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgChild4 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4.orgChild4ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgChild3 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3.orgChild3ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != + null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgChild2 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2.orgChild2ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != + null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgChild1 != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1.orgChild1ShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) != + null && + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) + ?.orgRoot != null + ? `${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot.orgRootShortName} ${profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.posMasterNo}` + : null; const _profile: any = { profileId: profile.id, prefix: profile.prefix, @@ -6560,7 +6504,7 @@ export class ProfileEmployeeController extends Controller { position: profile.position, leaveDate: profile.dateLeave, posMasterNo: posMaster == null ? null : posMaster.posMasterNo, - posLevelName: `${profile?.posType?.posTypeShortName ?? ""} ${profile?.posLevel?.posLevelName ?? ""}`.trim(), + posLevelName: `${profile?.posType?.posTypeShortName ?? ""} ${profile?.posLevel?.posLevelName ?? ""}`, posLevelRank: profile.posLevel == null ? null : profile.posLevel.posLevelRank, posLevelId: profile.posLevel == null ? null : profile.posLevel.id, posTypeName: profile.posType == null ? null : profile.posType.posTypeName, @@ -6589,8 +6533,7 @@ export class ProfileEmployeeController extends Controller { child4ShortName: child4 == null ? null : child4.orgChild4ShortName, node: null, nodeId: null, - posNo: _PosNo, - org: _Org, + posNo: shortName, salary: profile.amount, education: profile && profile.profileEducations.length > 0 diff --git a/src/controllers/ProfileEmployeeTempController.ts b/src/controllers/ProfileEmployeeTempController.ts index 406dce69..a8c017ae 100644 --- a/src/controllers/ProfileEmployeeTempController.ts +++ b/src/controllers/ProfileEmployeeTempController.ts @@ -1001,24 +1001,6 @@ export class ProfileEmployeeTempController extends Controller { } const record = await this.profileRepo.findOneBy({ id }); - const before = structuredClone(record); - // เช็คว่ามี profileHistory ของ profile นี้หรือไม่ - const historyCount = await this.profileHistoryRepo.count({ - where: { profileEmployeeId: id }, - }); - - // ถ้าไม่มีเลย ให้บันทึกข้อมูลเริ่มต้น (ก่อน update) ลงไปก่อน - if (historyCount === 0) { - await this.profileHistoryRepo.save( - Object.assign(new ProfileEmployeeHistory(), { - ...before, - birthDateOld: before?.birthDate, - profileEmployeeId: id, - id: undefined, - }), - ); - } - if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์นี้"); if (body.employeeClass == null || body.employeeClass == undefined || body.employeeClass == "") { diff --git a/src/controllers/ProfileGovernmentEmployeeController.ts b/src/controllers/ProfileGovernmentEmployeeController.ts index 7fdeb9a7..d97a452a 100644 --- a/src/controllers/ProfileGovernmentEmployeeController.ts +++ b/src/controllers/ProfileGovernmentEmployeeController.ts @@ -115,7 +115,7 @@ export class ProfileGovernmentEmployeeController extends Controller { record.posType == null && record.posLevel == null ? null : `${record.posType.posTypeShortName} ${record.posLevel.posLevelName}`, //ระดับ - posMasterNo: posMaster == null ? null : `${orgShortName} ${[posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ')}`, //เลขที่ตำแหน่ง + posMasterNo: posMaster == null ? null : `${orgShortName} ${posMaster.posMasterNoPrefix ?? ''}${posMaster.posMasterNo ?? ''}${posMaster.posMasterNoSuffix ?? ''}`, //เลขที่ตำแหน่ง posType: record.posType == null ? null : record.posType.posTypeName, //ประเภท dateLeave: record.birthDate == null ? null : calculateRetireDate(record.birthDate), dateRetireLaw: record.dateRetireLaw ?? null, @@ -281,7 +281,7 @@ export class ProfileGovernmentEmployeeController extends Controller { record?.isLeave == false ? posMaster == null ? null - : `${orgShortName} ${[posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ')}` + : `${orgShortName} ${posMaster.posMasterNoPrefix ?? ''}${posMaster.posMasterNo ?? ''}${posMaster.posMasterNoSuffix ?? ''}` : posNoLeave /*record && record?.profileSalary.length > 0 ? `${record?.profileSalary[0].posNoAbb} ${record?.profileSalary[0].posNo}` : null*/, //เลขที่ตำแหน่ง @@ -441,7 +441,7 @@ export class ProfileGovernmentEmployeeController extends Controller { record?.isLeave == false ? posMaster == null ? null - : `${orgShortName} ${[posMaster.posMasterNoPrefix, posMaster.posMasterNo, posMaster.posMasterNoSuffix].filter((p) => p !== null && p !== undefined && p !== '').join(' ')}` + : `${orgShortName} ${posMaster.posMasterNoPrefix ?? ''}${posMaster.posMasterNo ?? ''}${posMaster.posMasterNoSuffix ?? ''}` : posNoLeave /*record && record.profileSalary.length > 0 ? `${record?.profileSalary[0].posNoAbb} ${record?.profileSalary[0].posNo}` : null*/, //เลขที่ตำแหน่ง diff --git a/src/controllers/ProfileSalaryController.ts b/src/controllers/ProfileSalaryController.ts index c294cb25..277ac081 100644 --- a/src/controllers/ProfileSalaryController.ts +++ b/src/controllers/ProfileSalaryController.ts @@ -23,22 +23,12 @@ import { ProfileEmployee } from "../entities/ProfileEmployee"; import { In, IsNull, LessThan, MoreThan, Not } from "typeorm"; import permission from "../interfaces/permission"; import { setLogDataDiff } from "../interfaces/utils"; -import { ExecuteSalaryReportService } from "../services/ExecuteSalaryReportService"; -import { normalizeDurationSumSimple } from "../utils/tenure"; -import { - TenurePositionOfficer, - CreateTenurePositionOfficer, -} from "../entities/TenurePositionOfficer"; -import { TenureLevelOfficer, CreateTenureLevelOfficer } from "../entities/TenureLevelOfficer"; -import { - TenurePositionEmployee, - CreateTenurePositionEmployee, -} from "../entities/TenurePositionEmployee"; -import { TenureLevelEmployee, CreateTenureLevelEmployee } from "../entities/TenureLevelEmployee"; -import { - TenurePositionExecutiveOfficer, - CreateTenurePositionExecutiveOfficer, -} from "../entities/TenurePositionExecutiveOfficer"; +import { calculateTenure } from "../utils/tenure"; +import { TenurePositionOfficer } from "../entities/TenurePositionOfficer"; +import { TenureLevelOfficer } from "../entities/TenureLevelOfficer"; +import { TenurePositionEmployee } from "../entities/TenurePositionEmployee"; +import { TenureLevelEmployee } from "../entities/TenureLevelEmployee"; +import { TenurePositionExecutiveOfficer } from "../entities/TenurePositionExecutiveOfficer"; import { Command } from "../entities/Command"; import { OrgRoot } from "../entities/OrgRoot"; import { OrgRevision } from "../entities/OrgRevision"; @@ -56,303 +46,138 @@ export class ProfileSalaryController extends Controller { private profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); private salaryRepo = AppDataSource.getRepository(ProfileSalary); private salaryHistoryRepo = AppDataSource.getRepository(ProfileSalaryHistory); + private positionOfficerRepo = AppDataSource.getRepository(TenurePositionOfficer); + private positionEmployeeRepo = AppDataSource.getRepository(TenurePositionEmployee); + private levelOfficerRepo = AppDataSource.getRepository(TenureLevelOfficer); + private levelEmployeeRepo = AppDataSource.getRepository(TenureLevelEmployee); + private positionExecutiveOfficerRepo = AppDataSource.getRepository( + TenurePositionExecutiveOfficer, + ); private commandRepository = AppDataSource.getRepository(Command); private orgRootRepository = AppDataSource.getRepository(OrgRoot); + private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); + private positionRepo = AppDataSource.getRepository(Position); private registryRepo = AppDataSource.getRepository(Registry); private registryEmployeeRepo = AppDataSource.getRepository(RegistryEmployee); @Get("TenurePositionOfficer") public async cronjobTenurePositionOfficer() { + let data: any = []; + await this.positionOfficerRepo.clear(); + const profile = await this.profileRepo.find(); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); const baseCurrentDate = CURRENT_DATE[0].today; - - const profiles = await this.profileRepo.find({ - select: ["id", "position", "isLeave", "leaveDate"], - where: { position: Not(IsNull()) }, - }); - - const BATCH_SIZE = 50; - let successCount = 0; - let failCount = 0; - const allData: CreateTenurePositionOfficer[] = []; - - for (let i = 0; i < profiles.length; i += BATCH_SIZE) { - const batch = profiles.slice(i, Math.min(i + BATCH_SIZE, profiles.length)); - const results = await Promise.allSettled( - batch.map((profile) => - this.processSingleProfileForTenurePositionOfficer(profile, baseCurrentDate), - ), - ); - - results.forEach((result) => { - if (result.status === "fulfilled" && result.value) { - allData.push(result.value); - successCount++; - } else { - failCount++; - } - }); - } - - await AppDataSource.transaction(async (transactionalEntityManager) => { - await transactionalEntityManager.delete(TenurePositionOfficer, {}); - if (allData.length > 0) { - const entities = allData.map((data) => { - const entity = new TenurePositionOfficer(); - Object.assign(entity, data); - return entity; - }); - await transactionalEntityManager.save(TenurePositionOfficer, entities); - } - }); - - return new HttpSuccess({ - message: `อัปเดต tenure position officer สำเร็จ`, - total: profiles.length, - success: successCount, - failed: failCount, - }); - } - - private async processSingleProfileForTenurePositionOfficer( - profile: Pick, - baseCurrentDate: string, - ): Promise { - try { + for await (const x of profile) { + // Use leave date if available and valid, otherwise use current date let _currentDate = baseCurrentDate; - if (profile.isLeave && profile.leaveDate) { - _currentDate = Extension.toDateOnlyString(profile.leaveDate); + if (x.isLeave && x.leaveDate) { + _currentDate = Extension.toDateOnlyString(x.leaveDate); } - const position = await AppDataSource.query("CALL GetProfileSalaryPosition(?, ?)", [ - profile.id, + x.id, _currentDate, ]); const _position = position.length > 0 ? position[0] : []; - const mapPosition = _position.length > 1 ? _position.slice(1).map((curr: any, index: number) => ({ + days_diff: curr.days_diff, positionName: _position[index]?.positionName, - year: - curr.Years !== null && curr.Years !== undefined - ? Math.floor(Number(curr.Years)) - : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, - day: - curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, })) : []; - - const currentTenure = mapPosition.find((curr: any) => curr.positionName === profile.position); - - if (currentTenure) { - const normalized = normalizeDurationSumSimple( - currentTenure.year, - currentTenure.month, - currentTenure.day, + const calDayDiff = mapPosition + .filter((curr: any) => curr.positionName == x.position) + .reduce( + (acc: any, curr: any) => { + acc.days_diff += Number(curr.days_diff) || 0; + acc.positionName = curr.positionName; + return acc; + }, + { days_diff: 0, positionName: null }, ); - return { - profileId: profile.id, - positionName: currentTenure.positionName, - days_diff: null, - Years: normalized.years, - Months: normalized.months, - Days: normalized.days, - }; - } - return null; - } catch (error) { - return null; + const { year, month, day } = calculateTenure(calDayDiff.days_diff); + const mapData: any = { + profileId: x.id, + positionName: calDayDiff.positionName, + days_diff: calDayDiff.days_diff, + Years: year, + Months: month, + Days: day, + }; + data.push(mapData); } + await this.positionOfficerRepo.save(data); + + return new HttpSuccess(); } @Get("TenurePositionEmployee") public async cronjobTenurePositionEmployee() { + let data: any = []; + await this.positionEmployeeRepo.clear(); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); const baseCurrentDate = CURRENT_DATE[0].today; - - const profiles = await this.profileEmployeeRepo.find({ - select: ["id", "position", "isLeave", "leaveDate"], - where: { position: Not(IsNull()) }, - }); - - const BATCH_SIZE = 50; - let successCount = 0; - let failCount = 0; - const allData: CreateTenurePositionEmployee[] = []; - - for (let i = 0; i < profiles.length; i += BATCH_SIZE) { - const batch = profiles.slice(i, Math.min(i + BATCH_SIZE, profiles.length)); - const results = await Promise.allSettled( - batch.map((profile) => - this.processSingleProfileForTenurePositionEmployee(profile, baseCurrentDate), - ), - ); - - results.forEach((result) => { - if (result.status === "fulfilled" && result.value) { - allData.push(result.value); - successCount++; - } else { - failCount++; - } - }); - } - - await AppDataSource.transaction(async (transactionalEntityManager) => { - await transactionalEntityManager.delete(TenurePositionEmployee, {}); - if (allData.length > 0) { - const entities = allData.map((data) => { - const entity = new TenurePositionEmployee(); - Object.assign(entity, data); - return entity; - }); - await transactionalEntityManager.save(TenurePositionEmployee, entities); - } - }); - - return new HttpSuccess({ - message: `อัปเดต tenure position employee สำเร็จ`, - total: profiles.length, - success: successCount, - failed: failCount, - }); - } - - private async processSingleProfileForTenurePositionEmployee( - profile: Pick, - baseCurrentDate: string, - ): Promise { - try { + const profile = await this.profileEmployeeRepo.find(); + for await (const x of profile) { + // Use leave date if available and valid, otherwise use current date let _currentDate = baseCurrentDate; - if (profile.isLeave && profile.leaveDate) { - _currentDate = Extension.toDateOnlyString(profile.leaveDate); + if (x?.isLeave && x.leaveDate) { + _currentDate = Extension.toDateOnlyString(x.leaveDate); } - const position = await AppDataSource.query("CALL GetProfileEmployeeSalaryPosition(?, ?)", [ - profile.id, + x.id, _currentDate, ]); const _position = position.length > 0 ? position[0] : []; - const mapPosition = _position.length > 1 ? _position.slice(1).map((curr: any, index: number) => ({ + days_diff: curr.days_diff, positionName: _position[index]?.positionName, - year: - curr.Years !== null && curr.Years !== undefined - ? Math.floor(Number(curr.Years)) - : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, - day: - curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, })) : []; - - const currentTenure = mapPosition.find((curr: any) => curr.positionName === profile.position); - - if (currentTenure) { - const normalized = normalizeDurationSumSimple( - currentTenure.year, - currentTenure.month, - currentTenure.day, + const calDayDiff = mapPosition + .filter((curr: any) => curr.positionName == x.position) + .reduce( + (acc: any, curr: any) => { + acc.days_diff += Number(curr.days_diff) || 0; + acc.positionName = curr.positionName; + return acc; + }, + { days_diff: 0, positionName: null }, ); - return { - profileEmployeeId: profile.id, - positionName: currentTenure.positionName, - days_diff: null, - Years: normalized.years, - Months: normalized.months, - Days: normalized.days, - }; - } - return null; - } catch (error) { - return null; + const { year, month, day } = calculateTenure(calDayDiff.days_diff); + const mapData: any = { + profileEmployeeId: x.id, + positionName: calDayDiff.positionName, + days_diff: calDayDiff.days_diff, + Years: year, + Months: month, + Days: day, + }; + data.push(mapData); } + await this.positionEmployeeRepo.save(data); + + return new HttpSuccess(); } @Get("TenureLevelOfficer") public async cronjobTenureLevelOfficer() { + let data: any = []; + await this.levelOfficerRepo.clear(); + const profile = await this.profileRepo.find({ relations: ["posLevel", "posType"] }); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); const baseCurrentDate = CURRENT_DATE[0].today; - - const profiles = await this.profileRepo.find({ - relations: ["posLevel", "posType"], - select: ["id", "isLeave", "leaveDate", "posLevel", "posType"], - where: { - posLevel: Not(IsNull()), - posType: Not(IsNull()), - }, - }); - - const BATCH_SIZE = 50; - let successCount = 0; - let failCount = 0; - const allData: CreateTenureLevelOfficer[] = []; - - for (let i = 0; i < profiles.length; i += BATCH_SIZE) { - const batch = profiles.slice(i, Math.min(i + BATCH_SIZE, profiles.length)); - const results = await Promise.allSettled( - batch.map((profile) => - this.processSingleProfileForTenureLevelOfficer(profile, baseCurrentDate), - ), - ); - - results.forEach((result) => { - if (result.status === "fulfilled" && result.value) { - allData.push(result.value); - successCount++; - } else { - failCount++; - } - }); - } - - await AppDataSource.transaction(async (transactionalEntityManager) => { - await transactionalEntityManager.delete(TenureLevelOfficer, {}); - if (allData.length > 0) { - const entities = allData.map((data) => { - const entity = new TenureLevelOfficer(); - Object.assign(entity, data); - return entity; - }); - await transactionalEntityManager.save(TenureLevelOfficer, entities); - } - }); - - return new HttpSuccess({ - message: `อัปเดต tenure level officer สำเร็จ`, - total: profiles.length, - success: successCount, - failed: failCount, - }); - } - - private async processSingleProfileForTenureLevelOfficer( - profile: Pick & { - posLevel?: { posLevelName?: string } | null; - posType?: { posTypeName?: string } | null; - }, - baseCurrentDate: string, - ): Promise { - try { + for await (const x of profile) { + // Use leave date if available and valid, otherwise use current date let _currentDate = baseCurrentDate; - if (profile.isLeave && profile.leaveDate) { - _currentDate = Extension.toDateOnlyString(profile.leaveDate); + if (x?.isLeave && x.leaveDate) { + _currentDate = Extension.toDateOnlyString(x.leaveDate); } - const positionLevel = await AppDataSource.query("CALL GetProfileSalaryLevel(?, ?)", [ - profile.id, + x.id, _currentDate, ]); const _positionLevel = positionLevel.length > 0 ? positionLevel[0] : []; - const mapPositionLevel = _positionLevel.length > 1 ? _positionLevel.slice(1).map((curr: any, index: number) => ({ @@ -360,24 +185,13 @@ export class ProfileSalaryController extends Controller { positionType: _positionLevel[index]?.positionType, positionLevel: _positionLevel[index]?.positionLevel, positionCee: _positionLevel[index]?.positionCee, - year: - curr.Years !== null && curr.Years !== undefined - ? Math.floor(Number(curr.Years)) - : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, - day: - curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, })) : []; - const calDayDiff = mapPositionLevel .filter( (curr: any) => - curr.positionLevel === (profile.posLevel?.posLevelName ?? null) && - curr.positionType === (profile.posType?.posTypeName ?? null), + curr.positionLevel == (x.posLevel?.posLevelName ?? null) && + curr.positionType == (x.posType?.posTypeName ?? null), ) .reduce( (acc: any, curr: any) => { @@ -385,118 +199,45 @@ export class ProfileSalaryController extends Controller { acc.positionType = curr.positionType; acc.positionLevel = curr.positionLevel; acc.positionCee = curr.positionCee; - acc.year += curr.year; - acc.month += curr.month; - acc.day += curr.day; return acc; }, - { - days_diff: 0, - positionType: null, - positionLevel: null, - positionCee: null, - year: 0, - month: 0, - day: 0, - }, + { days_diff: 0, positionType: null, positionLevel: null, positionCee: null }, ); - - const normalized = normalizeDurationSumSimple( - calDayDiff.year, - calDayDiff.month, - calDayDiff.day, - ); - - return { - profileId: profile.id, + const { year, month, day } = calculateTenure(calDayDiff.days_diff); + const mapData: any = { + profileId: x.id, positionType: calDayDiff.positionType, positionLevel: calDayDiff.positionLevel, positionCee: calDayDiff.positionCee, days_diff: calDayDiff.days_diff, - Years: profile.posLevel == null ? 0 : normalized.years, - Months: profile.posLevel == null ? 0 : normalized.months, - Days: profile.posLevel == null ? 0 : normalized.days, + Years: x.posLevel == null ? 0 : year.toFixed(4), + Months: x.posLevel == null ? 0 : month.toFixed(4), + Days: x.posLevel == null ? 0 : day.toFixed(4), }; - } catch (error) { - return null; + data.push(mapData); } + await this.levelOfficerRepo.save(data); + + return new HttpSuccess(); } @Get("TenureLevelEmployee") public async cronjobTenureLevelEmployee() { + let data: any = []; + await this.levelEmployeeRepo.clear(); + const profile = await this.profileEmployeeRepo.find({ relations: ["posLevel", "posType"] }); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); const baseCurrentDate = CURRENT_DATE[0].today; - - const profiles = await this.profileEmployeeRepo.find({ - relations: ["posLevel", "posType"], - select: ["id", "isLeave", "leaveDate", "posLevel", "posType"], - where: { - posLevel: Not(IsNull()), - posType: Not(IsNull()), - }, - }); - - const BATCH_SIZE = 50; - let successCount = 0; - let failCount = 0; - const allData: CreateTenureLevelEmployee[] = []; - - for (let i = 0; i < profiles.length; i += BATCH_SIZE) { - const batch = profiles.slice(i, Math.min(i + BATCH_SIZE, profiles.length)); - const results = await Promise.allSettled( - batch.map((profile) => - this.processSingleProfileForTenureLevelEmployee(profile, baseCurrentDate), - ), - ); - - results.forEach((result) => { - if (result.status === "fulfilled" && result.value) { - allData.push(result.value); - successCount++; - } else { - failCount++; - } - }); - } - - await AppDataSource.transaction(async (transactionalEntityManager) => { - await transactionalEntityManager.delete(TenureLevelEmployee, {}); - if (allData.length > 0) { - const entities = allData.map((data) => { - const entity = new TenureLevelEmployee(); - Object.assign(entity, data); - return entity; - }); - await transactionalEntityManager.save(TenureLevelEmployee, entities); - } - }); - - return new HttpSuccess({ - message: `อัปเดต tenure level employee สำเร็จ`, - total: profiles.length, - success: successCount, - failed: failCount, - }); - } - - private async processSingleProfileForTenureLevelEmployee( - profile: Pick & { - posLevel?: { posLevelName?: string } | null; - posType?: { posTypeName?: string } | null; - }, - baseCurrentDate: string, - ): Promise { - try { + for await (const x of profile) { + // Use leave date if available and valid, otherwise use current date let _currentDate = baseCurrentDate; - if (profile.isLeave && profile.leaveDate) { - _currentDate = Extension.toDateOnlyString(profile.leaveDate); + if (x?.isLeave && x.leaveDate) { + _currentDate = Extension.toDateOnlyString(x.leaveDate); } - const positionLevel = await AppDataSource.query("CALL GetProfileEmployeeSalaryLevel(?, ?)", [ - profile.id, + x.id, _currentDate, ]); const _positionLevel = positionLevel.length > 0 ? positionLevel[0] : []; - const mapPositionLevel = _positionLevel.length > 1 ? _positionLevel.slice(1).map((curr: any, index: number) => ({ @@ -504,24 +245,13 @@ export class ProfileSalaryController extends Controller { positionType: _positionLevel[index]?.positionType, positionLevel: _positionLevel[index]?.positionLevel, positionCee: _positionLevel[index]?.positionCee, - year: - curr.Years !== null && curr.Years !== undefined - ? Math.floor(Number(curr.Years)) - : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, - day: - curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, })) : []; - const calDayDiff = mapPositionLevel .filter( (curr: any) => - curr.positionLevel === (profile.posLevel?.posLevelName ?? null) && - curr.positionType === (profile.posType?.posTypeName ?? null), + curr.positionLevel == (x.posLevel?.posLevelName ?? null) && + curr.positionType == (x.posType?.posTypeName ?? null), ) .reduce( (acc: any, curr: any) => { @@ -529,161 +259,97 @@ export class ProfileSalaryController extends Controller { acc.positionType = curr.positionType; acc.positionLevel = curr.positionLevel; acc.positionCee = curr.positionCee; - acc.year += curr.year; - acc.month += curr.month; - acc.day += curr.day; return acc; }, - { - days_diff: 0, - positionType: null, - positionLevel: null, - positionCee: null, - year: 0, - month: 0, - day: 0, - }, + { days_diff: 0, positionType: null, positionLevel: null, positionCee: null }, ); - - const normalized = normalizeDurationSumSimple( - calDayDiff.year, - calDayDiff.month, - calDayDiff.day, - ); - - return { - profileEmployeeId: profile.id, + const { year, month, day } = calculateTenure(calDayDiff.days_diff); + const mapData: any = { + profileEmployeeId: x.id, positionType: calDayDiff.positionType, positionLevel: calDayDiff.positionLevel, positionCee: calDayDiff.positionCee, days_diff: calDayDiff.days_diff, - Years: profile.posLevel == null ? 0 : normalized.years, - Months: profile.posLevel == null ? 0 : normalized.months, - Days: profile.posLevel == null ? 0 : normalized.days, + Years: x.posLevel == null ? 0 : year.toFixed(4), + Months: x.posLevel == null ? 0 : month.toFixed(4), + Days: x.posLevel == null ? 0 : day.toFixed(4), }; - } catch (error) { - return null; + data.push(mapData); } + await this.levelEmployeeRepo.save(data); + + return new HttpSuccess(); } @Get("TenurePositionExecutiveOfficer") public async cronjobTenureExecutivePositionOfficer() { + let data: any = []; + await this.positionExecutiveOfficerRepo.clear(); + const profile = await this.profileRepo.find(); + const orgRevision = await this.orgRevisionRepository.findOne({ + select: ["id"], + where: { + orgRevisionIsDraft: false, + orgRevisionIsCurrent: true, + }, + }); const CURRENT_DATE = await AppDataSource.query("SELECT CURRENT_DATE() as today"); const baseCurrentDate = CURRENT_DATE[0].today; - - const profiles = await this.profileRepo.find({ - select: ["id", "posExecutive", "isLeave", "leaveDate"], - where: { posExecutive: Not(IsNull()) }, - }); - - const BATCH_SIZE = 50; - let successCount = 0; - let failCount = 0; - const allData: CreateTenurePositionExecutiveOfficer[] = []; - - for (let i = 0; i < profiles.length; i += BATCH_SIZE) { - const batch = profiles.slice(i, Math.min(i + BATCH_SIZE, profiles.length)); - const results = await Promise.allSettled( - batch.map((profile) => - this.processSingleProfileForTenureExecutivePositionOfficer(profile, baseCurrentDate), - ), - ); - - results.forEach((result) => { - if (result.status === "fulfilled" && result.value) { - allData.push(result.value); - successCount++; - } else { - failCount++; - } - }); - } - - await AppDataSource.transaction(async (transactionalEntityManager) => { - await transactionalEntityManager.delete(TenurePositionExecutiveOfficer, {}); - if (allData.length > 0) { - const entities = allData.map((data) => { - const entity = new TenurePositionExecutiveOfficer(); - Object.assign(entity, data); - return entity; - }); - await transactionalEntityManager.save(TenurePositionExecutiveOfficer, entities); - } - }); - - return new HttpSuccess({ - message: `อัปเดต tenure executive position officer สำเร็จ`, - total: profiles.length, - success: successCount, - failed: failCount, - }); - } - - private async processSingleProfileForTenureExecutivePositionOfficer( - profile: Pick, - baseCurrentDate: string, - ): Promise { - try { + for await (const x of profile) { + // Use leave date if available and valid, otherwise use current date let _currentDate = baseCurrentDate; - if (profile.isLeave && profile.leaveDate) { - _currentDate = Extension.toDateOnlyString(profile.leaveDate); + if (x?.isLeave && x.leaveDate) { + _currentDate = Extension.toDateOnlyString(x.leaveDate); } - + const position = await this.positionRepo.findOne({ + where: { + positionIsSelected: true, + posMaster: { + orgRevisionId: orgRevision?.id, + current_holderId: x.id, + }, + }, + order: { createdAt: "DESC" }, + relations: { + posExecutive: true, + }, + }); const positionExecutive = await AppDataSource.query("CALL GetProfileSalaryExecutive(?, ?)", [ - profile.id, + x.id, _currentDate, ]); const _position = positionExecutive.length > 0 ? positionExecutive[0] : []; - const mapPosition = _position.length > 1 ? _position.slice(1).map((curr: any, index: number) => ({ days_diff: curr.days_diff, positionExecutive: _position[index]?.positionExecutive, - year: - curr.Years !== null && curr.Years !== undefined - ? Math.floor(Number(curr.Years)) - : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, - day: - curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, })) : []; - + const _posExecutiveName = position?.posExecutive?.posExecutiveName; const calDayDiff = mapPosition - .filter((curr: any) => curr.positionExecutive === profile.posExecutive) + .filter((curr: any) => _posExecutiveName && curr.positionExecutive == _posExecutiveName) .reduce( (acc: any, curr: any) => { acc.days_diff += Number(curr.days_diff) || 0; acc.positionExecutive = curr.positionExecutive; - acc.year += curr.year; - acc.month += curr.month; - acc.day += curr.day; return acc; }, - { days_diff: 0, positionExecutive: null, year: 0, month: 0, day: 0 }, + { days_diff: 0, positionExecutive: null }, ); - - const normalized = normalizeDurationSumSimple( - calDayDiff.year, - calDayDiff.month, - calDayDiff.day, - ); - - return { - profileId: profile.id, + const { year, month, day } = calculateTenure(calDayDiff.days_diff); + const mapData: any = { + profileId: x.id, positionExecutiveName: calDayDiff.positionExecutive, days_diff: calDayDiff.days_diff, - Years: normalized.years, - Months: normalized.months, - Days: normalized.days, + Years: year.toFixed(4), + Months: month.toFixed(4), + Days: day.toFixed(4), }; - } catch (error) { - return null; + data.push(mapData); } + await this.positionExecutiveOfficerRepo.save(data); + return new HttpSuccess(); } @Get("Registry") @@ -930,12 +596,8 @@ export class ProfileSalaryController extends Controller { ? _position.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, + year: curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, + month: curr.Months !== null && curr.Months !== undefined ? Math.floor(Number(curr.Months)) : 0, day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: _position[index]?.positionName, })) @@ -951,21 +613,19 @@ export class ProfileSalaryController extends Controller { existing.month += curr.month; existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days, year: curr.year, month: curr.month, day: curr.day }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + // Normalize the summed values (convert excess days to months, months to years) + while (existing.day >= 30) { + existing.month += Math.floor(existing.day / 30); + existing.day = existing.day % 30; + } + while (existing.month >= 12) { + existing.year += Math.floor(existing.month / 12); + existing.month = existing.month % 12; + } return acc; }, @@ -981,12 +641,8 @@ export class ProfileSalaryController extends Controller { ? _posLevel.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, + year: curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, + month: curr.Months !== null && curr.Months !== undefined ? Math.floor(Number(curr.Months)) : 0, day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: !_posLevel[index]?.positionType && _posLevel[index]?.positionCee @@ -1008,21 +664,19 @@ export class ProfileSalaryController extends Controller { existing.month += curr.month; existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days, year: curr.year, month: curr.month, day: curr.day }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + // Normalize the summed values (convert excess days to months, months to years) + while (existing.day >= 30) { + existing.month += Math.floor(existing.day / 30); + existing.day = existing.day % 30; + } + while (existing.month >= 12) { + existing.year += Math.floor(existing.month / 12); + existing.month = existing.month % 12; + } return acc; }, @@ -1039,12 +693,8 @@ export class ProfileSalaryController extends Controller { ? _posExecutive.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, + year: curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, + month: curr.Months !== null && curr.Months !== undefined ? Math.floor(Number(curr.Months)) : 0, day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: _posExecutive[index]?.positionExecutive, })) @@ -1060,21 +710,19 @@ export class ProfileSalaryController extends Controller { existing.month += curr.month; existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days, year: curr.year, month: curr.month, day: curr.day }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + // Normalize the summed values (convert excess days to months, months to years) + while (existing.day >= 30) { + existing.month += Math.floor(existing.day / 30); + existing.day = existing.day % 30; + } + while (existing.month >= 12) { + existing.year += Math.floor(existing.month / 12); + existing.month = existing.month % 12; + } return acc; }, @@ -1118,12 +766,8 @@ export class ProfileSalaryController extends Controller { ? _position.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, + year: curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, + month: curr.Months !== null && curr.Months !== undefined ? Math.floor(Number(curr.Months)) : 0, day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: _position[index]?.positionName, })) @@ -1139,21 +783,19 @@ export class ProfileSalaryController extends Controller { existing.month += curr.month; existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days, year: curr.year, month: curr.month, day: curr.day }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + // Normalize the summed values (convert excess days to months, months to years) + while (existing.day >= 30) { + existing.month += Math.floor(existing.day / 30); + existing.day = existing.day % 30; + } + while (existing.month >= 12) { + existing.year += Math.floor(existing.month / 12); + existing.month = existing.month % 12; + } return acc; }, @@ -1170,12 +812,8 @@ export class ProfileSalaryController extends Controller { ? _posLevel.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, + year: curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, + month: curr.Months !== null && curr.Months !== undefined ? Math.floor(Number(curr.Months)) : 0, day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: !_posLevel[index]?.positionType && _posLevel[index]?.positionCee @@ -1197,21 +835,19 @@ export class ProfileSalaryController extends Controller { existing.month += curr.month; existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days, year: curr.year, month: curr.month, day: curr.day }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + // Normalize the summed values (convert excess days to months, months to years) + while (existing.day >= 30) { + existing.month += Math.floor(existing.day / 30); + existing.day = existing.day % 30; + } + while (existing.month >= 12) { + existing.year += Math.floor(existing.month / 12); + existing.month = existing.month % 12; + } return acc; }, @@ -1228,12 +864,8 @@ export class ProfileSalaryController extends Controller { ? _posExecutive.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, + year: curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, + month: curr.Months !== null && curr.Months !== undefined ? Math.floor(Number(curr.Months)) : 0, day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: _posExecutive[index]?.positionExecutive, })) @@ -1249,21 +881,19 @@ export class ProfileSalaryController extends Controller { existing.month += curr.month; existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days, year: curr.year, month: curr.month, day: curr.day }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + // Normalize the summed values (convert excess days to months, months to years) + while (existing.day >= 30) { + existing.month += Math.floor(existing.day / 30); + existing.day = existing.day % 30; + } + while (existing.month >= 12) { + existing.year += Math.floor(existing.month / 12); + existing.month = existing.month % 12; + } return acc; }, @@ -1381,10 +1011,91 @@ export class ProfileSalaryController extends Controller { @Post("update") public async updateSalary(@Request() req: RequestWithUser, @Body() body: CreateProfileSalary) { - await new ExecuteSalaryReportService().executeOfficerSalaryUpdate([body], { - user: { sub: req.user.sub, name: req.user.name }, - req, + if (!body.profileId) { + throw new HttpError(HttpStatus.BAD_REQUEST, "กรุณากรอก profileId"); + } + + const profile = await this.profileRepo.findOneBy({ id: body.profileId }); + if (!profile) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); + } + await new permission().PermissionOrgUserUpdate(req, "SYS_REGISTRY_OFFICER", profile.id); + + const dest_item = await this.salaryRepo.findOne({ + where: { profileId: body.profileId }, + order: { order: "DESC" }, }); + const before = null; + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.commandId ?? "" }, + }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileRepo.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + order: dest_item == null ? 1 : dest_item.order + 1, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + + Object.assign(data, { ...body, ...meta }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + await this.salaryRepo.save(data, { data: req }); + setLogDataDiff(req, { before, after: data }); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + let _null: any = null; + profile.amount = body.amount ?? _null; + profile.amountSpecial = body.amountSpecial ?? _null; + profile.positionSalaryAmount = body.positionSalaryAmount ?? _null; + profile.mouthSalaryAmount = body.mouthSalaryAmount ?? _null; + await this.profileRepo.save(profile); return new HttpSuccess(); } diff --git a/src/controllers/ProfileSalaryEmployeeController.ts b/src/controllers/ProfileSalaryEmployeeController.ts index 6f6847f7..44b93a5d 100644 --- a/src/controllers/ProfileSalaryEmployeeController.ts +++ b/src/controllers/ProfileSalaryEmployeeController.ts @@ -27,8 +27,7 @@ import { Profile } from "../entities/Profile"; import { In, LessThan, IsNull, MoreThan } from "typeorm"; import permission from "../interfaces/permission"; import { setLogDataDiff } from "../interfaces/utils"; -import { ExecuteSalaryReportService } from "../services/ExecuteSalaryReportService"; -import { normalizeDurationSumSimple } from "../utils/tenure"; +import { calculateTenure } from "../utils/tenure"; import { Command } from "../entities/Command"; import { OrgRoot } from "../entities/OrgRoot"; import Extension from "../interfaces/extension"; @@ -162,14 +161,6 @@ export class ProfileSalaryEmployeeController extends Controller { _position.length > 1 ? _position.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, - // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, - day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: _position[index]?.positionName, })) : []; @@ -180,25 +171,15 @@ export class ProfileSalaryEmployeeController extends Controller { if (existing) { existing.days += curr.days; - existing.year += curr.year; - existing.month += curr.month; - existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -214,14 +195,6 @@ export class ProfileSalaryEmployeeController extends Controller { _posLevel.length > 1 ? _posLevel.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, - // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, - day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: !_posLevel[index]?.positionType && _posLevel[index]?.positionCee ? `ระดับ ${_posLevel[index]?.positionCee.trim()}` @@ -235,25 +208,15 @@ export class ProfileSalaryEmployeeController extends Controller { if (existing) { existing.days += curr.days; - existing.year += curr.year; - existing.month += curr.month; - existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -291,14 +254,6 @@ export class ProfileSalaryEmployeeController extends Controller { _position.length > 1 ? _position.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, - // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, - day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: _position[index]?.positionName, })) : []; @@ -309,25 +264,15 @@ export class ProfileSalaryEmployeeController extends Controller { if (existing) { existing.days += curr.days; - existing.year += curr.year; - existing.month += curr.month; - existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -343,14 +288,6 @@ export class ProfileSalaryEmployeeController extends Controller { _posLevel.length > 1 ? _posLevel.slice(1).map((curr: any, index: number) => ({ days: curr.days_diff ? Number(curr.days_diff) : 0, - // Use stored procedure's calculated values (calendar arithmetic) - year: - curr.Years !== null && curr.Years !== undefined ? Math.floor(Number(curr.Years)) : 0, - month: - curr.Months !== null && curr.Months !== undefined - ? Math.floor(Number(curr.Months)) - : 0, - day: curr.Days !== null && curr.Days !== undefined ? Math.floor(Number(curr.Days)) : 0, name: !_posLevel[index]?.positionType && _posLevel[index]?.positionCee ? `ระดับ ${_posLevel[index]?.positionCee.trim()}` @@ -364,25 +301,15 @@ export class ProfileSalaryEmployeeController extends Controller { if (existing) { existing.days += curr.days; - existing.year += curr.year; - existing.month += curr.month; - existing.day += curr.day; } else { - existing = { - name: curr.name, - days: curr.days, - year: curr.year, - month: curr.month, - day: curr.day, - }; + existing = { name: curr.name, days: curr.days }; acc.push(existing); } - // Normalize the summed values using calendar arithmetic - const normalized = normalizeDurationSumSimple(existing.year, existing.month, existing.day); - existing.year = normalized.years; - existing.month = normalized.months; - existing.day = normalized.days; + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -508,10 +435,94 @@ export class ProfileSalaryEmployeeController extends Controller { @Request() req: RequestWithUser, @Body() body: CreateProfileSalaryEmployee, ) { - await new ExecuteSalaryReportService().executeEmployeeSalaryUpdate([body], { - user: { sub: req.user.sub, name: req.user.name }, - req, + if (!body.profileEmployeeId) { + throw new HttpError(HttpStatus.BAD_REQUEST, "กรุณากรอก profileEmployeeId"); + } + + const profile = await this.profileRepo.findOneBy({ id: body.profileEmployeeId }); + if (!profile) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); + } + await new permission().PermissionOrgUserUpdate(req, "SYS_REGISTRY_EMP", profile.id); + + const dest_item = await this.salaryRepo.findOne({ + where: { profileEmployeeId: body.profileEmployeeId }, + order: { order: "DESC" }, }); + const before = null; + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + const _command = await this.commandRepository.findOne({ + where: { id: body.commandId ?? "" }, + }); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + } else { + let _profileAdmin = await this.profileGovementRepo.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? + ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + } + } + const data = new ProfileSalary(); + data.posNumCodeSit = _posNumCodeSit; + data.posNumCodeSitAbb = _posNumCodeSitAbb; + const meta = { + order: dest_item == null ? 1 : dest_item.order + 1, + createdUserId: req.user.sub, + createdFullName: req.user.name, + lastUpdateUserId: req.user.sub, + lastUpdateFullName: req.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + + Object.assign(data, { ...body, ...meta }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...data, id: undefined }); + + await this.salaryRepo.save(data, { data: req }); + setLogDataDiff(req, { before, after: data }); + history.profileSalaryId = data.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + let _null: any = null; + profile.amount = body.amount ?? _null; + profile.amountSpecial = body.amountSpecial ?? _null; + profile.positionSalaryAmount = body.positionSalaryAmount ?? _null; + profile.mouthSalaryAmount = body.mouthSalaryAmount ?? _null; + profile.salaryLevel = body.salaryLevel ?? _null; + profile.group = body.group ?? _null; + await this.profileRepo.save(profile); return new HttpSuccess(); } diff --git a/src/controllers/ProfileSalaryTempController.ts b/src/controllers/ProfileSalaryTempController.ts index c166d22d..35279fbc 100644 --- a/src/controllers/ProfileSalaryTempController.ts +++ b/src/controllers/ProfileSalaryTempController.ts @@ -1791,56 +1791,4 @@ export class ProfileSalaryTempController extends Controller { await this.salaryRepo.save(sortLevel); return new HttpSuccess(); } - - /** - * API เรียงลำดับทะเบียนประวัติและเงินเดือนที่กำลังแก้ไขตามวันที่คำสั่งมีผล - * @summary API เรียงลำดับทะเบียนประวัติและเงินเดือนที่กำลังแก้ไขตามวันที่คำสั่งมีผล - */ - @Put("sort-order") - public async reorderSalaryByCommandDate( - @Request() req: RequestWithUser, - @Body() body: { profileId: string; type: "OFFICER" | "EMPLOYEE" }, - ) { - const isOfficer = body.type.toUpperCase() === "OFFICER"; - - // Step 1: SELECT ข้อมูลตาม profileId และ type - const salaryTemps = await this.salaryRepo.find({ - where: isOfficer ? { profileId: body.profileId } : { profileEmployeeId: body.profileId }, - }); - - if (salaryTemps.length === 0) { - throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งเงินเดือน"); - } - - // Step 2: เรียงลำดับตาม commandDateAffect (ASC) - // ถ้า commandDateAffect เท่ากัน ให้ใช้ order เดิมเป็น secondary sort - const sortedSalary = salaryTemps.sort((a, b) => { - // ถ้า commandDateAffect เป็น null ให้ถือว่าเป็นค่าน้อยสุด - const dateA = a.commandDateAffect ? new Date(a.commandDateAffect).getTime() : 0; - const dateB = b.commandDateAffect ? new Date(b.commandDateAffect).getTime() : 0; - - if (dateA !== dateB) { - return dateA - dateB; // เรียงตามวันที่คำสั่งมีผล - } - - // ถ้าวันที่เท่ากัน ให้ใช้ order เดิม - const orderA = a.order ?? 0; - const orderB = b.order ?? 0; - return orderA - orderB; - }); - - // Step 3: UPDATE ฟิลด์ order ตามการเรียงใหม่ - const dateNow = new Date(); - const updatedSalary = sortedSalary.map((item, index) => ({ - ...item, - order: index + 1, - lastUpdateUserId: req.user.sub, - lastUpdateFullName: req.user.name, - lastUpdatedAt: dateNow, - })); - - await this.salaryRepo.save(updatedSalary); - - return new HttpSuccess(); - } } diff --git a/src/controllers/ScriptProfileOrgController.ts b/src/controllers/ScriptProfileOrgController.ts index 0494be98..aa6908e2 100644 --- a/src/controllers/ScriptProfileOrgController.ts +++ b/src/controllers/ScriptProfileOrgController.ts @@ -38,10 +38,6 @@ export class ScriptProfileOrgController extends Controller { process.env.CRONJOB_UPDATE_WINDOW_HOURS || "24", 10, ); - private readonly LEAVE_SERVICE_BATCH_SIZE = parseInt( - process.env.LEAVE_SERVICE_BATCH_SIZE || "50", - 10, - ); /** * Script to update profile's organizational structure in leave service and sync to Keycloak @@ -49,7 +45,7 @@ export class ScriptProfileOrgController extends Controller { * @summary Update org structure for profiles updated within a certain time window and sync to Keycloak */ @Post("update-org") - public async cronjobUpdateOrg(@Request() _request: RequestWithUser) { + public async cronjobUpdateOrg(@Request() request: RequestWithUser) { // Idempotency check - prevent concurrent runs if (this.isRunning) { console.log("cronjobUpdateOrg: Job already running, skipping this execution"); @@ -180,6 +176,21 @@ export class ScriptProfileOrgController extends Controller { }); } + // Update profile's org structure in leave service by calling API + console.log("cronjobUpdateOrg: Calling leave service API", { + payloadCount: payloads.length, + }); + + await axios.put(`${process.env.API_URL}/leave-beginning/schedule/update-dna`, payloads, { + headers: { + "Content-Type": "application/json", + api_key: process.env.API_KEY, + }, + timeout: 30000, // 30 second timeout + }); + + console.log("cronjobUpdateOrg: Leave service API call successful"); + // Group profile IDs by type for proper syncing const profileIdsByType = this.groupProfileIdsByType(payloads); @@ -245,90 +256,16 @@ export class ScriptProfileOrgController extends Controller { syncResults.failed += typeResult.failed; } - // Update profile's org structure in leave service by calling API - console.log("cronjobUpdateOrg: Calling leave service API with chunking", { - payloadCount: payloads.length, - batchSize: this.LEAVE_SERVICE_BATCH_SIZE, - expectedBatches: Math.ceil(payloads.length / this.LEAVE_SERVICE_BATCH_SIZE), - }); - - const chunks = this.chunkArray(payloads, this.LEAVE_SERVICE_BATCH_SIZE); - const leaveServiceResults = { - total: payloads.length, - success: 0, - failed: 0, - batchesCompleted: 0, - batchesFailed: 0, - }; - - for (let i = 0; i < chunks.length; i++) { - const chunk = chunks[i]; - const batchNumber = i + 1; - - console.log( - `cronjobUpdateOrg: Processing leave service batch ${batchNumber}/${chunks.length}`, - { - batchSize: chunk.length, - batchRange: `${i * this.LEAVE_SERVICE_BATCH_SIZE + 1}-${Math.min( - (batchNumber + 1) * this.LEAVE_SERVICE_BATCH_SIZE, - payloads.length, - )}`, - }, - ); - - try { - await axios.put( - `${process.env.API_URL}/leave-beginning/schedule/update-dna`, - chunk, - { - headers: { - "Content-Type": "application/json", - api_key: process.env.API_KEY, - }, - timeout: 120000, // 120 second timeout per chunk - }, - ); - - leaveServiceResults.success += chunk.length; - leaveServiceResults.batchesCompleted++; - - console.log(`cronjobUpdateOrg: Leave service batch ${batchNumber}/${chunks.length} completed`, { - success: chunk.length, - }); - } catch (error: any) { - leaveServiceResults.failed += chunk.length; - leaveServiceResults.batchesFailed++; - - console.error( - `cronjobUpdateOrg: Leave service batch ${batchNumber}/${chunks.length} failed`, - { - error: error.message, - batchSize: chunk.length, - responseStatus: error.response?.status, - responseData: error.response?.data, - }, - ); - - // Continue processing remaining batches - } - } - - console.log("cronjobUpdateOrg: Leave service API call completed", { - ...leaveServiceResults, - }); - const duration = Date.now() - startTime; console.log("cronjobUpdateOrg: Job completed", { duration: `${duration}ms`, processed: payloads.length, - leaveServiceResults, syncResults, }); return new HttpSuccess({ message: "Update org completed", processed: payloads.length, - leaveServiceResults, syncResults, duration: `${duration}ms`, }); diff --git a/src/controllers/SocketController.ts b/src/controllers/SocketController.ts index 13bdde1d..c975336f 100644 --- a/src/controllers/SocketController.ts +++ b/src/controllers/SocketController.ts @@ -36,26 +36,13 @@ export class SocketController extends Controller { }, @Request() req: RequestWithUser, ) { - const toArray = (value?: string | string[]) => { - if (Array.isArray(value)) return value.filter(Boolean); - if (typeof value === "string" && value.trim()) return [value]; - return [] as string[]; - }; - - const targetUserIds = toArray(payload.targetUserId); - const targetRoles = toArray(payload.roles); - - // If caller provides explicit user targets, do not combine with role targeting. - // This prevents accidental broad notifications when roles include common roles. - const recipients = - targetUserIds.length > 0 - ? { userId: targetUserIds, roles: [] as string[] } - : { userId: [req.user.sub], roles: targetRoles }; - sendWebSocket( "socket-notification", { success: !payload.error, message: payload.message }, - recipients, + { + roles: payload.roles || req.user.role || [], + userId: payload.targetUserId || req.user.sub || [], + }, ); } } diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 4902ce0f..2120dcff 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -580,27 +580,18 @@ export class KeycloakController extends Controller { new Brackets((qb) => { qb.orWhere( body.keyword != null && body.keyword != "" - ? `profile.citizenId LIKE :keyword` + ? `profile.citizenId like '%${body.keyword}%'` : "1=1", - { - keyword: `%${body.keyword}%`, - } ) .orWhere( body.keyword != null && body.keyword != "" - ? `profile.email LIKE :keyword` + ? `profile.email like '%${body.keyword}%'` : "1=1", - { - keyword: `%${body.keyword}%`, - } ) .orWhere( body.keyword != null && body.keyword != "" - ? `CONCAT(profile.prefix, profile.firstName," ",profile.lastName) LIKE :keyword` + ? `CONCAT(profile.prefix, profile.firstName," ",profile.lastName) like '%${body.keyword}%'` : "1=1", - { - keyword: `%${body.keyword}%`, - } ); }), ) @@ -634,27 +625,18 @@ export class KeycloakController extends Controller { new Brackets((qb) => { qb.orWhere( body.keyword != null && body.keyword != "" - ? `profileEmployee.citizenId LIKE :keyword` + ? `profileEmployee.citizenId like '%${body.keyword}%'` : "1=1", - { - keyword: `%${body.keyword}%`, - } ) .orWhere( body.keyword != null && body.keyword != "" - ? `profileEmployee.email LIKE :keyword` + ? `profileEmployee.email like '%${body.keyword}%'` : "1=1", - { - keyword: `%${body.keyword}%`, - } ) .orWhere( body.keyword != null && body.keyword != "" - ? `CONCAT(profileEmployee.prefix, profileEmployee.firstName," ",profileEmployee.lastName) LIKE :keyword` + ? `CONCAT(profileEmployee.prefix, profileEmployee.firstName," ",profileEmployee.lastName) like '%${body.keyword}%'` : "1=1", - { - keyword: `%${body.keyword}%`, - } ); }), ) diff --git a/src/controllers/WorkflowController.ts b/src/controllers/WorkflowController.ts index d8aa0b77..ae5c8859 100644 --- a/src/controllers/WorkflowController.ts +++ b/src/controllers/WorkflowController.ts @@ -865,7 +865,6 @@ export class WorkflowController extends Controller { type?: string | null; sortBy?: string | null; descending?: boolean; - isAllDirector?: boolean; }, ) { const userKeycloak = body.keycloakId ?? request.user.sub; @@ -954,46 +953,38 @@ export class WorkflowController extends Controller { let mainConditions: any[] = []; if (type.trim().toUpperCase() === "OPERATE" || body.type === "employee") { - - if (body.isAllDirector === true) { - mainConditions = [ - { ...baseCondition, orgRootId: In(roodIds) } - ]; - } - else { - mainConditions = [ - { ...baseCondition, orgRootId: In(roodIds), orgChild1Id: IsNull() }, - { - ...baseCondition, - orgRootId: In(roodIds), - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: IsNull(), - }, - { - ...baseCondition, - orgRootId: In(roodIds), - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: posMasterUser.orgChild2Id, - orgChild3Id: IsNull(), - }, - { - ...baseCondition, - orgRootId: In(roodIds), - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: posMasterUser.orgChild2Id, - orgChild3Id: posMasterUser.orgChild3Id, - orgChild4Id: IsNull(), - }, - { - ...baseCondition, - orgRootId: In(roodIds), - orgChild1Id: posMasterUser.orgChild1Id, - orgChild2Id: posMasterUser.orgChild2Id, - orgChild3Id: posMasterUser.orgChild3Id, - orgChild4Id: posMasterUser.orgChild4Id, - }, - ]; - } + mainConditions = [ + { ...baseCondition, orgRootId: In(roodIds), orgChild1Id: IsNull() }, + { + ...baseCondition, + orgRootId: In(roodIds), + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: IsNull(), + }, + { + ...baseCondition, + orgRootId: In(roodIds), + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: posMasterUser.orgChild2Id, + orgChild3Id: IsNull(), + }, + { + ...baseCondition, + orgRootId: In(roodIds), + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: posMasterUser.orgChild2Id, + orgChild3Id: posMasterUser.orgChild3Id, + orgChild4Id: IsNull(), + }, + { + ...baseCondition, + orgRootId: In(roodIds), + orgChild1Id: posMasterUser.orgChild1Id, + orgChild2Id: posMasterUser.orgChild2Id, + orgChild3Id: posMasterUser.orgChild3Id, + orgChild4Id: posMasterUser.orgChild4Id, + }, + ]; } else if (isLowLevel) { mainConditions = [ { diff --git a/src/entities/Command.ts b/src/entities/Command.ts index e6af1be8..c6b26626 100644 --- a/src/entities/Command.ts +++ b/src/entities/Command.ts @@ -34,14 +34,6 @@ export class Command extends EntityBase { }) issue: string; - @Column({ - nullable: true, - comment: "ชื่อย่อหน่วยงานที่ออกคำสั่ง", - length: 16, - default: null, - }) - shortName: string; - @Column({ nullable: true, comment: "เลขที่คำสั่ง", diff --git a/src/entities/OrgChild1.ts b/src/entities/OrgChild1.ts index ef2a9be6..e322df8b 100644 --- a/src/entities/OrgChild1.ts +++ b/src/entities/OrgChild1.ts @@ -174,46 +174,6 @@ export class OrgChild1 extends EntityBase { }) JOB_CODE: string; - @Column({ - nullable: true, - length: 3, - comment: "ROOT_CODE", - default: null, - }) - ROOT_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD1_CODE", - default: null, - }) - CHILD1_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD2_CODE", - default: null, - }) - CHILD2_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD3_CODE", - default: null, - }) - CHILD3_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD4_CODE", - default: null, - }) - CHILD4_CODE: string; - @ManyToOne(() => OrgRoot, (orgRoot) => orgRoot.orgChild1s) @JoinColumn({ name: "orgRootId" }) orgRoot: OrgRoot; @@ -268,21 +228,6 @@ export class CreateOrgChild1 { @Column() JOB_CODE?: string | null; - @Column() - ROOT_CODE?: string | null; - - @Column() - CHILD1_CODE?: string | null; - - @Column() - CHILD2_CODE?: string | null; - - @Column() - CHILD3_CODE?: string | null; - - @Column() - CHILD4_CODE?: string | null; - @Column() orgChild1PhoneEx?: string; @@ -338,21 +283,6 @@ export class UpdateOrgChild1 { @Column() JOB_CODE?: string | null; - @Column() - ROOT_CODE?: string | null; - - @Column() - CHILD1_CODE?: string | null; - - @Column() - CHILD2_CODE?: string | null; - - @Column() - CHILD3_CODE?: string | null; - - @Column() - CHILD4_CODE?: string | null; - @Column() orgChild1PhoneEx?: string; diff --git a/src/entities/OrgChild2.ts b/src/entities/OrgChild2.ts index 50f40769..b2ed6a0c 100644 --- a/src/entities/OrgChild2.ts +++ b/src/entities/OrgChild2.ts @@ -146,46 +146,6 @@ export class OrgChild2 extends EntityBase { }) JOB_CODE: string; - @Column({ - nullable: true, - length: 3, - comment: "ROOT_CODE", - default: null, - }) - ROOT_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD1_CODE", - default: null, - }) - CHILD1_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD2_CODE", - default: null, - }) - CHILD2_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD3_CODE", - default: null, - }) - CHILD3_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD4_CODE", - default: null, - }) - CHILD4_CODE: string; - @Column({ nullable: true, length: 40, @@ -262,21 +222,6 @@ export class CreateOrgChild2 { @Column() JOB_CODE?: string; - @Column() - ROOT_CODE?: string; - - @Column() - CHILD1_CODE?: string; - - @Column() - CHILD2_CODE?: string; - - @Column() - CHILD3_CODE?: string; - - @Column() - CHILD4_CODE?: string; - @Column() orgChild2PhoneEx?: string; @@ -324,21 +269,6 @@ export class UpdateOrgChild2 { @Column() JOB_CODE?: string | null; - @Column() - ROOT_CODE?: string | null; - - @Column() - CHILD1_CODE?: string | null; - - @Column() - CHILD2_CODE?: string | null; - - @Column() - CHILD3_CODE?: string | null; - - @Column() - CHILD4_CODE?: string | null; - @Column() orgChild2PhoneEx?: string; diff --git a/src/entities/OrgChild3.ts b/src/entities/OrgChild3.ts index 72a29336..4c376fb6 100644 --- a/src/entities/OrgChild3.ts +++ b/src/entities/OrgChild3.ts @@ -153,46 +153,6 @@ export class OrgChild3 extends EntityBase { }) JOB_CODE: string; - @Column({ - nullable: true, - length: 3, - comment: "ROOT_CODE", - default: null, - }) - ROOT_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD1_CODE", - default: null, - }) - CHILD1_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD2_CODE", - default: null, - }) - CHILD2_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD3_CODE", - default: null, - }) - CHILD3_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD4_CODE", - default: null, - }) - CHILD4_CODE: string; - @Column({ nullable: true, length: 40, @@ -270,21 +230,6 @@ export class CreateOrgChild3 { @Column() JOB_CODE?: string; - @Column() - ROOT_CODE?: string; - - @Column() - CHILD1_CODE?: string; - - @Column() - CHILD2_CODE?: string; - - @Column() - CHILD3_CODE?: string; - - @Column() - CHILD4_CODE?: string; - @Column() orgChild3PhoneEx?: string; @@ -334,21 +279,6 @@ export class UpdateOrgChild3 { @Column() JOB_CODE?: string | null; - @Column() - ROOT_CODE?: string | null; - - @Column() - CHILD1_CODE?: string | null; - - @Column() - CHILD2_CODE?: string | null; - - @Column() - CHILD3_CODE?: string | null; - - @Column() - CHILD4_CODE?: string | null; - @Column() orgChild3PhoneEx?: string; diff --git a/src/entities/OrgChild4.ts b/src/entities/OrgChild4.ts index 4f053b00..314c9393 100644 --- a/src/entities/OrgChild4.ts +++ b/src/entities/OrgChild4.ts @@ -158,46 +158,6 @@ export class OrgChild4 extends EntityBase { }) JOB_CODE: string; - @Column({ - nullable: true, - length: 3, - comment: "ROOT_CODE", - default: null, - }) - ROOT_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD1_CODE", - default: null, - }) - CHILD1_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD2_CODE", - default: null, - }) - CHILD2_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD3_CODE", - default: null, - }) - CHILD3_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD4_CODE", - default: null, - }) - CHILD4_CODE: string; - @Column({ nullable: true, length: 40, @@ -276,21 +236,6 @@ export class CreateOrgChild4 { @Column() JOB_CODE?: string; - @Column() - ROOT_CODE?: string; - - @Column() - CHILD1_CODE?: string; - - @Column() - CHILD2_CODE?: string; - - @Column() - CHILD3_CODE?: string; - - @Column() - CHILD4_CODE?: string; - @Column() orgChild4PhoneEx?: string; @@ -338,21 +283,6 @@ export class UpdateOrgChild4 { @Column() JOB_CODE?: string | null; - @Column() - ROOT_CODE?: string | null; - - @Column() - CHILD1_CODE?: string | null; - - @Column() - CHILD2_CODE?: string | null; - - @Column() - CHILD3_CODE?: string | null; - - @Column() - CHILD4_CODE?: string | null; - @Column() orgChild4PhoneEx?: string; diff --git a/src/entities/OrgRoot.ts b/src/entities/OrgRoot.ts index 02e22bb8..3aefad6e 100644 --- a/src/entities/OrgRoot.ts +++ b/src/entities/OrgRoot.ts @@ -166,46 +166,6 @@ export class OrgRoot extends EntityBase { }) JOB_CODE: string; - @Column({ - nullable: true, - length: 3, - comment: "ROOT_CODE", - default: null, - }) - ROOT_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD1_CODE", - default: null, - }) - CHILD1_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD2_CODE", - default: null, - }) - CHILD2_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD3_CODE", - default: null, - }) - CHILD3_CODE: string; - - @Column({ - nullable: true, - length: 3, - comment: "CHILD4_CODE", - default: null, - }) - CHILD4_CODE: string; - @ManyToOne(() => OrgRevision, (orgRevision) => orgRevision.orgRoots) @JoinColumn({ name: "orgRevisionId" }) orgRevision: OrgRevision; @@ -266,21 +226,6 @@ export class CreateOrgRoot { @Column() JOB_CODE?: string; - @Column() - ROOT_CODE?: string; - - @Column() - CHILD1_CODE?: string; - - @Column() - CHILD2_CODE?: string; - - @Column() - CHILD3_CODE?: string; - - @Column() - CHILD4_CODE?: string; - @Column() orgRootPhoneEx?: string; @@ -336,21 +281,6 @@ export class UpdateOrgRoot { @Column() JOB_CODE?: string | null; - @Column() - ROOT_CODE?: string | null; - - @Column() - CHILD1_CODE?: string | null; - - @Column() - CHILD2_CODE?: string | null; - - @Column() - CHILD3_CODE?: string | null; - - @Column() - CHILD4_CODE?: string | null; - @Column() orgRootPhoneEx?: string; diff --git a/src/entities/PosMasterEmployeeHistory.ts b/src/entities/PosMasterEmployeeHistory.ts index e0aa7853..b0418644 100644 --- a/src/entities/PosMasterEmployeeHistory.ts +++ b/src/entities/PosMasterEmployeeHistory.ts @@ -99,51 +99,51 @@ export class PosMasterEmployeeHistory extends EntityBase { }) ancestorDNA: string; - @Column({ - nullable: true, - length: 40, - comment: "คีย์นอก(FK)ของตาราง profileEmployee", - default: null, - }) - profileEmployeeId: string; + // @Column({ + // nullable: true, + // length: 40, + // comment: "คีย์นอก(FK)ของตาราง profile", + // default: null, + // }) + // profileId: string; - @Column({ - nullable: true, - length: 40, - comment: "dna ของตาราง orgRoot", - default: null, - }) - rootDnaId: string; + // @Column({ + // nullable: true, + // length: 40, + // comment: "dna ของตาราง orgRoot", + // default: null, + // }) + // rootDnaId: string; - @Column({ - nullable: true, - length: 40, - comment: "dna ของตาราง orgChild1", - default: null, - }) - child1DnaId: string; + // @Column({ + // nullable: true, + // length: 40, + // comment: "dna ของตาราง orgChild1", + // default: null, + // }) + // child1DnaId: string; - @Column({ - nullable: true, - length: 40, - comment: "dna ของตาราง orgChild2", - default: null, - }) - child2DnaId: string; + // @Column({ + // nullable: true, + // length: 40, + // comment: "dna ของตาราง orgChild2", + // default: null, + // }) + // child2DnaId: string; - @Column({ - nullable: true, - length: 40, - comment: "dna ของตาราง orgChild3", - default: null, - }) - child3DnaId: string; + // @Column({ + // nullable: true, + // length: 40, + // comment: "dna ของตาราง orgChild3", + // default: null, + // }) + // child3DnaId: string; - @Column({ - nullable: true, - length: 40, - comment: "dna ของตาราง orgChild4", - default: null, - }) - child4DnaId: string; + // @Column({ + // nullable: true, + // length: 40, + // comment: "dna ของตาราง orgChild4", + // default: null, + // }) + // child4DnaId: string; } diff --git a/src/entities/ProfileChangeName.ts b/src/entities/ProfileChangeName.ts index 61e64c24..b02117fc 100644 --- a/src/entities/ProfileChangeName.ts +++ b/src/entities/ProfileChangeName.ts @@ -31,14 +31,6 @@ export class ProfileChangeName extends EntityBase { }) prefix: string; - @Column({ - nullable: true, - comment: "ยศ", - length: 40, - default: null, - }) - rank: string; - @Column({ nullable: true, length: 100, @@ -111,7 +103,6 @@ export class CreateProfileChangeName { profileId: string | null; prefixId: string | null; prefix: string | null; - rank: string | null; firstName: string | null; lastName: string | null; status: string | null; @@ -122,7 +113,6 @@ export class CreateProfileChangeNameEmployee { profileEmployeeId: string | null; prefixId: string | null; prefix: string | null; - rank: string | null; firstName: string | null; lastName: string | null; status: string | null; @@ -132,7 +122,6 @@ export class CreateProfileChangeNameEmployee { export type UpdateProfileChangeName = { prefixId?: string | null; prefix?: string | null; - rank?: string | null; firstName?: string | null; lastName?: string | null; status?: string | null; diff --git a/src/entities/ProfileChangeNameHistory.ts b/src/entities/ProfileChangeNameHistory.ts index 5d58a9f7..5d296161 100644 --- a/src/entities/ProfileChangeNameHistory.ts +++ b/src/entities/ProfileChangeNameHistory.ts @@ -20,14 +20,6 @@ export class ProfileChangeNameHistory extends EntityBase { }) prefix: string; - @Column({ - nullable: true, - comment: "ยศ", - length: 40, - default: null, - }) - rank: string; - @Column({ nullable: true, length: 100, @@ -87,7 +79,6 @@ export class CreateProfileChangeNameHistory { profileChangeNameId: string | null; prefixId: string | null; prefix: string | null; - rank: string | null; firstName: string | null; lastName: string | null; status: string | null; @@ -98,7 +89,6 @@ export type UpdateProfileChangeNameHistory = { profileChangeNameId?: string | null; prefixId?: string | null; prefix?: string | null; - rank?: string | null; firstName?: string | null; lastName?: string | null; status?: string | null; diff --git a/src/entities/ProfileDiscipline.ts b/src/entities/ProfileDiscipline.ts index 7a5f463d..0065528b 100644 --- a/src/entities/ProfileDiscipline.ts +++ b/src/entities/ProfileDiscipline.ts @@ -61,14 +61,6 @@ export class ProfileDiscipline extends EntityBase { default: null, }) refCommandNo: string; - - @Column({ - nullable: true, - length: 40, - comment: "คีย์นอก(FK)ของตาราง command", - default: null, - }) - refCommandId: string; @Column({ nullable: true, diff --git a/src/entities/ProfileDisciplineHistory.ts b/src/entities/ProfileDisciplineHistory.ts index d99a7263..e7dfee58 100644 --- a/src/entities/ProfileDisciplineHistory.ts +++ b/src/entities/ProfileDisciplineHistory.ts @@ -51,14 +51,6 @@ export class ProfileDisciplineHistory extends EntityBase { }) refCommandNo: string; - @Column({ - nullable: true, - length: 40, - comment: "คีย์นอก(FK)ของตาราง command", - default: null, - }) - refCommandId: string; - @Column({ nullable: true, comment: "ล้างมลทิน", diff --git a/src/entities/ProfileEmployee.ts b/src/entities/ProfileEmployee.ts index 45f5e159..11c2159b 100644 --- a/src/entities/ProfileEmployee.ts +++ b/src/entities/ProfileEmployee.ts @@ -1014,7 +1014,7 @@ export class UpdateInformationProfileEmployee { } export type UpdateProfileEmployee = { - prefix?: string | null; + prefix: string; rank?: string | null; firstName: string; lastName: string; diff --git a/src/entities/TenureLevelEmployee.ts b/src/entities/TenureLevelEmployee.ts index 5654e306..36ae0176 100644 --- a/src/entities/TenureLevelEmployee.ts +++ b/src/entities/TenureLevelEmployee.ts @@ -74,7 +74,7 @@ export class TenureLevelEmployee extends EntityBase { positionLevel: string; } -export class CreateTenureLevelEmployee { +export class CreateTenureLevelOfficer { profileEmployeeId: string; positionCee: string | null; days_diff: number | null; diff --git a/src/interfaces/permission.ts b/src/interfaces/permission.ts index 5d22d274..4c3063de 100644 --- a/src/interfaces/permission.ts +++ b/src/interfaces/permission.ts @@ -39,7 +39,7 @@ class CheckAuth { } }); } - public async PermissionOrg(req: RequestWithUser, system: string, action: string, isDirector?: boolean) { + public async PermissionOrg(req: RequestWithUser, system: string, action: string) { if ( req.headers.hasOwnProperty("api_key") && req.headers["api_key"] && @@ -56,7 +56,7 @@ class CheckAuth { return await new CallAPI() .GetData(req, `/org/permission/org/${system}/${action}`) .then(async (x) => { - let privilege = isDirector && isDirector === true ? "CHILD" : x.privilege; + let privilege = x.privilege; let data: any = { root: [null], @@ -288,9 +288,6 @@ class CheckAuth { public async PermissionOrgList(req: RequestWithUser, system: string) { return await this.PermissionOrg(req, system, "LIST"); } - public async PermissionIsDirectorOrgList(req: RequestWithUser, system: string, isDirector: boolean) { - return await this.PermissionOrg(req, system, "LIST", isDirector); - } public async PermissionOrgUpdate(req: RequestWithUser, system: string) { return await this.PermissionOrg(req, system, "UPDATE"); } diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index 27b35585..347f28af 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -4,7 +4,7 @@ import { PosMaster } from "../entities/PosMaster"; import { Position } from "../entities/Position"; import { EmployeePosMaster } from "../entities/EmployeePosMaster"; import { EmployeePosition } from "../entities/EmployeePosition"; -import { EntityManager, In, IsNull, MoreThan, Not } from "typeorm"; +import { In, IsNull, MoreThan, Not } from "typeorm"; import { RequestWithUser } from "../middlewares/user"; import { Command } from "../entities/Command"; import { ProfileSalary } from "../entities/ProfileSalary"; @@ -254,23 +254,14 @@ export function calculateRetireYear(birthDate: Date) { return yy + 61; } -export async function removeProfileInOrganize( - profileId: string, - type: string, - manager?: EntityManager, -) { - // ถ้าส่ง manager เข้ามา → ทุก query/update อยู่ใน transaction ของ caller (all-or-nothing) - // ถ้าไม่ส่ง → ใช้ global DataSource เหมือนเดิม (backward compatible) - const ds = manager ?? AppDataSource; - const currentRevision = await ds - .getRepository(OrgRevision) +export async function removeProfileInOrganize(profileId: string, type: string) { + const currentRevision = await AppDataSource.getRepository(OrgRevision) .createQueryBuilder("orgRevision") .where("orgRevision.orgRevisionIsDraft = false") .andWhere("orgRevision.orgRevisionIsCurrent = true") .getOne(); - const draftRevision = await ds - .getRepository(OrgRevision) + const draftRevision = await AppDataSource.getRepository(OrgRevision) .createQueryBuilder("orgRevision") .where("orgRevision.orgRevisionIsDraft = true") .andWhere("orgRevision.orgRevisionIsCurrent = false") @@ -280,41 +271,36 @@ export async function removeProfileInOrganize( return; } if (type === "OFFICER") { - const findProfileInposMaster = await ds - .getRepository(PosMaster) + const findProfileInposMaster = await AppDataSource.getRepository(PosMaster) .createQueryBuilder("posMaster") .where("posMaster.orgRevisionId = :orgRevisionId", { orgRevisionId: currentRevision?.id }) .andWhere("posMaster.current_holderId = :profileId", { profileId }) .getOne(); - await ds - .getRepository(PosMaster) + await AppDataSource.getRepository(PosMaster) .createQueryBuilder() .update(PosMaster) - .set({ current_holderId: null, isSit: false }) + .set({ current_holderId: null }) .where("id = :id", { id: findProfileInposMaster?.id }) .execute(); - const findProfileInposMasterDraft = await ds - .getRepository(PosMaster) + const findProfileInposMasterDraft = await AppDataSource.getRepository(PosMaster) .createQueryBuilder("posMaster") .where("posMaster.orgRevisionId = :orgRevisionId", { orgRevisionId: draftRevision?.id }) .andWhere("posMaster.next_holderId = :profileId", { profileId }) .getOne(); - await ds - .getRepository(PosMaster) + await AppDataSource.getRepository(PosMaster) .createQueryBuilder() .update(PosMaster) - .set({ next_holderId: null, isSit: false }) + .set({ next_holderId: null }) .where("id = :id", { id: findProfileInposMasterDraft?.id }) .execute(); if (!findProfileInposMaster && !findProfileInposMasterDraft) { return; } - const findPosition = await ds - .getRepository(Position) + const findPosition = await AppDataSource.getRepository(Position) .createQueryBuilder("position") .where("position.posMasterId = :posMasterId", { posMasterId: findProfileInposMaster?.id }) .getMany(); @@ -322,8 +308,7 @@ export async function removeProfileInOrganize( if (!findPosition) { return; } - await ds - .getRepository(Position) + await AppDataSource.getRepository(Position) .createQueryBuilder() .update(Position) .set({ positionIsSelected: false }) @@ -331,27 +316,24 @@ export async function removeProfileInOrganize( .execute(); } if (type === "EMPLOYEE") { - const findProfileInEmpPosMaster = await ds - .getRepository(EmployeePosMaster) + const findProfileInEmpPosMaster = await AppDataSource.getRepository(EmployeePosMaster) .createQueryBuilder("employeePosMaster") .where("employeePosMaster.orgRevisionId = :orgRevisionId", { orgRevisionId: currentRevision?.id, }) .andWhere("employeePosMaster.current_holderId = :profileId", { profileId }) .getOne(); - await ds - .getRepository(EmployeePosMaster) + await AppDataSource.getRepository(EmployeePosMaster) .createQueryBuilder() .update(EmployeePosMaster) - .set({ current_holderId: null, isSit: false }) + .set({ current_holderId: null }) .where("id = :id", { id: findProfileInEmpPosMaster?.id }) .execute(); if (!findProfileInEmpPosMaster) { return; } - const findEmpPosition = await ds - .getRepository(EmployeePosition) + const findEmpPosition = await AppDataSource.getRepository(EmployeePosition) .createQueryBuilder("employeePosition") .where("employeePosition.posMasterId = :posMasterId", { posMasterId: findProfileInEmpPosMaster?.id, @@ -362,8 +344,7 @@ export async function removeProfileInOrganize( return; } - await ds - .getRepository(EmployeePosition) + await AppDataSource.getRepository(EmployeePosition) .createQueryBuilder() .update(EmployeePosition) .set({ positionIsSelected: false }) @@ -372,10 +353,8 @@ export async function removeProfileInOrganize( } } -export async function removePostMasterAct(profileId: string, manager?: EntityManager) { - const ds = manager ?? AppDataSource; - const currentRevision = await ds - .getRepository(OrgRevision) +export async function removePostMasterAct(profileId: string) { + const currentRevision = await AppDataSource.getRepository(OrgRevision) .createQueryBuilder("orgRevision") .where("orgRevision.orgRevisionIsDraft = false") .andWhere("orgRevision.orgRevisionIsCurrent = true") @@ -385,8 +364,7 @@ export async function removePostMasterAct(profileId: string, manager?: EntityMan return; } - const findProfileInposMaster = await ds - .getRepository(PosMaster) + const findProfileInposMaster = await AppDataSource.getRepository(PosMaster) .createQueryBuilder("posMaster") .where("posMaster.orgRevisionId = :orgRevisionId", { orgRevisionId: currentRevision?.id }) .andWhere("posMaster.current_holderId = :profileId", { profileId }) @@ -396,12 +374,11 @@ export async function removePostMasterAct(profileId: string, manager?: EntityMan return; } - const posMasterAct = await ds - .getRepository(PosMasterAct) + const posMasterAct = await AppDataSource.getRepository(PosMasterAct) .createQueryBuilder("posMasterAct") .where("posMasterAct.posMasterChildId = :posMasterChildId", { posMasterChildId: findProfileInposMaster.id }) .getMany(); - await ds.getRepository(PosMasterAct).remove(posMasterAct); + await AppDataSource.getRepository(PosMasterAct).remove(posMasterAct); } export async function checkReturnCommandType(commandId: string) { @@ -418,6 +395,43 @@ export async function checkReturnCommandType(commandId: string) { return true; } +export async function checkExceptCommandType(commandId: string) { + const commandRepository = AppDataSource.getRepository(Command); + const commandReciveRepository = AppDataSource.getRepository(CommandRecive); + const _type = await commandRepository.findOne({ + where: { + id: commandId, + }, + relations: ["commandType"], + }); + if (!["C-PM-25", "C-PM-26"].includes(String(_type?.commandType.code))) { + return { status: false, LeaveType: null, leaveRemark: null }; + } + const _commandRecive = await commandReciveRepository.findOne({ + where: { commandId: commandId }, + }); + + let _leaveType: string = ""; + switch (String(_type?.commandType.code)) { + case "C-PM-25": { + _leaveType = "DISCIPLINE_SUSPEND"; //คำสั่งพักจากราชการ + break; + } + case "C-PM-26": { + _leaveType = "DISCIPLINE_TEMP_SUSPEND"; //คำสั่งให้ออกจากราชการไว้ก่อน + break; + } + default: { + _leaveType = ""; + } + } + return { + status: true, + LeaveType: _leaveType, + leaveRemark: _commandRecive ? _commandRecive.remarkVertical : null, + }; +} + export async function checkCommandType(commandId: string) { const commandRepository = AppDataSource.getRepository(Command); const commandReciveRepository = AppDataSource.getRepository(CommandRecive); @@ -437,10 +451,7 @@ export async function checkCommandType(commandId: string) { "C-PM-23", "C-PM-19", "C-PM-20", - "C-PM-25", - "C-PM-26", "C-PM-43", - "C-PM-48" ].includes(String(_type?.commandType.code)) ) { // return false; @@ -489,26 +500,11 @@ export async function checkCommandType(commandId: string) { _retireTypeName = "ลาออกจากราชการ"; break; } - case "C-PM-25": { - _leaveType = "DISCIPLINE_SUSPEND"; - _retireTypeName = "พักจากราชการ"; - break; - } - case "C-PM-26": { - _leaveType = "DISCIPLINE_TEMP_SUSPEND"; - _retireTypeName = "ให้ออกจากราชการไว้ก่อน"; - break; - } case "C-PM-43": { _leaveType = "RETIRE_OUT_EMP"; _retireTypeName = "ให้ออกจากราชการ"; break; } - case "C-PM-48": { - _leaveType = "RETIRE_MILITARY"; - _retireTypeName = "รับราชการทหาร"; - break; - } default: { _leaveType = ""; _retireTypeName = ""; @@ -732,8 +728,6 @@ export function commandTypePath(commandCode: string): string | null { return "/salary/report/command36/employee/report"; //SALARY case "C-PM-47": return "/placement/appointment/gazette/report"; - case "C-PM-48": - return "/retirement/resign/command48/report"; default: return null; } @@ -757,23 +751,4 @@ export function resolveNodeId(data: any) { data.rootDnaId ?? null ); -} - -export function logPositionIsSelectedChange( - positionId: string, - oldValue: boolean, - newValue: boolean, - context: { - posMasterId?: string; - userId?: string; - endpoint?: string; - action?: string; - } -) { - if (oldValue !== newValue) { - console.log(`[positionIsSelected-DEBUG] Position ${positionId}: ${oldValue} -> ${newValue}`, { - ...context, - timestamp: new Date().toISOString(), - }); - } } \ No newline at end of file diff --git a/src/keycloak/index.ts b/src/keycloak/index.ts index f359a340..b59d5e81 100644 --- a/src/keycloak/index.ts +++ b/src/keycloak/index.ts @@ -1019,9 +1019,7 @@ export async function resetPassword(username: string) { if (!users.ok) { const errorText = await users.text(); - console.error( - `[resetPassword] Failed to search user. Status: ${users.status}, Error: ${errorText}`, - ); + console.error(`[resetPassword] Failed to search user. Status: ${users.status}, Error: ${errorText}`); return false; } @@ -1049,9 +1047,7 @@ export async function resetPassword(username: string) { if (!resetResponse.ok) { const errorText = await resetResponse.text(); - console.error( - `[resetPassword] Failed to send reset email. Status: ${resetResponse.status}, Error: ${errorText}`, - ); + console.error(`[resetPassword] Failed to send reset email. Status: ${resetResponse.status}, Error: ${errorText}`); return false; } @@ -1121,7 +1117,7 @@ export async function updateUserAttributes( return false; } - // console.log(`[updateUserAttributes] Successfully updated attributes for user ${userId}`); + console.log(`[updateUserAttributes] Successfully updated attributes for user ${userId}`); return true; } catch (error) { console.error(`[updateUserAttributes] Error updating attributes for user ${userId}:`, error); diff --git a/src/middlewares/auth.ts b/src/middlewares/auth.ts index fc006b33..9a571572 100644 --- a/src/middlewares/auth.ts +++ b/src/middlewares/auth.ts @@ -4,7 +4,6 @@ import { createDecoder, createVerifier } from "fast-jwt"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import { handleWebServiceAuth } from "./authWebService"; -import { handleInternalAuth } from "./authInternal"; if (!process.env.AUTH_PUBLIC_KEY && !process.env.AUTH_REALM_URL) { throw new Error("Require keycloak AUTH_PUBLIC_KEY or AUTH_REALM_URL."); @@ -40,11 +39,6 @@ export async function expressAuthentication( return { preferred_username: "bypassed" }; } - // เพิ่มการจัดการสำหรับ Internal Authentication (.NET service) - if (securityName === "internalAuth") { - return await handleInternalAuth(request); - } - // เพิ่มการจัดการสำหรับ Web Service Authentication if (securityName === "webServiceAuth") { return await handleWebServiceAuth(request); diff --git a/src/middlewares/authInternal.ts b/src/middlewares/authInternal.ts deleted file mode 100644 index 77da531c..00000000 --- a/src/middlewares/authInternal.ts +++ /dev/null @@ -1,30 +0,0 @@ -import * as express from "express"; -import HttpError from "../interfaces/http-error"; -import HttpStatus from "../interfaces/http-status"; - -// Internal Authentication (สำหรับ Internal Service เช่น .NET) -// ตรวจสอบ API Key จาก Environment Variable (API_KEY) -export async function handleInternalAuth(request: express.Request) { - // รองรับ header หลายรูปแบบ - const apiKey = - request.headers["api-key"] || request.headers["api_key"] || request.headers["apikey"]; - - if (!apiKey || typeof apiKey !== "string") { - throw new HttpError(HttpStatus.UNAUTHORIZED, "API Key is required"); - } - - // ตรวจสอบ API Key จาก Environment Variable (API_KEY) - if (apiKey !== process.env.API_KEY) { - console.log(`[InternalAuth] Invalid API key attempt: ${apiKey.substring(0, 5)}...`); - throw new HttpError(HttpStatus.UNAUTHORIZED, "Invalid API Key"); - } - - // console.log(`[InternalAuth] Authentication successful`); - - return { - sub: "internal_service", - preferred_username: "internal_service", - name: "Internal Service", - internalKey: true, - }; -} diff --git a/src/middlewares/authWebService.ts b/src/middlewares/authWebService.ts index 1f17b9cf..fa50b3fe 100644 --- a/src/middlewares/authWebService.ts +++ b/src/middlewares/authWebService.ts @@ -17,17 +17,7 @@ export async function handleWebServiceAuth(request: express.Request) { // ตรวจสอบ API Key กับฐานข้อมูล const apiKeyData = await AppDataSource.getRepository(ApiKey).findOne({ - select: { - id: true, - name: true, - keyApi: true, - accessType: true, - dnaRootId: true, - dnaChild1Id: true, - dnaChild2Id: true, - dnaChild3Id: true, - dnaChild4Id: true, - }, + select: { id: true, name: true, keyApi: true }, where: { keyApi: apiKey }, relations: ["apiNames"], }); @@ -50,12 +40,6 @@ export async function handleWebServiceAuth(request: express.Request) { name: apiKeyData.name, type: "web-service", accessApi: apiKeyData.apiNames.map((x) => x.id) ?? [], - accessType: apiKeyData.accessType, - dnaRootId: apiKeyData.dnaRootId, - dnaChild1Id: apiKeyData.dnaChild1Id, - dnaChild2Id: apiKeyData.dnaChild2Id, - dnaChild3Id: apiKeyData.dnaChild3Id, - dnaChild4Id: apiKeyData.dnaChild4Id, }; } diff --git a/src/middlewares/user.ts b/src/middlewares/user.ts index 09e32ef9..75c84d01 100644 --- a/src/middlewares/user.ts +++ b/src/middlewares/user.ts @@ -25,11 +25,5 @@ export type RequestWithUserWebService = Request & { id: string; name: string; accessApi: string[]; - accessType?: string; - dnaRootId?: string | null; - dnaChild1Id?: string | null; - dnaChild2Id?: string | null; - dnaChild3Id?: string | null; - dnaChild4Id?: string | null; }; }; diff --git a/src/migration/1779244154610-update_posMasterEmpHis_add_dna.ts b/src/migration/1779244154610-update_posMasterEmpHis_add_dna.ts deleted file mode 100644 index 5b7a4a1d..00000000 --- a/src/migration/1779244154610-update_posMasterEmpHis_add_dna.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdatePosMasterEmpHisAddDna1779244154610 implements MigrationInterface { - name = 'UpdatePosMasterEmpHisAddDna1779244154610' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`profileEmployeeId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง profileEmployee'`); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`rootDnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgRoot'`); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`child1DnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgChild1'`); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`child2DnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgChild2'`); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`child3DnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgChild3'`); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` ADD \`child4DnaId\` varchar(40) NULL COMMENT 'dna ของตาราง orgChild4'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`child4DnaId\``); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`child3DnaId\``); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`child2DnaId\``); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`child1DnaId\``); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`rootDnaId\``); - await queryRunner.query(`ALTER TABLE \`posMasterEmployeeHistory\` DROP COLUMN \`profileEmployeeId\``); - } -} diff --git a/src/migration/1779776860350-update_command_add_shortName.ts b/src/migration/1779776860350-update_command_add_shortName.ts deleted file mode 100644 index 8754d42b..00000000 --- a/src/migration/1779776860350-update_command_add_shortName.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateCommandAddShortName1779776860350 implements MigrationInterface { - name = 'UpdateCommandAddShortName1779776860350' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`command\` ADD \`shortName\` varchar(16) NULL COMMENT 'ชื่อย่อหน่วยงานที่ออกคำสั่ง'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`command\` DROP COLUMN \`shortName\``); - } - -} diff --git a/src/migration/1780634210221-update_profileDiscipline_add_refCommandId.ts b/src/migration/1780634210221-update_profileDiscipline_add_refCommandId.ts deleted file mode 100644 index 9e5d1851..00000000 --- a/src/migration/1780634210221-update_profileDiscipline_add_refCommandId.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateProfileDisciplineAddRefCommandId1780634210221 implements MigrationInterface { - name = 'UpdateProfileDisciplineAddRefCommandId1780634210221' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`profileDisciplineHistory\` ADD \`refCommandId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง command'`); - await queryRunner.query(`ALTER TABLE \`profileDiscipline\` ADD \`refCommandId\` varchar(40) NULL COMMENT 'คีย์นอก(FK)ของตาราง command'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`profileDiscipline\` DROP COLUMN \`refCommandId\``); - await queryRunner.query(`ALTER TABLE \`profileDisciplineHistory\` DROP COLUMN \`refCommandId\``); - } -} diff --git a/src/migration/1781174051201-update_profileChangeName_add_field_rank.ts b/src/migration/1781174051201-update_profileChangeName_add_field_rank.ts deleted file mode 100644 index b3ec5f92..00000000 --- a/src/migration/1781174051201-update_profileChangeName_add_field_rank.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateProfileChangeNameAddFieldRank1781174051201 implements MigrationInterface { - name = 'UpdateProfileChangeNameAddFieldRank1781174051201' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`profileChangeNameHistory\` ADD \`rank\` varchar(40) NULL COMMENT 'ยศ'`); - await queryRunner.query(`ALTER TABLE \`profileChangeName\` ADD \`rank\` varchar(40) NULL COMMENT 'ยศ'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`profileChangeName\` DROP COLUMN \`rank\``); - await queryRunner.query(`ALTER TABLE \`profileChangeNameHistory\` DROP COLUMN \`rank\``); - } - -} diff --git a/src/migration/1781517610929-update_root_c1_to_c4_add_field_code.ts b/src/migration/1781517610929-update_root_c1_to_c4_add_field_code.ts deleted file mode 100644 index 157ab487..00000000 --- a/src/migration/1781517610929-update_root_c1_to_c4_add_field_code.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateRootC1ToC4AddFieldCode1781517610929 implements MigrationInterface { - name = 'UpdateRootC1ToC4AddFieldCode1781517610929' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`orgChild4\` ADD \`CHILD4_CODE\` varchar(3) NULL COMMENT 'CHILD4_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild3\` ADD \`CHILD3_CODE\` varchar(3) NULL COMMENT 'CHILD3_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild2\` ADD \`CHILD2_CODE\` varchar(3) NULL COMMENT 'CHILD2_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild1\` ADD \`CHILD1_CODE\` varchar(3) NULL COMMENT 'CHILD1_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgRoot\` ADD \`ROOT_CODE\` varchar(3) NULL COMMENT 'ROOT_CODE'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`orgRoot\` DROP COLUMN \`ROOT_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild1\` DROP COLUMN \`CHILD1_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild2\` DROP COLUMN \`CHILD2_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild3\` DROP COLUMN \`CHILD3_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild4\` DROP COLUMN \`CHILD4_CODE\``); - } - -} diff --git a/src/migration/1781577597453-update_add_field_code_all_tables.ts b/src/migration/1781577597453-update_add_field_code_all_tables.ts deleted file mode 100644 index 39ea6466..00000000 --- a/src/migration/1781577597453-update_add_field_code_all_tables.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UpdateAddFieldCodeAllTables1781577597453 implements MigrationInterface { - name = 'UpdateAddFieldCodeAllTables1781577597453' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`orgChild4\` ADD \`ROOT_CODE\` varchar(3) NULL COMMENT 'ROOT_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild4\` ADD \`CHILD1_CODE\` varchar(3) NULL COMMENT 'CHILD1_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild4\` ADD \`CHILD2_CODE\` varchar(3) NULL COMMENT 'CHILD2_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild4\` ADD \`CHILD3_CODE\` varchar(3) NULL COMMENT 'CHILD3_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild3\` ADD \`ROOT_CODE\` varchar(3) NULL COMMENT 'ROOT_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild3\` ADD \`CHILD1_CODE\` varchar(3) NULL COMMENT 'CHILD1_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild3\` ADD \`CHILD2_CODE\` varchar(3) NULL COMMENT 'CHILD2_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild3\` ADD \`CHILD4_CODE\` varchar(3) NULL COMMENT 'CHILD4_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild2\` ADD \`ROOT_CODE\` varchar(3) NULL COMMENT 'ROOT_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild2\` ADD \`CHILD1_CODE\` varchar(3) NULL COMMENT 'CHILD1_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild2\` ADD \`CHILD3_CODE\` varchar(3) NULL COMMENT 'CHILD3_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild2\` ADD \`CHILD4_CODE\` varchar(3) NULL COMMENT 'CHILD4_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild1\` ADD \`ROOT_CODE\` varchar(3) NULL COMMENT 'ROOT_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild1\` ADD \`CHILD2_CODE\` varchar(3) NULL COMMENT 'CHILD2_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild1\` ADD \`CHILD3_CODE\` varchar(3) NULL COMMENT 'CHILD3_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgChild1\` ADD \`CHILD4_CODE\` varchar(3) NULL COMMENT 'CHILD4_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgRoot\` ADD \`CHILD1_CODE\` varchar(3) NULL COMMENT 'CHILD1_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgRoot\` ADD \`CHILD2_CODE\` varchar(3) NULL COMMENT 'CHILD2_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgRoot\` ADD \`CHILD3_CODE\` varchar(3) NULL COMMENT 'CHILD3_CODE'`); - await queryRunner.query(`ALTER TABLE \`orgRoot\` ADD \`CHILD4_CODE\` varchar(3) NULL COMMENT 'CHILD4_CODE'`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`orgRoot\` DROP COLUMN \`CHILD4_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgRoot\` DROP COLUMN \`CHILD3_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgRoot\` DROP COLUMN \`CHILD2_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgRoot\` DROP COLUMN \`CHILD1_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild1\` DROP COLUMN \`CHILD4_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild1\` DROP COLUMN \`CHILD3_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild1\` DROP COLUMN \`CHILD2_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild1\` DROP COLUMN \`ROOT_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild2\` DROP COLUMN \`CHILD4_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild2\` DROP COLUMN \`CHILD3_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild2\` DROP COLUMN \`CHILD1_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild2\` DROP COLUMN \`ROOT_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild3\` DROP COLUMN \`CHILD4_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild3\` DROP COLUMN \`CHILD2_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild3\` DROP COLUMN \`CHILD1_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild3\` DROP COLUMN \`ROOT_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild4\` DROP COLUMN \`CHILD3_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild4\` DROP COLUMN \`CHILD2_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild4\` DROP COLUMN \`CHILD1_CODE\``); - await queryRunner.query(`ALTER TABLE \`orgChild4\` DROP COLUMN \`ROOT_CODE\``); - } -} diff --git a/src/scripts/ClearOldOrgRevision.ts b/src/scripts/ClearOldOrgRevision.ts deleted file mode 100644 index 4d2be7a7..00000000 --- a/src/scripts/ClearOldOrgRevision.ts +++ /dev/null @@ -1,27 +0,0 @@ -import "dotenv/config"; -import "reflect-metadata"; -import { AppDataSource } from "../database/data-source"; -import { clearOldOrgRevisionData } from "../services/ClearOldOrgRevisionService"; - -// "clear:old-org-revision": "ts-node src/scripts/ClearOldOrgRevision.ts", - -const defaultOrgRevisionId = "24dacf63-d289-496c-8102-8b25079dbaf2"; - -async function main(): Promise { - const orgRevisionId = process.argv[2] || defaultOrgRevisionId; - - try { - await AppDataSource.initialize(); - const result = await clearOldOrgRevisionData(orgRevisionId); - console.info(JSON.stringify(result, null, 2)); - } catch (error) { - console.error("[ClearOldOrgRevision] Failed:", error); - process.exitCode = 1; - } finally { - if (AppDataSource.isInitialized) { - await AppDataSource.destroy(); - } - } -} - -void main(); diff --git a/src/services/ClearOldOrgRevisionService.ts b/src/services/ClearOldOrgRevisionService.ts deleted file mode 100644 index 056206f3..00000000 --- a/src/services/ClearOldOrgRevisionService.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { EntityManager, EntityTarget, In } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import { OrgRevision } from "../entities/OrgRevision"; -import { PosMaster } from "../entities/PosMaster"; -import { Position } from "../entities/Position"; -import { OrgRoot } from "../entities/OrgRoot"; -import { OrgChild1 } from "../entities/OrgChild1"; -import { OrgChild2 } from "../entities/OrgChild2"; -import { OrgChild3 } from "../entities/OrgChild3"; -import { OrgChild4 } from "../entities/OrgChild4"; -import { PosMasterAct } from "../entities/PosMasterAct"; -import { PosMasterAssign } from "../entities/PosMasterAssign"; -import { PermissionOrg } from "../entities/PermissionOrg"; -import { PermissionProfile } from "../entities/PermissionProfile"; -import { EmployeePosMaster } from "../entities/EmployeePosMaster"; -import { EmployeeTempPosMaster } from "../entities/EmployeeTempPosMaster"; -import { EmployeePosition } from "../entities/EmployeePosition"; -import { orgStructureCache } from "../utils/OrgStructureCache"; - -export interface ClearOldOrgRevisionSummary { - orgRevisionId: string; - orgRevisionName: string; - deleted: { - positions: number; - employeePositionsByPosMaster: number; - employeePositionsByTempPosMaster: number; - posMasterActsByParent: number; - posMasterActsByChild: number; - posMasterAssigns: number; - posMasters: number; - employeePosMasters: number; - employeeTempPosMasters: number; - permissionOrgs: number; - permissionProfiles: number; - orgChild4s: number; - orgChild3s: number; - orgChild2s: number; - orgChild1s: number; - orgRoots: number; - orgRevisions: number; - }; -} - -interface OrgRevisionSnapshot { - id: string; - orgRevisionName: string; - orgRevisionIsCurrent: boolean; - orgRevisionIsDraft: boolean; -} - -export async function clearOldOrgRevisionData( - orgRevisionId: string, -): Promise { - const result = await AppDataSource.transaction(async (manager) => { - const orgRevision = await manager.findOne(OrgRevision, { - where: { id: orgRevisionId }, - select: ["id", "orgRevisionName", "orgRevisionIsCurrent", "orgRevisionIsDraft"], - }); - - if (!orgRevision) { - throw new Error(`ไม่พบ orgRevision ที่ต้องการล้างข้อมูล: ${orgRevisionId}`); - } - - validateOrgRevisionForDeletion(orgRevision); - - const [posMasters, orgRoots, employeePosMasters, employeeTempPosMasters] = await Promise.all([ - manager.find(PosMaster, { - where: { orgRevisionId }, - select: ["id"], - }), - manager.find(OrgRoot, { - where: { orgRevisionId }, - select: ["id"], - }), - manager.find(EmployeePosMaster, { - where: { orgRevisionId }, - select: ["id"], - }), - manager.find(EmployeeTempPosMaster, { - where: { orgRevisionId }, - select: ["id"], - }), - ]); - - const posMasterIds = posMasters.map((item) => item.id); - const orgRootIds = orgRoots.map((item) => item.id); - const employeePosMasterIds = employeePosMasters.map((item) => item.id); - const employeeTempPosMasterIds = employeeTempPosMasters.map((item) => item.id); - - const [ - positionsCount, - employeePositionsByPosMasterCount, - employeePositionsByTempPosMasterCount, - posMasterActsByParentCount, - posMasterActsByChildCount, - posMasterAssignsCount, - permissionOrgsCount, - permissionProfilesCount, - orgChild4sCount, - orgChild3sCount, - orgChild2sCount, - orgChild1sCount, - ] = await Promise.all([ - countByIds(manager, Position, "posMasterId", posMasterIds), - countByIds(manager, EmployeePosition, "posMasterId", employeePosMasterIds), - countByIds(manager, EmployeePosition, "posMasterTempId", employeeTempPosMasterIds), - countByIds(manager, PosMasterAct, "posMasterId", posMasterIds), - countByIds(manager, PosMasterAct, "posMasterChildId", posMasterIds), - countByIds(manager, PosMasterAssign, "posMasterId", posMasterIds), - countByIds(manager, PermissionOrg, "orgRootId", orgRootIds), - countByIds(manager, PermissionProfile, "orgRootId", orgRootIds), - manager.count(OrgChild4, { where: { orgRevisionId } }), - manager.count(OrgChild3, { where: { orgRevisionId } }), - manager.count(OrgChild2, { where: { orgRevisionId } }), - manager.count(OrgChild1, { where: { orgRevisionId } }), - ]); - - if (positionsCount > 0) { - await manager.delete(Position, { posMasterId: In(posMasterIds) }); - } - if (employeePositionsByPosMasterCount > 0) { - await manager.delete(EmployeePosition, { posMasterId: In(employeePosMasterIds) }); - } - if (employeePositionsByTempPosMasterCount > 0) { - await manager.delete(EmployeePosition, { posMasterTempId: In(employeeTempPosMasterIds) }); - } - if (posMasterActsByParentCount > 0) { - await manager.delete(PosMasterAct, { posMasterId: In(posMasterIds) }); - } - if (posMasterActsByChildCount > 0) { - await manager.delete(PosMasterAct, { posMasterChildId: In(posMasterIds) }); - } - if (posMasterAssignsCount > 0) { - await manager.delete(PosMasterAssign, { posMasterId: In(posMasterIds) }); - } - - const posMastersCount = posMasterIds.length; - const employeePosMastersCount = employeePosMasterIds.length; - const employeeTempPosMastersCount = employeeTempPosMasterIds.length; - - if (posMastersCount > 0) { - await manager.delete(PosMaster, { orgRevisionId }); - } - if (employeePosMastersCount > 0) { - await manager.delete(EmployeePosMaster, { orgRevisionId }); - } - if (employeeTempPosMastersCount > 0) { - await manager.delete(EmployeeTempPosMaster, { orgRevisionId }); - } - - if (permissionOrgsCount > 0) { - await manager.delete(PermissionOrg, { orgRootId: In(orgRootIds) }); - } - if (permissionProfilesCount > 0) { - await manager.delete(PermissionProfile, { orgRootId: In(orgRootIds) }); - } - - if (orgChild4sCount > 0) { - await manager.delete(OrgChild4, { orgRevisionId }); - } - if (orgChild3sCount > 0) { - await manager.delete(OrgChild3, { orgRevisionId }); - } - if (orgChild2sCount > 0) { - await manager.delete(OrgChild2, { orgRevisionId }); - } - if (orgChild1sCount > 0) { - await manager.delete(OrgChild1, { orgRevisionId }); - } - - const orgRootsCount = orgRootIds.length; - if (orgRootsCount > 0) { - await manager.delete(OrgRoot, { orgRevisionId }); - } - - await manager.delete(OrgRevision, { id: orgRevisionId }); - - return { - orgRevisionId: orgRevision.id, - orgRevisionName: orgRevision.orgRevisionName, - deleted: { - positions: positionsCount, - employeePositionsByPosMaster: employeePositionsByPosMasterCount, - employeePositionsByTempPosMaster: employeePositionsByTempPosMasterCount, - posMasterActsByParent: posMasterActsByParentCount, - posMasterActsByChild: posMasterActsByChildCount, - posMasterAssigns: posMasterAssignsCount, - posMasters: posMastersCount, - employeePosMasters: employeePosMastersCount, - employeeTempPosMasters: employeeTempPosMastersCount, - permissionOrgs: permissionOrgsCount, - permissionProfiles: permissionProfilesCount, - orgChild4s: orgChild4sCount, - orgChild3s: orgChild3sCount, - orgChild2s: orgChild2sCount, - orgChild1s: orgChild1sCount, - orgRoots: orgRootsCount, - orgRevisions: 1, - }, - }; - }); - - orgStructureCache.invalidate(orgRevisionId); - return result; -} - -function validateOrgRevisionForDeletion(orgRevision: OrgRevisionSnapshot): void { - if (orgRevision.orgRevisionIsCurrent) { - throw new Error(`ไม่สามารถลบ orgRevision ปัจจุบันได้: ${orgRevision.id}`); - } - - if (orgRevision.orgRevisionIsDraft) { - throw new Error(`ไม่สามารถลบ orgRevision แบบร่างได้ด้วยสคริปต์นี้: ${orgRevision.id}`); - } -} - -async function countByIds( - manager: EntityManager, - entity: EntityTarget, - field: keyof Entity, - ids: string[], -): Promise { - if (ids.length === 0) { - return 0; - } - - const alias = "entity"; - return manager - .createQueryBuilder(entity, alias) - .where(`${alias}.${String(field)} IN (:...ids)`, { ids }) - .getCount(); -} diff --git a/src/services/CommandService.ts b/src/services/CommandService.ts index 0c0d7fe9..b002462c 100644 --- a/src/services/CommandService.ts +++ b/src/services/CommandService.ts @@ -1,12 +1,8 @@ import { AppDataSource } from "../database/data-source"; import { CommandRecive } from "../entities/CommandRecive"; import { Command } from "../entities/Command"; -import { CommandOperator } from "../entities/CommandOperator"; import { OrgRoot } from "../entities/OrgRoot"; -import { Position } from "../entities/Position"; import { Profile } from "../entities/Profile"; -import { RequestWithUser } from "../middlewares/user"; -import { EntityManager } from "typeorm"; export interface PosNumCodeSitResult { posNumCodeSit: string; @@ -21,42 +17,43 @@ export interface PosNumCodeSitResult { * เรียงลำดับผู้ได้รับคำสั่งใหม่หลังจากลบรายการ และอัพเดทสถานะคำสั่งถ้าไม่มีผู้ได้รับคำสั่งเหลือ * @param reciveId commandRecive.Id ของผู้ได้รับคำสั่ง * @param code ประเภทคำสั่ง - * @param manager ถ้าส่งเข้ามา → ทุก operation อยู่ใน transaction ของ caller (all-or-nothing) * @returns Promise */ export async function reOrderCommandRecivesAndDelete( - reciveId: string, - manager?: EntityManager, + reciveId: string ): Promise { - const ds = manager ?? AppDataSource; - const commandReciveRepo = ds.getRepository(CommandRecive); - const commandRepo = ds.getRepository(Command); + const commandReciveRepo = AppDataSource.getRepository(CommandRecive); + const commandRepo = AppDataSource.getRepository(Command); // ค้นหาข้อมูลผู้ได้รับคำสั่งตาม reciveId const commandRecive = await commandReciveRepo.findOne({ - where: { id: reciveId }, + where: { id: reciveId } }); - if (commandRecive == null) return; + if (commandRecive == null) + return; const commandId = commandRecive.commandId; // ลบตาม refId await commandReciveRepo.delete(commandRecive.id); - + const commandReciveList = await commandReciveRepo.find({ where: { commandId: commandId }, order: { order: "ASC" }, }); // ลำดับผู้ได้รับคำสั่งใหม่ if (commandReciveList.length > 0) { - for (let i = 0; i < commandReciveList.length; i++) { - commandReciveList[i].order = i + 1; - await commandReciveRepo.save(commandReciveList[i]); - } + await Promise.all( + commandReciveList.map(async (p, i) => { + p.order = i + 1; + await commandReciveRepo.save(p); + }) + ); } else { // ถ้าไม่มีผู้ได้รับคำสั่งเหลือเลย ให้ยกเลิกคำสั่ง await commandRepo.update({ id: commandId }, { status: "CANCEL" }); } + } /** @@ -147,101 +144,3 @@ export async function getPosNumCodeSit( commandExcecuteDate, }; } - -/** - * สร้าง/insert CommandOperator "เจ้าหน้าที่ดำเนินการ" สำหรับ command - * ใช้ userProfile ที่ query ไปแล้วถ้ามี ถ้าไม่มีค่อย query ใหม่ - * @param userProfile profile ที่ query ไปแล้ว (หรือ null ถ้ายังไม่ได้ query) - * @param commandId command id ที่จะผูกกับ operator - * @param request request context (สำหรับ user.sub / user.name) - * @param now timestamp สำหรับ audit fields - * @param manager ถ้าส่งเข้ามา → ทุก operation อยู่ใน transaction ของ caller (all-or-nothing) - * @returns Promise - */ -export async function ensureCommandOperator( - userProfile: Profile | null, - commandId: string, - request: RequestWithUser, - now: Date, - manager?: EntityManager, -): Promise { - const ds = manager ?? AppDataSource; - const profileRepo = ds.getRepository(Profile); - const positionRepo = ds.getRepository(Position); - const commandOperatorRepo = ds.getRepository(CommandOperator); - - if (!request.user.sub) return; - // ใช้ userProfile ที่ query ไปแล้วถ้ามี ถ้าไม่มีค่อย query ใหม่ - let profile = userProfile; - if (!profile) { - profile = await profileRepo.findOne({ - where: { keycloak: request.user.sub }, - relations: { - posLevel: true, - posType: true, - current_holders: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }, - }); - } - if (!profile) return; - - const currentHolder = profile.current_holders?.find( - (x) => - x.orgRevision?.orgRevisionIsDraft === false && - x.orgRevision?.orgRevisionIsCurrent === true, - ); - - const posNo = - currentHolder != null && currentHolder.orgChild4 != null - ? `${currentHolder.orgChild4.orgChild4ShortName} ${currentHolder.posMasterNo}` - : currentHolder != null && currentHolder.orgChild3 != null - ? `${currentHolder.orgChild3.orgChild3ShortName} ${currentHolder.posMasterNo}` - : currentHolder != null && currentHolder.orgChild2 != null - ? `${currentHolder.orgChild2.orgChild2ShortName} ${currentHolder.posMasterNo}` - : currentHolder != null && currentHolder.orgChild1 != null - ? `${currentHolder.orgChild1.orgChild1ShortName} ${currentHolder.posMasterNo}` - : currentHolder != null && currentHolder?.orgRoot != null - ? `${currentHolder.orgRoot.orgRootShortName} ${currentHolder.posMasterNo}` - : null; - - const position = await positionRepo.findOne({ - where: { - positionIsSelected: true, - posMaster: { - orgRevisionId: currentHolder?.orgRevisionId, - current_holderId: profile.id, - }, - }, - order: { createdAt: "DESC" }, - relations: { posExecutive: true }, - }); - - const operator = Object.assign(new CommandOperator(), { - profileId: profile?.id, - prefix: profile?.prefix, - firstName: profile?.firstName, - lastName: profile?.lastName, - posNo: posNo, - posType: profile?.posType?.posTypeName ?? null, - posLevel: profile?.posLevel?.posLevelName ?? null, - position: position?.positionName ?? null, - positionExecutive: position?.posExecutive?.posExecutiveName ?? null, - roleName: "เจ้าหน้าที่ดำเนินการ", - orderNo: 1, - commandId: commandId, - createdUserId: request.user.sub, - createdFullName: request.user.name, - createdAt: now, - lastUpdateUserId: request.user.sub, - lastUpdateFullName: request.user.name, - lastUpdatedAt: now, - }); - await commandOperatorRepo.save(operator); -} diff --git a/src/services/ExecuteOfficerProfileService.ts b/src/services/ExecuteOfficerProfileService.ts deleted file mode 100644 index f06159dc..00000000 --- a/src/services/ExecuteOfficerProfileService.ts +++ /dev/null @@ -1,1423 +0,0 @@ -import { EntityManager, In, Like } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatusCode from "../interfaces/http-status"; -import { CreateProfileAllFields, Profile } from "../entities/Profile"; -import { ProfileEmployee } from "../entities/ProfileEmployee"; -import { CreateProfileSalary, ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { CreateProfileEducation, ProfileEducation } from "../entities/ProfileEducation"; -import { ProfileEducationHistory } from "../entities/ProfileEducationHistory"; -import { - CreateProfileCertificate, - ProfileCertificate, -} from "../entities/ProfileCertificate"; -import { ProfileCertificateHistory } from "../entities/ProfileCertificateHistory"; -import { ProfileFamilyCouple } from "../entities/ProfileFamilyCouple"; -import { ProfileFamilyCoupleHistory } from "../entities/ProfileFamilyCoupleHistory"; -import { ProfileFamilyFather } from "../entities/ProfileFamilyFather"; -import { ProfileFamilyFatherHistory } from "../entities/ProfileFamilyFatherHistory"; -import { ProfileFamilyMother } from "../entities/ProfileFamilyMother"; -import { ProfileFamilyMotherHistory } from "../entities/ProfileFamilyMotherHistory"; -import { CreateProfileInsignia, ProfileInsignia } from "../entities/ProfileInsignia"; -import { ProfileInsigniaHistory } from "../entities/ProfileInsigniaHistory"; -import { ProfileAvatar } from "../entities/ProfileAvatar"; -import { PosLevel } from "../entities/PosLevel"; -import { PosType } from "../entities/PosType"; -import { Province } from "../entities/Province"; -import { District } from "../entities/District"; -import { SubDistrict } from "../entities/SubDistrict"; -import { OrgRoot } from "../entities/OrgRoot"; -import { RoleKeycloak } from "../entities/RoleKeycloak"; -import { PosMaster } from "../entities/PosMaster"; -import { Position } from "../entities/Position"; -import { Command } from "../entities/Command"; -import { - calculateRetireDate, - calculateRetireLaw, - removeProfileInOrganize, - setLogDataDiff, -} from "../interfaces/utils"; -import { - addUserRoles, - createUser, - getRoleMappings, - getRoles, - getUserByUsername, - removeUserRoles, - updateUserAttributes, -} from "../keycloak"; -import { CreatePosMasterHistoryOfficer } from "./PositionService"; -import { getOrgFullName, getPosMasterNo } from "../utils/org-formatting"; -import CallAPI from "../interfaces/call-api"; - -/** - * Input: ตำแหน่งที่จะกำหนดให้กับ profile ใหม่ (ส่งมาจากฝั่งบรรจุ) - */ -export interface OfficerPositionInput { - posmasterId: string; - positionId: string; - positionName: string; - posTypeId: string; - posLevelId: string; - posExecutiveId: string | null; - positionField: string | null; - positionExecutiveField: string | null; - positionArea: string | null; -} - -/** - * Input: ข้อมูลคู่สมรส - */ -export interface OfficerMarryInput { - marry?: boolean | null; - marryPrefix?: string | null; - marryFirstName?: string | null; - marryLastName?: string | null; - marryOccupation?: string | null; - marryNationality?: string | null; -} - -/** - * Input: ข้อมูลบิดา - */ -export interface OfficerFatherInput { - fatherPrefix?: string | null; - fatherFirstName?: string | null; - fatherLastName?: string | null; - fatherOccupation?: string | null; - fatherNationality?: string | null; -} - -/** - * Input: ข้อมูลมารดา - */ -export interface OfficerMotherInput { - motherPrefix?: string | null; - motherFirstName?: string | null; - motherLastName?: string | null; - motherOccupation?: string | null; - motherNationality?: string | null; -} - -/** - * Input: ข้อมูล 1 คนที่จะบรรจุ/แต่งตั้ง (ตรงกับ body.data[i] ของ endpoint เดิม) - */ -export interface OfficerProfileItem { - bodyProfile: CreateProfileAllFields; - bodyEducations?: CreateProfileEducation[]; - bodyCertificates?: CreateProfileCertificate[]; - bodySalarys?: CreateProfileSalary | null; - bodyPosition?: OfficerPositionInput | null; - bodyMarry?: OfficerMarryInput | null; - bodyFather?: OfficerFatherInput | null; - bodyMother?: OfficerMotherInput | null; -} - -/** - * Context สำหรับ audit/log — แยกขาดจาก HTTP request เพื่อให้ service - * สามารถถูกเรียกได้ทั้งจาก endpoint (ผ่าน Express req) และจาก message consumer - * (ผ่าน pseudo-req ที่สร้างขึ้น) โดยไม่ผูกติดกับ HTTP layer - */ -export interface ExecutionContext { - /** user ที่ทริกเกอร์งานนี้ (sub = keycloak id, name = ชื่อเต็ม) */ - user: { sub: string; name: string }; - /** Express request (สำหรับ subscriber pattern: setLogDataDiff, save({data: req})) */ - req?: any; -} - -/** - * Service สำหรับสร้าง/อัปเดตทะเบียนประวัติข้าราชการ (Profile) หลังออกคำสั่งบรรจุ - * - * ใช้กับ commandType: C-PM-01, 02, 14 - * - * - endpoint /org/command/excexute/create-officer-profile เรียกผ่าน service นี้ (thin wrapper) - * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (Linear Flow) - * - * Behavior ทั้งหมด preserve จาก CommandController.CreateOfficeProfileExcecute ต้นฉบับ - * - * Batch semantics: all-or-nothing — ประมวลผลทุกคนภายใต้ transaction เดียว (sequential) - * ถ้าคนใด throw จะ rollback ทั้ง batch และ propagate error ออกไป (ล้มเหลวทั้งหมด) - * ถ้าทุกคนสำเร็จจะ return result รายงาน success count - * - * ⚠️ หมายเหตุ Keycloak: operations (createUser/addUserRoles/removeUserRoles/updateUserAttributes) - * ทำภายใน transaction เพื่อ preserve behavior เดิม — Keycloak ไม่สามารถ rollback ได้ - * ถ้า DB rollback หลังจาก Keycloak operation สำเร็จ → Keycloak จะถูกเปลี่ยนไปแล้ว - * - * Design note: แก้ปัญหา "Circular Dependency" ระหว่าง API Org กับ API บรรจุ โดยให้ฝั่งบรรจุ - * ส่ง resultData กลับมา แล้วฝั่ง Org ประมวลผลสร้าง profile เองที่ต้นทาง แทนการเรียกซ้อนกัน - */ -export class ExecuteOfficerProfileService { - private commandRepository = AppDataSource.getRepository(Command); - private profileRepository = AppDataSource.getRepository(Profile); - private profileEmployeeRepository = AppDataSource.getRepository(ProfileEmployee); - private salaryRepo = AppDataSource.getRepository(ProfileSalary); - private salaryHistoryRepo = AppDataSource.getRepository(ProfileSalaryHistory); - private posLevelRepo = AppDataSource.getRepository(PosLevel); - private posTypeRepo = AppDataSource.getRepository(PosType); - private provinceRepo = AppDataSource.getRepository(Province); - private districtRepo = AppDataSource.getRepository(District); - private subDistrictRepo = AppDataSource.getRepository(SubDistrict); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - private roleKeycloakRepo = AppDataSource.getRepository(RoleKeycloak); - private posMasterRepository = AppDataSource.getRepository(PosMaster); - private positionRepository = AppDataSource.getRepository(Position); - private profileEducationRepo = AppDataSource.getRepository(ProfileEducation); - private profileEducationHistoryRepo = AppDataSource.getRepository(ProfileEducationHistory); - private certificateRepo = AppDataSource.getRepository(ProfileCertificate); - private certificateHistoryRepo = AppDataSource.getRepository(ProfileCertificateHistory); - private profileFamilyCoupleRepo = AppDataSource.getRepository(ProfileFamilyCouple); - private profileFamilyCoupleHistoryRepo = AppDataSource.getRepository(ProfileFamilyCoupleHistory); - private profileFamilyFatherRepo = AppDataSource.getRepository(ProfileFamilyFather); - private profileFamilyFatherHistoryRepo = AppDataSource.getRepository(ProfileFamilyFatherHistory); - private profileFamilyMotherRepo = AppDataSource.getRepository(ProfileFamilyMother); - private profileFamilyMotherHistoryRepo = AppDataSource.getRepository(ProfileFamilyMotherHistory); - private insigniaRepo = AppDataSource.getRepository(ProfileInsignia); - private insigniaHistoryRepo = AppDataSource.getRepository(ProfileInsigniaHistory); - private avatarRepository = AppDataSource.getRepository(ProfileAvatar); - - /** - * ประมวลผลสร้าง/อัปเดท profile ข้าราชการ ตามข้อมูลที่ฝั่งบรรจุส่งมา - * - * @param data รายการข้อมูลที่จะประมวลผล (1 คำสั่งอาจมีหลายคน) - * @param ctx context สำหรับ audit/log - */ - async executeCreateOfficerProfile( - data: OfficerProfileItem[], - ctx: ExecutionContext, - ): Promise { - const commandId = - data?.find((x: any) => x.bodySalarys?.commandId)?.bodySalarys?.commandId ?? "unknown"; - console.log( - `[ExecuteOfficerProfileService] Starting executeCreateOfficerProfile — commandId: ${commandId}`, - ); - console.log(`[ExecuteOfficerProfileService] Request body count: ${data?.length ?? 0}`); - - // ───────────────────────────────────────────────────────────── - // Normalize date fields - // ผ่าน HTTP endpoint → tsoa แปลง ISO string → Date ให้อัตโนมัติ - // แต่ผ่าน RabbitMQ handler (axios) → จะได้ string → ต้องแปลงเอง - // ไม่งั้น calculateRetireDate/getFullYear ฯลฯ จะพัง - // ───────────────────────────────────────────────────────────── - const toDate = (v: any): Date | null => { - if (v == null || v === "") return null; - if (v instanceof Date) return isNaN(v.getTime()) ? null : v; - const d = new Date(v); - return isNaN(d.getTime()) ? null : d; - }; - for (const item of data ?? []) { - const bp = item.bodyProfile as any; - if (bp) { - bp.birthDate = toDate(bp.birthDate); - bp.dateStart = toDate(bp.dateStart); - bp.dateAppoint = toDate(bp.dateAppoint); - bp.dateRetire = toDate(bp.dateRetire); - } - const bs = item.bodySalarys as any; - if (bs) { - bs.commandDateAffect = toDate(bs.commandDateAffect); - bs.commandDateSign = toDate(bs.commandDateSign); - } - if (item.bodyEducations) { - for (const edu of item.bodyEducations as any[]) { - edu.startDate = toDate(edu.startDate); - edu.endDate = toDate(edu.endDate); - edu.finishDate = toDate(edu.finishDate); - } - } - if (item.bodyCertificates) { - for (const cer of item.bodyCertificates as any[]) { - cer.expireDate = toDate(cer.expireDate); - cer.issueDate = toDate(cer.issueDate); - } - } - } - - const req = ctx.req; - const roleKeycloak = await this.roleKeycloakRepo.findOne({ - where: { name: Like("USER") }, - }); - console.log("[ExecuteOfficerProfileService] roleKeycloak found:", !!roleKeycloak); - const list = await getRoles(); - console.log( - "[ExecuteOfficerProfileService] Roles list retrieved, length:", - Array.isArray(list) ? list.length : "not array", - ); - if (!Array.isArray(list)) { - console.error( - "[ExecuteOfficerProfileService] Failed - Cannot get role(s) data from the server", - ); - throw new Error("Failed. Cannot get role(s) data from the server."); - } - let _posNumCodeSit: string = ""; - let _posNumCodeSitAbb: string = ""; - console.log("[ExecuteOfficerProfileService] Getting command data"); - const _command = await this.commandRepository.findOne({ - where: { - id: data.find((x) => x.bodySalarys?.commandId)?.bodySalarys?.commandId ?? "", - }, - }); - console.log( - "[ExecuteOfficerProfileService] Command found:", - !!_command, - "isBangkok:", - _command?.isBangkok, - ); - if (_command) { - if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - console.log("[ExecuteOfficerProfileService] Setting position codes for OFFICE"); - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - _posNumCodeSit = orgRootDeputy - ? orgRootDeputy?.orgRootName - : "สำนักปลัดกรุงเทพมหานคร"; - _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; - console.log( - "[ExecuteOfficerProfileService] OFFICE position codes set:", - _posNumCodeSit, - _posNumCodeSitAbb, - ); - } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - console.log("[ExecuteOfficerProfileService] Setting position codes for BANGKOK"); - _posNumCodeSit = "กรุงเทพมหานคร"; - _posNumCodeSitAbb = "กทม."; - console.log( - "[ExecuteOfficerProfileService] BANGKOK position codes set:", - _posNumCodeSit, - _posNumCodeSitAbb, - ); - } else { - console.log("[ExecuteOfficerProfileService] Setting position codes from admin profile"); - let _profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: _command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: [ - "current_holders", - "current_holders.orgRevision", - "current_holders.orgRoot", - ], - }); - _posNumCodeSit = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot - .orgRootName ?? ""; - _posNumCodeSitAbb = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - console.log( - "[ExecuteOfficerProfileService] Admin profile position codes set:", - _posNumCodeSit, - _posNumCodeSitAbb, - ); - } - } - const before = null; - const meta = { - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - console.log( - "[ExecuteOfficerProfileService] Starting to process", - data.length, - "profile(s)", - ); - - // ───────────────────────────────────────────────────────────── - // Single transaction ครอบทั้ง batch (all-or-nothing) - // ทุกคนใช้ manager ตัวเดียวกัน — คนใด throw จะ rollback ทั้ง batch - // และ propagate error ออกไป (ล้มเหลวทั้งหมด) โดย log error ของคนที่ทำให้ fail ก่อน rethrow - // ───────────────────────────────────────────────────────────── - await AppDataSource.transaction(async (manager) => { - for (const item of data ?? []) { - try { - await this.processOne( - item, - ctx, - manager, - roleKeycloak, - list, - _posNumCodeSit, - _posNumCodeSitAbb, - meta, - before, - commandId, - ); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteOfficerProfileService] Failed commandId=${commandId}, citizenId=${item.bodyProfile?.citizenId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - } - - /** - * ประมวลผล 1 คน ภายใน transaction เดียว (manager) - * ทุก save ใช้ manager.getRepository(...) เพื่อให้อยู่ใน transaction เดียวกัน - * ถ้า throw ระหว่างทาง → rollback ทั้งหมดของ batch (กัน partial commit) - */ - private async processOne( - item: OfficerProfileItem, - ctx: ExecutionContext, - manager: EntityManager, - roleKeycloak: RoleKeycloak | null, - list: any[], - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - meta: any, - before: any, - commandId: string, - ): Promise { - const req = ctx.req; - - // ───────────────────────────────────────────────────────────── - // repo ทั้งหมดสร้างจาก manager เพื่อให้อยู่ใน transaction เดียวกัน - // ───────────────────────────────────────────────────────────── - const profileRepository = manager.getRepository(Profile); - const profileEmployeeRepository = manager.getRepository(ProfileEmployee); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - const posLevelRepo = manager.getRepository(PosLevel); - const posTypeRepo = manager.getRepository(PosType); - const provinceRepo = manager.getRepository(Province); - const districtRepo = manager.getRepository(District); - const subDistrictRepo = manager.getRepository(SubDistrict); - const posMasterRepository = manager.getRepository(PosMaster); - const positionRepository = manager.getRepository(Position); - const profileEducationRepo = manager.getRepository(ProfileEducation); - const profileEducationHistoryRepo = manager.getRepository(ProfileEducationHistory); - const certificateRepo = manager.getRepository(ProfileCertificate); - const certificateHistoryRepo = manager.getRepository(ProfileCertificateHistory); - const profileFamilyCoupleRepo = manager.getRepository(ProfileFamilyCouple); - const profileFamilyCoupleHistoryRepo = manager.getRepository(ProfileFamilyCoupleHistory); - const profileFamilyFatherRepo = manager.getRepository(ProfileFamilyFather); - const profileFamilyFatherHistoryRepo = manager.getRepository(ProfileFamilyFatherHistory); - const profileFamilyMotherRepo = manager.getRepository(ProfileFamilyMother); - const profileFamilyMotherHistoryRepo = manager.getRepository(ProfileFamilyMotherHistory); - const insigniaRepo = manager.getRepository(ProfileInsignia); - const insigniaHistoryRepo = manager.getRepository(ProfileInsigniaHistory); - const avatarRepository = manager.getRepository(ProfileAvatar); - - console.log( - "[ExecuteOfficerProfileService] Processing citizenId:", - item.bodyProfile.citizenId, - ); - const _null: any = null; - if (item.bodyProfile.posLevelId === "") item.bodyProfile.posLevelId = null; - if (item.bodyProfile.posTypeId === "") item.bodyProfile.posTypeId = null; - if ( - item.bodyProfile.posLevelId && - !(await posLevelRepo.findOneBy({ id: item.bodyProfile.posLevelId })) - ) { - console.error( - "[ExecuteOfficerProfileService] ไม่พบข้อมูลระดับตำแหน่งนี้ posLevelId:", - item.bodyProfile.posLevelId, - ); - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลระดับตำแหน่งนี้"); - } - if ( - item.bodyProfile.posTypeId && - !(await posTypeRepo.findOneBy({ id: item.bodyProfile.posTypeId })) - ) { - console.error( - "[ExecuteOfficerProfileService] ไม่พบข้อมูลประเภทตำแหน่งนี้ posTypeId:", - item.bodyProfile.posTypeId, - ); - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลประเภทตำแหน่งนี้"); - } - - console.log( - "[ExecuteOfficerProfileService] Processing citizenId:", - item.bodyProfile.citizenId, - ); - let registrationProvinceId = await provinceRepo.findOneBy({ - id: item.bodyProfile.registrationProvinceId ?? "", - }); - let registrationDistrictId = await districtRepo.findOneBy({ - id: item.bodyProfile.registrationDistrictId ?? "", - }); - let registrationSubDistrictId = await subDistrictRepo.findOneBy({ - id: item.bodyProfile.registrationSubDistrictId ?? "", - }); - let currentProvinceId = await provinceRepo.findOneBy({ - id: item.bodyProfile.currentProvinceId ?? "", - }); - let currentDistrictId = await districtRepo.findOneBy({ - id: item.bodyProfile.currentDistrictId ?? "", - }); - let currentSubDistrictId = await subDistrictRepo.findOneBy({ - id: item.bodyProfile.currentSubDistrictId ?? "", - }); - console.log("[ExecuteOfficerProfileService] Address validation completed"); - - let _dateRetire = - item.bodyProfile.birthDate == null - ? _null - : calculateRetireDate(item.bodyProfile.birthDate); - let _dateRetireLaw = - item.bodyProfile.birthDate == null - ? _null - : calculateRetireLaw(item.bodyProfile.birthDate); - - let userKeycloakId: any; - let result: any; - console.log( - "[ExecuteOfficerProfileService] Checking Keycloak user for citizenId:", - item.bodyProfile.citizenId, - ); - const checkUser = await getUserByUsername(item.bodyProfile.citizenId); - console.log( - "[ExecuteOfficerProfileService] Keycloak user exists:", - checkUser.length > 0, - ); - if (checkUser.length == 0) { - console.log("[ExecuteOfficerProfileService] Creating new Keycloak user"); - let password = item.bodyProfile.citizenId; - if (item.bodyProfile.birthDate != null) { - const _date = new Date(item.bodyProfile.birthDate.toDateString()) - .getDate() - .toString() - .padStart(2, "0"); - const _month = ( - new Date(item.bodyProfile.birthDate.toDateString()).getMonth() + 1 - ) - .toString() - .padStart(2, "0"); - const _year = new Date(item.bodyProfile.birthDate.toDateString()).getFullYear() + 543; - password = `${_date}${_month}${_year}`; - } - console.log( - "[ExecuteOfficerProfileService] Calling createUser for:", - item.bodyProfile.citizenId, - ); - console.log( - "[ExecuteOfficerProfileService] createUser data - firstName:", - item.bodyProfile.firstName, - "lastName:", - item.bodyProfile.lastName, - ); - // กรอง "." ออกจาก firstName ก่อนส่งไป keycloak (ป้องกัน . หรืออักขระอื่นๆ) - const sanitizedFirstName = item.bodyProfile.firstName?.replace(/\./g, "") ?? ""; - userKeycloakId = await createUser(item.bodyProfile.citizenId, password, { - firstName: sanitizedFirstName, - lastName: item.bodyProfile.lastName, - }); - if ( - userKeycloakId && - typeof userKeycloakId === "object" && - userKeycloakId.errorMessage - ) { - console.error( - "[ExecuteOfficerProfileService] createUser FAILED - field:", - userKeycloakId.field, - "errorMessage:", - userKeycloakId.errorMessage, - "params:", - userKeycloakId.params, - ); - throw new HttpError( - HttpStatusCode.BAD_REQUEST, - `Keycloak validation failed: ${userKeycloakId.field} - ${userKeycloakId.errorMessage}`, - ); - } - console.log( - "[ExecuteOfficerProfileService] User created in Keycloak, userKeycloakId:", - userKeycloakId, - ); - result = await addUserRoles( - userKeycloakId, - list - .filter((v) => v.name === "USER") - .map((x) => ({ - id: x.id, - name: x.name, - })), - ); - console.log( - "[ExecuteOfficerProfileService] USER role assigned to new user, result:", - result, - ); - } else { - console.log("[ExecuteOfficerProfileService] Updating existing Keycloak user"); - userKeycloakId = checkUser[0].id; - console.log( - "[ExecuteOfficerProfileService] Existing userKeycloakId:", - userKeycloakId, - ); - const rolesData = await getRoleMappings(userKeycloakId); - if (rolesData) { - const _delRole = rolesData.map((x: any) => ({ - id: x.id, - name: x.name, - })); - console.log( - "[ExecuteOfficerProfileService] Removing old roles:", - _delRole.length, - ); - await removeUserRoles(userKeycloakId, _delRole); - } - result = await addUserRoles( - userKeycloakId, - list - .filter((v) => v.name === "USER") - .map((x) => ({ - id: x.id, - name: x.name, - })), - ); - console.log( - "[ExecuteOfficerProfileService] USER role assigned to existing user", - ); - } - - let profile: any = await profileRepository.findOne({ - where: { citizenId: item.bodyProfile.citizenId /*, isActive: true */ }, - relations: ["roleKeycloaks", "profileInsignias", "profileAvatars"], - }); - console.log( - "[ExecuteOfficerProfileService] Profile found:", - !!profile, - "for citizenId:", - item.bodyProfile.citizenId, - ); - let _oldInsigniaIds: string[] = []; - let _oldSalaries: any[] = []; - //ลูกจ้างประจำ หรือ บุคคลภายนอก - if (!profile) { - console.log( - "[ExecuteOfficerProfileService] No existing profile found, creating new profile", - ); - //กรณีลูกจ้างประจำมาสอบเป็นข้าราชการ ต้อง update สถานะโปรไฟล์เดิม - let profileEmployee: any = await profileEmployeeRepository.findOne({ - where: { citizenId: item.bodyProfile.citizenId }, - relations: ["profileInsignias", "roleKeycloaks"], - }); - console.log( - "[ExecuteOfficerProfileService] Employee profile found:", - !!profileEmployee, - ); - if (profileEmployee) { - console.log( - "[ExecuteOfficerProfileService] Converting employee profile to officer profile", - ); - const _order = await salaryRepo.findOne({ - where: { profileEmployeeId: profileEmployee.id }, - order: { order: "DESC" }, - }); - const profileEmpSalary = new ProfileSalary(); - profileEmpSalary.posNumCodeSit = _posNumCodeSit; - profileEmpSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - profileEmpSalary.order = _order == null ? 1 : _order.order + 1; - Object.assign(profileEmpSalary, { - ...item.bodySalarys, - ...meta, - profileEmployeeId: profileEmployee.id, - profileId: undefined, - }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...profileEmpSalary, id: undefined }); - profileEmpSalary.dateGovernment = item.bodySalarys?.commandDateAffect ?? meta.createdAt; - (profileEmpSalary.profileId = _null), - await salaryRepo.save(profileEmpSalary, { data: req }); - setLogDataDiff(req, { before, after: profileEmpSalary }); - history.profileSalaryId = profileEmpSalary.id; - await salaryHistoryRepo.save(history, { data: req }); - - if (profileEmployee.profileInsignias.length > 0) { - _oldInsigniaIds = profileEmployee.profileInsignias?.map((x: any) => x.id) ?? []; - } - await removeProfileInOrganize(profileEmployee.id, "EMPLOYEE", manager); - if (profileEmployee.keycloak != null) { - // const delUserKeycloak = await deleteUser(profileEmployee.keycloak); - // if (delUserKeycloak) { - // Task #228 - // profileEmployee.keycloak = _null; - profileEmployee.roleKeycloaks = []; - profileEmployee.isActive = false; - // } - } - profileEmployee.isLeave = true; - profileEmployee.leaveReason = "บรรจุข้าราชการ"; - profileEmployee.lastUpdateUserId = ctx.user.sub; - profileEmployee.lastUpdateFullName = ctx.user.name; - profileEmployee.lastUpdatedAt = new Date(); - await profileEmployeeRepository.save(profileEmployee); - setLogDataDiff(req, { before, after: profileEmployee }); - } - profile = Object.assign({ ...item.bodyProfile, ...meta }); - profile.dateRetire = _dateRetire; - profile.dateRetireLaw = _dateRetireLaw; - profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; - profile.keycloak = - userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; - profile.registrationAddress = item.bodyProfile.registrationAddress; - profile.registrationProvinceId = registrationProvinceId - ? registrationProvinceId.id - : _null; - profile.registrationDistrictId = registrationDistrictId - ? registrationDistrictId.id - : _null; - profile.registrationSubDistrictId = registrationSubDistrictId - ? registrationSubDistrictId.id - : _null; - profile.registrationZipCode = item.bodyProfile.registrationZipCode; - profile.currentAddress = item.bodyProfile.currentAddress; - profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; - profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; - profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; - profile.currentZipCode = item.bodyProfile.currentZipCode; - profile.email = item.bodyProfile.email; - profile.dateStart = item.bodyProfile.dateStart; - profile.amount = item.bodyProfile.amount ?? null; - profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; - profile.isProbation = item.bodyProfile.isProbation; - //เพิ่มใหม่จากรับโอน - profile.rank = item?.bodyProfile?.rank || null; - profile.prefix = item?.bodyProfile?.rank || item?.bodyProfile?.prefix || null; - profile.prefixMain = item?.bodyProfile?.prefix ?? null; - profile.firstName = item.bodyProfile.firstName ?? null; - profile.lastName = item.bodyProfile.lastName ?? null; - profile.birthDate = item.bodyProfile.birthDate ?? null; - profile.gender = item.bodyProfile.gender ?? null; - profile.relationship = item.bodyProfile.relationship ?? null; - profile.religion = item.bodyProfile.religion ?? null; - profile.ethnicity = item.bodyProfile.ethnicity; - profile.nationality = item.bodyProfile.nationality ?? null; - profile.bloodGroup = item.bodyProfile.bloodGroup ?? null; - profile.phone = item.bodyProfile.phone ?? null; - - console.log("[ExecuteOfficerProfileService] Saving new profile"); - await profileRepository.save(profile); - console.log( - "[ExecuteOfficerProfileService] New profile saved, profileId:", - profile.id, - ); - // update user attribute in keycloak - await updateUserAttributes(profile.keycloak ?? "", { - profileId: [profile.id], - prefix: [profile.prefix || ""], - }); - console.log("[ExecuteOfficerProfileService] Keycloak attributes updated"); - setLogDataDiff(req, { before, after: profile }); - } - //ขรก.ในระบบ หรือ ขรก.ในระบบที่สถานะพ้นจากราชการ - else { - console.log( - "[ExecuteOfficerProfileService] Existing profile found, isLeave:", - profile.isLeave, - "leaveType:", - profile.leaveType, - ); - //สร้างโปรไฟล์ใหม่ ถ้าสถานะพ้นราชการ คำสั่งโอนออกหรือคำสั่งขอลาออก - if ( - profile.isLeave && - ["PLACEMENT_TRANSFER", "RETIRE_RESIGN"].includes(profile.leaveType) - ) { - console.log( - "[ExecuteOfficerProfileService] Profile is leaving with eligible leave type, creating new profile record", - ); - //ดึง profileSalary เดิม - _oldSalaries = await salaryRepo.find({ - where: { profileId: profile.id }, - order: { order: "ASC" }, - }); - if (profile.profileInsignias.length > 0) { - _oldInsigniaIds = profile.profileInsignias?.map((x: any) => x.id) ?? []; - } - profile = Object.assign({ ...item.bodyProfile, ...meta }); - profile.dateRetire = _dateRetire; - profile.dateRetireLaw = _dateRetireLaw; - profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; - profile.keycloak = - userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; - profile.registrationAddress = item.bodyProfile.registrationAddress; - profile.registrationProvinceId = registrationProvinceId - ? registrationProvinceId.id - : _null; - profile.registrationDistrictId = registrationDistrictId - ? registrationDistrictId.id - : _null; - profile.registrationSubDistrictId = registrationSubDistrictId - ? registrationSubDistrictId.id - : _null; - profile.registrationZipCode = item.bodyProfile.registrationZipCode; - profile.currentAddress = item.bodyProfile.currentAddress; - profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; - profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; - profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; - profile.currentZipCode = item.bodyProfile.currentZipCode; - profile.email = item.bodyProfile.email; - profile.dateStart = item.bodyProfile.dateStart; - profile.amount = item.bodyProfile.amount ?? null; - profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; - profile.isProbation = item.bodyProfile.isProbation; - profile.rank = item?.bodyProfile?.rank || null; - profile.prefix = item?.bodyProfile?.rank || item?.bodyProfile?.prefix || null; - profile.prefixMain = item?.bodyProfile?.prefix ?? null; - profile.firstName = item.bodyProfile.firstName ?? null; - profile.lastName = item.bodyProfile.lastName ?? null; - profile.birthDate = item.bodyProfile.birthDate ?? null; - profile.gender = item.bodyProfile.gender ?? null; - profile.relationship = item.bodyProfile.relationship ?? null; - profile.religion = item.bodyProfile.religion ?? null; - profile.ethnicity = item.bodyProfile.ethnicity; - profile.nationality = item.bodyProfile.nationality ?? null; - profile.bloodGroup = item.bodyProfile.bloodGroup ?? null; - profile.phone = item.bodyProfile.phone ?? null; - await profileRepository.save(profile); - console.log( - "[ExecuteOfficerProfileService] New profile record saved for leaving officer, profileId:", - profile.id, - ); - setLogDataDiff(req, { before, after: profile }); - } else { - console.log( - "[ExecuteOfficerProfileService] Updating existing active profile", - ); - profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; - profile.keycloak = - userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; - profile.isProbation = item.bodyProfile.isProbation; - profile.isLeave = item.bodyProfile.isLeave; - profile.isRetirement = false; - profile.isActive = true; - profile.isDelete = false; - profile.dateLeave = _null; - profile.dateRetire = _dateRetire; - profile.dateRetireLaw = _dateRetireLaw; - profile.registrationAddress = item.bodyProfile.registrationAddress; - profile.registrationProvinceId = registrationProvinceId - ? registrationProvinceId.id - : _null; - profile.registrationDistrictId = registrationDistrictId - ? registrationDistrictId.id - : _null; - profile.registrationSubDistrictId = registrationSubDistrictId - ? registrationSubDistrictId.id - : _null; - profile.registrationZipCode = item.bodyProfile.registrationZipCode; - profile.currentAddress = item.bodyProfile.currentAddress; - profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; - profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; - profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; - profile.currentZipCode = item.bodyProfile.currentZipCode; - profile.email = item.bodyProfile.email; - profile.telephoneNumber = item.bodyProfile.telephoneNumber; - profile.phone = item.bodyProfile.phone; - profile.dateStart = item.bodyProfile.dateStart; - profile.amount = item.bodyProfile.amount ?? null; - profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; - profile.leaveCommandId = _null; - profile.leaveCommandNo = _null; - profile.leaveRemark = _null; - profile.leaveDate = _null; - profile.leaveType = _null; - profile.leaveReason = _null; - profile.lastUpdateUserId = ctx.user.sub; - profile.lastUpdateFullName = ctx.user.name; - profile.lastUpdatedAt = new Date(); - //เพิ่มใหม่จากรับโอน - profile.rank = item?.bodyProfile?.rank || null; - profile.prefix = item?.bodyProfile?.rank || item?.bodyProfile?.prefix || null; - profile.prefixMain = item?.bodyProfile?.prefix ?? null; - profile.firstName = - item.bodyProfile.firstName && item.bodyProfile.firstName != "" - ? item.bodyProfile.firstName - : profile.firstName; - profile.lastName = - item.bodyProfile.lastName && item.bodyProfile.lastName != "" - ? item.bodyProfile.lastName - : profile.lastName; - profile.birthDate = item.bodyProfile.birthDate - ? item.bodyProfile.birthDate - : profile.birthDate; - profile.gender = - item.bodyProfile.gender && item.bodyProfile.gender != "" - ? item.bodyProfile.gender - : profile.gender; - profile.relationship = - item.bodyProfile.relationship && item.bodyProfile.relationship != "" - ? item.bodyProfile.relationship - : profile.relationship; - profile.religion = - item.bodyProfile.religion && item.bodyProfile.religion != "" - ? item.bodyProfile.religion - : profile.religion; - profile.ethnicity = - item.bodyProfile.ethnicity && item.bodyProfile.ethnicity != "" - ? item.bodyProfile.ethnicity - : profile.ethnicity; - profile.nationality = - item.bodyProfile.nationality && item.bodyProfile.nationality != "" - ? item.bodyProfile.nationality - : profile.nationality; - profile.bloodGroup = - item.bodyProfile.bloodGroup && item.bodyProfile.bloodGroup != "" - ? item.bodyProfile.bloodGroup - : profile.bloodGroup; - profile.phone = - item.bodyProfile.phone && item.bodyProfile.phone != "" - ? item.bodyProfile.phone - : profile.phone; - await profileRepository.save(profile); - console.log( - "[ExecuteOfficerProfileService] Existing active profile updated, profileId:", - profile.id, - ); - setLogDataDiff(req, { before, after: profile }); - } - } - - if (profile && profile.id) { - console.log( - "[ExecuteOfficerProfileService] Processing additional data for profileId:", - profile.id, - ); - //Educations - if (item.bodyEducations && item.bodyEducations.length > 0) { - console.log( - "[ExecuteOfficerProfileService] Processing educations, count:", - item.bodyEducations.length, - ); - for (const education of item.bodyEducations) { - const profileEdu = new ProfileEducation(); - Object.assign(profileEdu, { ...education, ...meta }); - const eduHistory = new ProfileEducationHistory(); - Object.assign(eduHistory, { ...profileEdu, id: undefined }); - profileEdu.profileId = profile.id; - const educationLevel = await profileEducationRepo.findOne({ - select: ["id", "level", "profileId"], - where: { profileId: profile.id, isDeleted: false }, - order: { level: "DESC" }, - }); - profileEdu.level = educationLevel == null ? 1 : educationLevel.level + 1; - await profileEducationRepo.save(profileEdu, { data: req }); - setLogDataDiff(req, { before, after: profileEdu }); - eduHistory.profileEducationId = profileEdu.id; - await profileEducationHistoryRepo.save(eduHistory, { data: req }); - } - } - //Certificates - if (item.bodyCertificates && item.bodyCertificates.length > 0) { - console.log( - "[ExecuteOfficerProfileService] Processing certificates, count:", - item.bodyCertificates.length, - ); - for (const cer of item.bodyCertificates) { - const profileCer = new ProfileCertificate(); - Object.assign(profileCer, { ...cer, ...meta }); - const cerHistory = new ProfileCertificateHistory(); - Object.assign(cerHistory, { ...profileCer, id: undefined }); - profileCer.profileId = profile.id; - await certificateRepo.save(profileCer, { data: req }); - setLogDataDiff(req, { before, after: profileCer }); - cerHistory.profileCertificateId = profileCer.id; - await certificateHistoryRepo.save(cerHistory, { data: req }); - } - } - //FamilyCouple - if (item.bodyMarry != null) { - console.log("[ExecuteOfficerProfileService] Processing couple/marry data"); - const profileCouple = new ProfileFamilyCouple(); - const data = { - profileId: profile.id, - couple: item.bodyMarry.marry, - couplePrefix: item.bodyMarry.marryPrefix, - coupleFirstName: item.bodyMarry.marryFirstName, - coupleLastName: item.bodyMarry.marryLastName, - coupleCareer: item.bodyMarry.marryOccupation, - coupleLive: true, - }; - Object.assign(profileCouple, { ...data, ...meta }); - const coupleHistory = new ProfileFamilyCoupleHistory(); - Object.assign(coupleHistory, { ...profileCouple, id: undefined }); - profileCouple.profileId = profile.id; - await profileFamilyCoupleRepo.save(profileCouple, { data: req }); - setLogDataDiff(req, { before, after: profileCouple }); - coupleHistory.profileFamilyCoupleId = profileCouple.id; - await profileFamilyCoupleHistoryRepo.save(coupleHistory, { data: req }); - } - //FamilyFather - if (item.bodyFather != null) { - console.log("[ExecuteOfficerProfileService] Processing father data"); - const profileFather = new ProfileFamilyFather(); - const data = { - profileId: profile.id, - fatherPrefix: item.bodyFather.fatherPrefix, - fatherFirstName: item.bodyFather.fatherFirstName, - fatherLastName: item.bodyFather.fatherLastName, - fatherCareer: item.bodyFather.fatherOccupation, - fatherLive: true, - }; - Object.assign(profileFather, { ...data, ...meta }); - const fatherHistory = new ProfileFamilyFatherHistory(); - Object.assign(fatherHistory, { ...profileFather, id: undefined }); - profileFather.profileId = profile.id; - await profileFamilyFatherRepo.save(profileFather, { data: req }); - setLogDataDiff(req, { before, after: profileFather }); - fatherHistory.profileFamilyFatherId = profileFather.id; - await profileFamilyFatherHistoryRepo.save(fatherHistory, { data: req }); - } - //FamilyMother - if (item.bodyMother != null) { - console.log("[ExecuteOfficerProfileService] Processing mother data"); - const profileMother = new ProfileFamilyMother(); - const data = { - profileId: profile.id, - motherPrefix: item.bodyMother.motherPrefix, - motherFirstName: item.bodyMother.motherFirstName, - motherLastName: item.bodyMother.motherLastName, - motherCareer: item.bodyMother.motherOccupation, - motherLive: true, - }; - Object.assign(profileMother, { ...data, ...meta }); - const motherHistory = new ProfileFamilyMotherHistory(); - Object.assign(motherHistory, { ...profileMother, id: undefined }); - profileMother.profileId = profile.id; - await profileFamilyMotherRepo.save(profileMother, { data: req }); - setLogDataDiff(req, { before, after: profileMother }); - motherHistory.profileFamilyMotherId = profileMother.id; - await profileFamilyMotherHistoryRepo.save(motherHistory, { data: req }); - } - //Salary - //insert profileSalary อันเก่า กรณีพ้นราชการแล้วกลับมาบรรจุ - if (_oldSalaries.length > 0) { - console.log( - "[ExecuteOfficerProfileService] Restoring old salaries, count:", - _oldSalaries.length, - ); - for (const oldSal of _oldSalaries) { - const profileSal: any = new ProfileSalary(); - Object.assign(profileSal, { ...oldSal, ...meta }); - const salaryHistory = new ProfileSalaryHistory(); - Object.assign(salaryHistory, { ...profileSal, id: undefined }); - profileSal.profileId = profile.id; - await salaryRepo.save(profileSal, { data: req }); - setLogDataDiff(req, { before, after: profileSal }); - salaryHistory.profileSalaryId = profileSal.id; - await salaryHistoryRepo.save(salaryHistory, { data: req }); - } - } - //insert item.bodySalarys ต่อจากที่ insert เดิมไปแล้ว - if (item.bodySalarys && item.bodySalarys != null) { - console.log("[ExecuteOfficerProfileService] Processing new salary data"); - const dest_item = await salaryRepo.findOne({ - where: { profileId: profile.id }, - order: { order: "DESC" }, - }); - const profileSal: any = new ProfileSalary(); - profileSal.posNumCodeSit = _posNumCodeSit; - profileSal.posNumCodeSitAbb = _posNumCodeSitAbb; - Object.assign(profileSal, { ...item.bodySalarys, ...meta }); - const salaryHistory = new ProfileSalaryHistory(); - Object.assign(salaryHistory, { ...profileSal, id: undefined }); - profileSal.order = dest_item == null ? 1 : dest_item.order + 1; - profileSal.profileId = profile.id; - profileSal.dateGovernment = item.bodySalarys.commandDateAffect ?? meta.createdAt; - profileSal.amount = item.bodySalarys.amount ?? null; - profileSal.amountSpecial = item.bodySalarys.amountSpecial ?? null; - profileSal.positionSalaryAmount = item.bodySalarys.positionSalaryAmount ?? null; - profileSal.mouthSalaryAmount = item.bodySalarys.mouthSalaryAmount ?? null; - await salaryRepo.save(profileSal, { data: req }); - setLogDataDiff(req, { before, after: profileSal }); - salaryHistory.profileSalaryId = profileSal.id; - await salaryHistoryRepo.save(salaryHistory, { data: req }); - } - //Position - if (item.bodyPosition && item.bodyPosition != null) { - console.log("[ExecuteOfficerProfileService] Processing position assignment"); - // STEP 1: หา posMaster ที่จะใช้งานตาม id ที่ส่งมา (อาจเป็นตำแหน่งเก่าหรือใหม่ก็ได้) - console.log( - "[ExecuteOfficerProfileService] STEP 1: Finding posMaster, posmasterId:", - item.bodyPosition.posmasterId, - ); - let posMaster = await posMasterRepository.findOne({ - where: { - id: item.bodyPosition.posmasterId, - }, - relations: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - console.log("[ExecuteOfficerProfileService] posMaster found:", !!posMaster); - - // เช็คว่า posMaster ที่หามาอยู่ในโครงสร้างปัจจุบันหรือไม่ - const isCurrent = - posMaster?.orgRevision?.orgRevisionIsCurrent === true && - posMaster?.orgRevision?.orgRevisionIsDraft === false; - console.log("[ExecuteOfficerProfileService] posMaster isCurrent:", isCurrent); - - // ถ้าไม่อยู่ในโครงสร้างปัจจุบัน ให้หาตัวใหม่จาก ancestorDNA - if (!isCurrent && posMaster?.ancestorDNA) { - console.log( - "[ExecuteOfficerProfileService] Finding current posMaster from ancestorDNA", - ); - posMaster = await posMasterRepository.findOne({ - where: { - ancestorDNA: posMaster.ancestorDNA, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - console.log( - "[ExecuteOfficerProfileService] Current posMaster from ancestorDNA found:", - !!posMaster, - ); - } - - if (posMaster == null) { - console.error( - `[ExecuteOfficerProfileService] not found posMasterId: ${item.bodyPosition.posmasterId}`, - ); - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); - } - - // STEP 2: เคลียร์ข้อมูลตำแหน่งเก่าที่ครองอยู่ ในโครงสร้างปัจจุบัน - console.log("[ExecuteOfficerProfileService] STEP 2: Clearing old position data"); - const posMasterOld = await posMasterRepository.findOne({ - where: { - current_holderId: profile.id, - orgRevisionId: posMaster.orgRevisionId, - }, - }); - if (posMasterOld != null) { - // เคลียร์คนครองเก่าออกจากตำแหน่งเดิม - posMasterOld.current_holderId = null; - posMasterOld.lastUpdatedAt = new Date(); - } - - // หา position เก่าที่เลือกไว้ แล้วเคลียร์การเลือก - const positionOld = await positionRepository.findOne({ - where: { - posMasterId: posMasterOld?.id, - positionIsSelected: true, - }, - }); - if (positionOld != null) { - positionOld.positionIsSelected = false; - await positionRepository.save(positionOld); - } - - // STEP 3: เคลียร์ position ที่เลือกไว้อื่นๆ ใน posMaster ตัวใหม่ - console.log( - "[ExecuteOfficerProfileService] STEP 3: Clearing other selected positions in new posMaster", - ); - const checkPosition = await positionRepository.find({ - where: { - posMasterId: posMaster.id, - positionIsSelected: true, - }, - }); - if (checkPosition.length > 0) { - const clearPosition = checkPosition.map((positions) => ({ - ...positions, - positionIsSelected: false, - })); - await positionRepository.save(clearPosition); - } - - // STEP 4: กำหนดคนครองใหม่ให้กับ posMaster - console.log( - "[ExecuteOfficerProfileService] STEP 4: Assigning new holder to posMaster", - ); - posMaster.current_holderId = profile.id; - posMaster.lastUpdatedAt = new Date(); - // posMaster.conditionReason = _null; - // posMaster.isCondition = false; - if (posMasterOld != null) { - await posMasterRepository.save(posMasterOld); - console.log( - `[ExecuteOfficerProfileService] Creating PosMasterHistory — posMasterId: ${posMasterOld.id}, citizenId: ${item.bodyProfile?.citizenId} (old)`, - ); - await CreatePosMasterHistoryOfficer(posMasterOld.id, req, null, null, manager); - } - await posMasterRepository.save(posMaster); - console.log("[ExecuteOfficerProfileService] posMaster saved with new holder"); - - // STEP 5: กำหนด position ใหม่ - console.log( - "[ExecuteOfficerProfileService] STEP 5: Determining position to assign", - ); - // Match position ตามลำดับ priority: - // Condition 1: match จาก positionId - // Condition 2: match 7 ฟิลด์ (positionName, posTypeId, posLevelId, positionField, positionArea, positionExecutiveField, posExecutiveId) - // Condition 3: match 3 ฟิลด์ (positionName, posTypeId, posLevelId) - // Fallback: เลือก position แรกใน posMaster - - let positionNew: Position | null = null; - - // ═══════════════════════════════════════════════════════════ - // CONDITION 1: เช็คจาก positionId ตรง - // ═══════════════════════════════════════════════════════════ - console.log( - "[ExecuteOfficerProfileService] CONDITION 1: Checking by positionId:", - item.bodyPosition?.positionId, - ); - if (item.bodyPosition?.positionId) { - const positionById = await positionRepository.findOne({ - where: { - id: item.bodyPosition.positionId, - posMasterId: posMaster.id, // ต้องอยู่ใน posMaster ที่ถูกต้อง - }, - relations: ["posExecutive"], - }); - - if (positionById) { - positionNew = positionById; - console.log( - "[ExecuteOfficerProfileService] CONDITION 1 matched, positionId:", - positionById.id, - ); - } - } - - // ═══════════════════════════════════════════════════════════ - // CONDITION 2: Match 7 ฟิลด์ (ถ้า Condition 1 ไม่ match) - // ═══════════════════════════════════════════════════════════ - if (!positionNew && item.bodyPosition) { - console.log( - "[ExecuteOfficerProfileService] CONDITION 1 not matched, trying CONDITION 2: Match 7 fields", - ); - // สร้าง where clause แบบ dynamic - ใส่เฉพาะฟิลด์ที่ไม่ใช่ null - const whereCondition: any = { - posMasterId: posMaster.id, - positionName: item.bodyPosition.positionName, - posTypeId: item.bodyPosition.posTypeId, - posLevelId: item.bodyPosition.posLevelId, - }; - - if (item.bodyPosition.positionField) { - whereCondition.positionField = item.bodyPosition.positionField; - } - if (item.bodyPosition.posExecutiveId) { - whereCondition.posExecutiveId = item.bodyPosition.posExecutiveId; - } - if (item.bodyPosition.positionExecutiveField) { - whereCondition.positionExecutiveField = item.bodyPosition.positionExecutiveField; - } - if (item.bodyPosition.positionArea) { - whereCondition.positionArea = item.bodyPosition.positionArea; - } - - const positionBy7Fields = await positionRepository.findOne({ - where: whereCondition, - relations: ["posExecutive"], - order: { orderNo: "ASC" }, - }); - - if (positionBy7Fields) { - positionNew = positionBy7Fields; - console.log( - "[ExecuteOfficerProfileService] CONDITION 2 matched with 7 fields, positionId:", - positionBy7Fields.id, - ); - } - } - - // ═══════════════════════════════════════════════════════════ - // CONDITION 3: Match 3 ฟิลด์ (ถ้า Condition 2 ไม่ match) - // ═══════════════════════════════════════════════════════════ - if (!positionNew && item.bodyPosition) { - console.log( - "[ExecuteOfficerProfileService] CONDITION 2 not matched, trying CONDITION 3: Match 3 fields", - ); - const positionBy3Fields = await positionRepository.findOne({ - where: { - posMasterId: posMaster.id, - positionName: item.bodyPosition.positionName, - posTypeId: item.bodyPosition.posTypeId, - posLevelId: item.bodyPosition.posLevelId, - }, - relations: ["posExecutive"], - order: { orderNo: "ASC" }, - }); - - if (positionBy3Fields) { - positionNew = positionBy3Fields; - console.log( - "[ExecuteOfficerProfileService] CONDITION 3 matched with 3 fields, positionId:", - positionBy3Fields.id, - ); - } else { - console.log( - "[ExecuteOfficerProfileService] No position matched for profileId:", - profile.id, - ); - } - } - - // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit - profile.posMasterNo = getPosMasterNo(posMaster); - profile.org = getOrgFullName(posMaster); - // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ - if (positionNew != null) { - console.log( - "[ExecuteOfficerProfileService] Final position assignment, isSit:", - posMaster.isSit, - "positionId:", - positionNew.id, - ); - positionNew.positionIsSelected = true; - if (!posMaster.isSit) { - profile.posLevelId = positionNew.posLevelId; - profile.posTypeId = positionNew.posTypeId; - profile.position = positionNew.positionName; - profile.positionField = positionNew.positionField ?? null; - profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; - profile.positionArea = positionNew.positionArea ?? null; - profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; - // profile.dateStart = new Date(); - } - await positionRepository.save(positionNew, { data: req }); - } else if (!posMaster.isSit) { - // fallback: ตำแหน่งในโครงสร้างถูกแก้ไข ใช้ข้อมูลตำแหน่งที่สมัครสอบมา - console.log( - "[ExecuteOfficerProfileService] positionNew is null, using bodyPosition data as fallback", - ); - profile.position = item.bodyPosition.positionName ?? null; - profile.posTypeId = item.bodyPosition.posTypeId ?? null; - profile.posLevelId = item.bodyPosition.posLevelId ?? null; - profile.positionField = item.bodyPosition.positionField ?? null; - profile.positionArea = item.bodyPosition.positionArea ?? null; - profile.positionExecutiveField = item.bodyPosition.positionExecutiveField ?? null; - } - await profileRepository.save(profile, { data: req }); - setLogDataDiff(req, { before, after: profile }); - // await CreatePosMasterHistoryOfficer(posMaster.id, req); - console.log( - `[ExecuteOfficerProfileService] Creating PosMasterHistory — posMasterId: ${posMaster.id}, citizenId: ${item.bodyProfile?.citizenId}`, - ); - await CreatePosMasterHistoryOfficer(posMaster.id, req, null, { - positionId: positionNew?.id, - }, manager); - } - // Insignia - if (_oldInsigniaIds.length > 0) { - console.log( - "[ExecuteOfficerProfileService] Processing old insignias, count:", - _oldInsigniaIds.length, - ); - const _insignias = await insigniaRepo.find({ - where: { id: In(_oldInsigniaIds), isDeleted: false }, - order: { createdAt: "ASC" }, - }); - for (const oldInsignia of _insignias) { - const newInsigniaData: CreateProfileInsignia = { - profileId: profile.id, - year: oldInsignia.year, - no: oldInsignia.no, - volume: oldInsignia.volume, - section: oldInsignia.section, - page: oldInsignia.page, - receiveDate: oldInsignia.receiveDate, - insigniaId: oldInsignia.insigniaId, - dateAnnounce: oldInsignia.dateAnnounce, - issue: oldInsignia.issue, - volumeNo: oldInsignia.volumeNo, - refCommandDate: oldInsignia.refCommandDate, - refCommandNo: oldInsignia.refCommandNo, - note: oldInsignia.note, - isUpload: oldInsignia.isUpload, - }; - const insignia = new ProfileInsignia(); - Object.assign(insignia, { ...newInsigniaData, ...meta }); - const history = new ProfileInsigniaHistory(); - Object.assign(history, { ...insignia, id: undefined }); - await insigniaRepo.save(insignia, { data: req }); - setLogDataDiff(req, { before, after: insignia }); - history.profileInsigniaId = insignia.id; - await insigniaHistoryRepo.save(history, { data: req }); - } - } - // เพิ่มรูปภาพโปรไฟล์ - if (item.bodyProfile.objectRefId) { - console.log( - "[ExecuteOfficerProfileService] Processing profile avatar image, objectRefId:", - item.bodyProfile.objectRefId, - ); - const _profileAvatar = new ProfileAvatar(); - Object.assign(_profileAvatar, { - ...meta, - profileId: profile.id, - profileEmployeeId: undefined, - }); - if (profile.profileAvatars && profile.profileAvatars.length > 0) { - for (const avatarItem of profile.profileAvatars) { - avatarItem.isActive = false; - await avatarRepository.save(avatarItem); - } - } - await avatarRepository.save(_profileAvatar); - let avatar = `ทะเบียนประวัติ/โปรไฟล์/${profile.id}`; - let fileName = `profile-${_profileAvatar.id}`; - _profileAvatar.isActive = true; - _profileAvatar.avatar = avatar; - _profileAvatar.avatarName = fileName; - await avatarRepository.save(_profileAvatar, { data: req }); - profile.avatar = avatar; - profile.avatarName = fileName; - await profileRepository.save(profile, { data: req }); - const checkAvatar = await avatarRepository.findOne({ - where: { avatar: avatar, avatarName: fileName }, - }); - if (checkAvatar && checkAvatar.profileId == null) { - checkAvatar.profileId = profile.id; - await avatarRepository.save(checkAvatar); - } - //duplicate รูปภาพโปรไฟล์โดยอิงจากรูปภาพเดิม - await new CallAPI() - .PostData(req, `/salary/file/avatar/${item.bodyProfile.objectRefId}`, { - prefix: avatar, - fileName: fileName, - }) - .then(() => {}) - .catch(() => {}); - } - } - - console.log( - `[ExecuteOfficerProfileService] Completed processOne — citizenId: ${item.bodyProfile?.citizenId}`, - ); - } -} diff --git a/src/services/ExecuteOrgCommandService.ts b/src/services/ExecuteOrgCommandService.ts deleted file mode 100644 index 62ed231f..00000000 --- a/src/services/ExecuteOrgCommandService.ts +++ /dev/null @@ -1,860 +0,0 @@ -import { Double, EntityManager, In, Like } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatusCode from "../interfaces/http-status"; -import Extension from "../interfaces/extension"; -import CallAPI from "../interfaces/call-api"; -import { setLogDataDiff } from "../interfaces/utils"; -import { - CreatePosMasterHistoryEmployee, - CreatePosMasterHistoryEmployeeTemp, -} from "./PositionService"; -import { - addUserRoles, - createUser, - getRoles, - getUserByUsername, - getRoleMappings, -} from "../keycloak"; -import { Command } from "../entities/Command"; -import { Profile } from "../entities/Profile"; -import { ProfileEmployee } from "../entities/ProfileEmployee"; -import { OrgRoot } from "../entities/OrgRoot"; -import { OrgRevision } from "../entities/OrgRevision"; -import { RoleKeycloak } from "../entities/RoleKeycloak"; -import { EmployeePosMaster } from "../entities/EmployeePosMaster"; -import { EmployeeTempPosMaster } from "../entities/EmployeeTempPosMaster"; -import { EmployeePosition } from "../entities/EmployeePosition"; -import { PosMaster } from "../entities/PosMaster"; -import { Position } from "../entities/Position"; -import { ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { PosMasterAct } from "../entities/PosMasterAct"; -import { ProfileActposition } from "../entities/ProfileActposition"; -import { ProfileActpositionHistory } from "../entities/ProfileActpositionHistory"; -import { promisify } from "util"; - -const redis = require("redis"); -const REDIS_HOST = process.env.REDIS_HOST; -const REDIS_PORT = process.env.REDIS_PORT; - -/** - * Input: refIds ที่ consumer ใน rabbitmq build ขึ้น (เดิมคือ body.refIds ของ endpoint /excecute) - * ใช้กับ C-PM-21, C-PM-38, C-PM-40 - */ -export interface CommandRefItem { - refId: string; - commandId?: string | null; - amount: Double | null; - amountSpecial?: Double | null; - positionSalaryAmount: Double | null; - mouthSalaryAmount: Double | null; - commandNo: string | null; - commandYear: number; - commandDateAffect?: Date | string | null; - commandDateSign?: Date | string | null; - commandCode?: string | null; - commandName?: string | null; - remark: string | null; -} - -/** - * Context สำหรับ audit/log (เหมือน ExecuteSalaryService) - */ -export interface OrgCommandExecutionContext { - user: { sub: string; name: string }; - req?: any; -} - -/** - * Service สำหรับคำสั่งที่เดิม "ยิงเข้าตัว" (HTTP loopback เข้า org เอง) - * - * ใช้กับ commandType: - * - C-PM-21 : command21/employee/report/excecute (ลูกจ้าง → พนักงานประจำ) - * - C-PM-38 : command38/officer/report/excecute (เงินเดือน next_holder ข้าราชการ) - * - C-PM-40 : command40/officer/report/excecute (รักษาการ) - * - * - endpoint commandXX/.../excecute ทั้ง 3 เรียกผ่าน service นี้ (thin wrapper) - * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (Linear Flow / ทำต่อ) - * แทนการ PostData(path + "/excecute") ที่เป็น HTTP loopback เข้า org ตัวเอง - * - * Behavior ทั้งหมด preserve จาก CommandController ต้นฉบับ - * - * Batch semantics: all-or-nothing — ประมวลผลทุกคนภายใต้ transaction เดียว (sequential) - * ถ้าคนใด throw จะ rollback ทั้ง batch และ propagate error ออกไป (ล้มเหลวทั้งหมด) - * - * ⚠️ หมายเหตุ side-effect ที่อยู่นอก DB transaction: - * - Keycloak operations (createUser/addUserRoles/getRoleMappings) ใน C-PM-21 ทำภายใน transaction - * เพื่อ preserve behavior เดิม — Keycloak ไม่สามารถ rollback ได้ ถ้า DB rollback หลังจากนี้ - * Keycloak จะถูกเปลี่ยนไปแล้ว - * - .NET call (C-PM-21) ทำหลัง transaction commit แล้ว เพราะ .NET ไม่สามารถ rollback ได้ - * - Redis cache clear (C-PM-40) ทำหลัง transaction commit (เป็นการ del cache key — idempotent) - * - CreatePosMasterHistoryEmployeeTemp สร้าง nested transaction ของตัวเอง (ไม่รับ manager) - */ -export class ExecuteOrgCommandService { - private commandRepository = AppDataSource.getRepository(Command); - private profileRepository = AppDataSource.getRepository(Profile); - private profileEmployeeRepository = AppDataSource.getRepository(ProfileEmployee); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - private orgRevisionRepository = AppDataSource.getRepository(OrgRevision); - private roleKeycloakRepo = AppDataSource.getRepository(RoleKeycloak); - private posMasterRepository = AppDataSource.getRepository(PosMaster); - private posMasterActRepository = AppDataSource.getRepository(PosMasterAct); - - // ───────────────────────────────────────────────────────────── - // แก้ปัญหา _posNumCodeSit/_command resolution ที่ซ้ำกันในทุก endpoint - // (เดิมอยู่ใน controller — ย้ายมานี่ ทำครั้งเดียวก่อนเข้า transaction) - // ───────────────────────────────────────────────────────────── - private async resolvePosNumCodeSit( - commandId: string | null | undefined, - ): Promise<{ command: Command | null; posNumCodeSit: string; posNumCodeSitAbb: string }> { - let posNumCodeSit = ""; - let posNumCodeSitAbb = ""; - const command = commandId - ? await this.commandRepository.findOne({ where: { id: commandId } }) - : null; - if (command) { - if (command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; - } else if (command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - posNumCodeSit = "กรุงเทพมหานคร"; - posNumCodeSitAbb = "กทม."; - } else { - let profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - posNumCodeSit = - profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - posNumCodeSitAbb = - profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - } - } - return { command, posNumCodeSit, posNumCodeSitAbb }; - } - - // ═══════════════════════════════════════════════════════════════ - // C-PM-21 : command21/employee/report/excecute - // ลูกจ้างชั่วคราว → พนักงานประจำ (บรรจุ) - // ═══════════════════════════════════════════════════════════════ - /** - * @returns profileEmps ที่จะส่งต่อให้ .NET (สำหรับ consumer rabbitmq เรียก .NET เอง) - * ถ้าเรียกจาก thin-wrapper endpoint จะเรียก .NET ภายใน method นี้เอง - */ - async executeCommand21Employee( - data: CommandRefItem[], - ctx: OrgCommandExecutionContext, - options?: { callDotNet?: boolean }, - ): Promise<{ profileEmps: any[] }> { - const req = ctx.req; - const callDotNet = options?.callDotNet ?? true; - const commandId = data?.find((x) => x.commandId)?.commandId ?? ""; - console.log( - `[ExecuteOrgCommandService] executeCommand21Employee — commandId: ${commandId}, count: ${data?.length ?? 0}`, - ); - - const roleKeycloak = await this.roleKeycloakRepo.findOne({ - where: { name: Like("USER") }, - }); - const { command: _command, posNumCodeSit: _posNumCodeSit, posNumCodeSitAbb: _posNumCodeSitAbb } = - await this.resolvePosNumCodeSit(commandId); - - const profileEmps: any[] = []; - - await AppDataSource.transaction(async (manager) => { - for (const item of data ?? []) { - try { - await this.processOneCommand21( - item, - ctx, - manager, - roleKeycloak, - _command, - _posNumCodeSit, - _posNumCodeSitAbb, - profileEmps, - ); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteOrgCommandService] Failed C-PM-21, commandId=${commandId}, refId=${item.refId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - - // .NET call ทำหลัง commit (เหมือน endpoint เดิมที่เรียกหลัง Promise.all) — .NET ไม่ rollback ได้ - if (callDotNet && profileEmps.length > 0) { - await new CallAPI() - .PostData(req, "/placement/appointment/employee-appoint-21/report/excecute", { - profileEmps, - }) - .catch((error) => { - throw new Error(`Failed. Cannot update status. ${error?.message ?? ""}`); - }); - } - - console.log( - `[ExecuteOrgCommandService] Completed C-PM-21 — ${profileEmps.length} profiles sent to .NET`, - ); - return { profileEmps }; - } - - private async processOneCommand21( - item: CommandRefItem, - ctx: OrgCommandExecutionContext, - manager: EntityManager, - roleKeycloak: RoleKeycloak | null, - _command: Command | null, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - profileEmps: any[], - ): Promise { - const req = ctx.req; - - const profileEmployeeRepository = manager.getRepository(ProfileEmployee); - const employeePosMasterRepository = manager.getRepository(EmployeePosMaster); - const employeeTempPosMasterRepository = manager.getRepository(EmployeeTempPosMaster); - const employeePositionRepository = manager.getRepository(EmployeePosition); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - - const profile = await profileEmployeeRepository.findOne({ - where: { id: item.refId }, - relations: ["roleKeycloaks"], - }); - if (!profile) { - throw new HttpError(HttpStatusCode.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); - } - const orgRevision = await this.orgRevisionRepository.findOne({ - where: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }); - const _posMaster = await employeePosMasterRepository.findOne({ - where: { - orgRevisionId: orgRevision?.id, - id: profile.posmasterIdTemp, - }, - relations: { - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - const orgRootRef = _posMaster?.orgRoot ?? null; - const orgChild1Ref = _posMaster?.orgChild1 ?? null; - const orgChild2Ref = _posMaster?.orgChild2 ?? null; - const orgChild3Ref = _posMaster?.orgChild3 ?? null; - const orgChild4Ref = _posMaster?.orgChild4 ?? null; - let orgShortName = ""; - if (_posMaster != null) { - if (_posMaster.orgChild1Id === null) { - orgShortName = _posMaster.orgRoot?.orgRootShortName; - } else if (_posMaster.orgChild2Id === null) { - orgShortName = _posMaster.orgChild1?.orgChild1ShortName; - } else if (_posMaster.orgChild3Id === null) { - orgShortName = _posMaster.orgChild2?.orgChild2ShortName; - } else if (_posMaster.orgChild4Id === null) { - orgShortName = _posMaster.orgChild3?.orgChild3ShortName; - } else { - orgShortName = _posMaster.orgChild4?.orgChild4ShortName; - } - } - const dest_item = await salaryRepo.findOne({ - where: { profileEmployeeId: item.refId }, - order: { order: "DESC" }, - }); - const before = null; - const dataSalary = new ProfileSalary(); - dataSalary.posNumCodeSit = _posNumCodeSit; - dataSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - profileEmployeeId: profile.id, - amount: item.amount, - amountSpecial: item.amountSpecial, - positionSalaryAmount: item.positionSalaryAmount, - mouthSalaryAmount: item.mouthSalaryAmount, - position: profile.positionTemp, - positionName: profile.positionTemp, - positionType: profile.posTypeNameTemp, - positionLevel: profile.posLevelNameTemp, - order: dest_item == null ? 1 : dest_item.order + 1, - orgRoot: orgRootRef?.orgRootName ?? null, - orgChild1: orgChild1Ref?.orgChild1Name ?? null, - orgChild2: orgChild2Ref?.orgChild2Name ?? null, - orgChild3: orgChild3Ref?.orgChild3Name ?? null, - orgChild4: orgChild4Ref?.orgChild4Name ?? null, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - commandNo: item.commandNo, - commandYear: item.commandYear, - posNo: profile.posMasterNoTemp ?? "", - posNoAbb: orgShortName, - commandDateAffect: item.commandDateAffect, - commandDateSign: item.commandDateSign, - commandCode: item.commandCode, - commandName: item.commandName, - remark: item.remark, - }; - - Object.assign(dataSalary, meta); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...dataSalary, id: undefined }); - - await salaryRepo.save(dataSalary, { data: req }); - setLogDataDiff(req, { before, after: dataSalary }); - history.profileSalaryId = dataSalary.id; - await salaryHistoryRepo.save(history, { data: req }); - - const posMaster = await employeePosMasterRepository.findOne({ - where: { id: profile.posmasterIdTemp }, - relations: ["orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"], - }); - if (posMaster == null) - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); - - const posMasterOld = await employeePosMasterRepository.findOne({ - where: { - current_holderId: profile.id, - orgRevisionId: posMaster.orgRevisionId, - }, - }); - if (posMasterOld != null) { - posMasterOld.current_holderId = null; - posMasterOld.lastUpdatedAt = new Date(); - } - - const positionOld = await employeePositionRepository.findOne({ - where: { - posMasterId: posMasterOld?.id, - positionIsSelected: true, - }, - }); - if (positionOld != null) { - positionOld.positionIsSelected = false; - await employeePositionRepository.save(positionOld); - } - - const checkPosition = await employeePositionRepository.find({ - where: { - posMasterId: profile.posmasterIdTemp, - positionIsSelected: true, - }, - }); - if (checkPosition.length > 0) { - const clearPosition = checkPosition.map((positions) => ({ - ...positions, - positionIsSelected: false, - })); - await employeePositionRepository.save(clearPosition); - } - - posMaster.current_holderId = profile.id; - posMaster.lastUpdatedAt = new Date(); - posMaster.next_holderId = null; - if (posMasterOld != null) { - await employeePosMasterRepository.save(posMasterOld); - await CreatePosMasterHistoryEmployee(posMasterOld.id, req, undefined, manager); - } - await employeePosMasterRepository.save(posMaster); - await CreatePosMasterHistoryEmployee(posMaster.id, req, undefined, manager); - - const clsTempPosmaster = await employeeTempPosMasterRepository.find({ - where: { - current_holderId: profile.id, - orgRevisionId: posMaster.orgRevisionId, - }, - }); - - if (clsTempPosmaster.length > 0) { - const clearTempPosmaster = clsTempPosmaster.map((posMasterTemp) => ({ - ...posMasterTemp, - current_holderId: null, - next_holderId: null, - })); - await employeeTempPosMasterRepository.save(clearTempPosmaster); - - const checkTempPosition = await employeePositionRepository.find({ - where: { - posMasterTempId: In(clearTempPosmaster.map((x) => x.id)), - positionIsSelected: true, - }, - }); - if (checkTempPosition.length > 0) { - const clearTempPosition = checkTempPosition.map((positions) => ({ - ...positions, - positionIsSelected: false, - })); - await employeePositionRepository.save(clearTempPosition); - } - await Promise.all( - clsTempPosmaster.map( - async (posMasterTemp) => - await CreatePosMasterHistoryEmployeeTemp(posMasterTemp.id, req), - ), - ); - } - - const positionNew = await employeePositionRepository.findOne({ - where: { - id: profile.positionIdTemp, - posMasterId: profile.posmasterIdTemp, - }, - }); - - if (positionNew != null) { - // Create Keycloak - const checkUser = await getUserByUsername(profile.citizenId); - if (checkUser.length == 0) { - let password = profile.citizenId; - if (profile.birthDate != null) { - const _date = new Date(profile.birthDate.toDateString()) - .getDate() - .toString() - .padStart(2, "0"); - const _month = (new Date(profile.birthDate.toDateString()).getMonth() + 1) - .toString() - .padStart(2, "0"); - const _year = new Date(profile.birthDate.toDateString()).getFullYear() + 543; - password = `${_date}${_month}${_year}`; - } - // กรอง "." ออกจาก firstName ก่อนส่งไป keycloak - const sanitizedFirstName = profile.firstName?.replace(/\./g, "") ?? ""; - const userKeycloakId = await createUser(profile.citizenId, password, { - firstName: sanitizedFirstName, - lastName: profile.lastName, - }); - const list = await getRoles(); - if (!Array.isArray(list)) - throw new Error("Failed. Cannot get role(s) data from the server."); - const result = await addUserRoles( - userKeycloakId, - list - .filter((v) => v.name === "USER") - .map((x) => ({ - id: x.id, - name: x.name, - })), - ); - profile.keycloak = - userKeycloakId && typeof userKeycloakId == "string" ? userKeycloakId : ""; - profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; - // End Create Keycloak - } else { - const rolesData = await getRoleMappings(checkUser[0].id); - if (rolesData) { - const _roleKeycloak = await this.roleKeycloakRepo.find({ - where: { name: In(rolesData.map((x: any) => x.name)) }, - }); - profile.roleKeycloaks = - _roleKeycloak && _roleKeycloak.length > 0 ? _roleKeycloak : []; - } - profile.keycloak = checkUser[0].id; - } - positionNew.positionIsSelected = true; - profile.posLevelId = positionNew.posLevelId; - profile.posTypeId = positionNew.posTypeId; - profile.position = positionNew.positionName; - profile.employeeOc = posMaster?.orgRoot?.orgRootName ?? null; - profile.positionEmployeePositionId = positionNew.positionName; - profile.statusTemp = "DONE"; - profile.employeeClass = "PERM"; - const _null: any = null; - profile.employeeWage = item.amount == null ? _null : item.amount.toString(); - profile.dateStart = _command ? _command.commandExcecuteDate : new Date(); - profile.dateAppoint = _command ? _command.commandExcecuteDate : new Date(); - profile.amount = item.amount == null ? _null : item.amount; - profile.amountSpecial = item.amountSpecial == null ? _null : item.amountSpecial; - profileEmps.push({ - profileId: profile.id, - prefix: profile.prefix, - firstName: profile.firstName, - lastName: profile.lastName, - citizenId: profile.citizenId, - root: posMaster.orgRoot.orgRootName, - rootId: posMaster.orgRootId, - rootShortName: posMaster.orgRoot.orgRootShortName, - rootDnaId: posMaster.orgRoot?.ancestorDNA ?? _null, - child1DnaId: posMaster.orgChild1?.ancestorDNA ?? _null, - child2DnaId: posMaster.orgChild2?.ancestorDNA ?? _null, - child3DnaId: posMaster.orgChild3?.ancestorDNA ?? _null, - child4DnaId: posMaster.orgChild4?.ancestorDNA ?? _null, - }); - await profileEmployeeRepository.save(profile); - await employeePositionRepository.save(positionNew); - await CreatePosMasterHistoryEmployee(posMaster.id, req, undefined, manager); - //ลบออกคนออกจากโครงสร้างลูกจ้างชั่วคราว - const posMasterTemp = await employeeTempPosMasterRepository.findOne({ - where: { - orgRevisionId: orgRevision?.id, - current_holderId: profile.id, - }, - }); - if (posMasterTemp) { - await employeeTempPosMasterRepository.update(posMasterTemp.id, { - current_holderId: _null, - }); - await CreatePosMasterHistoryEmployeeTemp(posMasterTemp.id, req); - } - } - } - - // ═══════════════════════════════════════════════════════════════ - // C-PM-38 : command38/officer/report/excecute - // เงินเดือน next_holder ของข้าราชการ - // ═══════════════════════════════════════════════════════════════ - async executeCommand38Officer( - data: CommandRefItem[], - ctx: OrgCommandExecutionContext, - ): Promise { - const req = ctx.req; - const commandId = data?.find((x) => x.commandId)?.commandId ?? ""; - console.log( - `[ExecuteOrgCommandService] executeCommand38Officer — commandId: ${commandId}, count: ${data?.length ?? 0}`, - ); - - const { posNumCodeSit: _posNumCodeSit, posNumCodeSitAbb: _posNumCodeSitAbb } = - await this.resolvePosNumCodeSit(commandId); - - await AppDataSource.transaction(async (manager) => { - for (const item of data ?? []) { - try { - await this.processOneCommand38( - item, - ctx, - manager, - _posNumCodeSit, - _posNumCodeSitAbb, - ); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteOrgCommandService] Failed C-PM-38, commandId=${commandId}, refId=${item.refId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - - console.log(`[ExecuteOrgCommandService] Completed C-PM-38 — ${data?.length ?? 0} items`); - } - - private async processOneCommand38( - item: CommandRefItem, - ctx: OrgCommandExecutionContext, - manager: EntityManager, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - ): Promise { - const req = ctx.req; - - const posMasterRepository = manager.getRepository(PosMaster); - const profileRepository = manager.getRepository(Profile); - const positionRepository = manager.getRepository(Position); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - - const posMaster = await posMasterRepository.findOne({ - where: { id: item.refId }, - relations: [ - "orgRoot", - "orgChild1", - "orgChild2", - "orgChild3", - "orgChild4", - "current_holder", - "current_holder.posLevel", - "current_holder.posType", - ], - }); - if (!posMaster) { - throw new HttpError(HttpStatusCode.BAD_REQUEST, "ไม่พบตำแหน่งดังกล่าว"); - } - if (posMaster.next_holderId != null) { - const orgRootRef = posMaster?.orgRoot ?? null; - const orgChild1Ref = posMaster?.orgChild1 ?? null; - const orgChild2Ref = posMaster?.orgChild2 ?? null; - const orgChild3Ref = posMaster?.orgChild3 ?? null; - const orgChild4Ref = posMaster?.orgChild4 ?? null; - const shortName = - posMaster != null && posMaster.orgChild4 != null - ? `${posMaster.orgChild4.orgChild4ShortName}` - : posMaster != null && posMaster.orgChild3 != null - ? `${posMaster.orgChild3.orgChild3ShortName}` - : posMaster != null && posMaster.orgChild2 != null - ? `${posMaster.orgChild2.orgChild2ShortName}` - : posMaster != null && posMaster.orgChild1 != null - ? `${posMaster.orgChild1.orgChild1ShortName}` - : posMaster != null && posMaster?.orgRoot != null - ? `${posMaster.orgRoot.orgRootShortName}` - : null; - const profile = await profileRepository.findOne({ - where: { id: posMaster.next_holderId }, - }); - const position = await positionRepository.findOne({ - where: { - posMasterId: posMaster.id, - positionIsSelected: true, - }, - relations: ["posType", "posLevel"], - }); - const dest_item = await salaryRepo.findOne({ - where: { profileId: profile?.id }, - order: { order: "DESC" }, - }); - const before = null; - const dataSalary = new ProfileSalary(); - dataSalary.posNumCodeSit = _posNumCodeSit; - dataSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - profileId: profile?.id, - date: new Date(), - amount: item.amount, - commandId: item.commandId, - positionSalaryAmount: item.positionSalaryAmount, - mouthSalaryAmount: item.mouthSalaryAmount, - position: position?.positionName ?? null, - positionType: position?.posType?.posTypeName ?? null, - positionLevel: position?.posLevel?.posLevelName ?? null, - order: dest_item == null ? 1 : dest_item.order + 1, - orgRoot: orgRootRef?.orgRootName ?? null, - orgChild1: orgChild1Ref?.orgChild1Name ?? null, - orgChild2: orgChild2Ref?.orgChild2Name ?? null, - orgChild3: orgChild3Ref?.orgChild3Name ?? null, - orgChild4: orgChild4Ref?.orgChild4Name ?? null, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - commandNo: item.commandNo, - commandYear: item.commandYear, - posNo: posMaster.posMasterNo, - posNoAbb: shortName, - commandDateAffect: item.commandDateAffect, - commandDateSign: item.commandDateSign, - commandCode: item.commandCode, - commandName: item.commandName, - remark: item.remark, - }; - Object.assign(dataSalary, meta); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...dataSalary, id: undefined }); - - await salaryRepo.save(dataSalary, { data: req }); - setLogDataDiff(req, { before, after: dataSalary }); - history.profileSalaryId = dataSalary.id; - await salaryHistoryRepo.save(history, { data: req }); - } - } - - // ═══════════════════════════════════════════════════════════════ - // C-PM-40 : command40/officer/report/excecute - // รักษาการ (ProfileActposition) - // ═══════════════════════════════════════════════════════════════ - async executeCommand40Officer( - data: CommandRefItem[], - ctx: OrgCommandExecutionContext, - ): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? ""; - console.log( - `[ExecuteOrgCommandService] executeCommand40Officer — commandId: ${commandId}, count: ${data?.length ?? 0}`, - ); - - // 3. ตรวจสอบว่ามี data[0] หรือไม่ - const firstRef = data[0]; - if (!firstRef) { - throw new HttpError(HttpStatusCode.BAD_REQUEST, "ไม่พบข้อมูล refIds"); - } - - const profileIdsToClearCache = new Set(); - - await AppDataSource.transaction(async (manager) => { - // 1. Bulk update status - await manager.getRepository(PosMasterAct).update( - { id: In(data.map((x) => x.refId)) }, - { statusReport: "DONE" }, - ); - - // 2. ดึงข้อมูลครบทุก relation ที่จำเป็น - const posMasters = await manager.getRepository(PosMasterAct).find({ - where: { id: In(data.map((x) => x.refId)) }, - relations: [ - "posMasterChild", - "posMasterChild.current_holder", - "posMaster", - "posMaster.current_holder", - "posMaster.positions", - "posMaster.orgRoot", - "posMaster.orgChild1", - "posMaster.orgChild2", - "posMaster.orgChild3", - "posMaster.orgChild4", - ], - }); - - for (const item of posMasters) { - try { - // 4. ตรวจสอบข้อมูลที่จำเป็นทั้งหมด - if (!item.posMasterChild?.current_holderId || !item.posMaster) { - console.warn(`ข้ามรายการ ${item.id}: ข้อมูลไม่ครบ`); - continue; - } - - if (item.posMasterChild.current_holderId) { - profileIdsToClearCache.add(item.posMasterChild.current_holderId); - } - - // 5. สร้าง orgShortName แบบปลอดภัย - const orgShortName = - [ - item.posMaster?.orgChild4?.orgChild4ShortName, - item.posMaster?.orgChild3?.orgChild3ShortName, - item.posMaster?.orgChild2?.orgChild2ShortName, - item.posMaster?.orgChild1?.orgChild1ShortName, - item.posMaster?.orgRoot?.orgRootShortName, - ].find(Boolean) ?? ""; - - // 6. หา position ที่ถูกเลือกแบบปลอดภัย - const selectedPosition = item.posMaster?.positions; - const positionName = - selectedPosition - ?.map((pos) => pos.positionName) - .filter(Boolean) - .join(", ") ?? "-"; - - // 7. สร้าง metaAct แบบปลอดภัย - const metaAct = { - profileId: item.posMasterChild.current_holderId, - dateStart: firstRef.commandDateAffect ?? null, - dateEnd: null, - position: positionName, - status: true, - commandId: firstRef.commandId ?? null, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - commandNo: firstRef.commandNo ?? null, - refCommandNo: `${firstRef.commandNo ?? ""}/${firstRef.commandYear ? Extension.ToThaiYear(firstRef.commandYear) : ""}`, - commandYear: firstRef.commandYear ? Extension.ToThaiYear(firstRef.commandYear) : null, - posNo: - orgShortName && item.posMaster?.posMasterNo - ? `${orgShortName} ${item.posMaster.posMasterNo}` - : item.posMaster?.posMasterNo ?? "-", - posNoAbb: orgShortName, - commandDateAffect: firstRef.commandDateAffect ?? null, - commandDateSign: firstRef.commandDateSign ?? null, - commandCode: firstRef.commandCode ?? null, - commandName: firstRef.commandName ?? null, - remark: firstRef.remark ?? null, - }; - - // 8. ปิดสถานะรักษาการ - const actpositionRepository = manager.getRepository(ProfileActposition); - const actpositionHistoryRepository = manager.getRepository(ProfileActpositionHistory); - - const existingActPositions = await actpositionRepository.find({ - where: { - profileId: item.posMasterChild.current_holderId, - status: true, - isDeleted: false, - }, - }); - - if (existingActPositions.length > 0) { - const updatedActPositions = existingActPositions.map((_data) => ({ - ..._data, - status: false, - dateEnd: new Date(), - })); - - await actpositionRepository.save(updatedActPositions); - } - - // 9. บันทึกข้อมูลใหม่ - const dataAct = new ProfileActposition(); - Object.assign(dataAct, metaAct); - - const historyAct = new ProfileActpositionHistory(); - Object.assign(historyAct, { ...dataAct, id: undefined }); - - await actpositionRepository.save(dataAct); - historyAct.profileActpositionId = dataAct.id; - await actpositionHistoryRepository.save(historyAct); - } catch (error) { - console.error(`Error processing item ${item.id}:`, error); - throw new HttpError( - HttpStatusCode.INTERNAL_SERVER_ERROR, - `เกิดข้อผิดพลาดในการประมวลผลรายการ ${item.id}`, - ); - } - } - }); - - // Redis cache clear ทำหลัง commit (del cache key — idempotent) - if (profileIdsToClearCache.size > 0) { - await Promise.all( - Array.from(profileIdsToClearCache).map(async (profileId) => { - const redisClient = await redis.createClient({ - host: REDIS_HOST, - port: REDIS_PORT, - }); - - const delAsync = promisify(redisClient.del).bind(redisClient); - await delAsync("role_" + profileId); - await delAsync("menu_" + profileId); - - redisClient.quit(); - }), - ); - } - - console.log(`[ExecuteOrgCommandService] Completed C-PM-40 — ${data?.length ?? 0} items`); - } -} diff --git a/src/services/ExecuteSalaryCurrentService.ts b/src/services/ExecuteSalaryCurrentService.ts deleted file mode 100644 index d9039b30..00000000 --- a/src/services/ExecuteSalaryCurrentService.ts +++ /dev/null @@ -1,499 +0,0 @@ -import { Double, EntityManager } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatusCode from "../interfaces/http-status"; -import { Profile } from "../entities/Profile"; -import { ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { OrgRoot } from "../entities/OrgRoot"; -import { PosMaster } from "../entities/PosMaster"; -import { Position } from "../entities/Position"; -import { Command } from "../entities/Command"; -import { getOrgFullName, getPosMasterNo } from "../utils/org-formatting"; -import { logPositionIsSelectedChange, setLogDataDiff } from "../interfaces/utils"; -import { CreatePosMasterHistoryOfficer } from "./PositionService"; - -/** - * Input: ข้อมูล 1 คนสำหรับ endpoint excexute/salary-current - * (C-PM-03, 04, 05, 06, 07, 39, 47 — เปลี่ยนตำแหน่งปัจจุบันของข้าราชการ + salary ใหม่) - */ -export interface SalaryCurrentItem { - profileId: string; - amount?: Double | null; - amountSpecial?: Double | null; - positionSalaryAmount?: Double | null; - mouthSalaryAmount?: Double | null; - positionExecutive: string | null; - positionExecutiveField?: string | null; - positionArea?: string | null; - positionType: string | null; - positionLevel: string | null; - positionTypeId?: string | null; - positionLevelId?: string | null; - posmasterId: string; - positionId: string; - posExecutiveId?: string | null; - positionField?: string | null; - commandId?: string | null; - orgRoot?: string | null; - orgChild1?: string | null; - orgChild2?: string | null; - orgChild3?: string | null; - orgChild4?: string | null; - commandNo: string | null; - commandYear: number | null; - posNo: string | null; - posNoAbb: string | null; - commandDateAffect?: Date | string | null; - commandDateSign?: Date | string | null; - positionName: string | null; - commandCode?: string | null; - commandName?: string | null; - remark: string | null; -} - -/** - * Context สำหรับ audit/log - */ -export interface SalaryCurrentExecutionContext { - user: { sub: string; name: string }; - req?: any; -} - -/** - * Service สำหรับสร้าง ProfileSalary ของข้าราชการ + อัปเดตตำแหน่งปัจจุบัน (เปลี่ยนตำแหน่ง) - * - * ใช้กับ commandType: C-PM-03, 04, 05, 06, 07, 39, 47 - * - * - endpoint /org/command/excexute/salary-current เรียกผ่าน service นี้ (thin wrapper) - * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (Linear Flow) - * - * Behavior ทั้งหมด preserve จาก CommandController.newSalaryAndUpdateCurrent ต้นฉบับ - * - * Batch semantics: all-or-nothing — ประมวลผลทุกคนภายใต้ transaction เดียว (sequential) - * ถ้าคนใด throw จะ rollback ทั้ง batch และ propagate error ออกไป (ล้มเหลวทั้งหมด) - * ถ้าทุกคนสำเร็จจะ return result รายงาน success count - */ -export class ExecuteSalaryCurrentService { - private commandRepository = AppDataSource.getRepository(Command); - private profileRepository = AppDataSource.getRepository(Profile); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - - /** - * ประมวลผลสร้าง ProfileSalary + อัปเดตตำแหน่งปัจจุบันของข้าราชการทั้ง batch - * - * @returns สรุปผล success/failure ต่อคน - */ - async executeSalaryCurrent( - data: SalaryCurrentItem[], - ctx: SalaryCurrentExecutionContext, - ): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? "unknown"; - const commandCode = data?.find((x) => x.commandCode)?.commandCode ?? "unknown"; - console.log( - `[ExecuteSalaryCurrentService] Starting executeSalaryCurrent — commandCode: ${commandCode}, commandId: ${commandId}`, - ); - console.log(`[ExecuteSalaryCurrentService] Request body count: ${data?.length ?? 0}`); - - // ───────────────────────────────────────────────────────────── - // Normalize date fields (ผ่าน handler จะได้ string → ต้องแปลงเป็น Date) - // ───────────────────────────────────────────────────────────── - const toDate = (v: any): Date | null => { - if (v == null || v === "") return null; - if (v instanceof Date) return isNaN(v.getTime()) ? null : v; - const d = new Date(v); - return isNaN(d.getTime()) ? null : d; - }; - for (const item of data ?? []) { - const it = item as any; - it.commandDateAffect = toDate(it.commandDateAffect); - it.commandDateSign = toDate(it.commandDateSign); - } - - let _posNumCodeSit: string = ""; - let _posNumCodeSitAbb: string = ""; - const _command = await this.commandRepository.findOne({ - where: { id: data.find((x) => x.commandId)?.commandId ?? "" }, - }); - if (_command) { - if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; - } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - _posNumCodeSit = "กรุงเทพมหานคร"; - _posNumCodeSitAbb = "กทม."; - } else { - let _profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: _command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - _posNumCodeSit = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - _posNumCodeSitAbb = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - } - } - - // ───────────────────────────────────────────────────────────── - // Single transaction ครอบทั้ง batch (all-or-nothing) - // ทุกคนใช้ manager ตัวเดียวกัน — คนใด throw จะ rollback ทั้ง batch - // และ propagate error ออกไป (ล้มเหลวทั้งหมด) โดย log error ของคนที่ทำให้ fail ก่อน rethrow - // ───────────────────────────────────────────────────────────── - await AppDataSource.transaction(async (manager) => { - for (const item of data ?? []) { - try { - await this.processOne(item, ctx, manager, _posNumCodeSit, _posNumCodeSitAbb); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryCurrentService] Failed — commandCode: ${commandCode}, commandId: ${commandId}, profileId: ${item.profileId}, reason: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - } - - /** - * ประมวลผล 1 คน ภายใน transaction เดียว (manager) - * ทุก save ใช้ manager.getRepository(...) เพื่อให้อยู่ใน transaction เดียวกัน - * ถ้า throw ระหว่างทาง → rollback ทั้งหมดของคนนี้ (กัน partial commit) - */ - private async processOne( - item: SalaryCurrentItem, - ctx: SalaryCurrentExecutionContext, - manager: EntityManager, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - ): Promise { - const req = ctx.req; - - const profileRepository = manager.getRepository(Profile); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - const posMasterRepository = manager.getRepository(PosMaster); - const positionRepository = manager.getRepository(Position); - - const profile: any = await profileRepository.findOneBy({ id: item.profileId }); - if (!profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, `ไม่พบข้อมูลทะเบียนประวัตินี้ profileId: ${item.profileId}`); - } - let _null: any = null; - const dest_item = await salaryRepo.findOne({ - where: { profileId: item.profileId }, - order: { order: "DESC" }, - }); - const before = null; - const dataSalary = new ProfileSalary(); - - const meta = { - order: dest_item == null ? 1 : dest_item.order + 1, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - dataSalary.posNumCodeSit = _posNumCodeSit; - dataSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - Object.assign(dataSalary, { ...item, ...meta }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...dataSalary, id: undefined }); - await salaryRepo.save(dataSalary, { data: req }); - setLogDataDiff(req, { before, after: dataSalary }); - history.commandId = item.commandId ?? _null; - history.profileSalaryId = dataSalary.id; - await salaryHistoryRepo.save(history, { data: req }); - - // STEP 1: หา posMaster ที่จะใช้งานตาม id ที่ส่งมา - console.log( - `[ExecuteSalaryCurrentService] STEP 1: Finding posMaster — posmasterId: ${item.posmasterId}, profileId: ${item.profileId}`, - ); - let posMaster = await posMasterRepository.findOne({ - where: { id: item.posmasterId }, - relations: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - console.log( - `[ExecuteSalaryCurrentService] STEP 1: posMaster found: ${!!posMaster}, ancestorDNA: ${posMaster?.ancestorDNA ?? "null"}, orgRevisionId: ${posMaster?.orgRevisionId ?? "null"}`, - ); - - // เช็คว่า posMaster ที่หามาอยู่ในโครงสร้างปัจจุบันหรือไม่ - const isCurrent = - posMaster?.orgRevision?.orgRevisionIsCurrent === true && - posMaster?.orgRevision?.orgRevisionIsDraft === false; - console.log(`[ExecuteSalaryCurrentService] STEP 1: isCurrent: ${isCurrent}`); - - // ถ้าไม่อยู่ในโครงสร้างปัจจุบัน ให้หาตัวใหม่จาก ancestorDNA - if (!isCurrent && posMaster?.ancestorDNA) { - console.log( - `[ExecuteSalaryCurrentService] STEP 1: Not current — re-resolving via ancestorDNA: ${posMaster.ancestorDNA}`, - ); - posMaster = await posMasterRepository.findOne({ - where: { - ancestorDNA: posMaster.ancestorDNA, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - console.log( - `[ExecuteSalaryCurrentService] STEP 1: ancestorDNA re-resolve — found: ${!!posMaster}`, - ); - } - - if (posMaster == null) { - console.error( - `[ExecuteSalaryCurrentService] STEP 1: PosMaster not found — posmasterId: ${item.posmasterId}`, - ); - throw new HttpError(HttpStatusCode.NOT_FOUND, `ไม่พบข้อมูลตำแหน่งนี้ posMasterId: ${item.posmasterId}`); - } - - const posMasterOld = await posMasterRepository.findOne({ - where: { - current_holderId: item.profileId, - orgRevisionId: posMaster.orgRevisionId, - }, - }); - if (posMasterOld != null) { - posMasterOld.current_holderId = null; - posMasterOld.lastUpdatedAt = new Date(); - } - - const positionOld = await positionRepository.findOne({ - where: { - posMasterId: posMasterOld?.id, - positionIsSelected: true, - }, - }); - if (positionOld != null) { - logPositionIsSelectedChange(positionOld.id, positionOld.positionIsSelected, false, { - posMasterId: posMasterOld?.id, - userId: ctx.user.sub, - endpoint: "updateMaster", - action: "command_change_reset_old_position", - }); - - positionOld.positionIsSelected = false; - await positionRepository.save(positionOld); - } - - const checkPosition = await positionRepository.find({ - where: { - posMasterId: posMaster!.id, // ใช้ posMaster ตัวใหม่ (ที่อาจจะเปลี่ยนจาก ancestorDNA) - positionIsSelected: true, - }, - }); - if (checkPosition.length > 0) { - console.log( - `[positionIsSelected-DEBUG] Command change: clearing ${checkPosition.length} positions (posMasterId: ${posMaster!.id}, userId: ${ctx.user.sub}, endpoint: updateMaster)`, - ); - - const clearPosition = checkPosition.map((positions) => { - logPositionIsSelectedChange(positions.id, positions.positionIsSelected, false, { - posMasterId: posMaster!.id, - userId: ctx.user.sub, - endpoint: "updateMaster", - action: "command_change_clear_positions", - }); - - return { - ...positions, - positionIsSelected: false, - }; - }); - await positionRepository.save(clearPosition); - } - - posMaster.current_holderId = item.profileId; - posMaster.lastUpdatedAt = new Date(); - // posMaster.conditionReason = _null; - // posMaster.isCondition = false; - if (posMasterOld != null) { - await posMasterRepository.save(posMasterOld); - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - console.log( - `[ExecuteSalaryCurrentService] Creating PosMasterHistory — posMasterId: ${posMasterOld.id}, profileId: ${item.profileId} (old)`, - ); - await CreatePosMasterHistoryOfficer(posMasterOld.id, req, null, null, manager); - } - await posMasterRepository.save(posMaster); - - // STEP 2: กำหนด position ใหม่ - // Match position ตามลำดับ priority: - // Condition 1: match จาก positionId - // Condition 2: match 7 ฟิลด์ (positionName, posTypeId, posLevelId, positionField, positionArea, positionExecutiveField, posExecutiveId) - // Condition 3: match 3 ฟิลด์ (positionName, posTypeId, posLevelId) - // Fallback: เลือก position แรกใน posMaster - - let positionNew: Position | null = null; - - // Resolve ID: ใช้ positionTypeId/positionLevelId ก่อน ถ้าไม่มี fallback เป็น positionType/positionLevel - const posTypeId = item.positionTypeId || item.positionType; - const posLevelId = item.positionLevelId || item.positionLevel; - - console.log( - `[ExecuteSalaryCurrentService] STEP 2: Resolving position — posMasterId: ${posMaster.id}, positionId: ${item.positionId ?? "null"}, positionName: ${item.positionName ?? "null"}, posTypeId: ${posTypeId ?? "null"}, posLevelId: ${posLevelId ?? "null"}`, - ); - - // ═══════════════════════════════════════════════════════════ - // CONDITION 1: เช็คจาก positionId ตรง - // ═══════════════════════════════════════════════════════════ - if (item.positionId) { - const positionById = await positionRepository.findOne({ - where: { - id: item.positionId, - posMasterId: posMaster.id, // ต้องอยู่ใน posMaster ที่ถูกต้อง - }, - relations: ["posExecutive"], - }); - console.log( - `[ExecuteSalaryCurrentService] STEP 2 / Condition 1: match: ${!!positionById}`, - ); - - if (positionById) { - positionNew = positionById; - } - } - - // ═══════════════════════════════════════════════════════════ - // CONDITION 2: Match 7 ฟิลด์ (ถ้า Condition 1 ไม่ match) - // ═══════════════════════════════════════════════════════════ - if (!positionNew && item.positionName && posTypeId && posLevelId) { - // สร้าง where clause แบบ dynamic - ใส่เฉพาะฟิลด์ที่มีค่า - const whereCondition: any = { - posMasterId: posMaster.id, - positionName: item.positionName, - posTypeId: posTypeId, - posLevelId: posLevelId, - }; - - // เพิ่มเฉพาะฟิลด์ที่มีค่า (ไม่ใช่ null, undefined, หรือ string ว่าง) - if (item.positionField) { - whereCondition.positionField = item.positionField; - } - if (item.posExecutiveId) { - whereCondition.posExecutiveId = item.posExecutiveId; - } - if (item.positionExecutiveField) { - whereCondition.positionExecutiveField = item.positionExecutiveField; - } - if (item.positionArea) { - whereCondition.positionArea = item.positionArea; - } - - const positionBy7Fields = await positionRepository.findOne({ - where: whereCondition, - relations: ["posExecutive"], - order: { orderNo: "ASC" }, - }); - console.log( - `[ExecuteSalaryCurrentService] STEP 2 / Condition 2: match: ${!!positionBy7Fields}`, - whereCondition, - ); - - if (positionBy7Fields) { - positionNew = positionBy7Fields; - } - } - - // ═══════════════════════════════════════════════════════════ - // CONDITION 3: Match 3 ฟิลด์ (ถ้า Condition 2 ไม่ match) - // ═══════════════════════════════════════════════════════════ - if (!positionNew && item.positionName && posTypeId && posLevelId) { - const positionBy3Fields = await positionRepository.findOne({ - where: { - posMasterId: posMaster.id, - positionName: item.positionName, - posTypeId: posTypeId, - posLevelId: posLevelId, - }, - relations: ["posExecutive"], - order: { orderNo: "ASC" }, - }); - console.log( - `[ExecuteSalaryCurrentService] STEP 2 / Condition 3: match: ${!!positionBy3Fields}`, - ); - - if (positionBy3Fields) { - positionNew = positionBy3Fields; - } - } - - console.log( - `[ExecuteSalaryCurrentService] STEP 2: Resolved positionNew: ${positionNew ? positionNew.id : "null (no match — profile position not updated)"}`, - ); - - // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ - if (positionNew != null) { - positionNew.positionIsSelected = true; - // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit - profile.posMasterNo = getPosMasterNo(posMaster); - profile.org = getOrgFullName(posMaster); - if (!posMaster.isSit) { - profile.posLevelId = positionNew.posLevelId; - profile.posTypeId = positionNew.posTypeId; - profile.position = positionNew.positionName; - profile.positionField = positionNew.positionField ?? null; - profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; - profile.positionArea = positionNew.positionArea ?? null; - profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; - } - profile.amount = item.amount ?? null; - profile.amountSpecial = item.amountSpecial ?? null; - await profileRepository.save(profile); - await positionRepository.save(positionNew); - } - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - console.log( - `[ExecuteSalaryCurrentService] Creating PosMasterHistory — posMasterId: ${posMaster.id}, profileId: ${item.profileId}`, - ); - await CreatePosMasterHistoryOfficer(posMaster.id, req, null, null, manager); - - console.log( - `[ExecuteSalaryCurrentService] Completed processOne — profileId: ${item.profileId}, posMasterId: ${posMaster.id}`, - ); - } -} diff --git a/src/services/ExecuteSalaryEmployeeCurrentService.ts b/src/services/ExecuteSalaryEmployeeCurrentService.ts deleted file mode 100644 index 93b6eaed..00000000 --- a/src/services/ExecuteSalaryEmployeeCurrentService.ts +++ /dev/null @@ -1,316 +0,0 @@ -import { Double, EntityManager } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatusCode from "../interfaces/http-status"; -import HttpStatus from "../interfaces/http-status"; -import { Profile } from "../entities/Profile"; -import { ProfileEmployee } from "../entities/ProfileEmployee"; -import { ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { OrgRoot } from "../entities/OrgRoot"; -import { EmployeePosMaster } from "../entities/EmployeePosMaster"; -import { EmployeePosition } from "../entities/EmployeePosition"; -import { Command } from "../entities/Command"; -import { setLogDataDiff } from "../interfaces/utils"; -import { CreatePosMasterHistoryEmployee } from "./PositionService"; - -/** - * Input: ข้อมูล 1 คนสำหรับ endpoint excexute/salary-employee-current - * (C-PM-22, 24 — เปลี่ยนตำแหน่งปัจจุบันของลูกจ้าง + salary ใหม่) - */ -export interface SalaryEmployeeCurrentItem { - profileId: string; - amount?: Double | null; - amountSpecial?: Double | null; - positionSalaryAmount?: Double | null; - mouthSalaryAmount?: Double | null; - positionType: string | null; - positionLevel: string | null; - posmasterId: string; - positionId: string; - commandId?: string | null; - orgRoot?: string | null; - orgChild1?: string | null; - orgChild2?: string | null; - orgChild3?: string | null; - orgChild4?: string | null; - commandNo: string | null; - commandYear: number | null; - posNo: string | null; - posNoAbb: string | null; - commandDateAffect?: Date | string | null; - commandDateSign?: Date | string | null; - positionName: string | null; - commandCode?: string | null; - commandName?: string | null; - remark: string | null; -} - -/** - * Context สำหรับ audit/log - */ -export interface SalaryEmployeeCurrentExecutionContext { - user: { sub: string; name: string }; - req?: any; -} - -/** - * Service สำหรับสร้าง ProfileSalary ของลูกจ้าง + อัปเดตตำแหน่งปัจจุบัน (เปลี่ยนตำแหน่ง) - * - * ใช้กับ commandType: C-PM-22, 24 - * - * - endpoint /org/command/excexute/salary-employee-current เรียกผ่าน service นี้ (thin wrapper) - * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (Linear Flow) - * - * Behavior ทั้งหมด preserve จาก CommandController.newSalaryEmployeeAndUpdateCurrent ต้นฉบับ - * - * Batch semantics: all-or-nothing — ประมวลผลทุกคนภายใต้ transaction เดียว (sequential) - * ถ้าคนใด throw จะ rollback ทั้ง batch และ propagate error ออกไป (ล้มเหลวทั้งหมด) - * ถ้าทุกคนสำเร็จจะ return result รายงาน success count - */ -export class ExecuteSalaryEmployeeCurrentService { - private commandRepository = AppDataSource.getRepository(Command); - private profileRepository = AppDataSource.getRepository(Profile); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - - /** - * ประมวลผลสร้าง ProfileSalary + อัปเดตตำแหน่งปัจจุบันของลูกจ้างทั้ง batch - */ - async executeSalaryEmployeeCurrent( - data: SalaryEmployeeCurrentItem[], - ctx: SalaryEmployeeCurrentExecutionContext, - ): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? "unknown"; - const commandCode = data?.find((x) => x.commandCode)?.commandCode ?? "unknown"; - console.log( - `[ExecuteSalaryEmployeeCurrentService] Starting executeSalaryEmployeeCurrent — commandCode: ${commandCode}, commandId: ${commandId}`, - ); - console.log(`[ExecuteSalaryEmployeeCurrentService] Request body count: ${data?.length ?? 0}`); - - // ───────────────────────────────────────────────────────────── - // Normalize date fields (ผ่าน handler จะได้ string → ต้องแปลงเป็น Date) - // ───────────────────────────────────────────────────────────── - const toDate = (v: any): Date | null => { - if (v == null || v === "") return null; - if (v instanceof Date) return isNaN(v.getTime()) ? null : v; - const d = new Date(v); - return isNaN(d.getTime()) ? null : d; - }; - for (const item of data ?? []) { - const it = item as any; - it.commandDateAffect = toDate(it.commandDateAffect); - it.commandDateSign = toDate(it.commandDateSign); - } - - let _posNumCodeSit: string = ""; - let _posNumCodeSitAbb: string = ""; - const _command = await this.commandRepository.findOne({ - where: { id: data.find((x) => x.commandId)?.commandId ?? "" }, - }); - if (_command) { - if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; - } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - _posNumCodeSit = "กรุงเทพมหานคร"; - _posNumCodeSitAbb = "กทม."; - } else { - let _profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: _command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - _posNumCodeSit = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - _posNumCodeSitAbb = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - } - } - - // ───────────────────────────────────────────────────────────── - // Single transaction ครอบทั้ง batch (all-or-nothing) - // ทุกคนใช้ manager ตัวเดียวกัน — คนใด throw จะ rollback ทั้ง batch - // และ propagate error ออกไป (ล้มเหลวทั้งหมด) โดย log error ของคนที่ทำให้ fail ก่อน rethrow - // ───────────────────────────────────────────────────────────── - await AppDataSource.transaction(async (manager) => { - for (const item of data ?? []) { - try { - await this.processOne(item, ctx, manager, _posNumCodeSit, _posNumCodeSitAbb); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryEmployeeCurrentService] Failed commandCode=${commandCode}, commandId=${commandId}, profileId=${item.profileId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - } - - /** - * ประมวลผล 1 คน ภายใน transaction เดียว (manager) - * ทุก save ใช้ manager.getRepository(...) เพื่อให้อยู่ใน transaction เดียวกัน - * ถ้า throw ระหว่างทาง → rollback ทั้งหมดของคนนี้ + ทั้ง batch (กัน partial commit) - */ - private async processOne( - item: SalaryEmployeeCurrentItem, - ctx: SalaryEmployeeCurrentExecutionContext, - manager: EntityManager, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - ): Promise { - const req = ctx.req; - - const profileEmployeeRepository = manager.getRepository(ProfileEmployee); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - const employeePosMasterRepository = manager.getRepository(EmployeePosMaster); - const employeePositionRepository = manager.getRepository(EmployeePosition); - - const profile: any = await profileEmployeeRepository.findOneBy({ id: item.profileId }); - if (!profile) { - throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); - } - - const dest_item = await salaryRepo.findOne({ - where: { profileEmployeeId: item.profileId }, - order: { order: "DESC" }, - }); - const before = null; - const dataSalary = new ProfileSalary(); - dataSalary.posNumCodeSit = _posNumCodeSit; - dataSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - order: dest_item == null ? 1 : dest_item.order + 1, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - - Object.assign(dataSalary, { - ...item, - ...meta, - profileEmployeeId: item.profileId, - profileId: undefined, - }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...dataSalary, id: undefined }); - - await salaryRepo.save(dataSalary, { data: req }); - setLogDataDiff(req, { before, after: dataSalary }); - history.profileSalaryId = dataSalary.id; - await salaryHistoryRepo.save(history, { data: req }); - - const posMaster = await employeePosMasterRepository.findOne({ - where: { id: item.posmasterId }, - relations: ["orgRoot"], - }); - if (posMaster == null) - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); - - const posMasterOld = await employeePosMasterRepository.findOne({ - where: { - current_holderId: item.profileId, - orgRevisionId: posMaster.orgRevisionId, - }, - }); - if (posMasterOld != null) { - posMasterOld.current_holderId = null; - posMasterOld.lastUpdatedAt = new Date(); - } - // if (posMasterOld != null) posMasterOld.next_holderId = null; - - const positionOld = await employeePositionRepository.findOne({ - where: { - posMasterId: posMasterOld?.id, - positionIsSelected: true, - }, - }); - if (positionOld != null) { - positionOld.positionIsSelected = false; - await employeePositionRepository.save(positionOld); - } - - const checkPosition = await employeePositionRepository.find({ - where: { - posMasterId: item.posmasterId, - positionIsSelected: true, - }, - }); - if (checkPosition.length > 0) { - const clearPosition = checkPosition.map((positions) => ({ - ...positions, - positionIsSelected: false, - })); - await employeePositionRepository.save(clearPosition); - } - - posMaster.current_holderId = item.profileId; - posMaster.lastUpdatedAt = new Date(); - posMaster.next_holderId = null; - if (posMasterOld != null) { - await employeePosMasterRepository.save(posMasterOld); - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - console.log( - `[ExecuteSalaryEmployeeCurrentService] Creating PosMasterHistory — posMasterId: ${posMasterOld.id}, profileId: ${item.profileId} (old)`, - ); - await CreatePosMasterHistoryEmployee(posMasterOld.id, req, null, manager); - } - await employeePosMasterRepository.save(posMaster); - const positionNew = await employeePositionRepository.findOne({ - where: { - id: item.positionId, - posMasterId: item.posmasterId, - }, - }); - if (positionNew != null) { - positionNew.positionIsSelected = true; - profile.posLevelId = positionNew.posLevelId; - profile.posTypeId = positionNew.posTypeId; - profile.position = positionNew.positionName; - profile.employeeOc = posMaster?.orgRoot?.orgRootName ?? null; - profile.positionEmployeePositionId = positionNew.positionName; - profile.amount = item.amount ?? null; - profile.amountSpecial = item.amountSpecial ?? null; - await profileEmployeeRepository.save(profile); - await employeePositionRepository.save(positionNew); - } - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - console.log( - `[ExecuteSalaryEmployeeCurrentService] Creating PosMasterHistory — posMasterId: ${posMaster.id}, profileId: ${item.profileId}`, - ); - await CreatePosMasterHistoryEmployee(posMaster.id, req, null, manager); - - console.log( - `[ExecuteSalaryEmployeeCurrentService] Completed processOne — profileId: ${item.profileId}, posMasterId: ${posMaster.id}`, - ); - } -} diff --git a/src/services/ExecuteSalaryEmployeeLeaveService.ts b/src/services/ExecuteSalaryEmployeeLeaveService.ts deleted file mode 100644 index 95bfa948..00000000 --- a/src/services/ExecuteSalaryEmployeeLeaveService.ts +++ /dev/null @@ -1,382 +0,0 @@ -import { Double, EntityManager } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatus from "../interfaces/http-status"; -import { Profile } from "../entities/Profile"; -import { ProfileEmployee } from "../entities/ProfileEmployee"; -import { ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { OrgRoot } from "../entities/OrgRoot"; -import { OrgRevision } from "../entities/OrgRevision"; -import { EmployeePosMaster } from "../entities/EmployeePosMaster"; -import { CommandRecive } from "../entities/CommandRecive"; -import { Command } from "../entities/Command"; -import { checkCommandType, removeProfileInOrganize, setLogDataDiff } from "../interfaces/utils"; -import { reOrderCommandRecivesAndDelete } from "./CommandService"; -import { CreatePosMasterHistoryEmployee } from "./PositionService"; -import { deleteUser } from "../keycloak"; - -/** - * Input: ข้อมูล 1 คนสำหรับ endpoint excexute/salary-employee-leave - * (C-PM-23, 42, 43 — ลาออก/ยกเลิกลาออก/กลับเข้าราชการ ของลูกจ้าง) - */ -export interface SalaryEmployeeLeaveItem { - profileId: string; - amount?: Double | null; - amountSpecial?: Double | null; - positionSalaryAmount?: Double | null; - mouthSalaryAmount?: Double | null; - positionType: string | null; - positionLevel: string | null; - isLeave: boolean; - leaveReason?: string | null; - dateLeave?: Date | string | null; - isGovernment?: boolean | null; - commandId?: string | null; - orgRoot?: string | null; - orgChild1?: string | null; - orgChild2?: string | null; - orgChild3?: string | null; - orgChild4?: string | null; - positionExecutive?: string | null; - positionExecutiveField?: string | null; - positionArea?: string | null; - commandNo: string | null; - commandYear: number | null; - posNo: string | null; - posNoAbb: string | null; - commandDateAffect?: Date | string | null; - commandDateSign?: Date | string | null; - positionName: string | null; - commandCode?: string | null; - commandName?: string | null; - remark: string | null; - resignId: string | null; -} - -/** - * Context สำหรับ audit/log - */ -export interface SalaryEmployeeLeaveExecutionContext { - user: { sub: string; name: string }; - req?: any; -} - -/** - * Service สำหรับสร้าง ProfileSalary ลูกจ้าง + handle leave/กลับเข้าราชการ - * - * ใช้กับ commandType: C-PM-23, 42, 43 - * - * - endpoint /org/command/excexute/salary-employee-leave เรียกผ่าน service นี้ (thin wrapper) - * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (Linear Flow) - * - * Behavior ทั้งหมด preserve จาก CommandController.newSalaryEmployeeAndUpdateLeave ต้นฉบับ - * - * Batch semantics: all-or-nothing — ประมวลผลทุกคนภายใต้ transaction เดียว (sequential) - * ถ้าคนใด throw จะ rollback ทั้ง batch และ propagate error ออกไป (ล้มเหลวทั้งหมด) - * ถ้าทุกคนสำเร็จจะ return result รายงาน success count - * - * ⚠️ หมายเหตุ Keycloak: operation (deleteUser) ทำภายใน transaction เพื่อ preserve behavior - * เดิม — Keycloak ไม่สามารถ rollback ได้ ถ้า DB rollback หลังจาก Keycloak operation สำเร็จ - * → Keycloak จะถูกเปลี่ยนไปแล้ว - */ -export class ExecuteSalaryEmployeeLeaveService { - private commandRepository = AppDataSource.getRepository(Command); - private commandReciveRepository = AppDataSource.getRepository(CommandRecive); - private profileRepository = AppDataSource.getRepository(Profile); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - - /** - * ประมวลผลสร้าง ProfileSalary + handle leave ของลูกจ้างทั้ง batch - */ - async executeSalaryEmployeeLeave( - data: SalaryEmployeeLeaveItem[], - ctx: SalaryEmployeeLeaveExecutionContext, - ): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? "unknown"; - const commandCode = data?.find((x) => x.commandCode)?.commandCode ?? "unknown"; - console.log( - `[ExecuteSalaryEmployeeLeaveService] Starting executeSalaryEmployeeLeave — commandCode: ${commandCode}, commandId: ${commandId}`, - ); - console.log(`[ExecuteSalaryEmployeeLeaveService] Request body count: ${data?.length ?? 0}`); - - // ───────────────────────────────────────────────────────────── - // Normalize date fields (ผ่าน handler จะได้ string → ต้องแปลงเป็น Date) - // ───────────────────────────────────────────────────────────── - const toDate = (v: any): Date | null => { - if (v == null || v === "") return null; - if (v instanceof Date) return isNaN(v.getTime()) ? null : v; - const d = new Date(v); - return isNaN(d.getTime()) ? null : d; - }; - for (const item of data ?? []) { - const it = item as any; - it.dateLeave = toDate(it.dateLeave); - it.commandDateAffect = toDate(it.commandDateAffect); - it.commandDateSign = toDate(it.commandDateSign); - } - - let _posNumCodeSit: string = ""; - let _posNumCodeSitAbb: string = ""; - const _command = await this.commandRepository.findOne({ - where: { id: data.find((x) => x.commandId)?.commandId ?? "" }, - relations: { commandType: true }, - }); - if (_command) { - if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; - } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - _posNumCodeSit = "กรุงเทพมหานคร"; - _posNumCodeSitAbb = "กทม."; - } else { - let _profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: _command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - _posNumCodeSit = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - _posNumCodeSitAbb = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - } - } - const today = new Date().setHours(0, 0, 0, 0); - - // ───────────────────────────────────────────────────────────── - // Single transaction ครอบทั้ง batch (all-or-nothing) - // ทุกคนใช้ manager ตัวเดียวกัน — คนใด throw จะ rollback ทั้ง batch - // และ propagate error ออกไป (ล้มเหลวทั้งหมด) โดย log error ของคนที่ทำให้ fail ก่อน rethrow - // ───────────────────────────────────────────────────────────── - await AppDataSource.transaction(async (manager) => { - for (const item of data ?? []) { - try { - await this.processOne(item, ctx, manager, _command, _posNumCodeSit, _posNumCodeSitAbb, today); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryEmployeeLeaveService] Failed commandCode=${commandCode}, commandId=${commandId}, profileId=${item.profileId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - } - - /** - * ประมวลผล 1 คน ภายใน transaction เดียว (manager) - * ทุก save ใช้ manager.getRepository(...) เพื่อให้อยู่ใน transaction เดียวกัน - * ถ้า throw ระหว่างทาง → rollback ทั้งหมดของคนนี้ + ทั้ง batch (กัน partial commit) - */ - private async processOne( - item: SalaryEmployeeLeaveItem, - ctx: SalaryEmployeeLeaveExecutionContext, - manager: EntityManager, - _command: Command | null, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - today: number, - ): Promise { - const req = ctx.req; - - const commandReciveRepository = manager.getRepository(CommandRecive); - const profileEmployeeRepository = manager.getRepository(ProfileEmployee); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - const employeePosMasterRepository = manager.getRepository(EmployeePosMaster); - const orgRevisionRepo = manager.getRepository(OrgRevision); - - const profile = await profileEmployeeRepository.findOne({ - where: { id: item.profileId }, - relations: { - roleKeycloaks: true, - posType: true, - posLevel: true, - }, - }); - if (!profile) { - throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); - } - const code = _command?.commandType?.code; - //ออกคำสั่งยกเลิกลาออก ลบเฉพาะคนที่ขอยกเลิกลาออก - if (item.resignId && code && ["C-PM-42"].includes(code)) { - const commandResign = await commandReciveRepository.findOne({ - where: { refId: item.resignId }, - relations: { command: true }, - }); - const executeDate = commandResign - ? new Date(commandResign.command.commandExcecuteDate).setHours(0, 0, 0, 0) - : today; - if ( - commandResign && - _command.status !== "REPORTED" && - (_command.status !== "WAITING" || today < executeDate) - ) { - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - await reOrderCommandRecivesAndDelete(commandResign!.id, manager); - } - } - let _commandYear = item.commandYear; - if (item.commandYear) { - _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; - } - const dest_item = await salaryRepo.findOne({ - where: { profileEmployeeId: item.profileId }, - order: { order: "DESC" }, - }); - const before = null; - const dataSalary = new ProfileSalary(); - dataSalary.posNumCodeSit = _posNumCodeSit; - dataSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - order: dest_item == null ? 1 : dest_item.order + 1, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - - Object.assign(dataSalary, { - ...item, - ...meta, - profileEmployeeId: item.profileId, - profileId: undefined, - }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...dataSalary, id: undefined }); - dataSalary.dateGovernment = (item.commandDateAffect as Date) ?? meta.createdAt; - await salaryRepo.save(dataSalary, { data: req }); - setLogDataDiff(req, { before, after: dataSalary }); - history.profileSalaryId = dataSalary.id; - await salaryHistoryRepo.save(history, { data: req }); - - const _null: any = null; - profile.isLeave = item.isLeave; - profile.leaveReason = item.leaveReason ?? _null; - profile.dateLeave = item.dateLeave ?? _null; - profile.lastUpdateUserId = ctx.user.sub; - profile.lastUpdateFullName = ctx.user.name; - profile.lastUpdatedAt = new Date(); - // บันทึกประวัติก่อนลบตำแหน่ง - const clearProfile = await checkCommandType(String(item.commandId)); - const curRevision = await orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - let orgRootRef = null; - let orgChild1Ref = null; - let orgChild2Ref = null; - let orgChild3Ref = null; - let orgChild4Ref = null; - if (curRevision) { - const curPosMaster = await employeePosMasterRepository.findOne({ - where: { - current_holderId: profile.id, - orgRevisionId: curRevision.id, - }, - relations: { - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - orgRootRef = curPosMaster?.orgRoot ?? null; - orgChild1Ref = curPosMaster?.orgChild1 ?? null; - orgChild2Ref = curPosMaster?.orgChild2 ?? null; - orgChild3Ref = curPosMaster?.orgChild3 ?? null; - orgChild4Ref = curPosMaster?.orgChild4 ?? null; - if (curPosMaster) { - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - console.log( - `[ExecuteSalaryEmployeeLeaveService] Creating PosMasterHistory — posMasterId: ${curPosMaster.id}, profileId: ${item.profileId}, type: DELETE`, - ); - await CreatePosMasterHistoryEmployee(curPosMaster.id, req, "DELETE", manager); - } - } - - // ลบตำแหน่ง - if (item.isLeave == true) { - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - await removeProfileInOrganize(profile.id, "EMPLOYEE", manager); - } - - if (clearProfile.status) { - if (profile.keycloak != null && profile.keycloak != "" && profile.isDelete === false) { - // Keycloak deleteUser ทำภายใน transaction — ถ้า DB rollback หลังจากนี้ Keycloak จะถูกลบไปแล้ว - // (Keycloak ไม่สามารถ rollback ได้) - const delUserKeycloak = await deleteUser(profile.keycloak); - if (delUserKeycloak) { - // Task #228 - // profile.keycloak = _null; - profile.roleKeycloaks = []; - profile.isActive = false; - profile.isDelete = true; - } - } - profile.leaveCommandId = item.commandId ?? _null; - profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; - profile.leaveRemark = clearProfile.leaveRemark ?? _null; - profile.leaveDate = item.commandDateAffect ?? _null; - profile.leaveType = clearProfile.LeaveType ?? _null; - //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) - // profile.position = _null; - // profile.posTypeId = _null; - // profile.posLevelId = _null; - } - await profileEmployeeRepository.save(profile); - - // if (profile.id) { - // await this.keycloakAttributeService.clearOrgDnaAttributes( - // [profile.id], - // "PROFILE_EMPLOYEE", - // ); - // } - - // Task #2190 - if (code && ["C-PM-23", "C-PM-43"].includes(code)) { - let organizeName = ""; - if (orgRootRef) { - const names = [ - orgChild4Ref?.orgChild4Name, - orgChild3Ref?.orgChild3Name, - orgChild2Ref?.orgChild2Name, - orgChild1Ref?.orgChild1Name, - orgRootRef?.orgRootName, - ].filter(Boolean); - organizeName = names.join(" "); - } - } - - console.log( - `[ExecuteSalaryEmployeeLeaveService] Completed processOne — profileId: ${item.profileId}`, - ); - } -} diff --git a/src/services/ExecuteSalaryLeaveDisciplineService.ts b/src/services/ExecuteSalaryLeaveDisciplineService.ts deleted file mode 100644 index 99a1dffc..00000000 --- a/src/services/ExecuteSalaryLeaveDisciplineService.ts +++ /dev/null @@ -1,615 +0,0 @@ -import { Double, EntityManager } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatusCode from "../interfaces/http-status"; -import { Profile } from "../entities/Profile"; -import { ProfileEmployee } from "../entities/ProfileEmployee"; -import { ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { ProfileDiscipline } from "../entities/ProfileDiscipline"; -import { ProfileDisciplineHistory } from "../entities/ProfileDisciplineHistory"; -import { OrgRoot } from "../entities/OrgRoot"; -import { OrgRevision } from "../entities/OrgRevision"; -import { EmployeePosMaster } from "../entities/EmployeePosMaster"; -import { Command } from "../entities/Command"; -import { - checkCommandType, - removePostMasterAct, - removeProfileInOrganize, - setLogDataDiff, -} from "../interfaces/utils"; -import { - CreatePosMasterHistoryEmployee, - CreatePosMasterHistoryOfficer, -} from "./PositionService"; -import { deleteUser } from "../keycloak"; - -/** - * Input: ข้อมูล 1 คนสำหรับ endpoint excexute/salary-leave-discipline - * (C-PM-19, 20, 25, 26, 27, 28, 29, 30, 31, 32 — คำสั่งวินัย ข้าราชการ/ลูกจ้าง) - * - * profileType "OFFICER" → ข้าราชการ, ค่าอื่น/null → ลูกจ้าง - */ -export interface SalaryLeaveDisciplineItem { - profileId: string; - profileType?: string | null; - isLeave: boolean | null; - leaveReason?: string | null; - dateLeave?: Date | string | null; - detail?: string | null; - level?: string | null; - unStigma?: string | null; - commandId?: string | null; - amount?: Double | null; - amountSpecial?: Double | null; - positionSalaryAmount?: Double | null; - mouthSalaryAmount?: Double | null; - isGovernment?: boolean | null; - commandNo: string | null; - commandYear: number | null; - commandDateAffect?: Date | string | null; - commandDateSign?: Date | string | null; - commandCode?: string | null; - commandName?: string | null; - remark: string | null; - orgRoot?: string | null; - orgChild1?: string | null; - orgChild2?: string | null; - orgChild3?: string | null; - orgChild4?: string | null; - posNo?: string | null; - posNoAbb?: string | null; -} - -/** - * Context สำหรับ audit/log - */ -export interface SalaryLeaveDisciplineExecutionContext { - user: { sub: string; name: string }; - req?: any; -} - -/** - * Service สำหรับสร้าง ProfileSalary + ProfileDiscipline + handle leave ของคำสั่งวินัย - * - * ใช้กับ commandType: C-PM-19, 20, 25, 26, 27, 28, 29, 30, 31, 32 - * - * - endpoint /org/command/excexute/salary-leave-discipline เรียกผ่าน service นี้ (thin wrapper) - * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (Linear Flow) - * - * Behavior ทั้งหมด preserve จาก CommandController.newSalaryAndUpdateLeaveDiscipline ต้นฉบับ - * รวมถึงกรณี OFFICER ที่การ save ProfileSalary + ProfileDiscipline ถูก comment out ไว้ - * (เก็บไว้เพื่อ preserve behavior เดิม — มีเพียง EMPLOYEE เท่านั้นที่ save จริง) - * - * Batch semantics: all-or-nothing — ประมวลผลทุกคนภายใต้ transaction เดียว (sequential) - * ถ้าคนใด throw จะ rollback ทั้ง batch และ propagate error ออกไป (ล้มเหลวทั้งหมด) - * ถ้าทุกคนสำเร็จจะ return result รายงาน success count - * - * ⚠️ หมายเหตุ Keycloak: operation (deleteUser) ทำภายใน transaction เพื่อ preserve behavior - * เดิม — Keycloak ไม่สามารถ rollback ได้ ถ้า DB rollback หลังจาก Keycloak operation สำเร็จ - * → Keycloak จะถูกเปลี่ยนไปแล้ว - */ -export class ExecuteSalaryLeaveDisciplineService { - private commandRepository = AppDataSource.getRepository(Command); - private profileRepository = AppDataSource.getRepository(Profile); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - - /** - * ประมวลผลคำสั่งวินัยทั้ง batch - * - * @returns สรุปผล success/failure ต่อคน - */ - async executeSalaryLeaveDiscipline( - data: SalaryLeaveDisciplineItem[], - ctx: SalaryLeaveDisciplineExecutionContext, - ): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? "unknown"; - const commandCode = data?.find((x) => x.commandCode)?.commandCode ?? "unknown"; - console.log( - `[ExecuteSalaryLeaveDisciplineService] Starting executeSalaryLeaveDiscipline — commandCode: ${commandCode}, commandId: ${commandId}`, - ); - console.log(`[ExecuteSalaryLeaveDisciplineService] Request body count: ${data?.length ?? 0}`); - - // ───────────────────────────────────────────────────────────── - // Normalize date fields (ผ่าน handler จะได้ string → ต้องแปลงเป็น Date) - // ───────────────────────────────────────────────────────────── - const toDate = (v: any): Date | null => { - if (v == null || v === "") return null; - if (v instanceof Date) return isNaN(v.getTime()) ? null : v; - const d = new Date(v); - return isNaN(d.getTime()) ? null : d; - }; - for (const item of data ?? []) { - const it = item as any; - it.dateLeave = toDate(it.dateLeave); - it.commandDateAffect = toDate(it.commandDateAffect); - it.commandDateSign = toDate(it.commandDateSign); - } - - let _posNumCodeSit: string = ""; - let _posNumCodeSitAbb: string = ""; - const _command = await this.commandRepository.findOne({ - relations: ["commandType"], - where: { id: data.find((x) => x.commandId)?.commandId ?? "" }, - }); - if (_command) { - if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; - } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - _posNumCodeSit = "กรุงเทพมหานคร"; - _posNumCodeSitAbb = "กทม."; - } else { - let _profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: _command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - _posNumCodeSit = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - _posNumCodeSitAbb = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - } - } - - // ───────────────────────────────────────────────────────────── - // Single transaction ครอบทั้ง batch (all-or-nothing) - // ทุกคนใช้ manager ตัวเดียวกัน — คนใด throw จะ rollback ทั้ง batch - // และ propagate error ออกไป (ล้มเหลวทั้งหมด) โดย log error ของคนที่ทำให้ fail ก่อน rethrow - // ───────────────────────────────────────────────────────────── - await AppDataSource.transaction(async (manager) => { - for (const item of data ?? []) { - try { - await this.processOne(item, ctx, manager, _command, _posNumCodeSit, _posNumCodeSitAbb); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryLeaveDisciplineService] Failed commandCode=${commandCode}, commandId=${commandId}, profileId=${item.profileId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - } - - /** - * ประมวลผล 1 คน ภายใน transaction เดียว (manager) - * ทุก save ใช้ manager.getRepository(...) เพื่อให้อยู่ใน transaction เดียวกัน - * ถ้า throw ระหว่างทาง → rollback ทั้งหมดของคนนี้ + ทั้ง batch (กัน partial commit) - */ - private async processOne( - item: SalaryLeaveDisciplineItem, - ctx: SalaryLeaveDisciplineExecutionContext, - manager: EntityManager, - _command: Command | null, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - ): Promise { - const req = ctx.req; - - const profileRepository = manager.getRepository(Profile); - const profileEmployeeRepository = manager.getRepository(ProfileEmployee); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - const disciplineRepository = manager.getRepository(ProfileDiscipline); - const disciplineHistoryRepository = manager.getRepository(ProfileDisciplineHistory); - const orgRevisionRepo = manager.getRepository(OrgRevision); - const employeePosMasterRepository = manager.getRepository(EmployeePosMaster); - - let _commandYear = item.commandYear; - if (item.commandYear) { - _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; - } - - const orgRevision = await orgRevisionRepo.findOne({ - where: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }); - - let orgRootRef: any = null; - let orgChild1Ref: any = null; - let orgChild2Ref: any = null; - let orgChild3Ref: any = null; - let orgChild4Ref: any = null; - - const code = _command?.commandType?.code; - - // ═══════════════════════════════════════════════════════════ - // OFFICER (ข้าราชการ) - // ═══════════════════════════════════════════════════════════ - if (item.profileType && item.profileType.trim().toUpperCase() == "OFFICER") { - const profile: any = await profileRepository.findOne({ - relations: [ - "posLevel", - "posType", - "current_holders", - "current_holders.orgRoot", - "current_holders.orgChild1", - "current_holders.orgChild2", - "current_holders.orgChild3", - "current_holders.orgChild4", - "current_holders.positions", - "current_holders.positions.posExecutive", - "roleKeycloaks", - ], - where: { id: item.profileId }, - }); - if (!profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); - } - const lastSalary = await salaryRepo.findOne({ - where: { profileId: item.profileId }, - select: ["order"], - order: { order: "DESC" }, - }); - const nextOrder = lastSalary ? lastSalary.order + 1 : 1; - - //ลบตำแหน่งที่รักษาการแทน (await + ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน) - if (code && ["C-PM-19", "C-PM-20"].includes(code)) { - await removePostMasterAct(profile.id, manager); - } - - const orgRevisionRef = - profile?.current_holders?.find((x: any) => x.orgRevisionId == orgRevision?.id) ?? null; - orgRootRef = orgRevisionRef?.orgRoot ?? null; - orgChild1Ref = orgRevisionRef?.orgChild1 ?? null; - orgChild2Ref = orgRevisionRef?.orgChild2 ?? null; - orgChild3Ref = orgRevisionRef?.orgChild3 ?? null; - orgChild4Ref = orgRevisionRef?.orgChild4 ?? null; - - const position = - profile.current_holders - .filter((x: any) => x.orgRevisionId == orgRevision?.id)[0] - ?.positions?.filter((pos: any) => pos.positionIsSelected === true)[0] ?? null; - - // ประวัติตำแหน่ง - const data = new ProfileSalary(); - data.posNumCodeSit = _posNumCodeSit; - data.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - profileId: profile.id, - commandId: item.commandId, - position: profile.position, - positionName: profile.position, - positionType: profile?.posType?.posTypeName ?? null, - positionLevel: profile?.posLevel?.posLevelName ?? null, - positionExecutive: position?.posExecutive?.posExecutiveName ?? null, - amount: item.amount ? item.amount : null, - positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, - amountSpecial: item.amountSpecial ? item.amountSpecial : null, - mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, - order: nextOrder, - orgRoot: item.orgRoot, - orgChild1: item.orgChild1, - orgChild2: item.orgChild2, - orgChild3: item.orgChild3, - orgChild4: item.orgChild4, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - dateGovernment: item.commandDateAffect ?? new Date(), - isGovernment: item.isGovernment, - commandNo: item.commandNo, - commandYear: item.commandYear, - posNo: item.posNo, - posNoAbb: item.posNoAbb, - commandDateAffect: item.commandDateAffect, - commandDateSign: item.commandDateSign, - commandCode: item.commandCode, - commandName: item.commandName, - remark: item.remark, - }; - Object.assign(data, meta); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...data, id: undefined }); - // ── preserve: OFFICER branch ไม่ save ProfileSalary (comment ตามต้นฉบับ) ── - // await salaryRepo.save(data, { data: req }); - // history.profileSalaryId = data.id; - // await salaryHistoryRepo.save(history, { data: req }); - - // ประวัติวินัย - const dataDis = new ProfileDiscipline(); - const metaDis = { - date: item.commandDateAffect, - refCommandDate: item.commandDateSign, - refCommandNo: `${item.commandNo}/${item.commandYear}`, - refCommandId: item.commandId, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - Object.assign(dataDis, { ...item, ...metaDis }); - const historyDis = new ProfileDisciplineHistory(); - Object.assign(historyDis, { ...dataDis, id: undefined }); - // ── preserve: OFFICER branch ไม่ save ProfileDiscipline (comment ตามต้นฉบับ) ── - // await disciplineRepository.save(dataDis, { data: req }); - // historyDis.profileDisciplineId = dataDis.id; - // await disciplineHistoryRepository.save(historyDis, { data: req }); - - // ทะเบียนประวัติ - if (item.isLeave != null) { - const _profile: any = await profileRepository.findOne({ - where: { id: item.profileId }, - relations: ["roleKeycloaks"], - }); - if (!_profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); - } - const _null: any = null; - _profile.isLeave = item.isLeave; - _profile.leaveReason = item.leaveReason ?? _null; - _profile.dateLeave = item.dateLeave ?? _null; - _profile.lastUpdateUserId = ctx.user.sub; - _profile.lastUpdateFullName = ctx.user.name; - _profile.lastUpdatedAt = new Date(); - if (item.isLeave == true) { - if (orgRevisionRef) { - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - await CreatePosMasterHistoryOfficer(orgRevisionRef.id, req, "DELETE", null, manager); - } - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - await removeProfileInOrganize(_profile.id, "OFFICER", manager); - } - const clearProfile = await checkCommandType(String(item.commandId)); - if (clearProfile.status) { - if ( - _profile.keycloak != null && - _profile.keycloak != "" && - _profile.isDelete === false - ) { - // Keycloak ทำภายใน transaction — ไม่สามารถ rollback ได้ (ดู docstring ของ class) - const delUserKeycloak = await deleteUser(_profile.keycloak); - if (delUserKeycloak) { - // Task #228 - // _profile.keycloak = _null; - _profile.roleKeycloaks = []; - _profile.isActive = false; - _profile.isDelete = true; - } - } - _profile.leaveCommandId = item.commandId ?? _null; - _profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; - _profile.leaveRemark = clearProfile.leaveRemark ?? _null; - _profile.leaveDate = item.commandDateAffect ?? _null; - _profile.leaveType = clearProfile.LeaveType ?? _null; - //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) - // _profile.position = _null; - // _profile.posTypeId = _null; - // _profile.posLevelId = _null; - } - await profileRepository.save(_profile, { data: req }); - setLogDataDiff(req, { before: null, after: _profile }); - } - } - // ═══════════════════════════════════════════════════════════ - // EMPLOYEE (ลูกจ้าง) - // ═══════════════════════════════════════════════════════════ - else { - const profile: any = await profileEmployeeRepository.findOne({ - relations: [ - "posLevel", - "posType", - "current_holders", - "current_holders.orgRoot", - "current_holders.orgChild1", - "current_holders.orgChild2", - "current_holders.orgChild3", - "current_holders.orgChild4", - "roleKeycloaks", - ], - where: { id: item.profileId }, - }); - if (!profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); - } - const lastSalary = await salaryRepo.findOne({ - where: { profileEmployeeId: item.profileId }, - select: ["order"], - order: { order: "DESC" }, - }); - const nextOrder = lastSalary ? lastSalary.order + 1 : 1; - const orgRevisionRef = - profile?.current_holders?.find((x: any) => x.orgRevisionId == orgRevision?.id) ?? null; - orgRootRef = orgRevisionRef?.orgRoot ?? null; - orgChild1Ref = orgRevisionRef?.orgChild1 ?? null; - orgChild2Ref = orgRevisionRef?.orgChild2 ?? null; - orgChild3Ref = orgRevisionRef?.orgChild3 ?? null; - orgChild4Ref = orgRevisionRef?.orgChild4 ?? null; - - // ประวัติตำแหน่ง - const data = new ProfileSalary(); - data.posNumCodeSit = _posNumCodeSit; - data.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - profileEmployeeId: profile.id, - commandId: item.commandId, - position: profile.position, - positionName: profile.position, - positionType: profile?.posType?.posTypeName ?? null, - positionLevel: - profile?.posType && profile?.posLevel - ? `${profile?.posType?.posTypeShortName} ${profile?.posLevel?.posLevelName}` - : null, - amount: item.amount ? item.amount : null, - positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, - mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, - order: nextOrder, - orgRoot: item.orgRoot, - orgChild1: item.orgChild1, - orgChild2: item.orgChild2, - orgChild3: item.orgChild3, - orgChild4: item.orgChild4, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - dateGovernment: item.commandDateAffect ?? new Date(), - isGovernment: item.isGovernment, - commandNo: item.commandNo, - commandYear: item.commandYear, - posNo: item.posNo, - posNoAbb: item.posNoAbb, - commandDateAffect: item.commandDateAffect, - commandDateSign: item.commandDateSign, - commandCode: item.commandCode, - commandName: item.commandName, - remark: item.remark, - }; - Object.assign(data, meta); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...data, id: undefined }); - await salaryRepo.save(data, { data: req }); - setLogDataDiff(req, { before: null, after: data }); - history.profileSalaryId = data.id; - await salaryHistoryRepo.save(history, { data: req }); - - // ประวัติวินัย - const dataDis = new ProfileDiscipline(); - const metaDis = { - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - Object.assign(dataDis, { - ...item, - ...metaDis, - date: item.commandDateAffect, - refCommandDate: item.commandDateSign, - refCommandNo: item.commandNo, - profileEmployeeId: item.profileId, - profileId: undefined, - }); - const historyDis = new ProfileDisciplineHistory(); - Object.assign(historyDis, { ...dataDis, id: undefined }); - await disciplineRepository.save(dataDis, { data: req }); - setLogDataDiff(req, { before: null, after: dataDis }); - historyDis.profileDisciplineId = dataDis.id; - await disciplineHistoryRepository.save(historyDis, { data: req }); - - // ทะเบียนประวัติ - if (item.isLeave != null) { - const _profile: any = await profileEmployeeRepository.findOne({ - where: { id: item.profileId }, - relations: ["roleKeycloaks"], - }); - if (!_profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); - } - const _null: any = null; - _profile.isLeave = item.isLeave; - _profile.leaveReason = item.leaveReason ?? _null; - _profile.dateLeave = item.dateLeave ?? _null; - _profile.lastUpdateUserId = ctx.user.sub; - _profile.lastUpdateFullName = ctx.user.name; - _profile.lastUpdatedAt = new Date(); - if (item.isLeave == true) { - // บันทึกประวัติก่อนลบตำแหน่ง - const curRevision = await orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - if (curRevision) { - const curPosMaster = await employeePosMasterRepository.findOne({ - where: { - current_holderId: _profile.id, - orgRevisionId: curRevision.id, - }, - }); - if (curPosMaster) { - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - await CreatePosMasterHistoryEmployee(curPosMaster.id, req, "DELETE", manager); - } - } - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - await removeProfileInOrganize(_profile.id, "EMPLOYEE", manager); - } - const clearProfile = await checkCommandType(String(item.commandId)); - if (clearProfile.status) { - if ( - _profile.keycloak != null && - _profile.keycloak != "" && - _profile.isDelete === false - ) { - // Keycloak deleteUser ทำภายใน transaction — ถ้า DB rollback หลังจากนี้ Keycloak จะถูกลบไปแล้ว - // (Keycloak ไม่สามารถ rollback ได้) - const delUserKeycloak = await deleteUser(_profile.keycloak); - if (delUserKeycloak) { - // Task #228 - // _profile.keycloak = _null; - _profile.roleKeycloaks = []; - _profile.isActive = false; - _profile.isDelete = true; - } - } - _profile.leaveCommandId = item.commandId ?? _null; - _profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; - _profile.leaveRemark = clearProfile.leaveRemark ?? _null; - _profile.leaveDate = item.commandDateAffect ?? _null; - _profile.leaveType = clearProfile.LeaveType ?? _null; - //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) - // _profile.position = _null; - // _profile.posTypeId = _null; - // _profile.posLevelId = _null; - } - await profileEmployeeRepository.save(_profile, { data: req }); - setLogDataDiff(req, { before: null, after: _profile }); - } - } - - // Task #2190 (preserve: organizeName computed แต่ยังไม่ได้ใช้ในต้นฉบับ — เก็บไว้ตาม behavior เดิม) - if (_command && ["C-PM-19", "C-PM-20"].includes(_command.commandType.code)) { - let organizeName = ""; - if (orgRootRef) { - const names = [ - orgChild4Ref?.orgChild4Name, - orgChild3Ref?.orgChild3Name, - orgChild2Ref?.orgChild2Name, - orgChild1Ref?.orgChild1Name, - orgRootRef?.orgRootName, - ].filter(Boolean); - organizeName = names.join(" "); - } - } - - console.log( - `[ExecuteSalaryLeaveDisciplineService] Completed processOne — profileId: ${item.profileId}`, - ); - } -} diff --git a/src/services/ExecuteSalaryLeaveService.ts b/src/services/ExecuteSalaryLeaveService.ts deleted file mode 100644 index 98a1da41..00000000 --- a/src/services/ExecuteSalaryLeaveService.ts +++ /dev/null @@ -1,708 +0,0 @@ -import { Double, EntityManager, In, Like } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatusCode from "../interfaces/http-status"; -import { Profile } from "../entities/Profile"; -import { ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { OrgRoot } from "../entities/OrgRoot"; -import { OrgRevision } from "../entities/OrgRevision"; -import { PosMaster } from "../entities/PosMaster"; -import { Position } from "../entities/Position"; -import { RoleKeycloak } from "../entities/RoleKeycloak"; -import { CommandRecive } from "../entities/CommandRecive"; -import { Command } from "../entities/Command"; -import { - checkCommandType, - checkReturnCommandType, - removePostMasterAct, - removeProfileInOrganize, - setLogDataDiff, -} from "../interfaces/utils"; -import { reOrderCommandRecivesAndDelete } from "./CommandService"; -import { CreatePosMasterHistoryOfficer } from "./PositionService"; -import { getOrgFullName, getPosMasterNo } from "../utils/org-formatting"; -import { - addUserRoles, - createUser, - deleteUser, - getRoleMappings, - getRoles, - getUserByUsername, - updateUserAttributes, -} from "../keycloak"; - -/** - * Input: ข้อมูล 1 คนสำหรับ endpoint excexute/salary-leave - * (C-PM-08, 09, 17, 18, 41, 48 — ลาออก/พักราชการ/กลับเข้าราชการ ของข้าราชการ) - */ -export interface SalaryLeaveItem { - profileId: string; - amount?: Double | null; - amountSpecial?: Double | null; - positionSalaryAmount?: Double | null; - mouthSalaryAmount?: Double | null; - positionExecutive: string | null; - positionExecutiveField?: string | null; - positionArea?: string | null; - positionType: string | null; - positionLevel: string | null; - isLeave: boolean; - leaveReason?: string | null; - dateLeave?: Date | string | null; - posExecutiveId?: string | null; - positionField?: string | null; - commandId?: string | null; - isGovernment?: boolean | null; - orgRoot?: string | null; - orgChild1?: string | null; - orgChild2?: string | null; - orgChild3?: string | null; - orgChild4?: string | null; - commandNo: string | null; - commandYear: number | null; - posNo: string | null; - posNoAbb: string | null; - commandDateAffect?: Date | string | null; - commandDateSign?: Date | string | null; - positionName: string | null; - commandCode?: string | null; - commandName?: string | null; - remark: string | null; - positionId?: string | null; - positionTypeNew?: string | null; - positionLevelNew?: string | null; - positionNameNew?: string | null; - posmasterId?: string | null; - posTypeNameNew?: string | null; - posLevelNameNew?: string | null; - posNoNew?: string | null; - posNoAbbNew?: string | null; - orgRootNew?: string | null; - orgChild1New?: string | null; - orgChild2New?: string | null; - orgChild3New?: string | null; - orgChild4New?: string | null; - resignId?: string | null; -} - -/** - * Context สำหรับ audit/log - */ -export interface SalaryLeaveExecutionContext { - user: { sub: string; name: string }; - req?: any; -} - -/** - * Service สำหรับสร้าง ProfileSalary ข้าราชการ + handle leave/กลับเข้าราชการ - * - * ใช้กับ commandType: C-PM-08, 09, 17, 18, 41, 48 - * - * - endpoint /org/command/excexute/salary-leave เรียกผ่าน service นี้ (thin wrapper) - * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (Linear Flow) - * - * Behavior ทั้งหมด preserve จาก CommandController.newSalaryAndUpdateLeave ต้นฉบับ - * - * Batch semantics: all-or-nothing — ประมวลผลทุกคนภายใต้ transaction เดียว (sequential) - * ถ้าคนใด throw จะ rollback ทั้ง batch และ propagate error ออกไป (ล้มเหลวทั้งหมด) - * ถ้าทุกคนสำเร็จจะ return result รายงาน success count - * - * ⚠️ หมายเหตุ Keycloak: operations (deleteUser/createUser/addUserRoles/updateUserAttributes) - * ทำภายใน transaction เพื่อ preserve behavior เดิม — Keycloak ไม่สามารถ rollback ได้ - * ถ้า DB rollback หลังจาก Keycloak operation สำเร็จ → Keycloak จะถูกเปลี่ยนไปแล้ว - */ -export class ExecuteSalaryLeaveService { - private commandRepository = AppDataSource.getRepository(Command); - private commandReciveRepository = AppDataSource.getRepository(CommandRecive); - private profileRepository = AppDataSource.getRepository(Profile); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - private roleKeycloakRepo = AppDataSource.getRepository(RoleKeycloak); - - /** - * ประมวลผลสร้าง ProfileSalary + handle leave/กลับเข้าราชการ ของข้าราชการทั้ง batch - * - * @returns สรุปผล success/failure ต่อคน - */ - async executeSalaryLeave(data: SalaryLeaveItem[], ctx: SalaryLeaveExecutionContext): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? "unknown"; - const commandCode = data?.find((x) => x.commandCode)?.commandCode ?? "unknown"; - console.log( - `[ExecuteSalaryLeaveService] Starting executeSalaryLeave — commandCode: ${commandCode}, commandId: ${commandId}`, - ); - console.log(`[ExecuteSalaryLeaveService] Request body count: ${data?.length ?? 0}`); - - // ───────────────────────────────────────────────────────────── - // Normalize date fields (ผ่าน handler จะได้ string → ต้องแปลงเป็น Date) - // ───────────────────────────────────────────────────────────── - const toDate = (v: any): Date | null => { - if (v == null || v === "") return null; - if (v instanceof Date) return isNaN(v.getTime()) ? null : v; - const d = new Date(v); - return isNaN(d.getTime()) ? null : d; - }; - for (const item of data ?? []) { - const it = item as any; - it.dateLeave = toDate(it.dateLeave); - it.commandDateAffect = toDate(it.commandDateAffect); - it.commandDateSign = toDate(it.commandDateSign); - } - - const roleKeycloak = await this.roleKeycloakRepo.findOne({ - where: { name: Like("USER") }, - }); - let _posNumCodeSit: string = ""; - let _posNumCodeSitAbb: string = ""; - const _command = await this.commandRepository.findOne({ - relations: ["commandType"], - where: { id: data.find((x) => x.commandId)?.commandId ?? "" }, - }); - if (_command) { - if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; - } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - _posNumCodeSit = "กรุงเทพมหานคร"; - _posNumCodeSitAbb = "กทม."; - } else { - let _profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: _command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - _posNumCodeSit = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - _posNumCodeSitAbb = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - } - } - const today = new Date().setHours(0, 0, 0, 0); - - // ───────────────────────────────────────────────────────────── - // Single transaction ครอบทั้ง batch (all-or-nothing) - // ทุกคนใช้ manager ตัวเดียวกัน — คนใด throw จะ rollback ทั้ง batch - // และ propagate error ออกไป (ล้มเหลวทั้งหมด) โดย log error ของคนที่ทำให้ fail ก่อน rethrow - // ───────────────────────────────────────────────────────────── - await AppDataSource.transaction(async (manager) => { - for (const item of data ?? []) { - try { - await this.processOne( - item, - ctx, - manager, - _command, - _posNumCodeSit, - _posNumCodeSitAbb, - today, - roleKeycloak, - ); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryLeaveService] Failed commandCode=${commandCode}, commandId=${commandId}, profileId=${item.profileId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - } - - /** - * ประมวลผล 1 คน ภายใน transaction เดียว (manager) - * ทุก save ใช้ manager.getRepository(...) เพื่อให้อยู่ใน transaction เดียวกัน - * ถ้า throw ระหว่างทาง → rollback ทั้งหมดของคนนี้ + ทั้ง batch (กัน partial commit) - */ - private async processOne( - item: SalaryLeaveItem, - ctx: SalaryLeaveExecutionContext, - manager: EntityManager, - _command: Command | null, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - today: number, - roleKeycloak: RoleKeycloak | null, - ): Promise { - const req = ctx.req; - - const commandReciveRepository = manager.getRepository(CommandRecive); - const profileRepository = manager.getRepository(Profile); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - const posMasterRepository = manager.getRepository(PosMaster); - const positionRepository = manager.getRepository(Position); - const orgRevisionRepo = manager.getRepository(OrgRevision); - const roleKeycloakRepo = manager.getRepository(RoleKeycloak); - - const profile = await profileRepository.findOne({ - where: { id: item.profileId }, - relations: { - roleKeycloaks: true, - }, - }); - if (!profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); - } - //ลบตำแหน่งที่รักษาการแทน - const code = _command?.commandType?.code; - if (code && ["C-PM-08", "C-PM-17", "C-PM-18", "C-PM-48"].includes(code)) { - // await (เดิมไม่ await = fire-and-forget bug) + ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction - await removePostMasterAct(profile.id, manager); - } - //ออกคำสั่งยกเลิกลาออก ลบเฉพาะคนที่ขอยกเลิกลาออก - else if (item.resignId && code && ["C-PM-41"].includes(code)) { - const commandResign = await commandReciveRepository.findOne({ - where: { refId: item.resignId }, - relations: { command: true }, - }); - const executeDate = commandResign - ? new Date(commandResign.command.commandExcecuteDate).setHours(0, 0, 0, 0) - : today; - if ( - commandResign && - _command.status !== "REPORTED" && - (_command.status !== "WAITING" || today < executeDate) - ) { - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - await reOrderCommandRecivesAndDelete(commandResign!.id, manager); - } - } - let _commandYear = item.commandYear; - if (item.commandYear) { - _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; - } - const returnWork = await checkReturnCommandType(String(item.commandId)); - const dest_item = await salaryRepo.findOne({ - where: { profileId: item.profileId }, - order: { order: "DESC" }, - }); - const before = null; - const dataSalary = new ProfileSalary(); - dataSalary.posNumCodeSit = _posNumCodeSit; - dataSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - dataSalary.dateGovernment = (item.commandDateAffect as Date) ?? new Date(); - dataSalary.order = dest_item == null ? 1 : dest_item.order + 1; - const meta = { - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - if (!returnWork) { - Object.assign(dataSalary, { ...item, ...meta }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...dataSalary, id: undefined }); - await salaryRepo.save(dataSalary, { data: req }); - setLogDataDiff(req, { before, after: dataSalary }); - history.profileSalaryId = dataSalary.id; - await salaryHistoryRepo.save(history, { data: req }); - } - const _null: any = null; - profile.isLeave = item.isLeave; - profile.leaveReason = item.leaveReason ?? _null; - profile.dateLeave = item.dateLeave ?? _null; - profile.lastUpdateUserId = ctx.user.sub; - profile.lastUpdateFullName = ctx.user.name; - profile.lastUpdatedAt = new Date(); - const clearProfile = await checkCommandType(String(item.commandId)); - - //ปั๊มประวัติก่อนลบตำแหน่ง - const curRevision = await orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - let orgRootRef = null; - let orgChild1Ref = null; - let orgChild2Ref = null; - let orgChild3Ref = null; - let orgChild4Ref = null; - if (curRevision) { - const curPosMaster = await posMasterRepository.findOne({ - where: { - current_holderId: profile.id, - orgRevisionId: curRevision.id, - }, - relations: { - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - orgRootRef = curPosMaster?.orgRoot ?? null; - orgChild1Ref = curPosMaster?.orgChild1 ?? null; - orgChild2Ref = curPosMaster?.orgChild2 ?? null; - orgChild3Ref = curPosMaster?.orgChild3 ?? null; - orgChild4Ref = curPosMaster?.orgChild4 ?? null; - if (curPosMaster && clearProfile.LeaveType != "RETIRE_OUT_EMP") { - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - console.log( - `[ExecuteSalaryLeaveService] Creating PosMasterHistory — posMasterId: ${curPosMaster.id}, profileId: ${item.profileId}, type: DELETE`, - ); - await CreatePosMasterHistoryOfficer(curPosMaster.id, req, "DELETE", null, manager); - } - } - - //ลบตำแหน่ง - if (item.isLeave == true) { - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - await removeProfileInOrganize(profile.id, "OFFICER", manager); - } - if (clearProfile.status) { - if (profile.keycloak != null && profile.keycloak != "" && profile.isDelete === false) { - // Keycloak ทำภายใน transaction — ไม่สามารถ rollback ได้ (ดู docstring ของ class) - const delUserKeycloak = await deleteUser(profile.keycloak); - if (delUserKeycloak) { - // Task #228 - // profile.keycloak = _null; - profile.roleKeycloaks = []; - profile.isActive = false; - profile.isDelete = true; - } - } - profile.leaveCommandId = item.commandId ?? _null; - profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; - profile.leaveRemark = clearProfile.leaveRemark ?? _null; - profile.leaveDate = item.commandDateAffect ?? _null; - profile.leaveType = clearProfile.LeaveType ?? _null; - //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) - // profile.position = _null; - // profile.posTypeId = _null; - // profile.posLevelId = _null; - } - - if (item.isGovernment == true) { - if (returnWork) { - //ปลดตำแหน่งเดิมที่ไม่ถูกปลดออกจากกิ่งครั้งเมื่อออกคำสั่งพักราชการหรือออกราชการไว้ - await removeProfileInOrganize(profile.id, "OFFICER", manager); - //ปั๊มตำแหน่งใหม่ - // หา posMaster และเช็ค orgRevisionIsCurrent - let posMaster = await posMasterRepository.findOne({ - where: { id: item.posmasterId?.toString() }, - relations: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - - // เช็คว่า posMaster ที่หามาอยู่ในโครงสร้างปัจจุบันหรือไม่ - const isCurrent = - posMaster?.orgRevision?.orgRevisionIsCurrent === true && - posMaster?.orgRevision?.orgRevisionIsDraft === false; - - // ถ้าไม่อยู่ในโครงสร้างปัจจุบัน ให้หาตัวใหม่จาก ancestorDNA - if (!isCurrent && posMaster?.ancestorDNA) { - posMaster = await posMasterRepository.findOne({ - where: { - ancestorDNA: posMaster.ancestorDNA, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - } - - if (posMaster) { - const checkPosition = await positionRepository.find({ - where: { - posMasterId: posMaster.id, - positionIsSelected: true, - }, - }); - if (checkPosition.length > 0) { - const clearPosition = checkPosition.map((positions) => ({ - ...positions, - positionIsSelected: false, - })); - await positionRepository.save(clearPosition); - } - posMaster.current_holderId = profile.id; - posMaster.lastUpdatedAt = new Date(); - // posMaster.conditionReason = _null; - // posMaster.isCondition = false; - await posMasterRepository.save(posMaster); - - // Match position ตามลำดับ priority: - // Condition 1: match จาก positionId - // Condition 2: match 7 ฟิลด์ (positionName, posTypeId, posLevelId, positionField, positionArea, positionExecutiveField, posExecutiveId) - // Condition 3: match 3 ฟิลด์ (positionName, posTypeId, posLevelId) - // Fallback: เลือก position แรกใน posMaster - - let positionNew: Position | null = null; - - // ═══════════════════════════════════════════════════════════ - // CONDITION 1: เช็คจาก positionId ตรง - // ═══════════════════════════════════════════════════════════ - if (item.positionId) { - const positionById = await positionRepository.findOne({ - where: { - id: item.positionId, - posMasterId: posMaster.id, // ต้องอยู่ใน posMaster ที่ถูกต้อง - }, - relations: ["posExecutive"], - }); - - if (positionById) { - positionNew = positionById; - } - } - - // ═══════════════════════════════════════════════════════════ - // CONDITION 2: Match 7 ฟิลด์ (ถ้า Condition 1 ไม่ match) - // ═══════════════════════════════════════════════════════════ - if (!positionNew && item.positionNameNew && item.positionTypeNew && item.positionLevelNew) { - // สร้าง where clause แบบ dynamic - ใส่เฉพาะฟิลด์ที่มีค่า - const whereCondition: any = { - posMasterId: posMaster.id, - positionName: item.positionNameNew, - posTypeId: item.positionTypeNew, - posLevelId: item.positionLevelNew, - }; - - if (item.positionField) { - whereCondition.positionField = item.positionField; - } - if (item.posExecutiveId) { - whereCondition.posExecutiveId = item.posExecutiveId; - } - if (item.positionExecutiveField) { - whereCondition.positionExecutiveField = item.positionExecutiveField; - } - if (item.positionArea) { - whereCondition.positionArea = item.positionArea; - } - - const positionBy7Fields = await positionRepository.findOne({ - where: whereCondition, - relations: ["posExecutive"], - order: { orderNo: "ASC" }, - }); - - if (positionBy7Fields) { - positionNew = positionBy7Fields; - } - } - - // ═══════════════════════════════════════════════════════════ - // CONDITION 3: Match 3 ฟิลด์ (ถ้า Condition 2 ไม่ match) - // ═══════════════════════════════════════════════════════════ - if (!positionNew && item.positionNameNew && item.positionTypeNew && item.positionLevelNew) { - const positionBy3Fields = await positionRepository.findOne({ - where: { - posMasterId: posMaster.id, - positionName: item.positionNameNew, - posTypeId: item.positionTypeNew, - posLevelId: item.positionLevelNew, - }, - relations: ["posExecutive"], - order: { orderNo: "ASC" }, - }); - - if (positionBy3Fields) { - positionNew = positionBy3Fields; - } - } - - // // FALLBACK: เลือก position แรก (ถ้าไม่เจอทั้ง 2 condition) - // if (!positionNew) { - // const fallbackPositions = await positionRepository.find({ - // where: { - // posMasterId: posMaster.id, - // }, - // relations: ["posExecutive"], - // order: { - // orderNo: "ASC", - // }, - // take: 1, - // }); - - // if (fallbackPositions.length > 0) { - // positionNew = fallbackPositions[0]; - // } - // } - - if (positionNew) { - positionNew.positionIsSelected = true; - await positionRepository.save(positionNew, { data: req }); - } - // ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction เดียวกัน - console.log( - `[ExecuteSalaryLeaveService] Creating PosMasterHistory — posMasterId: ${posMaster.id}, profileId: ${item.profileId}`, - ); - await CreatePosMasterHistoryOfficer(posMaster.id, req, null, null, manager); - profile.posMasterNo = getPosMasterNo(posMaster); - profile.org = getOrgFullName(posMaster); - } - const newMapProfileSalary = { - profileId: profile.id, - commandId: item.commandId, - positionName: item.positionNameNew ?? null, - positionType: item.posTypeNameNew ?? null, - positionLevel: item.posLevelNameNew ?? null, - amount: item.amount ? item.amount : null, - positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, - amountSpecial: item.amountSpecial ? item.amountSpecial : null, - mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, - posNo: item.posNoNew, - posNoAbb: item.posNoAbbNew, - orgRoot: item.orgRootNew, - orgChild1: item.orgChild1New, - orgChild2: item.orgChild2New, - orgChild3: item.orgChild3New, - orgChild4: item.orgChild4New, - isGovernment: item.isGovernment, - commandNo: item.commandNo, - commandYear: item.commandYear, - commandDateAffect: item.commandDateAffect, - commandDateSign: item.commandDateSign, - commandCode: item.commandCode, - commandName: item.commandName, - remark: item.remark, - }; - Object.assign(dataSalary, { ...newMapProfileSalary, ...meta }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...dataSalary, id: undefined }); - await salaryRepo.save(dataSalary); - history.profileSalaryId = dataSalary.id; - await salaryHistoryRepo.save(history); - profile.leaveReason = _null; - profile.leaveCommandId = _null; - profile.leaveCommandNo = _null; - profile.leaveRemark = _null; - profile.leaveDate = _null; - profile.leaveType = _null; - profile.position = item.positionNameNew ?? _null; - profile.posTypeId = item.positionTypeNew ?? _null; - profile.posLevelId = item.positionLevelNew ?? _null; - } - let userKeycloakId; - const checkUser = await getUserByUsername(profile.citizenId); - //ถ้ายังไม่มี user keycloak ให้สร้างใหม่ - if (checkUser.length == 0) { - let password = profile.citizenId; - if (profile.birthDate != null) { - const _date = new Date(profile.birthDate.toDateString()) - .getDate() - .toString() - .padStart(2, "0"); - const _month = (new Date(profile.birthDate.toDateString()).getMonth() + 1) - .toString() - .padStart(2, "0"); - const _year = new Date(profile.birthDate.toDateString()).getFullYear() + 543; - password = `${_date}${_month}${_year}`; - } - // กรอง "." ออกจาก firstName ก่อนส่งไป keycloak - const sanitizedFirstName = profile.firstName?.replace(/\./g, "") ?? ""; - // Keycloak ทำภายใน transaction — ไม่สามารถ rollback ได้ (ดู docstring ของ class) - userKeycloakId = await createUser(profile.citizenId, password, { - firstName: sanitizedFirstName, - lastName: profile.lastName, - }); - const list = await getRoles(); - let result = false; - if (Array.isArray(list) && userKeycloakId) { - result = await addUserRoles( - userKeycloakId, - list - .filter((v) => v.name === "USER") - .map((x) => ({ - id: x.id, - name: x.name, - })), - ); - } - profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; - profile.keycloak = - userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; - } - //ถ้ามีอยู่แล้วให้ใช้อันเดิม - else { - const rolesData = await getRoleMappings(checkUser[0].id); - if (rolesData) { - const _roleKeycloak = await roleKeycloakRepo.find({ - where: { name: In(rolesData.map((x: any) => x.name)) }, - }); - profile.roleKeycloaks = - _roleKeycloak && _roleKeycloak.length > 0 ? _roleKeycloak : []; - } - profile.keycloak = checkUser[0].id; - } - profile.amount = item.amount ?? _null; - profile.amountSpecial = item.amountSpecial ?? _null; - profile.isActive = true; - profile.isDelete = false; - } - await profileRepository.save(profile); - - // if (profile.id) { - // await this.keycloakAttributeService.clearOrgDnaAttributes( - // [profile.id], - // "PROFILE", - // ); - // } - - // update user attribute in keycloak - await updateUserAttributes(profile.keycloak ?? "", { - profileId: [profile.id], - prefix: [profile.prefix || ""], - }); - - // Task #2190 - if (code && ["C-PM-17", "C-PM-18", "C-PM-48"].includes(code)) { - let organizeName = ""; - if (orgRootRef) { - const names = [ - orgChild4Ref?.orgChild4Name, - orgChild3Ref?.orgChild3Name, - orgChild2Ref?.orgChild2Name, - orgChild1Ref?.orgChild1Name, - orgRootRef?.orgRootName, - ].filter(Boolean); - organizeName = names.join(" "); - } - } - - console.log( - `[ExecuteSalaryLeaveService] Completed processOne — profileId: ${item.profileId}`, - ); - } -} diff --git a/src/services/ExecuteSalaryProbationService.ts b/src/services/ExecuteSalaryProbationService.ts deleted file mode 100644 index 41dfb7b6..00000000 --- a/src/services/ExecuteSalaryProbationService.ts +++ /dev/null @@ -1,539 +0,0 @@ -import { Double, EntityManager, In, Repository } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatusCode from "../interfaces/http-status"; -import { Profile } from "../entities/Profile"; -import { ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { Command } from "../entities/Command"; -import { OrgRoot } from "../entities/OrgRoot"; -import { OrgRevision } from "../entities/OrgRevision"; -import { checkCommandType, removeProfileInOrganize } from "../interfaces/utils"; -import { CreatePosMasterHistoryOfficer } from "./PositionService"; -import { deleteUser } from "../keycloak"; - -/** - * Input: ข้อมูล 1 คนที่ probation return กลับมา (หลัง Linear Flow refactor) - * - C-PM-11 : excexute/salary-probation (ผ่านทดลองงาน) - * - C-PM-12 : excexute/salary-probation-leave (ออกเพราะผลทดลองฯ ต่ำกว่ามาตรฐาน) - * - * shape เดียวกับ body.data ของ endpoint /org/command/excexute/salary-probation(-leave) เดิม - */ -export interface ProbationSalaryItem { - profileId: string; - commandId?: string | null; - amount?: Double | null; - amountSpecial?: Double | null; - positionSalaryAmount?: Double | null; - mouthSalaryAmount?: Double | null; - commandNo: string | null; - commandYear: number | null; - commandDateAffect?: Date | string | null; - commandDateSign?: Date | string | null; - positionName?: string | null; - commandCode?: string | null; - commandName?: string | null; - remark: string | null; - isGovernment?: boolean | null; // C-PM-12 เท่านั้น -} - -/** - * Context สำหรับ audit/log (เหมือน ExecuteSalaryService) - */ -export interface ProbationExecutionContext { - user: { sub: string; name: string }; - req?: any; -} - -/** - * Service สำหรับคำสั่งทดลองปฏิบัติหน้าที่ราชการ (probation) — C-PM-11, C-PM-12 - * - * เดิมเป็น circular callback: org AMQ → probation → PostData("/org/command/excexute/salary-probation(-leave)") - * หลัง Linear Flow: org AMQ → probation (return salary data) → เรียก service นี้โดยตรง (no callback) - * - * - C-PM-11 : executeProbationPass — สร้าง ProfileSalary + history, isProbation=false - * - C-PM-12 : executeProbationLeave — leave logic + deleteUser(Keycloak) + สร้าง ProfileSalary + history - * - * - endpoint /org/command/excexute/salary-probation(-leave) เรียกผ่าน service นี้ (thin wrapper) - * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (Linear Flow) - * - * Behavior ทั้งหมด preserve จาก CommandController - * (newSalaryAndUpdateLeaveDisciplinefgh / ExecuteCommand12Async ต้นฉบับ) - * - * Batch semantics: all-or-nothing — ประมวลผลทุกคนภายใต้ transaction เดียว (sequential) - * ถ้าคนใด throw จะ rollback ทั้ง batch และ propagate error ออกไป (ล้มเหลวทั้งหมด) - * - * ⚠️ Keycloak: deleteUser (C-PM-12) ทำภายใน transaction เพื่อ preserve behavior เดิม - * (consistent กับ ExecuteSalaryService C-PM-13/15/16) — Keycloak ไม่สามารถ rollback ได้ - * ถ้า DB rollback หลังจาก deleteUser สำเร็จ → user จะถูกลบใน Keycloak ไปแล้ว - */ -export class ExecuteSalaryProbationService { - private commandRepository = AppDataSource.getRepository(Command); - private profileRepository = AppDataSource.getRepository(Profile); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - - // ───────────────────────────────────────────────────────────── - // แก้ปัญหา _posNumCodeSit resolution ที่ซ้ำกันในทุก endpoint - // (เดิมอยู่ใน controller — ย้ายมานี่ ทำครั้งเดียวก่อนเข้า transaction) - // ───────────────────────────────────────────────────────────── - private async resolvePosNumCodeSit( - commandId: string | null | undefined, - ): Promise<{ posNumCodeSit: string; posNumCodeSitAbb: string }> { - let posNumCodeSit = ""; - let posNumCodeSitAbb = ""; - const command = commandId - ? await this.commandRepository.findOne({ where: { id: commandId } }) - : null; - if (command) { - if (command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - posNumCodeSit = orgRootDeputy ? orgRootDeputy.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy.orgRootShortName : "สนป."; - } else if (command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - posNumCodeSit = "กรุงเทพมหานคร"; - posNumCodeSitAbb = "กทม."; - } else { - const profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - posNumCodeSit = - profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - posNumCodeSitAbb = - profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - } - } - return { posNumCodeSit, posNumCodeSitAbb }; - } - - // normalize date (AMQ path ส่ง string มา → แปลงเป็น Date / null ถ้า invalid) - private toDate(v: any): Date | null { - if (v == null || v === "") return null; - if (v instanceof Date) return isNaN(v.getTime()) ? null : v; - const d = new Date(v); - return isNaN(d.getTime()) ? null : d; - } - - // ═══════════════════════════════════════════════════════════════ - // C-PM-11 : ผ่านทดลองปฏิบัติหน้าที่ราชการ - // สร้าง ProfileSalary + history แล้ว set isProbation=false - // ═══════════════════════════════════════════════════════════════ - async executeProbationPass( - data: ProbationSalaryItem[], - ctx: ProbationExecutionContext, - ): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? ""; - console.log( - `[ExecuteSalaryProbationService] executeProbationPass (C-PM-11) — commandId: ${commandId}, count: ${data?.length ?? 0}`, - ); - - // ───────────────────────────────────────────────────────────── - // Normalize date fields (ผ่าน AMQ handler จะได้ string → ต้องแปลงเป็น Date) - // ───────────────────────────────────────────────────────────── - for (const item of data ?? []) { - const it = item as any; - it.commandDateAffect = this.toDate(it.commandDateAffect); - it.commandDateSign = this.toDate(it.commandDateSign); - } - - const { posNumCodeSit: _posNumCodeSit, posNumCodeSitAbb: _posNumCodeSitAbb } = - await this.resolvePosNumCodeSit(commandId); - - const profileIds = (data ?? []).map((x) => x.profileId).filter(Boolean); - - await AppDataSource.transaction(async (manager) => { - const profileRepository = manager.getRepository(Profile); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - const orgRevisionRepo = manager.getRepository(OrgRevision); - - for (const item of data ?? []) { - try { - await this.processOneProbationPass( - item, - ctx, - _posNumCodeSit, - _posNumCodeSitAbb, - salaryRepo, - salaryHistoryRepo, - profileRepository, - orgRevisionRepo, - ); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryProbationService] Failed C-PM-11, commandId=${commandId}, profileId=${item.profileId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - - // C-PM-11: ผ่านทดลองงาน → isProbation = false (bulk update ใน transaction เดียวกัน) - if (profileIds.length > 0) { - await profileRepository.update({ id: In(profileIds) }, { isProbation: false }); - } - }); - - console.log(`[ExecuteSalaryProbationService] Completed C-PM-11 — ${data?.length ?? 0} items`); - } - - private async processOneProbationPass( - item: ProbationSalaryItem, - ctx: ProbationExecutionContext, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - salaryRepo: Repository, - salaryHistoryRepo: Repository, - profileRepository: Repository, - orgRevisionRepo: Repository, - ): Promise { - // current orgRevision (อ่านครั้งเดียวต่อคน — preserve query pattern ของ endpoint เดิม) - const orgRevision = await orgRevisionRepo.findOne({ - where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, - }); - - const profile: any = await profileRepository.findOne({ - relations: [ - "posType", - "posLevel", - "current_holders", - "current_holders.orgRoot", - "current_holders.orgChild1", - "current_holders.orgChild2", - "current_holders.orgChild3", - "current_holders.orgChild4", - ], - where: { id: item.profileId }, - }); - if (!profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); - } - - const lastSalary = await salaryRepo.findOne({ - where: { profileId: item.profileId }, - select: ["order"], - order: { order: "DESC" }, - }); - const nextOrder = lastSalary ? lastSalary.order + 1 : 1; - - const orgRevisionRef = - profile?.current_holders?.find((x: any) => x.orgRevisionId == orgRevision?.id) ?? null; - const shortName = - orgRevisionRef?.orgChild4?.orgChild4ShortName ?? - orgRevisionRef?.orgChild3?.orgChild3ShortName ?? - orgRevisionRef?.orgChild2?.orgChild2ShortName ?? - orgRevisionRef?.orgChild1?.orgChild1ShortName ?? - orgRevisionRef?.orgRoot?.orgRootShortName ?? - null; - const posNo = orgRevisionRef?.posMasterNo?.toString() ?? null; - // NOTE: endpoint เดิมไม่ได้ load relation "current_holders.positions" → position เป็น null (preserve) - const position = - profile.current_holders - ?.filter((x: any) => x.orgRevisionId == orgRevision?.id)[0] - ?.positions?.filter((pos: any) => pos.positionIsSelected === true)[0] ?? null; - - const dataSalary = new ProfileSalary(); - dataSalary.posNumCodeSit = _posNumCodeSit; - dataSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - profileId: item.profileId, - commandId: item.commandId, - positionName: profile.position, - positionType: profile?.posType?.posTypeName ?? null, - positionLevel: profile?.posLevel?.posLevelName ?? null, - positionExecutive: position?.posExecutive?.posExecutiveName ?? null, - amount: item.amount ? item.amount : null, - amountSpecial: item.amountSpecial ? item.amountSpecial : null, - positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, - mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, - order: nextOrder, - orgRoot: orgRevisionRef?.orgRoot?.orgRootName ?? null, - orgChild1: orgRevisionRef?.orgChild1?.orgChild1Name ?? null, - orgChild2: orgRevisionRef?.orgChild2?.orgChild2Name ?? null, - orgChild3: orgRevisionRef?.orgChild3?.orgChild3Name ?? null, - orgChild4: orgRevisionRef?.orgChild4?.orgChild4Name ?? null, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - commandNo: item.commandNo, - commandYear: item.commandYear, - posNo: posNo, - posNoAbb: shortName, - commandDateAffect: item.commandDateAffect, - commandDateSign: item.commandDateSign, - commandCode: item.commandCode, - commandName: item.commandName, - remark: item.remark, - }; - Object.assign(dataSalary, meta); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...dataSalary, id: undefined }); - - await salaryRepo.save(dataSalary); - history.profileSalaryId = dataSalary.id; - await salaryHistoryRepo.save(history); - } - - // ═══════════════════════════════════════════════════════════════ - // C-PM-12 : ออกจากราชการเพราะผลการทดลองปฏิบัติหน้าที่ราชการต่ำกว่ามาตรฐาน - // leave logic (removeProfileInOrganize + deleteUser Keycloak) + สร้าง ProfileSalary + history - // ═══════════════════════════════════════════════════════════════ - async executeProbationLeave( - data: ProbationSalaryItem[], - ctx: ProbationExecutionContext, - ): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? ""; - console.log( - `[ExecuteSalaryProbationService] executeProbationLeave (C-PM-12) — commandId: ${commandId}, count: ${data?.length ?? 0}`, - ); - - for (const item of data ?? []) { - const it = item as any; - it.commandDateAffect = this.toDate(it.commandDateAffect); - it.commandDateSign = this.toDate(it.commandDateSign); - } - - const { posNumCodeSit: _posNumCodeSit, posNumCodeSitAbb: _posNumCodeSitAbb } = - await this.resolvePosNumCodeSit(commandId); - - await AppDataSource.transaction(async (manager) => { - const profileRepository = manager.getRepository(Profile); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - const orgRevisionRepo = manager.getRepository(OrgRevision); - - for (const item of data ?? []) { - try { - await this.processOneProbationLeave( - item, - ctx, - manager, - _posNumCodeSit, - _posNumCodeSitAbb, - salaryRepo, - salaryHistoryRepo, - profileRepository, - orgRevisionRepo, - ); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryProbationService] Failed C-PM-12, commandId=${commandId}, profileId=${item.profileId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - - console.log(`[ExecuteSalaryProbationService] Completed C-PM-12 — ${data?.length ?? 0} items`); - } - - private async processOneProbationLeave( - item: ProbationSalaryItem, - ctx: ProbationExecutionContext, - manager: EntityManager, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - salaryRepo: Repository, - salaryHistoryRepo: Repository, - profileRepository: Repository, - orgRevisionRepo: Repository, - ): Promise { - const req = ctx.req; - - const profile: any = await profileRepository.findOne({ - relations: [ - "posType", - "posLevel", - "current_holders", - "current_holders.orgRoot", - "current_holders.orgChild1", - "current_holders.orgChild2", - "current_holders.orgChild3", - "current_holders.orgChild4", - "current_holders.positions", - "current_holders.positions.posExecutive", - ], - where: { id: item.profileId }, - }); - if (!profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์"); - } - - const lastSalary = await salaryRepo.findOne({ - where: { profileId: item.profileId }, - select: ["order"], - order: { order: "DESC" }, - }); - const nextOrder = lastSalary ? lastSalary.order + 1 : 1; - let _commandYear = item.commandYear; - if (item.commandYear) { - _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; - } - - // _profile (load แยกสำหรับ mutation เกี่ยวกับ Keycloak/leave — preserve pattern เดิม) - const _profile: any = await profileRepository.findOne({ - where: { id: item.profileId }, - relations: ["roleKeycloaks"], - }); - if (!_profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลโปรไฟล์"); - } - - let dateLeave_: any = item.commandDateAffect; - _profile.isLeave = true; - _profile.leaveReason = - "คำสั่งให้ข้าราชการออกจากราชการเพราะผลการทดลองปฏิบัติหน้าที่ราชการต่ำกว่ามาตรฐานที่กำหนด"; - _profile.dateLeave = dateLeave_; - _profile.lastUpdateUserId = ctx.user.sub; - _profile.lastUpdateFullName = ctx.user.name; - _profile.lastUpdatedAt = new Date(); - - const orgRevision = await orgRevisionRepo.findOne({ - where: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }); - const orgRevisionRef = - profile?.current_holders?.find((x: any) => x.orgRevisionId == orgRevision?.id) ?? null; - const orgRootRef = orgRevisionRef?.orgRoot ?? null; - const orgChild1Ref = orgRevisionRef?.orgChild1 ?? null; - const orgChild2Ref = orgRevisionRef?.orgChild2 ?? null; - const orgChild3Ref = orgRevisionRef?.orgChild3 ?? null; - const orgChild4Ref = orgRevisionRef?.orgChild4 ?? null; - - const matchHolder = profile.current_holders?.find((x: any) => x.orgRevisionId == orgRevision?.id); - const shortName = - !profile.current_holders || profile.current_holders.length == 0 - ? null - : matchHolder != null && matchHolder?.orgChild4 != null - ? `${matchHolder.orgChild4.orgChild4ShortName}` - : matchHolder != null && matchHolder?.orgChild3 != null - ? `${matchHolder.orgChild3.orgChild3ShortName}` - : matchHolder != null && matchHolder?.orgChild2 != null - ? `${matchHolder.orgChild2.orgChild2ShortName}` - : matchHolder != null && matchHolder?.orgChild1 != null - ? `${matchHolder.orgChild1.orgChild1ShortName}` - : matchHolder != null && matchHolder?.orgRoot != null - ? `${matchHolder.orgRoot.orgRootShortName}` - : null; - const posNo = `${matchHolder?.posMasterNo}`; - const position = - matchHolder?.positions?.filter((pos: any) => pos.positionIsSelected === true)[0] ?? null; - - const profileSalary: ProfileSalary = Object.assign(new ProfileSalary(), { - profileId: item.profileId, - commandId: item.commandId, - positionName: profile.position, - positionType: profile?.posType?.posTypeName ?? null, - positionLevel: profile?.posLevel?.posLevelName ?? null, - positionExecutive: position?.posExecutive?.posExecutiveName ?? null, - amount: item.amount ? item.amount : null, - amountSpecial: item.amountSpecial ? item.amountSpecial : null, - positionSalaryAmount: item.positionSalaryAmount ? item.positionSalaryAmount : null, - mouthSalaryAmount: item.mouthSalaryAmount ? item.mouthSalaryAmount : null, - order: nextOrder, - orgRoot: orgRootRef?.orgRootName ?? null, - orgChild1: orgChild1Ref?.orgChild1Name ?? null, - orgChild2: orgChild2Ref?.orgChild2Name ?? null, - orgChild3: orgChild3Ref?.orgChild3Name ?? null, - orgChild4: orgChild4Ref?.orgChild4Name ?? null, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - dateGovernment: item.commandDateAffect ?? new Date(), - isGovernment: item.isGovernment, - commandNo: item.commandNo, - commandYear: item.commandYear, - posNo: posNo, - posNoAbb: shortName, - commandDateAffect: item.commandDateAffect, - commandDateSign: item.commandDateSign, - commandCode: item.commandCode, - commandName: item.commandName, - remark: item.remark, - posNumCodeSit: _posNumCodeSit, - posNumCodeSitAbb: _posNumCodeSitAbb, - }); - - if (orgRevisionRef) { - await CreatePosMasterHistoryOfficer(orgRevisionRef.id, req, "DELETE", null, manager); - } - await removeProfileInOrganize(profile.id, "OFFICER", manager); - - const clearProfile = await checkCommandType(String(item.commandId)); - const _null: any = null; - if (clearProfile.status) { - // Keycloak deleteUser ทำภายใน transaction (preserve behavior เดิม — Keycloak ไม่ rollback ได้) - if (_profile.keycloak != null && _profile.keycloak != "" && _profile.isDelete === false) { - const delUserKeycloak = await deleteUser(_profile.keycloak); - if (delUserKeycloak) { - // Task #228 - _profile.roleKeycloaks = []; - _profile.isActive = false; - _profile.isDelete = true; - } - } - _profile.leaveCommandId = item.commandId ?? _null; - _profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; - _profile.leaveRemark = clearProfile.leaveRemark ?? _null; - _profile.leaveDate = item.commandDateAffect ?? _null; - _profile.leaveType = clearProfile.LeaveType ?? _null; - //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) - // _profile.position = _null; - // _profile.posTypeId = _null; - // _profile.posLevelId = _null; - } - await Promise.all([ - profileRepository.save(_profile), - salaryRepo.save(profileSalary), - ]); - - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...profileSalary, id: undefined }); - history.profileSalaryId = profileSalary.id; - await salaryHistoryRepo.save(history); - - console.log( - `[ExecuteSalaryProbationService] processOneProbationLeave done — profileId: ${item.profileId}`, - ); - } -} diff --git a/src/services/ExecuteSalaryReportService.ts b/src/services/ExecuteSalaryReportService.ts deleted file mode 100644 index 7c72c367..00000000 --- a/src/services/ExecuteSalaryReportService.ts +++ /dev/null @@ -1,318 +0,0 @@ -import { EntityManager, Repository } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatusCode from "../interfaces/http-status"; -import permission from "../interfaces/permission"; -import { setLogDataDiff } from "../interfaces/utils"; -import { Profile } from "../entities/Profile"; -import { ProfileEmployee } from "../entities/ProfileEmployee"; -import { - CreateProfileSalary, - CreateProfileSalaryEmployee, - ProfileSalary, -} from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { Command } from "../entities/Command"; -import { OrgRoot } from "../entities/OrgRoot"; - -/** - * Context สำหรับ audit/log (เหมือน ExecuteSalaryService) - */ -export interface SalaryReportExecutionContext { - user: { sub: string; name: string }; - req?: any; -} - -/** - * Service สำหรับคำสั่งเงินเดือนที่ยิงมาจาก salary service - * - * - C-PM-33, C-PM-34, C-PM-35, C-PM-45 : officer → /org/profile/salary/update - * - C-PM-36, C-PM-37, C-PM-46 : employee → /org/profile-employee/salary/update - * - * เดิมเป็น circular callback: org AMQ → salary service → PostData("/org/profile/(employee/)salary/update") - * หลัง Linear Flow: org AMQ → salary service (return salary data) → เรียก service นี้โดยตรง (no callback) - * - * - executeOfficerSalaryUpdate : สร้าง ProfileSalary + history + อัปเดต Profile (amount*) - * - executeEmployeeSalaryUpdate : สร้าง ProfileSalary + history + อัปเดต ProfileEmployee (amount* + salaryLevel/group) - * - * Behavior ทั้งหมด preserve จาก - * ProfileSalaryController.updateSalary / ProfileSalaryEmployeeController.updateSalary ต้นฉบับ - * (รวม permission check + setLogDataDiff + save({data: req})) - * - * Batch semantics: all-or-nothing — transaction เดียวครอบทั้ง batch - * ถ้าคนใด throw (validation/permission) จะ rollback ทั้ง batch และ propagate error - * - * ⚠️ หมายเหตุ permission check: ทำ per-item ภายใน transaction (preserve behavior เดิมที่เช็คทุกคน) - * ทำ HTTP loopback ไป /org/permission/user/... ด้วย token ใน ctx.req — หาก batch ใหญ่อาจช้า - * (เหมือนเดิม เพราะ salary เดิมก็ยิงเข้า endpoint ทีละคนพร้อม permission check) - */ -export class ExecuteSalaryReportService { - private commandRepository = AppDataSource.getRepository(Command); - private profileRepository = AppDataSource.getRepository(Profile); - private profileEmployeeRepository = AppDataSource.getRepository(ProfileEmployee); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - - // ───────────────────────────────────────────────────────────── - // resolve _posNumCodeSit/Abb จาก command (ทำครั้งเดียวก่อนเข้า transaction) - // admin-lookup ใช้ Profile (officer: profileRepo / employee: profileGovementRepo — ทั้งคู่คือ Profile) - // ───────────────────────────────────────────────────────────── - private async resolvePosNumCodeSit( - commandId: string | null | undefined, - ): Promise<{ posNumCodeSit: string; posNumCodeSitAbb: string }> { - let posNumCodeSit = ""; - let posNumCodeSitAbb = ""; - const command = commandId - ? await this.commandRepository.findOne({ where: { id: commandId } }) - : null; - if (command) { - if (command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - posNumCodeSit = orgRootDeputy ? orgRootDeputy.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy.orgRootShortName : "สนป."; - } else if (command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - posNumCodeSit = "กรุงเทพมหานคร"; - posNumCodeSitAbb = "กทม."; - } else { - const profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - posNumCodeSit = - profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - posNumCodeSitAbb = - profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - } - } - return { posNumCodeSit, posNumCodeSitAbb }; - } - - // ═══════════════════════════════════════════════════════════════ - // C-PM-33/34/35/45 : officer salary update - // ═══════════════════════════════════════════════════════════════ - async executeOfficerSalaryUpdate( - data: CreateProfileSalary[], - ctx: SalaryReportExecutionContext, - ): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? ""; - console.log( - `[ExecuteSalaryReportService] executeOfficerSalaryUpdate — commandId: ${commandId}, count: ${data?.length ?? 0}`, - ); - - const { posNumCodeSit: _posNumCodeSit, posNumCodeSitAbb: _posNumCodeSitAbb } = - await this.resolvePosNumCodeSit(commandId); - - await AppDataSource.transaction(async (manager) => { - const profileRepository = manager.getRepository(Profile); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - - for (const item of data ?? []) { - try { - await this.processOneOfficer( - item, - ctx, - _posNumCodeSit, - _posNumCodeSitAbb, - profileRepository, - salaryRepo, - salaryHistoryRepo, - ); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryReportService] Failed officer, commandId=${commandId}, profileId=${item.profileId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - - console.log(`[ExecuteSalaryReportService] Completed officer — ${data?.length ?? 0} items`); - } - - private async processOneOfficer( - item: CreateProfileSalary, - ctx: SalaryReportExecutionContext, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - profileRepository: Repository, - salaryRepo: Repository, - salaryHistoryRepo: Repository, - ): Promise { - const req = ctx.req; - - if (!item.profileId) { - throw new HttpError(HttpStatusCode.BAD_REQUEST, "กรุณากรอก profileId"); - } - const profile = await profileRepository.findOneBy({ id: item.profileId }); - if (!profile) { - throw new HttpError(HttpStatusCode.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); - } - await new permission().PermissionOrgUserUpdate(req, "SYS_REGISTRY_OFFICER", profile.id); - - const dest_item = await salaryRepo.findOne({ - where: { profileId: item.profileId }, - order: { order: "DESC" }, - }); - const before = null; - const data = new ProfileSalary(); - data.posNumCodeSit = _posNumCodeSit; - data.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - order: dest_item == null ? 1 : dest_item.order + 1, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - - Object.assign(data, { ...item, ...meta }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...data, id: undefined }); - await salaryRepo.save(data, { data: req }); - setLogDataDiff(req, { before, after: data }); - history.profileSalaryId = data.id; - await salaryHistoryRepo.save(history, { data: req }); - - const _null: any = null; - profile.amount = item.amount ?? _null; - profile.amountSpecial = item.amountSpecial ?? _null; - profile.positionSalaryAmount = item.positionSalaryAmount ?? _null; - profile.mouthSalaryAmount = item.mouthSalaryAmount ?? _null; - await profileRepository.save(profile); - } - - // ═══════════════════════════════════════════════════════════════ - // C-PM-36/37/46 : employee salary update - // ═══════════════════════════════════════════════════════════════ - async executeEmployeeSalaryUpdate( - data: CreateProfileSalaryEmployee[], - ctx: SalaryReportExecutionContext, - ): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? ""; - console.log( - `[ExecuteSalaryReportService] executeEmployeeSalaryUpdate — commandId: ${commandId}, count: ${data?.length ?? 0}`, - ); - - const { posNumCodeSit: _posNumCodeSit, posNumCodeSitAbb: _posNumCodeSitAbb } = - await this.resolvePosNumCodeSit(commandId); - - await AppDataSource.transaction(async (manager) => { - const profileEmployeeRepository = manager.getRepository(ProfileEmployee); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - - for (const item of data ?? []) { - try { - await this.processOneEmployee( - item, - ctx, - _posNumCodeSit, - _posNumCodeSitAbb, - profileEmployeeRepository, - salaryRepo, - salaryHistoryRepo, - ); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryReportService] Failed employee, commandId=${commandId}, profileEmployeeId=${item.profileEmployeeId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - - console.log(`[ExecuteSalaryReportService] Completed employee — ${data?.length ?? 0} items`); - } - - private async processOneEmployee( - item: CreateProfileSalaryEmployee, - ctx: SalaryReportExecutionContext, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - profileEmployeeRepository: Repository, - salaryRepo: Repository, - salaryHistoryRepo: Repository, - ): Promise { - const req = ctx.req; - - if (!item.profileEmployeeId) { - throw new HttpError(HttpStatusCode.BAD_REQUEST, "กรุณากรอก profileEmployeeId"); - } - const profile = await profileEmployeeRepository.findOneBy({ id: item.profileEmployeeId }); - if (!profile) { - throw new HttpError(HttpStatusCode.BAD_REQUEST, "ไม่พบ profile ดังกล่าว"); - } - await new permission().PermissionOrgUserUpdate(req, "SYS_REGISTRY_EMP", profile.id); - - const dest_item = await salaryRepo.findOne({ - where: { profileEmployeeId: item.profileEmployeeId }, - order: { order: "DESC" }, - }); - const before = null; - const data = new ProfileSalary(); - data.posNumCodeSit = _posNumCodeSit; - data.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - order: dest_item == null ? 1 : dest_item.order + 1, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - - Object.assign(data, { ...item, ...meta }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...data, id: undefined }); - - await salaryRepo.save(data, { data: req }); - setLogDataDiff(req, { before, after: data }); - history.profileSalaryId = data.id; - await salaryHistoryRepo.save(history, { data: req }); - - const _null: any = null; - profile.amount = item.amount ?? _null; - profile.amountSpecial = item.amountSpecial ?? _null; - profile.positionSalaryAmount = item.positionSalaryAmount ?? _null; - profile.mouthSalaryAmount = item.mouthSalaryAmount ?? _null; - profile.salaryLevel = item.salaryLevel ?? _null; - profile.group = item.group ?? _null; - await profileEmployeeRepository.save(profile); - } -} diff --git a/src/services/ExecuteSalaryService.ts b/src/services/ExecuteSalaryService.ts deleted file mode 100644 index 7bc07823..00000000 --- a/src/services/ExecuteSalaryService.ts +++ /dev/null @@ -1,391 +0,0 @@ -import { Double, EntityManager } from "typeorm"; -import { AppDataSource } from "../database/data-source"; -import HttpError from "../interfaces/http-error"; -import HttpStatusCode from "../interfaces/http-status"; -import { Profile } from "../entities/Profile"; -import { ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; -import { ProfileAssistance } from "../entities/ProfileAssistance"; -import { ProfileAssistanceHistory } from "../entities/ProfileAssistanceHistory"; -import { OrgRoot } from "../entities/OrgRoot"; -import { PosMaster } from "../entities/PosMaster"; -import { Command } from "../entities/Command"; -import { - checkCommandType, - removePostMasterAct, - removeProfileInOrganize, - setLogDataDiff, -} from "../interfaces/utils"; -import { CreatePosMasterHistoryOfficer } from "./PositionService"; -import { deleteUser } from "../keycloak"; - -/** - * Input: ข้อมูล 1 คนสำหรับ endpoint excexute/salary - * (C-PM-13 โอน, C-PM-15 ช่วยราชการ, C-PM-16 เกษียณ/ปลดเกษียณ) - */ -export interface SalaryItem { - profileId: string; - amount?: Double | null; - amountSpecial?: Double | null; - positionSalaryAmount?: Double | null; - mouthSalaryAmount?: Double | null; - positionExecutive: string | null; - positionExecutiveField?: string | null; - positionArea?: string | null; - positionType: string | null; - positionLevel: string | null; - commandId?: string | null; - leaveReason?: string | null; - dateLeave?: Date | string | null; - isLeave?: boolean; - orgRoot?: string | null; - orgChild1?: string | null; - orgChild2?: string | null; - orgChild3?: string | null; - orgChild4?: string | null; - officerOrg?: string | null; - dateStart?: Date | string | null; - dateEnd?: Date | string | null; - commandNo: string | null; - commandYear: number | null; - posNo: string | null; - posNoAbb: string | null; - commandDateAffect?: Date | string | null; - commandDateSign?: Date | string | null; - positionName: string | null; - commandCode?: string | null; - commandName?: string | null; - remark: string | null; - refId?: string | null; -} - -/** - * Context สำหรับ audit/log (เหมือน ExecuteOfficerProfileService) - */ -export interface SalaryExecutionContext { - user: { sub: string; name: string }; - req?: any; -} - -/** - * Service สำหรับสร้าง ProfileSalary ของข้าราชการ + handle leave/ออกจากราชการ/ช่วยราชการ - * - * ใช้กับ commandType: C-PM-13, 15, 16 - * - * - endpoint /org/command/excexute/salary เรียกผ่าน service นี้ (thin wrapper) - * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (Linear Flow) - * - * Behavior ทั้งหมด preserve จาก CommandController.newSalaryAndUpdate ต้นฉบับ - * - * Batch semantics: all-or-nothing — ประมวลผลทุกคนภายใต้ transaction เดียว (sequential) - * ถ้าคนใด throw จะ rollback ทั้ง batch และ propagate error ออกไป (ล้มเหลวทั้งหมด) - * ถ้าทุกคนสำเร็จจะ return result รายงาน success count - * - * ⚠️ หมายเหตุ Keycloak: operation (deleteUser) ทำภายใน transaction เพื่อ preserve behavior - * เดิม — Keycloak ไม่สามารถ rollback ได้ ถ้า DB rollback หลังจาก Keycloak operation สำเร็จ - * → Keycloak จะถูกเปลี่ยนไปแล้ว - */ -export class ExecuteSalaryService { - private commandRepository = AppDataSource.getRepository(Command); - private profileRepository = AppDataSource.getRepository(Profile); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - - /** - * ประมวลผลสร้าง ProfileSalary + handle leave/assistance ทั้ง batch - * - * @returns สรุปผล success/failure ต่อคน - */ - async executeSalary(data: SalaryItem[], ctx: SalaryExecutionContext): Promise { - const commandId = data?.find((x) => x.commandId)?.commandId ?? "unknown"; - const commandCode = data?.find((x) => x.commandCode)?.commandCode ?? "unknown"; - console.log( - `[ExecuteSalaryService] Starting executeSalary — commandCode: ${commandCode}, commandId: ${commandId}`, - ); - console.log(`[ExecuteSalaryService] Request body count: ${data?.length ?? 0}`); - - // ───────────────────────────────────────────────────────────── - // Normalize date fields (ผ่าน handler จะได้ string → ต้องแปลงเป็น Date) - // ───────────────────────────────────────────────────────────── - const toDate = (v: any): Date | null => { - if (v == null || v === "") return null; - if (v instanceof Date) return isNaN(v.getTime()) ? null : v; - const d = new Date(v); - return isNaN(d.getTime()) ? null : d; - }; - for (const item of data ?? []) { - const it = item as any; - it.dateLeave = toDate(it.dateLeave); - it.dateStart = toDate(it.dateStart); - it.dateEnd = toDate(it.dateEnd); - it.commandDateAffect = toDate(it.commandDateAffect); - it.commandDateSign = toDate(it.commandDateSign); - } - - let _posNumCodeSit: string = ""; - let _posNumCodeSitAbb: string = ""; - const _command = await this.commandRepository.findOne({ - relations: ["commandType"], - where: { id: data.find((x) => x.commandId)?.commandId ?? "" }, - }); - if (_command) { - if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; - } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - _posNumCodeSit = "กรุงเทพมหานคร"; - _posNumCodeSitAbb = "กทม."; - } else { - let _profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: _command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - _posNumCodeSit = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - _posNumCodeSitAbb = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - } - } - - // ───────────────────────────────────────────────────────────── - // Single transaction ครอบทั้ง batch (all-or-nothing) - // ทุกคนใช้ manager ตัวเดียวกัน — คนใด throw จะ rollback ทั้ง batch - // และ propagate error ออกไป (ล้มเหลวทั้งหมด) โดย log error ของคนที่ทำให้ fail ก่อน rethrow - // ───────────────────────────────────────────────────────────── - let successCount = 0; - await AppDataSource.transaction(async (manager) => { - for (const item of data ?? []) { - try { - await this.processOne(item, ctx, manager, _command, _posNumCodeSit, _posNumCodeSitAbb); - } catch (err) { - const reason = - err instanceof HttpError - ? err.message - : err instanceof Error - ? err.message - : "unexpected error"; - console.error( - `[ExecuteSalaryService] Failed commandCode=${commandCode}, commandId=${commandId}, profileId=${item.profileId}: ${reason}`, - err, - ); - throw err; // → rollback ทั้ง transaction + propagate เป็น batch failure - } - } - }); - } - - /** - * ประมวลผล 1 คน ภายใน transaction เดียว (manager) - * ทุก save ใช้ manager.getRepository(...) เพื่อให้อยู่ใน transaction เดียวกัน - * ถ้า throw ระหว่างทาง → rollback ทั้งหมดของคนนี้ + ทั้ง batch (กัน partial commit) - * - * หมายเหตุ: Keycloak deleteUser ทำก่อนเข้า transaction เพราะไม่สามารถ rollback ได้ - */ - private async processOne( - item: SalaryItem, - ctx: SalaryExecutionContext, - manager: EntityManager, - _command: Command | null, - _posNumCodeSit: string, - _posNumCodeSitAbb: string, - ): Promise { - const req = ctx.req; - - const profileRepository = manager.getRepository(Profile); - const salaryRepo = manager.getRepository(ProfileSalary); - const salaryHistoryRepo = manager.getRepository(ProfileSalaryHistory); - const posMasterRepository = manager.getRepository(PosMaster); - const assistanceRepository = manager.getRepository(ProfileAssistance); - const assistanceHistoryRepository = manager.getRepository(ProfileAssistanceHistory); - - const profile: any = await profileRepository.findOne({ - where: { id: item.profileId }, - relations: { - roleKeycloaks: true, - posType: true, - posLevel: true, - }, - }); - if (!profile) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลทะเบียนประวัตินี้"); - } - const posMaster: any = await posMasterRepository.findOne({ - where: { - current_holderId: item.profileId, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - - const orgRevisionRef = posMaster ? posMaster.id : null; - const orgRootRef = orgRevisionRef?.orgRoot ?? null; - const orgChild1Ref = orgRevisionRef?.orgChild1 ?? null; - const orgChild2Ref = orgRevisionRef?.orgChild2 ?? null; - const orgChild3Ref = orgRevisionRef?.orgChild3 ?? null; - const orgChild4Ref = orgRevisionRef?.orgChild4 ?? null; - - //ลบตำแหน่งที่รักษาการแทน - const code = _command?.commandType?.code; - if (code && ["C-PM-13"].includes(code)) { - // await (เดิมไม่ await = fire-and-forget bug) + ส่ง manager เข้าไปเพื่อให้อยู่ใน transaction - await removePostMasterAct(profile.id, manager); - } - - let _commandYear = item.commandYear; - if (item.commandYear) { - _commandYear = item.commandYear > 2500 ? item.commandYear : item.commandYear + 543; - } - const dest_item = await salaryRepo.findOne({ - where: { profileId: item.profileId }, - order: { order: "DESC" }, - }); - const before = null; - const dataSalary = new ProfileSalary(); - dataSalary.posNumCodeSit = _posNumCodeSit; - dataSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - const meta = { - order: dest_item == null ? 1 : dest_item.order + 1, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - if (item.isLeave != undefined && item.isLeave == true) { - console.log( - `[ExecuteSalaryService] Creating PosMasterHistory — posMasterId: ${orgRevisionRef}, profileId: ${item.profileId}, type: DELETE`, - ); - await CreatePosMasterHistoryOfficer(orgRevisionRef, req, "DELETE", null, manager); - await removeProfileInOrganize(profile.id, "OFFICER", manager); - } - const clearProfile = await checkCommandType(String(item.commandId)); - const _null: any = null; - if (clearProfile.status) { - // Keycloak deleteUser ทำก่อนเข้า transaction-bound save ด้านล่าง - // (ทำภายใน transaction เดียวกัน เพราะถ้า fail ต้อง rollback DB ด้วย) - // หมายเหตุ: Keycloak ไม่สามารถ rollback ได้ → ถ้า DB rollback หลังจากนี้ Keycloak จะถูกลบไปแล้ว - if (profile.keycloak != null && profile.keycloak != "" && profile.isDelete === false) { - const delUserKeycloak = await deleteUser(profile.keycloak); - if (delUserKeycloak) { - // Task #228 - // profile.keycloak = _null; - profile.roleKeycloaks = []; - profile.isActive = false; - profile.isDelete = true; - } - } - profile.isLeave = item.isLeave; - profile.leaveCommandId = item.commandId ?? _null; - profile.leaveCommandNo = `${item.commandNo}/${_commandYear}`; - profile.leaveRemark = clearProfile.leaveRemark ?? _null; - profile.leaveDate = item.commandDateAffect ?? _null; - profile.leaveType = clearProfile.LeaveType ?? _null; - //ออกจากราชการ ไม่ต้องลบตำแหน่งในทะเบียน (issue #1516) - // profile.position = _null; - // profile.posTypeId = _null; - // profile.posLevelId = _null; - profile.leaveReason = item.leaveReason ?? _null; - profile.dateLeave = item.dateLeave ?? _null; - profile.amount = item.amount ?? _null; - profile.amountSpecial = item.amountSpecial ?? _null; - await profileRepository.save(profile, { data: req }); - - // if (profile.id) { - // await this.keycloakAttributeService.clearOrgDnaAttributes( - // [profile.id], - // "PROFILE", - // ); - // } - } - Object.assign(dataSalary, { ...item, ...meta }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...dataSalary, id: undefined }); - - await salaryRepo.save(dataSalary, { data: req }); - setLogDataDiff(req, { before, after: dataSalary }); - history.profileSalaryId = dataSalary.id; - await salaryHistoryRepo.save(history, { data: req }); - - if (_command) { - if (["C-PM-15", "C-PM-16"].includes(_command.commandType.code)) { - // ประวัติคำสั่งให้ช่วยราชการ - const dataAssis = new ProfileAssistance(); - - const metaAssis = { - profileId: item.profileId, - agency: item.officerOrg, - dateStart: item.dateStart, - dateEnd: item.dateEnd, - commandNo: `${item.commandNo}/${_commandYear}`, - commandName: item.commandName, - refId: item.refId, - refCommandDate: new Date(), - commandId: item.commandId, - createdUserId: ctx.user.sub, - createdFullName: ctx.user.name, - lastUpdateUserId: ctx.user.sub, - lastUpdateFullName: ctx.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - status: _command.commandType.code == "C-PM-15" ? "PENDING" : "DONE", - }; - - Object.assign(dataAssis, metaAssis); - const historyAssis = new ProfileAssistanceHistory(); - Object.assign(historyAssis, { ...dataAssis, id: undefined }); - - await assistanceRepository.save(dataAssis); - historyAssis.profileAssistanceId = dataAssis.id; - await assistanceHistoryRepository.save(historyAssis); - } - // Task #2190 - else if (_command.commandType.code == "C-PM-13") { - let organizeName = ""; - if (orgRootRef) { - const names = [ - orgChild4Ref?.orgChild4Name, - orgChild3Ref?.orgChild3Name, - orgChild2Ref?.orgChild2Name, - orgChild1Ref?.orgChild1Name, - orgRootRef?.orgRootName, - ].filter(Boolean); - organizeName = names.join(" "); - } - } - } - - console.log( - `[ExecuteSalaryService] Completed processOne — profileId: ${item.profileId}`, - ); - } -} diff --git a/src/services/KeycloakAttributeService.ts b/src/services/KeycloakAttributeService.ts index 7bfe88ed..1e0f3f07 100644 --- a/src/services/KeycloakAttributeService.ts +++ b/src/services/KeycloakAttributeService.ts @@ -530,20 +530,18 @@ export class KeycloakAttributeService { // Initialize rate limiter if rate limiting is enabled if (rateLimit && rateLimit > 0) { rateLimiter = new RateLimiter(rateLimit); - console.log( - `[syncMissingEmpTypeByMonth] Rate limiting enabled: ${rateLimit} requests/second`, - ); + console.log(`[syncMissingEmpTypeByMonth] Rate limiting enabled: ${rateLimit} requests/second`); } // Select repository based on profile type - const repo = profileType === "PROFILE" ? this.profileRepo : this.profileEmployeeRepo; + const repo = + profileType === "PROFILE" ? this.profileRepo : this.profileEmployeeRepo; // Query profiles updated within the month const profiles = await repo .createQueryBuilder("p") .where("p.keycloak IS NOT NULL") .andWhere("p.keycloak != :empty", { empty: "" }) - .andWhere("p.isDelete = :isDelete", { isDelete: false }) .andWhere("p.lastUpdatedAt BETWEEN :start AND :end", { start: startDate, end: endDate, @@ -581,7 +579,8 @@ export class KeycloakAttributeService { try { // Check if empType is empty in Keycloak - const { isEmpty, currentEmpType } = await this.checkEmpTypeEmpty(keycloakUserId); + const { isEmpty, currentEmpType } = + await this.checkEmpTypeEmpty(keycloakUserId); result.profilesChecked++; @@ -608,7 +607,8 @@ export class KeycloakAttributeService { // Sync the profile const success = await withRetry( - async () => this.syncOnOrganizationChange(profile.id, profileType), + async () => + this.syncOnOrganizationChange(profile.id, profileType), 3, // maxRetries 1000, // baseDelay ); @@ -768,13 +768,7 @@ export class KeycloakAttributeService { maxRetries?: number; // Retry attempts for failed operations rateLimit?: number; // Requests per second clearProgress?: boolean; // Start fresh, ignore existing progress - }): Promise<{ - total: number; - success: number; - failed: number; - details: any[]; - resumed?: boolean; - }> { + }): Promise<{ total: number; success: number; failed: number; details: any[]; resumed?: boolean }> { const limit = options?.limit; const concurrency = options?.concurrency ?? 5; const resume = options?.resume ?? false; @@ -928,10 +922,7 @@ export class KeycloakAttributeService { // Save progress after each batch SyncProgressManager.save(updatedState); // Log progress every 50 items - if ( - updatedState.lastSyncedIndex % 50 === 0 || - updatedState.lastSyncedIndex === updatedState.totalProfiles - ) { + if (updatedState.lastSyncedIndex % 50 === 0 || updatedState.lastSyncedIndex === updatedState.totalProfiles) { SyncProgressManager.logProgress(updatedState); } }, diff --git a/src/services/PositionService.ts b/src/services/PositionService.ts index 940991d1..b1837b99 100644 --- a/src/services/PositionService.ts +++ b/src/services/PositionService.ts @@ -11,8 +11,6 @@ import { PosMasterHistory } from "../entities/PosMasterHistory"; import { Position } from "../entities/Position"; import { ProfileEducation } from "../entities/ProfileEducation"; import { RequestWithUser } from "../middlewares/user"; -import { Profile } from "../entities/Profile"; -import { ProfileEmployee } from "../entities/ProfileEmployee"; /** * function สำหรับดึงตำแหน่งที่รักษาการแทน @@ -20,7 +18,9 @@ import { ProfileEmployee } from "../entities/ProfileEmployee"; * - ถ้า posType = "อำนวยการ" หรือ "บริหาร" ใช้ posExecutiveName * - ถ้า posType อื่นๆ ใช้ positionName + posLevel */ -export async function getPosMasterPositions(posMasterIds: string[]): Promise> { +export async function getPosMasterPositions( + posMasterIds: string[] +): Promise> { if (posMasterIds.length === 0) { return new Map(); } @@ -61,9 +61,7 @@ export async function getPosMasterPositions(posMasterIds: string[]): Promise { - const execute = async (transactionManager: EntityManager) => { - const repoPosmaster = transactionManager.getRepository(EmployeePosMaster); - const repoHistory = transactionManager.getRepository(PosMasterEmployeeHistory); - const repoProfileEmployee = transactionManager.getRepository(ProfileEmployee); - - const pm = await repoPosmaster.findOne({ - where: { id: posMasterId }, - relations: [ - "positions", - "positions.posLevel", - "positions.posType", - // "positions.posExecutive", - "orgRoot", - "orgChild1", - "orgChild2", - "orgChild3", - "orgChild4", - "current_holder", - ], - }); - if (!pm) return; - if (!pm.ancestorDNA) return; - const _null: any = null; - const h = new PosMasterEmployeeHistory(); - const selectedPosition = - pm.positions.length > 0 - ? pm.positions.find((p) => p.positionIsSelected === true) ?? null - : null; - - let position = selectedPosition?.positionName ?? _null; - let posTypeName = selectedPosition?.posType?.posTypeName ?? _null; - let posLevelName = selectedPosition?.posType && selectedPosition?.posLevel - ? `${selectedPosition?.posType?.posTypeShortName ?? ""} ${selectedPosition?.posLevel?.posLevelName ?? ""}`.trim() - : _null; - if (pm.isSit && pm.current_holderId) { - const profile = await repoProfileEmployee.findOne({ - where: { id: pm.current_holderId }, - relations: ["posType", "posLevel"] - }); - position = profile?.position ?? _null; - posTypeName = profile?.posType?.posTypeName ?? _null; - posLevelName = profile?.posType && profile?.posLevel - ? `${profile?.posType?.posTypeShortName ?? ""} ${profile?.posLevel?.posLevelName ?? ""}`.trim() - : _null; - } - h.ancestorDNA = pm.ancestorDNA; - if (!type || type != "DELETE") { - h.profileEmployeeId = pm.current_holder?.id || _null; - h.prefix = pm.current_holder?.prefix || _null; - h.firstName = pm.current_holder?.firstName || _null; - h.lastName = pm.current_holder?.lastName || _null; - h.position = position; - h.posType = posTypeName; - h.posLevel = posLevelName; - } - h.rootDnaId = pm.orgRoot?.ancestorDNA || _null; - h.child1DnaId = pm.orgChild1?.ancestorDNA || _null; - h.child2DnaId = pm.orgChild2?.ancestorDNA || _null; - h.child3DnaId = pm.orgChild3?.ancestorDNA || _null; - h.child4DnaId = pm.orgChild4?.ancestorDNA || _null; - h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null; - h.posMasterNo = pm.posMasterNo ?? _null; - h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null; - h.shortName = - [ - pm.orgChild4?.orgChild4ShortName, - pm.orgChild3?.orgChild3ShortName, - pm.orgChild2?.orgChild2ShortName, - pm.orgChild1?.orgChild1ShortName, - pm.orgRoot?.orgRootShortName, - ].find((s) => typeof s === "string" && s.trim().length > 0) ?? _null; - const userId = request?.user?.sub ?? ""; - const userName = request?.user?.name ?? "system"; - h.createdUserId = userId; - h.createdFullName = userName; - h.lastUpdateUserId = userId; - h.lastUpdateFullName = userName; - h.createdAt = new Date(); - h.lastUpdatedAt = new Date(); - await repoHistory.save(h); - }; - try { - if (manager) { - await execute(manager); - return true; - } + await AppDataSource.transaction(async (manager) => { + const repoPosmaster = manager.getRepository(EmployeePosMaster); + const repoHistory = manager.getRepository(PosMasterEmployeeHistory); - await AppDataSource.transaction(async (transactionManager) => { - await execute(transactionManager); + const pm = await repoPosmaster.findOne({ + where: { id: posMasterId }, + relations: [ + "positions", + "positions.posLevel", + "positions.posType", + // "positions.posExecutive", + "orgRoot", + "orgChild1", + "orgChild2", + "orgChild3", + "orgChild4", + "current_holder", + ], + }); + if (!pm) return false; + if (!pm.ancestorDNA) return false; + const _null: any = null; + const h = new PosMasterEmployeeHistory(); + const selectedPosition = + pm.positions.length > 0 + ? pm.positions.find((p) => p.positionIsSelected === true) ?? null + : null; + h.ancestorDNA = pm.ancestorDNA; + if (!type || type != "DELETE") { + h.prefix = pm.current_holder?.prefix || _null; + h.firstName = pm.current_holder?.firstName || _null; + h.lastName = pm.current_holder?.lastName || _null; + h.position = selectedPosition?.positionName ?? _null; + h.posType = selectedPosition?.posType?.posTypeName ?? _null; + h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _null; + } + h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null; + h.posMasterNo = pm.posMasterNo ?? _null; + h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null; + h.shortName = + [ + pm.orgChild4?.orgChild4ShortName, + pm.orgChild3?.orgChild3ShortName, + pm.orgChild2?.orgChild2ShortName, + pm.orgChild1?.orgChild1ShortName, + pm.orgRoot?.orgRootShortName, + ].find((s) => typeof s === "string" && s.trim().length > 0) ?? _null; + const userId = request?.user?.sub ?? ""; + const userName = request?.user?.name ?? "system"; + h.createdUserId = userId; + h.createdFullName = userName; + h.lastUpdateUserId = userId; + h.lastUpdateFullName = userName; + h.createdAt = new Date(); + h.lastUpdatedAt = new Date(); + await repoHistory.save(h); }); + return true; } catch (err) { - if (manager) { - console.error("CreatePosMasterHistoryEmployee error (external transaction):", err); - throw err; - } console.error("CreatePosMasterHistoryEmployee transaction error:", err); return false; } @@ -507,15 +452,8 @@ export async function BatchSavePosMasterHistoryOfficer( const existing = historyByDna.get(op.posMasterDnaId)?.[0]; const shouldInsert = !existing && op.profileId && op.pm; const profileChanged = existing && existing.profileId !== op.profileId; - const positionChanged = - existing && - (existing.position !== op.pm?.position || - existing.posType !== op.pm?.posType || - existing.posLevel !== op.pm?.posLevel || - existing.posExecutive !== op.pm?.posExecutive); - // ถ้าไม่มี record เดิม หรือ profile เปลี่ยน หรือ position เปลี่ยน ให้สร้าง record ใหม่ - if (shouldInsert || profileChanged || positionChanged) { + if (shouldInsert || profileChanged) { const newPmh = new PosMasterHistory(); newPmh.ancestorDNA = op.posMasterDnaId; newPmh.prefix = op.pm?.prefix ?? _null; @@ -556,61 +494,3 @@ export async function BatchSavePosMasterHistoryOfficer( return false; } } - -/** - * อัพเดทประวัติคนครองตำแหน่งเมื่อมีการเปลี่ยนแปลงข้อมูล profile - * เช่น เปลี่ยนชื่อ - นามสกุล - * ใช้สำหรับบันทึกประวัติเมื่อ profile ที่ครองตำแหน่งมีการเปลี่ยนแปลง - * - * @param profileId ID ของ profile ที่ต้องการตรวจสอบ - * @param request RequestWithUser สำหรับบันทึกข้อมูลผู้ดำเนินการ - * @param type "OFFICER" สำหรับข้าราชการ | "EMPLOYEE" สำหรับลูกจ้างประจำ (default: "OFFICER") - */ -export async function updateHolderProfileHistory( - profileId: string, - request: RequestWithUser, - type: "OFFICER" | "EMPLOYEE" = "OFFICER", -): Promise { - try { - if (type === "OFFICER") { - const posMasterRepo = AppDataSource.getRepository(PosMaster); - const posMaster = await posMasterRepo.findOne({ - where: { - current_holderId: profileId, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: { - orgRevision: true, - }, - }); - - if (posMaster) { - await CreatePosMasterHistoryOfficer(posMaster.id, request); - } - } else if (type === "EMPLOYEE") { - const empPosMasterRepo = AppDataSource.getRepository(EmployeePosMaster); - const employeePosMaster = await empPosMasterRepo.findOne({ - where: { - current_holderId: profileId, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: { - orgRevision: true, - }, - }); - - if (employeePosMaster) { - await CreatePosMasterHistoryEmployee(employeePosMaster.id, request); - } - } - } catch (error) { - console.error("updateHolderProfileHistory error:", error); - throw error; - } -} diff --git a/src/services/ProfileLeaveService.ts b/src/services/ProfileLeaveService.ts index 16284bb7..327a1fe2 100644 --- a/src/services/ProfileLeaveService.ts +++ b/src/services/ProfileLeaveService.ts @@ -1,13 +1,12 @@ import { AppDataSource } from "../database/data-source"; import { Profile } from "./../entities/Profile"; import { ProfileEmployee } from "../entities/ProfileEmployee"; -import { ProfileSalary } from "./../entities/ProfileSalary"; import { OrgRoot } from "../entities/OrgRoot"; import { OrgChild1 } from "../entities/OrgChild1"; import { OrgChild2 } from "../entities/OrgChild2"; import { OrgChild3 } from "../entities/OrgChild3"; import { OrgChild4 } from "../entities/OrgChild4"; -import { Brackets, In, Repository } from "typeorm"; +import { Brackets, Repository } from "typeorm"; import Extension from "../interfaces/extension"; import { RequestWithUser } from "../middlewares/user"; @@ -63,7 +62,6 @@ interface OrgParentName { export class ProfileLeaveService { private profileEmployeeRepo: Repository; private profileRepo: Repository; - private profileSalaryRepo: Repository; private orgRootRepository: Repository; private child1Repository: Repository; private child2Repository: Repository; @@ -74,7 +72,6 @@ export class ProfileLeaveService { constructor() { this.profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); this.profileRepo = AppDataSource.getRepository(Profile); - this.profileSalaryRepo = AppDataSource.getRepository(ProfileSalary); this.orgRootRepository = AppDataSource.getRepository(OrgRoot); this.child1Repository = AppDataSource.getRepository(OrgChild1); this.child2Repository = AppDataSource.getRepository(OrgChild2); @@ -210,16 +207,19 @@ export class ProfileLeaveService { let params: NodeParams = {}; const orgLists = await this.findOrgNodeParentAll(node, nodeId); - - for (let index = 0; index <= node; index++) { - const config = this.nodeConfigs[index]; - const orgName = orgLists[config.nameField as keyof OrgParentName] || null; - if (orgName) { - nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition; - nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : ""; - params[config.paramKey] = orgName; - } - } + console.log("Org Hierarchy for Node Condition:", orgLists); + await Promise.all( + this.nodeConfigs.map(async (config, index) => { + if (index <= node) { + const orgName = orgLists[config.nameField as keyof OrgParentName] || null; + if (orgName) { + nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition; + nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : ""; + params[config.paramKey] = orgName; + } + } + }), + ); return { condition: nodeCondition, @@ -234,31 +234,53 @@ export class ProfileLeaveService { child3: string | null; child4: string | null; }): Promise { - const [rootName, child1, child2, child3, child4] = await Promise.all([ - orgIds.root - ? this.orgRootRepository.findOne({ where: { id: orgIds.root }, select: ["orgRootName"] }) - : Promise.resolve(null), - orgIds.child1 - ? this.child1Repository.findOne({ where: { id: orgIds.child1 }, select: ["orgChild1Name"] }) - : Promise.resolve(null), - orgIds.child2 - ? this.child2Repository.findOne({ where: { id: orgIds.child2 }, select: ["orgChild2Name"] }) - : Promise.resolve(null), - orgIds.child3 - ? this.child3Repository.findOne({ where: { id: orgIds.child3 }, select: ["orgChild3Name"] }) - : Promise.resolve(null), - orgIds.child4 - ? this.child4Repository.findOne({ where: { id: orgIds.child4 }, select: ["orgChild4Name"] }) - : Promise.resolve(null), - ]); - - return { - orgRootName: rootName?.orgRootName ?? null, - orgChild1Name: child1?.orgChild1Name ?? null, - orgChild2Name: child2?.orgChild2Name ?? null, - orgChild3Name: child3?.orgChild3Name ?? null, - orgChild4Name: child4?.orgChild4Name ?? null, + const orgNames: OrgParentName = { + orgRootName: null, + orgChild1Name: null, + orgChild2Name: null, + orgChild3Name: null, + orgChild4Name: null, }; + if (orgIds.root) { + const rootName = await this.orgRootRepository.findOne({ + where: { id: orgIds.root }, + select: ["orgRootName"], + }); + orgNames.orgRootName = rootName ? rootName.orgRootName : null; + } + if (orgIds.child1) { + const child1 = await this.child1Repository.findOne({ + where: { id: orgIds.child1 }, + select: ["orgChild1Name"], + }); + orgNames.orgChild1Name = child1 ? child1.orgChild1Name : null; + } + + if (orgIds.child2) { + const child2 = await this.child2Repository.findOne({ + where: { id: orgIds.child2 }, + select: ["orgChild2Name"], + }); + orgNames.orgChild2Name = child2 ? child2.orgChild2Name : null; + } + + if (orgIds.child3) { + const child3 = await this.child3Repository.findOne({ + where: { id: orgIds.child3 }, + select: ["orgChild3Name"], + }); + orgNames.orgChild3Name = child3 ? child3.orgChild3Name : null; + } + + if (orgIds.child4) { + const child4 = await this.child4Repository.findOne({ + where: { id: orgIds.child4 }, + select: ["orgChild4Name"], + }); + orgNames.orgChild4Name = child4 ? child4.orgChild4Name : null; + } + + return orgNames; } /** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */ @@ -295,15 +317,16 @@ export class ProfileLeaveService { return { condition: "1=0", params: {} }; // no access } - for (let index = 0; index < this.nodeConfigs.length; index++) { - const config = this.nodeConfigs[index]; - const orgName = orgLists[config.nameField as keyof OrgParentName] || null; - if (orgName) { - nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition; - nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : ""; - params[config.paramKey] = orgName; - } - } + await Promise.all( + this.nodeConfigs.map(async (config, index) => { + const orgName = orgLists[config.nameField as keyof OrgParentName] || null; + if (orgName) { + nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition; + nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : ""; + params[config.paramKey] = orgName; + } + }), + ); return { condition: nodeCondition, @@ -455,146 +478,97 @@ export class ProfileLeaveService { _data, } = filter; - const t0 = Date.now(); const searchQuery = this.buildSearchQuery(searchField, "profileEmployee"); - // สร้าง base WHERE conditions แชร์ระหว่าง count/id/data query - const baseWhere = (qb: any) => { - qb.where( - new Brackets((qb2) => { - qb2.where("profileEmployee.isLeave = :isLeave", { isLeave: true }).orWhere( + // สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary + const queryBuilder = this.profileEmployeeRepo + .createQueryBuilder("profileEmployee") + .leftJoinAndSelect("profileEmployee.posLevel", "posLevel") + .leftJoinAndSelect("profileEmployee.posType", "posType") + .leftJoinAndSelect("profileEmployee.profileEmployeeEmployment", "profileEmployeeEmployment") + .leftJoin( + "profileEmployee.profileSalary", + "profileSalary", + "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id and ps.positionName != 'เกษียณอายุราชการ')", + ) + .addSelect([ + "profileSalary.id", + "profileSalary.order", + "profileSalary.posNo", + "profileSalary.posNoAbb", + "profileSalary.orgRoot", + "profileSalary.orgChild1", + "profileSalary.orgChild2", + "profileSalary.orgChild3", + "profileSalary.orgChild4", + ]) + .where( + new Brackets((qb) => { + qb.where("profileEmployee.isLeave = :isLeave", { isLeave: true }).orWhere( "profileEmployee.isRetirement = :isRetirement", { isRetirement: true }, ); }), ) - .andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" }) - .andWhere( - new Brackets((qb2) => { - qb2.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", { - keyword: `%${searchKeyword}%`, - }); - }), - ); + .andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" }) + .andWhere( + new Brackets((qb) => { + qb.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", { + keyword: `%${searchKeyword}%`, + }); + }), + ); - if (posType) { - qb.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` }); - } - if (posLevel) { - qb.andWhere( - "CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2", - { keyword2: `${posLevel}` }, - ); - } - if (isProbation !== undefined && isProbation !== null) { - qb.andWhere("profileEmployee.isProbation = :isProbation", { isProbation }); - } - if (retireType) { - qb.andWhere("profileEmployee.leaveType = :retireType", { retireType }); - } - }; - - // Compute permission/node conditions เพียงครั้งเดียว - const conditions: { condition: string; params: Record }[] = []; - if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") { - conditions.push(await this.buildPermissionCondition(_data, isAll)); + // เพิ่มเงื่อนไขการค้นหา + if (posType) { + queryBuilder.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` }); } + + if (posLevel) { + queryBuilder.andWhere( + "CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2", + { keyword2: `${posLevel}` }, + ); + } + + if (isProbation) { + queryBuilder.andWhere(`profileEmployee.isProbation = ${isProbation}`); + } + + if (retireType) { + queryBuilder.andWhere("profileEmployee.leaveType = :retireType", { retireType }); + } + if (node !== null && node !== undefined && nodeId) { - conditions.push(await this.buildNodeCondition(node, nodeId, isAll)); - } - const applyConditions = (qb: any) => { - for (const cond of conditions) { - qb.andWhere(cond.condition, cond.params); + const [nodeCondition, permissionCondition] = await Promise.all([ + this.buildNodeCondition(node, nodeId, isAll), + this.buildPermissionCondition(_data, isAll), + ]); + // console.log("Permission Condition:", permissionCondition); + // console.log("Node Condition:", nodeCondition); + + queryBuilder.andWhere(nodeCondition.condition, nodeCondition.params); + + if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") { + queryBuilder.andWhere(permissionCondition.condition, permissionCondition.params); } - }; - - // console.log(`[ProfileLeaveService] getLeaveEmployees conditions took ${Date.now() - t0}ms`); - - // สร้าง salary EXISTS filter (ใช้ซ้ำทั้ง step1, step2) - const applySalaryFilter = (qb: any) => { - if (conditions.length > 0) { - let existsCond = "profileSalary.positionName != :notRetire"; - const existsParams: Record = { notRetire: "เกษียณอายุราชการ" }; - for (const cond of conditions) { - existsCond += ` AND ${cond.condition}`; - Object.assign(existsParams, cond.params); - } - qb.andWhere( - `EXISTS (SELECT 1 FROM profileSalary WHERE profileEmployeeId = profileEmployee.id AND ${existsCond} AND profileSalary.\`order\` = (SELECT MAX(ps.\`order\`) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id AND ps.positionName != :notRetire2))`, - { ...existsParams, notRetire2: "เกษียณอายุราชการ" } - ); - } - }; - - // Step 1: Count query - const countQb = this.profileEmployeeRepo - .createQueryBuilder("profileEmployee") - .leftJoinAndSelect("profileEmployee.posLevel", "posLevel") - .leftJoinAndSelect("profileEmployee.posType", "posType"); - baseWhere(countQb); - applySalaryFilter(countQb); - const total = await countQb.getCount(); - - // console.log(`[ProfileLeaveService] getLeaveEmployees count took ${Date.now() - t0}ms, total=${total}`); - - // Step 2: ดึงเฉพาะ profileEmployee IDs ที่ผ่านเงื่อนไข - const idQb = this.profileEmployeeRepo - .createQueryBuilder("profileEmployee") - .select(["profileEmployee.id"]) - .leftJoin("profileEmployee.posLevel", "posLevel") - .leftJoin("profileEmployee.posType", "posType"); - baseWhere(idQb); - applySalaryFilter(idQb); - idQb.orderBy(sortBy, sort).skip((page - 1) * pageSize).take(pageSize); - const rawIds = await idQb.getRawMany(); - const employeeIds = rawIds.map((r) => r.profileEmployee_id); - - // console.log(`[ProfileLeaveService] getLeaveEmployees ids took ${Date.now() - t0}ms, ids=${employeeIds.length}`); - - if (employeeIds.length === 0) { - return { data: [], total }; } - // Step 3: Load full data โดยไม่ JOIN salary - const records = await this.profileEmployeeRepo.find({ - where: { id: In(employeeIds) }, - relations: ["posLevel", "posType", "profileEmployeeEmployment"], - order: { [sortBy.split(".")[1]]: sort } as any, - }); + // เพิ่ม sorting และ pagination + queryBuilder + .orderBy(sortBy, sort) + .skip((page - 1) * pageSize) + .take(pageSize); - // Step 4: Load salary เฉพาะ row ที่มี order สูงสุดต่อ profileEmployeeId (INNER JOIN + GROUP BY) - const salaries = await this.profileSalaryRepo - .createQueryBuilder("ps") - .innerJoin( - (subQuery) => - subQuery - .select("ps2.profileEmployeeId", "pid") - .addSelect("MAX(ps2.order)", "maxOrd") - .from(ProfileSalary, "ps2") - .where("ps2.profileEmployeeId IN (:...employeeIds)", { employeeIds }) - .andWhere("ps2.positionName != :notRetire", { notRetire: "เกษียณอายุราชการ" }) - .groupBy("ps2.profileEmployeeId"), - "latest", - "latest.pid = ps.profileEmployeeId AND ps.order = latest.maxOrd" - ) - .getMany(); + const [records, total] = await queryBuilder.getManyAndCount(); - // สร้าง map: profileEmployeeId → salary ที่มี order สูงสุด - const salaryMap = new Map(); - for (const s of salaries) { - salaryMap.set(s.profileEmployeeId, s); - } + // print query for debug + // console.log("SQL Query:", queryBuilder.getSql()); - // แปลงข้อมูลพร้อม salary - const data = records.map((record) => { - const salary = salaryMap.get(record.id); - if (salary) { - (record as any).profileSalary = [salary]; - } - return this.transformEmployeeData(record); - }); + const data = await Promise.all( + records.map((record) => Promise.resolve(this.transformEmployeeData(record))), + ); - // console.log(`[ProfileLeaveService] getLeaveEmployees total took ${Date.now() - t0}ms, total=${total}`); return { data, total }; } @@ -675,143 +649,94 @@ export class ProfileLeaveService { _data, } = filter; - const t0 = Date.now(); const searchQuery = this.buildSearchQuery(searchField); - // สร้าง base WHERE conditions แชร์ระหว่าง count/id/data query - const baseWhere = (qb: any) => { - qb.where( - new Brackets((qb2) => { - qb2.where("profile.isLeave = :isLeave", { isLeave: true }).orWhere( + // สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary + const queryBuilder = this.profileRepo + .createQueryBuilder("profile") + .leftJoinAndSelect("profile.posLevel", "posLevel") + .leftJoinAndSelect("profile.posType", "posType") + .leftJoin( + "profile.profileSalary", + "profileSalary", + "profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id and ps.positionName != 'เกษียณอายุราชการ')", + ) + .addSelect([ + "profileSalary.id", + "profileSalary.order", + "profileSalary.posNo", + "profileSalary.posNoAbb", + "profileSalary.orgRoot", + "profileSalary.orgChild1", + "profileSalary.orgChild2", + "profileSalary.orgChild3", + "profileSalary.orgChild4", + "profileSalary.positionExecutive", + ]) + .where( + new Brackets((qb) => { + qb.where("profile.isLeave = :isLeave", { isLeave: true }).orWhere( "profile.isRetirement = :isRetirement", { isRetirement: true }, ); }), - ).andWhere( - new Brackets((qb2) => { - qb2.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", { + ) + .andWhere( + new Brackets((qb) => { + qb.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", { keyword: `%${searchKeyword}%`, }); }), ); - if (posType) { - qb.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` }); - } - if (posLevel) { - qb.andWhere("posLevel.posLevelName LIKE :keyword2", { keyword2: `${posLevel}` }); - } - if (isProbation !== undefined && isProbation !== null) { - qb.andWhere("profile.isProbation = :isProbation", { isProbation }); - } - if (retireType) { - qb.andWhere("profile.leaveType = :retireType", { retireType }); - } - }; - - // Compute permission/node conditions เพียงครั้งเดียว - const conditions: { condition: string; params: Record }[] = []; - if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") { - conditions.push(await this.buildPermissionCondition(_data, isAll)); + if (posType) { + queryBuilder.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` }); } + + if (posLevel) { + queryBuilder.andWhere("posLevel.posLevelName LIKE :keyword2", { keyword2: `${posLevel}` }); + } + + if (isProbation) { + queryBuilder.andWhere(`profile.isProbation = ${isProbation}`); + } + + if (retireType) { + queryBuilder.andWhere("profile.leaveType = :retireType", { retireType }); + } + + // เพิ่ม permission และ node conditions if (node !== null && node !== undefined && nodeId) { - conditions.push(await this.buildNodeCondition(node, nodeId, isAll)); - } - const applyConditions = (qb: any) => { - for (const cond of conditions) { - qb.andWhere(cond.condition, cond.params); + // สร้าง query conditions แบบ parallel + const [nodeCondition, permissionCondition] = await Promise.all([ + this.buildNodeCondition(node, nodeId, isAll), + this.buildPermissionCondition(_data, isAll), + ]); + console.log("Permission Condition:", permissionCondition); + console.log("Node Condition:", nodeCondition); + + queryBuilder.andWhere(nodeCondition.condition, nodeCondition.params); + + if (_data.privilege !== "OWNER" && _data.privilege !== "PARENT") { + queryBuilder.andWhere(permissionCondition.condition, permissionCondition.params); } - }; - - // console.log(`[ProfileLeaveService] getLeaveOfficer conditions took ${Date.now() - t0}ms`); - - // สร้าง salary EXISTS filter (ใช้ซ้ำทั้ง step1, step2) - const applySalaryFilter = (qb: any) => { - if (conditions.length > 0) { - let existsCond = "profileSalary.positionName != :notRetire"; - const existsParams: Record = { notRetire: "เกษียณอายุราชการ" }; - for (const cond of conditions) { - existsCond += ` AND ${cond.condition}`; - Object.assign(existsParams, cond.params); - } - qb.andWhere( - `EXISTS (SELECT 1 FROM profileSalary WHERE profileId = profile.id AND ${existsCond} AND profileSalary.\`order\` = (SELECT MAX(ps.\`order\`) FROM profileSalary ps WHERE ps.profileId = profile.id AND ps.positionName != :notRetire2))`, - { ...existsParams, notRetire2: "เกษียณอายุราชการ" } - ); - } - }; - - // Step 1: Count query - const countQb = this.profileRepo - .createQueryBuilder("profile") - .leftJoinAndSelect("profile.posLevel", "posLevel") - .leftJoinAndSelect("profile.posType", "posType"); - baseWhere(countQb); - applySalaryFilter(countQb); - const total = await countQb.getCount(); - - // console.log(`[ProfileLeaveService] getLeaveOfficer count took ${Date.now() - t0}ms, total=${total}`); - - // Step 2: ดึงเฉพาะ profile IDs ที่ผ่านเงื่อนไข - const idQb = this.profileRepo - .createQueryBuilder("profile") - .select(["profile.id"]) - .leftJoin("profile.posLevel", "posLevel") - .leftJoin("profile.posType", "posType"); - baseWhere(idQb); - applySalaryFilter(idQb); - idQb.orderBy(sortBy, sort).skip((page - 1) * pageSize).take(pageSize); - const rawIds = await idQb.getRawMany(); - const profileIds = rawIds.map((r) => r.profile_id); - - // console.log(`[ProfileLeaveService] getLeaveOfficer ids took ${Date.now() - t0}ms, ids=${profileIds.length}`); - - if (profileIds.length === 0) { - return { data: [], total }; } - // Step 3: Load full data โดยไม่ JOIN salary - const records = await this.profileRepo.find({ - where: { id: In(profileIds) }, - relations: ["posLevel", "posType"], - order: { [sortBy.split(".")[1]]: sort } as any, - }); - // console.log(`[ProfileLeaveService] getLeaveOfficer step3 (load profiles) took ${Date.now() - t0}ms`); + // เพิ่ม sorting และ pagination + queryBuilder + .orderBy(sortBy, sort) + .skip((page - 1) * pageSize) + .take(pageSize); - // Step 4: Load salary เฉพาะ row ที่มี order สูงสุดต่อ profileId (INNER JOIN + GROUP BY) - const salaries = await this.profileSalaryRepo - .createQueryBuilder("ps") - .innerJoin( - (subQuery) => - subQuery - .select("ps2.profileId", "pid") - .addSelect("MAX(ps2.order)", "maxOrd") - .from(ProfileSalary, "ps2") - .where("ps2.profileId IN (:...profileIds)", { profileIds }) - .andWhere("ps2.positionName != :notRetire", { notRetire: "เกษียณอายุราชการ" }) - .groupBy("ps2.profileId"), - "latest", - "latest.pid = ps.profileId AND ps.order = latest.maxOrd" - ) - .getMany(); - // console.log(`[ProfileLeaveService] getLeaveOfficer step4 (load salaries) took ${Date.now() - t0}ms, salary rows=${salaries.length}`); + const [records, total] = await queryBuilder.getManyAndCount(); - // สร้าง map: profileId → salary ที่มี order สูงสุด - const salaryMap = new Map(); - for (const s of salaries) { - salaryMap.set(s.profileId, s); - } + // print query for debug + // console.log("SQL Query:", queryBuilder.getSql()); - // แปลงข้อมูลพร้อม salary - const data = records.map((record) => { - const salary = salaryMap.get(record.id); - if (salary) { - (record as any).profileSalary = [salary]; - } - return this.transformOfficerData(record); - }); + const data = await Promise.all( + records.map((record) => Promise.resolve(this.transformOfficerData(record))), + ); - // console.log(`[ProfileLeaveService] getLeaveOfficer total took ${Date.now() - t0}ms, total=${total}`); return { data, total }; } } diff --git a/src/services/rabbitmq.ts b/src/services/rabbitmq.ts index 3a989a5f..c00b8150 100644 --- a/src/services/rabbitmq.ts +++ b/src/services/rabbitmq.ts @@ -29,16 +29,6 @@ import { sendWebSocket } from "./webSocket"; import { PayloadSendNoti } from "../interfaces/utils"; import { PermissionProfile } from "../entities/PermissionProfile"; import { PosMasterHistory } from "../entities/PosMasterHistory"; -import { ExecuteOfficerProfileService } from "./ExecuteOfficerProfileService"; -import { ExecuteSalaryService } from "./ExecuteSalaryService"; -import { ExecuteSalaryCurrentService } from "./ExecuteSalaryCurrentService"; -import { ExecuteSalaryEmployeeCurrentService } from "./ExecuteSalaryEmployeeCurrentService"; -import { ExecuteSalaryLeaveService } from "./ExecuteSalaryLeaveService"; -import { ExecuteSalaryEmployeeLeaveService } from "./ExecuteSalaryEmployeeLeaveService"; -import { ExecuteSalaryLeaveDisciplineService } from "./ExecuteSalaryLeaveDisciplineService"; -import { ExecuteOrgCommandService } from "./ExecuteOrgCommandService"; -import { ExecuteSalaryProbationService } from "./ExecuteSalaryProbationService"; -import { ExecuteSalaryReportService } from "./ExecuteSalaryReportService"; const redis = require("redis"); const REDIS_HOST = process.env.REDIS_HOST; @@ -176,23 +166,6 @@ function createConsumer( //----> consumer ); } -/** - * สร้าง pseudo Express request สำหรับ service ที่ถูกเรียกจาก RabbitMQ (ไม่ใช่ HTTP) - * ต้องมี `.app.locals.logData.sequence` เพราะ addLogSequence (ใน CallAPI) อ่านค่านี้ - * และ `.headers.authorization` + `.user` สำหรับ audit/auth - */ -function buildPseudoReq(token: string, user: any) { - return { - headers: { authorization: token }, - user, - app: { - locals: { - logData: { sequence: [] as any[] }, - }, - }, - }; -} - async function handler(msg: amqp.ConsumeMessage): Promise { //----> condition before process consumer // const repo = AppDataSource.getRepository(Command); @@ -347,246 +320,13 @@ async function handler(msg: amqp.ConsumeMessage): Promise { 20, ); - // ───────────────────────────────────────────────────────────── - // Linear Flow - // รับ resultData จาก .NET แล้วเรียก Service ตรงๆ ตาม commandType (ไม่ผ่าน HTTP loopback) - // - ExecuteOfficerProfileService : C-PM-01, 02, 14 (บรรจุ/รับโอน) - // - ExecuteSalaryCurrentService : C-PM-03, 04, 05, 06, 07, 39, 47 (แต่งตั้ง-เลื่อน-ย้าย) - // - ExecuteSalaryEmployeeCurrentService : C-PM-22, 24 (ลูกจ้าง ปรับระดับชั้นงาน-ย้าย) - // - ExecuteSalaryService : C-PM-13, 15, 16 (ให้โอน/ให้ช่วยราชการ/ให้กลับเข้าราชการ) - // - ExecuteSalaryLeaveService : C-PM-08, 09, 17, 18, 41, 48 (ข้าราชการ leave/กลับเข้าราชการ) - // - ExecuteSalaryEmployeeLeaveService : C-PM-23, 42, 43 (ลูกจ้าง leave) - // - ExecuteSalaryLeaveDisciplineService : C-PM-19, 20, 25, 26, 27, 28, 29, 30, 31, 32 (คำสั่งวินัย) - // - ExecuteOrgCommandService : C-PM-21, 38, 40 (org-self — path ชี้กลับ org เอง - // เรียก Service ตรงๆ ไม่ผ่าน HTTP loopback เพราะ PostData(path+"/excecute") = ยิงเข้าตัว) - // - คำสั่งอื่น ยังใช้ Circular Flow เดิม - // ───────────────────────────────────────────────────────────── - const code = command.commandType?.code; - const isOfficerProfile = ["C-PM-01", "C-PM-02", "C-PM-14"].includes(code); - const isSalaryCurrent = ["C-PM-03", "C-PM-04", "C-PM-05", "C-PM-06", "C-PM-07", "C-PM-39", "C-PM-47"].includes(code); - const isSalaryEmployeeCurrent = ["C-PM-22", "C-PM-24"].includes(code); - const isSalary = ["C-PM-13", "C-PM-15", "C-PM-16"].includes(code); - const isSalaryLeave = ["C-PM-08", "C-PM-09", "C-PM-17", "C-PM-18", "C-PM-41", "C-PM-48"].includes(code); - const isSalaryEmployeeLeave = ["C-PM-23", "C-PM-42", "C-PM-43"].includes(code); - const isSalaryLeaveDiscipline = ["C-PM-19", "C-PM-20", "C-PM-25", "C-PM-26", "C-PM-27", "C-PM-28", - "C-PM-29", "C-PM-30", "C-PM-31", "C-PM-32", - ].includes(code); - // C-PM-21/38/40: path ชี้กลับ org เอง (ไม่ใช่ .NET) → ต้องเรียก Service ตรงๆ ไม่ผ่าน loopback - const isCommand21 = code === "C-PM-21"; - const isCommand38 = code === "C-PM-38"; - const isCommand40 = code === "C-PM-40"; - const isOrgSelfLinear = isCommand21 || isCommand38 || isCommand40; - // C-PM-10/11/12: ยิงไป probation service — เป็น branch แยก (ไม่ใช่ .NET linear flow) - // - C-PM-10: fire-only (probation update ในตัวเอง ไม่มี org-side action) - // - C-PM-11/12: probation return salary data → เรียก ExecuteSalaryProbationService ตรงๆ - const isProbation = ["C-PM-10", "C-PM-11", "C-PM-12"].includes(code); - // C-PM-33/34/35/45 (officer) + C-PM-36/37/46 (employee): ยิงไป salary service - // เป็น branch แยก — salary return salary data → เรียก ExecuteSalaryReportService ตรงๆ - const isSalaryServiceOfficer = ["C-PM-33", "C-PM-34", "C-PM-35", "C-PM-45"].includes(code); - const isSalaryServiceEmployee = ["C-PM-36", "C-PM-37", "C-PM-46"].includes(code); - const isSalaryService = isSalaryServiceOfficer || isSalaryServiceEmployee; - const isLinearFlow = - isOfficerProfile || - isSalaryCurrent || - isSalaryEmployeeCurrent || - isSalary || - isSalaryLeave || - isSalaryEmployeeLeave || - isSalaryLeaveDiscipline; - - // Org-self (C-PM-21/38/40): เรียก Service ตรงๆ (Linear Flow / ทำต่อ) ไม่ผ่าน HTTP loopback - // เพราะ path ของ command เหล่านี้ชี้กลับ org เอง → PostData(path + "/excecute") = ยิงเข้าตัว - if (isOrgSelfLinear) { - console.log(`[AMQ] Linear Flow org-self (${code}) — เรียก Service ตรงๆ (no loopback)`); - const pseudoReq = buildPseudoReq(token, user); - const ctx = { - user: { sub: user?.sub ?? "system", name: user?.name ?? "System" }, - req: pseudoReq, - }; - const flatRefIds = chunks.flat(); - if (isCommand21) { - await new ExecuteOrgCommandService().executeCommand21Employee(flatRefIds, ctx); - } else if (isCommand38) { - await new ExecuteOrgCommandService().executeCommand38Officer(flatRefIds, ctx); - } else if (isCommand40) { - await new ExecuteOrgCommandService().executeCommand40Officer(flatRefIds, ctx); - } - console.log(`[AMQ] Processed ${flatRefIds.length} items via ExecuteOrgCommandService (${code})`); - } else if (isProbation) { - // Probation Linear Flow (C-PM-10/11/12) - // - C-PM-10: fire-only — probation อัปเดต appoint ในตัวเอง ไม่มี org-side action - // - C-PM-11/12: fire → probation return salary data → route ไป ExecuteSalaryProbationService - // แทนการ callback เข้า org (Circular Flow เดิม) - console.log(`[AMQ] Probation Linear Flow (${code})`); - if (code === "C-PM-10") { - for (const chunk of chunks) { - await new CallAPI().PostData( - { headers: { authorization: token } }, - path + "/excecute", - { refIds: chunk }, - false, - ); - } - console.log(`[AMQ] C-PM-10 fire-only — no org-side action`); - } else { - let resultData: any[] = []; - for (const chunk of chunks) { - const res = await new CallAPI().PostData( - { headers: { authorization: token } }, - path + "/excecute", - { refIds: chunk }, - false, - ); - // รองรับทั้ง array และ { data: [...] } (contract ของ probation หลัง Linear Flow) - if (res && Array.isArray(res.data)) { - resultData.push(...res.data); - } else if (Array.isArray(res)) { - resultData.push(...res); - } - } - - if (resultData.length > 0) { - const pseudoReq = buildPseudoReq(token, user); - const ctx = { - user: { sub: user?.sub ?? "system", name: user?.name ?? "System" }, - req: pseudoReq, - }; - - if (code === "C-PM-11") { - await new ExecuteSalaryProbationService().executeProbationPass(resultData, ctx); - console.log( - `[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryProbationService (C-PM-11)`, - ); - } else if (code === "C-PM-12") { - await new ExecuteSalaryProbationService().executeProbationLeave(resultData, ctx); - console.log( - `[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryProbationService (C-PM-12)`, - ); - } - } - } - } else if (isSalaryService) { - // Salary Service Linear Flow (C-PM-33/34/35/45 officer, C-PM-36/37/46 employee) - // fire → salary service return salary data → route ไป ExecuteSalaryReportService - // แทนการ callback เข้า /org/profile(/-employee)/salary/update (Circular Flow เดิม) - console.log(`[AMQ] Salary Service Linear Flow (${code})`); - let resultData: any[] = []; - for (const chunk of chunks) { - const res = await new CallAPI().PostData( - { headers: { authorization: token } }, - path + "/excecute", - { refIds: chunk }, - false, - ); - // รองรับทั้ง array และ { data: [...] } (contract ของ salary service หลัง Linear Flow) - if (res && Array.isArray(res.data)) { - resultData.push(...res.data); - } else if (Array.isArray(res)) { - resultData.push(...res); - } - } - - if (resultData.length > 0) { - const pseudoReq = buildPseudoReq(token, user); - const ctx = { - user: { sub: user?.sub ?? "system", name: user?.name ?? "System" }, - req: pseudoReq, - }; - - if (isSalaryServiceOfficer) { - await new ExecuteSalaryReportService().executeOfficerSalaryUpdate(resultData, ctx); - console.log( - `[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryReportService (officer)`, - ); - } else { - await new ExecuteSalaryReportService().executeEmployeeSalaryUpdate(resultData, ctx); - console.log( - `[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryReportService (employee)`, - ); - } - } - } else if (isLinearFlow) { - console.log(`[AMQ] Linear Flow (${code})`); - const isCpm32 = code === "C-PM-32"; - let resultData: any[] = []; - let resultData1: any[] = []; //เฉพาะ C-PM-32 (ฝั่ง "การพิจารณาลงโทษ") - - for (const chunk of chunks) { - const res = await new CallAPI().PostData( - { headers: { authorization: token } }, - path + "/excecute", - { refIds: chunk }, - false, - ); - if (isCpm32 && res && !Array.isArray(res)) { - // C-PM-32: response เป็น object { data, data1 } → แยก 2 track - console.log( - `[AMQ] C-PM-32 split response — data: ${res.data?.length ?? 0}, data1: ${res.data1?.length ?? 0}`, - ); - if (Array.isArray(res.data)) resultData.push(...res.data); - if (Array.isArray(res.data1)) resultData1.push(...res.data1); - } else if (Array.isArray(res)) { - console.log(`[AMQ] Push result data (${res.length})`); - resultData.push(...res); - } - } - - console.log(`[AMQ] Received ${resultData.length} profiles from .NET (${code})`); - - // Route ไป service ที่ถูกต้องตาม commandType - if (resultData.length > 0 || resultData1.length > 0) { - // สร้าง pseudo-req สำหรับ setLogDataDiff/save({data: req}) - const pseudoReq = buildPseudoReq(token, user); - const ctx = { - user: { sub: user?.sub ?? "system", name: user?.name ?? "System" }, - req: pseudoReq, - }; - - if (isOfficerProfile) { - await new ExecuteOfficerProfileService().executeCreateOfficerProfile(resultData, ctx); - console.log(`[AMQ] Processed ${resultData.length} profiles via ExecuteOfficerProfileService`); - } else if (isSalaryCurrent) { - await new ExecuteSalaryCurrentService().executeSalaryCurrent(resultData, ctx); - console.log(`[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryCurrentService`); - } else if (isSalaryEmployeeCurrent) { - await new ExecuteSalaryEmployeeCurrentService().executeSalaryEmployeeCurrent(resultData, ctx); - console.log(`[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryEmployeeCurrentService`); - } else if (isSalary) { - await new ExecuteSalaryService().executeSalary(resultData, ctx); - console.log(`[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryService`); - } else if (isSalaryLeave) { - await new ExecuteSalaryLeaveService().executeSalaryLeave(resultData, ctx); - console.log(`[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryLeaveService`); - } else if (isSalaryEmployeeLeave) { - await new ExecuteSalaryEmployeeLeaveService().executeSalaryEmployeeLeave(resultData, ctx); - console.log(`[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryEmployeeLeaveService`); - } else if (isSalaryLeaveDiscipline) { - // C-PM-32 (คำสั่งยุติเรื่อง): response เป็น object { data, data1 } - // profileId เดียวกันอาจอยู่ในทั้ง 2 track → ต้องส่งให้ org แยก 2 ครั้ง ห้าม merge - if (resultData.length > 0) { - await new ExecuteSalaryLeaveDisciplineService().executeSalaryLeaveDiscipline(resultData, ctx); - console.log( - `[AMQ] Processed ${resultData.length} profiles via ExecuteSalaryLeaveDisciplineService`, - ); - } - if (isCpm32 && resultData1.length > 0) { - await new ExecuteSalaryLeaveDisciplineService().executeSalaryLeaveDiscipline(resultData1, ctx); - console.log( - `[AMQ] Processed resultData1: ${resultData1.length} profiles via ExecuteSalaryLeaveDisciplineService`, - ); - } - } - } - } else { - console.log(`[AMQ] Circular Flow (${code})`); - for (const chunk of chunks) { - await new CallAPI().PostData( - { headers: { authorization: token } }, - path + "/excecute", - { refIds: chunk }, - false, - ); - } + for (const chunk of chunks) { + await new CallAPI().PostData( + { headers: { authorization: token } }, + path + "/excecute", + { refIds: chunk }, + false, + ); } Object.assign(command, { status, lastUpdateUserId, lastUpdateFullName, lastUpdatedAt }); @@ -752,14 +492,14 @@ async function handler_command_noti(msg: amqp.ConsumeMessage): Promise let profilesSend = command && command.commandSends.length > 0 ? command.commandSends - .filter((x: any) => x.profileId != null) - .map((x: any) => ({ - receiverUserId: x.profileId, - notiLink: "", - isSendMail: x.commandSendCCs.map((x: any) => x.name == "EMAIL").length > 0, - isSendInbox: x.commandSendCCs.map((x: any) => x.name == "INBOX").length > 0, - isSendNotification: true, - })) + .filter((x: any) => x.profileId != null) + .map((x: any) => ({ + receiverUserId: x.profileId, + notiLink: "", + isSendMail: x.commandSendCCs.map((x: any) => x.name == "EMAIL").length > 0, + isSendInbox: x.commandSendCCs.map((x: any) => x.name == "INBOX").length > 0, + isSendNotification: true, + })) : []; const payloadStr = await PayloadSendNoti(command.id); const profilesSendRequest = new CallAPI() @@ -955,19 +695,7 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { where: { orgRevisionId: orgRevisionPublish.id, }, - select: [ - "id", - "current_holderId", - "ancestorDNA", - "posMasterNo", - "posMasterNoPrefix", - "posMasterNoSuffix", - "orgRootId", - "orgChild1Id", - "orgChild2Id", - "orgChild3Id", - "orgChild4Id", - ], + select: ["id", "current_holderId", "ancestorDNA"], }); // Task #2160 ดึง posMasterAssign ของ revision เดิม @@ -1054,9 +782,8 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { // 2. Batch load profiles ทั้งหมดในครั้งเดียว (แก้ปัญหา N+1 Query) const profilesMap = new Map(); if (profileIds.length > 0) { - const profiles = await repoProfile.find({ - where: { id: In(profileIds) }, - relations: ["posType", "posLevel"], + const profiles = await repoProfile.findBy({ + id: In(profileIds), }); profiles.forEach((p) => profilesMap.set(p.id, p)); } @@ -1133,21 +860,7 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { const newHolderId = item?.next_holderId; const isHolderChanged = oldHolderId !== newHolderId; - // เช็คว่า holder เดิม แต่ตำแหน่งเปลี่ยน - const isSameHolder = oldHolderId === newHolderId && oldHolderId != null && newHolderId != null; - const isPositionChanged = - isSameHolder && - oldPm && - (oldPm.posMasterNo !== item.posMasterNo || - oldPm.posMasterNoPrefix !== item.posMasterNoPrefix || - oldPm.posMasterNoSuffix !== item.posMasterNoSuffix || - oldPm.orgRootId !== item.orgRoot?.id || - oldPm.orgChild1Id !== item.orgChild1?.id || - oldPm.orgChild2Id !== item.orgChild2?.id || - oldPm.orgChild3Id !== item.orgChild3?.id || - oldPm.orgChild4Id !== item.orgChild4?.id); - - if (isHolderChanged || isPositionChanged) { + if (isHolderChanged) { const nextHolderProfile = item.next_holderId != null && item.next_holderId !== "" ? profilesMap.get(item.next_holderId) @@ -1165,18 +878,6 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { item.orgRoot?.orgRootShortName, ].find((name) => typeof name === "string" && name.trim().length > 0) ?? _null; - // ถ้าเป็นตำแหน่งนั่งทับ (isSit = true) และมีคนครอง ใช้ตำแหน่งจาก profile แทน - let positionName = selectedPosition?.positionName ?? _null; - let posTypeName = selectedPosition?.posType?.posTypeName ?? _null; - let posLevelName = selectedPosition?.posLevel?.posLevelName ?? _null; - let posExecutiveName = selectedPosition?.posExecutive?.posExecutiveName ?? _null; - if (item.isSit && nextHolderProfile) { - positionName = nextHolderProfile.position ?? _null; - posTypeName = nextHolderProfile.posType?.posTypeName ?? _null; - posLevelName = nextHolderProfile.posLevel?.posLevelName ?? _null; - posExecutiveName = nextHolderProfile.posExecutive ?? _null; - } - historyRowsToSave.push({ ancestorDNA: item.ancestorDNA ? item.ancestorDNA : _null, prefix: nextHolderProfile?.prefix || _null, @@ -1186,11 +887,11 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { posMasterNoPrefix: item.posMasterNoPrefix ?? _null, posMasterNo: item.posMasterNo ?? _null, posMasterNoSuffix: item.posMasterNoSuffix ?? _null, - position: positionName, - posType: posTypeName, - posLevel: posLevelName, - posExecutive: posExecutiveName, - profileId: nextHolderProfile?.id || _null, + position: selectedPosition?.positionName ?? _null, + posType: selectedPosition?.posType?.posTypeName ?? _null, + posLevel: selectedPosition?.posLevel?.posLevelName ?? _null, + posExecutive: selectedPosition?.posExecutive?.posExecutiveName ?? _null, + profileId: _null, rootDnaId: item.orgRoot?.ancestorDNA || _null, child1DnaId: item.orgChild1?.ancestorDNA || _null, child2DnaId: item.orgChild2?.ancestorDNA || _null, @@ -1997,7 +1698,6 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { } async function clearMenuAndRoleCache(): Promise { - console.log("[AMQ] clearMenuAndRoleCache: Starting..."); const redisClient = redis.createClient({ host: REDIS_HOST, port: REDIS_PORT, @@ -2007,28 +1707,17 @@ async function clearMenuAndRoleCache(): Promise { const delAsync = promisify(redisClient.del).bind(redisClient); try { - // Clear menu and role cache (patterns that affect menu display) - const menuRolePatterns = ["menu_*", "role_*"]; - - for (const pattern of menuRolePatterns) { - console.log(`[AMQ] Checking pattern: ${pattern}`); - const keys = await keysAsync(pattern); - console.log(`[AMQ] Found ${keys.length} keys for pattern: ${pattern}`); - if (keys.length > 0) { - // Delete in chunks of 1000 to avoid argument limit - const chunkSize = 1000; - for (let i = 0; i < keys.length; i += chunkSize) { - const chunk = keys.slice(i, i + chunkSize); - await delAsync(...chunk); - } - console.log(`[AMQ] Cleared ${keys.length} cache keys for pattern: ${pattern}`); - } else { - console.log(`[AMQ] No keys found for pattern: ${pattern}`); - } + const menuKeys = await keysAsync("menu_*"); + if (menuKeys.length > 0) { + await delAsync(...menuKeys); + console.log(`[AMQ] Cleared ${menuKeys.length} menu cache keys`); + } + + const roleKeys = await keysAsync("role_*"); + if (roleKeys.length > 0) { + await delAsync(...roleKeys); + console.log(`[AMQ] Cleared ${roleKeys.length} role cache keys`); } - console.log("[AMQ] clearMenuAndRoleCache: Completed successfully"); - } catch (error) { - console.error("[AMQ] clearMenuAndRoleCache ERROR:", error); } finally { redisClient.quit(); } diff --git a/src/services/webSocket.ts b/src/services/webSocket.ts index 7d464655..36359cd8 100644 --- a/src/services/webSocket.ts +++ b/src/services/webSocket.ts @@ -22,7 +22,7 @@ export function initWebSocket() { }); io.on("connection", (ws) => { - // console.log("✅ Client connected to WebSocket"); + console.log("✅ Client connected to WebSocket"); ws.on("close", () => { console.log("❌ Client disconnected"); @@ -46,7 +46,7 @@ export async function sendWebSocket( ) { if (!io) initWebSocket(); // console.log( `🔔 :`,data.message); - + for (let [id, session] of io.of("/").sockets) { const user: { sub: string; diff --git a/src/utils/org-formatting.ts b/src/utils/org-formatting.ts index eb4b7a9d..fd61f33b 100644 --- a/src/utils/org-formatting.ts +++ b/src/utils/org-formatting.ts @@ -101,7 +101,7 @@ export function getOrgFullName(posMaster: PosMaster): string { } /** - * สร้างเลขที่ตำแหน่ง เช่น "กทม. กบ. 1234 ช" + * สร้างเลขที่ตำแหน่ง เช่น "กทม. กบ.1234ช" */ export function getPosMasterNo(posMaster: PosMaster): string { const orgShortName = getOrgShortName(posMaster); @@ -110,5 +110,5 @@ export function getPosMasterNo(posMaster: PosMaster): string { posMaster.posMasterNo, posMaster.posMasterNoSuffix, ].filter((part) => part !== null && part !== undefined); - return `${orgShortName} ${parts.join(' ')}`; + return `${orgShortName} ${parts.join('')}`; } diff --git a/src/utils/tenure.ts b/src/utils/tenure.ts index 1c97dff1..dbdedbb3 100644 --- a/src/utils/tenure.ts +++ b/src/utils/tenure.ts @@ -1,37 +1,18 @@ /** - * Normalize a duration sum using calendar arithmetic - * Converts excess days to months using average month length (30.4375 days) - * and excess months to years. Matches the logic used in stored procedures. - * - * @param years Total years from sum - * @param months Total months from sum - * @param days Total days from sum - * @returns Normalized { years, months, days } + * คำนวณอายุงานจากจำนวนวันรวม + * ใช้สูตรเดียวกับ Stored Procedure GetProfileSalaryPosition + * @param totalDays จำนวนวันรวม + * @returns { year, month, day } ปี เดือน วัน */ -export function normalizeDurationSumSimple( - years: number, - months: number, - days: number, -): { years: number; months: number; days: number } { - const DAYS_PER_MONTH = 30.4375; // Average days per month in Gregorian calendar +export function calculateTenure(totalDays: number) { + // Match stored procedure formula: + // days_diff / 365.2524 AS Years + // (days_diff / 30.4375) % 12 AS Months + // days_diff % 30.4375 AS Days - let totalMonths = months; - let totalDays = days; + const year = Math.floor(totalDays / 365.2524); + const month = Math.floor((totalDays / 30.4375) % 12); + const day = Math.floor(totalDays % 30.4375); - // Convert excess days to months - if (totalDays >= DAYS_PER_MONTH) { - const additionalMonths = Math.floor(totalDays / DAYS_PER_MONTH); - totalMonths += additionalMonths; - totalDays = totalDays - additionalMonths * DAYS_PER_MONTH; - } - - // Convert excess months to years - let totalYears = years; - if (totalMonths >= 12) { - const additionalYears = Math.floor(totalMonths / 12); - totalYears += additionalYears; - totalMonths = totalMonths % 12; - } - - return { years: totalYears, months: Math.floor(totalMonths), days: Math.floor(totalDays) }; + return { year, month, day }; } diff --git a/tsoa.json b/tsoa.json index e346e3b1..492907b8 100644 --- a/tsoa.json +++ b/tsoa.json @@ -29,12 +29,6 @@ "name": "X-API-Key", "description": "API KEY สำหรับ Web Service", "in": "header" - }, - "internalAuth": { - "type": "apiKey", - "name": "api-key", - "description": "API KEY สำหรับ Internal Service (.NET, HRMS)", - "in": "header" } }, "tags": [