diff --git a/prisma/migrations/20250113070153_add_payback_date/migration.sql b/prisma/migrations/20250113070153_add_payback_date/migration.sql new file mode 100644 index 0000000..4375e97 --- /dev/null +++ b/prisma/migrations/20250113070153_add_payback_date/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "CreditNote" ADD COLUMN "paybackDate" TIMESTAMP(3); diff --git a/prisma/migrations/20250114024611_add_credit_note_remark/migration.sql b/prisma/migrations/20250114024611_add_credit_note_remark/migration.sql new file mode 100644 index 0000000..05e0cf0 --- /dev/null +++ b/prisma/migrations/20250114024611_add_credit_note_remark/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "CreditNote" ADD COLUMN "remark" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d69cca0..218e903 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1606,12 +1606,14 @@ model CreditNote { value Float @default(0) reason String? detail String? + remark String? paybackType CreditNotePaybackType? paybackBank String? paybackAccount String? paybackAccountName String? paybackStatus PaybackStatus @default(Pending) + paybackDate DateTime? quotation Quotation @relation(fields: [quotationId], references: [id], onDelete: Cascade) quotationId String diff --git a/src/controllers/04-receipt-controller.ts b/src/controllers/04-receipt-controller.ts index 42dbc55..c66b5de 100644 --- a/src/controllers/04-receipt-controller.ts +++ b/src/controllers/04-receipt-controller.ts @@ -38,7 +38,13 @@ export class ReceiptController extends Controller { invoice: { include: { installments: true, - quotation: true, + quotation: { + include: { + customerBranch: { + include: { customer: true }, + }, + }, + }, createdBy: true, }, }, diff --git a/src/controllers/07-task-controller.ts b/src/controllers/07-task-controller.ts index 22357b4..77a593b 100644 --- a/src/controllers/07-task-controller.ts +++ b/src/controllers/07-task-controller.ts @@ -32,7 +32,15 @@ import { } from "../services/permission"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; -import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio"; +import { + deleteFile, + deleteFolder, + fileLocation, + getFile, + getPresigned, + listFile, + setFile, +} from "../utils/minio"; import { queryOrNot } from "../utils/relation"; const MANAGE_ROLES = ["system", "head_of_admin", "admin", "document_checker"]; @@ -429,6 +437,9 @@ export class TaskController extends Controller { if (!record) throw notFoundError("Task Order"); await permissionCheck(req.user, record.registeredBranch); + + await Promise.all([deleteFolder(fileLocation.task.attachment(taskOrderId))]); + await tx.taskOrder.delete({ where: { id: taskOrderId } }); }); } } diff --git a/src/controllers/08-credit-note-controller.ts b/src/controllers/08-credit-note-controller.ts index d47376c..ad29da1 100644 --- a/src/controllers/08-credit-note-controller.ts +++ b/src/controllers/08-credit-note-controller.ts @@ -24,7 +24,15 @@ import { } from "../services/permission"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; -import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio"; +import { + deleteFile, + deleteFolder, + fileLocation, + getFile, + getPresigned, + listFile, + setFile, +} from "../utils/minio"; import { notFoundError } from "../utils/error"; import { CreditNotePaybackType, CreditNoteStatus, Prisma } from "@prisma/client"; import { queryOrNot } from "../utils/relation"; @@ -56,6 +64,7 @@ type CreditNoteCreate = { quotationId: string; reason?: string; detail?: string; + remark?: string; paybackType?: CreditNotePaybackType; paybackBank?: string; paybackAccount?: string; @@ -66,6 +75,7 @@ type CreditNoteUpdate = { quotationId: string; reason?: string; detail?: string; + remark?: string; paybackType?: CreditNotePaybackType; paybackBank?: string; paybackAccount?: string; @@ -355,6 +365,7 @@ export class CreditNoteController extends Controller { data: { reason: body.reason, detail: body.detail, + remark: body.remark, paybackType: body.paybackType, paybackBank: body.paybackBank, paybackAccount: body.paybackAccount, @@ -471,6 +482,7 @@ export class CreditNoteController extends Controller { data: { reason: body.reason, detail: body.detail, + remark: body.remark, paybackType: body.paybackType, paybackBank: body.paybackBank, paybackAccount: body.paybackAccount, @@ -511,6 +523,11 @@ export class CreditNoteController extends Controller { if (!record) throw notFoundError("Credit Note"); await permissionCheck(req.user, record.quotation.registeredBranch); + + await Promise.all([ + deleteFolder(fileLocation.creditNote.slip(creditNoteId)), + deleteFolder(fileLocation.creditNote.attachment(creditNoteId)), + ]); return await prisma.creditNote.delete({ where: { id: creditNoteId } }); } } @@ -540,7 +557,7 @@ export class CreditNoteActionController extends Controller { async updateStatus( @Request() req: RequestWithUser, @Path() creditNoteId: string, - @Body() body: PaybackStatus, + @Body() body: { paybackStatus: PaybackStatus }, ) { await this.#checkPermission(req.user, creditNoteId); return await prisma.creditNote.update({ @@ -554,8 +571,10 @@ export class CreditNoteActionController extends Controller { quotation: true, }, data: { - creditNoteStatus: body === PaybackStatus.Done ? CreditNoteStatus.Success : undefined, - paybackStatus: body, + creditNoteStatus: + body.paybackStatus === PaybackStatus.Done ? CreditNoteStatus.Success : undefined, + paybackStatus: body.paybackStatus, + paybackDate: body.paybackStatus === PaybackStatus.Done ? new Date() : undefined, }, }); } @@ -620,4 +639,44 @@ export class CreditNoteAttachmentController extends Controller { await this.#checkPermission(req.user, creditNoteId); return await deleteFile(fileLocation.creditNote.attachment(creditNoteId, name)); } + + @Get("file-slip") + @Security("keycloak") + async listSlip(@Request() req: RequestWithUser, @Path() creditNoteId: string) { + await this.#checkPermission(req.user, creditNoteId); + return await listFile(fileLocation.creditNote.slip(creditNoteId)); + } + + @Get("file-slip/{name}") + @Security("keycloak") + async getSlip(@Path() creditNoteId: string, @Path() name: string) { + return await getFile(fileLocation.creditNote.slip(creditNoteId, name)); + } + + @Head("file-slip/{name}") + async headSlip(@Path() creditNoteId: string, @Path() name: string) { + return await getPresigned("head", fileLocation.creditNote.slip(creditNoteId, name)); + } + + @Put("file-slip/{name}") + @Security("keycloak") + async putSlip( + @Request() req: RequestWithUser, + @Path() creditNoteId: string, + @Path() name: string, + ) { + await this.#checkPermission(req.user, creditNoteId); + return await setFile(fileLocation.creditNote.slip(creditNoteId, name)); + } + + @Delete("file-slip/{name}") + @Security("keycloak") + async delSlip( + @Request() req: RequestWithUser, + @Path() creditNoteId: string, + @Path() name: string, + ) { + await this.#checkPermission(req.user, creditNoteId); + return await deleteFile(fileLocation.creditNote.slip(creditNoteId, name)); + } } diff --git a/src/utils/minio.ts b/src/utils/minio.ts index 7ec0a3b..f5f37e6 100644 --- a/src/utils/minio.ts +++ b/src/utils/minio.ts @@ -113,6 +113,8 @@ export const fileLocation = { attachment: (taskId: string, name?: string) => `task/attachment-${taskId}/${name || ""}`, }, creditNote: { - attachment: (taskId: string, name?: string) => `credit-note/attachment-${taskId}/${name || ""}`, + slip: (creditNoteId: string, name?: string) => `credit-note/slip-${creditNoteId}/${name || ""}`, + attachment: (creditNoteId: string, name?: string) => + `credit-note/attachment-${creditNoteId}/${name || ""}`, }, };