docs: add Padel Pro system design document
Complete design specification including: - Multi-site architecture for padel club management - Booking system with client app and admin panel - Point of sale (POS) with inventory - Tournaments and leagues module - Membership plans with benefits - Full database schema (Prisma) - UI/UX guidelines and color palette - Project structure (monorepo with Turborepo) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
560
docs/plans/2026-02-01-padel-pro-design.md
Normal file
560
docs/plans/2026-02-01-padel-pro-design.md
Normal file
@@ -0,0 +1,560 @@
|
|||||||
|
# Padel Pro - Documento de Diseño
|
||||||
|
|
||||||
|
**Fecha:** 2026-02-01
|
||||||
|
**Estado:** Aprobado
|
||||||
|
**Versión:** 1.0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resumen Ejecutivo
|
||||||
|
|
||||||
|
Sistema integral de gestión para cadena de clubes de pádel con múltiples sedes. Incluye gestión de reservas, punto de venta, torneos/ligas y membresías.
|
||||||
|
|
||||||
|
### Alcance
|
||||||
|
|
||||||
|
| Aspecto | Decisión |
|
||||||
|
|---------|----------|
|
||||||
|
| **Tipo de usuario** | Dueño de club/canchas |
|
||||||
|
| **Escala** | Múltiples sedes |
|
||||||
|
| **Modelo de negocio** | Mixto (reservas por hora + membresías) |
|
||||||
|
| **Funcionalidades** | Reservas, Torneos/Ligas, Punto de Venta, Membresías |
|
||||||
|
| **Pagos** | Efectivo, transferencias, terminal física |
|
||||||
|
| **Plataformas** | Web (admin) + App móvil (clientes) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Arquitectura
|
||||||
|
|
||||||
|
### Enfoque: Monolito Moderno
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ Next.js (Web + API) │
|
||||||
|
│ ┌─────────┐ ┌─────────┐ ┌───────────┐ │
|
||||||
|
│ │ Admin │ │ API │ │ Auth │ │
|
||||||
|
│ │ Panel │ │ Routes │ │ (NextAuth)│ │
|
||||||
|
│ └─────────┘ └─────────┘ └───────────┘ │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌─────────────────┐ ┌───────────────────┐
|
||||||
|
│ PostgreSQL │ │ React Native App │
|
||||||
|
│ (Prisma ORM) │ │ (Clientes) │
|
||||||
|
└─────────────────┘ └───────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Stack Técnico
|
||||||
|
|
||||||
|
| Capa | Tecnología |
|
||||||
|
|------|------------|
|
||||||
|
| **Frontend Web** | Next.js 14, TypeScript, Tailwind CSS, shadcn/ui, React Query |
|
||||||
|
| **Backend/API** | Next.js API Routes, Prisma ORM, NextAuth.js, Zod |
|
||||||
|
| **Base de datos** | PostgreSQL |
|
||||||
|
| **Mobile** | React Native, Expo, TypeScript, NativeWind |
|
||||||
|
| **Infraestructura** | Vercel/VPS, Railway/Supabase (DB), Cloudinary |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Módulos del Sistema
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────────────────────────┐
|
||||||
|
│ PADEL PRO - MÓDULOS │
|
||||||
|
├──────────────┬──────────────┬──────────────┬───────────────┤
|
||||||
|
│ RESERVAS │ TORNEOS │ POS │ MEMBRESÍAS │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ • Calendario │ • Crear │ • Productos │ • Planes │
|
||||||
|
│ • Disponib. │ torneos │ • Ventas │ • Beneficios │
|
||||||
|
│ • Booking │ • Brackets │ • Caja │ • Renovación │
|
||||||
|
│ • Bloqueos │ • Rankings │ • Cortes │ • Historial │
|
||||||
|
└──────────────┴──────────────┴──────────────┴───────────────┘
|
||||||
|
│
|
||||||
|
┌───────────────┴───────────────┐
|
||||||
|
│ CORE (Base) │
|
||||||
|
│ • Multi-sede │
|
||||||
|
│ • Usuarios y roles │
|
||||||
|
│ • Clientes │
|
||||||
|
│ • Pagos y caja │
|
||||||
|
│ • Reportes │
|
||||||
|
└───────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Roles de Usuario
|
||||||
|
|
||||||
|
| Rol | Permisos |
|
||||||
|
|-----|----------|
|
||||||
|
| **Super Admin** | Todas las sedes, reportes consolidados, configuración global |
|
||||||
|
| **Admin de Sede** | Gestiona una sede específica, reservas, caja y personal |
|
||||||
|
| **Recepcionista** | Opera reservas, cobra, registra pagos, atiende clientes |
|
||||||
|
| **Cliente (App)** | Reserva canchas, ve partidos, inscribe a torneos |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Modelo de Datos
|
||||||
|
|
||||||
|
### Entidades Core
|
||||||
|
|
||||||
|
```prisma
|
||||||
|
// Organización y Sedes
|
||||||
|
model Organization {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
logo String?
|
||||||
|
sites Site[]
|
||||||
|
users User[]
|
||||||
|
clients Client[]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model Site {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
organizationId String
|
||||||
|
organization Organization @relation(fields: [organizationId], references: [id])
|
||||||
|
name String
|
||||||
|
address String
|
||||||
|
phone String?
|
||||||
|
openTime String // "08:00"
|
||||||
|
closeTime String // "22:00"
|
||||||
|
courts Court[]
|
||||||
|
users User[]
|
||||||
|
products Product[]
|
||||||
|
tournaments Tournament[]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model Court {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
siteId String
|
||||||
|
site Site @relation(fields: [siteId], references: [id])
|
||||||
|
name String
|
||||||
|
type CourtType @default(DOUBLES)
|
||||||
|
pricePerHour Decimal
|
||||||
|
premiumPrice Decimal? // Precio horario premium
|
||||||
|
status CourtStatus @default(ACTIVE)
|
||||||
|
bookings Booking[]
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CourtType {
|
||||||
|
SINGLES
|
||||||
|
DOUBLES
|
||||||
|
MIXED
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CourtStatus {
|
||||||
|
ACTIVE
|
||||||
|
MAINTENANCE
|
||||||
|
INACTIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usuarios y Clientes
|
||||||
|
model User {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
organizationId String
|
||||||
|
organization Organization @relation(fields: [organizationId], references: [id])
|
||||||
|
siteId String?
|
||||||
|
site Site? @relation(fields: [siteId], references: [id])
|
||||||
|
email String @unique
|
||||||
|
password String
|
||||||
|
name String
|
||||||
|
phone String?
|
||||||
|
role UserRole
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UserRole {
|
||||||
|
SUPER_ADMIN
|
||||||
|
SITE_ADMIN
|
||||||
|
RECEPTIONIST
|
||||||
|
}
|
||||||
|
|
||||||
|
model Client {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
organizationId String
|
||||||
|
organization Organization @relation(fields: [organizationId], references: [id])
|
||||||
|
email String @unique
|
||||||
|
password String
|
||||||
|
name String
|
||||||
|
phone String?
|
||||||
|
photo String?
|
||||||
|
balance Decimal @default(0)
|
||||||
|
membership Membership?
|
||||||
|
bookings Booking[]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reservas
|
||||||
|
model Booking {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
courtId String
|
||||||
|
court Court @relation(fields: [courtId], references: [id])
|
||||||
|
clientId String
|
||||||
|
client Client @relation(fields: [clientId], references: [id])
|
||||||
|
date DateTime
|
||||||
|
startTime String // "10:00"
|
||||||
|
endTime String // "11:00"
|
||||||
|
price Decimal
|
||||||
|
status BookingStatus @default(PENDING)
|
||||||
|
paymentType PaymentType?
|
||||||
|
isPaid Boolean @default(false)
|
||||||
|
notes String?
|
||||||
|
payments Payment[]
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
createdBy String? // User ID si fue creada por staff
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BookingStatus {
|
||||||
|
PENDING
|
||||||
|
CONFIRMED
|
||||||
|
CANCELLED
|
||||||
|
COMPLETED
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PaymentType {
|
||||||
|
CASH
|
||||||
|
TRANSFER
|
||||||
|
CARD_TERMINAL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagos
|
||||||
|
model Payment {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
amount Decimal
|
||||||
|
method PaymentType
|
||||||
|
reference String?
|
||||||
|
bookingId String?
|
||||||
|
booking Booking? @relation(fields: [bookingId], references: [id])
|
||||||
|
saleId String?
|
||||||
|
sale Sale? @relation(fields: [saleId], references: [id])
|
||||||
|
createdBy String // User ID
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Membresías
|
||||||
|
model MembershipPlan {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
organizationId String
|
||||||
|
name String
|
||||||
|
price Decimal
|
||||||
|
freeHours Int
|
||||||
|
bookingDiscount Int // Porcentaje
|
||||||
|
storeDiscount Int // Porcentaje
|
||||||
|
extraBenefits String?
|
||||||
|
memberships Membership[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Membership {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
clientId String @unique
|
||||||
|
client Client @relation(fields: [clientId], references: [id])
|
||||||
|
planId String
|
||||||
|
plan MembershipPlan @relation(fields: [planId], references: [id])
|
||||||
|
startDate DateTime
|
||||||
|
endDate DateTime
|
||||||
|
hoursUsed Int @default(0)
|
||||||
|
status MembershipStatus @default(ACTIVE)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MembershipStatus {
|
||||||
|
ACTIVE
|
||||||
|
EXPIRED
|
||||||
|
CANCELLED
|
||||||
|
}
|
||||||
|
|
||||||
|
// Punto de Venta
|
||||||
|
model ProductCategory {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
products Product[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Product {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
siteId String
|
||||||
|
site Site @relation(fields: [siteId], references: [id])
|
||||||
|
categoryId String
|
||||||
|
category ProductCategory @relation(fields: [categoryId], references: [id])
|
||||||
|
name String
|
||||||
|
price Decimal
|
||||||
|
stock Int @default(0)
|
||||||
|
minStock Int @default(5)
|
||||||
|
saleItems SaleItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Sale {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
siteId String
|
||||||
|
items SaleItem[]
|
||||||
|
total Decimal
|
||||||
|
payments Payment[]
|
||||||
|
createdBy String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model SaleItem {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
saleId String
|
||||||
|
sale Sale @relation(fields: [saleId], references: [id])
|
||||||
|
productId String
|
||||||
|
product Product @relation(fields: [productId], references: [id])
|
||||||
|
quantity Int
|
||||||
|
price Decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control de Caja
|
||||||
|
model CashRegister {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
siteId String
|
||||||
|
openedBy String
|
||||||
|
closedBy String?
|
||||||
|
openingAmount Decimal
|
||||||
|
closingAmount Decimal?
|
||||||
|
expectedAmount Decimal?
|
||||||
|
openedAt DateTime @default(now())
|
||||||
|
closedAt DateTime?
|
||||||
|
status CashRegisterStatus @default(OPEN)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CashRegisterStatus {
|
||||||
|
OPEN
|
||||||
|
CLOSED
|
||||||
|
}
|
||||||
|
|
||||||
|
// Torneos
|
||||||
|
model Tournament {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
siteId String
|
||||||
|
site Site @relation(fields: [siteId], references: [id])
|
||||||
|
name String
|
||||||
|
description String?
|
||||||
|
date DateTime
|
||||||
|
endDate DateTime?
|
||||||
|
type TournamentType
|
||||||
|
category String? // A, B, C o null si no aplica
|
||||||
|
maxTeams Int
|
||||||
|
price Decimal
|
||||||
|
status TournamentStatus @default(DRAFT)
|
||||||
|
inscriptions TournamentInscription[]
|
||||||
|
matches Match[]
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TournamentType {
|
||||||
|
SINGLE_ELIMINATION
|
||||||
|
DOUBLE_ELIMINATION
|
||||||
|
ROUND_ROBIN
|
||||||
|
LEAGUE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TournamentStatus {
|
||||||
|
DRAFT
|
||||||
|
OPEN
|
||||||
|
IN_PROGRESS
|
||||||
|
FINISHED
|
||||||
|
CANCELLED
|
||||||
|
}
|
||||||
|
|
||||||
|
model TournamentInscription {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
tournamentId String
|
||||||
|
tournament Tournament @relation(fields: [tournamentId], references: [id])
|
||||||
|
player1Id String
|
||||||
|
player2Id String? // Para parejas
|
||||||
|
teamName String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
|
||||||
|
model Match {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
tournamentId String
|
||||||
|
tournament Tournament @relation(fields: [tournamentId], references: [id])
|
||||||
|
round Int
|
||||||
|
position Int
|
||||||
|
team1Id String?
|
||||||
|
team2Id String?
|
||||||
|
score1 String?
|
||||||
|
score2 String?
|
||||||
|
winnerId String?
|
||||||
|
courtId String?
|
||||||
|
scheduledAt DateTime?
|
||||||
|
status MatchStatus @default(PENDING)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MatchStatus {
|
||||||
|
PENDING
|
||||||
|
IN_PROGRESS
|
||||||
|
FINISHED
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Flujos Principales
|
||||||
|
|
||||||
|
### Flujo de Reserva - Cliente (App)
|
||||||
|
|
||||||
|
1. Cliente abre app y selecciona sede
|
||||||
|
2. Elige fecha en calendario
|
||||||
|
3. Ve horarios disponibles con precios
|
||||||
|
4. Selecciona horario y confirma
|
||||||
|
5. Reserva queda como PENDIENTE
|
||||||
|
6. Al llegar, staff cobra y marca como PAGADA
|
||||||
|
|
||||||
|
### Flujo de Reserva - Recepcionista (Web)
|
||||||
|
|
||||||
|
1. Ve calendario visual de canchas
|
||||||
|
2. Click en slot disponible
|
||||||
|
3. Busca cliente existente o crea nuevo
|
||||||
|
4. Si cliente presente: cobra y confirma
|
||||||
|
5. Si no presente: reserva pendiente
|
||||||
|
|
||||||
|
### Flujo de Venta POS
|
||||||
|
|
||||||
|
1. Recepcionista abre pantalla de caja
|
||||||
|
2. Agrega productos al ticket
|
||||||
|
3. Opcionalmente agrega reserva al mismo ticket
|
||||||
|
4. Selecciona método de pago
|
||||||
|
5. Registra pago y cierra venta
|
||||||
|
|
||||||
|
### Flujo de Torneo
|
||||||
|
|
||||||
|
1. Admin crea torneo (nombre, fecha, categoría, precio)
|
||||||
|
2. Abre inscripciones
|
||||||
|
3. Clientes se inscriben desde app
|
||||||
|
4. Admin cierra inscripciones y genera bracket
|
||||||
|
5. Asigna horarios y canchas
|
||||||
|
6. Registra resultados de partidos
|
||||||
|
7. Sistema avanza bracket automáticamente
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Diseño Visual
|
||||||
|
|
||||||
|
### Paleta de Colores
|
||||||
|
|
||||||
|
| Uso | Color | Hex |
|
||||||
|
|-----|-------|-----|
|
||||||
|
| Primario | Azul profundo | #1E3A5F |
|
||||||
|
| Acento | Verde pádel | #22C55E |
|
||||||
|
| Fondo | Blanco/Gris claro | #F8FAFC |
|
||||||
|
| Texto | Gris oscuro | #1E293B |
|
||||||
|
| Éxito | Verde | #22C55E |
|
||||||
|
| Alerta | Ámbar | #F59E0B |
|
||||||
|
| Error | Rojo suave | #EF4444 |
|
||||||
|
|
||||||
|
### Principios de Diseño
|
||||||
|
|
||||||
|
1. **Minimalista**: Solo información esencial en pantalla
|
||||||
|
2. **Touch-friendly**: Botones grandes en móvil (mínimo 44px)
|
||||||
|
3. **Feedback visual**: Confirmaciones claras, estados visibles
|
||||||
|
4. **Responsive**: Admin funciona en desktop y tablet
|
||||||
|
5. **Modo oscuro**: Opcional para ambas plataformas
|
||||||
|
6. **Consistencia**: Mismos patrones en web y móvil
|
||||||
|
|
||||||
|
### Componentes UI (shadcn/ui)
|
||||||
|
|
||||||
|
- Calendario de reservas (vista día/semana)
|
||||||
|
- Cards para estadísticas
|
||||||
|
- Tablas con filtros y búsqueda
|
||||||
|
- Modales para formularios
|
||||||
|
- Toast notifications
|
||||||
|
- Sidebar navegación colapsable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Estructura del Proyecto
|
||||||
|
|
||||||
|
```
|
||||||
|
padel-pro/
|
||||||
|
├── apps/
|
||||||
|
│ ├── web/ # Next.js (Admin + API)
|
||||||
|
│ │ ├── app/
|
||||||
|
│ │ │ ├── (admin)/ # Panel administrativo
|
||||||
|
│ │ │ │ ├── dashboard/
|
||||||
|
│ │ │ │ ├── bookings/
|
||||||
|
│ │ │ │ ├── pos/
|
||||||
|
│ │ │ │ ├── tournaments/
|
||||||
|
│ │ │ │ ├── clients/
|
||||||
|
│ │ │ │ ├── memberships/
|
||||||
|
│ │ │ │ ├── reports/
|
||||||
|
│ │ │ │ └── settings/
|
||||||
|
│ │ │ ├── (auth)/
|
||||||
|
│ │ │ │ ├── login/
|
||||||
|
│ │ │ │ └── forgot-password/
|
||||||
|
│ │ │ └── api/
|
||||||
|
│ │ │ ├── auth/
|
||||||
|
│ │ │ ├── bookings/
|
||||||
|
│ │ │ ├── clients/
|
||||||
|
│ │ │ ├── courts/
|
||||||
|
│ │ │ ├── products/
|
||||||
|
│ │ │ ├── sales/
|
||||||
|
│ │ │ ├── tournaments/
|
||||||
|
│ │ │ └── reports/
|
||||||
|
│ │ ├── components/
|
||||||
|
│ │ │ ├── ui/ # shadcn components
|
||||||
|
│ │ │ ├── booking/
|
||||||
|
│ │ │ ├── pos/
|
||||||
|
│ │ │ └── layout/
|
||||||
|
│ │ └── lib/
|
||||||
|
│ │ ├── db.ts
|
||||||
|
│ │ ├── auth.ts
|
||||||
|
│ │ └── utils.ts
|
||||||
|
│ │
|
||||||
|
│ └── mobile/ # React Native (Clientes)
|
||||||
|
│ ├── app/ # Expo Router
|
||||||
|
│ │ ├── (tabs)/
|
||||||
|
│ │ │ ├── index.tsx # Home/Reservar
|
||||||
|
│ │ │ ├── bookings.tsx # Mis reservas
|
||||||
|
│ │ │ ├── tournaments.tsx
|
||||||
|
│ │ │ └── profile.tsx
|
||||||
|
│ │ ├── (auth)/
|
||||||
|
│ │ └── booking/[id].tsx
|
||||||
|
│ ├── components/
|
||||||
|
│ └── services/
|
||||||
|
│ └── api.ts
|
||||||
|
│
|
||||||
|
├── packages/
|
||||||
|
│ └── shared/
|
||||||
|
│ ├── types/
|
||||||
|
│ └── validations/
|
||||||
|
│
|
||||||
|
├── prisma/
|
||||||
|
│ ├── schema.prisma
|
||||||
|
│ └── seed.ts
|
||||||
|
│
|
||||||
|
├── docs/
|
||||||
|
│ └── plans/
|
||||||
|
│
|
||||||
|
├── turbo.json
|
||||||
|
├── package.json
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Consideraciones de Seguridad
|
||||||
|
|
||||||
|
- Autenticación JWT con refresh tokens
|
||||||
|
- Passwords hasheados con bcrypt
|
||||||
|
- Validación de inputs con Zod
|
||||||
|
- Rate limiting en API
|
||||||
|
- CORS configurado correctamente
|
||||||
|
- Variables sensibles en environment
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Próximos Pasos
|
||||||
|
|
||||||
|
1. Inicializar monorepo con Turborepo
|
||||||
|
2. Configurar base de datos y Prisma
|
||||||
|
3. Implementar autenticación
|
||||||
|
4. Desarrollar módulo de reservas (core)
|
||||||
|
5. Agregar POS
|
||||||
|
6. Implementar torneos
|
||||||
|
7. Desarrollar app móvil
|
||||||
|
8. Testing y QA
|
||||||
|
9. Deploy a producción
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Documento generado colaborativamente - Febrero 2026*
|
||||||
Reference in New Issue
Block a user