From a0168ee4fb0e7d19d7b85499c1f40044b84b8ab1 Mon Sep 17 00:00:00 2001 From: Methapon Metanipat Date: Tue, 17 Sep 2024 10:57:42 +0700 Subject: [PATCH] feat: customer branch citizen --- .../20240917034817_add_relation/migration.sql | 39 +++++ prisma/schema.prisma | 21 +++ .../03-customer-branch-citizen-controller.ts | 161 ++++++++++++++++++ src/middlewares/customer-branch.ts | 33 ++++ 4 files changed, 254 insertions(+) create mode 100644 prisma/migrations/20240917034817_add_relation/migration.sql create mode 100644 src/controllers/03-customer-branch-citizen-controller.ts create mode 100644 src/middlewares/customer-branch.ts diff --git a/prisma/migrations/20240917034817_add_relation/migration.sql b/prisma/migrations/20240917034817_add_relation/migration.sql new file mode 100644 index 0000000..4f4a5b1 --- /dev/null +++ b/prisma/migrations/20240917034817_add_relation/migration.sql @@ -0,0 +1,39 @@ +/* + Warnings: + + - Added the required column `customerBranchId` to the `CustomerBranchCitizen` table without a default value. This is not possible if the table is not empty. + - Added the required column `customerBranchId` to the `CustomerBranchCommercialRegis` table without a default value. This is not possible if the table is not empty. + - Added the required column `customerBranchId` to the `CustomerBranchHouseRegis` table without a default value. This is not possible if the table is not empty. + - Added the required column `customerBranchId` to the `CustomerBranchPoa` table without a default value. This is not possible if the table is not empty. + - Added the required column `customerBranchId` to the `CustomerBranchVatRegis` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "CustomerBranchCitizen" ADD COLUMN "customerBranchId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "CustomerBranchCommercialRegis" ADD COLUMN "customerBranchId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "CustomerBranchHouseRegis" ADD COLUMN "customerBranchId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "CustomerBranchPoa" ADD COLUMN "customerBranchId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "CustomerBranchVatRegis" ADD COLUMN "customerBranchId" TEXT NOT NULL; + +-- AddForeignKey +ALTER TABLE "CustomerBranchCitizen" ADD CONSTRAINT "CustomerBranchCitizen_customerBranchId_fkey" FOREIGN KEY ("customerBranchId") REFERENCES "CustomerBranch"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CustomerBranchPoa" ADD CONSTRAINT "CustomerBranchPoa_customerBranchId_fkey" FOREIGN KEY ("customerBranchId") REFERENCES "CustomerBranch"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CustomerBranchHouseRegis" ADD CONSTRAINT "CustomerBranchHouseRegis_customerBranchId_fkey" FOREIGN KEY ("customerBranchId") REFERENCES "CustomerBranch"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CustomerBranchCommercialRegis" ADD CONSTRAINT "CustomerBranchCommercialRegis_customerBranchId_fkey" FOREIGN KEY ("customerBranchId") REFERENCES "CustomerBranch"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "CustomerBranchVatRegis" ADD CONSTRAINT "CustomerBranchVatRegis_customerBranchId_fkey" FOREIGN KEY ("customerBranchId") REFERENCES "CustomerBranch"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c492292..afdb1ff 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -527,6 +527,12 @@ model CustomerBranch { employee Employee[] quotation Quotation[] + + citizen CustomerBranchCitizen[] + poa CustomerBranchPoa[] + houseRegis CustomerBranchHouseRegis[] + commercialRegis CustomerBranchCommercialRegis[] + vatRegis CustomerBranchVatRegis[] } model CustomerBranchCitizen { @@ -560,12 +566,18 @@ model CustomerBranchCitizen { subDistrictId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + + customerBranchId String + customerBranch CustomerBranch @relation(fields: [customerBranchId], references: [id]) } model CustomerBranchPoa { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + + customerBranchId String + customerBranch CustomerBranch @relation(fields: [customerBranchId], references: [id]) } model CustomerBranchHouseRegis { @@ -616,6 +628,9 @@ model CustomerBranchHouseRegis { fatherNationality String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + + customerBranchId String + customerBranch CustomerBranch @relation(fields: [customerBranchId], references: [id]) } enum CommercialType { @@ -635,12 +650,18 @@ model CustomerBranchCommercialRegis { romanLetter String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + + customerBranchId String + customerBranch CustomerBranch @relation(fields: [customerBranchId], references: [id]) } model CustomerBranchVatRegis { id String @id @default(cuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + + customerBranchId String + customerBranch CustomerBranch @relation(fields: [customerBranchId], references: [id]) } model Employee { diff --git a/src/controllers/03-customer-branch-citizen-controller.ts b/src/controllers/03-customer-branch-citizen-controller.ts new file mode 100644 index 0000000..fabe7cd --- /dev/null +++ b/src/controllers/03-customer-branch-citizen-controller.ts @@ -0,0 +1,161 @@ +import { + Body, + Controller, + Delete, + Get, + Middlewares, + Path, + Post, + Put, + Route, + Security, + Tags, +} from "tsoa"; +import { RequestWithUser } from "../interfaces/user"; +import prisma from "../db"; +import HttpStatus from "../interfaces/http-status"; +import { connectOrDisconnect, connectOrNot } from "../utils/relation"; +import { notFoundError, relationError } from "../utils/error"; +import { permissionCheck } from "../middlewares/customer-branch"; + +const MANAGE_ROLES = [ + "system", + "head_of_admin", + "admin", + "head_of_account", + "account", + "head_of_sale", + "sale", +]; + +function globalAllow(user: RequestWithUser["user"]) { + const allowList = ["system", "head_of_admin", "admin", "head_of_account", "head_of_sale"]; + return allowList.some((v) => user.roles?.includes(v)); +} + +type CustomerBranchCitizenPayload = { + namePrefix?: string; + firstName: string; + firstNameEN?: string; + middleName?: string; + middleNameEN?: string; + lastName: string; + lastNameEN?: string; + issueDate: Date; + expireDate: Date; + nationality: string; + religion: string; + gender: string; + address?: string; + addressEN?: string; + soi?: string; + soiEN?: string; + moo?: string; + mooEN?: string; + street?: string; + streetEN?: string; + provinceId?: string; + districtId?: string; + subDistrictId?: string; +}; + +@Route("api/v1/customer-branch/{branchId}/citizen") +@Tags("Customer Branch Citizen") +@Middlewares(permissionCheck(globalAllow)) +export class CustomerBranchCitizenController extends Controller { + @Get() + @Security("keycloak") + async list(@Path() branchId: string) { + return prisma.customerBranchCitizen.findMany({ + orderBy: { createdAt: "asc" }, + where: { customerBranchId: branchId }, + }); + } + + @Get("{citizenId}") + @Security("keycloak") + async getById(@Path() branchId: string, @Path() citizenId: string) { + const record = await prisma.customerBranchCitizen.findFirst({ + where: { id: citizenId, customerBranchId: branchId }, + }); + if (!record) throw notFoundError("Citizen"); + return record; + } + + @Post() + @Security("keycloak", MANAGE_ROLES) + async create(@Path() branchId: string, @Body() body: CustomerBranchCitizenPayload) { + const { provinceId, districtId, subDistrictId, ...rest } = body; + const [province, district, subDistrict] = await prisma.$transaction([ + prisma.province.findUnique({ where: { id: body.provinceId ?? undefined } }), + prisma.district.findUnique({ where: { id: body.districtId ?? undefined } }), + prisma.subDistrict.findUnique({ where: { id: body.subDistrictId ?? undefined } }), + ]); + if (body.provinceId && !province) throw relationError("Province"); + if (body.districtId && !district) throw relationError("District"); + if (body.subDistrictId && !subDistrict) throw relationError("SubDistrict"); + const record = await prisma.customerBranchCitizen.create({ + data: { + ...rest, + province: connectOrNot(provinceId), + district: connectOrNot(districtId), + subDistrict: connectOrNot(subDistrictId), + customerBranch: { connect: { id: branchId } }, + }, + }); + + this.setStatus(HttpStatus.CREATED); + + return record; + } + + @Put("{citizenId}") + @Security("keycloak", MANAGE_ROLES) + async editById( + @Path() branchId: string, + @Path() citizenId: string, + @Body() body: CustomerBranchCitizenPayload, + ) { + const { provinceId, districtId, subDistrictId, ...rest } = body; + const [province, district, subDistrict, citizen] = await prisma.$transaction([ + prisma.province.findUnique({ where: { id: body.provinceId ?? undefined } }), + prisma.district.findUnique({ where: { id: body.districtId ?? undefined } }), + prisma.subDistrict.findUnique({ where: { id: body.subDistrictId ?? undefined } }), + prisma.customerBranchCitizen.findUnique({ + where: { id: citizenId, customerBranchId: branchId }, + }), + ]); + if (body.provinceId && !province) throw relationError("Province"); + if (body.districtId && !district) throw relationError("District"); + if (body.subDistrictId && !subDistrict) throw relationError("SubDistrict"); + if (!citizen) throw notFoundError("Citizen"); + + const record = await prisma.customerBranchCitizen.update({ + where: { id: citizenId, customerBranchId: branchId }, + data: { + ...rest, + province: connectOrDisconnect(provinceId), + district: connectOrDisconnect(districtId), + subDistrict: connectOrDisconnect(subDistrictId), + }, + }); + + this.setStatus(HttpStatus.CREATED); + + return record; + } + + @Delete("{citizenId}") + @Security("keycloak", MANAGE_ROLES) + async deleteById(@Path() branchId: string, @Path() citizenId: string) { + const record = await prisma.customerBranchCitizen.findFirst({ + where: { id: citizenId, customerBranchId: branchId }, + }); + + if (!record) throw notFoundError("Citizen"); + + return await prisma.customerBranchCitizen.delete({ + where: { id: citizenId, customerBranchId: branchId }, + }); + } +} diff --git a/src/middlewares/customer-branch.ts b/src/middlewares/customer-branch.ts new file mode 100644 index 0000000..c26b9df --- /dev/null +++ b/src/middlewares/customer-branch.ts @@ -0,0 +1,33 @@ +import express from "express"; +import { RequestWithUser } from "../interfaces/user"; +import prisma from "../db"; +import { branchRelationPermInclude, createPermCheck } from "../services/permission"; +import { notFoundError } from "../utils/error"; + +export function permissionCheck(globalAllow: (user: RequestWithUser["user"]) => boolean) { + const checker = createPermCheck(globalAllow); + + return async (req: RequestWithUser, _res: express.Response, next: express.NextFunction) => { + if ("employeeId" in req.params && typeof req.params.employeeId === "string") { + const id = req.params.customerBranchId; + const employee = await prisma.customerBranch.findFirst({ + where: { id }, + + include: { + customer: { + include: { + registeredBranch: { + include: branchRelationPermInclude(req.user), + }, + }, + }, + }, + }); + + if (!employee) throw notFoundError("Customer Branch"); + + await checker(req.user, employee.customer.registeredBranch); + } + next(); + }; +}