diff --git a/src/controllers/04-product-group-controller.ts b/src/controllers/04-product-group-controller.ts index 12e1170..c8bd006 100644 --- a/src/controllers/04-product-group-controller.ts +++ b/src/controllers/04-product-group-controller.ts @@ -18,12 +18,14 @@ import prisma from "../db"; import { RequestWithUser } from "../interfaces/user"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; +import { isSystem } from "../utils/keycloak"; type ProductGroupCreate = { name: string; detail: string; remark: string; status?: Status; + registeredBranchId?: string; }; type ProductGroupUpdate = { @@ -31,8 +33,66 @@ type ProductGroupUpdate = { detail?: string; remark?: string; status?: "ACTIVE" | "INACTIVE"; + registeredBranchId?: string; }; +const MANAGE_ROLES = [ + "system", + "head_of_admin", + "admin", + "head_of_account", + "account", + "head_of_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)); +} + +async function permissionCheck(user: RequestWithUser["user"], branchId: string) { + const record = await prisma.branch.findUnique({ + include: { + headOffice: { + include: { + branch: { where: { user: { some: { userId: user.sub } } } }, + user: { where: { userId: user.sub } }, + }, + }, + user: { where: { userId: user.sub } }, + }, + where: { id: branchId }, + }); + + if (!record) { + throw new HttpError(HttpStatus.NOT_FOUND, "Branch cannot be found.", "branchNotFound"); + } + + if (!isSystem(user)) { + if (!globalAllow(user) && record.user.length === 0) { + throw new HttpError( + HttpStatus.FORBIDDEN, + "You do not have permission to perform this action.", + "noPermission", + ); + } else { + if ( + (record.user.length === 0 && !record.headOffice) || + (record.headOffice && + record.headOffice.user.length === 0 && + record.headOffice.branch.length === 0) + ) { + throw new HttpError( + HttpStatus.FORBIDDEN, + "You do not have permission to perform this action.", + "noPermission", + ); + } + } + } + return record; +} + @Route("api/v1/product-group") @Tags("Product Group") export class ProductGroup extends Controller { @@ -45,6 +105,7 @@ export class ProductGroup extends Controller { @Get() @Security("keycloak") async getProductGroup( + @Request() req: RequestWithUser, @Query() query: string = "", @Query() status?: Status, @Query() page: number = 1, @@ -63,6 +124,34 @@ export class ProductGroup extends Controller { { name: { contains: query }, ...filterStatus(status) }, { detail: { contains: query }, ...filterStatus(status) }, ], + AND: [ + { + registeredBranch: isSystem(req.user) + ? undefined + : { + OR: [ + { + user: { some: { userId: req.user.sub } }, + }, + { + branch: globalAllow(req.user) + ? { some: { user: { some: { userId: req.user.sub } } } } + : undefined, + }, + { + headOffice: globalAllow(req.user) + ? { branch: { some: { user: { some: { userId: req.user.sub } } } } } + : undefined, + }, + { + headOffice: globalAllow(req.user) + ? { user: { some: { userId: req.user.sub } } } + : undefined, + }, + ], + }, + }, + ], } satisfies Prisma.ProductGroupWhereInput; const [result, total] = await prisma.$transaction([ @@ -111,8 +200,12 @@ export class ProductGroup extends Controller { } @Post() - @Security("keycloak", ["system", "head_of_admin", "admin", "account", "head_of_account"]) + @Security("keycloak", MANAGE_ROLES) async createProductGroup(@Request() req: RequestWithUser, @Body() body: ProductGroupCreate) { + if (body.registeredBranchId) { + await permissionCheck(req.user, body.registeredBranchId); + } + const record = await prisma.$transaction( async (tx) => { const last = await tx.runningNo.upsert({ @@ -149,13 +242,29 @@ export class ProductGroup extends Controller { } @Put("{groupId}") - @Security("keycloak", ["system", "head_of_admin", "admin", "account", "head_of_account"]) + @Security("keycloak", MANAGE_ROLES) async editProductGroup( @Request() req: RequestWithUser, @Body() body: ProductGroupUpdate, @Path() groupId: string, ) { - if (!(await prisma.productGroup.findUnique({ where: { id: groupId } }))) { + const record = await prisma.productGroup.findUnique({ + where: { id: groupId }, + include: { + registeredBranch: { + include: { + headOffice: { + include: { + branch: { where: { user: { some: { userId: req.user.sub } } } }, + user: { where: { userId: req.user.sub } }, + }, + }, + user: { where: { userId: req.user.sub } }, + }, + }, + }, + }); + if (!record) { throw new HttpError( HttpStatus.NOT_FOUND, "Product group cannot be found.", @@ -163,7 +272,82 @@ export class ProductGroup extends Controller { ); } - const record = await prisma.productGroup.update({ + if (!isSystem(req.user)) { + if (!globalAllow(req.user) && record.registeredBranch?.user.length === 0) { + throw new HttpError( + HttpStatus.FORBIDDEN, + "You do not have permission to perform this action.", + "noPermission", + ); + } else { + if ( + (record.registeredBranch?.user.length === 0 && !record.registeredBranch?.headOffice) || + (record.registeredBranch?.headOffice && + record.registeredBranch?.headOffice.user.length === 0 && + record.registeredBranch?.headOffice.branch.length === 0) + ) { + throw new HttpError( + HttpStatus.FORBIDDEN, + "You do not have permission to perform this action.", + "noPermission", + ); + } + } + } + + const [branch] = await prisma.$transaction([ + prisma.branch.findFirst({ + where: { id: body.registeredBranchId }, + include: { + user: { + where: { userId: req.user.sub }, + }, + headOffice: { + include: { + user: { + where: { userId: req.user.sub }, + }, + }, + }, + }, + }), + ]); + + if (body.registeredBranchId !== undefined && !isSystem(req.user)) { + if (!globalAllow(req.user)) { + if (body.registeredBranchId === null || (branch && branch.user.length === 0)) { + throw new HttpError( + HttpStatus.FORBIDDEN, + "You do not have permission to perform this action.", + "noPermission", + ); + } + } else { + if ( + body.registeredBranchId === null || + (branch && + branch.user.length === 0 && + branch.headOffice && + branch.headOffice.user.length === 0) + ) { + throw new HttpError( + HttpStatus.FORBIDDEN, + "You do not have permission to perform this action.", + "noPermission", + ); + } + } + } + + if (!!body.registeredBranchId && !branch) { + throw new HttpError( + HttpStatus.BAD_REQUEST, + "Branch cannot be found.", + "relationBranchNotFound", + ); + } + + const result = await prisma.productGroup.update({ include: { createdBy: true, updatedBy: true, @@ -172,13 +356,28 @@ export class ProductGroup extends Controller { where: { id: groupId }, }); - return record; + return result; } @Delete("{groupId}") - @Security("keycloak", ["system", "head_of_admin", "admin", "account", "head_of_account"]) - async deleteProductGroup(@Path() groupId: string) { - const record = await prisma.productGroup.findFirst({ where: { id: groupId } }); + @Security("keycloak", MANAGE_ROLES) + async deleteProductGroup(@Request() req: RequestWithUser, @Path() groupId: string) { + const record = await prisma.productGroup.findFirst({ + where: { id: groupId }, + include: { + registeredBranch: { + include: { + headOffice: { + include: { + branch: { where: { user: { some: { userId: req.user.sub } } } }, + user: { where: { userId: req.user.sub } }, + }, + }, + user: { where: { userId: req.user.sub } }, + }, + }, + }, + }); if (!record) { throw new HttpError( @@ -188,6 +387,29 @@ export class ProductGroup extends Controller { ); } + if (!isSystem(req.user)) { + if (!globalAllow(req.user) && record.registeredBranch?.user.length === 0) { + throw new HttpError( + HttpStatus.FORBIDDEN, + "You do not have permission to perform this action.", + "noPermission", + ); + } else { + if ( + (record.registeredBranch?.user.length === 0 && !record.registeredBranch?.headOffice) || + (record.registeredBranch?.headOffice && + record.registeredBranch?.headOffice.user.length === 0 && + record.registeredBranch?.headOffice.branch.length === 0) + ) { + throw new HttpError( + HttpStatus.FORBIDDEN, + "You do not have permission to perform this action.", + "noPermission", + ); + } + } + } + if (record.status !== Status.CREATED) { throw new HttpError(HttpStatus.FORBIDDEN, "Product group is in used.", "productGroupInUsed"); }