jws-backend/src/controllers/04-invoice-controller.ts
2024-10-25 16:37:59 +07:00

239 lines
6.5 KiB
TypeScript

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.$transaction(async (tx) => {
await tx.quotation.update({
where: { id: body.quotationId },
data: { quotationStatus: "PaymentInProcess" },
});
return await tx.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;
});
}
}