226 lines
6.3 KiB
TypeScript
226 lines
6.3 KiB
TypeScript
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,
|
|
},
|
|
});
|
|
});
|
|
}
|
|
}
|