refactor!: payment sys

This commit is contained in:
Methapon Metanipat 2024-10-25 13:58:29 +07:00
parent 3cbc157028
commit 02e17fcde4
7 changed files with 675 additions and 279 deletions

View file

@ -0,0 +1,56 @@
/*
Warnings:
- You are about to drop the `QuotationPayment` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "QuotationPayment" DROP CONSTRAINT "QuotationPayment_quotationId_fkey";
-- AlterTable
ALTER TABLE "QuotationProductServiceList" ADD COLUMN "invoiceId" TEXT;
-- DropTable
DROP TABLE "QuotationPayment";
-- CreateTable
CREATE TABLE "Invoice" (
"id" TEXT NOT NULL,
"quotationId" TEXT NOT NULL,
"amount" DOUBLE PRECISION,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdByUserId" TEXT NOT NULL,
CONSTRAINT "Invoice_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Payment" (
"id" TEXT NOT NULL,
"invoiceId" TEXT NOT NULL,
"paymentStatus" "PaymentStatus" NOT NULL,
"amount" DOUBLE PRECISION NOT NULL,
"date" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdByUserId" TEXT,
CONSTRAINT "Payment_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Payment_invoiceId_key" ON "Payment"("invoiceId");
-- AddForeignKey
ALTER TABLE "QuotationProductServiceList" ADD CONSTRAINT "QuotationProductServiceList_invoiceId_fkey" FOREIGN KEY ("invoiceId") REFERENCES "Invoice"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Invoice" ADD CONSTRAINT "Invoice_quotationId_fkey" FOREIGN KEY ("quotationId") REFERENCES "Quotation"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Invoice" ADD CONSTRAINT "Invoice_createdByUserId_fkey" FOREIGN KEY ("createdByUserId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Payment" ADD CONSTRAINT "Payment_invoiceId_fkey" FOREIGN KEY ("invoiceId") REFERENCES "Invoice"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Payment" ADD CONSTRAINT "Payment_createdByUserId_fkey" FOREIGN KEY ("createdByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View file

@ -423,6 +423,8 @@ model User {
quotationUpdated Quotation[] @relation("QuotationUpdatedByUser")
flowCreated WorkflowTemplate[] @relation("FlowCreatedByUser")
flowUpdated WorkflowTemplate[] @relation("FlowUpdatedByUser")
invoiceCreated Invoice[]
paymentCreated Payment[]
}
enum CustomerType {
@ -1106,8 +1108,7 @@ model Quotation {
status Status @default(CREATED)
statusOrder Int @default(0)
quotationStatus QuotationStatus @default(PaymentPending)
quotationPaymentData QuotationPayment[]
quotationStatus QuotationStatus @default(PaymentPending)
remark String?
@ -1152,26 +1153,8 @@ model Quotation {
updatedAt DateTime @updatedAt
updatedBy User? @relation(name: "QuotationUpdatedByUser", fields: [updatedByUserId], references: [id], onDelete: SetNull)
updatedByUserId String?
}
enum PaymentStatus {
PaymentWait
PaymentInProcess
PaymentRetry
PaymentSuccess
}
model QuotationPayment {
id String @id @default(cuid())
paymentStatus PaymentStatus
date DateTime
amount Float
remark String?
quotation Quotation @relation(fields: [quotationId], references: [id], onDelete: Cascade)
quotationId String
invoice Invoice[]
}
model QuotationPaySplit {
@ -1221,6 +1204,9 @@ model QuotationProductServiceList {
worker QuotationProductServiceWorker[]
requestWork RequestWork[]
invoice Invoice? @relation(fields: [invoiceId], references: [id])
invoiceId String?
}
model QuotationProductServiceWorker {
@ -1233,6 +1219,46 @@ model QuotationProductServiceWorker {
@@id([productServiceId, employeeId])
}
model Invoice {
id String @id @default(cuid())
quotation Quotation @relation(fields: [quotationId], references: [id])
quotationId String
productServiceList QuotationProductServiceList[]
amount Float?
payment Payment?
createdAt DateTime @default(now())
createdBy User @relation(fields: [createdByUserId], references: [id])
createdByUserId String
}
enum PaymentStatus {
PaymentWait
PaymentInProcess
PaymentRetry
PaymentSuccess
}
model Payment {
id String @id @default(cuid())
invoice Invoice @relation(fields: [invoiceId], references: [id])
invoiceId String @unique
paymentStatus PaymentStatus
amount Float
date DateTime?
createdAt DateTime @default(now())
createdBy User? @relation(fields: [createdByUserId], references: [id])
createdByUserId String?
}
model RequestData {
id String @id @default(cuid())

View file

@ -0,0 +1,233 @@
import { Prisma } from "@prisma/client";
import {
Body,
Controller,
Delete,
Get,
OperationId,
Path,
Post,
Put,
Query,
Request,
Route,
Security,
Tags,
} from "tsoa";
import prisma from "../db";
import { notFoundError } from "../utils/error";
import { RequestWithUser } from "../interfaces/user";
import {
branchRelationPermInclude,
createPermCheck,
createPermCondition,
} from "../services/permission";
type InvoicePayload = {
quotationId: string;
amount: number;
// NOTE: For individual list that will be include in the quotation
productServiceListId?: string[];
// NOTE: Will be pulled from quotation
installmentNo?: number[];
};
const MANAGE_ROLES = ["system", "head_of_admin", "admin", "head_of_account", "account"];
function globalAllow(user: RequestWithUser["user"]) {
const allowList = ["system", "head_of_admin", "head_of_account"];
return allowList.some((v) => user.roles?.includes(v));
}
const permissionCondCompany = createPermCondition((_) => true);
const permissionCheck = createPermCheck(globalAllow);
@Route("/api/v1/invoice")
@Tags("Invoice")
export class InvoiceController extends Controller {
@Get()
@OperationId("getInvoiceList")
@Security("keycloak")
async getInvoiceList(
@Request() req: RequestWithUser,
@Query() page: number = 1,
@Query() pageSize: number = 30,
@Query() quotationId?: string,
) {
const where: Prisma.InvoiceWhereInput = {
quotationId,
quotation: {
registeredBranch: {
OR: permissionCondCompany(req.user),
},
},
};
const [result, total] = await prisma.$transaction([
prisma.invoice.findMany({
where,
include: {
productServiceList: {
include: {
worker: true,
service: true,
work: true,
product: true,
},
},
quotation: true,
createdBy: true,
},
orderBy: { createdAt: "asc" },
}),
prisma.invoice.count({ where }),
]);
return { result, page, pageSize, total };
}
@Get("{invoiceId}")
@OperationId("getInvoice")
@Security("keycloak")
async getInvoice(@Path() invoiceId: string) {
const record = await prisma.invoice.findFirst({
where: { id: invoiceId },
include: {
productServiceList: {
include: {
worker: true,
service: true,
work: true,
product: true,
},
},
quotation: true,
createdBy: true,
},
orderBy: { createdAt: "asc" },
});
if (!record) throw notFoundError("Invoice");
return record;
}
@Post()
@OperationId("createInvoice")
@Security("keycloak", MANAGE_ROLES)
async createInvoice(@Request() req: RequestWithUser, @Body() body: InvoicePayload) {
const [quotation, productServiceList] = await prisma.$transaction([
prisma.quotation.findUnique({
where: { id: body.quotationId },
include: { registeredBranch: { include: branchRelationPermInclude(req.user) } },
}),
prisma.quotationProductServiceList.findMany({
where: {
OR: [
{ id: { in: body.productServiceListId }, invoiceId: null },
{ installmentNo: { in: body.installmentNo }, invoiceId: null },
],
},
}),
]);
if (!quotation) throw notFoundError("Quotation");
await permissionCheck(req.user, quotation.registeredBranch);
return await prisma.invoice.create({
data: {
productServiceList: { connect: productServiceList.map((v) => ({ id: v.id })) },
quotationId: body.quotationId,
amount: body.amount,
payment: {
create: {
paymentStatus: "PaymentWait",
amount: body.amount,
},
},
createdByUserId: req.user.sub,
},
});
}
@Put("{invoiceId}")
@OperationId("updateInvoice")
@Security("keycloak", MANAGE_ROLES)
async updateInvoice(
@Request() req: RequestWithUser,
@Body() body: InvoicePayload,
@Path() invoiceId: string,
) {
const [record, quotation, productServiceList] = await prisma.$transaction([
prisma.invoice.findUnique({
where: { id: invoiceId },
include: {
productServiceList: {
where: {
id: { notIn: body.productServiceListId },
installmentNo: { notIn: body.installmentNo },
},
},
},
}),
prisma.quotation.findUnique({
where: { id: body.quotationId },
include: { registeredBranch: { include: branchRelationPermInclude(req.user) } },
}),
prisma.quotationProductServiceList.findMany({
where: {
OR: [
{ id: { in: body.productServiceListId }, invoiceId: null },
{ installmentNo: { in: body.installmentNo }, invoiceId: null },
],
},
}),
]);
if (!record) throw notFoundError("Invoice");
if (!quotation) throw notFoundError("Quotation");
await permissionCheck(req.user, quotation.registeredBranch);
return await prisma.$transaction(async (tx) => {
return await tx.invoice.update({
where: { id: invoiceId },
data: {
productServiceList: {
disconnect: record.productServiceList.map((v) => ({ id: v.id })),
connect: productServiceList.map((v) => ({ id: v.id })),
},
},
});
});
}
@Delete("{invoiceId}")
@OperationId("deleteInvoice")
@Security("keycloak", MANAGE_ROLES)
async deleteInvoice(@Request() req: RequestWithUser, @Path() invoiceId: string) {
return await prisma.$transaction(async (tx) => {
const record = await tx.invoice.delete({
where: { id: invoiceId },
include: {
productServiceList: {
include: {
worker: true,
service: true,
work: true,
product: true,
},
},
quotation: {
include: { registeredBranch: { include: branchRelationPermInclude(req.user) } },
},
createdBy: true,
},
});
if (!record) throw notFoundError("Invoice");
await permissionCheck(req.user, record.quotation.registeredBranch);
return record;
});
}
}

View file

@ -0,0 +1,90 @@
import { Controller, Get, OperationId, Path, Query, Request, Route, Security, Tags } from "tsoa";
import prisma from "../db";
import { Prisma } from "@prisma/client";
import { notFoundError } from "../utils/error";
import { RequestWithUser } from "../interfaces/user";
import { createPermCondition } from "../services/permission";
const permissionCondCompany = createPermCondition((_) => true);
@Route("/api/v1/receipt")
@Tags("Receipt")
export class ReceiptController extends Controller {
@Get()
@OperationId("getReceiptList")
@Security("keycloak")
async getReceiptList(
@Request() req: RequestWithUser,
@Query() page: number = 1,
@Query() pageSize: number = 30,
@Query() quotationId?: string,
) {
const where: Prisma.PaymentWhereInput = {
paymentStatus: "PaymentSuccess",
invoice: {
quotationId,
quotation: {
registeredBranch: {
OR: permissionCondCompany(req.user),
},
},
},
};
const [result, total] = await prisma.$transaction([
prisma.payment.findMany({
where,
include: {
invoice: {
include: {
productServiceList: {
include: {
worker: true,
service: true,
work: true,
product: true,
},
},
quotation: true,
createdBy: true,
},
},
},
orderBy: { createdAt: "asc" },
}),
prisma.payment.count({ where }),
]);
return { result, page, pageSize, total };
}
@Get("{receiptId}")
@OperationId("getReceipt")
@Security("keycloak")
async getReceipt(@Path() receiptId: string) {
const record = await prisma.payment.findFirst({
where: { id: receiptId },
include: {
invoice: {
include: {
productServiceList: {
include: {
worker: true,
service: true,
work: true,
product: true,
},
},
quotation: true,
createdBy: true,
},
},
},
orderBy: { createdAt: "asc" },
});
if (!record) throw notFoundError("Receipt");
return record;
}
}

View file

@ -0,0 +1,249 @@
import {
Body,
Controller,
Delete,
Get,
Head,
Path,
Put,
Query,
Request,
Route,
Security,
Tags,
} from "tsoa";
import express from "express";
import { PaymentStatus, Prisma } from "@prisma/client";
import prisma from "../db";
import { notFoundError } from "../utils/error";
import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio";
import { RequestWithUser } from "../interfaces/user";
import {
branchRelationPermInclude,
createPermCheck,
createPermCondition,
} from "../services/permission";
const MANAGE_ROLES = ["system", "head_of_admin", "admin", "head_of_account", "account"];
function globalAllow(user: RequestWithUser["user"]) {
const allowList = ["system", "head_of_admin", "head_of_account"];
return allowList.some((v) => user.roles?.includes(v));
}
const permissionCondCompany = createPermCondition((_) => true);
const permissionCheck = createPermCheck(globalAllow);
@Tags("Payment")
@Route("api/v1/payment")
export class QuotationPayment extends Controller {
@Get()
@Security("keycloak")
async getPaymentList(
@Request() req: RequestWithUser,
@Query() page: number = 1,
@Query() pageSize: number = 30,
@Query() quotationId?: string,
) {
const where: Prisma.PaymentWhereInput = {
invoice: {
quotationId,
quotation: {
registeredBranch: {
OR: permissionCondCompany(req.user),
},
},
},
};
const [result, total] = await prisma.$transaction([
prisma.payment.findMany({
where,
include: {
invoice: {
include: {
productServiceList: {
include: {
worker: true,
service: true,
work: true,
product: true,
},
},
quotation: true,
createdBy: true,
},
},
},
orderBy: { createdAt: "asc" },
}),
prisma.payment.count({ where }),
]);
return { result, page, pageSize, total };
}
@Get("{quotationId}")
@Security("keycloak")
async getPayment(@Query() quotationId: string) {
const record = await prisma.payment.findFirst({
where: { invoice: { quotationId } },
include: { createdBy: true },
});
return record;
}
@Put("{paymentId}")
@Security("keycloak", MANAGE_ROLES)
async updatePayment(
@Path() paymentId: string,
@Body() body: { amount?: number; date?: Date; paymentStatus?: PaymentStatus },
) {
const record = await prisma.payment.findUnique({
where: { id: paymentId },
include: {
invoice: {
include: {
quotation: {
include: {
_count: {
select: { paySplit: true },
},
worker: true,
productServiceList: {
include: {
worker: true,
work: true,
service: true,
product: true,
},
},
},
},
},
},
},
});
if (!record) throw notFoundError("Payment");
return await prisma.$transaction(async (tx) => {
const quotation = record.invoice.quotation;
const payment = await tx.payment.update({
where: { id: paymentId, invoice: { quotationId: quotation.id } },
data: body,
});
const paymentSum = await prisma.payment.aggregate({
_sum: { amount: true },
where: { invoice: { quotationId: quotation.id } },
});
await tx.quotation.update({
where: { id: quotation.id },
data: {
quotationStatus:
paymentSum._sum.amount || 0 >= quotation.finalPrice
? "PaymentSuccess"
: "PaymentInProcess",
requestData:
quotation.quotationStatus === "PaymentPending"
? {
create: quotation.worker.map((v) => ({
employeeId: v.employeeId,
requestWork: {
create: quotation.productServiceList.flatMap((item) =>
item.worker.findIndex((w) => w.employeeId === v.employeeId) !== -1
? { productServiceId: item.id }
: [],
),
},
})),
}
: undefined,
},
});
return payment;
});
}
}
@Route("api/v1/payment/{paymentId}/attachment")
@Tags("Payment")
export class PaymentController extends Controller {
private async checkPermission(user: RequestWithUser["user"], id: string) {
const data = await prisma.payment.findUnique({
include: {
invoice: {
include: {
quotation: {
include: {
registeredBranch: {
include: branchRelationPermInclude(user),
},
},
},
},
},
},
where: { id },
});
if (!data) throw notFoundError("Payment");
await permissionCheck(user, data.invoice.quotation.registeredBranch);
return { paymentId: id, quotationId: data.invoice.quotationId };
}
@Get()
async listAttachment(@Request() req: RequestWithUser, @Path() paymentId: string) {
const { quotationId } = await this.checkPermission(req.user, paymentId);
return await listFile(fileLocation.quotation.payment(quotationId, paymentId));
}
@Head("{name}")
async headAttachment(
@Request() req: RequestWithUser,
@Path() paymentId: string,
@Path() name: string,
) {
const { quotationId } = await this.checkPermission(req.user, paymentId);
return req.res?.redirect(
await getPresigned("head", fileLocation.quotation.payment(quotationId, paymentId, name)),
);
}
@Get("{name}")
async getAttachment(
@Request() req: RequestWithUser,
@Path() paymentId: string,
@Path() name: string,
) {
const { quotationId } = await this.checkPermission(req.user, paymentId);
return req.res?.redirect(
await getFile(fileLocation.quotation.payment(quotationId, paymentId, name)),
);
}
@Put("{name}")
@Security("keycloak", MANAGE_ROLES)
async putAttachment(
@Request() req: RequestWithUser,
@Path() paymentId: string,
@Path() name: string,
) {
const { quotationId } = await this.checkPermission(req.user, paymentId);
return await setFile(fileLocation.quotation.payment(quotationId, paymentId, name));
}
@Delete("{name}")
@Security("keycloak", MANAGE_ROLES)
async deleteAttachment(
@Request() req: RequestWithUser,
@Path() paymentId: string,
@Path() name: string,
) {
const { quotationId } = await this.checkPermission(req.user, paymentId);
return await deleteFile(fileLocation.quotation.payment(quotationId, paymentId, name));
}
}

View file

@ -490,20 +490,6 @@ export class QuotationController extends Controller {
data: {
...rest,
...price,
quotationPaymentData: {
create:
rest.payCondition === "BillSplit" || rest.payCondition === "Split"
? rest.paySplit?.map((v) => ({
paymentStatus: "PaymentWait",
amount: v.amount,
date: v.date,
}))
: {
paymentStatus: "PaymentWait",
amount: price.finalPrice,
date: new Date(),
},
},
statusOrder: +(rest.status === "INACTIVE"),
code: `${currentYear.toString().padStart(2, "0")}${currentMonth.toString().padStart(2, "0")}${currentDate.toString().padStart(2, "0")}${lastQuotation.value.toString().padStart(4, "0")}`,
worker: {
@ -746,24 +732,6 @@ export class QuotationController extends Controller {
data: {
...rest,
...price,
quotationPaymentData:
price && record.quotationStatus === "PaymentPending"
? {
deleteMany: {},
create:
rest.payCondition === "BillSplit" || rest.payCondition === "Split"
? rest.paySplit?.map((v) => ({
paymentStatus: "PaymentWait",
amount: v.amount,
date: v.date,
}))
: {
paymentStatus: "PaymentWait",
amount: price.finalPrice,
date: new Date(),
},
}
: undefined,
statusOrder: +(rest.status === "INACTIVE"),
worker:
sortedEmployeeId.length > 0

View file

@ -1,226 +0,0 @@
import {
Body,
Controller,
Delete,
Get,
Head,
Path,
Post,
Put,
Query,
Request,
Route,
Tags,
} from "tsoa";
import express from "express";
import { PaymentStatus } from "@prisma/client";
import prisma from "../db";
import { notFoundError } from "../utils/error";
import HttpError from "../interfaces/http-error";
import HttpStatus from "../interfaces/http-status";
import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio";
@Tags("Payment")
@Route("api/v1/quotation/{quotationId}/payment")
export class QuotationPayment extends Controller {
@Get()
async getPayment(@Path() quotationId: string) {
const record = await prisma.quotation.findFirst({
where: { id: quotationId },
include: {
quotationPaymentData: {
orderBy: { date: "asc" },
},
createdBy: true,
updatedBy: true,
},
});
return record;
}
@Post()
async addPayment(
@Path() quotationId: string,
@Body() body: { amount: number; date: Date; remark: string; paymentStatus?: PaymentStatus },
) {
const record = await prisma.quotation.findUnique({
where: { id: quotationId },
});
if (!record) throw notFoundError("Quotation");
if (!body.paymentStatus && record.quotationStatus !== "PaymentPending") {
// NOTE: The quotation must be in waiting for payment or waiting for payment confirmation (re-submit payment)
throw new HttpError(
HttpStatus.PRECONDITION_FAILED,
"Cannot submit payment info of this quotation",
"quotationStatusWrong",
);
}
return await prisma.quotation.update({
where: { id: quotationId },
include: { quotationPaymentData: true },
data: {
quotationStatus: "PaymentInProcess",
quotationPaymentData: {
create: {
paymentStatus: "PaymentWait",
...body,
},
},
},
});
}
@Put("{paymentId}")
async updatePayment(
@Path() quotationId: string,
@Path() paymentId: string,
@Body() body: { amount?: number; date?: Date; remark?: string; paymentStatus?: PaymentStatus },
) {
const record = await prisma.quotationPayment.findUnique({
where: { id: paymentId, quotationId },
});
if (!record) throw notFoundError("Quotation Payment");
return await prisma.quotationPayment.update({
where: { id: paymentId, quotationId },
data: body,
});
}
@Get("{paymentId}/attachment")
async listPaymentFile(@Path() quotationId: string, @Path() paymentId: string) {
return await listFile(fileLocation.quotation.payment(quotationId, paymentId));
}
@Head("{paymentId}/attachment/{name}")
async headPaymentFile(
@Request() req: express.Request,
@Path() quotationId: string,
@Path() paymentId: string,
@Path() name: string,
) {
return req.res?.redirect(
await getPresigned("head", fileLocation.quotation.payment(quotationId, paymentId, name)),
);
}
@Get("{paymentId}/attachment/{name}")
async getPaymentFile(
@Request() req: express.Request,
@Path() quotationId: string,
@Path() paymentId: string,
@Path() name: string,
) {
return req.res?.redirect(
await getFile(fileLocation.quotation.payment(quotationId, paymentId, name)),
);
}
@Put("{paymentId}/attachment/{name}")
async uploadPayment(
@Path() quotationId: string,
@Path() paymentId: string,
@Path() name: string,
) {
const record = await prisma.quotation.findUnique({
where: { id: quotationId },
});
if (!record) throw notFoundError("Quotation");
if (record.quotationStatus !== "PaymentPending") {
// NOTE: The quotation must be in waiting for payment or waiting for payment confirmation (re-submit payment)
throw new HttpError(
HttpStatus.PRECONDITION_FAILED,
"Cannot submit payment info of this quotation",
"quotationStatusWrong",
);
}
return await setFile(fileLocation.quotation.payment(quotationId, paymentId, name));
}
@Delete("{paymentId}/attachment/{name}")
async deletePayment(
@Path() quotationId: string,
@Path() paymentId: string,
@Path() name: string,
) {
const record = await prisma.quotation.findUnique({
where: { id: quotationId },
});
if (!record) throw notFoundError("Quotation");
return await deleteFile(fileLocation.quotation.payment(quotationId, paymentId, name));
}
@Post("confirm")
async confirmPayment(@Path() quotationId: string, @Query() paymentId?: string) {
const record = await prisma.quotation.findUnique({
include: {
_count: {
select: {
quotationPaymentData: true,
paySplit: true,
},
},
worker: true,
quotationPaymentData: true,
productServiceList: {
include: {
worker: true,
work: true,
service: true,
product: true,
},
},
},
where: { id: quotationId },
});
if (!record) throw notFoundError("Quotation");
await prisma.$transaction(async (tx) => {
await tx.quotation.update({
where: { id: quotationId },
data: {
quotationStatus:
record.payCondition === "Full" ||
record.payCondition === "BillFull" ||
record._count.paySplit === record._count.quotationPaymentData
? "PaymentSuccess"
: undefined,
quotationPaymentData: {
update: paymentId
? {
where: { id: paymentId },
data: { paymentStatus: "PaymentSuccess" },
}
: undefined,
},
requestData:
record.quotationStatus === "PaymentPending"
? {
create: record.worker.map((v) => ({
employeeId: v.employeeId,
requestWork: {
create: record.productServiceList.flatMap((item) =>
item.worker.findIndex((w) => w.employeeId === v.employeeId) !== -1
? { productServiceId: item.id }
: [],
),
},
})),
}
: undefined,
},
});
});
}
}