hrms-api-org/src/services/PositionService.ts

405 lines
15 KiB
TypeScript
Raw Normal View History

import { In } from "typeorm";
import { SavePosMasterHistory } from "./../interfaces/OrgMapping";
2025-08-26 13:47:43 +07:00
import { AppDataSource } from "../database/data-source";
import { EmployeePosMaster } from "../entities/EmployeePosMaster";
import { EmployeeTempPosMaster } from "../entities/EmployeeTempPosMaster";
2025-09-18 17:08:38 +07:00
import { OrgRevision } from "../entities/OrgRevision";
2025-08-26 13:47:43 +07:00
import { PosMaster } from "../entities/PosMaster";
import { PosMasterEmployeeHistory } from "../entities/PosMasterEmployeeHistory";
import { PosMasterEmployeeTempHistory } from "../entities/PosMasterEmployeeTempHistory";
import { PosMasterHistory } from "../entities/PosMasterHistory";
2025-09-08 00:10:59 +07:00
import { ProfileEducation } from "../entities/ProfileEducation";
2025-08-26 13:47:43 +07:00
import { RequestWithUser } from "../middlewares/user";
export async function CreatePosMasterHistoryOfficer(
posMasterId: string,
request: RequestWithUser | null,
type?: string | null,
2025-08-26 13:47:43 +07:00
): Promise<boolean> {
try {
await AppDataSource.transaction(async (manager) => {
const repoPosmaster = manager.getRepository(PosMaster);
const repoHistory = manager.getRepository(PosMasterHistory);
2025-09-18 17:08:38 +07:00
const repoOrgRevision = manager.getRepository(OrgRevision);
2025-08-26 13:47:43 +07:00
const pm = await repoPosmaster.findOne({
where: { id: posMasterId },
relations: [
"positions",
"positions.posLevel",
"positions.posType",
"positions.posExecutive",
"orgRoot",
"orgChild1",
"orgChild2",
"orgChild3",
"orgChild4",
"current_holder",
2025-09-18 17:08:38 +07:00
"next_holder",
2025-08-26 13:47:43 +07:00
],
});
if (!pm) return false;
2025-09-04 12:10:53 +07:00
if (!pm.ancestorDNA) return false;
2025-09-18 17:08:38 +07:00
const checkCurrentRevision = await repoOrgRevision.findOne({
2026-01-05 11:35:49 +07:00
where: {
2025-09-18 17:08:38 +07:00
id: pm.orgRevisionId,
orgRevisionIsCurrent: true,
orgRevisionIsDraft: false,
},
});
2025-08-26 13:47:43 +07:00
const _null: any = null;
const h = new PosMasterHistory();
const selectedPosition =
pm.positions.length > 0
2025-09-08 00:10:59 +07:00
? pm.positions.find((p) => p.positionIsSelected === true) ?? null
2025-08-26 13:47:43 +07:00
: null;
2025-09-08 00:10:59 +07:00
h.ancestorDNA = pm.ancestorDNA ? pm.ancestorDNA : _null;
2026-01-05 11:35:49 +07:00
if (!type || type != "DELETE") {
if (checkCurrentRevision) {
2025-09-18 17:08:38 +07:00
h.prefix = pm.current_holder?.prefix || _null;
h.firstName = pm.current_holder?.firstName || _null;
h.lastName = pm.current_holder?.lastName || _null;
2026-01-05 11:35:49 +07:00
h.profileId = pm.current_holder?.id || _null;
} else {
2025-09-18 17:08:38 +07:00
h.prefix = pm.next_holder?.prefix || _null;
h.firstName = pm.next_holder?.firstName || _null;
h.lastName = pm.next_holder?.lastName || _null;
}
2025-09-09 11:26:55 +07:00
h.position = selectedPosition?.positionName ?? _null;
h.posType = selectedPosition?.posType?.posTypeName ?? _null;
h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _null;
}
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;
2025-08-26 13:47:43 +07:00
h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null;
h.posMasterNo = pm.posMasterNo ?? _null;
h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null;
h.posExecutive = selectedPosition?.posExecutive?.posExecutiveName ?? _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) {
console.error("CreatePosMasterHistoryOfficer transaction error:", err);
return false;
}
}
export async function CreatePosMasterHistoryEmployee(
posMasterId: string,
request: RequestWithUser | null,
): Promise<boolean> {
try {
await AppDataSource.transaction(async (manager) => {
const repoPosmaster = manager.getRepository(EmployeePosMaster);
const repoHistory = manager.getRepository(PosMasterEmployeeHistory);
const pm = await repoPosmaster.findOne({
where: { id: posMasterId },
relations: [
"positions",
"positions.posLevel",
"positions.posType",
2026-02-23 16:37:50 +07:00
// "positions.posExecutive",
2025-08-26 13:47:43 +07:00
"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;
h.prefix = pm.current_holder?.prefix || _null;
h.firstName = pm.current_holder?.firstName || _null;
h.lastName = pm.current_holder?.lastName || _null;
h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null;
h.posMasterNo = pm.posMasterNo ?? _null;
h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null;
h.position = selectedPosition?.positionName ?? _null;
h.posType = selectedPosition?.posType?.posTypeName ?? _null;
h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _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) {
console.error("CreatePosMasterHistoryEmployee transaction error:", err);
return false;
}
}
export async function CreatePosMasterHistoryEmployeeTemp(
posMasterId: string,
request: RequestWithUser | null,
): Promise<boolean> {
try {
await AppDataSource.transaction(async (manager) => {
const repoPosmaster = manager.getRepository(EmployeeTempPosMaster);
const repoHistory = manager.getRepository(PosMasterEmployeeTempHistory);
const pm = await repoPosmaster.findOne({
where: { id: posMasterId },
relations: [
"positions",
"positions.posLevel",
"positions.posType",
2026-02-23 16:37:50 +07:00
// "positions.posExecutive",
2025-08-26 13:47:43 +07:00
"orgRoot",
"orgChild1",
"orgChild2",
"orgChild3",
"orgChild4",
"current_holder",
],
});
if (!pm) return false;
if (!pm.ancestorDNA) return false;
const _null: any = null;
const h = new PosMasterEmployeeTempHistory();
const selectedPosition =
pm.positions.length > 0
? pm.positions.find((p) => p.positionIsSelected === true) ?? null
: null;
h.ancestorDNA = pm.ancestorDNA;
h.prefix = pm.current_holder?.prefix || _null;
h.firstName = pm.current_holder?.firstName || _null;
h.lastName = pm.current_holder?.lastName || _null;
h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null;
h.posMasterNo = pm.posMasterNo ?? _null;
h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null;
h.position = selectedPosition?.positionName ?? _null;
h.posType = selectedPosition?.posType?.posTypeName ?? _null;
h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _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) {
console.error("CreatePosMasterHistoryEmployeeTemp transaction error:", err);
return false;
}
}
2025-09-08 00:10:59 +07:00
export async function getTopDegrees(educations: ProfileEducation[]): Promise<string> {
// filter เฉพาะ isUse==true หรือ isEducation==true
const filtered = educations.filter((e) => e.isUse === true || e.isEducation === true);
// sort: isEducation==true ก่อน, ถ้าเท่ากัน sort ด้วย level น้อยสุด
const sorted = filtered.sort((a, b) => {
const aEdu = !!a.isEducation ? 0 : 1;
const bEdu = !!b.isEducation ? 0 : 1;
if (aEdu !== bEdu) return aEdu - bEdu;
const aLevel = typeof a.level === "number" ? a.level : Number.MAX_SAFE_INTEGER;
const bLevel = typeof b.level === "number" ? b.level : Number.MAX_SAFE_INTEGER;
return aLevel - bLevel;
});
return sorted
.map((e) => [e.degree, e.field].filter(Boolean).join(" "))
.filter(Boolean)
.join("\n");
}
export async function SavePosMasterHistoryOfficer(
queryRunner: any,
posMasterDnaId: string,
profileId: string | null,
pm: SavePosMasterHistory | null,
): Promise<boolean> {
try {
// Type workaround: entity columns are nullable but types don't reflect it
const _null: any = null;
const repoPosMasterHistory = queryRunner.manager.getRepository(PosMasterHistory);
const pmh = await repoPosMasterHistory.findOne({
where: {
ancestorDNA: posMasterDnaId,
},
order: { createdAt: "DESC" },
});
// Check if we need to insert a new history record
const shouldInsert = !pmh && profileId && pm;
const profileChanged = pmh && pmh.profileId !== profileId;
if (shouldInsert || profileChanged) {
// insert new record
const newPmh = new PosMasterHistory();
newPmh.ancestorDNA = posMasterDnaId;
newPmh.prefix = pm?.prefix ?? _null;
newPmh.firstName = pm?.firstName ?? _null;
newPmh.lastName = pm?.lastName ?? _null;
newPmh.position = pm?.position ?? _null;
newPmh.posType = pm?.posType ?? _null;
newPmh.posLevel = pm?.posLevel ?? _null;
newPmh.posExecutive = pm?.posExecutive ?? _null;
newPmh.profileId = profileId ?? _null;
newPmh.rootDnaId = pm?.rootDnaId ?? _null;
newPmh.child1DnaId = pm?.child1DnaId ?? _null;
newPmh.child2DnaId = pm?.child2DnaId ?? _null;
newPmh.child3DnaId = pm?.child3DnaId ?? _null;
newPmh.child4DnaId = pm?.child4DnaId ?? _null;
newPmh.shortName = pm?.shortName ?? _null;
newPmh.posMasterNoPrefix = pm?.posMasterNoPrefix ?? _null;
newPmh.posMasterNo = pm?.posMasterNo ?? _null;
newPmh.posMasterNoSuffix = pm?.posMasterNoSuffix ?? _null;
// Add audit fields for data integrity
newPmh.createdUserId = "system";
newPmh.createdFullName = "system";
newPmh.lastUpdateUserId = "system";
newPmh.lastUpdateFullName = "system";
newPmh.createdAt = new Date();
newPmh.lastUpdatedAt = new Date();
await queryRunner.manager.save(PosMasterHistory, newPmh);
return true;
}
return true;
} catch (err) {
console.error("SavePosMasterHistoryOfficer error:", err);
return false;
}
}
export interface BatchPosMasterHistoryOperation {
posMasterDnaId: string;
profileId: string | null;
pm: SavePosMasterHistory | null;
}
export async function BatchSavePosMasterHistoryOfficer(
queryRunner: any,
operations: BatchPosMasterHistoryOperation[],
): Promise<boolean> {
if (operations.length === 0) return true;
try {
const repoPosMasterHistory = queryRunner.manager.getRepository(PosMasterHistory);
const dnaIds = operations.map((op) => op.posMasterDnaId);
// Fetch all existing history records in ONE query
const existingHistory = await repoPosMasterHistory.find({
where: { ancestorDNA: In(dnaIds) },
order: { createdAt: "DESC" },
});
// Build lookup map
const historyByDna = new Map<string, PosMasterHistory[]>();
for (const h of existingHistory) {
if (!historyByDna.has(h.ancestorDNA)) {
historyByDna.set(h.ancestorDNA, []);
}
historyByDna.get(h.ancestorDNA)!.push(h);
}
// Process operations and collect new records
const newRecords: PosMasterHistory[] = [];
const _null: any = null;
for (const op of operations) {
const existing = historyByDna.get(op.posMasterDnaId)?.[0];
const shouldInsert = !existing && op.profileId && op.pm;
const profileChanged = existing && existing.profileId !== op.profileId;
if (shouldInsert || profileChanged) {
const newPmh = new PosMasterHistory();
newPmh.ancestorDNA = op.posMasterDnaId;
newPmh.prefix = op.pm?.prefix ?? _null;
newPmh.firstName = op.pm?.firstName ?? _null;
newPmh.lastName = op.pm?.lastName ?? _null;
newPmh.position = op.pm?.position ?? _null;
newPmh.posType = op.pm?.posType ?? _null;
newPmh.posLevel = op.pm?.posLevel ?? _null;
newPmh.posExecutive = op.pm?.posExecutive ?? _null;
newPmh.profileId = op.profileId ?? _null;
newPmh.rootDnaId = op.pm?.rootDnaId ?? _null;
newPmh.child1DnaId = op.pm?.child1DnaId ?? _null;
newPmh.child2DnaId = op.pm?.child2DnaId ?? _null;
newPmh.child3DnaId = op.pm?.child3DnaId ?? _null;
newPmh.child4DnaId = op.pm?.child4DnaId ?? _null;
newPmh.shortName = op.pm?.shortName ?? _null;
newPmh.posMasterNoPrefix = op.pm?.posMasterNoPrefix ?? _null;
newPmh.posMasterNo = op.pm?.posMasterNo ?? _null;
newPmh.posMasterNoSuffix = op.pm?.posMasterNoSuffix ?? _null;
newPmh.createdUserId = "system";
newPmh.createdFullName = "system";
newPmh.lastUpdateUserId = "system";
newPmh.lastUpdateFullName = "system";
newPmh.createdAt = new Date();
newPmh.lastUpdatedAt = new Date();
newRecords.push(newPmh);
}
}
// Batch insert all new records
if (newRecords.length > 0) {
await queryRunner.manager.save(PosMasterHistory, newRecords);
}
return true;
} catch (err) {
console.error("BatchSavePosMasterHistoryOfficer error:", err);
return false;
}
}