diff --git a/src/controllers/CommandController.ts b/src/controllers/CommandController.ts index 4b7c66e7..8ae7b5b7 100644 --- a/src/controllers/CommandController.ts +++ b/src/controllers/CommandController.ts @@ -104,6 +104,7 @@ import { LeaveType } from "../entities/LeaveType"; import { KeycloakAttributeService } from "../services/KeycloakAttributeService"; import { reOrderCommandRecivesAndDelete } from "../services/CommandService"; import { RetirementService } from "../services/RetirementService"; +import { OfficerProfileService } from "../services/OfficerProfileService"; import { promisify } from "util"; const REDIS_HOST = process.env.REDIS_HOST; const REDIS_PORT = process.env.REDIS_PORT; @@ -6661,6 +6662,10 @@ export class CommandController extends Controller { return new HttpSuccess(); } + /** + * API สร้างทะเบียนประวัติข้าราชการ หลังออกคำสั่งบรรจุสอบ หรือ รับโอน + * @summary API สร้างทะเบียนประวัติข้าราชการ หลังออกคำสั่งบรรจุสอบ หรือ รับโอน + */ @Post("excexute/create-officer-profile") public async CreateOfficeProfileExcecute( @Request() req: RequestWithUser, @@ -6707,953 +6712,10 @@ export class CommandController extends Controller { }[]; }, ) { - console.log("[Excexute/CreateOfficerProfile] Starting CreateOfficeProfileExcecute"); - console.log("[Excexute/CreateOfficerProfile] Request body count:", body.data?.length); - const roleKeycloak = await this.roleKeycloakRepo.findOne({ - where: { name: Like("USER") }, + await new OfficerProfileService().executeCreateOfficerProfile(body.data, { + user: { sub: req.user.sub, name: req.user.name }, + req, }); - console.log("[Excexute/CreateOfficerProfile] roleKeycloak found:", !!roleKeycloak); - const list = await getRoles(); - console.log("[Excexute/CreateOfficerProfile] Roles list retrieved, length:", Array.isArray(list) ? list.length : "not array"); - if (!Array.isArray(list)) { - console.error("[Excexute/CreateOfficerProfile] Failed - Cannot get role(s) data from the server"); - throw new Error("Failed. Cannot get role(s) data from the server."); - } - let _posNumCodeSit: string = ""; - let _posNumCodeSitAbb: string = ""; - console.log("[Excexute/CreateOfficerProfile] Getting command data"); - const _command = await this.commandRepository.findOne({ - where: { id: body.data.find((x) => x.bodySalarys?.commandId)?.bodySalarys?.commandId ?? "" }, - }); - console.log("[Excexute/CreateOfficerProfile] Command found:", !!_command, "isBangkok:", _command?.isBangkok); - if (_command) { - if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { - console.log("[Excexute/CreateOfficerProfile] Setting position codes for OFFICE"); - const orgRootDeputy = await this.orgRootRepository.findOne({ - where: { - isDeputy: true, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: ["orgRevision"], - }); - _posNumCodeSit = orgRootDeputy ? orgRootDeputy?.orgRootName : "สำนักปลัดกรุงเทพมหานคร"; - _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; - console.log("[Excexute/CreateOfficerProfile] OFFICE position codes set:", _posNumCodeSit, _posNumCodeSitAbb); - } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { - console.log("[Excexute/CreateOfficerProfile] Setting position codes for BANGKOK"); - _posNumCodeSit = "กรุงเทพมหานคร"; - _posNumCodeSitAbb = "กทม."; - console.log("[Excexute/CreateOfficerProfile] BANGKOK position codes set:", _posNumCodeSit, _posNumCodeSitAbb); - } else { - console.log("[Excexute/CreateOfficerProfile] Setting position codes from admin profile"); - let _profileAdmin = await this.profileRepository.findOne({ - where: { - keycloak: _command?.createdUserId.toString(), - current_holders: { - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - }, - relations: ["current_holders", "current_holders.orgRevision", "current_holders.orgRoot"], - }); - _posNumCodeSit = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot.orgRootName ?? - ""; - _posNumCodeSitAbb = - _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot - .orgRootShortName ?? ""; - console.log("[Excexute/CreateOfficerProfile] Admin profile position codes set:", _posNumCodeSit, _posNumCodeSitAbb); - } - } - const before = null; - const meta = { - createdUserId: req.user.sub, - createdFullName: req.user.name, - lastUpdateUserId: req.user.sub, - lastUpdateFullName: req.user.name, - createdAt: new Date(), - lastUpdatedAt: new Date(), - }; - console.log("[Excexute/CreateOfficerProfile] Starting to process", body.data.length, "profile(s)"); - await Promise.all( - body.data.map(async (item, index) => { - console.log("[Excexute/CreateOfficerProfile] Processing item", index + 1, "of", body.data.length); - const _null: any = null; - if (item.bodyProfile.posLevelId === "") item.bodyProfile.posLevelId = null; - if (item.bodyProfile.posTypeId === "") item.bodyProfile.posTypeId = null; - if ( - item.bodyProfile.posLevelId && - !(await this.posLevelRepo.findOneBy({ id: item.bodyProfile.posLevelId })) - ) { - console.error("[Excexute/CreateOfficerProfile] ไม่พบข้อมูลระดับตำแหน่งนี้ posLevelId:", item.bodyProfile.posLevelId); - throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลระดับตำแหน่งนี้"); - } - if ( - item.bodyProfile.posTypeId && - !(await this.posTypeRepo.findOneBy({ id: item.bodyProfile.posTypeId })) - ) { - console.error("[Excexute/CreateOfficerProfile] ไม่พบข้อมูลประเภทตำแหน่งนี้ posTypeId:", item.bodyProfile.posTypeId); - throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลประเภทตำแหน่งนี้"); - } - - console.log("[Excexute/CreateOfficerProfile] Processing citizenId:", item.bodyProfile.citizenId); - let registrationProvinceId = await this.provinceRepo.findOneBy({ - id: item.bodyProfile.registrationProvinceId ?? "", - }); - let registrationDistrictId = await this.districtRepo.findOneBy({ - id: item.bodyProfile.registrationDistrictId ?? "", - }); - let registrationSubDistrictId = await this.subDistrictRepo.findOneBy({ - id: item.bodyProfile.registrationSubDistrictId ?? "", - }); - let currentProvinceId = await this.provinceRepo.findOneBy({ - id: item.bodyProfile.currentProvinceId ?? "", - }); - let currentDistrictId = await this.districtRepo.findOneBy({ - id: item.bodyProfile.currentDistrictId ?? "", - }); - let currentSubDistrictId = await this.subDistrictRepo.findOneBy({ - id: item.bodyProfile.currentSubDistrictId ?? "", - }); - console.log("[Excexute/CreateOfficerProfile] Address validation completed"); - - let _dateRetire = - item.bodyProfile.birthDate == null - ? _null - : calculateRetireDate(item.bodyProfile.birthDate); - let _dateRetireLaw = - item.bodyProfile.birthDate == null - ? _null - : calculateRetireLaw(item.bodyProfile.birthDate); - - let userKeycloakId: any; - let result: any; - console.log("[Excexute/CreateOfficerProfile] Checking Keycloak user for citizenId:", item.bodyProfile.citizenId); - const checkUser = await getUserByUsername(item.bodyProfile.citizenId); - console.log("[Excexute/CreateOfficerProfile] Keycloak user exists:", checkUser.length > 0); - if (checkUser.length == 0) { - console.log("[Excexute/CreateOfficerProfile] Creating new Keycloak user"); - let password = item.bodyProfile.citizenId; - if (item.bodyProfile.birthDate != null) { - const _date = new Date(item.bodyProfile.birthDate.toDateString()) - .getDate() - .toString() - .padStart(2, "0"); - const _month = (new Date(item.bodyProfile.birthDate.toDateString()).getMonth() + 1) - .toString() - .padStart(2, "0"); - const _year = new Date(item.bodyProfile.birthDate.toDateString()).getFullYear() + 543; - password = `${_date}${_month}${_year}`; - } - console.log("[Excexute/CreateOfficerProfile] Calling createUser for:", item.bodyProfile.citizenId); - console.log("[Excexute/CreateOfficerProfile] createUser data - firstName:", item.bodyProfile.firstName, "lastName:", item.bodyProfile.lastName); - // กรอง "." ออกจาก firstName ก่อนส่งไป keycloak (ป้องกัน . หรืออักขระอื่นๆ) - const sanitizedFirstName = item.bodyProfile.firstName?.replace(/\./g, "") ?? ""; - userKeycloakId = await createUser(item.bodyProfile.citizenId, password, { - firstName: sanitizedFirstName, - lastName: item.bodyProfile.lastName, - }); - if (userKeycloakId && typeof userKeycloakId === "object" && userKeycloakId.errorMessage) { - console.error("[Excexute/CreateOfficerProfile] createUser FAILED - field:", userKeycloakId.field, "errorMessage:", userKeycloakId.errorMessage, "params:", userKeycloakId.params); - throw new HttpError(HttpStatus.BAD_REQUEST, `Keycloak validation failed: ${userKeycloakId.field} - ${userKeycloakId.errorMessage}`); - } - console.log("[Excexute/CreateOfficerProfile] User created in Keycloak, userKeycloakId:", userKeycloakId); - result = await addUserRoles( - userKeycloakId, - list - .filter((v) => v.name === "USER") - .map((x) => ({ - id: x.id, - name: x.name, - })), - ); - console.log("[Excexute/CreateOfficerProfile] USER role assigned to new user, result:", result); - } else { - console.log("[Excexute/CreateOfficerProfile] Updating existing Keycloak user"); - userKeycloakId = checkUser[0].id; - console.log("[Excexute/CreateOfficerProfile] Existing userKeycloakId:", userKeycloakId); - const rolesData = await getRoleMappings(userKeycloakId); - if (rolesData) { - const _delRole = rolesData.map((x: any) => ({ - id: x.id, - name: x.name, - })); - console.log("[Excexute/CreateOfficerProfile] Removing old roles:", _delRole.length); - await removeUserRoles(userKeycloakId, _delRole); - } - result = await addUserRoles( - userKeycloakId, - list - .filter((v) => v.name === "USER") - .map((x) => ({ - id: x.id, - name: x.name, - })), - ); - console.log("[Excexute/CreateOfficerProfile] USER role assigned to existing user"); - } - - let profile: any = await this.profileRepository.findOne({ - where: { citizenId: item.bodyProfile.citizenId /*, isActive: true */ }, - relations: ["roleKeycloaks", "profileInsignias", "profileAvatars"], - }); - console.log("[Excexute/CreateOfficerProfile] Profile found:", !!profile, "for citizenId:", item.bodyProfile.citizenId); - let _oldInsigniaIds: string[] = []; - let _oldSalaries: any[] = []; - //ลูกจ้างประจำ หรือ บุคคลภายนอก - if (!profile) { - console.log("[Excexute/CreateOfficerProfile] No existing profile found, creating new profile"); - //กรณีลูกจ้างประจำมาสอบเป็นข้าราชการ ต้อง update สถานะโปรไฟล์เดิม - let profileEmployee: any = await this.profileEmployeeRepository.findOne({ - where: { citizenId: item.bodyProfile.citizenId }, - relations: ["profileInsignias", "roleKeycloaks"], - }); - console.log("[Excexute/CreateOfficerProfile] Employee profile found:", !!profileEmployee); - if (profileEmployee) { - console.log("[Excexute/CreateOfficerProfile] Converting employee profile to officer profile"); - const _order = await this.salaryRepo.findOne({ - where: { profileEmployeeId: profileEmployee.id }, - order: { order: "DESC" }, - }); - const profileEmpSalary = new ProfileSalary(); - profileEmpSalary.posNumCodeSit = _posNumCodeSit; - profileEmpSalary.posNumCodeSitAbb = _posNumCodeSitAbb; - profileEmpSalary.order = _order == null ? 1 : _order.order + 1; - Object.assign(profileEmpSalary, { - ...item.bodySalarys, - ...meta, - profileEmployeeId: profileEmployee.id, - profileId: undefined, - }); - const history = new ProfileSalaryHistory(); - Object.assign(history, { ...profileEmpSalary, id: undefined }); - profileEmpSalary.dateGovernment = item.bodySalarys?.commandDateAffect ?? meta.createdAt; - (profileEmpSalary.profileId = _null), - await this.salaryRepo.save(profileEmpSalary, { data: req }); - setLogDataDiff(req, { before, after: profileEmpSalary }); - history.profileSalaryId = profileEmpSalary.id; - await this.salaryHistoryRepo.save(history, { data: req }); - - if (profileEmployee.profileInsignias.length > 0) { - _oldInsigniaIds = profileEmployee.profileInsignias?.map((x: any) => x.id) ?? []; - } - await removeProfileInOrganize(profileEmployee.id, "EMPLOYEE"); - if (profileEmployee.keycloak != null) { - // const delUserKeycloak = await deleteUser(profileEmployee.keycloak); - // if (delUserKeycloak) { - // Task #228 - // profileEmployee.keycloak = _null; - profileEmployee.roleKeycloaks = []; - profileEmployee.isActive = false; - // } - } - profileEmployee.isLeave = true; - profileEmployee.leaveReason = "บรรจุข้าราชการ"; - profileEmployee.lastUpdateUserId = req.user.sub; - profileEmployee.lastUpdateFullName = req.user.name; - profileEmployee.lastUpdatedAt = new Date(); - await this.profileEmployeeRepository.save(profileEmployee); - setLogDataDiff(req, { before, after: profileEmployee }); - } - profile = Object.assign({ ...item.bodyProfile, ...meta }); - profile.dateRetire = _dateRetire; - profile.dateRetireLaw = _dateRetireLaw; - profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; - profile.keycloak = - userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; - profile.registrationAddress = item.bodyProfile.registrationAddress; - profile.registrationProvinceId = registrationProvinceId - ? registrationProvinceId.id - : _null; - profile.registrationDistrictId = registrationDistrictId - ? registrationDistrictId.id - : _null; - profile.registrationSubDistrictId = registrationSubDistrictId - ? registrationSubDistrictId.id - : _null; - profile.registrationZipCode = item.bodyProfile.registrationZipCode; - profile.currentAddress = item.bodyProfile.currentAddress; - profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; - profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; - profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; - profile.currentZipCode = item.bodyProfile.currentZipCode; - profile.email = item.bodyProfile.email; - profile.dateStart = item.bodyProfile.dateStart; - profile.amount = item.bodyProfile.amount ?? null; - profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; - profile.isProbation = item.bodyProfile.isProbation; - //เพิ่มใหม่จากรับโอน - profile.rank = item?.bodyProfile?.rank || null; - profile.prefix = item?.bodyProfile?.rank || item?.bodyProfile?.prefix || null; - profile.prefixMain = item?.bodyProfile?.prefix ?? null; - profile.firstName = item.bodyProfile.firstName ?? null; - profile.lastName = item.bodyProfile.lastName ?? null; - profile.birthDate = item.bodyProfile.birthDate ?? null; - profile.gender = item.bodyProfile.gender ?? null; - profile.relationship = item.bodyProfile.relationship ?? null; - profile.religion = item.bodyProfile.religion ?? null; - profile.ethnicity = item.bodyProfile.ethnicity; - profile.nationality = item.bodyProfile.nationality ?? null; - profile.bloodGroup = item.bodyProfile.bloodGroup ?? null; - profile.phone = item.bodyProfile.phone ?? null; - - console.log("[Excexute/CreateOfficerProfile] Saving new profile"); - await this.profileRepository.save(profile); - console.log("[Excexute/CreateOfficerProfile] New profile saved, profileId:", profile.id); - // update user attribute in keycloak - await updateUserAttributes(profile.keycloak ?? "", { - profileId: [profile.id], - prefix: [profile.prefix || ""], - }); - console.log("[Excexute/CreateOfficerProfile] Keycloak attributes updated"); - setLogDataDiff(req, { before, after: profile }); - } - //ขรก.ในระบบ หรือ ขรก.ในระบบที่สถานะพ้นจากราชการ - else { - console.log("[Excexute/CreateOfficerProfile] Existing profile found, isLeave:", profile.isLeave, "leaveType:", profile.leaveType); - //สร้างโปรไฟล์ใหม่ ถ้าสถานะพ้นราชการ คำสั่งโอนออกหรือคำสั่งขอลาออก - if ( - profile.isLeave && - ["PLACEMENT_TRANSFER", "RETIRE_RESIGN"].includes(profile.leaveType) - ) { - console.log("[Excexute/CreateOfficerProfile] Profile is leaving with eligible leave type, creating new profile record"); - //ดึง profileSalary เดิม - _oldSalaries = await this.salaryRepo.find({ - where: { profileId: profile.id }, - order: { order: "ASC" }, - }); - if (profile.profileInsignias.length > 0) { - _oldInsigniaIds = profile.profileInsignias?.map((x: any) => x.id) ?? []; - } - profile = Object.assign({ ...item.bodyProfile, ...meta }); - profile.dateRetire = _dateRetire; - profile.dateRetireLaw = _dateRetireLaw; - profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; - profile.keycloak = - userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; - profile.registrationAddress = item.bodyProfile.registrationAddress; - profile.registrationProvinceId = registrationProvinceId - ? registrationProvinceId.id - : _null; - profile.registrationDistrictId = registrationDistrictId - ? registrationDistrictId.id - : _null; - profile.registrationSubDistrictId = registrationSubDistrictId - ? registrationSubDistrictId.id - : _null; - profile.registrationZipCode = item.bodyProfile.registrationZipCode; - profile.currentAddress = item.bodyProfile.currentAddress; - profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; - profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; - profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; - profile.currentZipCode = item.bodyProfile.currentZipCode; - profile.email = item.bodyProfile.email; - profile.dateStart = item.bodyProfile.dateStart; - profile.amount = item.bodyProfile.amount ?? null; - profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; - profile.isProbation = item.bodyProfile.isProbation; - profile.rank = item?.bodyProfile?.rank || null; - profile.prefix = item?.bodyProfile?.rank || item?.bodyProfile?.prefix || null; - profile.prefixMain = item?.bodyProfile?.prefix ?? null; - profile.firstName = item.bodyProfile.firstName ?? null; - profile.lastName = item.bodyProfile.lastName ?? null; - profile.birthDate = item.bodyProfile.birthDate ?? null; - profile.gender = item.bodyProfile.gender ?? null; - profile.relationship = item.bodyProfile.relationship ?? null; - profile.religion = item.bodyProfile.religion ?? null; - profile.ethnicity = item.bodyProfile.ethnicity; - profile.nationality = item.bodyProfile.nationality ?? null; - profile.bloodGroup = item.bodyProfile.bloodGroup ?? null; - profile.phone = item.bodyProfile.phone ?? null; - await this.profileRepository.save(profile); - console.log("[Excexute/CreateOfficerProfile] New profile record saved for leaving officer, profileId:", profile.id); - setLogDataDiff(req, { before, after: profile }); - } else { - console.log("[Excexute/CreateOfficerProfile] Updating existing active profile"); - profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; - profile.keycloak = - userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; - profile.isProbation = item.bodyProfile.isProbation; - profile.isLeave = item.bodyProfile.isLeave; - profile.isRetirement = false; - profile.isActive = true; - profile.isDelete = false; - profile.dateLeave = _null; - profile.dateRetire = _dateRetire; - profile.dateRetireLaw = _dateRetireLaw; - profile.registrationAddress = item.bodyProfile.registrationAddress; - profile.registrationProvinceId = registrationProvinceId - ? registrationProvinceId.id - : _null; - profile.registrationDistrictId = registrationDistrictId - ? registrationDistrictId.id - : _null; - profile.registrationSubDistrictId = registrationSubDistrictId - ? registrationSubDistrictId.id - : _null; - profile.registrationZipCode = item.bodyProfile.registrationZipCode; - profile.currentAddress = item.bodyProfile.currentAddress; - profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; - profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; - profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; - profile.currentZipCode = item.bodyProfile.currentZipCode; - profile.email = item.bodyProfile.email; - profile.telephoneNumber = item.bodyProfile.telephoneNumber; - profile.phone = item.bodyProfile.phone; - profile.dateStart = item.bodyProfile.dateStart; - profile.amount = item.bodyProfile.amount ?? null; - profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; - profile.leaveCommandId = _null; - profile.leaveCommandNo = _null; - profile.leaveRemark = _null; - profile.leaveDate = _null; - profile.leaveType = _null; - profile.leaveReason = _null; - profile.lastUpdateUserId = req.user.sub; - profile.lastUpdateFullName = req.user.name; - profile.lastUpdatedAt = new Date(); - //เพิ่มใหม่จากรับโอน - profile.rank = item?.bodyProfile?.rank || null; - profile.prefix = item?.bodyProfile?.rank || item?.bodyProfile?.prefix || null; - profile.prefixMain = item?.bodyProfile?.prefix ?? null; - profile.firstName = - item.bodyProfile.firstName && item.bodyProfile.firstName != "" - ? item.bodyProfile.firstName - : profile.firstName; - profile.lastName = - item.bodyProfile.lastName && item.bodyProfile.lastName != "" - ? item.bodyProfile.lastName - : profile.lastName; - profile.birthDate = item.bodyProfile.birthDate - ? item.bodyProfile.birthDate - : profile.birthDate; - profile.gender = - item.bodyProfile.gender && item.bodyProfile.gender != "" - ? item.bodyProfile.gender - : profile.gender; - profile.relationship = - item.bodyProfile.relationship && item.bodyProfile.relationship != "" - ? item.bodyProfile.relationship - : profile.relationship; - profile.religion = - item.bodyProfile.religion && item.bodyProfile.religion != "" - ? item.bodyProfile.religion - : profile.religion; - profile.ethnicity = - item.bodyProfile.ethnicity && item.bodyProfile.ethnicity != "" - ? item.bodyProfile.ethnicity - : profile.ethnicity; - profile.nationality = - item.bodyProfile.nationality && item.bodyProfile.nationality != "" - ? item.bodyProfile.nationality - : profile.nationality; - profile.bloodGroup = - item.bodyProfile.bloodGroup && item.bodyProfile.bloodGroup != "" - ? item.bodyProfile.bloodGroup - : profile.bloodGroup; - profile.phone = - item.bodyProfile.phone && item.bodyProfile.phone != "" - ? item.bodyProfile.phone - : profile.phone; - await this.profileRepository.save(profile); - console.log("[Excexute/CreateOfficerProfile] Existing active profile updated, profileId:", profile.id); - setLogDataDiff(req, { before, after: profile }); - } - } - - if (profile && profile.id) { - console.log("[Excexute/CreateOfficerProfile] Processing additional data for profileId:", profile.id); - //Educations - if (item.bodyEducations && item.bodyEducations.length > 0) { - console.log("[Excexute/CreateOfficerProfile] Processing educations, count:", item.bodyEducations.length); - await Promise.all( - item.bodyEducations.map(async (education) => { - const profileEdu = new ProfileEducation(); - Object.assign(profileEdu, { ...education, ...meta }); - const eduHistory = new ProfileEducationHistory(); - Object.assign(eduHistory, { ...profileEdu, id: undefined }); - profileEdu.profileId = profile.id; - const educationLevel = await this.profileEducationRepo.findOne({ - select: ["id", "level", "profileId"], - where: { profileId: profile.id, isDeleted: false }, - order: { level: "DESC" }, - }); - profileEdu.level = educationLevel == null ? 1 : educationLevel.level + 1; - await this.profileEducationRepo.save(profileEdu, { data: req }); - setLogDataDiff(req, { before, after: profileEdu }); - eduHistory.profileEducationId = profileEdu.id; - await this.profileEducationHistoryRepo.save(eduHistory, { data: req }); - }), - ); - } - //Certificates - if (item.bodyCertificates && item.bodyCertificates.length > 0) { - console.log("[Excexute/CreateOfficerProfile] Processing certificates, count:", item.bodyCertificates.length); - await Promise.all( - item.bodyCertificates.map(async (cer) => { - const profileCer = new ProfileCertificate(); - Object.assign(profileCer, { ...cer, ...meta }); - const cerHistory = new ProfileCertificateHistory(); - Object.assign(cerHistory, { ...profileCer, id: undefined }); - profileCer.profileId = profile.id; - await this.certificateRepo.save(profileCer, { data: req }); - setLogDataDiff(req, { before, after: profileCer }); - cerHistory.profileCertificateId = profileCer.id; - await this.certificateHistoryRepo.save(cerHistory, { data: req }); - }), - ); - } - //FamilyCouple - if (item.bodyMarry != null) { - console.log("[Excexute/CreateOfficerProfile] Processing couple/marry data"); - const profileCouple = new ProfileFamilyCouple(); - const data = { - profileId: profile.id, - couple: item.bodyMarry.marry, - couplePrefix: item.bodyMarry.marryPrefix, - coupleFirstName: item.bodyMarry.marryFirstName, - coupleLastName: item.bodyMarry.marryLastName, - coupleCareer: item.bodyMarry.marryOccupation, - coupleLive: true, - }; - Object.assign(profileCouple, { ...data, ...meta }); - const coupleHistory = new ProfileFamilyCoupleHistory(); - Object.assign(coupleHistory, { ...profileCouple, id: undefined }); - profileCouple.profileId = profile.id; - await this.profileFamilyCoupleRepo.save(profileCouple, { data: req }); - setLogDataDiff(req, { before, after: profileCouple }); - coupleHistory.profileFamilyCoupleId = profileCouple.id; - await this.profileFamilyCoupleHistoryRepo.save(coupleHistory, { data: req }); - } - //FamilyFather - if (item.bodyFather != null) { - console.log("[Excexute/CreateOfficerProfile] Processing father data"); - const profileFather = new ProfileFamilyFather(); - const data = { - profileId: profile.id, - fatherPrefix: item.bodyFather.fatherPrefix, - fatherFirstName: item.bodyFather.fatherFirstName, - fatherLastName: item.bodyFather.fatherLastName, - fatherCareer: item.bodyFather.fatherOccupation, - fatherLive: true, - }; - Object.assign(profileFather, { ...data, ...meta }); - const fatherHistory = new ProfileFamilyFatherHistory(); - Object.assign(fatherHistory, { ...profileFather, id: undefined }); - profileFather.profileId = profile.id; - await this.profileFamilyFatherRepo.save(profileFather, { data: req }); - setLogDataDiff(req, { before, after: profileFather }); - fatherHistory.profileFamilyFatherId = profileFather.id; - await this.profileFamilyFatherHistoryRepo.save(fatherHistory, { data: req }); - } - //FamilyMother - if (item.bodyMother != null) { - console.log("[Excexute/CreateOfficerProfile] Processing mother data"); - const profileMother = new ProfileFamilyMother(); - const data = { - profileId: profile.id, - motherPrefix: item.bodyMother.motherPrefix, - motherFirstName: item.bodyMother.motherFirstName, - motherLastName: item.bodyMother.motherLastName, - motherCareer: item.bodyMother.motherOccupation, - motherLive: true, - }; - Object.assign(profileMother, { ...data, ...meta }); - const motherHistory = new ProfileFamilyMotherHistory(); - Object.assign(motherHistory, { ...profileMother, id: undefined }); - profileMother.profileId = profile.id; - await this.profileFamilyMotherRepo.save(profileMother, { data: req }); - setLogDataDiff(req, { before, after: profileMother }); - motherHistory.profileFamilyMotherId = profileMother.id; - await this.profileFamilyMotherHistoryRepo.save(motherHistory, { data: req }); - } - //Salary - //insert profileSalary อันเก่า กรณีพ้นราชการแล้วกลับมาบรรจุ - if (_oldSalaries.length > 0) { - console.log("[Excexute/CreateOfficerProfile] Restoring old salaries, count:", _oldSalaries.length); - await Promise.all( - _oldSalaries.map(async (oldSal) => { - const profileSal: any = new ProfileSalary(); - Object.assign(profileSal, { ...oldSal, ...meta }); - const salaryHistory = new ProfileSalaryHistory(); - Object.assign(salaryHistory, { ...profileSal, id: undefined }); - profileSal.profileId = profile.id; - await this.salaryRepo.save(profileSal, { data: req }); - setLogDataDiff(req, { before, after: profileSal }); - salaryHistory.profileSalaryId = profileSal.id; - await this.salaryHistoryRepo.save(salaryHistory, { data: req }); - }), - ); - } - //insert item.bodySalarys ต่อจากที่ insert เดิมไปแล้ว - if (item.bodySalarys && item.bodySalarys != null) { - console.log("[Excexute/CreateOfficerProfile] Processing new salary data"); - const dest_item = await this.salaryRepo.findOne({ - where: { profileId: profile.id }, - order: { order: "DESC" }, - }); - const profileSal: any = new ProfileSalary(); - profileSal.posNumCodeSit = _posNumCodeSit; - profileSal.posNumCodeSitAbb = _posNumCodeSitAbb; - Object.assign(profileSal, { ...item.bodySalarys, ...meta }); - const salaryHistory = new ProfileSalaryHistory(); - Object.assign(salaryHistory, { ...profileSal, id: undefined }); - profileSal.order = dest_item == null ? 1 : dest_item.order + 1; - profileSal.profileId = profile.id; - profileSal.dateGovernment = item.bodySalarys.commandDateAffect ?? meta.createdAt; - profileSal.amount = item.bodySalarys.amount ?? null; - profileSal.amountSpecial = item.bodySalarys.amountSpecial ?? null; - profileSal.positionSalaryAmount = item.bodySalarys.positionSalaryAmount ?? null; - profileSal.mouthSalaryAmount = item.bodySalarys.mouthSalaryAmount ?? null; - await this.salaryRepo.save(profileSal, { data: req }); - setLogDataDiff(req, { before, after: profileSal }); - salaryHistory.profileSalaryId = profileSal.id; - await this.salaryHistoryRepo.save(salaryHistory, { data: req }); - } - //Position - if (item.bodyPosition && item.bodyPosition != null) { - console.log("[Excexute/CreateOfficerProfile] Processing position assignment"); - // STEP 1: หา posMaster ที่จะใช้งานตาม id ที่ส่งมา (อาจเป็นตำแหน่งเก่าหรือใหม่ก็ได้) - console.log("[Excexute/CreateOfficerProfile] STEP 1: Finding posMaster, posmasterId:", item.bodyPosition.posmasterId); - let posMaster = await this.posMasterRepository.findOne({ - where: { - id: item.bodyPosition.posmasterId, - }, - relations: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - console.log("[Excexute/CreateOfficerProfile] posMaster found:", !!posMaster); - - // เช็คว่า posMaster ที่หามาอยู่ในโครงสร้างปัจจุบันหรือไม่ - const isCurrent = - posMaster?.orgRevision?.orgRevisionIsCurrent === true && - posMaster?.orgRevision?.orgRevisionIsDraft === false; - console.log("[Excexute/CreateOfficerProfile] posMaster isCurrent:", isCurrent); - - // ถ้าไม่อยู่ในโครงสร้างปัจจุบัน ให้หาตัวใหม่จาก ancestorDNA - if (!isCurrent && posMaster?.ancestorDNA) { - console.log("[Excexute/CreateOfficerProfile] Finding current posMaster from ancestorDNA"); - posMaster = await this.posMasterRepository.findOne({ - where: { - ancestorDNA: posMaster.ancestorDNA, - orgRevision: { - orgRevisionIsCurrent: true, - orgRevisionIsDraft: false, - }, - }, - relations: { - orgRevision: true, - orgRoot: true, - orgChild1: true, - orgChild2: true, - orgChild3: true, - orgChild4: true, - }, - }); - console.log("[Excexute/CreateOfficerProfile] Current posMaster from ancestorDNA found:", !!posMaster); - } - - if (posMaster == null) { - console.error( - `[Excexute/CreateOfficerProfile] not found posMasterId: ${item.bodyPosition.posmasterId}` - ); - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); - } - - // STEP 2: เคลียร์ข้อมูลตำแหน่งเก่าที่ครองอยู่ ในโครงสร้างปัจจุบัน - console.log("[Excexute/CreateOfficerProfile] STEP 2: Clearing old position data"); - const posMasterOld = await this.posMasterRepository.findOne({ - where: { - current_holderId: profile.id, - orgRevisionId: posMaster.orgRevisionId, - }, - }); - if (posMasterOld != null) { - // เคลียร์คนครองเก่าออกจากตำแหน่งเดิม - posMasterOld.current_holderId = null; - posMasterOld.lastUpdatedAt = new Date(); - } - - // หา position เก่าที่เลือกไว้ แล้วเคลียร์การเลือก - const positionOld = await this.positionRepository.findOne({ - where: { - posMasterId: posMasterOld?.id, - positionIsSelected: true, - }, - }); - if (positionOld != null) { - positionOld.positionIsSelected = false; - await this.positionRepository.save(positionOld); - } - - // STEP 3: เคลียร์ position ที่เลือกไว้อื่นๆ ใน posMaster ตัวใหม่ - console.log("[Excexute/CreateOfficerProfile] STEP 3: Clearing other selected positions in new posMaster"); - const checkPosition = await this.positionRepository.find({ - where: { - posMasterId: posMaster.id, - positionIsSelected: true, - }, - }); - if (checkPosition.length > 0) { - const clearPosition = checkPosition.map((positions) => ({ - ...positions, - positionIsSelected: false, - })); - await this.positionRepository.save(clearPosition); - } - - // STEP 4: กำหนดคนครองใหม่ให้กับ posMaster - console.log("[Excexute/CreateOfficerProfile] STEP 4: Assigning new holder to posMaster"); - posMaster.current_holderId = profile.id; - posMaster.lastUpdatedAt = new Date(); - // posMaster.conditionReason = _null; - // posMaster.isCondition = false; - if (posMasterOld != null) { - await this.posMasterRepository.save(posMasterOld); - await CreatePosMasterHistoryOfficer(posMasterOld.id, req); - } - await this.posMasterRepository.save(posMaster); - console.log("[Excexute/CreateOfficerProfile] posMaster saved with new holder"); - - // STEP 5: กำหนด position ใหม่ - console.log("[Excexute/CreateOfficerProfile] STEP 5: Determining position to assign"); - // Match position ตามลำดับ priority: - // Condition 1: match จาก positionId - // Condition 2: match 7 ฟิลด์ (positionName, posTypeId, posLevelId, positionField, positionArea, positionExecutiveField, posExecutiveId) - // Condition 3: match 3 ฟิลด์ (positionName, posTypeId, posLevelId) - // Fallback: เลือก position แรกใน posMaster - - let positionNew: Position | null = null; - - // ═══════════════════════════════════════════════════════════ - // CONDITION 1: เช็คจาก positionId ตรง - // ═══════════════════════════════════════════════════════════ - console.log("[Excexute/CreateOfficerProfile] CONDITION 1: Checking by positionId:", item.bodyPosition?.positionId); - if (item.bodyPosition?.positionId) { - const positionById = await this.positionRepository.findOne({ - where: { - id: item.bodyPosition.positionId, - posMasterId: posMaster.id, // ต้องอยู่ใน posMaster ที่ถูกต้อง - }, - relations: ["posExecutive"], - }); - - if (positionById) { - positionNew = positionById; - console.log("[Excexute/CreateOfficerProfile] CONDITION 1 matched, positionId:", positionById.id); - } - } - - // ═══════════════════════════════════════════════════════════ - // CONDITION 2: Match 7 ฟิลด์ (ถ้า Condition 1 ไม่ match) - // ═══════════════════════════════════════════════════════════ - if (!positionNew && item.bodyPosition) { - console.log("[Excexute/CreateOfficerProfile] CONDITION 1 not matched, trying CONDITION 2: Match 7 fields"); - // สร้าง where clause แบบ dynamic - ใส่เฉพาะฟิลด์ที่ไม่ใช่ null - const whereCondition: any = { - posMasterId: posMaster.id, - positionName: item.bodyPosition.positionName, - posTypeId: item.bodyPosition.posTypeId, - posLevelId: item.bodyPosition.posLevelId, - }; - - if (item.bodyPosition.positionField) { - whereCondition.positionField = item.bodyPosition.positionField; - } - if (item.bodyPosition.posExecutiveId) { - whereCondition.posExecutiveId = item.bodyPosition.posExecutiveId; - } - if (item.bodyPosition.positionExecutiveField) { - whereCondition.positionExecutiveField = item.bodyPosition.positionExecutiveField; - } - if (item.bodyPosition.positionArea) { - whereCondition.positionArea = item.bodyPosition.positionArea; - } - - const positionBy7Fields = await this.positionRepository.findOne({ - where: whereCondition, - relations: ["posExecutive"], - order: { orderNo: "ASC" } - }); - - if (positionBy7Fields) { - positionNew = positionBy7Fields; - console.log("[Excexute/CreateOfficerProfile] CONDITION 2 matched with 7 fields, positionId:", positionBy7Fields.id); - } - } - - // ═══════════════════════════════════════════════════════════ - // CONDITION 3: Match 3 ฟิลด์ (ถ้า Condition 2 ไม่ match) - // ═══════════════════════════════════════════════════════════ - if (!positionNew && item.bodyPosition) { - console.log("[Excexute/CreateOfficerProfile] CONDITION 2 not matched, trying CONDITION 3: Match 3 fields"); - const positionBy3Fields = await this.positionRepository.findOne({ - where: { - posMasterId: posMaster.id, - positionName: item.bodyPosition.positionName, - posTypeId: item.bodyPosition.posTypeId, - posLevelId: item.bodyPosition.posLevelId, - }, - relations: ["posExecutive"], - order: { orderNo: "ASC" } - }); - - if (positionBy3Fields) { - positionNew = positionBy3Fields; - console.log("[Excexute/CreateOfficerProfile] CONDITION 3 matched with 3 fields, positionId:", positionBy3Fields.id); - } else { - console.log("[Excexute/CreateOfficerProfile] No position matched for profileId:", profile.id); - } - } - - // // ═══════════════════════════════════════════════════════════ - // // FALLBACK: ถ้าทั้ง 3 ไม่ match ให้เลือก position แรกใน posMaster - // // ═══════════════════════════════════════════════════════════ - // if (!positionNew) { - // const fallbackPositions = await this.positionRepository.find({ - // where: { - // posMasterId: posMaster.id, - // }, - // relations: ["posExecutive"], - // order: { - // orderNo: "ASC", - // }, - // take: 1, - // }); - - // if (fallbackPositions.length > 0) { - // positionNew = fallbackPositions[0]; - // } - // } - - // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit - profile.posMasterNo = getPosMasterNo(posMaster); - profile.org = getOrgFullName(posMaster); - // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ - if (positionNew != null) { - console.log("[Excexute/CreateOfficerProfile] Final position assignment, isSit:", posMaster.isSit, "positionId:", positionNew.id); - positionNew.positionIsSelected = true; - if (!posMaster.isSit) { - profile.posLevelId = positionNew.posLevelId; - profile.posTypeId = positionNew.posTypeId; - profile.position = positionNew.positionName; - profile.positionField = positionNew.positionField ?? null; - profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; - profile.positionArea = positionNew.positionArea ?? null; - profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; - // profile.dateStart = new Date(); - } - await this.positionRepository.save(positionNew, { data: req }); - } else if (!posMaster.isSit) { - // fallback: ตำแหน่งในโครงสร้างถูกแก้ไข ใช้ข้อมูลตำแหน่งที่สมัครสอบมา - console.log("[Excexute/CreateOfficerProfile] positionNew is null, using bodyPosition data as fallback"); - profile.position = item.bodyPosition.positionName ?? null; - profile.posTypeId = item.bodyPosition.posTypeId ?? null; - profile.posLevelId = item.bodyPosition.posLevelId ?? null; - profile.positionField = item.bodyPosition.positionField ?? null; - profile.positionArea = item.bodyPosition.positionArea ?? null; - profile.positionExecutiveField = item.bodyPosition.positionExecutiveField ?? null; - } - await this.profileRepository.save(profile, { data: req }); - setLogDataDiff(req, { before, after: profile }); - // await CreatePosMasterHistoryOfficer(posMaster.id, req); - await CreatePosMasterHistoryOfficer(posMaster.id, req, null, { - positionId: positionNew?.id, - }); - } - // Insignia - if (_oldInsigniaIds.length > 0) { - console.log("[Excexute/CreateOfficerProfile] Processing old insignias, count:", _oldInsigniaIds.length); - const _insignias = await this.insigniaRepo.find({ - where: { id: In(_oldInsigniaIds), isDeleted: false }, - order: { createdAt: "ASC" }, - }); - for (const oldInsignia of _insignias) { - const newInsigniaData: CreateProfileInsignia = { - profileId: profile.id, - year: oldInsignia.year, - no: oldInsignia.no, - volume: oldInsignia.volume, - section: oldInsignia.section, - page: oldInsignia.page, - receiveDate: oldInsignia.receiveDate, - insigniaId: oldInsignia.insigniaId, - dateAnnounce: oldInsignia.dateAnnounce, - issue: oldInsignia.issue, - volumeNo: oldInsignia.volumeNo, - refCommandDate: oldInsignia.refCommandDate, - refCommandNo: oldInsignia.refCommandNo, - note: oldInsignia.note, - isUpload: oldInsignia.isUpload, - }; - const insignia = new ProfileInsignia(); - Object.assign(insignia, { ...newInsigniaData, ...meta }); - const history = new ProfileInsigniaHistory(); - Object.assign(history, { ...insignia, id: undefined }); - await this.insigniaRepo.save(insignia, { data: req }); - setLogDataDiff(req, { before, after: insignia }); - history.profileInsigniaId = insignia.id; - await this.insigniaHistoryRepo.save(history, { data: req }); - } - } - // เพิ่มรูปภาพโปรไฟล์ - if (item.bodyProfile.objectRefId) { - console.log("[Excexute/CreateOfficerProfile] Processing profile avatar image, objectRefId:", item.bodyProfile.objectRefId); - const _profileAvatar = new ProfileAvatar(); - Object.assign(_profileAvatar, { - ...meta, - profileId: profile.id, - profileEmployeeId: undefined, - }); - if (profile.profileAvatars && profile.profileAvatars.length > 0) { - await Promise.all( - profile.profileAvatars.map(async (item: any) => { - item.isActive = false; - await this.avatarRepository.save(item); - }), - ); - } - await this.avatarRepository.save(_profileAvatar); - let avatar = `ทะเบียนประวัติ/โปรไฟล์/${profile.id}`; - let fileName = `profile-${_profileAvatar.id}`; - _profileAvatar.isActive = true; - _profileAvatar.avatar = avatar; - _profileAvatar.avatarName = fileName; - await this.avatarRepository.save(_profileAvatar, { data: req }); - profile.avatar = avatar; - profile.avatarName = fileName; - await this.profileRepository.save(profile, { data: req }); - const checkAvatar = await this.avatarRepository.findOne({ - where: { avatar: avatar, avatarName: fileName }, - }); - if (checkAvatar && checkAvatar.profileId == null) { - checkAvatar.profileId = profile.id; - await this.avatarRepository.save(checkAvatar); - } - //duplicate รูปภาพโปรไฟล์โดยอิงจากรูปภาพเดิม - await new CallAPI() - .PostData(req, `/salary/file/avatar/${item.bodyProfile.objectRefId}`, { - prefix: avatar, - fileName: fileName, - }) - .then(() => {}) - .catch(() => {}); - } - } - }), - ); - console.log("[Excexute/CreateOfficerProfile] CreateOfficeProfileExcecute completed successfully"); return new HttpSuccess(); } diff --git a/src/services/OfficerProfileService.ts b/src/services/OfficerProfileService.ts new file mode 100644 index 00000000..fae024d3 --- /dev/null +++ b/src/services/OfficerProfileService.ts @@ -0,0 +1,1310 @@ +import { In, Like } from "typeorm"; +import { AppDataSource } from "../database/data-source"; +import HttpError from "../interfaces/http-error"; +import HttpStatusCode from "../interfaces/http-status"; +import { CreateProfileAllFields, Profile } from "../entities/Profile"; +import { ProfileEmployee } from "../entities/ProfileEmployee"; +import { CreateProfileSalary, ProfileSalary } from "../entities/ProfileSalary"; +import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; +import { CreateProfileEducation, ProfileEducation } from "../entities/ProfileEducation"; +import { ProfileEducationHistory } from "../entities/ProfileEducationHistory"; +import { + CreateProfileCertificate, + ProfileCertificate, +} from "../entities/ProfileCertificate"; +import { ProfileCertificateHistory } from "../entities/ProfileCertificateHistory"; +import { ProfileFamilyCouple } from "../entities/ProfileFamilyCouple"; +import { ProfileFamilyCoupleHistory } from "../entities/ProfileFamilyCoupleHistory"; +import { ProfileFamilyFather } from "../entities/ProfileFamilyFather"; +import { ProfileFamilyFatherHistory } from "../entities/ProfileFamilyFatherHistory"; +import { ProfileFamilyMother } from "../entities/ProfileFamilyMother"; +import { ProfileFamilyMotherHistory } from "../entities/ProfileFamilyMotherHistory"; +import { CreateProfileInsignia, ProfileInsignia } from "../entities/ProfileInsignia"; +import { ProfileInsigniaHistory } from "../entities/ProfileInsigniaHistory"; +import { ProfileAvatar } from "../entities/ProfileAvatar"; +import { PosLevel } from "../entities/PosLevel"; +import { PosType } from "../entities/PosType"; +import { Province } from "../entities/Province"; +import { District } from "../entities/District"; +import { SubDistrict } from "../entities/SubDistrict"; +import { OrgRoot } from "../entities/OrgRoot"; +import { RoleKeycloak } from "../entities/RoleKeycloak"; +import { PosMaster } from "../entities/PosMaster"; +import { Position } from "../entities/Position"; +import { Command } from "../entities/Command"; +import { + calculateRetireDate, + calculateRetireLaw, + removeProfileInOrganize, + setLogDataDiff, +} from "../interfaces/utils"; +import { + addUserRoles, + createUser, + getRoleMappings, + getRoles, + getUserByUsername, + removeUserRoles, + updateUserAttributes, +} from "../keycloak"; +import { CreatePosMasterHistoryOfficer } from "./PositionService"; +import { getOrgFullName, getPosMasterNo } from "../utils/org-formatting"; +import CallAPI from "../interfaces/call-api"; + +/** + * Input: ตำแหน่งที่จะกำหนดให้กับ profile ใหม่ (ส่งมาจากฝั่งบรรจุ) + */ +export interface OfficerPositionInput { + posmasterId: string; + positionId: string; + positionName: string; + posTypeId: string; + posLevelId: string; + posExecutiveId: string | null; + positionField: string | null; + positionExecutiveField: string | null; + positionArea: string | null; +} + +/** + * Input: ข้อมูลคู่สมรส + */ +export interface OfficerMarryInput { + marry?: boolean | null; + marryPrefix?: string | null; + marryFirstName?: string | null; + marryLastName?: string | null; + marryOccupation?: string | null; + marryNationality?: string | null; +} + +/** + * Input: ข้อมูลบิดา + */ +export interface OfficerFatherInput { + fatherPrefix?: string | null; + fatherFirstName?: string | null; + fatherLastName?: string | null; + fatherOccupation?: string | null; + fatherNationality?: string | null; +} + +/** + * Input: ข้อมูลมารดา + */ +export interface OfficerMotherInput { + motherPrefix?: string | null; + motherFirstName?: string | null; + motherLastName?: string | null; + motherOccupation?: string | null; + motherNationality?: string | null; +} + +/** + * Input: ข้อมูล 1 คนที่จะบรรจุ/แต่งตั้ง (ตรงกับ body.data[i] ของ endpoint เดิม) + */ +export interface OfficerProfileItem { + bodyProfile: CreateProfileAllFields; + bodyEducations?: CreateProfileEducation[]; + bodyCertificates?: CreateProfileCertificate[]; + bodySalarys?: CreateProfileSalary | null; + bodyPosition?: OfficerPositionInput | null; + bodyMarry?: OfficerMarryInput | null; + bodyFather?: OfficerFatherInput | null; + bodyMother?: OfficerMotherInput | null; +} + +/** + * Context สำหรับ audit/log — แยกขาดจาก HTTP request เพื่อให้ service + * สามารถถูกเรียกได้ทั้งจาก endpoint (ผ่าน Express req) และจาก message consumer + * (ผ่าน pseudo-req ที่สร้างขึ้น) โดยไม่ผูกติดกับ HTTP layer + */ +export interface ExecutionContext { + /** user ที่ทริกเกอร์งานนี้ (sub = keycloak id, name = ชื่อเต็ม) */ + user: { sub: string; name: string }; + /** Express request (สำหรับ subscriber pattern: setLogDataDiff, save({data: req})) */ + req?: any; +} + +/** + * Service สำหรับสร้าง/อัปเดตทะเบียนประวัติข้าราชการ (Profile) หลังออกคำสั่งบรรจุ + * + * ถูกออกแบบมาเพื่อแก้ปัญหา "Circular Dependency" ระหว่าง API Org กับ API บรรจุ + * โดยให้ฝั่งบรรจุส่ง resultData กลับมา แล้วฝั่ง Org ประมวลผลสร้าง profile เอง + * ที่ต้นทาง (Linear Flow) แทนการเรียกซ้อนกันกลับไปมา + * + * - endpoint /org/command/excexute/create-officer-profile เรียกผ่าน service นี้ (thin wrapper) + * - consumer ใน rabbitmq handler เรียกผ่าน service นี้โดยตรง (no HTTP loopback) + * + * Behavior ทั้งหมด preserve จาก CommandController.CreateOfficeProfileExcecute ต้นฉบับ + */ +export class OfficerProfileService { + private commandRepository = AppDataSource.getRepository(Command); + private profileRepository = AppDataSource.getRepository(Profile); + private profileEmployeeRepository = AppDataSource.getRepository(ProfileEmployee); + private salaryRepo = AppDataSource.getRepository(ProfileSalary); + private salaryHistoryRepo = AppDataSource.getRepository(ProfileSalaryHistory); + private posLevelRepo = AppDataSource.getRepository(PosLevel); + private posTypeRepo = AppDataSource.getRepository(PosType); + private provinceRepo = AppDataSource.getRepository(Province); + private districtRepo = AppDataSource.getRepository(District); + private subDistrictRepo = AppDataSource.getRepository(SubDistrict); + private orgRootRepository = AppDataSource.getRepository(OrgRoot); + private roleKeycloakRepo = AppDataSource.getRepository(RoleKeycloak); + private posMasterRepository = AppDataSource.getRepository(PosMaster); + private positionRepository = AppDataSource.getRepository(Position); + private profileEducationRepo = AppDataSource.getRepository(ProfileEducation); + private profileEducationHistoryRepo = AppDataSource.getRepository(ProfileEducationHistory); + private certificateRepo = AppDataSource.getRepository(ProfileCertificate); + private certificateHistoryRepo = AppDataSource.getRepository(ProfileCertificateHistory); + private profileFamilyCoupleRepo = AppDataSource.getRepository(ProfileFamilyCouple); + private profileFamilyCoupleHistoryRepo = AppDataSource.getRepository(ProfileFamilyCoupleHistory); + private profileFamilyFatherRepo = AppDataSource.getRepository(ProfileFamilyFather); + private profileFamilyFatherHistoryRepo = AppDataSource.getRepository(ProfileFamilyFatherHistory); + private profileFamilyMotherRepo = AppDataSource.getRepository(ProfileFamilyMother); + private profileFamilyMotherHistoryRepo = AppDataSource.getRepository(ProfileFamilyMotherHistory); + private insigniaRepo = AppDataSource.getRepository(ProfileInsignia); + private insigniaHistoryRepo = AppDataSource.getRepository(ProfileInsigniaHistory); + private avatarRepository = AppDataSource.getRepository(ProfileAvatar); + + /** + * ประมวลผลสร้าง/อัปเดท profile ข้าราชการ ตามข้อมูลที่ฝั่งบรรจุส่งมา + * + * @param data รายการข้อมูลที่จะประมวลผล (1 คำสั่งอาจมีหลายคน) + * @param ctx context สำหรับ audit/log + */ + async executeCreateOfficerProfile( + data: OfficerProfileItem[], + ctx: ExecutionContext, + ): Promise { + console.log("[OfficerProfileService] Starting executeCreateOfficerProfile"); + console.log("[OfficerProfileService] Request body count:", data?.length); + + const req = ctx.req; + const roleKeycloak = await this.roleKeycloakRepo.findOne({ + where: { name: Like("USER") }, + }); + console.log("[OfficerProfileService] roleKeycloak found:", !!roleKeycloak); + const list = await getRoles(); + console.log( + "[OfficerProfileService] Roles list retrieved, length:", + Array.isArray(list) ? list.length : "not array", + ); + if (!Array.isArray(list)) { + console.error( + "[OfficerProfileService] Failed - Cannot get role(s) data from the server", + ); + throw new Error("Failed. Cannot get role(s) data from the server."); + } + let _posNumCodeSit: string = ""; + let _posNumCodeSitAbb: string = ""; + console.log("[OfficerProfileService] Getting command data"); + const _command = await this.commandRepository.findOne({ + where: { + id: data.find((x) => x.bodySalarys?.commandId)?.bodySalarys?.commandId ?? "", + }, + }); + console.log( + "[OfficerProfileService] Command found:", + !!_command, + "isBangkok:", + _command?.isBangkok, + ); + if (_command) { + if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { + console.log("[OfficerProfileService] Setting position codes for OFFICE"); + const orgRootDeputy = await this.orgRootRepository.findOne({ + where: { + isDeputy: true, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: ["orgRevision"], + }); + _posNumCodeSit = orgRootDeputy + ? orgRootDeputy?.orgRootName + : "สำนักปลัดกรุงเทพมหานคร"; + _posNumCodeSitAbb = orgRootDeputy ? orgRootDeputy?.orgRootShortName : "สนป."; + console.log( + "[OfficerProfileService] OFFICE position codes set:", + _posNumCodeSit, + _posNumCodeSitAbb, + ); + } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { + console.log("[OfficerProfileService] Setting position codes for BANGKOK"); + _posNumCodeSit = "กรุงเทพมหานคร"; + _posNumCodeSitAbb = "กทม."; + console.log( + "[OfficerProfileService] BANGKOK position codes set:", + _posNumCodeSit, + _posNumCodeSitAbb, + ); + } else { + console.log("[OfficerProfileService] Setting position codes from admin profile"); + let _profileAdmin = await this.profileRepository.findOne({ + where: { + keycloak: _command?.createdUserId.toString(), + current_holders: { + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + }, + relations: [ + "current_holders", + "current_holders.orgRevision", + "current_holders.orgRoot", + ], + }); + _posNumCodeSit = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootName)?.orgRoot + .orgRootName ?? ""; + _posNumCodeSitAbb = + _profileAdmin?.current_holders.find((x) => x.orgRoot.orgRootShortName)?.orgRoot + .orgRootShortName ?? ""; + console.log( + "[OfficerProfileService] Admin profile position codes set:", + _posNumCodeSit, + _posNumCodeSitAbb, + ); + } + } + const before = null; + const meta = { + createdUserId: ctx.user.sub, + createdFullName: ctx.user.name, + lastUpdateUserId: ctx.user.sub, + lastUpdateFullName: ctx.user.name, + createdAt: new Date(), + lastUpdatedAt: new Date(), + }; + console.log( + "[OfficerProfileService] Starting to process", + data.length, + "profile(s)", + ); + await Promise.all( + data.map(async (item, index) => { + console.log( + "[OfficerProfileService] Processing item", + index + 1, + "of", + data.length, + ); + const _null: any = null; + if (item.bodyProfile.posLevelId === "") item.bodyProfile.posLevelId = null; + if (item.bodyProfile.posTypeId === "") item.bodyProfile.posTypeId = null; + if ( + item.bodyProfile.posLevelId && + !(await this.posLevelRepo.findOneBy({ id: item.bodyProfile.posLevelId })) + ) { + console.error( + "[OfficerProfileService] ไม่พบข้อมูลระดับตำแหน่งนี้ posLevelId:", + item.bodyProfile.posLevelId, + ); + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลระดับตำแหน่งนี้"); + } + if ( + item.bodyProfile.posTypeId && + !(await this.posTypeRepo.findOneBy({ id: item.bodyProfile.posTypeId })) + ) { + console.error( + "[OfficerProfileService] ไม่พบข้อมูลประเภทตำแหน่งนี้ posTypeId:", + item.bodyProfile.posTypeId, + ); + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลประเภทตำแหน่งนี้"); + } + + console.log( + "[OfficerProfileService] Processing citizenId:", + item.bodyProfile.citizenId, + ); + let registrationProvinceId = await this.provinceRepo.findOneBy({ + id: item.bodyProfile.registrationProvinceId ?? "", + }); + let registrationDistrictId = await this.districtRepo.findOneBy({ + id: item.bodyProfile.registrationDistrictId ?? "", + }); + let registrationSubDistrictId = await this.subDistrictRepo.findOneBy({ + id: item.bodyProfile.registrationSubDistrictId ?? "", + }); + let currentProvinceId = await this.provinceRepo.findOneBy({ + id: item.bodyProfile.currentProvinceId ?? "", + }); + let currentDistrictId = await this.districtRepo.findOneBy({ + id: item.bodyProfile.currentDistrictId ?? "", + }); + let currentSubDistrictId = await this.subDistrictRepo.findOneBy({ + id: item.bodyProfile.currentSubDistrictId ?? "", + }); + console.log("[OfficerProfileService] Address validation completed"); + + let _dateRetire = + item.bodyProfile.birthDate == null + ? _null + : calculateRetireDate(item.bodyProfile.birthDate); + let _dateRetireLaw = + item.bodyProfile.birthDate == null + ? _null + : calculateRetireLaw(item.bodyProfile.birthDate); + + let userKeycloakId: any; + let result: any; + console.log( + "[OfficerProfileService] Checking Keycloak user for citizenId:", + item.bodyProfile.citizenId, + ); + const checkUser = await getUserByUsername(item.bodyProfile.citizenId); + console.log( + "[OfficerProfileService] Keycloak user exists:", + checkUser.length > 0, + ); + if (checkUser.length == 0) { + console.log("[OfficerProfileService] Creating new Keycloak user"); + let password = item.bodyProfile.citizenId; + if (item.bodyProfile.birthDate != null) { + const _date = new Date(item.bodyProfile.birthDate.toDateString()) + .getDate() + .toString() + .padStart(2, "0"); + const _month = ( + new Date(item.bodyProfile.birthDate.toDateString()).getMonth() + 1 + ) + .toString() + .padStart(2, "0"); + const _year = new Date(item.bodyProfile.birthDate.toDateString()).getFullYear() + 543; + password = `${_date}${_month}${_year}`; + } + console.log( + "[OfficerProfileService] Calling createUser for:", + item.bodyProfile.citizenId, + ); + console.log( + "[OfficerProfileService] createUser data - firstName:", + item.bodyProfile.firstName, + "lastName:", + item.bodyProfile.lastName, + ); + // กรอง "." ออกจาก firstName ก่อนส่งไป keycloak (ป้องกัน . หรืออักขระอื่นๆ) + const sanitizedFirstName = item.bodyProfile.firstName?.replace(/\./g, "") ?? ""; + userKeycloakId = await createUser(item.bodyProfile.citizenId, password, { + firstName: sanitizedFirstName, + lastName: item.bodyProfile.lastName, + }); + if ( + userKeycloakId && + typeof userKeycloakId === "object" && + userKeycloakId.errorMessage + ) { + console.error( + "[OfficerProfileService] createUser FAILED - field:", + userKeycloakId.field, + "errorMessage:", + userKeycloakId.errorMessage, + "params:", + userKeycloakId.params, + ); + throw new HttpError( + HttpStatusCode.BAD_REQUEST, + `Keycloak validation failed: ${userKeycloakId.field} - ${userKeycloakId.errorMessage}`, + ); + } + console.log( + "[OfficerProfileService] User created in Keycloak, userKeycloakId:", + userKeycloakId, + ); + result = await addUserRoles( + userKeycloakId, + list + .filter((v) => v.name === "USER") + .map((x) => ({ + id: x.id, + name: x.name, + })), + ); + console.log( + "[OfficerProfileService] USER role assigned to new user, result:", + result, + ); + } else { + console.log("[OfficerProfileService] Updating existing Keycloak user"); + userKeycloakId = checkUser[0].id; + console.log( + "[OfficerProfileService] Existing userKeycloakId:", + userKeycloakId, + ); + const rolesData = await getRoleMappings(userKeycloakId); + if (rolesData) { + const _delRole = rolesData.map((x: any) => ({ + id: x.id, + name: x.name, + })); + console.log( + "[OfficerProfileService] Removing old roles:", + _delRole.length, + ); + await removeUserRoles(userKeycloakId, _delRole); + } + result = await addUserRoles( + userKeycloakId, + list + .filter((v) => v.name === "USER") + .map((x) => ({ + id: x.id, + name: x.name, + })), + ); + console.log( + "[OfficerProfileService] USER role assigned to existing user", + ); + } + + let profile: any = await this.profileRepository.findOne({ + where: { citizenId: item.bodyProfile.citizenId /*, isActive: true */ }, + relations: ["roleKeycloaks", "profileInsignias", "profileAvatars"], + }); + console.log( + "[OfficerProfileService] Profile found:", + !!profile, + "for citizenId:", + item.bodyProfile.citizenId, + ); + let _oldInsigniaIds: string[] = []; + let _oldSalaries: any[] = []; + //ลูกจ้างประจำ หรือ บุคคลภายนอก + if (!profile) { + console.log( + "[OfficerProfileService] No existing profile found, creating new profile", + ); + //กรณีลูกจ้างประจำมาสอบเป็นข้าราชการ ต้อง update สถานะโปรไฟล์เดิม + let profileEmployee: any = await this.profileEmployeeRepository.findOne({ + where: { citizenId: item.bodyProfile.citizenId }, + relations: ["profileInsignias", "roleKeycloaks"], + }); + console.log( + "[OfficerProfileService] Employee profile found:", + !!profileEmployee, + ); + if (profileEmployee) { + console.log( + "[OfficerProfileService] Converting employee profile to officer profile", + ); + const _order = await this.salaryRepo.findOne({ + where: { profileEmployeeId: profileEmployee.id }, + order: { order: "DESC" }, + }); + const profileEmpSalary = new ProfileSalary(); + profileEmpSalary.posNumCodeSit = _posNumCodeSit; + profileEmpSalary.posNumCodeSitAbb = _posNumCodeSitAbb; + profileEmpSalary.order = _order == null ? 1 : _order.order + 1; + Object.assign(profileEmpSalary, { + ...item.bodySalarys, + ...meta, + profileEmployeeId: profileEmployee.id, + profileId: undefined, + }); + const history = new ProfileSalaryHistory(); + Object.assign(history, { ...profileEmpSalary, id: undefined }); + profileEmpSalary.dateGovernment = item.bodySalarys?.commandDateAffect ?? meta.createdAt; + (profileEmpSalary.profileId = _null), + await this.salaryRepo.save(profileEmpSalary, { data: req }); + setLogDataDiff(req, { before, after: profileEmpSalary }); + history.profileSalaryId = profileEmpSalary.id; + await this.salaryHistoryRepo.save(history, { data: req }); + + if (profileEmployee.profileInsignias.length > 0) { + _oldInsigniaIds = profileEmployee.profileInsignias?.map((x: any) => x.id) ?? []; + } + await removeProfileInOrganize(profileEmployee.id, "EMPLOYEE"); + if (profileEmployee.keycloak != null) { + // const delUserKeycloak = await deleteUser(profileEmployee.keycloak); + // if (delUserKeycloak) { + // Task #228 + // profileEmployee.keycloak = _null; + profileEmployee.roleKeycloaks = []; + profileEmployee.isActive = false; + // } + } + profileEmployee.isLeave = true; + profileEmployee.leaveReason = "บรรจุข้าราชการ"; + profileEmployee.lastUpdateUserId = ctx.user.sub; + profileEmployee.lastUpdateFullName = ctx.user.name; + profileEmployee.lastUpdatedAt = new Date(); + await this.profileEmployeeRepository.save(profileEmployee); + setLogDataDiff(req, { before, after: profileEmployee }); + } + profile = Object.assign({ ...item.bodyProfile, ...meta }); + profile.dateRetire = _dateRetire; + profile.dateRetireLaw = _dateRetireLaw; + profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; + profile.keycloak = + userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; + profile.registrationAddress = item.bodyProfile.registrationAddress; + profile.registrationProvinceId = registrationProvinceId + ? registrationProvinceId.id + : _null; + profile.registrationDistrictId = registrationDistrictId + ? registrationDistrictId.id + : _null; + profile.registrationSubDistrictId = registrationSubDistrictId + ? registrationSubDistrictId.id + : _null; + profile.registrationZipCode = item.bodyProfile.registrationZipCode; + profile.currentAddress = item.bodyProfile.currentAddress; + profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; + profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; + profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; + profile.currentZipCode = item.bodyProfile.currentZipCode; + profile.email = item.bodyProfile.email; + profile.dateStart = item.bodyProfile.dateStart; + profile.amount = item.bodyProfile.amount ?? null; + profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; + profile.isProbation = item.bodyProfile.isProbation; + //เพิ่มใหม่จากรับโอน + profile.rank = item?.bodyProfile?.rank || null; + profile.prefix = item?.bodyProfile?.rank || item?.bodyProfile?.prefix || null; + profile.prefixMain = item?.bodyProfile?.prefix ?? null; + profile.firstName = item.bodyProfile.firstName ?? null; + profile.lastName = item.bodyProfile.lastName ?? null; + profile.birthDate = item.bodyProfile.birthDate ?? null; + profile.gender = item.bodyProfile.gender ?? null; + profile.relationship = item.bodyProfile.relationship ?? null; + profile.religion = item.bodyProfile.religion ?? null; + profile.ethnicity = item.bodyProfile.ethnicity; + profile.nationality = item.bodyProfile.nationality ?? null; + profile.bloodGroup = item.bodyProfile.bloodGroup ?? null; + profile.phone = item.bodyProfile.phone ?? null; + + console.log("[OfficerProfileService] Saving new profile"); + await this.profileRepository.save(profile); + console.log( + "[OfficerProfileService] New profile saved, profileId:", + profile.id, + ); + // update user attribute in keycloak + await updateUserAttributes(profile.keycloak ?? "", { + profileId: [profile.id], + prefix: [profile.prefix || ""], + }); + console.log("[OfficerProfileService] Keycloak attributes updated"); + setLogDataDiff(req, { before, after: profile }); + } + //ขรก.ในระบบ หรือ ขรก.ในระบบที่สถานะพ้นจากราชการ + else { + console.log( + "[OfficerProfileService] Existing profile found, isLeave:", + profile.isLeave, + "leaveType:", + profile.leaveType, + ); + //สร้างโปรไฟล์ใหม่ ถ้าสถานะพ้นราชการ คำสั่งโอนออกหรือคำสั่งขอลาออก + if ( + profile.isLeave && + ["PLACEMENT_TRANSFER", "RETIRE_RESIGN"].includes(profile.leaveType) + ) { + console.log( + "[OfficerProfileService] Profile is leaving with eligible leave type, creating new profile record", + ); + //ดึง profileSalary เดิม + _oldSalaries = await this.salaryRepo.find({ + where: { profileId: profile.id }, + order: { order: "ASC" }, + }); + if (profile.profileInsignias.length > 0) { + _oldInsigniaIds = profile.profileInsignias?.map((x: any) => x.id) ?? []; + } + profile = Object.assign({ ...item.bodyProfile, ...meta }); + profile.dateRetire = _dateRetire; + profile.dateRetireLaw = _dateRetireLaw; + profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; + profile.keycloak = + userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; + profile.registrationAddress = item.bodyProfile.registrationAddress; + profile.registrationProvinceId = registrationProvinceId + ? registrationProvinceId.id + : _null; + profile.registrationDistrictId = registrationDistrictId + ? registrationDistrictId.id + : _null; + profile.registrationSubDistrictId = registrationSubDistrictId + ? registrationSubDistrictId.id + : _null; + profile.registrationZipCode = item.bodyProfile.registrationZipCode; + profile.currentAddress = item.bodyProfile.currentAddress; + profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; + profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; + profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; + profile.currentZipCode = item.bodyProfile.currentZipCode; + profile.email = item.bodyProfile.email; + profile.dateStart = item.bodyProfile.dateStart; + profile.amount = item.bodyProfile.amount ?? null; + profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; + profile.isProbation = item.bodyProfile.isProbation; + profile.rank = item?.bodyProfile?.rank || null; + profile.prefix = item?.bodyProfile?.rank || item?.bodyProfile?.prefix || null; + profile.prefixMain = item?.bodyProfile?.prefix ?? null; + profile.firstName = item.bodyProfile.firstName ?? null; + profile.lastName = item.bodyProfile.lastName ?? null; + profile.birthDate = item.bodyProfile.birthDate ?? null; + profile.gender = item.bodyProfile.gender ?? null; + profile.relationship = item.bodyProfile.relationship ?? null; + profile.religion = item.bodyProfile.religion ?? null; + profile.ethnicity = item.bodyProfile.ethnicity; + profile.nationality = item.bodyProfile.nationality ?? null; + profile.bloodGroup = item.bodyProfile.bloodGroup ?? null; + profile.phone = item.bodyProfile.phone ?? null; + await this.profileRepository.save(profile); + console.log( + "[OfficerProfileService] New profile record saved for leaving officer, profileId:", + profile.id, + ); + setLogDataDiff(req, { before, after: profile }); + } else { + console.log( + "[OfficerProfileService] Updating existing active profile", + ); + profile.roleKeycloaks = result && roleKeycloak ? [roleKeycloak] : []; + profile.keycloak = + userKeycloakId && typeof userKeycloakId === "string" ? userKeycloakId : ""; + profile.isProbation = item.bodyProfile.isProbation; + profile.isLeave = item.bodyProfile.isLeave; + profile.isRetirement = false; + profile.isActive = true; + profile.isDelete = false; + profile.dateLeave = _null; + profile.dateRetire = _dateRetire; + profile.dateRetireLaw = _dateRetireLaw; + profile.registrationAddress = item.bodyProfile.registrationAddress; + profile.registrationProvinceId = registrationProvinceId + ? registrationProvinceId.id + : _null; + profile.registrationDistrictId = registrationDistrictId + ? registrationDistrictId.id + : _null; + profile.registrationSubDistrictId = registrationSubDistrictId + ? registrationSubDistrictId.id + : _null; + profile.registrationZipCode = item.bodyProfile.registrationZipCode; + profile.currentAddress = item.bodyProfile.currentAddress; + profile.currentProvinceId = currentProvinceId ? currentProvinceId.id : _null; + profile.currentDistrictId = currentDistrictId ? currentDistrictId.id : _null; + profile.currentSubDistrictId = currentSubDistrictId ? currentSubDistrictId.id : _null; + profile.currentZipCode = item.bodyProfile.currentZipCode; + profile.email = item.bodyProfile.email; + profile.telephoneNumber = item.bodyProfile.telephoneNumber; + profile.phone = item.bodyProfile.phone; + profile.dateStart = item.bodyProfile.dateStart; + profile.amount = item.bodyProfile.amount ?? null; + profile.amountSpecial = item.bodyProfile.amountSpecial ?? null; + profile.leaveCommandId = _null; + profile.leaveCommandNo = _null; + profile.leaveRemark = _null; + profile.leaveDate = _null; + profile.leaveType = _null; + profile.leaveReason = _null; + profile.lastUpdateUserId = ctx.user.sub; + profile.lastUpdateFullName = ctx.user.name; + profile.lastUpdatedAt = new Date(); + //เพิ่มใหม่จากรับโอน + profile.rank = item?.bodyProfile?.rank || null; + profile.prefix = item?.bodyProfile?.rank || item?.bodyProfile?.prefix || null; + profile.prefixMain = item?.bodyProfile?.prefix ?? null; + profile.firstName = + item.bodyProfile.firstName && item.bodyProfile.firstName != "" + ? item.bodyProfile.firstName + : profile.firstName; + profile.lastName = + item.bodyProfile.lastName && item.bodyProfile.lastName != "" + ? item.bodyProfile.lastName + : profile.lastName; + profile.birthDate = item.bodyProfile.birthDate + ? item.bodyProfile.birthDate + : profile.birthDate; + profile.gender = + item.bodyProfile.gender && item.bodyProfile.gender != "" + ? item.bodyProfile.gender + : profile.gender; + profile.relationship = + item.bodyProfile.relationship && item.bodyProfile.relationship != "" + ? item.bodyProfile.relationship + : profile.relationship; + profile.religion = + item.bodyProfile.religion && item.bodyProfile.religion != "" + ? item.bodyProfile.religion + : profile.religion; + profile.ethnicity = + item.bodyProfile.ethnicity && item.bodyProfile.ethnicity != "" + ? item.bodyProfile.ethnicity + : profile.ethnicity; + profile.nationality = + item.bodyProfile.nationality && item.bodyProfile.nationality != "" + ? item.bodyProfile.nationality + : profile.nationality; + profile.bloodGroup = + item.bodyProfile.bloodGroup && item.bodyProfile.bloodGroup != "" + ? item.bodyProfile.bloodGroup + : profile.bloodGroup; + profile.phone = + item.bodyProfile.phone && item.bodyProfile.phone != "" + ? item.bodyProfile.phone + : profile.phone; + await this.profileRepository.save(profile); + console.log( + "[OfficerProfileService] Existing active profile updated, profileId:", + profile.id, + ); + setLogDataDiff(req, { before, after: profile }); + } + } + + if (profile && profile.id) { + console.log( + "[OfficerProfileService] Processing additional data for profileId:", + profile.id, + ); + //Educations + if (item.bodyEducations && item.bodyEducations.length > 0) { + console.log( + "[OfficerProfileService] Processing educations, count:", + item.bodyEducations.length, + ); + await Promise.all( + item.bodyEducations.map(async (education) => { + const profileEdu = new ProfileEducation(); + Object.assign(profileEdu, { ...education, ...meta }); + const eduHistory = new ProfileEducationHistory(); + Object.assign(eduHistory, { ...profileEdu, id: undefined }); + profileEdu.profileId = profile.id; + const educationLevel = await this.profileEducationRepo.findOne({ + select: ["id", "level", "profileId"], + where: { profileId: profile.id, isDeleted: false }, + order: { level: "DESC" }, + }); + profileEdu.level = educationLevel == null ? 1 : educationLevel.level + 1; + await this.profileEducationRepo.save(profileEdu, { data: req }); + setLogDataDiff(req, { before, after: profileEdu }); + eduHistory.profileEducationId = profileEdu.id; + await this.profileEducationHistoryRepo.save(eduHistory, { data: req }); + }), + ); + } + //Certificates + if (item.bodyCertificates && item.bodyCertificates.length > 0) { + console.log( + "[OfficerProfileService] Processing certificates, count:", + item.bodyCertificates.length, + ); + await Promise.all( + item.bodyCertificates.map(async (cer) => { + const profileCer = new ProfileCertificate(); + Object.assign(profileCer, { ...cer, ...meta }); + const cerHistory = new ProfileCertificateHistory(); + Object.assign(cerHistory, { ...profileCer, id: undefined }); + profileCer.profileId = profile.id; + await this.certificateRepo.save(profileCer, { data: req }); + setLogDataDiff(req, { before, after: profileCer }); + cerHistory.profileCertificateId = profileCer.id; + await this.certificateHistoryRepo.save(cerHistory, { data: req }); + }), + ); + } + //FamilyCouple + if (item.bodyMarry != null) { + console.log("[OfficerProfileService] Processing couple/marry data"); + const profileCouple = new ProfileFamilyCouple(); + const data = { + profileId: profile.id, + couple: item.bodyMarry.marry, + couplePrefix: item.bodyMarry.marryPrefix, + coupleFirstName: item.bodyMarry.marryFirstName, + coupleLastName: item.bodyMarry.marryLastName, + coupleCareer: item.bodyMarry.marryOccupation, + coupleLive: true, + }; + Object.assign(profileCouple, { ...data, ...meta }); + const coupleHistory = new ProfileFamilyCoupleHistory(); + Object.assign(coupleHistory, { ...profileCouple, id: undefined }); + profileCouple.profileId = profile.id; + await this.profileFamilyCoupleRepo.save(profileCouple, { data: req }); + setLogDataDiff(req, { before, after: profileCouple }); + coupleHistory.profileFamilyCoupleId = profileCouple.id; + await this.profileFamilyCoupleHistoryRepo.save(coupleHistory, { data: req }); + } + //FamilyFather + if (item.bodyFather != null) { + console.log("[OfficerProfileService] Processing father data"); + const profileFather = new ProfileFamilyFather(); + const data = { + profileId: profile.id, + fatherPrefix: item.bodyFather.fatherPrefix, + fatherFirstName: item.bodyFather.fatherFirstName, + fatherLastName: item.bodyFather.fatherLastName, + fatherCareer: item.bodyFather.fatherOccupation, + fatherLive: true, + }; + Object.assign(profileFather, { ...data, ...meta }); + const fatherHistory = new ProfileFamilyFatherHistory(); + Object.assign(fatherHistory, { ...profileFather, id: undefined }); + profileFather.profileId = profile.id; + await this.profileFamilyFatherRepo.save(profileFather, { data: req }); + setLogDataDiff(req, { before, after: profileFather }); + fatherHistory.profileFamilyFatherId = profileFather.id; + await this.profileFamilyFatherHistoryRepo.save(fatherHistory, { data: req }); + } + //FamilyMother + if (item.bodyMother != null) { + console.log("[OfficerProfileService] Processing mother data"); + const profileMother = new ProfileFamilyMother(); + const data = { + profileId: profile.id, + motherPrefix: item.bodyMother.motherPrefix, + motherFirstName: item.bodyMother.motherFirstName, + motherLastName: item.bodyMother.motherLastName, + motherCareer: item.bodyMother.motherOccupation, + motherLive: true, + }; + Object.assign(profileMother, { ...data, ...meta }); + const motherHistory = new ProfileFamilyMotherHistory(); + Object.assign(motherHistory, { ...profileMother, id: undefined }); + profileMother.profileId = profile.id; + await this.profileFamilyMotherRepo.save(profileMother, { data: req }); + setLogDataDiff(req, { before, after: profileMother }); + motherHistory.profileFamilyMotherId = profileMother.id; + await this.profileFamilyMotherHistoryRepo.save(motherHistory, { data: req }); + } + //Salary + //insert profileSalary อันเก่า กรณีพ้นราชการแล้วกลับมาบรรจุ + if (_oldSalaries.length > 0) { + console.log( + "[OfficerProfileService] Restoring old salaries, count:", + _oldSalaries.length, + ); + await Promise.all( + _oldSalaries.map(async (oldSal) => { + const profileSal: any = new ProfileSalary(); + Object.assign(profileSal, { ...oldSal, ...meta }); + const salaryHistory = new ProfileSalaryHistory(); + Object.assign(salaryHistory, { ...profileSal, id: undefined }); + profileSal.profileId = profile.id; + await this.salaryRepo.save(profileSal, { data: req }); + setLogDataDiff(req, { before, after: profileSal }); + salaryHistory.profileSalaryId = profileSal.id; + await this.salaryHistoryRepo.save(salaryHistory, { data: req }); + }), + ); + } + //insert item.bodySalarys ต่อจากที่ insert เดิมไปแล้ว + if (item.bodySalarys && item.bodySalarys != null) { + console.log("[OfficerProfileService] Processing new salary data"); + const dest_item = await this.salaryRepo.findOne({ + where: { profileId: profile.id }, + order: { order: "DESC" }, + }); + const profileSal: any = new ProfileSalary(); + profileSal.posNumCodeSit = _posNumCodeSit; + profileSal.posNumCodeSitAbb = _posNumCodeSitAbb; + Object.assign(profileSal, { ...item.bodySalarys, ...meta }); + const salaryHistory = new ProfileSalaryHistory(); + Object.assign(salaryHistory, { ...profileSal, id: undefined }); + profileSal.order = dest_item == null ? 1 : dest_item.order + 1; + profileSal.profileId = profile.id; + profileSal.dateGovernment = item.bodySalarys.commandDateAffect ?? meta.createdAt; + profileSal.amount = item.bodySalarys.amount ?? null; + profileSal.amountSpecial = item.bodySalarys.amountSpecial ?? null; + profileSal.positionSalaryAmount = item.bodySalarys.positionSalaryAmount ?? null; + profileSal.mouthSalaryAmount = item.bodySalarys.mouthSalaryAmount ?? null; + await this.salaryRepo.save(profileSal, { data: req }); + setLogDataDiff(req, { before, after: profileSal }); + salaryHistory.profileSalaryId = profileSal.id; + await this.salaryHistoryRepo.save(salaryHistory, { data: req }); + } + //Position + if (item.bodyPosition && item.bodyPosition != null) { + console.log("[OfficerProfileService] Processing position assignment"); + // STEP 1: หา posMaster ที่จะใช้งานตาม id ที่ส่งมา (อาจเป็นตำแหน่งเก่าหรือใหม่ก็ได้) + console.log( + "[OfficerProfileService] STEP 1: Finding posMaster, posmasterId:", + item.bodyPosition.posmasterId, + ); + let posMaster = await this.posMasterRepository.findOne({ + where: { + id: item.bodyPosition.posmasterId, + }, + relations: { + orgRevision: true, + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + console.log("[OfficerProfileService] posMaster found:", !!posMaster); + + // เช็คว่า posMaster ที่หามาอยู่ในโครงสร้างปัจจุบันหรือไม่ + const isCurrent = + posMaster?.orgRevision?.orgRevisionIsCurrent === true && + posMaster?.orgRevision?.orgRevisionIsDraft === false; + console.log("[OfficerProfileService] posMaster isCurrent:", isCurrent); + + // ถ้าไม่อยู่ในโครงสร้างปัจจุบัน ให้หาตัวใหม่จาก ancestorDNA + if (!isCurrent && posMaster?.ancestorDNA) { + console.log( + "[OfficerProfileService] Finding current posMaster from ancestorDNA", + ); + posMaster = await this.posMasterRepository.findOne({ + where: { + ancestorDNA: posMaster.ancestorDNA, + orgRevision: { + orgRevisionIsCurrent: true, + orgRevisionIsDraft: false, + }, + }, + relations: { + orgRevision: true, + orgRoot: true, + orgChild1: true, + orgChild2: true, + orgChild3: true, + orgChild4: true, + }, + }); + console.log( + "[OfficerProfileService] Current posMaster from ancestorDNA found:", + !!posMaster, + ); + } + + if (posMaster == null) { + console.error( + `[OfficerProfileService] not found posMasterId: ${item.bodyPosition.posmasterId}`, + ); + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); + } + + // STEP 2: เคลียร์ข้อมูลตำแหน่งเก่าที่ครองอยู่ ในโครงสร้างปัจจุบัน + console.log("[OfficerProfileService] STEP 2: Clearing old position data"); + const posMasterOld = await this.posMasterRepository.findOne({ + where: { + current_holderId: profile.id, + orgRevisionId: posMaster.orgRevisionId, + }, + }); + if (posMasterOld != null) { + // เคลียร์คนครองเก่าออกจากตำแหน่งเดิม + posMasterOld.current_holderId = null; + posMasterOld.lastUpdatedAt = new Date(); + } + + // หา position เก่าที่เลือกไว้ แล้วเคลียร์การเลือก + const positionOld = await this.positionRepository.findOne({ + where: { + posMasterId: posMasterOld?.id, + positionIsSelected: true, + }, + }); + if (positionOld != null) { + positionOld.positionIsSelected = false; + await this.positionRepository.save(positionOld); + } + + // STEP 3: เคลียร์ position ที่เลือกไว้อื่นๆ ใน posMaster ตัวใหม่ + console.log( + "[OfficerProfileService] STEP 3: Clearing other selected positions in new posMaster", + ); + const checkPosition = await this.positionRepository.find({ + where: { + posMasterId: posMaster.id, + positionIsSelected: true, + }, + }); + if (checkPosition.length > 0) { + const clearPosition = checkPosition.map((positions) => ({ + ...positions, + positionIsSelected: false, + })); + await this.positionRepository.save(clearPosition); + } + + // STEP 4: กำหนดคนครองใหม่ให้กับ posMaster + console.log( + "[OfficerProfileService] STEP 4: Assigning new holder to posMaster", + ); + posMaster.current_holderId = profile.id; + posMaster.lastUpdatedAt = new Date(); + // posMaster.conditionReason = _null; + // posMaster.isCondition = false; + if (posMasterOld != null) { + await this.posMasterRepository.save(posMasterOld); + await CreatePosMasterHistoryOfficer(posMasterOld.id, req); + } + await this.posMasterRepository.save(posMaster); + console.log("[OfficerProfileService] posMaster saved with new holder"); + + // STEP 5: กำหนด position ใหม่ + console.log( + "[OfficerProfileService] STEP 5: Determining position to assign", + ); + // Match position ตามลำดับ priority: + // Condition 1: match จาก positionId + // Condition 2: match 7 ฟิลด์ (positionName, posTypeId, posLevelId, positionField, positionArea, positionExecutiveField, posExecutiveId) + // Condition 3: match 3 ฟิลด์ (positionName, posTypeId, posLevelId) + // Fallback: เลือก position แรกใน posMaster + + let positionNew: Position | null = null; + + // ═══════════════════════════════════════════════════════════ + // CONDITION 1: เช็คจาก positionId ตรง + // ═══════════════════════════════════════════════════════════ + console.log( + "[OfficerProfileService] CONDITION 1: Checking by positionId:", + item.bodyPosition?.positionId, + ); + if (item.bodyPosition?.positionId) { + const positionById = await this.positionRepository.findOne({ + where: { + id: item.bodyPosition.positionId, + posMasterId: posMaster.id, // ต้องอยู่ใน posMaster ที่ถูกต้อง + }, + relations: ["posExecutive"], + }); + + if (positionById) { + positionNew = positionById; + console.log( + "[OfficerProfileService] CONDITION 1 matched, positionId:", + positionById.id, + ); + } + } + + // ═══════════════════════════════════════════════════════════ + // CONDITION 2: Match 7 ฟิลด์ (ถ้า Condition 1 ไม่ match) + // ═══════════════════════════════════════════════════════════ + if (!positionNew && item.bodyPosition) { + console.log( + "[OfficerProfileService] CONDITION 1 not matched, trying CONDITION 2: Match 7 fields", + ); + // สร้าง where clause แบบ dynamic - ใส่เฉพาะฟิลด์ที่ไม่ใช่ null + const whereCondition: any = { + posMasterId: posMaster.id, + positionName: item.bodyPosition.positionName, + posTypeId: item.bodyPosition.posTypeId, + posLevelId: item.bodyPosition.posLevelId, + }; + + if (item.bodyPosition.positionField) { + whereCondition.positionField = item.bodyPosition.positionField; + } + if (item.bodyPosition.posExecutiveId) { + whereCondition.posExecutiveId = item.bodyPosition.posExecutiveId; + } + if (item.bodyPosition.positionExecutiveField) { + whereCondition.positionExecutiveField = item.bodyPosition.positionExecutiveField; + } + if (item.bodyPosition.positionArea) { + whereCondition.positionArea = item.bodyPosition.positionArea; + } + + const positionBy7Fields = await this.positionRepository.findOne({ + where: whereCondition, + relations: ["posExecutive"], + order: { orderNo: "ASC" }, + }); + + if (positionBy7Fields) { + positionNew = positionBy7Fields; + console.log( + "[OfficerProfileService] CONDITION 2 matched with 7 fields, positionId:", + positionBy7Fields.id, + ); + } + } + + // ═══════════════════════════════════════════════════════════ + // CONDITION 3: Match 3 ฟิลด์ (ถ้า Condition 2 ไม่ match) + // ═══════════════════════════════════════════════════════════ + if (!positionNew && item.bodyPosition) { + console.log( + "[OfficerProfileService] CONDITION 2 not matched, trying CONDITION 3: Match 3 fields", + ); + const positionBy3Fields = await this.positionRepository.findOne({ + where: { + posMasterId: posMaster.id, + positionName: item.bodyPosition.positionName, + posTypeId: item.bodyPosition.posTypeId, + posLevelId: item.bodyPosition.posLevelId, + }, + relations: ["posExecutive"], + order: { orderNo: "ASC" }, + }); + + if (positionBy3Fields) { + positionNew = positionBy3Fields; + console.log( + "[OfficerProfileService] CONDITION 3 matched with 3 fields, positionId:", + positionBy3Fields.id, + ); + } else { + console.log( + "[OfficerProfileService] No position matched for profileId:", + profile.id, + ); + } + } + + // // ═══════════════════════════════════════════════════════════ + // // FALLBACK: ถ้าทั้ง 3 ไม่ match ให้เลือก position แรกใน posMaster + // // ═══════════════════════════════════════════════════════════ + // if (!positionNew) { + // const fallbackPositions = await this.positionRepository.find({ + // where: { + // posMasterId: posMaster.id, + // }, + // relations: ["posExecutive"], + // order: { + // orderNo: "ASC", + // }, + // take: 1, + // }); + + // if (fallbackPositions.length > 0) { + // positionNew = fallbackPositions[0]; + // } + // } + + // อัพเดท org และ posMasterNo ตลอดไม่ต้องดัก isSit + profile.posMasterNo = getPosMasterNo(posMaster); + profile.org = getOrgFullName(posMaster); + // ถ้าไม่ใช่ตำแหน่งนั่งทับ (isSit = false) ถึงจะอัพเดทตำแหน่งในทะเบียนประวัติ + if (positionNew != null) { + console.log( + "[OfficerProfileService] Final position assignment, isSit:", + posMaster.isSit, + "positionId:", + positionNew.id, + ); + positionNew.positionIsSelected = true; + if (!posMaster.isSit) { + profile.posLevelId = positionNew.posLevelId; + profile.posTypeId = positionNew.posTypeId; + profile.position = positionNew.positionName; + profile.positionField = positionNew.positionField ?? null; + profile.posExecutive = positionNew.posExecutive?.posExecutiveName ?? null; + profile.positionArea = positionNew.positionArea ?? null; + profile.positionExecutiveField = positionNew.positionExecutiveField ?? null; + // profile.dateStart = new Date(); + } + await this.positionRepository.save(positionNew, { data: req }); + } else if (!posMaster.isSit) { + // fallback: ตำแหน่งในโครงสร้างถูกแก้ไข ใช้ข้อมูลตำแหน่งที่สมัครสอบมา + console.log( + "[OfficerProfileService] positionNew is null, using bodyPosition data as fallback", + ); + profile.position = item.bodyPosition.positionName ?? null; + profile.posTypeId = item.bodyPosition.posTypeId ?? null; + profile.posLevelId = item.bodyPosition.posLevelId ?? null; + profile.positionField = item.bodyPosition.positionField ?? null; + profile.positionArea = item.bodyPosition.positionArea ?? null; + profile.positionExecutiveField = item.bodyPosition.positionExecutiveField ?? null; + } + await this.profileRepository.save(profile, { data: req }); + setLogDataDiff(req, { before, after: profile }); + // await CreatePosMasterHistoryOfficer(posMaster.id, req); + await CreatePosMasterHistoryOfficer(posMaster.id, req, null, { + positionId: positionNew?.id, + }); + } + // Insignia + if (_oldInsigniaIds.length > 0) { + console.log( + "[OfficerProfileService] Processing old insignias, count:", + _oldInsigniaIds.length, + ); + const _insignias = await this.insigniaRepo.find({ + where: { id: In(_oldInsigniaIds), isDeleted: false }, + order: { createdAt: "ASC" }, + }); + for (const oldInsignia of _insignias) { + const newInsigniaData: CreateProfileInsignia = { + profileId: profile.id, + year: oldInsignia.year, + no: oldInsignia.no, + volume: oldInsignia.volume, + section: oldInsignia.section, + page: oldInsignia.page, + receiveDate: oldInsignia.receiveDate, + insigniaId: oldInsignia.insigniaId, + dateAnnounce: oldInsignia.dateAnnounce, + issue: oldInsignia.issue, + volumeNo: oldInsignia.volumeNo, + refCommandDate: oldInsignia.refCommandDate, + refCommandNo: oldInsignia.refCommandNo, + note: oldInsignia.note, + isUpload: oldInsignia.isUpload, + }; + const insignia = new ProfileInsignia(); + Object.assign(insignia, { ...newInsigniaData, ...meta }); + const history = new ProfileInsigniaHistory(); + Object.assign(history, { ...insignia, id: undefined }); + await this.insigniaRepo.save(insignia, { data: req }); + setLogDataDiff(req, { before, after: insignia }); + history.profileInsigniaId = insignia.id; + await this.insigniaHistoryRepo.save(history, { data: req }); + } + } + // เพิ่มรูปภาพโปรไฟล์ + if (item.bodyProfile.objectRefId) { + console.log( + "[OfficerProfileService] Processing profile avatar image, objectRefId:", + item.bodyProfile.objectRefId, + ); + const _profileAvatar = new ProfileAvatar(); + Object.assign(_profileAvatar, { + ...meta, + profileId: profile.id, + profileEmployeeId: undefined, + }); + if (profile.profileAvatars && profile.profileAvatars.length > 0) { + await Promise.all( + profile.profileAvatars.map(async (item: any) => { + item.isActive = false; + await this.avatarRepository.save(item); + }), + ); + } + await this.avatarRepository.save(_profileAvatar); + let avatar = `ทะเบียนประวัติ/โปรไฟล์/${profile.id}`; + let fileName = `profile-${_profileAvatar.id}`; + _profileAvatar.isActive = true; + _profileAvatar.avatar = avatar; + _profileAvatar.avatarName = fileName; + await this.avatarRepository.save(_profileAvatar, { data: req }); + profile.avatar = avatar; + profile.avatarName = fileName; + await this.profileRepository.save(profile, { data: req }); + const checkAvatar = await this.avatarRepository.findOne({ + where: { avatar: avatar, avatarName: fileName }, + }); + if (checkAvatar && checkAvatar.profileId == null) { + checkAvatar.profileId = profile.id; + await this.avatarRepository.save(checkAvatar); + } + //duplicate รูปภาพโปรไฟล์โดยอิงจากรูปภาพเดิม + await new CallAPI() + .PostData(req, `/salary/file/avatar/${item.bodyProfile.objectRefId}`, { + prefix: avatar, + fileName: fileName, + }) + .then(() => {}) + .catch(() => {}); + } + } + }), + ); + console.log("[OfficerProfileService] executeCreateOfficerProfile completed successfully"); + } +} diff --git a/src/services/rabbitmq.ts b/src/services/rabbitmq.ts index b9668904..b9c53537 100644 --- a/src/services/rabbitmq.ts +++ b/src/services/rabbitmq.ts @@ -29,6 +29,7 @@ import { sendWebSocket } from "./webSocket"; import { PayloadSendNoti } from "../interfaces/utils"; import { PermissionProfile } from "../entities/PermissionProfile"; import { PosMasterHistory } from "../entities/PosMasterHistory"; +import { OfficerProfileService } from "./OfficerProfileService"; const redis = require("redis"); const REDIS_HOST = process.env.REDIS_HOST; @@ -320,13 +321,55 @@ async function handler(msg: amqp.ConsumeMessage): Promise { 20, ); - for (const chunk of chunks) { - await new CallAPI().PostData( - { headers: { authorization: token } }, - path + "/excecute", - { refIds: chunk }, - false, - ); + // ───────────────────────────────────────────────────────────── + // Linear Flow + // ทดสอบเฉพาะ C-PM-01 รับ resultData จาก .NET แล้วเรียก OfficerProfileService ตรงๆ ไม่ผ่าน HTTP loopback + // ───────────────────────────────────────────────────────────── + const isLinearFlow = command.commandType?.code === "C-PM-01"; + + if (isLinearFlow) { + let resultData: any[] = []; + + for (const chunk of chunks) { + const res = await new CallAPI().PostData( + { headers: { authorization: token } }, + path + "/excecute", + { refIds: chunk }, + false, + ); + // CallAPI.PostData คืน response.data.result (ตาม call-api.ts) + if (res?.result && Array.isArray(res.result)) { + resultData.push(...res.result); + } + } + + console.log(`[AMQ] Received ${resultData.length} profiles from .NET (C-PM-01)`); + + // เรียก OfficerProfileService + if (resultData.length > 0) { + // สร้าง pseudo-req สำหรับ setLogDataDiff/save({data: req}) + const pseudoReq = { + headers: { authorization: token }, + user, + }; + const ctx = { + user: { sub: user?.sub ?? "system", name: user?.name ?? "System" }, + req: pseudoReq, + }; + + await new OfficerProfileService().executeCreateOfficerProfile(resultData, ctx); + console.log(`[AMQ] Processed ${resultData.length} profiles via OfficerProfileService`); + } + } else { + // Flow เดิม + for (const chunk of chunks) { + await new CallAPI().PostData( + { headers: { authorization: token } }, + path + "/excecute", + { refIds: chunk }, + false, + ); + } } Object.assign(command, { status, lastUpdateUserId, lastUpdateFullName, lastUpdatedAt });