Deilify — Sistema de Gestión de Cartera e IA

SaaS Multi-tenant REST API (Flask-RESTX) Frontend React 19 Machine Learning (RF + XGBoost) SQLAlchemy 2.0

Deilify es una solución integral SaaS (Software as a Service) multi-tenant diseñada para optimizar la gestión de cuentas por cobrar, seguimiento de facturación, registro de recaudos y prevención de mora empresarial. Combina una arquitectura backend desacoplada en Python / Flask, una interfaz de usuario reactiva en React 19 + TypeScript + Tailwind CSS, y un motor de Machine Learning supervisado capaz de predecir la probabilidad de incumplimiento de clientes con base en su comportamiento transaccional histórico.

Frontend Real
React 19
Vite 6 · Tailwind 4 · Radix
Backend Real
Flask 3.1
Flask-RESTX · Swagger Docs
Base de Datos
SQLAlchemy 2
SQLite / PostgreSQL · Alembic
Inteligencia Artificial
Scikit + XGB
13 Features · Scoring 0-100

Propuesta de Valor & Capacidades Operativas

  • Aislamiento Multi-tenant Estricto: Cada registro de facturas, clientes, pagos y usuarios está vinculado a una empresa_id. El backend extrae esta identidad de forma segura desde los claims del token JWT, garantizando confidencialidad absoluta entre organizaciones.
  • Cálculo Dinámico de Saldo y Mora: Al registrar un recaudo, el backend actualiza de inmediato el saldo_pendiente de la factura. Si se salda por completo, transmuta su estado a PAGADA. Mediante CLI programado (flask cartera check-overdue), las facturas vencidas impagas se clasifican automáticamente.
  • KPIs Financieros Automatizados: Generación instantánea de DSO (Days Sales Outstanding) y distribución de edades de vencimiento en buckets estándares de la industria: 0–30 días, 31–60 días, 61–90 días y 90+ días.
  • Scoring Predictivo de Riesgo Crediticio: El módulo de IA extrae 13 variables de comportamiento histórico (frecuencia de mora, promedio de días de retraso, ratio deuda/límite, notas de cobranza) y evalúa al cliente mediante modelos entrenados (RandomForest / XGBoost) retornando un score de riesgo (0 a 100) y un dictamen descriptivo en lenguaje natural.

Estructura del Ecosistema Deilify

El código auditado está dividido en tres proyectos clave en el espacio de trabajo:

back-deilify

API REST construida con Flask, Flask-RESTX y SQLAlchemy. Expone endpoints de autenticación, administración de usuarios, clientes, cartera financiera y el pipeline completo de entrenamiento e inferencia de Machine Learning.

deilify-frontend

Single Page Application (SPA) en React 19 + Vite 6 + Tailwind CSS. Tableros analíticos con Recharts, formularios con Zod y React Hook Form, gestión de sesión por JWT en AuthContext y visualización interactiva de predicciones de riesgo.

documentacion-cartera

Portal unificado de documentación técnica interactiva y repositorio de arquitectura que describe la realidad fáctica del sistema, inventarios, modelos, contratos de API y guías para el equipo técnico.

Estado del Proyecto & Auditoría General

Clasificación rigurosa basada exclusivamente en la existencia y funcionalidad del código fuente.

Tabla Maestra de Estado de Módulos

Módulo Estado General Backend Frontend Observaciones Técnicas
Autenticación COMPLETADO Sí (rama develop) Sí (Login.tsx) Registro de empresa + usuario, login con Argon2id, JWT con claims de rol y tenant, /auth/me.
Usuarios COMPLETADO Sí (rama develop) Sí (Usuarios.tsx) CRUD de usuarios por empresa con control de permisos ADMIN/USER y prevención de auto-eliminación.
Clientes COMPLETADO Sí (rama develop) Sí (Clientes.tsx) CRUD completo de clientes, validación de identificación única por empresa, ficha detallada en ClienteDetalle.tsx.
Cartera (Cálculos & Dashboard) COMPLETADO Sí (/cartera/dashboard) Sí (Dashboard.tsx) DSO proyectado, distribución de cartera vencida por edades (0-30, 31-60, 61-90, 90+) con Pandas.
Sincronización de Facturas COMPLETADO Sí (POST /cartera/facturas) Sí (Modal en Facturas.tsx) Carga por lote o unitaria con validación de pertenencia de cliente y creación de saldo pendiente.
Listado General de Facturas PARCIAL Incompleto Parcial El backend carece de GET /cartera/facturas general; frontend intenta extraerlas del dashboard agregado.
Registro de Recaudos (Pagos) COMPLETADO Sí (POST /cartera/recaudos) Sí (Modal en Pagos.tsx) Descuenta saldo en factura, cambia estado a PAGADA si saldo ≤ 0. Requiere mapear campo referencia a transaccion_id.
Listado Histórico de Pagos PARCIAL Incompleto Parcial No existe endpoint para listar todos los pagos de la empresa; actualmente solo se consultan dentro de cada cliente.
Inteligencia Artificial (Entrenamiento) COMPLETADO Sí (/api/v1/ai/train) Sí (IA.tsx) Entrenamiento de RandomForest y XGBoost, métricas de validación, selección automática por ROC AUC y guardado en disco.
Inteligencia Artificial (Inferencia) COMPLETADO Sí (/api/v1/ai/predict/<id>) Sí (ClienteDetalle.tsx) Predicción en tiempo real con 13 variables, cálculo de score 0-100, escala de 5 niveles y recomendación de cobro.
Reporte Inteligente IA COMPLETADO Sí (/predict/<id>/report) Sí (ClienteDetalle.tsx) Consolida KPIs de cartera del cliente, score predictivo y un dictamen descriptivo narrativo sintetizado.
Gestión de Cobros / Refinanciación NO IMPLEMENTADO 0 bytes (acuerdo_pago.py) Alias visual app/models/acuerdo_pago.py está vacío. GestionCobros.tsx en frontend es un alias a Pagos.
Módulo de Reportes PDF/Excel NO IMPLEMENTADO 0 bytes (api/reportes) Alias visual Endpoints de exportación no programados. Reportes.tsx en frontend redirige a Dashboard.
Control de Planes Comerciales NO IMPLEMENTADO 0 bytes (middleware/plan.py) No conectado Existía en la documentación antigua pero nunca fue codificado en el backend. No hay limitación de planes.
Documentación Antigua ("GesCartera") OBSOLETO N/A N/A La landing previa describía Vue.js 3, Pinia, triggers SQL y tablas no respaldadas por el código.

Arquitectura Global del Sistema

Topología técnica y flujo de datos de extremo a extremo.

Diagrama de Arquitectura de Capas

CAPA CLIENTE (FRONTEND SPA)
React 19.2 + TypeScript + Tailwind CSS 4
React Router 7 · Axios Client · Recharts · AuthContext
HTTPS / JSON REST
CAPA DE CONTROLADORES & ROUTING (BACKEND)
Flask 3.1 + Flask-RESTX (Namespaces)
/auth · /usuarios · /clientes · /cartera · /api/v1/ai
JWT Auth & Tenant Extraction
CAPA DE SERVICIOS & NEGOCIO
AuthService · UsuarioService · CarteraService · AI Services
Reglas financieras · Cálculo DSO · Extracción de 13 Features
ORM / Persistencia & ML Engine
BASE DE DATOS
SQLAlchemy 2.0 ORM
SQLite / PostgreSQL · Alembic
MOTOR MACHINE LEARNING
Scikit-Learn + XGBoost
Joblib Storage · Inferencias en vivo

Flujo de una Operación Real: Registro de un Recaudo (Pago)

  1. Interacción en Frontend: El analista abre el modal de registro de pago en Pagos.tsx, ingresa ID de factura, monto, fecha, método y referencia.
  2. Envío HTTP: Axios intercepta la petición, añade el token en la cabecera Authorization: Bearer <JWT> y envía un POST /cartera/recaudos.
  3. Autenticación & Aislamiento: Flask-JWT-Extended valida la firma del token. El middleware tenant.py extrae la empresa_id del usuario autenticado.
  4. Validación de Esquema: Marshmallow (PagoSchema) valida que los campos requeridos y tipos numéricos sean válidos.
  5. Lógica Financiera (CarteraService): Se consulta la factura en base de datos verificando que pertenezca a la empresa_id. Se resta el monto pagado de saldo_pendiente. Si el saldo es ≤ 0, el estado de la factura cambia automáticamente a EstadoFactura.PAGADA.
  6. Persistencia: SQLAlchemy persiste el nuevo registro en la tabla pagos y confirma la transacción en una sola operación atómica.
  7. Respuesta: El backend retorna status 201 Created con el ID del pago generado; la interfaz en React notifica con un Toast de éxito y refresca el tablero.

Backend — Estructura & Patrones de Diseño

Repositorio: back-deilify (Rama develop)

Estructura de Directorios del Backend

back-deilify/ ├── wsgi.py // Punto de entrada WSGI (app.run) ├── requirements.txt // Dependencias Python (Flask, SQLAlchemy, XGBoost, etc.) ├── alembic.ini // Configuración de migraciones Alembic ├── app/ │ ├── __init__.py // Application Factory (create_app) │ ├── config.py // Configuración central y variables de entorno │ ├── extensions.py // Instancias globales (db_session, Base, jwt, api) │ ├── models/ // Modelos declarativos SQLAlchemy 2.0 │ │ ├── empresa.py // Tabla empresas │ │ ├── usuario.py // Tabla usuarios con hash Argon2id │ │ ├── cliente.py // Tabla clientes y condiciones de crédito │ │ ├── factura.py // Tabla facturas y Enum EstadoFactura │ │ ├── pago.py // Tabla pagos / recaudos │ │ ├── prediccion_ia.py // Tablas ai_models y prediction_logs │ │ ├── audit_log.py // Tabla historial_cobranza │ │ └── acuerdo_pago.py // [0 bytes - Stub no implementado] │ ├── api/ // Controladores HTTP Flask-RESTX │ │ ├── root.py // GET / (Health Check) │ │ ├── auth/ // POST /auth/register, /auth/login, GET /auth/me │ │ ├── usuarios/ // CRUD /usuarios │ │ ├── clientes/ // CRUD /clientes │ │ └── cartera/ // /cartera/dashboard, /facturas, /recaudos, /estado-cuenta │ ├── services/ // Lógica de negocio desacoplada │ │ ├── auth_service.py // Registro y emisión de tokens con claims │ │ ├── usuario_service.py // Gestión y validación de usuarios │ │ └── cartera_service.py // Cálculos financieros y aplicación de pagos │ ├── schemas/ // Esquemas Marshmallow para validación/serialización │ │ ├── auth.py │ │ ├── usuarios.py │ │ └── cartera.py │ ├── middleware/ │ │ ├── tenant.py // Extracción multi-tenant de empresa_id │ │ ├── plan.py // [0 bytes - Stub no implementado] │ │ └── audit.py // [0 bytes - Stub no implementado] │ ├── ia/ // Módulo de Inteligencia Artificial (Clean Architecture) │ │ ├── controllers/ // ai_controller.py (Endpoints Flask-RESTX) │ │ ├── services/ // model_training_service.py & prediction_service.py │ │ ├── repositories/ // ai_model_repository.py & prediction_repository.py │ │ ├── ml/ // features.py (Extracción de variables) │ │ ├── routes/ // ai_routes.py (Namespace /api/v1/ai) │ │ ├── schemas/ // ai_schemas.py (Marshmallow) │ │ ├── modelos/ // Almacenamiento local de archivos .joblib │ │ └── reporte_inteligente.py // Generador narrativo SmartReportService │ └── utils/ │ └── commands.py // CLI Click (flask cartera check-overdue) ├── migrations/ // Scripts de migración Alembic └── tests/ // Pruebas automatizadas (test_auth, test_usuarios, ia/)

Patrones Arquitectónicos Aplicados

Application Factory: La función create_app() en app/__init__.py inicializa dinámicamente extensiones, modelos, namespaces de RESTX y comandos CLI, permitiendo inyectar configuraciones independientes para pruebas unitarias.

Clean Architecture en IA: El módulo de IA implementa separación estricta entre capa de presentación (Controllers RESTX), Casos de Uso (Services), Acceso a Datos (Repositories) y Algoritmos de Machine Learning (ML Features).

Scoped Session de SQLAlchemy: Manejo de sesiones de base de datos thread-safe con db_session = scoped_session(sessionmaker()).

Backend — Inventario Completo de Endpoints Reales

Catálogo exhaustivo de los 24 endpoints existentes en el código fuente de back-deilify.

Root / Monitoreo

GET / Health check del servicio backend

Módulo: app.api.root | Autenticación: Pública (No requiere token)

Respuesta (200 OK):

{"status": "ok", "message": "Deilify backend está en marcha"}

Autenticación (Namespace /auth)

POST /auth/register Registro de nuevo usuario y empresa

Módulo: app.api.auth | Autenticación: Pública

Descripción: Crea un usuario. Si no se pasa empresa_id, crea una nueva Empresa y vincula al usuario.

Body JSON requerido:
{
  "nombre": "string (min 2)",
  "email": "correo@ejemplo.com",
  "password": "min 6 caracteres",
  "rol": "ADMIN | USER",
  "empresa_nombre": "Mi Empresa",
  "empresa_nit": "900123456-1"
}
Respuesta (201 Created):
{
  "id": 1,
  "nombre": "Juan Perez",
  "email": "juan@deilify.com",
  "rol": "ADMIN",
  "empresa_id": 1,
  "created_at": "2026-09-14T...",
  "empresa": {"id": 1, "nombre": "...", "nit": "..."}
}

Errores: 400 Bad Request si el email o NIT ya existen, o validación de campos falla.

POST /auth/login Autenticación y generación de JWT con claims

Módulo: app.api.auth | Autenticación: Pública

Descripción: Valida credenciales contra el hash Argon2id y genera un JWT con claims role y empresa_id.

Body JSON:
{
  "email": "juan@deilify.com",
  "password": "mypassword123"
}
Respuesta (200 OK):
{
  "access_token": "eyJhbGciOiJIUzI1Ni...",
  "usuario": {
    "id": 1,
    "nombre": "Juan Perez",
    "email": "juan@deilify.com",
    "rol": "ADMIN",
    "empresa_id": 1,
    "empresa": {"id": 1, "nombre": "Deilify", "nit": "..."}
  }
}

Errores: 401 Unauthorized si las credenciales son incorrectas.

GET /auth/me Perfil del usuario autenticado en sesión

Módulo: app.api.auth | Autenticación: Bearer JWT

Respuesta (200 OK): Información detallada del usuario y empresa asociada.

Errores: 401 Unauthorized, 404 Not Found.

Usuarios (Namespace /usuarios)

GET /usuarios/ Listar todos los usuarios de la empresa

Módulo: app.api.usuarios | Autenticación: Bearer JWT

Respuesta (200 OK): Array con todos los usuarios registrados bajo la empresa_id del token.

POST /usuarios/ Crear usuario en la empresa (Requiere ADMIN)

Módulo: app.api.usuarios | Permisos: Rol ADMIN

Body: {"nombre": str, "email": str, "password": str, "rol": "ADMIN | USER"}

Errores: 403 Forbidden si no es administrador, 400 Bad Request si el correo ya existe.

GET /usuarios/{id} Detalle de un usuario de la empresa

Módulo: app.api.usuarios | Permisos: Mismo tenant

Errores: 403 Forbidden (otra empresa), 404 Not Found.

PUT /usuarios/{id} Modificar datos de usuario (ADMIN o auto-edición)

Módulo: app.api.usuarios | Permisos: ADMIN o el propio usuario

Regla: Solo un administrador puede modificar el campo rol.

DELETE /usuarios/{id} Eliminar usuario de la empresa (Requiere ADMIN)

Módulo: app.api.usuarios | Permisos: ADMIN (no puede auto-eliminarse)

Respuesta (200 OK): {"message": "Usuario eliminado correctamente"}

Clientes (Namespace /clientes)

GET /clientes/ Listar clientes de la empresa (orden alfabético)

Módulo: app.api.clientes | Autenticación: Bearer JWT

Respuesta (200 OK): Lista de clientes con id, nombre, identificacion, dias_plazo, limite_credito.

POST /clientes/ Crear cliente en la empresa

Módulo: app.api.clientes | Autenticación: Bearer JWT

Body: {"nombre": str, "identificacion": str, "dias_plazo": int, "limite_credito": float}

Validación: La identificacion debe ser única dentro de la misma empresa.

GET /clientes/{id} Consultar detalle de un cliente

Módulo: app.api.clientes | Errores: 403 Forbidden, 404 Not Found

PUT /clientes/{id} Actualizar información de cliente

Módulo: app.api.clientes | Body parcial: Permite actualizar nombre, identificación, plazo o límite.

DELETE /clientes/{id} Eliminar cliente sin facturas asociadas

Módulo: app.api.clientes | Restricción: Bloqueado con error 400 si el cliente posee facturas.

Cartera & Finanzas (Namespace /cartera)

GET /cartera/dashboard Métricas financieras: Cartera Vencida, DSO y Edades

Módulo: app.api.cartera | Autenticación: Bearer JWT

Respuesta (200 OK):

{
  "cartera_vencida_total": 12500000.0,
  "dso_proyectado": 38.4,
  "distribucion_edades": {
    "0-30": 6000000.0,
    "31-60": 4000000.0,
    "61-90": 2000000.0,
    "90+": 500000.0
  }
}
POST /cartera/facturas Sincronización y carga de facturas por lote

Módulo: app.api.cartera | Autenticación: Bearer JWT

Body JSON: Array de facturas a importar:

[
  {
    "cliente_id": 1,
    "numero": "FAC-2026-001",
    "fecha_emision": "2026-09-01T00:00:00",
    "fecha_vencimiento": "2026-09-30T00:00:00",
    "monto_total": 450000.0
  }
]

Efecto: Crea facturas con saldo_pendiente = monto_total y estado PENDIENTE.

POST /cartera/recaudos Registrar pago y descontar saldo de factura

Módulo: app.api.cartera | Autenticación: Bearer JWT

Body JSON requerido:
{
  "factura_id": 10,
  "monto": 250000.0,
  "metodo_pago": "transferencia",
  "transaccion_id": "TRX-893120"
}
Respuesta (201 Created):
{
  "message": "Pago registrado",
  "pago_id": 4
}

Regla financiera: Resta el monto del saldo pendiente. Si saldo ≤ 0, cambia el estado a EstadoFactura.PAGADA.

GET /cartera/clientes/{id}/estado-cuenta Estado de cuenta con historial de facturas del cliente

Módulo: app.api.cartera | Autenticación: Bearer JWT

Respuesta (200 OK): Array con todas las facturas del cliente, fecha de vencimiento, montos, saldos y estados.

Inteligencia Artificial (Namespace /api/v1/ai)

POST /api/v1/ai/train Entrenar modelos RandomForest y XGBoost (ADMIN)

Módulo: app.ia | Permisos: Requiere claim role: "ADMIN"

Descripción: Extrae 13 variables cuantitativas de clientes históricos, entrena RandomForest y XGBoost, evalúa métricas, almacena el archivo .joblib, guarda registros en ai_models y activa automáticamente el de mayor ROC AUC.

{
  "version": "v_20260914_210000",
  "best_model_id": 2,
  "best_algorithm": "XGBClassifier",
  "models": [
    {"id": 1, "algorithm": "RandomForestClassifier", "accuracy": 0.93, "precision": 0.91, "recall": 0.89, "f1_score": 0.90, "roc_auc": 0.94, "is_active": false},
    {"id": 2, "algorithm": "XGBClassifier", "accuracy": 0.96, "precision": 0.94, "recall": 0.92, "f1_score": 0.93, "roc_auc": 0.97, "is_active": true}
  ]
}
GET /api/v1/ai/models Listar todos los modelos de IA registrados y métricas

Módulo: app.ia | Autenticación: Bearer JWT

Respuesta (200 OK): Lista con ID, algoritmo, accuracy, precision, recall, f1, roc_auc y booleano is_active.

PUT /api/v1/ai/models/{id}/activate Activar manualmente un modelo para inferencias (ADMIN)

Módulo: app.ia | Permisos: Rol ADMIN

Efecto: Marca is_active = True en el modelo elegido y False en todos los demás.

POST /api/v1/ai/predict/{client_id} Inferencia de riesgo de mora individual en tiempo real

Módulo: app.ia | Autenticación: Bearer JWT

Respuesta (200 OK):

{
  "client_id": 5,
  "risk_score": 74,
  "probability": 0.742,
  "risk_level": "ALTO",
  "recommendation": "Seguimiento preventivo"
}
GET /api/v1/ai/predictions Historial paginado de predicciones registradas

Módulo: app.ia | Query Params: page (default 1), per_page (default 10)

Respuesta (200 OK): Total de predicciones, página y logs con score, probabilidad y fecha.

GET /api/v1/ai/predict/{client_id}/report Reporte inteligente 360° con dictamen narrativo

Módulo: app.ia | Autenticación: Bearer JWT

Respuesta (200 OK):

{
  "cliente_id": 5,
  "nombre": "Comercializadora XYZ",
  "analisis_cartera": {
    "total_facturas": 8,
    "facturas_vencidas": 1,
    "saldo_pendiente_total": 1200000.0,
    "ratio_uso_credito": 0.40,
    "promedio_dias_retraso": 4.2
  },
  "evaluacion_ia": {
    "score_riesgo": 32,
    "probabilidad_mora": 0.32,
    "nivel_riesgo": "BAJO",
    "recomendacion": "Monitoreo estándar"
  },
  "dictamen_detallado": "El cliente Comercializadora XYZ tiene un límite de crédito de 3,000,000.00 con plazo de 30 días..."
}

Backend — Seguridad, JWT & Multi-tenancy

Aislamiento lógico, roles de acceso y protección de datos.

Tokens JWT y Claims Personalizados

La autenticación en Deilify está basada en Flask-JWT-Extended. Al iniciar sesión exitosamente en /auth/login, se genera un token con la siguiente estructura de payload:

{
  "sub": "1",                 // ID del usuario (identity)
  "role": "ADMIN",            // Rol del usuario: "ADMIN" o "USER"
  "empresa_id": 1,            // Tenant ID al que pertenece el usuario
  "exp": 1726358400           // Tiempo de expiración (1 día por configuración)
}

Mecanismo de Extracción Multi-tenant (tenant.py)

En app/middleware/tenant.py, la función get_empresa_id_from_jwt() asegura que ninguna consulta acceda a información de otra empresa:

def get_empresa_id_from_jwt():
    claims = get_jwt()
    emp_id = claims.get("empresa_id")
    if emp_id is not None:
        return int(emp_id)
    # Soporte para identidad directa o fallback controlado en tests
    ...

Control de Acceso por Roles (RBAC)

Operación Rol Requerido Comportamiento si es Denegado
Entrenamiento IA (POST /api/v1/ai/train) ADMIN 403 Forbidden: "Se requiere rol de ADMINISTRADOR"
Activar Modelo IA (PUT /ai/models/<id>/activate) ADMIN 403 Forbidden: "Se requiere rol de ADMINISTRADOR"
Crear Usuarios (POST /usuarios/) ADMIN 403 Forbidden: "Solo administradores pueden crear usuarios"
Eliminar Usuarios (DELETE /usuarios/<id>) ADMIN 403 Forbidden (además no permite auto-eliminación)
Operar Cartera, Facturas y Pagos USER / ADMIN Requiere token válido del tenant

Almacenamiento de Contraseñas

Deilify utiliza Argon2id mediante la biblioteca argon2-cffi. Cada contraseña se hashea con sales criptográficos antes de persistirse en la base de datos (Usuario.set_password), y se valida con Usuario.check_password.

Frontend — Stack Real & Estructura

Repositorio: deilify-frontend

Stack Tecnológico Real

Librería / Herramienta Versión Propósito en Deilify
React 19.2.0 Biblioteca declarativa principal de interfaz de usuario.
Vite 6.3.5 Herramienta de compilación ultra rápida y servidor de desarrollo HMR.
TypeScript 5.x Tipado estático para páginas y componentes.
React Router DOM 7.1.3 Enrutamiento del lado del cliente, rutas protegidas y layouts.
Tailwind CSS 4.1.12 Framework de estilos utilitarios moderno.
Radix UI Primitivas Componentes accesibles: Dialog, Dropdown, Tabs, Accordion, Select.
Lucide React 0.487.0 Iconografía SVG estilizada para toda la aplicación.
Recharts 2.15.2 Visualización de gráficas de barras y pastel para DSO y edades de mora.
Axios 1.7.9 Cliente HTTP con interceptores para inyección y expiración de JWT.
React Hook Form + Zod 7.55 / 4.1 Gestión reactiva de formularios y validación estricta de esquemas.
React Hot Toast 2.6.0 Feedback visual e interactivo de operaciones y errores.

Estructura de Directorios del Frontend

deilify-frontend/ ├── package.json ├── vite.config.ts ├── src/ │ ├── main.tsx // Punto de entrada ReactDOM │ ├── App.tsx // Enrutador principal y Toaster │ ├── api/ // Módulos Axios para consumo del Backend │ │ ├── axios.js // Instancia base e interceptores de Authorization │ │ ├── auth.service.js // /auth/login, /auth/register, /auth/me │ │ ├── cartera.service.js // /cartera/dashboard, facturas, recaudos │ │ ├── clientes.service.js// /clientes CRUD │ │ ├── usuarios.service.js// /usuarios CRUD │ │ └── ai.service.js // /api/v1/ai/ models, train, predict, report │ ├── context/ │ │ └── AuthContext.jsx // Estado global de sesión, token, usuario y empresa │ ├── routes/ │ │ └── PrivateRoute.jsx // Guard de autenticación (redirección a /login) │ ├── layouts/ │ │ └── AppLayout.tsx // Layout con Sidebar, Header y rutas internas │ ├── pages/ // Vistas de la aplicación │ │ ├── Login.tsx // Inicio de sesión y registro de empresa │ │ ├── Dashboard.tsx // Métricas de cartera, Recharts y predicciones │ │ ├── Clientes.tsx // Listado, búsqueda y creación de clientes │ │ ├── ClienteDetalle.tsx // Estado de cuenta, facturas, pagos y scoring IA │ │ ├── Facturas.tsx // Gestión y carga masiva de facturas │ │ ├── Pagos.tsx // Registro de recaudos sobre facturas │ │ ├── IA.tsx // Gestión de modelos, métricas y entrenamiento │ │ ├── Usuarios.tsx // Administración de accesos ADMIN/USER │ │ ├── Configuracion.tsx // Datos de empresa y sesión │ │ ├── Perfil.tsx // Datos del usuario actual │ │ ├── CarteraModule.tsx // [Alias placeholder a Facturas] │ │ ├── GestionCobros.tsx // [Alias placeholder a Pagos] │ │ └── Reportes.tsx // [Alias placeholder a Dashboard] │ ├── components/ // Componentes UI y comunes │ │ ├── AppHeader.tsx │ │ ├── AppSidebar.tsx │ │ ├── common/ // DataTable, MetricCard, Modal, Feedback │ │ └── ui/ // Primitivas Radix + Tailwind │ └── utils/ // errorHandler.js, format.js, normalizers.js

Frontend — Páginas, Vistas & Componentes

Detalle de pantallas y su conexión con el backend.

Catálogo de Vistas

Ruta Componente Estado de Conexión Propósito
/login Login.tsx Conectado Formulario dual con modo "Iniciar Sesión" y "Registrar Empresa". Llama a authService.login o register.
/ o /dashboard Dashboard.tsx Conectado KPIs agregados de cartera, barras de antigüedad (Recharts), distribución de estado y listado de últimas predicciones IA.
/clientes Clientes.tsx Conectado Tabla de clientes con búsqueda interactiva, botón para nuevo cliente y acciones de edición y eliminación.
/clientes/:id ClienteDetalle.tsx Conectado Ficha 360° del cliente: estado de cuenta con facturas y pagos, botón "Predecir riesgo" y tarjeta de reporte predictivo.
/facturas Facturas.tsx Parcial Modal de creación/carga de facturas conectada a POST /cartera/facturas. La lectura de listado depende del dashboard.
/pagos Pagos.tsx Conectado Modal de registro de pagos conectada a POST /cartera/recaudos.
/ia/modelos IA.tsx Conectado Pestañas interactivas: Modelos (tabla de métricas + botón Activar), Entrenamiento (botón Entrenar), Predicciones e Historial.
/usuarios Usuarios.tsx Conectado Listado y modal CRUD de usuarios con asignación de roles ADMIN/USER. Protegido en frontend para rol ADMIN.
/configuracion Configuracion.tsx Conectado Datos de la empresa y usuario activo, botón para refrescar sesión y cierre de sesión.
/perfil Perfil.tsx Conectado Tarjeta con datos del usuario retornados por /auth/me.

Frontend — Estado, AuthContext & Consumo API

Gestión de sesión y peticiones HTTP.

Manejo de Sesión Global (AuthContext.jsx)

El frontend utiliza la Context API de React con persistencia local:

  • deilify_token: Almacena el JWT en localStorage (o sessionStorage si no se selecciona "Recordar").
  • deilify_user: Datos del usuario en sesión (nombre, email, rol).
  • deilify_company: Información básica de la empresa (nombre, NIT).
  • hasRole('ADMIN'): Utilidad para verificar privilegios en vistas protegidas.

Interceptor de Axios (src/api/axios.js)

api.interceptors.request.use((config) => {
  const token = localStorage.getItem("deilify_token") || sessionStorage.getItem("deilify_token");
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});

api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem("deilify_token");
      window.dispatchEvent(new Event("auth-logout"));
    }
    return Promise.reject(error);
  }
);

Base de Datos — Modelos Relacionales

Mapeo objeto-relacional real definido en SQLAlchemy 2.0 (8 tablas).

empresas Entidad Tenant de cada organización
ColumnaTipoModificadores / Relación
PK idIntegerAutoincremental
nombreString(100)NOT NULL — Razón social
nitString(20)NOT NULL, UNIQUE
created_atDateTimeDefault: utcnow
usuarios Usuarios pertenecientes a cada empresa
ColumnaTipoModificadores / Relación
PK idIntegerAutoincremental
FK empresa_idIntegerNOT NULL → empresas.id
nombreString(100)NOT NULL
emailString(120)NOT NULL, UNIQUE
password_hashString(255)NOT NULL — Hash Argon2id
rolString(20)NOT NULL, Default "USER" ("ADMIN" | "USER")
created_atDateTimeDefault: utcnow
clientes Deudores / Compradores gestionados
ColumnaTipoModificadores / Relación
PK idIntegerAutoincremental
FK empresa_idIntegerNOT NULL → empresas.id
nombreString(150)NOT NULL
identificacionString(20)NOT NULL (Validada única por tenant en API)
dias_plazoIntegerDefault: 30 días
limite_creditoFloatDefault: 0.0
facturas Cuentas por cobrar emitidas
ColumnaTipoModificadores / Relación
PK idIntegerAutoincremental
FK empresa_idIntegerNOT NULL → empresas.id
FK cliente_idIntegerNOT NULL → clientes.id
numeroString(50)NOT NULL — Código o consecutivo
fecha_emisionDateTimeNOT NULL
fecha_vencimientoDateTimeNOT NULL
monto_totalFloatNOT NULL
saldo_pendienteFloatNOT NULL (Disminuye con recaudos)
estadoEnum(EstadoFactura)PAGADA | PENDIENTE | VENCIDA (Default: PENDIENTE)
pagos Recaudos y abonos a facturas
ColumnaTipoModificadores / Relación
PK idIntegerAutoincremental
FK empresa_idIntegerNOT NULL → empresas.id
FK factura_idIntegerNOT NULL → facturas.id
montoFloatNOT NULL — Valor del recaudo
fecha_pagoDateTimeDefault: utcnow
metodo_pagoString(50)NOT NULL (transferencia, tarjeta, efectivo, etc.)
transaccion_idString(100)UNIQUE, Nullable — Referencia bancaria
ai_models Modelos de Machine Learning registrados
ColumnaTipoModificadores / Relación
PK idIntegerAutoincremental
nameString(100)NOT NULL
versionString(50)NOT NULL
algorithmString(100)NOT NULL (RandomForestClassifier / XGBClassifier)
accuracyFloatNOT NULL
precisionFloatNOT NULL
recallFloatNOT NULL
f1_scoreFloatNOT NULL
roc_aucFloatNOT NULL (Criterio de selección del mejor)
model_pathString(255)NOT NULL (Ruta al archivo .joblib)
is_activeBooleanNOT NULL, Default False
created_atDateTimeNOT NULL, Default utcnow
prediction_logs Trazabilidad histórica de inferencias predictivas
ColumnaTipoModificadores / Relación
PK idIntegerAutoincremental
FK client_idIntegerNOT NULL → clientes.id
FK model_idIntegerNOT NULL → ai_models.id
predictionFloatNOT NULL (Clase predicha: 0 o 1)
risk_scoreIntegerNOT NULL (Puntaje de 0 a 100)
probabilityFloatNOT NULL (Probabilidad 0.0 a 1.0)
created_atDateTimeNOT NULL, Default utcnow
historial_cobranza Notas de gestión y seguimiento de cartera
ColumnaTipoModificadores / Relación
PK idIntegerAutoincremental
FK empresa_idIntegerNOT NULL → empresas.id
FK cliente_idIntegerNOT NULL → clientes.id
fechaDateTimeDefault: utcnow
notaTextNOT NULL — Detalle de la gestión
usuario_idIntegerNullable — ID del gestor

Base de Datos — Migraciones Alembic

Evolución del esquema y compatibilidad de motores.

Estrategia de Inicialización

En desarrollo, el backend ejecuta Base.metadata.create_all(bind=engine) en app/__init__.py, asegurando que las tablas se creen automáticamente si no existen.

Para entornos de producción y control formal de cambios, se utiliza Alembic configurado en alembic.ini con scripts en migrations/versions/.

Comandos de Migración

# Crear una nueva revisión automática
alembic revision --autogenerate -m "nombre_de_migracion"

# Aplicar migraciones pendientes
alembic upgrade head

# Revertir última migración
alembic downgrade -1

Inteligencia Artificial — Pipeline de Machine Learning

Arquitectura y funcionamiento real del motor predictivo en app/ia/.

Módulo 100% Implementado: A diferencia de una "IA conceptual", Deilify cuenta con un pipeline de Machine Learning real completamente programado con Scikit-Learn, XGBoost, Pandas y Joblib, respaldado por pruebas de integración automatizadas.

Flujo de Entrenamiento (ModelTrainingService)

1. build_training_dataset() consulta Clientes, Facturas y Pagos
2. Valida dataset (≥ 10 clientes y al menos 2 clases en target)
3. División Train 80% / Test 20% estratificada por target
4. Entrena RandomForestClassifier y XGBClassifier
5. Evalúa Accuracy, Precision, Recall, F1 y ROC AUC
6. Persiste modelos en app/ia/modelos/*.joblib
7. Selecciona el modelo con mayor ROC AUC y lo activa en DB

Flujo de Inferencia en Vivo (PredictionService)

  1. Se invoca POST /api/v1/ai/predict/<client_id>.
  2. Se valida que el cliente exista y pertenezca a la empresa del token JWT.
  3. Se busca el modelo marcado como is_active = True en la tabla ai_models.
  4. Se carga el clasificador serializado desde el archivo .joblib mediante joblib.load().
  5. Se extraen las 13 características del cliente en tiempo real a partir de sus facturas vigentes e históricas.
  6. El modelo computa clf.predict_proba() para obtener la probabilidad de mora.
  7. Se escala la probabilidad a un score entero entre 0 y 100.
  8. Se categoriza el nivel de riesgo y se formula una recomendación accionable.
  9. Se persiste el log en la tabla prediction_logs para trazabilidad y auditoría.

Inteligencia Artificial — Feature Engineering

Las 13 variables financieras cuantitativas calculadas en app/ia/ml/features.py.

Variables del Modelo (FEATURE_COLUMNS)

Variable Tipo Cálculo & Definición en el Código
limite_credito Float Límite de crédito asignado contractualmente al cliente.
dias_plazo Integer Plazo de pago estipulado en días (ej. 30 días).
total_facturas Integer Cantidad histórica total de facturas emitidas al cliente.
facturas_pagadas Integer Facturas con saldo_pendiente ≤ 0 o estado PAGADA.
facturas_vencidas Integer Facturas con saldo_pendiente > 0 y fecha_vencimiento < hoy.
total_monto_facturado Float Sumatoria total del monto facturado históricamente.
saldo_pendiente_total Float Deuda total impaga consolidada en el momento del análisis.
monto_promedio_factura Float total_monto_facturado / total_facturas.
ratio_saldo_limite Float saldo_pendiente_total / limite_credito (Nivel de consumo de crédito).
promedio_dias_pago Float Días de retraso promedio entre pago y vencimiento en facturas pagadas y vigentes.
max_dias_mora Float Retraso máximo en días registrado en toda la historia del cliente.
tasa_mora Float Proporción de facturas con mora respecto al total emitido (0.0 a 1.0).
cantidad_notas_cobranza Integer Conteo de gestiones de cobro registradas en historial_cobranza.

Definición del Target de Entrenamiento

La función determine_target(features) clasifica al cliente con etiqueta de riesgo target = 1 si cumple cualquiera de las siguientes condiciones empíricas:

if features["facturas_vencidas"] > 0 or features["max_dias_mora"] > 30 or features["promedio_dias_pago"] > 15:
    return 1  # Cliente en mora / riesgo
return 0      # Cliente al día / bajo riesgo

Inteligencia Artificial — Escala de Riesgo & Reportes

Mapeo de puntajes y síntesis de lenguaje natural.

Escala de Riesgo Financiero (0 a 100)

Rango de Score Nivel de Riesgo Acción y Recomendación Sugerida
0 – 20 MUY BAJO Sin observaciones / Aprobación estándar
21 – 40 BAJO Monitoreo estándar
41 – 60 MEDIO Monitoreo periódico
61 – 80 ALTO Seguimiento preventivo
81 – 100 MUY ALTO Acción de cobro inmediata / Suspensión de crédito

Síntesis Narrativa (SmartReportService)

Ejemplo de Dictamen Generado Automáticamente por el Backend:

"El cliente Distribuidora del Norte S.A.S. tiene un límite de crédito de $10,000,000.00 con plazo de 30 días. Actualmente posee 2 factura(s) vencida(s) con un retraso máximo de 45 días, lo cual representa un riesgo directo de mora. Históricamente presenta un promedio de demora en sus pagos de 18.5 días. Su saldo pendiente actual ($8,500,000.00) consume un porcentaje crítico de su límite de crédito (85.0%)."

Estado de Integración Frontend ↔ Backend

Auditoría cruzada de contratos de comunicación y coherencia de datos.

Matriz Comparativa de Integración

Funcionalidad Backend Disponible Frontend Implementado Integración Estado Real
Registro de Empresa / Usuario Sí (POST /auth/register) Sí (Login.tsx) Sí Completo
Login JWT Sí (POST /auth/login) Sí (Login.tsx) Sí Completo
Perfil de Sesión Sí (GET /auth/me) Sí (Perfil.tsx, Configuracion.tsx) Sí Completo
CRUD de Usuarios Sí (/usuarios/) Sí (Usuarios.tsx) Sí Completo
CRUD de Clientes Sí (/clientes/) Sí (Clientes.tsx) Sí Completo
Dashboard (DSO, Antigüedad) Sí (/cartera/dashboard) Sí (Dashboard.tsx) Sí Completo
Carga de Facturas Sí (POST /cartera/facturas) Sí (Modal Facturas.tsx) Sí Completo
Listado General de Facturas No (Solo por cliente) Parcial (Extrae de dashboard) Inconsistente En desarrollo
Registro de Recaudo Sí (POST /cartera/recaudos) Sí (Modal Pagos.tsx) Sí (Alinear referencia) Completo
Listado Histórico de Pagos No (Solo en estado de cuenta) Parcial (Extrae de dashboard) Inconsistente En desarrollo
Estado de Cuenta Cliente Sí (/cartera/clientes/<id>/estado-cuenta) Sí (ClienteDetalle.tsx) Sí Completo
Entrenamiento de Modelos IA Sí (POST /api/v1/ai/train) Sí (IA.tsx) Sí Completo
Listar & Activar Modelos IA Sí (/api/v1/ai/models) Sí (IA.tsx) Sí Completo
Inferencia de Riesgo Individual Sí (/api/v1/ai/predict/<id>) Sí (ClienteDetalle.tsx) Sí Completo
Historial de Predicciones Sí (/api/v1/ai/predictions) Sí (IA.tsx y Dashboard.tsx) Sí Completo
Reporte Inteligente IA 360° Sí (/predict/<id>/report) Sí (ClienteDetalle.tsx) Sí Completo

Inconsistencias y Hallazgos de Integración

  • Desfase en Listado de Facturas: Facturas.tsx llama a carteraService.getDashboard() esperando encontrar un arreglo data.facturas para alimentar la tabla principal. Sin embargo, /cartera/dashboard retorna únicamente agregados estadísticos (cartera vencida, DSO y distribución por edades). Se requiere implementar GET /cartera/facturas en el backend.
  • Nombre de campo en Recaudos: En Pagos.tsx el formulario envía referencia, mientras que el esquema del backend (PagoSchema) espera transaccion_id. Se recomienda mapear transaccion_id: parsed.data.referencia.
  • Rutas huérfanas en Frontend: Los archivos CarteraModule.tsx, GestionCobros.tsx y Reportes.tsx son placeholders que reexportan otros componentes. No están enlazados en el menú lateral principal.

Código Real vs Documentación Antigua

Corrección de discrepancias históricas y mitos del proyecto.

Tópico Lo que decía la documentación antigua ("GesCartera") La realidad en el código fuente actual (Deilify)
Framework Frontend Vue.js 3 (Composition API, Pinia, Vuetify, VeeValidate). React 19.2.0, TypeScript, Vite 6, Tailwind CSS 4, Radix UI y Recharts.
Nombre de la Plataforma "GesCartera" Deilify (Deilify SaaS)
Base de Datos PostgreSQL 15 con triggers PL/pgSQL obligatorios y pg_cron. SQLAlchemy 2.0 ORM. Por defecto corre sobre SQLite (sqlite:///deilify.db) y soporta PostgreSQL vía SQLALCHEMY_DATABASE_URI.
Lógica de Aplicación de Pago Disparador SQL nativo de base de datos (trg_aplicar_pago). Lógica en capa de servicios Python (CarteraService.registrar_pago descuenta saldo y actualiza estado).
Endpoints de Cartera Rutas como /api/v1/cartera/resumen, /vencida, /morosos, /acuerdos. Rutas unificadas bajo /cartera: /cartera/dashboard, /cartera/facturas, /cartera/recaudos, /cartera/clientes/<id>/estado-cuenta.
Módulo de Inteligencia Artificial Documentado como genérico con segmentación y rutas inexistentes. Pipeline real completo: RandomForest + XGBoost, 13 features cuantitativas, selección automática por ROC AUC y reportes narrativos.
Planes Comerciales Decorador @requiere_plan('enterprise') para bloquear IA. No implementado. El archivo middleware/plan.py está vacío. La IA está accesible para usuarios autenticados.

Guía de Instalación & Desarrollo

Instrucciones precisas para ejecutar el proyecto en local.

Requisitos Previos

1. Puesta en Marcha del Backend (back-deilify)

# 1. Ingresar a la carpeta del backend
cd back-deilify

# 2. Asegurarse de estar en la rama de desarrollo
git checkout develop

# 3. Crear y activar entorno virtual
# En Windows (PowerShell):
python -m venv venv
.\venv\Scripts\Activate.ps1

# En Linux / macOS:
python3 -m venv venv
source venv/bin/activate

# 4. Instalar dependencias de Python
pip install -r requirements.txt

# 5. Configurar variables de entorno mínimas (ejemplo)
# Windows PowerShell:
$env:FLASK_DEBUG = "1"
$env:SECRET_KEY = "clave-secreta-desarrollo"
$env:JWT_SECRET_KEY = "clave-jwt-desarrollo"

# Linux / macOS:
export FLASK_DEBUG="1"
export SECRET_KEY="clave-secreta-desarrollo"
export JWT_SECRET_KEY="clave-jwt-desarrollo"

# 6. Iniciar el servidor de desarrollo
python wsgi.py
# Servidor escuchando en: http://localhost:5000

2. Puesta en Marcha del Frontend (deilify-frontend)

# 1. Ingresar a la carpeta del frontend
cd deilify-frontend

# 2. Instalar dependencias con npm
npm install

# 3. Configurar variable de entorno para apuntar a la API
# Crear archivo .env en la raíz de deilify-frontend con:
VITE_API_URL=http://localhost:5000

# 4. Iniciar el servidor Vite
npm run dev
# Interfaz disponible en: http://localhost:5173

Variables de Entorno

Variables del Backend (back-deilify):

FLASK_DEBUG=1
SECRET_KEY=clave-secreta-para-sesiones-flask
JWT_SECRET_KEY=clave-secreta-para-firmar-tokens-jwt
SQLALCHEMY_DATABASE_URI=sqlite:///deilify.db  # O postgresql://user:pass@host:5432/dbname

Variables del Frontend (deilify-frontend):

VITE_API_URL=http://localhost:5000
VITE_API_TIMEOUT=15000

Comandos CLI Útiles

# Marcar facturas vencidas impagas en la base de datos
flask cartera check-overdue

# Ejecutar pruebas unitarias e integración de IA y Auth
python -m unittest discover -s tests

Roadmap Técnico & Recomendaciones

Priorización estratégica para la evolución y madurez de Deilify.

Prioridad Alta (Inmediata)

  1. Fusionar cambios de develop a main en back-deilify: Actualmente la rama main carece de los módulos de auth, usuarios y clientes que ya están terminados en develop. Se debe realizar el Merge Request para que producción coincida con el desarrollo.
  2. Crear endpoint GET /cartera/facturas: Permitir al frontend listar todas las facturas de la empresa paginadas con filtros por estado (PAGADA, PENDIENTE, VENCIDA) y cliente, resolviendo el desfase en Facturas.tsx.
  3. Crear endpoint GET /cartera/pagos: Exponer el historial general de recaudos de la empresa para poblar directamente la vista Pagos.tsx.
  4. Alinear campo transaccion_id en Pagos.tsx: Ajustar el modal de registro de pagos para que envíe transaccion_id en lugar de referencia.

Prioridad Media (Consolidación)

  1. Implementar Módulo de Acuerdos de Pago (Refinanciación): Completar el archivo app/models/acuerdo_pago.py con tabla de acuerdos, cuotas e integración con facturas refinanciadas.
  2. Exportación de Reportes Financieros: Desarrollar generador de reportes PDF y Excel en app/api/reportes para estados de cuenta de cartera y recaudos mensuales.
  3. Configuración de Producción con Gunicorn & Docker: Llenar el archivo Dockerfile (actualmente en 0 bytes) y definir docker-compose.yml orquestando API, PostgreSQL y Frontend Nginx.

Prioridad Baja (Optimizaciones Futuras)

  1. Reentrenamiento Periódico Automatizado de IA: Configurar un Celery Beat o cron job para reentrenar los modelos de mora trimestralmente a medida que ingresan nuevos recaudos.
  2. Auditoría Completa de Modificaciones: Implementar middleware de logging estructurado que grabe snapshots de cambios críticos en cartera.

Recomendaciones Técnicas del Arquitecto

Unificación de Modelos Numéricos: En producción financiera de alto volumen con PostgreSQL, migrar los campos de montos de Float a Numeric(18, 2) para evitar imprecisiones de coma flotante en balances contables.