Data, pro rate, valores

Prezados (as),

Gostaria de uma ajuda para resolução do seguinte problema com datas, pro rata e valores.

Tenho os seguintes dados iniciais:

DATA: 01/01/2021 A 10/05/2021)
VALOR MENSAL:R$ 27.587,68

Com estes dados iniciais tenho que realizar o seguinte:

A data deverá ser distribuida em períodos, no caso deverá ter 5 competências que são do mês janeiro/21 a maio/21.
Sendo que maio/21 existe um pro rata de 10 dias.

Após distribuir as datas e crias as competências tenho que atrelar o valor mensal de R$ 27.587,68 sendo que no mês de maio/21 existe um pro rata de 10 dias no valor de 9.185,89.

A Saída deverá ser assim:

1ª - 01/01/2021 A 31/01/2021 = 27.587,68 (MÊS COMERCIAL = 30 DIAS)
2ª - 01/02/2021 A 28/02/2021 = 27.587,68 (MÊS COMERCIAL = 30 DIAS)
3ª - 01/03/2021 A 31/03/2021 = 27.587,68 (MÊS COMERCIAL = 30 DIAS)
4ª - 01/04/2021 A 30/04/2021 = 27.587,68 (MÊS COMERCIAL = 30 DIAS)
5ª - 01/05/2021 A 10/05/2021 = 9.185,89 (AQUI EXISTE PRO RATA DE 10 DIAS)

Total do período: R$ 119.536,61

Observação: O código deverá ser genérico podendo realizar o procedimento com outras datas e valores.

Eu pensei numa solução assim:

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;

public class Main {
  private static final int COMERCIAL_MONTH = 30;

  private static BigDecimal getDiffOrZero(final int begin, final int end) {
    final int value = end - begin - 1;
    return value < 0 ? BigDecimal.ZERO : BigDecimal.valueOf(value);
  }

  protected static void calculate(final int pos, final LocalDate begin, final LocalDate end, final BigDecimal base) {
    final BigDecimal temp = getDiffOrZero(begin.getMonthValue(), end.getMonthValue());
    final BigDecimal part = base.divide(BigDecimal.valueOf(COMERCIAL_MONTH), 6, RoundingMode.DOWN);
    final BigDecimal v1 = base.multiply(temp);
    final BigDecimal v2 = part.multiply(BigDecimal.valueOf(COMERCIAL_MONTH - begin.getDayOfMonth() + 1));
    final BigDecimal v3 = part.multiply(BigDecimal.valueOf(end.getDayOfMonth()));
    final BigDecimal total = v1.add(v2).add(v3).setScale(2, RoundingMode.DOWN);
    System.out.printf("[%d] %9.2f + %9.2f + %9.2f = %9.2f\n", pos, v1, v2, v3, total);
  }

  public static void main(String... args) {
    final BigDecimal base = new BigDecimal("27587.68");

    calculate(1, LocalDate.of(2021, 1, 1), LocalDate.of(2021, 5, 10), base);
    calculate(2, LocalDate.of(2021, 4, 1), LocalDate.of(2021, 5, 10), base);
    calculate(3, LocalDate.of(2021, 5, 1), LocalDate.of(2021, 5, 10), base);
    calculate(4, LocalDate.of(2021, 1, 11), LocalDate.of(2021, 5, 10), base);
    calculate(5, LocalDate.of(2021, 1, 31), LocalDate.of(2021, 5, 10), base);
    calculate(6, LocalDate.of(2021, 1, 1), LocalDate.of(2021, 5, 1), base);
    calculate(7, LocalDate.of(2021, 1, 1), LocalDate.of(2021, 5, 15), base);
    calculate(8, LocalDate.of(2021, 1, 1), LocalDate.of(2021, 5, 31), base);
  }
}

Eu fiz alguns testes, mas não sei se vai funcionar para todas as possiveis combinações de datas.

De qualquer forma acho que pode servir pra lhe dar uma ideia.

Fala wldomiciano!
Obrigado pela ajuda!
Mas não entendi o seu código.
Quando executei ficou assim:
run:
[1] 82763,04 + 27587,68 + 9195,89 = 119546,61
[2] 0,00 + 27587,68 + 9195,89 = 36783,57
[3] 0,00 + 27587,68 + 9195,89 = 36783,57
[4] 82763,04 + 18391,79 + 9195,89 = 110350,71
[5] 82763,04 + 0,00 + 9195,89 = 91958,93
[6] 82763,04 + 27587,68 + 919,59 = 111270,30
[7] 82763,04 + 27587,68 + 13793,84 = 124144,55
[8] 82763,04 + 27587,68 + 28507,27 = 138857,98

Eu preciso que seja assim, onde a 1ª parecela refere se a data de 01/01/2021 A 31/01/2021 que tem o valor de 27.587,68.

E assim sucessivamente até o fin do periodo que é 5ª parcela - 01/05/2021 A 10/05/2021 = 9.185,89

1ª - 01/01/2021 A 31/01/2021 = 27.587,68
2ª - 01/02/2021 A 28/02/2021 = 27.587,68
3ª - 01/03/2021 A 31/03/2021 = 27.587,68
4ª - 01/04/2021 A 30/04/2021 = 27.587,68
5ª - 01/05/2021 A 10/05/2021 = 9.185,89

Valor total = 119.536,61

Fiz uma modificações, tirei os BigDecimal e estou usei double pra ficar mais facil de ler, veja:

import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
  private static final int COMERCIAL_MONTH = 30;

  private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/uuuu");

  private static int getDiffOrZero(final int begin, final int end) {
    final int value = end - begin - 1;
    return value < 0 ? 0 : value;
  }

  private static String format(final LocalDate date) {
    return date.format(formatter);
  }

  private static LocalDate lastDay(final LocalDate date) {
    return date.withDayOfMonth(date.lengthOfMonth());
  }

  protected static void calculate(final int pos, final LocalDate begin, final LocalDate end, final double base) {
    final int temp = getDiffOrZero(begin.getMonthValue(), end.getMonthValue());
    final double part = base / COMERCIAL_MONTH;
    final double v1 = base * temp;
    final double v2 = part * (COMERCIAL_MONTH - begin.getDayOfMonth() + 1);
    final double v3 = part * end.getDayOfMonth();

    final NumberFormat nf = NumberFormat.getInstance(Locale.forLanguageTag("pt-BR"));

    nf.setMaximumFractionDigits(2);

    System.out.printf("1ª - %s A %s = %s\n", format(begin), format(lastDay(begin)), nf.format(v2));

    for (int i = 0, len = temp; i < len; ++i) {
      final LocalDate a = begin.plusMonths(i + 1).withDayOfMonth(1);
      final LocalDate b = lastDay(a);
      System.out.printf("%dª - %s A %s = %s\n", i + 2, format(a), format(b), nf.format(base));
    }

    System.out.printf("%dª - %s A %s = %s\n", temp + 2, format(end.withDayOfMonth(1)), format(end), nf.format(v3));

    System.out.println("\nValor total = " + nf.format(v1 + v2 + v3));
  }

  public static void main(String... args) {
    calculate(1, LocalDate.of(2021, 1, 1), LocalDate.of(2021, 5, 10), 27587.68);
  }
}
2 curtidas

Fala wldomiciano!
Muito obrigado pela ajuda!
Agora entendi!!!

Eu tinha conseguido fazer apenas desse jeito:

import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.Period;

public class DATA3
{
public static void main(String[] args) throws Exception
{
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(“dd/MM/uuuu”);
LocalDate localDateAntigo = LocalDate.parse(“05/01/2021”, formatter);
LocalDate localDateNovo = LocalDate.parse(“10/05/2021”, formatter);

Period periodo = Period.between(localDateAntigo, localDateNovo);
int dia = periodo.getDays()+1;
System.out.println(periodo.getMonths() + " Meses " + dia + " Dias");
System.out.println();

double v3= 27587.68;
double v1 = 30.0;
double v2 = (periodo.getDays())+1;
double re=v3/v1;
double v4= re*v2;
DecimalFormat df = new DecimalFormat("#,##0.00");
String dx = df.format(v4);

double soma=0;
for (int e = 1; e <= periodo.toTotalMonths(); e++)
{
int i=1;
if(e==1)
{
i=0;
}

System.out.print("Parcela " + e + “: “);
localDateAntigo = localDateAntigo.plusMonths(i);
System.out.print(””+ localDateAntigo.format(formatter));

double de=re*30;
DecimalFormat di = new DecimalFormat("#,##0.00");
String da = di.format(de);
System.out.println(" - " + da);

double mu=de*periodo.toTotalMonths();
soma = mu + v4;

}

long parcela=0;
if(periodo.getDays()> 0)
{
parcela = periodo.toTotalMonths()+1;
System.out.print(“Parcela " + parcela+ “: “);
System.out.print(””+ localDateNovo.format(formatter));
System.out.println(” - " + dx);
}

DecimalFormat se = new DecimalFormat("#,##0.00");
String so = se.format(soma);
System.out.println("Valor total: " + so);
}
}

1 curtida

Uma dica, você pode usar a classe java.time.temporal.TemporalAdjusters, que já tem isso pronto:

private static LocalDate lastDay(final LocalDate date) {
    return date.with(TemporalAdjusters.lastDayOfMonth());
}

Ou, com import static para deixar mais legível (na minha opinião):

import static java.time.temporal.TemporalAdjusters.lastDayOfMonth;

private static LocalDate lastDay(final LocalDate date) {
    return date.with(lastDayOfMonth());
}

E o método getDiffOrZero não vai funcionar se as datas estiverem em anos diferentes (por exemplo, se for de novembro de 2020 até maio de 2021). Pra mim não ficou claro se todas as datas estão no mesmo ano, mas se for pra aceitar quaisquer datas, melhor fazer algo assim:

private static int getDiffOrZero(LocalDate begin, LocalDate end) {
    final int value = (int) ChronoUnit.MONTHS.between(begin, end) - 1;
    return value < 0 ? 0 : value;
}
2 curtidas

Vale lembrar que para valores monetários é melhor usar BigDecimal, já que float e double possuem problemas de imprecisão: https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems

Fala hugokotsubo!
Obrigado pela ajuda!
Tem razão, quando coloco as datas em anos diferentes (por exemplo, (2019, 1, 1) A (2020, 4, 30) dá erro.
A saída:
1ª - 01/01/2019 A 31/01/2019 = 27.587,68
2ª - 01/02/2019 A 28/02/2019 = 27.587,68
3ª - 01/03/2019 A 31/03/2019 = 27.587,68
4ª - 01/04/2020 A 30/04/2020 = 27.587,68
Valor total = 110.350,72

Mas, tentei atualizar com o método getDiffOrZero que você informou e não deu certo.

O que exatamente não deu certo?

Dica: quando você diz que algo não funcionou, tem que dar mais detalhes (o código, os dados usados, qual deveria ser o resultado e o que aconteceu - resultado errado, erro, etc). Senão não tem como a gente adivinhar… :slight_smile:

Enfim, aqui eu acho que funcionou. Ao mudar o método getDiffOrZero, você tem que passar as datas em vez de só o mês:

private static int getDiffOrZero(LocalDate begin, LocalDate end) {
    final int value = (int) ChronoUnit.MONTHS.between(begin, end) - 1;
    return value < 0 ? 0 : value;
}

protected static void calculate(final int pos, final LocalDate begin, final LocalDate end, final double base) {
    // *** AQUI *** Passe as datas em vez de só o mês
    final int temp = getDiffOrZero(begin, end);

    // o resto é igual ao código do @wldomiciano ...
}

Testei com:

calculate(1, LocalDate.of(2019, 1, 1), LocalDate.of(2020, 4, 30), 27587.68);

E a saída foi:

1ª - 2019-01-01 A 2019-01-31 = 27.587,68
2ª - 2019-02-01 A 2019-02-28 = 27.587,68
3ª - 2019-03-01 A 2019-03-31 = 27.587,68
4ª - 2019-04-01 A 2019-04-30 = 27.587,68
5ª - 2019-05-01 A 2019-05-31 = 27.587,68
6ª - 2019-06-01 A 2019-06-30 = 27.587,68
7ª - 2019-07-01 A 2019-07-31 = 27.587,68
8ª - 2019-08-01 A 2019-08-31 = 27.587,68
9ª - 2019-09-01 A 2019-09-30 = 27.587,68
10ª - 2019-10-01 A 2019-10-31 = 27.587,68
11ª - 2019-11-01 A 2019-11-30 = 27.587,68
12ª - 2019-12-01 A 2019-12-31 = 27.587,68
13ª - 2020-01-01 A 2020-01-31 = 27.587,68
14ª - 2020-02-01 A 2020-02-29 = 27.587,68
15ª - 2020-03-01 A 2020-03-31 = 27.587,68
16ª - 2020-04-01 A 2020-04-30 = 27.587,68

Valor total = 441.402,88
1 curtida

Obrigado, hugokotsubo!
É isso mesmo!
Estava faltando acertar a variável temp.
Obrigado!