Skip to content

Diseño de Base de Datos - Portal Multi-Tenant

Estrategia Multi-Tenant

El Portal PWA reutiliza la arquitectura multi-tenant existente de Bautista ERP:

  • DB ini (schema public): Configuración global del sistema

    • Nuevas tablas: tenant_domains (mapeo dominios → tenants)
    • Tablas existentes: sistema (empresas/sucursales)
  • DB {tenant} (schema public - LEVEL_EMPRESA): Datos específicos del tenant

    • Nuevas tablas: portal_clients, portal_payments, portal_cupones
    • Tablas existentes: ordcon (clientes), ordcta (cuenta corriente)

Diagrama Entidad-Relación

┌────────────────────────────────────────────────────────────┐
│ DB: ini (Schema: public) - CONFIGURACIÓN GLOBAL            │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  ┌──────────────────┐         ┌──────────────────┐        │
│  │ sistema          │◄────────┤ tenant_domains   │        │
│  │ (existente)      │         │ (nuevo)          │        │
│  ├──────────────────┤         ├──────────────────┤        │
│  │ cnum (PK)        │         │ id (PK)          │        │
│  │ cnombre          │         │ sistema_id (FK)  │        │
│  │ ...              │         │ domain (UNIQUE)  │        │
│  └──────────────────┘         │ database         │        │
│                               │ schema_default   │        │
│                               │ branding_config  │        │
│                               │ status           │        │
│                               └──────────────────┘        │
│                                                            │
└────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────┐
│ DB: {tenant} (Schema: public) - DATOS DEL TENANT           │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  ┌──────────────────┐         ┌──────────────────┐        │
│  │ ordcon           │◄────────┤ portal_clients   │        │
│  │ (existente)      │         │ (nuevo)          │        │
│  ├──────────────────┤         ├──────────────────┤        │
│  │ cnro (PK)        │         │ id (PK - UUID)   │        │
│  │ cnom             │         │ cliente_id (FK)  │        │
│  │ ccui (CUIT)      │         │ dni_cuit         │        │
│  │ ...              │         │ email            │        │
│  └────────┬─────────┘         │ phone            │        │
│           │                   │ last_login       │        │
│           │                   │ failed_attempts  │        │
│           │                   │ locked_until     │        │
│           │                   └──────────────────┘        │
│           │                            │                  │
│           │                   ┌────────▼──────────┐       │
│           │                   │ portal_payments   │       │
│           │                   │ (nuevo)           │       │
│           │                   ├───────────────────┤       │
│           │                   │ id (PK - UUID)    │       │
│           │                   │ cliente_id (FK)   │       │
│           │                   │ payment_method    │       │
│           │                   │ amount            │       │
│           │                   │ status            │       │
│           │                   │ external_id       │       │
│           │                   │ facturas_pagadas  │       │
│           │                   │ recibo_id (FK)    │       │
│           │                   └───────────────────┘       │
│           │                            │                  │
│           │                   ┌────────▼──────────┐       │
│           │                   │ portal_cupones    │       │
│           │                   │ (nuevo)           │       │
│           │                   ├───────────────────┤       │
│           │                   │ id (PK - UUID)    │       │
│           │                   │ cliente_id (FK)   │       │
│           │                   │ codigo_barras     │       │
│           │                   │ monto             │       │
│           │                   │ facturas (JSONB)  │       │
│           │                   │ estado            │       │
│           │                   │ fecha_vencimiento │       │
│           │                   │ recibo_id (FK)    │       │
│           │                   └───────────────────┘       │
│           │                                                │
│           │                   ┌──────────────────┐        │
│           └──────────────────►│ ordcta           │        │
│                               │ (existente)      │        │
│                               ├──────────────────┤        │
│                               │ id (PK - UUID)   │        │
│                               │ cliente_id       │        │
│                               │ tipo_movimiento  │        │
│                               │ monto            │        │
│                               │ ...              │        │
│                               └──────────────────┘        │
│                                                            │
└────────────────────────────────────────────────────────────┘

Entidades Nuevas

1. tenant_domains (DB: ini)

Propósito de negocio: Mapeo de dominios a tenants/empresas para resolver automáticamente qué empresa está accediendo al portal según el dominio usado.

Campos clave:

  • domain: El dominio web usado para acceder (ej: ctacte.empresaA.com.ar)
  • sistema_id: Referencia a la empresa en tabla sistema
  • database: Nombre de la base de datos del tenant
  • branding_config: Configuración de marca (colores, logos, nombre de la app)
  • status: Estado del dominio (activo, inactivo, suspendido)

Ejemplo de datos:

json
{
  "domain": "ctacte.empresaA.com.ar",
  "database": "empresa_a",
  "branding_config": {
    "app_name": "Portal Empresa A",
    "short_name": "EmpA",
    "logo_url": "https://cdn.example.com/logo-empresa-a.png",
    "primary_color": "#1e40af",
    "theme_color": "#1e40af"
  },
  "status": "active"
}

2. portal_clients (DB: {tenant})

Propósito de negocio: Datos de acceso y seguridad de clientes al portal. Se crea automáticamente al primer login vinculando con el cliente existente en ordcon.

Campos clave:

  • cliente_id: Vincula con el cliente existente en ordcon
  • dni_cuit: Credencial de identificación del cliente
  • email, phone: Datos de contacto
  • last_login: Última vez que accedió
  • failed_login_attempts: Intentos fallidos (para bloqueo temporal)
  • locked_until: Fecha hasta la que está bloqueado (seguridad)

Relación con ordcon:

  • Se busca el cliente en ordcon por DNI/CUIT o ID de cliente
  • Si no existe en portal_clients, se crea automáticamente desde ordcon

3. portal_payments (DB: {tenant})

Propósito de negocio: Histórico de pagos realizados vía portal (online o cupones). Permite rastrear pagos, reintentarlos en caso de falla, y vincularlos con los recibos generados.

Campos clave:

  • cliente_id: Cliente que realizó el pago
  • payment_method: Método usado (mercadopago, pagotic, cupón, etc.)
  • amount: Monto total pagado
  • status: Estado del pago (pending, approved, rejected, cancelled, refunded)
  • external_id: ID del pago en el gateway externo (para evitar duplicados)
  • facturas_pagadas: JSON con las facturas que se pagaron
  • recibo_id: Vincula con el recibo generado en ordcta

Métodos de pago válidos: mercadopago, pagotic, pagomiscuentas, cupon, manual, none

Estados válidos: pending, approved, rejected, cancelled, refunded

Ejemplo de facturas_pagadas:

json
[
  {
    "id": "uuid-factura-1",
    "tipo": "Factura A",
    "numero": 123,
    "monto": 10000.00
  },
  {
    "id": "uuid-factura-2",
    "tipo": "Factura B",
    "numero": 456,
    "monto": 5000.00
  }
]

Ejemplo de respuesta del gateway (external_response):

json
{
  "payment_id": 1234567890,
  "status": "approved",
  "status_detail": "accredited",
  "payment_type_id": "credit_card",
  "transaction_amount": 15000.00,
  "date_approved": "2026-01-20T14:30:00Z"
}

4. portal_cupones (DB: {tenant})

Propósito de negocio: Cupones de pago generados por clientes para pagar en ubicaciones físicas. Incluyen código de barras único y fecha de vencimiento.

Campos clave:

  • cliente_id: Cliente que generó el cupón
  • codigo_barras: Código ITF de 19 dígitos (único)
  • monto: Monto total del cupón
  • facturas: JSON con las facturas incluidas en el cupón
  • estado: Estado del cupón (pending, used, expired, cancelled)
  • fecha_vencimiento: Fecha límite para usar el cupón
  • recibo_id: Vincula con el recibo si el cupón fue usado

Estados válidos: pending, used, expired, cancelled

Estructura del código de barras (19 dígitos):

SUCU(4) + CLIENTE(6) + TIMESTAMP(8) + DV(1)
0001   + 056789    + 20260127    + 4     = 0001056789202601274

Ejemplo de facturas en cupón:

json
[
  {
    "id": "uuid-factura-1",
    "tipo": "Factura A",
    "numero": 123,
    "fecha": "2026-01-01",
    "vencimiento": "2026-01-31",
    "monto": 10000.00
  }
]

Entidades Existentes Reutilizadas

ordcon (Clientes)

Propósito: Tabla maestra de clientes del sistema ERP.

Campos usados por el portal:

  • cnro: ID del cliente (clave primaria)
  • cnom: Nombre del cliente
  • ccui: CUIT del cliente
  • cemail: Email (opcional)
  • ctel: Teléfono (opcional)

ordcta (Cuenta Corriente)

Propósito: Movimientos de cuenta corriente (facturas, recibos, notas).

Campos usados por el portal:

  • id: UUID del movimiento
  • cliente_id: Cliente del movimiento
  • tipo_movimiento: "Factura", "Recibo", "Nota de Crédito", etc.
  • monto: Monto del movimiento
  • saldo: Saldo pendiente
  • fecha: Fecha del movimiento
  • vencimiento: Fecha de vencimiento (para facturas)

Uso en portal:

  • Consulta de deudas: Buscar facturas con saldo > 0
  • Generación de recibos: Crear nuevo movimiento tipo "Recibo" al aprobar pago

Flujos de Datos Clave

Identificación de Cliente

  1. Cliente ingresa DNI/CUIT
  2. Se busca en portal_clients por dni_cuit
  3. Si no existe, se busca en ordcon y se crea entrada en portal_clients

Consulta de Deudas

  1. Buscar en ordcta movimientos del cliente
  2. Filtrar por tipo "Factura" y saldo > 0
  3. Ordenar por fecha de vencimiento

Proceso de Pago Online

  1. Cliente selecciona facturas a pagar
  2. Se crea registro en portal_payments con estado "pending"
  3. Se redirige al gateway de pago con external_id único
  4. Gateway notifica resultado vía webhook
  5. Se actualiza portal_payments con respuesta del gateway
  6. Si aprobado, se genera recibo en ordcta y se vincula

Generación de Cupón

  1. Cliente selecciona facturas
  2. Se genera código de barras único (19 dígitos)
  3. Se crea registro en portal_cupones con estado "pending"
  4. Se genera PDF con código de barras
  5. Cuando se usa físicamente, se marca como "used" y se vincula recibo