356 lines
9.8 KiB
TypeScript
356 lines
9.8 KiB
TypeScript
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, whereDateQuery } from "../utils/relation";
|
|
|
|
type WorkflowPayload = {
|
|
name: string;
|
|
step: {
|
|
id?: string;
|
|
name: string;
|
|
detail?: string | null;
|
|
type?: string | null;
|
|
value?: string[] | null;
|
|
attributes?: { [key: string]: any };
|
|
responsiblePersonId?: string[];
|
|
responsibleInstitution?: string[];
|
|
responsibleGroup?: string[];
|
|
messengerByArea?: boolean;
|
|
}[];
|
|
registeredBranchId?: string;
|
|
status?: Status;
|
|
};
|
|
|
|
const MANAGE_ROLES = [
|
|
"system",
|
|
"head_of_admin",
|
|
"admin",
|
|
"executive",
|
|
"accountant",
|
|
"branch_admin",
|
|
"branch_manager",
|
|
"branch_accountant",
|
|
];
|
|
|
|
function globalAllow(user: RequestWithUser["user"]) {
|
|
const listAllowed = MANAGE_ROLES;
|
|
return user.roles?.some((v) => listAllowed.includes(v)) || false;
|
|
}
|
|
|
|
const permissionCondCompany = createPermCondition(globalAllow);
|
|
const permissionCheckCompany = createPermCheck(globalAllow);
|
|
|
|
@Route("api/v1/workflow-template")
|
|
@Tags("Workflow")
|
|
export class FlowTemplateController extends Controller {
|
|
@Get()
|
|
@Security("keycloak")
|
|
async getFlowTemplate(
|
|
@Request() req: RequestWithUser,
|
|
@Query() page: number = 1,
|
|
@Query() pageSize: number = 30,
|
|
@Query() status?: Status,
|
|
@Query() query = "",
|
|
@Query() activeOnly?: boolean,
|
|
@Query() startDate?: Date,
|
|
@Query() endDate?: Date,
|
|
) {
|
|
const where = {
|
|
OR: queryOrNot(query, [
|
|
{ name: { contains: query, mode: "insensitive" } },
|
|
{
|
|
step: {
|
|
some: { name: { contains: query, mode: "insensitive" } },
|
|
},
|
|
},
|
|
]),
|
|
AND: {
|
|
...filterStatus(activeOnly ? Status.ACTIVE : status),
|
|
registeredBranch: {
|
|
OR: permissionCondCompany(req.user, { activeOnly: true }),
|
|
},
|
|
},
|
|
...whereDateQuery(startDate, endDate),
|
|
} satisfies Prisma.WorkflowTemplateWhereInput;
|
|
const [result, total] = await prisma.$transaction([
|
|
prisma.workflowTemplate.findMany({
|
|
where,
|
|
include: {
|
|
step: {
|
|
include: {
|
|
value: true,
|
|
responsiblePerson: {
|
|
include: { user: true },
|
|
},
|
|
responsibleInstitution: true,
|
|
responsibleGroup: true,
|
|
},
|
|
orderBy: { order: "asc" },
|
|
},
|
|
},
|
|
orderBy: [{ statusOrder: "asc" }, { createdAt: "asc" }],
|
|
take: pageSize,
|
|
skip: (page - 1) * pageSize,
|
|
}),
|
|
prisma.workflowTemplate.count({ where }),
|
|
]);
|
|
|
|
return {
|
|
result: result.map((r) => ({
|
|
...r,
|
|
step: r.step.map((v) => ({
|
|
...v,
|
|
responsibleInstitution: v.responsibleInstitution.map((institution) => institution.group),
|
|
responsibleGroup: v.responsibleGroup.map((group) => group.group),
|
|
})),
|
|
})),
|
|
page,
|
|
pageSize,
|
|
total,
|
|
};
|
|
}
|
|
|
|
@Get("{templateId}")
|
|
@Security("keycloak")
|
|
async getFlowTemplateById(@Request() _req: RequestWithUser, @Path() templateId: string) {
|
|
const record = await prisma.workflowTemplate.findFirst({
|
|
include: {
|
|
step: {
|
|
orderBy: { order: "asc" },
|
|
include: {
|
|
value: true,
|
|
responsiblePerson: {
|
|
include: { user: true },
|
|
},
|
|
responsibleInstitution: true,
|
|
responsibleGroup: true,
|
|
},
|
|
},
|
|
},
|
|
where: { id: templateId },
|
|
orderBy: { createdAt: "asc" },
|
|
});
|
|
|
|
if (!record) throw notFoundError("FlowTemplate");
|
|
|
|
return {
|
|
...record,
|
|
step: record.step.map((v) => ({
|
|
...v,
|
|
responsibleInstitution: v.responsibleInstitution.map((institution) => institution.group),
|
|
responsibleGroup: v.responsibleGroup.map((group) => group.group),
|
|
})),
|
|
};
|
|
}
|
|
|
|
@Post()
|
|
@Security("keycloak", MANAGE_ROLES)
|
|
async createFlowTemplate(@Request() req: RequestWithUser, @Body() body: WorkflowPayload) {
|
|
const where = {
|
|
OR: [
|
|
{ name: { contains: body.name } },
|
|
{
|
|
step: {
|
|
some: { name: { contains: body.name } },
|
|
},
|
|
},
|
|
],
|
|
AND: {
|
|
registeredBranch: {
|
|
OR: permissionCondCompany(req.user),
|
|
},
|
|
},
|
|
} satisfies Prisma.WorkflowTemplateWhereInput;
|
|
|
|
const exists = await prisma.workflowTemplate.findFirst({ where });
|
|
|
|
if (exists) {
|
|
throw new HttpError(
|
|
HttpStatus.BAD_REQUEST,
|
|
"Workflow template with this name already exists",
|
|
"sameNameExists",
|
|
);
|
|
}
|
|
|
|
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.workflowTemplate.create({
|
|
data: {
|
|
...body,
|
|
registeredBranchId: userAffiliatedBranch.id,
|
|
step: {
|
|
create: body.step.map((v, i) => ({
|
|
type: v.type,
|
|
value: {
|
|
create: v.value?.map((val) => ({
|
|
value: val,
|
|
})),
|
|
},
|
|
name: v.name,
|
|
detail: v.detail,
|
|
order: i + 1,
|
|
attributes: v.attributes,
|
|
messengerByArea: v.messengerByArea,
|
|
responsiblePerson: {
|
|
create: v.responsiblePersonId?.map((id) => ({
|
|
userId: id,
|
|
})),
|
|
},
|
|
responsibleInstitution: {
|
|
create: v.responsibleInstitution?.map((group) => ({ group })),
|
|
},
|
|
responsibleGroup: {
|
|
create: v.responsibleGroup?.map((group) => ({ group })),
|
|
},
|
|
})),
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
@Put("{templateId}")
|
|
@Security("keycloak", MANAGE_ROLES)
|
|
async updateFlowTemplate(
|
|
@Request() req: RequestWithUser,
|
|
@Path() templateId: string,
|
|
@Body() body: WorkflowPayload,
|
|
) {
|
|
const record = await prisma.workflowTemplate.findUnique({
|
|
where: { id: templateId },
|
|
include: {
|
|
registeredBranch: {
|
|
include: branchRelationPermInclude(req.user),
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!record) throw notFoundError("Workflow");
|
|
|
|
await permissionCheckCompany(req.user, record.registeredBranch);
|
|
|
|
return await prisma.workflowTemplate.update({
|
|
where: { id: templateId },
|
|
data: {
|
|
...body,
|
|
statusOrder: +(body.status === "INACTIVE"),
|
|
step: {
|
|
deleteMany: {
|
|
id: { notIn: body.step.flatMap((v) => v.id || []) },
|
|
},
|
|
upsert: body.step.map((v, i) => ({
|
|
where: { id: v.id || "" },
|
|
create: {
|
|
type: v.type,
|
|
name: v.name,
|
|
detail: v.detail,
|
|
order: i + 1,
|
|
attributes: v.attributes,
|
|
value: {
|
|
create: v.value?.map((val) => ({ value: val })),
|
|
},
|
|
messengerByArea: v.messengerByArea,
|
|
responsiblePerson: {
|
|
createMany: {
|
|
data: v.responsiblePersonId?.map((id) => ({ userId: id })) || [],
|
|
skipDuplicates: true,
|
|
},
|
|
},
|
|
id: undefined,
|
|
},
|
|
update: {
|
|
type: v.type,
|
|
name: v.name,
|
|
detail: v.detail,
|
|
order: i + 1,
|
|
attributes: v.attributes,
|
|
value: {
|
|
deleteMany: {},
|
|
create: v.value?.map((val) => ({ value: val })),
|
|
},
|
|
messengerByArea: v.messengerByArea,
|
|
responsiblePerson: v.responsiblePersonId
|
|
? {
|
|
deleteMany: {
|
|
userId: { notIn: v.responsiblePersonId },
|
|
},
|
|
createMany: {
|
|
data: v.responsiblePersonId?.map((id) => ({ userId: id })) || [],
|
|
skipDuplicates: true,
|
|
},
|
|
}
|
|
: undefined,
|
|
responsibleInstitution: {
|
|
deleteMany: {},
|
|
create: v.responsibleInstitution?.map((group) => ({ group })),
|
|
},
|
|
responsibleGroup: {
|
|
deleteMany: {},
|
|
create: v.responsibleGroup?.map((group) => ({ group })),
|
|
},
|
|
},
|
|
})),
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
@Delete("{templateId}")
|
|
@Security("keycloak", MANAGE_ROLES)
|
|
async deleteFlowTemplateById(@Request() req: RequestWithUser, @Path() templateId: string) {
|
|
const record = await prisma.workflowTemplate.findUnique({
|
|
where: { id: templateId },
|
|
include: {
|
|
registeredBranch: {
|
|
include: branchRelationPermInclude(req.user),
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!record) throw notFoundError("Workflow");
|
|
|
|
await permissionCheckCompany(req.user, record.registeredBranch);
|
|
|
|
return await prisma.workflowTemplate.delete({
|
|
where: { id: templateId },
|
|
});
|
|
}
|
|
}
|