From fc27b4b43ebc3dd81fdabd49aa8f9a0480f3d94d Mon Sep 17 00:00:00 2001 From: dmolinari Date: Fri, 5 Dec 2025 12:25:18 -0300 Subject: [PATCH] =?UTF-8?q?feat(Reportes):=20Ajusta=20c=C3=A1lculo=20de=20?= =?UTF-8?q?promedios=20en=20Listado=20de=20Distribuci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se modifica la lógica de cálculo para la fila "General" en la tabla de promedios del reporte de Listado de Distribución para distribuidores. **Motivación:** Por requerimiento explícito del usuario final, el cálculo de los promedios generales (Llevados, Devueltos, Ventas y % Devolución) debe ser un promedio aritmético simple de los valores de los días de la semana mostrados en la tabla (ej. Viernes, Sábado, Domingo), en lugar del promedio ponderado que se calculaba anteriormente basado en los totales generales. **Cambios Realizados:** 1. **Backend (`ListadoDistribucionDistribuidoresViewModel.cs`):** * Se actualizó la propiedad `PromedioGeneral` para que calcule sus valores (Promedio\_Llevados, Promedio\_Devueltos, etc.) promediando directamente los valores de la colección `PromediosPorDia`. 2. **PDF (`ListadoDistribucionDistribuidoresDocument.cs`):** * Se ajustó la lógica de renderizado de la fila "General" para que el porcentaje de devolución también se calcule como el promedio de los porcentajes de los días individuales, asegurando consistencia con el ViewModel. 3. **Frontend (`ReporteListadoDistribucionPage.tsx`):** * Se modificó el cálculo del estado `totalesPromedios` dentro de la función `handleGenerarReporte`. Ahora, en lugar de usar los totales de la tabla de detalle, suma los valores de la tabla de promedios y los divide por la cantidad de días para obtener un promedio simple. **Resultado:** Tanto la interfaz web como el PDF generado ahora muestran en la fila "General" un promedio simple de las filas de promedios diarios, alineándose con la lógica solicitada por el usuario. --- ...stadoDistribucionDistribuidoresDocument.cs | 30 +++++++++---------- ...tadoDistribucionDistribuidoresViewModel.cs | 26 ++++++++-------- Backend/GestionIntegral.Api/Program.cs | 2 +- .../ReporteListadoDistribucionPage.tsx | 26 +++++++++------- 4 files changed, 44 insertions(+), 40 deletions(-) diff --git a/Backend/GestionIntegral.Api/Controllers/Reportes/PdfTemplates/ListadoDistribucionDistribuidoresDocument.cs b/Backend/GestionIntegral.Api/Controllers/Reportes/PdfTemplates/ListadoDistribucionDistribuidoresDocument.cs index 185df88..aa79120 100644 --- a/Backend/GestionIntegral.Api/Controllers/Reportes/PdfTemplates/ListadoDistribucionDistribuidoresDocument.cs +++ b/Backend/GestionIntegral.Api/Controllers/Reportes/PdfTemplates/ListadoDistribucionDistribuidoresDocument.cs @@ -24,7 +24,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates { page.Margin(1, Unit.Centimetre); page.DefaultTextStyle(x => x.FontFamily("Arial").FontSize(10)); - + page.Header().Element(ComposeHeader); page.Content().Element(ComposeContent); page.Footer().AlignCenter().Text(x => { x.Span("Página "); x.CurrentPageNumber(); }); @@ -46,7 +46,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates }); }); } - + void ComposeContent(IContainer container) { container.PaddingTop(8, Unit.Millimetre).Column(column => @@ -54,7 +54,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates column.Spacing(15); column.Item().Text("Distribución").SemiBold().FontSize(12); column.Item().Element(ComposeDetalleDiarioTable); - + column.Item().PaddingTop(5, Unit.Millimetre); column.Item().Text("Promedios").SemiBold().FontSize(12); @@ -90,14 +90,14 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates var llevados = item.Llevados ?? 0; var devueltos = item.Devueltos ?? 0; var ventaNetaDia = llevados - devueltos; - if(llevados > 0) + if (llevados > 0) { ventaNetaAcumulada += ventaNetaDia; conteoDias++; } var promedio = conteoDias > 0 ? ventaNetaAcumulada / conteoDias : 0; var porcDevolucion = llevados > 0 ? (decimal)devueltos * 100 / llevados : 0; - + table.Cell().Border(1).Padding(3).Text(item.Dia.ToString()); table.Cell().Border(1).Padding(3).AlignRight().Text(llevados.ToString("N0")); table.Cell().Border(1).Padding(3).AlignRight().Text(devueltos.ToString("N0")); @@ -105,12 +105,12 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates table.Cell().Border(1).Padding(3).AlignRight().Text(promedio.ToString("N0")); table.Cell().Border(1).Padding(3).AlignRight().Text(porcDevolucion.ToString("F2") + "%"); } - + var totalLlevados = Model.DetalleDiario.Sum(i => i.Llevados ?? 0); var totalDevueltos = Model.DetalleDiario.Sum(i => i.Devueltos ?? 0); var totalVentaNeta = totalLlevados - totalDevueltos; var totalPorcDevolucion = totalLlevados > 0 ? (decimal)totalDevueltos * 100 / totalLlevados : 0; - + var boldStyle = TextStyle.Default.SemiBold(); table.Cell().Border(1).Padding(3); table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(totalLlevados.ToString("N0")).Style(boldStyle)); @@ -120,11 +120,11 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(totalPorcDevolucion.ToString("F2") + "%").Style(boldStyle)); }); } - + void ComposePromediosTable(IContainer container) { - var dayOrder = new Dictionary { { "Lunes", 1 }, { "Martes", 2 }, { "Miércoles", 3 }, { "Jueves", 4 }, { "Viernes", 5 }, { "Sábado", 6 }, { "Domingo", 7 }}; - + var dayOrder = new Dictionary { { "Lunes", 1 }, { "Martes", 2 }, { "Miércoles", 3 }, { "Jueves", 4 }, { "Viernes", 5 }, { "Sábado", 6 }, { "Domingo", 7 } }; + container.Table(table => { table.ColumnsDefinition(columns => @@ -147,7 +147,7 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates var llevados = item.Promedio_Llevados ?? 0; var devueltos = item.Promedio_Devueltos ?? 0; var porcDevolucion = llevados > 0 ? (decimal)devueltos * 100 / llevados : 0; - + table.Cell().Border(1).Padding(3).Text(item.Dia); table.Cell().Border(1).Padding(3).AlignRight().Text(item.Cant?.ToString("N0") ?? "0"); table.Cell().Border(1).Padding(3).AlignRight().Text(llevados.ToString("N0")); @@ -161,16 +161,16 @@ namespace GestionIntegral.Api.Controllers.Reportes.PdfTemplates if (general != null) { var boldStyle = TextStyle.Default.SemiBold(); - var llevadosGeneral = general.Llevados ?? 0; // Usamos el total para el % - var devueltosGeneral = general.Devueltos ?? 0; // Usamos el total para el % - var porcDevolucionGeneral = llevadosGeneral > 0 ? (decimal)devueltosGeneral * 100 / llevadosGeneral : 0; + var avgPercentage = Model.PromediosPorDia + .Where(p => p.Dia != "General" && (p.Promedio_Llevados ?? 0) > 0) + .Average(p => (decimal)(p.Promedio_Devueltos ?? 0) * 100 / (p.Promedio_Llevados ?? 1)); table.Cell().Border(1).Padding(3).Text(t => t.Span(general.Dia).Style(boldStyle)); table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Cant?.ToString("N0")).Style(boldStyle)); table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Promedio_Llevados?.ToString("N0")).Style(boldStyle)); table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Promedio_Devueltos?.ToString("N0")).Style(boldStyle)); table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(general.Promedio_Ventas?.ToString("N0")).Style(boldStyle)); - table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(porcDevolucionGeneral.ToString("F2") + "%").Style(boldStyle)); + table.Cell().Border(1).Padding(3).AlignRight().Text(t => t.Span(avgPercentage.ToString("F2") + "%").Style(boldStyle)); } }); } diff --git a/Backend/GestionIntegral.Api/Models/Dtos/Reportes/ViewModels/ListadoDistribucionDistribuidoresViewModel.cs b/Backend/GestionIntegral.Api/Models/Dtos/Reportes/ViewModels/ListadoDistribucionDistribuidoresViewModel.cs index 7e1404b..1c0990e 100644 --- a/Backend/GestionIntegral.Api/Models/Dtos/Reportes/ViewModels/ListadoDistribucionDistribuidoresViewModel.cs +++ b/Backend/GestionIntegral.Api/Models/Dtos/Reportes/ViewModels/ListadoDistribucionDistribuidoresViewModel.cs @@ -20,26 +20,26 @@ namespace GestionIntegral.Api.Dtos.Reportes.ViewModels { get { - if (DetalleDiario == null || !DetalleDiario.Any()) + if (PromediosPorDia == null || !PromediosPorDia.Any()) { return null; } - - var diasConDatos = DetalleDiario.Count(d => (d.Llevados ?? 0) > 0); - if (diasConDatos == 0) return null; - - var totalLlevados = DetalleDiario.Sum(d => d.Llevados ?? 0); - var totalDevueltos = DetalleDiario.Sum(d => d.Devueltos ?? 0); + var promediosValidos = PromediosPorDia.Where(p => p.Dia != "General").ToList(); + if (!promediosValidos.Any()) return null; + var countPromedios = promediosValidos.Count; + var sumPromLlevados = promediosValidos.Sum(p => p.Promedio_Llevados ?? 0); + var sumPromDevueltos = promediosValidos.Sum(p => p.Promedio_Devueltos ?? 0); + var sumPromVentas = promediosValidos.Sum(p => p.Promedio_Ventas ?? 0); return new ListadoDistribucionDistPromedioDiaDto { Dia = "General", - Cant = diasConDatos, - Promedio_Llevados = totalLlevados / diasConDatos, - Promedio_Devueltos = totalDevueltos / diasConDatos, - Promedio_Ventas = (totalLlevados - totalDevueltos) / diasConDatos, - Llevados = totalLlevados, // Guardamos el total para el cálculo del % - Devueltos = totalDevueltos // Guardamos el total para el cálculo del % + Cant = promediosValidos.Sum(p => p.Cant ?? 0), + Promedio_Llevados = (int)Math.Round((decimal)sumPromLlevados / countPromedios, MidpointRounding.AwayFromZero), + Promedio_Devueltos = (int)Math.Round((decimal)sumPromDevueltos / countPromedios, MidpointRounding.AwayFromZero), + Promedio_Ventas = (int)Math.Round((decimal)sumPromVentas / countPromedios, MidpointRounding.AwayFromZero), + Llevados = (int)Math.Round((decimal)sumPromLlevados / countPromedios, MidpointRounding.AwayFromZero), + Devueltos = (int)Math.Round((decimal)sumPromDevueltos / countPromedios, MidpointRounding.AwayFromZero) }; } } diff --git a/Backend/GestionIntegral.Api/Program.cs b/Backend/GestionIntegral.Api/Program.cs index 064ad81..87684e2 100644 --- a/Backend/GestionIntegral.Api/Program.cs +++ b/Backend/GestionIntegral.Api/Program.cs @@ -187,7 +187,7 @@ builder.Services.AddCors(options => policy => { policy.WithOrigins( - "http://localhost:5173", // Para desarrollo local + "http://localhost:5175", // Para desarrollo local "https://gestion.eldiaservicios.com" // Para producción ) .AllowAnyHeader() diff --git a/Frontend/src/pages/Reportes/ReporteListadoDistribucionPage.tsx b/Frontend/src/pages/Reportes/ReporteListadoDistribucionPage.tsx index ecd2582..d986a7f 100644 --- a/Frontend/src/pages/Reportes/ReporteListadoDistribucionPage.tsx +++ b/Frontend/src/pages/Reportes/ReporteListadoDistribucionPage.tsx @@ -121,20 +121,24 @@ const ReporteListadoDistribucionPage: React.FC = () => { porcentajeDevolucion: item.promedio_Llevados > 0 ? (item.promedio_Devueltos / item.promedio_Llevados) * 100 : 0, })); - // Calcular totales para la tabla de promedios (ponderados por Cant. Días) const totalDiasPromedios = promediosConCalculos.reduce((sum, item) => sum + (item.cant || 0), 0); - const totalPonderadoLlevados = promediosConCalculos.reduce((sum, item) => sum + ((item.promedio_Llevados || 0) * (item.cant || 0)), 0); - const totalPonderadoDevueltos = promediosConCalculos.reduce((sum, item) => sum + ((item.promedio_Devueltos || 0) * (item.cant || 0)), 0); - const totalPonderadoVentas = promediosConCalculos.reduce((sum, item) => sum + ((item.promedio_Ventas || 0) * (item.cant || 0)), 0); + const countPromedios = promediosConCalculos.length; - setTotalesPromedios({ - cantDias: totalDiasPromedios, - promLlevados: totalDiasPromedios > 0 ? totalPonderadoLlevados / totalDiasPromedios : 0, - promDevueltos: totalDiasPromedios > 0 ? totalPonderadoDevueltos / totalDiasPromedios : 0, - promVentas: totalDiasPromedios > 0 ? totalPonderadoVentas / totalDiasPromedios : 0, - porcentajeDevolucionGeneral: totalPonderadoLlevados > 0 ? (totalPonderadoDevueltos / totalPonderadoLlevados) * 100 : 0, - }); + // LÓGICA DE PROMEDIO DE PROMEDIOS + if (countPromedios > 0) { + const sumPromLlevados = promediosConCalculos.reduce((sum, item) => sum + (item.promedio_Llevados || 0), 0); + const sumPromDevueltos = promediosConCalculos.reduce((sum, item) => sum + (item.promedio_Devueltos || 0), 0); + const sumPromVentas = promediosConCalculos.reduce((sum, item) => sum + (item.promedio_Ventas || 0), 0); + const sumPorcDevolucion = promediosConCalculos.reduce((sum, item) => sum + (item.porcentajeDevolucion || 0), 0); + setTotalesPromedios({ + cantDias: totalDiasPromedios, + promLlevados: sumPromLlevados / countPromedios, + promDevueltos: sumPromDevueltos / countPromedios, + promVentas: sumPromVentas / countPromedios, + porcentajeDevolucionGeneral: sumPorcDevolucion / countPromedios, + }); + } setReportData({ detalleSimple: detalleConCalculos, promediosPorDia: promediosConCalculos });