731 lines
12 KiB
Markdown
731 lines
12 KiB
Markdown
# API Reference
|
|
|
|
> **Base URL:** `http://<host>: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: <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: <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 |
|