import { Body, Controller, Delete, Get, Path, Post, Put, Query, Request, Route, Security, Tags, } from "tsoa"; import { RequestWithUser } from "../interfaces/user"; import prisma from "../db"; import { Prisma, Status } from "@prisma/client"; import { branchRelationPermInclude, createPermCheck, createPermCondition, } from "../services/permission"; import HttpError from "../interfaces/http-error"; import HttpStatus from "../interfaces/http-status"; import { notFoundError } from "../utils/error"; import { filterStatus } from "../services/prisma"; import { queryOrNot } from "../utils/relation"; type PropertyPayload = { name: string; nameEN: string; type: Record; registeredBranchId?: string; status?: Status; }; const permissionCondCompany = createPermCondition((_) => true); const permissionCheckCompany = createPermCheck((_) => true); @Route("api/v1/property") @Tags("Property") @Security("keycloak") export class PropertiesController extends Controller { @Get() async getProperties( @Request() req: RequestWithUser, @Query() page: number = 1, @Query() pageSize: number = 30, @Query() status?: Status, @Query() query = "", @Query() activeOnly?: boolean, ) { const where = { OR: queryOrNot(query, [{ name: { contains: query } }, { nameEN: { contains: query } }]), AND: { ...filterStatus(activeOnly ? Status.ACTIVE : status), registeredBranch: { OR: permissionCondCompany(req.user, { activeOnly: true }), }, }, } satisfies Prisma.PropertyWhereInput; const [result, total] = await prisma.$transaction([ prisma.property.findMany({ where, orderBy: [{ statusOrder: "asc" }, { createdAt: "asc" }], take: pageSize, skip: (page - 1) * pageSize, }), prisma.property.count({ where }), ]); return { result, page, pageSize, total, }; } @Get("{propertyId}") async getPropertyById(@Request() _req: RequestWithUser, @Path() propertyId: string) { const record = await prisma.property.findFirst({ where: { id: propertyId }, orderBy: { createdAt: "asc" }, }); if (!record) throw notFoundError("Property"); return record; } @Post() async createProperty(@Request() req: RequestWithUser, @Body() body: PropertyPayload) { const where = { OR: [{ name: { contains: body.name } }, { nameEN: { contains: body.nameEN } }], AND: { registeredBranch: { OR: permissionCondCompany(req.user), }, }, } satisfies Prisma.PropertyWhereInput; const exists = await prisma.property.findFirst({ where }); if (exists) { throw new HttpError( HttpStatus.BAD_REQUEST, "Property with this name already exists", "samePropertyNameExists", ); } const userAffiliatedBranch = await prisma.branch.findFirst({ include: branchRelationPermInclude(req.user), where: body.registeredBranchId ? { id: body.registeredBranchId } : { user: { some: { userId: req.user.sub } }, }, }); if (!userAffiliatedBranch) { throw new HttpError( HttpStatus.BAD_REQUEST, "You must be affilated with at least one branch or specify branch to be registered (System permission required).", "reqMinAffilatedBranch", ); } await permissionCheckCompany(req.user, userAffiliatedBranch); return await prisma.property.create({ data: { ...body, statusOrder: +(body.status === "INACTIVE"), registeredBranchId: userAffiliatedBranch.id, }, }); } @Put("{propertyId}") async updatePropertyById( @Request() req: RequestWithUser, @Path() propertyId: string, @Body() body: PropertyPayload, ) { const record = await prisma.property.findUnique({ where: { id: propertyId }, include: { registeredBranch: { include: branchRelationPermInclude(req.user), }, }, }); if (!record) throw notFoundError("Property"); await permissionCheckCompany(req.user, record.registeredBranch); return await prisma.property.update({ where: { id: propertyId }, data: { ...body, statusOrder: +(body.status === "INACTIVE"), }, }); } @Delete("{propertyId}") async deletePropertyById(@Request() req: RequestWithUser, @Path() propertyId: string) { const record = await prisma.property.findUnique({ where: { id: propertyId }, include: { registeredBranch: { include: branchRelationPermInclude(req.user), }, }, }); if (!record) throw notFoundError("Property"); await permissionCheckCompany(req.user, record.registeredBranch); return await prisma.property.delete({ where: { id: propertyId }, }); } }