From 824727582d28ab2b58b994701ca09ae29ec9ea12 Mon Sep 17 00:00:00 2001 From: Methapon Metanipat <162551568+Methapon-Frappet@users.noreply.github.com> Date: Wed, 30 Oct 2024 17:32:46 +0700 Subject: [PATCH] refactor: invoice relation (#2) * refactor: update database table * refactor: update invoice to have relation with installments * chore: add migration --- .../migration.sql | 77 ++++++++++ prisma/schema.prisma | 13 +- src/controllers/04-invoice-controller.ts | 135 ++---------------- src/controllers/04-receipt-controller.ts | 18 +-- src/controllers/05-payment-controller.ts | 18 +-- src/controllers/05-quotation-controller.ts | 8 +- 6 files changed, 106 insertions(+), 163 deletions(-) create mode 100644 prisma/migrations/20241030095801_update_invoice_relation/migration.sql diff --git a/prisma/migrations/20241030095801_update_invoice_relation/migration.sql b/prisma/migrations/20241030095801_update_invoice_relation/migration.sql new file mode 100644 index 0000000..28149c1 --- /dev/null +++ b/prisma/migrations/20241030095801_update_invoice_relation/migration.sql @@ -0,0 +1,77 @@ +/* + Warnings: + + - You are about to drop the column `invoice` on the `QuotationPaySplit` table. All the data in the column will be lost. + - You are about to drop the column `invoiceId` on the `QuotationProductServiceList` table. All the data in the column will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "QuotationProductServiceList" DROP CONSTRAINT "QuotationProductServiceList_invoiceId_fkey"; + +-- AlterTable +ALTER TABLE "QuotationPaySplit" DROP COLUMN "invoice", +ADD COLUMN "invoiceId" TEXT; + +-- AlterTable +ALTER TABLE "QuotationProductServiceList" DROP COLUMN "invoiceId"; + +-- CreateTable +CREATE TABLE "Notification" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "detail" TEXT NOT NULL, + "receiverId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Notification_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "NotificationGroup" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + + CONSTRAINT "NotificationGroup_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "_NotificationToNotificationGroup" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL +); + +-- CreateTable +CREATE TABLE "_NotificationToUser" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "_NotificationToNotificationGroup_AB_unique" ON "_NotificationToNotificationGroup"("A", "B"); + +-- CreateIndex +CREATE INDEX "_NotificationToNotificationGroup_B_index" ON "_NotificationToNotificationGroup"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_NotificationToUser_AB_unique" ON "_NotificationToUser"("A", "B"); + +-- CreateIndex +CREATE INDEX "_NotificationToUser_B_index" ON "_NotificationToUser"("B"); + +-- AddForeignKey +ALTER TABLE "Notification" ADD CONSTRAINT "Notification_receiverId_fkey" FOREIGN KEY ("receiverId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "QuotationPaySplit" ADD CONSTRAINT "QuotationPaySplit_invoiceId_fkey" FOREIGN KEY ("invoiceId") REFERENCES "Invoice"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_NotificationToNotificationGroup" ADD CONSTRAINT "_NotificationToNotificationGroup_A_fkey" FOREIGN KEY ("A") REFERENCES "Notification"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_NotificationToNotificationGroup" ADD CONSTRAINT "_NotificationToNotificationGroup_B_fkey" FOREIGN KEY ("B") REFERENCES "NotificationGroup"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_NotificationToUser" ADD CONSTRAINT "_NotificationToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "Notification"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_NotificationToUser" ADD CONSTRAINT "_NotificationToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ce5dff8..7e49bf2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1194,12 +1194,14 @@ model Quotation { model QuotationPaySplit { id String @id @default(cuid()) - no Int - amount Float - invoice Boolean @default(false) + no Int + amount Float quotation Quotation? @relation(fields: [quotationId], references: [id], onDelete: Cascade) quotationId String? + + invoice Invoice? @relation(fields: [invoiceId], references: [id]) + invoiceId String? } model QuotationWorker { @@ -1238,9 +1240,6 @@ model QuotationProductServiceList { worker QuotationProductServiceWorker[] requestWork RequestWork[] - - invoice Invoice? @relation(fields: [invoiceId], references: [id]) - invoiceId String? } model QuotationProductServiceWorker { @@ -1259,7 +1258,7 @@ model Invoice { quotation Quotation @relation(fields: [quotationId], references: [id]) quotationId String - productServiceList QuotationProductServiceList[] + installments QuotationPaySplit[] amount Float? diff --git a/src/controllers/04-invoice-controller.ts b/src/controllers/04-invoice-controller.ts index 0dc21bb..b7998af 100644 --- a/src/controllers/04-invoice-controller.ts +++ b/src/controllers/04-invoice-controller.ts @@ -2,12 +2,10 @@ import { Prisma } from "@prisma/client"; import { Body, Controller, - Delete, Get, OperationId, Path, Post, - Put, Query, Request, Route, @@ -26,10 +24,7 @@ import { 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[]; + installmentNo: number[]; }; const MANAGE_ROLES = ["system", "head_of_admin", "admin", "head_of_account", "account"]; @@ -67,14 +62,7 @@ export class InvoiceController extends Controller { prisma.invoice.findMany({ where, include: { - productServiceList: { - include: { - worker: true, - service: true, - work: true, - product: true, - }, - }, + installments: true, quotation: true, createdBy: true, }, @@ -93,14 +81,7 @@ export class InvoiceController extends Controller { const record = await prisma.invoice.findFirst({ where: { id: invoiceId }, include: { - productServiceList: { - include: { - worker: true, - service: true, - work: true, - product: true, - }, - }, + installments: true, quotation: true, createdBy: true, }, @@ -116,44 +97,35 @@ export class InvoiceController extends Controller { @OperationId("createInvoice") @Security("keycloak", MANAGE_ROLES) async createInvoice(@Request() req: RequestWithUser, @Body() body: InvoicePayload) { - const [quotation, productServiceList] = await prisma.$transaction([ + const [quotation] = 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({ + const record = await tx.quotation.update({ + include: { + paySplit: { + where: { no: { in: body.installmentNo } }, + }, + }, where: { id: body.quotationId }, data: { quotationStatus: "PaymentInProcess", - paySplit: body.installmentNo - ? { - updateMany: { - where: { no: { in: body.installmentNo } }, - data: { invoice: true }, - }, - } - : undefined, }, }); return await tx.invoice.create({ data: { - productServiceList: { connect: productServiceList.map((v) => ({ id: v.id })) }, quotationId: body.quotationId, amount: body.amount, + installments: { + connect: record.paySplit.map((v) => ({ id: v.id })), + }, payment: { create: { paymentStatus: "PaymentWait", @@ -165,85 +137,4 @@ export class InvoiceController extends Controller { }); }); } - - @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; - }); - } } diff --git a/src/controllers/04-receipt-controller.ts b/src/controllers/04-receipt-controller.ts index 678b2c7..42dbc55 100644 --- a/src/controllers/04-receipt-controller.ts +++ b/src/controllers/04-receipt-controller.ts @@ -37,14 +37,7 @@ export class ReceiptController extends Controller { include: { invoice: { include: { - productServiceList: { - include: { - worker: true, - service: true, - work: true, - product: true, - }, - }, + installments: true, quotation: true, createdBy: true, }, @@ -67,14 +60,7 @@ export class ReceiptController extends Controller { include: { invoice: { include: { - productServiceList: { - include: { - worker: true, - service: true, - work: true, - product: true, - }, - }, + installments: true, quotation: true, createdBy: true, }, diff --git a/src/controllers/05-payment-controller.ts b/src/controllers/05-payment-controller.ts index 96c959d..5a811e9 100644 --- a/src/controllers/05-payment-controller.ts +++ b/src/controllers/05-payment-controller.ts @@ -61,14 +61,7 @@ export class QuotationPayment extends Controller { include: { invoice: { include: { - productServiceList: { - include: { - worker: true, - service: true, - work: true, - product: true, - }, - }, + installments: true, quotation: true, createdBy: true, }, @@ -90,14 +83,7 @@ export class QuotationPayment extends Controller { include: { invoice: { include: { - productServiceList: { - include: { - worker: true, - service: true, - work: true, - product: true, - }, - }, + installments: true, quotation: true, createdBy: true, }, diff --git a/src/controllers/05-quotation-controller.ts b/src/controllers/05-quotation-controller.ts index 3d9d364..67f4865 100644 --- a/src/controllers/05-quotation-controller.ts +++ b/src/controllers/05-quotation-controller.ts @@ -289,7 +289,9 @@ export class QuotationController extends Controller { worker: true, }, }, - paySplit: true, + paySplit: { + include: { invoice: true }, + }, createdBy: true, updatedBy: true, }, @@ -482,7 +484,9 @@ export class QuotationController extends Controller { worker: true, }, }, - paySplit: true, + paySplit: { + include: { invoice: true }, + }, worker: true, customerBranch: { include: { customer: true },