2026-10-05 17:53:41 -05:00
using iTextSharp.text ;
using Microsoft.Extensions.Configuration ;
using Net.Business.Entities ;
using Net.Business.Entities.ServicesLayer ;
using Net.Business.Entities.ServicesLayer.Invoices ;
using Net.Business.Entities.ServicesLayer.Items ;
2026-10-16 00:22:11 -05:00
using Net.Business.Entities.ServicesLayer.PurchaseDeliveryNotes ;
2026-10-05 17:53:41 -05:00
using Net.Business.Entities.ServicesLayer.StockTransfer ;
using Net.Business.Entities.WMS.Transferencia ;
using Net.Connection ;
using Net.CrossCotting ;
using Net.Data.HelperData.Invoices ;
using Net.Data.HelperData.Stock ;
using Net.Data.ServicesLayer ;
using Net.Data.ServicesLayer.StockTransfer ;
using Sap.Data.Hana ;
using System ;
using System.Collections.Generic ;
using System.Data ;
using System.Linq ;
using System.Net.Http ;
using System.Text.RegularExpressions ;
using System.Threading.Tasks ;
namespace Net.Data.WMS.Transferencia
{
public class TransferenciaRepository : RepositoryBase < object >, ITransferenciaRepository
{
#region Variables Globales
private string _aplicacionName ;
private string _metodoName ;
private readonly Regex regex = new Regex ( @"<(\w+)>.*" );
private readonly IHttpClientFactory _clientFactory ;
private readonly IConfiguration _configuration ;
#endregion
public TransferenciaRepository ( IConnectionSQL context , IHttpClientFactory clientFactory , IConfiguration configuration ) : base ( context )
{
_clientFactory = clientFactory ;
_configuration = configuration ;
_aplicacionName = GetType (). Name ;
}
public async Task < BE_ResultadoTransaccion < object >> Create ( BE_TransferenciaRequest entidad )
{
_metodoName = regex . Match ( System . Reflection . MethodBase . GetCurrentMethod (). DeclaringType . Name ). Groups [ 1 ]. Value . ToString ();
ResultadoTransaccion < object > resultadoTransaccion = new ResultadoTransaccion < object >( _metodoName , _aplicacionName );
return await Task . Run (() =>
{
try
{
//=== CREA INSTANCIA PARA CONECTAR A SAP
StockTransferRepository stockTransferRepository = new StockTransferRepository ( _clientFactory , _configuration );
BE_StockTransfer StockTransfer = new BE_StockTransfer
{
DocDate = entidad . FechaOrden ,
DueDate = entidad . FechaOrden ,
TaxDate = entidad . FechaOrden ,
FromWarehouse = entidad . CodigoSubAlmacenOrigen ,
ToWarehouse = entidad . CodigoSubAlmacenDestino ,
Comments = "Transferencia Generado desde Cubico - SAP" ,
StockTransferLines = new List < BE_StockTransferLine >()
};
entidad . TransferenciaDetalles . ForEach ( x =>
{
var Detalle = new BE_StockTransferLine
{
FromWarehouseCode = entidad . CodigoSubAlmacenOrigen ,
WarehouseCode = entidad . CodigoSubAlmacenDestino ,
ItemCode = x . CodigoArticulo ,
Quantity = ( double ) x . CantidadPedida ,
BaseLine = entidad . DocEntry == 0 ? null : ( int? ) x . Item ,
BaseEntry = entidad . DocEntry == 0 ? null : ( int? ) entidad . DocEntry ,
BaseType = entidad . DocEntry == 0 ? null : "InventoryTransferRequest" ,
LineNum = x . Item ,
BatchNumbers = new List < BE_StockTransferLineBatchNumber >()
};
StockTransfer . StockTransferLines . Add ( Detalle );
});
// == REALIZA DISTRIBUCION DE STOCK POR LOTE / ALMACEN
StockTransfer . StockTransferLines . ForEach ( line =>
{
#region CONSULTA A HANA
List < BE_ItemStock > ListaStock = HelperStock . Get ( line . ItemCode , line . FromWarehouseCode );
var CantidadPendiente = line . Quantity ;
var ListOrdenadaFecha = ListaStock . OrderBy ( x => x . ExpDate ). ToList ();
ListOrdenadaFecha . ForEach ( Lote =>
{
if ( CantidadPendiente > 0 )
{
if ( Lote . OnHandQty >= CantidadPendiente )
{
line . BatchNumbers . Add ( new BE_StockTransferLineBatchNumber () { BatchNumber = Lote . DistNumber , Quantity = CantidadPendiente });
CantidadPendiente = 0 ;
}
else if ( Lote . OnHandQty < CantidadPendiente )
{
line . BatchNumbers . Add ( new BE_StockTransferLineBatchNumber () { BatchNumber = Lote . DistNumber , Quantity = Lote . OnHandQty });
CantidadPendiente -= Lote . OnHandQty ;
}
}
});
#endregion
});
2026-10-16 00:22:11 -05:00
//=== SI EL ALMACEN ES ALM99 (almacen de costeo de importaciones), AMARRA CON LA ENTRADA DE MERCANCIA
2026-10-16 07:38:01 -05:00
// 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.
2026-10-05 17:53:41 -05:00
if ( entidad . CodigoSubAlmacenOrigen == "ALM99" )
{
StockTransfer . U_SYP_MDMT = "21" ;
2026-10-16 00:22:11 -05:00
PurchaseDeliveryNotesRepository _purchaseDeliveryNotesRepository = new PurchaseDeliveryNotesRepository ( _clientFactory , _configuration );
if ( entidad . DocEntryEM > 0 )
2026-10-05 17:53:41 -05:00
{
2026-10-16 00:22:11 -05:00
// 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 )
2026-10-05 17:53:41 -05:00
{
2026-10-16 00:22:11 -05:00
return resultadoTransaccion . GetInformacionResultado ( $"SAP: La entrada de mercaderia {entidad.DocEntryEM} no se encuentra" , entidad );
}
2026-10-05 17:53:41 -05:00
2026-10-16 00:22:11 -05:00
foreach ( var line in StockTransfer . StockTransferLines )
{
var Lines = pdn . DocumentLines . FirstOrDefault ( l => l . BaseLine == line . LineNum );
if ( Lines == null )
2026-10-05 17:53:41 -05:00
{
2026-10-16 00:22:11 -05:00
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 ();
2026-10-05 17:53:41 -05:00
line . BaseLine = Lines . LineNum ;
line . BaseType = "PurchaseDeliveryNotes" ;
2026-10-16 00:22:11 -05:00
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.
2026-10-05 17:53:41 -05:00
}
}
var ResultTransaction = stockTransferRepository . StockTransfer ( StockTransfer ). Result ;
if ( ResultTransaction . StatusCode == - 1 )
{
return resultadoTransaccion . GetInformacionResultado ( ResultTransaction . Message . ToString (), entidad );
}
// === SOLO SE EJECUTA CUANDO TIENE UN DOCUMENTO BASE [SOLICITUD DE TRASLADO]
if ( entidad . DocEntry != 0 )
{
var Result_SolicitudTraslado = stockTransferRepository . GetInventoryTransferRequest ( entidad . DocEntry ). Result ;
if ( Result_SolicitudTraslado . DocEntry == - 1 )
{
return resultadoTransaccion . GetInformacionResultado ( "SAP: La solicitud no se encuentra" , entidad );
}
var _SolicitudTraslado = Result_SolicitudTraslado . ListData . FirstOrDefault ();
// === SOLO CIERRA CUANDO EL DOCUMENTO ESTA ABIERTO
if ( _SolicitudTraslado . DocumentStatus == "bost_Open" )
{
var Response_Close = stockTransferRepository . CloseInventoryTransferRequest ( entidad . DocEntry ). Result ;
if ( Response_Close . DocEntry == - 1 )
{
return resultadoTransaccion . GetInformacionResultado ( "SAP: La Solicitud no se puede cerrar en automatico" , entidad );
}
}
}
return resultadoTransaccion . GetInformacionResultado (( int ) ResultTransaction . Data . DocEntry , ResultTransaction . Data . DocNum . ToString (), "Registrado Correctamente" , entidad );
}
catch ( Exception ex )
{
return resultadoTransaccion . GetInformacionResultado ( ex . Message . ToString (), entidad );
}
});
}
}
}