Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SU-503 : Tasa Maxima - Maximum legal rate #1374

Merged
merged 4 commits into from
Dec 12, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -381,173 +381,67 @@ private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTe
}
////////////

if (!isMidTermRescheduling || isSameInterestRates) {
LocalDate nextDates = rescheduleFromDate;
LocalDate startDate = null;
BigDecimal currentInterst = BigDecimal.ZERO;
if (!loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment().isEmpty()) {
List<LoanTermVariationsData> list = loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment();
ListIterator<LoanTermVariationsData> iterator = list.listIterator();
int prevIndex = 0;

while (iterator.hasNext()) {
int index = iterator.nextIndex();
int nextIndex = prevIndex + 1;
LoanTermVariationsData midInterst = iterator.next();
LocalDate currentDates = midInterst.getTermVariationApplicableFrom();
currentInterst = midInterst.getDecimalValue();
if (index == list.size() - 1) {
nextIndex = index;
}
nextDates = list.get(nextIndex).getTermVariationApplicableFrom();
if (iterator.hasNext()) {
nextDates = list.get(index + 1).getTermVariationApplicableFrom();
currentInterst = list.get(index + 1).getDecimalValue();
}
if (startDate == null) {
startDate = currentDates;

} else {
if (!iterator.hasPrevious()) {
startDate = currentDates;
} else {
startDate = list.get(index - 1).getTermVariationApplicableFrom();
currentInterst = list.get(index - 1).getDecimalValue();
}
}
prevIndex = nextIndex;
}
}

if (nextDates != null && nextDates.isAfter(periodStartDateApplicableForInterest) && nextDates.isBefore(periodEndDate)
&& currentInterst.compareTo(BigDecimal.ZERO) == 1) {
BigDecimal totalMidPeriodInterestRates = BigDecimal.ZERO;

loanApplicationTerms.setAnnualNominalInterestRate(currentInterst);
PrincipalInterest midPrincipalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(calculator,
interestCalculationGraceOnRepaymentPeriodFractionParam, totalCumulativePrincipal, totalCumulativeInterest,
totalInterestDueForLoan, cumulatingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms,
periodNumber, mc, principalVariation, compoundingMap, periodStartDateApplicableForInterest, nextDates,
interestRates, accruedInterestByAdvancePmt);
Money midPeriodInterestRate = midPrincipalInterestForThisPeriod.interest().add(totalMidPeriodInterestRates);

final Money midPeriodPrincipal = midPrincipalInterestForThisPeriod.principal();
Money interestPrincipal = midPeriodInterestRate.add(midPrincipalInterestForThisPeriod.principal());

loanApplicationTerms.setAnnualNominalInterestRate(annualNominalInterestRate);
PrincipalInterest endPrincipalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(calculator,
interestCalculationGraceOnRepaymentPeriodFractionParam, totalCumulativePrincipal, totalCumulativeInterest,
totalInterestDueForLoan, cumulatingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms,
periodNumber, mc, principalVariation, compoundingMap, nextDates, periodEndDate, interestRates,
accruedInterestByAdvancePmt);

final Money periodEndInterestRate = endPrincipalInterestForThisPeriod.interest();
final Money totalInterestRate = midPeriodInterestRate.add(periodEndInterestRate);
final Money periodEndPrincipal = endPrincipalInterestForThisPeriod.principal();

Money totalPrincipal;
if (midPeriodPrincipal.isEqualTo(periodEndPrincipal)) {
totalPrincipal = midPeriodPrincipal;
} else {
totalPrincipal = interestPrincipal.minus(totalInterestRate);
}
principalInterestForThisPeriod = new PrincipalInterest(totalPrincipal, totalInterestRate,
endPrincipalInterestForThisPeriod.interestPaymentDueToGrace());
} else {

principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(calculator,
interestCalculationGraceOnRepaymentPeriodFractionParam, totalCumulativePrincipal, totalCumulativeInterest,
totalInterestDueForLoan, cumulatingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms,
periodNumber, mc, principalVariation, compoundingMap, periodStartDateApplicableForInterest, periodEndDate,
interestRates, accruedInterestByAdvancePmt);
}
if (!isMidTermRescheduling) {

principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(calculator,
interestCalculationGraceOnRepaymentPeriodFractionParam, totalCumulativePrincipal, totalCumulativeInterest,
totalInterestDueForLoan, cumulatingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms,
periodNumber, mc, principalVariation, compoundingMap, periodStartDateApplicableForInterest, periodEndDate,
interestRates, accruedInterestByAdvancePmt);
} else {
Money totalMidPeriodInterestRates = Money.zero(currency);
Money totalMidPrincipal = Money.zero(currency);
LocalDate nextDates = rescheduleFromDate;
if (!loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment().isEmpty()) {
LocalDate startDate = null;
BigDecimal currentInterst = null;
List<LoanTermVariationsData> list = loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment();
ListIterator<LoanTermVariationsData> iterator = list.listIterator();
int prevIndex = 0;
while (iterator.hasNext()) {
int index = iterator.nextIndex();
int nextIndex = prevIndex + 1;
LoanTermVariationsData midInterst = iterator.next();
currentInterst = midInterst.getDecimalValue();
LocalDate currentDates = midInterst.getTermVariationApplicableFrom();

if (index == list.size() - 1) {
nextIndex = index;
}
nextDates = list.get(nextIndex).getTermVariationApplicableFrom();

if (!currentInterst.equals(interestRatePerPeriod) && !currentInterst.equals(annualNominalInterestRate)) {
List<Map<String, Object>> loanTermVariationsData = processLoanTermVariations(list,
periodStartDateApplicableForInterest);
for (Map<String, Object> entry : loanTermVariationsData) {

if (iterator.hasNext()) {
nextDates = list.get(index + 1).getTermVariationApplicableFrom();
}
if (startDate == null) {
startDate = currentDates;
rescheduleFromDate = currentDates;
} else {
if (!iterator.hasPrevious()) {
startDate = currentDates;
} else {
startDate = list.get(index - 1).getTermVariationApplicableFrom();
}
}

loanApplicationTerms.setAnnualNominalInterestRate(currentInterst);
final PrincipalInterest midPrincipalInterestForThisPeriods = calculatePrincipalInterestComponentsForPeriod(
calculator, interestCalculationGraceOnRepaymentPeriodFractionParam, totalCumulativePrincipal,
totalCumulativeInterest, totalInterestDueForLoan, cumulatingInterestPaymentDueToGrace,
outstandingBalance, loanApplicationTerms, periodNumber, mc, principalVariation, compoundingMap,
startDate, nextDates, interestRates);
Money midPeriodInterestRates = midPrincipalInterestForThisPeriods.interest();
totalMidPeriodInterestRates = midPeriodInterestRates;
startDate = (LocalDate) entry.get("startDate");
nextDates = (LocalDate) entry.get("nextDate");
currentInterst = (BigDecimal) entry.get("currentInterst");

loanApplicationTerms.setAnnualNominalInterestRate(currentInterst);
if (startDate.equals(periodStartDate)) {
loanApplicationTerms.setAnnualNominalInterestRate(interestRatePerPeriod);
}
prevIndex = nextIndex;
final PrincipalInterest midPrincipalInterestForThisPeriods = calculatePrincipalInterestComponentsForPeriod(
calculator, interestCalculationGraceOnRepaymentPeriodFractionParam, totalCumulativePrincipal,
totalCumulativeInterest, totalInterestDueForLoan, cumulatingInterestPaymentDueToGrace, outstandingBalance,
loanApplicationTerms, periodNumber, mc, principalVariation, compoundingMap, startDate, nextDates,
interestRates);

Money midPeriodInterestRates = midPrincipalInterestForThisPeriods.interest();

totalMidPeriodInterestRates = totalMidPeriodInterestRates.add(midPeriodInterestRates);
totalMidPrincipal = midPrincipalInterestForThisPeriods.principal();
}
}

/*
* Start mind interest calculation
*/
loanApplicationTerms.setAnnualNominalInterestRate(interestRatePerPeriod);
final PrincipalInterest midPrincipalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(calculator,
interestCalculationGraceOnRepaymentPeriodFractionParam, totalCumulativePrincipal, totalCumulativeInterest,
totalInterestDueForLoan, cumulatingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms,
periodNumber, mc, principalVariation, compoundingMap, periodStartDateApplicableForInterest, rescheduleFromDate,
interestRates);

Money midPeriodInterestRate = midPrincipalInterestForThisPeriod.interest().add(totalMidPeriodInterestRates);

final Money midPeriodPrincipal = midPrincipalInterestForThisPeriod.principal();
Money interestPrincipal = midPeriodInterestRate.add(midPrincipalInterestForThisPeriod.principal());

/*
* End mind interest calculation Start End interest Calculation
*/
rescheduleFromDate = nextDates;

loanApplicationTerms.setAnnualNominalInterestRate(annualNominalInterestRate);

PrincipalInterest endPrincipalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(calculator,
interestCalculationGraceOnRepaymentPeriodFractionParam, totalCumulativePrincipal, totalCumulativeInterest,
totalInterestDueForLoan, cumulatingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms,
periodNumber, mc, principalVariation, compoundingMap, rescheduleFromDate, periodEndDate, interestRates,
accruedInterestByAdvancePmt);

final Money periodEndInterestRate = endPrincipalInterestForThisPeriod.interest();
final Money totalInterestRate = midPeriodInterestRate.add(periodEndInterestRate);
final Money totalInterestRate = totalMidPeriodInterestRates.add(periodEndInterestRate);
final Money periodEndPrincipal = endPrincipalInterestForThisPeriod.principal();
Money totalPrincipal;
if (midPeriodPrincipal.isEqualTo(periodEndPrincipal)) {
totalPrincipal = midPeriodPrincipal;
} else {
totalPrincipal = interestPrincipal.minus(totalInterestRate);

Money totalPrincipal = totalMidPrincipal.minus(totalInterestRate);
if (totalPrincipal.equals(periodEndPrincipal)) {
totalPrincipal = periodEndPrincipal;
}

/*
Expand Down Expand Up @@ -695,6 +589,58 @@ private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTe
scheduleParams.getTotalRepaymentExpected().getAmount(), totalOutstanding);
}

private List<Map<String, Object>> processLoanTermVariations(List<LoanTermVariationsData> list, LocalDate periodeStartDate) {
List<Map<String, Object>> resultList = new ArrayList<>();
Set<String> uniquePairs = new HashSet<>(); // To keep track of unique startDate and nextDate pairs
ListIterator<LoanTermVariationsData> iterator = list.listIterator();

LocalDate startDate = null;
LocalDate nextDates;
BigDecimal currentInterst;
BigDecimal interest = BigDecimal.ZERO;

while (iterator.hasNext()) {
int index = iterator.nextIndex();
LoanTermVariationsData midInterst = iterator.next();
currentInterst = midInterst.getDecimalValue();
LocalDate currentDates = midInterst.getTermVariationApplicableFrom();

int nextIndex = (index == list.size() - 1) ? index : index + 1;
nextDates = list.get(nextIndex).getTermVariationApplicableFrom();

if (startDate == null) {
startDate = periodeStartDate;
interest = currentInterst;
} else {
if (!iterator.hasPrevious()) {
startDate = currentDates;
interest = currentInterst;
} else {
interest = list.get(index - 1).getDecimalValue();
startDate = list.get(index - 1).getTermVariationApplicableFrom();
}
}

// Create a unique key based on startDate and nextDate
String uniqueKey = startDate.toString() + "-" + nextDates.toString();

// Save only if the pair is unique
if (!uniquePairs.contains(uniqueKey)) {
uniquePairs.add(uniqueKey);

// Store currentInterst, startDate, and nextDates in a map
Map<String, Object> result = new HashMap<>();
result.put("currentInterst", interest);
result.put("startDate", startDate);
result.put("nextDate", nextDates);

// Add the map to the result list
resultList.add(result);
}
}
return resultList;
}

private void updateIndividualChargeAmountForInstallment(Set<LoanCharge> loanCharges, LoanScheduleModelPeriod installment) {

Collection<LoanCharge> mandatoryInsuranceCharges = loanCharges.stream().filter(LoanCharge::isMandatoryInsurance).toList();
Expand Down
Loading