Nivel: N4 – CCO / Desarrollo
Esquema completo de la base de datos del Nucleo 1: Usuarios y Perfiles en Supabase (PostgreSQL). Define tablas, columnas, relaciones, RLS y triggers. Este documento es el plano de construccion para el desarrollador.
El Nucleo 1 es el centro de todo el ERP. La tabla public.profiles es la Celula Madre: todas las demas tablas de todos los nucleos la referencian. Sin usuario, no hay pedido, no hay cuota, no hay expediente.
Cada tabla tiene reglas que controlan quien puede ver y editar que. Esto se aplica a nivel de base de datos — ni el frontend ni el backend pueden saltarselas.
4.1 profiles
Operacion
Quien puede
Regla SQL simplificada
SELECT (ver)
Tu propio perfil + admins
auth.uid() = id OR has_role('admin')
UPDATE (editar)
Tu propio perfil (campos permitidos)
auth.uid() = id (no puede cambiar cedula ni estatus)
INSERT (crear)
Solo el trigger de auth
Se crea automaticamente al registrarse
DELETE (borrar)
Nadie
Prohibido. Se cambia estatus a ‘inactivo’
4.2 user_roles
Operacion
Quien puede
Logica
SELECT
El propio usuario + admins + coordinadores
Ver tus roles o los de tu grupo
INSERT/UPDATE
Solo admins y triggers automaticos
Un alumno no puede asignarse roles
DELETE
Nadie
Se desactiva (activo = false)
4.3 Datos sensibles
Tabla
SELECT
INSERT/UPDATE
Razon
user_datos_bancarios
Solo el dueno + finanzas
Solo el dueno
Datos financieros privados
user_contacto_emergencia
El dueno + RRHH + coordinador
Solo el dueno
Dato personal sensible
metadata_instructores
El instructor + coordinador CIA + admin
Solo admin + coordinador CIA
Datos operativos del instructor
5. Triggers y automatizaciones
Trigger
Se activa cuando
Que hace
on_auth_user_created
Un usuario se registra via Supabase Auth
Crea la fila en profiles con el UUID, email y estatus ‘activo’
on_profile_updated
Se edita profiles
Actualiza updated_at. Recalcula perfil_completado
on_role_assigned
Se inserta en user_roles
Notifica al usuario. Si es ‘alumno_cia’, crea fila en historial_academico
on_payment_completed
Se registra un pago (Nucleo 5)
Suma puntos de fidelidad. Verifica si sube de nivel
on_document_expired
Cron job diario
Revisa expedientes. Crea alerta si un documento vence en 30/15/5 dias
on_mora_detected
Cuota pasa fecha de vencimiento
Cambia estatus de compromiso. Si acumula 3 cuotas: suspende al usuario
6. SQL de creacion (referencia)
Script simplificado para crear las tablas principales. El script completo se ejecutara al iniciar la Fase 1 de desarrollo.
-- =============================================
-- NUCLEO 1: USUARIOS Y PERFILES
-- =============================================
-- 1. Enums
CREATE TYPE persona_tipo AS ENUM ('natural', 'juridica');
CREATE TYPE estatus_tipo AS ENUM ('activo', 'suspendido', 'inactivo', 'graduado', 'retirado');
CREATE TYPE genero_tipo AS ENUM ('masculino', 'femenino', 'otro', 'no_especifica');
CREATE TYPE rol_tipo AS ENUM (
'alumno_cia', 'alumno_cc',
'cliente_aerotienda', 'cliente_bistro', 'cliente_nautica',
'instructor', 'empleado', 'proveedor',
'admin', 'coordinador', 'gerente'
);
-- 2. Tabla maestra
CREATE TABLE public.profiles (
id uuid PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
cedula varchar(20) UNIQUE,
tipo_persona persona_tipo DEFAULT 'natural',
nombre varchar(100) NOT NULL,
segundo_nombre varchar(100),
apellido varchar(100) NOT NULL,
segundo_apellido varchar(100),
email varchar(255) UNIQUE NOT NULL,
telefono varchar(20),
fecha_nacimiento date,
genero genero_tipo,
foto_url text,
estatus estatus_tipo DEFAULT 'activo',
nivel_fidelidad varchar(50) DEFAULT 'Pasajero',
puntos integer DEFAULT 0,
perfil_completado integer DEFAULT 0,
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now()
);
-- 3. Roles (muchos a muchos)
CREATE TABLE public.user_roles (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
profile_id uuid REFERENCES profiles(id) ON DELETE CASCADE,
rol rol_tipo NOT NULL,
activo boolean DEFAULT true,
asignado_por varchar(20) DEFAULT 'manual',
fecha_asignacion timestamptz DEFAULT now(),
fecha_desactivacion timestamptz,
UNIQUE(profile_id, rol)
);
-- 4. Trigger: crear profile al registrarse
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS trigger AS $$
BEGIN
INSERT INTO public.profiles (id, email, nombre, apellido)
VALUES (
NEW.id,
NEW.email,
COALESCE(NEW.raw_user_meta_data->>'nombre', ''),
COALESCE(NEW.raw_user_meta_data->>'apellido', '')
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
-- 5. RLS
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view own profile"
ON profiles FOR SELECT
USING (auth.uid() = id);
CREATE POLICY "Users can update own profile"
ON profiles FOR UPDATE
USING (auth.uid() = id)
WITH CHECK (auth.uid() = id);
CREATE POLICY "Admins can view all profiles"
ON profiles FOR SELECT
USING (
EXISTS (
SELECT 1 FROM user_roles
WHERE profile_id = auth.uid()
AND rol IN ('admin', 'coordinador', 'gerente')
AND activo = true
)
);
7. Conexion con otros Nucleos
Nucleo
Tabla
Como se conecta con profiles
2 – Comercial
pedidos
pedidos.user_id → profiles.id
4 – Produccion
cronogramas
cronogramas.alumno_id → profiles.id
5 – Finanzas
compromisos, cuotas
compromisos.user_id → profiles.id
6 – Documental
expedientes
expedientes.user_id → profiles.id
7 – RRHH
empleados
empleados.profile_id → profiles.id
8 – Soporte
tickets
tickets.created_by → profiles.id
Regla: Ninguna tabla de ningun Nucleo puede guardar nombre, cedula o email directamente. Siempre referencian a profiles.id con Foreign Key. Un solo lugar donde vive la identidad.