Linear Flow Probation+Salary #224
This commit is contained in:
parent
3d2fc5128a
commit
7b37cc37db
6 changed files with 976 additions and 572 deletions
|
|
@ -109,6 +109,7 @@ import { ExecuteSalaryEmployeeCurrentService } from "../services/ExecuteSalaryEm
|
||||||
import { ExecuteSalaryLeaveService } from "../services/ExecuteSalaryLeaveService";
|
import { ExecuteSalaryLeaveService } from "../services/ExecuteSalaryLeaveService";
|
||||||
import { ExecuteSalaryEmployeeLeaveService } from "../services/ExecuteSalaryEmployeeLeaveService";
|
import { ExecuteSalaryEmployeeLeaveService } from "../services/ExecuteSalaryEmployeeLeaveService";
|
||||||
import { ExecuteSalaryLeaveDisciplineService } from "../services/ExecuteSalaryLeaveDisciplineService";
|
import { ExecuteSalaryLeaveDisciplineService } from "../services/ExecuteSalaryLeaveDisciplineService";
|
||||||
|
import { ExecuteSalaryProbationService } from "../services/ExecuteSalaryProbationService";
|
||||||
import { ExecuteOrgCommandService } from "../services/ExecuteOrgCommandService";
|
import { ExecuteOrgCommandService } from "../services/ExecuteOrgCommandService";
|
||||||
|
|
||||||
@Route("api/v1/org/command")
|
@Route("api/v1/org/command")
|
||||||
|
|
@ -4424,170 +4425,10 @@ export class CommandController extends Controller {
|
||||||
}[];
|
}[];
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
let _posNumCodeSit: string = "";
|
await new ExecuteSalaryProbationService().executeProbationPass(body.data, {
|
||||||
let _posNumCodeSitAbb: string = "";
|
user: { sub: req.user.sub, name: req.user.name },
|
||||||
let commandType: any = "";
|
req,
|
||||||
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();
|
return new HttpSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4614,245 +4455,10 @@ export class CommandController extends Controller {
|
||||||
}[];
|
}[];
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
let _posNumCodeSit: string = "";
|
await new ExecuteSalaryProbationService().executeProbationLeave(body.data, {
|
||||||
let _posNumCodeSitAbb: string = "";
|
user: { sub: req.user.sub, name: req.user.name },
|
||||||
const _command = await this.commandRepository.findOne({
|
req,
|
||||||
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();
|
return new HttpSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import { ProfileEmployee } from "../entities/ProfileEmployee";
|
||||||
import { In, IsNull, LessThan, MoreThan, Not } from "typeorm";
|
import { In, IsNull, LessThan, MoreThan, Not } from "typeorm";
|
||||||
import permission from "../interfaces/permission";
|
import permission from "../interfaces/permission";
|
||||||
import { setLogDataDiff } from "../interfaces/utils";
|
import { setLogDataDiff } from "../interfaces/utils";
|
||||||
|
import { ExecuteSalaryReportService } from "../services/ExecuteSalaryReportService";
|
||||||
import { normalizeDurationSumSimple } from "../utils/tenure";
|
import { normalizeDurationSumSimple } from "../utils/tenure";
|
||||||
import {
|
import {
|
||||||
TenurePositionOfficer,
|
TenurePositionOfficer,
|
||||||
|
|
@ -1380,91 +1381,10 @@ export class ProfileSalaryController extends Controller {
|
||||||
|
|
||||||
@Post("update")
|
@Post("update")
|
||||||
public async updateSalary(@Request() req: RequestWithUser, @Body() body: CreateProfileSalary) {
|
public async updateSalary(@Request() req: RequestWithUser, @Body() body: CreateProfileSalary) {
|
||||||
if (!body.profileId) {
|
await new ExecuteSalaryReportService().executeOfficerSalaryUpdate([body], {
|
||||||
throw new HttpError(HttpStatus.BAD_REQUEST, "กรุณากรอก profileId");
|
user: { sub: req.user.sub, name: req.user.name },
|
||||||
}
|
req,
|
||||||
|
|
||||||
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();
|
return new HttpSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import { Profile } from "../entities/Profile";
|
||||||
import { In, LessThan, IsNull, MoreThan } from "typeorm";
|
import { In, LessThan, IsNull, MoreThan } from "typeorm";
|
||||||
import permission from "../interfaces/permission";
|
import permission from "../interfaces/permission";
|
||||||
import { setLogDataDiff } from "../interfaces/utils";
|
import { setLogDataDiff } from "../interfaces/utils";
|
||||||
|
import { ExecuteSalaryReportService } from "../services/ExecuteSalaryReportService";
|
||||||
import { normalizeDurationSumSimple } from "../utils/tenure";
|
import { normalizeDurationSumSimple } from "../utils/tenure";
|
||||||
import { Command } from "../entities/Command";
|
import { Command } from "../entities/Command";
|
||||||
import { OrgRoot } from "../entities/OrgRoot";
|
import { OrgRoot } from "../entities/OrgRoot";
|
||||||
|
|
@ -507,94 +508,10 @@ export class ProfileSalaryEmployeeController extends Controller {
|
||||||
@Request() req: RequestWithUser,
|
@Request() req: RequestWithUser,
|
||||||
@Body() body: CreateProfileSalaryEmployee,
|
@Body() body: CreateProfileSalaryEmployee,
|
||||||
) {
|
) {
|
||||||
if (!body.profileEmployeeId) {
|
await new ExecuteSalaryReportService().executeEmployeeSalaryUpdate([body], {
|
||||||
throw new HttpError(HttpStatus.BAD_REQUEST, "กรุณากรอก profileEmployeeId");
|
user: { sub: req.user.sub, name: req.user.name },
|
||||||
}
|
req,
|
||||||
|
|
||||||
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();
|
return new HttpSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
539
src/services/ExecuteSalaryProbationService.ts
Normal file
539
src/services/ExecuteSalaryProbationService.ts
Normal file
|
|
@ -0,0 +1,539 @@
|
||||||
|
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<void> {
|
||||||
|
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<ProfileSalary>,
|
||||||
|
salaryHistoryRepo: Repository<ProfileSalaryHistory>,
|
||||||
|
profileRepository: Repository<Profile>,
|
||||||
|
orgRevisionRepo: Repository<OrgRevision>,
|
||||||
|
): Promise<void> {
|
||||||
|
// 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<void> {
|
||||||
|
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<ProfileSalary>,
|
||||||
|
salaryHistoryRepo: Repository<ProfileSalaryHistory>,
|
||||||
|
profileRepository: Repository<Profile>,
|
||||||
|
orgRevisionRepo: Repository<OrgRevision>,
|
||||||
|
): Promise<void> {
|
||||||
|
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}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
318
src/services/ExecuteSalaryReportService.ts
Normal file
318
src/services/ExecuteSalaryReportService.ts
Normal file
|
|
@ -0,0 +1,318 @@
|
||||||
|
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<void> {
|
||||||
|
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<Profile>,
|
||||||
|
salaryRepo: Repository<ProfileSalary>,
|
||||||
|
salaryHistoryRepo: Repository<ProfileSalaryHistory>,
|
||||||
|
): Promise<void> {
|
||||||
|
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<void> {
|
||||||
|
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<ProfileEmployee>,
|
||||||
|
salaryRepo: Repository<ProfileSalary>,
|
||||||
|
salaryHistoryRepo: Repository<ProfileSalaryHistory>,
|
||||||
|
): Promise<void> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,8 @@ import { ExecuteSalaryLeaveService } from "./ExecuteSalaryLeaveService";
|
||||||
import { ExecuteSalaryEmployeeLeaveService } from "./ExecuteSalaryEmployeeLeaveService";
|
import { ExecuteSalaryEmployeeLeaveService } from "./ExecuteSalaryEmployeeLeaveService";
|
||||||
import { ExecuteSalaryLeaveDisciplineService } from "./ExecuteSalaryLeaveDisciplineService";
|
import { ExecuteSalaryLeaveDisciplineService } from "./ExecuteSalaryLeaveDisciplineService";
|
||||||
import { ExecuteOrgCommandService } from "./ExecuteOrgCommandService";
|
import { ExecuteOrgCommandService } from "./ExecuteOrgCommandService";
|
||||||
|
import { ExecuteSalaryProbationService } from "./ExecuteSalaryProbationService";
|
||||||
|
import { ExecuteSalaryReportService } from "./ExecuteSalaryReportService";
|
||||||
|
|
||||||
const redis = require("redis");
|
const redis = require("redis");
|
||||||
const REDIS_HOST = process.env.REDIS_HOST;
|
const REDIS_HOST = process.env.REDIS_HOST;
|
||||||
|
|
@ -357,6 +359,15 @@ async function handler(msg: amqp.ConsumeMessage): Promise<boolean> {
|
||||||
const isCommand38 = code === "C-PM-38";
|
const isCommand38 = code === "C-PM-38";
|
||||||
const isCommand40 = code === "C-PM-40";
|
const isCommand40 = code === "C-PM-40";
|
||||||
const isOrgSelfLinear = isCommand21 || isCommand38 || isCommand40;
|
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 =
|
const isLinearFlow =
|
||||||
isOfficerProfile ||
|
isOfficerProfile ||
|
||||||
isSalaryCurrent ||
|
isSalaryCurrent ||
|
||||||
|
|
@ -384,6 +395,99 @@ async function handler(msg: amqp.ConsumeMessage): Promise<boolean> {
|
||||||
await new ExecuteOrgCommandService().executeCommand40Officer(flatRefIds, ctx);
|
await new ExecuteOrgCommandService().executeCommand40Officer(flatRefIds, ctx);
|
||||||
}
|
}
|
||||||
console.log(`[AMQ] Processed ${flatRefIds.length} items via ExecuteOrgCommandService (${code})`);
|
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 = { headers: { authorization: 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 = { headers: { authorization: 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) {
|
} else if (isLinearFlow) {
|
||||||
console.log(`[AMQ] Linear Flow (${code})`);
|
console.log(`[AMQ] Linear Flow (${code})`);
|
||||||
const isCpm32 = code === "C-PM-32";
|
const isCpm32 = code === "C-PM-32";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue