import { Controller, Post, Put, Patch, Delete, Route, Security, Tags, Body, Path, Request, Response, Get, Query, } from "tsoa"; import { AppDataSource } from "../database/data-source"; import HttpStatus from "../interfaces/http-status"; import HttpSuccess from "../interfaces/http-success"; import HttpStatusCode from "../interfaces/http-status"; import HttpError from "../interfaces/http-error"; import { Command } from "../entities/Command"; import { Brackets, LessThan, MoreThan, Double, In, Between, IsNull, Not, Any } from "typeorm"; import { CommandType } from "../entities/CommandType"; import { Profile, CreateProfileAllFields } from "../entities/Profile"; import { RequestWithUser, RequestWithUserWebService } from "../middlewares/user"; import { OrgRevision } from "../entities/OrgRevision"; import { ProfileEmployee } from "../entities/ProfileEmployee"; import { PosMaster } from "../entities/PosMaster"; import permission from "../interfaces/permission"; import { viewCurrentTenureOfficer } from "../entities/view/viewCurrentTenureOfficer"; import { CommandController } from "./CommandController"; import Extension from "../interfaces/extension"; import { viewRegistryOfficer } from "../entities/view/viewRegistryOfficer"; import { viewRegistryEmployee } from "../entities/view/viewRegistryEmployee"; import { Registry } from "../entities/Registry"; import { RegistryEmployee } from "../entities/RegistryEmployee"; import { TenurePositionOfficer } from "../entities/TenurePositionOfficer"; import { PosMasterAssign, PosMasterAssignDTO } from "../entities/PosMasterAssign"; import { PermissionProfile } from "../entities/PermissionProfile"; import { OrgRoot } from "../entities/OrgRoot"; import { MetaWorkflow } from "../entities/MetaWorkflow"; import { MetaState } from "../entities/MetaState"; import { MetaStateOperator } from "../entities/MetaStateOperator"; import { Workflow } from "../entities/Workflow"; import { State } from "../entities/State"; import { StateOperator } from "../entities/StateOperator"; import { StateOperatorUser } from "../entities/StateOperatorUser"; import { commandTypePath, calculateGovAge, calculateAge, calculateRetireDate, calculateRetireLaw, removeProfileInOrganize, setLogDataDiff, } from "../interfaces/utils"; import CallAPI from "../interfaces/call-api"; import { PostRetireToExprofile } from "./ExRetirementController" import { Position } from "../entities/Position"; import { PosLevel } from "../entities/PosLevel"; import { TenureLevelOfficer } from "../entities/TenureLevelOfficer"; import { TenurePositionEmployee } from "../entities/TenurePositionEmployee"; import { TenureLevelEmployee } from "../entities/TenureLevelEmployee"; import { TenurePositionExecutiveOfficer } from "../entities/TenurePositionExecutiveOfficer"; @Route("api/v1/org/DevTest") @Tags("DevTest") @Security("bearerAuth") @Response( HttpStatusCode.INTERNAL_SERVER_ERROR, "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", ) export class DevTestController extends Controller { private commandRepository = AppDataSource.getRepository(Command); private commandTypeRepository = AppDataSource.getRepository(CommandType); private orgRevisionRepo = AppDataSource.getRepository(OrgRevision); private orgRootRepo = AppDataSource.getRepository(OrgRoot); private posMasterRepo = AppDataSource.getRepository(PosMaster); private profileRepo = AppDataSource.getRepository(Profile); private profileEmpRepo = AppDataSource.getRepository(ProfileEmployee); private registryRepo = AppDataSource.getRepository(Registry); private registryEmployeeRepo = AppDataSource.getRepository(RegistryEmployee); private posMasterAssignRepository = AppDataSource.getRepository(PosMasterAssign); private permissionProfilesRepository = AppDataSource.getRepository(PermissionProfile); private profileEmployeeRepo = AppDataSource.getRepository(ProfileEmployee); private metaWorkflowRepo = AppDataSource.getRepository(MetaWorkflow); private metaStateRepo = AppDataSource.getRepository(MetaState); private metaStateOperatorRepo = AppDataSource.getRepository(MetaStateOperator); private workflowRepo = AppDataSource.getRepository(Workflow); private stateRepo = AppDataSource.getRepository(State); private stateOperatorRepo = AppDataSource.getRepository(StateOperator); private stateOperatorUserRepo = AppDataSource.getRepository(StateOperatorUser); private positionRepository = AppDataSource.getRepository(Position); private positionOfficerRepo = AppDataSource.getRepository(TenurePositionOfficer); private positionEmployeeRepo = AppDataSource.getRepository(TenurePositionEmployee); private levelOfficerRepo = AppDataSource.getRepository(TenureLevelOfficer); private levelEmployeeRepo = AppDataSource.getRepository(TenureLevelEmployee); private positionExecutiveOfficerRepo = AppDataSource.getRepository( TenurePositionExecutiveOfficer, ); @Patch("tick-officer-registry") public async calculateOfficerPosition( @Request() req: RequestWithUser, @Body() body: { profileIds: string[]; }, ) { console.log("1.") /** * =============================== * PREPARE DATA * =============================== */ const profile = await this.profileRepo.find({ where: { id: In(body.profileIds) }, relations: { posLevel: true, posType: true, }, }); if (!profile.length) return; const [{ today }] = await AppDataSource.query( "SELECT CURRENT_DATE() as today", ); const orgRevision = await this.orgRevisionRepo.findOne({ select: ["id"], where: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true, }, }); /** * =============================== * TRANSACTION * =============================== */ const queryRunner = AppDataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); console.log("2.") try { /** * =============================== * RESULT BUFFERS (SAVE ARRAY) * =============================== */ const positionOfficerBulk: any[] = []; const levelOfficerBulk: any[] = []; const executiveOfficerBulk: any[] = []; console.log("3.") /** * =============================== * MAIN LOOP (SINGLE LOOP) * =============================== */ for (const x of profile) { const currentDate = x.isLeave && x.leaveDate ? Extension.toDateOnlyString(x.leaveDate) : today; /** * ==================================== * PARALLEL STORED PROCEDURES * ==================================== */ const [ positionResult, levelResult, executiveResult, ] = await Promise.all([ AppDataSource.query("CALL GetProfileSalaryPosition(?, ?)", [ x.id, currentDate, ]), AppDataSource.query("CALL GetProfileSalaryLevel(?, ?)", [ x.id, currentDate, ]), AppDataSource.query("CALL GetProfileSalaryExecutive(?, ?)", [ x.id, currentDate, ]), ]); console.log("4.",x.id) /** * ==================================== * POSITION * ==================================== */ const posRows = positionResult?.[0] ?? []; const posMap = posRows.length > 1 ? posRows.slice(1).map((r: any, i: number) => ({ days_diff: Number(r.days_diff) || 0, positionName: posRows[i]?.positionName, })) : []; const posCal = posMap .filter((p:any) => p.positionName === x.position) .reduce( (a:any, c:any) => ({ days_diff: a.days_diff + c.days_diff, positionName: c.positionName, }), { days_diff: 0, positionName: null }, ); positionOfficerBulk.push({ profileId: x.id, positionName: posCal.positionName, days_diff: posCal.days_diff, Years: Math.floor(posCal.days_diff / 365.2524), Months: Math.floor((posCal.days_diff / 30.4375) % 12), Days: Math.floor(posCal.days_diff % 30.4375), }); console.log("5.",x.id) /** * ==================================== * 2️⃣ POSITION LEVEL * ==================================== */ const lvlRows = levelResult?.[0] ?? []; const lvlMap = lvlRows.length > 1 ? lvlRows.slice(1).map((r: any, i: number) => ({ days_diff: Number(r.days_diff) || 0, positionType: lvlRows[i]?.positionType, positionLevel: lvlRows[i]?.positionLevel, positionCee: lvlRows[i]?.positionCee, })) : []; const lvlCal = lvlMap .filter( (l:any) => l.positionLevel === x.posLevel?.posLevelName && l.positionType === x.posType?.posTypeName, ) .reduce( (a:any, c:any) => ({ days_diff: a.days_diff + c.days_diff, positionType: c.positionType, positionLevel: c.positionLevel, positionCee: c.positionCee, }), { days_diff: 0, positionType: null, positionLevel: null, positionCee: null, }, ); levelOfficerBulk.push({ profileId: x.id, positionType: lvlCal.positionType, positionLevel: lvlCal.positionLevel, positionCee: lvlCal.positionCee, days_diff: lvlCal.days_diff, Years: x.posLevel ? (lvlCal.days_diff / 365.2524).toFixed(4) : 0, Months: x.posLevel ? ((lvlCal.days_diff / 30.4375) % 12).toFixed(4) : 0, Days: x.posLevel ? (lvlCal.days_diff % 30.4375).toFixed(4) : 0, }); console.log("6.",x.id) /** * ==================================== * 3️⃣ POSITION EXECUTIVE * ==================================== */ const exeRows = executiveResult?.[0] ?? []; const exeMap = exeRows.length > 1 ? exeRows.slice(1).map((r: any, i: number) => ({ days_diff: Number(r.days_diff) || 0, positionExecutive: exeRows[i]?.positionExecutive, })) : []; const position = await this.positionRepository.findOne({ where: { positionIsSelected: true, posMaster: { orgRevisionId: orgRevision?.id, current_holderId: x.id, }, }, order: { createdAt: "DESC" }, relations: { posExecutive: true, }, }); const exeName = position?.posExecutive?.posExecutiveName; const exeCal = exeMap .filter((e:any) => exeName && e.positionExecutive === exeName) .reduce( (a:any, c:any) => ({ days_diff: a.days_diff + c.days_diff, positionExecutive: c.positionExecutive, }), { days_diff: 0, positionExecutive: null }, ); executiveOfficerBulk.push({ profileId: x.id, positionExecutiveName: exeCal.positionExecutive, days_diff: exeCal.days_diff, Years: (exeCal.days_diff / 365.2524).toFixed(4), Months: ((exeCal.days_diff / 30.4375) % 12).toFixed(4), Days: (exeCal.days_diff % 30.4375).toFixed(4), }); } console.log("7.") /** * =============================== * CLEAR ALL DATA AND SAVE ARRAY (BULK) * =============================== */ await queryRunner.manager .createQueryBuilder() .delete() .from(this.positionOfficerRepo.target) .execute(); await queryRunner.manager .createQueryBuilder() .delete() .from(this.levelOfficerRepo.target) .execute(); await queryRunner.manager .createQueryBuilder() .delete() .from(this.positionExecutiveOfficerRepo.target) .execute(); console.log("8.") await queryRunner.manager.save(this.positionOfficerRepo.target, positionOfficerBulk); await queryRunner.manager.save(this.levelOfficerRepo.target, levelOfficerBulk); await queryRunner.manager.save(this.positionExecutiveOfficerRepo.target,executiveOfficerBulk); console.log("9.") /** * =============================== * REGISTRY OFFICER (SYNC VIEW) * =============================== */ const allRegis = await queryRunner.manager .getRepository(viewRegistryOfficer) .createQueryBuilder("registryOfficer") .where("registryOfficer.profileId IN (:...profileIds)", { profileIds: new Set(profile.map((p) => p.id)) }) .getMany(); const mapRegistryData = allRegis.map((x) => ({ ...x, isProbation: Boolean(x.isProbation), isLeave: Boolean(x.isLeave), isRetirement: Boolean(x.isRetirement), Educations: x.Educations ? JSON.stringify(x.Educations) : "", })); console.log("10.") await queryRunner.manager .createQueryBuilder() .delete() .from(this.registryRepo.target) .execute(); if (mapRegistryData.length > 0) { await queryRunner.manager.save(this.registryRepo.target, mapRegistryData); } console.log("11.") /** * =============================== * COMMIT * =============================== */ await queryRunner.commitTransaction(); } catch (error) { await queryRunner.rollbackTransaction(); throw error; } finally { await queryRunner.release(); } } @Post("getDNA") public async GetData( @Request() req: RequestWithUser ){ let _data: any = { root: null, child1: null, child2: null, child3: null, child4: null, }; _data = await new permission().PermissionOrgList(req, "COMMAND"); return new HttpSuccess(_data); } @Post("calculateGovAge") public async calculateGovAge( @Request() req: RequestWithUser, @Body() body: { profileId: string; }, ){ return new HttpSuccess(await calculateGovAge(body.profileId, "OFFICER")); } /** * @summary Test Job กวาดออกคำสั่ง ทำงานทุกๆตี2 */ @Post("cronjobCommand") async CronjobCommand() { const commandController = new CommandController(); await commandController.cronjobCommand(); } /** * @summary payload & Endpoint ออกคำสั่ง */ @Put("path-excec/{id}") async Bright( @Path() id: string, @Request() request: RequestWithUser, ) { const command = await this.commandRepository.findOne({ where: { id: id }, relations: ["commandType", "commandRecives", "commandSends", "commandSends.commandSendCCs"], }); if (!command) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลคำสั่งนี้"); } const path = commandTypePath(command.commandType.code); return new HttpSuccess({ path: path + "/excecute", refIds: command.commandRecives .filter((x) => x.refId != null) .map((x) => ({ refId: x.refId, commandNo: command.commandNo, commandYear: command.commandYear, commandId: command.id, remark: command.positionDetail, amount: x.amount, amountSpecial: x.amountSpecial, positionSalaryAmount: x.positionSalaryAmount, mouthSalaryAmount: x.mouthSalaryAmount, commandCode: command.commandType.commandCode, commandName: command.commandType.name, commandDateAffect: command.commandExcecuteDate, commandDateSign: command.commandAffectDate, })), }); } /** * API รายละเอียดรายการคำสั่ง tab4 แนบท้าย * @summary API รายละเอียดรายการคำสั่ง tab4 แนบท้าย * @param {string} id Id คำสั่ง * @param {string} profileId profileId */ @Get("tab4/attachment/{id}/{profileId}") async GetByIdTab4Attachment( @Path() id: string, @Path() profileId: string, @Request() request: RequestWithUser ) { await new permission().PermissionGet(request, "COMMAND"); let profile: Profile | ProfileEmployee | null = null; profile = await this.profileRepo.findOne({ where: { id: profileId } }); if (!profile) { profile = await this.profileEmpRepo.findOne({ where: { id: profileId } }); if (!profile) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลบุคคลากรนี้"); } const command = await this.commandRepository.findOne({ where: { id }, relations: ["commandType", "commandRecives"], }); if (!command) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบข้อมูลคำสั่งนี้"); } let _command: any = []; const path = commandTypePath(command.commandType.code); if (path == null) throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบประเภทคำสั่งนี้ในระบบ"); await new CallAPI() .PostData(request, path + "/attachment", { refIds: command.commandRecives .filter((x) => x.refId != null && x.profileId != null && x.profileId == profileId ) .map((x) => ({ refId: x.refId, Sequence: x.order, CitizenId: x.citizenId, Prefix: x.prefix, FirstName: x.firstName, LastName: x.lastName, Amount: x.amount, PositionSalaryAmount: x.positionSalaryAmount, MouthSalaryAmount: x.mouthSalaryAmount, RemarkHorizontal: x.remarkHorizontal, RemarkVertical: x.remarkVertical, CommandYear: command.commandYear, CommandExcecuteDate: command.commandExcecuteDate, })), }) .then(async (res) => { _command = res; }) .catch(() => {}); let issue = command.isBangkok == "OFFICE" ? "สำนักปลัดกรุงเทพมหานคร" : command.isBangkok == "BANGKOK" ? "กรุงเทพมหานคร" : null; if (issue == null) { const orgRevisionActive = await this.orgRevisionRepo.findOne({ where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, relations: ["posMasters", "posMasters.orgRoot"], }); if (orgRevisionActive != null) { const profile = await this.profileRepo.findOne({ where: { keycloak: command.createdUserId.toString(), }, }); if (profile != null) { issue = orgRevisionActive?.posMasters?.filter((x) => x.current_holderId == profile.id)[0] ?.orgRoot?.orgRootName || null; } } } if (issue == null) issue = "..................................."; return new HttpSuccess({ template: command.commandType.fileAttachment, reportName: "xlsx-report", data: { data: _command, issuerOrganizationName: issue, commandNo: command.commandNo == null ? "" : Extension.ToThaiNumber(command.commandNo), commandYear: command.commandYear == null ? "" : Extension.ToThaiNumber(Extension.ToThaiYear(command.commandYear).toString()), commandExcecuteDate: command.commandExcecuteDate == null ? "" : Extension.ToThaiNumber(Extension.ToThaiFullDate2(command.commandExcecuteDate)), }, }); } }