Parcelador e Arredondador

<< Click to Display Table of Contents >>

Navigation:  Outros >

Parcelador e Arredondador

Previous pageReturn to chapter overviewNext page

Problema

 

Quando tem que fazer parcelamento de valores, os valores sempre ficam quebrados. Por exemplo:

 

Valor: R$ 118,64 = 3x R$  39,55
Valor: R$ 671,38 = 4x R$ 167,85
Valor: R$ 545,59 = 5x R$ 109,12
 

Mas e se você quiser um parcelmento mais bonito? Por exemplo:

 

Valor Total   R$ 118,64
Parcela 1/3    R$ 40,00
Parcela 2/3    R$ 40,00
Parcela 3/3    R$ 38,64
------------------------
Valor Total   R$ 671,38
Parcela 1/4   R$ 165,00
Parcela 2/4   R$ 165,00
Parcela 3/4   R$ 165,00
Parcela 4/4   R$ 176,38
------------------------
Valor Total   R$ 545,59
Parcela 1/5   R$ 110,00
Parcela 2/5   R$ 110,00
Parcela 3/5   R$ 110,00
Parcela 4/5   R$ 110,00
Parcela 5/5   R$ 105,59

 

Use o Parcelador!

Ele faz um rateamento, deixa as parcelas com final 0,00 ou 5,00 e deixa o valor quebrado apenas para a última parcela

 

Parcelador.cs

 

    class Parcelador
    {
        private const int MAX_PARCELAS = 12;
 
        // retorna o numero logo antes da vírgula (ou ponto)
        // 123.45 = 3      9.87 = 9      1002.01 = 2
        private static int GetDigitoUnitario(decimal valor)
        {
            if (valor < 1)
                return 0;
 
            string st = (Math.Truncate(valor)).ToString();
 
            // ultimo caracter em inteiro
            return Convert.ToInt16(st.Substring(st.Length - 1, 1));
        }
 
        // decimal[] parcelas = CalcularParcelas(valor, nParcelas);
        public static decimal[] CalcularParcelas(decimal valorTotal, int nParcelas)
        {
            // cria retorno 
            decimal[] parcelas = new decimal[MAX_PARCELAS];
 
            // estourou parcelas... cria exceção
            if (nParcelas > MAX_PARCELAS)
                throw new Exception("O número máximo de parcelas é " + MAX_PARCELAS);
 
            // calcula valor da parcela e centavos
            decimal vParcela = valorTotal / nParcelas;
            decimal vCentavos = (vParcela - Math.Truncate(vParcela)) * nParcelas;
            vParcela = Math.Truncate(vParcela);
 
            // se a parcela for maior que 30 reais faz o arredondamento na casa decimal
            // assim, se o dígito antes da vírgula for 1 ou 2 ele arrendonda para baixo  (ex: 11,00 e 12,00  puxa pra 10,00)
            // se o dígito for 6 ou 7 ele arredonda para 5.  (ex: 16,00 ou 17,00 puxa para 15,00)
            // se o dígito for 3 ou 4 arredonda para 5. (ex: 13,00 ou 14,00 puxa para 15,00)
            // se o dígito for 8 ou 9 arredonda para 10. (ex: 18,00 ou 19,00 puxa para 20,00)
            if (vParcela > 30)
            {
                int digito = GetDigitoUnitario(vParcela);
                switch (digito)
                {
                    case 1:
                    case 2: vParcela -= digito; break;
                    case 6:
                    case 7: vParcela -= (digito - 5); break;
                    case 3:
                    case 4: vParcela += (5 - digito); break;
                    case 8:
                    case 9: vParcela += (10 - digito); break;
                }
            }
 
            // a diferença será adicional na última parcela (quer seja positiva ou negativa)
            decimal Diferenca = valorTotal - (vParcela * nParcelas);
 
            // seta o valor das parcelas
            for (int i = 0; i < nParcelas; i++)
                parcelas[i] = vParcela;
 
            // coloca a diferença na última
            parcelas[nParcelas - 1] = parcelas[nParcelas - 1] + Diferenca;  // parcelas[nParcelas-1] + vCentavos;
 
            return parcelas;
        }
    }

 

Como usar

 

    class Program
    {
        // essa rotina é chama 1x para cada teste
        static void Mostra(decimal valor, int nParcelas)
        {
            // vamos calcular as parcelas
            decimal[] parcelas = Parcelador.CalcularParcelas(valor, nParcelas);
            decimal total = 0;
 
            // mostra o total no output
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(string.Format("Valor Total: {0:C}", valor));
            Console.ForegroundColor = ConsoleColor.White;
 
            // soma as parcelas e exibe - vamos ver se a soma bate com o argumento da função
            for (int i = 0; i < nParcelas; i++)
            {
                Console.Write(string.Format("Parcela {0}/{2}   {1:C}\n", i + 1, parcelas[i], nParcelas));
                total += parcelas[i];
            }
            Console.WriteLine("------------------------------");
 
            // a soma não bate? --- ERRO!
            if (valor != total)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("  ERROR   ERROR   ERROR");
                Console.ReadLine();
            }
        }
 
        // usando para pegar um double randomico entre 2 valores
        static decimal NextDouble(Random rng, double min, double max)
        {
            return (decimal)(min + (rng.NextDouble() * (max - min)));
        }
 
        static void Main(string[] args)
        {
            const int QTD_TESTES = 100; // 1-10000
            const double VL_MINIMO = 10; // 1-9999
            const double VL_MAXIMO = 1500; // 1-9999
            const int MAX_PARCELAS = 11;  // 1-11  (tem um +2 ali em baixo, então na prática será 2-12)
 
            // vamos testar 
            Random random = new Random();
 
            for (int i = 0; i < QTD_TESTES; i++)
            {
                decimal valor = NextDouble(random, VL_MINIMO, VL_MAXIMO);
                Mostra(valor, random.Next(MAX_PARCELAS)+2);
            }
 
            Console.ReadLine();
        }
    }