feat: add recurring bookings feature with related handlers and services
- Introduced recurring booking groups with the ability to create and cancel them. - Added database migrations for recurring bookings, including new tables and relationships. - Implemented handlers for creating and canceling recurring bookings in the admin booking module. - Enhanced existing booking services to support recurring bookings logic. - Updated API contract to include new schemas for recurring bookings. - Refactored existing code for improved readability and maintainability.
This commit is contained in:
@@ -14,6 +14,7 @@ model Complex {
|
||||
users ComplexUser[]
|
||||
invitations ComplexInvitation[]
|
||||
courts Court[]
|
||||
recurringGroups RecurringBookingGroup[]
|
||||
|
||||
@@index([planCode])
|
||||
@@index([complexSlug])
|
||||
|
||||
@@ -15,6 +15,11 @@ enum CourtBookingStatus {
|
||||
NOSHOW
|
||||
}
|
||||
|
||||
enum RecurringBookingGroupStatus {
|
||||
ACTIVE
|
||||
CANCELLED
|
||||
}
|
||||
|
||||
model Sport {
|
||||
id String @id @db.Uuid
|
||||
name String @unique
|
||||
@@ -44,6 +49,7 @@ model Court {
|
||||
priceRules CourtPriceRule[]
|
||||
bookings CourtBooking[]
|
||||
maintenances CourtMaintenance[]
|
||||
recurringGroups RecurringBookingGroup[]
|
||||
|
||||
@@index([complexId])
|
||||
@@index([sportId])
|
||||
@@ -95,23 +101,26 @@ model CourtPriceRule {
|
||||
}
|
||||
|
||||
model CourtBooking {
|
||||
id String @id @db.Uuid
|
||||
bookingCode String @unique @map("booking_code") @db.VarChar(8)
|
||||
courtId String @map("court_id") @db.Uuid
|
||||
bookingDate DateTime @map("booking_date") @db.Date
|
||||
startTime String @map("start_time") @db.VarChar(5)
|
||||
endTime String @map("end_time") @db.VarChar(5)
|
||||
customerName String @map("customer_name") @db.VarChar(120)
|
||||
customerPhone String @map("customer_phone") @db.VarChar(30)
|
||||
customerEmail String @map("customer_email") @db.VarChar(254)
|
||||
status CourtBookingStatus @default(CONFIRMED)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
|
||||
id String @id @db.Uuid
|
||||
bookingCode String @unique @map("booking_code") @db.VarChar(8)
|
||||
courtId String @map("court_id") @db.Uuid
|
||||
bookingDate DateTime @map("booking_date") @db.Date
|
||||
startTime String @map("start_time") @db.VarChar(5)
|
||||
endTime String @map("end_time") @db.VarChar(5)
|
||||
customerName String @map("customer_name") @db.VarChar(120)
|
||||
customerPhone String @map("customer_phone") @db.VarChar(30)
|
||||
customerEmail String @map("customer_email") @db.VarChar(254)
|
||||
status CourtBookingStatus @default(CONFIRMED)
|
||||
recurringGroupId String? @map("recurring_group_id") @db.Uuid
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
|
||||
recurringGroup RecurringBookingGroup? @relation(fields: [recurringGroupId], references: [id])
|
||||
|
||||
@@unique([courtId, bookingDate, startTime])
|
||||
@@index([courtId, bookingDate])
|
||||
@@index([bookingDate])
|
||||
@@index([recurringGroupId])
|
||||
@@map("court_bookings")
|
||||
}
|
||||
|
||||
@@ -132,3 +141,28 @@ model CourtBookingLog {
|
||||
@@map("court_booking_logs")
|
||||
@@index([courtId, newStatus])
|
||||
}
|
||||
|
||||
model RecurringBookingGroup {
|
||||
id String @id @db.Uuid
|
||||
complexId String @map("complex_id") @db.Uuid
|
||||
courtId String @map("court_id") @db.Uuid
|
||||
startTime String @map("start_time") @db.VarChar(5)
|
||||
endTime String @map("end_time") @db.VarChar(5)
|
||||
dayOfWeek DayOfWeek @map("day_of_week")
|
||||
startDate DateTime @map("start_date") @db.Date
|
||||
endDate DateTime? @map("end_date") @db.Date
|
||||
status RecurringBookingGroupStatus @default(ACTIVE)
|
||||
customerName String @map("customer_name") @db.VarChar(120)
|
||||
customerPhone String @map("customer_phone") @db.VarChar(30)
|
||||
customerEmail String @map("customer_email") @db.VarChar(254)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
|
||||
complex Complex @relation(fields: [complexId], references: [id], onDelete: Cascade)
|
||||
bookings CourtBooking[]
|
||||
|
||||
@@index([complexId])
|
||||
@@index([courtId])
|
||||
@@index([status])
|
||||
@@map("recurring_booking_groups")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "RecurringBookingGroupStatus" AS ENUM ('ACTIVE', 'CANCELLED');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "court_bookings" ADD COLUMN "recurring_group_id" UUID;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "recurring_booking_groups" (
|
||||
"id" UUID NOT NULL,
|
||||
"complex_id" UUID NOT NULL,
|
||||
"court_id" UUID NOT NULL,
|
||||
"start_time" VARCHAR(5) NOT NULL,
|
||||
"end_time" VARCHAR(5) NOT NULL,
|
||||
"day_of_week" "DayOfWeek" NOT NULL,
|
||||
"start_date" DATE NOT NULL,
|
||||
"end_date" DATE,
|
||||
"status" "RecurringBookingGroupStatus" NOT NULL DEFAULT 'ACTIVE',
|
||||
"customer_name" VARCHAR(120) NOT NULL,
|
||||
"customer_phone" VARCHAR(30) NOT NULL,
|
||||
"customer_email" VARCHAR(254) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "recurring_booking_groups_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "recurring_booking_groups_complex_id_idx" ON "recurring_booking_groups"("complex_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "recurring_booking_groups_court_id_idx" ON "recurring_booking_groups"("court_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "recurring_booking_groups_status_idx" ON "recurring_booking_groups"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "court_bookings_recurring_group_id_idx" ON "court_bookings"("recurring_group_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "court_bookings" ADD CONSTRAINT "court_bookings_recurring_group_id_fkey" FOREIGN KEY ("recurring_group_id") REFERENCES "recurring_booking_groups"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "recurring_booking_groups" ADD CONSTRAINT "recurring_booking_groups_court_id_fkey" FOREIGN KEY ("court_id") REFERENCES "courts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "recurring_booking_groups" ADD CONSTRAINT "recurring_booking_groups_complex_id_fkey" FOREIGN KEY ("complex_id") REFERENCES "complexes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -21,6 +21,7 @@ export const planSeeds = [
|
||||
publicBookingPage: true,
|
||||
advancedReports: false,
|
||||
whatsappReminders: false,
|
||||
fixedSlots: false,
|
||||
},
|
||||
pricing: {
|
||||
overrides: {
|
||||
@@ -51,6 +52,7 @@ export const planSeeds = [
|
||||
publicBookingPage: true,
|
||||
advancedReports: true,
|
||||
whatsappReminders: true,
|
||||
fixedSlots: true,
|
||||
},
|
||||
pricing: {
|
||||
overrides: {
|
||||
@@ -81,6 +83,7 @@ export const planSeeds = [
|
||||
publicBookingPage: true,
|
||||
advancedReports: true,
|
||||
whatsappReminders: true,
|
||||
fixedSlots: true,
|
||||
},
|
||||
pricing: {
|
||||
overrides: {
|
||||
|
||||
Reference in New Issue
Block a user