🔧 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->0se 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) * Yhace que la lógica sea errónea. - Solución Inmediata: Proporciónale la clase
TrabajoCompuestoCalculatorque 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 usadoubleodecimal.
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