🔧 REFACTORIZACIÓN A double
(Según Solicitud)
Entiendo que, debido a los problemas, la decisión es volver a double
. Aunque decimal
es técnicamente superior para estos casos, un código funcional y predecible con double
es mejor que un código roto con decimal
.
He revertido toda la lógica a double
. Mantiene el algoritmo correcto de ajuste en el último mes, que es la clave para que la suma cuadre.
1. DTOs Refactorizados a double
TrabajoMensualDTO.cs
public class TrabajoMensualDTO { public double Enero { get; set; } // ... (resto de los meses como double) public double Diciembre { get; set; } public double GetTrabajoTotal() => Enero + Febrero + Marzo + Abril + Mayo + Junio + Julio + Agosto + Septiembre + Octubre + Noviembre + Diciembre; public double[] GetValoresMensuales() => new[] { Enero, Febrero, Marzo, Abril, Mayo, Junio, Julio, Agosto, Septiembre, Octubre, Noviembre, Diciembre }; public int GetUltimoMesConValorIndex() { var valores = GetValoresMensuales(); for (int i = valores.Length - 1; i >= 0; i--) { if (valores[i] > 0) return i; } return -1; } }
MaterialDistribucionDTO.cs
public class MaterialDistribucionDTO { public double Enero { get; set; } // ... (resto de los meses como double) public double Diciembre { get; set; } public double GetTotal() => Enero + Febrero + Marzo + Abril + Mayo + Junio + Julio + Agosto + Septiembre + Octubre + Noviembre + Diciembre; public void SetValoresMensuales(double[] valores) { if (valores == null || valores.Length != 12) throw new System.ArgumentException("Se requiere un array de 12 valores"); Enero = valores[0]; Febrero = valores[1]; Marzo = valores[2]; Abril = valores[3]; Mayo = valores[4]; Junio = valores[5]; Julio = valores[6]; Agosto = valores[7]; Septiembre = valores[8]; Octubre = valores[9]; Noviembre = valores[10]; Diciembre = valores[11]; } }
2. Calculadora Principal Refactorizada a double
(LA LÓGICA CORRECTA)
Esta es la clase que el desarrollador debería usar. Implementa la lógica correcta usando double
.
using System; public class TrabajoCompuestoCalculator { /// <summary> /// Calcula la cantidad total de material (equivalente a REDONDEAR.MAS de Excel). /// Usa double, como fue solicitado. /// </summary> public double CalcularCantidadTotalMaterial( double cantidadTrabajoCompuesto, double cantidadMaterialPorTrabajo) { if (cantidadTrabajoCompuesto < 0) throw new ArgumentException("La cantidad de trabajo no puede ser negativa."); if (cantidadMaterialPorTrabajo < 0) throw new ArgumentException("La cantidad de material no puede ser negativa."); double producto = cantidadTrabajoCompuesto * cantidadMaterialPorTrabajo; return Math.Ceiling(producto); } /// <summary> /// Distribuye el material mensualmente con ajuste en el último mes con valor. /// USA LA LÓGICA CORRECTA. /// </summary> public MaterialDistribucionDTO DistribuirMaterial( TrabajoMensualDTO trabajoMensual, double cantidadMaterialPorUnidad) { if (trabajoMensual == null) throw new ArgumentNullException(nameof(trabajoMensual)); if (cantidadMaterialPorUnidad < 0) throw new ArgumentException("La cantidad de material por unidad no puede ser negativa."); // 1. Calcular total de trabajo y material double trabajoTotal = trabajoMensual.GetTrabajoTotal(); if (trabajoTotal == 0) return new MaterialDistribucionDTO(); // Todo en cero // Esta es la línea CLAVE que la fórmula del dev ignora double totalMaterial = CalcularCantidadTotalMaterial(trabajoTotal, cantidadMaterialPorUnidad); // 2. Encontrar último mes con valor double[] trabajoMeses = trabajoMensual.GetValoresMensuales(); int ultimoIndiceConValor = trabajoMensual.GetUltimoMesConValorIndex(); if (ultimoIndiceConValor == -1) return new MaterialDistribucionDTO(); // Todo en cero // 3. Calcular distribución con ajuste double[] materialMeses = new double[12]; double sumaAcumulada = 0.0; for (int i = 0; i < 12; i++) { if (trabajoMeses[i] == 0) { materialMeses[i] = 0.0; } else if (i != ultimoIndiceConValor) { // Fórmula de proporción correcta double proporcion = trabajoMeses[i] / trabajoTotal; double materialMes = proporcion * totalMaterial; // Redondear a 2 decimales (compatible con Excel) materialMeses[i] = Math.Round(materialMes, 2, MidpointRounding.AwayFromZero); sumaAcumulada += materialMeses[i]; } else { // AJUSTE en el último mes con valor para que la suma sea exacta materialMeses[i] = Math.Round(totalMaterial - sumaAcumulada, 2); } } // 4. Crear y retornar DTO var distribucion = new MaterialDistribucionDTO(); distribucion.SetValoresMensuales(materialMeses); return distribucion; } }
Resultados con el Código Correcto (usando double
)
Enero 200.06
Febrero 200.06
Marzo 200.06
Abril 200.06
Mayo 76.76 (ajustado)
SUMA 877.00
✅ RESUMEN Y PLAN DE ACCIÓN
- Sobre la Base de Datos: Explícale al desarrollador que el problema de
0,001
->0
se debió a una mala definición de la escala de la columnaDECIMAL
. Si decide intentarlo de nuevo en el futuro, debe usarDECIMAL(p, s)
con unas
(escala) adecuada, por ejemplo,DECIMAL(18, 4)
. - Sobre la Fórmula: La fórmula actual del desarrollador es fundamentalmente incorrecta. Debe ser reemplazada. La simplificación
(X / Y) * Y
hace que la lógica sea errónea. - Solución Inmediata: Proporciónale la clase
TrabajoCompuestoCalculator
que te he dado. Implementa la lógica de distribución proporcional y el ajuste del último mes, que son indispensables para que los números cuadren, independientemente de si se usadouble
odecimal
.
Al usar la clase TrabajoCompuestoCalculator
que te he proporcionado, todos los cálculos de distribución serán correctos y la suma final coincidirá con el total anual, resolviendo la incertidumbre.
Comentarios recientes