Files
mini-erp/mini-backend/docs/sap-proxy.md
T
GeorgeandClaude Sonnet 5 ab3c613165 Initial commit: mini-erp (backend FastAPI + frontend React/Vite)
Base para el flujo de ramas por cliente.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 10:34:12 -05:00

4.7 KiB

Flujo de Proxy SAP

Función Central: forward_to_sapb1

Todas las operaciones CRUD pasan por esta función en core/sap.py, que actúa como proxy hacia SAP Service Layer.

flowchart TD
    START([Inicio]) --> INPUT["method, endpoint_path,\njson_data, params, headers"]
    INPUT --> BUILD["Construye URL:\nSAPB1_SL_URL / endpoint_path"]
    BUILD --> HEADERS["Adjunta headers:\nCookie: B1SESSION=...\nContent-Type: application/json"]
    HEADERS --> SEND[Envía request a SAP]

    SEND --> CHECK{Respuesta}
    CHECK -->|2xx| SUCCESS[Retorna Response al router]
    CHECK -->|401| REFRESH["Force refresh:\nensure_sap_session()"]
    CHECK -->|Timeout / Error| FAIL[ConnectionError]

    REFRESH --> RETRY{Es reintento?}
    RETRY -->|No| SEND2["Reintenta request\ncon nueva sesión"]
    RETRY -->|Sí| ERROR["Extrae error.message.value\nde SAP y lanza HTTPException"]

    SEND2 --> CHECK2{Respuesta}
    CHECK2 -->|2xx| SUCCESS
    CHECK2 -->|4xx/5xx| ERROR

    SUCCESS --> END([Fin])
    FAIL --> END
    ERROR --> END

    style ERROR fill:#f96,stroke:#a33

Diagrama de Secuencia — Operación CRUD Típica

Ejemplo con GET /api/Items:

sequenceDiagram
    participant F as Frontend
    participant MW as Middleware
    participant R as router/items.py
    participant C as core/sap.py
    participant SAP as SAP Service Layer

    F->>MW: GET /api/Items?$skip=0&$top=20\nX-Session-Token: [token]
    MW->>MW: Valida sesión
    MW->>R: request.state.emp_id = "123"

    R->>C: forward_to_sapb1(\n  GET, "Items?$skip=0&$top=20&$inlinecount=allpages")

    C->>SAP: GET /b1s/v1/Items?$skip=0&$top=20\nCookie: B1SESSION=abc123
    SAP-->>C: 200 - value: [...], odata.count: 150

    C-->>R: Response
    R-->>F: 200 - data: [...], total: 150

Re-autenticación Automática ante 401

Cuando SAP rechaza la sesión actual (401), el sistema renueva automáticamente la sesión de SAP y reintenta:

sequenceDiagram
    participant R as Router
    participant C as core/sap.py
    participant SAP as SAP Service Layer

    R->>C: forward_to_sapb1(GET, "Items?...")
    C->>SAP: GET /b1s/v1/Items?...\nCookie: B1SESSION=abc123
    SAP-->>C: 401 Unauthorized

    Note over C: Sesión SAP expirada\n→ Re-autentica y reintenta

    C->>SAP: POST /Login\nUserName: manager, Password: ..., CompanyDB: ...
    SAP-->>C: 200 SessionId: xyz789
    C->>C: Actualiza sap_session_store

    C->>SAP: GET /b1s/v1/Items?...\nCookie: B1SESSION=xyz789
    SAP-->>C: 200 - value: [...]

    C-->>R: Response 200

    Note over R,C: Si el reintento también falla (4xx/5xx)\n→ se extrae error.message.value\n→ se lanza HTTPException

Parámetros OData y Headers SAP Soportados

Query Parameters (OData)

Parámetro Uso Ejemplo
$skip Paginación — desplazamiento $skip=20
$top Paginación — límite por página $top=20
$select Selección de campos $select=ItemCode,ItemName
$filter Filtrado $filter=ItemCode eq 'A001'
$orderby Ordenamiento $orderby=ItemCode asc
$inlinecount Contar registros totales $inlinecount=allpages

Headers SAP

Header Función
B1S-PageSize Tamaño de página personalizado
B1S-CaseInsensitive Búsqueda sin distinción mayúsc/minúscula
B1S-ReplaceCollectionsOnPatch Reemplazar colecciones completas en PATCH

Manejo de Errores

Situación Comportamiento
SAP devuelve 2xx Retorna Response al router (200 body, 201 body, 204 vacío)
SAP devuelve 401 Re-autentica automáticamente y reintenta 1 vez
SAP devuelve 4xx/5xx Extrae error.message.value y lanza HTTPException(status_code, detail)
Timeout de conexión Lanza ConnectionError
Error de red Lanza ConnectionError
Reintento también falla Lanza HTTPException con el mensaje de error SAP
SAP no disponible HTTPException(500)