import { Body, Controller, Delete, Get, Put, Path, Post, Query, Request, Route, Security, Tags, } from "tsoa"; import { Prisma, Status } from "@prisma/client"; import prisma from "../../db"; import { RequestWithUser } from "../../interfaces/user"; import HttpError from "../../interfaces/http-error"; import HttpStatus from "../../interfaces/http-status"; type WorkCreate = { order: number; name: string; productId: string[]; attributes?: { [key: string]: any; }; }; type WorkUpdate = { order?: number; name?: string; productId?: string[]; attributes?: { [key: string]: any; }; }; @Route("api/v1/work") @Tags("Work") @Security("keycloak") export class WorkController extends Controller { @Get() async getWork( @Query() query: string = "", @Query() page: number = 1, @Query() pageSize: number = 30, ) { const where = { OR: [{ name: { contains: query } }], } satisfies Prisma.WorkWhereInput; const [result, total] = await prisma.$transaction([ prisma.work.findMany({ include: { productOnWork: { include: { product: true, }, orderBy: { order: "asc", }, }, }, orderBy: { createdAt: "asc" }, where, take: pageSize, skip: (page - 1) * pageSize, }), prisma.work.count({ where }), ]); return { result, page, pageSize, total }; } @Get("{workId}") async getWorkById(@Path() workId: string) { const record = await prisma.work.findFirst({ where: { id: workId }, }); if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "Work cannot be found.", "workNotFound"); return record; } @Get("{workId}/product") async getProductOfWork( @Path() workId: string, @Query() query: string = "", @Query() page: number = 1, @Query() pageSize: number = 30, ) { const where = { AND: [ { workProduct: { some: { workId } }, }, { OR: [{ name: { contains: query } }], }, ], } satisfies Prisma.ProductWhereInput; const [result, total] = await prisma.$transaction([ prisma.product.findMany({ where, take: pageSize, skip: (page - 1) * pageSize, }), prisma.product.count({ where, }), ]); return { result, page, pageSize, total }; } @Post() async createWork(@Request() req: RequestWithUser, @Body() body: WorkCreate) { const { productId, ...payload } = body; const exist = await prisma.work.findFirst({ include: { productOnWork: { include: { product: true, }, }, }, where: { productOnWork: { every: { productId: { in: productId }, }, }, NOT: { OR: [ { productOnWork: { some: { productId: { notIn: productId }, }, }, }, { productOnWork: { none: {}, }, }, ], }, }, }); if (exist) return exist; const productList = await prisma.product.findMany({ where: { id: { in: productId } }, }); if (productList.length !== productId.length) { throw new HttpError(HttpStatus.BAD_REQUEST, "Some product not found.", "someProductBadReq"); } const record = await prisma.work.create({ include: { productOnWork: { include: { product: true, }, orderBy: { order: "asc", }, }, }, data: { ...payload, productOnWork: { createMany: { data: productId.map((v, i) => ({ order: i + 1, productId: v, createdBy: req.user.name, updateBy: req.user.name, })), }, }, createdBy: req.user.name, updateBy: req.user.name, }, }); await prisma.product.updateMany({ where: { id: { in: body.productId }, status: Status.CREATED }, data: { status: Status.ACTIVE }, }); this.setStatus(HttpStatus.CREATED); return record; } @Put("{workId}") async editWork( @Request() req: RequestWithUser, @Body() body: WorkUpdate, @Path() workId: string, ) { const { productId, ...payload } = body; if (!(await prisma.work.findUnique({ where: { id: workId } }))) { throw new HttpError(HttpStatus.NOT_FOUND, "Work cannot be found.", "workNotFound"); } const exist = await prisma.work.findFirst({ include: { productOnWork: { include: { product: true, }, }, }, where: { productOnWork: { every: { productId: { in: productId }, }, }, NOT: { OR: [ { id: workId }, { productOnWork: { some: { productId: { notIn: productId }, }, }, }, { productOnWork: { none: {}, }, }, ], }, }, }); if (exist) return exist; const record = await prisma.work.update({ include: { productOnWork: { include: { product: true, }, orderBy: { order: "asc", }, }, }, where: { id: workId }, data: { ...payload, productOnWork: productId ? { deleteMany: { productId: { notIn: productId }, }, upsert: productId.map((v, i) => ({ where: { workId_productId: { workId, productId: v, }, }, update: { order: i + 1 }, create: { order: i + 1, productId: v, createdBy: req.user.name, updateBy: req.user.name, }, })), } : undefined, updateBy: req.user.name, }, }); return record; } @Delete("{workId}") async deleteWork(@Path() workId: string) { const record = await prisma.work.findFirst({ where: { id: workId } }); if (!record) { throw new HttpError(HttpStatus.NOT_FOUND, "Work cannot be found.", "workNotFound"); } if (record.status !== Status.CREATED) { throw new HttpError(HttpStatus.FORBIDDEN, "Work is in used.", "workInUsed"); } return await prisma.work.delete({ where: { id: workId } }); } }