FINERACT-2081: Fix end of month and partial interest calculation for 360/30
This commit is contained in:
+4
-4
@@ -1549,10 +1549,10 @@ public final class LoanApplicationTerms {
|
||||
public LoanProductMinimumRepaymentScheduleRelatedDetail toLoanProductRelatedDetailMinimumData() {
|
||||
final CurrencyData currency = new CurrencyData(this.currency.getCode(), this.currency.getDecimalPlaces(),
|
||||
this.currency.getInMultiplesOf());
|
||||
return new LoanProductRelatedDetailMinimumData(currency, principal, inArrearsTolerance, interestRatePerPeriod,
|
||||
annualNominalInterestRate, interestChargingGrace, interestPaymentGrace, principalGrace,
|
||||
recurringMoratoriumOnPrincipalPeriods, interestMethod, interestCalculationPeriodMethod, daysInYearType, daysInMonthType,
|
||||
amortizationMethod, repaymentPeriodFrequencyType, repaymentEvery, numberOfRepayments);
|
||||
return new LoanProductRelatedDetailMinimumData(currency, interestRatePerPeriod, annualNominalInterestRate, interestChargingGrace,
|
||||
interestPaymentGrace, principalGrace, recurringMoratoriumOnPrincipalPeriods, interestMethod,
|
||||
interestCalculationPeriodMethod, daysInYearType, daysInMonthType, amortizationMethod, repaymentPeriodFrequencyType,
|
||||
repaymentEvery, numberOfRepayments);
|
||||
}
|
||||
|
||||
public Integer getLoanTermFrequency() {
|
||||
|
||||
+6
-28
@@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.loanproduct.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
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;
|
||||
@@ -32,39 +31,28 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaym
|
||||
public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRepaymentScheduleRelatedDetail {
|
||||
|
||||
private final CurrencyData currency;
|
||||
|
||||
private final Money principal;
|
||||
private final Money inArrearsTolerance;
|
||||
|
||||
private final BigDecimal interestRatePerPeriod;
|
||||
private final BigDecimal annualNominalInterestRate;
|
||||
|
||||
private final Integer interestChargingGrace;
|
||||
private final Integer interestPaymentGrace;
|
||||
private final Integer principalGrace;
|
||||
private final Integer recurringMoratoriumOnPrincipalPeriods;
|
||||
|
||||
private final InterestMethod interestMethod;
|
||||
private final InterestCalculationPeriodMethod interestCalculationPeriodMethod;
|
||||
|
||||
private final DaysInYearType daysInYearType;
|
||||
private final DaysInMonthType daysInMonthType;
|
||||
|
||||
private final AmortizationMethod amortizationMethod;
|
||||
|
||||
private final PeriodFrequencyType repaymentPeriodFrequencyType;
|
||||
private final Integer repaymentEvery;
|
||||
private final Integer numberOfRepayments;
|
||||
|
||||
public LoanProductRelatedDetailMinimumData(CurrencyData currency, Money principal, Money inArrearsTolerance,
|
||||
BigDecimal interestRatePerPeriod, BigDecimal annualNominalInterestRate, Integer interestChargingGrace,
|
||||
Integer interestPaymentGrace, Integer principalGrace, Integer recurringMoratoriumOnPrincipalPeriods,
|
||||
InterestMethod interestMethod, InterestCalculationPeriodMethod interestCalculationPeriodMethod, DaysInYearType daysInYearType,
|
||||
DaysInMonthType daysInMonthType, AmortizationMethod amortizationMethod, PeriodFrequencyType repaymentPeriodFrequencyType,
|
||||
Integer repaymentEvery, Integer numberOfRepayments) {
|
||||
public LoanProductRelatedDetailMinimumData(CurrencyData currency, BigDecimal interestRatePerPeriod,
|
||||
BigDecimal annualNominalInterestRate, Integer interestChargingGrace, Integer interestPaymentGrace, Integer principalGrace,
|
||||
Integer recurringMoratoriumOnPrincipalPeriods, InterestMethod interestMethod,
|
||||
InterestCalculationPeriodMethod interestCalculationPeriodMethod, DaysInYearType daysInYearType, DaysInMonthType daysInMonthType,
|
||||
AmortizationMethod amortizationMethod, PeriodFrequencyType repaymentPeriodFrequencyType, Integer repaymentEvery,
|
||||
Integer numberOfRepayments) {
|
||||
this.currency = currency;
|
||||
this.principal = principal;
|
||||
this.inArrearsTolerance = inArrearsTolerance;
|
||||
this.interestRatePerPeriod = interestRatePerPeriod;
|
||||
this.annualNominalInterestRate = annualNominalInterestRate;
|
||||
this.interestChargingGrace = defaultToNullIfZero(interestChargingGrace);
|
||||
@@ -94,11 +82,6 @@ public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRe
|
||||
return currency;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Money getPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getGraceOnInterestCharged() {
|
||||
return interestChargingGrace;
|
||||
@@ -119,11 +102,6 @@ public class LoanProductRelatedDetailMinimumData implements LoanProductMinimumRe
|
||||
return recurringMoratoriumOnPrincipalPeriods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Money getInArrearsTolerance() {
|
||||
return inArrearsTolerance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getNominalInterestRatePerPeriod() {
|
||||
return interestRatePerPeriod;
|
||||
|
||||
-5
@@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.loanproduct.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
/**
|
||||
@@ -30,8 +29,6 @@ public interface LoanProductMinimumRepaymentScheduleRelatedDetail {
|
||||
|
||||
CurrencyData getCurrencyData();
|
||||
|
||||
Money getPrincipal();
|
||||
|
||||
Integer getGraceOnInterestCharged();
|
||||
|
||||
Integer getGraceOnInterestPayment();
|
||||
@@ -40,8 +37,6 @@ public interface LoanProductMinimumRepaymentScheduleRelatedDetail {
|
||||
|
||||
Integer getRecurringMoratoriumOnPrincipalPeriods();
|
||||
|
||||
Money getInArrearsTolerance();
|
||||
|
||||
BigDecimal getNominalInterestRatePerPeriod();
|
||||
|
||||
PeriodFrequencyType getInterestPeriodFrequencyType();
|
||||
|
||||
-2
@@ -263,12 +263,10 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
return currency.toData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Money getPrincipal() {
|
||||
return Money.of(getCurrencyData(), this.principal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Money getInArrearsTolerance() {
|
||||
return Money.of(getCurrencyData(), this.inArrearsTolerance);
|
||||
}
|
||||
|
||||
+17
-16
@@ -65,7 +65,6 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
@@ -76,6 +75,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanPaymentAllocationRul
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator;
|
||||
@@ -183,10 +183,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
|
||||
MoneyHolder overpaymentHolder = new MoneyHolder(Money.zero(currency));
|
||||
final Loan loan = loanTransactions.get(0).getLoan();
|
||||
LoanTermVariationsDataWrapper loanTermVariations = Optional
|
||||
.ofNullable(loan.getActiveLoanTermVariations()).map(loanTermVariationsSet -> loanTermVariationsSet.stream()
|
||||
.map(LoanTermVariations::toData).collect(Collectors.toCollection(ArrayList::new)))
|
||||
.map(LoanTermVariationsDataWrapper::new).orElse(null);
|
||||
List<LoanTermVariationsData> loanTermVariations = loan.getActiveLoanTermVariations().stream().map(LoanTermVariations::toData)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
final Integer installmentAmountInMultiplesOf = loan.getLoanProduct().getInstallmentAmountInMultiplesOf();
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
ProgressiveLoanInterestScheduleModel scheduleModel = emiCalculator.generateInstallmentInterestScheduleModel(installments,
|
||||
@@ -235,8 +233,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
public ChangedTransactionDetail reprocessLoanTransactions(LocalDate disbursementDate, List<LoanTransaction> loanTransactions,
|
||||
MonetaryCurrency currency, List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> charges) {
|
||||
LocalDate currentDate = DateUtils.getBusinessLocalDate();
|
||||
return reprocessProgressiveLoanTransactions(disbursementDate, currentDate, loanTransactions, currency, installments, charges)
|
||||
.getLeft();
|
||||
Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> result = reprocessProgressiveLoanTransactions(disbursementDate,
|
||||
currentDate, loanTransactions, currency, installments, charges);
|
||||
return result.getLeft();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -823,16 +822,18 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<ChangeOperation> createSortedChangeList(final LoanTermVariationsDataWrapper loanTermVariations,
|
||||
private List<ChangeOperation> createSortedChangeList(final List<LoanTermVariationsData> loanTermVariations,
|
||||
final List<LoanTransaction> loanTransactions, final Set<LoanCharge> charges) {
|
||||
final List<ChangeOperation> changeOperations = new ArrayList<>();
|
||||
if (loanTermVariations != null) {
|
||||
if (!loanTermVariations.getInterestPauseVariations().isEmpty()) {
|
||||
changeOperations.addAll(loanTermVariations.getInterestPauseVariations().stream().map(ChangeOperation::new).toList());
|
||||
}
|
||||
if (!loanTermVariations.getInterestRateFromInstallment().isEmpty()) {
|
||||
changeOperations.addAll(loanTermVariations.getInterestRateFromInstallment().stream().map(ChangeOperation::new).toList());
|
||||
}
|
||||
List<ChangeOperation> changeOperations = new ArrayList<>();
|
||||
Map<LoanTermVariationType, List<LoanTermVariationsData>> loanTermVariationsMap = loanTermVariations.stream()
|
||||
.collect(Collectors.groupingBy(ltvd -> LoanTermVariationType.fromInt(ltvd.getTermType().getId().intValue())));
|
||||
if (loanTermVariationsMap.get(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT) != null) {
|
||||
changeOperations.addAll(loanTermVariationsMap.get(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT).stream()
|
||||
.map(ChangeOperation::new).toList());
|
||||
}
|
||||
if (loanTermVariationsMap.get(LoanTermVariationType.INTEREST_PAUSE) != null) {
|
||||
changeOperations
|
||||
.addAll(loanTermVariationsMap.get(LoanTermVariationType.INTEREST_PAUSE).stream().map(ChangeOperation::new).toList());
|
||||
}
|
||||
if (charges != null) {
|
||||
changeOperations.addAll(charges.stream().map(ChangeOperation::new).toList());
|
||||
|
||||
+18
-5
@@ -27,18 +27,22 @@ import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
|
||||
|
||||
@Data
|
||||
@@ -48,18 +52,18 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
private final List<RepaymentPeriod> repaymentPeriods;
|
||||
private final TreeSet<InterestRate> interestRates;
|
||||
private final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail;
|
||||
private final LoanTermVariationsDataWrapper loanTermVariations;
|
||||
private final Map<LoanTermVariationType, List<LoanTermVariationsData>> loanTermVariations;
|
||||
private final Integer installmentAmountInMultiplesOf;
|
||||
private final MathContext mc;
|
||||
private final Money zero;
|
||||
|
||||
public ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> repaymentPeriods,
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail,
|
||||
final LoanTermVariationsDataWrapper loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
final List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
this.repaymentPeriods = repaymentPeriods;
|
||||
this.interestRates = new TreeSet<>(Collections.reverseOrder());
|
||||
this.loanProductRelatedDetail = loanProductRelatedDetail;
|
||||
this.loanTermVariations = loanTermVariations;
|
||||
this.loanTermVariations = buildLoanTermVariationMap(loanTermVariations);
|
||||
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
|
||||
this.mc = mc;
|
||||
this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
|
||||
@@ -67,7 +71,8 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
|
||||
private ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> repaymentPeriods, final TreeSet<InterestRate> interestRates,
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail,
|
||||
final LoanTermVariationsDataWrapper loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
final Map<LoanTermVariationType, List<LoanTermVariationsData>> loanTermVariations, final Integer installmentAmountInMultiplesOf,
|
||||
final MathContext mc) {
|
||||
this.mc = mc;
|
||||
this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods,
|
||||
(previousPeriod, repaymentPeriod) -> new RepaymentPeriod(previousPeriod, repaymentPeriod, mc));
|
||||
@@ -343,4 +348,12 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
: date.isAfter(previousInterestPeriod.getDueDate()) ? previousInterestPeriod.getDueDate() : date;
|
||||
}
|
||||
|
||||
private Map<LoanTermVariationType, List<LoanTermVariationsData>> buildLoanTermVariationMap(
|
||||
final List<LoanTermVariationsData> loanTermVariationsData) {
|
||||
if (loanTermVariationsData == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return loanTermVariationsData.stream()
|
||||
.collect(Collectors.groupingBy(ltvd -> LoanTermVariationType.fromInt(ltvd.getTermType().getId().intValue())));
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -37,6 +37,7 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
@@ -88,9 +89,12 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
// generate list of proposed schedule due dates
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = scheduledDateGenerator.generateRepaymentPeriods(mc,
|
||||
periodStartDate, loanApplicationTerms, holidayDetailDTO);
|
||||
List<LoanTermVariationsData> loanTermVariations = loanApplicationTerms.getLoanTermVariations() != null
|
||||
? loanApplicationTerms.getLoanTermVariations().getExceptionData()
|
||||
: null;
|
||||
final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanApplicationTerms.toLoanProductRelatedDetailMinimumData(),
|
||||
loanApplicationTerms.getLoanTermVariations(), loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc);
|
||||
expectedRepaymentPeriods, loanApplicationTerms.toLoanProductRelatedDetailMinimumData(), loanTermVariations,
|
||||
loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc);
|
||||
final List<LoanScheduleModelPeriod> periods = new ArrayList<>(expectedRepaymentPeriods.size());
|
||||
|
||||
prepareDisbursementsOnLoanApplicationTerms(loanApplicationTerms);
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@ import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OutstandingDetails;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.PeriodDueDetails;
|
||||
@@ -39,13 +39,13 @@ public interface EMICalculator {
|
||||
@NotNull
|
||||
ProgressiveLoanInterestScheduleModel generatePeriodInterestScheduleModel(@NotNull List<LoanScheduleModelRepaymentPeriod> periods,
|
||||
@NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail,
|
||||
LoanTermVariationsDataWrapper loanTermVariations, Integer installmentAmountInMultiplesOf, MathContext mc);
|
||||
List<LoanTermVariationsData> loanTermVariations, Integer installmentAmountInMultiplesOf, MathContext mc);
|
||||
|
||||
@NotNull
|
||||
ProgressiveLoanInterestScheduleModel generateInstallmentInterestScheduleModel(
|
||||
@NotNull List<LoanRepaymentScheduleInstallment> installments,
|
||||
@NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail,
|
||||
LoanTermVariationsDataWrapper loanTermVariations, Integer installmentAmountInMultiplesOf, MathContext mc);
|
||||
List<LoanTermVariationsData> loanTermVariations, Integer installmentAmountInMultiplesOf, MathContext mc);
|
||||
|
||||
Optional<RepaymentPeriod> findRepaymentPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate dueDate);
|
||||
|
||||
|
||||
+129
-23
@@ -23,6 +23,8 @@ import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Year;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -35,8 +37,9 @@ 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.data.LoanTermVariationsDataWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.EmiAdjustment;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.InterestPeriod;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OutstandingDetails;
|
||||
@@ -58,7 +61,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
@NotNull
|
||||
public ProgressiveLoanInterestScheduleModel generatePeriodInterestScheduleModel(@NotNull List<LoanScheduleModelRepaymentPeriod> periods,
|
||||
@NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail,
|
||||
LoanTermVariationsDataWrapper loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
return generateInterestScheduleModel(periods, LoanScheduleModelRepaymentPeriod::periodFromDate,
|
||||
LoanScheduleModelRepaymentPeriod::periodDueDate, loanProductRelatedDetail, loanTermVariations,
|
||||
installmentAmountInMultiplesOf, mc);
|
||||
@@ -69,7 +72,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
public ProgressiveLoanInterestScheduleModel generateInstallmentInterestScheduleModel(
|
||||
@NotNull List<LoanRepaymentScheduleInstallment> installments,
|
||||
@NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail,
|
||||
LoanTermVariationsDataWrapper loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
installments = installments.stream().filter(installment -> !installment.isDownPayment() && !installment.isAdditional()).toList();
|
||||
return generateInterestScheduleModel(installments, LoanRepaymentScheduleInstallment::getFromDate,
|
||||
LoanRepaymentScheduleInstallment::getDueDate, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf,
|
||||
@@ -79,7 +82,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
@NotNull
|
||||
private <T> ProgressiveLoanInterestScheduleModel generateInterestScheduleModel(@NotNull List<T> periods, Function<T, LocalDate> from,
|
||||
Function<T, LocalDate> to, @NotNull LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail,
|
||||
LoanTermVariationsDataWrapper loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
final Money zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
|
||||
final AtomicReference<RepaymentPeriod> prev = new AtomicReference<>();
|
||||
List<RepaymentPeriod> repaymentPeriods = periods.stream().map(e -> {
|
||||
@@ -314,8 +317,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
}
|
||||
calculateOutstandingBalance(scheduleModel);
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
||||
if (onlyOnActualModelShouldApply
|
||||
&& (scheduleModel.loanTermVariations() == null || scheduleModel.loanTermVariations().getDueDateVariation().isEmpty())) {
|
||||
if (onlyOnActualModelShouldApply && (scheduleModel.loanTermVariations() == null
|
||||
|| scheduleModel.loanTermVariations().get(LoanTermVariationType.DUE_DATE) == null)) {
|
||||
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, relatedRepaymentPeriods);
|
||||
}
|
||||
}
|
||||
@@ -420,18 +423,124 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
repaymentPeriod.getInterestPeriods().forEach(interestPeriod -> {
|
||||
interestPeriod.setRateFactor(calculateRateFactorPerPeriod(scheduleModel, repaymentPeriod, interestPeriod.getFromDate(),
|
||||
interestPeriod.getDueDate(), false));
|
||||
interestPeriod.setRateFactorTillPeriodDueDate(calculateRateFactorPerPeriod(scheduleModel, repaymentPeriod,
|
||||
interestPeriod.getFromDate(), repaymentPeriod.getDueDate(), true));
|
||||
interestPeriod.getDueDate()));
|
||||
interestPeriod.setRateFactorTillPeriodDueDate(calculateRateFactorPerPeriodForInterest(scheduleModel, repaymentPeriod,
|
||||
interestPeriod.getFromDate(), repaymentPeriod.getDueDate()));
|
||||
});
|
||||
}
|
||||
|
||||
BigDecimal calculateRateFactorPerPeriodForInterest(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final RepaymentPeriod repaymentPeriod, final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate) {
|
||||
final MathContext mc = scheduleModel.mc();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail();
|
||||
final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriodFromDate),
|
||||
scheduleModel.mc());
|
||||
final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType());
|
||||
final DaysInMonthType daysInMonthType = DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType());
|
||||
final PeriodFrequencyType repaymentFrequency = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
|
||||
|
||||
final BigDecimal daysInYear = BigDecimal.valueOf(daysInYearType.getNumberOfDays(interestPeriodFromDate));
|
||||
final BigDecimal actualDaysInPeriod = BigDecimal
|
||||
.valueOf(DateUtils.getDifferenceInDays(interestPeriodFromDate, interestPeriodDueDate));
|
||||
final BigDecimal calculatedDaysInPeriod = BigDecimal
|
||||
.valueOf(DateUtils.getDifferenceInDays(repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate()));
|
||||
final int numberOfYearsDifferenceInPeriod = interestPeriodDueDate.getYear() - interestPeriodFromDate.getYear();
|
||||
final boolean partialPeriodCalculationNeeded = daysInYearType == DaysInYearType.ACTUAL && numberOfYearsDifferenceInPeriod > 0;
|
||||
|
||||
// TODO check: loanApplicationTerms.calculatePeriodsBetweenDates(startDate, endDate); // calculate period data
|
||||
// TODO review: (repayment frequency: days, weeks, years; validation day is month fix 30)
|
||||
// TODO refactor this logic to represent in interest period
|
||||
if (partialPeriodCalculationNeeded) {
|
||||
final BigDecimal cumulatedPeriodFractions = calculatePeriodFractions(interestPeriodFromDate, interestPeriodDueDate, mc);
|
||||
return rateFactorByRepaymentPartialPeriod(interestRate, BigDecimal.ONE, cumulatedPeriodFractions, BigDecimal.ONE,
|
||||
BigDecimal.ONE, mc);
|
||||
}
|
||||
|
||||
if (daysInMonthType.equals(DaysInMonthType.ACTUAL)) {
|
||||
return rateFactorByRepaymentPeriod(interestRate, actualDaysInPeriod, BigDecimal.ONE, daysInYear, BigDecimal.ONE, BigDecimal.ONE,
|
||||
mc);
|
||||
} else if (daysInMonthType.isDaysInMonth_30()) {
|
||||
BigDecimal periodRatio = switch (repaymentFrequency) {
|
||||
case YEARS -> calculatePeriodRatio(scheduleModel, repaymentPeriod, ChronoUnit.YEARS, mc);
|
||||
case MONTHS -> calculatePeriodRatio(scheduleModel, repaymentPeriod, ChronoUnit.MONTHS, mc);
|
||||
case WEEKS -> calculatePeriodRatio(scheduleModel, repaymentPeriod, ChronoUnit.WEEKS, mc);
|
||||
case DAYS -> calculatePeriodRatio(scheduleModel, repaymentPeriod, ChronoUnit.DAYS, mc);
|
||||
default -> throw new UnsupportedOperationException("Unsupported repayment frequency: " + repaymentFrequency);
|
||||
};
|
||||
|
||||
return calculateRateFactorPerPeriodBasedOnRepaymentFrequency(interestRate, repaymentFrequency, periodRatio,
|
||||
BigDecimal.valueOf(30), daysInYear, actualDaysInPeriod, calculatedDaysInPeriod, mc);
|
||||
}
|
||||
throw new UnsupportedOperationException(
|
||||
"Unsupported combination: Days in year: " + daysInYearType + ", days in month: " + daysInMonthType);
|
||||
}
|
||||
|
||||
private static BigDecimal calculatePeriodRatio(ProgressiveLoanInterestScheduleModel scheduleModel, RepaymentPeriod repaymentPeriod,
|
||||
ChronoUnit chronoUnit, MathContext mc) {
|
||||
|
||||
LocalDate seedDate = calculateSeedDate(scheduleModel, repaymentPeriod);
|
||||
int numberOfPeriodBetweenSeedDateAndActualRepaymentPeriod = switch (chronoUnit) {
|
||||
case DAYS, WEEKS, YEARS -> DateUtils.getExactDifference(seedDate, repaymentPeriod.getFromDate(), chronoUnit);
|
||||
case MONTHS -> {
|
||||
int seedDateDay = seedDate.getDayOfMonth();
|
||||
int targetDateDay = repaymentPeriod.getFromDate().getDayOfMonth();
|
||||
int targetDateLastDay = ((LocalDate) TemporalAdjusters.lastDayOfMonth().adjustInto(repaymentPeriod.getFromDate()))
|
||||
.getDayOfMonth();
|
||||
// In case target date is the last day of the month and the seed date day is later than the target date
|
||||
// day, we need to move it by 1 days
|
||||
if (targetDateLastDay == targetDateDay && seedDateDay > targetDateDay) {
|
||||
yield DateUtils.getExactDifference(seedDate, repaymentPeriod.getFromDate().plusDays(1), chronoUnit);
|
||||
} else {
|
||||
yield DateUtils.getExactDifference(seedDate, repaymentPeriod.getFromDate(), chronoUnit);
|
||||
}
|
||||
}
|
||||
default -> throw new UnsupportedOperationException("Unsupported chrono unit: " + chronoUnit);
|
||||
};
|
||||
|
||||
int multiplicator = numberOfPeriodBetweenSeedDateAndActualRepaymentPeriod + 1;
|
||||
LocalDate fromDate = repaymentPeriod.getFromDate();
|
||||
while (fromDate.isBefore(repaymentPeriod.getDueDate())) {
|
||||
fromDate = seedDate.plus(multiplicator, chronoUnit);
|
||||
if (!fromDate.isAfter(repaymentPeriod.getDueDate())) {
|
||||
multiplicator++;
|
||||
} else {
|
||||
LocalDate fullPeriodDate = fromDate;
|
||||
multiplicator = multiplicator - numberOfPeriodBetweenSeedDateAndActualRepaymentPeriod - 1;
|
||||
fromDate = seedDate.plus(multiplicator, chronoUnit);
|
||||
final long differenceInDays = DateUtils.getDifferenceInDays(fromDate, repaymentPeriod.getDueDate());
|
||||
final long fullPeriodDifferenceInDays = DateUtils.getDifferenceInDays(fromDate, fullPeriodDate);
|
||||
return BigDecimal.valueOf(differenceInDays).divide(BigDecimal.valueOf(fullPeriodDifferenceInDays), mc)
|
||||
.add(BigDecimal.valueOf(multiplicator));
|
||||
}
|
||||
}
|
||||
multiplicator = multiplicator - numberOfPeriodBetweenSeedDateAndActualRepaymentPeriod - 1;
|
||||
return BigDecimal.valueOf(multiplicator);
|
||||
}
|
||||
|
||||
private static LocalDate calculateSeedDate(ProgressiveLoanInterestScheduleModel scheduleModel, RepaymentPeriod repaymentPeriod) {
|
||||
LocalDate seedDate = scheduleModel.getStartDate();
|
||||
LocalDate calculatedDate;
|
||||
int multiplicator = 1;
|
||||
ChronoUnit chronoUnit = switch (scheduleModel.loanProductRelatedDetail().getRepaymentPeriodFrequencyType()) {
|
||||
case YEARS -> ChronoUnit.YEARS;
|
||||
case MONTHS -> ChronoUnit.MONTHS;
|
||||
case WEEKS -> ChronoUnit.WEEKS;
|
||||
case DAYS -> ChronoUnit.DAYS;
|
||||
default -> throw new UnsupportedOperationException(
|
||||
"Unsupported repayment frequency: " + scheduleModel.loanProductRelatedDetail().getRepaymentPeriodFrequencyType());
|
||||
};
|
||||
do {
|
||||
calculatedDate = seedDate.plus(multiplicator, chronoUnit);
|
||||
multiplicator++;
|
||||
} while (calculatedDate.isBefore(repaymentPeriod.getDueDate()));
|
||||
return calculatedDate.equals(repaymentPeriod.getDueDate()) ? seedDate : repaymentPeriod.getFromDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate Rate Factor for an exact Period
|
||||
*/
|
||||
private BigDecimal calculateRateFactorPerPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final RepaymentPeriod repaymentPeriod, final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate,
|
||||
final boolean isTillDate) {
|
||||
final RepaymentPeriod repaymentPeriod, final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate) {
|
||||
final MathContext mc = scheduleModel.mc();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail();
|
||||
final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriodFromDate),
|
||||
@@ -441,7 +550,6 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
final PeriodFrequencyType repaymentFrequency = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
|
||||
final BigDecimal repaymentEvery = BigDecimal.valueOf(loanProductRelatedDetail.getRepayEvery());
|
||||
|
||||
final BigDecimal daysInMonth = BigDecimal.valueOf(daysInMonthType.getNumberOfDays(interestPeriodFromDate));
|
||||
final BigDecimal daysInYear = BigDecimal.valueOf(daysInYearType.getNumberOfDays(interestPeriodFromDate));
|
||||
final BigDecimal actualDaysInPeriod = BigDecimal
|
||||
.valueOf(DateUtils.getDifferenceInDays(interestPeriodFromDate, interestPeriodDueDate));
|
||||
@@ -449,26 +557,24 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
.valueOf(DateUtils.getDifferenceInDays(repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate()));
|
||||
final int numberOfYearsDifferenceInPeriod = interestPeriodDueDate.getYear() - interestPeriodFromDate.getYear();
|
||||
final boolean partialPeriodCalculationNeeded = daysInYearType == DaysInYearType.ACTUAL && numberOfYearsDifferenceInPeriod > 0;
|
||||
|
||||
long addedDays = !isTillDate || scheduleModel.loanTermVariations() == null ? 0L
|
||||
: scheduleModel.loanTermVariations().getDueDateVariation().stream()
|
||||
.filter(x -> !repaymentPeriod.getFromDate().isAfter(x.getTermVariationApplicableFrom())
|
||||
&& !repaymentPeriod.getDueDate().isBefore(x.getTermVariationApplicableFrom())
|
||||
&& !repaymentPeriod.getDueDate().isAfter(x.getDateValue()))
|
||||
.map(x -> DateUtils.getDifferenceInDays(x.getTermVariationApplicableFrom(), x.getDateValue()))
|
||||
.reduce(0L, Long::sum);
|
||||
final BigDecimal daysInMonth = daysInMonthType.isDaysInMonth_30() ? BigDecimal.valueOf(30) : calculatedDaysInPeriod;
|
||||
|
||||
// TODO check: loanApplicationTerms.calculatePeriodsBetweenDates(startDate, endDate); // calculate period data
|
||||
// TODO review: (repayment frequency: days, weeks, years; validation day is month fix 30)
|
||||
// TODO refactor this logic to represent in interest period
|
||||
if (partialPeriodCalculationNeeded) {
|
||||
final BigDecimal cumulatedPeriodFractions = calculatePeriodFractions(interestPeriodFromDate, interestPeriodDueDate, mc);
|
||||
return rateFactorByRepaymentPartialPeriod(interestRate, repaymentEvery, cumulatedPeriodFractions, BigDecimal.ONE,
|
||||
return rateFactorByRepaymentPartialPeriod(interestRate, BigDecimal.ONE, cumulatedPeriodFractions, BigDecimal.ONE,
|
||||
BigDecimal.ONE, mc);
|
||||
}
|
||||
|
||||
return calculateRateFactorPerPeriodBasedOnRepaymentFrequency(interestRate, repaymentFrequency, repaymentEvery, daysInMonth,
|
||||
daysInYear, actualDaysInPeriod, calculatedDaysInPeriod.subtract(BigDecimal.valueOf(addedDays), mc), mc);
|
||||
return switch (daysInMonthType) {
|
||||
case ACTUAL -> rateFactorByRepaymentPeriod(interestRate, actualDaysInPeriod, BigDecimal.ONE, daysInYear, BigDecimal.ONE,
|
||||
BigDecimal.ONE, mc);
|
||||
case DAYS_30 -> calculateRateFactorPerPeriodBasedOnRepaymentFrequency(interestRate, repaymentFrequency, repaymentEvery,
|
||||
daysInMonth, daysInYear, actualDaysInPeriod, calculatedDaysInPeriod, mc);
|
||||
default -> throw new UnsupportedOperationException("Unsupported combination: Days in month: " + daysInMonthType);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+432
-68
@@ -33,7 +33,6 @@ 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.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsDataWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.InterestPeriod;
|
||||
@@ -46,7 +45,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -64,7 +62,6 @@ class ProgressiveEMICalculatorTest {
|
||||
private static MathContext mc = new MathContext(12, RoundingMode.HALF_EVEN);
|
||||
private static LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = Mockito
|
||||
.mock(LoanProductMinimumRepaymentScheduleRelatedDetail.class);
|
||||
private static LoanTermVariationsDataWrapper loanTermVariations = Mockito.mock(LoanTermVariationsDataWrapper.class);
|
||||
|
||||
private static final CurrencyData currency = new CurrencyData("USD", "USD", 2, 1, "$", "USD");
|
||||
|
||||
@@ -87,11 +84,6 @@ class ProgressiveEMICalculatorTest {
|
||||
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
Mockito.when(loanTermVariations.getDueDateVariation()).thenReturn(new ArrayList<>());
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void tearDown() {
|
||||
threadLocalContextUtil.close();
|
||||
@@ -178,7 +170,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
Assertions.assertTrue(interestScheduleModel != null);
|
||||
Assertions.assertTrue(interestScheduleModel.loanProductRelatedDetail() != null);
|
||||
@@ -217,7 +209,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -261,7 +253,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -297,7 +289,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -333,7 +325,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -381,7 +373,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -423,7 +415,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestModel = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -470,7 +462,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestModel = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -516,7 +508,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -562,7 +554,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -669,7 +661,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -740,7 +732,7 @@ class ProgressiveEMICalculatorTest {
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -817,7 +809,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
Money disbursedAmount = toMoney(100);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -866,7 +858,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -915,7 +907,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 5), disbursedAmount);
|
||||
@@ -945,10 +937,10 @@ class ProgressiveEMICalculatorTest {
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2023, 12, 12), LocalDate.of(2024, 1, 12)),
|
||||
repayment(2, LocalDate.of(2024, 1, 12), LocalDate.of(2024, 2, 12)),
|
||||
repayment(3, LocalDate.of(2024, 2, 12), LocalDate.of(2024, 3, 1)),
|
||||
repayment(4, LocalDate.of(2024, 3, 12), LocalDate.of(2024, 4, 1)),
|
||||
repayment(5, LocalDate.of(2024, 4, 12), LocalDate.of(2024, 5, 1)),
|
||||
repayment(6, LocalDate.of(2024, 5, 12), LocalDate.of(2024, 6, 1)));
|
||||
repayment(3, LocalDate.of(2024, 2, 12), LocalDate.of(2024, 3, 12)),
|
||||
repayment(4, LocalDate.of(2024, 3, 12), LocalDate.of(2024, 4, 12)),
|
||||
repayment(5, LocalDate.of(2024, 4, 12), LocalDate.of(2024, 5, 12)),
|
||||
repayment(6, LocalDate.of(2024, 5, 12), LocalDate.of(2024, 6, 12)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(9.4822);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
@@ -961,7 +953,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2023, 12, 12), disbursedAmount);
|
||||
@@ -997,7 +989,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), toMoney(300.0));
|
||||
Assertions.assertEquals(6.15, toDouble(interestSchedule.getTotalDueInterest()));
|
||||
@@ -1039,7 +1031,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(1000.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -1072,7 +1064,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -1105,7 +1097,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -1132,13 +1124,13 @@ class ProgressiveEMICalculatorTest {
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.INVALID.getValue());
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.DAYS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(15);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -1153,7 +1145,7 @@ class ProgressiveEMICalculatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void soma_test_disbursedAmt1000_dayInYears360_daysInMonthDoesntMatter_repayEvery15Days() {
|
||||
public void test_disbursedAmt1000_dayInYears360_daysInMonthDoesntMatter_repayEvery15Days() {
|
||||
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1)),
|
||||
@@ -1180,7 +1172,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(1000.0);
|
||||
LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
|
||||
@@ -1228,7 +1220,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestModel = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(1000.0);
|
||||
emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -1290,7 +1282,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestModel = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount1st = toMoney(1000.0);
|
||||
final Money disbursedAmount2nd = toMoney(1000.0);
|
||||
@@ -1363,7 +1355,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -1401,7 +1393,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
@@ -1437,6 +1429,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
@@ -1475,6 +1468,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
@@ -1513,6 +1507,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
@@ -1551,6 +1546,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
@@ -1589,6 +1585,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
@@ -1630,6 +1627,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
@@ -1674,6 +1672,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
@@ -1714,6 +1713,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
@@ -1751,24 +1751,24 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanTermVariations.getDueDateVariation()).thenReturn(new ArrayList<>(List.of(//
|
||||
List<LoanTermVariationsData> loanTermVariationsData = List.of(//
|
||||
new LoanTermVariationsData(1L, LoanTermVariationType.DUE_DATE.getValue(), //
|
||||
LocalDate.of(2024, 2, 1), null, //
|
||||
LocalDate.of(2024, 3, 1), false))));
|
||||
LocalDate.of(2024, 3, 1), false));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariationsData, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 1.13, 15.88, 84.12);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 1.13, 15.88, 84.12);
|
||||
checkPeriod(interestSchedule, 1, 0, 17.01, 0.005833333333, 0.49, 16.52, 67.60);
|
||||
checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.39, 16.62, 50.98);
|
||||
checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.30, 16.71, 34.27);
|
||||
checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.20, 16.81, 17.46);
|
||||
checkPeriod(interestSchedule, 5, 0, 17.56, 0.005833333333, 0.10, 17.46, 0.0);
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 1.17, 15.84, 84.16);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 1.17, 15.84, 84.16);
|
||||
checkPeriod(interestSchedule, 1, 0, 17.01, 0.005833333333, 0.49, 16.52, 67.64);
|
||||
checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.39, 16.62, 51.02);
|
||||
checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.30, 16.71, 34.31);
|
||||
checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.20, 16.81, 17.50);
|
||||
checkPeriod(interestSchedule, 5, 0, 17.60, 0.005833333333, 0.10, 17.50, 0.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1790,27 +1790,27 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanTermVariations.getDueDateVariation()).thenReturn(new ArrayList<>(List.of(//
|
||||
List<LoanTermVariationsData> loanTermVariationsData = List.of(//
|
||||
new LoanTermVariationsData(1L, LoanTermVariationType.DUE_DATE.getValue(), //
|
||||
LocalDate.of(2024, 2, 1), null, //
|
||||
LocalDate.of(2024, 3, 1), false), //
|
||||
new LoanTermVariationsData(2L, LoanTermVariationType.DUE_DATE.getValue(), //
|
||||
LocalDate.of(2024, 5, 1), null, //
|
||||
LocalDate.of(2024, 6, 1), false))));
|
||||
LocalDate.of(2024, 6, 1), false));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariationsData, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 1.13, 15.88, 84.12);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 1.13, 15.88, 84.12);
|
||||
checkPeriod(interestSchedule, 1, 0, 17.01, 0.005833333333, 0.49, 16.52, 67.60);
|
||||
checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.80, 16.21, 51.39);
|
||||
checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.30, 16.71, 34.68);
|
||||
checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.20, 16.81, 17.87);
|
||||
checkPeriod(interestSchedule, 5, 0, 17.97, 0.005833333333, 0.10, 17.87, 0.0);
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 1.17, 15.84, 84.16);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 1.17, 15.84, 84.16);
|
||||
checkPeriod(interestSchedule, 1, 0, 17.01, 0.005833333333, 0.49, 16.52, 67.64);
|
||||
checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.79, 16.22, 51.42);
|
||||
checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.30, 16.71, 34.71);
|
||||
checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.20, 16.81, 17.90);
|
||||
checkPeriod(interestSchedule, 5, 0, 18.00, 0.005833333333, 0.10, 17.90, 0.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1832,24 +1832,388 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanTermVariations.getDueDateVariation()).thenReturn(new ArrayList<>(List.of(//
|
||||
List<LoanTermVariationsData> loanTermVariationsData = List.of(//
|
||||
new LoanTermVariationsData(1L, LoanTermVariationType.DUE_DATE.getValue(), //
|
||||
LocalDate.of(2024, 3, 1), null, //
|
||||
LocalDate.of(2024, 4, 15), false))));
|
||||
LocalDate.of(2024, 4, 15), false));
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc);
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariationsData, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 1, 0, 17.01, 0.005833333333, 1.24, 15.77, 67.80);
|
||||
checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.40, 16.61, 51.19);
|
||||
checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.30, 16.71, 34.48);
|
||||
checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.20, 16.81, 17.67);
|
||||
checkPeriod(interestSchedule, 5, 0, 17.77, 0.005833333333, 0.10, 17.67, 0.0);
|
||||
checkPeriod(interestSchedule, 1, 0, 17.01, 0.005833333333, 1.20, 15.81, 67.76);
|
||||
checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.40, 16.61, 51.15);
|
||||
checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.30, 16.71, 34.44);
|
||||
checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.20, 16.81, 17.63);
|
||||
checkPeriod(interestSchedule, 5, 0, 17.73, 0.005833333333, 0.10, 17.63, 0.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_actual_actual_repayment_schedule_across_multiple_years() {
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_UP);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2023, 11, 13), LocalDate.of(2023, 12, 13)),
|
||||
repayment(2, LocalDate.of(2023, 12, 13), LocalDate.of(2024, 1, 13)),
|
||||
repayment(3, LocalDate.of(2024, 1, 13), LocalDate.of(2024, 2, 13)),
|
||||
repayment(4, LocalDate.of(2024, 2, 13), LocalDate.of(2024, 3, 13)),
|
||||
repayment(5, LocalDate.of(2024, 3, 13), LocalDate.of(2024, 4, 13)),
|
||||
repayment(6, LocalDate.of(2024, 4, 13), LocalDate.of(2024, 5, 13)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(9.99);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).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);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(5000.0);
|
||||
LocalDate disbursementDate = LocalDate.of(2023, 11, 13);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 857.71, 41.05, 816.66, 4183.34, false);
|
||||
checkPeriod(interestSchedule, 1, 857.71, 35.45, 822.26, 3361.08, false);
|
||||
checkPeriod(interestSchedule, 2, 857.71, 28.44, 829.27, 2531.81, false);
|
||||
checkPeriod(interestSchedule, 3, 857.71, 20.04, 837.67, 1694.14, false);
|
||||
checkPeriod(interestSchedule, 4, 857.71, 14.33, 843.38, 850.76, false);
|
||||
checkPeriod(interestSchedule, 5, 857.73, 6.97, 850.76, 0, false);
|
||||
|
||||
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
|
||||
Money.of(currency, BigDecimal.valueOf(820.76)));
|
||||
emiCalculator.payInterest(interestSchedule, LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
|
||||
Money.of(currency, BigDecimal.valueOf(36.95)));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 857.71, 36.95, 820.76, 4179.24, true);
|
||||
checkPeriod(interestSchedule, 1, 857.71, 38.85, 818.86, 3360.38, false);
|
||||
checkPeriod(interestSchedule, 2, 857.71, 28.43, 829.28, 2531.1, false);
|
||||
checkPeriod(interestSchedule, 3, 857.71, 20.04, 837.67, 1693.43, false);
|
||||
checkPeriod(interestSchedule, 4, 857.71, 14.33, 843.38, 850.05, false);
|
||||
checkPeriod(interestSchedule, 5, 857.01, 6.96, 850.05, 0, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_actual_actual_repayment_schedule_across_multiple_years_overdue() {
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_UP);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2023, 11, 13), LocalDate.of(2023, 12, 13)),
|
||||
repayment(2, LocalDate.of(2023, 12, 13), LocalDate.of(2024, 1, 13)),
|
||||
repayment(3, LocalDate.of(2024, 1, 13), LocalDate.of(2024, 2, 13)),
|
||||
repayment(4, LocalDate.of(2024, 2, 13), LocalDate.of(2024, 3, 13)),
|
||||
repayment(5, LocalDate.of(2024, 3, 13), LocalDate.of(2024, 4, 13)),
|
||||
repayment(6, LocalDate.of(2024, 4, 13), LocalDate.of(2024, 5, 13)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(9.99);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).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);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(5000.0);
|
||||
LocalDate disbursementDate = LocalDate.of(2023, 11, 13);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
LocalDate dueDate = expectedRepaymentPeriods.get(i).getDueDate();
|
||||
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, dueDate, dueDate);
|
||||
Money duePrincipal = dueAmounts.getDuePrincipal();
|
||||
emiCalculator.addBalanceCorrection(interestSchedule, dueDate, duePrincipal);
|
||||
}
|
||||
|
||||
checkPeriod(interestSchedule, 0, 857.71, 41.05, 816.66, 5000.0, false);
|
||||
checkPeriod(interestSchedule, 1, 857.71, 42.37, 815.34, 5000.0, false);
|
||||
checkPeriod(interestSchedule, 2, 857.71, 42.31, 815.4, 5000.0, false);
|
||||
checkPeriod(interestSchedule, 3, 857.71, 39.58, 818.13, 5000.0, false);
|
||||
checkPeriod(interestSchedule, 4, 857.71, 42.31, 815.4, 5000.0, false);
|
||||
checkPeriod(interestSchedule, 5, 960.01, 40.94, 919.07, 5000.0, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_repayment_actual_actual_repayment_schedule_across_multiple_years_overdue() {
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_UP);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2023, 11, 13), LocalDate.of(2023, 12, 13)),
|
||||
repayment(2, LocalDate.of(2023, 12, 13), LocalDate.of(2024, 1, 13)),
|
||||
repayment(3, LocalDate.of(2024, 1, 13), LocalDate.of(2024, 2, 13)),
|
||||
repayment(4, LocalDate.of(2024, 2, 13), LocalDate.of(2024, 3, 13)),
|
||||
repayment(5, LocalDate.of(2024, 3, 13), LocalDate.of(2024, 4, 13)),
|
||||
repayment(6, LocalDate.of(2024, 4, 13), LocalDate.of(2024, 5, 13)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(9.99);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).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);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(5000.0);
|
||||
LocalDate disbursementDate = LocalDate.of(2023, 11, 13);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
|
||||
|
||||
emiCalculator.payInterest(interestSchedule, LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
|
||||
Money.of(currency, BigDecimal.valueOf(36.95)));
|
||||
emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2023, 12, 13), LocalDate.of(2023, 12, 10),
|
||||
Money.of(currency, BigDecimal.valueOf(820.76)));
|
||||
|
||||
for (int i = 1; i < 6; i++) {
|
||||
LocalDate dueDate = expectedRepaymentPeriods.get(i).getDueDate();
|
||||
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, dueDate, dueDate);
|
||||
Money duePrincipal = dueAmounts.getDuePrincipal();
|
||||
emiCalculator.addBalanceCorrection(interestSchedule, dueDate, duePrincipal);
|
||||
}
|
||||
|
||||
checkPeriod(interestSchedule, 0, 857.71, 36.95, 820.76, 4179.24, true);
|
||||
checkPeriod(interestSchedule, 1, 857.71, 38.85, 818.86, 4179.24, false);
|
||||
checkPeriod(interestSchedule, 2, 857.71, 35.36, 822.35, 4179.24, false);
|
||||
checkPeriod(interestSchedule, 3, 857.71, 33.08, 824.63, 4179.24, false);
|
||||
checkPeriod(interestSchedule, 4, 857.71, 35.36, 822.35, 4179.24, false);
|
||||
checkPeriod(interestSchedule, 5, 925.27, 34.22, 891.05, 4179.24, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_360_30_repayment_schedule_disbursement_month_end() {
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_UP);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2023, 10, 31), LocalDate.of(2023, 11, 30)),
|
||||
repayment(2, LocalDate.of(2023, 11, 30), LocalDate.of(2023, 12, 31)),
|
||||
repayment(3, LocalDate.of(2023, 12, 31), LocalDate.of(2024, 1, 31)),
|
||||
repayment(4, LocalDate.of(2024, 1, 31), LocalDate.of(2024, 2, 29)),
|
||||
repayment(5, LocalDate.of(2024, 2, 29), LocalDate.of(2024, 3, 31)),
|
||||
repayment(6, LocalDate.of(2024, 3, 31), LocalDate.of(2024, 4, 30)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(9.99);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).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.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(2450.0);
|
||||
LocalDate disbursementDate = LocalDate.of(2023, 10, 31);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 420.31, 20.40, 399.91, 2050.09, false);
|
||||
checkPeriod(interestSchedule, 1, 420.31, 17.07, 403.24, 1646.85, false);
|
||||
checkPeriod(interestSchedule, 2, 420.31, 13.71, 406.60, 1240.25, false);
|
||||
checkPeriod(interestSchedule, 3, 420.31, 10.33, 409.98, 830.27, false);
|
||||
checkPeriod(interestSchedule, 4, 420.31, 6.91, 413.40, 416.87, false);
|
||||
checkPeriod(interestSchedule, 5, 420.34, 3.47, 416.87, 0.00, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_360_30_repayment_schedule_disbursement_near_month_end() {
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_UP);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2023, 10, 30), LocalDate.of(2023, 11, 30)),
|
||||
repayment(2, LocalDate.of(2023, 11, 30), LocalDate.of(2023, 12, 30)),
|
||||
repayment(3, LocalDate.of(2023, 12, 30), LocalDate.of(2024, 1, 30)),
|
||||
repayment(4, LocalDate.of(2024, 1, 30), LocalDate.of(2024, 2, 29)),
|
||||
repayment(5, LocalDate.of(2024, 2, 29), LocalDate.of(2024, 3, 30)),
|
||||
repayment(6, LocalDate.of(2024, 3, 30), LocalDate.of(2024, 4, 30)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(45.0);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).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.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(10000.0);
|
||||
LocalDate disbursementDate = LocalDate.of(2023, 10, 30);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 1892.12, 375.00, 1517.12, 8482.88, false);
|
||||
checkPeriod(interestSchedule, 1, 1892.12, 318.11, 1574.01, 6908.87, false);
|
||||
checkPeriod(interestSchedule, 2, 1892.12, 259.08, 1633.04, 5275.83, false);
|
||||
checkPeriod(interestSchedule, 3, 1892.12, 197.84, 1694.28, 3581.55, false);
|
||||
checkPeriod(interestSchedule, 4, 1892.12, 134.31, 1757.81, 1823.74, false);
|
||||
checkPeriod(interestSchedule, 5, 1892.13, 68.39, 1823.74, 0.00, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_360_30_repayment_schedule_disbursement_repay_every_2_months() {
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_UP);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2023, 10, 29), LocalDate.of(2023, 12, 29)),
|
||||
repayment(2, LocalDate.of(2023, 12, 29), LocalDate.of(2024, 2, 29)),
|
||||
repayment(3, LocalDate.of(2024, 2, 29), LocalDate.of(2024, 4, 29)),
|
||||
repayment(4, LocalDate.of(2024, 4, 29), LocalDate.of(2024, 6, 29)),
|
||||
repayment(5, LocalDate.of(2024, 6, 29), LocalDate.of(2024, 8, 29)),
|
||||
repayment(6, LocalDate.of(2024, 8, 29), LocalDate.of(2024, 10, 29)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(19.99);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).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(2);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(3333.0);
|
||||
LocalDate disbursementDate = LocalDate.of(2023, 10, 29);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 622.04, 111.04, 511.00, 2822.00, false);
|
||||
checkPeriod(interestSchedule, 1, 622.04, 94.02, 528.02, 2293.98, false);
|
||||
checkPeriod(interestSchedule, 2, 622.04, 76.43, 545.61, 1748.37, false);
|
||||
checkPeriod(interestSchedule, 3, 622.04, 58.25, 563.79, 1184.58, false);
|
||||
checkPeriod(interestSchedule, 4, 622.04, 39.47, 582.57, 602.01, false);
|
||||
checkPeriod(interestSchedule, 5, 622.07, 20.06, 602.01, 0.00, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_actual_actual_repayment_schedule_disbursement_month_end() {
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_UP);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2023, 10, 31), LocalDate.of(2023, 11, 30)),
|
||||
repayment(2, LocalDate.of(2023, 11, 30), LocalDate.of(2023, 12, 31)),
|
||||
repayment(3, LocalDate.of(2023, 12, 31), LocalDate.of(2024, 1, 31)),
|
||||
repayment(4, LocalDate.of(2024, 1, 31), LocalDate.of(2024, 2, 29)),
|
||||
repayment(5, LocalDate.of(2024, 2, 29), LocalDate.of(2024, 3, 31)),
|
||||
repayment(6, LocalDate.of(2024, 3, 31), LocalDate.of(2024, 4, 30)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(45.0);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).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);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(245000.0);
|
||||
LocalDate disbursementDate = LocalDate.of(2023, 10, 31);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 46343.27, 9061.64, 37281.63, 207718.37, false);
|
||||
checkPeriod(interestSchedule, 1, 46343.27, 7938.83, 38404.44, 169313.93, false);
|
||||
checkPeriod(interestSchedule, 2, 46343.27, 6453.36, 39889.91, 129424.02, false);
|
||||
checkPeriod(interestSchedule, 3, 46343.27, 4614.71, 41728.56, 87695.46, false);
|
||||
checkPeriod(interestSchedule, 4, 46343.27, 3342.49, 43000.78, 44694.68, false);
|
||||
checkPeriod(interestSchedule, 5, 46343.25, 1648.57, 44694.68, 0.00, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_actual_actual_repayment_schedule_disbursement_near_month_end() {
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_UP);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2021, 10, 30), LocalDate.of(2021, 11, 30)),
|
||||
repayment(2, LocalDate.of(2021, 11, 30), LocalDate.of(2021, 12, 30)),
|
||||
repayment(3, LocalDate.of(2021, 12, 30), LocalDate.of(2022, 1, 30)),
|
||||
repayment(4, LocalDate.of(2022, 1, 30), LocalDate.of(2022, 2, 28)),
|
||||
repayment(5, LocalDate.of(2022, 2, 28), LocalDate.of(2022, 3, 30)),
|
||||
repayment(6, LocalDate.of(2022, 3, 30), LocalDate.of(2022, 4, 30)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(9.4822);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).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);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(1500.0);
|
||||
LocalDate disbursementDate = LocalDate.of(2021, 10, 30);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 256.95, 12.08, 244.87, 1255.13, false);
|
||||
checkPeriod(interestSchedule, 1, 256.95, 9.78, 247.17, 1007.96, false);
|
||||
checkPeriod(interestSchedule, 2, 256.95, 8.12, 248.83, 759.13, false);
|
||||
checkPeriod(interestSchedule, 3, 256.95, 5.72, 251.23, 507.90, false);
|
||||
checkPeriod(interestSchedule, 4, 256.95, 3.96, 252.99, 254.91, false);
|
||||
checkPeriod(interestSchedule, 5, 256.96, 2.05, 254.91, 0.00, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_actual_actual_repayment_schedule_disbursement_near_month_end_repay_every_2_months() {
|
||||
MathContext mc = new MathContext(12, RoundingMode.HALF_UP);
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
|
||||
repayment(1, LocalDate.of(2022, 10, 29), LocalDate.of(2022, 12, 29)),
|
||||
repayment(2, LocalDate.of(2022, 12, 29), LocalDate.of(2023, 2, 28)),
|
||||
repayment(3, LocalDate.of(2023, 2, 28), LocalDate.of(2023, 4, 29)),
|
||||
repayment(4, LocalDate.of(2023, 4, 29), LocalDate.of(2023, 6, 29)),
|
||||
repayment(5, LocalDate.of(2023, 6, 29), LocalDate.of(2023, 8, 29)),
|
||||
repayment(6, LocalDate.of(2023, 8, 29), LocalDate.of(2023, 10, 29)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(7);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).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);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
|
||||
|
||||
final Money disbursedAmount = toMoney(5000);
|
||||
LocalDate disbursementDate = LocalDate.of(2022, 10, 29);
|
||||
|
||||
emiCalculator.addDisbursement(interestSchedule, disbursementDate, disbursedAmount);
|
||||
|
||||
checkPeriod(interestSchedule, 0, 867.68, 58.49, 809.19, 4190.81, false);
|
||||
checkPeriod(interestSchedule, 1, 867.68, 49.03, 818.65, 3372.16, false);
|
||||
checkPeriod(interestSchedule, 2, 867.68, 38.80, 828.88, 2543.28, false);
|
||||
checkPeriod(interestSchedule, 3, 867.68, 29.75, 837.93, 1705.35, false);
|
||||
checkPeriod(interestSchedule, 4, 867.68, 19.95, 847.73, 857.62, false);
|
||||
checkPeriod(interestSchedule, 5, 867.65, 10.03, 857.62, 0.00, false);
|
||||
}
|
||||
|
||||
private static LoanScheduleModelRepaymentPeriod repayment(int periodNumber, LocalDate fromDate, LocalDate dueDate) {
|
||||
|
||||
Reference in New Issue
Block a user