fix sort registry and filter node of registry retire

This commit is contained in:
Warunee Tamkoo 2025-10-04 13:54:17 +07:00
parent abab998cd0
commit cd9c1721c1
3 changed files with 301 additions and 162 deletions

View file

@ -5742,6 +5742,8 @@ export class ProfileController extends Controller {
@Query() sortBy: string = "profile.dateLeave",
@Query() sort: "ASC" | "DESC" = "ASC",
) {
let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER");
const { data, total } = await this.profileLeaveService.getLeaveOfficer(request, {
page,
pageSize,
@ -5756,6 +5758,7 @@ export class ProfileController extends Controller {
retireType,
sortBy,
sort,
_data,
});
// let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_OFFICER");
@ -6403,7 +6406,6 @@ export class ProfileController extends Controller {
total: 1,
},
})
// ...existing code...
async listProfile(
@Request() request: RequestWithUser,
@Query("page") page: number = 1,
@ -6587,7 +6589,7 @@ export class ProfileController extends Controller {
}),
)
.addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order")
.orderBy("sort_order", sort)
.orderBy(sortBy ? sortBy : "sort_order", sort)
.addOrderBy("orgRoot.orgRootOrder", sort)
.addOrderBy("orgChild1.orgChild1Order", sort)
.addOrderBy("orgChild2.orgChild2Order", sort)

View file

@ -2674,6 +2674,8 @@ export class ProfileEmployeeController extends Controller {
@Query() sortBy: string = "profileEmployee.dateLeave",
@Query() sort: "ASC" | "DESC" = "DESC",
) {
let _data = await new permission().PermissionOrgList(request, "SYS_REGISTRY_EMP");
const { data, total } = await this.profileLeaveService.getLeaveEmployees(request, {
page,
pageSize,
@ -2688,6 +2690,7 @@ export class ProfileEmployeeController extends Controller {
retireType,
sortBy,
sort,
_data,
});
return new HttpSuccess({ data, total });
@ -2915,7 +2918,7 @@ export class ProfileEmployeeController extends Controller {
nodeId: nodeId,
})
.addSelect("CASE WHEN current_holders.posMasterNo IS NULL THEN 1 ELSE 0 END", "sort_order")
.orderBy("sort_order", sort)
.orderBy(sortBy ? sortBy : "sort_order", sort)
.addOrderBy("orgRoot.orgRootOrder", sort)
.addOrderBy("orgChild1.orgChild1Order", sort)
.addOrderBy("orgChild2.orgChild2Order", sort)

View file

@ -8,10 +8,9 @@ import { OrgChild3 } from "../entities/OrgChild3";
import { OrgChild4 } from "../entities/OrgChild4";
import { Brackets, Repository } from "typeorm";
import Extension from "../interfaces/extension";
import permission from "../interfaces/permission";
import { RequestWithUser } from "../middlewares/user";
export interface LeaveFilter {
interface LeaveFilter {
page: number;
pageSize: number;
searchField?: "firstName" | "lastName" | "fullName" | "citizenId" | "position" | "posNo";
@ -25,13 +24,41 @@ export interface LeaveFilter {
retireType?: string;
sortBy?: string;
sort: "ASC" | "DESC";
_data: DataPermission;
}
export interface OrganizationCondition {
interface DataPermission {
root: string | null;
child1: string | null;
child2: string | null;
child3: string | null;
child4: string | null;
privilege: string;
}
interface OrganizationCondition {
condition: string;
params: Record<string, any>;
}
interface NodeConfig {
repository: Repository<any>;
nameField: string;
condition: string;
isAllTrue: string;
paramKey: string;
parentIdField: string;
}
interface NodeParams {
[key: string]: string | null | undefined;
}
interface OrgParentName {
orgRootName: string | null;
orgChild1Name: string | null;
orgChild2Name: string | null;
orgChild3Name: string | null;
orgChild4Name: string | null;
}
export class ProfileLeaveService {
private profileEmployeeRepo: Repository<ProfileEmployee>;
private profileRepo: Repository<Profile>;
@ -40,6 +67,7 @@ export class ProfileLeaveService {
private child2Repository: Repository<OrgChild2>;
private child3Repository: Repository<OrgChild3>;
private child4Repository: Repository<OrgChild4>;
private readonly nodeConfigs: NodeConfig[];
constructor() {
this.profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee);
@ -49,6 +77,49 @@ export class ProfileLeaveService {
this.child2Repository = AppDataSource.getRepository(OrgChild2);
this.child3Repository = AppDataSource.getRepository(OrgChild3);
this.child4Repository = AppDataSource.getRepository(OrgChild4);
this.nodeConfigs = [
{
repository: this.orgRootRepository,
nameField: "orgRootName",
condition: "profileSalary.orgRoot = :orgRoot",
isAllTrue: "profileSalary.orgChild1 IS NULL",
paramKey: "orgRoot",
parentIdField: "",
},
{
repository: this.child1Repository,
nameField: "orgChild1Name",
condition: "profileSalary.orgChild1 = :orgChild1",
isAllTrue: "profileSalary.orgChild2 IS NULL",
paramKey: "orgChild1",
parentIdField: "orgRootId",
},
{
repository: this.child2Repository,
nameField: "orgChild2Name",
condition: "profileSalary.orgChild2 = :orgChild2",
isAllTrue: "profileSalary.orgChild3 IS NULL",
paramKey: "orgChild2",
parentIdField: "orgChild1Id",
},
{
repository: this.child3Repository,
nameField: "orgChild3Name",
condition: "profileSalary.orgChild3 = :orgChild3",
isAllTrue: "profileSalary.orgChild4 IS NULL",
paramKey: "orgChild3",
parentIdField: "orgChild2Id",
},
{
repository: this.child4Repository,
nameField: "orgChild4Name",
condition: "profileSalary.orgChild4 = :orgChild4",
isAllTrue: "",
paramKey: "orgChild4",
parentIdField: "orgChild3Id",
},
];
}
/** สร้าง query สำหรับการค้นหาตามฟิลด์ต่างๆ */
@ -69,132 +140,198 @@ export class ProfileLeaveService {
}
}
/** สร้างเงื่อนไขการค้นหาตาม node และ nodeId */
async buildNodeCondition(
node?: number,
nodeId?: string,
isAll?: boolean,
): Promise<OrganizationCondition> {
let condition = "1=1";
let nodeAll = "";
const params: Record<string, any> = {};
async findOrgNodeParentAll(node: number, nodeId: string): Promise<OrgParentName> {
const orgNames: OrgParentName = {
orgRootName: null,
orgChild1Name: null,
orgChild2Name: null,
orgChild3Name: null,
orgChild4Name: null,
};
if (!node || !nodeId) {
return { condition, params };
if (!nodeId || node < 0 || node >= this.nodeConfigs.length) {
return orgNames;
}
// สร้าง nodeAll condition - เพิ่มการตรวจสอบ IS NULL
if (isAll === false && node < 4) {
const nextLevels = ["orgChild1", "orgChild2", "orgChild3", "orgChild4"];
nodeAll = ` AND (profileSalary.${nextLevels[node]} IS NULL)`;
}
let currentNode = node;
let currentNodeId = nodeId;
try {
switch (node) {
case 0: {
const orgRoot = await this.orgRootRepository.findOne({ where: { id: nodeId } });
if (orgRoot) {
condition = "(profileSalary.orgRoot = :orgRoot)";
params.orgRoot = orgRoot.orgRootName;
}
break;
}
case 1: {
const orgChild1 = await this.child1Repository.findOne({ where: { id: nodeId } });
if (orgChild1) {
condition = "(profileSalary.orgChild1 = :orgChild1)";
params.orgChild1 = orgChild1.orgChild1Name;
}
break;
}
case 2: {
const orgChild2 = await this.child2Repository.findOne({ where: { id: nodeId } });
if (orgChild2) {
condition = "(profileSalary.orgChild2 = :orgChild2)";
params.orgChild2 = orgChild2.orgChild2Name;
}
break;
}
case 3: {
const orgChild3 = await this.child3Repository.findOne({ where: { id: nodeId } });
if (orgChild3) {
condition = "(profileSalary.orgChild3 = :orgChild3)";
params.orgChild3 = orgChild3.orgChild3Name;
}
break;
}
case 4: {
const orgChild4 = await this.child4Repository.findOne({ where: { id: nodeId } });
if (orgChild4) {
condition = "(profileSalary.orgChild4 = :orgChild4)";
params.orgChild4 = orgChild4.orgChild4Name;
}
break;
}
while (currentNode >= 0) {
const config = this.nodeConfigs[currentNode];
// Build select fields dynamically, excluding empty parentIdField
const selectFields = [config.nameField, "id"];
if (config.parentIdField && config.parentIdField.trim() !== "") {
selectFields.push(config.parentIdField);
}
const orgData = await config.repository.findOne({
where: { id: currentNodeId },
select: selectFields,
});
if (!orgData) {
break;
}
const orgName = orgData[config.nameField] || null;
if (orgName) {
orgNames[config.nameField as keyof OrgParentName] = orgName;
}
// Check if parentIdField exists and is not empty before accessing it
if (config.parentIdField && config.parentIdField.trim() !== "") {
currentNodeId = orgData[config.parentIdField];
currentNode -= 1;
} else {
// If no parent field (root level), break the loop
break;
}
} catch (error) {
console.error("Error building node condition:", error);
}
return { condition: condition + nodeAll, params };
return orgNames;
}
/** สร้างเงื่อนไขการค้นหาตาม permission */
async buildPermissionCondition(
request: RequestWithUser,
/** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */
async buildNodeCondition(
node: number,
nodeId: string,
isAll?: boolean,
permissionType: string = "SYS_REGISTRY_OFFICER",
): Promise<OrganizationCondition> {
const _data = await new permission().PermissionOrgList(request, permissionType);
let condition = "1=1";
let nodeAll = "";
const params: Record<string, any> = {};
try {
if (_data.root) {
const orgRootPms = await this.orgRootRepository.findOne({ where: { id: _data.root } });
if (orgRootPms) {
condition = "(profileSalary.orgRoot = :orgRootPms OR profileSalary.id IS NULL)";
params.orgRootPms = orgRootPms.orgRootName;
}
if (isAll === false)
nodeAll = " AND (profileSalary.orgChild1 IS NULL OR profileSalary.id IS NULL)";
} else if (_data.child1) {
const orgChild1Pms = await this.child1Repository.findOne({ where: { id: _data.child1 } });
if (orgChild1Pms) {
condition = "(profileSalary.orgChild1 = :orgChild1Pms OR profileSalary.id IS NULL)";
params.orgChild1Pms = orgChild1Pms.orgChild1Name;
}
if (isAll === false)
nodeAll = " AND (profileSalary.orgChild2 IS NULL OR profileSalary.id IS NULL)";
} else if (_data.child2) {
const orgChild2Pms = await this.child2Repository.findOne({ where: { id: _data.child2 } });
if (orgChild2Pms) {
condition = "(profileSalary.orgChild2 = :orgChild2Pms OR profileSalary.id IS NULL)";
params.orgChild2Pms = orgChild2Pms.orgChild2Name;
}
if (isAll === false)
nodeAll = " AND (profileSalary.orgChild3 IS NULL OR profileSalary.id IS NULL)";
} else if (_data.child3) {
const orgChild3Pms = await this.child3Repository.findOne({ where: { id: _data.child3 } });
if (orgChild3Pms) {
condition = "(profileSalary.orgChild3 = :orgChild3Pms OR profileSalary.id IS NULL)";
params.orgChild3Pms = orgChild3Pms.orgChild3Name;
}
if (isAll === false)
nodeAll = " AND (profileSalary.orgChild4 IS NULL OR profileSalary.id IS NULL)";
} else if (_data.child4) {
const orgChild4Pms = await this.child4Repository.findOne({ where: { id: _data.child4 } });
if (orgChild4Pms) {
condition = "(profileSalary.orgChild4 = :orgChild4Pms OR profileSalary.id IS NULL)";
params.orgChild4Pms = orgChild4Pms.orgChild4Name;
}
}
} catch (error) {
console.error("Error building permission condition:", error);
// Early return สำหรับ edge cases
if (!nodeId || node < 0 || node >= this.nodeConfigs.length) {
return { condition: "1=1", params: {} };
}
return { condition: condition + nodeAll, params };
let nodeCondition = "";
let params: NodeParams = {};
const orgLists = await this.findOrgNodeParentAll(node, nodeId);
console.log("Org Hierarchy for Node Condition:", orgLists);
await Promise.all(
this.nodeConfigs.map(async (config, index) => {
if (index <= node) {
const orgName = orgLists[config.nameField as keyof OrgParentName] || null;
if (orgName) {
nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition;
nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : "";
params[config.paramKey] = orgName;
}
}
}),
);
return {
condition: nodeCondition,
params,
};
}
async getOrgNameFromId(orgIds: {
root: string | null;
child1: string | null;
child2: string | null;
child3: string | null;
child4: string | null;
}): Promise<OrgParentName> {
const orgNames: OrgParentName = {
orgRootName: null,
orgChild1Name: null,
orgChild2Name: null,
orgChild3Name: null,
orgChild4Name: null,
};
if (orgIds.root) {
const rootName = await this.orgRootRepository.findOne({
where: { id: orgIds.root },
select: ["orgRootName"],
});
orgNames.orgRootName = rootName ? rootName.orgRootName : null;
}
if (orgIds.child1) {
const child1 = await this.child1Repository.findOne({
where: { id: orgIds.child1 },
select: ["orgChild1Name"],
});
orgNames.orgChild1Name = child1 ? child1.orgChild1Name : null;
}
if (orgIds.child2) {
const child2 = await this.child2Repository.findOne({
where: { id: orgIds.child2 },
select: ["orgChild2Name"],
});
orgNames.orgChild2Name = child2 ? child2.orgChild2Name : null;
}
if (orgIds.child3) {
const child3 = await this.child3Repository.findOne({
where: { id: orgIds.child3 },
select: ["orgChild3Name"],
});
orgNames.orgChild3Name = child3 ? child3.orgChild3Name : null;
}
if (orgIds.child4) {
const child4 = await this.child4Repository.findOne({
where: { id: orgIds.child4 },
select: ["orgChild4Name"],
});
orgNames.orgChild4Name = child4 ? child4.orgChild4Name : null;
}
return orgNames;
}
/** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */
async buildPermissionCondition(
_data: DataPermission,
isAll?: boolean,
): Promise<OrganizationCondition> {
// Early return สำหรับ OWNER privilege
if (_data.privilege === "OWNER") {
return { condition: "1=1", params: {} };
}
// const nodeFields = ["root", "child1", "child2", "child3", "child4"] as const;
let nodeCondition = "";
let params: NodeParams = {};
const orgLists = await this.getOrgNameFromId({
root: _data.root,
child1: _data.child1,
child2: _data.child2,
child3: _data.child3,
child4: _data.child4,
});
// console.log("Org Hierarchy for Permission Condition:", orgLists);
// check orgLists has at least one non-null value
if (
!orgLists.orgRootName &&
!orgLists.orgChild1Name &&
!orgLists.orgChild2Name &&
!orgLists.orgChild3Name &&
!orgLists.orgChild4Name
) {
return { condition: "1=0", params: {} }; // no access
}
await Promise.all(
this.nodeConfigs.map(async (config, index) => {
const orgName = orgLists[config.nameField as keyof OrgParentName] || null;
if (orgName) {
nodeCondition += index > 0 ? ` AND ${config.condition}` : config.condition;
nodeCondition += isAll === false && config.isAllTrue ? ` AND ${config.isAllTrue}` : "";
params[config.paramKey] = orgName;
}
}),
);
return {
condition: nodeCondition,
params,
};
}
/** แปลงข้อมูลลูกจ้างก่อน response */
@ -338,14 +475,9 @@ export class ProfileLeaveService {
retireType,
sortBy = "profileEmployee.dateLeave",
sort,
_data,
} = filter;
// สร้าง query conditions แบบ parallel
const [nodeCondition, permissionCondition] = await Promise.all([
this.buildNodeCondition(node, nodeId, isAll),
this.buildPermissionCondition(request, isAll, "SYS_REGISTRY_EMP"),
]);
const searchQuery = this.buildSearchQuery(searchField, "profileEmployee");
// สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary
@ -381,14 +513,9 @@ export class ProfileLeaveService {
.andWhere("profileEmployee.employeeClass LIKE :type", { type: "PERM" })
.andWhere(
new Brackets((qb) => {
qb.orWhere(
searchKeyword != undefined && searchKeyword != null && searchKeyword != ""
? searchQuery
: "1=1",
{
keyword: `%${searchKeyword}%`,
},
);
qb.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", {
keyword: `%${searchKeyword}%`,
});
}),
);
@ -404,7 +531,7 @@ export class ProfileLeaveService {
);
}
if (isProbation !== undefined && isProbation !== null) {
if (isProbation) {
queryBuilder.andWhere(`profileEmployee.isProbation = ${isProbation}`);
}
@ -412,13 +539,21 @@ export class ProfileLeaveService {
queryBuilder.andWhere("profileEmployee.leaveType = :retireType", { retireType });
}
// เพิ่ม permission และ node conditions
queryBuilder
.andWhere(permissionCondition.condition, permissionCondition.params)
.andWhere(nodeCondition.condition, nodeCondition.params);
if (node !== null && node !== undefined && nodeId) {
const [nodeCondition, permissionCondition] = await Promise.all([
this.buildNodeCondition(node, nodeId, isAll),
this.buildPermissionCondition(_data, isAll),
]);
// console.log("Permission Condition:", permissionCondition);
// console.log("Node Condition:", nodeCondition);
queryBuilder.andWhere(nodeCondition.condition, nodeCondition.params);
if (_data.privilege !== "OWNER") {
queryBuilder.andWhere(permissionCondition.condition, permissionCondition.params);
}
}
console.log("Permission Condition:", permissionCondition);
console.log("Node Condition:", nodeCondition);
// เพิ่ม sorting และ pagination
queryBuilder
.orderBy(sortBy, sort)
@ -430,7 +565,6 @@ export class ProfileLeaveService {
// print query for debug
// console.log("SQL Query:", queryBuilder.getSql());
// แปลงข้อมูลแบบ parallel
const data = await Promise.all(
records.map((record) => Promise.resolve(this.transformEmployeeData(record))),
);
@ -441,7 +575,7 @@ export class ProfileLeaveService {
/**
* response
*/
transformOfficerData(employee: Profile): any {
transformOfficerData(employee: Profile) {
// ตรวจสอบว่า profileSalary มีข้อมูลหรือไม่
const salary =
employee.profileSalary && employee.profileSalary.length > 0
@ -512,14 +646,9 @@ export class ProfileLeaveService {
retireType,
sortBy = "profile.dateLeave",
sort,
_data,
} = 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
@ -554,18 +683,12 @@ export class ProfileLeaveService {
)
.andWhere(
new Brackets((qb) => {
qb.orWhere(
searchKeyword != undefined && searchKeyword != null && searchKeyword != ""
? searchQuery
: "1=1",
{
keyword: `%${searchKeyword}%`,
},
);
qb.orWhere(searchKeyword && searchKeyword != "" ? searchQuery : "1=1", {
keyword: `%${searchKeyword}%`,
});
}),
);
// เพิ่มเงื่อนไขการค้นหา
if (posType) {
queryBuilder.andWhere("posType.posTypeName LIKE :keyword1", { keyword1: `${posType}` });
}
@ -574,7 +697,7 @@ export class ProfileLeaveService {
queryBuilder.andWhere("posLevel.posLevelName LIKE :keyword2", { keyword2: `${posLevel}` });
}
if (isProbation !== undefined && isProbation !== null) {
if (isProbation) {
queryBuilder.andWhere(`profile.isProbation = ${isProbation}`);
}
@ -583,9 +706,21 @@ export class ProfileLeaveService {
}
// เพิ่ม permission และ node conditions
queryBuilder
.andWhere(permissionCondition.condition, permissionCondition.params)
.andWhere(nodeCondition.condition, nodeCondition.params);
if (node !== null && node !== undefined && nodeId) {
// สร้าง query conditions แบบ parallel
const [nodeCondition, permissionCondition] = await Promise.all([
this.buildNodeCondition(node, nodeId, isAll),
this.buildPermissionCondition(_data, isAll),
]);
console.log("Permission Condition:", permissionCondition);
console.log("Node Condition:", nodeCondition);
queryBuilder.andWhere(nodeCondition.condition, nodeCondition.params);
if (_data.privilege !== "OWNER") {
queryBuilder.andWhere(permissionCondition.condition, permissionCondition.params);
}
}
// เพิ่ม sorting และ pagination
queryBuilder
@ -598,7 +733,6 @@ export class ProfileLeaveService {
// print query for debug
// console.log("SQL Query:", queryBuilder.getSql());
// แปลงข้อมูลแบบ parallel
const data = await Promise.all(
records.map((record) => Promise.resolve(this.transformOfficerData(record))),
);