diff --git a/src/app.ts b/src/app.ts index 236fb105..7f0e0220 100644 --- a/src/app.ts +++ b/src/app.ts @@ -13,12 +13,16 @@ import { OrganizationController } from "./controllers/OrganizationController"; import logMiddleware from "./middlewares/logs"; import { CommandController } from "./controllers/CommandController"; import { ProfileSalaryController } from "./controllers/ProfileSalaryController"; +import { DateSerializer } from "./interfaces/date-serializer"; import { initWebSocket } from "./services/webSocket"; async function main() { await AppDataSource.initialize(); + // Setup custom Date serialization for local timezone + DateSerializer.setupDateSerialization(); + initWebSocket(); const app = express(); diff --git a/src/controllers/EmployeePositionController.ts b/src/controllers/EmployeePositionController.ts index 86bf8bde..3b4d0f0c 100644 --- a/src/controllers/EmployeePositionController.ts +++ b/src/controllers/EmployeePositionController.ts @@ -1006,6 +1006,7 @@ export class EmployeePositionController extends Controller { */ @Post("master/list") async listEmp( + @Request() request: RequestWithUser, @Body() body: { id: string; @@ -1026,7 +1027,7 @@ export class EmployeePositionController extends Controller { let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; - + let _data = await new permission().PermissionOrgList(request, "SYS_ORG_EMP"); if (body.type === 0) { typeCondition = { orgRootId: body.id, @@ -1139,6 +1140,56 @@ export class EmployeePositionController extends Controller { .leftJoinAndSelect("positions.posType", "posType") .leftJoinAndSelect("positions.posLevel", "posLevel") .where(conditions) + .andWhere( + _data.root != undefined && _data.root != null + ? _data.root[0] != null + ? `posMaster.orgRootId IN (:...root)` + : `posMaster.orgRootId is null` + : "1=1", + { + root: _data.root, + }, + ) + .andWhere( + _data.child1 != undefined && _data.child1 != null + ? _data.child1[0] != null + ? `posMaster.orgChild1Id IN (:...child1)` + : `posMaster.orgChild1Id is null` + : "1=1", + { + child1: _data.child1, + }, + ) + .andWhere( + _data.child2 != undefined && _data.child2 != null + ? _data.child2[0] != null + ? `posMaster.orgChild2Id IN (:...child2)` + : `posMaster.orgChild2Id is null` + : "1=1", + { + child2: _data.child2, + }, + ) + .andWhere( + _data.child3 != undefined && _data.child3 != null + ? _data.child3[0] != null + ? `posMaster.orgChild3Id IN (:...child3)` + : `posMaster.orgChild3Id is null` + : "1=1", + { + child3: _data.child3, + }, + ) + .andWhere( + _data.child4 != undefined && _data.child4 != null + ? _data.child4[0] != null + ? `posMaster.orgChild4Id IN (:...child4)` + : `posMaster.orgChild4Id is null` + : "1=1", + { + child4: _data.child4, + }, + ) .orWhere( new Brackets((qb) => { qb.andWhere( @@ -1330,6 +1381,40 @@ export class EmployeePositionController extends Controller { }; }), ); + + if(_data.privilege === 'NORMAL'|| _data.privilege === 'PARENT'|| _data.privilege === 'CHILD'){ //PARENT จะไม่มีทางเห็น ROOT , CHILD ยึดจาก CHILD ที่อยู่ลงไปข้างล่างและจะไม่เห็น CHILD ที่อยู่เหนือกว่า + const nextChildMap:any = { //เอาไวเช็ค CHILD ถัดไป + 0: _data.child1, + 1: _data.child2, + 2: _data.child3, + 3: _data.child4, + }; + const childValue = nextChildMap[body.type]; + if(_data.privilege === 'NORMAL'){ + if (Array.isArray(childValue) && childValue.some(item => item != null)) { + return new HttpSuccess({ data: [], total: 0 }); + } + }else if(_data.privilege === 'PARENT'){ + if (body.type == 0){ + return new HttpSuccess({ data: [], total: 0 }); + } + } else if (_data.privilege === 'CHILD') { + const higherChildChecks = [ + { type: [0], child: _data.child1, next: _data.child2 }, + { type: [0, 1], child: _data.child2, next: _data.child3 }, + { type: [0, 1, 2], child: _data.child3, next: _data.child4 }, + { type: [0, 1, 2, 3], child: _data.child4, next: true }, + ]; + + for (const check of higherChildChecks) { + if (Array.isArray(check.child) && check.next == null) { + if (check.type.includes(body.type)) { + return new HttpSuccess({ data: [], total: 0 }); + } + } + } + } + } return new HttpSuccess({ data: formattedData, total }); } diff --git a/src/controllers/EmployeeTempPositionController.ts b/src/controllers/EmployeeTempPositionController.ts index 7f526c8d..50c58a4b 100644 --- a/src/controllers/EmployeeTempPositionController.ts +++ b/src/controllers/EmployeeTempPositionController.ts @@ -754,6 +754,7 @@ export class EmployeeTempPositionController extends Controller { */ @Post("master/list") async listEmp( + @Request() request: RequestWithUser, @Body() body: { id: string; @@ -774,7 +775,7 @@ export class EmployeeTempPositionController extends Controller { let searchShortName2 = `CONCAT(orgChild2.orgChild2ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; let searchShortName3 = `CONCAT(orgChild3.orgChild3ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; let searchShortName4 = `CONCAT(orgChild4.orgChild4ShortName," ",posMaster.posMasterNoPrefix,posMaster.posMasterNo,posMaster.posMasterNoSuffix)`; - + let _data = await new permission().PermissionOrgList(request, "SYS_ORG_TEMP"); if (body.type === 0) { typeCondition = { orgRootId: body.id, @@ -887,6 +888,56 @@ export class EmployeeTempPositionController extends Controller { .leftJoinAndSelect("positions.posType", "posType") .leftJoinAndSelect("positions.posLevel", "posLevel") .where(conditions) + .andWhere( + _data.root != undefined && _data.root != null + ? _data.root[0] != null + ? `posMaster.orgRootId IN (:...root)` + : `posMaster.orgRootId is null` + : "1=1", + { + root: _data.root, + }, + ) + .andWhere( + _data.child1 != undefined && _data.child1 != null + ? _data.child1[0] != null + ? `posMaster.orgChild1Id IN (:...child1)` + : `posMaster.orgChild1Id is null` + : "1=1", + { + child1: _data.child1, + }, + ) + .andWhere( + _data.child2 != undefined && _data.child2 != null + ? _data.child2[0] != null + ? `posMaster.orgChild2Id IN (:...child2)` + : `posMaster.orgChild2Id is null` + : "1=1", + { + child2: _data.child2, + }, + ) + .andWhere( + _data.child3 != undefined && _data.child3 != null + ? _data.child3[0] != null + ? `posMaster.orgChild3Id IN (:...child3)` + : `posMaster.orgChild3Id is null` + : "1=1", + { + child3: _data.child3, + }, + ) + .andWhere( + _data.child4 != undefined && _data.child4 != null + ? _data.child4[0] != null + ? `posMaster.orgChild4Id IN (:...child4)` + : `posMaster.orgChild4Id is null` + : "1=1", + { + child4: _data.child4, + }, + ) .orWhere( new Brackets((qb) => { qb.andWhere( @@ -1078,6 +1129,39 @@ export class EmployeeTempPositionController extends Controller { }; }), ); + if(_data.privilege === 'NORMAL'|| _data.privilege === 'PARENT'|| _data.privilege === 'CHILD'){ //PARENT จะไม่มีทางเห็น ROOT , CHILD ยึดจาก CHILD ที่อยู่ลงไปข้างล่างและจะไม่เห็น CHILD ที่อยู่เหนือกว่า + const nextChildMap:any = { //เอาไวเช็ค CHILD ถัดไป + 0: _data.child1, + 1: _data.child2, + 2: _data.child3, + 3: _data.child4, + }; + const childValue = nextChildMap[body.type]; + if(_data.privilege === 'NORMAL'){ + if (Array.isArray(childValue) && childValue.some(item => item != null)) { + return new HttpSuccess({ data: [], total: 0 }); + } + }else if(_data.privilege === 'PARENT'){ + if (body.type == 0){ + return new HttpSuccess({ data: [], total: 0 }); + } + } else if (_data.privilege === 'CHILD') { + const higherChildChecks = [ + { type: [0], child: _data.child1, next: _data.child2 }, + { type: [0, 1], child: _data.child2, next: _data.child3 }, + { type: [0, 1, 2], child: _data.child3, next: _data.child4 }, + { type: [0, 1, 2, 3], child: _data.child4, next: true }, + ]; + + for (const check of higherChildChecks) { + if (Array.isArray(check.child) && check.next == null) { + if (check.type.includes(body.type)) { + return new HttpSuccess({ data: [], total: 0 }); + } + } + } + } + } return new HttpSuccess({ data: formattedData, total }); } @@ -1958,6 +2042,7 @@ export class EmployeeTempPositionController extends Controller { }, relations: [ "positions", + "positions.posType", "orgRevision", "orgRoot", "orgChild1", @@ -1993,6 +2078,7 @@ export class EmployeeTempPositionController extends Controller { profile.posTypeId = position?.posTypeId ?? _null; profile.position = position?.positionName ?? _null; profile.employeeOc = Org ?? _null; + profile.positionEmployeeGroupId = position?.posType?.posTypeName ?? _null; profile.positionEmployeePositionId = position?.positionName ?? _null; await this.profileRepository.save(profile); } @@ -2029,22 +2115,22 @@ export class EmployeeTempPositionController extends Controller { if (!dataMaster) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลตำแหน่งนี้"); } - if (dataMaster.current_holderId != null) { - const profile = await this.profileRepository.findOne({ - where: { - id: dataMaster.current_holderId, - }, - }); - const _null: any = null; - if (profile != null) { - profile.posLevelId = _null; - profile.posTypeId = _null; - profile.position = _null; - profile.employeeOc = _null; - profile.positionEmployeePositionId = _null; - await this.profileRepository.save(profile); - } - } + // if (dataMaster.current_holderId != null) { + // const profile = await this.profileRepository.findOne({ + // where: { + // id: dataMaster.current_holderId, + // }, + // }); + // const _null: any = null; + // if (profile != null) { + // profile.posLevelId = _null; + // profile.posTypeId = _null; + // profile.position = _null; + // profile.employeeOc = _null; + // profile.positionEmployeePositionId = _null; + // await this.profileRepository.save(profile); + // } + // } await this.employeeTempPosMasterRepository.update(id, { isSit: false, diff --git a/src/controllers/OrganizationController.ts b/src/controllers/OrganizationController.ts index 8544c1f3..142d8033 100644 --- a/src/controllers/OrganizationController.ts +++ b/src/controllers/OrganizationController.ts @@ -6108,7 +6108,7 @@ export class OrganizationController extends Controller { if (!orgRevision) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูล"); } - let _data = { + let _data:any = { root: null, child1: null, child2: null, @@ -6121,6 +6121,62 @@ export class OrganizationController extends Controller { ) { _data = await new permission().PermissionOrgList(request, system.trim().toUpperCase()); } + + const profile = await this.profileRepo.findOne({ + where: { keycloak: request.user.sub }, + relations: ["permissionProfiles", "current_holders"], + }); + + if (!profile) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งานในทะเบียนประวัติ"); + } + + let _privilege = await new permission().PermissionOrgList(request, system); + const attrOwnership = _privilege.root === null ? true : false; + + if (orgRevision.orgRevisionIsDraft && !orgRevision.orgRevisionIsCurrent && !attrOwnership) { + if(Array.isArray(profile.permissionProfiles) && profile.permissionProfiles.length > 0){ + _data.root = profile.permissionProfiles.map((x) => x.orgRootId); + }else{ + return new HttpSuccess({ remark: "", data: [] }); + } + } + + // กำหนดการเข้าถึงข้อมูลตามสถานะและสิทธิ์ + const isCurrentActive = !orgRevision.orgRevisionIsDraft && orgRevision.orgRevisionIsCurrent; + if (isCurrentActive) { + if(_privilege.privilege !== "OWNER"){ + if(_privilege.privilege == "NORMAL"){ + const holder = profile.current_holders.find(x => x.orgRevisionId === id); + if (!holder) return; + _data.root = [holder.orgRootId]; + _data.child1 = [holder.orgChild1Id]; + _data.child2 = [holder.orgChild2Id]; + _data.child3 = [holder.orgChild3Id]; + _data.child4 = [holder.orgChild4Id]; + }else if(_privilege.privilege == "CHILD"){ + const holder = profile.current_holders.find(x => x.orgRevisionId === id); + if (!holder) return; + _data.root = [holder.orgRootId]; + if (_privilege.root && _privilege.child1 === null) { + } else if (_privilege.child1 && _privilege.child2 === null) { + _data.child1 = [holder.orgChild1Id]; + } else if (_privilege.child2 && _privilege.child3 === null) { + _data.child1 = [holder.orgChild1Id]; + _data.child2 = [holder.orgChild2Id]; + } else if (_privilege.child3 && _privilege.child4 === null) { + _data.child1 = [holder.orgChild1Id]; + _data.child2 = [holder.orgChild2Id]; + _data.child3 = [holder.orgChild3Id]; + _data.child4 = [holder.orgChild4Id]; + } + }else{ + _data.root = [profile.current_holders.find((x) => x.orgRevisionId === id)?.orgRootId]; + } + } else { + if (!attrOwnership) _data = _privilege; + } + } const orgRootData = await AppDataSource.getRepository(OrgRoot) .createQueryBuilder("orgRoot") diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 52de0922..3ce5a3e0 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -19,6 +19,7 @@ import { AppDataSource } from "../database/data-source"; import HttpSuccess from "../interfaces/http-success"; import HttpStatus from "../interfaces/http-status"; import HttpError from "../interfaces/http-error"; +import HttpStatusCode from "../interfaces/http-status"; import { Brackets, Double, In, IsNull, Like, Not } from "typeorm"; import { OrgRevision } from "../entities/OrgRevision"; import { @@ -2039,9 +2040,13 @@ export class ProfileEmployeeController extends Controller { if (!result) { throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); } - await new permission().PermissionOrgUserDelete(request, "SYS_REGISTRY_EMP", result.id); - await this.informationHistoryRepository.delete({ profileEmployeeId: id }); - await this.profileRepo.remove(result); + try{ + await new permission().PermissionOrgUserDelete(request, "SYS_REGISTRY_EMP", result.id); + await this.informationHistoryRepository.delete({ profileEmployeeId: id }); + await this.profileRepo.remove(result); + } catch { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่สามารถลบข้อมูลได้ เนื่องจากข้อมูลนี้ถูกใช้งานในระบบอื่น"); + } return new HttpSuccess(); } diff --git a/src/controllers/ProfileEmployeeTempController.ts b/src/controllers/ProfileEmployeeTempController.ts index 6dc49b1a..e843b218 100644 --- a/src/controllers/ProfileEmployeeTempController.ts +++ b/src/controllers/ProfileEmployeeTempController.ts @@ -69,6 +69,7 @@ import axios from "axios"; import { deleteUser } from "../keycloak"; import { ProfileSalaryHistory } from "../entities/ProfileSalaryHistory"; import { getTopDegrees } from "../services/PositionService"; +import HttpStatusCode from "../interfaces/http-status"; @Route("api/v1/org/profile-temp") @Tags("ProfileEmployee") @Security("bearerAuth") @@ -1027,8 +1028,13 @@ export class ProfileEmployeeTempController extends Controller { if (!result) { throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); } - await this.informationHistoryRepository.delete({ profileEmployeeId: id }); - await this.profileRepo.remove(result); + + try{ + await this.informationHistoryRepository.delete({ profileEmployeeId: id }); + await this.profileRepo.remove(result); + } catch { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่สามารถลบข้อมูลได้ เนื่องจากข้อมูลนี้ถูกใช้งานในระบบอื่น"); + } return new HttpSuccess(); } diff --git a/src/interfaces/date-serializer.ts b/src/interfaces/date-serializer.ts new file mode 100644 index 00000000..5c876ffc --- /dev/null +++ b/src/interfaces/date-serializer.ts @@ -0,0 +1,24 @@ +// Custom Date serializer for local timezone +export class DateSerializer { + static toLocalTime(date: Date): string | null { + if (!date) return null; + + // Convert UTC date to Thailand timezone (+07:00) + const offset = 7 * 60; // Thailand is UTC+7 + const localTime = new Date(date.getTime() + offset * 60 * 1000); + + // Format as ISO string but replace Z with +07:00 + const isoString = localTime.toISOString(); + return isoString.replace("Z", "+07:00"); + } + + static setupDateSerialization() { + // Override Date.prototype.toJSON to use local time + Date.prototype.toJSON = function () { + const offset = 7 * 60; // Thailand timezone offset in minutes + const localTime = new Date(this.getTime() + offset * 60 * 1000); + const isoString = localTime.toISOString(); + return isoString.replace("Z", "+07:00"); + }; + } +} diff --git a/src/middlewares/logs.ts b/src/middlewares/logs.ts index 1a4e00cb..ce4802d2 100644 --- a/src/middlewares/logs.ts +++ b/src/middlewares/logs.ts @@ -69,6 +69,12 @@ async function logMiddleware(req: Request, res: Response, next: NextFunction) { if (req.url.startsWith("/api/v1/org/profile/")) system = "registry"; if (req.url.startsWith("/api/v1/org/profile-employee/")) system = "registry"; if (req.url.startsWith("/api/v1/org/profile-temp/")) system = "registry"; + + if (req.url.startsWith("/api/v1/org/commandType/admin")) system = "admin"; + if (req.url.startsWith("/api/v1/org/commandSys/")) system = "admin"; + if (req.url.startsWith("/api/v1/org/commandSalary/")) system = "admin"; + if (req.url.startsWith("/api/v1/org/apiKey/")) system = "admin"; + if (req.url.startsWith("/api/v1/org/api-manage/")) system = "admin"; const level = LOG_LEVEL_MAP[process.env.LOG_LEVEL ?? "debug"] || 4; const profileByKeycloak = await repoProfile.findOne({