feat: tambah fitur approval task pada project dan divisi

- tambah model ProjectTaskApproval dan DivisionProjectTaskApproval di schema prisma
- tambah field isApprover pada model User
- tambah API approval project task: GET riwayat, POST ajukan, PUT setujui/tolak
- tambah API approval division task: GET riwayat, POST ajukan, PUT setujui/tolak
- notifikasi dikirim ke approver, admin divisi, dan submitter via FCM, web push, dan in-app
- tambah PATCH endpoint untuk toggle isApprover pada mobile user API
- perbaiki pengecekan role approver menggunakan UserRole.id
This commit is contained in:
2026-05-07 16:04:11 +08:00
parent b7ce72a41b
commit 732e26ca0d
5 changed files with 912 additions and 25 deletions

View File

@@ -0,0 +1,48 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "isApprover" BOOLEAN NOT NULL DEFAULT false;
-- CreateTable
CREATE TABLE "ProjectTaskApproval" (
"id" TEXT NOT NULL,
"idTask" TEXT NOT NULL,
"idUser" TEXT NOT NULL,
"idApprover" TEXT,
"status" INTEGER NOT NULL DEFAULT 0,
"note" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProjectTaskApproval_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "DivisionProjectTaskApproval" (
"id" TEXT NOT NULL,
"idTask" TEXT NOT NULL,
"idUser" TEXT NOT NULL,
"idApprover" TEXT,
"status" INTEGER NOT NULL DEFAULT 0,
"note" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "DivisionProjectTaskApproval_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "ProjectTaskApproval" ADD CONSTRAINT "ProjectTaskApproval_idTask_fkey" FOREIGN KEY ("idTask") REFERENCES "ProjectTask"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProjectTaskApproval" ADD CONSTRAINT "ProjectTaskApproval_idUser_fkey" FOREIGN KEY ("idUser") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProjectTaskApproval" ADD CONSTRAINT "ProjectTaskApproval_idApprover_fkey" FOREIGN KEY ("idApprover") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "DivisionProjectTaskApproval" ADD CONSTRAINT "DivisionProjectTaskApproval_idTask_fkey" FOREIGN KEY ("idTask") REFERENCES "DivisionProjectTask"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "DivisionProjectTaskApproval" ADD CONSTRAINT "DivisionProjectTaskApproval_idUser_fkey" FOREIGN KEY ("idUser") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "DivisionProjectTaskApproval" ADD CONSTRAINT "DivisionProjectTaskApproval_idApprover_fkey" FOREIGN KEY ("idApprover") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@@ -109,6 +109,7 @@ model User {
img String?
isFirstLogin Boolean @default(true)
isWithoutOTP Boolean @default(false)
isApprover Boolean @default(false)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -125,13 +126,17 @@ model User {
DivisionDocumentFolderFile DivisionDocumentFolderFile[]
DivisionCalendar DivisionCalendar[]
DivisionCalendarMember DivisionCalendarMember[]
Notifications Notifications[] @relation("UserToUser")
Notifications2 Notifications[] @relation("UserFromUser")
Subscribe Subscribe?
Discussion Discussion[]
DiscussionMember DiscussionMember[]
DiscussionComment DiscussionComment[]
TokenDeviceUser TokenDeviceUser[]
Notifications Notifications[] @relation("UserToUser")
Notifications2 Notifications[] @relation("UserFromUser")
Subscribe Subscribe?
Discussion Discussion[]
DiscussionMember DiscussionMember[]
DiscussionComment DiscussionComment[]
TokenDeviceUser TokenDeviceUser[]
ProjectTaskApprovalSubmitted ProjectTaskApproval[] @relation("ApprovalSubmitter")
ProjectTaskApprovalHandled ProjectTaskApproval[] @relation("ApprovalApprover")
DivisionProjectTaskApprovalSubmitted DivisionProjectTaskApproval[] @relation("DivApprovalSubmitter")
DivisionProjectTaskApprovalHandled DivisionProjectTaskApproval[] @relation("DivApprovalApprover")
}
model TokenDeviceUser {
@@ -260,15 +265,16 @@ model ProjectTask {
idProject String
title String
desc String?
status Int @default(0) // 0 = todo, 1 = done
notifikasi Boolean @default(false)
dateStart DateTime @db.Date
dateEnd DateTime @db.Date
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
ProjectTaskDetail ProjectTaskDetail[]
ProjectTaskFile ProjectTaskFile[]
status Int @default(0) // 0 = todo, 1 = done, 2 = waiting_approval
notifikasi Boolean @default(false)
dateStart DateTime @db.Date
dateEnd DateTime @db.Date
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
ProjectTaskDetail ProjectTaskDetail[]
ProjectTaskFile ProjectTaskFile[]
ProjectTaskApproval ProjectTaskApproval[]
}
model ProjectTaskFile {
@@ -373,15 +379,16 @@ model DivisionProjectTask {
idProject String
title String
desc String? @db.Text
status Int @default(0) // 0 = todo, 1 = done
notifikasi Boolean @default(false)
dateStart DateTime @db.Date
dateEnd DateTime @db.Date
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
DivisionProjectTaskDetail DivisionProjectTaskDetail[]
DivisionProjectTaskFile DivisionProjectTaskFile[]
status Int @default(0) // 0 = todo, 1 = done, 2 = waiting_approval
notifikasi Boolean @default(false)
dateStart DateTime @db.Date
dateEnd DateTime @db.Date
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
DivisionProjectTaskDetail DivisionProjectTaskDetail[]
DivisionProjectTaskFile DivisionProjectTaskFile[]
DivisionProjectTaskApproval DivisionProjectTaskApproval[]
}
model DivisionProjectTaskDetail {
@@ -694,3 +701,31 @@ model Setting{
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model ProjectTaskApproval {
id String @id @default(cuid())
ProjectTask ProjectTask @relation(fields: [idTask], references: [id])
idTask String
Submitter User @relation("ApprovalSubmitter", fields: [idUser], references: [id])
idUser String
Approver User? @relation("ApprovalApprover", fields: [idApprover], references: [id])
idApprover String?
status Int @default(0) // 0 = pending, 1 = approved, 2 = rejected
note String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model DivisionProjectTaskApproval {
id String @id @default(cuid())
DivisionProjectTask DivisionProjectTask @relation(fields: [idTask], references: [id])
idTask String
Submitter User @relation("DivApprovalSubmitter", fields: [idUser], references: [id])
idUser String
Approver User? @relation("DivApprovalApprover", fields: [idApprover], references: [id])
idApprover String?
status Int @default(0) // 0 = pending, 1 = approved, 2 = rejected
note String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}