Documentar la transferencia desde ALM99 (docs/TRANSFERENCIA_IMPORTACION.md) y comentar el codigo

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-16 07:38:01 -05:00
1 parent d53d5d07c5
commit 3111bb3842
5 files changed
+117 -2

No files matched your search

+3 -1
View File
@@ -121,7 +121,9 @@ transferencias siempre, sin depender de `Notificacion`. Detalle y tabla en
sin amarre). Con `DocEntryEM` informado se respeta (antes reventaba con referencia nula si
la entrada o la línea no existían). Sin probar contra SAP: validar la consulta, que
`isIns='Y'` sea factura de reserva y que SAP acepte `BaseType "PurchaseDeliveryNotes"` en
la línea de `StockTransfers`.
la línea de `StockTransfers`. Detalle, errores y diagnóstico en
`docs/TRANSFERENCIA_IMPORTACION.md`. Probado con datos reales (SQL Server) solo la consulta: entradas
de 1 y 2 líneas, y una transferencia cancelada libera la entrada.
- Sin probar contra SAP: confirmar con la factura 292410 que SAP devuelve
`BaseType`/`BaseEntry` en las líneas y `BatchNumbers` en las de la Entrega.
@@ -35,7 +35,9 @@ namespace Net.Data.HelperData.Invoices
// Lineas de todas las Entradas de Mercaderia (no anuladas) enlazadas (BaseType 18) a la Factura de Reserva
// de proveedores con ese DocNum (isIns = 'Y'). Si una linea ya tiene transferencia de stock (TargetType 67)
// y esa transferencia no esta anulada, TransferenciaDocEntry trae su DocEntry; si no, 0.
// y esa transferencia no esta anulada, TransferenciaDocEntry trae su DocEntry; si no, 0 (una transferencia cancelada
// libera la entrada). Lo usa TransferenciaRepository (origen ALM99); ver docs/TRANSFERENCIA_IMPORTACION.md.
// OJO: Get() (procedimiento ITCA_WMS_SAP_GetEntradaPorImportacion) ya no se usa; devolvia un solo DocEntry.
public static List<BE_EntradaImportacionLinea> GetEntradasPorFactura(string DocNum)
{
var lista = new List<BE_EntradaImportacionLinea>();
@@ -8,6 +8,9 @@ namespace Net.Data.WMS.Transferencia
// Elige la Entrada de Mercaderia de una Factura de Reserva que corresponde a una transferencia desde ALM99.
// WMS recibe por partes: cada entrada tiene su propio juego de articulos/cantidades, y WMS notifica una
// transferencia por entrada con ese mismo juego (se compara solo ItemCode y cantidad, no el numero de item).
// Reglas: coincidencia exacta de articulos y cantidades (sumadas por articulo); entre las que coinciden se toma la mas antigua
// (menor DocEntry) sin transferencia; si todas las que coinciden ya estan transferidas se rechaza para no duplicar.
// Ver docs/TRANSFERENCIA_IMPORTACION.md.
public static class EntradaImportacion
{
private const decimal Tolerancia = 0.0001m;
@@ -112,6 +112,12 @@ namespace Net.Data.WMS.Transferencia
});
//=== SI EL ALMACEN ES ALM99 (almacen de costeo de importaciones), AMARRA CON LA ENTRADA DE MERCANCIA
// Proceso: factura de reserva -> WMS recibe por partes y crea una entrada por recepcion (todas a ALM99) -> costeo en SAP ->
// WMS notifica UNA transferencia por entrada, con numorden = DocNum de la factura de reserva y en el detalle los
// articulos/cantidades de esa entrada. Detalle completo y diagnostico en docs/TRANSFERENCIA_IMPORTACION.md.
// Casos: (1) DocEntryEM informado -> se usa esa entrada; (2) sin DocEntryEM -> se busca la entrada de la factura que
// coincida con el detalle y no tenga transferencia (EntradaImportacion.Elegir); (3) la factura no tiene entradas ->
// como antes, lotes del stock de HANA sin amarre.
if (entidad.CodigoSubAlmacenOrigen == "ALM99")
{
StockTransfer.U_SYP_MDMT = "21";
+102
View File
@@ -0,0 +1,102 @@
# 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 = <numorden>`: 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 <ESQUEMA> 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 "<ESQUEMA>"."OPCH" F
INNER JOIN "<ESQUEMA>"."PDN1" L ON L."BaseType" = 18 AND L."BaseEntry" = F."DocEntry"
INNER JOIN "<ESQUEMA>"."OPDN" E ON E."DocEntry" = L."DocEntry"
LEFT JOIN "<ESQUEMA>"."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 `[<BASE>].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.