feat(Reportes): Ajusta cálculo de promedios en Listado de Distribución
All checks were successful
Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 3m58s

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.
This commit is contained in:
2025-12-05 12:25:18 -03:00
parent 35e8d803b9
commit fc27b4b43e
4 changed files with 44 additions and 40 deletions

View File

@@ -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<string, int> { { "Lunes", 1 }, { "Martes", 2 }, { "Miércoles", 3 }, { "Jueves", 4 }, { "Viernes", 5 }, { "Sábado", 6 }, { "Domingo", 7 }};
var dayOrder = new Dictionary<string, int> { { "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));
}
});
}

View File

@@ -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)
};
}
}

View File

@@ -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()

View File

@@ -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 });