Files
2026-08-17 11:58:38 -05:00

11 KiB

Arquitectura General

Diagrama de Componentes

graph TB
    subgraph "FastAPI Application"
        M[main.py\nEntry Point]
        C[context.py\nRequest ID Context]
        L[logger_config.py\nStructured Logging]

        subgraph "Core"
            SAP[core/sap.py\nSAP Session & Proxy]
            SES[core/session.py\nEmployee Sessions]
            HANA[core/hana.py\nConexión directa HANA]
            AG[core/api_gateway.py\nSesión y Proxy APIGateway]
            CRUD[core/crud_router.py\nFactory de routers CRUD OData]
        end

        subgraph "Routers"
            A[auth.py\nLogin / Logout]
            BP[business_partners.py\nSocios de Negocio]
            IT[items.py\nArtículos]
            SL[sales.py\nFacturas / Pedidos / Guías]
            PR[purchases.py\nÓrdenes / Facturas / Guías Compra]
            CT[catalogs.py\nCatálogos]
            BT[batch.py\nLotes]
            PM[permissions.py\nPermisos de Menú]
            APG[apigateway.py\nCrystal Reports]
            RP[reports.py\nReportes Jinja/HTML → PDF]
        end

        MP[menu_permissions.json]
        TPL[templates/reports/*.html]
    end

    subgraph "External"
        SAP_SL[SAP B1 Service Layer]
        SAP_AG[SAP B1 API Gateway\nrs/v1/*]
        SAP_HANA[(SAP HANA)]
    end

    M --> C
    M --> L
    C --> L

    M --> A
    M --> BP
    M --> IT
    M --> SL
    M --> PR
    M --> CT
    M --> BT
    M --> PM
    M --> APG
    M --> RP

    A --> SAP
    A --> SES
    BP --> CRUD
    IT --> CRUD
    SL --> CRUD
    PR --> CRUD
    CT --> CRUD
    BT --> CRUD
    CRUD --> SAP
    PM --> MP
    APG --> AG
    RP --> HANA
    RP --> TPL

    SAP --> SAP_SL
    SES --> SAP
    AG --> SAP_AG
    HANA --> SAP_HANA

business_partners.py, items.py, sales.py, purchases.py, batch.py y la mayor parte de catalogs.py ya no implementan cada endpoint a mano: usan core/crud_router.py, una factory (make_crud_router) que genera el list/get/create/update típico de un recurso OData (mismo bloque de $skip/$top/$filter y llamada a forward_to_sapb1) a partir de un nombre de recurso y unos flags (si soporta $orderby, si expone POST, headers extra para el PATCH, etc). Esto evita que un copy-paste entre recursos deje, por ejemplo, un PATCH apuntando al endpoint de otro recurso. Un router de documentos de mercadeo (ventas o compras) puede generar varios recursos a la vez iterando esa factory, por eso sales.py y purchases.py ya no son un único router sino un APIRouter que incluye uno por cada tipo de documento.

Pipeline de Middleware

Las peticiones atraviesan los siguientes middleware en orden:

flowchart LR
    REQ[Request] --> CORS[CORSMiddleware\nallow_origins=*]
    CORS --> CID[correlation_id_middleware\nX-Request-ID]
    CID --> SES_MW[session_middleware\nValida X-Session-Token]
    SES_MW --> ROUTER{Router}
    ROUTER -->|/api/Login| A[auth.py]
    ROUTER -->|Otros /api/*| DEST[routers → core/sap.py, core/api_gateway.py o core/hana.py]

Detalle de Middleware

Middleware Función
CORSMiddleware Permite peticiones desde cualquier origen
correlation_id_middleware Genera o extrae X-Request-ID para trazabilidad en logs
session_middleware Valida token de sesión, adjunta el usuario (webUser) a request.state y al contexto de logging

Arquitectura de Sesiones

Existen tres capas de sesión/credenciales independientes:

flowchart LR
    subgraph Employee["Employee Session"]
        T["Token aleatorio → webUser"]
        E["employee_sessions dict"]
        TK["active_tokens dict"]
        T --> TK
        TK --> E
    end

    subgraph SAP["SAP B1 Service Layer Session"]
        S["sap_session_store"]
        MC["Manager Credentials (.env: SAPB1_SL_*)"]
        S -.->|Renueva en 401| MC
    end

    subgraph AG["SAP API Gateway Session"]
        AS["api_gateway_session (requests.Session)"]
        AC["Manager Credentials (.env: APIGATEWAY_*)"]
        AS -.->|Renueva en 401| AC
    end
Capa Almacenamiento Ámbito Timeout Renovación
SAP Service Layer sap_session_store (core/sap.py) Global Gestionado SAP Automática al recibir 401
SAP API Gateway api_gateway_session (core/api_gateway.py) Global Gestionado SAP Automática al recibir 401
Empleado active_tokens / employee_sessions (core/session.py) Por empleado 30 min inactividad Login manual

core/hana.py no mantiene sesión: abre una conexión nueva a HANA por petición (hana_connection(), context manager) y la cierra al terminar.

Estructura de Rutas

Método Ruta Router Descripción
POST /api/Login auth.py Autenticar empleado
POST /api/Logout auth.py Cerrar sesión
GET /api/BusinessPartners business_partners Listar socios
GET /api/BusinessPartners/{code} business_partners Obtener socio
POST /api/BusinessPartners business_partners Crear socio
PATCH /api/BusinessPartners/{code} business_partners Actualizar socio
GET /api/Items items.py Listar artículos
GET /api/Items/{code} items.py Obtener artículo
POST /api/Items items.py Crear artículo
PATCH /api/Items/{code} items.py Actualizar artículo
GET/POST/PATCH /api/Invoices[/{key}] sales.py Facturas de venta
GET/POST/PATCH /api/Orders[/{key}] sales.py Pedidos de venta
GET/POST/PATCH /api/DeliveryNotes[/{key}] sales.py Guías de remisión de venta
GET/PATCH /api/PurchaseOrders[/{key}] purchases.py Órdenes de compra
GET/PATCH /api/PurchaseDeliveryNotes[/{key}] purchases.py Guías de recepción de compra
GET/PATCH /api/PurchaseInvoices[/{key}] purchases.py Facturas de compra
GET /api/Currencies catalogs.py Listar monedas
GET /api/BusinessPartnerGroups catalogs.py Listar grupos de socios
GET /api/ItemGroups catalogs.py Listar grupos artículos
GET /api/Warehouses catalogs.py Listar almacenes
GET /api/SalesTaxCodes catalogs.py Listar códigos de IVA
GET /api/FactoringIndicators catalogs.py Listar indicadores de factoring
GET /api/Sunat/BusinessPartners/DocumentType catalogs.py Tipos de documento SUNAT (campo de usuario EXX_TIPODOCU)
GET /api/BatchNumberDetails batch.py Listar lotes
GET /api/BatchNumberDetails/{id} batch.py Obtener lote
PATCH /api/BatchNumberDetails/{id} batch.py Actualizar lote
GET /api/user/menu permissions.py Obtener menú permitido
GET /api/apigateway/reports apigateway.py Listar reportes Crystal Reports autorizados
GET /api/apigateway/reports/{docCode}/parameters apigateway.py Parámetros de un reporte Crystal Reports
POST /api/apigateway/reports/{docCode}/export apigateway.py Exportar reporte Crystal Reports a PDF
POST /api/reports/stickers reports.py Generar PDF/HTML de stickers de guía de remisión (consulta directa a HANA + plantilla Jinja + WeasyPrint)

Los recursos servidos por core/crud_router.py (todos los de la tabla salvo auth, permissions, apigateway y reports) soportan además skip, top, fields y filter como query params, y algunos (Invoices, Orders, DeliveryNotes, PurchaseOrders, PurchaseDeliveryNotes, PurchaseInvoices, BatchNumberDetails) también orderby. Ver API Reference para el detalle completo por endpoint.

Dependencias del Proyecto

graph TD
    main --> context
    main --> logger_config
    logger_config --> context
    main --> auth
    main --> business_partners
    main --> items
    main --> sales
    main --> purchases
    main --> catalogs
    main --> batch
    main --> permissions
    main --> apigateway_router[apigateway]
    main --> reports_router[reports]

    auth --> sap_core
    auth --> session_core
    business_partners --> crud_router
    items --> crud_router
    sales --> crud_router
    purchases --> crud_router
    catalogs --> crud_router
    catalogs --> sap_core
    batch --> crud_router
    crud_router --> sap_core
    permissions --> menu_json[menu_permissions.json]
    apigateway_router --> api_gateway_core[api_gateway]
    reports_router --> hana_core[hana]
    reports_router --> templates_reports[templates/reports/*.html]

Configuración por Entorno

La aplicación carga siempre mini-backend/.env (un solo archivo por despliegue, sin sufijos de entorno) mediante load_dotenv. Ese archivo no se versiona (está en .gitignore); la plantilla de referencia con todas las variables necesarias y valores de ejemplo es mini-backend/.env.example.

Grupos de variables:

Grupo Variables Usado por
SAP Service Layer SAPB1_SL_URL, SAPB1_SL_USER, SAPB1_SL_PASSWORD, SAPB1_SL_COMPANYDB, SAPB1_SL_TIMEOUT, SAPB1_SL_VERIFY_SSL core/sap.py, routers/auth.py
SAP API Gateway APIGATEWAY_URL, APIGATEWAY_USER, APIGATEWAY_PASSWORD, APIGATEWAY_COMPANYDB, APIGATEWAY_DBINSTANCE, APIGATEWAY_TIMEOUT core/api_gateway.py
HANA (acceso directo) HANA_HOST, HANA_PORT, HANA_USER, HANA_PASSWORD, HANA_SCHEMA core/hana.py, routers/reports.py
Logging LOG_ENVIRONMENT (production = JSON estructurado, cualquier otro valor = texto legible en consola) main.py, logger_config.py

Al desplegar para un cliente nuevo (ver flujo de branches por cliente en CLAUDE.md de la raíz), se copia .env.example a .env dentro de mini-backend/ y se completa con las credenciales reales de SAP Business One / HANA de ese cliente. Las credenciales reales nunca deben documentarse ni commitearse.