Files
app-padel/backend/prisma/schema.prisma
Ivan Alcaraz b8a964dc2c FASE 4 COMPLETADA: Pagos y Monetización con MercadoPago
Implementados 4 módulos con agent swarm:

1. MERCADOPAGO INTEGRADO
   - SDK oficial de MercadoPago
   - Crear preferencias de pago
   - Webhooks para notificaciones
   - Reembolsos y cancelaciones
   - Estados: PENDING, PROCESSING, COMPLETED, REFUNDED

2. SISTEMA DE BONOS Y PACKS
   - Pack 5, Pack 10, Pack Mensual
   - Compra online con MP
   - Uso FIFO automático
   - Control de expiración
   - Aplicación en reservas

3. SUSCRIPCIONES/MEMBRESÍAS
   - Planes: Básico, Premium, Anual VIP
   - Beneficios: descuentos, reservas gratis, prioridad
   - Cobro recurrente vía MP
   - Estados: ACTIVE, PAUSED, CANCELLED
   - Aplicación automática en reservas

4. CLASES CON PROFESORES
   - Registro de coaches con verificación
   - Tipos: Individual, Grupal, Clínica
   - Horarios y disponibilidad
   - Reservas con pago integrado
   - Sistema de reseñas

Endpoints nuevos:
- /payments/* - Pagos MercadoPago
- /bonus-packs/*, /bonuses/* - Bonos
- /subscription-plans/*, /subscriptions/* - Suscripciones
- /coaches/* - Profesores
- /classes/*, /class-enrollments/* - Clases

Variables de entorno:
- MERCADOPAGO_ACCESS_TOKEN
- MERCADOPAGO_PUBLIC_KEY
- MERCADOPAGO_WEBHOOK_SECRET

Datos de prueba:
- 3 Bonus Packs
- 3 Planes de suscripción
- 1 Coach verificado (admin)
- 3 Clases disponibles
2026-01-31 09:02:25 +00:00

1224 lines
30 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[]
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[]
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[]
// 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")
}