<< Click to Display Table of Contents >> Parcelador e Arredondador |
![]() ![]() ![]() |
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();
}
}