FINERACT-1981: Provide more general EMI calculator interface

This commit is contained in:
Janos Meszaros
2024-07-12 13:09:46 +02:00
committed by Adam Saghy
parent c9d1b81cc6
commit a4f077d675
5 changed files with 89 additions and 156 deletions
@@ -119,13 +119,16 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
: scheduleParams.getPeriodStartDate();
List<PreGeneratedLoanSchedulePeriod> expectedRepaymentPeriods = getScheduledDateGenerator()
.generateRepaymentPeriods(startDate, loanApplicationTerms, holidayDetailDTO);
emiCalculationResult = getEMICalculator().calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
emiCalculationResult = getEMICalculator().calculateEMIValueAndRateFactors(scheduleParams.getOutstandingBalanceAsPerRest(),
loanApplicationTerms.toLoanProductRelatedDetail(), expectedRepaymentPeriods, scheduleParams.getPeriodNumber(),
loanApplicationTerms.getNumberOfRepayments(), mc);
}
// 5 determine principal,interest of repayment period
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(loanApplicationTerms,
scheduleParams, emiCalculationResult, mc);
PrincipalInterest principalInterestForThisPeriod = getEMICalculator().calculatePrincipalInterestComponentsForPeriod(
emiCalculationResult, scheduleParams.getOutstandingBalanceAsPerRest(),
loanApplicationTerms.getInstallmentAmountInMultiplesOf(), scheduleParams.getPeriodNumber(),
loanApplicationTerms.getActualNoOfRepaymnets(), mc);
// update cumulative fields for principal
currentPeriodParams.setPrincipalForThisPeriod(principalInterestForThisPeriod.principal());
@@ -237,9 +240,6 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
public abstract PaymentPeriodsInOneYearCalculator getPaymentPeriodsInOneYearCalculator();
public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(LoanApplicationTerms loanApplicationTerms,
LoanScheduleParams loanScheduleParams, EMICalculationResult emiCalculationResult, MathContext mc);
protected abstract EMICalculator getEMICalculator();
// Private, internal methods
@@ -18,12 +18,7 @@
*/
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
import java.math.BigDecimal;
import java.math.MathContext;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams;
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculationResult;
import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
import org.springframework.stereotype.Component;
@@ -49,31 +44,4 @@ public class ProgressiveLoanScheduleGenerator extends AbstractProgressiveLoanSch
protected EMICalculator getEMICalculator() {
return emiCalculator;
}
@Override
public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(final LoanApplicationTerms loanApplicationTerms,
final LoanScheduleParams loanScheduleParams, final EMICalculationResult emiCalculationResult, final MathContext mc) {
final Money equalMonthlyInstallmentValue = loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null
? Money.roundToMultiplesOf(emiCalculationResult.getEqualMonthlyInstallmentValue(),
loanApplicationTerms.getInstallmentAmountInMultiplesOf())
: emiCalculationResult.getEqualMonthlyInstallmentValue();
final BigDecimal rateFactorMinus1 = emiCalculationResult.getNextRepaymentPeriodRateFactorMinus1();
final Money calculatedInterest = loanScheduleParams.getOutstandingBalanceAsPerRest().multipliedBy(rateFactorMinus1);
final Money calculatedPrincipal = equalMonthlyInstallmentValue.minus(calculatedInterest);
return new PrincipalInterest(
adjustCalculatedPrincipalWithRemainingBalanceInLastPeriod(calculatedPrincipal, loanApplicationTerms, loanScheduleParams),
calculatedInterest, Money.zero(loanApplicationTerms.getCurrency()));
}
private Money adjustCalculatedPrincipalWithRemainingBalanceInLastPeriod(final Money calculatedPrincipal,
final LoanApplicationTerms loanApplicationTerms, final LoanScheduleParams loanScheduleParams) {
final boolean isLastRepaymentPeriod = loanScheduleParams.getPeriodNumber() == loanApplicationTerms.getActualNoOfRepaymnets();
if (isLastRepaymentPeriod) {
final Money remainingAmount = loanScheduleParams.getOutstandingBalanceAsPerRest().minus(calculatedPrincipal);
return calculatedPrincipal.plus(remainingAmount);
}
return calculatedPrincipal;
}
}
@@ -20,12 +20,18 @@ package org.apache.fineract.portfolio.loanproduct.calc;
import java.math.MathContext;
import java.util.List;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.PrincipalInterest;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
public interface EMICalculator {
EMICalculationResult calculateEMIValueAndRateFactors(LoanApplicationTerms loanApplicationTerms, LoanScheduleParams scheduleParams,
List<? extends LoanScheduleModelPeriod> expectedRepaymentPeriods, MathContext mc);
EMICalculationResult calculateEMIValueAndRateFactors(Money outstandingBalanceAsPerRest,
LoanProductRelatedDetail loanProductRelatedDetail, List<? extends LoanScheduleModelPeriod> expectedRepaymentPeriods,
Integer actualPeriodNumber, Integer numberOfRepayments, MathContext mc);
PrincipalInterest calculatePrincipalInterestComponentsForPeriod(EMICalculationResult emiCalculationResult,
Money outstandingBalanceAsPerRest, Integer installmentAmountInMultiplesOf, Integer actualPeriodNumber,
Integer actualNoOfRepayments, MathContext mc);
}
@@ -23,15 +23,15 @@ import java.math.MathContext;
import java.time.LocalDate;
import java.time.Year;
import java.util.List;
import java.util.Objects;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.PrincipalInterest;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.springframework.stereotype.Component;
@@ -43,29 +43,13 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Calculate Equal Monthly Installment value and Rate Factor -1 values for calculate Interest
*
* @param loanApplicationTerms
* LoanTermApplication
*
* @param scheduleParams
* Loan Schedule Params
*
* @param expectedRepaymentPeriods
* Expected Repayment Periods
*
* @param mc
* MathContext for rounding
*
* @return EMICalculationResult Contains rate factor for each period and calculated EMI
*/
@Override
public EMICalculationResult calculateEMIValueAndRateFactors(final LoanApplicationTerms loanApplicationTerms,
final LoanScheduleParams scheduleParams, final List<? extends LoanScheduleModelPeriod> expectedRepaymentPeriods,
final MathContext mc) {
final LoanProductRelatedDetail loanProductRelatedDetail = loanApplicationTerms.toLoanProductRelatedDetail();
public EMICalculationResult calculateEMIValueAndRateFactors(final Money outstandingBalanceAsPerRest,
final LoanProductRelatedDetail loanProductRelatedDetail, final List<? extends LoanScheduleModelPeriod> expectedRepaymentPeriods,
final Integer actualPeriodNumber, final Integer numberOfRepayments, final MathContext mc) {
final BigDecimal nominalInterestRatePerPeriod = calcNominalInterestRatePerPeriod(
loanProductRelatedDetail.getNominalInterestRatePerPeriod(), mc);
final Money outstandingBalance = scheduleParams.getOutstandingBalanceAsPerRest();
final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType());
final DaysInMonthType daysInMonthType = DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType());
final PeriodFrequencyType repaymentFrequency = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
@@ -74,7 +58,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
final List<BigDecimal> rateFactorList = getRateFactorList(expectedRepaymentPeriods, nominalInterestRatePerPeriod, daysInYearType,
daysInMonthType, repaymentFrequency, repaymentEvery, mc);
return calculateEMI(loanApplicationTerms, scheduleParams, rateFactorList, outstandingBalance, mc);
return calculateEMI(rateFactorList, actualPeriodNumber, numberOfRepayments, outstandingBalanceAsPerRest, mc);
}
/**
@@ -203,23 +187,18 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Calculate EMI parts and return an EMI calculation result object with repayment installment rate factors
*
* @param rateFactorList
* @param outstandingBalanceForRest
* @param mc
* @return
*/
EMICalculationResult calculateEMI(final LoanApplicationTerms loanApplicationTerms, final LoanScheduleParams loanScheduleParams,
final List<BigDecimal> rateFactorList, final Money outstandingBalanceForRest, final MathContext mc) {
EMICalculationResult calculateEMI(final List<BigDecimal> rateFactorList, final Integer actualPeriodNumber,
final Integer numberOfRepayments, final Money outstandingBalanceForRest, final MathContext mc) {
final BigDecimal rateFactorN = MathUtil.stripTrailingZeros(calculateRateFactorN(rateFactorList, mc));
final BigDecimal fnResult = MathUtil.stripTrailingZeros(calculateFnResult(rateFactorList, mc));
final Money emiValue = Money.of(loanApplicationTerms.getCurrency(),
final Money emiValue = Money.of(outstandingBalanceForRest.getCurrency(),
calculateEMIValue(rateFactorN, outstandingBalanceForRest.getAmount(), fnResult, mc));
final List<BigDecimal> rateFactorMinus1List = getRateFactorMinus1List(rateFactorList, mc);
final Money adjustedEqualMonthlyInstallmentValue = adjustEMIForMoreStreamlinedRepaymentSchedule(loanApplicationTerms,
loanScheduleParams, emiValue, rateFactorMinus1List, mc);
final Money adjustedEqualMonthlyInstallmentValue = adjustEMIForMoreStreamlinedRepaymentSchedule(actualPeriodNumber,
numberOfRepayments, outstandingBalanceForRest, emiValue, rateFactorMinus1List, mc);
return new EMICalculationResult(adjustedEqualMonthlyInstallmentValue, rateFactorMinus1List);
}
@@ -228,23 +207,16 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* Due to rounding or unequal installments, the first calculated EMI might not be the best one! Reiterate with
* adjusted EMI to get a better streamlined repayment schedule (less difference between calculated EMI and last
* installment EMI).
*
* @param loanApplicationTerms
* @param loanScheduleParams
* @param equalMonthlyInstallmentValue
* @param rateFactorMinus1List
* @param mc
* @return
*/
Money adjustEMIForMoreStreamlinedRepaymentSchedule(final LoanApplicationTerms loanApplicationTerms,
final LoanScheduleParams loanScheduleParams, final Money equalMonthlyInstallmentValue, List<BigDecimal> rateFactorMinus1List,
Money adjustEMIForMoreStreamlinedRepaymentSchedule(final Integer actualPeriodNumber, final Integer numberOfRepayments,
final Money outstandingBalanceAsPerRest, final Money equalMonthlyInstallmentValue, List<BigDecimal> rateFactorMinus1List,
final MathContext mc) {
int numberOfUpcomingPeriods = loanApplicationTerms.getNumberOfRepayments() - loanScheduleParams.getPeriodNumber() + 1;
int numberOfUpcomingPeriods = numberOfRepayments - actualPeriodNumber + 1;
if (numberOfUpcomingPeriods < 2) {
return equalMonthlyInstallmentValue;
}
RepaymentScheduleModel repaymentScheduleModel = generateRepaymentScheduleModel(loanApplicationTerms, loanScheduleParams,
RepaymentScheduleModel repaymentScheduleModel = generateRepaymentScheduleModel(numberOfRepayments, outstandingBalanceAsPerRest,
equalMonthlyInstallmentValue, rateFactorMinus1List);
Money calculatedLastEMI = repaymentScheduleModel.getScheduleList().get(repaymentScheduleModel.getScheduleList().size() - 1).emi();
Money originalDifference = calculatedLastEMI.minus(equalMonthlyInstallmentValue);
@@ -262,8 +234,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
Money adjustment = originalDifference.dividedBy(numberOfUpcomingPeriods, mc.getRoundingMode());
Money adjustedEqualMonthlyInstallmentValue = equalMonthlyInstallmentValue.plus(adjustment);
RepaymentScheduleModel repaymentScheduleModelWithAdjustedEMI = generateRepaymentScheduleModel(loanApplicationTerms,
loanScheduleParams, adjustedEqualMonthlyInstallmentValue, rateFactorMinus1List);
RepaymentScheduleModel repaymentScheduleModelWithAdjustedEMI = generateRepaymentScheduleModel(numberOfRepayments,
outstandingBalanceAsPerRest, adjustedEqualMonthlyInstallmentValue, rateFactorMinus1List);
Money calculatedLastEMIAfterAdjustment = repaymentScheduleModelWithAdjustedEMI.getScheduleList()
.get(repaymentScheduleModelWithAdjustedEMI.getScheduleList().size() - 1).emi();
Money differenceAfterEMIAdjustment = calculatedLastEMIAfterAdjustment.minus(adjustedEqualMonthlyInstallmentValue);
@@ -275,16 +247,15 @@ public final class ProgressiveEMICalculator implements EMICalculator {
}
}
RepaymentScheduleModel generateRepaymentScheduleModel(LoanApplicationTerms loanApplicationTerms, LoanScheduleParams loanScheduleParams,
RepaymentScheduleModel generateRepaymentScheduleModel(final Integer numberOfRepayments, final Money outstandingBalanceAsPerRest,
Money equalMonthlyInstallmentValue, List<BigDecimal> rateFactorMinus1List) {
RepaymentScheduleModel repaymentScheduleModel = new RepaymentScheduleModel();
Money balanceOfLoan = loanScheduleParams.getOutstandingBalanceAsPerRest();
for (int i = 0; i < loanApplicationTerms.getNumberOfRepayments(); i++) {
Money balanceOfLoan = outstandingBalanceAsPerRest;
for (int i = 0; i < numberOfRepayments; i++) {
final Money calculatedInterest = balanceOfLoan.multipliedBy(rateFactorMinus1List.get(i));
// WE need to calculate EMI differently for last installment (decided by number of repayments or when
// schedule got shorter then planned)
if (balanceOfLoan.isLessThan(equalMonthlyInstallmentValue.minus(calculatedInterest))
|| i == loanApplicationTerms.getNumberOfRepayments() - 1) {
if (balanceOfLoan.isLessThan(equalMonthlyInstallmentValue.minus(calculatedInterest)) || i == numberOfRepayments - 1) {
equalMonthlyInstallmentValue = balanceOfLoan.plus(calculatedInterest);
}
final Money calculatedPrincipal = equalMonthlyInstallmentValue.minus(calculatedInterest);
@@ -302,9 +273,6 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Rate factor -1 values
*
* @param rateFactors
* @param mc
* @return
*/
List<BigDecimal> getRateFactorMinus1List(final List<BigDecimal> rateFactors, final MathContext mc) {
return rateFactors.stream().map(it -> it.subtract(BigDecimal.ONE, mc)).toList();
@@ -312,10 +280,6 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Calculate Rate Factor Product from rate factors
*
* @param rateFactors
* @param mc
* @return
*/
BigDecimal calculateRateFactorN(final List<BigDecimal> rateFactors, final MathContext mc) {
return rateFactors.stream().reduce(BigDecimal.ONE, (BigDecimal acc, BigDecimal value) -> acc.multiply(value, mc));
@@ -323,10 +287,6 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Summarize Fn values
*
* @param rateFactors
* @param mc
* @return
*/
BigDecimal calculateFnResult(final List<BigDecimal> rateFactors, final MathContext mc) {
return rateFactors.stream().skip(1).reduce(BigDecimal.ONE,
@@ -335,12 +295,6 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Calculate the EMI (Equal Monthly Installment) value
*
* @param rateFactorN
* @param outstandingBalanceForRest
* @param fnResult
* @param mc
* @return
*/
BigDecimal calculateEMIValue(final BigDecimal rateFactorN, final BigDecimal outstandingBalanceForRest, final BigDecimal fnResult,
final MathContext mc) {
@@ -484,13 +438,6 @@ public final class ProgressiveEMICalculator implements EMICalculator {
/**
* Calculate Rate Factor based on Partial Period
*
* @param interestRate
* @param repaymentEvery
* @param cumulatedPeriodRatio
* @param actualDaysInPeriod
* @param calculatedDaysInPeriod
* @param mc
* @return
*/
BigDecimal rateFactorByRepaymentPartialPeriod(final BigDecimal interestRate, final BigDecimal repaymentEvery,
final BigDecimal cumulatedPeriodRatio, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod,
@@ -516,4 +463,31 @@ public final class ProgressiveEMICalculator implements EMICalculator {
BigDecimal fnValue(final BigDecimal previousFnValue, final BigDecimal currentRateFactor, final MathContext mc) {
return BigDecimal.ONE.add(previousFnValue.multiply(currentRateFactor, mc), mc);
}
@Override
public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(final EMICalculationResult emiCalculationResult,
final Money outstandingBalanceAsPerRest, final Integer installmentAmountInMultiplesOf, final Integer actualPeriodNumber,
final Integer actualNoOfRepayments, final MathContext mc) {
final Money equalMonthlyInstallmentValue = installmentAmountInMultiplesOf != null
? Money.roundToMultiplesOf(emiCalculationResult.getEqualMonthlyInstallmentValue(), installmentAmountInMultiplesOf)
: emiCalculationResult.getEqualMonthlyInstallmentValue();
final BigDecimal rateFactorMinus1 = emiCalculationResult.getNextRepaymentPeriodRateFactorMinus1();
final Money calculatedInterest = outstandingBalanceAsPerRest.multipliedBy(rateFactorMinus1);
final Money calculatedPrincipal = equalMonthlyInstallmentValue.minus(calculatedInterest);
return new PrincipalInterest(
adjustCalculatedPrincipalWithRemainingBalanceInLastPeriod(calculatedPrincipal, outstandingBalanceAsPerRest,
actualPeriodNumber, actualNoOfRepayments),
calculatedInterest, Money.zero(equalMonthlyInstallmentValue.getCurrency()));
}
Money adjustCalculatedPrincipalWithRemainingBalanceInLastPeriod(final Money calculatedPrincipal,
final Money outstandingBalanceAsPerRest, final Integer actualPeriodNumber, final Integer actualNoOfRepayments) {
final boolean isLastRepaymentPeriod = Objects.equals(actualPeriodNumber, actualNoOfRepayments);
if (isLastRepaymentPeriod) {
final Money remainingAmount = outstandingBalanceAsPerRest.minus(calculatedPrincipal);
return calculatedPrincipal.plus(remainingAmount);
}
return calculatedPrincipal;
}
}
@@ -34,8 +34,6 @@ import org.apache.fineract.portfolio.common.domain.DaysInYearType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleModelDownPaymentPeriod;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelDisbursementPeriod;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.PreGeneratedLoanSchedulePeriod;
@@ -55,8 +53,6 @@ class ProgressiveEMICalculatorTest {
private static final ProgressiveEMICalculator emiCalculator = new ProgressiveEMICalculator();
private static MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
private static LoanScheduleParams scheduleParams = Mockito.mock(LoanScheduleParams.class);
private static LoanApplicationTerms loanApplicationTerms = Mockito.mock(LoanApplicationTerms.class);
private static LoanProductRelatedDetail loanProductRelatedDetail = Mockito.mock(LoanProductRelatedDetail.class);
private static final MonetaryCurrency monetaryCurrency = MonetaryCurrency
@@ -79,8 +75,6 @@ class ProgressiveEMICalculatorTest {
// When
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
Mockito.when(loanApplicationTerms.toLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
Mockito.when(loanApplicationTerms.getCurrency()).thenReturn(monetaryCurrency);
}
private BigDecimal getRateFactorsByMonth(final DaysInYearType daysInYearType, final DaysInMonthType daysInMonthType,
@@ -163,15 +157,14 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(100);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
// 17.13
Assertions.assertEquals(BigDecimal.valueOf(17.13), result.getEqualMonthlyInstallmentValue().getAmount());
@@ -205,15 +198,14 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(10_000);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
// 1713.12
Assertions.assertEquals(BigDecimal.valueOf(1713.12), result.getEqualMonthlyInstallmentValue().getAmount());
@@ -244,15 +236,14 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(100);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_365.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
// 17.13
Assertions.assertEquals(BigDecimal.valueOf(17.13), result.getEqualMonthlyInstallmentValue().getAmount());
@@ -287,15 +278,14 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(100);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
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);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
// 17.13
Assertions.assertEquals(BigDecimal.valueOf(17.13), result.getEqualMonthlyInstallmentValue().getAmount());
@@ -330,15 +320,14 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(100);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_364.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.INVALID.getValue());
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.WEEKS);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
// 16.77
Assertions.assertEquals(BigDecimal.valueOf(16.77), result.getEqualMonthlyInstallmentValue().getAmount());
@@ -374,15 +363,14 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(100);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.INVALID.getValue());
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.WEEKS);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(2);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
// 16.88
Assertions.assertEquals(new BigDecimal("16.88"), result.getEqualMonthlyInstallmentValue().getAmount());
@@ -418,15 +406,14 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(100);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.INVALID.getValue());
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.DAYS);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(15);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
// 16.90
Assertions.assertEquals(new BigDecimal("16.90"), result.getEqualMonthlyInstallmentValue().getAmount());
@@ -456,7 +443,6 @@ class ProgressiveEMICalculatorTest {
final Money downPaymentValue = Money.of(monetaryCurrency, BigDecimal.valueOf(25));
final Money outstandingBalance = principal.minus(downPaymentValue);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
@@ -474,8 +460,8 @@ class ProgressiveEMICalculatorTest {
expectedRepaymentPeriods.add(new PreGeneratedLoanSchedulePeriod(5, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1)));
expectedRepaymentPeriods.add(new PreGeneratedLoanSchedulePeriod(6, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1)));
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 5, mc);
// 15.36
Assertions.assertEquals(new BigDecimal("15.36"), result.getEqualMonthlyInstallmentValue().getAmount());
@@ -501,7 +487,6 @@ class ProgressiveEMICalculatorTest {
final BigDecimal interestRate = BigDecimal.valueOf(0);
final Money outstandingBalance = Money.of(monetaryCurrency, BigDecimal.valueOf(1000));
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
@@ -514,8 +499,8 @@ class ProgressiveEMICalculatorTest {
expectedRepaymentPeriods.add(new PreGeneratedLoanSchedulePeriod(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1)));
expectedRepaymentPeriods.add(new PreGeneratedLoanSchedulePeriod(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1)));
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams,
expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 4, mc);
// 250.00
Assertions.assertEquals(new BigDecimal("250.00"), result.getEqualMonthlyInstallmentValue().getAmount());
@@ -542,7 +527,6 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(100);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.INVALID.getValue());
@@ -552,7 +536,8 @@ class ProgressiveEMICalculatorTest {
expectedRepaymentPeriods.add(new PreGeneratedLoanSchedulePeriod(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 1, 16)));
try {
emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams, expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
Assertions.fail();
} catch (Exception e) {
Assertions.assertInstanceOf(UnsupportedOperationException.class, e);
@@ -568,7 +553,6 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(100);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.INVALID.getValue());
@@ -578,7 +562,8 @@ class ProgressiveEMICalculatorTest {
expectedRepaymentPeriods.add(new PreGeneratedLoanSchedulePeriod(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 1, 16)));
try {
emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams, expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
Assertions.fail();
} catch (Exception e) {
Assertions.assertInstanceOf(UnsupportedOperationException.class, e);
@@ -594,7 +579,6 @@ class ProgressiveEMICalculatorTest {
final BigDecimal principal = BigDecimal.valueOf(100);
final Money outstandingBalance = Money.of(monetaryCurrency, principal);
Mockito.when(scheduleParams.getOutstandingBalanceAsPerRest()).thenReturn(outstandingBalance);
Mockito.when(loanProductRelatedDetail.getNominalInterestRatePerPeriod()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.INVALID.getValue());
@@ -604,7 +588,8 @@ class ProgressiveEMICalculatorTest {
expectedRepaymentPeriods.add(new PreGeneratedLoanSchedulePeriod(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 1, 16)));
try {
emiCalculator.calculateEMIValueAndRateFactors(loanApplicationTerms, scheduleParams, expectedRepaymentPeriods, mc);
final EMICalculationResult result = emiCalculator.calculateEMIValueAndRateFactors(outstandingBalance, loanProductRelatedDetail,
expectedRepaymentPeriods, 1, 6, mc);
Assertions.fail();
} catch (Exception e) {
Assertions.assertInstanceOf(UnsupportedOperationException.class, e);