Files
app-padel/backend/prisma/schema.prisma
Ivan Alcaraz dd10891432
Some checks failed
CI/CD Pipeline / 🧪 Tests (push) Has been cancelled
CI/CD Pipeline / 🏗️ Build (push) Has been cancelled
CI/CD Pipeline / 🚀 Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / 🚀 Deploy to Production (push) Has been cancelled
CI/CD Pipeline / 🏷️ Create Release (push) Has been cancelled
CI/CD Pipeline / 🧹 Cleanup (push) Has been cancelled
FASE 7 COMPLETADA: Testing y Lanzamiento - PROYECTO FINALIZADO
Implementados 4 módulos con agent swarm:

1. TESTING FUNCIONAL (Jest)
   - Configuración Jest + ts-jest
   - Tests unitarios: auth, booking, court (55 tests)
   - Tests integración: routes (56 tests)
   - Factories y utilidades de testing
   - Coverage configurado (70% servicios)
   - Scripts: test, test:watch, test:coverage

2. TESTING DE USUARIO (Beta)
   - Sistema de beta testers
   - Feedback con categorías y severidad
   - Beta issues tracking
   - 8 testers de prueba creados
   - API completa para gestión de feedback

3. DOCUMENTACIÓN COMPLETA
   - API.md - 150+ endpoints documentados
   - SETUP.md - Guía de instalación
   - DEPLOY.md - Deploy en VPS
   - ARCHITECTURE.md - Arquitectura del sistema
   - APP_STORE.md - Material para stores
   - Postman Collection completa
   - PM2 ecosystem config
   - Nginx config con SSL

4. GO LIVE Y PRODUCCIÓN
   - Sistema de monitoreo (logs, health checks)
   - Servicio de alertas multi-canal
   - Pre-deploy check script
   - Docker + docker-compose producción
   - Backup automatizado
   - CI/CD GitHub Actions
   - Launch checklist completo

ESTADÍSTICAS FINALES:
- Fases completadas: 7/7
- Archivos creados: 250+
- Líneas de código: 60,000+
- Endpoints API: 150+
- Tests: 110+
- Documentación: 5,000+ líneas

PROYECTO COMPLETO Y LISTO PARA PRODUCCIÓN
2026-01-31 22:30:44 +00:00

1803 lines
44 KiB
Plaintext

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
// Modelo de Usuario
model User {
id String @id @default(uuid())
email String @unique
password String
// Datos personales
firstName String
lastName String
phone String?
avatarUrl String?
city String?
birthDate DateTime?
// Datos de juego (usamos String para simular enums en SQLite)
role String @default("PLAYER") // PLAYER, ADMIN, SUPERADMIN
playerLevel String @default("BEGINNER") // BEGINNER, ELEMENTARY, INTERMEDIATE, ADVANCED, COMPETITION, PROFESSIONAL
handPreference String @default("RIGHT") // RIGHT, LEFT, BOTH
positionPreference String @default("BOTH") // DRIVE, BACKHAND, BOTH
bio String?
yearsPlaying Int?
// Estadísticas globales
matchesPlayed Int @default(0)
matchesWon Int @default(0)
matchesLost Int @default(0)
totalPoints Int @default(0)
// Estado
isActive Boolean @default(true)
isVerified Boolean @default(false)
lastLogin DateTime?
// Relaciones
bookings Booking[]
levelHistory LevelHistory[]
// Relaciones con MatchResult
team1Player1Matches MatchResult[] @relation("Team1Player1")
team1Player2Matches MatchResult[] @relation("Team1Player2")
team2Player1Matches MatchResult[] @relation("Team2Player1")
team2Player2Matches MatchResult[] @relation("Team2Player2")
// Relación con UserStats
userStats UserStats[]
// Amistades
friendsSent Friend[] @relation("FriendRequestsSent")
friendsReceived Friend[] @relation("FriendRequestsReceived")
// Grupos
groupsCreated Group[] @relation("GroupsCreated")
groupMembers GroupMember[] @relation("GroupMemberships")
// Ligas
leaguesCreated League[] @relation("LeaguesCreated") // Ligas creadas por el usuario
teamCaptain LeagueTeam[] @relation("TeamCaptain") // Equipos donde es capitán
leagueTeamMembers LeagueTeamMember[] // Membresías de equipo en liga
// Reservas recurrentes
recurringBookings RecurringBooking[]
// Torneos
tournamentsCreated Tournament[] @relation("TournamentsCreated")
tournamentParticipations TournamentParticipant[]
// Bonos (Fase 4.2)
userBonuses UserBonus[]
// Pagos (Fase 4.1)
payments Payment[]
// Suscripciones (Fase 4.3)
subscriptions UserSubscription[]
// Clases con profesores (Fase 4.4)
coach Coach?
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[]
// Monitoreo y logs (Fase 7.4)
systemLogs SystemLog[]
// Feedback Beta (Fase 7.2)
betaTester BetaTester?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("users")
}
// Modelo de Historial de Niveles
model LevelHistory {
id String @id @default(uuid())
// Niveles
oldLevel String
newLevel String
// Referencias
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Quién realizó el cambio (admin)
changedBy String
// Metadata
reason String?
createdAt DateTime @default(now())
@@index([userId])
@@map("level_history")
}
// Modelo de Cancha
model Court {
id String @id @default(uuid())
name String @unique
description String?
// Características
type String @default("PANORAMIC") // PANORAMIC, OUTDOOR, INDOOR, SINGLE
isIndoor Boolean @default(false)
hasLighting Boolean @default(true)
hasParking Boolean @default(false)
// Precio por hora (en centavos para evitar decimales)
pricePerHour Int @default(2000)
// Imagen
imageUrl String?
// Estado
isActive Boolean @default(true)
// Relaciones
bookings Booking[]
schedules CourtSchedule[]
recurringBookings RecurringBooking[]
leagueMatches LeagueMatch[]
tournamentMatches TournamentMatch[]
classBookings ClassBooking[]
// Servicios del Club (Fase 6.3)
orders Order[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("courts")
}
// Modelo de Horarios de Cancha (días y horas de operación)
model CourtSchedule {
id String @id @default(uuid())
// Día de la semana (0=Domingo, 1=Lunes, ..., 6=Sábado)
dayOfWeek Int
// Horario
openTime String
closeTime String
// Precio especial para esta franja (opcional)
priceOverride Int?
// Relación
court Court @relation(fields: [courtId], references: [id], onDelete: Cascade)
courtId String
@@unique([courtId, dayOfWeek])
@@map("court_schedules")
}
// Modelo de Reserva
model Booking {
id String @id @default(uuid())
// Fecha y hora
date DateTime
startTime String
endTime String
// Estado (PENDING, CONFIRMED, CANCELLED, COMPLETED, NO_SHOW)
status String @default("PENDING")
// Precio
totalPrice Int
// Notas
notes String?
// Relaciones
user User @relation(fields: [userId], references: [id])
userId String
court Court @relation(fields: [courtId], references: [id])
courtId String
// Relación con MatchResult
matchResult MatchResult?
// Referencia a reserva recurrente (si aplica)
recurringBooking RecurringBooking? @relation(fields: [recurringBookingId], references: [id])
recurringBookingId String?
// 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
@@index([userId])
@@index([courtId])
@@index([date])
@@index([recurringBookingId])
@@map("bookings")
}
// Modelo de Amistad
model Friend {
id String @id @default(uuid())
// Quien envía la solicitud
requester User @relation("FriendRequestsSent", fields: [requesterId], references: [id])
requesterId String
// Quien recibe la solicitud
addressee User @relation("FriendRequestsReceived", fields: [addresseeId], references: [id])
addresseeId String
// Estado (PENDING, ACCEPTED, REJECTED, BLOCKED)
status String @default("PENDING")
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([requesterId, addresseeId])
@@index([requesterId])
@@index([addresseeId])
@@index([status])
@@map("friends")
}
// Modelo de Grupo
model Group {
id String @id @default(uuid())
name String
description String?
// Creador del grupo
createdBy User @relation("GroupsCreated", fields: [createdById], references: [id])
createdById String
// Relaciones
members GroupMember[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([createdById])
@@map("groups")
}
// Modelo de Miembro de Grupo
model GroupMember {
id String @id @default(uuid())
// Grupo
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
groupId String
// Usuario
user User @relation("GroupMemberships", fields: [userId], references: [id])
userId String
// Rol (ADMIN, MEMBER)
role String @default("MEMBER")
// Fecha de unión
joinedAt DateTime @default(now())
@@unique([groupId, userId])
@@index([groupId])
@@index([userId])
@@map("group_members")
}
// Modelo de Reserva Recurrente
model RecurringBooking {
id String @id @default(uuid())
// Usuario que crea la reserva recurrente
user User @relation(fields: [userId], references: [id])
userId String
// Cancha
court Court @relation(fields: [courtId], references: [id])
courtId String
// Día de la semana (0=Domingo, 1=Lunes, ..., 6=Sábado)
dayOfWeek Int
// Horario
startTime String
endTime String
// Rango de fechas
startDate DateTime
endDate DateTime?
// Estado
isActive Boolean @default(true)
// Relaciones
bookings Booking[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([courtId])
@@index([dayOfWeek])
@@index([isActive])
@@map("recurring_bookings")
}
// Modelo de Resultado de Partido
model MatchResult {
id String @id @default(uuid())
// Relación opcional con reserva
booking Booking? @relation(fields: [bookingId], references: [id], onDelete: SetNull)
bookingId String? @unique
// Jugadores del Equipo 1
team1Player1 User @relation("Team1Player1", fields: [team1Player1Id], references: [id])
team1Player1Id String
team1Player2 User @relation("Team1Player2", fields: [team1Player2Id], references: [id])
team1Player2Id String
// Jugadores del Equipo 2
team2Player1 User @relation("Team2Player1", fields: [team2Player1Id], references: [id])
team2Player1Id String
team2Player2 User @relation("Team2Player2", fields: [team2Player2Id], references: [id])
team2Player2Id String
// Resultado
team1Score Int
team2Score Int
winner String // TEAM1, TEAM2, DRAW
// Fecha en que se jugó el partido
playedAt DateTime
// Confirmaciones (JSON array de userIds)
confirmedBy String @default("[]")
// Timestamps
createdAt DateTime @default(now())
@@index([team1Player1Id])
@@index([team1Player2Id])
@@index([team2Player1Id])
@@index([team2Player2Id])
@@index([playedAt])
@@map("match_results")
}
// Modelo de Estadísticas de Usuario por Período
model UserStats {
id String @id @default(uuid())
// Usuario
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Período (MONTH, YEAR, ALL_TIME)
period String
// Valor del período (ej: "2024-01" para mes, "2024" para año)
periodValue String
// Estadísticas de partidos
matchesPlayed Int @default(0)
matchesWon Int @default(0)
matchesLost Int @default(0)
// Estadísticas de torneos
tournamentsPlayed Int @default(0)
tournamentsWon Int @default(0)
// Puntos para ranking
points Int @default(0)
// Timestamps
updatedAt DateTime @updatedAt
@@unique([userId, period, periodValue])
@@index([userId])
@@index([period, periodValue])
@@index([points])
@@map("user_stats")
}
// Modelo de Torneo
model Tournament {
id String @id @default(uuid())
name String
description String?
// Tipo de torneo
type String // ELIMINATION, ROUND_ROBIN, SWISS, CONSOLATION
// Categoría
category String // MEN, WOMEN, MIXED
// Niveles permitidos (almacenados como JSON string)
allowedLevels String
// Capacidad
maxParticipants Int
// Fechas importantes
registrationStartDate DateTime
registrationEndDate DateTime
startDate DateTime
endDate DateTime
// Canchas asignadas (almacenadas como JSON string de IDs)
courtIds String
// Precio de inscripción (en centavos)
price Int @default(0)
// Estado del torneo
status String @default("DRAFT") // DRAFT, OPEN, CLOSED, IN_PROGRESS, FINISHED, CANCELLED
// Creador (admin)
createdBy User @relation("TournamentsCreated", fields: [createdById], references: [id])
createdById String
// Relaciones
participants TournamentParticipant[] @relation("TournamentParticipants")
matches TournamentMatch[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
@@index([createdById])
@@index([startDate])
@@index([registrationStartDate])
@@map("tournaments")
}
// Modelo de Participante de Torneo
model TournamentParticipant {
id String @id @default(uuid())
// Torneo
tournament Tournament @relation("TournamentParticipants", fields: [tournamentId], references: [id], onDelete: Cascade)
tournamentId String
// Usuario (jugador individual)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Fecha de inscripción
registrationDate DateTime @default(now())
// Estado del pago
paymentStatus String @default("PENDING") // PENDING, PAID, REFUNDED
// Número de cabeza de serie (opcional)
seed Int?
// Estado de la inscripción
status String @default("REGISTERED") // REGISTERED, CONFIRMED, WITHDRAWN
// Timestamps
updatedAt DateTime @updatedAt
// Relaciones con partidos (como jugador individual)
team1Player1Matches TournamentMatch[] @relation("T1P1")
team1Player2Matches TournamentMatch[] @relation("T1P2")
team2Player1Matches TournamentMatch[] @relation("T2P1")
team2Player2Matches TournamentMatch[] @relation("T2P2")
@@unique([tournamentId, userId])
@@index([tournamentId])
@@index([userId])
@@index([paymentStatus])
@@index([status])
@@map("tournament_participants")
}
// Modelo de Partido de Torneo
model TournamentMatch {
id String @id @default(uuid())
// Torneo
tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
tournamentId String
// Ronda (1=final, 2=semifinal, etc. o número de ronda en liga)
round Int
// Número de partido en la ronda
matchNumber Int
// Posición en el cuadro
position Int
// Equipo 1 (pareja o individual)
team1Player1 TournamentParticipant? @relation("T1P1", fields: [team1Player1Id], references: [id], onDelete: SetNull)
team1Player1Id String?
team1Player2 TournamentParticipant? @relation("T1P2", fields: [team1Player2Id], references: [id], onDelete: SetNull)
team1Player2Id String?
// Equipo 2 (pareja o individual)
team2Player1 TournamentParticipant? @relation("T2P1", fields: [team2Player1Id], references: [id], onDelete: SetNull)
team2Player1Id String?
team2Player2 TournamentParticipant? @relation("T2P2", fields: [team2Player2Id], references: [id], onDelete: SetNull)
team2Player2Id String?
// Cancha asignada
court Court? @relation(fields: [courtId], references: [id], onDelete: SetNull)
courtId String?
// Fecha y hora programada
scheduledDate DateTime?
scheduledTime String?
// Estado del partido
status String @default("PENDING") // PENDING, SCHEDULED, IN_PROGRESS, FINISHED, CANCELLED, BYE
// Resultado
team1Score Int?
team2Score Int?
winner String? // TEAM1, TEAM2, DRAW
// Avance en cuadro
nextMatch TournamentMatch? @relation("NextMatch", fields: [nextMatchId], references: [id], onDelete: SetNull)
nextMatchId String?
parentMatches TournamentMatch[] @relation("NextMatch")
// Confirmaciones del resultado (JSON array de userIds)
confirmedBy String @default("[]")
// Metadatos adicionales (para sistemas suizo, round robin, etc)
metadata String? // JSON string con datos adicionales
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([tournamentId])
@@index([round])
@@index([status])
@@index([courtId])
@@index([nextMatchId])
@@index([tournamentId, round])
@@map("tournament_matches")
}
// ============================================
// Modelos de Liga por Equipos (Fase 3.3)
// ============================================
// Modelo de Liga
model League {
id String @id @default(uuid())
name String
description String?
// Tipo y formato
type String @default("TEAM_LEAGUE") // TEAM_LEAGUE, INDIVIDUAL_LEAGUE
format String @default("DOUBLE_ROUND_ROBIN") // SINGLE_ROUND_ROBIN, DOUBLE_ROUND_ROBIN, etc.
// Configuración de partidos por jornada
matchesPerMatchday Int @default(2) // Número de partidos entre dos equipos por jornada
// Fechas
startDate DateTime?
endDate DateTime?
// Estado
status String @default("DRAFT") // DRAFT, ACTIVE, FINISHED, CANCELLED
// Creador (admin)
createdBy User @relation("LeaguesCreated", fields: [createdById], references: [id])
createdById String
// Relaciones
teams LeagueTeam[]
matches LeagueMatch[]
standings LeagueStanding[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
@@index([createdById])
@@index([startDate])
@@map("leagues")
}
// Modelo de Equipo en Liga
model LeagueTeam {
id String @id @default(uuid())
name String
description String?
// Liga
league League @relation(fields: [leagueId], references: [id], onDelete: Cascade)
leagueId String
// Capitán del equipo
captain User @relation("TeamCaptain", fields: [captainId], references: [id])
captainId String
// Relaciones
members LeagueTeamMember[]
// Partidos como equipo 1 o 2
matchesAsTeam1 LeagueMatch[] @relation("Team1Matches")
matchesAsTeam2 LeagueMatch[] @relation("Team2Matches")
// Clasificación
standing LeagueStanding?
// Timestamps
createdAt DateTime @default(now())
@@unique([leagueId, name])
@@index([leagueId])
@@index([captainId])
@@map("league_teams")
}
// Modelo de Miembro de Equipo
model LeagueTeamMember {
id String @id @default(uuid())
// Equipo
team LeagueTeam @relation(fields: [teamId], references: [id], onDelete: Cascade)
teamId String
// Usuario
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Estado
isActive Boolean @default(true)
// Fecha de unión
joinedAt DateTime @default(now())
// Timestamps
updatedAt DateTime @updatedAt
@@unique([teamId, userId])
@@index([teamId])
@@index([userId])
@@map("league_team_members")
}
// Modelo de Partido de Liga
model LeagueMatch {
id String @id @default(uuid())
// Liga
league League @relation(fields: [leagueId], references: [id], onDelete: Cascade)
leagueId String
// Jornada
matchday Int // Número de jornada
// Equipos
team1 LeagueTeam @relation("Team1Matches", fields: [team1Id], references: [id])
team1Id String
team2 LeagueTeam @relation("Team2Matches", fields: [team2Id], references: [id])
team2Id String
// Cancha y horario (opcional)
court Court? @relation(fields: [courtId], references: [id], onDelete: SetNull)
courtId String?
scheduledDate DateTime? // Fecha programada
scheduledTime String? // Hora programada (formato HH:mm)
// Estado
status String @default("SCHEDULED") // SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED, POSTPONED, WALKOVER
// Resultado
team1Score Int? // Sets ganados por equipo 1
team2Score Int? // Sets ganados por equipo 2
// Detalle de sets (almacenado como JSON)
// Ej: [{team1Games: 6, team2Games: 4}, {team1Games: 6, team2Games: 2}]
setDetails String?
// Ganador
winner String? // TEAM1, TEAM2, DRAW
// Fecha de finalización
completedAt DateTime?
// Notas
notes String?
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([leagueId])
@@index([matchday])
@@index([team1Id])
@@index([team2Id])
@@index([status])
@@index([scheduledDate])
@@map("league_matches")
}
// Modelo de Clasificación
model LeagueStanding {
id String @id @default(uuid())
// Liga
league League @relation(fields: [leagueId], references: [id], onDelete: Cascade)
leagueId String
// Equipo
team LeagueTeam @relation(fields: [teamId], references: [id], onDelete: Cascade)
teamId String @unique
// Partidos
matchesPlayed Int @default(0)
matchesWon Int @default(0)
matchesLost Int @default(0)
matchesDrawn Int @default(0)
// Sets
setsFor Int @default(0)
setsAgainst Int @default(0)
// Games (opcional)
gamesFor Int @default(0)
gamesAgainst Int @default(0)
// Puntos
points Int @default(0)
// Posición actual
position Int @default(0)
// Timestamps
updatedAt DateTime @updatedAt
@@unique([leagueId, teamId])
@@index([leagueId])
@@index([position])
@@index([points])
@@map("league_standings")
}
// ============================================
// Modelo de Pagos (Fase 4.1)
// ============================================
model Payment {
id String @id @default(uuid())
// Usuario que realiza el pago
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Tipo de pago: BOOKING, TOURNAMENT, BONUS, SUBSCRIPTION, CLASS
type String
// ID de la entidad relacionada (booking, tournament, etc.)
referenceId String
// Monto en centavos (para evitar decimales)
amount Int
// Moneda (ARS, MXN, etc.)
currency String @default("ARS")
// Proveedor de pago
provider String @default("MERCADOPAGO")
// IDs de MercadoPago
providerPaymentId String? // ID del pago en MP (cuando se confirma)
providerPreferenceId String @unique // ID de la preferencia MP
// Estado del pago
status String @default("PENDING") // PENDING, PROCESSING, COMPLETED, FAILED, REFUNDED, CANCELLED
// Información del método de pago
paymentMethod String?
installments Int? // Cantidad de cuotas
// Metadata adicional (JSON)
metadata String?
// Fechas
paidAt DateTime?
refundedAt DateTime?
refundAmount Int? // Monto reembolsado en centavos
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([status])
@@index([type, referenceId])
@@index([providerPaymentId])
@@index([providerPreferenceId])
@@index([createdAt])
@@map("payments")
}
// ============================================
// Modelos de Sistema de Bonos (Fase 4.2)
// ============================================
// Modelo de Pack de Bonos (tipos de bonos disponibles)
model BonusPack {
id String @id @default(uuid())
name String
description String?
// Configuración del bono
numberOfBookings Int // Cantidad de reservas incluidas
price Int // Precio del bono en centavos
validityDays Int // Días de validez desde la compra
// Estado
isActive Boolean @default(true)
// Relaciones
userBonuses UserBonus[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([isActive])
@@map("bonus_packs")
}
// Modelo de Bono de Usuario (bonos comprados)
model UserBonus {
id String @id @default(uuid())
// Relaciones
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
bonusPack BonusPack @relation(fields: [bonusPackId], references: [id])
bonusPackId String
// Uso del bono
totalBookings Int
usedBookings Int @default(0)
remainingBookings Int
// Fechas
purchaseDate DateTime
expirationDate DateTime
// Estado: ACTIVE, EXPIRED, DEPLETED
status String @default("ACTIVE")
// Referencia al pago
paymentId String?
// Relaciones
usages BonusUsage[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([status])
@@index([expirationDate])
@@index([userId, status])
@@map("user_bonuses")
}
// Modelo de Uso de Bono (registro de usos)
model BonusUsage {
id String @id @default(uuid())
// Relaciones
userBonus UserBonus @relation(fields: [userBonusId], references: [id], onDelete: Cascade)
userBonusId String
// Reserva asociada
booking Booking @relation(fields: [bookingId], references: [id])
bookingId String
// Fecha de uso
usedAt DateTime @default(now())
@@unique([bookingId])
@@index([userBonusId])
@@index([usedAt])
@@map("bonus_usages")
}
// ============================================
// Modelos de Sistema de Suscripciones (Fase 4.3)
// ============================================
// Modelo de Plan de Suscripción (planes disponibles)
model SubscriptionPlan {
id String @id @default(uuid())
name String
description String?
// Tipo de plan
type String // MONTHLY, QUARTERLY, YEARLY
// Precio en centavos
price Int
// Características (JSON array de strings)
features String? // Ej: ["Reservas ilimitadas", "Prioridad en reservas"]
// Beneficios del plan (almacenados como JSON)
// discountPercentage: porcentaje de descuento en reservas
// freeBookingsPerMonth: cantidad de reservas gratis por mes
// priorityBooking: prioridad en reservas
// tournamentDiscount: descuento en torneos
benefits String // JSON: { discountPercentage, freeBookingsPerMonth, priorityBooking, tournamentDiscount }
// ID del plan en MercadoPago
mercadoPagoPlanId String?
// Estado
isActive Boolean @default(true)
// Relaciones
subscriptions UserSubscription[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([type])
@@index([isActive])
@@map("subscription_plans")
}
// Modelo de Suscripción de Usuario
model UserSubscription {
id String @id @default(uuid())
// Usuario
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Plan
plan SubscriptionPlan @relation(fields: [planId], references: [id])
planId String
// Estado: PENDING, ACTIVE, PAUSED, CANCELLED, EXPIRED
status String @default("PENDING")
// Fechas de suscripción
startDate DateTime?
endDate DateTime?
// Período actual
currentPeriodStart DateTime?
currentPeriodEnd DateTime?
// Cancelar al final del período
cancelAtPeriodEnd Boolean @default(false)
// Referencia a MercadoPago
mercadoPagoSubscriptionId String?
// Método de pago vinculado
paymentMethodId String?
// Fechas de pagos
lastPaymentDate DateTime?
nextPaymentDate DateTime?
// Contador de reservas gratis usadas en el período actual
freeBookingsUsed Int @default(0)
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([userId, status])
@@index([userId])
@@index([planId])
@@index([status])
@@index([mercadoPagoSubscriptionId])
@@map("user_subscriptions")
}
// ============================================
// Modelos de Clases con Profesores (Fase 4.4)
// ============================================
// Modelo de Profesor (Coach)
model Coach {
id String @id @default(uuid())
// Relación con usuario
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String @unique
// Perfil profesional
bio String?
specialties String? // JSON array de especialidades
certifications String? // JSON array de certificaciones
yearsExperience Int @default(0)
hourlyRate Int @default(0) // en centavos
photoUrl String?
// Estado
isActive Boolean @default(true)
isVerified Boolean @default(false)
// Calificaciones
rating Float?
reviewCount Int @default(0)
// Relaciones
availabilities CoachAvailability[]
classes Class[]
classBookings ClassBooking[]
coachReviews CoachReview[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([isActive])
@@index([isVerified])
@@index([userId])
@@map("coaches")
}
// Modelo de Disponibilidad del Coach
model CoachAvailability {
id String @id @default(uuid())
// Relación con coach
coach Coach @relation(fields: [coachId], references: [id], onDelete: Cascade)
coachId String
// Día de la semana (0=Domingo, 1=Lunes, ..., 6=Sábado)
dayOfWeek Int
// Horario
startTime String
endTime String
// Estado
isAvailable Boolean @default(true)
@@index([coachId])
@@index([coachId, dayOfWeek])
@@index([dayOfWeek])
@@map("coach_availabilities")
}
// Modelo de Clase (programa/tipo de clase)
model Class {
id String @id @default(uuid())
// Relación con coach
coach Coach @relation(fields: [coachId], references: [id], onDelete: Cascade)
coachId String
// Información de la clase
title String
description String?
// Tipo: INDIVIDUAL, GROUP, CLINIC
type String @default("INDIVIDUAL")
// Configuración
maxStudents Int @default(1) // Máximo de alumnos
price Int @default(0) // Precio por persona en centavos
duration Int @default(60) // Duración en minutos
// Nivel mínimo requerido
levelRequired String?
// Estado
isActive Boolean @default(true)
// Relaciones
sessions ClassBooking[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([coachId])
@@index([type])
@@index([isActive])
@@map("classes")
}
// Modelo de Sesión de Clase (instancia específica de una clase)
model ClassBooking {
id String @id @default(uuid())
// Relaciones
class Class @relation(fields: [classId], references: [id], onDelete: Cascade)
classId String
coach Coach @relation(fields: [coachId], references: [id])
coachId String
court Court? @relation(fields: [courtId], references: [id], onDelete: SetNull)
courtId String?
// Fecha y hora
date DateTime
startTime String
// Estudiantes (JSON array de userIds)
students String @default("[]")
// Cupo
maxStudents Int @default(1)
enrolledStudents Int @default(0)
// Estado: AVAILABLE, FULL, COMPLETED, CANCELLED
status String @default("AVAILABLE")
// Precio
price Int @default(0)
// Pago
paymentId String?
// Relaciones
enrollments StudentEnrollment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([classId])
@@index([coachId])
@@index([courtId])
@@index([date])
@@index([status])
@@map("class_bookings")
}
// Modelo de Inscripción de Estudiante
model StudentEnrollment {
id String @id @default(uuid())
// Relaciones
classBooking ClassBooking @relation(fields: [classBookingId], references: [id], onDelete: Cascade)
classBookingId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Referencia al pago
paymentId String?
// Estado: PENDING, CONFIRMED, CANCELLED, ATTENDED
status String @default("PENDING")
// Timestamps
enrolledAt DateTime @default(now())
cancelledAt DateTime?
@@unique([classBookingId, userId])
@@index([userId])
@@index([status])
@@index([classBookingId])
@@map("student_enrollments")
}
// Modelo de Reseña de Coach
model CoachReview {
id String @id @default(uuid())
// Relaciones
coach Coach @relation(fields: [coachId], references: [id], onDelete: Cascade)
coachId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
// Calificación (1-5)
rating Int
comment String?
createdAt DateTime @default(now())
@@unique([coachId, userId])
@@index([coachId])
@@index([userId])
@@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")
}
// ============================================
// Modelos de Sistema de Feedback Beta (Fase 7.2)
// ============================================
// Modelo de Beta Tester
model BetaTester {
id String @id @default(uuid())
// Usuario
userId String @unique
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Fecha de registro como tester
joinedAt DateTime @default(now())
// Contador de feedback enviado
feedbackCount Int @default(0)
// Estado: ACTIVE, INACTIVE
status String @default("ACTIVE")
// Plataforma: WEB, IOS, ANDROID
platform String @default("WEB")
// Versión de la app
appVersion String?
// Relaciones
feedbacks Feedback[]
// Timestamps
updatedAt DateTime @updatedAt
@@index([userId])
@@index([status])
@@index([platform])
@@map("beta_testers")
}
// Modelo de Feedback
model Feedback {
id String @id @default(uuid())
// Usuario que envía el feedback
userId String
// Tipo: BUG, FEATURE, IMPROVEMENT, OTHER
type String
// Categoría: UI, PERFORMANCE, BOOKING, PAYMENT, etc.
category String
// Título y descripción
title String
description String
// Severidad: LOW, MEDIUM, HIGH, CRITICAL
severity String @default("LOW")
// Estado: PENDING, IN_PROGRESS, RESOLVED, CLOSED
status String @default("PENDING")
// URLs de screenshots (JSON array)
screenshots String? // JSON array de URLs
// Información del dispositivo (JSON)
deviceInfo String? // JSON con device info
// Referencia a issue relacionada
betaIssueId String?
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resolvedAt DateTime?
resolvedBy String?
// Relaciones
betaTester BetaTester? @relation(fields: [userId], references: [userId])
betaIssue BetaIssue? @relation(fields: [betaIssueId], references: [id])
@@index([userId])
@@index([type])
@@index([category])
@@index([status])
@@index([severity])
@@index([betaIssueId])
@@index([createdAt])
@@map("feedbacks")
}
// Modelo de Issue Beta (para tracking de bugs/features)
model BetaIssue {
id String @id @default(uuid())
// Título y descripción
title String
description String
// Estado: OPEN, IN_PROGRESS, FIXED, WONT_FIX
status String @default("OPEN")
// Prioridad: LOW, MEDIUM, HIGH, CRITICAL
priority String @default("MEDIUM")
// Asignado a (userId)
assignedTo String?
// IDs de feedback relacionados (JSON array)
relatedFeedbackIds String @default("[]")
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
resolvedAt DateTime?
// Relaciones
feedbacks Feedback[]
@@index([status])
@@index([priority])
@@index([assignedTo])
@@map("beta_issues")
}
// ============================================
// Modelos de Monitoreo y Logging (Fase 7.4)
// ============================================
// Modelo de Log del Sistema
model SystemLog {
id String @id @default(uuid())
// Nivel del log: INFO, WARN, ERROR, CRITICAL
level String @default("INFO")
// Servicio que generó el log
service String // api, database, redis, email, payment, etc.
// Mensaje
message String
// Metadata adicional (JSON)
metadata String? // JSON con datos adicionales
// Usuario relacionado (opcional)
userId String?
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
// Información de la petición
requestId String?
ipAddress String?
userAgent String?
// Timestamps
createdAt DateTime @default(now())
// Resolución (para logs de error)
resolvedAt DateTime?
resolvedBy String?
@@index([level])
@@index([service])
@@index([userId])
@@index([createdAt])
@@index([level, createdAt])
@@index([service, createdAt])
@@map("system_logs")
}
// Modelo de Health Check
model HealthCheck {
id String @id @default(uuid())
// Estado: HEALTHY, DEGRADED, UNHEALTHY
status String @default("HEALTHY")
// Servicio verificado: api, db, redis, email, payment, etc.
service String
// Tiempo de respuesta en ms
responseTime Int
// Timestamp de verificación
checkedAt DateTime @default(now())
// Mensaje de error (si aplica)
errorMessage String?
// Metadata adicional (JSON)
metadata String? // JSON con datos adicionales
@@index([status])
@@index([service])
@@index([checkedAt])
@@index([service, checkedAt])
@@index([status, checkedAt])
@@map("health_checks")
}
// Modelo de Configuración del Sistema
model SystemConfig {
id String @id @default(uuid())
// Clave de configuración
key String @unique
// Valor (JSON string)
value String
// Descripción
description String?
// Categoría
category String @default("GENERAL") // GENERAL, SECURITY, MAINTENANCE, NOTIFICATIONS
// Estado
isActive Boolean @default(true)
// Quién modificó
updatedBy String?
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([category])
@@index([isActive])
@@index([key, isActive])
@@map("system_configs")
}