fix registry retire

This commit is contained in:
Warunee Tamkoo 2025-10-03 13:05:24 +07:00
parent 4994c829b5
commit b76895012c
2 changed files with 516 additions and 474 deletions

View file

@ -1,4 +1,5 @@
import { AppDataSource } from "../database/data-source";
import { Profile } from "./../entities/Profile";
import { ProfileEmployee } from "../entities/ProfileEmployee";
import { OrgRoot } from "../entities/OrgRoot";
import { OrgChild1 } from "../entities/OrgChild1";
@ -10,7 +11,7 @@ import Extension from "../interfaces/extension";
import permission from "../interfaces/permission";
import { RequestWithUser } from "../middlewares/user";
export interface LeaveEmployeeFilter {
export interface LeaveFilter {
page: number;
pageSize: number;
searchField?: "firstName" | "lastName" | "fullName" | "citizenId" | "position" | "posNo";
@ -32,7 +33,8 @@ export interface OrganizationCondition {
}
export class ProfileLeaveService {
private profileRepo: Repository<ProfileEmployee>;
private profileEmployeeRepo: Repository<ProfileEmployee>;
private profileRepo: Repository<Profile>;
private orgRootRepository: Repository<OrgRoot>;
private child1Repository: Repository<OrgChild1>;
private child2Repository: Repository<OrgChild2>;
@ -40,7 +42,8 @@ export class ProfileLeaveService {
private child4Repository: Repository<OrgChild4>;
constructor() {
this.profileRepo = AppDataSource.getRepository(ProfileEmployee);
this.profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee);
this.profileRepo = AppDataSource.getRepository(Profile);
this.orgRootRepository = AppDataSource.getRepository(OrgRoot);
this.child1Repository = AppDataSource.getRepository(OrgChild1);
this.child2Repository = AppDataSource.getRepository(OrgChild2);
@ -48,29 +51,25 @@ export class ProfileLeaveService {
this.child4Repository = AppDataSource.getRepository(OrgChild4);
}
/**
* query
*/
buildSearchQuery(searchField?: string): string {
/** สร้าง query สำหรับการค้นหาตามฟิลด์ต่างๆ */
buildSearchQuery(searchField?: string, type: string = "profile"): string {
switch (searchField) {
case "citizenId":
return "profileEmployee.citizenId LIKE :keyword";
return `${type}.citizenId LIKE :keyword`;
case "position":
return "profileEmployee.position LIKE :keyword";
return `${type}.position LIKE :keyword`;
case "posNo":
return `
(profileSalary.posNoAbb IS NOT NULL AND CONCAT(profileSalary.posNoAbb, profileSalary.posNo) LIKE :keyword)
OR (profileSalary.posNoAbb IS NOT NULL AND CONCAT(profileSalary.posNoAbb, " ", profileSalary.posNo) LIKE :keyword)
OR (profileSalary.posNo IS NOT NULL AND profileSalary.posNo LIKE :keyword)
(CONCAT(profileSalary.posNoAbb, profileSalary.posNo) LIKE :keyword)
OR (CONCAT(profileSalary.posNoAbb, " ", profileSalary.posNo) LIKE :keyword)
OR (profileSalary.posNo LIKE :keyword)
`;
default:
return "CONCAT(profileEmployee.prefix, profileEmployee.firstName, ' ', profileEmployee.lastName) LIKE :keyword";
return `CONCAT(${type}.prefix, ${type}.firstName, ' ', ${type}.lastName) LIKE :keyword`;
}
}
/**
* node nodeId
*/
/** สร้างเงื่อนไขการค้นหาตาม node และ nodeId */
async buildNodeCondition(
node?: number,
nodeId?: string,
@ -87,7 +86,7 @@ export class ProfileLeaveService {
// สร้าง nodeAll condition - เพิ่มการตรวจสอบ IS NULL
if (isAll === false && node < 4) {
const nextLevels = ["orgChild1", "orgChild2", "orgChild3", "orgChild4"];
nodeAll = ` AND (profileSalary.${nextLevels[node]} IS NULL OR profileSalary.id IS NULL)`;
nodeAll = ` AND (profileSalary.${nextLevels[node]} IS NULL)`;
}
try {
@ -95,7 +94,7 @@ export class ProfileLeaveService {
case 0: {
const orgRoot = await this.orgRootRepository.findOne({ where: { id: nodeId } });
if (orgRoot) {
condition = "(profileSalary.orgRoot = :orgRoot OR profileSalary.id IS NULL)";
condition = "(profileSalary.orgRoot = :orgRoot)";
params.orgRoot = orgRoot.orgRootName;
}
break;
@ -103,7 +102,7 @@ export class ProfileLeaveService {
case 1: {
const orgChild1 = await this.child1Repository.findOne({ where: { id: nodeId } });
if (orgChild1) {
condition = "(profileSalary.orgChild1 = :orgChild1 OR profileSalary.id IS NULL)";
condition = "(profileSalary.orgChild1 = :orgChild1)";
params.orgChild1 = orgChild1.orgChild1Name;
}
break;
@ -111,7 +110,7 @@ export class ProfileLeaveService {
case 2: {
const orgChild2 = await this.child2Repository.findOne({ where: { id: nodeId } });
if (orgChild2) {
condition = "(profileSalary.orgChild2 = :orgChild2 OR profileSalary.id IS NULL)";
condition = "(profileSalary.orgChild2 = :orgChild2)";
params.orgChild2 = orgChild2.orgChild2Name;
}
break;
@ -119,7 +118,7 @@ export class ProfileLeaveService {
case 3: {
const orgChild3 = await this.child3Repository.findOne({ where: { id: nodeId } });
if (orgChild3) {
condition = "(profileSalary.orgChild3 = :orgChild3 OR profileSalary.id IS NULL)";
condition = "(profileSalary.orgChild3 = :orgChild3)";
params.orgChild3 = orgChild3.orgChild3Name;
}
break;
@ -127,7 +126,7 @@ export class ProfileLeaveService {
case 4: {
const orgChild4 = await this.child4Repository.findOne({ where: { id: nodeId } });
if (orgChild4) {
condition = "(profileSalary.orgChild4 = :orgChild4 OR profileSalary.id IS NULL)";
condition = "(profileSalary.orgChild4 = :orgChild4)";
params.orgChild4 = orgChild4.orgChild4Name;
}
break;
@ -140,14 +139,13 @@ export class ProfileLeaveService {
return { condition: condition + nodeAll, params };
}
/**
* permission
*/
/** สร้างเงื่อนไขการค้นหาตาม permission */
async buildPermissionCondition(
request: RequestWithUser,
isAll?: boolean,
permissionType: string = "SYS_REGISTRY_OFFICER",
): Promise<OrganizationCondition> {
const _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_EMP");
const _data = await new permission().PermissionOrgList(request, permissionType);
let condition = "1=1";
let nodeAll = "";
const params: Record<string, any> = {};
@ -199,9 +197,7 @@ export class ProfileLeaveService {
return { condition: condition + nodeAll, params };
}
/**
* format
*/
/** แปลงข้อมูลลูกจ้างก่อน response */
transformEmployeeData(employee: ProfileEmployee): any {
const dateEmployment =
employee.profileEmployeeEmployment?.length === 0 || !employee.profileEmployeeEmployment
@ -323,12 +319,10 @@ export class ProfileLeaveService {
};
}
/**
*
*/
/** ค้นหาลูกจ้างที่พ้นจากราชการ */
async getLeaveEmployees(
request: RequestWithUser,
filter: LeaveEmployeeFilter,
filter: LeaveFilter,
): Promise<{ data: any[]; total: number }> {
const {
page,
@ -349,13 +343,13 @@ export class ProfileLeaveService {
// สร้าง query conditions แบบ parallel
const [nodeCondition, permissionCondition] = await Promise.all([
this.buildNodeCondition(node, nodeId, isAll),
this.buildPermissionCondition(request, isAll),
this.buildPermissionCondition(request, isAll, "SYS_REGISTRY_EMP"),
]);
const searchQuery = this.buildSearchQuery(searchField);
const searchQuery = this.buildSearchQuery(searchField, "profileEmployee");
// สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary
const queryBuilder = this.profileRepo
const queryBuilder = this.profileEmployeeRepo
.createQueryBuilder("profileEmployee")
.leftJoinAndSelect("profileEmployee.posLevel", "posLevel")
.leftJoinAndSelect("profileEmployee.posType", "posType")
@ -384,7 +378,19 @@ export class ProfileLeaveService {
);
}),
)
.andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" });
.andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" })
.andWhere(
new Brackets((qb) => {
qb.orWhere(
searchKeyword != undefined && searchKeyword != null && searchKeyword != ""
? searchQuery
: "1=1",
{
keyword: `%${searchKeyword}%`,
},
);
}),
);
// เพิ่มเงื่อนไขการค้นหา
if (posType) {
@ -393,7 +399,7 @@ export class ProfileLeaveService {
if (posLevel) {
queryBuilder.andWhere(
"CONCAT(posType.posTypeShortName,' ',posLevel.posLevelName) LIKE :keyword2",
"CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2",
{ keyword2: `${posLevel}` },
);
}
@ -406,8 +412,177 @@ export class ProfileLeaveService {
queryBuilder.andWhere("profileEmployee.leaveType = :retireType", { retireType });
}
if (searchKeyword) {
queryBuilder.andWhere(searchQuery, { keyword: `%${searchKeyword}%` });
// เพิ่ม permission และ node conditions
queryBuilder
.andWhere(permissionCondition.condition, permissionCondition.params)
.andWhere(nodeCondition.condition, nodeCondition.params);
console.log("Permission Condition:", permissionCondition);
console.log("Node Condition:", nodeCondition);
// เพิ่ม sorting และ pagination
queryBuilder
.orderBy(sortBy, sort)
.skip((page - 1) * pageSize)
.take(pageSize);
const [records, total] = await queryBuilder.getManyAndCount();
// print query for debug
// console.log("SQL Query:", queryBuilder.getSql());
// แปลงข้อมูลแบบ parallel
const data = await Promise.all(
records.map((record) => Promise.resolve(this.transformEmployeeData(record))),
);
return { data, total };
}
/**
* response
*/
transformOfficerData(employee: Profile): any {
// ตรวจสอบว่า profileSalary มีข้อมูลหรือไม่
const salary =
employee.profileSalary && employee.profileSalary.length > 0
? employee.profileSalary[0]
: null;
const posNo =
salary?.posNoAbb && salary?.posNo
? `${salary.posNoAbb} ${salary.posNo}`
: salary?.posNo || "";
const posExecutive = salary?.positionExecutive ? salary.positionExecutive : null;
const root = salary?.orgRoot ? salary.orgRoot : null;
// สร้าง organization hierarchy - ใช้ข้อมูลจาก temp fields ถ้า salary ไม่มี
const org = salary
? [salary.orgChild4, salary.orgChild3, salary.orgChild2, salary.orgChild1, salary.orgRoot]
.filter(Boolean)
.join("\n")
: ["", "", "", "", ""].filter(Boolean).join("\n");
const orgRootShortName = salary?.posNoAbb ? salary.posNoAbb : null;
return {
id: employee.id,
avatar: employee.avatar,
avatarName: employee.avatarName,
dateAppoint: employee.dateAppoint,
prefix: employee.prefix,
rank: employee.rank,
firstName: employee.firstName,
lastName: employee.lastName,
citizenId: employee.citizenId,
posLevel: employee.posLevel?.posLevelName || null,
posType: employee.posType?.posTypeName || null,
posLevelId: employee.posLevel?.id || null,
posTypeId: employee.posType?.id || null,
position: employee.position,
posExecutive,
posNo,
rootId: null,
root,
orgRootShortName,
orgRevisionId: null,
org,
};
}
/**
*
*/
async getLeaveOfficer(
request: RequestWithUser,
filter: LeaveFilter,
): Promise<{ data: any[]; total: number }> {
const {
page,
pageSize,
searchField,
searchKeyword = "",
posType,
posLevel,
isProbation,
node,
nodeId,
isAll,
retireType,
sortBy = "profile.dateLeave",
sort,
} = filter;
// สร้าง query conditions แบบ parallel
const [nodeCondition, permissionCondition] = await Promise.all([
this.buildNodeCondition(node, nodeId, isAll),
this.buildPermissionCondition(request, isAll, "SYS_REGISTRY_OFFICER"),
]);
const searchQuery = this.buildSearchQuery(searchField);
// สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary
const queryBuilder = this.profileRepo
.createQueryBuilder("profile")
.leftJoinAndSelect("profile.posLevel", "posLevel")
.leftJoinAndSelect("profile.posType", "posType")
.leftJoin(
"profile.profileSalary",
"profileSalary",
"profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileId = profile.id and ps.positionName != 'เกษียณอายุราชการ')",
)
.addSelect([
"profileSalary.id",
"profileSalary.order",
"profileSalary.posNo",
"profileSalary.posNoAbb",
"profileSalary.orgRoot",
"profileSalary.orgChild1",
"profileSalary.orgChild2",
"profileSalary.orgChild3",
"profileSalary.orgChild4",
"profileSalary.positionExecutive",
])
.where(
new Brackets((qb) => {
qb.where("profile.isLeave = :isLeave", { isLeave: true }).orWhere(
"profile.isRetirement = :isRetirement",
{ isRetirement: true },
);
}),
)
.andWhere(
new Brackets((qb) => {
qb.orWhere(
searchKeyword != undefined && searchKeyword != null && searchKeyword != ""
? searchQuery
: "1=1",
{
keyword: `%${searchKeyword}%`,
},
);
}),
);
// เพิ่มเงื่อนไขการค้นหา
if (posType) {
queryBuilder.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` });
}
if (posLevel) {
queryBuilder.andWhere(
"CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2",
{ keyword2: `${posLevel}` },
);
}
if (isProbation !== undefined && isProbation !== null) {
queryBuilder.andWhere(`profile.isProbation = ${isProbation}`);
}
if (retireType) {
queryBuilder.andWhere("profile.leaveType = :retireType", { retireType });
}
// เพิ่ม permission และ node conditions
@ -423,9 +598,12 @@ export class ProfileLeaveService {
const [records, total] = await queryBuilder.getManyAndCount();
// print query for debug
// console.log("SQL Query:", queryBuilder.getSql());
// แปลงข้อมูลแบบ parallel
const data = await Promise.all(
records.map((record) => Promise.resolve(this.transformEmployeeData(record))),
records.map((record) => Promise.resolve(this.transformOfficerData(record))),
);
return { data, total };