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:
1 parent
d53d5d07c5
commit
36d7180547
5 files changed
+117
-2
No files matched your search
@@ -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";
|
||||
|
||||
@@ -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.
|
||||
Reference in new issue
Block a user