diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c296111..cb8ee9c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -480,6 +480,8 @@ model User { paymentCreated Payment[] notificationReceive Notification[] @relation("NotificationReceiver") notificationRead Notification[] + taskOrderCreated TaskOrder[] @relation("TaskOrderCreatedByUser") + taskOrderAccepted TaskOrder[] } model UserResponsibleArea { @@ -981,6 +983,8 @@ model Institution { subDistrictId String selectedImage String? + + taskOrder TaskOrder[] } model WorkflowTemplate { @@ -1449,5 +1453,38 @@ model RequestWorkStepStatus { attributes Json? + taskOrder TaskOrder? @relation(fields: [taskOrderId], references: [id]) + taskOrderId String? + @@id([step, requestWorkId]) } + +enum TaskStatus { + Pending + InProgress + Validate + Complete +} + +model TaskOrder { + id String @id @default(cuid()) + + code String + + taskName String + taskStatus TaskStatus @default(Pending) + taskList RequestWorkStepStatus[] + + contactName String + contactTel String + + institution Institution @relation(fields: [institutionId], references: [id]) + institutionId String + + acceptedBy User? @relation(fields: [acceptedByUserId], references: [id]) + acceptedByUserId String? + + createdAt DateTime @default(now()) + createdBy User @relation(name: "TaskOrderCreatedByUser", fields: [createdByUserId], references: [id]) + createdByUserId String +} diff --git a/src/controllers/06-request-list-controller.ts b/src/controllers/06-request-list-controller.ts index 6c293d6..ec6df9a 100644 --- a/src/controllers/06-request-list-controller.ts +++ b/src/controllers/06-request-list-controller.ts @@ -1,7 +1,12 @@ import { Body, Controller, Get, Path, Put, Query, Request, Route, Security, Tags } from "tsoa"; import { RequestWithUser } from "../interfaces/user"; import prisma from "../db"; -import { Prisma, RequestDataStatus, RequestWorkStatus } from "@prisma/client"; +import { + Prisma, + RequestDataStatus, + RequestWorkStatus, + RequestWorkStepStatus, +} from "@prisma/client"; import { createPermCondition } from "../services/permission"; import { queryOrNot } from "../utils/relation"; import { notFoundError } from "../utils/error"; @@ -161,8 +166,10 @@ export class RequestListController extends Controller { @Query() page: number = 1, @Query() pageSize: number = 30, @Query() requestDataId?: string, + @Query() workStatus?: RequestWorkStatus, ) { const where = { + stepStatus: { some: { workStatus } }, request: { id: requestDataId, quotation: { diff --git a/src/controllers/07-task-controller.ts b/src/controllers/07-task-controller.ts new file mode 100644 index 0000000..40b18b1 --- /dev/null +++ b/src/controllers/07-task-controller.ts @@ -0,0 +1,239 @@ +import { + Body, + Controller, + Delete, + Get, + Path, + Post, + Put, + Query, + Request, + Route, + Security, + Tags, +} from "tsoa"; +import prisma from "../db"; +import { notFoundError } from "../utils/error"; +import { TaskStatus } from "@prisma/client"; +import { RequestWithUser } from "../interfaces/user"; + +@Route("/api/v1/task") +@Tags("Task Order") +export class TaskController extends Controller { + @Get() + @Security("keycloak") + async getTaskOrderList(@Query() query: string, @Query() page = 1, @Query() pageSize = 30) { + const [result, total] = await prisma.$transaction([ + prisma.taskOrder.findMany({ + include: { + institution: true, + acceptedBy: true, + createdBy: true, + }, + }), + prisma.taskOrder.count(), + ]); + + return { result, total, page, pageSize }; + } + + @Get("{taskId}") + @Security("keycloak") + async getTaskOrder(@Path() taskId: string) { + const record = await prisma.taskOrder.findFirst({ + where: { id: taskId }, + include: { + taskList: { + include: { + requestWork: { + include: { + productService: true, + }, + }, + }, + }, + institution: true, + acceptedBy: true, + createdBy: true, + }, + }); + + if (!record) throw notFoundError("Task Order"); + + return record; + } + + @Post() + async createTaskOrderList( + @Request() req: RequestWithUser, + @Body() + body: { + taskName: string; + taskStatus: TaskStatus; + + contactName: string; + contactTel: string; + + institutionId: string; + + requestWork: { requestWorkId: string; step: number }[]; + }, + ) { + return await prisma.$transaction(async (tx) => { + const last = await tx.runningNo.upsert({ + where: { + key: "TASK", + }, + create: { + key: "TASK", + value: 1, + }, + update: { + value: { increment: 1 }, + }, + }); + const current = new Date(); + const year = `${current.getFullYear()}`.slice(-2).padStart(2, "0"); + const month = `${current.getMonth() + 1}`.padStart(2, "0"); + + const code = `PO${year}${month}${last.value.toString().padStart(6, "0")}`; + + await tx.taskOrder.create({ + include: { + taskList: { + include: { + requestWork: true, + }, + }, + institution: true, + acceptedBy: true, + createdBy: true, + }, + data: { + ...body, + code, + createdByUserId: req.user.sub, + taskList: { + connect: body.requestWork.map((v) => ({ step_requestWorkId: v })), + }, + }, + }); + }); + } + + @Put("{taskId}") + async editTaskById( + @Path() taskId: string, + @Body() + body: { + taskName: string; + taskStatus: TaskStatus; + + contactName: string; + contactTel: string; + + institutionId: string; + + requestWork: { requestWorkId: string; step: number }[]; + }, + ) { + const record = await prisma.taskOrder.findFirst({ + where: { id: taskId }, + include: { + taskList: { + include: { requestWork: true }, + }, + institution: true, + acceptedBy: true, + createdBy: true, + }, + }); + + await prisma.taskOrder.update({ + where: { id: taskId }, + include: { + taskList: { + include: { + requestWork: true, + }, + }, + institution: true, + acceptedBy: true, + createdBy: true, + }, + data: { + ...body, + taskList: { + disconnect: record?.taskList + .filter( + (lhs) => + !body.requestWork.find( + (rhs) => lhs.requestWorkId === rhs.requestWorkId && lhs.step === rhs.step, + ), + ) + .map((v) => ({ + step_requestWorkId: { + requestWorkId: v.requestWorkId, + step: v.step, + }, + })), + connect: body.requestWork.map((v) => ({ step_requestWorkId: v })), + }, + }, + }); + } + + @Delete("{taskId}") + async deleteTask(@Path() taskId: string) { + await prisma.$transaction(async (tx) => { + let record = await tx.taskOrder.deleteMany({ where: { id: taskId } }); + + if (record.count <= 0) throw notFoundError("Task Order"); + }); + } +} + +@Route("/api/v1/task/{taskId}") +@Tags("Task Order") +export class TaskActionController extends Controller { + @Post("accept") + async acceptTaskOrder(@Request() req: RequestWithUser, @Path() taskId: string) { + const record = await prisma.taskOrder.findFirst({ + include: { + taskList: { + orderBy: { step: "asc" }, + }, + }, + where: { id: taskId }, + }); + + if (!record) throw notFoundError("Task Order"); + + return await prisma.$transaction(async (tx) => { + await tx.taskOrder.update({ + where: { id: taskId }, + data: { + acceptedByUserId: req.user.sub, + }, + }); + await tx.requestWorkStepStatus.updateMany({ + where: { taskOrderId: taskId }, + data: { + workStatus: "InProgress", + }, + }); + await tx.requestData.updateMany({ + where: { + requestWork: { + some: { + stepStatus: { + some: { taskOrderId: taskId }, + }, + }, + }, + }, + data: { requestDataStatus: "InProgress" }, + }); + }); + } +}