refactor: migrate from old db/ to supabase CLI structure

- Migrated database schema from db/migrations to supabase/migrations
- Added Supabase CLI configuration (config.toml, .gitignore)
- Added kiosk role and amenities tables for touch kiosks
- Added notification system for artist alerts
- Added seed data with test locations and staff
- Removed old migration scripts and documentation
- Updated tasks_mg.md with current setup

Features:
- 2 locations: ANCHOR:23 - Via KLAVA and TEST
- Kiosk role for touch screen check-in/booking
- Amenities: coffee, cocktails, mocktails for clients
- Notifications when client arrives
- 1 staff + 4 artists + 1 kiosk per location
- Services: hair, nails, makeup, lashes
This commit is contained in:
Marco Gallegos
2026-01-16 00:01:32 -06:00
parent a2054ba403
commit 18071cfb63
34 changed files with 1168 additions and 8626 deletions

View File

@@ -1,148 +0,0 @@
# 🎉 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

View File

@@ -1,314 +0,0 @@
# ✅ 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.

View File

@@ -1,267 +0,0 @@
# 🚀 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

View File

@@ -1,370 +0,0 @@
# 🎉 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?**

View File

@@ -1,375 +0,0 @@
# 🎉 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!** 🚀

View File

@@ -1,610 +0,0 @@
# 🔐 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?**

View File

@@ -1,734 +0,0 @@
# 📋 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?**

View File

@@ -1,322 +0,0 @@
# 🚀 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.