# API Reference > **Base URL:** `http://:8000` > **API Version:** 1.1.0 > **Content-Type:** `application/json` --- ## Headers Globales | Header | Requerido | Descripción | |--------|-----------|-------------| | `Content-Type` | Sí | `application/json` | | `X-Session-Token` | Sí (excepto `/api/Login`) | Token obtenido en el login | | `X-Request-ID` | Opcional | UUID para trazabilidad; si no se envía, el servidor lo genera | --- ## Formato de Errores Todas las respuestas de error siguen el mismo formato: ```json { "detail": "Mensaje de error de SAP o del servidor" } ``` | Código | Significado | |--------|-------------| | 400 | Error de negocio SAP (ej: código duplicado, validación fallida) | | 401 | Token inválido/expirado, o credenciales incorrectas | | 404 | Recurso no encontrado en SAP | | 500 | Error de conexión con SAP o error interno | --- ## Autenticación ### `POST /api/Login` Inicia sesión de empleado. Busca en SAP `EmployeesInfo` al empleado cuyo campo de usuario `U_WEB_USER` coincida con `Username`, y valida `Password` contra su campo `U_WEB_PASS`. También exige que el empleado esté activo (`Active == "tYES"`). **Request body:** ```json { "Username": "jgarcia", "Password": "********" } ``` **Response 200:** ```json { "message": "Login exitoso", "token": "aB3x...token_urlsafe_32_chars...", "username": "Juan", "company": "NOMBRE_COMPANYDB" } ``` **Response 401:** ```json { "detail": "Usuario o contraseña incorrectos" } ``` Otros posibles `detail` en 401: `"Usuario no registrado"` (no existe ningún empleado con ese `U_WEB_USER`) y `"Usuario inactivo"` (el empleado existe pero `Active != "tYES"`). --- ### `POST /api/Logout` Cierra la sesión del empleado. **Headers:** `X-Session-Token: ` **Response 200:** ```json { "message": "Logout exitoso" } ``` --- ## Socios de Negocio ### `GET /api/BusinessPartners` Lista paginada de socios de negocio. **Query parameters:** | Parámetro | Tipo | Default | Descripción | |-----------|------|---------|-------------| | `skip` | int | 0 | Registros a saltar (`$skip`) | | `top` | int | 50 | Registros por página (`$top`) | | `fields` | string | — | Campos a retornar, separados por coma (`$select`) | | `filter` | string | — | Filtro OData (`$filter`). Activa `B1S-CaseInsensitive` | **Response 200:** ```json { "odata.metadata": "https://.../$metadata#BusinessPartners", "value": [ { "CardCode": "C001", "CardName": "Cliente Demo", "CardType": "C" } ], "odata.count": 150 } ``` --- ### `GET /api/BusinessPartners/{bp_code}` Obtiene un socio de negocio por `CardCode`. **Response 200:** ```json { "CardCode": "C001", "CardName": "Cliente Demo", "CardType": "C", "Phone1": "123456789", "EmailAddress": "cliente@demo.com" } ``` --- ### `POST /api/BusinessPartners` Crea un nuevo socio de negocio. **Request body:** ```json { "CardCode": "C001", "CardName": "Cliente Demo", "CardType": "C" } ``` **Response 201:** ```json { "CardCode": "C001" } ``` --- ### `PATCH /api/BusinessPartners/{bp_code}` Actualiza un socio de negocio existente. **Request body:** ```json { "CardName": "Nuevo Nombre", "Phone1": "987654321" } ``` **Response 204:** Sin contenido. --- ## Artículos ### `GET /api/Items` Lista paginada de artículos. **Query parameters:** | Parámetro | Tipo | Default | Descripción | |-----------|------|---------|-------------| | `skip` | int | 0 | Registros a saltar | | `top` | int | 50 | Registros por página | | `fields` | string | — | Campos a retornar (`$select`) | | `filter` | string | — | Filtro OData (`$filter`) | **Response 200:** ```json { "odata.metadata": "https://.../$metadata#Items", "value": [ { "ItemCode": "A001", "ItemName": "Artículo Demo", "InventoryUOM": "UN" } ], "odata.count": 250 } ``` --- ### `GET /api/Items/{item_code}` Obtiene un artículo por `ItemCode`. **Response 200:** ```json { "ItemCode": "A001", "ItemName": "Artículo Demo", "InventoryUOM": "UN", "SalesPrice": 100.50 } ``` --- ### `POST /api/Items` Crea un nuevo artículo. **Request body:** ```json { "ItemCode": "A001", "ItemName": "Artículo Demo", "InventoryUOM": "UN" } ``` **Response 201:** ```json { "ItemCode": "A001" } ``` --- ### `PATCH /api/Items/{item_code}` Actualiza un artículo existente. **Request body:** ```json { "ItemName": "Nuevo Nombre", "SalesPrice": 120.00 } ``` **Response 204:** Sin contenido. --- ## Ventas ### Facturas (A/R Invoices) #### `GET /api/Invoices` Lista paginada de facturas de venta. **Query parameters:** | Parámetro | Tipo | Default | Descripción | |-----------|------|---------|-------------| | `skip` | int | 0 | Registros a saltar | | `top` | int | 50 | Registros por página | | `fields` | string | — | Campos a retornar | | `filter` | string | — | Filtro OData | | `orderby` | string | — | Ordenamiento (`$orderby`) | **Response 200:** ```json { "odata.metadata": "https://.../$metadata#Invoices", "value": [ { "DocEntry": 1001, "CardCode": "C001", "DocTotal": 1500.00, "DocDate": "2026-07-06" } ], "odata.count": 45 } ``` --- #### `POST /api/Invoices` Crea una factura de venta. **Response 201:** ```json { "DocEntry": 1001 } ``` --- #### `GET /api/Invoices/{invoice_key}` Obtiene una factura por `DocEntry`. --- #### `PATCH /api/Invoices/{invoice_key}` Actualiza una factura. Usa header `B1S-ReplaceCollectionsOnPatch: true`. **Response 204:** Sin contenido. --- ### Pedidos (Sales Orders) #### `GET /api/Orders` Lista paginada de pedidos de venta. Mismos parámetros que Invoices. #### `POST /api/Orders` Crea un pedido de venta. **Response 201:** ```json { "DocEntry": 2001 } ``` #### `GET /api/Orders/{order_key}` Obtiene un pedido por `DocEntry`. #### `PATCH /api/Orders/{order_key}` Actualiza un pedido. Usa header `B1S-ReplaceCollectionsOnPatch: true`. **Response 204:** Sin contenido. --- ### Guías de Remisión de Venta (Delivery Notes) #### `GET /api/DeliveryNotes` Lista paginada de guías de remisión de venta. Mismos parámetros que Invoices/Orders. #### `POST /api/DeliveryNotes` Crea una guía de remisión de venta. **Response 201:** ```json { "DocEntry": 3001 } ``` #### `GET /api/DeliveryNotes/{key}` Obtiene una guía de remisión por `DocEntry`. #### `PATCH /api/DeliveryNotes/{key}` Actualiza una guía de remisión. Usa header `B1S-ReplaceCollectionsOnPatch: true`. **Response 204:** Sin contenido. --- ## Compras ### Órdenes de Compra #### `GET /api/PurchaseOrders` Lista paginada. Mismos parámetros que Invoices/Orders. #### `POST /api/PurchaseOrders` Crea una orden de compra. **Response 201:** ```json { "DocEntry": 4001 } ``` #### `GET /api/PurchaseOrders/{key}` Obtiene una orden de compra por `DocEntry`. #### `PATCH /api/PurchaseOrders/{key}` Actualiza una orden de compra. Usa header `B1S-ReplaceCollectionsOnPatch: true`. **Response 204:** Sin contenido. --- ### Facturas de Compra (A/P Invoices) #### `GET /api/PurchaseInvoices` Lista paginada. Mismos parámetros que Invoices/Orders. #### `POST /api/PurchaseInvoices` Crea una factura de compra. **Response 201:** ```json { "DocEntry": 4501 } ``` #### `GET /api/PurchaseInvoices/{key}` Obtiene una factura de compra por `DocEntry`. #### `PATCH /api/PurchaseInvoices/{key}` Actualiza una factura de compra. Usa header `B1S-ReplaceCollectionsOnPatch: true`. **Response 204:** Sin contenido. --- ### Guías de Recepción de Compra (Purchase Delivery Notes) #### `GET /api/PurchaseDeliveryNotes` Lista paginada. Mismos parámetros que Invoices/Orders. #### `POST /api/PurchaseDeliveryNotes` Crea una guía de recepción de compra. **Response 201:** ```json { "DocEntry": 5001 } ``` #### `GET /api/PurchaseDeliveryNotes/{key}` Obtiene una guía de recepción de compra por `DocEntry`. #### `PATCH /api/PurchaseDeliveryNotes/{key}` Actualiza una guía de recepción de compra. Usa header `B1S-ReplaceCollectionsOnPatch: true`. **Response 204:** Sin contenido. --- ## Catálogos ### `GET /api/Currencies` Lista de monedas. **Response 200:** ```json { "value": [ { "Code": "USD", "Name": "US Dollar" }, { "Code": "EUR", "Name": "Euro" } ] } ``` --- ### `GET /api/BusinessPartnerGroups` Lista de grupos de socios de negocio. **Response 200:** ```json { "value": [ { "Number": 1, "GroupName": "Clientes Nacionales" } ] } ``` --- ### `GET /api/Warehouses` Lista paginada de almacenes. Acepta `skip`, `top`, `fields`, `filter`. --- ### `GET /api/ItemGroups` Lista de grupos de artículos. **Response 200:** ```json { "value": [ { "Number": 1, "GroupName": "Materia Prima" } ] } ``` --- ### `GET /api/SalesTaxCodes` Lista paginada de códigos de IVA. Acepta `skip`, `top`, `fields`, `filter`. --- ### `GET /api/FactoringIndicators` Lista paginada de indicadores de factoring. Acepta `skip`, `top`, `fields`, `filter`. --- ### `GET /api/Sunat/BusinessPartners/DocumentType` Lista los valores del campo de usuario `EXX_TIPODOCU` (tabla `OCRD`, tipo de documento SUNAT del socio de negocio), consultando `UserFieldsMD` en SAP. Endpoint fijo, sin parámetros de paginación. **Response 200:** ```json { "value": [ { "FieldID": 1, "Description": "DNI" }, { "FieldID": 2, "Description": "RUC" } ] } ``` --- ## Lotes ### `GET /api/BatchNumberDetails` Lista paginada de lotes/números de serie. Acepta `skip`, `top`, `fields`, `filter`, `orderby`. --- ### `GET /api/BatchNumberDetails/{batch_id}` Obtiene un lote por ID. --- ### `PATCH /api/BatchNumberDetails/{batch_id}` Actualiza un lote. **Response 204:** Sin contenido. --- ## Permisos ### `GET /api/user/menu` Retorna las rutas de menú permitidas para el empleado autenticado. **Headers:** `X-Session-Token: ` **Response 200:** ```json { "allowedPaths": [ "/inventario/*", "/ventas/*", "/compras/*" ] } ``` **Response 401:** ```json { "detail": "Usuario no autenticado" } ``` --- ## Reportes (API Gateway — Crystal Reports) Estos endpoints no van contra el Service Layer sino contra el **SAP API Gateway** (`core/api_gateway.py`), que expone los reportes Crystal Reports autorizados para la compañía. ### `GET /api/apigateway/reports` Lista los reportes Crystal Reports autorizados para el usuario/compañía. **Response 200:** ```json [ { "DocCode": "R001", "DocName": "Guía de Remisión" } ] ``` --- ### `GET /api/apigateway/reports/{docCode}/parameters` Obtiene los parámetros de entrada que requiere un reporte puntual (por `DocCode`) antes de exportarlo. --- ### `POST /api/apigateway/reports/{docCode}/export` Exporta el reporte a PDF. El body es la lista de parámetros que el reporte necesita (formato definido por el propio API Gateway / Crystal Reports). **Request body:** ```json [ { "name": "DocEntry", "value": "1001" } ] ``` **Response 200:** `application/pdf` (streaming, `Content-Disposition: inline`). --- ## Reportes (Jinja/HTML → PDF) Generados desde plantillas HTML propias (`templates/reports/`) renderizadas con Jinja2 y convertidas a PDF con WeasyPrint, a partir de datos consultados directamente en HANA (`core/hana.py`), sin pasar por el Service Layer. ### `POST /api/reports/stickers` Genera el sticker de una guía de remisión de venta (`DeliveryNotes`) para imprimir etiquetas de despacho. **Request body:** ```json { "doc_entry": 3001, "cantidad": 4, "formato": "pdf", "papel": "Carta" } ``` | Campo | Tipo | Requerido | Descripción | |-------|------|-----------|-------------| | `doc_entry` | int | Sí | `DocEntry` de la guía de remisión en HANA | | `cantidad` | int | Sí | Cantidad de stickers a generar | | `formato` | `"pdf"` \| `"html"` | No (default `"pdf"`) | Formato de salida | | `papel` | `"A4"` \| `"Carta"` | No (default `"Carta"`) | Tamaño de papel del PDF | **Response 200:** `application/pdf` o `text/html` según `formato`, streaming. **Response 404:** ```json { "detail": "No se encontró el documento 3001" } ``` **Response 500:** error al consultar HANA. --- ## Resumen de Códigos de Respuesta | Método | Éxito | Cuerpo en éxito | Error SAP | Error de conexión | |--------|-------|----------------|-----------|-------------------| | GET | 200 | JSON del recurso | 4xx con `detail` | 500 | | POST | 201 | `{"CardCode"/"ItemCode"/"DocEntry": valor}` | 4xx con `detail` | 500 | | PATCH | 204 | Sin cuerpo | 4xx con `detail` | 500 |