# Transferencia de stock desde ALM99 (importaciones) — v2.1.9.0 Documento de soporte. Código: `Net.Data/WMS/Transferencia/TransferenciaRepository.cs` (bloque `ALM99`), `Net.Data/WMS/Transferencia/EntradaImportacion.cs` (elección de la entrada) y `Net.Data/HelperData/Invoices/HelperInvoice.GetEntradasPorFactura` (consulta HANA). Endpoint: `POST /api/Transferencia`. ## Proceso de negocio 1. En SAP se crea una **Factura de Reserva de proveedores** y se envía al WMS para su recepción. 2. El WMS recibe **por partes**: por cada recepción crea una **Entrada de Mercadería** (`PurchaseDeliveryNotes`) enlazada a la factura (`BaseType 18`, `BaseEntry`, `BaseLine`). Hay una o varias entradas por factura y todas ingresan al almacén **ALM99** (costeo). El WMS no guarda el `DocEntry` de las entradas, solo sabe qué artículos recibió en cada una. 3. En SAP se hace el costeo (precio de entrega) de las entradas. 4. Terminado el costeo, el WMS notifica **una transferencia por entrada** hacia el almacén final. ## JSON que envía el WMS - `numorden` = **DocNum de la factura de reserva** (único). - `codigoSubAlmacenOrigen` = `ALM99`; `codigoSubAlmacenDestino` = almacén final (p. ej. `ALM21`). - `docEntry` = 0 (no hay solicitud de traslado); `docEntryEM` no se envía. - `detalle` = los artículos y cantidades **de una sola entrada**. Solo se usan `codigoarticulo` y `cantidadpedida`; `item` y `loteproducto` se ignoran en este caso. ## Qué hace la API (origen = ALM99 y sin `docEntryEM`) 1. Consulta HANA (`HelperInvoice.GetEntradasPorFactura`): factura `OPCH` con `DocNum = numorden` y `isIns = 'Y'` (= factura de reserva) → líneas `PDN1` con `BaseType 18` y `BaseEntry` = factura → entrada `OPDN` no anulada (`CANCELED = 'N'`). 2. Una entrada **ya tiene transferencia** si la línea tiene `TargetType = 67` y su `TrgetEntry` apunta a una transferencia `OWTR` con `CANCELED = 'N'`. Una transferencia anulada libera la entrada. 3. `EntradaImportacion.Elegir` compara, por entrada, el conjunto `ItemCode` → cantidad (sumada por artículo) contra el detalle del JSON (también sumado). Debe coincidir **exacto** (mismos artículos, mismas cantidades, tolerancia 0.0001). Se toma la **entrada más antigua** (menor `DocEntry`) que coincida y no tenga transferencia. 4. Se lee la entrada por Service Layer (`PurchaseDeliveryNotes(docEntry)`) y las líneas de la transferencia salen **de la entrada**: artículo, cantidad, lotes reales y base (`BaseType "PurchaseDeliveryNotes"`, `BaseEntry`, `BaseLine` = línea de la entrada). Si los lotes de una línea no suman su cantidad, responde error antes de enviar a SAP. 5. Se envía `StockTransfers` con `U_SYP_MDMT = "21"`. ## Respuestas de error (400, mensaje en la respuesta y en `ITCA_LOG_INTEGRACION`) | Mensaje | Causa | |---|---| | `SAP: Ninguna entrada de la factura de reserva X coincide con los articulos y cantidades enviados. Entradas pendientes de transferir (DocNum): ...` | El JSON no es igual a ninguna entrada (falta/sobra un artículo o cambia una cantidad). Lista las entradas aún sin transferir para comparar. | | `SAP: Todas las entradas de la factura de reserva X con esos articulos y cantidades ya estan transferidas: entrada N ya transferida en la transferencia M` | Reintento del mismo JSON o entrada ya transferida (evita duplicados). | | `SAP: Los lotes de la linea L (item) de la entrada N no cubren la cantidad Q` | La entrada tiene lotes que no suman la cantidad de la línea. | | `SAP: La entrada de mercaderia N no se puede leer` | Falló el GET al Service Layer. | | Error de SAP al crear `StockTransfers` | Ver la fila `PROCESO='ServiceLayer Error'` (JSON enviado + mensaje de SAP). | ## Otros casos del mismo endpoint (no cambiaron) - **Transferencia simple** (origen distinto de ALM99, `docEntry = 0`): lotes por stock HANA (FEFO), sin base. - **Con solicitud de traslado** (`docEntry ≠ 0`): la solicitud es la base y se cierra al terminar. - **ALM99 con `docEntryEM` informado**: se respeta; lotes y base de esa entrada, amarrando `BaseLine` del JSON con `BaseLine` de la entrada. Si la entrada o la línea no existen responde error claro. - **ALM99 cuando la factura no tiene entradas** (o `numorden` no es una factura de reserva): se mantiene el comportamiento anterior (lotes del stock de HANA, sin amarre). ## Verificado y pendiente - Verificado con datos reales (SQL Server): la consulta encuentra las entradas por `BaseType 18` y `isIns='Y'`; muestra el estado de transferencia por línea (entradas con 1 y con 2 líneas) y una transferencia **cancelada** vuelve a dejar la entrada libre (`TransferenciaDocEntry = 0`). - Verificado con simulación: la lógica de `Elegir` (coincidencia, entrada más antigua, ya transferida, no coincide). - **Pendiente de confirmar contra SAP/HANA reales**: que la consulta corra en HANA (la API usa HANA); que SAP acepte `BaseType "PurchaseDeliveryNotes"` en las líneas de `StockTransfers`; y el caso en que una entrada de varias líneas tenga transferida solo una línea (hoy cuenta como ya transferida). ## Cómo diagnosticar ("no se genera la transferencia") 1. Ver la fila en `"ITC_WMS_SAP"."ITCA_LOG_INTEGRACION"` con `PROCESO = 'Transferencia'` y `NRO_ORDEN = `: trae el JSON del WMS y la respuesta (el mensaje de la tabla de arriba). 2. Ejecutar la consulta de `HelperInvoice.GetEntradasPorFactura` (versión SQL Server/HANA al final) con el `DocNum`: si no devuelve filas, la factura no existe, no es de reserva (`isIns`) o sus entradas no tienen `BaseType 18`; si `TransferenciaDocEntry ≠ 0`, esa entrada ya fue transferida. 3. Comparar artículos y cantidades del JSON contra las filas de esa entrada. ```sql -- HANA (reemplazar y el DocNum de la factura de reserva) SELECT E."DocEntry", E."DocNum", L."LineNum", L."ItemCode", L."Quantity", CASE WHEN L."TargetType" = 67 AND W."DocEntry" IS NOT NULL AND W."CANCELED" = 'N' THEN W."DocEntry" ELSE 0 END AS "TransferenciaDocEntry" FROM ""."OPCH" F INNER JOIN ""."PDN1" L ON L."BaseType" = 18 AND L."BaseEntry" = F."DocEntry" INNER JOIN ""."OPDN" E ON E."DocEntry" = L."DocEntry" LEFT JOIN ""."OWTR" W ON L."TargetType" = 67 AND W."DocEntry" = L."TrgetEntry" WHERE F."DocNum" = 262000082 AND F."isIns" = 'Y' AND E."CANCELED" = 'N' ORDER BY E."DocEntry", L."LineNum"; ``` En SQL Server es la misma consulta sin comillas dobles y con `[].dbo.OPCH`, etc. ## Historia Antes (también en el código del desarrollador anterior y en el commit inicial) el bloque dependía de `DocEntryEM`, que el WMS no envía: nunca se amarraba a la entrada y los lotes salían solo del stock de HANA (sin validar que cubrieran la cantidad). La versión original buscaba la entrada por `NumOrden` con `HelperInvoice.Get` (procedimiento `ITCA_WMS_SAP_GetEntradaPorImportacion`, un solo `DocEntry`), que quedó comentado; no servía para varias entradas por factura. Esa función sigue en el código sin llamadores.