Appearance
Arquitectura General - Portal de Clientes
Vision General
El Portal de Clientes es un sistema donde cada tenant tiene su propia instancia Docker del frontend configurada via .env, mientras que el backend es compartido (bautista-backend) con un modulo DDD Modules/Portal/. No existe resolucion por dominio: el frontend envia tenant_id y sucursal_id en cada request, y el backend resuelve la conexion a la base de datos correspondiente via ini.sistema.
Estado: Planificado
Modelo de Deployment
mermaid
graph LR
subgraph "Infraestructura por Tenant"
direction TB
D1["Docker Tenant A<br/>.env:<br/>BACKEND_URL=https://api.bautista.com<br/>TENANT_ID=1<br/>SUCURSAL_ID=1<br/>APP_NAME=Portal Empresa A<br/>LOGO_URL=...<br/>PRIMARY_COLOR=#1e40af"]
D2["Docker Tenant B<br/>.env:<br/>BACKEND_URL=https://api.bautista.com<br/>TENANT_ID=2<br/>SUCURSAL_ID=3<br/>APP_NAME=Portal Club XYZ<br/>LOGO_URL=...<br/>PRIMARY_COLOR=#c41e3a"]
end
subgraph "Backend Unico"
BE["bautista-backend<br/>Modules/Portal/<br/>Auth | Account | Payment | Cupon"]
end
D1 -->|REST + JWT| BE
D2 -->|REST + JWT| BECada instancia Docker del frontend:
- Sirve la misma app React (repo
portal-usuarios) - Se configura unicamente via
.enval momento del deploy - La URL del frontend es irrelevante para la logica -- puede ser cualquier dominio
- El branding (nombre, logo, colores) se define en
.envy opcionalmente se complementa condata_configdel tenant
Separacion Frontend y Backend
Frontend (Repo portal-usuarios)
- Repositorio: independiente, submodulo git en
/var/www/Bautista/portal-usuarios - Tecnologia: React PWA
- Deploy: una instancia Docker por tenant
- Configuracion:
.envconBACKEND_URL,TENANT_ID,SUCURSAL_ID, variables de branding - Independencia: completamente separado de
bautista-app(ERP administrativo)
Backend (Modulo DDD en bautista-backend)
- Ubicacion:
Modules/Portal/dentro de bautista-backend - NO usa arquitectura legacy (
App\Controller\Portal\) - Sub-modulos:
Auth/-- login, registro, reset passwordAccount/-- consulta cuenta corriente, deudasPayment/-- pagos online, webhooksCupon/-- generacion de cupones (reutilizaCuponPagoServiceyCuponValidacionService)
- Endpoints:
/portal/* - Reutiliza: modelos y servicios existentes del ERP
Flujo de Autenticacion
mermaid
sequenceDiagram
participant C as Cliente (Browser)
participant F as Frontend Docker
participant B as Backend (Modules/Portal/Auth)
participant I as ini.sistema
participant DB as PostgreSQL (tenant DB)
C->>F: Accede al portal
F->>C: Muestra login
C->>F: Ingresa DNI/CUIT + password
F->>B: POST /portal/auth/login<br/>{dni, password, tenant_id, sucursal_id}
B->>I: Validar tenant_id existe
I-->>B: DB name del tenant
B->>I: Validar sucursal_id pertenece al tenant
I-->>B: OK
B->>DB: Buscar en portal_users por DNI
DB-->>B: Usuario encontrado
B->>B: Verificar bcrypt hash
B-->>F: JWT {portal_user_id, tenant_id, sucursal_id}
F->>F: Almacena JWT
F-->>C: DashboardAuto-registro
mermaid
sequenceDiagram
participant C as Cliente
participant F as Frontend
participant B as Backend (Auth)
participant DB as PostgreSQL
C->>F: Formulario de registro
F->>B: POST /portal/auth/register<br/>{dni, email, password, tenant_id, sucursal_id}
B->>DB: Buscar DNI/CUIT en ordcon
alt DNI existe en ordcon
B->>DB: Crear portal_users con bcrypt hash
B-->>F: Registro exitoso
else DNI NO existe en ordcon
B-->>F: Error: debe ser cliente existente
endEl auto-registro requiere que el DNI/CUIT coincida con un registro existente en ordcon. Si no hay match, el registro falla. Esto garantiza que solo clientes reales de la empresa puedan crear cuentas en el portal.
Reset de Password
- El usuario solicita reset via email
- El backend genera un codigo temporal y lo envia por email
- El usuario ingresa el codigo y define nueva password
- Seguridad moderada: el portal es mayormente lectura de deudas + pagos
Flujo de Request (Request Autenticado)
mermaid
sequenceDiagram
participant F as Frontend Docker<br/>(tenant_id=1, suc_id=1)
participant MW as JWT Middleware
participant P as Modules/Portal/Account
participant I as ini.sistema
participant DB as PostgreSQL
F->>MW: GET /portal/deudas<br/>Authorization: Bearer JWT
MW->>MW: Validar JWT
MW->>MW: Extraer: portal_user_id=42,<br/>tenant_id=1, sucursal_id=1
MW->>I: tenant_id=1 -> DB name?
I-->>MW: "empresa_a"
MW->>I: sucursal_id=1 -> schema?
I-->>MW: "suc0001"
MW->>MW: Configurar conexion:<br/>DB=empresa_a, schema=suc0001
MW->>P: Request con contexto inyectado
P->>DB: Query deudas del portal_user
DB-->>P: Resultados
P-->>F: JSON {success: true, data: [...]}Flujo de Schema Resolution
El backend NO recibe nombres de DB o schemas. Solo recibe IDs numericos y los resuelve:
mermaid
flowchart TD
A[JWT contiene<br/>tenant_id + sucursal_id] --> B{tenant_id valido<br/>en ini.sistema?}
B -->|Si| C[Obtener DB name<br/>de ini.sistema]
B -->|No| X[401 Unauthorized]
C --> D{sucursal_id pertenece<br/>al tenant?}
D -->|Si| E[Obtener schema name]
D -->|No| X
E --> F[Configurar conexion:<br/>DB + schema]
F --> G[Procesar request]- Frontend
.envtieneTENANT_IDySUCURSAL_ID(IDs numericos) - En login, frontend envia estos IDs junto con credenciales
- Backend valida que el tenant existe en
ini.sistema - Backend valida que la sucursal pertenece al tenant
- JWT generado con:
portal_user_id,tenant_id,sucursal_id(solo IDs) - En cada request:
tenant_id-> DB name (deini.sistema),sucursal_id-> schema
Flujo de Pago Online (Automatico)
mermaid
sequenceDiagram
participant C as Cliente
participant F as Frontend
participant B as Backend (Payment)
participant GW as Payment Gateway
participant DB as PostgreSQL
C->>F: Selecciona facturas a pagar
F->>B: POST /portal/pagos/iniciar<br/>{facturas: [...], monto}
B->>GW: Crear preferencia de pago
GW-->>B: URL de pago + payment_id
B->>DB: INSERT portal_payments<br/>status='pending'
B-->>F: {redirect_url: "..."}
F->>C: Redirige a gateway
C->>GW: Completa el pago
GW->>B: POST /portal/pagos/webhook<br/>{payment_id, status}
B->>B: Validar webhook
B->>DB: UPDATE portal_payments<br/>status='approved'
B->>DB: AUTO crear recibo en ctacte<br/>(via ReciboRelationsService)
C->>F: Retorna a pagina de exito
F->>B: GET /portal/pagos/{id}
B-->>F: Pago confirmadoEl flujo es completamente automatico:
- El cliente selecciona facturas y el backend crea el pago en el gateway
- El cliente completa el pago en el gateway externo
- El gateway notifica via webhook al backend
- El backend actualiza
portal_paymentsy auto-crea el recibo en ctacte usando servicios existentes comoReciboRelationsService
Decisiones Arquitectonicas Clave
Frontend como Docker por tenant (no app generica multi-tenant)
| Aspecto | Docker por tenant | App unica multi-tenant |
|---|---|---|
| Configuracion | .env simple por instancia | Resolucion por dominio, tabla tenant_domains |
| Branding | Variables de entorno | Logica runtime compleja |
| DNS | Cada tenant maneja su dominio | Wildcard DNS, certificados complejos |
| Complejidad | Baja -- cada instancia es autonoma | Alta -- resolucion de tenant en runtime |
| Escalabilidad | Agregar Docker, nueva .env | Agregar registro en BD, DNS |
| Aislamiento | Completo a nivel de proceso | Compartido, aislamiento logico |
Modulo DDD vs Legacy
| Aspecto | Modules/Portal/ (DDD) | App\Controller\Portal\ (Legacy) |
|---|---|---|
| Organizacion | Bounded contexts claros | Plano, sin estructura |
| Dependencias | Explicitas entre sub-modulos | Acoplamiento implicito |
| Testing | Aislado por sub-modulo | Dificil de aislar |
| Consistencia | Patron moderno del proyecto | Patron en desuso |
JWT con IDs numericos vs datos de conexion
| Aspecto | IDs numericos | Nombres DB/schema |
|---|---|---|
| Seguridad | No expone infraestructura | Revela nombres internos |
| Flexibilidad | Renombrar DB sin afectar tokens | Tokens invalidos si cambia nombre |
| Resolucion | Backend resuelve via ini.sistema | Directo pero inseguro |
Componentes del Sistema
Tablas Nuevas
| Tabla | Schema level | Proposito |
|---|---|---|
portal_users | Mismo que ordcon | Credenciales portal (bcrypt hash) |
portal_payments | Mismo que ordcon | Registro de pagos online |
Servicios Reutilizados
| Servicio | Uso en Portal |
|---|---|
CuponPagoService | Generacion de cupones de pago |
CuponValidacionService | Validacion de cupones |
ReciboRelationsService | Auto-creacion de recibos post-pago |
Servicios Nuevos (en Modules/Portal/)
| Sub-modulo | Servicio | Responsabilidad |
|---|---|---|
| Auth/ | PortalAuthService | Login, registro, reset password |
| Account/ | PortalAccountService | Consulta deudas, datos de cuenta |
| Payment/ | PortalPaymentService | Inicio de pago, webhooks |
| Cupon/ | PortalCuponService | Wrapper sobre CuponPagoService |
Documentos Relacionados
- Multi-Tenancy -- Estrategia Docker por tenant con resolucion via ini.sistema
- ADR -- Registro completo de decisiones arquitectonicas
- Backend -- Modulo DDD Modules/Portal/
- Frontend -- Repo portal-usuarios