Merge pull request #21 from Frappet/feat/properties
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 6s
All checks were successful
Spell Check / Spell Check with Typos (push) Successful in 6s
feat: property management
This commit is contained in:
commit
542c260aba
4 changed files with 222 additions and 0 deletions
|
|
@ -0,0 +1,16 @@
|
|||
-- CreateTable
|
||||
CREATE TABLE "Property" (
|
||||
"id" TEXT NOT NULL,
|
||||
"registeredBranchId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"nameEN" TEXT NOT NULL,
|
||||
"type" JSONB NOT NULL,
|
||||
"status" "Status" NOT NULL DEFAULT 'CREATED',
|
||||
"statusOrder" INTEGER NOT NULL DEFAULT 0,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Property_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Property" ADD CONSTRAINT "Property_registeredBranchId_fkey" FOREIGN KEY ("registeredBranchId") REFERENCES "Branch"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
|
@ -318,6 +318,7 @@ model Branch {
|
|||
workflowTemplate WorkflowTemplate[]
|
||||
taskOrder TaskOrder[]
|
||||
notification Notification[]
|
||||
property Property[]
|
||||
}
|
||||
|
||||
model BranchBank {
|
||||
|
|
@ -1004,6 +1005,23 @@ model Institution {
|
|||
taskOrder TaskOrder[]
|
||||
}
|
||||
|
||||
model Property {
|
||||
id String @id @default(cuid())
|
||||
|
||||
registeredBranch Branch @relation(fields: [registeredBranchId], references: [id])
|
||||
registeredBranchId String
|
||||
|
||||
name String
|
||||
nameEN String
|
||||
|
||||
type Json
|
||||
|
||||
status Status @default(CREATED)
|
||||
statusOrder Int @default(0)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
}
|
||||
|
||||
model WorkflowTemplate {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
|
|
|
|||
187
src/controllers/04-properties-controller.ts
Normal file
187
src/controllers/04-properties-controller.ts
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
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<string, any>;
|
||||
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 },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@
|
|||
{ "name": "Employee Other Info" },
|
||||
{ "name": "Institution" },
|
||||
{ "name": "Workflow" },
|
||||
{ "name": "Property" },
|
||||
{ "name": "Product Group" },
|
||||
{ "name": "Product" },
|
||||
{ "name": "Work" },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue