🔧 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

  1. 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 columna DECIMAL. Si decide intentarlo de nuevo en el futuro, debe usar DECIMAL(p, s) con una s (escala) adecuada, por ejemplo, DECIMAL(18, 4).
  2. 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.
  3. 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 usa double o decimal.

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.