Skip to content

Diseno de Base de Datos - Portal de Clientes

Estrategia Multi-Tenant

El Portal reutiliza la arquitectura multi-tenant existente de Bautista ERP con una regla especifica: las tablas del portal viven en el mismo schema que ordcon.

Regla de Ubicacion de Schema

La ubicacion de portal_users y portal_payments es dinamica, determinada por la configuracion del tenant en ini.sistema:

  • Si el tenant tiene ordcon por sucursal (configuracion tipica): las tablas van en el schema sucXXXX correspondiente
  • Si el tenant tiene ordcon compartido en public: las tablas van en el schema public

Esta regla garantiza que las FK a ordcon.cnro siempre sean locales al schema, sin necesidad de cross-schema JOINs.

Resolucion en Runtime

JWT { tenant_id, sucursal_id }
       |              |
       v              v
  ini.sistema    schema resolution
       |              |
       v              v
  database name   sucXXXX o public
  1. tenant_id del JWT se busca en ini.sistema para obtener el nombre de la base de datos
  2. sucursal_id del JWT determina el schema (sucXXXX)
  3. Si el tenant tiene ordcon en public, el schema es public independientemente del sucursal_id

Diagrama Entidad-Relacion

mermaid
erDiagram
    ordcon ||--o| portal_users : "cnro = cliente_id"
    ordcon ||--o{ portal_payments : "cnro = cliente_id"
    ordcon ||--o{ ordcta : "cnro = cliente_id"
    portal_payments o|--o| recibo : "recibo_id"

    ordcon {
        int cnro PK
        string cnom
        string ccui
        string cemail
        string ctel
    }

    portal_users {
        uuid id PK
        int cliente_id FK
        string dni_cuit UK
        string email
        string password_hash
        string refresh_token UK
        timestamp refresh_token_expires
        timestamp last_login
        int failed_login_attempts
        timestamp locked_until
        timestamp created_at
        timestamp updated_at
    }

    portal_payments {
        uuid id PK
        int cliente_id FK
        int tenant_id
        int sucursal_id
        string gateway
        string payment_method
        numeric amount
        enum status
        string external_id UK
        jsonb external_response
        jsonb facturas_pagadas
        int recibo_id FK
        timestamp payment_date
        timestamp created_at
        timestamp updated_at
    }

    ordcta {
        uuid id PK
        int cliente_id
        string tipo_movimiento
        numeric monto
        numeric saldo
        date fecha
        date vencimiento
    }

Nota: Todas las entidades del diagrama viven en el mismo schema (determinado por la regla de ubicacion). ordcon, ordcta y recibo son tablas existentes del ERP.

Tablas Nuevas

1. portal_users

Proposito: Credenciales de acceso y datos de seguridad de usuarios del portal. Vincula un cliente existente en ordcon con su cuenta de acceso al portal.

Campos:

CampoTipoRestriccionesDescripcion
idUUIDPKIdentificador unico
cliente_idintegerFK → ordcon.cnro, NOT NULLVinculacion con cliente existente
dni_cuitvarchar(20)UNIQUE, NOT NULLCredencial de identificacion (login)
emailvarchar(255)NOT NULLEmail del usuario (para reset de password)
password_hashvarchar(255)NOT NULLHash bcrypt del password
refresh_tokenvarchar(255)UNIQUE, nullableUUID del refresh token activo (una sesion por usuario)
refresh_token_expirestimestampnullableExpiracion del refresh token
last_logintimestampnullableUltimo acceso exitoso
failed_login_attemptsintegerNOT NULL, default 0Contador de intentos fallidos
locked_untiltimestampnullableBloqueo temporal tras 5 intentos fallidos
created_attimestampNOT NULL, default now()Fecha de creacion
updated_attimestampNOT NULL, default now()Ultima actualizacion

Indices:

  • uq_portal_users_dni_cuit (UNIQUE) — Login por DNI/CUIT
  • uq_portal_users_refresh_token (UNIQUE) — Lookup rapido de refresh token
  • idx_portal_users_cliente_id — Busqueda por cliente vinculado
  • idx_portal_users_email — Busqueda por email (para password reset)

Relacion con ordcon:

  • El registro en portal_users se crea durante el auto-registro
  • El cliente debe existir previamente en ordcon con DNI/CUIT coincidente
  • FK con ON DELETE CASCADE: si se borra el cliente en ordcon, se borra su acceso al portal

Flujo de auto-registro:

  1. Usuario ingresa DNI/CUIT + email + password
  2. Se busca en ordcon un registro con ccui igual al DNI/CUIT ingresado
  3. Si existe match, se crea el registro en portal_users vinculando cliente_id = ordcon.cnro
  4. Si no existe match, se rechaza el registro

2. portal_payments

Proposito: Registro de pagos realizados via portal. Almacena el estado completo del pago incluyendo la respuesta del gateway, y se vincula automaticamente con el recibo generado.

Campos:

CampoTipoRestriccionesDescripcion
idUUIDPKIdentificador unico
cliente_idintegerFK → ordcon.cnro, NOT NULLCliente que realizo el pago
tenant_idintegerNOT NULLID del tenant (para resolucion en webhook)
sucursal_idintegerNOT NULLID de la sucursal (para resolucion en webhook)
gatewayvarchar(50)NOT NULLAdapter de gateway usado (paypertic, mercadopago). Para webhook routing y reportes
payment_methodvarchar(50)NOT NULLMetodo de pago (mercadopago, pagotic, etc.)
amountnumeric(15,2)NOT NULLMonto total pagado
statusvarchar(20)NOT NULL, default 'pending'Estado del pago
external_idvarchar(255)UNIQUEID del pago en el gateway externo
external_responsejsonbnullableRespuesta completa del gateway
facturas_pagadasjsonbNOT NULLArray de facturas incluidas en el pago
recibo_idintegerFK → recibo, nullableRecibo generado automaticamente tras aprobacion
payment_datetimestampNOT NULLFecha/hora del pago
created_attimestampNOT NULL, default now()Fecha de creacion
updated_attimestampNOT NULL, default now()Ultima actualizacion

Valores de status: pending, approved, rejected, refunded, cancelled

Indices:

  • uq_portal_payments_external_id (UNIQUE) — Idempotencia: evita procesar el mismo pago dos veces
  • idx_portal_payments_cliente_id — Pagos de un cliente
  • idx_portal_payments_status — Filtrado por estado
  • idx_portal_payments_gateway — Filtrado por gateway (reportes, estadisticas por proveedor)

Estructura 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
  }
]

Estructura de external_response (ejemplo MercadoPago):

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"
}

Pago automatico: Cuando el webhook del gateway notifica status = approved, el sistema automaticamente crea un recibo en ordcta y vincula recibo_id en el registro de portal_payments. No hay intervencion manual.

Tablas Reutilizadas del ERP

ordcon (Clientes)

Proposito: Tabla maestra de clientes del sistema ERP. El portal NO crea clientes nuevos; solo vincula clientes existentes con cuentas de acceso.

Campos usados por el portal:

  • cnro: ID del cliente (PK, referenciado por portal_users.cliente_id y portal_payments.cliente_id)
  • cnom: Nombre del cliente (mostrado en la UI del portal)
  • ccui: CUIT/DNI del cliente (usado para validar auto-registro)
  • cemail: Email (opcional, puede diferir del email de portal_users)
  • ctel: Telefono (opcional)

ordcta (Cuenta Corriente)

Proposito: Movimientos de cuenta corriente (facturas, recibos, notas de credito).

Campos usados por el portal:

  • id: UUID del movimiento
  • cliente_id: Cliente del movimiento
  • tipo_movimiento: "Factura", "Recibo", "Nota de Credito", 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
  • Generacion de recibos: Crear nuevo movimiento tipo "Recibo" automaticamente al aprobar pago

Tablas Eliminadas del Diseno Original

Tabla eliminadaRazon
tenant_domainsLa resolucion de tenant se configura en .env al momento del deploy Docker. No se necesita tabla de mapeo.
portal_cuponesSe reutiliza el sistema de cupones existente del ERP. No se crea tabla nueva.
portal_clientsReemplazada por portal_users que incluye password_hash para autenticacion con password.

Flujos de Datos

Auto-Registro de Usuario

mermaid
sequenceDiagram
    participant U as Usuario
    participant API as Portal API
    participant DB as Database

    U->>API: POST /register {dni_cuit, email, password}
    API->>DB: SELECT FROM ordcon WHERE ccui = dni_cuit
    alt ordcon encontrado
        API->>DB: SELECT FROM portal_users WHERE dni_cuit = dni_cuit
        alt ya registrado
            API-->>U: 409 Conflict - Usuario ya existe
        else no registrado
            API->>DB: INSERT portal_users {cliente_id, dni_cuit, email, password_hash}
            API-->>U: 201 Created + JWT
        end
    else ordcon no encontrado
        API-->>U: 404 - DNI/CUIT no encontrado en el sistema
    end

Consulta de Deudas

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

Proceso de Pago Online (automatico)

mermaid
sequenceDiagram
    participant U as Usuario
    participant API as Portal API
    participant GW as Gateway
    participant DB as Database

    U->>API: POST /pagos/iniciar {facturas}
    API->>DB: INSERT portal_payments (status=pending)
    API->>GW: Crear preferencia de pago
    GW-->>API: URL de pago
    API-->>U: Redirect a gateway

    Note over GW: Usuario paga en gateway

    GW->>API: Webhook (payment approved)
    API->>DB: UPDATE portal_payments SET status=approved
    API->>DB: INSERT recibo en ordcta (automatico)
    API->>DB: UPDATE portal_payments SET recibo_id=nuevo_recibo