2025-08-08 09:48:15 -03:00
using GestionIntegral.Api.Data.Repositories.Distribucion ;
2025-05-27 11:21:00 -03:00
using GestionIntegral.Api.Data.Repositories.Reportes ;
2025-08-08 09:48:15 -03:00
using GestionIntegral.Api.Data.Repositories.Suscripciones ;
2025-05-27 11:21:00 -03:00
using GestionIntegral.Api.Dtos.Reportes ;
namespace GestionIntegral.Api.Services.Reportes
{
public class ReportesService : IReportesService
{
private readonly IReportesRepository _reportesRepository ;
2025-08-08 09:48:15 -03:00
private readonly IFacturaRepository _facturaRepository ;
private readonly IFacturaDetalleRepository _facturaDetalleRepository ;
private readonly IPublicacionRepository _publicacionRepository ;
private readonly IEmpresaRepository _empresaRepository ;
private readonly ISuscriptorRepository _suscriptorRepository ;
private readonly ISuscripcionRepository _suscripcionRepository ;
2025-05-27 11:21:00 -03:00
private readonly ILogger < ReportesService > _logger ;
2025-08-08 09:48:15 -03:00
public ReportesService ( IReportesRepository reportesRepository , IFacturaRepository facturaRepository , IFacturaDetalleRepository facturaDetalleRepository , IPublicacionRepository publicacionRepository , IEmpresaRepository empresaRepository
, ISuscriptorRepository suscriptorRepository , ISuscripcionRepository suscripcionRepository , ILogger < ReportesService > logger )
2025-05-27 11:21:00 -03:00
{
_reportesRepository = reportesRepository ;
2025-08-08 09:48:15 -03:00
_facturaRepository = facturaRepository ;
_facturaDetalleRepository = facturaDetalleRepository ;
_publicacionRepository = publicacionRepository ;
_empresaRepository = empresaRepository ;
_suscriptorRepository = suscriptorRepository ;
_suscripcionRepository = suscripcionRepository ;
2025-05-27 11:21:00 -03:00
_logger = logger ;
}
public async Task < ( IEnumerable < ExistenciaPapelDto > Data , string? Error ) > ObtenerExistenciaPapelAsync (
2025-05-27 18:17:56 -03:00
DateTime fechaDesde , DateTime fechaHasta , int? idPlanta , bool consolidado )
2025-05-27 11:21:00 -03:00
{
if ( fechaDesde > fechaHasta )
{
return ( Enumerable . Empty < ExistenciaPapelDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
}
if ( ! consolidado & & ! idPlanta . HasValue )
{
return ( Enumerable . Empty < ExistenciaPapelDto > ( ) , "Se requiere un ID de planta para reportes no consolidados." ) ;
}
try
{
var dataFromRepo = await _reportesRepository . GetExistenciaPapelAsync ( fechaDesde , fechaHasta , idPlanta , consolidado ) ;
var dataWithUtcDates = dataFromRepo . Select ( dto = >
{
if ( dto . FechaEstimacionFinStock . HasValue )
{
dto . FechaEstimacionFinStock = DateTime . SpecifyKind ( dto . FechaEstimacionFinStock . Value . Date , DateTimeKind . Utc ) ;
}
return dto ;
} ) . ToList ( ) ;
return ( dataWithUtcDates , null ) ;
}
2025-05-27 18:17:56 -03:00
catch ( ArgumentNullException ex ) when ( ex . ParamName = = "idPlanta" )
2025-05-27 11:21:00 -03:00
{
_logger . LogWarning ( ex , "ArgumentNullException para idPlanta en ObtenerExistenciaPapelAsync." ) ;
2025-05-27 18:17:56 -03:00
return ( Enumerable . Empty < ExistenciaPapelDto > ( ) , ex . Message ) ;
2025-05-27 11:21:00 -03:00
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Existencia de Papel." ) ;
2025-05-27 18:17:56 -03:00
return ( Enumerable . Empty < ExistenciaPapelDto > ( ) , "Error interno al generar el reporte de existencia de papel." ) ;
}
}
public async Task < ( IEnumerable < MovimientoBobinasDto > Data , string? Error ) > ObtenerMovimientoBobinasAsync ( DateTime fechaDesde , DateTime fechaHasta , int idPlanta )
{
if ( fechaDesde > fechaHasta )
{
return ( Enumerable . Empty < MovimientoBobinasDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
}
2025-05-28 16:01:59 -03:00
int diasPeriodo = ( fechaHasta . Date - fechaDesde . Date ) . Days + 1 ;
2025-05-27 18:17:56 -03:00
try
{
var data = await _reportesRepository . GetMovimientoBobinasAsync ( fechaDesde . Date , diasPeriodo , idPlanta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Movimiento de Bobinas." ) ;
return ( Enumerable . Empty < MovimientoBobinasDto > ( ) , "Error interno al generar el reporte de movimiento de bobinas." ) ;
}
}
public async Task < ( IEnumerable < MovimientoBobinaEstadoDetalleDto > Detalle , IEnumerable < MovimientoBobinaEstadoTotalDto > Totales , string? Error ) > ObtenerMovimientoBobinasPorEstadoAsync ( DateTime fechaDesde , DateTime fechaHasta , int idPlanta )
{
if ( fechaDesde > fechaHasta )
{
return ( Enumerable . Empty < MovimientoBobinaEstadoDetalleDto > ( ) , Enumerable . Empty < MovimientoBobinaEstadoTotalDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
}
try
{
var detalle = await _reportesRepository . GetMovimientoBobinasEstadoDetalleAsync ( fechaDesde . Date , fechaHasta . Date , idPlanta ) ;
var totales = await _reportesRepository . GetMovimientoBobinasEstadoTotalesAsync ( fechaDesde . Date , fechaHasta . Date , idPlanta ) ;
2025-05-28 16:01:59 -03:00
var detalleUtc = detalle . Select ( d = >
{
2025-05-27 18:17:56 -03:00
d . FechaMovimiento = DateTime . SpecifyKind ( d . FechaMovimiento , DateTimeKind . Utc ) ;
return d ;
} ) . ToList ( ) ;
return ( detalleUtc , totales , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Movimiento de Bobinas por Estado." ) ;
return ( Enumerable . Empty < MovimientoBobinaEstadoDetalleDto > ( ) , Enumerable . Empty < MovimientoBobinaEstadoTotalDto > ( ) , "Error interno al generar el reporte de movimiento de bobinas por estado." ) ;
2025-05-27 11:21:00 -03:00
}
}
2025-05-27 18:17:56 -03:00
public async Task < ( IEnumerable < ListadoDistribucionGeneralResumenDto > Resumen , IEnumerable < ListadoDistribucionGeneralPromedioDiaDto > Promedios , string? Error ) > ObtenerListadoDistribucionGeneralAsync ( int idPublicacion , DateTime fechaDesde , DateTime fechaHasta )
{
if ( fechaDesde > fechaHasta )
return ( Enumerable . Empty < ListadoDistribucionGeneralResumenDto > ( ) , Enumerable . Empty < ListadoDistribucionGeneralPromedioDiaDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
try
{
// El SP SP_DistObtenerResumenMensual usa Mes y Año de fechaDesde.
// El SP SP_DistObtenerResumenMensualPorDiaSemana también.
var resumenData = await _reportesRepository . GetListadoDistribucionGeneralResumenAsync ( idPublicacion , fechaDesde , fechaHasta ) ;
var promediosData = await _reportesRepository . GetListadoDistribucionGeneralPromedioDiaAsync ( idPublicacion , fechaDesde , fechaHasta ) ;
2025-05-28 16:01:59 -03:00
2025-05-27 18:17:56 -03:00
var resumenUtc = resumenData . Select ( r = > { r . Fecha = DateTime . SpecifyKind ( r . Fecha , DateTimeKind . Utc ) ; return r ; } ) . ToList ( ) ;
2025-05-27 11:21:00 -03:00
2025-05-27 18:17:56 -03:00
return ( resumenUtc , promediosData , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Listado Distribucion General." ) ;
return ( Enumerable . Empty < ListadoDistribucionGeneralResumenDto > ( ) , Enumerable . Empty < ListadoDistribucionGeneralPromedioDiaDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
public async Task < ( IEnumerable < ListadoDistribucionCanillasSimpleDto > Simple , IEnumerable < ListadoDistribucionCanillasPromedioDiaDto > Promedios , string? Error ) > ObtenerListadoDistribucionCanillasAsync ( int idPublicacion , DateTime fechaDesde , DateTime fechaHasta )
{
if ( fechaDesde > fechaHasta )
return ( Enumerable . Empty < ListadoDistribucionCanillasSimpleDto > ( ) , Enumerable . Empty < ListadoDistribucionCanillasPromedioDiaDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
2025-05-28 16:01:59 -03:00
2025-05-27 18:17:56 -03:00
try
{
var simpleData = await _reportesRepository . GetListadoDistribucionCanillasSimpleAsync ( idPublicacion , fechaDesde , fechaHasta ) ;
var promediosData = await _reportesRepository . GetListadoDistribucionCanillasPromedioDiaAsync ( idPublicacion , fechaDesde , fechaHasta ) ;
return ( simpleData , promediosData , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Listado Distribucion Canillas." ) ;
return ( Enumerable . Empty < ListadoDistribucionCanillasSimpleDto > ( ) , Enumerable . Empty < ListadoDistribucionCanillasPromedioDiaDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
2025-05-28 16:01:59 -03:00
2025-05-27 18:17:56 -03:00
public async Task < ( IEnumerable < ListadoDistribucionCanillasImporteDto > Data , string? Error ) > ObtenerListadoDistribucionCanillasConImporteAsync ( int idPublicacion , DateTime fechaDesde , DateTime fechaHasta , bool esAccionista )
{
if ( fechaDesde > fechaHasta )
return ( Enumerable . Empty < ListadoDistribucionCanillasImporteDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
try
{
var data = await _reportesRepository . GetListadoDistribucionCanillasImporteAsync ( idPublicacion , fechaDesde , fechaHasta , esAccionista ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Listado Distribucion Canillas con Importe." ) ;
return ( Enumerable . Empty < ListadoDistribucionCanillasImporteDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
public async Task < ( IEnumerable < VentaMensualSecretariaElDiaDto > Data , string? Error ) > ObtenerVentaMensualSecretariaElDiaAsync ( DateTime fechaDesde , DateTime fechaHasta )
{
if ( fechaDesde > fechaHasta ) return ( Enumerable . Empty < VentaMensualSecretariaElDiaDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
try
{
var data = await _reportesRepository . GetVentaMensualSecretariaElDiaAsync ( fechaDesde , fechaHasta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Venta Mensual Secretaria El Dia." ) ;
return ( Enumerable . Empty < VentaMensualSecretariaElDiaDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
public async Task < ( IEnumerable < VentaMensualSecretariaElPlataDto > Data , string? Error ) > ObtenerVentaMensualSecretariaElPlataAsync ( DateTime fechaDesde , DateTime fechaHasta )
{
if ( fechaDesde > fechaHasta ) return ( Enumerable . Empty < VentaMensualSecretariaElPlataDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
try
{
var data = await _reportesRepository . GetVentaMensualSecretariaElPlataAsync ( fechaDesde , fechaHasta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Venta Mensual Secretaria El Plata." ) ;
return ( Enumerable . Empty < VentaMensualSecretariaElPlataDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
2025-05-28 16:01:59 -03:00
2025-05-27 18:17:56 -03:00
public async Task < ( IEnumerable < VentaMensualSecretariaTirDevoDto > Data , string? Error ) > ObtenerVentaMensualSecretariaTirDevoAsync ( DateTime fechaDesde , DateTime fechaHasta )
{
if ( fechaDesde > fechaHasta ) return ( Enumerable . Empty < VentaMensualSecretariaTirDevoDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
try
{
var data = await _reportesRepository . GetVentaMensualSecretariaTirDevoAsync ( fechaDesde , fechaHasta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Venta Mensual Secretaria Tirada/Devolucion." ) ;
return ( Enumerable . Empty < VentaMensualSecretariaTirDevoDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
2025-05-28 16:01:59 -03:00
2025-05-27 18:17:56 -03:00
public async Task < (
2025-06-24 12:52:37 -03:00
IEnumerable < DetalleDistribucionCanillaDto > Canillas ,
IEnumerable < DetalleDistribucionCanillaDto > CanillasAcc ,
IEnumerable < DetalleDistribucionCanillaAllDto > CanillasAll ,
IEnumerable < DetalleDistribucionCanillaDto > CanillasFechaLiq ,
IEnumerable < DetalleDistribucionCanillaDto > CanillasAccFechaLiq ,
IEnumerable < ObtenerCtrlDevolucionesDto > CtrlDevolucionesRemitos ,
IEnumerable < ControlDevolucionesReporteDto > CtrlDevolucionesParaDistCan ,
IEnumerable < DevueltosOtrosDiasDto > CtrlDevolucionesOtrosDias ,
string? Error
) > ObtenerReporteDistribucionCanillasAsync ( DateTime fecha , int idEmpresa )
2025-05-27 18:17:56 -03:00
{
try
{
var canillasTask = _reportesRepository . GetDetalleDistribucionCanillasPubliAsync ( fecha , idEmpresa ) ;
var canillasAccTask = _reportesRepository . GetDetalleDistribucionCanillasAccPubliAsync ( fecha , idEmpresa ) ;
var canillasAllTask = _reportesRepository . GetDetalleDistribucionCanillasAllPubliAsync ( fecha , idEmpresa ) ;
var canillasFechaLiqTask = _reportesRepository . GetDetalleDistribucionCanillasPubliFechaLiqAsync ( fecha , idEmpresa ) ;
var canillasAccFechaLiqTask = _reportesRepository . GetDetalleDistribucionCanillasAccPubliFechaLiqAsync ( fecha , idEmpresa ) ;
2025-05-28 16:01:59 -03:00
var ctrlDevolucionesRemitosTask = _reportesRepository . GetReporteObtenerCtrlDevolucionesAsync ( fecha , idEmpresa ) ; // SP_ObtenerCtrlDevoluciones
var ctrlDevolucionesParaDistCanTask = _reportesRepository . GetReporteCtrlDevolucionesParaDistCanAsync ( fecha , idEmpresa ) ; // SP_DistCanillasCantidadEntradaSalida
var ctrlDevolucionesOtrosDiasTask = _reportesRepository . GetEntradaSalidaOtrosDiasAsync ( fecha , idEmpresa ) ; // SP_DistCanillasCantidadEntradaSalidaOtrosDias
await Task . WhenAll (
canillasTask , canillasAccTask , canillasAllTask ,
canillasFechaLiqTask , canillasAccFechaLiqTask ,
ctrlDevolucionesRemitosTask , ctrlDevolucionesParaDistCanTask ,
ctrlDevolucionesOtrosDiasTask
) ;
2025-05-27 18:17:56 -03:00
2025-06-24 12:52:37 -03:00
var detallesOriginales = await ctrlDevolucionesParaDistCanTask ? ? Enumerable . Empty < ControlDevolucionesReporteDto > ( ) ;
var detallesOrdenados = detallesOriginales . OrderBy ( d = > d . Tipo ) . ToList ( ) ;
2025-05-28 16:01:59 -03:00
Func < IEnumerable < DetalleDistribucionCanillaDto > , IEnumerable < DetalleDistribucionCanillaDto > > toUtc =
items = > items ? . Select ( c = > { if ( c . Fecha . HasValue ) c . Fecha = DateTime . SpecifyKind ( c . Fecha . Value . Date , DateTimeKind . Utc ) ; return c ; } ) . ToList ( )
2025-05-27 18:17:56 -03:00
? ? Enumerable . Empty < DetalleDistribucionCanillaDto > ( ) ;
return (
toUtc ( await canillasTask ) ,
toUtc ( await canillasAccTask ) ,
await canillasAllTask ? ? Enumerable . Empty < DetalleDistribucionCanillaAllDto > ( ) ,
toUtc ( await canillasFechaLiqTask ) ,
toUtc ( await canillasAccFechaLiqTask ) ,
2025-05-28 16:01:59 -03:00
await ctrlDevolucionesRemitosTask ? ? Enumerable . Empty < ObtenerCtrlDevolucionesDto > ( ) ,
2025-06-24 12:52:37 -03:00
detallesOrdenados ,
2025-05-28 16:01:59 -03:00
await ctrlDevolucionesOtrosDiasTask ? ? Enumerable . Empty < DevueltosOtrosDiasDto > ( ) ,
2025-05-27 18:17:56 -03:00
null
) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Reporte Distribucion Canillas para fecha {Fecha} y empresa {IdEmpresa}." , fecha , idEmpresa ) ;
return (
Enumerable . Empty < DetalleDistribucionCanillaDto > ( ) ,
Enumerable . Empty < DetalleDistribucionCanillaDto > ( ) ,
Enumerable . Empty < DetalleDistribucionCanillaAllDto > ( ) ,
Enumerable . Empty < DetalleDistribucionCanillaDto > ( ) ,
Enumerable . Empty < DetalleDistribucionCanillaDto > ( ) ,
Enumerable . Empty < ObtenerCtrlDevolucionesDto > ( ) ,
Enumerable . Empty < ControlDevolucionesReporteDto > ( ) ,
2025-05-28 16:01:59 -03:00
Enumerable . Empty < DevueltosOtrosDiasDto > ( ) ,
2025-05-27 18:17:56 -03:00
"Error interno al generar el reporte de distribución de canillas."
) ;
}
}
public async Task < ( IEnumerable < TiradasPublicacionesSeccionesDto > Data , string? Error ) > ObtenerTiradasPublicacionesSeccionesAsync ( int idPublicacion , DateTime fechaDesde , DateTime fechaHasta , int idPlanta )
{
if ( fechaDesde > fechaHasta ) return ( Enumerable . Empty < TiradasPublicacionesSeccionesDto > ( ) , "Fecha 'Desde' no puede ser mayor que 'Hasta'." ) ;
try
{
var data = await _reportesRepository . GetTiradasPublicacionesSeccionesAsync ( idPublicacion , fechaDesde , fechaHasta , idPlanta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Tiradas por Publicación y Secciones." ) ;
return ( Enumerable . Empty < TiradasPublicacionesSeccionesDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
public async Task < ( IEnumerable < TiradasPublicacionesSeccionesDto > Data , string? Error ) > ObtenerTiradasPublicacionesSeccionesConsolidadoAsync ( int idPublicacion , DateTime fechaDesde , DateTime fechaHasta )
{
if ( fechaDesde > fechaHasta ) return ( Enumerable . Empty < TiradasPublicacionesSeccionesDto > ( ) , "Fecha 'Desde' no puede ser mayor que 'Hasta'." ) ;
try
{
var data = await _reportesRepository . GetTiradasPublicacionesSeccionesConsolidadoAsync ( idPublicacion , fechaDesde , fechaHasta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Tiradas por Publicación y Secciones (Consolidado)." ) ;
return ( Enumerable . Empty < TiradasPublicacionesSeccionesDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
public async Task < ( IEnumerable < ConsumoBobinasSeccionDto > Data , string? Error ) > ObtenerConsumoBobinasPorSeccionAsync ( DateTime fechaDesde , DateTime fechaHasta , int idPlanta )
{
if ( fechaDesde > fechaHasta ) return ( Enumerable . Empty < ConsumoBobinasSeccionDto > ( ) , "Fecha 'Desde' no puede ser mayor que 'Hasta'." ) ;
try
{
var data = await _reportesRepository . GetConsumoBobinasPorSeccionAsync ( fechaDesde , fechaHasta , idPlanta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Consumo de Bobinas por Sección." ) ;
return ( Enumerable . Empty < ConsumoBobinasSeccionDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
public async Task < ( IEnumerable < ConsumoBobinasSeccionDto > Data , string? Error ) > ObtenerConsumoBobinasPorSeccionConsolidadoAsync ( DateTime fechaDesde , DateTime fechaHasta )
{
if ( fechaDesde > fechaHasta ) return ( Enumerable . Empty < ConsumoBobinasSeccionDto > ( ) , "Fecha 'Desde' no puede ser mayor que 'Hasta'." ) ;
try
{
var data = await _reportesRepository . GetConsumoBobinasPorSeccionConsolidadoAsync ( fechaDesde , fechaHasta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Consumo de Bobinas por Sección (Consolidado)." ) ;
return ( Enumerable . Empty < ConsumoBobinasSeccionDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
public async Task < ( IEnumerable < ConsumoBobinasPublicacionDto > Data , string? Error ) > ObtenerConsumoBobinasPorPublicacionAsync ( DateTime fechaDesde , DateTime fechaHasta )
{
if ( fechaDesde > fechaHasta ) return ( Enumerable . Empty < ConsumoBobinasPublicacionDto > ( ) , "Fecha 'Desde' no puede ser mayor que 'Hasta'." ) ;
try
{
var data = await _reportesRepository . GetConsumoBobinasPorPublicacionAsync ( fechaDesde , fechaHasta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Consumo de Bobinas por Publicación." ) ;
return ( Enumerable . Empty < ConsumoBobinasPublicacionDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
public async Task < ( IEnumerable < ComparativaConsumoBobinasDto > Data , string? Error ) > ObtenerComparativaConsumoBobinasAsync ( DateTime fechaInicioMesA , DateTime fechaFinMesA , DateTime fechaInicioMesB , DateTime fechaFinMesB , int idPlanta )
{
if ( fechaInicioMesA > fechaFinMesA | | fechaInicioMesB > fechaFinMesB ) return ( Enumerable . Empty < ComparativaConsumoBobinasDto > ( ) , "Fechas de inicio no pueden ser mayores que las de fin para los meses." ) ;
try
{
var data = await _reportesRepository . GetComparativaConsumoBobinasAsync ( fechaInicioMesA , fechaFinMesA , fechaInicioMesB , fechaFinMesB , idPlanta ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Comparativa de Consumo de Bobinas." ) ;
return ( Enumerable . Empty < ComparativaConsumoBobinasDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
public async Task < ( IEnumerable < ComparativaConsumoBobinasDto > Data , string? Error ) > ObtenerComparativaConsumoBobinasConsolidadoAsync ( DateTime fechaInicioMesA , DateTime fechaFinMesA , DateTime fechaInicioMesB , DateTime fechaFinMesB )
{
if ( fechaInicioMesA > fechaFinMesA | | fechaInicioMesB > fechaFinMesB ) return ( Enumerable . Empty < ComparativaConsumoBobinasDto > ( ) , "Fechas de inicio no pueden ser mayores que las de fin para los meses." ) ;
try
{
var data = await _reportesRepository . GetComparativaConsumoBobinasConsolidadoAsync ( fechaInicioMesA , fechaFinMesA , fechaInicioMesB , fechaFinMesB ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Comparativa de Consumo de Bobinas (Consolidado)." ) ;
return ( Enumerable . Empty < ComparativaConsumoBobinasDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
// Implementación para ReporteCuentasDistribuidores
public async Task < (
IEnumerable < BalanceCuentaDistDto > EntradasSalidas ,
IEnumerable < BalanceCuentaDebCredDto > DebitosCreditos ,
IEnumerable < BalanceCuentaPagosDto > Pagos ,
IEnumerable < SaldoDto > Saldos ,
string? Error
) > ObtenerReporteCuentasDistribuidorAsync ( int idDistribuidor , int idEmpresa , DateTime fechaDesde , DateTime fechaHasta )
{
2025-05-28 16:01:59 -03:00
if ( fechaDesde > fechaHasta )
2025-05-27 18:17:56 -03:00
return ( Enumerable . Empty < BalanceCuentaDistDto > ( ) , Enumerable . Empty < BalanceCuentaDebCredDto > ( ) , Enumerable . Empty < BalanceCuentaPagosDto > ( ) , Enumerable . Empty < SaldoDto > ( ) , "Fecha 'Desde' no puede ser mayor que 'Hasta'." ) ;
try
{
var esTask = _reportesRepository . GetBalanceCuentaDistEntradaSalidaPorEmpresaAsync ( idDistribuidor , idEmpresa , fechaDesde , fechaHasta ) ;
var dcTask = _reportesRepository . GetBalanceCuentDistDebCredEmpresaAsync ( idDistribuidor , idEmpresa , fechaDesde , fechaHasta ) ;
var paTask = _reportesRepository . GetBalanceCuentDistPagosEmpresaAsync ( idDistribuidor , idEmpresa , fechaDesde , fechaHasta ) ;
var saTask = _reportesRepository . GetBalanceCuentSaldosEmpresasAsync ( "Distribuidores" , idDistribuidor , idEmpresa ) ;
await Task . WhenAll ( esTask , dcTask , paTask , saTask ) ;
2025-05-28 16:01:59 -03:00
Func < IEnumerable < BalanceCuentaDistDto > , IEnumerable < BalanceCuentaDistDto > > esToUtc =
items = > items ? . Select ( i = > { i . Fecha = DateTime . SpecifyKind ( i . Fecha . Date , DateTimeKind . Utc ) ; return i ; } ) . ToList ( )
2025-05-27 18:17:56 -03:00
? ? Enumerable . Empty < BalanceCuentaDistDto > ( ) ;
2025-05-28 16:01:59 -03:00
Func < IEnumerable < BalanceCuentaDebCredDto > , IEnumerable < BalanceCuentaDebCredDto > > dcToUtc =
2025-05-27 18:17:56 -03:00
items = > items ? . Select ( i = > { i . Fecha = DateTime . SpecifyKind ( i . Fecha . Date , DateTimeKind . Utc ) ; return i ; } ) . ToList ( )
? ? Enumerable . Empty < BalanceCuentaDebCredDto > ( ) ;
2025-05-28 16:01:59 -03:00
Func < IEnumerable < BalanceCuentaPagosDto > , IEnumerable < BalanceCuentaPagosDto > > paToUtc =
2025-05-27 18:17:56 -03:00
items = > items ? . Select ( i = > { i . Fecha = DateTime . SpecifyKind ( i . Fecha . Date , DateTimeKind . Utc ) ; return i ; } ) . ToList ( )
? ? Enumerable . Empty < BalanceCuentaPagosDto > ( ) ;
return (
esToUtc ( await esTask ) ,
dcToUtc ( await dcTask ) ,
paToUtc ( await paTask ) ,
await saTask ? ? Enumerable . Empty < SaldoDto > ( ) ,
null
) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Reporte Cuentas Distribuidor." ) ;
return (
Enumerable . Empty < BalanceCuentaDistDto > ( ) ,
Enumerable . Empty < BalanceCuentaDebCredDto > ( ) ,
Enumerable . Empty < BalanceCuentaPagosDto > ( ) ,
Enumerable . Empty < SaldoDto > ( ) ,
"Error interno al generar el reporte."
) ;
}
}
2025-05-31 23:48:42 -03:00
public async Task < ( IEnumerable < ListadoDistribucionDistSimpleDto > Simple , IEnumerable < ListadoDistribucionDistPromedioDiaDto > Promedios , string? Error ) > ObtenerListadoDistribucionDistribuidoresAsync ( int idDistribuidor , int idPublicacion , DateTime fechaDesde , DateTime fechaHasta )
{
if ( fechaDesde > fechaHasta )
return ( Enumerable . Empty < ListadoDistribucionDistSimpleDto > ( ) , Enumerable . Empty < ListadoDistribucionDistPromedioDiaDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
try
{
// Llamar a los métodos específicos del repositorio
var simpleDataTask = _reportesRepository . GetListadoDistribucionDistSimpleAsync ( idDistribuidor , idPublicacion , fechaDesde , fechaHasta ) ;
var promediosDataTask = _reportesRepository . GetListadoDistribucionDistPromedioDiaAsync ( idDistribuidor , idPublicacion , fechaDesde , fechaHasta ) ;
await Task . WhenAll ( simpleDataTask , promediosDataTask ) ;
return ( await simpleDataTask , await promediosDataTask , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener Listado Distribucion (Distribuidores). Params: Dist={idDistribuidor}, Pub={idPublicacion}, Desde={fechaDesde}, Hasta={fechaHasta}" , idDistribuidor , idPublicacion , fechaDesde , fechaHasta ) ;
return ( Enumerable . Empty < ListadoDistribucionDistSimpleDto > ( ) , Enumerable . Empty < ListadoDistribucionDistPromedioDiaDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
2025-06-03 13:45:20 -03:00
public async Task < (
IEnumerable < LiquidacionCanillaDetalleDto > Detalles ,
IEnumerable < LiquidacionCanillaGananciaDto > Ganancias ,
string? Error
) > ObtenerDatosTicketLiquidacionAsync ( DateTime fecha , int idCanilla )
{
try
{
var detallesTask = _reportesRepository . GetLiquidacionCanillaDetalleAsync ( fecha , idCanilla ) ;
var gananciasTask = _reportesRepository . GetLiquidacionCanillaGananciasAsync ( fecha , idCanilla ) ;
await Task . WhenAll ( detallesTask , gananciasTask ) ;
var detalles = await detallesTask ;
var ganancias = await gananciasTask ;
if ( ( detalles = = null | | ! detalles . Any ( ) ) & & ( ganancias = = null | | ! ganancias . Any ( ) ) )
{
// Podrías optar por no devolver error aquí si es válido que uno de los dos esté vacío
// y manejarlo en el controlador o el RDLC.
}
// Convertir fechas a UTC si es necesario para el RDLC (aunque estos DTOs no tienen fechas)
return (
detalles ? ? Enumerable . Empty < LiquidacionCanillaDetalleDto > ( ) ,
ganancias ? ? Enumerable . Empty < LiquidacionCanillaGananciaDto > ( ) ,
null
) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en ReportesService al obtener datos para Ticket Liquidación Canilla. Fecha: {Fecha}, Canilla: {IdCanilla}" , fecha , idCanilla ) ;
return (
Enumerable . Empty < LiquidacionCanillaDetalleDto > ( ) ,
Enumerable . Empty < LiquidacionCanillaGananciaDto > ( ) ,
"Error interno al obtener los datos para el ticket de liquidación."
) ;
}
}
2025-06-06 18:33:09 -03:00
public async Task < ( IEnumerable < ListadoDistCanMensualDiariosDto > Data , string? Error ) > ObtenerReporteMensualDiariosAsync ( DateTime fechaDesde , DateTime fechaHasta , bool esAccionista )
{
try
{
var data = await _reportesRepository . GetReporteMensualDiariosAsync ( fechaDesde , fechaHasta , esAccionista ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error al obtener reporte mensual canillitas (diarios)." ) ;
return ( Enumerable . Empty < ListadoDistCanMensualDiariosDto > ( ) , "Error al obtener datos del reporte (diarios)." ) ;
}
}
public async Task < ( IEnumerable < ListadoDistCanMensualPubDto > Data , string? Error ) > ObtenerReporteMensualPorPublicacionAsync ( DateTime fechaDesde , DateTime fechaHasta , bool esAccionista )
{
try
{
var data = await _reportesRepository . GetReporteMensualPorPublicacionAsync ( fechaDesde , fechaHasta , esAccionista ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error al obtener reporte mensual canillitas (por publicación)." ) ;
return ( Enumerable . Empty < ListadoDistCanMensualPubDto > ( ) , "Error al obtener datos del reporte (por publicación)." ) ;
}
}
2025-08-08 09:48:15 -03:00
public async Task < ( IEnumerable < FacturasParaReporteDto > Data , string? Error ) > ObtenerFacturasParaReportePublicidad ( int anio , int mes )
{
if ( anio < 2020 | | mes < 1 | | mes > 12 )
{
return ( Enumerable . Empty < FacturasParaReporteDto > ( ) , "Período no válido." ) ;
}
var periodo = $"{anio}-{mes:D2}" ;
try
{
// Llamada directa al nuevo método del repositorio
var data = await _reportesRepository . GetDatosReportePublicidadAsync ( periodo ) ;
return ( data , null ) ;
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en servicio al obtener datos para reporte de publicidad para el período {Periodo}" , periodo ) ;
return ( new List < FacturasParaReporteDto > ( ) , "Error interno al generar el reporte." ) ;
}
}
Feat: Implementa Reporte de Distribución de Suscripciones y Refactoriza Gestión de Ajustes
Se introduce una nueva funcionalidad de reporte crucial para la logística y se realiza una refactorización mayor del sistema de ajustes para garantizar la correcta imputación contable.
### ✨ Nuevas Características
- **Nuevo Reporte de Distribución de Suscripciones (RR011):**
- Se añade un nuevo reporte en PDF que genera un listado de todas las suscripciones activas en un rango de fechas.
- El reporte está diseñado para el equipo de reparto, incluyendo datos clave como nombre del suscriptor, dirección, teléfono, días de entrega y observaciones.
- Se implementa el endpoint, la lógica de servicio, la consulta a la base de datos y el template de QuestPDF en el backend.
- Se crea la página correspondiente en el frontend (React) con su selector de fechas y se añade la ruta y el enlace en el menú de reportes.
### 🔄 Refactorización Mayor
- **Asociación de Ajustes a Empresas:**
- Se refactoriza por completo la entidad `Ajuste` para incluir una referencia obligatoria a una `IdEmpresa`.
- **Motivo:** Corregir un error crítico en la lógica de negocio donde los ajustes de un suscriptor se aplicaban a la primera factura generada, sin importar a qué empresa correspondía el ajuste.
- Se provee un script de migración SQL para actualizar el esquema de la base de datos (`susc_Ajustes`).
- Se actualizan todos los DTOs, repositorios y servicios (backend) para manejar la nueva relación.
- Se modifica el `FacturacionService` para que ahora aplique los ajustes pendientes correspondientes a cada empresa dentro de su bucle de facturación.
- Se actualiza el formulario de creación/edición de ajustes en el frontend (React) para incluir un selector de empresa obligatorio.
### ⚡️ Optimizaciones de Rendimiento
- **Solución de N+1 Queries:**
- Se optimiza el método `ObtenerTodos` en `SuscriptorService` para obtener todas las formas de pago en una única consulta en lugar de una por cada suscriptor.
- Se optimiza el método `ObtenerAjustesPorSuscriptor` en `AjusteService` para obtener todos los nombres de usuarios y empresas en dos consultas masivas, en lugar de N consultas individuales.
- Se añade el método `GetByIdsAsync` al `IUsuarioRepository` y su implementación para soportar esta optimización.
### 🐛 Corrección de Errores
- Se corrigen múltiples errores en el script de migración de base de datos (uso de `GO` dentro de transacciones, error de "columna inválida").
- Se soluciona un error de SQL (`INSERT` statement) en `AjusteRepository` que impedía la creación de nuevos ajustes.
- Se corrige un bug en el `AjusteService` que causaba que el nombre de la empresa apareciera como "N/A" en la UI debido a un mapeo incompleto en el método optimizado.
- Se corrige la lógica de generación de emails en `FacturacionService` para mostrar correctamente el nombre de la empresa en cada sección del resumen de cuenta.
2025-08-09 17:39:21 -03:00
Feat: Implementa auditoría de envíos masivos y mejora de procesos
Se introduce un sistema completo para auditar los envíos masivos de correos durante el cierre mensual y se refactoriza la interfaz de usuario de procesos para una mayor claridad y escalabilidad. Además, se mejora la lógica de negocio para la gestión de bajas de suscripciones.
### ✨ Nuevas Características
- **Auditoría de Envíos Masivos (Cierre Mensual):**
- Se crea una nueva tabla `com_LotesDeEnvio` para registrar cada ejecución del proceso de facturación mensual.
- El `FacturacionService` ahora crea un "lote" al iniciar el cierre, registra el resultado de cada envío de email individual asociándolo a dicho lote, y actualiza las estadísticas finales (enviados, fallidos) al terminar.
- Se implementa un nuevo `LotesEnvioController` con un endpoint para consultar los detalles de cualquier lote de envío histórico.
### 🔄 Refactorización y Mejoras
- **Rediseño de la Página de Procesos:**
- La antigua página "Facturación" se renombra a `CierreYProcesosPage` y se rediseña completamente utilizando una interfaz de Pestañas (Tabs).
- **Pestaña "Procesos Mensuales":** Aisla las acciones principales (Generar Cierre, Archivo de Débito, Procesar Respuesta), mostrando un resumen del resultado del último envío.
- **Pestaña "Historial de Cierres":** Muestra una tabla con todos los lotes de envío pasados y permite al usuario ver los detalles de cada uno en un modal.
- **Filtros para el Historial de Cierres:**
- Se añaden filtros por Mes y Año a la pestaña de "Historial de Cierres", permitiendo al usuario buscar y auditar procesos pasados de manera eficiente. El filtrado se realiza en el backend para un rendimiento óptimo.
- **Lógica de `FechaFin` Obligatoria para Bajas:**
- Se implementa una regla de negocio crucial: al cambiar el estado de una suscripción a "Pausada" o "Cancelada", ahora es obligatorio establecer una `FechaFin`.
- **Frontend:** El modal de suscripciones ahora gestiona esto automáticamente, haciendo el campo `FechaFin` requerido y visible según el estado seleccionado.
- **Backend:** Se añade una validación en `SuscripcionService` como segunda capa de seguridad para garantizar la integridad de los datos.
### 🐛 Corrección de Errores
- **Reporte de Distribución:** Se corrigió un bug en la generación del PDF donde la columna de fecha no mostraba la "Fecha de Baja" para las suscripciones finalizadas. Ahora se muestra la fecha correcta según la sección (Altas o Bajas).
- **Errores de Compilación y Dependencias:** Se solucionaron varios errores de compilación en el backend, principalmente relacionados con la falta de registro de los nuevos repositorios (`ILoteDeEnvioRepository`, `IEmailLogService`, etc.) en el contenedor de inyección de dependencias (`Program.cs`).
- **Errores de Tipado en Frontend:** Se corrigieron múltiples errores de TypeScript en `CierreYProcesosPage` debidos a la inconsistencia entre `PascalCase` (C#) y `camelCase` (JSON/TypeScript), asegurando un mapeo correcto de los datos de la API.
2025-08-11 11:14:03 -03:00
public async Task < ( IEnumerable < DistribucionSuscripcionDto > Altas , IEnumerable < DistribucionSuscripcionDto > Bajas , string? Error ) > ObtenerReporteDistribucionSuscripcionesAsync ( DateTime fechaDesde , DateTime fechaHasta )
Feat: Implementa Reporte de Distribución de Suscripciones y Refactoriza Gestión de Ajustes
Se introduce una nueva funcionalidad de reporte crucial para la logística y se realiza una refactorización mayor del sistema de ajustes para garantizar la correcta imputación contable.
### ✨ Nuevas Características
- **Nuevo Reporte de Distribución de Suscripciones (RR011):**
- Se añade un nuevo reporte en PDF que genera un listado de todas las suscripciones activas en un rango de fechas.
- El reporte está diseñado para el equipo de reparto, incluyendo datos clave como nombre del suscriptor, dirección, teléfono, días de entrega y observaciones.
- Se implementa el endpoint, la lógica de servicio, la consulta a la base de datos y el template de QuestPDF en el backend.
- Se crea la página correspondiente en el frontend (React) con su selector de fechas y se añade la ruta y el enlace en el menú de reportes.
### 🔄 Refactorización Mayor
- **Asociación de Ajustes a Empresas:**
- Se refactoriza por completo la entidad `Ajuste` para incluir una referencia obligatoria a una `IdEmpresa`.
- **Motivo:** Corregir un error crítico en la lógica de negocio donde los ajustes de un suscriptor se aplicaban a la primera factura generada, sin importar a qué empresa correspondía el ajuste.
- Se provee un script de migración SQL para actualizar el esquema de la base de datos (`susc_Ajustes`).
- Se actualizan todos los DTOs, repositorios y servicios (backend) para manejar la nueva relación.
- Se modifica el `FacturacionService` para que ahora aplique los ajustes pendientes correspondientes a cada empresa dentro de su bucle de facturación.
- Se actualiza el formulario de creación/edición de ajustes en el frontend (React) para incluir un selector de empresa obligatorio.
### ⚡️ Optimizaciones de Rendimiento
- **Solución de N+1 Queries:**
- Se optimiza el método `ObtenerTodos` en `SuscriptorService` para obtener todas las formas de pago en una única consulta en lugar de una por cada suscriptor.
- Se optimiza el método `ObtenerAjustesPorSuscriptor` en `AjusteService` para obtener todos los nombres de usuarios y empresas en dos consultas masivas, en lugar de N consultas individuales.
- Se añade el método `GetByIdsAsync` al `IUsuarioRepository` y su implementación para soportar esta optimización.
### 🐛 Corrección de Errores
- Se corrigen múltiples errores en el script de migración de base de datos (uso de `GO` dentro de transacciones, error de "columna inválida").
- Se soluciona un error de SQL (`INSERT` statement) en `AjusteRepository` que impedía la creación de nuevos ajustes.
- Se corrige un bug en el `AjusteService` que causaba que el nombre de la empresa apareciera como "N/A" en la UI debido a un mapeo incompleto en el método optimizado.
- Se corrige la lógica de generación de emails en `FacturacionService` para mostrar correctamente el nombre de la empresa en cada sección del resumen de cuenta.
2025-08-09 17:39:21 -03:00
{
if ( fechaDesde > fechaHasta )
{
Feat: Implementa auditoría de envíos masivos y mejora de procesos
Se introduce un sistema completo para auditar los envíos masivos de correos durante el cierre mensual y se refactoriza la interfaz de usuario de procesos para una mayor claridad y escalabilidad. Además, se mejora la lógica de negocio para la gestión de bajas de suscripciones.
### ✨ Nuevas Características
- **Auditoría de Envíos Masivos (Cierre Mensual):**
- Se crea una nueva tabla `com_LotesDeEnvio` para registrar cada ejecución del proceso de facturación mensual.
- El `FacturacionService` ahora crea un "lote" al iniciar el cierre, registra el resultado de cada envío de email individual asociándolo a dicho lote, y actualiza las estadísticas finales (enviados, fallidos) al terminar.
- Se implementa un nuevo `LotesEnvioController` con un endpoint para consultar los detalles de cualquier lote de envío histórico.
### 🔄 Refactorización y Mejoras
- **Rediseño de la Página de Procesos:**
- La antigua página "Facturación" se renombra a `CierreYProcesosPage` y se rediseña completamente utilizando una interfaz de Pestañas (Tabs).
- **Pestaña "Procesos Mensuales":** Aisla las acciones principales (Generar Cierre, Archivo de Débito, Procesar Respuesta), mostrando un resumen del resultado del último envío.
- **Pestaña "Historial de Cierres":** Muestra una tabla con todos los lotes de envío pasados y permite al usuario ver los detalles de cada uno en un modal.
- **Filtros para el Historial de Cierres:**
- Se añaden filtros por Mes y Año a la pestaña de "Historial de Cierres", permitiendo al usuario buscar y auditar procesos pasados de manera eficiente. El filtrado se realiza en el backend para un rendimiento óptimo.
- **Lógica de `FechaFin` Obligatoria para Bajas:**
- Se implementa una regla de negocio crucial: al cambiar el estado de una suscripción a "Pausada" o "Cancelada", ahora es obligatorio establecer una `FechaFin`.
- **Frontend:** El modal de suscripciones ahora gestiona esto automáticamente, haciendo el campo `FechaFin` requerido y visible según el estado seleccionado.
- **Backend:** Se añade una validación en `SuscripcionService` como segunda capa de seguridad para garantizar la integridad de los datos.
### 🐛 Corrección de Errores
- **Reporte de Distribución:** Se corrigió un bug en la generación del PDF donde la columna de fecha no mostraba la "Fecha de Baja" para las suscripciones finalizadas. Ahora se muestra la fecha correcta según la sección (Altas o Bajas).
- **Errores de Compilación y Dependencias:** Se solucionaron varios errores de compilación en el backend, principalmente relacionados con la falta de registro de los nuevos repositorios (`ILoteDeEnvioRepository`, `IEmailLogService`, etc.) en el contenedor de inyección de dependencias (`Program.cs`).
- **Errores de Tipado en Frontend:** Se corrigieron múltiples errores de TypeScript en `CierreYProcesosPage` debidos a la inconsistencia entre `PascalCase` (C#) y `camelCase` (JSON/TypeScript), asegurando un mapeo correcto de los datos de la API.
2025-08-11 11:14:03 -03:00
return ( Enumerable . Empty < DistribucionSuscripcionDto > ( ) , Enumerable . Empty < DistribucionSuscripcionDto > ( ) , "La fecha 'Desde' no puede ser mayor que la fecha 'Hasta'." ) ;
Feat: Implementa Reporte de Distribución de Suscripciones y Refactoriza Gestión de Ajustes
Se introduce una nueva funcionalidad de reporte crucial para la logística y se realiza una refactorización mayor del sistema de ajustes para garantizar la correcta imputación contable.
### ✨ Nuevas Características
- **Nuevo Reporte de Distribución de Suscripciones (RR011):**
- Se añade un nuevo reporte en PDF que genera un listado de todas las suscripciones activas en un rango de fechas.
- El reporte está diseñado para el equipo de reparto, incluyendo datos clave como nombre del suscriptor, dirección, teléfono, días de entrega y observaciones.
- Se implementa el endpoint, la lógica de servicio, la consulta a la base de datos y el template de QuestPDF en el backend.
- Se crea la página correspondiente en el frontend (React) con su selector de fechas y se añade la ruta y el enlace en el menú de reportes.
### 🔄 Refactorización Mayor
- **Asociación de Ajustes a Empresas:**
- Se refactoriza por completo la entidad `Ajuste` para incluir una referencia obligatoria a una `IdEmpresa`.
- **Motivo:** Corregir un error crítico en la lógica de negocio donde los ajustes de un suscriptor se aplicaban a la primera factura generada, sin importar a qué empresa correspondía el ajuste.
- Se provee un script de migración SQL para actualizar el esquema de la base de datos (`susc_Ajustes`).
- Se actualizan todos los DTOs, repositorios y servicios (backend) para manejar la nueva relación.
- Se modifica el `FacturacionService` para que ahora aplique los ajustes pendientes correspondientes a cada empresa dentro de su bucle de facturación.
- Se actualiza el formulario de creación/edición de ajustes en el frontend (React) para incluir un selector de empresa obligatorio.
### ⚡️ Optimizaciones de Rendimiento
- **Solución de N+1 Queries:**
- Se optimiza el método `ObtenerTodos` en `SuscriptorService` para obtener todas las formas de pago en una única consulta en lugar de una por cada suscriptor.
- Se optimiza el método `ObtenerAjustesPorSuscriptor` en `AjusteService` para obtener todos los nombres de usuarios y empresas en dos consultas masivas, en lugar de N consultas individuales.
- Se añade el método `GetByIdsAsync` al `IUsuarioRepository` y su implementación para soportar esta optimización.
### 🐛 Corrección de Errores
- Se corrigen múltiples errores en el script de migración de base de datos (uso de `GO` dentro de transacciones, error de "columna inválida").
- Se soluciona un error de SQL (`INSERT` statement) en `AjusteRepository` que impedía la creación de nuevos ajustes.
- Se corrige un bug en el `AjusteService` que causaba que el nombre de la empresa apareciera como "N/A" en la UI debido a un mapeo incompleto en el método optimizado.
- Se corrige la lógica de generación de emails en `FacturacionService` para mostrar correctamente el nombre de la empresa en cada sección del resumen de cuenta.
2025-08-09 17:39:21 -03:00
}
Feat: Implementa auditoría de envíos masivos y mejora de procesos
Se introduce un sistema completo para auditar los envíos masivos de correos durante el cierre mensual y se refactoriza la interfaz de usuario de procesos para una mayor claridad y escalabilidad. Además, se mejora la lógica de negocio para la gestión de bajas de suscripciones.
### ✨ Nuevas Características
- **Auditoría de Envíos Masivos (Cierre Mensual):**
- Se crea una nueva tabla `com_LotesDeEnvio` para registrar cada ejecución del proceso de facturación mensual.
- El `FacturacionService` ahora crea un "lote" al iniciar el cierre, registra el resultado de cada envío de email individual asociándolo a dicho lote, y actualiza las estadísticas finales (enviados, fallidos) al terminar.
- Se implementa un nuevo `LotesEnvioController` con un endpoint para consultar los detalles de cualquier lote de envío histórico.
### 🔄 Refactorización y Mejoras
- **Rediseño de la Página de Procesos:**
- La antigua página "Facturación" se renombra a `CierreYProcesosPage` y se rediseña completamente utilizando una interfaz de Pestañas (Tabs).
- **Pestaña "Procesos Mensuales":** Aisla las acciones principales (Generar Cierre, Archivo de Débito, Procesar Respuesta), mostrando un resumen del resultado del último envío.
- **Pestaña "Historial de Cierres":** Muestra una tabla con todos los lotes de envío pasados y permite al usuario ver los detalles de cada uno en un modal.
- **Filtros para el Historial de Cierres:**
- Se añaden filtros por Mes y Año a la pestaña de "Historial de Cierres", permitiendo al usuario buscar y auditar procesos pasados de manera eficiente. El filtrado se realiza en el backend para un rendimiento óptimo.
- **Lógica de `FechaFin` Obligatoria para Bajas:**
- Se implementa una regla de negocio crucial: al cambiar el estado de una suscripción a "Pausada" o "Cancelada", ahora es obligatorio establecer una `FechaFin`.
- **Frontend:** El modal de suscripciones ahora gestiona esto automáticamente, haciendo el campo `FechaFin` requerido y visible según el estado seleccionado.
- **Backend:** Se añade una validación en `SuscripcionService` como segunda capa de seguridad para garantizar la integridad de los datos.
### 🐛 Corrección de Errores
- **Reporte de Distribución:** Se corrigió un bug en la generación del PDF donde la columna de fecha no mostraba la "Fecha de Baja" para las suscripciones finalizadas. Ahora se muestra la fecha correcta según la sección (Altas o Bajas).
- **Errores de Compilación y Dependencias:** Se solucionaron varios errores de compilación en el backend, principalmente relacionados con la falta de registro de los nuevos repositorios (`ILoteDeEnvioRepository`, `IEmailLogService`, etc.) en el contenedor de inyección de dependencias (`Program.cs`).
- **Errores de Tipado en Frontend:** Se corrigieron múltiples errores de TypeScript en `CierreYProcesosPage` debidos a la inconsistencia entre `PascalCase` (C#) y `camelCase` (JSON/TypeScript), asegurando un mapeo correcto de los datos de la API.
2025-08-11 11:14:03 -03:00
Feat: Implementa Reporte de Distribución de Suscripciones y Refactoriza Gestión de Ajustes
Se introduce una nueva funcionalidad de reporte crucial para la logística y se realiza una refactorización mayor del sistema de ajustes para garantizar la correcta imputación contable.
### ✨ Nuevas Características
- **Nuevo Reporte de Distribución de Suscripciones (RR011):**
- Se añade un nuevo reporte en PDF que genera un listado de todas las suscripciones activas en un rango de fechas.
- El reporte está diseñado para el equipo de reparto, incluyendo datos clave como nombre del suscriptor, dirección, teléfono, días de entrega y observaciones.
- Se implementa el endpoint, la lógica de servicio, la consulta a la base de datos y el template de QuestPDF en el backend.
- Se crea la página correspondiente en el frontend (React) con su selector de fechas y se añade la ruta y el enlace en el menú de reportes.
### 🔄 Refactorización Mayor
- **Asociación de Ajustes a Empresas:**
- Se refactoriza por completo la entidad `Ajuste` para incluir una referencia obligatoria a una `IdEmpresa`.
- **Motivo:** Corregir un error crítico en la lógica de negocio donde los ajustes de un suscriptor se aplicaban a la primera factura generada, sin importar a qué empresa correspondía el ajuste.
- Se provee un script de migración SQL para actualizar el esquema de la base de datos (`susc_Ajustes`).
- Se actualizan todos los DTOs, repositorios y servicios (backend) para manejar la nueva relación.
- Se modifica el `FacturacionService` para que ahora aplique los ajustes pendientes correspondientes a cada empresa dentro de su bucle de facturación.
- Se actualiza el formulario de creación/edición de ajustes en el frontend (React) para incluir un selector de empresa obligatorio.
### ⚡️ Optimizaciones de Rendimiento
- **Solución de N+1 Queries:**
- Se optimiza el método `ObtenerTodos` en `SuscriptorService` para obtener todas las formas de pago en una única consulta en lugar de una por cada suscriptor.
- Se optimiza el método `ObtenerAjustesPorSuscriptor` en `AjusteService` para obtener todos los nombres de usuarios y empresas en dos consultas masivas, en lugar de N consultas individuales.
- Se añade el método `GetByIdsAsync` al `IUsuarioRepository` y su implementación para soportar esta optimización.
### 🐛 Corrección de Errores
- Se corrigen múltiples errores en el script de migración de base de datos (uso de `GO` dentro de transacciones, error de "columna inválida").
- Se soluciona un error de SQL (`INSERT` statement) en `AjusteRepository` que impedía la creación de nuevos ajustes.
- Se corrige un bug en el `AjusteService` que causaba que el nombre de la empresa apareciera como "N/A" en la UI debido a un mapeo incompleto en el método optimizado.
- Se corrige la lógica de generación de emails en `FacturacionService` para mostrar correctamente el nombre de la empresa en cada sección del resumen de cuenta.
2025-08-09 17:39:21 -03:00
try
{
Feat: Implementa auditoría de envíos masivos y mejora de procesos
Se introduce un sistema completo para auditar los envíos masivos de correos durante el cierre mensual y se refactoriza la interfaz de usuario de procesos para una mayor claridad y escalabilidad. Además, se mejora la lógica de negocio para la gestión de bajas de suscripciones.
### ✨ Nuevas Características
- **Auditoría de Envíos Masivos (Cierre Mensual):**
- Se crea una nueva tabla `com_LotesDeEnvio` para registrar cada ejecución del proceso de facturación mensual.
- El `FacturacionService` ahora crea un "lote" al iniciar el cierre, registra el resultado de cada envío de email individual asociándolo a dicho lote, y actualiza las estadísticas finales (enviados, fallidos) al terminar.
- Se implementa un nuevo `LotesEnvioController` con un endpoint para consultar los detalles de cualquier lote de envío histórico.
### 🔄 Refactorización y Mejoras
- **Rediseño de la Página de Procesos:**
- La antigua página "Facturación" se renombra a `CierreYProcesosPage` y se rediseña completamente utilizando una interfaz de Pestañas (Tabs).
- **Pestaña "Procesos Mensuales":** Aisla las acciones principales (Generar Cierre, Archivo de Débito, Procesar Respuesta), mostrando un resumen del resultado del último envío.
- **Pestaña "Historial de Cierres":** Muestra una tabla con todos los lotes de envío pasados y permite al usuario ver los detalles de cada uno en un modal.
- **Filtros para el Historial de Cierres:**
- Se añaden filtros por Mes y Año a la pestaña de "Historial de Cierres", permitiendo al usuario buscar y auditar procesos pasados de manera eficiente. El filtrado se realiza en el backend para un rendimiento óptimo.
- **Lógica de `FechaFin` Obligatoria para Bajas:**
- Se implementa una regla de negocio crucial: al cambiar el estado de una suscripción a "Pausada" o "Cancelada", ahora es obligatorio establecer una `FechaFin`.
- **Frontend:** El modal de suscripciones ahora gestiona esto automáticamente, haciendo el campo `FechaFin` requerido y visible según el estado seleccionado.
- **Backend:** Se añade una validación en `SuscripcionService` como segunda capa de seguridad para garantizar la integridad de los datos.
### 🐛 Corrección de Errores
- **Reporte de Distribución:** Se corrigió un bug en la generación del PDF donde la columna de fecha no mostraba la "Fecha de Baja" para las suscripciones finalizadas. Ahora se muestra la fecha correcta según la sección (Altas o Bajas).
- **Errores de Compilación y Dependencias:** Se solucionaron varios errores de compilación en el backend, principalmente relacionados con la falta de registro de los nuevos repositorios (`ILoteDeEnvioRepository`, `IEmailLogService`, etc.) en el contenedor de inyección de dependencias (`Program.cs`).
- **Errores de Tipado en Frontend:** Se corrigieron múltiples errores de TypeScript en `CierreYProcesosPage` debidos a la inconsistencia entre `PascalCase` (C#) y `camelCase` (JSON/TypeScript), asegurando un mapeo correcto de los datos de la API.
2025-08-11 11:14:03 -03:00
// Ejecutamos ambas consultas en paralelo para mayor eficiencia
var altasTask = _reportesRepository . GetDistribucionSuscripcionesActivasAsync ( fechaDesde , fechaHasta ) ;
var bajasTask = _reportesRepository . GetDistribucionSuscripcionesBajasAsync ( fechaDesde , fechaHasta ) ;
await Task . WhenAll ( altasTask , bajasTask ) ;
return ( await altasTask , await bajasTask , null ) ;
Feat: Implementa Reporte de Distribución de Suscripciones y Refactoriza Gestión de Ajustes
Se introduce una nueva funcionalidad de reporte crucial para la logística y se realiza una refactorización mayor del sistema de ajustes para garantizar la correcta imputación contable.
### ✨ Nuevas Características
- **Nuevo Reporte de Distribución de Suscripciones (RR011):**
- Se añade un nuevo reporte en PDF que genera un listado de todas las suscripciones activas en un rango de fechas.
- El reporte está diseñado para el equipo de reparto, incluyendo datos clave como nombre del suscriptor, dirección, teléfono, días de entrega y observaciones.
- Se implementa el endpoint, la lógica de servicio, la consulta a la base de datos y el template de QuestPDF en el backend.
- Se crea la página correspondiente en el frontend (React) con su selector de fechas y se añade la ruta y el enlace en el menú de reportes.
### 🔄 Refactorización Mayor
- **Asociación de Ajustes a Empresas:**
- Se refactoriza por completo la entidad `Ajuste` para incluir una referencia obligatoria a una `IdEmpresa`.
- **Motivo:** Corregir un error crítico en la lógica de negocio donde los ajustes de un suscriptor se aplicaban a la primera factura generada, sin importar a qué empresa correspondía el ajuste.
- Se provee un script de migración SQL para actualizar el esquema de la base de datos (`susc_Ajustes`).
- Se actualizan todos los DTOs, repositorios y servicios (backend) para manejar la nueva relación.
- Se modifica el `FacturacionService` para que ahora aplique los ajustes pendientes correspondientes a cada empresa dentro de su bucle de facturación.
- Se actualiza el formulario de creación/edición de ajustes en el frontend (React) para incluir un selector de empresa obligatorio.
### ⚡️ Optimizaciones de Rendimiento
- **Solución de N+1 Queries:**
- Se optimiza el método `ObtenerTodos` en `SuscriptorService` para obtener todas las formas de pago en una única consulta en lugar de una por cada suscriptor.
- Se optimiza el método `ObtenerAjustesPorSuscriptor` en `AjusteService` para obtener todos los nombres de usuarios y empresas en dos consultas masivas, en lugar de N consultas individuales.
- Se añade el método `GetByIdsAsync` al `IUsuarioRepository` y su implementación para soportar esta optimización.
### 🐛 Corrección de Errores
- Se corrigen múltiples errores en el script de migración de base de datos (uso de `GO` dentro de transacciones, error de "columna inválida").
- Se soluciona un error de SQL (`INSERT` statement) en `AjusteRepository` que impedía la creación de nuevos ajustes.
- Se corrige un bug en el `AjusteService` que causaba que el nombre de la empresa apareciera como "N/A" en la UI debido a un mapeo incompleto en el método optimizado.
- Se corrige la lógica de generación de emails en `FacturacionService` para mostrar correctamente el nombre de la empresa en cada sección del resumen de cuenta.
2025-08-09 17:39:21 -03:00
}
catch ( Exception ex )
{
_logger . LogError ( ex , "Error en servicio al obtener datos para reporte de distribución de suscripciones." ) ;
Feat: Implementa auditoría de envíos masivos y mejora de procesos
Se introduce un sistema completo para auditar los envíos masivos de correos durante el cierre mensual y se refactoriza la interfaz de usuario de procesos para una mayor claridad y escalabilidad. Además, se mejora la lógica de negocio para la gestión de bajas de suscripciones.
### ✨ Nuevas Características
- **Auditoría de Envíos Masivos (Cierre Mensual):**
- Se crea una nueva tabla `com_LotesDeEnvio` para registrar cada ejecución del proceso de facturación mensual.
- El `FacturacionService` ahora crea un "lote" al iniciar el cierre, registra el resultado de cada envío de email individual asociándolo a dicho lote, y actualiza las estadísticas finales (enviados, fallidos) al terminar.
- Se implementa un nuevo `LotesEnvioController` con un endpoint para consultar los detalles de cualquier lote de envío histórico.
### 🔄 Refactorización y Mejoras
- **Rediseño de la Página de Procesos:**
- La antigua página "Facturación" se renombra a `CierreYProcesosPage` y se rediseña completamente utilizando una interfaz de Pestañas (Tabs).
- **Pestaña "Procesos Mensuales":** Aisla las acciones principales (Generar Cierre, Archivo de Débito, Procesar Respuesta), mostrando un resumen del resultado del último envío.
- **Pestaña "Historial de Cierres":** Muestra una tabla con todos los lotes de envío pasados y permite al usuario ver los detalles de cada uno en un modal.
- **Filtros para el Historial de Cierres:**
- Se añaden filtros por Mes y Año a la pestaña de "Historial de Cierres", permitiendo al usuario buscar y auditar procesos pasados de manera eficiente. El filtrado se realiza en el backend para un rendimiento óptimo.
- **Lógica de `FechaFin` Obligatoria para Bajas:**
- Se implementa una regla de negocio crucial: al cambiar el estado de una suscripción a "Pausada" o "Cancelada", ahora es obligatorio establecer una `FechaFin`.
- **Frontend:** El modal de suscripciones ahora gestiona esto automáticamente, haciendo el campo `FechaFin` requerido y visible según el estado seleccionado.
- **Backend:** Se añade una validación en `SuscripcionService` como segunda capa de seguridad para garantizar la integridad de los datos.
### 🐛 Corrección de Errores
- **Reporte de Distribución:** Se corrigió un bug en la generación del PDF donde la columna de fecha no mostraba la "Fecha de Baja" para las suscripciones finalizadas. Ahora se muestra la fecha correcta según la sección (Altas o Bajas).
- **Errores de Compilación y Dependencias:** Se solucionaron varios errores de compilación en el backend, principalmente relacionados con la falta de registro de los nuevos repositorios (`ILoteDeEnvioRepository`, `IEmailLogService`, etc.) en el contenedor de inyección de dependencias (`Program.cs`).
- **Errores de Tipado en Frontend:** Se corrigieron múltiples errores de TypeScript en `CierreYProcesosPage` debidos a la inconsistencia entre `PascalCase` (C#) y `camelCase` (JSON/TypeScript), asegurando un mapeo correcto de los datos de la API.
2025-08-11 11:14:03 -03:00
return ( Enumerable . Empty < DistribucionSuscripcionDto > ( ) , Enumerable . Empty < DistribucionSuscripcionDto > ( ) , "Error interno al generar el reporte." ) ;
Feat: Implementa Reporte de Distribución de Suscripciones y Refactoriza Gestión de Ajustes
Se introduce una nueva funcionalidad de reporte crucial para la logística y se realiza una refactorización mayor del sistema de ajustes para garantizar la correcta imputación contable.
### ✨ Nuevas Características
- **Nuevo Reporte de Distribución de Suscripciones (RR011):**
- Se añade un nuevo reporte en PDF que genera un listado de todas las suscripciones activas en un rango de fechas.
- El reporte está diseñado para el equipo de reparto, incluyendo datos clave como nombre del suscriptor, dirección, teléfono, días de entrega y observaciones.
- Se implementa el endpoint, la lógica de servicio, la consulta a la base de datos y el template de QuestPDF en el backend.
- Se crea la página correspondiente en el frontend (React) con su selector de fechas y se añade la ruta y el enlace en el menú de reportes.
### 🔄 Refactorización Mayor
- **Asociación de Ajustes a Empresas:**
- Se refactoriza por completo la entidad `Ajuste` para incluir una referencia obligatoria a una `IdEmpresa`.
- **Motivo:** Corregir un error crítico en la lógica de negocio donde los ajustes de un suscriptor se aplicaban a la primera factura generada, sin importar a qué empresa correspondía el ajuste.
- Se provee un script de migración SQL para actualizar el esquema de la base de datos (`susc_Ajustes`).
- Se actualizan todos los DTOs, repositorios y servicios (backend) para manejar la nueva relación.
- Se modifica el `FacturacionService` para que ahora aplique los ajustes pendientes correspondientes a cada empresa dentro de su bucle de facturación.
- Se actualiza el formulario de creación/edición de ajustes en el frontend (React) para incluir un selector de empresa obligatorio.
### ⚡️ Optimizaciones de Rendimiento
- **Solución de N+1 Queries:**
- Se optimiza el método `ObtenerTodos` en `SuscriptorService` para obtener todas las formas de pago en una única consulta en lugar de una por cada suscriptor.
- Se optimiza el método `ObtenerAjustesPorSuscriptor` en `AjusteService` para obtener todos los nombres de usuarios y empresas en dos consultas masivas, en lugar de N consultas individuales.
- Se añade el método `GetByIdsAsync` al `IUsuarioRepository` y su implementación para soportar esta optimización.
### 🐛 Corrección de Errores
- Se corrigen múltiples errores en el script de migración de base de datos (uso de `GO` dentro de transacciones, error de "columna inválida").
- Se soluciona un error de SQL (`INSERT` statement) en `AjusteRepository` que impedía la creación de nuevos ajustes.
- Se corrige un bug en el `AjusteService` que causaba que el nombre de la empresa apareciera como "N/A" en la UI debido a un mapeo incompleto en el método optimizado.
- Se corrige la lógica de generación de emails en `FacturacionService` para mostrar correctamente el nombre de la empresa en cada sección del resumen de cuenta.
2025-08-09 17:39:21 -03:00
}
}
2025-05-27 11:21:00 -03:00
}
}