From 28b5408d5be725f45fa1faae25c3cd9fcdc11ab8 Mon Sep 17 00:00:00 2001 From: adisak Date: Mon, 20 Apr 2026 08:05:16 +0700 Subject: [PATCH 1/4] #2427 and migration --- src/controllers/CommandController.ts | 23 +++++++-- src/controllers/PositionController.ts | 10 +++- src/entities/Profile.ts | 48 +++++++++++++++++++ ...08026834-add_position_fields_to_profile.ts | 37 ++++++++++++++ src/services/rabbitmq.ts | 7 +++ src/utils/org-formatting.ts | 39 +++++++++++++++ 6 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 src/migration/1776308026834-add_position_fields_to_profile.ts diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index da0ed3fe..e505aa3a 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -48,6 +48,7 @@ import { import { Position } from "../entities/Position"; import { PosMaster } from "../entities/PosMaster"; import { EmployeePosition } from "../entities/EmployeePosition"; +import { getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; import { EmployeePosMaster } from "../entities/EmployeePosMaster"; import { ProfileDiscipline } from "../entities/ProfileDiscipline"; import { ProfileDisciplineHistory } from "../entities/ProfileDisciplineHistory"; @@ -3660,6 +3661,7 @@ export class CommandController extends Controller { 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, "ไม่พบข้อมูลตำแหน่งนี้"); @@ -3715,6 +3717,7 @@ export class CommandController extends Controller { id: item.positionId, posMasterId: item.posmasterId, }, + relations: ["posExecutive"], }); // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ if (positionNew != null) { @@ -3723,6 +3726,12 @@ export class CommandController extends Controller { 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.posMasterNo = getPosMasterNo(posMaster); + profile.org = getOrgFullName(posMaster); } profile.amount = item.amount ?? null; profile.amountSpecial = item.amountSpecial ?? null; @@ -6876,7 +6885,7 @@ export class CommandController extends Controller { where: { id: item.bodyPosition.posmasterId, }, - relations: { orgRevision: true } + relations: { orgRevision: true, orgRoot: true, orgChild1: true, orgChild2: true, orgChild3: true, orgChild4: true } }); // เช็คว่า posMaster ที่หามาอยู่ในโครงสร้างปัจจุบันหรือไม่ @@ -6893,9 +6902,8 @@ export class CommandController extends Controller { orgRevisionIsDraft: false } }, - relations: { orgRevision: true } - }); - } + relations: { orgRevision: true, orgRoot: true, orgChild1: true, orgChild2: true, orgChild3: true, orgChild4: true } + }); } if (posMaster == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); @@ -6983,6 +6991,7 @@ export class CommandController extends Controller { id: item.bodyPosition.positionId, posMasterId: posMaster.id, }, + relations: ["posExecutive"], }); } @@ -6993,6 +7002,12 @@ export class CommandController extends Controller { 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.posMasterNo = getPosMasterNo(posMaster); + profile.org = getOrgFullName(posMaster); // profile.dateStart = new Date(); await this.profileRepository.save(profile, { data: req }); setLogDataDiff(req, { before, after: profile }); diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 75d6c2a0..23806e6c 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -39,6 +39,7 @@ import { AuthRole } from "../entities/AuthRole"; import { RequestWithUser } from "../middlewares/user"; import permission from "../interfaces/permission"; import { resolveNodeLevel, setLogDataDiff } from "../interfaces/utils"; +import { getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; import { PosMasterAssign } from "../entities/PosMasterAssign"; import { Assign } from "../entities/Assign"; import { ProfileEmployee } from "../entities/ProfileEmployee"; @@ -3793,7 +3794,7 @@ export class PositionController extends Controller { await new permission().PermissionUpdate(request, "SYS_ORG"); const dataMaster = await this.posMasterRepository.findOne({ where: { id: requestBody.posMaster }, - relations: ["positions"], + relations: ["positions", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"], }); if (!dataMaster) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); @@ -3825,6 +3826,7 @@ export class PositionController extends Controller { if (_profile) { let _position = await this.positionRepository.findOne({ where: { id: requestBody.position, posMasterId: requestBody.posMaster }, + relations: ["posExecutive"], }); if (_position) { // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ @@ -3832,6 +3834,12 @@ export class PositionController extends Controller { _profile.position = _position.positionName; _profile.posTypeId = _position.posTypeId; _profile.posLevelId = _position.posLevelId; + _profile.positionField = _position.positionField ?? undefined; + _profile.posExecutive = _position.posExecutive?.posExecutiveName ?? undefined; + _profile.positionArea = _position.positionArea ?? undefined; + _profile.positionExecutiveField = _position.positionExecutiveField ?? undefined; + _profile.posMasterNo = getPosMasterNo(dataMaster); + _profile.org = getOrgFullName(dataMaster); await this.profileRepository.save(_profile); setLogDataDiff(request, { before, after: _profile }); } diff --git a/src/entities/Profile.ts b/src/entities/Profile.ts index 72a1d505..a875a969 100644 --- a/src/entities/Profile.ts +++ b/src/entities/Profile.ts @@ -140,6 +140,54 @@ export class Profile extends EntityBase { }) posTypeId: string | null; + @Column({ + nullable: true, + comment: "สายงาน", + length: 45, + default: null, + }) + positionField: string; + + @Column({ + nullable: true, + comment: "ตำแหน่งทางการบริหาร", + length: 255, + default: null, + }) + posExecutive?: string; + + @Column({ + nullable: true, + comment: "ด้าน/สาขา", + length: 255, + default: null, + }) + positionArea?: string; + + @Column({ + nullable: true, + comment: "ด้านทางการบริหาร", + length: 255, + default: null, + }) + positionExecutiveField?: string; + + @Column({ + nullable: true, + comment: "เลขที่ตำแหน่ง", + length: 255, + default: null, + }) + posMasterNo?: string; + + @Column({ + nullable: true, + comment: "สังกัด", + type: "text", + default: null, + }) + org?: string; + @Column({ nullable: true, length: 255, diff --git a/src/migration/1776308026834-add_position_fields_to_profile.ts b/src/migration/1776308026834-add_position_fields_to_profile.ts new file mode 100644 index 00000000..9b214460 --- /dev/null +++ b/src/migration/1776308026834-add_position_fields_to_profile.ts @@ -0,0 +1,37 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddPositionFieldsToProfile1776308026834 implements MigrationInterface { + name = 'AddPositionFieldsToProfile1776308026834' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`profile\` ADD \`positionField\` varchar(45) NULL COMMENT 'สายงาน'`); + await queryRunner.query(`ALTER TABLE \`profile\` ADD \`posExecutive\` varchar(255) NULL COMMENT 'ตำแหน่งทางการบริหาร'`); + await queryRunner.query(`ALTER TABLE \`profile\` ADD \`positionArea\` varchar(255) NULL COMMENT 'ด้าน/สาขา'`); + await queryRunner.query(`ALTER TABLE \`profile\` ADD \`positionExecutiveField\` varchar(255) NULL COMMENT 'ด้านทางการบริหาร'`); + await queryRunner.query(`ALTER TABLE \`profile\` ADD \`posMasterNo\` varchar(255) NULL COMMENT 'เลขที่ตำแหน่ง'`); + await queryRunner.query(`ALTER TABLE \`profile\` ADD \`org\` text NULL COMMENT 'สังกัด'`); + + await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`positionField\` varchar(45) NULL COMMENT 'สายงาน'`); + await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`posExecutive\` varchar(255) NULL COMMENT 'ตำแหน่งทางการบริหาร'`); + await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`positionArea\` varchar(255) NULL COMMENT 'ด้าน/สาขา'`); + await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`positionExecutiveField\` varchar(255) NULL COMMENT 'ด้านทางการบริหาร'`); + await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`posMasterNo\` varchar(255) NULL COMMENT 'เลขที่ตำแหน่ง'`); + await queryRunner.query(`ALTER TABLE \`profileHistory\` ADD \`org\` text NULL COMMENT 'สังกัด'`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`org\``); + await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`posMasterNo\``); + await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`positionExecutiveField\``); + await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`positionArea\``); + await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`posExecutive\``); + await queryRunner.query(`ALTER TABLE \`profileHistory\` DROP COLUMN \`positionField\``); + + await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`org\``); + await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`posMasterNo\``); + await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`positionExecutiveField\``); + await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`positionArea\``); + await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`posExecutive\``); + await queryRunner.query(`ALTER TABLE \`profile\` DROP COLUMN \`positionField\``); + } +} diff --git a/src/services/rabbitmq.ts b/src/services/rabbitmq.ts index a8011900..5341d03c 100644 --- a/src/services/rabbitmq.ts +++ b/src/services/rabbitmq.ts @@ -3,6 +3,7 @@ import { AppDataSource } from "../database/data-source"; import { Command } from "../entities/Command"; import { chunkArray, commandTypePath } from "../interfaces/utils"; import CallAPI from "../interfaces/call-api"; +import { getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; import HttpError from "../interfaces/http-error"; import HttpStatusCode from "../interfaces/http-status"; import { PosMaster } from "../entities/PosMaster"; @@ -668,6 +669,12 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { profile.posLevelId = position?.posLevelId ?? _null; profile.posTypeId = position?.posTypeId ?? _null; profile.position = position?.positionName ?? _null; + profile.positionField = position?.positionField ?? _null; + profile.posExecutive = position?.posExecutive?.posExecutiveName ?? _null; + profile.positionArea = position?.positionArea ?? _null; + profile.positionExecutiveField = position?.positionExecutiveField ?? _null; + profile.posMasterNo = getPosMasterNo(item) ?? _null; + profile.org = getOrgFullName(item) ?? _null; await repoProfile.save(profile); } } diff --git a/src/utils/org-formatting.ts b/src/utils/org-formatting.ts index 701fb478..8b460a2f 100644 --- a/src/utils/org-formatting.ts +++ b/src/utils/org-formatting.ts @@ -68,3 +68,42 @@ export function filterPosMasters( ): PosMaster[] { return posMasters.filter((x) => x[childLevelIdKey] == null && x.isDirector === true); } + +/** + * สร้าง orgShortName จาก posMaster (ต้อง load org relations มาก่อน) + */ +export function getOrgShortName(posMaster: PosMaster): string { + if (posMaster.orgChild1Id === null) { + return posMaster.orgRoot?.orgRootShortName ?? ""; + } else if (posMaster.orgChild2Id === null) { + return posMaster.orgChild1?.orgChild1ShortName ?? ""; + } else if (posMaster.orgChild3Id === null) { + return posMaster.orgChild2?.orgChild2ShortName ?? ""; + } else if (posMaster.orgChild4Id === null) { + return posMaster.orgChild3?.orgChild3ShortName ?? ""; + } else { + return posMaster.orgChild4?.orgChild4ShortName ?? ""; + } +} + +/** + * สร้างชื่อสังกัดเต็ม จาก posMaster (join ด้วย \n) + */ +export function getOrgFullName(posMaster: PosMaster): string { + const parts = [ + posMaster.orgChild4?.orgChild4Name, + posMaster.orgChild3?.orgChild3Name, + posMaster.orgChild2?.orgChild2Name, + posMaster.orgChild1?.orgChild1Name, + posMaster.orgRoot?.orgRootName, + ]; + return parts.filter((part) => part !== undefined && part !== null).join("\n"); +} + +/** + * สร้างเลขที่ตำแหน่ง เช่น "กทม. 1234" + */ +export function getPosMasterNo(posMaster: PosMaster): string { + const orgShortName = getOrgShortName(posMaster); + return `${orgShortName} ${posMaster.posMasterNo}`; +} From f1c8ecf6993269d008fd537b11589f916e2f562d Mon Sep 17 00:00:00 2001 From: Adisak Date: Mon, 20 Apr 2026 16:01:38 +0700 Subject: [PATCH 2/4] insert position to profile --- src/controllers/CommandController.ts | 14 ++++++---- src/controllers/PositionController.ts | 9 +++--- src/services/rabbitmq.ts | 40 +++++++++++++++------------ 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index e505aa3a..e97abce5 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -3722,6 +3722,9 @@ export class CommandController extends Controller { // ถ้าไม่ใช่ตำแหน่งนั่งทับ (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; @@ -3730,8 +3733,6 @@ export class CommandController extends Controller { profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; profile.positionArea = positionNew.positionArea ?? null; profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; - profile.posMasterNo = getPosMasterNo(posMaster); - profile.org = getOrgFullName(posMaster); } profile.amount = item.amount ?? null; profile.amountSpecial = item.amountSpecial ?? null; @@ -6998,6 +6999,9 @@ export class CommandController extends Controller { // ถ้าไม่ใช่ตำแหน่งนั่งทับ (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; @@ -7006,12 +7010,10 @@ export class CommandController extends Controller { profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; profile.positionArea = positionNew.positionArea ?? null; profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; - profile.posMasterNo = getPosMasterNo(posMaster); - profile.org = getOrgFullName(posMaster); // profile.dateStart = new Date(); - await this.profileRepository.save(profile, { data: req }); - setLogDataDiff(req, { before, after: profile }); } + await this.profileRepository.save(profile, { data: req }); + setLogDataDiff(req, { before, after: profile }); await this.positionRepository.save(positionNew, { data: req }); } // await CreatePosMasterHistoryOfficer(posMaster.id, req); diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index 23806e6c..a1378892 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -3829,6 +3829,9 @@ export class PositionController extends Controller { relations: ["posExecutive"], }); if (_position) { + // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit + _profile.posMasterNo = getPosMasterNo(dataMaster); + _profile.org = getOrgFullName(dataMaster); // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ if(!dataMaster.isSit){ _profile.position = _position.positionName; @@ -3838,11 +3841,9 @@ export class PositionController extends Controller { _profile.posExecutive = _position.posExecutive?.posExecutiveName ?? undefined; _profile.positionArea = _position.positionArea ?? undefined; _profile.positionExecutiveField = _position.positionExecutiveField ?? undefined; - _profile.posMasterNo = getPosMasterNo(dataMaster); - _profile.org = getOrgFullName(dataMaster); - await this.profileRepository.save(_profile); - setLogDataDiff(request, { before, after: _profile }); } + await this.profileRepository.save(_profile); + setLogDataDiff(request, { before, after: _profile }); } } dataMaster.current_holderId = requestBody.profileId; diff --git a/src/services/rabbitmq.ts b/src/services/rabbitmq.ts index 5341d03c..6ba40258 100644 --- a/src/services/rabbitmq.ts +++ b/src/services/rabbitmq.ts @@ -652,29 +652,33 @@ async function handler_org(msg: amqp.ConsumeMessage): Promise { await posMasterAssignRepository.save(newAssigns); } - // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ - if (item.next_holderId != null && !item.isSit) { + // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit + if (item.next_holderId != null) { const profile = await repoProfile.findOne({ where: { id: item.next_holderId == null ? "" : item.next_holderId }, }); - if (profile != null && item.positions.length > 0) { - let position = await item.positions.find((x) => x.positionIsSelected == true); - if (position == null) { - position = await item.positions.find((x) => x.posLevelId == profile?.posLevelId); - if (position == null) { - position = await item.positions.sort((a, b) => a.orderNo - b.orderNo)[0]; - } - } - - profile.posLevelId = position?.posLevelId ?? _null; - profile.posTypeId = position?.posTypeId ?? _null; - profile.position = position?.positionName ?? _null; - profile.positionField = position?.positionField ?? _null; - profile.posExecutive = position?.posExecutive?.posExecutiveName ?? _null; - profile.positionArea = position?.positionArea ?? _null; - profile.positionExecutiveField = position?.positionExecutiveField ?? _null; + if (profile != null) { profile.posMasterNo = getPosMasterNo(item) ?? _null; profile.org = getOrgFullName(item) ?? _null; + + // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ + if (!item.isSit && item.positions.length > 0) { + let position = await item.positions.find((x) => x.positionIsSelected == true); + if (position == null) { + position = await item.positions.find((x) => x.posLevelId == profile?.posLevelId); + if (position == null) { + position = await item.positions.sort((a, b) => a.orderNo - b.orderNo)[0]; + } + } + + profile.posLevelId = position?.posLevelId ?? _null; + profile.posTypeId = position?.posTypeId ?? _null; + profile.position = position?.positionName ?? _null; + profile.positionField = position?.positionField ?? _null; + profile.posExecutive = position?.posExecutive?.posExecutiveName ?? _null; + profile.positionArea = position?.positionArea ?? _null; + profile.positionExecutiveField = position?.positionExecutiveField ?? _null; + } await repoProfile.save(profile); } } From 5e52206987ff9a1b50b7a91708bb1853164daf61 Mon Sep 17 00:00:00 2001 From: Adisak Date: Mon, 20 Apr 2026 17:23:15 +0700 Subject: [PATCH 3/4] update --- src/controllers/OrganizationController.ts | 24 +++++++++++++++++------ src/controllers/PositionController.ts | 17 +++++++++++++++- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 39752b7e..ee8f3413 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -66,7 +66,7 @@ import { import { orgStructureCache } from "../utils/OrgStructureCache"; import { OrgIdMapping, AllOrgMappings, SavePosMasterHistory } from "../interfaces/OrgMapping"; import { OrgPermissionData, NodeLevel } from "../interfaces/OrgTypes"; -import { formatPosMaster, generateLabelName, filterPosMasters } from "../utils/org-formatting"; +import { formatPosMaster, generateLabelName, filterPosMasters, getPosMasterNo, getOrgFullName } from "../utils/org-formatting"; @Route("api/v1/org") @Tags("Organization") @@ -8933,13 +8933,25 @@ export class OrganizationController extends Controller { const draftPosMaster = draftPosMasterMap.get(draftPosMasterId) as any; // Collect profile update for the selected position + // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit + if (nextHolderId != null && draftPos.positionIsSelected) { + const _null: any = null; + profileUpdates.set(nextHolderId, { + posMasterNo: draftPosMaster ? (getPosMasterNo(draftPosMaster as PosMaster) ?? _null) : _null, + org: draftPosMaster ? (getOrgFullName(draftPosMaster as PosMaster) ?? _null) : _null, + }); + } // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ if (nextHolderId != null && draftPos.positionIsSelected && !draftPosMaster?.isSit) { - profileUpdates.set(nextHolderId, { - position: draftPos.positionName, - posTypeId: draftPos.posTypeId, - posLevelId: draftPos.posLevelId, - }); + 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 = diff --git a/src/controllers/PositionController.ts b/src/controllers/PositionController.ts index a1378892..7b1e1594 100644 --- a/src/controllers/PositionController.ts +++ b/src/controllers/PositionController.ts @@ -1257,7 +1257,7 @@ export class PositionController extends Controller { ) { await new permission().PermissionUpdate(request, "SYS_ORG"); const posMaster = await this.posMasterRepository.findOne({ - relations: ["positions", "orgRevision"], + relations: ["positions", "orgRevision", "orgRoot", "orgChild1", "orgChild2", "orgChild3", "orgChild4"], where: { id: id }, }); if (!posMaster) { @@ -1452,6 +1452,17 @@ export class PositionController extends Controller { }), ); + // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit + if (posMaster.orgRevision?.orgRevisionIsCurrent == true && posMaster.current_holderId) { + const _profile = await this.profileRepository.findOne({ + where: { id: posMaster.current_holderId }, + }); + if (_profile) { + _profile.posMasterNo = getPosMasterNo(posMaster); + _profile.org = getOrgFullName(posMaster); + await this.profileRepository.save(_profile); + } + } // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ if (posMaster.orgRevision?.orgRevisionIsCurrent == true && !posMaster.isSit) { const _position = requestBody.positions.find((p) => p.positionIsSelected == true); @@ -1464,6 +1475,10 @@ export class PositionController extends Controller { _profile.position = _position.posDictName ?? _null; _profile.posTypeId = _position.posTypeId; _profile.posLevelId = _position.posLevelId; + _profile.positionField = _position.posDictField ?? _null; + _profile.posExecutive = _position.posExecutiveId ?? _null; + _profile.positionArea = _position.posDictArea ?? _null; + _profile.positionExecutiveField = _position.posDictExecutiveField ?? _null; await this.profileRepository.save(_profile); } } From 7e3982a96d2b639b58bb6e6d01ea49671f02b270 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Mon, 20 Apr 2026 18:20:20 +0700 Subject: [PATCH 4/4] =?UTF-8?q?fixed=20calculate=20tenure=20(=E0=B8=AA?= =?UTF-8?q?=E0=B8=B9=E0=B8=95=E0=B8=A3=E0=B8=84=E0=B8=B3=E0=B8=99=E0=B8=A7?= =?UTF-8?q?=E0=B8=99=E0=B8=AD=E0=B8=B2=E0=B8=A2=E0=B8=B8=E0=B8=A3=E0=B8=B2?= =?UTF-8?q?=E0=B8=8A=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=88=E0=B8=B2=E0=B8=81?= =?UTF-8?q?=20diff=20date)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/ProfileSalaryController.ts | 90 +++++++++---------- .../ProfileSalaryEmployeeController.ts | 29 +++--- src/utils/tenure.ts | 23 +++++ 3 files changed, 85 insertions(+), 57 deletions(-) create mode 100644 src/utils/tenure.ts diff --git a/src/controllers/ProfileSalaryController.ts b/src/controllers/ProfileSalaryController.ts index c8193750..8cc9d376 100644 --- a/src/controllers/ProfileSalaryController.ts +++ b/src/controllers/ProfileSalaryController.ts @@ -23,6 +23,7 @@ import { ProfileEmployee } from "../entities/ProfileEmployee"; import { In, IsNull, LessThan, MoreThan, Not } from "typeorm"; import permission from "../interfaces/permission"; import { setLogDataDiff } from "../interfaces/utils"; +import { calculateTenure } from "../utils/tenure"; import { TenurePositionOfficer } from "../entities/TenurePositionOfficer"; import { TenureLevelOfficer } from "../entities/TenureLevelOfficer"; import { TenurePositionEmployee } from "../entities/TenurePositionEmployee"; @@ -92,16 +93,14 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionName: null }, ); + const { year, month, day } = calculateTenure(calDayDiff.days_diff); const mapData: any = { profileId: x.id, positionName: calDayDiff.positionName, days_diff: calDayDiff.days_diff, - // Years: (calDayDiff.days_diff / 365.2524).toFixed(4), - // Months: ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), - // Days: (calDayDiff.days_diff % 30.4375).toFixed(4), - Years: Math.floor(calDayDiff.days_diff / 365.2524), - Months: Math.floor((calDayDiff.days_diff / 30.4375) % 12), - Days: Math.floor(calDayDiff.days_diff % 30.4375), + Years: year, + Months: month, + Days: day, }; // data.push(_mapData); await this.positionOfficerRepo.save(mapData); @@ -143,16 +142,14 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionName: null }, ); + const { year, month, day } = calculateTenure(calDayDiff.days_diff); const mapData: any = { profileEmployeeId: x.id, positionName: calDayDiff.positionName, days_diff: calDayDiff.days_diff, - // Years: (calDayDiff.days_diff / 365.2524).toFixed(4), - // Months: ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), - // Days: (calDayDiff.days_diff % 30.4375).toFixed(4), - Years: Math.floor(calDayDiff.days_diff / 365.2524), - Months: Math.floor((calDayDiff.days_diff / 30.4375) % 12), - Days: Math.floor(calDayDiff.days_diff % 30.4375), + Years: year, + Months: month, + Days: day, }; // data.push(_mapData); await this.positionEmployeeRepo.save(mapData); @@ -202,15 +199,16 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionType: null, positionLevel: null, positionCee: null }, ); + 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: x.posLevel == null ? 0 : (calDayDiff.days_diff / 365.2524).toFixed(4), - Months: x.posLevel == null ? 0 : ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), - Days: x.posLevel == null ? 0 : (calDayDiff.days_diff % 30.4375).toFixed(4), + Years: x.posLevel == null ? 0 : year.toFixed(4), + Months: x.posLevel == null ? 0 : month.toFixed(4), + Days: x.posLevel == null ? 0 : day.toFixed(4), }; // data.push(_mapData); await this.levelOfficerRepo.save(mapData); @@ -260,15 +258,16 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionType: null, positionLevel: null, positionCee: null }, ); + 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: x.posLevel == null ? 0 : (calDayDiff.days_diff / 365.2524).toFixed(4), - Months: x.posLevel == null ? 0 : ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), - Days: x.posLevel == null ? 0 : (calDayDiff.days_diff % 30.4375).toFixed(4), + Years: x.posLevel == null ? 0 : year.toFixed(4), + Months: x.posLevel == null ? 0 : month.toFixed(4), + Days: x.posLevel == null ? 0 : day.toFixed(4), }; // data.push(_mapData); await this.levelEmployeeRepo.save(mapData); @@ -331,13 +330,14 @@ export class ProfileSalaryController extends Controller { }, { days_diff: 0, positionExecutive: null }, ); + const { year, month, day } = calculateTenure(calDayDiff.days_diff); const mapData: any = { profileId: x.id, positionExecutiveName: calDayDiff.positionExecutive, days_diff: calDayDiff.days_diff, - Years: (calDayDiff.days_diff / 365.2524).toFixed(4), - Months: ((calDayDiff.days_diff / 30.4375) % 12).toFixed(4), - Days: (calDayDiff.days_diff % 30.4375).toFixed(4), + Years: year.toFixed(4), + Months: month.toFixed(4), + Days: day.toFixed(4), }; await this.positionExecutiveOfficerRepo.save(mapData); } @@ -602,10 +602,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - // Recalculate year, month, and day - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -641,10 +641,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - // Recalculate year, month, and day - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -675,10 +675,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - // Recalculate year, month, and day - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -739,10 +739,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - // Recalculate year, month, and day - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -782,10 +782,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - // Recalculate year, month, and day - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -819,10 +819,10 @@ export class ProfileSalaryController extends Controller { acc.push(existing); } - // Recalculate year, month, and day - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, diff --git a/src/controllers/ProfileSalaryEmployeeController.ts b/src/controllers/ProfileSalaryEmployeeController.ts index 5b87003c..7428e913 100644 --- a/src/controllers/ProfileSalaryEmployeeController.ts +++ b/src/controllers/ProfileSalaryEmployeeController.ts @@ -27,6 +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 { calculateTenure } from "../utils/tenure"; import { Command } from "../entities/Command"; import { OrgRoot } from "../entities/OrgRoot"; import Extension from "../interfaces/extension"; @@ -175,9 +176,10 @@ export class ProfileSalaryEmployeeController extends Controller { acc.push(existing); } - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -211,9 +213,10 @@ export class ProfileSalaryEmployeeController extends Controller { acc.push(existing); } - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -266,9 +269,10 @@ export class ProfileSalaryEmployeeController extends Controller { acc.push(existing); } - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, @@ -302,9 +306,10 @@ export class ProfileSalaryEmployeeController extends Controller { acc.push(existing); } - existing.year = Math.floor(existing.days / 365.2524); - existing.month = Math.floor((existing.days / 30.4375) % 12); - existing.day = Math.ceil(existing.days % 30.4375); + const { year, month, day } = calculateTenure(existing.days); + existing.year = year; + existing.month = month; + existing.day = day; return acc; }, diff --git a/src/utils/tenure.ts b/src/utils/tenure.ts new file mode 100644 index 00000000..577d314b --- /dev/null +++ b/src/utils/tenure.ts @@ -0,0 +1,23 @@ +/** + * คำนวณอายุงานจากจำนวนวันรวม + * @param totalDays จำนวนวันรวม + * @returns { year, month, day } ปี เดือน วัน + */ +export function calculateTenure(totalDays: number) { + // 1. แปลงเป็น year เต็ม + const year = Math.floor(totalDays / 365.2524); + + // 2. วันที่เหลือหลังหัก year ออก + const remainAfterYear = totalDays - year * 365.2524; + + // 3. แปลงเป็น month เต็ม + const month = Math.floor(remainAfterYear / 30.4375); + + // 4. วันที่เหลือหลังหัก month ออก + const remainAfterMonth = remainAfterYear - month * 30.4375; + + // 5. ปัดลง เฉพาะวัน + const day = Math.floor(remainAfterMonth); + + return { year, month, day }; +}