FINERACT-2114: Interest rate modification
This commit is contained in:
committed by
Adam Saghy
parent
80f4627623
commit
1286b1e72f
-4
@@ -1890,8 +1890,4 @@ public final class LoanApplicationTerms {
|
|||||||
this.variationDays += daysToAdd;
|
this.variationDays += daysToAdd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalDate getLoanEndDate() {
|
|
||||||
return loanEndDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-1
@@ -331,7 +331,10 @@ public class LoanRescheduleRequestDataValidator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (rescheduleFromDate != null) {
|
if (rescheduleFromDate != null) {
|
||||||
installment = loan.getRepaymentScheduleInstallment(rescheduleFromDate);
|
final boolean isProgressiveLoanSchedule = loan.getLoanProductRelatedDetail()
|
||||||
|
.getLoanScheduleType() == LoanScheduleType.PROGRESSIVE;
|
||||||
|
installment = isProgressiveLoanSchedule ? loan.getRelatedRepaymentScheduleInstallment(rescheduleFromDate)
|
||||||
|
: loan.getRepaymentScheduleInstallment(rescheduleFromDate);
|
||||||
|
|
||||||
if (installment == null) {
|
if (installment == null) {
|
||||||
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(
|
dataValidatorBuilder.reset().failWithCodeNoParameterAddedToErrorCode(
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public record ProgressiveLoanInterestRate(LocalDate effectiveFrom, LocalDate validFrom,
|
||||||
|
BigDecimal interestRate) implements Comparable<ProgressiveLoanInterestRate> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NotNull ProgressiveLoanInterestRate o) {
|
||||||
|
return this.effectiveFrom().compareTo(o.effectiveFrom());
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
-2
@@ -18,13 +18,40 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||||
|
|
||||||
public record ProgressiveLoanInterestScheduleModel(List<ProgressiveLoanInterestRepaymentModel> repayments,
|
public record ProgressiveLoanInterestScheduleModel(List<ProgressiveLoanInterestRepaymentModel> repayments, //
|
||||||
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, MathContext mc) {
|
List<ProgressiveLoanInterestRate> interestRates, //
|
||||||
|
LoanProductRelatedDetail loanProductRelatedDetail, //
|
||||||
|
Integer installmentAmountInMultiplesOf, //
|
||||||
|
MathContext mc) {
|
||||||
|
|
||||||
|
public ProgressiveLoanInterestScheduleModel(List<ProgressiveLoanInterestRepaymentModel> repayments,
|
||||||
|
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, MathContext mc) {
|
||||||
|
this(repayments, new ArrayList<>(1), loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addInterestRate(final LocalDate newInterestDueDate, final BigDecimal newInterestRate) {
|
||||||
|
interestRates.add(new ProgressiveLoanInterestRate(newInterestDueDate, newInterestDueDate.plusDays(1), newInterestRate));
|
||||||
|
interestRates.sort(Collections.reverseOrder());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getInterestRate(final LocalDate effectiveDate) {
|
||||||
|
return interestRates.isEmpty() ? loanProductRelatedDetail.getNominalInterestRatePerPeriod() : findInterestRate(effectiveDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal findInterestRate(final LocalDate effectiveDate) {
|
||||||
|
return interestRates.stream().filter(ir -> !ir.effectiveFrom().isAfter(effectiveDate))
|
||||||
|
.map(ProgressiveLoanInterestRate::interestRate).findFirst()
|
||||||
|
.orElse(loanProductRelatedDetail.getNominalInterestRatePerPeriod());
|
||||||
|
}
|
||||||
|
|
||||||
public int getLoanTermInDays() {
|
public int getLoanTermInDays() {
|
||||||
if (repayments.isEmpty()) {
|
if (repayments.isEmpty()) {
|
||||||
|
|||||||
+9
-1
@@ -75,7 +75,6 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
|||||||
if (lastDueDateVariation != null) {
|
if (lastDueDateVariation != null) {
|
||||||
loanEndDate = lastDueDateVariation.getDateValue();
|
loanEndDate = lastDueDateVariation.getDateValue();
|
||||||
}
|
}
|
||||||
loanApplicationTerms.updateLoanEndDate(loanEndDate);
|
|
||||||
|
|
||||||
// determine the total charges due at time of disbursement
|
// determine the total charges due at time of disbursement
|
||||||
final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges);
|
final BigDecimal chargesDueAtTimeOfDisbursement = deriveTotalChargesDueAtTimeOfDisbursement(loanCharges);
|
||||||
@@ -111,6 +110,15 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
|||||||
processDisbursements(loanApplicationTerms, scheduleParams, interestScheduleModel, periods, chargesDueAtTimeOfDisbursement);
|
processDisbursements(loanApplicationTerms, scheduleParams, interestScheduleModel, periods, chargesDueAtTimeOfDisbursement);
|
||||||
repaymentPeriod.setPeriodNumber(scheduleParams.getInstalmentNumber());
|
repaymentPeriod.setPeriodNumber(scheduleParams.getInstalmentNumber());
|
||||||
|
|
||||||
|
for (var interestRateChange : loanApplicationTerms.getLoanTermVariations().getInterestRateFromInstallment()) {
|
||||||
|
final LocalDate interestRateChangeEffectiveDate = interestRateChange.getTermVariationApplicableFrom().minusDays(1);
|
||||||
|
final BigDecimal newInterestRate = interestRateChange.getDecimalValue();
|
||||||
|
if (interestRateChangeEffectiveDate.isAfter(repaymentPeriod.getFromDate())
|
||||||
|
&& !interestRateChangeEffectiveDate.isAfter(repaymentPeriod.getDueDate())) {
|
||||||
|
emiCalculator.changeInterestRate(interestScheduleModel, interestRateChangeEffectiveDate, newInterestRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
emiCalculator.findInterestRepaymentPeriod(interestScheduleModel, repaymentPeriod.getDueDate())
|
emiCalculator.findInterestRepaymentPeriod(interestScheduleModel, repaymentPeriod.getDueDate())
|
||||||
.ifPresent(interestRepaymentPeriod -> {
|
.ifPresent(interestRepaymentPeriod -> {
|
||||||
final Money principalDue = interestRepaymentPeriod.getPrincipalDue();
|
final Money principalDue = interestRepaymentPeriod.getPrincipalDue();
|
||||||
|
|||||||
+4
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanproduct.calc;
|
package org.apache.fineract.portfolio.loanproduct.calc;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -42,6 +43,9 @@ public interface EMICalculator {
|
|||||||
|
|
||||||
void addDisbursement(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate disbursementDueDate, Money disbursedAmount);
|
void addDisbursement(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate disbursementDueDate, Money disbursedAmount);
|
||||||
|
|
||||||
|
void changeInterestRate(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate newInterestEffectiveDate,
|
||||||
|
BigDecimal newInterestRate);
|
||||||
|
|
||||||
ProgressiveLoanInterestScheduleModel makeScheduleModelDeepCopy(ProgressiveLoanInterestScheduleModel scheduleModel);
|
ProgressiveLoanInterestScheduleModel makeScheduleModelDeepCopy(ProgressiveLoanInterestScheduleModel scheduleModel);
|
||||||
|
|
||||||
ProgressiveLoanInterestScheduleModel makeScheduleModelDeepCopy(ProgressiveLoanInterestScheduleModel scheduleModel,
|
ProgressiveLoanInterestScheduleModel makeScheduleModelDeepCopy(ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||||
|
|||||||
+58
-1
@@ -97,6 +97,27 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<ProgressiveLoanInterestRepaymentInterestPeriod> findInterestPeriodForInterestChange(
|
||||||
|
final ProgressiveLoanInterestRepaymentModel repaymentPeriod, final LocalDate interestRateChangeEffectiveDate) {
|
||||||
|
if (repaymentPeriod == null || interestRateChangeEffectiveDate == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return repaymentPeriod.getInterestPeriods().stream()//
|
||||||
|
.filter(interestPeriod -> interestRateChangeEffectiveDate.isEqual(interestPeriod.getFromDate()))//
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<ProgressiveLoanInterestRepaymentModel> findInterestRepaymentPeriodForInterestChange(
|
||||||
|
final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate interestChangeEffectiveDate) {
|
||||||
|
if (scheduleModel == null || interestChangeEffectiveDate == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return scheduleModel.repayments().stream()//
|
||||||
|
.filter(repaymentPeriod -> !interestChangeEffectiveDate.isBefore(repaymentPeriod.getFromDate())
|
||||||
|
&& interestChangeEffectiveDate.isBefore(repaymentPeriod.getDueDate()))//
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add disbursement to Interest Period
|
* Add disbursement to Interest Period
|
||||||
*/
|
*/
|
||||||
@@ -154,6 +175,42 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
return new ProgressiveLoanInterestScheduleModel(repayments, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
return new ProgressiveLoanInterestScheduleModel(repayments, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changeInterestRate(final ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate newInterestEffectiveDate,
|
||||||
|
final BigDecimal newInterestRate) {
|
||||||
|
final ProgressiveLoanInterestRepaymentModel repaymentPeriod = findInterestRepaymentPeriodForInterestChange(scheduleModel,
|
||||||
|
newInterestEffectiveDate).orElse(null);
|
||||||
|
if (repaymentPeriod == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scheduleModel.addInterestRate(newInterestEffectiveDate, newInterestRate);
|
||||||
|
var interestPeriodOptional = findInterestPeriodForInterestChange(repaymentPeriod, newInterestEffectiveDate);
|
||||||
|
if (interestPeriodOptional.isEmpty()) {
|
||||||
|
insertInterestPeriod(scheduleModel, repaymentPeriod, newInterestEffectiveDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateEMIValueAndRateFactors(repaymentPeriod.getDueDate(), scheduleModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertInterestPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||||
|
final ProgressiveLoanInterestRepaymentModel repaymentPeriod, final LocalDate interestChangeDueDate) {
|
||||||
|
// period start date
|
||||||
|
final ProgressiveLoanInterestRepaymentInterestPeriod previousInterestPeriod = repaymentPeriod.getInterestPeriods().stream()
|
||||||
|
.filter(interestPeriod -> interestChangeDueDate.isAfter(interestPeriod.getFromDate())
|
||||||
|
&& interestChangeDueDate.isBefore(interestPeriod.getDueDate()))//
|
||||||
|
.findFirst()//
|
||||||
|
.get();//
|
||||||
|
|
||||||
|
final Money zeroAmount = Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency());
|
||||||
|
final var interestPeriod = new ProgressiveLoanInterestRepaymentInterestPeriod(interestChangeDueDate,
|
||||||
|
previousInterestPeriod.getDueDate(), BigDecimal.ZERO, zeroAmount, zeroAmount);
|
||||||
|
|
||||||
|
previousInterestPeriod.setDueDate(interestChangeDueDate);
|
||||||
|
|
||||||
|
repaymentPeriod.getInterestPeriods().add(interestPeriod);
|
||||||
|
Collections.sort(repaymentPeriod.getInterestPeriods());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate Equal Monthly Installment value and Rate Factor -1 values for calculate Interest
|
* Calculate Equal Monthly Installment value and Rate Factor -1 values for calculate Interest
|
||||||
*/
|
*/
|
||||||
@@ -259,7 +316,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
|||||||
final ProgressiveLoanInterestRepaymentInterestPeriod interestPeriod, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
final ProgressiveLoanInterestRepaymentInterestPeriod interestPeriod, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||||
final MathContext mc = scheduleModel.mc();
|
final MathContext mc = scheduleModel.mc();
|
||||||
final LoanProductRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail();
|
final LoanProductRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail();
|
||||||
final BigDecimal interestRate = calcNominalInterestRatePercentage(loanProductRelatedDetail.getNominalInterestRatePerPeriod(), mc);
|
final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriod.getFromDate()), mc);
|
||||||
final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType());
|
final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType());
|
||||||
final DaysInMonthType daysInMonthType = DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType());
|
final DaysInMonthType daysInMonthType = DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType());
|
||||||
final PeriodFrequencyType repaymentFrequency = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
|
final PeriodFrequencyType repaymentFrequency = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
|
||||||
|
|||||||
+91
@@ -25,6 +25,7 @@ import java.time.LocalDate;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
@@ -50,6 +51,7 @@ class ProgressiveEMICalculatorTest {
|
|||||||
|
|
||||||
private static final ProgressiveEMICalculator emiCalculator = new ProgressiveEMICalculator(null);
|
private static final ProgressiveEMICalculator emiCalculator = new ProgressiveEMICalculator(null);
|
||||||
|
|
||||||
|
private static MockedStatic<ThreadLocalContextUtil> threadLocalContextUtil = Mockito.mockStatic(ThreadLocalContextUtil.class);
|
||||||
private static MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
|
private static MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
|
||||||
private static LoanProductRelatedDetail loanProductRelatedDetail = Mockito.mock(LoanProductRelatedDetail.class);
|
private static LoanProductRelatedDetail loanProductRelatedDetail = Mockito.mock(LoanProductRelatedDetail.class);
|
||||||
|
|
||||||
@@ -241,6 +243,95 @@ class ProgressiveEMICalculatorTest {
|
|||||||
checkPeriod(interestSchedule, 5, 0, 17.13, 0.007901833333, 0.13, 17.00, 0.0);
|
checkPeriod(interestSchedule, 5, 0, 17.13, 0.007901833333, 0.13, 17.00, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEMICalculation_disbursedAmt100_dayInYears360_daysInMonth30_repayEvery1Month_reschedule_interest_on0201_4per() {
|
||||||
|
final MathContext mc = MoneyHelper.getMathContext();
|
||||||
|
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = new ArrayList<>();
|
||||||
|
|
||||||
|
expectedRepaymentPeriods.add(repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(2, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(5, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(6, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1)));
|
||||||
|
|
||||||
|
final BigDecimal interestRate = new BigDecimal("7");
|
||||||
|
final Integer installmentAmountInMultiplesOf = null;
|
||||||
|
|
||||||
|
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
|
||||||
|
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
|
||||||
|
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||||
|
|
||||||
|
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
|
||||||
|
|
||||||
|
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||||
|
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||||
|
|
||||||
|
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||||
|
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||||
|
|
||||||
|
final BigDecimal interestRateNewValue = new BigDecimal("4");
|
||||||
|
final LocalDate interestChangeDate = LocalDate.of(2024, 2, 2);
|
||||||
|
final LocalDate interestEffectiveDate = interestChangeDate.minusDays(1);
|
||||||
|
emiCalculator.changeInterestRate(interestSchedule, interestEffectiveDate, interestRateNewValue);
|
||||||
|
|
||||||
|
checkDisbursementOnPeriod(interestSchedule, 0, disbursedAmount);
|
||||||
|
checkPeriod(interestSchedule, 0, 0, 17.01, 0.005833333333, 0.58, 16.43, 83.57);
|
||||||
|
checkPeriod(interestSchedule, 1, 0, 16.88, 0.003333333333, 0.28, 16.60, 66.97);
|
||||||
|
checkPeriod(interestSchedule, 2, 0, 16.88, 0.003333333333, 0.22, 16.66, 50.31);
|
||||||
|
checkPeriod(interestSchedule, 3, 0, 16.88, 0.003333333333, 0.17, 16.71, 33.60);
|
||||||
|
checkPeriod(interestSchedule, 4, 0, 16.88, 0.003333333333, 0.11, 16.77, 16.83);
|
||||||
|
checkPeriod(interestSchedule, 5, 0, 16.89, 0.003333333333, 0.06, 16.83, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEMICalculation_disbursedAmt100_dayInYears360_daysInMonth30_repayEvery1Month_reschedule_interest_on0215_4per() {
|
||||||
|
final MathContext mc = MoneyHelper.getMathContext();
|
||||||
|
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = new ArrayList<>();
|
||||||
|
|
||||||
|
expectedRepaymentPeriods.add(repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(2, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(5, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1)));
|
||||||
|
expectedRepaymentPeriods.add(repayment(6, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1)));
|
||||||
|
|
||||||
|
final BigDecimal interestRate = new BigDecimal("7");
|
||||||
|
final Integer installmentAmountInMultiplesOf = null;
|
||||||
|
|
||||||
|
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
|
||||||
|
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
|
||||||
|
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||||
|
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
|
||||||
|
|
||||||
|
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
|
||||||
|
|
||||||
|
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
|
||||||
|
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||||
|
|
||||||
|
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
|
||||||
|
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||||
|
|
||||||
|
final BigDecimal interestRateNewValue = new BigDecimal("4");
|
||||||
|
final LocalDate interestChangeDate = LocalDate.of(2024, 2, 15);
|
||||||
|
final LocalDate interestEffectiveDate = interestChangeDate.minusDays(1);
|
||||||
|
emiCalculator.changeInterestRate(interestSchedule, interestEffectiveDate, interestRateNewValue);
|
||||||
|
|
||||||
|
checkDisbursementOnPeriod(interestSchedule, 0, disbursedAmount);
|
||||||
|
checkPeriod(interestSchedule, 0, 0, 17.01, 0.005833333333, 0.58, 16.43, 83.57);
|
||||||
|
checkPeriod(interestSchedule, 1, 0, 16.90, 0.002614942529, 0.22, 0.37, 16.53, 67.04);
|
||||||
|
checkPeriod(interestSchedule, 1, 1, 16.90, 0.001839080460, 0.15, 0.37, 16.53, 67.04);
|
||||||
|
checkPeriod(interestSchedule, 2, 0, 16.90, 0.003333333333, 0.22, 16.68, 50.36);
|
||||||
|
checkPeriod(interestSchedule, 3, 0, 16.90, 0.003333333333, 0.17, 16.73, 33.63);
|
||||||
|
checkPeriod(interestSchedule, 4, 0, 16.90, 0.003333333333, 0.11, 16.79, 16.84);
|
||||||
|
checkPeriod(interestSchedule, 5, 0, 16.90, 0.003333333333, 0.06, 16.84, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
// @Test
|
// @Test
|
||||||
// public void testEMICalculation_disbursedAmt100_dayInYearsActual_daysInMonthActual_repayEvery1Month_reschedule() {
|
// public void testEMICalculation_disbursedAmt100_dayInYearsActual_daysInMonthActual_repayEvery1Month_reschedule() {
|
||||||
// final MathContext mc = MoneyHelper.getMathContext();
|
// final MathContext mc = MoneyHelper.getMathContext();
|
||||||
|
|||||||
Reference in New Issue
Block a user