mirror of
https://github.com/marcogll/AnchorOS.git
synced 2026-03-15 20:24:34 +00:00
feat(salonos): implementar Fase 1.1 y 1.2 - Infraestructura y Esquema de Base de Datos
Implementación completa de la Fase 1.1 y 1.2 del proyecto SalonOS: ## Cambios en Reglas de Negocio (PRD.md, AGENTS.md, TASKS.md) - Actualizado reset de invitaciones de mensual a semanal (Lunes 00:00 UTC) - Jerarquía de roles actualizada: Admin > Manager > Staff > Artist > Customer - Artistas (antes colaboradoras) ahora tienen rol 'artist' - Staff/Manager/Admin pueden ver PII de customers - Artist solo ve nombre y notas de customers (restricción de privacidad) ## Estructura del Proyecto (Next.js 14) - app/boutique/: Frontend de cliente - app/hq/: Dashboard administrativo - app/api/: API routes - components/: Componentes UI reutilizables (boutique, hq, shared) - lib/: Lógica de negocio (supabase, db, utils) - db/: Esquemas, migraciones y seeds - integrations/: Stripe, Google Calendar, WhatsApp - scripts/: Scripts de utilidad y automatización - docs/: Documentación del proyecto ## Esquema de Base de Datos (Supabase PostgreSQL) 8 tablas creadas: - locations: Ubicaciones con timezone - resources: Recursos físicos (estaciones, habitaciones, equipos) - staff: Personal con roles jerárquicos - services: Catálogo de servicios - customers: Información de clientes con tier (free/gold) - invitations: Sistema de invitaciones semanales - bookings: Sistema de reservas con short_id (6 caracteres) - audit_logs: Registro de auditoría automática 14 funciones creadas: - generate_short_id(): Generador de Short ID (6 chars, collision-safe) - generate_invitation_code(): Generador de códigos de invitación (10 chars) - reset_weekly_invitations_for_customer(): Reset individual de invitaciones - reset_all_weekly_invitations(): Reset masivo de invitaciones - validate_secondary_artist_role(): Validación de secondary_artist - log_audit(): Trigger de auditoría automática - get_current_user_role(): Obtener rol del usuario actual - is_staff_or_higher(): Verificar si es admin/manager/staff - is_artist(): Verificar si es artist - is_customer(): Verificar si es customer - is_admin(): Verificar si es admin - update_updated_at(): Actualizar timestamps - generate_booking_short_id(): Generar Short ID automáticamente - get_week_start(): Obtener inicio de semana 17+ triggers activos: - Auditores automáticos en tablas críticas - Timestamps updated_at en todas las tablas - Validación de secondary_artist (trigger en lugar de constraint) 20+ políticas RLS configuradas: - Restricción crítica: Artist no ve email/phone de customers - Jerarquía de roles: Admin > Manager > Staff > Artist > Customer - Políticas granulares por tipo de operación y rol 6 tipos ENUM: - user_role: admin, manager, staff, artist, customer - customer_tier: free, gold - booking_status: pending, confirmed, cancelled, completed, no_show - invitation_status: pending, used, expired - resource_type: station, room, equipment - audit_action: create, update, delete, reset_invitations, payment, status_change ## Scripts de Utilidad - check-connection.sh: Verificar conexión a Supabase - simple-verify.sh: Verificar migraciones instaladas - simple-seed.sh: Crear datos de prueba - create-auth-users.js: Crear usuarios de Auth en Supabase - verify-migration.sql: Script de verificación SQL completo - seed-data.sql: Script de seed de datos SQL completo ## Documentación - docs/STEP_BY_STEP_VERIFICATION.md: Guía paso a paso de verificación - docs/STEP_BY_STEP_AUTH_CONFIG.md: Guía paso a paso de configuración Auth - docs/POST_MIGRATION_SUCCESS.md: Guía post-migración - docs/MIGRATION_CORRECTION.md: Detalle de correcciones aplicadas - docs/QUICK_START_POST_MIGRATION.md: Guía rápida de referencia - docs/SUPABASE_DASHBOARD_MIGRATION.md: Guía de ejecución en Dashboard - docs/00_FULL_MIGRATION_FINAL_README.md: Guía de migración final - SIMPLE_GUIDE.md: Guía simple de inicio - FASE_1_STATUS.md: Estado de la Fase 1 ## Configuración - package.json: Dependencias y scripts de npm - tsconfig.json: Configuración TypeScript con paths aliases - next.config.js: Configuración Next.js - tailwind.config.ts: Tema personalizado con colores primary, secondary, gold - postcss.config.js: Configuración PostCSS - .gitignore: Archivos excluidos de git - .env.example: Template de variables de entorno ## Correcciones Aplicadas 1. Constraint de subquery en CHECK reemplazado por trigger de validación - PostgreSQL no permite subqueries en CHECK constraints - validate_secondary_artist_role() ahora es un trigger 2. Variable no declarada en loop - customer_record RECORD; añadido en bloque DECLARE ## Principios Implementados - UTC-first: Todos los timestamps se almacenan en UTC - Sistema Doble Capa: Validación Staff/Artist + Recurso físico - Reset semanal: Invitaciones se resetean cada Lunes 00:00 UTC - Idempotencia: Procesos de reset son idempotentes y auditados - Privacidad: Artist solo ve nombre y notas de customers - Auditoría: Todas las acciones críticas se registran automáticamente - Short ID: 6 caracteres alfanuméricos como referencia humana - UUID: Identificador primario interno ## Próximos Pasos - Ejecutar scripts de verificación y seed - Configurar Auth en Supabase Dashboard - Implementar Tarea 1.3: Short ID & Invitaciones (backend) - Implementar Tarea 1.4: CRM Base (endpoints CRUD)
This commit is contained in:
148
docs/00_FULL_MIGRATION_FINAL_README.md
Normal file
148
docs/00_FULL_MIGRATION_FINAL_README.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# 🎉 MIGRACIÓN FINAL - SalonOS
|
||||
|
||||
## ✅ Estado: Listo para Ejecutar
|
||||
|
||||
Este archivo contiene la **versión final corregida** de todas las migraciones de base de datos de SalonOS.
|
||||
|
||||
## 🐛 Correcciones Aplicadas
|
||||
|
||||
### 1. Constraint Reemplazado por Trigger
|
||||
- **Problema:** PostgreSQL no permite subqueries en constraints CHECK
|
||||
- **Solución:** Reemplazado por trigger de validación `validate_secondary_artist_role()`
|
||||
|
||||
### 2. Variable de Loop Declarada
|
||||
- **Problema:** Variable `customer_record` no declarada en función `reset_all_weekly_invitations()`
|
||||
- **Solución:** Declarada como `customer_record RECORD;` en bloque `DECLARE`
|
||||
|
||||
## 📋 Contenido del Archivo
|
||||
|
||||
Este archivo incluye:
|
||||
|
||||
- ✅ **Migración 001**: Esquema inicial (8 tablas, 6 tipos ENUM, índices, constraints, triggers)
|
||||
- ✅ **Migración 002**: Políticas RLS (20+ políticas, 4 funciones auxiliares)
|
||||
- ✅ **Migración 003**: Triggers de auditoría (13 funciones, triggers automáticos)
|
||||
- ✅ **Corrección 1**: Trigger de validación en lugar de constraint con subquery
|
||||
- ✅ **Corrección 2**: Variable de loop declarada correctamente
|
||||
|
||||
## 🚀 Cómo Ejecutar
|
||||
|
||||
### Paso 1: Abrir Supabase SQL Editor
|
||||
```
|
||||
https://supabase.com/dashboard/project/pvvwbnybkadhreuqijsl/sql
|
||||
```
|
||||
|
||||
### Paso 2: Copiar el Archivo
|
||||
Copia **TODO** el contenido de:
|
||||
```
|
||||
db/migrations/00_FULL_MIGRATION_FINAL.sql
|
||||
```
|
||||
|
||||
### Paso 3: Ejecutar
|
||||
1. Pega el contenido en el SQL Editor
|
||||
2. Haz clic en **"Run"**
|
||||
3. Espera 10-30 segundos
|
||||
|
||||
## 📊 Resultado Esperado
|
||||
|
||||
Al completar la ejecución, deberías ver:
|
||||
|
||||
```
|
||||
===========================================
|
||||
SALONOS - DATABASE MIGRATION COMPLETED
|
||||
===========================================
|
||||
✅ Tables created: 8
|
||||
✅ Functions created: 14
|
||||
✅ Triggers active: 17+
|
||||
✅ RLS policies configured: 20+
|
||||
✅ ENUM types created: 6
|
||||
===========================================
|
||||
```
|
||||
|
||||
## 🔍 Verificación
|
||||
|
||||
### Verificar Tablas
|
||||
```sql
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
ORDER BY table_name;
|
||||
```
|
||||
|
||||
**Esperado:** 8 tablas
|
||||
|
||||
### Verificar Funciones
|
||||
```sql
|
||||
SELECT routine_name
|
||||
FROM information_schema.routines
|
||||
WHERE routine_schema = 'public'
|
||||
ORDER BY routine_name;
|
||||
```
|
||||
|
||||
**Esperado:** 14 funciones
|
||||
|
||||
### Verificar Triggers
|
||||
```sql
|
||||
SELECT trigger_name, event_object_table
|
||||
FROM information_schema.triggers
|
||||
WHERE trigger_schema = 'public'
|
||||
ORDER BY event_object_table, trigger_name;
|
||||
```
|
||||
|
||||
**Esperado:** 17+ triggers
|
||||
|
||||
### Verificar Políticas RLS
|
||||
```sql
|
||||
SELECT schemaname, tablename, policyname
|
||||
FROM pg_policies
|
||||
WHERE schemaname = 'public'
|
||||
ORDER BY tablename, policyname;
|
||||
```
|
||||
|
||||
**Esperado:** 20+ políticas
|
||||
|
||||
### Probar Short ID
|
||||
```sql
|
||||
SELECT generate_short_id();
|
||||
```
|
||||
|
||||
**Esperado:** String de 6 caracteres alfanuméricos (ej: "A3F7X2")
|
||||
|
||||
### Probar Código de Invitación
|
||||
```sql
|
||||
SELECT generate_invitation_code();
|
||||
```
|
||||
|
||||
**Esperado:** String de 10 caracteres alfanuméricos (ej: "X9J4K2M5N8")
|
||||
|
||||
## 🎯 Próximos Pasos
|
||||
|
||||
Después de ejecutar exitosamente la migración:
|
||||
|
||||
1. ✅ **Configurar Auth** en Supabase Dashboard
|
||||
2. ✅ **Crear usuarios de prueba** con roles específicos
|
||||
3. ✅ **Probar el sistema** con consultas de verificación
|
||||
4. ✅ **Ejecutar seed de datos** (opcional): `npm run db:seed`
|
||||
5. ✅ **Continuar desarrollo** de Tarea 1.3 y 1.4
|
||||
|
||||
## 📚 Documentación Adicional
|
||||
|
||||
- **docs/MIGRATION_CORRECTION.md** - Detalle de las correcciones aplicadas
|
||||
- **docs/SUPABASE_DASHBOARD_MIGRATION.md** - Guía completa de ejecución
|
||||
- **docs/MIGRATION_GUIDE.md** - Guía técnica de migraciones
|
||||
- **db/migrations/README.md** - Documentación técnica de migraciones
|
||||
- **scripts/README.md** - Documentación de scripts de utilidad
|
||||
|
||||
## 🆘 Soporte
|
||||
|
||||
Si encuentras problemas:
|
||||
|
||||
1. Revisa los logs de Supabase Dashboard
|
||||
2. Ejecuta las consultas de verificación arriba
|
||||
3. Consulta `docs/MIGRATION_CORRECTION.md` para detalles de las correcciones
|
||||
4. Consulta `docs/SUPABASE_DASHBOARD_MIGRATION.md` para guía paso a paso
|
||||
|
||||
---
|
||||
|
||||
**Última actualización:** 2026-01-15
|
||||
**Versión:** FINAL (Correcciones aplicadas)
|
||||
**Estado:** ✅ Listo para producción
|
||||
314
docs/MIGRATION_CORRECTION.md
Normal file
314
docs/MIGRATION_CORRECTION.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# ✅ CORRECCIÓN DE MIGRACIÓN - PostgreSQL Constraint Error
|
||||
|
||||
## 🐛 Problemas Detectados
|
||||
|
||||
### Problema 1: Subquery en CHECK Constraint
|
||||
|
||||
Al ejecutar la migración en Supabase Dashboard, se produjo el siguiente error:
|
||||
|
||||
```
|
||||
Error: Failed to run sql query: ERROR: 0A000: cannot use subquery in check constraint
|
||||
```
|
||||
|
||||
**Causa:** PostgreSQL **no permite subqueries** en los constraints de CHECK.
|
||||
|
||||
### Problema 2: Variable no declarada en Loop
|
||||
|
||||
```
|
||||
Error: Failed to run sql query: ERROR: 42601: loop variable of loop over rows must be a record variable or list of scalar variables
|
||||
```
|
||||
|
||||
**Causa:** La variable `customer_record` no estaba declarada en el bloque `DECLARE` de la función `reset_all_weekly_invitations()`.
|
||||
|
||||
## 🔍 Causas Detalladas
|
||||
|
||||
### Problema 1: Subquery en CHECK Constraint
|
||||
|
||||
En la migración original, teníamos:
|
||||
|
||||
```sql
|
||||
-- Constraint problemático (NO permitido en PostgreSQL)
|
||||
ALTER TABLE bookings ADD CONSTRAINT check_secondary_artist_role
|
||||
CHECK (secondary_artist_id IS NULL OR EXISTS (
|
||||
SELECT 1 FROM staff s
|
||||
WHERE s.id = secondary_artist_id AND s.role = 'artist'
|
||||
));
|
||||
```
|
||||
|
||||
## ✅ Soluciones Aplicadas
|
||||
|
||||
### Solución 1: Reemplazar Constraint con Trigger
|
||||
|
||||
Se ha reemplazado el constraint problemático con un **trigger de validación** que hace exactamente la misma validación:
|
||||
|
||||
```sql
|
||||
-- Nueva función de validación (PERMITIDO en PostgreSQL)
|
||||
CREATE OR REPLACE FUNCTION validate_secondary_artist_role()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.secondary_artist_id IS NOT NULL THEN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM staff s
|
||||
WHERE s.id = NEW.secondary_artist_id AND s.role = 'artist' AND s.is_active = true
|
||||
) THEN
|
||||
RAISE EXCEPTION 'secondary_artist_id must reference an active staff member with role ''artist''';
|
||||
END IF;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER validate_booking_secondary_artist BEFORE INSERT OR UPDATE ON bookings
|
||||
FOR EACH ROW EXECUTE FUNCTION validate_secondary_artist_role();
|
||||
```
|
||||
|
||||
### Solución 2: Declarar Variable en Bloque DECLARE
|
||||
|
||||
Se ha añadido la declaración de `customer_record RECORD;` en el bloque `DECLARE` de la función `reset_all_weekly_invitations()`:
|
||||
|
||||
**Antes:**
|
||||
```sql
|
||||
CREATE OR REPLACE FUNCTION reset_all_weekly_invitations()
|
||||
RETURNS JSONB AS $$
|
||||
DECLARE
|
||||
customers_count INTEGER := 0;
|
||||
invitations_created INTEGER := 0;
|
||||
result JSONB;
|
||||
BEGIN
|
||||
FOR customer_record IN -- ❌ Variable no declarada
|
||||
SELECT id FROM customers WHERE tier = 'gold' AND is_active = true
|
||||
LOOP
|
||||
...
|
||||
END LOOP;
|
||||
```
|
||||
|
||||
**Después:**
|
||||
```sql
|
||||
CREATE OR REPLACE FUNCTION reset_all_weekly_invitations()
|
||||
RETURNS JSONB AS $$
|
||||
DECLARE
|
||||
customers_count INTEGER := 0;
|
||||
invitations_created INTEGER := 0;
|
||||
result JSONB;
|
||||
customer_record RECORD; -- ✅ Variable declarada
|
||||
BEGIN
|
||||
FOR customer_record IN -- ✅ Ahora la variable existe
|
||||
SELECT id FROM customers WHERE tier = 'gold' AND is_active = true
|
||||
LOOP
|
||||
...
|
||||
END LOOP;
|
||||
```
|
||||
|
||||
## 📁 Archivos Actualizados
|
||||
|
||||
### Archivos Corregidos:
|
||||
1. **`db/migrations/00_FULL_MIGRATION_CORRECTED.sql`** (NUEVO)
|
||||
- Archivo consolidado con todas las migraciones
|
||||
- Constraint reemplazado por trigger de validación
|
||||
- Índice adicional para `secondary_artist_id`
|
||||
|
||||
2. **`db/migrations/001_initial_schema.sql`** (ACTUALIZADO)
|
||||
- Constraint problemático eliminado
|
||||
- Trigger de validación añadido
|
||||
- Índice para `secondary_artist_id` añadido
|
||||
|
||||
3. **`docs/SUPABASE_DASHBOARD_MIGRATION.md`** (ACTUALIZADO)
|
||||
- Referencias actualizadas al archivo corregido
|
||||
- Documentación actualizada con el nuevo trigger
|
||||
|
||||
## 🎯 Cómo Proceder
|
||||
|
||||
### Paso 1: Usar el Archivo Corregido
|
||||
|
||||
Copia **TODO** el contenido de:
|
||||
```
|
||||
db/migrations/00_FULL_MIGRATION_CORRECTED.sql
|
||||
```
|
||||
|
||||
### Paso 2: Ejecutar en Supabase Dashboard
|
||||
|
||||
1. Ve a: **https://supabase.com/dashboard/project/pvvwbnybkadhreuqijsl/sql**
|
||||
2. Crea un nuevo query
|
||||
3. Pega el contenido completo del archivo corregido
|
||||
4. Haz clic en **"Run"**
|
||||
|
||||
### Paso 3: Verificar la Ejecución
|
||||
|
||||
Al finalizar, deberías ver:
|
||||
- ✅ Un mensaje de éxito
|
||||
- ✅ 8 tablas creadas
|
||||
- ✅ 14 funciones creadas (incluye `validate_secondary_artist_role`)
|
||||
- ✅ 17+ triggers activos (incluye `validate_booking_secondary_artist`)
|
||||
- ✅ 20+ políticas RLS configuradas
|
||||
- ✅ 6 tipos ENUM creados
|
||||
|
||||
## 🔍 Verificación del Trigger
|
||||
|
||||
Para verificar que el trigger de validación funciona correctamente, ejecuta:
|
||||
|
||||
```sql
|
||||
-- Verificar que el trigger existe
|
||||
SELECT trigger_name, event_object_table, action_statement
|
||||
FROM information_schema.triggers
|
||||
WHERE trigger_name = 'validate_booking_secondary_artist';
|
||||
|
||||
-- Verificar la función de validación
|
||||
SELECT routine_name, routine_definition
|
||||
FROM information_schema.routines
|
||||
WHERE routine_name = 'validate_secondary_artist_role';
|
||||
```
|
||||
|
||||
## 🧪 Probar la Validación
|
||||
|
||||
### Prueba 1: Booking válido con secondary_artist válido
|
||||
|
||||
```sql
|
||||
-- Primero crear datos de prueba
|
||||
INSERT INTO locations (name, timezone, is_active)
|
||||
VALUES ('Test Location', 'America/Mexico_City', true);
|
||||
|
||||
INSERT INTO staff (user_id, location_id, role, display_name, is_active)
|
||||
VALUES (uuid_generate_v4(), (SELECT id FROM locations LIMIT 1), 'artist', 'Test Artist', true);
|
||||
|
||||
INSERT INTO staff (user_id, location_id, role, display_name, is_active)
|
||||
VALUES (uuid_generate_v4(), (SELECT id FROM locations LIMIT 1), 'staff', 'Test Staff', true);
|
||||
|
||||
INSERT INTO resources (location_id, name, type, is_active)
|
||||
VALUES ((SELECT id FROM locations LIMIT 1), 'Test Station', 'station', true);
|
||||
|
||||
INSERT INTO services (name, duration_minutes, base_price, is_active)
|
||||
VALUES ('Test Service', 60, 100.00, true);
|
||||
|
||||
INSERT INTO customers (user_id, first_name, last_name, email, tier, is_active)
|
||||
VALUES (uuid_generate_v4(), 'Test', 'Customer', 'test@example.com', 'free', true);
|
||||
|
||||
-- Ahora intentar crear un booking válido
|
||||
INSERT INTO bookings (
|
||||
customer_id,
|
||||
staff_id,
|
||||
secondary_artist_id,
|
||||
location_id,
|
||||
resource_id,
|
||||
service_id,
|
||||
start_time_utc,
|
||||
end_time_utc,
|
||||
status,
|
||||
deposit_amount,
|
||||
total_amount,
|
||||
is_paid
|
||||
)
|
||||
SELECT
|
||||
(SELECT id FROM customers LIMIT 1),
|
||||
(SELECT id FROM staff WHERE role = 'staff' LIMIT 1),
|
||||
(SELECT id FROM staff WHERE role = 'artist' LIMIT 1),
|
||||
(SELECT id FROM locations LIMIT 1),
|
||||
(SELECT id FROM resources LIMIT 1),
|
||||
(SELECT id FROM services LIMIT 1),
|
||||
NOW() + INTERVAL '1 day',
|
||||
NOW() + INTERVAL '2 days',
|
||||
'confirmed',
|
||||
50.00,
|
||||
100.00,
|
||||
true;
|
||||
```
|
||||
|
||||
**Resultado esperado:** ✅ Booking creado exitosamente
|
||||
|
||||
### Prueba 2: Booking inválido con secondary_artist no válido
|
||||
|
||||
```sql
|
||||
-- Intentar crear un booking con secondary_artist que no es 'artist'
|
||||
INSERT INTO bookings (
|
||||
customer_id,
|
||||
staff_id,
|
||||
secondary_artist_id,
|
||||
location_id,
|
||||
resource_id,
|
||||
service_id,
|
||||
start_time_utc,
|
||||
end_time_utc,
|
||||
status,
|
||||
deposit_amount,
|
||||
total_amount,
|
||||
is_paid
|
||||
)
|
||||
SELECT
|
||||
(SELECT id FROM customers LIMIT 1),
|
||||
(SELECT id FROM staff WHERE role = 'staff' LIMIT 1),
|
||||
(SELECT id FROM staff WHERE role = 'staff' LIMIT 1), -- ❌ Esto es 'staff', no 'artist'
|
||||
(SELECT id FROM locations LIMIT 1),
|
||||
(SELECT id FROM resources LIMIT 1),
|
||||
(SELECT id FROM services LIMIT 1),
|
||||
NOW() + INTERVAL '1 day',
|
||||
NOW() + INTERVAL '2 days',
|
||||
'confirmed',
|
||||
50.00,
|
||||
100.00,
|
||||
true;
|
||||
```
|
||||
|
||||
**Resultado esperado:** ❌ Error: `secondary_artist_id must reference an active staff member with role 'artist'`
|
||||
|
||||
### Prueba 3: Booking sin secondary_artist
|
||||
|
||||
```sql
|
||||
-- Crear un booking sin secondary_artist (debe ser válido)
|
||||
INSERT INTO bookings (
|
||||
customer_id,
|
||||
staff_id,
|
||||
location_id,
|
||||
resource_id,
|
||||
service_id,
|
||||
start_time_utc,
|
||||
end_time_utc,
|
||||
status,
|
||||
deposit_amount,
|
||||
total_amount,
|
||||
is_paid
|
||||
)
|
||||
SELECT
|
||||
(SELECT id FROM customers LIMIT 1),
|
||||
(SELECT id FROM staff WHERE role = 'staff' LIMIT 1),
|
||||
(SELECT id FROM locations LIMIT 1),
|
||||
(SELECT id FROM resources LIMIT 1),
|
||||
(SELECT id FROM services LIMIT 1),
|
||||
NOW() + INTERVAL '1 day',
|
||||
NOW() + INTERVAL '2 days',
|
||||
'confirmed',
|
||||
50.00,
|
||||
100.00,
|
||||
true;
|
||||
```
|
||||
|
||||
**Resultado esperado:** ✅ Booking creado exitosamente (secondary_artist es opcional)
|
||||
|
||||
## 📊 Resumen de Cambios
|
||||
|
||||
| Elemento | Antes | Después |
|
||||
|----------|--------|---------|
|
||||
| **Constraint** | `check_secondary_artist_role` con subquery | Eliminado |
|
||||
| **Trigger** | No existía | `validate_booking_secondary_artist` añadido |
|
||||
| **Función** | No existía | `validate_secondary_artist_role()` añadida |
|
||||
| **Índice** | No existía para `secondary_artist_id` | `idx_bookings_secondary_artist` añadido |
|
||||
| **Loop variable** | No declarada en `reset_all_weekly_invitations()` | `customer_record RECORD;` añadido |
|
||||
| **Total funciones** | 13 | 14 |
|
||||
| **Total triggers** | 15+ | 17+ |
|
||||
|
||||
## 🎓 Lecciones Aprendidas
|
||||
|
||||
1. **PostgreSQL no permite subqueries** en constraints CHECK
|
||||
2. **Los triggers de validación** son la alternativa correcta para validaciones complejas
|
||||
3. **Los loops en PL/pgSQL** requieren declarar las variables en el bloque `DECLARE`
|
||||
4. **Los triggers** pueden hacer validaciones que no son posibles con constraints
|
||||
5. **La documentación** debe mantenerse sincronizada con el código SQL
|
||||
6. **La sintaxis de PostgreSQL** es estricta y requiere declaraciones explícitas
|
||||
|
||||
## 📚 Referencias
|
||||
|
||||
- [PostgreSQL Constraints Documentation](https://www.postgresql.org/docs/current/ddl-constraints.html)
|
||||
- [PostgreSQL Triggers Documentation](https://www.postgresql.org/docs/current/sql-createtrigger.html)
|
||||
- [PostgreSQL ERROR: 0A000](https://www.postgresql.org/docs/current/errcodes-appendix.html)
|
||||
|
||||
---
|
||||
|
||||
**¿Necesitas ayuda adicional?** Una vez que hayas ejecutado la migración corregida, avísame para verificar que todo esté funcionando correctamente.
|
||||
267
docs/MIGRATION_GUIDE.md
Normal file
267
docs/MIGRATION_GUIDE.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# 🚀 Guía de Ejecución de Migraciones - SalonOS
|
||||
|
||||
Esta guía explica cómo ejecutar las migraciones de base de datos en Supabase.
|
||||
|
||||
## ⚠️ Requisitos Previos
|
||||
|
||||
1. **Cuenta de Supabase** con un proyecto creado
|
||||
2. **PostgreSQL client (psql)** instalado en tu máquina
|
||||
3. **Variables de entorno** configuradas en `.env.local`
|
||||
|
||||
## 📋 Paso 1: Configurar Variables de Entorno
|
||||
|
||||
Copia el archivo `.env.example` a `.env.local`:
|
||||
|
||||
```bash
|
||||
cp .env.example .env.local
|
||||
```
|
||||
|
||||
Edita el archivo `.env.local` con tus credenciales de Supabase:
|
||||
|
||||
```bash
|
||||
# Supabase
|
||||
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here
|
||||
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here
|
||||
```
|
||||
|
||||
### Cómo obtener las credenciales de Supabase
|
||||
|
||||
1. Ve a [Supabase Dashboard](https://supabase.com/dashboard)
|
||||
2. Selecciona tu proyecto
|
||||
3. Ve a **Settings → API**
|
||||
4. Copia:
|
||||
- **Project URL** → `NEXT_PUBLIC_SUPABASE_URL`
|
||||
- **anon public** → `NEXT_PUBLIC_SUPABASE_ANON_KEY`
|
||||
- **service_role** → `SUPABASE_SERVICE_ROLE_KEY`
|
||||
|
||||
## 🎯 Paso 2: Ejecutar Migraciones
|
||||
|
||||
### Opción A: Automática (Recomendada)
|
||||
|
||||
Usa el script de migración automatizado:
|
||||
|
||||
```bash
|
||||
# Dar permisos de ejecución al script
|
||||
chmod +x db/migrate.sh
|
||||
|
||||
# Ejecutar el script
|
||||
./db/migrate.sh
|
||||
```
|
||||
|
||||
### Opción B: Manual con psql
|
||||
|
||||
Si prefieres ejecutar las migraciones manualmente:
|
||||
|
||||
```bash
|
||||
# Exportar DATABASE_URL
|
||||
export DATABASE_URL="postgresql://postgres:[PASSWORD]@[PROJECT-ID].supabase.co:5432/postgres"
|
||||
|
||||
# Ejecutar cada migración en orden
|
||||
psql $DATABASE_URL -f db/migrations/001_initial_schema.sql
|
||||
psql $DATABASE_URL -f db/migrations/002_rls_policies.sql
|
||||
psql $DATABASE_URL -f db/migrations/003_audit_triggers.sql
|
||||
```
|
||||
|
||||
### Opción C: Vía Supabase Dashboard
|
||||
|
||||
1. Ve a [Supabase Dashboard → SQL Editor](https://supabase.com/dashboard/project/[PROJECT-ID]/sql)
|
||||
2. Copia el contenido de cada migración en orden
|
||||
3. Ejecuta `001_initial_schema.sql` primero
|
||||
4. Luego `002_rls_policies.sql`
|
||||
5. Finalmente `003_audit_triggers.sql`
|
||||
|
||||
## ✅ Paso 3: Verificar la Instalación
|
||||
|
||||
Ejecuta estas consultas para verificar que todo esté correcto:
|
||||
|
||||
### Verificar Tablas
|
||||
|
||||
```sql
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
ORDER BY table_name;
|
||||
```
|
||||
|
||||
**Esperado:** 8 tablas (locations, resources, staff, services, customers, invitations, bookings, audit_logs)
|
||||
|
||||
### Verificar Funciones
|
||||
|
||||
```sql
|
||||
SELECT routine_name
|
||||
FROM information_schema.routines
|
||||
WHERE routine_schema = 'public'
|
||||
ORDER BY routine_name;
|
||||
```
|
||||
|
||||
**Esperado:** 13 funciones incluyendo `generate_short_id`, `reset_weekly_invitations_for_customer`, etc.
|
||||
|
||||
### Verificar Triggers
|
||||
|
||||
```sql
|
||||
SELECT trigger_name, event_object_table
|
||||
FROM information_schema.triggers
|
||||
WHERE trigger_schema = 'public'
|
||||
ORDER BY event_object_table, trigger_name;
|
||||
```
|
||||
|
||||
**Esperado:** Múltiples triggers para auditoría y timestamps
|
||||
|
||||
### Verificar Políticas RLS
|
||||
|
||||
```sql
|
||||
SELECT schemaname, tablename, policyname, permissive, roles, cmd
|
||||
FROM pg_policies
|
||||
WHERE schemaname = 'public'
|
||||
ORDER BY tablename, policyname;
|
||||
```
|
||||
|
||||
**Esperado:** Múltiples políticas por rol (admin, manager, staff, artist, customer)
|
||||
|
||||
### Verificar Tipos ENUM
|
||||
|
||||
```sql
|
||||
SELECT typname, enumlabel
|
||||
FROM pg_enum e
|
||||
JOIN pg_type t ON e.enumtypid = t.oid
|
||||
WHERE t.typtype = 'e'
|
||||
ORDER BY t.typname, e.enumsortorder;
|
||||
```
|
||||
|
||||
**Esperado:** 6 tipos ENUM (user_role, customer_tier, booking_status, invitation_status, resource_type, audit_action)
|
||||
|
||||
## 🔍 Paso 4: Probar Funcionalidad
|
||||
|
||||
### Generar Short ID
|
||||
|
||||
```sql
|
||||
SELECT generate_short_id();
|
||||
```
|
||||
|
||||
**Esperado:** Un string de 6 caracteres alfanuméricos (ej: "A3F7X2")
|
||||
|
||||
### Generar Código de Invitación
|
||||
|
||||
```sql
|
||||
SELECT generate_invitation_code();
|
||||
```
|
||||
|
||||
**Esperado:** Un string de 10 caracteres alfanuméricos (ej: "X9J4K2M5N8")
|
||||
|
||||
### Obtener Inicio de Semana
|
||||
|
||||
```sql
|
||||
SELECT get_week_start(CURRENT_DATE);
|
||||
```
|
||||
|
||||
**Esperado:** El lunes de la semana actual
|
||||
|
||||
### Resetear Invitaciones de un Cliente
|
||||
|
||||
```sql
|
||||
-- Primero necesitas un cliente Gold en la base de datos
|
||||
-- Esto creará 5 invitaciones nuevas para la semana actual
|
||||
SELECT reset_weekly_invitations_for_customer('[CUSTOMER_UUID]');
|
||||
```
|
||||
|
||||
## 🚨 Solución de Problemas
|
||||
|
||||
### Error: "FATAL: password authentication failed"
|
||||
|
||||
**Causa:** La contraseña en DATABASE_URL es incorrecta.
|
||||
|
||||
**Solución:** Verifica que estés usando el `SUPABASE_SERVICE_ROLE_KEY` como contraseña en la URL de conexión.
|
||||
|
||||
### Error: "relation already exists"
|
||||
|
||||
**Causa:** Una tabla ya existe. La migración anterior puede haber fallado parcialmente.
|
||||
|
||||
**Solución:** Elimina las tablas existentes o ejecuta una limpieza completa:
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS audit_logs CASCADE;
|
||||
DROP TABLE IF EXISTS bookings CASCADE;
|
||||
DROP TABLE IF EXISTS invitations CASCADE;
|
||||
DROP TABLE IF EXISTS customers CASCADE;
|
||||
DROP TABLE IF EXISTS services CASCADE;
|
||||
DROP TABLE IF EXISTS staff CASCADE;
|
||||
DROP TABLE IF EXISTS resources CASCADE;
|
||||
DROP TABLE IF EXISTS locations CASCADE;
|
||||
|
||||
DROP FUNCTION IF EXISTS generate_short_id();
|
||||
DROP FUNCTION IF EXISTS generate_invitation_code();
|
||||
DROP FUNCTION IF EXISTS reset_weekly_invitations_for_customer(UUID);
|
||||
DROP FUNCTION IF EXISTS reset_all_weekly_invitations();
|
||||
DROP FUNCTION IF EXISTS log_audit();
|
||||
DROP FUNCTION IF EXISTS get_current_user_role();
|
||||
DROP FUNCTION IF EXISTS is_staff_or_higher();
|
||||
DROP FUNCTION IF EXISTS is_artist();
|
||||
DROP FUNCTION IF EXISTS is_customer();
|
||||
DROP FUNCTION IF EXISTS is_admin();
|
||||
DROP FUNCTION IF EXISTS update_updated_at();
|
||||
DROP FUNCTION IF EXISTS generate_booking_short_id();
|
||||
DROP FUNCTION IF EXISTS get_week_start(DATE);
|
||||
```
|
||||
|
||||
### Error: "must be owner of table"
|
||||
|
||||
**Causa:** No tienes permisos de superusuario o owner de la tabla.
|
||||
|
||||
**Solución:** Asegúrate de estar usando el `SUPABASE_SERVICE_ROLE_KEY` (no el anon key).
|
||||
|
||||
### Error: RLS no funciona
|
||||
|
||||
**Causa:** RLS no está habilitado o el usuario no tiene un rol asignado.
|
||||
|
||||
**Solución:**
|
||||
1. Verifica que RLS está habilitado: `SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';`
|
||||
2. Verifica que el usuario tenga un registro en `staff` o `customers`
|
||||
3. Verifica las políticas RLS: `SELECT * FROM pg_policies WHERE schemaname = 'public';`
|
||||
|
||||
## 📚 Documentación Adicional
|
||||
|
||||
- **PRD.md:** Reglas de negocio del sistema
|
||||
- **TASKS.md:** Plan de ejecución por fases
|
||||
- **AGENTS.md:** Roles y responsabilidades de IA
|
||||
- **db/migrations/README.md:** Documentación técnica de migraciones
|
||||
|
||||
## 🎓 Próximos Pasos
|
||||
|
||||
Después de completar las migraciones:
|
||||
|
||||
1. **Configurar Auth en Supabase Dashboard**
|
||||
- Habilitar Email/SMS authentication
|
||||
- Configurar Magic Links
|
||||
- Crear usuarios de prueba
|
||||
|
||||
2. **Crear Seeds de Datos**
|
||||
- Locations de prueba
|
||||
- Staff con diferentes roles
|
||||
- Services del catálogo
|
||||
- Customers Free y Gold
|
||||
|
||||
3. **Implementar Tarea 1.3**
|
||||
- Backend API endpoints para Short ID
|
||||
- Tests unitarios
|
||||
- Edge Function o Cron Job para reset semanal
|
||||
|
||||
4. **Implementar Tarea 1.4**
|
||||
- Endpoints CRUD de customers
|
||||
- Lógica de cálculo automático de Tier
|
||||
- Sistema de referidos
|
||||
|
||||
## 🆘 Soporte
|
||||
|
||||
Si encuentras problemas:
|
||||
|
||||
1. Revisa los logs de Supabase Dashboard
|
||||
2. Verifica que las variables de entorno estén correctamente configuradas
|
||||
3. Ejecuta las consultas de verificación en el "Paso 3"
|
||||
4. Consulta la sección de "Solución de Problemas"
|
||||
|
||||
---
|
||||
|
||||
**Última actualización:** 2026-01-15
|
||||
**Versión de migraciones:** 001, 002, 003
|
||||
**Estado:** ✅ Listo para producción
|
||||
370
docs/POST_MIGRATION_SUCCESS.md
Normal file
370
docs/POST_MIGRATION_SUCCESS.md
Normal file
@@ -0,0 +1,370 @@
|
||||
# 🎉 Migraciones Exitosas - SalonOS
|
||||
|
||||
## ✅ Estado: Migraciones Completadas
|
||||
|
||||
¡Excelente! Las migraciones de base de datos se han ejecutado exitosamente en Supabase.
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Paso 1: Verificar la Instalación
|
||||
|
||||
Vamos a ejecutar un script de verificación para confirmar que todo se creó correctamente.
|
||||
|
||||
### Ejecutar Script de Verificación
|
||||
|
||||
1. Ve a: **https://supabase.com/dashboard/project/pvvwbnybkadhreuqijsl/sql**
|
||||
2. Haz clic en **"New query"**
|
||||
3. Copia el contenido de: **`scripts/verify-migration.sql`**
|
||||
4. Pega el contenido en el SQL Editor
|
||||
5. Haz clic en **"Run"**
|
||||
|
||||
### Resultado Esperado
|
||||
|
||||
Deberías ver:
|
||||
|
||||
```
|
||||
TABLAS | locations
|
||||
TABLAS | resources
|
||||
TABLAS | staff
|
||||
TABLAS | services
|
||||
TABLAS | customers
|
||||
TABLAS | invitations
|
||||
TABLAS | bookings
|
||||
TABLAS | audit_logs
|
||||
...
|
||||
FUNCIONES | generate_short_id
|
||||
FUNCIONES | generate_invitation_code
|
||||
FUNCIONES | reset_all_weekly_invitations
|
||||
FUNCIONES | validate_secondary_artist_role
|
||||
...
|
||||
TRIGGERS | locations_updated_at
|
||||
TRIGGERS | validate_booking_secondary_artist
|
||||
...
|
||||
POLÍTICAS RLS | customers_select_admin_manager
|
||||
...
|
||||
ENUM TYPES | user_role
|
||||
ENUM TYPES | customer_tier
|
||||
...
|
||||
SHORT ID TEST | A3F7X2
|
||||
INVITATION CODE TEST | X9J4K2M5N8
|
||||
...
|
||||
RESUMEN | Tablas: 8
|
||||
RESUMEN | Funciones: 14
|
||||
RESUMEN | Triggers: 17+
|
||||
RESUMEN | Políticas RLS: 20+
|
||||
RESUMEN | Tipos ENUM: 6
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌱 Paso 2: Crear Datos de Prueba
|
||||
|
||||
Ahora vamos a crear datos de prueba para poder desarrollar y probar el sistema.
|
||||
|
||||
### Ejecutar Script de Seed
|
||||
|
||||
1. En el mismo SQL Editor, haz clic en **"New query"**
|
||||
2. Copia el contenido de: **`scripts/seed-data.sql`**
|
||||
3. Pega el contenido en el SQL Editor
|
||||
4. Haz clic en **"Run"**
|
||||
|
||||
### Resultado Esperado
|
||||
|
||||
Deberías ver:
|
||||
|
||||
```
|
||||
==========================================
|
||||
SALONOS - SEED DE DATOS COMPLETADO
|
||||
==========================================
|
||||
Locations: 3
|
||||
Resources: 6
|
||||
Staff: 8
|
||||
Services: 6
|
||||
Customers: 4
|
||||
Invitations: 15
|
||||
Bookings: 5
|
||||
==========================================
|
||||
✅ Base de datos lista para desarrollo
|
||||
==========================================
|
||||
```
|
||||
|
||||
### Datos Creados
|
||||
|
||||
**Locations (3):**
|
||||
- Salón Principal - Centro
|
||||
- Salón Norte - Polanco
|
||||
- Salón Sur - Coyoacán
|
||||
|
||||
**Resources (6):**
|
||||
- 3 estaciones en Centro
|
||||
- 2 estaciones en Polanco
|
||||
- 1 estación en Coyoacán
|
||||
|
||||
**Staff (8):**
|
||||
- 1 Admin
|
||||
- 2 Managers
|
||||
- 1 Staff
|
||||
- 4 Artists (María, Ana, Carla, Laura)
|
||||
|
||||
**Services (6):**
|
||||
- Corte y Estilizado ($500)
|
||||
- Color Completo ($1,200)
|
||||
- Balayage Premium ($2,000) - **Dual Artist**
|
||||
- Tratamiento Kératina ($1,500)
|
||||
- Peinado Evento ($800)
|
||||
- Servicio Express ($600) - **Dual Artist**
|
||||
|
||||
**Customers (4):**
|
||||
- Sofía Ramírez (Gold) - VIP
|
||||
- Valentina Hernández (Gold)
|
||||
- Camila López (Free)
|
||||
- Isabella García (Gold) - VIP
|
||||
|
||||
**Invitations (15):**
|
||||
- 5 para cada cliente Gold (Sofía, Valentina, Isabella)
|
||||
|
||||
**Bookings (5):**
|
||||
- 1 Balayage Premium para Sofía
|
||||
- 1 Color Completo para Valentina
|
||||
- 1 Corte y Estilizado para Camila
|
||||
- 1 Servicio Express Dual Artist para Isabella (con secondary_artist)
|
||||
- 1 Peinado Evento para Sofía
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Paso 3: Probar Funcionalidades
|
||||
|
||||
### Probar Short ID
|
||||
|
||||
```sql
|
||||
SELECT generate_short_id();
|
||||
```
|
||||
|
||||
**Resultado esperado:** String de 6 caracteres (ej: "A3F7X2")
|
||||
|
||||
### Probar Código de Invitación
|
||||
|
||||
```sql
|
||||
SELECT generate_invitation_code();
|
||||
```
|
||||
|
||||
**Resultado esperado:** String de 10 caracteres (ej: "X9J4K2M5N8")
|
||||
|
||||
### Verificar Bookings Creados
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
b.short_id,
|
||||
c.first_name || ' ' || c.last_name as customer,
|
||||
s.display_name as artist,
|
||||
svc.name as service,
|
||||
b.start_time_utc,
|
||||
b.end_time_utc,
|
||||
b.status,
|
||||
b.total_amount
|
||||
FROM bookings b
|
||||
JOIN customers c ON b.customer_id = c.id
|
||||
JOIN staff s ON b.staff_id = s.id
|
||||
JOIN services svc ON b.service_id = svc.id
|
||||
ORDER BY b.start_time_utc;
|
||||
```
|
||||
|
||||
### Verificar Invitaciones
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
i.code,
|
||||
inv.first_name || ' ' || inv.last_name as inviter,
|
||||
i.status,
|
||||
i.week_start_date,
|
||||
i.expiry_date
|
||||
FROM invitations i
|
||||
JOIN customers inv ON i.inviter_id = inv.id
|
||||
WHERE i.status = 'pending'
|
||||
ORDER BY inv.first_name, i.expiry_date;
|
||||
```
|
||||
|
||||
### Verificar Staff y Roles
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
s.display_name,
|
||||
s.role,
|
||||
l.name as location,
|
||||
s.phone,
|
||||
s.is_active
|
||||
FROM staff s
|
||||
JOIN locations l ON s.location_id = l.id
|
||||
ORDER BY l.name, s.role, s.display_name;
|
||||
```
|
||||
|
||||
### Verificar Auditoría
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
entity_type,
|
||||
action,
|
||||
new_values->>'operation' as operation,
|
||||
new_values->>'table_name' as table_name,
|
||||
created_at
|
||||
FROM audit_logs
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
### Probar Validación de Secondary Artist
|
||||
|
||||
**Test 1: Intentar crear booking con secondary_artist válido**
|
||||
|
||||
```sql
|
||||
-- Este debe funcionar
|
||||
INSERT INTO bookings (
|
||||
customer_id,
|
||||
staff_id,
|
||||
secondary_artist_id,
|
||||
location_id,
|
||||
resource_id,
|
||||
service_id,
|
||||
start_time_utc,
|
||||
end_time_utc,
|
||||
status,
|
||||
deposit_amount,
|
||||
total_amount,
|
||||
is_paid,
|
||||
notes
|
||||
)
|
||||
SELECT
|
||||
(SELECT id FROM customers WHERE email = 'sofia.ramirez@example.com'),
|
||||
(SELECT id FROM staff WHERE display_name = 'Artist María García'),
|
||||
(SELECT id FROM staff WHERE display_name = 'Artist Ana Rodríguez'),
|
||||
(SELECT id FROM locations WHERE name = 'Salón Principal - Centro'),
|
||||
(SELECT id FROM resources WHERE location_id = (SELECT id FROM locations WHERE name = 'Salón Principal - Centro') LIMIT 1 OFFSET 2 LIMIT 1),
|
||||
(SELECT id FROM services WHERE name = 'Balayage Premium'),
|
||||
NOW() + INTERVAL '7 days',
|
||||
NOW() + INTERVAL '7 days' + INTERVAL '3 hours',
|
||||
'confirmed',
|
||||
200.00,
|
||||
2000.00,
|
||||
true,
|
||||
'Test de validación - secondary_artist válido'
|
||||
RETURNING short_id;
|
||||
```
|
||||
|
||||
**Resultado esperado:** ✅ Booking creado exitosamente
|
||||
|
||||
**Test 2: Intentar crear booking con secondary_artist inválido**
|
||||
|
||||
```sql
|
||||
-- Este debe fallar
|
||||
INSERT INTO bookings (
|
||||
customer_id,
|
||||
staff_id,
|
||||
secondary_artist_id,
|
||||
location_id,
|
||||
resource_id,
|
||||
service_id,
|
||||
start_time_utc,
|
||||
end_time_utc,
|
||||
status,
|
||||
deposit_amount,
|
||||
total_amount,
|
||||
is_paid,
|
||||
notes
|
||||
)
|
||||
SELECT
|
||||
(SELECT id FROM customers WHERE email = 'sofia.ramirez@example.com'),
|
||||
(SELECT id FROM staff WHERE display_name = 'Artist María García'),
|
||||
(SELECT id FROM staff WHERE display_name = 'Manager Centro'), -- ❌ Esto NO es 'artist'
|
||||
(SELECT id FROM locations WHERE name = 'Salón Principal - Centro'),
|
||||
(SELECT id FROM resources WHERE location_id = (SELECT id FROM locations WHERE name = 'Salón Principal - Centro') LIMIT 1 OFFSET 2 LIMIT 1),
|
||||
(SELECT id FROM services WHERE name = 'Balayage Premium'),
|
||||
NOW() + INTERVAL '8 days',
|
||||
NOW() + INTERVAL '8 days' + INTERVAL '3 hours',
|
||||
'confirmed',
|
||||
200.00,
|
||||
2000.00,
|
||||
true,
|
||||
'Test de validación - secondary_artist inválido';
|
||||
```
|
||||
|
||||
**Resultado esperado:** ❌ Error: `secondary_artist_id must reference an active staff member with role 'artist'`
|
||||
|
||||
---
|
||||
|
||||
## ✅ Paso 4: Verificar Checklist
|
||||
|
||||
Antes de continuar con el desarrollo, asegúrate de:
|
||||
|
||||
- [x] Migraciones ejecutadas exitosamente
|
||||
- [ ] Script de verificación ejecutado y todo correcto
|
||||
- [ ] Script de seed ejecutado y datos creados
|
||||
- [ ] Short ID generable
|
||||
- [ ] Código de invitación generable
|
||||
- [ ] Validación de secondary_artist funcionando
|
||||
- [ ] Auditoría registrando correctamente
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Próximos Pasos
|
||||
|
||||
### Configurar Auth en Supabase Dashboard
|
||||
|
||||
1. Ve a: **Authentication → Providers**
|
||||
2. Habilita **Email Provider**
|
||||
3. Configura **Email Templates** (opcional)
|
||||
4. Habilita **SMS Provider** si usas Twilio (opcional)
|
||||
|
||||
### Crear Usuarios en Auth
|
||||
|
||||
Para los datos de seed, necesitas crear usuarios en Supabase Auth:
|
||||
|
||||
1. Ve a: **Authentication → Users**
|
||||
2. Haz clic en **"Add user"** para cada usuario de staff y customer
|
||||
3. Usa los mismos UUIDs que están en el seed para los `user_id` de staff y customers
|
||||
|
||||
### Continuar con el Desarrollo
|
||||
|
||||
Ahora que la base de datos está lista, puedes continuar con:
|
||||
|
||||
1. **Tarea 1.3:** Short ID & Invitations
|
||||
- Implementar endpoints de API
|
||||
- Tests unitarios
|
||||
- Edge Function o Cron Job para reset semanal
|
||||
|
||||
2. **Tarea 1.4:** CRM Base
|
||||
- Endpoints CRUD de customers
|
||||
- Lógica de cálculo automático de Tier
|
||||
- Sistema de referidos
|
||||
|
||||
3. **Fase 2:** Motor de Agendamiento
|
||||
- Validación Staff/Artist
|
||||
- Validación Recursos
|
||||
- Servicios Express (Dual Artist)
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentación Disponible
|
||||
|
||||
- **`docs/00_FULL_MIGRATION_FINAL_README.md`** - Guía de migración final
|
||||
- **`docs/MIGRATION_CORRECTION.md`** - Detalle de correcciones
|
||||
- **`docs/SUPABASE_DASHBOARD_MIGRATION.md`** - Guía de ejecución
|
||||
- **`scripts/verify-migration.sql`** - Script de verificación
|
||||
- **`scripts/seed-data.sql`** - Script de datos de prueba
|
||||
- **`FASE_1_STATUS.md`** - Estado de la Fase 1
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Soporte
|
||||
|
||||
Si encuentras problemas:
|
||||
|
||||
1. Revisa los logs de Supabase Dashboard
|
||||
2. Ejecuta el script de verificación
|
||||
3. Consulta la documentación arriba
|
||||
4. Verifica que las funciones y triggers estén creados correctamente
|
||||
|
||||
---
|
||||
|
||||
**¡Felicidades!** 🎉 Tu base de datos de SalonOS está completamente configurada y lista para el desarrollo.
|
||||
|
||||
**¿Listo para configurar Auth en Supabase Dashboard o continuar con el desarrollo de la aplicación?**
|
||||
375
docs/QUICK_START_POST_MIGRATION.md
Normal file
375
docs/QUICK_START_POST_MIGRATION.md
Normal file
@@ -0,0 +1,375 @@
|
||||
# 🎉 SALONOS - GUÍA RÁPIDA POST-MIGRACIÓN
|
||||
|
||||
## ✅ ESTADO ACTUAL
|
||||
|
||||
- ✅ Migraciones ejecutadas exitosamente en Supabase
|
||||
- ✅ 8 tablas, 14 funciones, 17+ triggers, 20+ políticas RLS, 6 tipos ENUM creados
|
||||
- ✅ Base de datos lista para desarrollo
|
||||
- ✅ Scripts de verificación y seed creados
|
||||
|
||||
---
|
||||
|
||||
## 📋 PASOS PENDIENTES
|
||||
|
||||
### Paso 1: Verificar Instalación de Migraciones ✅
|
||||
**Guía:** `docs/STEP_BY_STEP_VERIFICATION.md`
|
||||
|
||||
**Qué hacer:**
|
||||
1. Abrir Supabase SQL Editor
|
||||
2. Ejecutar consultas de verificación (12 consultas en total)
|
||||
3. Verificar que todo esté correcto
|
||||
|
||||
**Duración estimada:** 5-10 minutos
|
||||
|
||||
---
|
||||
|
||||
### Paso 2: Crear Datos de Prueba ✅
|
||||
**Guía:** `docs/STEP_BY_STEP_VERIFICATION.md` (Sección 2)
|
||||
|
||||
**Qué hacer:**
|
||||
1. Ejecutar seed por secciones (9 secciones en total)
|
||||
2. Crear locations, resources, staff, services, customers, invitations, bookings
|
||||
3. Verificar que todos los datos se crearon correctamente
|
||||
|
||||
**Duración estimada:** 10-15 minutos
|
||||
|
||||
**Datos a crear:**
|
||||
- 3 locations (Centro, Polanco, Coyoacán)
|
||||
- 6 resources (estaciones)
|
||||
- 8 staff (1 admin, 2 managers, 1 staff, 4 artists)
|
||||
- 6 services (catálogo completo)
|
||||
- 4 customers (mix Free/Gold)
|
||||
- 15 invitations (5 por cliente Gold)
|
||||
- 5 bookings de prueba
|
||||
|
||||
---
|
||||
|
||||
### Paso 3: Configurar Auth en Supabase Dashboard ✅
|
||||
**Guía:** `docs/STEP_BY_STEP_AUTH_CONFIG.md`
|
||||
|
||||
**Qué hacer:**
|
||||
1. Habilitar Email Provider
|
||||
2. Configurar Site URL y Redirect URLs
|
||||
3. Configurar SMTP (opcional)
|
||||
4. Configurar SMS Provider (opcional)
|
||||
5. Crear usuarios de staff (8 usuarios)
|
||||
6. Crear usuarios de customers (4 usuarios)
|
||||
7. Actualizar tablas staff y customers con user_ids correctos
|
||||
8. Configurar Email Templates (opcional)
|
||||
|
||||
**Duración estimada:** 20-30 minutos
|
||||
|
||||
**Usuarios a crear:**
|
||||
|
||||
**Staff (8):**
|
||||
- Admin Principal: `admin@salonos.com`
|
||||
- Manager Centro: `manager.centro@salonos.com`
|
||||
- Manager Polanco: `manager.polanco@salonos.com`
|
||||
- Staff Coordinadora: `staff.coordinadora@salonos.com`
|
||||
- Artist María García: `artist.maria@salonos.com`
|
||||
- Artist Ana Rodríguez: `artist.ana@salonos.com`
|
||||
- Artist Carla López: `artist.carla@salonos.com`
|
||||
- Artist Laura Martínez: `artist.laura@salonos.com`
|
||||
|
||||
**Customers (4):**
|
||||
- Sofía Ramírez (Gold): `sofia.ramirez@example.com`
|
||||
- Valentina Hernández (Gold): `valentina.hernandez@example.com`
|
||||
- Camila López (Free): `camila.lopez@example.com`
|
||||
- Isabella García (Gold): `isabella.garcia@example.com`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 RESUMEN DE CONSULTAS RÁPIDAS
|
||||
|
||||
### Verificar Tablas
|
||||
```sql
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name IN ('locations', 'resources', 'staff', 'services', 'customers', 'invitations', 'bookings', 'audit_logs')
|
||||
ORDER BY table_name;
|
||||
```
|
||||
|
||||
### Verificar Funciones
|
||||
```sql
|
||||
SELECT routine_name
|
||||
FROM information_schema.routines
|
||||
WHERE routine_schema = 'public'
|
||||
ORDER BY routine_name;
|
||||
```
|
||||
|
||||
### Probar Short ID
|
||||
```sql
|
||||
SELECT generate_short_id();
|
||||
```
|
||||
|
||||
### Probar Código de Invitación
|
||||
```sql
|
||||
SELECT generate_invitation_code();
|
||||
```
|
||||
|
||||
### Verificar Bookings
|
||||
```sql
|
||||
SELECT
|
||||
b.short_id,
|
||||
c.first_name || ' ' || c.last_name as customer,
|
||||
s.display_name as artist,
|
||||
svc.name as service,
|
||||
b.start_time_utc,
|
||||
b.status,
|
||||
b.total_amount
|
||||
FROM bookings b
|
||||
JOIN customers c ON b.customer_id = c.id
|
||||
JOIN staff s ON b.staff_id = s.id
|
||||
JOIN services svc ON b.service_id = svc.id
|
||||
ORDER BY b.start_time_utc;
|
||||
```
|
||||
|
||||
### Verificar Staff y Roles
|
||||
```sql
|
||||
SELECT
|
||||
s.display_name,
|
||||
s.role,
|
||||
l.name as location,
|
||||
s.phone,
|
||||
s.is_active
|
||||
FROM staff s
|
||||
JOIN locations l ON s.location_id = l.id
|
||||
ORDER BY l.name, s.role, s.display_name;
|
||||
```
|
||||
|
||||
### Verificar Invitaciones
|
||||
```sql
|
||||
SELECT
|
||||
i.code,
|
||||
inv.first_name || ' ' || inv.last_name as inviter,
|
||||
i.status,
|
||||
i.week_start_date,
|
||||
i.expiry_date
|
||||
FROM invitations i
|
||||
JOIN customers inv ON i.inviter_id = inv.id
|
||||
WHERE i.status = 'pending'
|
||||
ORDER BY inv.first_name, i.expiry_date;
|
||||
```
|
||||
|
||||
### Verificar Auditoría
|
||||
```sql
|
||||
SELECT
|
||||
entity_type,
|
||||
action,
|
||||
new_values->>'operation' as operation,
|
||||
new_values->>'table_name' as table_name,
|
||||
created_at
|
||||
FROM audit_logs
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ CHECKLIST COMPLETO
|
||||
|
||||
### Verificación de Migraciones
|
||||
- [ ] 8 tablas creadas (locations, resources, staff, services, customers, invitations, bookings, audit_logs)
|
||||
- [ ] 14 funciones creadas
|
||||
- [ ] 17+ triggers activos
|
||||
- [ ] 20+ políticas RLS configuradas
|
||||
- [ ] 6 tipos ENUM creados
|
||||
- [ ] Short ID generable
|
||||
- [ ] Código de invitación generable
|
||||
|
||||
### Seed de Datos
|
||||
- [ ] 3 locations creadas
|
||||
- [ ] 6 resources creadas
|
||||
- [ ] 8 staff creados
|
||||
- [ ] 6 services creados
|
||||
- [ ] 4 customers creados
|
||||
- [ ] 15 invitaciones creadas (5 por cliente Gold)
|
||||
- [ ] 5 bookings creados
|
||||
- [ ] 1 booking con secondary_artist
|
||||
|
||||
### Configuración de Auth
|
||||
- [ ] Email Provider habilitado
|
||||
- [ ] Site URL configurado
|
||||
- [ ] 8 usuarios de staff creados en Supabase Auth
|
||||
- [ ] 4 usuarios de customers creados en Supabase Auth
|
||||
- [ ] Tabla staff actualizada con user_ids correctos
|
||||
- [ ] Tabla customers actualizada con user_ids correctos
|
||||
- [ ] Email templates configurados (opcional)
|
||||
|
||||
### Pruebas Funcionales
|
||||
- [ ] Login con admin funciona
|
||||
- [ ] Login con customer funciona
|
||||
- [ ] Políticas RLS funcionan (Artist no ve email/phone de customers)
|
||||
- [ ] Short ID se genera automáticamente al crear booking
|
||||
- [ ] Validación de secondary_artist funciona
|
||||
- [ ] Auditoría se registra correctamente
|
||||
|
||||
---
|
||||
|
||||
## 📚 DOCUMENTACIÓN DISPONIBLE
|
||||
|
||||
### Guías Principales
|
||||
1. **`docs/STEP_BY_STEP_VERIFICATION.md`**
|
||||
- Guía paso a paso para ejecutar scripts de verificación y seed
|
||||
- 12 consultas de verificación
|
||||
- 9 secciones de seed de datos
|
||||
- Consultas adicionales de prueba
|
||||
|
||||
2. **`docs/STEP_BY_STEP_AUTH_CONFIG.md`**
|
||||
- Guía paso a paso para configurar Auth en Supabase Dashboard
|
||||
- Configuración de Email Provider
|
||||
- Configuración de SMS Provider (opcional)
|
||||
- Creación de usuarios de staff y customers
|
||||
- Actualización de tablas con user_ids
|
||||
- Configuración de Email Templates (opcional)
|
||||
|
||||
### Documentación de Migraciones
|
||||
3. **`docs/00_FULL_MIGRATION_FINAL_README.md`**
|
||||
- Guía de la migración final
|
||||
- Instrucciones de ejecución
|
||||
- Consultas de verificación
|
||||
|
||||
4. **`docs/MIGRATION_CORRECTION.md`**
|
||||
- Detalle de las correcciones aplicadas
|
||||
- Problemas encontrados y soluciones
|
||||
|
||||
5. **`docs/SUPABASE_DASHBOARD_MIGRATION.md`**
|
||||
- Guía de ejecución en Supabase Dashboard
|
||||
- Solución de problemas
|
||||
|
||||
6. **`docs/POST_MIGRATION_SUCCESS.md`**
|
||||
- Guía general post-migración
|
||||
- Scripts de prueba
|
||||
- Verificación de funcionalidades
|
||||
|
||||
### Documentación Técnica
|
||||
7. **`db/migrations/README.md`**
|
||||
- Documentación técnica de migraciones
|
||||
- Orden de ejecución
|
||||
- Verificación
|
||||
|
||||
8. **`db/migrations/00_FULL_MIGRATION_FINAL.sql`**
|
||||
- Script final consolidado
|
||||
- Todas las migraciones en un archivo
|
||||
|
||||
### Scripts
|
||||
9. **`scripts/verify-migration.sql`**
|
||||
- Script completo de verificación
|
||||
- 12 consultas de verificación
|
||||
|
||||
10. **`scripts/seed-data.sql`**
|
||||
- Script completo de seed
|
||||
- Crea todos los datos de prueba
|
||||
|
||||
### Estado del Proyecto
|
||||
11. **`FASE_1_STATUS.md`**
|
||||
- Estado actualizado de la Fase 1
|
||||
- Tareas completadas
|
||||
- Próximos pasos
|
||||
|
||||
---
|
||||
|
||||
## 🚀 PRÓXIMOS PASOS (Después de Auth Configurado)
|
||||
|
||||
### Desarrollo del Frontend
|
||||
|
||||
1. **Crear página de login** (`app/boutique/(auth)/login/page.tsx`)
|
||||
2. **Crear página de registro** (`app/boutique/(auth)/register/page.tsx`)
|
||||
3. **Crear página de dashboard de cliente** (`app/boutique/(customer)/dashboard/page.tsx`)
|
||||
4. **Crear página de bookings** (`app/boutique/(customer)/bookings/page.tsx`)
|
||||
|
||||
### Desarrollo del Backend
|
||||
|
||||
1. **Tarea 1.3: Short ID & Invitaciones**
|
||||
- API endpoint: `POST /api/bookings` (crea booking con short_id)
|
||||
- API endpoint: `GET /api/invitations` (lista invitaciones)
|
||||
- API endpoint: `POST /api/invitations/reset` (reset manual)
|
||||
- Tests unitarios
|
||||
- Edge Function o Cron Job para reset semanal (Lunes 00:00 UTC)
|
||||
|
||||
2. **Tarea 1.4: CRM Base (Customers)**
|
||||
- API endpoint: `GET /api/customers` (lista customers)
|
||||
- API endpoint: `GET /api/customers/[id]` (detalle de customer)
|
||||
- API endpoint: `POST /api/customers` (crear customer)
|
||||
- API endpoint: `PUT /api/customers/[id]` (actualizar customer)
|
||||
- API endpoint: `DELETE /api/customers/[id]` (eliminar customer)
|
||||
- Lógica de cálculo automático de Tier
|
||||
- Sistema de referidos
|
||||
|
||||
### Fase 2: Motor de Agendamiento
|
||||
|
||||
1. **Tarea 2.1: Disponibilidad Doble Capa**
|
||||
- Validación Staff/Artist (horario laboral + Google Calendar)
|
||||
- Validación Recurso (disponibilidad de estación física)
|
||||
- Regla de prioridad dinámica
|
||||
|
||||
2. **Tarea 2.2: Servicios Express (Dual Artist)**
|
||||
- Lógica de booking dual
|
||||
- Aplicación automática de Premium Fee
|
||||
|
||||
3. **Tarea 2.3: Google Calendar Sync**
|
||||
- Integración vía Service Account
|
||||
- Sincronización bidireccional
|
||||
- Manejo de conflictos
|
||||
|
||||
---
|
||||
|
||||
## 💡 TIPS ÚTILES
|
||||
|
||||
### Tip 1: Ejecutar Scripts en el Orden Correcto
|
||||
Siempre ejecuta:
|
||||
1. Verificación → Seed → Auth Config
|
||||
|
||||
### Tip 2: Verificar cada Paso
|
||||
No continúes al siguiente paso hasta verificar que el anterior esté correcto.
|
||||
|
||||
### Tip 3: Usar Pestañas Separadas
|
||||
Abre múltiples pestañas en el SQL Editor para separar:
|
||||
- Pestaña 1: Verificación
|
||||
- Pestaña 2: Seed
|
||||
- Pestaña 3: Pruebas adicionales
|
||||
|
||||
### Tip 4: Guardar los user_ids
|
||||
Copia los user_ids de Supabase Auth en un archivo de notas para usarlos cuando actualices las tablas staff y customers.
|
||||
|
||||
### Tip 5: Probar con Diferentes Roles
|
||||
Inicia sesión con diferentes roles (admin, manager, staff, artist, customer) para verificar que las políticas RLS funcionen correctamente.
|
||||
|
||||
---
|
||||
|
||||
## 🆘 AYUDA
|
||||
|
||||
Si encuentras problemas:
|
||||
|
||||
1. **Revisa los logs de Supabase Dashboard**
|
||||
2. **Ejecuta las consultas de verificación**
|
||||
3. **Consulta la guía de solución de problemas en cada documento**
|
||||
4. **Verifica que las variables de entorno estén correctas en .env.local**
|
||||
5. **Asegúrate de estar usando el proyecto correcto de Supabase**
|
||||
|
||||
---
|
||||
|
||||
## 🎉 ¡FELICIDADES!
|
||||
|
||||
Has completado exitosamente:
|
||||
|
||||
✅ **FASE 1.1:** Infraestructura Base (Next.js 14 structure)
|
||||
✅ **FASE 1.2:** Esquema de Base de Datos Inicial (8 tablas, RLS, triggers)
|
||||
✅ **MIGRACIONES:** Ejecutadas exitosamente en Supabase
|
||||
✅ **VERIFICACIÓN:** Scripts creados y listos para ejecutar
|
||||
✅ **SEED DE DATOS:** Scripts creados y listos para ejecutar
|
||||
✅ **AUTH CONFIGURACIÓN:** Guía completa creada
|
||||
|
||||
**Tu base de datos de SalonOS está lista para el desarrollo!**
|
||||
|
||||
---
|
||||
|
||||
**¿Qué deseas hacer ahora?**
|
||||
|
||||
1. **Ejecutar scripts de verificación y seed** (usa `docs/STEP_BY_STEP_VERIFICATION.md`)
|
||||
2. **Configurar Auth en Supabase Dashboard** (usa `docs/STEP_BY_STEP_AUTH_CONFIG.md`)
|
||||
3. **Comenzar el desarrollo del frontend** (Next.js)
|
||||
4. **Implementar las tareas de backend** (Tarea 1.3 y 1.4)
|
||||
|
||||
**¡El futuro es tuyo!** 🚀
|
||||
610
docs/STEP_BY_STEP_AUTH_CONFIG.md
Normal file
610
docs/STEP_BY_STEP_AUTH_CONFIG.md
Normal file
@@ -0,0 +1,610 @@
|
||||
# 🔐 Guía Paso a Paso - Configuración de Auth en Supabase Dashboard
|
||||
|
||||
## 🎯 Objetivo
|
||||
|
||||
Configurar el sistema de autenticación de Supabase para que los usuarios puedan:
|
||||
- Registrarse con email
|
||||
- Iniciar sesión con Magic Links
|
||||
- Tener roles asignados (Admin, Manager, Staff, Artist, Customer)
|
||||
|
||||
---
|
||||
|
||||
## 📋 Paso 1: Abrir Configuración de Auth
|
||||
|
||||
1. Ve a: **https://supabase.com/dashboard/project/pvvwbnybkadhreuqijsl**
|
||||
2. En el menú lateral, haz clic en **"Authentication"**
|
||||
3. Haz clic en **"Providers"**
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Paso 2: Configurar Email Provider
|
||||
|
||||
### 2.1 Habilitar Email Auth
|
||||
|
||||
1. En la sección **"Providers"**, busca **"Email"**
|
||||
2. Haz clic en el botón **"Enable"**
|
||||
3. Configura las siguientes opciones:
|
||||
|
||||
**Email Confirmation:**
|
||||
```
|
||||
Confirm email: ON (activado)
|
||||
```
|
||||
|
||||
**Email Templates:**
|
||||
- **Confirm signup:** Habilitar
|
||||
- **Reset password:** Habilitar
|
||||
- **Email change:** Habilitar (opcional)
|
||||
- **Magic Link:** Habilitar (opcional)
|
||||
|
||||
### 2.2 Configurar Site URL
|
||||
|
||||
1. En la sección **"URL Configuration"**, configura:
|
||||
- **Site URL:** `http://localhost:3000`
|
||||
- **Redirect URLs:** `http://localhost:3000/auth/callback`
|
||||
|
||||
**Nota:** Para producción, cambiar `localhost:3000` por tu dominio de producción.
|
||||
|
||||
### 2.3 Configurar SMTP (Opcional)
|
||||
|
||||
Para desarrollo, puedes usar el SMTP por defecto de Supabase.
|
||||
|
||||
Si deseas usar tu propio servidor SMTP:
|
||||
|
||||
1. Ve a **"Authentication" → "URL Configuration"**
|
||||
2. Desplázate hasta **"SMTP Settings"**
|
||||
3. Configura:
|
||||
- **SMTP Host:** `smtp.gmail.com` (ejemplo)
|
||||
- **SMTP Port:** `587`
|
||||
- **SMTP User:** `tu-email@gmail.com`
|
||||
- **SMTP Password:** `tu-app-password`
|
||||
- **Sender Email:** `tu-email@gmail.com`
|
||||
- **Sender Name:** `SalonOS`
|
||||
|
||||
---
|
||||
|
||||
## 📱 Paso 3: Configurar SMS Provider (Opcional)
|
||||
|
||||
Para autenticación por SMS (opcional para inicio):
|
||||
|
||||
### 3.1 Habilitar Twilio
|
||||
|
||||
1. En **"Providers"**, busca **"Phone"**
|
||||
2. Haz clic en **"Enable"**
|
||||
3. Selecciona **"Twilio"** como proveedor
|
||||
4. Configura:
|
||||
- **Account SID:** Obtenido de Twilio Dashboard
|
||||
- **Auth Token:** Obtenido de Twilio Dashboard
|
||||
- **Twilio Phone Number:** `+14155238886` (o tu número de Twilio)
|
||||
- **Message Service SID:** (opcional)
|
||||
|
||||
### 3.2 Verificar SMS Test
|
||||
|
||||
1. En la sección **"Phone"**, haz clic en **"Test"**
|
||||
2. Ingresa un número de teléfono de prueba
|
||||
3. Envía un mensaje de prueba
|
||||
|
||||
---
|
||||
|
||||
## 🧑 Paso 4: Crear Usuarios de Staff
|
||||
|
||||
### 4.1 Obtener User IDs del Seed
|
||||
|
||||
Primero, necesitamos los `user_id` que se crearon en el seed. Ejecuta esta consulta en el SQL Editor:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
s.display_name,
|
||||
s.role,
|
||||
s.user_id as supabase_user_id_to_create
|
||||
FROM staff s
|
||||
ORDER BY s.role, s.display_name;
|
||||
```
|
||||
|
||||
Copia los `user_id` de cada miembro del staff.
|
||||
|
||||
### 4.2 Crear Usuarios en Supabase Auth
|
||||
|
||||
**Opción A: Manual (recomendado para empezar)**
|
||||
|
||||
1. Ve a **"Authentication" → "Users"**
|
||||
2. Haz clic en **"Add user"**
|
||||
3. Para cada miembro del staff, crea un usuario:
|
||||
|
||||
**Admin Principal:**
|
||||
- **Email:** `admin@salonos.com`
|
||||
- **Password:** `Admin123!` (o una segura)
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata (opcional):**
|
||||
```json
|
||||
{
|
||||
"role": "admin",
|
||||
"display_name": "Admin Principal"
|
||||
}
|
||||
```
|
||||
|
||||
**Manager Centro:**
|
||||
- **Email:** `manager.centro@salonos.com`
|
||||
- **Password:** `Manager123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"role": "manager",
|
||||
"display_name": "Manager Centro"
|
||||
}
|
||||
```
|
||||
|
||||
**Manager Polanco:**
|
||||
- **Email:** `manager.polanco@salonos.com`
|
||||
- **Password:** `Manager123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"role": "manager",
|
||||
"display_name": "Manager Polanco"
|
||||
}
|
||||
```
|
||||
|
||||
**Staff Coordinadora:**
|
||||
- **Email:** `staff.coordinadora@salonos.com`
|
||||
- **Password:** `Staff123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"role": "staff",
|
||||
"display_name": "Staff Coordinadora"
|
||||
}
|
||||
```
|
||||
|
||||
**Artist María García:**
|
||||
- **Email:** `artist.maria@salonos.com`
|
||||
- **Password:** `Artist123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"role": "artist",
|
||||
"display_name": "Artist María García"
|
||||
}
|
||||
```
|
||||
|
||||
**Artist Ana Rodríguez:**
|
||||
- **Email:** `artist.ana@salonos.com`
|
||||
- **Password:** `Artist123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"role": "artist",
|
||||
"display_name": "Artist Ana Rodríguez"
|
||||
}
|
||||
```
|
||||
|
||||
**Artist Carla López:**
|
||||
- **Email:** `artist.carla@salonos.com`
|
||||
- **Password:** `Artist123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"role": "artist",
|
||||
"display_name": "Artist Carla López"
|
||||
}
|
||||
```
|
||||
|
||||
**Artist Laura Martínez:**
|
||||
- **Email:** `artist.laura@salonos.com`
|
||||
- **Password:** `Artist123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"role": "artist",
|
||||
"display_name": "Artist Laura Martínez"
|
||||
}
|
||||
```
|
||||
|
||||
**Opción B: Automática con SQL (más avanzado)**
|
||||
|
||||
Si prefieres crear usuarios automáticamente con SQL y luego actualizar sus IDs en la tabla staff:
|
||||
|
||||
1. Crea una tabla temporal para mapear los usuarios:
|
||||
```sql
|
||||
-- Primero, crea los usuarios en Supabase Auth manualmente (opción A)
|
||||
-- Luego ejecuta esta consulta para obtener sus IDs:
|
||||
SELECT
|
||||
id as auth_user_id,
|
||||
email
|
||||
FROM auth.users
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 8;
|
||||
```
|
||||
|
||||
2. Actualiza la tabla staff con los nuevos IDs:
|
||||
```sql
|
||||
-- Ejemplo para actualizar un usuario
|
||||
UPDATE staff
|
||||
SET user_id = 'NUEVO_AUTH_USER_ID_DESDE_SUPABASE'
|
||||
WHERE display_name = 'Artist María García';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👩 Step 5: Crear Usuarios de Customers
|
||||
|
||||
### 5.1 Obtener User IDs del Seed
|
||||
|
||||
Ejecuta esta consulta en el SQL Editor:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
c.email,
|
||||
c.first_name || ' ' || c.last_name as full_name,
|
||||
c.tier,
|
||||
c.user_id as supabase_user_id_to_create
|
||||
FROM customers c
|
||||
ORDER BY c.last_name, c.first_name;
|
||||
```
|
||||
|
||||
### 5.2 Crear Usuarios en Supabase Auth
|
||||
|
||||
1. Ve a **"Authentication" → "Users"**
|
||||
2. Haz clic en **"Add user"**
|
||||
3. Para cada customer, crea un usuario:
|
||||
|
||||
**Sofía Ramírez (Gold):**
|
||||
- **Email:** `sofia.ramirez@example.com`
|
||||
- **Password:** `Customer123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"tier": "gold",
|
||||
"display_name": "Sofía Ramírez"
|
||||
}
|
||||
```
|
||||
|
||||
**Valentina Hernández (Gold):**
|
||||
- **Email:** `valentina.hernandez@example.com`
|
||||
- **Password:** `Customer123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"tier": "gold",
|
||||
"display_name": "Valentina Hernández"
|
||||
}
|
||||
```
|
||||
|
||||
**Camila López (Free):**
|
||||
- **Email:** `camila.lopez@example.com`
|
||||
- **Password:** `Customer123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"tier": "free",
|
||||
"display_name": "Camila López"
|
||||
}
|
||||
```
|
||||
|
||||
**Isabella García (Gold):**
|
||||
- **Email:** `isabella.garcia@example.com`
|
||||
- **Password:** `Customer123!`
|
||||
- **Auto Confirm User:** ON
|
||||
- **User Metadata:**
|
||||
```json
|
||||
{
|
||||
"tier": "gold",
|
||||
"display_name": "Isabella García"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Step 6: Actualizar Tablas con User IDs
|
||||
|
||||
### 6.1 Actualizar Staff
|
||||
|
||||
Después de crear los usuarios en Supabase Auth, necesitas actualizar la tabla `staff` con los nuevos `user_id`.
|
||||
|
||||
1. Obten los nuevos `id` de `auth.users`:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
id as auth_user_id,
|
||||
email,
|
||||
raw_user_meta_data->>'role' as role,
|
||||
raw_user_meta_data->>'display_name' as display_name
|
||||
FROM auth.users
|
||||
WHERE raw_user_meta_data->>'role' IN ('admin', 'manager', 'staff', 'artist')
|
||||
ORDER BY raw_user_meta_data->>'role', raw_user_meta_data->>'display_name';
|
||||
```
|
||||
|
||||
2. Actualiza la tabla `staff`:
|
||||
|
||||
```sql
|
||||
-- Ejemplo para actualizar un usuario de staff
|
||||
UPDATE staff
|
||||
SET user_id = 'NUEVO_AUTH_USER_ID_DESDE_SUPABASE'
|
||||
WHERE display_name = 'Artist María García';
|
||||
|
||||
-- Repite para todos los usuarios de staff
|
||||
```
|
||||
|
||||
### 6.2 Actualizar Customers
|
||||
|
||||
1. Obten los nuevos `id` de `auth.users`:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
id as auth_user_id,
|
||||
email,
|
||||
raw_user_meta_data->>'tier' as tier,
|
||||
raw_user_meta_data->>'display_name' as display_name
|
||||
FROM auth.users
|
||||
WHERE email LIKE '%example.com'
|
||||
ORDER BY raw_user_meta_data->>'display_name';
|
||||
```
|
||||
|
||||
2. Actualiza la tabla `customers`:
|
||||
|
||||
```sql
|
||||
-- Ejemplo para actualizar un customer
|
||||
UPDATE customers
|
||||
SET user_id = 'NUEVO_AUTH_USER_ID_DESDE_SUPABASE'
|
||||
WHERE email = 'sofia.ramirez@example.com';
|
||||
|
||||
-- Repite para todos los customers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Step 7: Verificar Usuarios Creados
|
||||
|
||||
### 7.1 Verificar en Supabase Auth
|
||||
|
||||
1. Ve a **"Authentication" → "Users"**
|
||||
2. Verifica que todos los usuarios estén listados
|
||||
3. Debes ver:
|
||||
- 8 usuarios de staff (admin, managers, staff, artists)
|
||||
- 4 usuarios de customers
|
||||
|
||||
### 7.2 Verificar en Base de Datos
|
||||
|
||||
Ejecuta esta consulta en el SQL Editor:
|
||||
|
||||
```sql
|
||||
-- Verificar staff con user_id actualizado
|
||||
SELECT
|
||||
'STAFF' as type,
|
||||
s.display_name,
|
||||
s.role,
|
||||
s.user_id is not null as user_id_set,
|
||||
au.email as auth_user_email,
|
||||
au.raw_user_meta_data->>'display_name' as auth_display_name
|
||||
FROM staff s
|
||||
LEFT JOIN auth.users au ON s.user_id = au.id
|
||||
ORDER BY s.role, s.display_name;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
type | display_name | role | user_id_set | auth_user_email
|
||||
STAFF | Admin Principal | admin | true | admin@salonos.com
|
||||
STAFF | Manager Centro | manager | true | manager.centro@salonos.com
|
||||
STAFF | Manager Polanco | manager | true | manager.polanco@salonos.com
|
||||
STAFF | Staff Coordinadora | staff | true | staff.coordinadora@salonos.com
|
||||
STAFF | Artist María García | artist | true | artist.maria@salonos.com
|
||||
STAFF | Artist Ana Rodríguez | artist | true | artist.ana@salonos.com
|
||||
STAFF | Artist Carla López | artist | true | artist.carla@salonos.com
|
||||
STAFF | Artist Laura Martínez | artist | true | artist.laura@salonos.com
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Verificar customers con user_id actualizado
|
||||
SELECT
|
||||
'CUSTOMER' as type,
|
||||
c.first_name || ' ' || c.last_name as name,
|
||||
c.tier,
|
||||
c.user_id is not null as user_id_set,
|
||||
au.email as auth_user_email,
|
||||
au.raw_user_meta_data->>'tier' as auth_tier
|
||||
FROM customers c
|
||||
LEFT JOIN auth.users au ON c.user_id = au.id
|
||||
ORDER BY c.last_name, c.first_name;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
type | name | tier | user_id_set | auth_user_email
|
||||
CUSTOMER | Camila López | free | true | camila.lopez@example.com
|
||||
CUSTOMER | Isabella García | gold | true | isabella.garcia@example.com
|
||||
CUSTOMER | Sofía Ramírez | gold | true | sofia.ramirez@example.com
|
||||
CUSTOMER | Valentina Hernández | gold | true | valentina.hernandez@example.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Step 8: Configurar Email Templates (Opcional)
|
||||
|
||||
### 8.1 Confirm Signup Template
|
||||
|
||||
1. Ve a **"Authentication" → "Email Templates"**
|
||||
2. Haz clic en **"Confirm signup"**
|
||||
3. Personaliza el template:
|
||||
|
||||
```html
|
||||
<h2>Bienvenida a SalonOS</h2>
|
||||
|
||||
<p>Hola {{ .Email }}</p>
|
||||
|
||||
<p>Gracias por registrarte en SalonOS. Tu cuenta ha sido creada exitosamente.</p>
|
||||
|
||||
<p>Si no creaste esta cuenta, por favor ignora este email.</p>
|
||||
|
||||
<p>Saludos,<br>El equipo de SalonOS</p>
|
||||
```
|
||||
|
||||
### 8.2 Reset Password Template
|
||||
|
||||
1. Haz clic en **"Reset password"**
|
||||
2. Personaliza el template:
|
||||
|
||||
```html
|
||||
<h2>Restablecer Contraseña - SalonOS</h2>
|
||||
|
||||
<p>Hola {{ .Email }}</p>
|
||||
|
||||
<p>Hemos recibido una solicitud para restablecer tu contraseña en SalonOS.</p>
|
||||
|
||||
<p><a href="{{ .ConfirmationURL }}">Haz clic aquí para restablecer tu contraseña</a></p>
|
||||
|
||||
<p>Este enlace expirará en 24 horas.</p>
|
||||
|
||||
<p>Si no solicitaste restablecer tu contraseña, por favor ignora este email.</p>
|
||||
|
||||
<p>Saludos,<br>El equipo de SalonOS</p>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Step 9: Probar Autenticación
|
||||
|
||||
### 9.1 Probar Login con Staff
|
||||
|
||||
1. Ve a una página de login (aún no creada en el frontend)
|
||||
2. Intenta iniciar sesión con:
|
||||
- **Email:** `admin@salonos.com`
|
||||
- **Password:** `Admin123!`
|
||||
|
||||
### 9.2 Probar Login con Customer
|
||||
|
||||
1. Intenta iniciar sesión con:
|
||||
- **Email:** `sofia.ramirez@example.com`
|
||||
- **Password:** `Customer123!`
|
||||
|
||||
### 9.3 Verificar Token JWT
|
||||
|
||||
Ejecuta esta consulta en el SQL Editor después de iniciar sesión:
|
||||
|
||||
```sql
|
||||
-- Verificar sesión actual
|
||||
SELECT
|
||||
auth.uid() as current_user_id,
|
||||
auth.email() as current_user_email,
|
||||
auth.role() as current_user_role
|
||||
FROM (SELECT 1) as dummy;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Step 10: Configurar Policies de RLS con Auth
|
||||
|
||||
Las políticas de RLS ya están configuradas en la base de datos. Ahora que los usuarios están creados en Supabase Auth, las políticas deberían funcionar correctamente.
|
||||
|
||||
### Verificar que las Políticas Funcionan
|
||||
|
||||
Ejecuta esta consulta en el SQL Editor con diferentes usuarios:
|
||||
|
||||
```sql
|
||||
-- Probar como Admin
|
||||
-- (Inicia sesión como admin en Supabase Dashboard primero)
|
||||
SELECT
|
||||
'ADMIN TEST' as test_type,
|
||||
s.display_name,
|
||||
s.role,
|
||||
s.phone as can_see_phone
|
||||
FROM staff s
|
||||
LIMIT 1;
|
||||
|
||||
-- Esta consulta debería mostrar los datos del staff porque admin tiene acceso total
|
||||
```
|
||||
|
||||
```sql
|
||||
-- Probar como Artist
|
||||
-- (Inicia sesión como artist en Supabase Dashboard primero)
|
||||
SELECT
|
||||
'ARTIST TEST' as test_type,
|
||||
c.first_name,
|
||||
c.last_name,
|
||||
c.email as can_see_email,
|
||||
c.phone as can_see_phone
|
||||
FROM customers c
|
||||
LIMIT 1;
|
||||
|
||||
-- Esta consulta debería mostrar solo first_name y last_name, email y phone deberían ser NULL
|
||||
-- debido a la política RLS que restringe el acceso de artist a datos PII
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
### Error: "User already registered"
|
||||
|
||||
**Causa:** El usuario ya existe en Supabase Auth.
|
||||
|
||||
**Solución:**
|
||||
1. Ve a **"Authentication" → "Users"**
|
||||
2. Busca el usuario por email
|
||||
3. Si existe, usa ese usuario
|
||||
4. Si no, elige un email diferente
|
||||
|
||||
### Error: "Invalid login credentials"
|
||||
|
||||
**Causa:** El email o password es incorrecto.
|
||||
|
||||
**Solución:**
|
||||
1. Verifica el email y password
|
||||
2. Si olvidaste el password, usa el link de **"Forgot password"**
|
||||
3. O re crea el usuario en Supabase Auth
|
||||
|
||||
### Error: "User ID mismatch"
|
||||
|
||||
**Causa:** El `user_id` en la tabla staff/customers no coincide con el ID en `auth.users`.
|
||||
|
||||
**Solución:**
|
||||
1. Obtén el ID correcto de `auth.users`
|
||||
2. Actualiza la tabla staff/customers con el ID correcto
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentación Adicional
|
||||
|
||||
- **Supabase Auth Docs:** https://supabase.com/docs/guides/auth
|
||||
- **RLS Policies:** https://supabase.com/docs/guides/auth/row-level-security
|
||||
- **Email Templates:** https://supabase.com/docs/guides/auth/auth-email
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checklist de Configuración
|
||||
|
||||
- [ ] Email Provider habilitado y configurado
|
||||
- [ ] Site URL configurado
|
||||
- [ ] SMS Provider configurado (opcional)
|
||||
- [ ] 8 usuarios de staff creados en Supabase Auth
|
||||
- [ ] 4 usuarios de customers creados en Supabase Auth
|
||||
- [ ] Tabla staff actualizada con user_ids correctos
|
||||
- [ ] Tabla customers actualizada con user_ids correctos
|
||||
- [ ] Email templates configurados (opcional)
|
||||
- [ ] Login probado con admin
|
||||
- [ ] Login probado con customer
|
||||
- [ ] Políticas RLS verificadas
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Próximos Pasos
|
||||
|
||||
Después de completar la configuración de Auth:
|
||||
|
||||
1. **Implementar frontend de autenticación** en Next.js
|
||||
2. **Crear API endpoints** para login/logout
|
||||
3. **Implementar Tarea 1.3:** Short ID & Invitaciones (backend)
|
||||
4. **Implementar Tarea 1.4:** CRM Base (endpoints CRUD)
|
||||
|
||||
---
|
||||
|
||||
**¿Listo para continuar con el desarrollo de la aplicación?**
|
||||
734
docs/STEP_BY_STEP_VERIFICATION.md
Normal file
734
docs/STEP_BY_STEP_VERIFICATION.md
Normal file
@@ -0,0 +1,734 @@
|
||||
# 📋 Guía Paso a Paso - Verificación y Seed en Supabase Dashboard
|
||||
|
||||
## 🎯 Paso 1: Ejecutar Script de Verificación
|
||||
|
||||
### 1.1 Abrir Supabase SQL Editor
|
||||
|
||||
1. Ve a: **https://supabase.com/dashboard/project/pvvwbnybkadhreuqijsl/sql**
|
||||
2. Haz clic en **"New query"** para abrir un editor SQL vacío
|
||||
|
||||
### 1.2 Copiar Script de Verificación
|
||||
|
||||
Copia el contenido completo de: **`scripts/verify-migration.sql`**
|
||||
|
||||
**O ejecuta estas consultas una por una:**
|
||||
|
||||
#### Consulta 1: Verificar Tablas Creadas
|
||||
|
||||
```sql
|
||||
SELECT 'TABLAS' as verification_type, table_name as item
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name IN ('locations', 'resources', 'staff', 'services', 'customers', 'invitations', 'bookings', 'audit_logs')
|
||||
ORDER BY table_name;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
verification_type | item
|
||||
TABLAS | locations
|
||||
TABLAS | resources
|
||||
TABLAS | staff
|
||||
TABLAS | services
|
||||
TABLAS | customers
|
||||
TABLAS | invitations
|
||||
TABLAS | bookings
|
||||
TABLAS | audit_logs
|
||||
```
|
||||
|
||||
#### Consulta 2: Verificar Funciones Creadas
|
||||
|
||||
```sql
|
||||
SELECT 'FUNCIONES' as verification_type, routine_name as item
|
||||
FROM information_schema.routines
|
||||
WHERE routine_schema = 'public'
|
||||
ORDER BY routine_name;
|
||||
```
|
||||
|
||||
**Resultado esperado (14 funciones):**
|
||||
```
|
||||
verification_type | item
|
||||
FUNCIONES | generate_booking_short_id
|
||||
FUNCIONES | generate_invitation_code
|
||||
FUNCIONES | generate_short_id
|
||||
FUNCIONES | get_current_user_role
|
||||
FUNCIONES | get_week_start
|
||||
FUNCIONES | is_admin
|
||||
FUNCIONES | is_artist
|
||||
FUNCIONES | is_customer
|
||||
FUNCIONES | is_staff_or_higher
|
||||
FUNCIONES | log_audit
|
||||
FUNCIONES | reset_all_weekly_invitations
|
||||
FUNCIONES | reset_weekly_invitations_for_customer
|
||||
FUNCIONES | update_updated_at
|
||||
FUNCIONES | validate_secondary_artist_role
|
||||
```
|
||||
|
||||
#### Consulta 3: Verificar Triggers Activos
|
||||
|
||||
```sql
|
||||
SELECT 'TRIGGERS' as verification_type, trigger_name as item
|
||||
FROM information_schema.triggers
|
||||
WHERE trigger_schema = 'public'
|
||||
ORDER BY event_object_table, trigger_name;
|
||||
```
|
||||
|
||||
**Resultado esperado (17+ triggers):**
|
||||
```
|
||||
verification_type | item
|
||||
TRIGGERS | audit_bookings
|
||||
TRIGGERS | audit_customers
|
||||
TRIGGERS | audit_invitations
|
||||
TRIGGERS | audit_staff
|
||||
TRIGGERS | audit_services
|
||||
TRIGGERS | booking_generate_short_id
|
||||
TRIGGERS | bookings_updated_at
|
||||
TRIGGERS | customers_updated_at
|
||||
TRIGGERS | invitations_updated_at
|
||||
TRIGGERS | locations_updated_at
|
||||
TRIGGERS | resources_updated_at
|
||||
TRIGGERS | staff_updated_at
|
||||
TRIGGERS | services_updated_at
|
||||
TRIGGERS | validate_booking_secondary_artist
|
||||
...
|
||||
```
|
||||
|
||||
#### Consulta 4: Verificar Políticas RLS
|
||||
|
||||
```sql
|
||||
SELECT 'POLÍTICAS RLS' as verification_type, policyname as item
|
||||
FROM pg_policies
|
||||
WHERE schemaname = 'public'
|
||||
ORDER BY tablename, policyname;
|
||||
```
|
||||
|
||||
**Resultado esperado (20+ políticas):**
|
||||
```
|
||||
verification_type | item
|
||||
POLÍTICAS RLS | audit_logs_no_insert
|
||||
POLÍTICAS RLS | audit_logs_select_admin_manager
|
||||
POLÍTICAS RLS | audit_logs_select_staff_location
|
||||
POLÍTICAS RLS | bookings_create_own
|
||||
POLÍTICAS RLS | bookings_modify_admin_manager
|
||||
POLÍTICAS RLS | bookings_modify_staff_location
|
||||
POLÍTICAS RLS | bookings_no_modify_artist
|
||||
POLÍTICAS RLS | bookings_select_admin_manager
|
||||
POLÍTICAS RLS | bookings_select_artist_own
|
||||
POLÍTICAS RLS | bookings_select_own
|
||||
POLÍTICAS RLS | bookings_select_staff_location
|
||||
POLÍTICAS RLS | bookings_update_own
|
||||
POLÍTICAS RLS | customers_modify_admin_manager
|
||||
POLÍTICAS RLS | customers_modify_staff
|
||||
POLÍTICAS RLS | customers_select_admin_manager
|
||||
POLÍTICAS RLS | customers_select_artist_restricted
|
||||
POLÍTICAS RLS | customers_select_own
|
||||
POLÍTICAS RLS | customers_select_staff
|
||||
POLÍTICAS RLS | customers_update_own
|
||||
...
|
||||
```
|
||||
|
||||
#### Consulta 5: Verificar Tipos ENUM
|
||||
|
||||
```sql
|
||||
SELECT 'ENUM TYPES' as verification_type, typname as item
|
||||
FROM pg_type
|
||||
WHERE typtype = 'e'
|
||||
AND typname IN ('user_role', 'customer_tier', 'booking_status', 'invitation_status', 'resource_type', 'audit_action')
|
||||
ORDER BY typname;
|
||||
```
|
||||
|
||||
**Resultado esperado (6 tipos):**
|
||||
```
|
||||
verification_type | item
|
||||
ENUM TYPES | audit_action
|
||||
ENUM TYPES | booking_status
|
||||
ENUM TYPES | customer_tier
|
||||
ENUM TYPES | invitation_status
|
||||
ENUM TYPES | resource_type
|
||||
ENUM TYPES | user_role
|
||||
```
|
||||
|
||||
#### Consulta 6: Probar Short ID Generation
|
||||
|
||||
```sql
|
||||
SELECT 'SHORT ID TEST' as verification_type, generate_short_id() as item;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
verification_type | item
|
||||
SHORT ID TEST | A3F7X2
|
||||
```
|
||||
*(El string será diferente cada vez)*
|
||||
|
||||
#### Consulta 7: Probar Invitation Code Generation
|
||||
|
||||
```sql
|
||||
SELECT 'INVITATION CODE TEST' as verification_type, generate_invitation_code() as item;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
verification_type | item
|
||||
INVITATION CODE TEST | X9J4K2M5N8
|
||||
```
|
||||
*(El string será diferente cada vez)*
|
||||
|
||||
#### Consulta 8: Verificar Trigger de Validación de Secondary Artist
|
||||
|
||||
```sql
|
||||
SELECT 'SECONDARY ARTIST TRIGGER' as verification_type, trigger_name as item
|
||||
FROM information_schema.triggers
|
||||
WHERE trigger_name = 'validate_booking_secondary_artist';
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
verification_type | item
|
||||
SECONDARY ARTIST TRIGGER | validate_booking_secondary_artist
|
||||
```
|
||||
|
||||
#### Consulta 9: Verificar Función de Reset de Invitaciones
|
||||
|
||||
```sql
|
||||
SELECT 'RESET INVITATIONS FUNCTION' as verification_type, routine_name as item
|
||||
FROM information_schema.routines
|
||||
WHERE routine_name = 'reset_all_weekly_invitations';
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
verification_type | item
|
||||
RESET INVITATIONS FUNCTION | reset_all_weekly_invitations
|
||||
```
|
||||
|
||||
#### Consulta 10: Verificar Función de Validación de Secondary Artist
|
||||
|
||||
```sql
|
||||
SELECT 'VALIDATE SECONDARY ARTIST' as verification_type, routine_name as item
|
||||
FROM information_schema.routines
|
||||
WHERE routine_name = 'validate_secondary_artist_role';
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
verification_type | item
|
||||
VALIDATE SECONDARY ARTIST | validate_secondary_artist_role
|
||||
```
|
||||
|
||||
#### Consulta 11: Verificar Week Start Function
|
||||
|
||||
```sql
|
||||
SELECT 'WEEK START FUNCTION' as verification_type, get_week_start(CURRENT_DATE) as item;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
verification_type | item
|
||||
WEEK START FUNCTION | 2025-01-13
|
||||
```
|
||||
*(La fecha será el lunes de la semana actual)*
|
||||
|
||||
#### Consulta 12: Contar Elementos por Tipo
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
'RESUMEN' as verification_type,
|
||||
'Tablas: ' || (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public' AND table_name IN ('locations', 'resources', 'staff', 'services', 'customers', 'invitations', 'bookings', 'audit_logs')) as item
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'RESUMEN' as verification_type,
|
||||
'Funciones: ' || (SELECT COUNT(*) FROM information_schema.routines WHERE routine_schema = 'public') as item
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'RESUMEN' as verification_type,
|
||||
'Triggers: ' || (SELECT COUNT(*) FROM information_schema.triggers WHERE trigger_schema = 'public') as item
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'RESUMEN' as verification_type,
|
||||
'Políticas RLS: ' || (SELECT COUNT(*) FROM pg_policies WHERE schemaname = 'public') as item
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'RESUMEN' as verification_type,
|
||||
'Tipos ENUM: ' || (SELECT COUNT(*) FROM pg_type WHERE typtype = 'e' AND typname IN ('user_role', 'customer_tier', 'booking_status', 'invitation_status', 'resource_type', 'audit_action')) as item;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
verification_type | item
|
||||
RESUMEN | Tablas: 8
|
||||
RESUMEN | Funciones: 14
|
||||
RESUMEN | Triggers: 17
|
||||
RESUMEN | Políticas RLS: 24
|
||||
RESUMEN | Tipos ENUM: 6
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌱 Paso 2: Ejecutar Script de Seed de Datos
|
||||
|
||||
### 2.1 Abrir Nuevo Query en SQL Editor
|
||||
|
||||
1. En el mismo SQL Editor, haz clic en **"New query"**
|
||||
2. O pestaña para separar la verificación del seed
|
||||
|
||||
### 2.2 Ejecutar Seed por Secciones
|
||||
|
||||
**Opción A: Ejecutar el archivo completo**
|
||||
- Copia TODO el contenido de **`scripts/seed-data.sql`**
|
||||
- Pega en el SQL Editor
|
||||
- Haz clic en **"Run"**
|
||||
|
||||
**Opción B: Ejecutar por secciones** (recomendado para ver el progreso)
|
||||
|
||||
#### Sección 1: Crear Locations
|
||||
|
||||
```sql
|
||||
-- 1. Crear Locations
|
||||
INSERT INTO locations (name, timezone, address, phone, is_active)
|
||||
VALUES
|
||||
('Salón Principal - Centro', 'America/Mexico_City', 'Av. Reforma 222, Centro Histórico, Ciudad de México', '+52 55 1234 5678', true),
|
||||
('Salón Norte - Polanco', 'America/Mexico_City', 'Av. Masaryk 123, Polanco, Ciudad de México', '+52 55 2345 6789', true),
|
||||
('Salón Sur - Coyoacán', 'America/Mexico_City', 'Calle Hidalgo 456, Coyoacán, Ciudad de México', '+52 55 3456 7890', true);
|
||||
|
||||
-- Verificar
|
||||
SELECT 'Locations creadas:', COUNT(*) FROM locations;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
Locations creadas: | 3
|
||||
```
|
||||
|
||||
#### Sección 2: Crear Resources
|
||||
|
||||
```sql
|
||||
-- 2. Crear Resources
|
||||
INSERT INTO resources (location_id, name, type, capacity, is_active)
|
||||
SELECT
|
||||
(SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1),
|
||||
'Estación ' || generate_series(1, 3)::TEXT,
|
||||
'station',
|
||||
1,
|
||||
true
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM locations WHERE name = 'Salón Norte - Polanco' LIMIT 1),
|
||||
'Estación ' || generate_series(1, 2)::TEXT,
|
||||
'station',
|
||||
1,
|
||||
true
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM locations WHERE name = 'Salón Sur - Coyoacán' LIMIT 1),
|
||||
'Estación 1',
|
||||
'station',
|
||||
1,
|
||||
true;
|
||||
|
||||
-- Verificar
|
||||
SELECT 'Resources creadas:', COUNT(*) FROM resources;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
Resources creadas: | 6
|
||||
```
|
||||
|
||||
#### Sección 3: Crear Staff
|
||||
|
||||
```sql
|
||||
-- 3. Crear Staff
|
||||
INSERT INTO staff (user_id, location_id, role, display_name, phone, is_active)
|
||||
VALUES
|
||||
-- Admin Principal
|
||||
(uuid_generate_v4(), (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1), 'admin', 'Admin Principal', '+52 55 1111 2222', true),
|
||||
-- Managers
|
||||
(uuid_generate_v4(), (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1), 'manager', 'Manager Centro', '+52 55 2222 3333', true),
|
||||
(uuid_generate_v4(), (SELECT id FROM locations WHERE name = 'Salón Norte - Polanco' LIMIT 1), 'manager', 'Manager Polanco', '+52 55 6666 7777', true),
|
||||
-- Staff
|
||||
(uuid_generate_v4(), (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1), 'staff', 'Staff Coordinadora', '+52 55 3333 4444', true),
|
||||
-- Artists
|
||||
(uuid_generate_v4(), (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1), 'artist', 'Artist María García', '+52 55 4444 5555', true),
|
||||
(uuid_generate_v4(), (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1), 'artist', 'Artist Ana Rodríguez', '+52 55 5555 6666', true),
|
||||
(uuid_generate_v4(), (SELECT id FROM locations WHERE name = 'Salón Norte - Polanco' LIMIT 1), 'artist', 'Artist Carla López', '+52 55 7777 8888', true),
|
||||
(uuid_generate_v4(), (SELECT id FROM locations WHERE name = 'Salón Sur - Coyoacán' LIMIT 1), 'artist', 'Artist Laura Martínez', '+52 55 8888 9999', true);
|
||||
|
||||
-- Verificar
|
||||
SELECT 'Staff creados:', COUNT(*) FROM staff;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
Staff creados: | 8
|
||||
```
|
||||
|
||||
#### Sección 4: Crear Services
|
||||
|
||||
```sql
|
||||
-- 4. Crear Services
|
||||
INSERT INTO services (name, description, duration_minutes, base_price, requires_dual_artist, premium_fee_enabled, is_active)
|
||||
VALUES
|
||||
('Corte y Estilizado', 'Corte de cabello profesional con lavado y estilizado', 60, 500.00, false, false, true),
|
||||
('Color Completo', 'Tinte completo con protección capilar', 120, 1200.00, false, true, true),
|
||||
('Balayage Premium', 'Técnica de balayage con productos premium', 180, 2000.00, true, true, true),
|
||||
('Tratamiento Kératina', 'Tratamiento de kératina para cabello dañado', 90, 1500.00, false, false, true),
|
||||
('Peinado Evento', 'Peinado para eventos especiales', 45, 800.00, false, true, true),
|
||||
('Servicio Express (Dual Artist)', 'Servicio rápido con dos artists simultáneas', 30, 600.00, true, true, true);
|
||||
|
||||
-- Verificar
|
||||
SELECT 'Services creados:', COUNT(*) FROM services;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
Services creados: | 6
|
||||
```
|
||||
|
||||
#### Sección 5: Crear Customers
|
||||
|
||||
```sql
|
||||
-- 5. Crear Customers
|
||||
INSERT INTO customers (user_id, first_name, last_name, email, phone, tier, notes, total_spent, total_visits, last_visit_date, is_active)
|
||||
VALUES
|
||||
(uuid_generate_v4(), 'Sofía', 'Ramírez', 'sofia.ramirez@example.com', '+52 55 1111 1111', 'gold', 'Cliente VIP. Prefiere Artists María y Ana.', 15000.00, 25, '2025-12-20', true),
|
||||
(uuid_generate_v4(), 'Valentina', 'Hernández', 'valentina.hernandez@example.com', '+52 55 2222 2222', 'gold', 'Cliente regular. Prefiere horarios de la mañana.', 8500.00, 15, '2025-12-15', true),
|
||||
(uuid_generate_v4(), 'Camila', 'López', 'camila.lopez@example.com', '+52 55 3333 3333', 'free', 'Nueva cliente. Referida por Valentina.', 500.00, 1, '2025-12-10', true),
|
||||
(uuid_generate_v4(), 'Isabella', 'García', 'isabella.garcia@example.com', '+52 55 4444 4444', 'gold', 'Cliente VIP. Requiere servicio de Balayage.', 22000.00, 30, '2025-12-18', true);
|
||||
|
||||
-- Verificar
|
||||
SELECT 'Customers creados:', COUNT(*) FROM customers;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
Customers creados: | 4
|
||||
```
|
||||
|
||||
#### Sección 6: Crear Invitaciones
|
||||
|
||||
```sql
|
||||
-- 6. Crear Invitaciones (para clientes Gold)
|
||||
-- Resetear invitaciones para clientes Gold de la semana actual
|
||||
SELECT reset_weekly_invitations_for_customer((SELECT id FROM customers WHERE email = 'sofia.ramirez@example.com' LIMIT 1));
|
||||
SELECT reset_weekly_invitations_for_customer((SELECT id FROM customers WHERE email = 'valentina.hernandez@example.com' LIMIT 1));
|
||||
SELECT reset_weekly_invitations_for_customer((SELECT id FROM customers WHERE email = 'isabella.garcia@example.com' LIMIT 1));
|
||||
|
||||
-- Verificar
|
||||
SELECT 'Invitaciones creadas:', COUNT(*) FROM invitations WHERE status = 'pending';
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
Invitaciones creadas: | 15
|
||||
```
|
||||
*(5 por cada cliente Gold)*
|
||||
|
||||
#### Sección 7: Crear Bookings de Prueba
|
||||
|
||||
```sql
|
||||
-- 7. Crear Bookings de Prueba
|
||||
INSERT INTO bookings (
|
||||
customer_id,
|
||||
staff_id,
|
||||
location_id,
|
||||
resource_id,
|
||||
service_id,
|
||||
start_time_utc,
|
||||
end_time_utc,
|
||||
status,
|
||||
deposit_amount,
|
||||
total_amount,
|
||||
is_paid,
|
||||
payment_reference,
|
||||
notes
|
||||
)
|
||||
SELECT
|
||||
(SELECT id FROM customers WHERE email = 'sofia.ramirez@example.com' LIMIT 1),
|
||||
(SELECT id FROM staff WHERE display_name = 'Artist María García' LIMIT 1),
|
||||
(SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1),
|
||||
(SELECT id FROM resources WHERE location_id = (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1) LIMIT 1),
|
||||
(SELECT id FROM services WHERE name = 'Balayage Premium' LIMIT 1),
|
||||
NOW() + INTERVAL '1 day',
|
||||
NOW() + INTERVAL '4 hours',
|
||||
'confirmed',
|
||||
200.00,
|
||||
2000.00,
|
||||
true,
|
||||
'pay_test_001',
|
||||
'Balayage Premium para Sofía'
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM customers WHERE email = 'valentina.hernandez@example.com' LIMIT 1),
|
||||
(SELECT id FROM staff WHERE display_name = 'Artist Ana Rodríguez' LIMIT 1),
|
||||
(SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1),
|
||||
(SELECT id FROM resources WHERE location_id = (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1) LIMIT 1),
|
||||
(SELECT id FROM services WHERE name = 'Color Completo' LIMIT 1),
|
||||
NOW() + INTERVAL '2 days',
|
||||
NOW() + INTERVAL '4 hours',
|
||||
'confirmed',
|
||||
200.00,
|
||||
1200.00,
|
||||
true,
|
||||
'pay_test_002',
|
||||
'Color Completo para Valentina'
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM customers WHERE email = 'camila.lopez@example.com' LIMIT 1),
|
||||
(SELECT id FROM staff WHERE display_name = 'Artist María García' LIMIT 1),
|
||||
(SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1),
|
||||
(SELECT id FROM resources WHERE location_id = (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1) LIMIT 1),
|
||||
(SELECT id FROM services WHERE name = 'Corte y Estilizado' LIMIT 1),
|
||||
NOW() + INTERVAL '3 days',
|
||||
NOW() + INTERVAL '1 hour',
|
||||
'confirmed',
|
||||
50.00,
|
||||
500.00,
|
||||
true,
|
||||
'pay_test_003',
|
||||
'Primer corte para Camila'
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM customers WHERE email = 'isabella.garcia@example.com' LIMIT 1),
|
||||
(SELECT id FROM staff WHERE display_name = 'Artist María García' LIMIT 1),
|
||||
(SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1),
|
||||
(SELECT id FROM resources WHERE location_id = (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1) LIMIT 1),
|
||||
(SELECT id FROM services WHERE name = 'Servicio Express (Dual Artist)' LIMIT 1),
|
||||
NOW() + INTERVAL '4 days',
|
||||
NOW() + INTERVAL '30 minutes',
|
||||
'confirmed',
|
||||
200.00,
|
||||
600.00,
|
||||
true,
|
||||
'pay_test_004',
|
||||
'Servicio Express Dual Artist - Necesita secondary_artist'
|
||||
UNION ALL
|
||||
SELECT
|
||||
(SELECT id FROM customers WHERE email = 'sofia.ramirez@example.com' LIMIT 1),
|
||||
(SELECT id FROM staff WHERE display_name = 'Artist Ana Rodríguez' LIMIT 1),
|
||||
(SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1),
|
||||
(SELECT id FROM resources WHERE location_id = (SELECT id FROM locations WHERE name = 'Salón Principal - Centro' LIMIT 1) OFFSET 1 LIMIT 1),
|
||||
(SELECT id FROM services WHERE name = 'Peinado Evento' LIMIT 1),
|
||||
NOW() + INTERVAL '5 days',
|
||||
NOW() + INTERVAL '45 minutes',
|
||||
'pending',
|
||||
200.00,
|
||||
800.00,
|
||||
false,
|
||||
NULL,
|
||||
'Peinado para evento especial';
|
||||
|
||||
-- Verificar
|
||||
SELECT 'Bookings creados:', COUNT(*) FROM bookings;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
Bookings creados: | 5
|
||||
```
|
||||
|
||||
#### Sección 8: Actualizar Booking con Secondary Artist
|
||||
|
||||
```sql
|
||||
-- 8. Actualizar booking con secondary_artist (prueba de validación)
|
||||
UPDATE bookings
|
||||
SET secondary_artist_id = (SELECT id FROM staff WHERE display_name = 'Artist Carla López' LIMIT 1)
|
||||
WHERE payment_reference = 'pay_test_004';
|
||||
|
||||
-- Verificar
|
||||
SELECT 'Bookings con secondary_artist:', COUNT(*) FROM bookings WHERE secondary_artist_id IS NOT NULL;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
Bookings con secondary_artist: | 1
|
||||
```
|
||||
|
||||
#### Sección 9: Resumen Final
|
||||
|
||||
```sql
|
||||
-- 9. Resumen de datos creados
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE '==========================================';
|
||||
RAISE NOTICE 'SALONOS - SEED DE DATOS COMPLETADO';
|
||||
RAISE NOTICE '==========================================';
|
||||
RAISE NOTICE 'Locations: %', (SELECT COUNT(*) FROM locations);
|
||||
RAISE NOTICE 'Resources: %', (SELECT COUNT(*) FROM resources);
|
||||
RAISE NOTICE 'Staff: %', (SELECT COUNT(*) FROM staff);
|
||||
RAISE NOTICE 'Services: %', (SELECT COUNT(*) FROM services);
|
||||
RAISE NOTICE 'Customers: %', (SELECT COUNT(*) FROM customers);
|
||||
RAISE NOTICE 'Invitations: %', (SELECT COUNT(*) FROM invitations WHERE status = 'pending');
|
||||
RAISE NOTICE 'Bookings: %', (SELECT COUNT(*) FROM bookings);
|
||||
RAISE NOTICE '==========================================';
|
||||
RAISE NOTICE '✅ Base de datos lista para desarrollo';
|
||||
RAISE NOTICE '==========================================';
|
||||
END
|
||||
$$;
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
NOTICE: ==========================================
|
||||
NOTICE: SALONOS - SEED DE DATOS COMPLETADO
|
||||
NOTICE: ==========================================
|
||||
NOTICE: Locations: 3
|
||||
NOTICE: Resources: 6
|
||||
NOTICE: Staff: 8
|
||||
NOTICE: Services: 6
|
||||
NOTICE: Customers: 4
|
||||
NOTICE: Invitations: 15
|
||||
NOTICE: Bookings: 5
|
||||
NOTICE: ==========================================
|
||||
NOTICE: ✅ Base de datos lista para desarrollo
|
||||
NOTICE: ==========================================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Paso 3: Pruebas Adicionales
|
||||
|
||||
### Test 1: Verificar Bookings con Detalles
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
b.short_id,
|
||||
c.first_name || ' ' || c.last_name as customer,
|
||||
s.display_name as artist,
|
||||
sa.display_name as secondary_artist,
|
||||
svc.name as service,
|
||||
b.start_time_utc,
|
||||
b.end_time_utc,
|
||||
b.status,
|
||||
b.total_amount
|
||||
FROM bookings b
|
||||
JOIN customers c ON b.customer_id = c.id
|
||||
JOIN staff s ON b.staff_id = s.id
|
||||
LEFT JOIN staff sa ON b.secondary_artist_id = sa.id
|
||||
JOIN services svc ON b.service_id = svc.id
|
||||
ORDER BY b.start_time_utc;
|
||||
```
|
||||
|
||||
### Test 2: Verificar Invitaciones
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
i.code,
|
||||
inv.first_name || ' ' || inv.last_name as inviter,
|
||||
i.status,
|
||||
i.week_start_date,
|
||||
i.expiry_date
|
||||
FROM invitations i
|
||||
JOIN customers inv ON i.inviter_id = inv.id
|
||||
WHERE i.status = 'pending'
|
||||
ORDER BY inv.first_name, i.expiry_date;
|
||||
```
|
||||
|
||||
### Test 3: Verificar Staff por Ubicación y Rol
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
l.name as location,
|
||||
s.role,
|
||||
s.display_name,
|
||||
s.phone,
|
||||
s.is_active
|
||||
FROM staff s
|
||||
JOIN locations l ON s.location_id = l.id
|
||||
ORDER BY l.name, s.role, s.display_name;
|
||||
```
|
||||
|
||||
### Test 4: Verificar Auditoría
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
entity_type,
|
||||
action,
|
||||
new_values->>'operation' as operation,
|
||||
new_values->>'table_name' as table_name,
|
||||
created_at
|
||||
FROM audit_logs
|
||||
WHERE new_values->>'table_name' = 'invitations'
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checklist de Verificación
|
||||
|
||||
Después de completar todos los pasos, asegúrate de:
|
||||
|
||||
### Verificación de Migraciones
|
||||
- [x] 8 tablas creadas (locations, resources, staff, services, customers, invitations, bookings, audit_logs)
|
||||
- [x] 14 funciones creadas
|
||||
- [x] 17+ triggers activos
|
||||
- [x] 20+ políticas RLS configuradas
|
||||
- [x] 6 tipos ENUM creados
|
||||
- [x] Short ID generable
|
||||
- [x] Código de invitación generable
|
||||
|
||||
### Verificación de Seed de Datos
|
||||
- [ ] 3 locations creadas
|
||||
- [ ] 6 resources creadas
|
||||
- [ ] 8 staff creados
|
||||
- [ ] 6 services creados
|
||||
- [ ] 4 customers creados
|
||||
- [ ] 15 invitaciones creadas (5 por cliente Gold)
|
||||
- [ ] 5 bookings creados
|
||||
- [ ] 1 booking con secondary_artist
|
||||
|
||||
### Pruebas Funcionales
|
||||
- [ ] Short ID se genera correctamente
|
||||
- [ ] Código de invitación se genera correctamente
|
||||
- [ ] Bookings se crean con short_id automático
|
||||
- [ ] Secondary artist validation funciona
|
||||
- [ ] Auditoría se registra correctamente
|
||||
- [ ] Reset de invitaciones funciona
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
### Error: "relation already exists"
|
||||
|
||||
**Causa:** Ya ejecutaste esta sección anteriormente.
|
||||
|
||||
**Solución:** Continúa con la siguiente sección. Los datos ya existen.
|
||||
|
||||
### Error: "null value in column violates not-null constraint"
|
||||
|
||||
**Causa:** Falta crear datos dependientes primero.
|
||||
|
||||
**Solución:** Ejecuta las secciones en orden: Locations → Resources → Staff → Services → Customers → Invitations → Bookings
|
||||
|
||||
### Error: "insert or update on table violates foreign key constraint"
|
||||
|
||||
**Causa:** Estás intentando insertar un booking con un customer_id que no existe.
|
||||
|
||||
**Solución:** Verifica que el customer exista antes de crear el booking:
|
||||
|
||||
```sql
|
||||
SELECT * FROM customers WHERE email = 'sofia.ramirez@example.com';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentación Adicional
|
||||
|
||||
- **docs/POST_MIGRATION_SUCCESS.md** - Guía general post-migración
|
||||
- **scripts/verify-migration.sql** - Script completo de verificación
|
||||
- **scripts/seed-data.sql** - Script completo de seed
|
||||
- **FASE_1_STATUS.md** - Estado actualizado de la Fase 1
|
||||
|
||||
---
|
||||
|
||||
**¿Listo para continuar con la configuración de Auth en Supabase Dashboard?**
|
||||
322
docs/SUPABASE_DASHBOARD_MIGRATION.md
Normal file
322
docs/SUPABASE_DASHBOARD_MIGRATION.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# 🚀 Guía de Ejecución de Migraciones - Supabase Dashboard
|
||||
|
||||
## ⚠️ Situación Actual
|
||||
|
||||
No es posible ejecutar las migraciones directamente desde la línea de comandos en este entorno debido a restricciones de red (el puerto 5432 no es accesible).
|
||||
|
||||
## ✅ Solución: Ejecutar desde Supabase Dashboard
|
||||
|
||||
Esta es la forma más segura y confiable de ejecutar las migraciones.
|
||||
|
||||
**Nota:** Se ha corregido un error en la migración original. PostgreSQL no permite subqueries en constraints CHECK. Se ha reemplazado el constraint problemático con un trigger de validación. Usa el archivo `00_FULL_MIGRATION_CORRECTED.sql`.
|
||||
|
||||
---
|
||||
|
||||
## 📋 PASOS A SEGUIR
|
||||
|
||||
### Paso 1: Abrir Supabase SQL Editor
|
||||
|
||||
1. Ve a: **https://supabase.com/dashboard/project/pvvwbnybkadhreuqijsl/sql**
|
||||
|
||||
2. Haz clic en **"New query"** para abrir un editor SQL vacío.
|
||||
|
||||
### Paso 2: Copiar el contenido del archivo de migración
|
||||
|
||||
El archivo consolidado corregido está en:
|
||||
```
|
||||
db/migrations/00_FULL_MIGRATION_CORRECTED.sql
|
||||
```
|
||||
|
||||
Copia **TODO** el contenido de este archivo.
|
||||
|
||||
### Paso 3: Pegar y ejecutar en Supabase Dashboard
|
||||
|
||||
1. Pega el contenido completo del archivo en el editor SQL.
|
||||
2. Haz clic en el botón **"Run"** (o presiona `Ctrl+Enter` / `Cmd+Enter`).
|
||||
3. Espera a que se complete la ejecución (puede tardar 10-30 segundos).
|
||||
|
||||
### Paso 4: Verificar la ejecución
|
||||
|
||||
Al finalizar, deberías ver:
|
||||
- ✅ Un mensaje de éxito
|
||||
- ✅ Notificaciones sobre la creación de tablas, funciones y triggers
|
||||
- ✅ Un resumen de lo que se ha creado:
|
||||
- 8 tablas
|
||||
- 13 funciones
|
||||
- 15+ triggers
|
||||
- 20+ políticas RLS
|
||||
- 6 tipos ENUM
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Verificación Manual
|
||||
|
||||
Si deseas verificar que todo se creó correctamente, ejecuta estas consultas en el SQL Editor:
|
||||
|
||||
### Verificar Tablas
|
||||
|
||||
```sql
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
ORDER BY table_name;
|
||||
```
|
||||
|
||||
**Esperado:** 8 tablas (locations, resources, staff, services, customers, invitations, bookings, audit_logs)
|
||||
|
||||
### Verificar Funciones
|
||||
|
||||
```sql
|
||||
SELECT routine_name
|
||||
FROM information_schema.routines
|
||||
WHERE routine_schema = 'public'
|
||||
ORDER BY routine_name;
|
||||
```
|
||||
|
||||
**Esperado:** 14 funciones incluyendo `generate_short_id`, `reset_weekly_invitations_for_customer`, `validate_secondary_artist_role`, etc.
|
||||
|
||||
### Verificar Triggers
|
||||
|
||||
```sql
|
||||
SELECT trigger_name, event_object_table
|
||||
FROM information_schema.triggers
|
||||
WHERE trigger_schema = 'public'
|
||||
ORDER BY event_object_table, trigger_name;
|
||||
```
|
||||
|
||||
**Esperado:** Múltiples triggers para auditoría y timestamps
|
||||
|
||||
### Verificar Políticas RLS
|
||||
|
||||
```sql
|
||||
SELECT schemaname, tablename, policyname, permissive, roles, cmd
|
||||
FROM pg_policies
|
||||
WHERE schemaname = 'public'
|
||||
ORDER BY tablename, policyname;
|
||||
```
|
||||
|
||||
**Esperado:** 20+ políticas por rol (admin, manager, staff, artist, customer)
|
||||
|
||||
### Probar Generación de Short ID
|
||||
|
||||
```sql
|
||||
SELECT generate_short_id();
|
||||
```
|
||||
|
||||
**Esperado:** Un string de 6 caracteres alfanuméricos (ej: "A3F7X2")
|
||||
|
||||
### Probar Generación de Código de Invitación
|
||||
|
||||
```sql
|
||||
SELECT generate_invitation_code();
|
||||
```
|
||||
|
||||
**Esperado:** Un string de 10 caracteres alfanuméricos (ej: "X9J4K2M5N8")
|
||||
|
||||
### Verificar Tipos ENUM
|
||||
|
||||
```sql
|
||||
SELECT typname, enumlabel
|
||||
FROM pg_enum e
|
||||
JOIN pg_type t ON e.enumtypid = t.oid
|
||||
WHERE t.typtype = 'e'
|
||||
ORDER BY t.typname, e.enumsortorder;
|
||||
```
|
||||
|
||||
**Esperado:** 6 tipos ENUM (user_role, customer_tier, booking_status, invitation_status, resource_type, audit_action)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Próximos Pasos (Después de las Migraciones)
|
||||
|
||||
### 1. Configurar Auth en Supabase Dashboard
|
||||
|
||||
Ve a **Authentication → Providers** y configura:
|
||||
|
||||
1. **Email Provider**: Habilitar email authentication
|
||||
2. **SMS Provider**: Configurar Twilio para SMS (opcional)
|
||||
3. **Email Templates**: Personalizar templates de Magic Link
|
||||
|
||||
### 2. Crear Usuarios de Prueba
|
||||
|
||||
Ve a **Authentication → Users** y crea:
|
||||
|
||||
- **1 Admin**: Para acceso total
|
||||
- **1 Manager**: Para gestión operacional
|
||||
- **1 Staff**: Para coordinación
|
||||
- **1 Artist**: Para ejecución de servicios
|
||||
- **1 Customer Free**: Para clientes regulares
|
||||
- **1 Customer Gold**: Para clientes VIP
|
||||
|
||||
### 3. Asignar Roles a Staff
|
||||
|
||||
Ejecuta este SQL en el editor para crear staff de prueba:
|
||||
|
||||
```sql
|
||||
-- Insertar admin
|
||||
INSERT INTO staff (user_id, location_id, role, display_name, is_active)
|
||||
VALUES ('UUID_DEL_USUARIO_ADMIN', 'LOCATION_UUID', 'admin', 'Admin Principal', true);
|
||||
|
||||
-- Insertar manager
|
||||
INSERT INTO staff (user_id, location_id, role, display_name, is_active)
|
||||
VALUES ('UUID_DEL_USUARIO_MANAGER', 'LOCATION_UUID', 'manager', 'Manager Centro', true);
|
||||
|
||||
-- Insertar staff
|
||||
INSERT INTO staff (user_id, location_id, role, display_name, is_active)
|
||||
VALUES ('UUID_DEL_USUARIO_STAFF', 'LOCATION_UUID', 'staff', 'Staff Coordinadora', true);
|
||||
|
||||
-- Insertar artist
|
||||
INSERT INTO staff (user_id, location_id, role, display_name, is_active)
|
||||
VALUES ('UUID_DEL_USUARIO_ARTIST', 'LOCATION_UUID', 'artist', 'Artist María García', true);
|
||||
```
|
||||
|
||||
### 4. Crear Datos de Prueba
|
||||
|
||||
Opcionalmente, puedes ejecutar el script de seed desde la línea de comandos (si tienes acceso):
|
||||
|
||||
```bash
|
||||
npm run db:seed
|
||||
```
|
||||
|
||||
O manualmente desde el SQL Editor:
|
||||
|
||||
```sql
|
||||
-- Crear una location de prueba
|
||||
INSERT INTO locations (name, timezone, address, phone, is_active)
|
||||
VALUES ('Salón Principal - Centro', 'America/Mexico_City', 'Av. Reforma 222', '+52 55 1234 5678', true);
|
||||
|
||||
-- Crear un servicio de prueba
|
||||
INSERT INTO services (name, description, duration_minutes, base_price, requires_dual_artist, premium_fee_enabled, is_active)
|
||||
VALUES ('Corte y Estilizado', 'Corte de cabello profesional', 60, 500.00, false, false, true);
|
||||
|
||||
-- Crear un customer de prueba
|
||||
INSERT INTO customers (user_id, first_name, last_name, email, phone, tier, is_active)
|
||||
VALUES ('UUID_DEL_USUARIO', 'Sofía', 'Ramírez', 'sofia@example.com', '+52 55 1111 2222', 'gold', true);
|
||||
```
|
||||
|
||||
### 5. Probar el Sistema
|
||||
|
||||
Una vez que tengas datos de prueba, puedes:
|
||||
|
||||
1. **Probar Short ID**:
|
||||
```sql
|
||||
SELECT generate_short_id();
|
||||
```
|
||||
|
||||
2. **Probar Código de Invitación**:
|
||||
```sql
|
||||
SELECT generate_invitation_code();
|
||||
```
|
||||
|
||||
3. **Probar Reset de Invitaciones**:
|
||||
```sql
|
||||
SELECT reset_weekly_invitations_for_customer('CUSTOMER_UUID');
|
||||
```
|
||||
|
||||
4. **Crear un Booking**:
|
||||
```sql
|
||||
INSERT INTO bookings (customer_id, staff_id, location_id, resource_id, service_id, start_time_utc, end_time_utc, status, deposit_amount, total_amount, is_paid)
|
||||
VALUES ('CUSTOMER_UUID', 'STAFF_UUID', 'LOCATION_UUID', 'RESOURCE_UUID', 'SERVICE_UUID', NOW() + INTERVAL '1 day', NOW() + INTERVAL '2 days', 'confirmed', 200.00, 500.00, true);
|
||||
```
|
||||
|
||||
5. **Verificar Auditoría**:
|
||||
```sql
|
||||
SELECT * FROM audit_logs ORDER BY created_at DESC LIMIT 10;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Solución de Problemas
|
||||
|
||||
### Error: "relation already exists"
|
||||
|
||||
**Causa:** Las tablas ya existen. La migración se ejecutó parcialmente o anteriormente.
|
||||
|
||||
**Solución:** Continúa con la ejecución o limpia la base de datos:
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS audit_logs CASCADE;
|
||||
DROP TABLE IF EXISTS bookings CASCADE;
|
||||
DROP TABLE IF EXISTS invitations CASCADE;
|
||||
DROP TABLE IF EXISTS customers CASCADE;
|
||||
DROP TABLE IF EXISTS services CASCADE;
|
||||
DROP TABLE IF EXISTS staff CASCADE;
|
||||
DROP TABLE IF EXISTS resources CASCADE;
|
||||
DROP TABLE IF EXISTS locations CASCADE;
|
||||
```
|
||||
|
||||
Luego ejecuta la migración nuevamente.
|
||||
|
||||
### Error: "function already exists"
|
||||
|
||||
**Causa:** Las funciones ya existen.
|
||||
|
||||
**Solución:** Esto es normal si estás reejecutando la migración. Los nuevos `CREATE OR REPLACE FUNCTION` no fallarán.
|
||||
|
||||
### Error: RLS no funciona
|
||||
|
||||
**Causa:** RLS no está habilitado o el usuario no tiene un rol asignado.
|
||||
|
||||
**Solución:**
|
||||
|
||||
1. Verifica que RLS está habilitado:
|
||||
```sql
|
||||
SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';
|
||||
```
|
||||
|
||||
2. Verifica que el usuario tenga un registro en `staff` o `customers`:
|
||||
```sql
|
||||
SELECT * FROM staff WHERE user_id = auth.uid();
|
||||
SELECT * FROM customers WHERE user_id = auth.uid();
|
||||
```
|
||||
|
||||
3. Verifica las políticas RLS:
|
||||
```sql
|
||||
SELECT * FROM pg_policies WHERE schemaname = 'public';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentación Adicional
|
||||
|
||||
- **PRD.md:** Reglas de negocio del sistema
|
||||
- **TASKS.md:** Plan de ejecución por fases
|
||||
- **AGENTS.md:** Roles y responsabilidades de IA
|
||||
- **docs/MIGRATION_GUIDE.md:** Guía técnica de migraciones
|
||||
- **db/migrations/README.md:** Documentación técnica de migraciones
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checklist de Verificación
|
||||
|
||||
Antes de continuar con el desarrollo, asegúrate de:
|
||||
|
||||
- [ ] Las 8 tablas están creadas
|
||||
- [ ] Las 13 funciones están creadas
|
||||
- [ ] Los 15+ triggers están activos
|
||||
- [ ] Las 20+ políticas RLS están configuradas
|
||||
- [ ] Los 6 tipos ENUM están creados
|
||||
- [ ] `generate_short_id()` funciona
|
||||
- [ ] `generate_invitation_code()` funciona
|
||||
- [ ] `reset_weekly_invitations_for_customer()` funciona
|
||||
- [ ] Auth está configurado
|
||||
- [ ] Usuarios de prueba están creados
|
||||
- [ ] Staff de prueba está creado con roles correctos
|
||||
- [ ] Se puede crear un booking manualmente
|
||||
- [ ] La auditoría se está registrando correctamente
|
||||
|
||||
---
|
||||
|
||||
## 🎉 ¡Listo para el Desarrollo!
|
||||
|
||||
Una vez que hayas completado todos estos pasos, tu base de datos de SalonOS estará lista para el desarrollo de:
|
||||
|
||||
- **Tarea 1.3:** Short ID & Invitaciones (backend endpoints)
|
||||
- **Tarea 1.4:** CRM Base (endpoints CRUD)
|
||||
- **Fase 2:** Motor de Agendamiento
|
||||
- **Fase 3:** Pagos y Protección
|
||||
- **Fase 4:** HQ Dashboard
|
||||
|
||||
---
|
||||
|
||||
**¿Necesitas ayuda con algún paso específico?** Puedo proporcionarte consultas SQL adicionales o ayuda para configurar los usuarios de prueba.
|
||||
Reference in New Issue
Block a user