FASE 6 PARCIAL: Extras y Diferenciadores (base implementada)

Implementados módulos base de Fase 6:

1. WALL OF FAME (base)
   - Modelo de base de datos
   - Servicio CRUD
   - Controladores
   - Endpoints: GET /wall-of-fame/*

2. ACHIEVEMENTS/LOGROS (base)
   - Modelo de logros desbloqueables
   - Servicio de progreso
   - Controladores base
   - Endpoints: GET /achievements/*

3. QR CHECK-IN (completo)
   - Generación de códigos QR
   - Validación y procesamiento
   - Check-in/check-out
   - Endpoints: /checkin/*

4. BASE DE DATOS
   - Tablas: wall_of_fame, achievements, qr_codes, check_ins
   - Tablas preparadas: equipment, orders, notifications, activities

Estructura lista para:
- Equipment/Material rental
- Orders/Servicios del club
- Wearables integration
- Challenges/Retos

Nota: Algunos módulos avanzados requieren ajustes finales.
This commit is contained in:
2026-01-31 21:59:36 +00:00
parent 5e50dd766f
commit e135e7ad24
51 changed files with 11323 additions and 4 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,121 @@
-- CreateTable
CREATE TABLE "menu_items" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"description" TEXT,
"category" TEXT NOT NULL DEFAULT 'OTHER',
"price" INTEGER NOT NULL,
"imageUrl" TEXT,
"isAvailable" BOOLEAN NOT NULL DEFAULT true,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"preparationTime" INTEGER,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "orders" (
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"bookingId" TEXT NOT NULL,
"courtId" TEXT NOT NULL,
"items" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'PENDING',
"totalAmount" INTEGER NOT NULL,
"paymentStatus" TEXT NOT NULL DEFAULT 'PENDING',
"paymentId" TEXT,
"notes" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "orders_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "orders_bookingId_fkey" FOREIGN KEY ("bookingId") REFERENCES "bookings" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT "orders_courtId_fkey" FOREIGN KEY ("courtId") REFERENCES "courts" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "notifications" (
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"type" TEXT NOT NULL,
"title" TEXT NOT NULL,
"message" TEXT NOT NULL,
"data" TEXT,
"isRead" BOOLEAN NOT NULL DEFAULT false,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "notifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "user_activities" (
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"source" TEXT NOT NULL,
"activityType" TEXT NOT NULL DEFAULT 'PADEL_GAME',
"startTime" DATETIME NOT NULL,
"endTime" DATETIME NOT NULL,
"duration" INTEGER NOT NULL,
"caloriesBurned" INTEGER NOT NULL,
"heartRateAvg" INTEGER,
"heartRateMax" INTEGER,
"steps" INTEGER,
"distance" REAL,
"metadata" TEXT,
"bookingId" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_activities_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "user_activities_bookingId_fkey" FOREIGN KEY ("bookingId") REFERENCES "bookings" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateIndex
CREATE INDEX "menu_items_category_idx" ON "menu_items"("category");
-- CreateIndex
CREATE INDEX "menu_items_isAvailable_idx" ON "menu_items"("isAvailable");
-- CreateIndex
CREATE INDEX "menu_items_isActive_idx" ON "menu_items"("isActive");
-- CreateIndex
CREATE INDEX "orders_userId_idx" ON "orders"("userId");
-- CreateIndex
CREATE INDEX "orders_bookingId_idx" ON "orders"("bookingId");
-- CreateIndex
CREATE INDEX "orders_courtId_idx" ON "orders"("courtId");
-- CreateIndex
CREATE INDEX "orders_status_idx" ON "orders"("status");
-- CreateIndex
CREATE INDEX "orders_paymentStatus_idx" ON "orders"("paymentStatus");
-- CreateIndex
CREATE INDEX "orders_createdAt_idx" ON "orders"("createdAt");
-- CreateIndex
CREATE INDEX "notifications_userId_idx" ON "notifications"("userId");
-- CreateIndex
CREATE INDEX "notifications_type_idx" ON "notifications"("type");
-- CreateIndex
CREATE INDEX "notifications_isRead_idx" ON "notifications"("isRead");
-- CreateIndex
CREATE INDEX "notifications_createdAt_idx" ON "notifications"("createdAt");
-- CreateIndex
CREATE INDEX "user_activities_userId_idx" ON "user_activities"("userId");
-- CreateIndex
CREATE INDEX "user_activities_source_idx" ON "user_activities"("source");
-- CreateIndex
CREATE INDEX "user_activities_activityType_idx" ON "user_activities"("activityType");
-- CreateIndex
CREATE INDEX "user_activities_startTime_idx" ON "user_activities"("startTime");
-- CreateIndex
CREATE INDEX "user_activities_bookingId_idx" ON "user_activities"("bookingId");

View File

@@ -0,0 +1,139 @@
-- CreateTable
CREATE TABLE "qr_codes" (
"id" TEXT NOT NULL PRIMARY KEY,
"code" TEXT NOT NULL,
"type" TEXT NOT NULL,
"referenceId" TEXT NOT NULL,
"expiresAt" DATETIME NOT NULL,
"usedAt" DATETIME,
"usedBy" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- CreateTable
CREATE TABLE "check_ins" (
"id" TEXT NOT NULL PRIMARY KEY,
"bookingId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"qrCodeId" TEXT,
"checkInTime" DATETIME NOT NULL,
"checkOutTime" DATETIME,
"method" TEXT NOT NULL,
"verifiedBy" TEXT,
"notes" TEXT,
CONSTRAINT "check_ins_qrCodeId_fkey" FOREIGN KEY ("qrCodeId") REFERENCES "qr_codes" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT "check_ins_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "check_ins_bookingId_fkey" FOREIGN KEY ("bookingId") REFERENCES "bookings" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "equipment_items" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"description" TEXT,
"category" TEXT NOT NULL,
"brand" TEXT,
"model" TEXT,
"size" TEXT,
"condition" TEXT NOT NULL DEFAULT 'NEW',
"hourlyRate" INTEGER,
"dailyRate" INTEGER,
"depositRequired" INTEGER NOT NULL DEFAULT 0,
"quantityTotal" INTEGER NOT NULL DEFAULT 1,
"quantityAvailable" INTEGER NOT NULL DEFAULT 1,
"imageUrl" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateTable
CREATE TABLE "equipment_rentals" (
"id" TEXT NOT NULL PRIMARY KEY,
"userId" TEXT NOT NULL,
"bookingId" TEXT,
"startDate" DATETIME NOT NULL,
"endDate" DATETIME NOT NULL,
"totalCost" INTEGER NOT NULL,
"depositAmount" INTEGER NOT NULL,
"depositReturned" INTEGER NOT NULL DEFAULT 0,
"status" TEXT NOT NULL DEFAULT 'RESERVED',
"pickedUpAt" DATETIME,
"returnedAt" DATETIME,
"paymentId" TEXT,
"notes" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "equipment_rentals_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "equipment_rentals_bookingId_fkey" FOREIGN KEY ("bookingId") REFERENCES "bookings" ("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "equipment_rental_items" (
"id" TEXT NOT NULL PRIMARY KEY,
"rentalId" TEXT NOT NULL,
"itemId" TEXT NOT NULL,
"quantity" INTEGER NOT NULL DEFAULT 1,
"hourlyRate" INTEGER,
"dailyRate" INTEGER,
CONSTRAINT "equipment_rental_items_rentalId_fkey" FOREIGN KEY ("rentalId") REFERENCES "equipment_rentals" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "equipment_rental_items_itemId_fkey" FOREIGN KEY ("itemId") REFERENCES "equipment_items" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "qr_codes_code_key" ON "qr_codes"("code");
-- CreateIndex
CREATE INDEX "qr_codes_code_idx" ON "qr_codes"("code");
-- CreateIndex
CREATE INDEX "qr_codes_referenceId_idx" ON "qr_codes"("referenceId");
-- CreateIndex
CREATE INDEX "qr_codes_type_idx" ON "qr_codes"("type");
-- CreateIndex
CREATE INDEX "qr_codes_isActive_idx" ON "qr_codes"("isActive");
-- CreateIndex
CREATE INDEX "check_ins_bookingId_idx" ON "check_ins"("bookingId");
-- CreateIndex
CREATE INDEX "check_ins_userId_idx" ON "check_ins"("userId");
-- CreateIndex
CREATE INDEX "check_ins_checkInTime_idx" ON "check_ins"("checkInTime");
-- CreateIndex
CREATE INDEX "check_ins_method_idx" ON "check_ins"("method");
-- CreateIndex
CREATE INDEX "equipment_items_category_idx" ON "equipment_items"("category");
-- CreateIndex
CREATE INDEX "equipment_items_isActive_idx" ON "equipment_items"("isActive");
-- CreateIndex
CREATE INDEX "equipment_items_quantityAvailable_idx" ON "equipment_items"("quantityAvailable");
-- CreateIndex
CREATE INDEX "equipment_rentals_userId_idx" ON "equipment_rentals"("userId");
-- CreateIndex
CREATE INDEX "equipment_rentals_bookingId_idx" ON "equipment_rentals"("bookingId");
-- CreateIndex
CREATE INDEX "equipment_rentals_status_idx" ON "equipment_rentals"("status");
-- CreateIndex
CREATE INDEX "equipment_rentals_startDate_idx" ON "equipment_rentals"("startDate");
-- CreateIndex
CREATE INDEX "equipment_rentals_endDate_idx" ON "equipment_rentals"("endDate");
-- CreateIndex
CREATE INDEX "equipment_rental_items_rentalId_idx" ON "equipment_rental_items"("rentalId");
-- CreateIndex
CREATE INDEX "equipment_rental_items_itemId_idx" ON "equipment_rental_items"("itemId");

View File

@@ -0,0 +1,228 @@
-- Fase 6: Extras y Diferenciadores
-- Wall of Fame
CREATE TABLE IF NOT EXISTS wall_of_fame_entries (
id TEXT PRIMARY KEY,
tournament_id TEXT,
title TEXT NOT NULL,
description TEXT,
winners TEXT NOT NULL, -- JSON array
category TEXT NOT NULL DEFAULT 'TOURNAMENT',
image_url TEXT,
event_date DATETIME NOT NULL,
is_active BOOLEAN DEFAULT 1,
featured BOOLEAN DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (tournament_id) REFERENCES tournaments(id) ON DELETE SET NULL
);
-- Achievements (Logros)
CREATE TABLE IF NOT EXISTS achievements (
id TEXT PRIMARY KEY,
code TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
description TEXT,
category TEXT NOT NULL DEFAULT 'GAMES',
icon TEXT,
color TEXT DEFAULT '#16a34a',
requirement_type TEXT NOT NULL,
requirement_value INTEGER NOT NULL,
points_reward INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT 1
);
-- User Achievements
CREATE TABLE IF NOT EXISTS user_achievements (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
achievement_id TEXT NOT NULL,
unlocked_at DATETIME,
progress INTEGER DEFAULT 0,
is_completed BOOLEAN DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (achievement_id) REFERENCES achievements(id) ON DELETE CASCADE,
UNIQUE(user_id, achievement_id)
);
-- Challenges (Retos)
CREATE TABLE IF NOT EXISTS challenges (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT,
type TEXT NOT NULL DEFAULT 'WEEKLY',
requirement_type TEXT NOT NULL,
requirement_value INTEGER NOT NULL,
start_date DATETIME NOT NULL,
end_date DATETIME NOT NULL,
reward_points INTEGER DEFAULT 0,
participants TEXT DEFAULT '[]', -- JSON array
winners TEXT DEFAULT '[]', -- JSON array
is_active BOOLEAN DEFAULT 1
);
-- User Challenges
CREATE TABLE IF NOT EXISTS user_challenges (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
challenge_id TEXT NOT NULL,
progress INTEGER DEFAULT 0,
is_completed BOOLEAN DEFAULT 0,
completed_at DATETIME,
reward_claimed BOOLEAN DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (challenge_id) REFERENCES challenges(id) ON DELETE CASCADE,
UNIQUE(user_id, challenge_id)
);
-- QR Codes
CREATE TABLE IF NOT EXISTS qr_codes (
id TEXT PRIMARY KEY,
code TEXT UNIQUE NOT NULL,
type TEXT NOT NULL DEFAULT 'BOOKING_CHECKIN',
reference_id TEXT NOT NULL,
expires_at DATETIME NOT NULL,
used_at DATETIME,
used_by TEXT,
is_active BOOLEAN DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (used_by) REFERENCES users(id) ON DELETE SET NULL
);
-- Check-ins
CREATE TABLE IF NOT EXISTS check_ins (
id TEXT PRIMARY KEY,
booking_id TEXT NOT NULL,
user_id TEXT NOT NULL,
qr_code_id TEXT,
check_in_time DATETIME DEFAULT CURRENT_TIMESTAMP,
check_out_time DATETIME,
method TEXT DEFAULT 'QR',
verified_by TEXT,
notes TEXT,
FOREIGN KEY (booking_id) REFERENCES bookings(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (qr_code_id) REFERENCES qr_codes(id) ON DELETE SET NULL
);
-- Equipment Items
CREATE TABLE IF NOT EXISTS equipment_items (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
category TEXT NOT NULL DEFAULT 'RACKET',
brand TEXT,
model TEXT,
size TEXT,
condition TEXT DEFAULT 'GOOD',
hourly_rate INTEGER,
daily_rate INTEGER,
deposit_required INTEGER DEFAULT 0,
quantity_total INTEGER DEFAULT 1,
quantity_available INTEGER DEFAULT 1,
image_url TEXT,
is_active BOOLEAN DEFAULT 1
);
-- Equipment Rentals
CREATE TABLE IF NOT EXISTS equipment_rentals (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
items TEXT NOT NULL, -- JSON array
booking_id TEXT,
start_date DATETIME NOT NULL,
end_date DATETIME NOT NULL,
total_cost INTEGER NOT NULL,
deposit_amount INTEGER DEFAULT 0,
status TEXT DEFAULT 'RESERVED',
picked_up_at DATETIME,
returned_at DATETIME,
payment_id TEXT,
notes TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (booking_id) REFERENCES bookings(id) ON DELETE SET NULL
);
-- Menu Items
CREATE TABLE IF NOT EXISTS menu_items (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
category TEXT NOT NULL DEFAULT 'DRINK',
price INTEGER NOT NULL,
image_url TEXT,
is_available BOOLEAN DEFAULT 1,
preparation_time INTEGER,
is_active BOOLEAN DEFAULT 1
);
-- Orders
CREATE TABLE IF NOT EXISTS orders (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
booking_id TEXT NOT NULL,
court_id TEXT NOT NULL,
items TEXT NOT NULL, -- JSON array
status TEXT DEFAULT 'PENDING',
total_amount INTEGER NOT NULL,
payment_status TEXT DEFAULT 'PENDING',
payment_id TEXT,
notes TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (booking_id) REFERENCES bookings(id) ON DELETE CASCADE,
FOREIGN KEY (court_id) REFERENCES courts(id) ON DELETE CASCADE
);
-- Notifications
CREATE TABLE IF NOT EXISTS notifications (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
type TEXT NOT NULL,
title TEXT NOT NULL,
message TEXT NOT NULL,
data TEXT, -- JSON
is_read BOOLEAN DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- User Activity (Wearables)
CREATE TABLE IF NOT EXISTS user_activities (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
source TEXT DEFAULT 'MANUAL',
activity_type TEXT DEFAULT 'PADEL_GAME',
start_time DATETIME NOT NULL,
end_time DATETIME NOT NULL,
duration INTEGER NOT NULL,
calories_burned INTEGER,
heart_rate_avg INTEGER,
heart_rate_max INTEGER,
steps INTEGER,
distance REAL,
metadata TEXT, -- JSON
booking_id TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (booking_id) REFERENCES bookings(id) ON DELETE SET NULL
);
-- Indexes
CREATE INDEX IF NOT EXISTS idx_wof_event_date ON wall_of_fame_entries(event_date);
CREATE INDEX IF NOT EXISTS idx_wof_featured ON wall_of_fame_entries(featured);
CREATE INDEX IF NOT EXISTS idx_achievements_category ON achievements(category);
CREATE INDEX IF NOT EXISTS idx_user_achievements_user ON user_achievements(user_id);
CREATE INDEX IF NOT EXISTS idx_challenges_dates ON challenges(start_date, end_date);
CREATE INDEX IF NOT EXISTS idx_qr_codes ON qr_codes(code);
CREATE INDEX IF NOT EXISTS idx_check_ins_booking ON check_ins(booking_id);
CREATE INDEX IF NOT EXISTS idx_check_ins_time ON check_ins(check_in_time);
CREATE INDEX IF NOT EXISTS idx_equipment_category ON equipment_items(category);
CREATE INDEX IF NOT EXISTS idx_rentals_user ON equipment_rentals(user_id);
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
CREATE INDEX IF NOT EXISTS idx_notifications_user ON notifications(user_id);
CREATE INDEX IF NOT EXISTS idx_activities_user ON user_activities(user_id);

View File

@@ -90,6 +90,17 @@ model User {
studentEnrollments StudentEnrollment[]
coachReviews CoachReview[]
// Servicios del Club (Fase 6.3)
orders Order[]
notifications Notification[]
userActivities UserActivity[]
// Check-ins (Fase 6.2)
checkIns CheckIn[]
// Alquileres de equipamiento (Fase 6.2)
equipmentRentals EquipmentRental[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -148,6 +159,9 @@ model Court {
tournamentMatches TournamentMatch[]
classBookings ClassBooking[]
// Servicios del Club (Fase 6.3)
orders Order[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -211,6 +225,16 @@ model Booking {
// Uso de bonos
bonusUsages BonusUsage[]
// Servicios del Club (Fase 6.3)
orders Order[]
userActivities UserActivity[]
// Alquileres de equipamiento asociados (Fase 6.2)
equipmentRentals EquipmentRental[]
// Check-ins asociados (Fase 6.2)
checkIns CheckIn[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -1221,3 +1245,320 @@ model CoachReview {
@@index([rating])
@@map("coach_reviews")
}
// ============================================
// Modelos de Servicios del Club (Fase 6.3)
// ============================================
// Modelo de Item del Menú (productos del bar/cafetería)
model MenuItem {
id String @id @default(uuid())
// Información básica
name String
description String?
// Categoría: DRINK, SNACK, FOOD, OTHER
category String @default("OTHER")
// Precio en centavos
price Int
// Imagen
imageUrl String?
// Disponibilidad y estado
isAvailable Boolean @default(true)
isActive Boolean @default(true)
// Tiempo de preparación en minutos
preparationTime Int?
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([category])
@@index([isAvailable])
@@index([isActive])
@@map("menu_items")
}
// Modelo de Pedido (órdenes a la cancha)
model Order {
id String @id @default(uuid())
// Usuario que realiza el pedido
user User @relation(fields: [userId], references: [id])
userId String
// Reserva asociada (vinculado a reserva activa)
booking Booking @relation(fields: [bookingId], references: [id])
bookingId String
// Cancha donde se entregará
court Court @relation(fields: [courtId], references: [id])
courtId String
// Items del pedido (JSON array de {itemId, quantity, notes, price})
items String
// Estado del pedido: PENDING, PREPARING, READY, DELIVERED, CANCELLED
status String @default("PENDING")
// Monto total en centavos
totalAmount Int
// Estado de pago: PENDING, PAID
paymentStatus String @default("PENDING")
// ID de referencia de pago (MercadoPago u otro)
paymentId String?
// Notas adicionales
notes String?
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([bookingId])
@@index([courtId])
@@index([status])
@@index([paymentStatus])
@@index([createdAt])
@@map("orders")
}
// Modelo de Notificación (push/in-app)
model Notification {
id String @id @default(uuid())
// Usuario destinatario
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Tipo de notificación: ORDER_READY, BOOKING_REMINDER, TOURNAMENT_START, etc.
type String
// Contenido
title String
message String
// Datos adicionales (JSON)
data String? // Puede contener orderId, bookingId, etc.
// Estado de lectura
isRead Boolean @default(false)
// Timestamps
createdAt DateTime @default(now())
@@index([userId])
@@index([type])
@@index([isRead])
@@index([createdAt])
@@map("notifications")
}
// ============================================
// Modelos de Integración con Wearables (Fase 6.3)
// ============================================
// Modelo de Actividad de Usuario (registro de actividad física)
model UserActivity {
id String @id @default(uuid())
// Usuario
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Fuente de datos: APPLE_HEALTH, GOOGLE_FIT, MANUAL
source String
// Tipo de actividad: PADEL_GAME, WORKOUT
activityType String @default("PADEL_GAME")
// Tiempos
startTime DateTime
endTime DateTime
duration Int // Duración en minutos
// Métricas de salud
caloriesBurned Int
heartRateAvg Int?
heartRateMax Int?
steps Int?
distance Float? // km
// Metadatos adicionales (JSON)
metadata String?
// Reserva asociada (opcional)
booking Booking? @relation(fields: [bookingId], references: [id], onDelete: SetNull)
bookingId String?
// Timestamps
createdAt DateTime @default(now())
@@index([userId])
@@index([source])
@@index([activityType])
@@index([startTime])
@@index([bookingId])
@@map("user_activities")
}
// ============================================
// Modelos de Check-in Digital QR (Fase 6.2)
// ============================================
// Modelo de Código QR
model QRCode {
id String @id @default(uuid())
code String @unique
type String // BOOKING_CHECKIN, EVENT_ACCESS, etc.
referenceId String // ID de la entidad (booking, etc.)
expiresAt DateTime
usedAt DateTime?
usedBy String? // userId que usó el QR
isActive Boolean @default(true)
createdAt DateTime @default(now())
// Relaciones
checkIns CheckIn[]
@@index([code])
@@index([referenceId])
@@index([type])
@@index([isActive])
@@map("qr_codes")
}
// Modelo de CheckIn (registro de asistencia)
model CheckIn {
id String @id @default(uuid())
bookingId String
userId String
qrCodeId String?
checkInTime DateTime
checkOutTime DateTime?
method String // QR, MANUAL
verifiedBy String? // admin que verificó
notes String?
// Relaciones
qrCode QRCode? @relation(fields: [qrCodeId], references: [id], onDelete: SetNull)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
@@index([bookingId])
@@index([userId])
@@index([checkInTime])
@@index([method])
@@map("check_ins")
}
// ============================================
// Modelos de Gestión de Material (Fase 6.2)
// ============================================
// Modelo de Item de Equipamiento
model EquipmentItem {
id String @id @default(uuid())
name String
description String?
category String // RACKET, BALLS, ACCESSORIES, SHOES
brand String?
model String?
size String? // talla si aplica
condition String @default("NEW") // NEW, GOOD, FAIR, POOR
hourlyRate Int? // tarifa por hora (en centavos)
dailyRate Int? // tarifa por día (en centavos)
depositRequired Int @default(0) // depósito requerido (en centavos)
quantityTotal Int @default(1)
quantityAvailable Int @default(1)
imageUrl String?
isActive Boolean @default(true)
// Relaciones
rentals EquipmentRentalItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([category])
@@index([isActive])
@@index([quantityAvailable])
@@map("equipment_items")
}
// Modelo de Alquiler de Equipamiento
model EquipmentRental {
id String @id @default(uuid())
userId String
bookingId String? // vinculado a reserva opcional
startDate DateTime
endDate DateTime
totalCost Int // en centavos
depositAmount Int // en centavos
depositReturned Int @default(0) // depósito devuelto (en centavos)
status String @default("RESERVED") // RESERVED, PICKED_UP, RETURNED, LATE, DAMAGED
pickedUpAt DateTime?
returnedAt DateTime?
paymentId String?
notes String?
// Relaciones
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
booking Booking? @relation(fields: [bookingId], references: [id], onDelete: SetNull)
items EquipmentRentalItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([bookingId])
@@index([status])
@@index([startDate])
@@index([endDate])
@@map("equipment_rentals")
}
// Modelo intermedio para items de un alquiler
model EquipmentRentalItem {
id String @id @default(uuid())
rentalId String
itemId String
quantity Int @default(1)
hourlyRate Int? // tarifa aplicada al momento del alquiler
dailyRate Int? // tarifa aplicada al momento del alquiler
// Relaciones
rental EquipmentRental @relation(fields: [rentalId], references: [id], onDelete: Cascade)
item EquipmentItem @relation(fields: [itemId], references: [id], onDelete: Cascade)
@@index([rentalId])
@@index([itemId])
@@map("equipment_rental_items")
}