From 038faefd357063e4f0d69fa263839b67cfd8fd56 Mon Sep 17 00:00:00 2001 From: dmolinari Date: Tue, 12 Aug 2025 12:49:56 -0300 Subject: [PATCH] =?UTF-8?q?Fix:=20Formato=20de=20Archivo=20de=20D=C3=A9bit?= =?UTF-8?q?o=20Modificado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Suscripciones/DebitoAutomaticoService.cs | 104 +++++++----------- 1 file changed, 38 insertions(+), 66 deletions(-) diff --git a/Backend/GestionIntegral.Api/Services/Suscripciones/DebitoAutomaticoService.cs b/Backend/GestionIntegral.Api/Services/Suscripciones/DebitoAutomaticoService.cs index 72e8d1f..dfe8abe 100644 --- a/Backend/GestionIntegral.Api/Services/Suscripciones/DebitoAutomaticoService.cs +++ b/Backend/GestionIntegral.Api/Services/Suscripciones/DebitoAutomaticoService.cs @@ -17,8 +17,8 @@ namespace GestionIntegral.Api.Services.Suscripciones private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; - private const string NRO_PRESTACION = "123456"; - private const string ORIGEN_EMPRESA = "ELDIA"; + private const string NRO_PRESTACION = "123456"; // Reemplazar por el número real + private const string ORIGEN_EMPRESA = "EMPRESA"; public DebitoAutomaticoService( IFacturaRepository facturaRepository, @@ -40,9 +40,7 @@ namespace GestionIntegral.Api.Services.Suscripciones public async Task<(string? ContenidoArchivo, string? NombreArchivo, string? Error)> GenerarArchivoPagoDirecto(int anio, int mes, int idUsuario) { - // Se define la identificación del archivo. - // Este número debe ser gestionado para no repetirse en archivos generados - // para la misma prestación y fecha. + // Este número debe ser gestionado para no repetirse. Por ahora, lo mantenemos como 1. const int identificacionArchivo = 1; var periodo = $"{anio}-{mes:D2}"; @@ -62,8 +60,6 @@ namespace GestionIntegral.Api.Services.Suscripciones var importeTotal = facturasParaDebito.Sum(f => f.Factura.ImporteFinal); var cantidadRegistros = facturasParaDebito.Count(); - - // Se utiliza la variable 'identificacionArchivo' para nombrar el archivo. var nombreArchivo = $"{NRO_PRESTACION}{fechaGeneracion:yyyyMMdd}{identificacionArchivo}.txt"; var nuevoLote = new LoteDebito @@ -78,13 +74,11 @@ namespace GestionIntegral.Api.Services.Suscripciones if (loteCreado == null) throw new DataException("No se pudo crear el registro del lote de débito."); var sb = new StringBuilder(); - // Se pasa la 'identificacionArchivo' al método que crea el Header. sb.Append(CrearRegistroHeader(fechaGeneracion, importeTotal, cantidadRegistros, identificacionArchivo)); foreach (var item in facturasParaDebito) { sb.Append(CrearRegistroDetalle(item.Factura, item.Suscriptor)); } - // Se pasa la 'identificacionArchivo' al método que crea el Trailer. sb.Append(CrearRegistroTrailer(fechaGeneracion, importeTotal, cantidadRegistros, identificacionArchivo)); var idsFacturas = facturasParaDebito.Select(f => f.Factura.IdFactura); @@ -108,17 +102,14 @@ namespace GestionIntegral.Api.Services.Suscripciones var facturas = await _facturaRepository.GetByPeriodoAsync(periodo); var resultado = new List<(Factura, Suscriptor)>(); - foreach (var f in facturas.Where(fa => fa.EstadoPago == "Pendiente")) + foreach (var f in facturas.Where(fa => fa.EstadoPago == "Pendiente" || fa.EstadoPago == "Pagada Parcialmente" || fa.EstadoPago == "Rechazada")) { var suscriptor = await _suscriptorRepository.GetByIdAsync(f.IdSuscriptor); - - // Se valida que el CBU de Banelco (22 caracteres) exista antes de intentar la conversión. if (suscriptor == null || string.IsNullOrWhiteSpace(suscriptor.CBU) || suscriptor.CBU.Length != 22) { - _logger.LogWarning("Suscriptor ID {IdSuscriptor} omitido del lote de débito por CBU inválido o ausente (se esperan 22 dígitos).", suscriptor?.IdSuscriptor); + _logger.LogWarning("Suscriptor ID {IdSuscriptor} omitido del lote de débito por CBU inválido o ausente (se esperan 22 dígitos).", f.IdSuscriptor); continue; } - var formaPago = await _formaPagoRepository.GetByIdAsync(suscriptor.IdFormaPagoPreferida); if (formaPago != null && formaPago.RequiereCBU) { @@ -128,26 +119,13 @@ namespace GestionIntegral.Api.Services.Suscripciones return resultado; } - // Lógica de conversión de CBU. private string ConvertirCbuBanelcoASnp(string cbu22) { - if (string.IsNullOrEmpty(cbu22) || cbu22.Length != 22) - { - _logger.LogError("Se intentó convertir un CBU inválido de {Length} caracteres. Se devolverá un campo vacío.", cbu22?.Length ?? 0); - // Devolver un string de 26 espacios/ceros según la preferencia del banco para campos erróneos. - return "".PadRight(26); - } - - // El formato SNP de 26 se obtiene insertando un "0" al inicio y "000" después del 8vo caracter del CBU de 22. - // Formato Banelco (22): [BBBSSSSX] [T....Y] - // Posiciones: (0-7) (8-21) - // Formato SNP (26): 0[BBBSSSSX]000[T....Y] + if (string.IsNullOrEmpty(cbu22) || cbu22.Length != 22) return "".PadRight(26); try { - string bloque1 = cbu22.Substring(0, 8); // Contiene código de banco, sucursal y DV del bloque 1. - string bloque2 = cbu22.Substring(8); // Contiene el resto de la cadena. - - // Reconstruir en formato SNP de 26 dígitos según el instructivo. + string bloque1 = cbu22.Substring(0, 8); + string bloque2 = cbu22.Substring(8); return $"0{bloque1}000{bloque2}"; } catch (Exception ex) @@ -157,27 +135,23 @@ namespace GestionIntegral.Api.Services.Suscripciones } } - // --- Métodos de Formateo y Mapeo --- + // --- Helpers de Formateo --- private string FormatString(string? value, int length) => (value ?? "").PadRight(length); private string FormatNumeric(long value, int length) => value.ToString().PadLeft(length, '0'); + private string FormatNumericString(string? value, int length) => (value ?? "").PadLeft(length, '0'); private string MapTipoDocumento(string tipo) => tipo.ToUpper() switch { - "DNI" => "0096", - "CUIT" => "0080", - "CUIL" => "0086", - "LE" => "0089", - "LC" => "0090", - _ => "0000" // Tipo no especificado o C.I. Policía Federal según anexo. + "DNI" => "0096", "CUIT" => "0080", "CUIL" => "0086", "LE" => "0089", "LC" => "0090", _ => "0000" }; private string CrearRegistroHeader(DateTime fechaGeneracion, decimal importeTotal, int cantidadRegistros, int identificacionArchivo) { var sb = new StringBuilder(); - sb.Append("00"); // Tipo de Registro Header - sb.Append(FormatString(NRO_PRESTACION, 6)); - sb.Append("C"); // Servicio: Sistema Nacional de Pagos + sb.Append("00"); + sb.Append(FormatNumericString(NRO_PRESTACION, 6)); + sb.Append("C"); sb.Append(fechaGeneracion.ToString("yyyyMMdd")); - sb.Append(FormatString(identificacionArchivo.ToString(), 1)); // Identificación de Archivo + sb.Append(FormatString(identificacionArchivo.ToString(), 1)); sb.Append(FormatString(ORIGEN_EMPRESA, 7)); sb.Append(FormatNumeric((long)(importeTotal * 100), 14)); sb.Append(FormatNumeric(cantidadRegistros, 7)); @@ -188,35 +162,33 @@ namespace GestionIntegral.Api.Services.Suscripciones private string CrearRegistroDetalle(Factura factura, Suscriptor suscriptor) { - // Convertimos el CBU de 22 (Banelco) a 26 (SNP) antes de usarlo. string cbu26 = ConvertirCbuBanelcoASnp(suscriptor.CBU!); - var sb = new StringBuilder(); - sb.Append("0370"); // Tipo de Registro Detalle (Orden de Débito) - sb.Append(FormatString(suscriptor.IdSuscriptor.ToString(), 22)); // Identificación de Cliente - sb.Append(FormatString(cbu26, 26)); // CBU en formato SNP de 26 caracteres. - sb.Append(FormatString($"SUSC-{factura.IdFactura}", 15)); // Referencia Unívoca de la factura. + sb.Append("0370"); + sb.Append(FormatString(suscriptor.IdSuscriptor.ToString(), 22)); + sb.Append(cbu26); + sb.Append(FormatString($"SUSC-{factura.IdFactura}", 15)); sb.Append(factura.FechaVencimiento.ToString("yyyyMMdd")); sb.Append(FormatNumeric((long)(factura.ImporteFinal * 100), 14)); sb.Append(FormatNumeric(0, 8)); // Fecha 2do Vencimiento sb.Append(FormatNumeric(0, 14)); // Importe 2do Vencimiento sb.Append(FormatNumeric(0, 8)); // Fecha 3er Vencimiento sb.Append(FormatNumeric(0, 14)); // Importe 3er Vencimiento - sb.Append("0"); // Moneda (0 = Pesos) - sb.Append(FormatString("", 3)); // Motivo Rechazo (vacío en el envío) + sb.Append("0"); + sb.Append(FormatString("", 3)); sb.Append(FormatString(MapTipoDocumento(suscriptor.TipoDocumento), 4)); - sb.Append(FormatString(suscriptor.NroDocumento, 11)); - sb.Append(FormatString("", 22)); // Nueva ID Cliente - sb.Append(FormatString("", 26)); // Nueva CBU - sb.Append(FormatNumeric(0, 14)); // Importe Mínimo - sb.Append(FormatNumeric(0, 8)); // Fecha Próximo Vencimiento - sb.Append(FormatString("", 22)); // Identificación Cuenta Anterior - sb.Append(FormatString("", 40)); // Mensaje ATM - sb.Append(FormatString($"Susc.{factura.Periodo}", 10)); // Concepto Factura - sb.Append(FormatNumeric(0, 8)); // Fecha de Cobro - sb.Append(FormatNumeric(0, 14)); // Importe Cobrado - sb.Append(FormatNumeric(0, 8)); // Fecha de Acreditamiento - sb.Append(FormatString("", 26)); // Libre + sb.Append(FormatNumericString(suscriptor.NroDocumento, 11)); + sb.Append(FormatString("", 22)); + sb.Append(FormatString("", 26)); + sb.Append(FormatNumeric(0, 14)); + sb.Append(FormatNumeric(0, 8)); + sb.Append(FormatString("", 22)); + sb.Append(FormatString("", 40)); + sb.Append(FormatString($"Susc.{factura.Periodo}", 10)); + sb.Append(FormatNumeric(0, 8)); + sb.Append(FormatNumeric(0, 14)); + sb.Append(FormatNumeric(0, 8)); + sb.Append(FormatString("", 26)); sb.Append("\r\n"); return sb.ToString(); } @@ -224,16 +196,16 @@ namespace GestionIntegral.Api.Services.Suscripciones private string CrearRegistroTrailer(DateTime fechaGeneracion, decimal importeTotal, int cantidadRegistros, int identificacionArchivo) { var sb = new StringBuilder(); - sb.Append("99"); // Tipo de Registro Trailer - sb.Append(FormatString(NRO_PRESTACION, 6)); - sb.Append("C"); // Servicio: Sistema Nacional de Pagos + sb.Append("99"); + sb.Append(FormatNumericString(NRO_PRESTACION, 6)); + sb.Append("C"); sb.Append(fechaGeneracion.ToString("yyyyMMdd")); - sb.Append(FormatString(identificacionArchivo.ToString(), 1)); // Identificación de Archivo + sb.Append(FormatString(identificacionArchivo.ToString(), 1)); sb.Append(FormatString(ORIGEN_EMPRESA, 7)); sb.Append(FormatNumeric((long)(importeTotal * 100), 14)); sb.Append(FormatNumeric(cantidadRegistros, 7)); sb.Append(FormatString("", 304)); - // La última línea del archivo no lleva salto de línea (\r\n). + sb.Append("\r\n"); return sb.ToString(); }