# Sprint 8 — Etapa 1: Migración DB (aditiva) > **Tipo:** Base de datos — migración aditiva > **Rama:** main > **Estimación:** 1-2 horas > **Dependencias:** Etapa 0 completada ✅ --- ## Objetivo Crear la infraestructura de datos para el modelo `organizations → areas → processes` sin romper nada de lo que ya funciona. Esta migración es **completamente aditiva**: crea lo nuevo, no elimina lo viejo. Las columnas `client` y `group_id` de `processes`, y la tabla `process_groups`, se eliminan en una migration de limpieza posterior (después de que Etapas 3-4 migren la UI). --- ## Contexto del schema actual Leer las migraciones en `supabase/migrations/` antes de escribir cualquier SQL. Schema relevante al momento de esta etapa: **`processes` — columnas existentes relevantes:** - `client text NOT NULL DEFAULT ''` — el campo libre "clientName" (se elimina más adelante) - `group_id uuid REFERENCES public.process_groups(id) ON DELETE SET NULL` — agrupador viejo (se elimina más adelante) - `org_id uuid REFERENCES public.organizations(id) ON DELETE SET NULL` — ← este es el correcto, agregado en migración 014 **`process_groups` — tabla existente:** - `id, name, created_by, created_at, updated_at` — no tiene FKs entrantes salvo `processes.group_id` **`organizations` — tabla existente desde migración 014:** - InQuality existe con `is_provider = true` - Otras orgs creadas automáticamente desde `processes.client` en la migración 014 **Funciones SECURITY DEFINER disponibles (migración 015):** - `public.is_inquality()` → true si el usuario pertenece a InQuality - `public.current_org()` → org_id del usuario actual - `public.current_platform_role()` → rol del usuario actual - `public.is_platform_admin()` → true si es platform_admin --- ## Alcance estricto — Migration 017 Crear el archivo `supabase/migrations/017_create_areas.sql`. ### Paso 1: Crear tabla `areas` ```sql -- Nueva tabla areas: reemplazará process_groups como agrupador normalizado por org CREATE TABLE IF NOT EXISTS public.areas ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), org_id uuid NOT NULL REFERENCES public.organizations(id) ON DELETE CASCADE, name text NOT NULL, description text, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); ALTER TABLE public.areas ENABLE ROW LEVEL SECURITY; ``` ### Paso 2: Agregar `area_id` a `processes` ```sql -- area_id reemplazará group_id como agrupador normalizado dentro de una org -- Nullable: procesos sin área asignada son válidos ("Sin área") ALTER TABLE public.processes ADD COLUMN IF NOT EXISTS area_id uuid REFERENCES public.areas(id) ON DELETE SET NULL; ``` ### Paso 3: RLS para `areas` ```sql -- SELECT: InQuality ve todas las áreas; cliente ve solo las de su org CREATE POLICY "areas_select" ON public.areas FOR SELECT USING ( public.is_inquality() OR org_id = public.current_org() ); -- INSERT / UPDATE / DELETE: solo platform_admin CREATE POLICY "areas_insert" ON public.areas FOR INSERT WITH CHECK ( public.current_platform_role() = 'platform_admin' ); CREATE POLICY "areas_update" ON public.areas FOR UPDATE USING ( public.current_platform_role() = 'platform_admin' ); CREATE POLICY "areas_delete" ON public.areas FOR DELETE USING ( public.current_platform_role() = 'platform_admin' ); ``` ### Paso 4: Migración de datos — asignar procesos sin org a InQuality ```sql -- Procesos sin org_id son datos de prueba de InQuality. -- Asignarlos a la org de InQuality (is_provider = true) para limpiar los NULLs. UPDATE public.processes SET org_id = ( SELECT id FROM public.organizations WHERE is_provider = true LIMIT 1 ) WHERE org_id IS NULL; ``` ### Lo que esta migración NO hace (intencional) ``` ❌ NO dropea la columna `client` de processes ❌ NO dropea la columna `group_id` de processes ❌ NO dropea la tabla `process_groups` ❌ NO modifica ninguna política RLS existente ``` Estas eliminaciones ocurren en una migration posterior, después de que la UI deje de usar esas columnas. --- ## Verificación post-migración Después de aplicar la migración en Supabase (SQL Editor o `supabase db push`), verificar con estas queries: ```sql -- 1. Verificar que la tabla areas existe con RLS activa SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public' AND tablename = 'areas'; -- Esperado: rowsecurity = true -- 2. Verificar columna area_id en processes SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema = 'public' AND table_name = 'processes' AND column_name = 'area_id'; -- Esperado: una fila, data_type = 'uuid', is_nullable = 'YES' -- 3. Verificar que no quedan procesos sin org_id SELECT COUNT(*) as sin_org FROM public.processes WHERE org_id IS NULL; -- Esperado: 0 -- 4. Verificar políticas de areas SELECT policyname, cmd FROM pg_policies WHERE schemaname = 'public' AND tablename = 'areas'; -- Esperado: 4 políticas (areas_select, areas_insert, areas_update, areas_delete) -- 5. Verificar que process_groups sigue existiendo (no se eliminó) SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'process_groups'; -- Esperado: 1 fila (la tabla sigue ahí — se elimina más adelante) ``` --- ## Criterios de validación - [ ] Archivo `supabase/migrations/017_create_areas.sql` creado - [ ] Migración aplicada en Supabase (reportar resultado del SQL Editor o `supabase db push`) - [ ] Las 5 queries de verificación retornan los valores esperados - [ ] `npm run build` sin errores (no hay cambios en TypeScript — build debe ser idéntico al de Etapa 0) - [ ] `npm run test` sin regresiones (592 tests, sin cambios) - [ ] `process_groups` sigue existiendo en la DB (verificar con query 5) --- ## Restricciones explícitas - **NO** modificar ningún archivo TypeScript en esta etapa — solo SQL - **NO** dropear `process_groups`, `client`, ni `group_id` — eso viene después - **NO** tocar las políticas RLS existentes en `processes`, `organizations`, ni otras tablas - **NO** crear datos de prueba en `areas` — la tabla arranca vacía --- ## Archivos autorizados a modificar/crear ``` simulador-web/ └── supabase/ └── migrations/ └── 017_create_areas.sql ← único archivo a crear ``` Ningún archivo TypeScript debe cambiar en esta etapa. --- ## Commit esperado ``` feat(db): migración 017 — tabla areas, area_id en processes, RLS, bulk-assign org ``` --- ## Reporte esperado Al finalizar, reportar: 1. Contenido exacto del archivo `017_create_areas.sql` 2. Resultado de aplicar la migración (output de Supabase) 3. Resultado de las 5 queries de verificación (con los valores reales) 4. Resultado de `npm run build` y `npm run test` 5. Confirmar que `process_groups` sigue existiendo (query 5)