diff --git a/src/controllers/05-quotation-controller.ts b/src/controllers/05-quotation-controller.ts index 1021a48..65e2c3b 100644 --- a/src/controllers/05-quotation-controller.ts +++ b/src/controllers/05-quotation-controller.ts @@ -27,6 +27,8 @@ import { isUsedError, notFoundError, relationError } from "../utils/error"; import { precisionRound } from "../utils/arithmetic"; import { queryOrNot } from "../utils/relation"; import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio"; +import HttpError from "../interfaces/http-error"; +import HttpStatus from "../interfaces/http-status"; type QuotationCreate = { registeredBranchId: string; @@ -813,113 +815,76 @@ export class QuotationController extends Controller { where: { id: quotationId }, }); } +} - @Post("{quotationId}/worker") - @Security("keycloak", MANAGE_ROLES) +@Route("api/v1/quotation/{quotationId}") +@Tags("Quotation") +export class QuotationActionController extends Controller { + @Post("add-worker") async addWorker( @Request() req: RequestWithUser, @Path() quotationId: string, @Body() body: { workerId: string; - productServiceListId: string[]; + productServiceId: string[]; }[], ) { - return await prisma.$transaction(async (tx) => { - let record = await tx.quotation.findFirst({ - include: { - _count: { - select: { paySplit: true }, - }, - customerBranch: { - include: { - customer: { - include: { - registeredBranch: { include: branchRelationPermInclude(req.user) }, - }, - }, - }, - }, - worker: true, - productServiceList: { + const ids = { + employee: body.map((v) => v.workerId), + productService: body + .flatMap((v) => v.productServiceId) + .filter((lhs, i, a) => a.findIndex((rhs) => lhs === rhs) === i), + }; + + const [quotation, employee, productService] = await prisma.$transaction( + async (tx) => + await Promise.all([ + tx.quotation.findFirst({ include: { worker: true, - work: true, - service: true, - product: true, - }, - }, - }, - where: { id: quotationId }, - }); - - if (!record) throw notFoundError("Quotation"); - - let quotation = record; - - const paymentSum = await tx.payment.aggregate({ - _sum: { amount: true }, - where: { - invoice: { - quotationId: quotation.id, - payment: { paymentStatus: "PaymentSuccess" }, - }, - }, - }); - - body = body.filter((a) => !quotation.worker.find((b) => b.employeeId === a.workerId)); - if (!paymentSum._sum.amount) { - return await tx.quotation.update({ - include: { - _count: { - select: { paySplit: true }, - }, - customerBranch: { - include: { - customer: { - include: { - registeredBranch: { include: branchRelationPermInclude(req.user) }, - }, + _count: { + select: { + worker: true, }, }, }, - worker: true, - productServiceList: { - include: { - worker: true, - work: true, - service: true, - product: true, - }, - }, - }, - where: { id: quotationId }, - data: { - worker: { - createMany: { - data: body.map((v, i) => ({ - employeeId: v.workerId, - no: quotation.worker.length + i + 1, - })), - }, - }, - productServiceList: { - update: quotation.productServiceList.map((a) => ({ - where: { id: a.id }, - data: { - worker: { - createMany: { - data: body - .filter((b) => b.productServiceListId.includes(a.id)) - .map((val) => ({ employeeId: val.workerId })), - }, - }, - }, - })), - }, - }, - }); - } + where: { id: quotationId }, + }), + tx.employee.findMany({ + where: { id: { in: ids.employee } }, + take: ids.employee.length + 1, // NOTE: Do not find further as it should be equal to input + }), + tx.quotationProductServiceList.findMany({ + where: { id: { in: ids.productService }, quotationId }, + take: ids.productService.length + 1, // NOTE: Do not find further as it should be equal to input + }), + ]), + ); + + if (!quotation) throw relationError("Quotation"); + if (ids.employee.length !== employee.length) throw relationError("Worker"); + if (ids.productService.length !== productService.length) throw relationError("Product"); + if (quotation._count.worker >= (quotation.workerMax || 0)) { + throw new HttpError( + HttpStatus.PRECONDITION_FAILED, + "Worker exceed current quotation max worker.", + "QuotationWorkerExceed", + ); + } + + await prisma.$transaction(async (tx) => { + await tx.quotationProductServiceWorker.createMany({ + data: body + .filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId)) + .flatMap((lhs) => + lhs.productServiceId.map((rhs) => ({ + employeeId: lhs.workerId, + productServiceId: rhs, + })), + ), + skipDuplicates: true, + }); const current = new Date(); const year = `${current.getFullYear()}`.slice(-2).padStart(2, "0"); @@ -935,21 +900,37 @@ export class QuotationController extends Controller { }, update: { value: { increment: quotation.worker.length } }, }); - + console.log(quotation.worker); await tx.quotation.update({ where: { id: quotationId }, data: { - requestData: { - create: body.map((v, i) => ({ - code: `TR${year}${month}${(lastRequest.value - quotation.worker.length + i + 1).toString().padStart(6, "0")}`, - employeeId: v.workerId, - requestWork: { - create: v.productServiceListId - .filter((a) => quotation.productServiceList.find((b) => a === b.id)) - .map((v) => ({ productServiceId: v })), - }, - })), + worker: { + createMany: { + data: body + .filter((lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId)) + .map((v, i) => ({ + no: quotation._count.worker + i + 1, + employeeId: v.workerId, + })), + }, }, + requestData: + quotation.quotationStatus === "PaymentInProcess" || + quotation.quotationStatus === "PaymentSuccess" + ? { + create: body + .filter( + (lhs) => !quotation.worker.find((rhs) => rhs.employeeId === lhs.workerId), + ) + .map((v, i) => ({ + code: `TR${year}${month}${(lastRequest.value - quotation._count.worker + i + 1).toString().padStart(6, "0")}`, + employeeId: v.workerId, + requestWork: { + create: v.productServiceId.map((v) => ({ productServiceId: v })), + }, + })), + } + : undefined, }, }); });