Ir al contenido

Arquitectura

ERP DLM es un ERP completo construido con Python FastAPI + Vue 3 + PrimeVue 4 + PostgreSQL 16, containerizado con Docker Compose.

Capa Tecnología
Backend Python 3.13, FastAPI, SQLAlchemy 2.0 async, asyncpg, Alembic, Pydantic v2
Frontend Vue 3 Composition API, TypeScript, PrimeVue 4, Pinia, vue-i18n, Vite
Base de datos PostgreSQL 16 (UUID PKs, soft deletes, timestamps, JSONB)
Infraestructura Docker Compose (postgres, redis, backend, frontend), Caddy reverse proxy
erp-dlm/
├── backend/
│ └── app/
│ ├── core/ # Auth, DB, module_loader, servicios core
│ └── modules/ # ~50 módulos funcionales
├── frontend/
│ └── src/
│ ├── core/ # Layouts, componentes reutilizables, API client, tours
│ ├── modules/ # Vistas por módulo
│ ├── locales/ # es.json, en.json
│ └── router/ # Vue Router con lazy loading
├── docs/ # Documentación técnica adicional
└── docs-site/ # Este sitio de documentación (Astro + Starlight)

Cada módulo funcional vive en backend/app/modules/<nombre>/ y contiene típicamente:

  • __init__.py — puede estar vacío.
  • api.py — variable router = APIRouter(prefix="", ...).
  • models.py — tablas SQLAlchemy.
  • schemas.py — modelos Pydantic.
  • service.py — lógica de negocio.

backend/app/core/module_loader.py escanea backend/app/modules/ en el arranque y registra automáticamente cada módulo con el prefijo /api/{nombre-del-modulo}.

Reglas importantes:

  • El router de cada api.py debe tener prefix="".
  • El loader convierte guiones bajos en guiones: sales_invoices se monta como /api/sales-invoices.
  • Algunos módulos se excluyen del auto-descubrimiento y se registran manualmente en main.py:
    • bank_reconciliation → /api/bank-reconciliation
    • mcp_server → /api/mcp
    • client_portal → /api/portal
    • accounting/reports_api.py → /api
    • saas_manager → /api/saas

El sistema soporta multi-tenant a través del header Host.

  • erp.ovlicuo.com → instancia por defecto.
  • acme-sl.erp.ovlicuo.com → tenant acme-sl.

El middleware (backend/app/core/tenant_middleware.py) extrae el subdominio, consulta la base de datos del gestor SaaS (saas_manager) y crea un engine SQLAlchemy cacheado para la base de datos del tenant. La variable request.state.tenant_subdomain se propaga a get_db().

Servicio Fichero Responsabilidad
accounting_service backend/app/core/accounting_service.py Genera asientos contables automáticos desde facturas y define mapeo de subcuentas de IVA/RE
numbering backend/app/core/numbering.py Numeración automática de documentos: {SERIE}-{AÑO}-{SECUENCIA:05d}
pdf_service backend/app/core/pdf_service.py Generación de PDFs con ReportLab
stock_service backend/app/core/stock_service.py Gestión de stock por almacén, reservas e-commerce y transferencias
tax_reports backend/app/core/tax_reports.py Modelos fiscales españoles: 303, 347, 349, 111, 115, 390, 190, 130
document_workflow backend/app/core/document_workflow.py Conversión entre documentos de ventas/compras

El backend usa JWT:

  • Login: POST /api/auth/login devuelve access_token y refresh_token.
  • Refresh: POST /api/auth/refresh.
  • Perfil: GET /api/auth/me.
  • Permisos: GET /api/auth/me/permissions.

Para integraciones externas existe el módulo api_tokens, que permite crear tokens con ámbitos (api_access) y filtrar por company_id en entornos multi-tenant.

  • Views orquestan páginas, Components reciben props y emiten eventos, Composables contienen la lógica.
  • apiClient tiene baseURL: '/api': las llamadas usan rutas sin duplicar el prefijo (apiClient.get('/agents')).
  • PrimeVue 4: usa Select (no Dropdown), ToggleSwitch (no InputSwitch) y Tabs/TabList/Tab (no TabView).
  • Cada nueva vista del router bajo MainLayout requiere tour (core/tours/<routeName>.ts), registro en core/tours/registry.ts, guía en core/guide/guide-content.ts y claves i18n en es.json y en.json.