From 5bb8da818c22cdf606d5c07cf6e2a5ec03e9a9f8 Mon Sep 17 00:00:00 2001 From: Methapon Metanipat Date: Tue, 20 Aug 2024 15:41:48 +0700 Subject: [PATCH] refactor: customer create and update process --- .../migration.sql | 56 +++ .../20240820063852_add_field/migration.sql | 2 + .../20240820084114_update_field/migration.sql | 13 + prisma/schema.prisma | 54 +-- src/controllers/customer-branch-controller.ts | 197 ++++----- src/controllers/customer-controller.ts | 401 +++--------------- 6 files changed, 261 insertions(+), 462 deletions(-) create mode 100644 prisma/migrations/20240820053920_update_customer_structure/migration.sql create mode 100644 prisma/migrations/20240820063852_add_field/migration.sql create mode 100644 prisma/migrations/20240820084114_update_field/migration.sql diff --git a/prisma/migrations/20240820053920_update_customer_structure/migration.sql b/prisma/migrations/20240820053920_update_customer_structure/migration.sql new file mode 100644 index 0000000..bbde7ac --- /dev/null +++ b/prisma/migrations/20240820053920_update_customer_structure/migration.sql @@ -0,0 +1,56 @@ +/* + Warnings: + + - You are about to drop the column `customerName` on the `Customer` table. All the data in the column will be lost. + - You are about to drop the column `customerNameEN` on the `Customer` table. All the data in the column will be lost. + - You are about to drop the column `personName` on the `Customer` table. All the data in the column will be lost. + - You are about to drop the column `personNameEN` on the `Customer` table. All the data in the column will be lost. + - You are about to drop the column `taxNo` on the `Customer` table. All the data in the column will be lost. + - You are about to drop the column `branchNo` on the `CustomerBranch` table. All the data in the column will be lost. + - You are about to drop the column `bussinessType` on the `CustomerBranch` table. All the data in the column will be lost. + - You are about to drop the column `bussinessTypeEN` on the `CustomerBranch` table. All the data in the column will be lost. + - You are about to drop the column `name` on the `CustomerBranch` table. All the data in the column will be lost. + - You are about to drop the column `nameEN` on the `CustomerBranch` table. All the data in the column will be lost. + - You are about to drop the column `taxNo` on the `CustomerBranch` table. All the data in the column will be lost. + - You are about to drop the column `zipCode` on the `CustomerBranch` table. All the data in the column will be lost. + - Added the required column `birthDate` to the `Customer` table without a default value. This is not possible if the table is not empty. + - Added the required column `firstName` to the `Customer` table without a default value. This is not possible if the table is not empty. + - Added the required column `gender` to the `Customer` table without a default value. This is not possible if the table is not empty. + - Added the required column `lastName` to the `Customer` table without a default value. This is not possible if the table is not empty. + - Added the required column `businessType` to the `CustomerBranch` table without a default value. This is not possible if the table is not empty. + - Added the required column `businessTypeEN` to the `CustomerBranch` table without a default value. This is not possible if the table is not empty. + - Added the required column `workplace` to the `CustomerBranch` table without a default value. This is not possible if the table is not empty. + - Added the required column `workplaceEN` to the `CustomerBranch` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Customer" DROP COLUMN "customerName", +DROP COLUMN "customerNameEN", +DROP COLUMN "personName", +DROP COLUMN "personNameEN", +DROP COLUMN "taxNo", +ADD COLUMN "birthDate" TIMESTAMP(3) NOT NULL, +ADD COLUMN "firstName" TEXT NOT NULL, +ADD COLUMN "firstNameEN" TEXT, +ADD COLUMN "gender" TEXT NOT NULL, +ADD COLUMN "lastName" TEXT NOT NULL, +ADD COLUMN "lastNameEN" TEXT, +ADD COLUMN "namePrefix" TEXT; + +-- AlterTable +ALTER TABLE "CustomerBranch" DROP COLUMN "branchNo", +DROP COLUMN "bussinessType", +DROP COLUMN "bussinessTypeEN", +DROP COLUMN "name", +DROP COLUMN "nameEN", +DROP COLUMN "taxNo", +DROP COLUMN "zipCode", +ADD COLUMN "businessType" TEXT NOT NULL, +ADD COLUMN "businessTypeEN" TEXT NOT NULL, +ADD COLUMN "citizenId" TEXT, +ADD COLUMN "workplace" TEXT NOT NULL, +ADD COLUMN "workplaceEN" TEXT NOT NULL, +ALTER COLUMN "legalPersonNo" DROP NOT NULL, +ALTER COLUMN "registerName" DROP NOT NULL, +ALTER COLUMN "registerDate" DROP NOT NULL, +ALTER COLUMN "authorizedCapital" DROP NOT NULL; diff --git a/prisma/migrations/20240820063852_add_field/migration.sql b/prisma/migrations/20240820063852_add_field/migration.sql new file mode 100644 index 0000000..afad969 --- /dev/null +++ b/prisma/migrations/20240820063852_add_field/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "CustomerBranch" ADD COLUMN "legalPersonName" TEXT; diff --git a/prisma/migrations/20240820084114_update_field/migration.sql b/prisma/migrations/20240820084114_update_field/migration.sql new file mode 100644 index 0000000..af2c325 --- /dev/null +++ b/prisma/migrations/20240820084114_update_field/migration.sql @@ -0,0 +1,13 @@ +/* + Warnings: + + - You are about to drop the column `code` on the `Customer` table. All the data in the column will be lost. + - You are about to drop the column `legalPersonName` on the `CustomerBranch` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Customer" DROP COLUMN "code"; + +-- AlterTable +ALTER TABLE "CustomerBranch" DROP COLUMN "legalPersonName", +ADD COLUMN "registerNameEN" TEXT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5e083d2..9c9747b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -403,14 +403,17 @@ enum CustomerType { } model Customer { - id String @id @default(uuid()) - code String - personName String - personNameEN String? - customerType CustomerType - customerName String - customerNameEN String - taxNo String? + id String @id @default(uuid()) + + customerType CustomerType + + namePrefix String? + firstName String + firstNameEN String? + lastName String + lastNameEN String? + gender String + birthDate DateTime status Status @default(CREATED) statusOrder Int @default(0) @@ -430,24 +433,25 @@ model Customer { } model CustomerBranch { - id String @id @default(uuid()) - branchNo Int - code String - legalPersonNo String - - name String - nameEN String - + id String @id @default(uuid()) customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade) customerId String - taxNo String? - registerName String - registerDate DateTime - authorizedCapital String + code String - address String - addressEN String + // NOTE: About (Natural Person) + citizenId String? + // NOTE: About (Legal Entity) + legalPersonNo String? + registerName String? + registerNameEN String? + registerDate DateTime? + authorizedCapital String? + + workplace String + workplaceEN String + address String + addressEN String province Province? @relation(fields: [provinceId], references: [id], onDelete: SetNull) provinceId String? @@ -458,14 +462,12 @@ model CustomerBranch { subDistrict SubDistrict? @relation(fields: [subDistrictId], references: [id], onDelete: SetNull) subDistrictId String? - zipCode String - email String telephoneNo String employmentOffice String - bussinessType String - bussinessTypeEN String + businessType String + businessTypeEN String jobPosition String jobPositionEN String jobDescription String diff --git a/src/controllers/customer-branch-controller.ts b/src/controllers/customer-branch-controller.ts index c6ed578..2f30b48 100644 --- a/src/controllers/customer-branch-controller.ts +++ b/src/controllers/customer-branch-controller.ts @@ -137,27 +137,30 @@ export class CustomerBranchController extends Controller { const where = { OR: [ - { nameEN: { contains: query }, zipCode, ...filterStatus(status) }, - { name: { contains: query }, zipCode, ...filterStatus(status) }, - { email: { contains: query }, zipCode, ...filterStatus(status) }, - { code: { contains: query }, zipCode, ...filterStatus(status) }, - { address: { contains: query }, zipCode, ...filterStatus(status) }, - { addressEN: { contains: query }, zipCode, ...filterStatus(status) }, - { province: { name: { contains: query } }, zipCode, ...filterStatus(status) }, - { province: { nameEN: { contains: query } }, zipCode, ...filterStatus(status) }, - { district: { name: { contains: query } }, zipCode, ...filterStatus(status) }, - { district: { nameEN: { contains: query } }, zipCode, ...filterStatus(status) }, - { subDistrict: { name: { contains: query } }, zipCode, ...filterStatus(status) }, - { subDistrict: { nameEN: { contains: query } }, zipCode, ...filterStatus(status) }, + { registerName: { contains: query } }, + { registerNameEN: { contains: query } }, + { email: { contains: query } }, + { code: { contains: query } }, + { address: { contains: query } }, + { addressEN: { contains: query } }, + { province: { name: { contains: query } } }, + { province: { nameEN: { contains: query } } }, + { district: { name: { contains: query } } }, + { district: { nameEN: { contains: query } } }, + { subDistrict: { name: { contains: query } } }, + { subDistrict: { nameEN: { contains: query } } }, { customer: { - OR: [{ customerName: { contains: query } }, { customerNameEN: { contains: query } }], + OR: [ + { firstName: { contains: query } }, + { firstNameEN: { contains: query } }, + { lastName: { contains: query } }, + { lastNameEN: { contains: query } }, + ], }, - zipCode, - status, }, ], - AND: { customerId }, + AND: { customerId, subDistrict: zipCode ? { zipCode } : undefined, ...filterStatus(status) }, } satisfies Prisma.CustomerBranchWhereInput; const [result, total] = await prisma.$transaction([ @@ -263,7 +266,15 @@ export class CustomerBranchController extends Controller { prisma.province.findFirst({ where: { id: body.provinceId || undefined } }), prisma.district.findFirst({ where: { id: body.districtId || undefined } }), prisma.subDistrict.findFirst({ where: { id: body.subDistrictId || undefined } }), - prisma.customer.findFirst({ where: { id: body.customerId || undefined } }), + prisma.customer.findFirst({ + where: { id: body.customerId || undefined }, + include: { + branch: { + take: 1, + orderBy: { createdAt: "asc" }, + }, + }, + }), ]); if (body.provinceId && !province) throw new HttpError( @@ -294,48 +305,37 @@ export class CustomerBranchController extends Controller { const record = await prisma.$transaction( async (tx) => { - const conflict = await tx.customerBranch.findFirst({ - where: { customerId, branchNo: rest.branchNo }, - }); - if (conflict) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "Branch with current no already exists.", - "branchSameNoExist", - ); - } - - const last = await tx.runningNo.upsert({ - where: { - key: `CUSTOMER_${customer.code.slice(0, -6)}`, - }, - create: { - key: `CUSTOMER_${customer.code.slice(0, -6)}`, - value: 1, - }, - update: { value: { increment: 1 } }, - }); - - return await tx.customerBranch.create({ - include: { - province: true, - district: true, - subDistrict: true, - createdBy: true, - updatedBy: true, - }, - data: { - ...rest, - statusOrder: +(rest.status === "INACTIVE"), - code: `${customer.code.slice(0, -6)}${`${last.value - 1}`.padStart(6, "0")}`, - customer: { connect: { id: customerId } }, - province: { connect: provinceId ? { id: provinceId } : undefined }, - district: { connect: districtId ? { id: districtId } : undefined }, - subDistrict: { connect: subDistrictId ? { id: subDistrictId } : undefined }, - createdBy: { connect: { id: req.user.sub } }, - updatedBy: { connect: { id: req.user.sub } }, - }, - }); + // const last = await tx.runningNo.upsert({ + // where: { + // key: `CUSTOMER_${customer.code.slice(0, -6)}`, + // }, + // create: { + // key: `CUSTOMER_${customer.code.slice(0, -6)}`, + // value: 1, + // }, + // update: { value: { increment: 1 } }, + // }); + // + // return await tx.customerBranch.create({ + // include: { + // province: true, + // district: true, + // subDistrict: true, + // createdBy: true, + // updatedBy: true, + // }, + // data: { + // ...rest, + // statusOrder: +(rest.status === "INACTIVE"), + // code: `${customer.code.slice(0, -6)}${`${last.value - 1}`.padStart(6, "0")}`, + // customer: { connect: { id: customerId } }, + // province: { connect: provinceId ? { id: provinceId } : undefined }, + // district: { connect: districtId ? { id: districtId } : undefined }, + // subDistrict: { connect: subDistrictId ? { id: subDistrictId } : undefined }, + // createdBy: { connect: { id: req.user.sub } }, + // updatedBy: { connect: { id: req.user.sub } }, + // }, + // }); }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, ); @@ -352,6 +352,12 @@ export class CustomerBranchController extends Controller { @Body() body: CustomerBranchUpdate, @Path() branchId: string, ) { + const branch = await prisma.customerBranch.findUnique({ where: { id: branchId } }); + + if (!branch) { + throw new HttpError(HttpStatus.NOT_FOUND, "Branch cannot be found.", "branchNotFound"); + } + if (body.provinceId || body.districtId || body.subDistrictId || body.customerId) { const [province, district, subDistrict, customer] = await prisma.$transaction([ prisma.province.findFirst({ where: { id: body.provinceId || undefined } }), @@ -385,44 +391,41 @@ export class CustomerBranchController extends Controller { ); } - const { provinceId, districtId, subDistrictId, customerId, ...rest } = body; - - if (!(await prisma.customerBranch.findUnique({ where: { id: branchId } }))) { - throw new HttpError(HttpStatus.NOT_FOUND, "Branch cannot be found.", "branchNotFound"); - } - - const record = await prisma.customerBranch.update({ - where: { id: branchId }, - include: { - province: true, - district: true, - subDistrict: true, - createdBy: true, - updatedBy: true, - }, - data: { - ...rest, - statusOrder: +(rest.status === "INACTIVE"), - customer: { connect: customerId ? { id: customerId } : undefined }, - province: { - connect: provinceId ? { id: provinceId } : undefined, - disconnect: provinceId === null || undefined, - }, - district: { - connect: districtId ? { id: districtId } : undefined, - disconnect: districtId === null || undefined, - }, - subDistrict: { - connect: subDistrictId ? { id: subDistrictId } : undefined, - disconnect: subDistrictId === null || undefined, - }, - updatedBy: { connect: { id: req.user.sub } }, - }, - }); - - this.setStatus(HttpStatus.CREATED); - - return record; + // const { provinceId, districtId, subDistrictId, customerId, ...rest } = body; + // + // + // const record = await prisma.customerBranch.update({ + // where: { id: branchId }, + // include: { + // province: true, + // district: true, + // subDistrict: true, + // createdBy: true, + // updatedBy: true, + // }, + // data: { + // ...rest, + // statusOrder: +(rest.status === "INACTIVE"), + // customer: { connect: customerId ? { id: customerId } : undefined }, + // province: { + // connect: provinceId ? { id: provinceId } : undefined, + // disconnect: provinceId === null || undefined, + // }, + // district: { + // connect: districtId ? { id: districtId } : undefined, + // disconnect: districtId === null || undefined, + // }, + // subDistrict: { + // connect: subDistrictId ? { id: subDistrictId } : undefined, + // disconnect: subDistrictId === null || undefined, + // }, + // updatedBy: { connect: { id: req.user.sub } }, + // }, + // }); + // + // this.setStatus(HttpStatus.CREATED); + // + // return record; } @Delete("{branchId}") diff --git a/src/controllers/customer-controller.ts b/src/controllers/customer-controller.ts index 11b9c26..32efafe 100644 --- a/src/controllers/customer-controller.ts +++ b/src/controllers/customer-controller.ts @@ -15,7 +15,7 @@ import { } from "tsoa"; import { RequestWithUser } from "../interfaces/user"; import prisma from "../db"; -import minio, { presignedGetObjectIfExist } from "../services/minio"; +import minio, { deleteFolder, presignedGetObjectIfExist } from "../services/minio"; import HttpStatus from "../interfaces/http-status"; import HttpError from "../interfaces/http-error"; @@ -37,95 +37,31 @@ const MANAGE_ROLES = [ export type CustomerCreate = { registeredBranchId?: string; - code: string; - status?: Status; - personName: string; - personNameEN?: string; + customerType: CustomerType; - customerName: string; - customerNameEN: string; - taxNo?: string | null; - customerBranch?: { - status?: Status; - - legalPersonNo: string; - - branchNo: number; - taxNo: string | null; - name: string; - nameEN: string; - addressEN: string; - address: string; - zipCode: string; - email: string; - telephoneNo: string; - - registerName: string; - registerDate: Date; - authorizedCapital: string; - - employmentOffice: string; - bussinessType: string; - bussinessTypeEN: string; - jobPosition: string; - jobPositionEN: string; - jobDescription: string; - saleEmployee: string; - payDate: Date; - wageRate: number; - - subDistrictId?: string | null; - districtId?: string | null; - provinceId?: string | null; - }[]; + namePrefix: string; + firstName: string; + firstNameEN?: string; + lastName: string; + lastNameEN?: string; + gender: string; + birthDate: Date; }; export type CustomerUpdate = { registeredBranchId?: string; status?: "ACTIVE" | "INACTIVE"; - personName?: string; - personNameEN?: string; - customerType?: CustomerType; - customerName?: string; - customerNameEN?: string; - taxNo?: string | null; - customerBranch?: { - id?: string; - status?: Status; - - legalPersonNo: string; - - branchNo: number; - taxNo: string | null; - name: string; - nameEN: string; - addressEN: string; - address: string; - zipCode: string; - email: string; - telephoneNo: string; - - registerName: string; - registerDate: Date; - authorizedCapital: string; - - employmentOffice: string; - bussinessType: string; - bussinessTypeEN: string; - jobPosition: string; - jobPositionEN: string; - jobDescription: string; - saleEmployee: string; - payDate: Date; - wageRate: number; - - subDistrictId?: string | null; - districtId?: string | null; - provinceId?: string | null; - }[]; + customerType: CustomerType; + namePrefix: string; + firstName: string; + firstNameEN?: string; + lastName: string; + lastNameEN?: string; + gender: string; + birthDate: Date; }; function imageLocation(id: string) { @@ -167,16 +103,17 @@ export class CustomerController extends Controller { ) { const filterStatus = (val?: Status) => { if (!val) return {}; - return val !== Status.CREATED && val !== Status.ACTIVE ? { status: val } : { OR: [{ status: Status.CREATED }, { status: Status.ACTIVE }] }; }; const where = { OR: [ - { customerName: { contains: query }, customerType, ...filterStatus(status) }, - { customerNameEN: { contains: query }, customerType, ...filterStatus(status) }, + { namePrefix: { contains: query } }, + { firstName: { contains: query } }, + { firstNameEN: { contains: query } }, ], + AND: { customerType, ...filterStatus(status) }, } satisfies Prisma.CustomerWhereInput; const [result, total] = await prisma.$transaction([ @@ -190,9 +127,7 @@ export class CustomerController extends Controller { district: true, subDistrict: true, }, - orderBy: { - branchNo: "asc", - }, + orderBy: { createdAt: "asc" }, } : undefined, createdBy: true, @@ -206,21 +141,7 @@ export class CustomerController extends Controller { prisma.customer.count({ where }), ]); - return { - result: await Promise.all( - result.map(async (v) => ({ - ...v, - imageUrl: await presignedGetObjectIfExist( - MINIO_BUCKET, - imageLocation(v.id), - 12 * 60 * 60, - ), - })), - ), - page, - pageSize, - total, - }; + return { result, page, pageSize, total }; } @Get("{customerId}") @@ -234,71 +155,27 @@ export class CustomerController extends Controller { district: true, subDistrict: true, }, - orderBy: { branchNo: "asc" }, + orderBy: { createdAt: "asc" }, }, createdBy: true, updatedBy: true, }, where: { id: customerId }, }); - if (!record) + + if (!record) { throw new HttpError(HttpStatus.NOT_FOUND, "Customer cannot be found.", "customerNotFound"); - return Object.assign(record, { - imageUrl: await presignedGetObjectIfExist( - MINIO_BUCKET, - imageLocation(record.id), - 12 * 60 * 60, - ), - }); + } + return record; } @Post() @Security("keycloak", MANAGE_ROLES) async create(@Request() req: RequestWithUser, @Body() body: CustomerCreate) { - const { customerBranch, ...payload } = body; - - const provinceId = body.customerBranch?.reduce((acc, cur) => { - if (cur.provinceId && !acc.includes(cur.provinceId)) return acc.concat(cur.provinceId); - return acc; - }, []); - const districtId = body.customerBranch?.reduce((acc, cur) => { - if (cur.districtId && !acc.includes(cur.districtId)) return acc.concat(cur.districtId); - return acc; - }, []); - const subDistrictId = body.customerBranch?.reduce((acc, cur) => { - if (cur.subDistrictId && !acc.includes(cur.subDistrictId)) - return acc.concat(cur.subDistrictId); - return acc; - }, []); - - const [province, district, subDistrict, branch] = await prisma.$transaction([ - prisma.province.findMany({ where: { id: { in: provinceId } } }), - prisma.district.findMany({ where: { id: { in: districtId } } }), - prisma.subDistrict.findMany({ where: { id: { in: subDistrictId } } }), + const [branch] = await prisma.$transaction([ prisma.branch.findFirst({ where: { id: body.registeredBranchId } }), ]); - if (provinceId && province.length !== provinceId?.length) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "Some province cannot be found.", - "relationProvinceNotFound", - ); - } - if (districtId && district.length !== districtId?.length) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "Some district cannot be found.", - "relationDistrictNotFound", - ); - } - if (subDistrictId && subDistrict.length !== subDistrictId?.length) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "Some sub district cannot be found.", - "relationSubDistrictNotFound", - ); - } if (!!body.registeredBranchId && !branch) { throw new HttpError( HttpStatus.BAD_REQUEST, @@ -306,27 +183,24 @@ export class CustomerController extends Controller { "relationBranchNotFound", ); } + if (!body.registeredBranchId) { body.registeredBranchId = undefined; } const record = await prisma.$transaction( async (tx) => { - body.code = body.code.toLocaleUpperCase(); - - const exist = await tx.customer.findFirst({ - where: { code: body.code }, + await tx.branch.updateMany({ + where: { + id: body.registeredBranchId, + status: "CREATED", + }, + data: { + status: "INACTIVE", + statusOrder: 1, + }, }); - - if (exist) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "Customer with same code already exists.", - "sameCustomerCodeExists", - ); - } - - return await prisma.customer.create({ + return await tx.customer.create({ include: { branch: { include: { @@ -339,20 +213,8 @@ export class CustomerController extends Controller { updatedBy: true, }, data: { - ...payload, - statusOrder: +(payload.status === "INACTIVE"), - code: `${body.code}000000`, - branch: { - createMany: { - data: - customerBranch?.map((v) => ({ - ...v, - code: body.code, - createdByUserId: req.user.sub, - updatedByUserId: req.user.sub, - })) || [], - }, - }, + ...body, + statusOrder: +(body.status === "INACTIVE"), createdByUserId: req.user.sub, updatedByUserId: req.user.sub, }, @@ -363,18 +225,7 @@ export class CustomerController extends Controller { this.setStatus(HttpStatus.CREATED); - return Object.assign(record, { - imageUrl: await presignedGetObjectIfExist( - MINIO_BUCKET, - imageLocation(record.id), - 12 * 60 * 60, - ), - imageUploadUrl: await minio.presignedPutObject( - MINIO_BUCKET, - imageLocation(record.id), - 12 * 60 * 60, - ), - }); + return record; } @Put("{customerId}") @@ -390,80 +241,25 @@ export class CustomerController extends Controller { throw new HttpError(HttpStatus.NOT_FOUND, "Customer cannot be found.", "customerNotFound"); } - const provinceId = body.customerBranch?.reduce((acc, cur) => { - if (cur.provinceId && !acc.includes(cur.provinceId)) return acc.concat(cur.provinceId); - return acc; - }, []); - const districtId = body.customerBranch?.reduce((acc, cur) => { - if (cur.districtId && !acc.includes(cur.districtId)) return acc.concat(cur.districtId); - return acc; - }, []); - const subDistrictId = body.customerBranch?.reduce((acc, cur) => { - if (cur.subDistrictId && !acc.includes(cur.subDistrictId)) - return acc.concat(cur.subDistrictId); - return acc; - }, []); - - const [province, district, subDistrict] = await prisma.$transaction([ - prisma.province.findMany({ where: { id: { in: provinceId } } }), - prisma.district.findMany({ where: { id: { in: districtId } } }), - prisma.subDistrict.findMany({ where: { id: { in: subDistrictId } } }), + const [branch] = await prisma.$transaction([ + prisma.customer.findUnique({ where: { id: customerId } }), + prisma.branch.findFirst({ where: { id: body.registeredBranchId } }), ]); - if (provinceId && province.length !== provinceId?.length) { + if (!!body.registeredBranchId && !branch) { throw new HttpError( HttpStatus.BAD_REQUEST, - "Some province cannot be found.", - "relationProvinceNotFound", - ); - } - if (districtId && district.length !== districtId?.length) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "Some district cannot be found.", - "relationDistrictNotFound", - ); - } - if (subDistrictId && subDistrict.length !== subDistrictId?.length) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "Some sub district cannot be found.", - "relationSubDistrictNotFound", + "Branch cannot be found.", + "relationBranchNotFound", ); } - const { customerBranch, ...payload } = body; - - const relation = await prisma.customerBranch.findMany({ - where: { - customerId, - }, - }); - - if ( - customerBranch && - relation.find((a) => !customerBranch.find((b) => a.id === b.id) && a.status !== "CREATED") - ) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "One or more branch cannot be delete and is missing.", - "oneOrMoreBranchMissing", - ); + if (!body.registeredBranchId) { + body.registeredBranchId = undefined; } - if ( - customerBranch && - relation.find((a) => customerBranch.find((b) => a.id !== b.id && a.branchNo === b.branchNo)) - ) { - throw new HttpError( - HttpStatus.BAD_REQUEST, - "Branch cannot have same number.", - "oneOrMoreBranchNoExist", - ); - } - - const record = await prisma.customer - .update({ + const record = await prisma.$transaction(async (tx) => { + return await tx.customer.update({ include: { branch: { include: { @@ -477,74 +273,14 @@ export class CustomerController extends Controller { }, where: { id: customerId }, data: { - ...payload, - statusOrder: +(payload.status === "INACTIVE"), - branch: - (customerBranch && { - deleteMany: { - id: { - notIn: customerBranch.map((v) => v.id).filter((v): v is string => !!v) || [], - }, - status: Status.CREATED, - }, - upsert: customerBranch.map((v) => ({ - where: { id: v.id || "" }, - create: { - ...v, - code: `${customer.code}-${v.branchNo.toString().padStart(2, "0")}`, - createdByUserId: req.user.sub, - updatedByUserId: req.user.sub, - id: undefined, - }, - update: { - ...v, - code: undefined, - branchNo: undefined, - updatedByUserId: req.user.sub, - }, - })), - }) || - undefined, + ...body, + statusOrder: +(body.status === "INACTIVE"), updatedByUserId: req.user.sub, }, - }) - .then((v) => { - if (customerBranch) { - relation - .filter((a) => !customerBranch.find((b) => b.id === a.id)) - .forEach((deleted) => { - new Promise((resolve, reject) => { - const item: string[] = []; - - const stream = minio.listObjectsV2(MINIO_BUCKET, `customer/${deleted.id}`); - - stream.on("data", (v) => v && v.name && item.push(v.name)); - stream.on("end", () => resolve(item)); - stream.on("error", () => reject(new Error("MinIO error."))); - }).then((list) => { - list.map(async (v) => { - await minio.removeObject(MINIO_BUCKET, v, { - forceDelete: true, - }); - }); - }); - }); - } - return v; }); - - return Object.assign(record, { - imageUrl: await presignedGetObjectIfExist( - MINIO_BUCKET, - imageLocation(record.id), - 12 * 60 * 60, - ), - imageUploadUrl: await minio.presignedPutObject( - MINIO_BUCKET, - imageLocation(record.id), - 12 * 60 * 60, - ), }); + + return record; } @Delete("{customerId}") @@ -560,24 +296,11 @@ export class CustomerController extends Controller { throw new HttpError(HttpStatus.FORBIDDEN, "Customer is in used.", "customerInUsed"); } - return await prisma.customer.delete({ where: { id: customerId } }).then((v) => { - new Promise((resolve, reject) => { - const item: string[] = []; - - const stream = minio.listObjectsV2(MINIO_BUCKET, `customer/${customerId}`); - - stream.on("data", (v) => v && v.name && item.push(v.name)); - stream.on("end", () => resolve(item)); - stream.on("error", () => reject(new Error("MinIO error."))); - }).then((list) => { - list.map(async (v) => { - await minio.removeObject(MINIO_BUCKET, v, { - forceDelete: true, - }); - }); - }); - return v; - }); + return await prisma.customer + .delete({ where: { id: customerId } }) + .then( + async (data) => await deleteFolder(MINIO_BUCKET, `customer/${customerId}`).then(() => data), + ); } @Get("{customerId}/image")