import { Body, Controller, Get, Path, Post, Request, Route, Security, Tags } from "tsoa"; import { AppDataSource } from "../database/data-source"; import { RequestWithUser } from "../middlewares/user"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import HttpSuccess from "../interfaces/http-success"; import { Workflow } from "../entities/Workflow"; import { State } from "../entities/State"; import { StateOperator } from "../entities/StateOperator"; import { StateOperatorUser } from "../entities/StateOperatorUser"; import CallAPI from "../interfaces/call-api"; import { Profile } from "../entities/Profile"; import { StateUserComment } from "../entities/StateUserComment"; import { MetaWorkflow } from "../entities/MetaWorkflow"; import { MetaState } from "../entities/MetaState"; import { MetaStateOperator } from "../entities/MetaStateOperator"; import { PosMaster } from "../entities/PosMaster"; import { IsNull, Not } from "typeorm"; @Route("api/v1/org/workflow") @Tags("Workflow") @Security("bearerAuth") export class WorkflowController extends Controller { private workflowRepo = AppDataSource.getRepository(Workflow); private stateRepo = AppDataSource.getRepository(State); private stateOperatorRepo = AppDataSource.getRepository(StateOperator); private stateOperatorUserRepo = AppDataSource.getRepository(StateOperatorUser); private stateUserCommentRepo = AppDataSource.getRepository(StateUserComment); private profileRepo = AppDataSource.getRepository(Profile); private metaWorkflowRepo = AppDataSource.getRepository(MetaWorkflow); private metaStateRepo = AppDataSource.getRepository(MetaState); private metaStateOperatorRepo = AppDataSource.getRepository(MetaStateOperator); private posMasterRepo = AppDataSource.getRepository(PosMaster); @Post("add-workflow") public async checkWorkflow( @Request() req: RequestWithUser, @Body() body: { refId: string; sysName: string; posLevelName: string; posTypeName: string; }, ) { const profile = await this.profileRepo.findOne({ where: { keycloak: req.user.sub, }, }); if (!profile) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งาน"); const metaWorkflow = await this.metaWorkflowRepo.findOne({ where: { sysName: body.sysName, posLevelName: body.posLevelName, posTypeName: body.posTypeName, }, }); if (!metaWorkflow) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบกระบวนการนี้ได้"); const meta = { createdUserId: req.user.sub, createdFullName: req.user.name, lastUpdateUserId: req.user.sub, lastUpdateFullName: req.user.name, createdAt: new Date(), lastUpdatedAt: new Date(), }; const workflow = new Workflow(); Object.assign(workflow, { ...metaWorkflow, id: undefined, ...meta, ...body, system: body.sysName, }); await this.workflowRepo.save(workflow); const metaState = await this.metaStateRepo.find({ where: { metaWorkflowId: metaWorkflow.id, }, order: { order: "ASC" }, }); await Promise.all( metaState.map(async (item) => { const state = new State(); Object.assign(state, { ...item, id: undefined, workflowId: workflow.id, ...meta }); await this.stateRepo.save(state); if (state.order == 1) { workflow.stateId = state.id; await this.workflowRepo.save(workflow); } const metaStateOperator = await this.metaStateOperatorRepo.find({ where: { metaStateId: item.id, }, }); await Promise.all( metaStateOperator.map(async (item1) => { const stateOperator = new StateOperator(); Object.assign(stateOperator, { ...item1, id: undefined, stateId: state.id, ...meta }); await this.stateOperatorRepo.save(stateOperator); }), ); }), ); const stateOperatorUser = new StateOperatorUser(); Object.assign(stateOperatorUser, { profileId: profile.id, operator: "Owner", order: 1, workflowId: workflow.id, ...meta, }); await this.stateOperatorUserRepo.save(stateOperatorUser); const profileOfficer = await this.posMasterRepo.find({ where: { posMasterAssigns: { assignId: body.sysName }, orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, }, relations: ["orgChild1"], }); await Promise.all( profileOfficer.map(async (item, i) => { if (item.orgChild1 == null || item.orgChild1.isOfficer == false) { const stateOperatorUser = new StateOperatorUser(); Object.assign(stateOperatorUser, { profileId: item.current_holderId, operator: "Officer", order: i + 2, workflowId: workflow.id, ...meta, }); await this.stateOperatorUserRepo.save(stateOperatorUser); } else { const stateOperatorUser = new StateOperatorUser(); Object.assign(stateOperatorUser, { profileId: item.current_holderId, operator: "PersonnelOfficer", order: i + 2, workflowId: workflow.id, ...meta, }); await this.stateOperatorUserRepo.save(stateOperatorUser); } }), ); return new HttpSuccess(); } @Post("check-iscan") public async checkIsCan( @Request() req: RequestWithUser, @Body() body: { workflowId: string; stateId: string; profileId: string; action: string; }, ) { const stateOperatorUser = await this.stateOperatorUserRepo.findOne({ where: { profileId: body.profileId, workflowId: body.workflowId, }, }); if (!stateOperatorUser) throw new HttpError(HttpStatus.NOT_FOUND, "ผู้ใช้งานนี้ไม่มีหน้าที่ในกระบวนการนี้"); const operator = await this.stateOperatorRepo.findOne({ where: { operator: stateOperatorUser.operator, state: { id: body.stateId, workflow: { id: body.workflowId } }, }, }); if (!operator) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่สามารถดำเนินการกระบวนการนี้ได้"); let isCan = false; switch (body.action.trim().toLocaleUpperCase()) { case "VIEW": isCan = operator.canView; case "UPDATE": isCan = operator.canUpdate; case "DELETE": isCan = operator.canDelete; case "CANCEL": isCan = operator.canCancel; case "OPERATE": isCan = operator.canOperate; case "CHANGESTATE": isCan = operator.canChangeState; case "COMMENT": isCan = operator.canComment; case "SIGN": isCan = operator.canSign; default: isCan = false; } if (isCan == false) { throw new HttpError(HttpStatus.NOT_FOUND, "ไม่สามารถดำเนินการได้"); } else { return new HttpSuccess(); } } @Post("check-state-now") public async checkStateNow( @Request() req: RequestWithUser, @Body() body: { refId: string; system: string; }, ) { const workflow = await this.workflowRepo.findOne({ where: { refId: body.refId, sysName: body.system, }, relations: ["stateOperatorUsers"], }); if (!workflow) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่สามารถดำเนินการกระบวนการนี้ได้"); const state = await this.stateRepo.findOne({ where: { id: workflow.stateId, }, relations: ["stateOperators"], }); if (!state) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลขั้นตอนการอนุมัติ"); return new HttpSuccess({ stateId: state.id, stateNo: state.order, stateName: state.name, }); } @Post("check-user-now") public async checkUserNow( @Request() req: RequestWithUser, @Body() body: { refId: string; system: string; }, ) { const stateOperatorUser = await this.stateOperatorUserRepo.findOne({ where: { workflow: { refId: body.refId, sysName: body.system, }, profile: { keycloak: req.user.sub, }, }, relations: ["workflow"], }); const workflow = await this.workflowRepo.findOne({ where: { refId: body.refId, sysName: body.system, }, }); if (!workflow) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่สามารถดำเนินการกระบวนการนี้ได้"); const operator = await this.stateOperatorRepo.findOne({ where: { operator: stateOperatorUser?.operator || "", stateId: workflow.stateId, }, relations: ["state"], }); if (!operator) { const state = await this.stateRepo.findOne({ where: { id: workflow.stateId, }, }); return new HttpSuccess({ stateId: state?.id || null, stateNo: state?.order || null, stateName: state?.name || null, operator: stateOperatorUser?.operator || null, can_view: false, can_update: false, can_operate: false, can_change_state: false, can_delete: false, can_cancel: false, }); } return new HttpSuccess({ stateId: operator.state.id, stateNo: operator.state.order, stateName: operator.state.name, operator: operator.operator, can_view: operator.canView, can_update: operator.canUpdate, can_operate: operator.canOperate, can_change_state: operator.canChangeState, can_delete: operator.canDelete, can_cancel: operator.canCancel, }); } @Post("check-state-all") public async checkStateAll( @Request() req: RequestWithUser, @Body() body: { refId: string; system: string; }, ) { const state = await this.stateRepo.find({ where: { workflow: { refId: body.refId, sysName: body.system, }, }, order: { order: "ASC", stateUserComments: { order: "ASC" } }, relations: ["stateUserComments", "stateUserComments.profile"], }); const _state = state.map((x) => ({ stateId: x.id, stateNo: x.order, stateName: x.name, stateUserComments: x.stateUserComments.map((x) => ({ id: x.id, prefix: x.profile.prefix, firstName: x.profile.firstName, lastName: x.profile.lastName, isComment: x.profile.keycloak == req.user.sub ? true : false, profileId: x.profileId, isAcceptSetting: x.isAcceptSetting, isApproveSetting: x.isApproveSetting, isReasonSetting: x.isReasonSetting, isAccept: x.isAccept, isApprove: x.isApprove, reason: x.reason, })), })); return new HttpSuccess(_state); } @Post("state-next") public async stateNext( @Request() req: RequestWithUser, @Body() body: { refId: string; system: string; }, ) { const stateOperatorUserNow = await this.stateOperatorUserRepo.findOne({ where: { workflow: { refId: body.refId, sysName: body.system, }, profile: { keycloak: req.user.sub, }, }, }); const stateOperatorUser = await this.stateOperatorUserRepo.find({ where: { workflow: { refId: body.refId, sysName: body.system, }, profile: { keycloak: Not(req.user.sub), }, operator: stateOperatorUserNow?.operator || "", }, }); await this.stateOperatorUserRepo.remove(stateOperatorUser); const workflow = await this.workflowRepo.findOne({ where: { refId: body.refId, sysName: body.system, }, relations: ["stateOperatorUsers"], }); if (!workflow) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่สามารถดำเนินการกระบวนการนี้ได้"); const state = await this.stateRepo.findOne({ where: { id: workflow.stateId, }, relations: ["stateOperators"], }); if (!state) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลขั้นตอนการอนุมัติ"); const _state = await this.stateRepo.findOne({ where: { order: state.order + 1, workflowId: state.workflowId, }, relations: ["stateOperators"], }); //noti let profileNow = workflow.stateOperatorUsers .filter((x) => state.stateOperators.map((s) => s.operator).includes(x.operator)) .map((x) => x.profile); await new CallAPI() .PostData(req, "/placement/noti/profiles", { subject: `รายการถูกส่ง`, body: `รายการถูกส่ง`, receiverUserIds: profileNow, payload: "", //แนบไฟล์ isSendMail: true, isSendInbox: true, isSendNotification: true, }) .catch((error) => { console.error("Error calling API:", error); }); if (_state != null) { let profileNext = workflow.stateOperatorUsers .filter((x) => _state.stateOperators.map((s) => s.operator).includes(x.operator)) .map((x) => x.profile); await new CallAPI() .PostData(req, "/placement/noti/profiles", { subject: `ได้รับรายการ`, body: `ได้รับรายการ`, receiverUserIds: profileNext, payload: "", //แนบไฟล์ isSendMail: true, isSendInbox: true, isSendNotification: true, }) .catch((error) => { console.error("Error calling API:", error); }); workflow.stateId = _state.id; await this.workflowRepo.save(workflow); } return new HttpSuccess({ stateId: _state?.id || null, stateNo: _state?.order || null, stateName: _state?.name || null, stateType: _state?.type || null, }); } @Post("state-back") public async stateBack( @Request() req: RequestWithUser, @Body() body: { stateId: string; }, ) { const state = await this.stateRepo.findOne({ where: { id: body.stateId, }, }); if (!state) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลขั้นตอนการอนุมัติ"); const _state = await this.stateRepo.findOne({ where: { order: state.order - 1, workflowId: state.workflowId, }, }); return new HttpSuccess({ stateId: _state?.id || null, stateNo: _state?.order || null, stateName: _state?.name || null, stateType: _state?.type || null, }); } @Post("add-step") public async addStep( @Request() req: RequestWithUser, @Body() body: { stateId: string; profileId: string; isAcceptSetting: boolean; isApproveSetting: boolean; isReasonSetting: boolean; }, ) { const profile = await this.profileRepo.findOne({ where: { id: body.profileId, }, }); if (!profile) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบผู้ใช้งานนี้"); const state = await this.stateRepo.findOne({ where: { id: body.stateId, }, relations: ["stateUserComments"], }); if (!state) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลขั้นตอนการอนุมัติ"); const stateUserComment = new StateUserComment(); stateUserComment.order = state.stateUserComments.length + 1; stateUserComment.stateId = body.stateId; stateUserComment.profileId = body.profileId; stateUserComment.isAcceptSetting = body.isAcceptSetting; stateUserComment.isApproveSetting = body.isApproveSetting; stateUserComment.isReasonSetting = body.isReasonSetting; stateUserComment.createdUserId = req.user.sub; stateUserComment.createdFullName = req.user.name; stateUserComment.createdAt = new Date(); stateUserComment.lastUpdateUserId = req.user.sub; stateUserComment.lastUpdateFullName = req.user.name; stateUserComment.lastUpdatedAt = new Date(); await this.stateUserCommentRepo.save(stateUserComment); await new CallAPI() .PostData(req, "/placement/noti/profiles", { subject: `ได้รับรายการ`, body: `ได้รับรายการ`, receiverUserIds: [body.profileId], payload: "", //แนบไฟล์ isSendMail: true, isSendInbox: true, isSendNotification: true, }) .catch((error) => { console.error("Error calling API:", error); }); return new HttpSuccess(); } @Post("comment") public async createcomment( @Request() req: RequestWithUser, @Body() body: { stateUserCommentId: string; isAccept?: boolean | null; isApprove?: boolean | null; reason?: string | null; }, ) { const stateUserComment = await this.stateUserCommentRepo.findOne({ where: { id: body.stateUserCommentId, }, }); if (!stateUserComment) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลขั้นตอนการอนุมัติ"); let _null: any = null; stateUserComment.isAccept = body.isAccept == null ? _null : body.isAccept; stateUserComment.isApprove = body.isApprove == null ? _null : body.isApprove; stateUserComment.reason = body.reason == null ? _null : body.reason; stateUserComment.lastUpdateUserId = req.user.sub; stateUserComment.lastUpdateFullName = req.user.name; stateUserComment.lastUpdatedAt = new Date(); await this.stateUserCommentRepo.save(stateUserComment); return new HttpSuccess(); } @Post("comment-state") public async getCommentState( @Request() req: RequestWithUser, @Body() body: { stateId: string; }, ) { const state = await this.stateRepo.findOne({ where: { id: body.stateId, }, order: { stateUserComments: { order: "ASC" } }, relations: ["stateUserComments"], }); if (!state) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลขั้นตอนการอนุมัติ"); return new HttpSuccess(state.stateUserComments); } @Post("comment-state-user") public async getCommentStateUser( @Request() req: RequestWithUser, @Body() body: { stateId: string; }, ) { const stateUserComment = await this.stateUserCommentRepo.findOne({ where: { profile: { keycloak: req.user.sub, }, stateId: body.stateId, }, }); return new HttpSuccess({ id: stateUserComment?.id || null, isAccept: stateUserComment?.isAccept || null, isApprove: stateUserComment?.isApprove || null, reason: stateUserComment?.reason || null, isAcceptSetting: stateUserComment?.isAcceptSetting || null, isApproveSetting: stateUserComment?.isApproveSetting || null, isReasonSetting: stateUserComment?.isReasonSetting || null, order: stateUserComment?.order || null, stateId: stateUserComment?.stateId || null, profileId: stateUserComment?.profileId || null, }); } /** * * */ @Get("commander") async getProfilePlacement(@Request() req: RequestWithUser) { const posMasterUser = await this.posMasterRepo.findOne({ where: { orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, current_holder: { keycloak: req.user.sub }, }, }); if (!posMasterUser || !posMasterUser.orgRootId) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบตำแหน่งผู้ใช้งาน"); const posMasters = await this.posMasterRepo.find({ where: { orgRevision: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, isDirector: true, current_holderId: Not(IsNull()), orgRootId: posMasterUser.orgRootId, }, relations: ["current_holder", "orgRoot"], }); let _posMasters = posMasters.map((data) => ({ id: data.current_holderId, prefix: data.current_holder.prefix, firstName: data.current_holder.firstName, lastName: data.current_holder.lastName, position: data.current_holder.position, posLevel: data.current_holder.posLevel, posType: data.current_holder.posType, orgRoot: data.orgRoot.orgRootName, })); return new HttpSuccess(_posMasters); } /** * API เช็ค สกจ * * @summary เช็ค สกจ * */ @Get("keycloak/isofficer/{system}") async getIsOfficerByKeycloak(@Path() system: string, @Request() req: RequestWithUser) { const profile = await this.profileRepo.findOne({ where: { keycloak: req.user.sub, }, }); if (!profile) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลผู้ใช้งาน"); const profileOfficer = await this.posMasterRepo.findOne({ where: { posMasterAssigns: { assignId: system }, orgRevision: { orgRevisionIsDraft: false, orgRevisionIsCurrent: true }, current_holderId: profile.id, }, relations: ["orgChild1"], }); if (!profileOfficer) return new HttpSuccess({ isOfficer: false, isStaff: false }); let isOfficer = profileOfficer.orgChild1 == null ? false : profileOfficer.orgChild1.isOfficer; return new HttpSuccess({ isOfficer: isOfficer, isStaff: !isOfficer }); } }