Transferencia desde ALM99: elegir la entrada de la factura de reserva por articulos/cantidades y que no tenga transferencia. Version 2.1.9.0
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
31b0929342
commit
baaf85554a
7 files changed
+198
-26
No files matched your search
@@ -1,5 +1,7 @@
|
||||
using Sap.Data.Hana;
|
||||
using Net.Business.Entities.WMS.Transferencia;
|
||||
using Sap.Data.Hana;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
|
||||
namespace Net.Data.HelperData.Invoices
|
||||
@@ -30,5 +32,47 @@ namespace Net.Data.HelperData.Invoices
|
||||
|
||||
return DocEntry;
|
||||
}
|
||||
|
||||
// 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.
|
||||
public static List<BE_EntradaImportacionLinea> GetEntradasPorFactura(string DocNum)
|
||||
{
|
||||
var lista = new List<BE_EntradaImportacionLinea>();
|
||||
DataTable dt = new DataTable();
|
||||
string esquema = LocalData.Schema;
|
||||
using (HanaConnection con = new HanaConnection(LocalData.ConnectionString))
|
||||
{
|
||||
con.Open();
|
||||
HanaCommand cmd = new HanaCommand(
|
||||
$"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\" = ? AND F.\"isIns\" = 'Y' AND E.\"CANCELED\" = 'N' " +
|
||||
$"ORDER BY E.\"DocEntry\", L.\"LineNum\"", con);
|
||||
cmd.Parameters.Add("DocNum", HanaDbType.VarChar).Value = DocNum ?? "";
|
||||
HanaDataAdapter da = new HanaDataAdapter(cmd);
|
||||
da.Fill(dt);
|
||||
con.Close();
|
||||
}
|
||||
|
||||
for (int x = 0; x < dt.Rows.Count; x++)
|
||||
{
|
||||
lista.Add(new BE_EntradaImportacionLinea()
|
||||
{
|
||||
DocEntry = Convert.ToInt32(dt.Rows[x]["DocEntry"]),
|
||||
DocNum = Convert.ToInt32(dt.Rows[x]["DocNum"]),
|
||||
LineNum = Convert.ToInt32(dt.Rows[x]["LineNum"]),
|
||||
ItemCode = Convert.ToString(dt.Rows[x]["ItemCode"]),
|
||||
Quantity = Convert.ToDecimal(dt.Rows[x]["Quantity"]),
|
||||
TransferenciaDocEntry = Convert.ToInt32(dt.Rows[x]["TransferenciaDocEntry"]),
|
||||
});
|
||||
}
|
||||
|
||||
return lista;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Net.Business.Entities.WMS.Transferencia;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
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).
|
||||
public static class EntradaImportacion
|
||||
{
|
||||
private const decimal Tolerancia = 0.0001m;
|
||||
|
||||
// Devuelve el DocEntry de la entrada elegida, o 0 con el motivo en "error".
|
||||
public static int Elegir(string docNumFactura, List<BE_EntradaImportacionLinea> filas, List<BE_TransferenciaDetalle> detalle, out string error)
|
||||
{
|
||||
error = null;
|
||||
var pedido = (detalle ?? new List<BE_TransferenciaDetalle>())
|
||||
.GroupBy(x => x.CodigoArticulo)
|
||||
.ToDictionary(g => g.Key, g => g.Sum(x => x.CantidadPedida));
|
||||
|
||||
var entradas = filas.GroupBy(x => x.DocEntry).OrderBy(g => g.Key).ToList();
|
||||
var coincidentes = entradas.Where(g => Coincide(g, pedido)).ToList();
|
||||
|
||||
// La mas antigua que aun no tenga transferencia
|
||||
var libre = coincidentes.FirstOrDefault(g => !g.Any(l => l.TransferenciaDocEntry > 0));
|
||||
if (libre != null)
|
||||
{
|
||||
return libre.Key;
|
||||
}
|
||||
|
||||
if (coincidentes.Count > 0)
|
||||
{
|
||||
var detalleYa = coincidentes.Select(g => $"entrada {g.First().DocNum} (DocEntry {g.Key}) ya transferida en la transferencia {g.First(l => l.TransferenciaDocEntry > 0).TransferenciaDocEntry}");
|
||||
error = $"SAP: Todas las entradas de la factura de reserva {docNumFactura} con esos articulos y cantidades ya estan transferidas: {string.Join("; ", detalleYa)}";
|
||||
return 0;
|
||||
}
|
||||
|
||||
var pendientes = entradas.Where(g => !g.Any(l => l.TransferenciaDocEntry > 0)).Select(g => $"{g.First().DocNum}");
|
||||
error = $"SAP: Ninguna entrada de la factura de reserva {docNumFactura} coincide con los articulos y cantidades enviados. " +
|
||||
$"Entradas pendientes de transferir (DocNum): {(pendientes.Any() ? string.Join(", ", pendientes) : "ninguna")}";
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static bool Coincide(IGrouping<int, BE_EntradaImportacionLinea> entrada, Dictionary<string, decimal> pedido)
|
||||
{
|
||||
var enEntrada = entrada.GroupBy(x => x.ItemCode).ToDictionary(g => g.Key, g => g.Sum(x => x.Quantity));
|
||||
if (enEntrada.Count != pedido.Count) return false;
|
||||
return enEntrada.All(kv => pedido.TryGetValue(kv.Key, out decimal cant) && Math.Abs(cant - kv.Value) <= Tolerancia);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using Net.Business.Entities;
|
||||
using Net.Business.Entities.ServicesLayer;
|
||||
using Net.Business.Entities.ServicesLayer.Invoices;
|
||||
using Net.Business.Entities.ServicesLayer.Items;
|
||||
using Net.Business.Entities.ServicesLayer.PurchaseDeliveryNotes;
|
||||
using Net.Business.Entities.ServicesLayer.StockTransfer;
|
||||
using Net.Business.Entities.WMS.Transferencia;
|
||||
using Net.Connection;
|
||||
@@ -110,36 +111,80 @@ namespace Net.Data.WMS.Transferencia
|
||||
#endregion
|
||||
});
|
||||
|
||||
//=== SI EL ALMACEN ES ALM99, FUERZA EL AMARRE CON LA ENTRADA DE MERCANCIA
|
||||
//=== SI EL ALMACEN ES ALM99 (almacen de costeo de importaciones), AMARRA CON LA ENTRADA DE MERCANCIA
|
||||
if (entidad.CodigoSubAlmacenOrigen == "ALM99")
|
||||
{
|
||||
StockTransfer.U_SYP_MDMT = "21";
|
||||
int DocEntry_Entada = entidad.DocEntryEM; //HelperInvoice.Get(entidad.NumOrden);
|
||||
if(DocEntry_Entada > 0)
|
||||
PurchaseDeliveryNotesRepository _purchaseDeliveryNotesRepository = new PurchaseDeliveryNotesRepository(_clientFactory, _configuration);
|
||||
|
||||
if (entidad.DocEntryEM > 0)
|
||||
{
|
||||
PurchaseDeliveryNotesRepository _purchaseDeliveryNotesRepository = new PurchaseDeliveryNotesRepository(_clientFactory, _configuration);
|
||||
var _purchaseDeliveryNotesRepository_Response = _purchaseDeliveryNotesRepository.GetPurchaseDeliveryNotes(DocEntry_Entada).Result.ListData.FirstOrDefault();
|
||||
|
||||
//=== RESETEA STOCK POR LOTES
|
||||
StockTransfer.StockTransferLines.ForEach(line =>
|
||||
// WMS indica la entrada: se toman sus lotes y se amarra linea por linea (BaseLine del JSON == BaseLine de la entrada)
|
||||
var pdn = _purchaseDeliveryNotesRepository.GetPurchaseDeliveryNotes(entidad.DocEntryEM).Result.ListData?.FirstOrDefault();
|
||||
if (pdn == null)
|
||||
{
|
||||
var Lines = _purchaseDeliveryNotesRepository_Response.DocumentLines.FirstOrDefault(line_purchaseDeliveryNotesRepository_Response => line_purchaseDeliveryNotesRepository_Response.BaseLine == line.LineNum);
|
||||
line.BatchNumbers = new List<BE_StockTransferLineBatchNumber>();
|
||||
return resultadoTransaccion.GetInformacionResultado($"SAP: La entrada de mercaderia {entidad.DocEntryEM} no se encuentra", entidad);
|
||||
}
|
||||
|
||||
//=== AGREGA LOTES
|
||||
Lines.BatchNumbers.ForEach(lotes =>
|
||||
foreach (var line in StockTransfer.StockTransferLines)
|
||||
{
|
||||
var Lines = pdn.DocumentLines.FirstOrDefault(l => l.BaseLine == line.LineNum);
|
||||
if (Lines == null)
|
||||
{
|
||||
line.BatchNumbers.Add(new BE_StockTransferLineBatchNumber()
|
||||
{
|
||||
BatchNumber = lotes.BatchNumber,
|
||||
Quantity = lotes.Quantity,
|
||||
});
|
||||
});
|
||||
|
||||
return resultadoTransaccion.GetInformacionResultado($"SAP: La linea {line.LineNum} no existe en la entrada de mercaderia {entidad.DocEntryEM}", entidad);
|
||||
}
|
||||
line.BatchNumbers = (Lines.BatchNumbers ?? new List<BE_PurchaseDeliveryNotesBatchNumbers>())
|
||||
.Select(lotes => new BE_StockTransferLineBatchNumber() { BatchNumber = lotes.BatchNumber, Quantity = lotes.Quantity }).ToList();
|
||||
line.BaseLine = Lines.LineNum;
|
||||
line.BaseType = "PurchaseDeliveryNotes";
|
||||
line.BaseEntry = DocEntry_Entada;
|
||||
});
|
||||
line.BaseEntry = entidad.DocEntryEM;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// WMS envia en NumOrden el DocNum de la Factura de Reserva y en el detalle los articulos/cantidades de UNA de sus
|
||||
// entradas: se busca la entrada con ese mismo juego que aun no tenga transferencia y se transfiere esa.
|
||||
var filas = HelperInvoice.GetEntradasPorFactura(entidad.NumOrden);
|
||||
if (filas.Count > 0)
|
||||
{
|
||||
int docEntryEntrada = EntradaImportacion.Elegir(entidad.NumOrden, filas, entidad.TransferenciaDetalles, out string errorEntrada);
|
||||
if (docEntryEntrada == 0)
|
||||
{
|
||||
return resultadoTransaccion.GetInformacionResultado(errorEntrada, entidad);
|
||||
}
|
||||
|
||||
var pdn = _purchaseDeliveryNotesRepository.GetPurchaseDeliveryNotes(docEntryEntrada).Result.ListData?.FirstOrDefault();
|
||||
if (pdn == null)
|
||||
{
|
||||
return resultadoTransaccion.GetInformacionResultado($"SAP: La entrada de mercaderia {docEntryEntrada} no se puede leer", entidad);
|
||||
}
|
||||
|
||||
// Las lineas de la transferencia salen de la entrada (articulo, cantidad y lotes reales de cada linea)
|
||||
StockTransfer.StockTransferLines = new List<BE_StockTransferLine>();
|
||||
foreach (var l in pdn.DocumentLines)
|
||||
{
|
||||
var lotes = (l.BatchNumbers ?? new List<BE_PurchaseDeliveryNotesBatchNumbers>())
|
||||
.Select(b => new BE_StockTransferLineBatchNumber() { BatchNumber = b.BatchNumber, Quantity = b.Quantity }).ToList();
|
||||
if (lotes.Count > 0 && Math.Abs(lotes.Sum(b => b.Quantity) - l.Quantity) > 0.0001)
|
||||
{
|
||||
return resultadoTransaccion.GetInformacionResultado($"SAP: Los lotes de la linea {l.LineNum} ({l.ItemCode}) de la entrada {docEntryEntrada} no cubren la cantidad {l.Quantity}", entidad);
|
||||
}
|
||||
StockTransfer.StockTransferLines.Add(new BE_StockTransferLine
|
||||
{
|
||||
FromWarehouseCode = entidad.CodigoSubAlmacenOrigen,
|
||||
WarehouseCode = entidad.CodigoSubAlmacenDestino,
|
||||
ItemCode = l.ItemCode,
|
||||
Quantity = l.Quantity,
|
||||
LineNum = l.LineNum,
|
||||
BaseType = "PurchaseDeliveryNotes",
|
||||
BaseEntry = docEntryEntrada,
|
||||
BaseLine = l.LineNum,
|
||||
BatchNumbers = lotes
|
||||
});
|
||||
}
|
||||
}
|
||||
// Si la factura no tiene entradas (o NumOrden no es una factura de reserva) se mantiene lo de antes:
|
||||
// transferencia con los lotes del stock de HANA, sin amarre.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user