Files
AnchorOS/docs/MIGRATION_GUIDE.md
Marco Gallegos 4707ddbd5a 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)
2026-01-15 14:58:28 -06:00

7.2 KiB

🚀 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:

cp .env.example .env.local

Edita el archivo .env.local con tus credenciales de Supabase:

# 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
  2. Selecciona tu proyecto
  3. Ve a Settings → API
  4. Copia:
    • Project URLNEXT_PUBLIC_SUPABASE_URL
    • anon publicNEXT_PUBLIC_SUPABASE_ANON_KEY
    • service_roleSUPABASE_SERVICE_ROLE_KEY

🎯 Paso 2: Ejecutar Migraciones

Opción A: Automática (Recomendada)

Usa el script de migración automatizado:

# 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:

# 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
  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

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

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

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

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

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

SELECT generate_short_id();

Esperado: Un string de 6 caracteres alfanuméricos (ej: "A3F7X2")

Generar Código de Invitación

SELECT generate_invitation_code();

Esperado: Un string de 10 caracteres alfanuméricos (ej: "X9J4K2M5N8")

Obtener Inicio de Semana

SELECT get_week_start(CURRENT_DATE);

Esperado: El lunes de la semana actual

Resetear Invitaciones de un Cliente

-- 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:

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