Add reservations module and rename teams visibility label to file-access level.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-27 09:38:17 +03:30
co-authored by Cursor
parent db65b55660
commit d133c8ca34
15 changed files with 1646 additions and 15 deletions
@@ -0,0 +1,66 @@
-- CreateTable
CREATE TABLE "ReservationService" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT NOT NULL DEFAULT '',
"durationMinutes" INTEGER NOT NULL DEFAULT 30,
"workDays" INTEGER[] DEFAULT ARRAY[0, 1, 2, 3, 4, 5, 6]::INTEGER[],
"dayStartHour" INTEGER NOT NULL DEFAULT 9,
"dayEndHour" INTEGER NOT NULL DEFAULT 17,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ReservationService_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ReservationAppointment" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"serviceId" TEXT NOT NULL,
"personId" TEXT NOT NULL,
"assigneeId" TEXT,
"startsAt" TIMESTAMP(3) NOT NULL,
"endsAt" TIMESTAMP(3) NOT NULL,
"status" TEXT NOT NULL DEFAULT 'pending',
"notes" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ReservationAppointment_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "ReservationService_organizationId_isActive_idx" ON "ReservationService"("organizationId", "isActive");
-- CreateIndex
CREATE INDEX "ReservationAppointment_organizationId_startsAt_idx" ON "ReservationAppointment"("organizationId", "startsAt");
-- CreateIndex
CREATE INDEX "ReservationAppointment_organizationId_status_idx" ON "ReservationAppointment"("organizationId", "status");
-- CreateIndex
CREATE INDEX "ReservationAppointment_serviceId_startsAt_idx" ON "ReservationAppointment"("serviceId", "startsAt");
-- CreateIndex
CREATE INDEX "ReservationAppointment_personId_idx" ON "ReservationAppointment"("personId");
-- CreateIndex
CREATE INDEX "ReservationAppointment_assigneeId_startsAt_idx" ON "ReservationAppointment"("assigneeId", "startsAt");
-- AddForeignKey
ALTER TABLE "ReservationService" ADD CONSTRAINT "ReservationService_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ReservationAppointment" ADD CONSTRAINT "ReservationAppointment_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ReservationAppointment" ADD CONSTRAINT "ReservationAppointment_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "ReservationService"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ReservationAppointment" ADD CONSTRAINT "ReservationAppointment_personId_fkey" FOREIGN KEY ("personId") REFERENCES "Person"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ReservationAppointment" ADD CONSTRAINT "ReservationAppointment_assigneeId_fkey" FOREIGN KEY ("assigneeId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
+49
View File
@@ -200,6 +200,8 @@ model Organization {
smsCampaigns SmsCampaign[]
wooCommerceConnections WooCommerceConnection[]
voipConnections VoipConnection[]
reservationServices ReservationService[]
reservationAppointments ReservationAppointment[]
}
model User {
@@ -245,6 +247,7 @@ model User {
savedViews SavedView[] @relation("SavedViewOwner")
assignedProjectCards ProjectCard[] @relation("ProjectCardAssignees")
teamMemberships TeamMember[]
reservationAssignments ReservationAppointment[] @relation("ReservationAssignee")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -425,6 +428,7 @@ model Person {
smartNotes Note[]
quotes Quote[]
invoices Invoice[]
reservationAppointments ReservationAppointment[]
ownerId String?
owner User? @relation("PersonOwner", fields: [ownerId], references: [id], onDelete: SetNull)
tickets Ticket[]
@@ -1830,3 +1834,48 @@ model VoipCallLog {
@@index([personId])
@@index([activityId])
}
/// خدمت قابل رزرو (مثلاً مشاوره ۳۰ دقیقه‌ای)
model ReservationService {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
name String
description String @default("")
durationMinutes Int @default(30)
/// روزهای کار ۰=یکشنبه … ۶=شنبه
workDays Int[] @default([0, 1, 2, 3, 4, 5, 6])
dayStartHour Int @default(9)
dayEndHour Int @default(17)
isActive Boolean @default(true)
appointments ReservationAppointment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([organizationId, isActive])
}
model ReservationAppointment {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
serviceId String
service ReservationService @relation(fields: [serviceId], references: [id], onDelete: Restrict)
personId String
person Person @relation(fields: [personId], references: [id], onDelete: Cascade)
assigneeId String?
assignee User? @relation("ReservationAssignee", fields: [assigneeId], references: [id], onDelete: SetNull)
startsAt DateTime
endsAt DateTime
/// pending | confirmed | canceled | done
status String @default("pending")
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([organizationId, startsAt])
@@index([organizationId, status])
@@index([serviceId, startsAt])
@@index([personId])
@@index([assigneeId, startsAt])
}