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 ExecuteOfficerProfileService { 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("[ExecuteOfficerProfileService] Starting executeCreateOfficerProfile"); console.log("[ExecuteOfficerProfileService] Request body count:", data?.length); // ───────────────────────────────────────────────────────────── // Normalize date fields // ผ่าน HTTP endpoint → tsoa แปลง ISO string → Date ให้อัตโนมัติ // แต่ผ่าน RabbitMQ handler (axios) → จะได้ string → ต้องแปลงเอง // ไม่งั้น calculateRetireDate/getFullYear ฯลฯ จะพัง // ───────────────────────────────────────────────────────────── const toDate = (v: any): Date | null => { if (v == null || v === "") return null; if (v instanceof Date) return isNaN(v.getTime()) ? null : v; const d = new Date(v); return isNaN(d.getTime()) ? null : d; }; for (const item of data ?? []) { const bp = item.bodyProfile as any; if (bp) { bp.birthDate = toDate(bp.birthDate); bp.dateStart = toDate(bp.dateStart); bp.dateAppoint = toDate(bp.dateAppoint); bp.dateRetire = toDate(bp.dateRetire); } const bs = item.bodySalarys as any; if (bs) { bs.commandDateAffect = toDate(bs.commandDateAffect); bs.commandDateSign = toDate(bs.commandDateSign); } if (item.bodyEducations) { for (const edu of item.bodyEducations as any[]) { edu.startDate = toDate(edu.startDate); edu.endDate = toDate(edu.endDate); edu.finishDate = toDate(edu.finishDate); } } if (item.bodyCertificates) { for (const cer of item.bodyCertificates as any[]) { cer.expireDate = toDate(cer.expireDate); cer.issueDate = toDate(cer.issueDate); } } } const req = ctx.req; const roleKeycloak = await this.roleKeycloakRepo.findOne({ where: { name: Like("USER") }, }); console.log("[ExecuteOfficerProfileService] roleKeycloak found:", !!roleKeycloak); const list = await getRoles(); console.log( "[ExecuteOfficerProfileService] Roles list retrieved, length:", Array.isArray(list) ? list.length : "not array", ); if (!Array.isArray(list)) { console.error( "[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] Getting command data"); const _command = await this.commandRepository.findOne({ where: { id: data.find((x) => x.bodySalarys?.commandId)?.bodySalarys?.commandId ?? "", }, }); console.log( "[ExecuteOfficerProfileService] Command found:", !!_command, "isBangkok:", _command?.isBangkok, ); if (_command) { if (_command?.isBangkok?.toLocaleUpperCase() == "OFFICE") { console.log("[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] OFFICE position codes set:", _posNumCodeSit, _posNumCodeSitAbb, ); } else if (_command?.isBangkok?.toLocaleUpperCase() == "BANGKOK") { console.log("[ExecuteOfficerProfileService] Setting position codes for BANGKOK"); _posNumCodeSit = "กรุงเทพมหานคร"; _posNumCodeSitAbb = "กทม."; console.log( "[ExecuteOfficerProfileService] BANGKOK position codes set:", _posNumCodeSit, _posNumCodeSitAbb, ); } else { console.log("[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] Starting to process", data.length, "profile(s)", ); await Promise.all( data.map(async (item, index) => { console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] ไม่พบข้อมูลระดับตำแหน่งนี้ posLevelId:", item.bodyProfile.posLevelId, ); throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลระดับตำแหน่งนี้"); } if ( item.bodyProfile.posTypeId && !(await this.posTypeRepo.findOneBy({ id: item.bodyProfile.posTypeId })) ) { console.error( "[ExecuteOfficerProfileService] ไม่พบข้อมูลประเภทตำแหน่งนี้ posTypeId:", item.bodyProfile.posTypeId, ); throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลประเภทตำแหน่งนี้"); } console.log( "[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] Checking Keycloak user for citizenId:", item.bodyProfile.citizenId, ); const checkUser = await getUserByUsername(item.bodyProfile.citizenId); console.log( "[ExecuteOfficerProfileService] Keycloak user exists:", checkUser.length > 0, ); if (checkUser.length == 0) { console.log("[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] Calling createUser for:", item.bodyProfile.citizenId, ); console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] USER role assigned to new user, result:", result, ); } else { console.log("[ExecuteOfficerProfileService] Updating existing Keycloak user"); userKeycloakId = checkUser[0].id; console.log( "[ExecuteOfficerProfileService] Existing userKeycloakId:", userKeycloakId, ); const rolesData = await getRoleMappings(userKeycloakId); if (rolesData) { const _delRole = rolesData.map((x: any) => ({ id: x.id, name: x.name, })); console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] Profile found:", !!profile, "for citizenId:", item.bodyProfile.citizenId, ); let _oldInsigniaIds: string[] = []; let _oldSalaries: any[] = []; //ลูกจ้างประจำ หรือ บุคคลภายนอก if (!profile) { console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] Employee profile found:", !!profileEmployee, ); if (profileEmployee) { console.log( "[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] Saving new profile"); await this.profileRepository.save(profile); console.log( "[ExecuteOfficerProfileService] New profile saved, profileId:", profile.id, ); // update user attribute in keycloak await updateUserAttributes(profile.keycloak ?? "", { profileId: [profile.id], prefix: [profile.prefix || ""], }); console.log("[ExecuteOfficerProfileService] Keycloak attributes updated"); setLogDataDiff(req, { before, after: profile }); } //ขรก.ในระบบ หรือ ขรก.ในระบบที่สถานะพ้นจากราชการ else { console.log( "[ExecuteOfficerProfileService] Existing profile found, isLeave:", profile.isLeave, "leaveType:", profile.leaveType, ); //สร้างโปรไฟล์ใหม่ ถ้าสถานะพ้นราชการ คำสั่งโอนออกหรือคำสั่งขอลาออก if ( profile.isLeave && ["PLACEMENT_TRANSFER", "RETIRE_RESIGN"].includes(profile.leaveType) ) { console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] New profile record saved for leaving officer, profileId:", profile.id, ); setLogDataDiff(req, { before, after: profile }); } else { console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] Existing active profile updated, profileId:", profile.id, ); setLogDataDiff(req, { before, after: profile }); } } if (profile && profile.id) { console.log( "[ExecuteOfficerProfileService] Processing additional data for profileId:", profile.id, ); //Educations if (item.bodyEducations && item.bodyEducations.length > 0) { console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] Processing position assignment"); // STEP 1: หา posMaster ที่จะใช้งานตาม id ที่ส่งมา (อาจเป็นตำแหน่งเก่าหรือใหม่ก็ได้) console.log( "[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] posMaster found:", !!posMaster); // เช็คว่า posMaster ที่หามาอยู่ในโครงสร้างปัจจุบันหรือไม่ const isCurrent = posMaster?.orgRevision?.orgRevisionIsCurrent === true && posMaster?.orgRevision?.orgRevisionIsDraft === false; console.log("[ExecuteOfficerProfileService] posMaster isCurrent:", isCurrent); // ถ้าไม่อยู่ในโครงสร้างปัจจุบัน ให้หาตัวใหม่จาก ancestorDNA if (!isCurrent && posMaster?.ancestorDNA) { console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] Current posMaster from ancestorDNA found:", !!posMaster, ); } if (posMaster == null) { console.error( `[ExecuteOfficerProfileService] not found posMasterId: ${item.bodyPosition.posmasterId}`, ); throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); } // STEP 2: เคลียร์ข้อมูลตำแหน่งเก่าที่ครองอยู่ ในโครงสร้างปัจจุบัน console.log("[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] posMaster saved with new holder"); // STEP 5: กำหนด position ใหม่ console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] CONDITION 1 matched, positionId:", positionById.id, ); } } // ═══════════════════════════════════════════════════════════ // CONDITION 2: Match 7 ฟิลด์ (ถ้า Condition 1 ไม่ match) // ═══════════════════════════════════════════════════════════ if (!positionNew && item.bodyPosition) { console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] CONDITION 2 matched with 7 fields, positionId:", positionBy7Fields.id, ); } } // ═══════════════════════════════════════════════════════════ // CONDITION 3: Match 3 ฟิลด์ (ถ้า Condition 2 ไม่ match) // ═══════════════════════════════════════════════════════════ if (!positionNew && item.bodyPosition) { console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] CONDITION 3 matched with 3 fields, positionId:", positionBy3Fields.id, ); } else { console.log( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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( "[ExecuteOfficerProfileService] 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("[ExecuteOfficerProfileService] executeCreateOfficerProfile completed successfully"); } }