From 1b53b0ce9b71fd48241ee138311c0e259d17805c Mon Sep 17 00:00:00 2001 From: AdisakKanthawilang Date: Wed, 11 Dec 2024 17:13:03 +0700 Subject: [PATCH] =?UTF-8?q?#984(1)=20=E0=B9=81=E0=B8=A5=E0=B8=B0=20#830?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/ReportController.ts | 204 +++++++++++++++++++++++++++- src/interfaces/utils.ts | 12 +- 2 files changed, 212 insertions(+), 4 deletions(-) diff --git a/src/controllers/ReportController.ts b/src/controllers/ReportController.ts index 4f88c33f..1226f2d6 100644 --- a/src/controllers/ReportController.ts +++ b/src/controllers/ReportController.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Route, Security, Tags, SuccessResponse, Response, Path } from "tsoa"; +import { Controller, Get, Route, Security, Tags, SuccessResponse, Response, Path, Query } from "tsoa"; import { AppDataSource } from "../database/data-source"; import HttpSuccess from "../interfaces/http-success"; import HttpStatusCode from "../interfaces/http-status"; @@ -17,6 +17,7 @@ import { EmployeePosMaster } from "../entities/EmployeePosMaster"; import Extension from "../interfaces/extension"; import { LeaveType } from "../entities/LeaveType"; import HttpStatus from "../interfaces/http-status"; +import { Profile } from "../entities/Profile"; @Route("api/v1/org/report") @Tags("Report") @Security("bearerAuth") @@ -34,7 +35,207 @@ export class ReportController extends Controller { private posTypepository = AppDataSource.getRepository(PosType); private posLevelRepository = AppDataSource.getRepository(PosLevel); private posMasterRepository = AppDataSource.getRepository(PosMaster); + private profileRepository = AppDataSource.getRepository(Profile); private empPosMasterRepository = AppDataSource.getRepository(EmployeePosMaster); + /** + * API รายงานสถิติข้อมูลข้าราชการ กทม. สามัญ + * + * @summary รายงานสถิติข้อมูลข้าราชการ กทม. สามัญ + * + */ + @Get("registry-officer") + async registryOfficer( + @Query() rootId: string, + @Query() year?: number, + @Query() ageMin?: number, + @Query() ageMax?: number, + ) { + if (ageMin && (ageMin < 20 || ageMin > 80)) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ageMin must be between 20 and 80"); + } + + if (ageMax && (ageMax < 20 || ageMax > 80)) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ageMax must be between 20 and 80"); + } + + const minAge = ageMin ?? 20; + const maxAge = ageMax ?? 80; + + if (minAge > maxAge) { + throw new HttpError(HttpStatus.NOT_FOUND, "ageMin cannot be greater than ageMax"); + } + const yearInAD = year?year - 543:null; + const currentRevision = await this.orgRevisionRepository.findOne({ + where:{ + orgRevisionIsCurrent: true + } + }); + const rawdataProfile = await this.posMasterRepository + .createQueryBuilder('posMaster') + .leftJoinAndSelect('posMaster.current_holder', 'current_holder') + .leftJoinAndSelect('posMaster.positions', 'positions') + .leftJoinAndSelect('posMaster.orgRoot', 'orgRoot') + .leftJoinAndSelect('positions.posExecutive', 'posExecutive') + .leftJoinAndSelect('current_holder.posType', 'posType') + .leftJoinAndSelect('current_holder.posLevel', 'posLevel') + .leftJoinAndSelect('current_holder.profileEducations', 'profileEducations') + .where('posMaster.orgRevisionId = :currentRevisionId', { currentRevisionId: currentRevision?.id }) + .andWhere('posMaster.orgRootId = :rootId', { rootId }) + .andWhere('posMaster.current_holderId Is Not Null') + .andWhere('positions.positionIsSelected = :positionIsSelected', { positionIsSelected: true }) + .andWhere( yearInAD && yearInAD != null? 'YEAR(current_holder.dateAppoint) = :year': "1=1", { year: yearInAD }) + .andWhere(` + TIMESTAMPDIFF(YEAR, current_holder.birthDate, CURDATE()) >= :minAge + AND TIMESTAMPDIFF(YEAR, current_holder.birthDate, CURDATE()) <= :maxAge + `, { minAge, maxAge }) + .orderBy("posType.posTypeaName","ASC") + .getMany(); + if(!rawdataProfile){ + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลบุคคลนี้ในระบบ"); + } + const mapData = rawdataProfile + .map((x) => { + const latestEducation = x.current_holder.profileEducations.sort((a:any, b:any) => b.endDate - a.startDate)[0]; + return { + name: x.current_holder.firstName + " " + x.current_holder.lastName, + affiliation: x.orgRoot.orgRootName, + gender: x.current_holder.gender, + positionName: x.positions[0]?x.positions[0].positionName:"-", + status: x.current_holder.relationship, + posType: x.current_holder.posType.posTypeName, + posLevel: x.current_holder.posLevel.posLevelName, + degree: latestEducation ? latestEducation.educationLevel : "-", + posExecutive: x.positions[0].posExecutive?x.positions[0].posExecutive.posExecutiveName:"-", + currentPreiodPos: "-", + levelPeriodPos: "-", + }; + }); + + const groupedData = mapData.reduce((acc:any, item) => { + const key = `${item.posType} - ${item.affiliation} - ${item.gender} - ${item.degree || 'ไม่พบข้อมูล'} - ${item.status || 'ไม่พบข้อมูล'} - ${item.positionName} - ${item.posLevel} - ${item.posExecutive || 'ไม่พบข้อมูล'} `; + if (!acc[key]) { + acc[key] = { + posType: item.posType, + affiliation: item.affiliation, + gender: item.gender, + degree: item.degree, + status: item.status, + positionName: item.positionName, + posLevel: item.posLevel, + posExecutive: item.posExecutive, + currentPreiodPos: "-", + levelPeriodPos: "-", + count: 0, + }; + } + acc[key].count++; + + return acc; + }, {}); + + return new HttpSuccess({ + template: "registry-officer", + reportName: "xlsx-report", + data: groupedData, + }); + } + /** + * API รายงานสถิติข้อมูลลูกจ้างประจำ กทม. + * + * @summary รายงานสถิติข้อมูลลูกจ้างประจำ กทม. + * + */ + @Get("registry-emp") + async registryEmp( + @Query() rootId: string, + @Query() year?: number, + @Query() ageMin?: number, + @Query() ageMax?: number, + ) { + if (ageMin && (ageMin < 20 || ageMin > 80)) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ageMin must be between 20 and 80"); + } + + if (ageMax && (ageMax < 20 || ageMax > 80)) { + throw new HttpError(HttpStatus.BAD_REQUEST, "ageMax must be between 20 and 80"); + } + + const minAge = ageMin ?? 20; + const maxAge = ageMax ?? 80; + + if (minAge > maxAge) { + throw new HttpError(HttpStatus.NOT_FOUND, "ageMin cannot be greater than ageMax"); + } + const yearInAD = year?year - 543:null; + const currentRevision = await this.orgRevisionRepository.findOne({ + where:{ + orgRevisionIsCurrent: true + } + }); + const rawdataProfile = await this.empPosMasterRepository + .createQueryBuilder('posMaster') + .leftJoinAndSelect('posMaster.current_holder', 'current_holder') + .leftJoinAndSelect('posMaster.positions', 'positions') + .leftJoinAndSelect('posMaster.orgRoot', 'orgRoot') + .leftJoinAndSelect('current_holder.posType', 'posType') + .leftJoinAndSelect('current_holder.posLevel', 'posLevel') + .leftJoinAndSelect('current_holder.profileEducations', 'profileEducations') + .where('posMaster.orgRevisionId = :currentRevisionId', { currentRevisionId: currentRevision?.id }) + .andWhere('posMaster.orgRootId = :rootId', { rootId }) + .andWhere('posMaster.current_holderId Is Not Null') + .andWhere('positions.positionIsSelected = :positionIsSelected', { positionIsSelected: true }) + .andWhere( yearInAD && yearInAD != null? 'YEAR(current_holder.dateAppoint) = :year': "1=1", { year: yearInAD }) + .andWhere(` + TIMESTAMPDIFF(YEAR, current_holder.birthDate, CURDATE()) >= :minAge + AND TIMESTAMPDIFF(YEAR, current_holder.birthDate, CURDATE()) <= :maxAge + `, { minAge, maxAge }) + .getMany(); + if(!rawdataProfile){ + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลบุคคลนี้ในระบบ"); + } + const mapData = rawdataProfile.map((x) => { + const latestEducation = x.current_holder.profileEducations.sort((a:any, b:any) => b.endDate - a.startDate)[0]; + return { + name: x.current_holder.firstName + " " + x.current_holder.lastName, + affiliation: x.orgRoot.orgRootName, + gender: x.current_holder.gender, + positionName: x.positions[0]?x.positions[0].positionName: "-", + status: x.current_holder.relationship, + posType: x.current_holder.posType.posTypeName, + posLevel: x.current_holder.posLevel.posLevelName, + degree: latestEducation ? latestEducation.educationLevel : "-", + period: "-", + }; + }); + + const groupedData = mapData.reduce((acc:any, item) => { + const key = `${item.affiliation} - ${item.gender} - ${item.degree || 'ไม่พบข้อมูล'} - ${item.status || 'ไม่พบข้อมูล'} - ${item.posType} - ${item.positionName} - ${item.posLevel} + `; + if (!acc[key]) { + acc[key] = { + affiliation: item.affiliation, + gender: item.gender, + degree: item.degree, + status: item.status, + posType: item.posType, + positionName: item.positionName, + posLevel: item.posLevel, + period: "-", + count: 0, + }; + } + acc[key].count++; + + return acc; + }, {}); + + return new HttpSuccess({ + template: "registry-emp", + reportName: "xlsx-report", + data: mapData, + }); + + } /** * API Report1 @@ -6471,4 +6672,5 @@ export class ReportController extends Controller { } }); } + } diff --git a/src/interfaces/utils.ts b/src/interfaces/utils.ts index c02c10e5..bd664473 100644 --- a/src/interfaces/utils.ts +++ b/src/interfaces/utils.ts @@ -8,6 +8,7 @@ import { In, IsNull, MoreThan, Not } from "typeorm"; import { RequestWithUser } from "../middlewares/user"; import { Command } from "../entities/Command"; import { ProfileSalary } from "../entities/ProfileSalary"; +import { Profile } from "../entities/Profile"; export function calculateAge(start: Date, end = new Date()) { if (start.getTime() > end.getTime()) return null; @@ -85,10 +86,15 @@ export async function calculateGovAge(profileId: string, type: string) { return { years, months, days }; }; - - const firstStartDate = new Date(records[0].date); + const profile = await AppDataSource.getRepository(Profile).findOne({ + where:{ + id:profileId + } + }) + // const firstStartDate = new Date(records[0].date); + const firstStartDate = profile?.dateAppoint; const firstEndDate = endDateFristRec ? new Date(endDateFristRec.dateGovernment) : new Date(); - + const { years: totalYears1, months: totalMonths1,