# Integración de XPERT con sistemas externos (no-SAP)

**Para quién es este documento:** para el equipo técnico (interno o del prospecto/cliente) que va a construir o evaluar la conexión entre XPERT y un ERP/sistema legado que no es SAP Business One.

**Para quién NO hace falta que sea 100% técnico:** la primera sección ("El concepto") está pensada para poder explicarse a un interlocutor de negocio sin conocimientos de programación.

---

## 1. El concepto (sin jerga)

XPERT nació conectado a SAP Business One, pero **no depende de SAP en sí** — depende de que, del otro lado de una URL configurable, haya "algo" que le conteste con un formato específico de datos (JSON, sobre HTTP). Ese "algo" hoy es SAP real. En desarrollo usamos un simulador (`mock/`) que le contesta exactamente igual, y XPERT funciona idéntico — lo cual demuestra que el acoplamiento es al **formato**, no al producto SAP.

Para integrar un cliente sin SAP hay dos caminos:

1. **Conexión directa**: el sistema del cliente expone él mismo esos mismos endpoints con ese mismo formato. Poco realista en sistemas legados.
2. **Middleware / adaptador** (recomendado): un servicio intermedio que de un lado habla exactamente el idioma que XPERT espera (lo documentado abajo), y del otro lado se conecta como pueda al sistema real del cliente (su base de datos, su propia API, archivos, lo que exista). **XPERT no requiere ningún cambio de código** — solo se reconfigura la URL de conexión (`sap_config.service_layer_url`) apuntando al middleware.

Este documento especifica **exactamente qué debe producir y aceptar ese middleware (o el sistema del cliente, si se conecta directo)** para que XPERT funcione sin tocarle una línea de código.

---

## 2. Arquitectura de la conexión

- **Transporte**: HTTP/HTTPS, cuerpos en JSON.
- **Autenticación**: por sesión (mismo patrón que SAP Service Layer / OData).
  1. `POST {base_url}/Login` con body `{ "CompanyDB": "...", "UserName": "...", "Password": "..." }`.
  2. Respuesta esperada: `{ "SessionId": "..." }`. XPERT guarda ese ID y lo manda como `Cookie: B1SESSION={SessionId}` en cada pedido posterior.
  3. `POST {base_url}/Logout` para cerrar sesión.
  4. Si XPERT recibe HTTP 401 en cualquier pedido, reintenta hacer login una sola vez automáticamente antes de fallar.
- **Configuración de conexión**: vive en una sola fila de la tabla `sap_config` (URL base, empresa/company DB, usuario, contraseña cifrada, si verifica SSL, timeout). Es lo único que cambia entre un cliente y otro — el resto del código de XPERT es idéntico.
- **Errores**: cualquier respuesta con HTTP ≥ 400 debe traer, si es posible, `{ "error": { "message": { "value": "texto descriptivo" } } }` — ese texto es lo que XPERT le muestra al usuario y guarda en su log de errores.

---

## 3. Datos maestros que XPERT necesita LEER

Se traen bajo demanda (sincronización manual desde el panel de administración), paginados con `$top`/`$skip`.

### 3.1 Artículos — `GET /Items`

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `ItemCode` | string | sí | Código único del artículo (clave). |
| `ItemName` | string | sí | Descripción. |
| `BarCode` | string | no | Código de barras. |
| `ItemsGroupCode` | string | no | Código de la categoría/familia (ver 3.1.1). |
| `InventoryUOM` | string | sí | Unidad de medida base (ej. "UN", "KG"). |
| `ManageBatchNumbers` | `"tYES"` \| `"tNO"` | sí | Si maneja lote/vencimiento. |
| `ManageSerialNumbers` | `"tYES"` \| `"tNO"` | sí | Si maneja número de serie. |

#### 3.1.1 Categorías/grupos de artículo — `GET /ItemGroups`
| Campo | Tipo |
|---|---|
| `Number` | string (código) |
| `GroupName` | string |

### 3.2 Almacenes/depósitos — `GET /Warehouses`
| Campo | Tipo |
|---|---|
| `WarehouseCode` | string (código único) |
| `WarehouseName` | string |
| `Street`, `City` | string (opcionales) |

### 3.3 Clientes y proveedores — `GET /BusinessPartners`
Filtro por tipo: `$filter=CardType eq 'cSupplier'` (proveedores) o `'cCustomer'` (clientes).

| Campo | Tipo |
|---|---|
| `CardCode` | string (código único) |
| `CardName` | string |
| `CardType` | `"cSupplier"` \| `"cCustomer"` |

---

## 4. Documentos que XPERT necesita LEER (para importar y operar)

Todos con la misma forma general: lista de encabezados vía `GET` con `$filter=DocumentStatus eq 'bost_Open'`, y detalle de uno vía `GET /Entidad(DocEntry)`.

**Estado del documento**: campo `DocumentStatus`, valores `"bost_Open"` (abierto) / `"bost_Close"` (cerrado). **Cantidad pendiente por línea**: `OpenQuantity` (vs. `Quantity` = cantidad original del documento).

### 4.1 Órdenes de Compra — `GET /PurchaseOrders`
Para el circuito de recepción/putaway.

| Campo (encabezado) | Tipo |
|---|---|
| `DocEntry` | int (clave interna) |
| `DocNum` | int (número visible al usuario) |
| `CardCode`, `CardName` | string (proveedor) |
| `DocDate` | date (`YYYY-MM-DD`) |
| `DocumentStatus` | ver arriba |
| `DocumentLines[]` | ver abajo |

| Campo (línea, dentro de `DocumentLines`) | Tipo |
|---|---|
| `LineNum` | int (empieza en 0) |
| `ItemCode` | string |
| `ItemDescription` | string |
| `Quantity` | number |
| `OpenQuantity` | number |
| `WarehouseCode` | string |

### 4.2 Facturas de Reserva (compra con factura anticipada) — `GET /PurchaseInvoices` — *opcional*
Misma forma que 4.1, más `IsReserveInvoice: "tYES"`. Solo si el negocio del cliente factura antes de que llegue la mercadería.

### 4.3 Solicitudes de Traslado entre depósitos — `GET /InventoryTransferRequests` — *opcional, si hay múltiples depósitos/sucursales*
| Campo (encabezado) | Tipo |
|---|---|
| `DocEntry`, `DocNum` | int |
| `FromWarehouse`, `ToWarehouse` | string (código de almacén) |
| `DocDate` | date |
| `DocumentStatus` | ver arriba |
| `StockTransferLines[]` | igual forma que `DocumentLines`, sin `WarehouseCode` |

### 4.4 Pedidos de Venta — `GET /Orders`
Para el circuito de picking/packing/despacho.

| Campo (encabezado) | Tipo |
|---|---|
| `DocEntry`, `DocNum` | int |
| `CardCode`, `CardName` | string (cliente) |
| `DocDate`, `DocDueDate` | date (fecha de pedido / fecha requerida) |
| `DocumentStatus` | ver arriba |
| `DocumentLines[]` | `{LineNum, ItemCode, ItemDescription, Quantity, UoMCode?}` |

### 4.5 Devoluciones a proveedor — `GET /PurchaseCreditNotes`, `GET /PurchaseReturnRequests` — *opcional*
Misma forma que 4.1, usadas solo si el cliente maneja devolución de mercadería a proveedores.

---

## 5. Confirmaciones que XPERT ENVÍA de vuelta (write-back)

Esto es lo que suele olvidarse de pedir: cuando algo se completa físicamente en el depósito, XPERT tiene que poder avisarle al sistema externo. Son todos `POST`, cuerpo JSON, y XPERT espera como respuesta `{ "DocEntry": <int>, "DocNum": <int> }` del comprobante creado.

**Patrón `BaseType`/`BaseEntry`/`BaseLine`**: cuando una confirmación cierra un documento importado en el punto 4, cada línea lleva de vuelta a qué documento y línea original corresponde:
- `BaseType`: código numérico de qué tipo de documento es el origen (22 = Orden de Compra, 18 = Factura de Reserva, 17 = Pedido de Venta, 20 = entrada de mercadería siendo devuelta, 14 = entrega siendo devuelta, 1250000001 = Solicitud de Traslado).
- `BaseEntry`: el `DocEntry` del documento origen.
- `BaseLine`: el `LineNum` de la línea origen.

Esto es lo que le permite al sistema externo descontar automáticamente `OpenQuantity` y cerrar el documento cuando corresponde.

### 5.1 Entrada de mercadería (recepción contra OC) — `POST /PurchaseDeliveryNotes`
```json
{
  "DocDate": "2026-07-22",
  "DocumentLines": [
    { "BaseType": 22, "BaseEntry": 6001, "BaseLine": 0, "Quantity": 80, "WarehouseCode": "CD01",
      "BatchNumbers": [{ "BatchNumber": "L001", "Quantity": 80, "ExpiryDate": "2027-01-01" }] }
  ]
}
```
`BatchNumbers` solo si el artículo maneja lote (`ManageBatchNumbers = "tYES"`).

### 5.2 Confirmación de traslado entrante — `POST /StockTransfers`
Misma forma que 5.1 pero con `BaseType: 1250000001`, más `FromWarehouse`/`ToWarehouse` en el encabezado.

### 5.3 Alta de traslado saliente (nace en XPERT, sin documento base) — `POST /InventoryTransferRequests`
```json
{
  "DocDate": "2026-07-22", "DocDueDate": "2026-07-24", "Comments": "opcional",
  "FromWarehouse": "CD01", "ToWarehouse": "SUC01",
  "StockTransferLines": [{ "ItemCode": "ABC123", "Quantity": 10 }]
}
```

### 5.4 Remito/entrega (despacho contra Pedido de Venta) — `POST /DeliveryNotes`
Misma forma que 5.1 pero `BaseType: 17`, y las líneas llevan `ItemCode` explícito además de `BaseEntry`/`BaseLine`.

### 5.5 Devoluciones — `POST /PurchaseCreditNotes` / `POST /PurchaseReturnRequests` (a proveedor, `BaseType: 20`) y `POST /CreditNotes` (de cliente, `BaseType: 14`)

### 5.6 Ajustes de inventario por conteo — `POST /InventoryGenEntries` (sobrantes) / `POST /InventoryGenExits` (faltantes)
```json
{ "DocDate": "2026-07-22", "DocumentLines": [{ "ItemCode": "ABC123", "Quantity": 5, "WarehouseCode": "CD01" }] }
```

### 5.7 Alta/edición de artículo — `POST /Items` y `PATCH /Items('{ItemCode}')` — *solo si XPERT es dueño del maestro de artículos para ese cliente* (configuración por empresa)

---

## 6. Vínculo de identificadores entre sistemas

XPERT guarda, en sus propias tablas, una columna de "código externo" por cada maestro:

| Tabla XPERT | Columna | Se vincula con |
|---|---|---|
| `items` | `sap_item_code` | `ItemCode` del sistema externo |
| `warehouses` | `sap_warehouse_code` | `WarehouseCode` |
| `customers` | `sap_card_code` | `CardCode` (tipo cliente) |
| `suppliers` | `sap_card_code` | `CardCode` (tipo proveedor) |

**Importante para el prospecto:** los códigos no tienen que "renombrarse" — alcanza con que sean estables y únicos del lado de ellos. XPERT los usa tal cual como clave de vínculo.

---

## 7. Qué preguntarle al prospecto (checklist)

1. ¿Tienen listado de artículos, almacenes y clientes/proveedores exportable o consultable? ¿Con qué campos?
2. ¿Cómo se identifica hoy una Orden de Compra / Pedido de Venta "abierta" vs "cerrada" en su sistema?
3. ¿Su sistema tiene algún concepto de "entrada de mercadería" / "remito de salida" al que se le pueda avisar cuando algo se completa en el depósito? Si no lo tiene, ¿qué tabla o proceso habría que tocar para reflejar eso?
4. ¿Manejan lotes/vencimientos o números de serie en algún artículo?
5. ¿Tienen más de un depósito/sucursal? ¿Hacen traslados entre ellos?
6. **Medio de conexión disponible** (definir cuál de estas opciones tienen, condiciona todo el proyecto):
   - API REST/SOAP propia documentada.
   - Acceso de lectura/escritura a su base de datos.
   - Intercambio de archivos (CSV/XML) en carpetas o por FTP.
   - Ninguna de las anteriores, pero tienen un programador disponible para agregar algo puntual.
7. ¿Quién es el contacto técnico del lado del prospecto para resolver dudas durante la integración?

---

## 8. ¿Se puede construir el middleware con Claude?

Sí. El patrón ya está probado dentro de este mismo proyecto (`mock/`, que habla exactamente este contrato). Para un cliente real hace falta uno de los cuatro medios de conexión del punto 7 — sin al menos uno de esos, no hay con qué integrarse. Con cualquiera de ellos, se puede escribir un middleware (probablemente en PHP, para no sumar tecnología nueva al stack) que traduzca entre el contrato de este documento y lo que el sistema del cliente realmente ofrezca.
