2025-10-02 18:42:14 +07:00
import { AppDataSource } from "../database/data-source" ;
2025-10-03 13:05:24 +07:00
import { Profile } from "./../entities/Profile" ;
2025-10-02 18:42:14 +07:00
import { ProfileEmployee } from "../entities/ProfileEmployee" ;
import { OrgRoot } from "../entities/OrgRoot" ;
import { OrgChild1 } from "../entities/OrgChild1" ;
import { OrgChild2 } from "../entities/OrgChild2" ;
import { OrgChild3 } from "../entities/OrgChild3" ;
import { OrgChild4 } from "../entities/OrgChild4" ;
import { Brackets , Repository } from "typeorm" ;
import Extension from "../interfaces/extension" ;
import { RequestWithUser } from "../middlewares/user" ;
2025-10-04 13:54:17 +07:00
interface LeaveFilter {
2025-10-02 18:42:14 +07:00
page : number ;
pageSize : number ;
searchField ? : "firstName" | "lastName" | "fullName" | "citizenId" | "position" | "posNo" ;
searchKeyword? : string ;
posType? : string ;
posLevel? : string ;
isProbation? : boolean ;
node? : number ;
nodeId? : string ;
isAll? : boolean ;
retireType? : string ;
sortBy? : string ;
sort : "ASC" | "DESC" ;
2025-10-04 13:54:17 +07:00
_data : DataPermission ;
2025-10-02 18:42:14 +07:00
}
2025-10-04 13:54:17 +07:00
interface DataPermission {
root : string | null ;
child1 : string | null ;
child2 : string | null ;
child3 : string | null ;
child4 : string | null ;
privilege : string ;
}
interface OrganizationCondition {
2025-10-02 18:42:14 +07:00
condition : string ;
params : Record < string , any > ;
}
2025-10-04 13:54:17 +07:00
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 ;
}
2025-10-02 18:42:14 +07:00
export class ProfileLeaveService {
2025-10-03 13:05:24 +07:00
private profileEmployeeRepo : Repository < ProfileEmployee > ;
private profileRepo : Repository < Profile > ;
2025-10-02 18:42:14 +07:00
private orgRootRepository : Repository < OrgRoot > ;
private child1Repository : Repository < OrgChild1 > ;
private child2Repository : Repository < OrgChild2 > ;
private child3Repository : Repository < OrgChild3 > ;
private child4Repository : Repository < OrgChild4 > ;
2025-10-04 13:54:17 +07:00
private readonly nodeConfigs : NodeConfig [ ] ;
2025-10-02 18:42:14 +07:00
constructor ( ) {
2025-10-03 13:05:24 +07:00
this . profileEmployeeRepo = AppDataSource . getRepository ( ProfileEmployee ) ;
this . profileRepo = AppDataSource . getRepository ( Profile ) ;
2025-10-02 18:42:14 +07:00
this . orgRootRepository = AppDataSource . getRepository ( OrgRoot ) ;
this . child1Repository = AppDataSource . getRepository ( OrgChild1 ) ;
this . child2Repository = AppDataSource . getRepository ( OrgChild2 ) ;
this . child3Repository = AppDataSource . getRepository ( OrgChild3 ) ;
this . child4Repository = AppDataSource . getRepository ( OrgChild4 ) ;
2025-10-04 13:54:17 +07:00
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" ,
} ,
] ;
2025-10-02 18:42:14 +07:00
}
2025-10-03 13:05:24 +07:00
/** สร้าง query สำหรับการค้นหาตามฟิลด์ต่างๆ */
buildSearchQuery ( searchField? : string , type : string = "profile" ) : string {
2025-10-02 18:42:14 +07:00
switch ( searchField ) {
case "citizenId" :
2025-10-03 13:05:24 +07:00
return ` ${ type } .citizenId LIKE :keyword ` ;
2025-10-02 18:42:14 +07:00
case "position" :
2025-10-03 13:05:24 +07:00
return ` ${ type } .position LIKE :keyword ` ;
2025-10-02 18:42:14 +07:00
case "posNo" :
return `
2025-10-03 13:05:24 +07:00
( CONCAT ( profileSalary . posNoAbb , profileSalary . posNo ) LIKE :keyword )
OR ( CONCAT ( profileSalary . posNoAbb , " " , profileSalary . posNo ) LIKE :keyword )
OR ( profileSalary . posNo LIKE :keyword )
2025-10-02 18:42:14 +07:00
` ;
default :
2025-10-03 13:05:24 +07:00
return ` CONCAT( ${ type } .prefix, ${ type } .firstName, ' ', ${ type } .lastName) LIKE :keyword ` ;
2025-10-02 18:42:14 +07:00
}
}
2025-10-04 13:54:17 +07:00
async findOrgNodeParentAll ( node : number , nodeId : string ) : Promise < OrgParentName > {
const orgNames : OrgParentName = {
orgRootName : null ,
orgChild1Name : null ,
orgChild2Name : null ,
orgChild3Name : null ,
orgChild4Name : null ,
} ;
if ( ! nodeId || node < 0 || node >= this . nodeConfigs . length ) {
return orgNames ;
}
let currentNode = node ;
let currentNodeId = nodeId ;
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 ;
}
}
return orgNames ;
}
/** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */
2025-10-02 18:42:14 +07:00
async buildNodeCondition (
2025-10-04 13:54:17 +07:00
node : number ,
nodeId : string ,
2025-10-02 18:42:14 +07:00
isAll? : boolean ,
) : Promise < OrganizationCondition > {
2025-10-04 13:54:17 +07:00
// Early return สำหรับ edge cases
if ( ! nodeId || node < 0 || node >= this . nodeConfigs . length ) {
return { condition : "1=1" , 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 ;
}
}
} ) ,
) ;
2025-10-02 18:42:14 +07:00
2025-10-04 13:54:17 +07:00
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 ;
2025-10-02 18:42:14 +07:00
}
2025-10-04 13:54:17 +07:00
if ( orgIds . child2 ) {
const child2 = await this . child2Repository . findOne ( {
where : { id : orgIds.child2 } ,
select : [ "orgChild2Name" ] ,
} ) ;
orgNames . orgChild2Name = child2 ? child2.orgChild2Name : null ;
2025-10-02 18:42:14 +07:00
}
2025-10-04 13:54:17 +07:00
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 ;
2025-10-02 18:42:14 +07:00
}
2025-10-04 13:54:17 +07:00
return orgNames ;
2025-10-02 18:42:14 +07:00
}
2025-10-04 13:54:17 +07:00
/** สร้างเงื่อนไขการค้นหาตาม node และ nodeId และเช็คกับ permission */
2025-10-02 18:42:14 +07:00
async buildPermissionCondition (
2025-10-04 13:54:17 +07:00
_data : DataPermission ,
2025-10-02 18:42:14 +07:00
isAll? : boolean ,
) : Promise < OrganizationCondition > {
2025-10-04 13:54:17 +07:00
// Early return สำหรับ OWNER privilege
2026-02-20 11:46:46 +07:00
if ( _data . privilege === "OWNER" || _data . privilege === "PARENT" ) {
2025-10-04 13:54:17 +07:00
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
2025-10-02 18:42:14 +07:00
}
2025-10-04 13:54:17 +07:00
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 ,
} ;
2025-10-02 18:42:14 +07:00
}
2025-10-03 13:05:24 +07:00
/** แปลงข้อมูลลูกจ้างก่อน response */
2025-10-02 18:42:14 +07:00
transformEmployeeData ( employee : ProfileEmployee ) : any {
const dateEmployment =
employee . profileEmployeeEmployment ? . length === 0 || ! employee . profileEmployeeEmployment
? null
: employee . profileEmployeeEmployment . reduce ( ( latest , current ) = > {
return latest . date > current . date ? latest : current ;
} ) . date ;
// ตรวจสอบว่า 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 || "" ;
// สร้าง organization hierarchy - ใช้ข้อมูลจาก temp fields ถ้า salary ไม่มี
const org = salary
? [ salary . orgChild4 , salary . orgChild3 , salary . orgChild2 , salary . orgChild1 , salary . orgRoot ]
. filter ( Boolean )
. join ( "\n" )
: [
employee . child4Temp ,
employee . child3Temp ,
employee . child2Temp ,
employee . child1Temp ,
employee . rootTemp ,
]
. filter ( Boolean )
. join ( "\n" ) ;
// สร้าง node information
const getNodeInfo = ( nodeTemp : string ) = > {
switch ( nodeTemp ) {
case "0" :
return {
name : employee.rootTemp ,
shortName : employee.rootShortNameTemp ,
} ;
case "1" :
return {
name : employee.child1Temp ,
shortName : employee.child1ShortNameTemp ,
} ;
case "2" :
return {
name : employee.child2Temp ,
shortName : employee.child2ShortNameTemp ,
} ;
case "3" :
return {
name : employee.child3Temp ,
shortName : employee.child3ShortNameTemp ,
} ;
case "4" :
return {
name : employee.child4Temp ,
shortName : employee.child4ShortNameTemp ,
} ;
default :
return { name : null , shortName : null } ;
}
} ;
const nodeInfo = getNodeInfo ( employee . nodeTemp || "0" ) ;
return {
id : employee.id ,
avatar : employee.avatar ,
avatarName : employee.avatarName ,
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 ,
posTypeShortName : employee.posType?.posTypeShortName || null ,
posLevelId : employee.posLevel?.id || null ,
posTypeId : employee.posType?.id || null ,
positionId : employee.positionIdTemp ,
posmasterId : employee.posmasterIdTemp ,
position : employee.position ,
posNo ,
employeeClass : employee.employeeClass ,
govAge : Extension.CalculateGovAge ( employee . dateAppoint , 0 , 0 ) ,
age : Extension.CalculateAgeStrV2 ( employee . birthDate , 0 , 0 , "GET" ) ,
dateEmployment ,
dateAppoint : employee.dateAppoint ,
dateStart : employee.dateStart ,
createdAt : employee.createdAt ,
dateRetireLaw : employee.dateRetireLaw ,
draftOrganizationOrganization : nodeInfo.name ,
draftPositionEmployee : employee.positionTemp ,
draftOrgEmployeeStatus : employee.statusTemp ,
node : employee.nodeTemp ,
nodeId : employee.nodeIdTemp ,
nodeName : nodeInfo.name ,
nodeShortName : nodeInfo.shortName ,
root : employee.rootTemp || null ,
rootId : employee.rootIdTemp || null ,
rootShortName : employee.rootShortNameTemp || null ,
child1 : employee.child1Temp || null ,
child1Id : employee.child1IdTemp || null ,
child1ShortName : employee.child1ShortNameTemp || null ,
child2 : employee.child2Temp || null ,
child2Id : employee.child2IdTemp || null ,
child2ShortName : employee.child2ShortNameTemp || null ,
child3 : employee.child3Temp || null ,
child3Id : employee.child3IdTemp || null ,
child3ShortName : employee.child3ShortNameTemp || null ,
child4 : employee.child4Temp || null ,
child4Id : employee.child4IdTemp || null ,
child4ShortName : employee.child4ShortNameTemp || null ,
org ,
} ;
}
2025-10-03 13:05:24 +07:00
/** ค้นหาลูกจ้างที่พ้นจากราชการ */
2025-10-02 18:42:14 +07:00
async getLeaveEmployees (
request : RequestWithUser ,
2025-10-03 13:05:24 +07:00
filter : LeaveFilter ,
2025-10-02 18:42:14 +07:00
) : Promise < { data : any [ ] ; total : number } > {
const {
page ,
pageSize ,
searchField ,
searchKeyword = "" ,
posType ,
posLevel ,
isProbation ,
node ,
nodeId ,
isAll ,
retireType ,
sortBy = "profileEmployee.dateLeave" ,
sort ,
2025-10-04 13:54:17 +07:00
_data ,
2025-10-02 18:42:14 +07:00
} = filter ;
2025-10-03 13:05:24 +07:00
const searchQuery = this . buildSearchQuery ( searchField , "profileEmployee" ) ;
2025-10-02 18:42:14 +07:00
// สร้าง main query - เปลี่ยนจาก leftJoinAndSelect เป็น leftJoin สำหรับ profileSalary
2025-10-03 13:05:24 +07:00
const queryBuilder = this . profileEmployeeRepo
2025-10-02 18:42:14 +07:00
. createQueryBuilder ( "profileEmployee" )
. leftJoinAndSelect ( "profileEmployee.posLevel" , "posLevel" )
. leftJoinAndSelect ( "profileEmployee.posType" , "posType" )
. leftJoinAndSelect ( "profileEmployee.profileEmployeeEmployment" , "profileEmployeeEmployment" )
. leftJoin (
"profileEmployee.profileSalary" ,
"profileSalary" ,
"profileSalary.order = (SELECT MAX(ps.order) FROM profileSalary ps WHERE ps.profileEmployeeId = profileEmployee.id and ps.positionName != 'เกษียณอายุราชการ')" ,
)
. addSelect ( [
"profileSalary.id" ,
"profileSalary.order" ,
"profileSalary.posNo" ,
"profileSalary.posNoAbb" ,
"profileSalary.orgRoot" ,
"profileSalary.orgChild1" ,
"profileSalary.orgChild2" ,
"profileSalary.orgChild3" ,
"profileSalary.orgChild4" ,
] )
. where (
new Brackets ( ( qb ) = > {
qb . where ( "profileEmployee.isLeave = :isLeave" , { isLeave : true } ) . orWhere (
"profileEmployee.isRetirement = :isRetirement" ,
{ isRetirement : true } ,
) ;
} ) ,
)
2025-10-03 13:05:24 +07:00
. andWhere ( "profileEmployee.employeeClass LIKE :type" , { type : "PERM" } )
. andWhere (
new Brackets ( ( qb ) = > {
2025-10-04 13:54:17 +07:00
qb . orWhere ( searchKeyword && searchKeyword != "" ? searchQuery : "1=1" , {
keyword : ` % ${ searchKeyword } % ` ,
} ) ;
2025-10-03 13:05:24 +07:00
} ) ,
) ;
2025-10-02 18:42:14 +07:00
// เพิ่มเงื่อนไขการค้นหา
if ( posType ) {
queryBuilder . andWhere ( "posType.posTypeName LIKE :keyword1" , { keyword1 : ` ${ posType } ` } ) ;
}
if ( posLevel ) {
queryBuilder . andWhere (
2025-10-03 13:05:24 +07:00
"CONCAT(posType.posTypeShortName, ' ', posLevel.posLevelName) LIKE :keyword2" ,
2025-10-02 18:42:14 +07:00
{ keyword2 : ` ${ posLevel } ` } ,
) ;
}
2025-10-04 13:54:17 +07:00
if ( isProbation ) {
2025-10-02 18:42:14 +07:00
queryBuilder . andWhere ( ` profileEmployee.isProbation = ${ isProbation } ` ) ;
}
if ( retireType ) {
queryBuilder . andWhere ( "profileEmployee.leaveType = :retireType" , { retireType } ) ;
}
2025-10-04 13:54:17 +07:00
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 ) ;
2026-02-20 11:46:46 +07:00
if ( _data . privilege !== "OWNER" && _data . privilege !== "PARENT" ) {
2025-10-04 13:54:17 +07:00
queryBuilder . andWhere ( permissionCondition . condition , permissionCondition . params ) ;
}
}
2025-10-03 13:05:24 +07:00
// เพิ่ม 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());
const data = await Promise . all (
records . map ( ( record ) = > Promise . resolve ( this . transformEmployeeData ( record ) ) ) ,
) ;
return { data , total } ;
}
/ * *
* แ ป ล ง ข ้ อ ม ู ล ล ู ก จ ้ า ง ก ่ อ น response
* /
2025-10-04 13:54:17 +07:00
transformOfficerData ( employee : Profile ) {
2025-10-03 13:05:24 +07:00
// ตรวจสอบว่า 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 ,
2025-10-04 13:54:17 +07:00
_data ,
2025-10-03 13:05:24 +07:00
} = filter ;
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 ) = > {
2025-10-04 13:54:17 +07:00
qb . orWhere ( searchKeyword && searchKeyword != "" ? searchQuery : "1=1" , {
keyword : ` % ${ searchKeyword } % ` ,
} ) ;
2025-10-03 13:05:24 +07:00
} ) ,
) ;
if ( posType ) {
queryBuilder . andWhere ( "posType.posTypeName LIKE :keyword1" , { keyword1 : ` ${ posType } ` } ) ;
}
if ( posLevel ) {
2025-10-03 16:44:43 +07:00
queryBuilder . andWhere ( "posLevel.posLevelName LIKE :keyword2" , { keyword2 : ` ${ posLevel } ` } ) ;
2025-10-03 13:05:24 +07:00
}
2025-10-04 13:54:17 +07:00
if ( isProbation ) {
2025-10-03 13:05:24 +07:00
queryBuilder . andWhere ( ` profile.isProbation = ${ isProbation } ` ) ;
}
if ( retireType ) {
queryBuilder . andWhere ( "profile.leaveType = :retireType" , { retireType } ) ;
2025-10-02 18:42:14 +07:00
}
// เพิ่ม permission และ node conditions
2025-10-04 13:54:17 +07:00
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 ) ;
2026-02-20 11:46:46 +07:00
if ( _data . privilege !== "OWNER" && _data . privilege !== "PARENT" ) {
2025-10-04 13:54:17 +07:00
queryBuilder . andWhere ( permissionCondition . condition , permissionCondition . params ) ;
}
}
2025-10-02 18:42:14 +07:00
// เพิ่ม sorting และ pagination
queryBuilder
. orderBy ( sortBy , sort )
. skip ( ( page - 1 ) * pageSize )
. take ( pageSize ) ;
const [ records , total ] = await queryBuilder . getManyAndCount ( ) ;
2025-10-03 13:05:24 +07:00
// print query for debug
// console.log("SQL Query:", queryBuilder.getSql());
2025-10-02 18:42:14 +07:00
const data = await Promise . all (
2025-10-03 13:05:24 +07:00
records . map ( ( record ) = > Promise . resolve ( this . transformOfficerData ( record ) ) ) ,
2025-10-02 18:42:14 +07:00
) ;
return { data , total } ;
}
}