Skip to content

Mapa Forense de Integración: Módulos de la Aplicación vs. Tablas de Supabase ​

Este documento sirve como guía técnica y forense de referencia para la sincronización y persistencia de datos entre el frontend de ITecel y la base de datos PostgreSQL en Supabase.


1. Resumen de Mapeo Módulos ↔ Tablas ​

Módulo en Interfaz (Tab)Tabla(s) SupabaseClave Primaria (PK)Claves Foráneas (FK) Críticas
Directorio de Viviendas (directory)housing_units, sectionsid (TEXT)condo_id → condos(id), section_id → sections(id)
Generación de Cuotas (generateDebts)debt_templates, generated_debtsid (TEXT)condo_id → condos(id), unit_id → housing_units(id), template_id → debt_templates(id)
Cobranzas y Recibos (payments)payment_records, generated_debts, bank_movementsid (TEXT)condo_id → condos(id), unit_id → housing_units(id), account_id → bank_accounts(id)
Egresos y Facturas (expenses)expenses, suppliers, bank_movementsid (TEXT)condo_id → condos(id), supplier_id → suppliers(id), account_id → bank_accounts(id)
Estados de Cuenta (accountStatement)housing_units, generated_debts, payment_records, statement_dispatchesLectura combinada + id (TEXT)Reconciliación estricta por unit_id, condo_id → condos(id)
Cuentas Bancarias (bankAccounts)bank_accounts, bank_movementsid (TEXT)condo_id → condos(id), related_account_id → bank_accounts(id)
Presupuesto Anual (budget)budgets(condo_id, year)condo_id → condos(id)
Reportes Financieros (financials)Agregación de generated_debts, payment_records, expenses, bank_movementsN/A (Lectura)Consistencia de estados y fechas (issue_date, due_date, date)
Panel Administrador (adminDashboard)KPIs calculados desde todas las tablasN/A (Lectura)Cartera por cobrar, saldos bancarios, morosidad
Configuración y Respaldo (settings)condos + todas las 11 entidadesid (TEXT)Payload canónico serializeCondoState / deserializeCondoState

2. Orden Secuencial Obligatorio de Inserción (Topological Sort) ​

Debido a las restricciones de integridad referencial (FOREIGN KEY ... REFERENCES ... ON DELETE CASCADE), la función supabaseSyncFullCondo ejecuta los UPSERT en 11 pasos estrictos:

  1. condos: Registro raíz del condominio.
  2. sections: Secciones/torres históricas (opcionales).
  3. housing_units: Viviendas del condominio (Base para adeudos, recibos y movimientos).
  4. suppliers: Proveedores de servicios y bienes.
  5. bank_accounts: Cuentas bancarias de la copropiedad.
  6. debt_templates: Plantillas maestras de emisión de cuotas.
  7. generated_debts: Adeudos individuales emitidos por vivienda.
    • Dependencia: unit_id DEBE existir previamente en housing_units.
    • Dependencia: template_id debe existir en debt_templates o ser null.
  8. expenses: Egresos registrados (depende de suppliers y bank_accounts).
  9. payment_records: Recibos de cobro histórico emitidos a copropietarios.
  10. bank_movements: Movimientos bancarios (Kardex financiero).
    • Dependencias: account_id, payment_record_id, expense_id, debt_id, unit_id.
  11. budgets: Presupuestos anuales divididos por categorías de ingresos/egresos ordinarios y extraordinarios.

3. Detalle Forense de la Tabla generated_debts ​

La tabla generated_debts almacena la cartera y el historial de cobro por vivienda:

sql
CREATE TABLE generated_debts (
  id TEXT PRIMARY KEY,
  condo_id TEXT NOT NULL REFERENCES condos(id) ON DELETE CASCADE,
  unit_id TEXT NOT NULL REFERENCES housing_units(id) ON DELETE CASCADE,
  template_id TEXT REFERENCES debt_templates(id) ON DELETE SET NULL,
  template_name TEXT DEFAULT '',
  unit_number TEXT NOT NULL,
  unit_identifier TEXT DEFAULT '',
  section_name TEXT DEFAULT '',
  concept TEXT NOT NULL,
  amount NUMERIC(14,2) NOT NULL DEFAULT 0.00,
  discount TEXT DEFAULT '0.00',
  discount_days INTEGER DEFAULT 0,
  issue_date DATE NOT NULL,
  due_date DATE NOT NULL,
  budget_category TEXT NOT NULL DEFAULT 'IngOrd - a. ALICUOTAS',
  fund_type TEXT NOT NULL DEFAULT 'ordinary',
  debt_type TEXT DEFAULT 'fee',
  notes TEXT DEFAULT '',
  status TEXT NOT NULL DEFAULT 'pending',
  paid_amount NUMERIC(14,2) NOT NULL DEFAULT 0.00,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Reglas de Normalización Aplicadas: ​

  1. Reconciliación de unit_id: Si un adeudo restaurado de backup no tiene unit_id válido o apunta a un ID local obsoleto, el motor de sincronización busca la vivienda por unit_number o unit_identifier en condo.units y asigna el unit_id canónico.
  2. Prevención de Violación FK: Cualquier adeudo huérfano (cuya vivienda fue eliminada del archivo) es filtrado antes del envío por lotes para evitar que aborte la transacción completa. Se registra una advertencia con console.warn.
  3. Desduplicación de IDs en el Lote: Postgres prohíbe múltiples operaciones sobre la misma clave primaria en una sola sentencia ON CONFLICT DO UPDATE. Si el array contiene claves repetidas, se desduplican antes del envío.
  4. Sanitización de Fechas: Las fechas issue_date y due_date son forzadas al formato canónico YYYY-MM-DD sin cadenas vacías ni timestamps inválidos.
  5. Carga en Bloques con Recuperación Fila por Fila (Chunking + Fallback):
    • Se sube en bloques de 50 registros (CHUNK_SIZE = 50).
    • Si un bloque falla por cualquier razón en Postgres, se activa el fallback fila por fila: salva todos los adeudos válidos y aisla e imprime el error exacto (message, details, hint, code) de la fila conflictiva.

4. Guía de Diagnóstico de Errores Comunes ​

Código/Mensaje de ErrorCausa RaízSolución Implementada
23503: foreign key constraint "generated_debts_unit_id_fkey"Un adeudo tiene unit_id que no existe en housing_units.Reconciliación automática por número de vivienda + exclusión preventiva de huérfanos.
23503: foreign key constraint "generated_debts_template_id_fkey"Un adeudo referencia una plantilla eliminada o no sincronizada.template_id se convierte a null si no está en el conjunto de plantillas válidas.
21000: ON CONFLICT DO UPDATE command cannot affect row a second timeDos filas en el mismo lote de subida comparten el mismo id.Desduplicación con sufijo unívoco antes de ejecutar el upsert.
22007: invalid input syntax for type date: ""La fecha de emisión o vencimiento viene como cadena vacía "".Sanitizador sanitizeDate asigna fecha por defecto en formato YYYY-MM-DD.
23502: null value in column "id" / "concept" violates not-null constraintCampos obligatorios vacíos o nulos en el objeto JavaScript.Coerción y fallbacks a cadenas no vacías en generatedDebtToRow.

5. Matriz de Control de Acceso por Tabla y Rol (Fase 4-A: Row Level Security Real) ​

Todas las tablas de negocio tienen habilitado Row Level Security (ENABLE ROW LEVEL SECURITY). Las políticas permisivas dev (USING (true)) han sido completamente retiradas.

Identificación de Roles en Base de Datos: ​

  • Superadmin (is_superadmin()): Usuario autenticado con registro en app_users donde role = 'admin' y condo_id IS NULL.
  • Admin de Condominio (is_condo_admin(condo_id)): Usuario autenticado con registro en app_users donde role = 'admin' y condo_id coincide con el condominio.
  • Comité (is_condo_committee(condo_id)): Usuario autenticado con registro en app_users donde role = 'committee' y condo_id coincide. Solo lectura sobre su condominio.
  • Residente (is_condo_resident(condo_id, unit_id)): Usuario autenticado con registro en app_users donde role = 'resident', condo_id coincide y unit_id coincide con su vivienda asignada.
  • Edge Functions / Backend Admin: Conexión vía SUPABASE_SERVICE_ROLE_KEY (service_role), el cual posee el atributo nativo de Postgres BYPASSRLS.
TablaSuperadminAdmin de CondominioMiembro del ComitéResidente AsignadoAnónimo / Externo
condosTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT solo de su condoSELECT solo de su condoBloqueado (0 filas)
sectionsTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT solo de su condoSELECT solo de su condoBloqueado (0 filas)
housing_unitsTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT de todas las unidades de su condoSELECT únicamente de su unidad asignada (unit_id)Bloqueado (0 filas)
bank_accountsTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT solo de su condoBloqueado (0 filas)Bloqueado (0 filas)
suppliersTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT solo de su condoBloqueado (0 filas)Bloqueado (0 filas)
debt_templatesTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT solo de su condoBloqueado (0 filas)Bloqueado (0 filas)
generated_debtsTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT de todos los adeudos de su condoSELECT solo de sus adeudos asignados (unit_id)Bloqueado (0 filas)
payment_recordsTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT de todos los pagos de su condoSELECT solo de sus pagos asignados (unit_id)Bloqueado (0 filas)
expensesTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT solo de su condoBloqueado (0 filas)Bloqueado (0 filas)
bank_movementsTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT solo de su condoBloqueado (0 filas)Bloqueado (0 filas)
yearly_budgetsTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT solo de su condoBloqueado (0 filas)Bloqueado (0 filas)
unit_paymentsTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT de todos los pagos de su condoSELECT solo de sus pagos asignados (unit_id)Bloqueado (0 filas)
app_usersTotal (S, I, U, D)SELECT / I / U / D de committee y resident de su condo. Ver su propio usuario.SELECT solo de su propio usuario (auth_user_id / email)SELECT solo de su propio usuario (auth_user_id / email)Bloqueado (0 filas)
user_invitationsTotal (S, I, U, D)Total en su condo (S, I, U, D)BloqueadoSELECT solo si email = get_auth_email()Bloqueado (0 filas)
statement_dispatchesTotal (S, I, U, D)Total en su condo (S, I, U, D)SELECT solo de su condoSELECT solo si coincide su unit_id o emailBloqueado (0 filas)
licensesTotal (S, I, U, D)SELECT solo de la licencia de su propio condoBloqueadoBloqueadoBloqueado (0 filas)
central_backupsTotal (S, I, U, D)BloqueadoBloqueadoBloqueadoBloqueado (0 filas)

6. Estandarización de Identidad vs. Campos Descriptivos (Mejora M2) ​

Un principio cardinal derivado de la arquitectura de seguridad y Row Level Security (RLS) en ITECEL ADM es la estricta separación entre Identidad y Campos Descriptivos:

A. Identidad (Seguridad y Control de Acceso) ​

  • Fuente de Verdad: auth.users (auth.uid(), email) y public.app_users (auth_user_id, email, role, condo_id, unit_id).
  • Propósito: Autenticación criptográfica, evaluación de políticas RLS mediante get_auth_email() e is_superadmin(), y resolución de contextos de trabajo.
  • Inmutabilidad Relativa: Modificar el correo de acceso requiere procedimientos de actualización de cuenta en Supabase Auth y sincronización en app_users.

B. Campos Descriptivos (Informativos e Institucionales) ​

  • condos.email / condo.profile.email: Campo puramente descriptivo e institucional. Representa la casilla de correo oficial de contacto o atención del condominio (ej. administracion@condominiolaspalmas.com o contacto@edificiocentral.cl).
  • Regla Estricta: condos.email NUNCA debe ser utilizado para autenticación, validación de permisos RLS o asunciones de identidad de usuario. Un administrador puede gestionar un condominio accediendo con su cuenta personal (admin1@empresa.com) mientras el condominio expone ante copropietarios su correo de soporte institucional (condo@edificio.com).
  • housing_units.email / contacts.email: Representa los canales de notificación para cobranza y avisos de estado de cuenta. La autenticación del residente se vincula únicamente cuando su dirección coincide con un usuario registrado en auth.users / app_users asociado a unit_id.

Wiki Técnica Oficial — ITECEL ADM