Merge branch 'develop' into adiDev

This commit is contained in:
Adisak 2026-06-12 10:01:30 +07:00
commit 1b1a234bfb
15 changed files with 197 additions and 46 deletions

View file

@ -4321,7 +4321,7 @@ export class CommandController extends Controller {
}
//ลบตำแหน่งที่รักษาการแทน
const code = _command?.commandType?.code;
if (code && ["C-PM-08", "C-PM-17", "C-PM-18"].includes(code)) {
if (code && ["C-PM-08", "C-PM-17", "C-PM-18", "C-PM-48"].includes(code)) {
removePostMasterAct(profile.id);
}
//ออกคำสั่งยกเลิกลาออก ลบเฉพาะคนที่ขอยกเลิกลาออก
@ -4733,7 +4733,7 @@ export class CommandController extends Controller {
});
// Task #2190
if (code && ["C-PM-17", "C-PM-18"].includes(code)) {
if (code && ["C-PM-17", "C-PM-18", "C-PM-48"].includes(code)) {
let organizeName = "";
if (orgRootRef) {
const names = [
@ -5594,6 +5594,7 @@ export class CommandController extends Controller {
date: item.commandDateAffect,
refCommandDate: item.commandDateSign,
refCommandNo: `${item.commandNo}/${item.commandYear}`,
refCommandId: item.commandId,
createdUserId: req.user.sub,
createdFullName: req.user.name,
lastUpdateUserId: req.user.sub,
@ -6117,6 +6118,7 @@ export class CommandController extends Controller {
date: item.commandDateAffect,
refCommandDate: item.commandDateSign,
refCommandNo: `${item.commandNo}/${item.commandYear}`,
refCommandId: item.commandId,
profileEmployeeId: item.profileId,
profileId: undefined,
});

View file

@ -8563,7 +8563,7 @@ export class OrganizationController extends Controller {
orgChild3Id,
orgChild4Id,
current_holderId: draftPos.next_holderId,
next_holderId: draftPos.next_holderId,
next_holderId: null,
isSit: draftPos.isSit,
reason: draftPos.reason,
isDirector: draftPos.isDirector,
@ -8592,6 +8592,7 @@ export class OrganizationController extends Controller {
orgChild3Id,
orgChild4Id,
current_holderId: draftPos.next_holderId,
next_holderId: null,
statusReport: "DONE",
});
@ -8984,7 +8985,16 @@ 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"],
relations: [
"orgRoot",
"orgChild1",
"orgChild2",
"orgChild3",
"orgChild4",
"next_holder",
"next_holder.posType",
"next_holder.posLevel",
],
});
// Fetch ALL positions for ALL posMasters in just 2 queries
@ -9122,17 +9132,22 @@ export class OrganizationController extends Controller {
org: draftPosMaster ? getOrgFullName(draftPosMaster as PosMaster) ?? _null : _null,
});
}
// ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ
if (nextHolderId != null && draftPos.positionIsSelected && !draftPosMaster?.isSit) {
const existing = profileUpdates.get(nextHolderId) || {};
existing.position = draftPos.positionName;
existing.posTypeId = draftPos.posTypeId;
existing.posLevelId = draftPos.posLevelId;
existing.positionField = draftPos.positionField ?? null;
existing.posExecutive = (draftPos as any).posExecutive?.posExecutiveName ?? null;
existing.positionArea = draftPos.positionArea ?? null;
existing.positionExecutiveField = draftPos.positionExecutiveField ?? null;
profileUpdates.set(nextHolderId, existing);
if (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);
}
// ยังบันทึกประวัติคนครอง
if (draftPosMaster && draftPosMaster.ancestorDNA) {
// Find the selected position from draft positions
const selectedPos =
@ -9144,10 +9159,11 @@ export class OrganizationController extends Controller {
prefix: draftPosMaster.next_holder?.prefix ?? null,
firstName: draftPosMaster.next_holder?.firstName ?? null,
lastName: draftPosMaster.next_holder?.lastName ?? null,
position: selectedPos.positionName ?? null,
posType: (selectedPos as any).posType?.posTypeName ?? null,
posLevel: (selectedPos as any).posLevel?.posLevelName ?? null,
posExecutive: (selectedPos as any).posExecutive?.posExecutiveName ?? null,
// 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,
profileId: nextHolderId,
rootDnaId: draftPosMaster.orgRoot?.ancestorDNA ?? null,
child1DnaId: draftPosMaster.orgChild1?.ancestorDNA ?? null,

View file

@ -9153,28 +9153,41 @@ export class OrganizationDotnetController extends Controller {
}
/**
* API profileId
* @summary API profileId
* API
* @summary API
*/
@Post("check-isLeave")
@Security("internalAuth")
async findProfileIsLeave(
@Body()
req: { profileIds: string[] }
req: { citizenIds: string[] }
) {
const profile = await this.profileRepo.find({
select: { id: true },
const profiles = await this.profileRepo.find({
select: {
id: true,
citizenId: true,
isLeave: true,
isActive: true
},
where: {
id: In(req.profileIds),
isLeave: true
citizenId: In(req.citizenIds)
}
});
if (profile.length === 0) {
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
}))
);
return new HttpSuccess(profile.map(p => p.id));
}
}

View file

@ -4242,6 +4242,7 @@ 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")
@ -4253,6 +4254,8 @@ 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, {
@ -4431,6 +4434,7 @@ export class PositionController extends Controller {
typeCommand: string | null;
posType?: string | null;
posLevel?: string | null;
profileId?: string | null;
isAll: boolean;
isBlank: boolean;
},
@ -4472,9 +4476,13 @@ export class PositionController extends Controller {
posLevel: posLevel?.id,
};
} else if (body.typeCommand == "APPOINT") {
conditionA = "posType.posTypeRank > :posTypeRank";
// เดิม : กรองเฉพาะ posTypeRank ที่สูงกว่า
// conditionA = "posType.posTypeRank > :posTypeRank";
// ใหม่ : กรองเฉพาะ posType ที่สูงหรือต่ำกว่าก็ได้
conditionA = "positions.posTypeId != :currentPosType";
params = {
posTypeRank: posType?.posTypeRank ?? 0,
// posTypeRank: posType?.posTypeRank ?? 0,
currentPosType: posType?.id,
};
} else if (body.typeCommand == "SLIP") {
conditionA = "positions.posTypeId LIKE :posType AND posLevel.posLevelRank > :posLevelRank";
@ -4538,8 +4546,13 @@ 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")
@ -4552,6 +4565,8 @@ 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)

View file

@ -112,6 +112,7 @@ export class ProfileChangeNameController extends Controller {
profile.firstName = body.firstName ?? profile.firstName;
profile.lastName = body.lastName ?? profile.lastName;
profile.prefix = body.prefix ?? profile.prefix;
profile.rank = body.rank ?? profile.rank;
profile.prefixMain = profile.rank ?? profile.prefix;
await this.profileRepository.save(profile, { data: req });
setLogDataDiff(req, { before, after: profile });
@ -184,6 +185,7 @@ export class ProfileChangeNameController extends Controller {
profile.firstName = body.firstName ?? profile.firstName;
profile.lastName = body.lastName ?? profile.lastName;
profile.prefix = body.prefix ?? profile.prefix;
profile.rank = body.rank ?? profile.rank;
profile.prefixMain = profile.rank ?? profile.prefix;
await this.profileRepository.save(profile, { data: req });
setLogDataDiff(req, { before: before_profile, after: profile });

View file

@ -118,6 +118,7 @@ export class ProfileChangeNameEmployeeController extends Controller {
profile.firstName = body.firstName ?? profile.firstName;
profile.lastName = body.lastName ?? profile.lastName;
profile.prefix = body.prefix ?? profile.prefix;
profile.rank = body.rank ?? profile.rank;
profile.prefixMain = profile.rank ?? profile.prefix;
await this.profileEmployeeRepo.save(profile, { data: req });
setLogDataDiff(req, { before, after: profile });
@ -191,6 +192,7 @@ export class ProfileChangeNameEmployeeController extends Controller {
profile.firstName = body.firstName ?? profile.firstName;
profile.lastName = body.lastName ?? profile.lastName;
profile.prefix = body.prefix ?? profile.prefix;
profile.rank = body.rank ?? profile.rank;
profile.prefixMain = profile.rank ?? profile.prefix;
await this.profileEmployeeRepo.save(profile);
}

View file

@ -109,6 +109,7 @@ export class ProfileChangeNameEmployeeTempController extends Controller {
profile.firstName = body.firstName ?? profile.firstName;
profile.lastName = body.lastName ?? profile.lastName;
profile.prefix = body.prefix ?? profile.prefix;
profile.rank = body.rank ?? profile.rank;
profile.prefixMain = profile.rank ?? profile.prefix;
await this.profileEmployeeRepo.save(profile, { data: req });
setLogDataDiff(req, { before, after: profile });
@ -179,6 +180,7 @@ export class ProfileChangeNameEmployeeTempController extends Controller {
profile.firstName = body.firstName ?? profile.firstName;
profile.lastName = body.lastName ?? profile.lastName;
profile.prefix = body.prefix ?? profile.prefix;
profile.rank = body.rank ?? profile.rank;
profile.prefixMain = profile.rank ?? profile.prefix;
await this.profileEmployeeRepo.save(profile);
}

View file

@ -31,6 +31,14 @@ export class ProfileChangeName extends EntityBase {
})
prefix: string;
@Column({
nullable: true,
comment: "ยศ",
length: 40,
default: null,
})
rank: string;
@Column({
nullable: true,
length: 100,
@ -103,6 +111,7 @@ export class CreateProfileChangeName {
profileId: string | null;
prefixId: string | null;
prefix: string | null;
rank: string | null;
firstName: string | null;
lastName: string | null;
status: string | null;
@ -113,6 +122,7 @@ export class CreateProfileChangeNameEmployee {
profileEmployeeId: string | null;
prefixId: string | null;
prefix: string | null;
rank: string | null;
firstName: string | null;
lastName: string | null;
status: string | null;
@ -122,6 +132,7 @@ export class CreateProfileChangeNameEmployee {
export type UpdateProfileChangeName = {
prefixId?: string | null;
prefix?: string | null;
rank?: string | null;
firstName?: string | null;
lastName?: string | null;
status?: string | null;

View file

@ -20,6 +20,14 @@ export class ProfileChangeNameHistory extends EntityBase {
})
prefix: string;
@Column({
nullable: true,
comment: "ยศ",
length: 40,
default: null,
})
rank: string;
@Column({
nullable: true,
length: 100,
@ -79,6 +87,7 @@ export class CreateProfileChangeNameHistory {
profileChangeNameId: string | null;
prefixId: string | null;
prefix: string | null;
rank: string | null;
firstName: string | null;
lastName: string | null;
status: string | null;
@ -89,6 +98,7 @@ export type UpdateProfileChangeNameHistory = {
profileChangeNameId?: string | null;
prefixId?: string | null;
prefix?: string | null;
rank?: string | null;
firstName?: string | null;
lastName?: string | null;
status?: string | null;

View file

@ -61,6 +61,14 @@ export class ProfileDiscipline extends EntityBase {
default: null,
})
refCommandNo: string;
@Column({
nullable: true,
length: 40,
comment: "คีย์นอก(FK)ของตาราง command",
default: null,
})
refCommandId: string;
@Column({
nullable: true,

View file

@ -51,6 +51,14 @@ export class ProfileDisciplineHistory extends EntityBase {
})
refCommandNo: string;
@Column({
nullable: true,
length: 40,
comment: "คีย์นอก(FK)ของตาราง command",
default: null,
})
refCommandId: string;
@Column({
nullable: true,
comment: "ล้างมลทิน",

View file

@ -417,6 +417,7 @@ export async function checkCommandType(commandId: string) {
"C-PM-25",
"C-PM-26",
"C-PM-43",
"C-PM-48"
].includes(String(_type?.commandType.code))
) {
// return false;
@ -480,6 +481,11 @@ export async function checkCommandType(commandId: string) {
_retireTypeName = "ให้ออกจากราชการ";
break;
}
case "C-PM-48": {
_leaveType = "RETIRE_MILITARY";
_retireTypeName = "รับราชการทหาร";
break;
}
default: {
_leaveType = "";
_retireTypeName = "";
@ -703,6 +709,8 @@ 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;
}

View file

@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class UpdateProfileDisciplineAddRefCommandId1780634210221 implements MigrationInterface {
name = 'UpdateProfileDisciplineAddRefCommandId1780634210221'
public async up(queryRunner: QueryRunner): Promise<void> {
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<void> {
await queryRunner.query(`ALTER TABLE \`profileDiscipline\` DROP COLUMN \`refCommandId\``);
await queryRunner.query(`ALTER TABLE \`profileDisciplineHistory\` DROP COLUMN \`refCommandId\``);
}
}

View file

@ -0,0 +1,16 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class UpdateProfileChangeNameAddFieldRank1781174051201 implements MigrationInterface {
name = 'UpdateProfileChangeNameAddFieldRank1781174051201'
public async up(queryRunner: QueryRunner): Promise<void> {
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<void> {
await queryRunner.query(`ALTER TABLE \`profileChangeName\` DROP COLUMN \`rank\``);
await queryRunner.query(`ALTER TABLE \`profileChangeNameHistory\` DROP COLUMN \`rank\``);
}
}

View file

@ -11,6 +11,7 @@ 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";
/**
* function
@ -18,9 +19,7 @@ import { RequestWithUser } from "../middlewares/user";
* - posType = "อำนวยการ" "บริหาร" posExecutiveName
* - posType positionName + posLevel
*/
export async function getPosMasterPositions(
posMasterIds: string[]
): Promise<Map<string, string>> {
export async function getPosMasterPositions(posMasterIds: string[]): Promise<Map<string, string>> {
if (posMasterIds.length === 0) {
return new Map();
}
@ -61,7 +60,9 @@ export async function getPosMasterPositions(
let positionText = "";
if (posTypeName === "อำนวยการ" || posTypeName === "บริหาร") {
positionText = pos.posExecutive?.posExecutiveName || `${pos.positionName || ""}ระดับ${pos.posLevel?.posLevelName || ""}`.trim();
positionText =
pos.posExecutive?.posExecutiveName ||
`${pos.positionName || ""}ระดับ${pos.posLevel?.posLevelName || ""}`.trim();
} else {
positionText = `${pos.positionName || ""}${pos.posLevel?.posLevelName || ""}`.trim();
}
@ -72,7 +73,6 @@ export async function getPosMasterPositions(
return positionMap;
}
export async function CreatePosMasterHistoryOfficer(
posMasterId: string,
request: RequestWithUser | null,
@ -85,6 +85,7 @@ export async function CreatePosMasterHistoryOfficer(
const repoHistory = transactionManager.getRepository(PosMasterHistory);
const repoOrgRevision = transactionManager.getRepository(OrgRevision);
const repoPosition = transactionManager.getRepository(Position);
const repoProfile = transactionManager.getRepository(Profile);
const pm = await repoPosmaster.findOne({
where: { id: posMasterId },
@ -132,6 +133,21 @@ export async function CreatePosMasterHistoryOfficer(
: null;
}
let position = selectedPosition?.positionName ?? _null;
let posTypeName = selectedPosition?.posType?.posTypeName ?? _null;
let posLevelName = selectedPosition?.posLevel?.posLevelName ?? _null;
let posExecutiveName = selectedPosition?.posExecutive?.posExecutiveName ?? _null;
if (pm.isSit && pm.current_holderId) {
const profile = await repoProfile.findOne({
where: { id: pm.current_holderId },
relations: ["posType", "posLevel"],
});
position = profile?.position ?? _null;
posTypeName = profile?.posType?.posTypeName ?? _null;
posLevelName = profile?.posLevel?.posLevelName ?? _null;
posExecutiveName = profile?.posExecutive ?? _null;
}
h.ancestorDNA = pm.ancestorDNA ? pm.ancestorDNA : _null;
if (!type || type != "DELETE") {
if (checkCurrentRevision) {
@ -144,9 +160,9 @@ export async function CreatePosMasterHistoryOfficer(
h.firstName = pm.next_holder?.firstName || _null;
h.lastName = pm.next_holder?.lastName || _null;
}
h.position = selectedPosition?.positionName ?? _null;
h.posType = selectedPosition?.posType?.posTypeName ?? _null;
h.posLevel = selectedPosition?.posLevel?.posLevelName ?? _null;
h.position = position;
h.posType = posTypeName;
h.posLevel = posLevelName;
}
h.rootDnaId = pm.orgRoot?.ancestorDNA || _null;
h.child1DnaId = pm.orgChild1?.ancestorDNA || _null;
@ -156,7 +172,7 @@ export async function CreatePosMasterHistoryOfficer(
h.posMasterNoPrefix = pm.posMasterNoPrefix ?? _null;
h.posMasterNo = pm.posMasterNo ?? _null;
h.posMasterNoSuffix = pm.posMasterNoSuffix ?? _null;
h.posExecutive = selectedPosition?.posExecutive?.posExecutiveName ?? _null;
h.posExecutive = posExecutiveName;
h.shortName =
[
pm.orgChild4?.orgChild4ShortName,
@ -459,8 +475,15 @@ 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;
if (shouldInsert || profileChanged) {
// ถ้าไม่มี record เดิม หรือ profile เปลี่ยน หรือ position เปลี่ยน ให้สร้าง record ใหม่
if (shouldInsert || profileChanged || positionChanged) {
const newPmh = new PosMasterHistory();
newPmh.ancestorDNA = op.posMasterDnaId;
newPmh.prefix = op.pm?.prefix ?? _null;
@ -525,11 +548,11 @@ export async function updateHolderProfileHistory(
orgRevision: {
orgRevisionIsCurrent: true,
orgRevisionIsDraft: false,
}
},
},
relations: {
orgRevision : true
}
orgRevision: true,
},
});
if (posMaster) {
@ -543,11 +566,11 @@ export async function updateHolderProfileHistory(
orgRevision: {
orgRevisionIsCurrent: true,
orgRevisionIsDraft: false,
}
},
},
relations: {
orgRevision : true
}
orgRevision: true,
},
});
if (employeePosMaster) {