diff --git a/prisma/migrations/20241007045748_add_workflow_table/migration.sql b/prisma/migrations/20241007045748_add_workflow_table/migration.sql new file mode 100644 index 0000000..81e5ddb --- /dev/null +++ b/prisma/migrations/20241007045748_add_workflow_table/migration.sql @@ -0,0 +1,59 @@ +-- CreateTable +CREATE TABLE "WorkflowTemplate" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "status" "Status" NOT NULL DEFAULT 'CREATED', + "statusOrder" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdByUserId" TEXT, + "updatedAt" TIMESTAMP(3) NOT NULL, + "updatedByUserId" TEXT, + + CONSTRAINT "WorkflowTemplate_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "WorkflowTemplateStep" ( + "id" TEXT NOT NULL, + "order" INTEGER NOT NULL, + "name" TEXT NOT NULL, + "type" TEXT, + "workflowTemplateId" TEXT, + + CONSTRAINT "WorkflowTemplateStep_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "WorkflowTemplateStepValue" ( + "id" TEXT NOT NULL, + "value" TEXT NOT NULL, + "workflowTemplateStepId" TEXT NOT NULL, + + CONSTRAINT "WorkflowTemplateStepValue_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "WorkflowTemplateStepUser" ( + "userId" TEXT NOT NULL, + "workflowTemplateStepId" TEXT NOT NULL, + + CONSTRAINT "WorkflowTemplateStepUser_pkey" PRIMARY KEY ("userId","workflowTemplateStepId") +); + +-- AddForeignKey +ALTER TABLE "WorkflowTemplate" ADD CONSTRAINT "WorkflowTemplate_createdByUserId_fkey" FOREIGN KEY ("createdByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkflowTemplate" ADD CONSTRAINT "WorkflowTemplate_updatedByUserId_fkey" FOREIGN KEY ("updatedByUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkflowTemplateStep" ADD CONSTRAINT "WorkflowTemplateStep_workflowTemplateId_fkey" FOREIGN KEY ("workflowTemplateId") REFERENCES "WorkflowTemplate"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkflowTemplateStepValue" ADD CONSTRAINT "WorkflowTemplateStepValue_workflowTemplateStepId_fkey" FOREIGN KEY ("workflowTemplateStepId") REFERENCES "WorkflowTemplateStep"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkflowTemplateStepUser" ADD CONSTRAINT "WorkflowTemplateStepUser_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkflowTemplateStepUser" ADD CONSTRAINT "WorkflowTemplateStepUser_workflowTemplateStepId_fkey" FOREIGN KEY ("workflowTemplateStepId") REFERENCES "WorkflowTemplateStep"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 35d3b04..f2ab6c1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -385,6 +385,7 @@ model User { branch BranchUser[] userMenuPermission UserMenuPermission[] userMenuComponentPermission UserMenuComponentPermission[] + workflowTemplateStepUser WorkflowTemplateStepUser[] userCreated User[] @relation("UserCreatedByUser") userUpdated User[] @relation("UserUpdatedByUser") @@ -417,6 +418,8 @@ model User { productUpdated Product[] @relation("ProductUpdatedByUser") quotationCreated Quotation[] @relation("QuotationCreatedByUser") quotationUpdated Quotation[] @relation("QuotationUpdatedByUser") + flowCreated WorkflowTemplate[] @relation("FlowCreatedByUser") + flowUpdated WorkflowTemplate[] @relation("FlowUpdatedByUser") } enum CustomerType { @@ -882,6 +885,55 @@ model EmployeeOtherInfo { updatedByUserId String? } +model WorkflowTemplate { + id String @id @default(cuid()) + name String + + step WorkflowTemplateStep[] + + status Status @default(CREATED) + statusOrder Int @default(0) + + createdAt DateTime @default(now()) + createdBy User? @relation(name: "FlowCreatedByUser", fields: [createdByUserId], references: [id], onDelete: SetNull) + createdByUserId String? + updatedAt DateTime @updatedAt + updatedBy User? @relation(name: "FlowUpdatedByUser", fields: [updatedByUserId], references: [id], onDelete: SetNull) + updatedByUserId String? +} + +model WorkflowTemplateStep { + id String @id @default(cuid()) + + order Int + name String + type String? + value WorkflowTemplateStepValue[] // NOTE: For enum or options type + responsiblePerson WorkflowTemplateStepUser[] + + workflowTemplate WorkflowTemplate? @relation(fields: [workflowTemplateId], references: [id]) + workflowTemplateId String? +} + +model WorkflowTemplateStepValue { + id String @id @default(cuid()) + + value String + + workflowTemplateStep WorkflowTemplateStep @relation(fields: [workflowTemplateStepId], references: [id]) + workflowTemplateStepId String +} + +model WorkflowTemplateStepUser { + userId String + user User @relation(fields: [userId], references: [id]) + + workflowTemplateStep WorkflowTemplateStep @relation(fields: [workflowTemplateStepId], references: [id]) + workflowTemplateStepId String + + @@id([userId, workflowTemplateStepId]) +} + model ProductGroup { id String @id @default(cuid()) diff --git a/src/controllers/04-flow-template-controller.ts b/src/controllers/04-flow-template-controller.ts new file mode 100644 index 0000000..739af82 --- /dev/null +++ b/src/controllers/04-flow-template-controller.ts @@ -0,0 +1,108 @@ +import { Body, Controller, Delete, Get, Path, Post, Put, Query, Request, Route, Tags } from "tsoa"; +import { RequestWithUser } from "../interfaces/user"; +import prisma from "../db"; + +type WorkflowPayload = { + name: string; + step: { + name: string; + type?: string; + value?: string[]; + responsiblePersonId?: string[]; + }[]; +}; + +@Route("api/v1/workflow-template") +@Tags("Workflow") +export class FlowTemplateController extends Controller { + @Get() + async getFlowTemplate( + @Request() _req: RequestWithUser, + @Query() page: number = 1, + @Query() pageSize: number = 30, + ) { + return await prisma.workflowTemplate.findMany({ + include: { + step: { + include: { value: true }, + }, + }, + orderBy: { createdAt: "asc" }, + take: pageSize, + skip: (page - 1) * pageSize, + }); + } + + @Get("{templateId}") + async getFlowTemplateById(@Request() _req: RequestWithUser, @Path() templateId: string) { + return await prisma.workflowTemplate.findFirst({ + include: { + step: { + include: { value: true }, + }, + }, + where: { id: templateId }, + orderBy: { createdAt: "asc" }, + }); + } + + @Post() + async createFlowTemplate(@Request() _req: RequestWithUser, @Body() body: WorkflowPayload) { + return await prisma.workflowTemplate.create({ + data: { + ...body, + step: { + create: body.step.map((v, i) => ({ + type: v.type, + value: { + create: v.value?.map((val) => ({ + value: val, + })), + }, + name: v.name, + order: i + 1, + responsiblePerson: { + create: v.responsiblePersonId?.map((id) => ({ + userId: id, + })), + }, + })), + }, + }, + }); + } + + @Put("{templateId}") + async updateFlowTemplate( + @Request() _req: RequestWithUser, + @Path() templateId: string, + @Body() body: WorkflowPayload, + ) { + return await prisma.workflowTemplate.update({ + where: { id: templateId }, + data: { + ...body, + step: { + create: body.step.map((v, i) => ({ + type: v.type, + name: v.name, + order: i + 1, + value: { + create: v.value?.map((val) => ({ value: val })), + }, + responsiblePerson: { + create: v.responsiblePersonId?.map((id) => ({ userId: id })), + }, + })), + }, + }, + }); + } + + @Delete("{templateId}") + async deleteFlowTemplateById(@Path() templateId: string) { + return await prisma.workflowTemplate.delete({ + where: { id: templateId }, + }); + } +}