import { Branch, Prisma, Status, User } from "@prisma/client"; import { Body, Controller, Delete, Get, Path, Post, Query, Request, Route, Security, Tags, } from "tsoa"; import prisma from "../db"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import { RequestWithUser } from "../interfaces/user"; import { branchRelationPermInclude, createPermCheck } from "../services/permission"; import { queryOrNot, whereDateQuery } from "../utils/relation"; const MANAGE_ROLES = [ "system", "head_of_admin", "admin", "executive", "accountant", "branch_admin", "branch_manager", "branch_accountant", ]; function globalAllow(user: RequestWithUser["user"]) { const listAllowed = ["system", "head_of_admin", "admin", "executive", "accountant"]; return user.roles?.some((v) => listAllowed.includes(v)) || false; } type BranchUserBody = { user: string[] }; async function userBranchCodeGen(branch: Branch, user: User[]) { await prisma.$transaction( async (tx) => { for (const usr of user) { if (usr.code !== null) continue; const typ = usr.userType; const mapTypeNo = { USER: 1, MESSENGER: 2, DELEGATE: 3, AGENCY: 4, }[typ]; const last = await tx.runningNo.upsert({ where: { key: `BR_USR_${branch.code}_${mapTypeNo}`, }, create: { key: `BR_USR_${branch.code}_${mapTypeNo}`, value: 1, }, update: { value: { increment: 1 } }, }); await tx.user.update({ where: { id: usr.id }, data: { code: mapTypeNo + `${last.value}`.padStart(6, "0"), }, }); } }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, ); } const permissionCheck = createPermCheck(globalAllow); @Route("api/v1/branch/{branchId}/manager") @Tags("Branch User") export class BranchManagerUserController extends Controller { @Get() @Security("keycloak") async getBranchAdmin(@Path() branchId: string) { return await prisma.user.findFirst({ where: { branch: { some: { branchId }, }, userRole: "branch_manager", }, }); } } @Route("api/v1/user/{userId}/branch") @Tags("Branch User") export class UserBranchController extends Controller { @Get() @Security("keycloak") async getUserBranch( @Path() userId: string, @Query() zipCode?: string, @Query() query: string = "", @Query() page: number = 1, @Query() pageSize: number = 30, @Query() startDate?: Date, @Query() endDate?: Date, ) { const where = { AND: { branch: { subDistrict: { zipCode } }, userId, }, OR: queryOrNot(query, [ { branch: { name: { contains: query, mode: "insensitive" } } }, { branch: { nameEN: { contains: query, mode: "insensitive" } } }, ]), ...whereDateQuery(startDate, endDate), } satisfies Prisma.BranchUserWhereInput; const [result, total] = await prisma.$transaction([ prisma.branchUser.findMany({ orderBy: { branch: { createdAt: "asc" } }, include: { branch: { include: { province: true, district: true, subDistrict: true, headOffice: { include: { province: true, district: true, subDistrict: true, }, }, }, }, }, where, take: pageSize, skip: (page - 1) * pageSize, }), prisma.branchUser.count({ where }), ]); return { result: result.map((v) => v.branch), page, pageSize, total }; } } @Route("api/v1/branch/{branchId}/user") @Tags("Branch User") export class BranchUserController extends Controller { @Get() @Security("keycloak") async getBranchUser( @Path() branchId: string, @Query() zipCode?: string, @Query() query: string = "", @Query() page: number = 1, @Query() pageSize: number = 30, @Query() startDate?: Date, @Query() endDate?: Date, ) { const where = { AND: { user: { subDistrict: { zipCode } }, branchId, }, OR: [ { user: { firstName: { contains: query, mode: "insensitive" } } }, { user: { firstNameEN: { contains: query, mode: "insensitive" } } }, { user: { lastName: { contains: query, mode: "insensitive" } } }, { user: { lastNameEN: { contains: query, mode: "insensitive" } } }, { user: { email: { contains: query, mode: "insensitive" } } }, { user: { telephoneNo: { contains: query, mode: "insensitive" } } }, ], ...whereDateQuery(startDate, endDate), } satisfies Prisma.BranchUserWhereInput; const [result, total] = await prisma.$transaction([ prisma.branchUser.findMany({ orderBy: { user: { createdAt: "asc" } }, include: { user: { include: { province: true, district: true, subDistrict: true, }, }, }, where, take: pageSize, skip: (page - 1) * pageSize, }), prisma.branchUser.count({ where }), ]); return { result: result.map((v) => v.user), page, pageSize, total }; } @Post() @Security("keycloak", MANAGE_ROLES) async createBranchUser( @Request() req: RequestWithUser, @Path() branchId: string, @Body() body: BranchUserBody, ) { const [branch, user] = await prisma.$transaction([ prisma.branch.findUnique({ include: branchRelationPermInclude(req.user), where: { id: branchId }, }), prisma.user.findMany({ include: { branch: { include: { branch: { include: branchRelationPermInclude(req.user) }, }, }, }, where: { id: { in: body.user } }, }), ]); await permissionCheck(req.user, branch); await Promise.all( user.map(async (u) => { await Promise.all(u.branch.map(async (b) => await permissionCheck(req.user, b.branch))); }), ); if (!branch) { throw new HttpError(HttpStatus.BAD_REQUEST, "Branch cannot be found.", "branchBadReq"); } if (user.length !== body.user.length) { throw new HttpError( HttpStatus.BAD_REQUEST, "One or more user cannot be found.", "oneOrMoreUserBadReq", ); } await prisma.$transaction([ prisma.user.updateMany({ where: { id: { in: body.user }, status: Status.CREATED }, data: { status: Status.ACTIVE }, }), prisma.branchUser.createMany({ data: user .filter((a) => !a.branch.some((b) => b.branchId === branchId)) .map((v) => ({ branchId, userId: v.id, createdByUserId: req.user.sub, updatedByUserId: req.user.sub, })), }), ]); await userBranchCodeGen(branch, user); } @Delete() @Security("keycloak", MANAGE_ROLES) async deleteBranchUser( @Request() req: RequestWithUser, @Path() branchId: string, @Body() body: BranchUserBody, ) { const branch = await prisma.branch.findUnique({ include: branchRelationPermInclude(req.user), where: { id: branchId }, }); await permissionCheck(req.user, branch); await prisma.$transaction( body.user.map((v) => prisma.branchUser.deleteMany({ where: { branchId, userId: v } })), ); await prisma.user.updateMany({ where: { branch: { none: {} }, }, data: { code: null }, }); } @Delete("{userId}") async deleteBranchUserById( @Request() req: RequestWithUser, @Path() branchId: string, @Path() userId: string, ) { const branch = await prisma.branch.findUnique({ include: branchRelationPermInclude(req.user), where: { id: branchId }, }); await permissionCheck(req.user, branch); await prisma.branchUser.deleteMany({ where: { branchId, userId }, }); await prisma.user.updateMany({ where: { id: userId, branch: { none: {} }, }, data: { code: null }, }); } }