FINERACT-1981: Remove hardcoded MoneyHelper dependency during progressive loan

This commit is contained in:
Andrii Kulminskyi
2024-10-22 18:45:28 +02:00
committed by Adam Saghy
parent 806ee69798
commit a9ecdf97a4
31 changed files with 536 additions and 355 deletions
@@ -176,7 +176,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
final Integer installmentAmountInMultiplesOf = loan.getLoanProduct().getInstallmentAmountInMultiplesOf();
final LoanProductRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
ProgressiveLoanInterestScheduleModel scheduleModel = emiCalculator.generateModel(loanProductRelatedDetail,
installmentAmountInMultiplesOf, installments);
installmentAmountInMultiplesOf, installments, overpaymentHolder.getMoneyObject().getMc());
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(currency, installments, charges, overpaymentHolder,
changedTransactionDetail, scheduleModel);
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.util.Optional;
import lombok.EqualsAndHashCode;
@@ -44,9 +45,10 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
private Money disbursementAmount;
private Money balanceCorrectionAmount;
private Money outstandingLoanBalance;
private final MathContext mc;
public InterestPeriod(RepaymentPeriod repaymentPeriod, LocalDate fromDate, LocalDate dueDate, BigDecimal rateFactor,
Money disbursementAmount, Money balanceCorrectionAmount, Money outstandingLoanBalance) {
Money disbursementAmount, Money balanceCorrectionAmount, Money outstandingLoanBalance, MathContext mc) {
this.repaymentPeriod = repaymentPeriod;
this.fromDate = fromDate;
this.dueDate = dueDate;
@@ -54,12 +56,13 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
this.disbursementAmount = disbursementAmount;
this.balanceCorrectionAmount = balanceCorrectionAmount;
this.outstandingLoanBalance = outstandingLoanBalance;
this.mc = mc;
}
public InterestPeriod(RepaymentPeriod repaymentPeriod, InterestPeriod interestPeriod) {
public InterestPeriod(RepaymentPeriod repaymentPeriod, InterestPeriod interestPeriod, MathContext mc) {
this(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(), interestPeriod.getRateFactor(),
interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
interestPeriod.getOutstandingLoanBalance());
interestPeriod.getOutstandingLoanBalance(), mc);
}
@Override
@@ -72,11 +75,11 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
}
public void addDisbursementAmount(final Money disbursementAmount) {
this.disbursementAmount = MathUtil.plus(this.disbursementAmount, disbursementAmount);
this.disbursementAmount = MathUtil.plus(this.disbursementAmount, disbursementAmount, mc);
}
public Money getCalculatedDueInterest() {
return getOutstandingLoanBalance().multipliedBy(getRateFactor());
return getOutstandingLoanBalance().multipliedBy(getRateFactor(), mc);
}
public void updateOutstandingLoanBalance() {
@@ -86,17 +89,17 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
InterestPeriod previousInterestPeriod = previousRepaymentPeriod.get().getInterestPeriods()
.get(previousRepaymentPeriod.get().getInterestPeriods().size() - 1);
this.outstandingLoanBalance = previousInterestPeriod.getOutstandingLoanBalance()//
.plus(previousInterestPeriod.getDisbursementAmount())//
.plus(previousInterestPeriod.getBalanceCorrectionAmount())//
.minus(previousRepaymentPeriod.get().getDuePrincipal())//
.plus(previousRepaymentPeriod.get().getPaidPrincipal());//
.plus(previousInterestPeriod.getDisbursementAmount(), mc)//
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc)//
.minus(previousRepaymentPeriod.get().getDuePrincipal(), mc)//
.plus(previousRepaymentPeriod.get().getPaidPrincipal(), mc);//
}
} else {
int index = getRepaymentPeriod().getInterestPeriods().indexOf(this);
InterestPeriod previousInterestPeriod = getRepaymentPeriod().getInterestPeriods().get(index - 1);
this.outstandingLoanBalance = previousInterestPeriod.getOutstandingLoanBalance() //
.plus(previousInterestPeriod.getBalanceCorrectionAmount()) //
.plus(previousInterestPeriod.getDisbursementAmount()); //
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc) //
.plus(previousInterestPeriod.getDisbursementAmount(), mc); //
}
}
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
@@ -38,33 +39,36 @@ public class ProgressiveLoanInterestScheduleModel {
private final List<InterestRate> interestRates;
private final LoanProductRelatedDetail loanProductRelatedDetail;
private final Integer installmentAmountInMultiplesOf;
private MathContext mc;
public ProgressiveLoanInterestScheduleModel(List<RepaymentPeriod> repaymentPeriods, LoanProductRelatedDetail loanProductRelatedDetail,
Integer installmentAmountInMultiplesOf) {
Integer installmentAmountInMultiplesOf, MathContext mc) {
this.repaymentPeriods = repaymentPeriods;
this.interestRates = new ArrayList<>();
this.loanProductRelatedDetail = loanProductRelatedDetail;
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
this.mc = mc;
}
private ProgressiveLoanInterestScheduleModel(List<RepaymentPeriod> repaymentPeriods, final List<InterestRate> interestRates,
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf) {
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, MathContext mc) {
this.mc = mc;
this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods);
this.interestRates = new ArrayList<>(interestRates);
this.loanProductRelatedDetail = loanProductRelatedDetail;
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
}
public ProgressiveLoanInterestScheduleModel deepCopy() {
public ProgressiveLoanInterestScheduleModel deepCopy(MathContext mc) {
return new ProgressiveLoanInterestScheduleModel(repaymentPeriods, interestRates, loanProductRelatedDetail,
installmentAmountInMultiplesOf);
installmentAmountInMultiplesOf, mc);
}
private List<RepaymentPeriod> copyRepaymentPeriods(final List<RepaymentPeriod> repaymentPeriods) {
final List<RepaymentPeriod> repaymentCopies = new ArrayList<>(repaymentPeriods.size());
RepaymentPeriod previousPeriod = null;
for (RepaymentPeriod repaymentPeriod : repaymentPeriods) {
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, repaymentPeriod);
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, repaymentPeriod, mc);
previousPeriod = currentPeriod;
repaymentCopies.add(currentPeriod);
}
@@ -175,11 +179,11 @@ public class ProgressiveLoanInterestScheduleModel {
previousInterestPeriod.addDisbursementAmount(disbursedAmount);
previousInterestPeriod.addBalanceCorrectionAmount(correctionAmount);
final InterestPeriod interestPeriod = new InterestPeriod(repaymentPeriod, previousInterestPeriod.getDueDate(), originalDueDate,
BigDecimal.ZERO, getZero(), getZero(), getZero());
BigDecimal.ZERO, getZero(mc), getZero(mc), getZero(mc), mc);
repaymentPeriod.getInterestPeriods().add(interestPeriod);
}
private Money getZero() {
return Money.zero(loanProductRelatedDetail.getCurrency());
private Money getZero(MathContext mc) {
return Money.zero(loanProductRelatedDetail.getCurrency(), mc);
}
}
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanaccount.loanschedule.data;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@@ -54,20 +55,23 @@ public class RepaymentPeriod {
private Memo<Money> calculatedDueInterestCalculation;
private Memo<Money> dueInterestCalculation;
private Memo<Money> outstandingBalanceCalculation;
private final MathContext mc;
public RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, Money emi) {
public RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, Money emi, MathContext mc) {
this.previous = previous;
this.fromDate = fromDate;
this.dueDate = dueDate;
this.emi = emi;
this.mc = mc;
this.interestPeriods = new ArrayList<>();
// There is always at least 1 interest period, by default with same from-due date as repayment period
getInterestPeriods().add(new InterestPeriod(this, getFromDate(), getDueDate(), BigDecimal.ZERO, getZero(), getZero(), getZero()));
this.paidInterest = getZero();
this.paidPrincipal = getZero();
getInterestPeriods()
.add(new InterestPeriod(this, getFromDate(), getDueDate(), BigDecimal.ZERO, getZero(mc), getZero(mc), getZero(mc), mc));
this.paidInterest = getZero(mc);
this.paidPrincipal = getZero(mc);
}
public RepaymentPeriod(RepaymentPeriod previous, RepaymentPeriod repaymentPeriod) {
public RepaymentPeriod(RepaymentPeriod previous, RepaymentPeriod repaymentPeriod, MathContext mc) {
this.previous = previous;
this.fromDate = repaymentPeriod.fromDate;
this.dueDate = repaymentPeriod.dueDate;
@@ -75,9 +79,10 @@ public class RepaymentPeriod {
this.interestPeriods = new ArrayList<>();
this.paidPrincipal = repaymentPeriod.paidPrincipal;
this.paidInterest = repaymentPeriod.paidInterest;
this.mc = mc;
// There is always at least 1 interest period, by default with same from-due date as repayment period
for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) {
interestPeriods.add(new InterestPeriod(this, interestPeriod));
interestPeriods.add(new InterestPeriod(this, interestPeriod, mc));
}
}
@@ -105,21 +110,21 @@ public class RepaymentPeriod {
}
private Money calculateCalculatedDueInterest() {
Money calculatedDueInterest = getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest).reduce(getZero(),
Money::plus);
Money calculatedDueInterest = getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest).reduce(getZero(mc),
(m1, m2) -> m1.plus(m2, mc));
if (getPrevious().isPresent()) {
calculatedDueInterest = calculatedDueInterest.add(getPrevious().get().getUnrecognizedInterest());
calculatedDueInterest = calculatedDueInterest.add(getPrevious().get().getUnrecognizedInterest(), mc);
}
return calculatedDueInterest;
}
private Money getZero() {
private Money getZero(MathContext mc) {
// EMI is always initiated
return this.emi.zero();
return this.emi.zero(mc);
}
public Money getCalculatedDuePrincipal() {
return getEmi().minus(getCalculatedDueInterest());
return getEmi().minus(getCalculatedDueInterest(), mc);
}
public boolean isFullyPaid() {
@@ -138,11 +143,11 @@ public class RepaymentPeriod {
public Money getDuePrincipal() {
// Due principal might be the maximum paid if there is pay-off or early repayment
return MathUtil.max(getEmi().minus(getDueInterest()), getPaidPrincipal(), false);
return MathUtil.max(getEmi().minus(getDueInterest(), mc), getPaidPrincipal(), false);
}
public Money getUnrecognizedInterest() {
return getCalculatedDueInterest().minus(getDueInterest());
return getCalculatedDueInterest().minus(getDueInterest(), mc);
}
public Money getOutstandingLoanBalance() {
@@ -150,22 +155,22 @@ public class RepaymentPeriod {
outstandingBalanceCalculation = Memo.of(() -> {
InterestPeriod lastInstallmentPeriod = getInterestPeriods().get(getInterestPeriods().size() - 1);
Money calculatedOutStandingLoanBalance = lastInstallmentPeriod.getOutstandingLoanBalance() //
.plus(lastInstallmentPeriod.getBalanceCorrectionAmount()) //
.plus(lastInstallmentPeriod.getDisbursementAmount()) //
.minus(getDuePrincipal())//
.plus(getPaidPrincipal());//
return MathUtil.negativeToZero(calculatedOutStandingLoanBalance);
.plus(lastInstallmentPeriod.getBalanceCorrectionAmount(), mc) //
.plus(lastInstallmentPeriod.getDisbursementAmount(), mc) //
.minus(getDuePrincipal(), mc)//
.plus(getPaidPrincipal(), mc);//
return MathUtil.negativeToZero(calculatedOutStandingLoanBalance, mc);
}, () -> new Object[] { paidPrincipal, paidInterest, interestPeriods });
}
return outstandingBalanceCalculation.get();
}
public void addPaidPrincipalAmount(Money paidPrincipal) {
this.paidPrincipal = MathUtil.plus(this.paidPrincipal, paidPrincipal);
this.paidPrincipal = MathUtil.plus(this.paidPrincipal, paidPrincipal, mc);
}
public void addPaidInterestAmount(Money paidInterest) {
this.paidInterest = MathUtil.plus(this.paidInterest, paidInterest);
this.paidInterest = MathUtil.plus(this.paidInterest, paidInterest, mc);
}
public Money getInitialBalanceForEmiRecalculation() {
@@ -173,10 +178,10 @@ public class RepaymentPeriod {
if (getPrevious().isPresent()) {
initialBalance = getPrevious().get().getOutstandingLoanBalance();
} else {
initialBalance = getZero();
initialBalance = getZero(mc);
}
Money totalDisbursedAmount = getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount).reduce(getZero(),
Money::plus);
return initialBalance.add(totalDisbursedAmount);
Money totalDisbursedAmount = getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount).reduce(getZero(mc),
(m1, m2) -> m1.plus(m2, mc));
return initialBalance.add(totalDisbursedAmount, mc);
}
}
@@ -87,17 +87,17 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
: loanApplicationTerms.getSubmittedOnDate();
final LoanScheduleParams scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency,
Money.of(currency, chargesDueAtTimeOfDisbursement), periodStartDate, Money.zero(currency));
Money.of(currency, chargesDueAtTimeOfDisbursement, mc), periodStartDate, Money.zero(currency, mc), mc);
// charges which depends on total loan interest will be added to this
// set and handled separately after all installments generated
final Set<LoanCharge> nonCompoundingCharges = separateTotalCompoundingPercentageCharges(loanCharges);
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = scheduledDateGenerator
.generateRepaymentPeriods(periodStartDate, loanApplicationTerms, holidayDetailDTO);
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = scheduledDateGenerator.generateRepaymentPeriods(mc,
periodStartDate, loanApplicationTerms, holidayDetailDTO);
final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator.generateInterestScheduleModel(
expectedRepaymentPeriods, loanApplicationTerms.toLoanProductRelatedDetail(),
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc);
final List<LoanScheduleModelPeriod> periods = new ArrayList<>(expectedRepaymentPeriods.size());
prepareDisbursementsOnLoanApplicationTerms(loanApplicationTerms);
@@ -110,7 +110,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
scheduleParams.setActualRepaymentDate(repaymentPeriod.getDueDate());
processDisbursements(loanApplicationTerms, disbursementDataList, scheduleParams, interestScheduleModel, periods,
chargesDueAtTimeOfDisbursement, false);
chargesDueAtTimeOfDisbursement, false, mc);
repaymentPeriod.setPeriodNumber(scheduleParams.getInstalmentNumber());
if (loanApplicationTerms.getLoanTermVariations() != null) {
@@ -135,7 +135,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
scheduleParams.addTotalCumulativePrincipal(principalDue);
scheduleParams.addTotalCumulativeInterest(interestDue);
// add everything
scheduleParams.addTotalRepaymentExpected(principalDue.plus(interestDue));
scheduleParams.addTotalRepaymentExpected(principalDue.plus(interestDue, mc));
});
applyChargesForCurrentPeriod(repaymentPeriod, loanCharges, scheduleParams, currency, mc);
@@ -147,7 +147,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
if (loanApplicationTerms.isMultiDisburseLoan()) {
processDisbursements(loanApplicationTerms, disbursementDataList, scheduleParams, interestScheduleModel, periods,
chargesDueAtTimeOfDisbursement, true);
chargesDueAtTimeOfDisbursement, true, mc);
}
// determine fees and penalties for charges which depends on total
@@ -158,8 +158,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
final BigDecimal totalOutstanding = BigDecimal.ZERO;
return LoanScheduleModel.from(periods, applicationCurrency, interestScheduleModel.getLoanTermInDays(),
scheduleParams.getPrincipalToBeScheduled().plus(loanApplicationTerms.getDownPaymentAmount()),
scheduleParams.getTotalCumulativePrincipal().plus(loanApplicationTerms.getDownPaymentAmount()).getAmount(),
scheduleParams.getPrincipalToBeScheduled().plus(loanApplicationTerms.getDownPaymentAmount(), mc),
scheduleParams.getTotalCumulativePrincipal().plus(loanApplicationTerms.getDownPaymentAmount(), mc).getAmount(),
totalPrincipalPaid, scheduleParams.getTotalCumulativeInterest().getAmount(),
scheduleParams.getTotalFeeChargesCharged().getAmount(), scheduleParams.getTotalPenaltyChargesCharged().getAmount(),
scheduleParams.getTotalRepaymentExpected().getAmount(), totalOutstanding);
@@ -167,7 +167,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
public LoanScheduleModel generate(final MathContext mc, final LoanRepaymentScheduleModelData modelData) {
LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(modelData);
LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(modelData, mc);
return generate(mc, loanApplicationTerms, null, null);
}
@@ -184,7 +184,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
private void processDisbursements(final LoanApplicationTerms loanApplicationTerms,
final ArrayList<DisbursementData> disbursementDataList, final LoanScheduleParams scheduleParams,
final ProgressiveLoanInterestScheduleModel interestScheduleModel, final List<LoanScheduleModelPeriod> periods,
final BigDecimal chargesDueAtTimeOfDisbursement, final boolean includeDisbursementsAfterMaturityDate) {
final BigDecimal chargesDueAtTimeOfDisbursement, final boolean includeDisbursementsAfterMaturityDate, final MathContext mc) {
for (DisbursementData disbursementData : disbursementDataList) {
final LocalDate disbursementDate = disbursementData.disbursementDate();
@@ -202,7 +202,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
Money outstandingBalance = emiCalculator.getOutstandingLoanBalance(interestScheduleModel, periodDueDate, disbursementDate);
final Money disbursedAmount = Money.of(loanApplicationTerms.getCurrency(), disbursementData.getPrincipal());
final Money disbursedAmount = Money.of(loanApplicationTerms.getCurrency(), disbursementData.getPrincipal(), mc);
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod
.disbursement(disbursementData.disbursementDate(), disbursedAmount, chargesDueAtTimeOfDisbursement);
periods.add(disbursementPeriod);
@@ -218,7 +218,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
}
}
Money downPaymentAmount = Money.zero(loanApplicationTerms.getCurrency());
Money downPaymentAmount = Money.zero(loanApplicationTerms.getCurrency(), mc);
if (loanApplicationTerms.isDownPaymentEnabled()) {
downPaymentAmount = Money.of(loanApplicationTerms.getCurrency(), MathUtil.percentageOf(disbursedAmount.getAmount(),
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19));
@@ -321,7 +321,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable, final boolean isFirstPeriod,
final MathContext mc) {
Money cumulative = Money.zero(monetaryCurrency);
Money cumulative = Money.zero(monetaryCurrency, mc);
if (loanCharges != null) {
for (final LoanCharge loanCharge : loanCharges) {
if (!loanCharge.isDueAtDisbursement() && loanCharge.isFeeCharge()) {
@@ -356,7 +356,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
final Money totalInterestChargedForFullLoanTerm, boolean isInstallmentChargeApplicable, final boolean isFirstPeriod,
final MathContext mc) {
Money cumulative = Money.zero(monetaryCurrency);
Money cumulative = Money.zero(monetaryCurrency, mc);
if (loanCharges != null) {
for (final LoanCharge loanCharge : loanCharges) {
if (loanCharge.isPenaltyCharge()) {
@@ -407,8 +407,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
final Collection<LoanScheduleModelPeriod> periods, final Set<LoanCharge> nonCompoundingCharges, MathContext mc) {
for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) {
if (loanScheduleModelPeriod.isRepaymentPeriod()) {
PrincipalInterest principalInterest = new PrincipalInterest(Money.of(currency, loanScheduleModelPeriod.principalDue()),
Money.of(currency, loanScheduleModelPeriod.interestDue()), null);
PrincipalInterest principalInterest = new PrincipalInterest(Money.of(currency, loanScheduleModelPeriod.principalDue(), mc),
Money.of(currency, loanScheduleModelPeriod.interestDue(), mc), null);
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
loanScheduleModelPeriod.periodDueDate(), nonCompoundingCharges, currency, principalInterest,
scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativeInterest(),
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanproduct.calc;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
@@ -33,10 +34,10 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
public interface EMICalculator {
ProgressiveLoanInterestScheduleModel generateInterestScheduleModel(List<LoanScheduleModelRepaymentPeriod> periods,
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf);
LoanProductRelatedDetail loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, MathContext mc);
ProgressiveLoanInterestScheduleModel generateModel(LoanProductRelatedDetail loanProductRelatedDetail,
Integer installmentAmountInMultiplesOf, List<LoanRepaymentScheduleInstallment> repaymentPeriods);
Integer installmentAmountInMultiplesOf, List<LoanRepaymentScheduleInstallment> repaymentPeriods, MathContext mc);
Optional<RepaymentPeriod> findRepaymentPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate dueDate);
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanproduct.calc;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.time.Year;
import java.util.ArrayList;
@@ -29,7 +30,6 @@ import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
@@ -40,12 +40,10 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLo
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.RepaymentPeriod;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
@DependsOn("moneyHelper")
public final class ProgressiveEMICalculator implements EMICalculator {
private static final BigDecimal DIVISOR_100 = new BigDecimal("100");
@@ -53,19 +51,19 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override
public ProgressiveLoanInterestScheduleModel generateInterestScheduleModel(final List<LoanScheduleModelRepaymentPeriod> periods,
final LoanProductRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf) {
final Money zeroAmount = Money.zero(loanProductRelatedDetail.getCurrency());
final LoanProductRelatedDetail loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
final Money zeroAmount = Money.zero(loanProductRelatedDetail.getCurrency(), mc);
final ArrayList<RepaymentPeriod> interestRepaymentModelList = new ArrayList<>(periods.size());
RepaymentPeriod previousPeriod = null;
for (final LoanScheduleModelRepaymentPeriod period : periods) {
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, period.periodFromDate(), period.periodDueDate(),
zeroAmount);
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, period.periodFromDate(), period.periodDueDate(), zeroAmount,
mc);
previousPeriod = currentPeriod;
interestRepaymentModelList.add(currentPeriod);
}
return new ProgressiveLoanInterestScheduleModel(interestRepaymentModelList, loanProductRelatedDetail,
installmentAmountInMultiplesOf);
installmentAmountInMultiplesOf, mc);
}
@Override
@@ -85,7 +83,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
final Money disbursedAmount) {
scheduleModel
.changeOutstandingBalanceAndUpdateInterestPeriods(disbursementDueDate, disbursedAmount,
Money.zero(disbursedAmount.getCurrency()))
Money.zero(disbursedAmount.getCurrency(), scheduleModel.mc()))
.ifPresent((repaymentPeriod) -> calculateEMIValueAndRateFactors(repaymentPeriod.getDueDate(), scheduleModel));
}
@@ -130,7 +128,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override
public PayableDetails getPayableDetails(final ProgressiveLoanInterestScheduleModel scheduleModel,
final LocalDate repaymentPeriodDueDate, final LocalDate targetDate) {
RepaymentPeriod repaymentPeriod = scheduleModel.deepCopy().repaymentPeriods().stream()
MathContext mc = scheduleModel.mc();
RepaymentPeriod repaymentPeriod = scheduleModel.deepCopy(mc).repaymentPeriods().stream()
.filter(rp -> rp.getDueDate().equals(repaymentPeriodDueDate)).findFirst().orElseThrow();
LocalDate adjustedTargetDate = targetDate;
InterestPeriod interestPeriod;
@@ -151,19 +150,19 @@ public final class ProgressiveEMICalculator implements EMICalculator {
// TODO: gather all the unrecognized interest from previous periods based on target date
Money payableInterest = targetDate.isBefore(repaymentPeriod.getFromDate())
? Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency())
? Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc)
: repaymentPeriod.getDueInterest();
Money outstandingLoanBalance = interestPeriod.getOutstandingLoanBalance().add(interestPeriod.getDisbursementAmount());
Money calculatedEmi = outstandingLoanBalance.plus(payableInterest);
Money calculatedEmi = outstandingLoanBalance.plus(payableInterest, mc);
if (calculatedEmi.isLessThan(repaymentPeriod.getEmi())) {
// Review this logic
repaymentPeriod.setEmi(outstandingLoanBalance.plus(payableInterest).plus(repaymentPeriod.getPaidInterest())
.plus(repaymentPeriod.getPaidPrincipal()));
repaymentPeriod.setEmi(outstandingLoanBalance.plus(payableInterest).plus(repaymentPeriod.getPaidInterest(), mc)
.plus(repaymentPeriod.getPaidPrincipal(), mc));
}
Money payablePrincipal = repaymentPeriod.getEmi().minus(payableInterest);
Money payablePrincipal = repaymentPeriod.getEmi().minus(payableInterest, mc);
return new PayableDetails(repaymentPeriod.getEmi(), payablePrincipal, payableInterest,
interestPeriod.getOutstandingLoanBalance().add(interestPeriod.getDisbursementAmount()));
interestPeriod.getOutstandingLoanBalance().add(interestPeriod.getDisbursementAmount(), mc));
}
@Override
@@ -189,18 +188,19 @@ public final class ProgressiveEMICalculator implements EMICalculator {
}
private void calculateLastUnpaidRepaymentPeriodEMI(ProgressiveLoanInterestScheduleModel scheduleModel) {
MathContext mc = scheduleModel.mc();
Money totalDueInterest = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueInterest)
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency()), Money::plus); // 1.46
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc), (m1, m2) -> m1.plus(m2, mc)); // 1.46
Money totalEMI = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getEmi)
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency()), Money::plus); // 101.48
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc), (m1, m2) -> m1.plus(m2, mc)); // 101.48
Money totalDisbursedAmount = scheduleModel.repaymentPeriods().stream()
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount))
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency()), Money::plus); // 100
.reduce(Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc), (m1, m2) -> m1.plus(m2, mc)); // 100
Money diff = totalDisbursedAmount.plus(totalDueInterest).minus(totalEMI);
Money diff = totalDisbursedAmount.plus(totalDueInterest, mc).minus(totalEMI, mc);
Optional<RepaymentPeriod> findLastUnpaidRepaymentPeriod = scheduleModel.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid())
.reduce((first, second) -> second);
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> repaymentPeriod.setEmi(repaymentPeriod.getEmi().add(diff)));
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> repaymentPeriod.setEmi(repaymentPeriod.getEmi().add(diff, mc)));
}
private void calculateOutstandingBalance(ProgressiveLoanInterestScheduleModel scheduleModel) {
@@ -209,24 +209,26 @@ public final class ProgressiveEMICalculator implements EMICalculator {
private void checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(final ProgressiveLoanInterestScheduleModel scheduleModel,
final List<RepaymentPeriod> relatedRepaymentPeriods) {
MathContext mc = scheduleModel.mc();
final Money emiDifference = getDifferenceBetweenLastTwoPeriod(relatedRepaymentPeriods, scheduleModel);
final int numberOfRelatedPeriods = relatedRepaymentPeriods.size();
double lowerHalfOfRelatedPeriods = Math.floor(numberOfRelatedPeriods / 2.0);
if (emiDifference.isZero() || lowerHalfOfRelatedPeriods == 0.0) {
if (emiDifference.isZero(mc) || lowerHalfOfRelatedPeriods == 0.0) {
return;
}
final Money originalEmi = relatedRepaymentPeriods.get(numberOfRelatedPeriods - 2).getEmi();
boolean shouldBeAdjusted = emiDifference.abs().multipliedBy(100)
.isGreaterThan(Money.of(originalEmi.getCurrency(), BigDecimal.valueOf(lowerHalfOfRelatedPeriods)));
boolean shouldBeAdjusted = emiDifference.abs(mc).multipliedBy(100, mc)
.isGreaterThan(Money.of(originalEmi.getCurrency(), BigDecimal.valueOf(lowerHalfOfRelatedPeriods), mc));
if (shouldBeAdjusted) {
Money adjustment = emiDifference.dividedBy(numberOfRelatedPeriods, MoneyHelper.getMathContext().getRoundingMode());
Money adjustedEqualMonthlyInstallmentValue = applyInstallmentAmountInMultiplesOf(scheduleModel, originalEmi.plus(adjustment));
Money adjustment = emiDifference.dividedBy(numberOfRelatedPeriods, mc.getRoundingMode(), mc);
Money adjustedEqualMonthlyInstallmentValue = applyInstallmentAmountInMultiplesOf(scheduleModel,
originalEmi.plus(adjustment, mc));
if (adjustedEqualMonthlyInstallmentValue.isEqualTo(originalEmi)) {
return;
}
final LocalDate relatedPeriodsFirstDueDate = relatedRepaymentPeriods.get(0).getDueDate();
final ProgressiveLoanInterestScheduleModel newScheduleModel = scheduleModel.deepCopy();
final ProgressiveLoanInterestScheduleModel newScheduleModel = scheduleModel.deepCopy(mc);
newScheduleModel.repaymentPeriods().forEach(period -> {
if (!period.getDueDate().isBefore(relatedPeriodsFirstDueDate)) {
period.setEmi(adjustedEqualMonthlyInstallmentValue);
@@ -235,7 +237,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
calculateOutstandingBalance(newScheduleModel);
calculateLastUnpaidRepaymentPeriodEMI(newScheduleModel);
final Money newEmiDifference = getDifferenceBetweenLastTwoPeriod(newScheduleModel.repaymentPeriods(), scheduleModel);
final boolean newEmiHasLessDifference = newEmiDifference.abs().isLessThan(emiDifference.abs());
final boolean newEmiHasLessDifference = newEmiDifference.abs(mc).isLessThan(emiDifference.abs(mc));
if (!newEmiHasLessDifference) {
return;
}
@@ -263,8 +265,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*
* @return Rate Interest Rate in fraction format
*/
BigDecimal calcNominalInterestRatePercentage(final BigDecimal interestRate) {
return MathUtil.nullToZero(interestRate).divide(DIVISOR_100, MoneyHelper.getMathContext());
BigDecimal calcNominalInterestRatePercentage(final BigDecimal interestRate, MathContext mc) {
return MathUtil.nullToZero(interestRate).divide(DIVISOR_100, mc);
}
/**
@@ -286,8 +288,10 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*/
BigDecimal calculateRateFactorPerPeriod(final RepaymentPeriod repaymentPeriod, final InterestPeriod interestPeriod,
final ProgressiveLoanInterestScheduleModel scheduleModel) {
final MathContext mc = scheduleModel.mc();
final LoanProductRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail();
final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriod.getFromDate()));
final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriod.getFromDate()),
scheduleModel.mc());
final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType());
final DaysInMonthType daysInMonthType = DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType());
final PeriodFrequencyType repaymentFrequency = loanProductRelatedDetail.getRepaymentPeriodFrequencyType();
@@ -306,13 +310,13 @@ public final class ProgressiveEMICalculator implements EMICalculator {
// 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(interestPeriod);
final BigDecimal cumulatedPeriodFractions = calculatePeriodFractions(interestPeriod, mc);
return rateFactorByRepaymentPartialPeriod(interestRate, repaymentEvery, cumulatedPeriodFractions, BigDecimal.ONE,
BigDecimal.ONE);
BigDecimal.ONE, mc);
}
return calculateRateFactorPerPeriodBasedOnRepaymentFrequency(interestRate, repaymentFrequency, repaymentEvery, daysInMonth,
daysInYear, actualDaysInPeriod, calculatedDaysInPeriod);
daysInYear, actualDaysInPeriod, calculatedDaysInPeriod, mc);
}
/**
@@ -321,7 +325,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* @param interestPeriod
* @return
*/
BigDecimal calculatePeriodFractions(InterestPeriod interestPeriod) {
BigDecimal calculatePeriodFractions(InterestPeriod interestPeriod, MathContext mc) {
BigDecimal cumulatedRateFactor = BigDecimal.ZERO;
int actualYear = interestPeriod.getFromDate().getYear();
int endYear = interestPeriod.getDueDate().getYear();
@@ -332,8 +336,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
endOfActualYear = actualYear == endYear ? interestPeriod.getDueDate() : LocalDate.of(actualYear, 12, 31);
BigDecimal numberOfDaysInYear = BigDecimal.valueOf(Year.of(actualYear).length());
BigDecimal calculatedDaysInActualYear = BigDecimal.valueOf(DateUtils.getDifferenceInDays(actualDate, endOfActualYear));
cumulatedRateFactor = cumulatedRateFactor
.add(calculatedDaysInActualYear.divide(numberOfDaysInYear, MoneyHelper.getMathContext()), MoneyHelper.getMathContext());
cumulatedRateFactor = cumulatedRateFactor.add(calculatedDaysInActualYear.divide(numberOfDaysInYear, mc), mc);
actualDate = endOfActualYear;
actualYear++;
}
@@ -354,14 +357,15 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*/
BigDecimal calculateRateFactorPerPeriodBasedOnRepaymentFrequency(final BigDecimal interestRate,
final PeriodFrequencyType repaymentFrequency, final BigDecimal repaymentEvery, final BigDecimal daysInMonth,
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod,
final MathContext mc) {
return switch (repaymentFrequency) {
case DAYS ->
rateFactorByRepaymentEveryDay(interestRate, repaymentEvery, daysInYear, actualDaysInPeriod, calculatedDaysInPeriod);
rateFactorByRepaymentEveryDay(interestRate, repaymentEvery, daysInYear, actualDaysInPeriod, calculatedDaysInPeriod, mc);
case WEEKS ->
rateFactorByRepaymentEveryWeek(interestRate, repaymentEvery, daysInYear, actualDaysInPeriod, calculatedDaysInPeriod);
rateFactorByRepaymentEveryWeek(interestRate, repaymentEvery, daysInYear, actualDaysInPeriod, calculatedDaysInPeriod, mc);
case MONTHS -> rateFactorByRepaymentEveryMonth(interestRate, repaymentEvery, daysInMonth, daysInYear, actualDaysInPeriod,
calculatedDaysInPeriod);
calculatedDaysInPeriod, mc);
default -> throw new UnsupportedOperationException("Invalid repayment frequency"); // not supported yet
};
}
@@ -370,14 +374,15 @@ public final class ProgressiveEMICalculator implements EMICalculator {
if (repaymentPeriods.isEmpty()) {
return;
}
final BigDecimal rateFactorN = MathUtil.stripTrailingZeros(calculateRateFactorPlus1N(repaymentPeriods));
final BigDecimal fnResult = MathUtil.stripTrailingZeros(calculateFnResult(repaymentPeriods));
final MathContext mc = scheduleModel.mc();
final BigDecimal rateFactorN = MathUtil.stripTrailingZeros(calculateRateFactorPlus1N(repaymentPeriods, mc));
final BigDecimal fnResult = MathUtil.stripTrailingZeros(calculateFnResult(repaymentPeriods, mc));
final RepaymentPeriod startPeriod = repaymentPeriods.get(0);
// TODO: double check
final Money outstandingBalance = startPeriod.getInitialBalanceForEmiRecalculation();
final Money equalMonthlyInstallment = Money.of(outstandingBalance.getCurrency(),
calculateEMIValue(rateFactorN, outstandingBalance.getAmount(), fnResult));
calculateEMIValue(rateFactorN, outstandingBalance.getAmount(), fnResult, mc), mc);
final Money finalEqualMonthlyInstallment = applyInstallmentAmountInMultiplesOf(scheduleModel, equalMonthlyInstallment);
repaymentPeriods.forEach(period -> period.setEmi(finalEqualMonthlyInstallment));
@@ -392,39 +397,40 @@ public final class ProgressiveEMICalculator implements EMICalculator {
Money getDifferenceBetweenLastTwoPeriod(final List<RepaymentPeriod> repaymentPeriods,
final ProgressiveLoanInterestScheduleModel scheduleModel) {
MathContext mc = scheduleModel.mc();
int numberOfUpcomingPeriods = repaymentPeriods.size();
if (numberOfUpcomingPeriods < 2) {
return Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency());
return Money.zero(scheduleModel.loanProductRelatedDetail().getCurrency(), mc);
}
final RepaymentPeriod lastPeriod = repaymentPeriods.get(numberOfUpcomingPeriods - 1);
final RepaymentPeriod penultimatePeriod = repaymentPeriods.get(numberOfUpcomingPeriods - 2);
return lastPeriod.getEmi().minus(penultimatePeriod.getEmi());
return lastPeriod.getEmi().minus(penultimatePeriod.getEmi(), mc);
}
/**
* Calculate Rate Factor Product from rate factors
*/
BigDecimal calculateRateFactorPlus1N(final List<RepaymentPeriod> periods) {
BigDecimal calculateRateFactorPlus1N(final List<RepaymentPeriod> periods, MathContext mc) {
return periods.stream().map(RepaymentPeriod::getRateFactorPlus1).reduce(BigDecimal.ONE,
(BigDecimal acc, BigDecimal value) -> acc.multiply(value, MoneyHelper.getMathContext()));
(BigDecimal acc, BigDecimal value) -> acc.multiply(value, mc));
}
/**
* Summarize Fn values
*/
BigDecimal calculateFnResult(final List<RepaymentPeriod> periods) {
BigDecimal calculateFnResult(final List<RepaymentPeriod> periods, final MathContext mc) {
return periods.stream()//
.skip(1)//
.map(RepaymentPeriod::getRateFactorPlus1)//
.reduce(BigDecimal.ONE, this::fnValue);//
.reduce(BigDecimal.ONE, (previousFnValue, currentRateFactor) -> fnValue(previousFnValue, currentRateFactor, mc));//
}
/**
* Calculate the EMI (Equal Monthly Installment) value
*/
BigDecimal calculateEMIValue(final BigDecimal rateFactorPlus1N, final BigDecimal outstandingBalanceForRest, final BigDecimal fnResult) {
return rateFactorPlus1N.multiply(outstandingBalanceForRest, MoneyHelper.getMathContext()).divide(fnResult,
MoneyHelper.getMathContext());
BigDecimal calculateEMIValue(final BigDecimal rateFactorPlus1N, final BigDecimal outstandingBalanceForRest, final BigDecimal fnResult,
MathContext mc) {
return rateFactorPlus1N.multiply(outstandingBalanceForRest, mc).divide(fnResult, mc);
}
/**
@@ -452,9 +458,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* @return Rate Factor for period
*/
BigDecimal rateFactorByRepaymentEveryDay(final BigDecimal interestRate, final BigDecimal repaymentEvery, final BigDecimal daysInYear,
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod, MathContext mc) {
return rateFactorByRepaymentPeriod(interestRate, BigDecimal.ONE, repaymentEvery, daysInYear, actualDaysInPeriod,
calculatedDaysInPeriod);
calculatedDaysInPeriod, mc);
}
/**
@@ -482,9 +488,9 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* @return Rate Factor for period
*/
BigDecimal rateFactorByRepaymentEveryWeek(final BigDecimal interestRate, final BigDecimal repaymentEvery, final BigDecimal daysInYear,
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod, MathContext mc) {
return rateFactorByRepaymentPeriod(interestRate, ONE_WEEK_IN_DAYS, repaymentEvery, daysInYear, actualDaysInPeriod,
calculatedDaysInPeriod);
calculatedDaysInPeriod, mc);
}
/**
@@ -515,9 +521,10 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* @return Rate Factor for period
*/
BigDecimal rateFactorByRepaymentEveryMonth(final BigDecimal interestRate, final BigDecimal repaymentEvery, final BigDecimal daysInMonth,
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod,
final MathContext mc) {
return rateFactorByRepaymentPeriod(interestRate, daysInMonth, repaymentEvery, daysInYear, actualDaysInPeriod,
calculatedDaysInPeriod);
calculatedDaysInPeriod, mc);
}
/**
@@ -549,14 +556,14 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*/
BigDecimal rateFactorByRepaymentPeriod(final BigDecimal interestRate, final BigDecimal repaymentPeriodMultiplierInDays,
final BigDecimal repaymentEvery, final BigDecimal daysInYear, final BigDecimal actualDaysInPeriod,
final BigDecimal calculatedDaysInPeriod) {
final BigDecimal calculatedDaysInPeriod, final MathContext mc) {
final BigDecimal interestFractionPerPeriod = repaymentPeriodMultiplierInDays//
.multiply(repaymentEvery, MoneyHelper.getMathContext())//
.divide(daysInYear, MoneyHelper.getMathContext());//
.multiply(repaymentEvery, mc)//
.divide(daysInYear, mc);//
return interestRate//
.multiply(interestFractionPerPeriod, MoneyHelper.getMathContext())//
.multiply(actualDaysInPeriod, MoneyHelper.getMathContext())//
.divide(calculatedDaysInPeriod, MoneyHelper.getMathContext());//
.multiply(interestFractionPerPeriod, mc)//
.multiply(actualDaysInPeriod, mc)//
.divide(calculatedDaysInPeriod, mc);//
}
/**
@@ -564,12 +571,13 @@ public final class ProgressiveEMICalculator implements EMICalculator {
*
*/
BigDecimal rateFactorByRepaymentPartialPeriod(final BigDecimal interestRate, final BigDecimal repaymentEvery,
final BigDecimal cumulatedPeriodRatio, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod) {
final BigDecimal cumulatedPeriodRatio, final BigDecimal actualDaysInPeriod, final BigDecimal calculatedDaysInPeriod,
final MathContext mc) {
final BigDecimal interestFractionPerPeriod = repaymentEvery.multiply(cumulatedPeriodRatio);
return interestRate//
.multiply(interestFractionPerPeriod, MoneyHelper.getMathContext())//
.multiply(actualDaysInPeriod, MoneyHelper.getMathContext())//
.divide(calculatedDaysInPeriod, MoneyHelper.getMathContext());//
.multiply(interestFractionPerPeriod, mc)//
.multiply(actualDaysInPeriod, mc)//
.divide(calculatedDaysInPeriod, mc);//
}
/**
@@ -582,13 +590,13 @@ public final class ProgressiveEMICalculator implements EMICalculator {
* @param currentRateFactor
*
*/
BigDecimal fnValue(final BigDecimal previousFnValue, final BigDecimal currentRateFactor) {
return BigDecimal.ONE.add(previousFnValue.multiply(currentRateFactor, MoneyHelper.getMathContext()), MoneyHelper.getMathContext());
BigDecimal fnValue(final BigDecimal previousFnValue, final BigDecimal currentRateFactor, final MathContext mc) {
return BigDecimal.ONE.add(previousFnValue.multiply(currentRateFactor, mc), mc);
}
@Override
public ProgressiveLoanInterestScheduleModel generateModel(LoanProductRelatedDetail loanProductRelatedDetail,
Integer installmentAmountInMultiplesOf, List<LoanRepaymentScheduleInstallment> repaymentPeriods) {
Integer installmentAmountInMultiplesOf, List<LoanRepaymentScheduleInstallment> repaymentPeriods, MathContext mc) {
List<LoanRepaymentScheduleInstallment> repaymentModelsWithoutDownPayment = repaymentPeriods.stream()
.filter(period -> !period.isDownPayment() && !period.isAdditional()).toList();
@@ -596,11 +604,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
RepaymentPeriod previousPeriod = null;
for (LoanRepaymentScheduleInstallment repaymentModel : repaymentModelsWithoutDownPayment) {
RepaymentPeriod currentPeriod = new RepaymentPeriod(previousPeriod, repaymentModel.getFromDate(), repaymentModel.getDueDate(),
Money.zero(repaymentModel.getLoan().getCurrency()));
Money.zero(repaymentModel.getLoan().getCurrency(), mc), mc);
previousPeriod = currentPeriod;
repaymentModels.add(currentPeriod);
}
return new ProgressiveLoanInterestScheduleModel(repaymentModels, loanProductRelatedDetail, installmentAmountInMultiplesOf);
return new ProgressiveLoanInterestScheduleModel(repaymentModels, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
}
}
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -90,11 +91,12 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
private static final MonetaryCurrency MONETARY_CURRENCY = new MonetaryCurrency("USD", 2, 1);
private static final MockedStatic<MoneyHelper> MONEY_HELPER = mockStatic(MoneyHelper.class);
private AdvancedPaymentScheduleTransactionProcessor underTest;
private final EMICalculator emiCalculator = Mockito.mock(EMICalculator.class);
private static final EMICalculator emiCalculator = Mockito.mock(EMICalculator.class);
@BeforeAll
public static void init() {
MONEY_HELPER.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
MONEY_HELPER.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
}
@AfterAll
@@ -20,77 +20,36 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
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.common.domain.DaysInMonthType;
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class LoanScheduleGeneratorTest {
private static final ProgressiveEMICalculator emiCalculator = new ProgressiveEMICalculator();
private static MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
private static final ApplicationCurrency APPLICATION_CURRENCY = new ApplicationCurrency("USD", "USD", 2, 1, "USD", "$");
private static final MonetaryCurrency MONETARY_CURRENCY = MonetaryCurrency.fromApplicationCurrency(APPLICATION_CURRENCY);
private static final BigDecimal DISBURSEMENT_AMOUNT = BigDecimal.valueOf(192.22);
private static final BigDecimal NOMINAL_INTEREST_RATE = BigDecimal.valueOf(9.99);
private static final int NUMBER_OF_REPAYMENTS = 6;
private static final int REPAYMENT_FREQUENCY = 1;
private static final String REPAYMENT_FREQUENCY_TYPE = "MONTHS";
private static final LocalDate DISBURSEMENT_DATE = LocalDate.of(2024, 1, 15);
@BeforeAll
public static void init() {
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
}
@AfterAll
public static void destruct() {
moneyHelper.close();
}
private static final MathContext mc = new MathContext(12, RoundingMode.HALF_EVEN);
@Test
void testGenerateLoanSchedule() {
LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), APPLICATION_CURRENCY,
DISBURSEMENT_AMOUNT, DISBURSEMENT_DATE, NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE,
NOMINAL_INTEREST_RATE, true, DaysInMonthType.DAYS_30, DaysInYearType.DAYS_360, null, null, null);
final MathContext mc = MoneyHelper.getMathContext();
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = new ArrayList<>();
expectedRepaymentPeriods.add(repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1)));
expectedRepaymentPeriods.add(repayment(2, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1)));
expectedRepaymentPeriods.add(repayment(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1)));
expectedRepaymentPeriods.add(repayment(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1)));
expectedRepaymentPeriods.add(repayment(5, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1)));
expectedRepaymentPeriods.add(repayment(6, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1)));
ScheduledDateGenerator mockScheduledDateGenerator = Mockito.mock(ScheduledDateGenerator.class);
ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(mockScheduledDateGenerator, emiCalculator);
when(mockScheduledDateGenerator.generateRepaymentPeriods(any(), any(), any())).thenReturn(expectedRepaymentPeriods);
LoanScheduleModel loanSchedule = generator.generate(mc, modelData);
List<LoanScheduleModelPeriod> periods = loanSchedule.getPeriods();
List<LoanScheduleModelPeriod> periods = getLoanScheduleModelPeriods();
assertEquals(7, periods.size(), "Expected 7 periods including the downpayment period.");
@@ -111,6 +70,18 @@ class LoanScheduleGeneratorTest {
BigDecimal.valueOf(0.27), BigDecimal.valueOf(32.87), BigDecimal.ZERO);
}
private static List<LoanScheduleModelPeriod> getLoanScheduleModelPeriods() {
LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), APPLICATION_CURRENCY,
DISBURSEMENT_AMOUNT, DISBURSEMENT_DATE, NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE,
NOMINAL_INTEREST_RATE, true, DaysInMonthType.DAYS_30, DaysInYearType.DAYS_360, null, null, null);
ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator);
LoanScheduleModel loanSchedule = generator.generate(mc, modelData);
return loanSchedule.getPeriods();
}
private void checkPeriod(LoanScheduleModelPeriod period, int expectedPeriodNumber, LocalDate expectedFromDate,
LocalDate expectedDueDate, BigDecimal expectedPrincipalDue, BigDecimal expectedInterestDue, BigDecimal expectedTotalDue,
BigDecimal expectedOutstandingLoanBalance) {
@@ -123,10 +94,4 @@ class LoanScheduleGeneratorTest {
assertEquals(0, expectedTotalDue.compareTo(repaymentPeriod.getTotalDue().getAmount()));
assertEquals(0, expectedOutstandingLoanBalance.compareTo(repaymentPeriod.getOutstandingLoanBalance().getAmount()));
}
private static LoanScheduleModelRepaymentPeriod repayment(int periodNumber, LocalDate fromDate, LocalDate dueDate) {
final Money zeroAmount = Money.zero(MONETARY_CURRENCY);
return LoanScheduleModelRepaymentPeriod.repayment(periodNumber, fromDate, dueDate, zeroAmount, zeroAmount, zeroAmount, zeroAmount,
zeroAmount, zeroAmount, false);
}
}
@@ -59,6 +59,7 @@ class ProgressiveEMICalculatorTest {
private static MockedStatic<ThreadLocalContextUtil> threadLocalContextUtil = Mockito.mockStatic(ThreadLocalContextUtil.class);
private static MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
private static MathContext mc = new MathContext(12, RoundingMode.HALF_EVEN);
private static LoanProductRelatedDetail loanProductRelatedDetail = Mockito.mock(LoanProductRelatedDetail.class);
private static final MonetaryCurrency monetaryCurrency = MonetaryCurrency
@@ -95,7 +96,7 @@ class ProgressiveEMICalculatorTest {
final BigDecimal daysInYear = BigDecimal.valueOf(daysInYearType.getNumberOfDays(period.getFromDate()));
final BigDecimal daysInMonth = BigDecimal.valueOf(daysInMonthType.getNumberOfDays(period.getFromDate()));
final BigDecimal rateFactor = emiCalculator.rateFactorByRepaymentEveryMonth(interestRate, BigDecimal.ONE, daysInMonth, daysInYear,
daysInPeriod, daysInPeriod);
daysInPeriod, daysInPeriod, mc);
return rateFactor.setScale(12, MoneyHelper.getRoundingMode());
}
@@ -145,7 +146,7 @@ class ProgressiveEMICalculatorTest {
BigDecimal rateFactorPlus1 = getRateFactorsByMonth(daysInYearType, daysInMonthType, interestRate, period).add(BigDecimal.ONE,
MoneyHelper.getMathContext());
final BigDecimal currentFnValue = emiCalculator.fnValue(previousFnValue, rateFactorPlus1);
final BigDecimal currentFnValue = emiCalculator.fnValue(previousFnValue, rateFactorPlus1, mc);
fnValuesCalculated.add(currentFnValue);
previousFnValue = currentFnValue;
@@ -171,7 +172,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator
.generateInterestScheduleModel(expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf);
.generateInterestScheduleModel(expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
Assertions.assertTrue(interestScheduleModel != null);
Assertions.assertTrue(interestScheduleModel.loanProductRelatedDetail() != null);
@@ -211,7 +212,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = toMoney(100.0);
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -256,7 +257,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = toMoney(100.0);
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -293,7 +294,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -330,7 +331,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -370,7 +371,7 @@ class ProgressiveEMICalculatorTest {
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -414,7 +415,7 @@ class ProgressiveEMICalculatorTest {
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 14));
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -460,7 +461,7 @@ class ProgressiveEMICalculatorTest {
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -577,7 +578,7 @@ class ProgressiveEMICalculatorTest {
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -661,7 +662,7 @@ class ProgressiveEMICalculatorTest {
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = toMoney(100.0);
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -739,7 +740,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -788,7 +789,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -837,7 +838,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 5), disbursedAmount);
@@ -881,7 +882,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2023, 12, 12), disbursedAmount);
@@ -915,7 +916,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(1000));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -948,7 +949,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -981,7 +982,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -1014,7 +1015,7 @@ class ProgressiveEMICalculatorTest {
Mockito.when(loanProductRelatedDetail.getCurrency()).thenReturn(monetaryCurrency);
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods,
loanProductRelatedDetail, installmentAmountInMultiplesOf);
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
final Money disbursedAmount = Money.of(monetaryCurrency, BigDecimal.valueOf(100));
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount);
@@ -1031,7 +1032,7 @@ class ProgressiveEMICalculatorTest {
private static LoanScheduleModelRepaymentPeriod repayment(int periodNumber, LocalDate fromDate, LocalDate dueDate) {
final Money zeroAmount = Money.zero(monetaryCurrency);
return LoanScheduleModelRepaymentPeriod.repayment(periodNumber, fromDate, dueDate, zeroAmount, zeroAmount, zeroAmount, zeroAmount,
zeroAmount, zeroAmount, false);
zeroAmount, zeroAmount, false, mc);
}
@NotNull