FINERACT-2412: Full term tranche - Schedule handling and Calculations
This commit is contained in:
committed by
Adam Saghy
parent
3e197e367b
commit
09031aa5a7
+32
-9
@@ -119,6 +119,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.DueType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.FutureInstallmentAllocationRule;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
|
||||
import org.apache.fineract.portfolio.util.InstallmentProcessingHelper;
|
||||
@@ -1595,7 +1596,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
}
|
||||
|
||||
disbursementTransaction.resetDerivedComponents();
|
||||
recalculateRepaymentPeriodsWithEMICalculation(amortizableAmount, model, installments, transactionDate, currency);
|
||||
recalculateRepaymentPeriodsWithEMICalculation(amortizableAmount, model, installments, disbursementTransaction, currency);
|
||||
allocateOverpayment(disbursementTransaction, transactionCtx);
|
||||
}
|
||||
|
||||
@@ -1669,7 +1670,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
Money amortizableAmount = capitalizedIncomeTransaction.getAmount(currency);
|
||||
emiCalculator.addCapitalizedIncome(model, transactionDate, amortizableAmount);
|
||||
|
||||
recalculateRepaymentPeriodsWithEMICalculation(amortizableAmount, model, installments, transactionDate, currency);
|
||||
recalculateRepaymentPeriodsWithEMICalculation(amortizableAmount, model, installments, capitalizedIncomeTransaction, currency);
|
||||
allocateOverpayment(capitalizedIncomeTransaction, transactionCtx);
|
||||
}
|
||||
|
||||
@@ -1689,9 +1690,11 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
allocateOverpayment(capitalizedIncomeTransaction, transactionCtx);
|
||||
}
|
||||
|
||||
private void recalculateRepaymentPeriodsWithEMICalculation(Money amortizableAmount, ProgressiveLoanInterestScheduleModel model,
|
||||
List<LoanRepaymentScheduleInstallment> installments, LocalDate transactionDate, MonetaryCurrency currency) {
|
||||
boolean isPostMaturityDisbursement = installments.stream().filter(i -> !i.isDownPayment() && !i.isAdditional())
|
||||
private void recalculateRepaymentPeriodsWithEMICalculation(final Money amortizableAmount,
|
||||
final ProgressiveLoanInterestScheduleModel model, final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTransaction loanTransaction, final MonetaryCurrency currency) {
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
final boolean isPostMaturityDisbursement = installments.stream().filter(i -> !i.isDownPayment() && !i.isAdditional())
|
||||
.allMatch(i -> i.getDueDate().isBefore(transactionDate));
|
||||
|
||||
if (amortizableAmount.isGreaterThanZero()) {
|
||||
@@ -1703,14 +1706,34 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
}
|
||||
}
|
||||
|
||||
final ListIterator<LoanRepaymentScheduleInstallment> iterator = installments.listIterator();
|
||||
final AtomicInteger installmentCounter = new AtomicInteger();
|
||||
final ILoanConfigurationDetails loanProductRelatedDetail = model.loanProductRelatedDetail();
|
||||
|
||||
model.repaymentPeriods().forEach(rm -> {
|
||||
LoanRepaymentScheduleInstallment installment = installments.stream().filter(
|
||||
ri -> ri.getDueDate().equals(rm.getDueDate()) && !ri.isDownPayment() && !ri.getDueDate().isBefore(transactionDate))
|
||||
.findFirst().orElse(null);
|
||||
if (installment != null) {
|
||||
LoanRepaymentScheduleInstallment installment = null;
|
||||
while (iterator.hasNext() && (installment == null || installment.isAdditional() || installment.isDownPayment())) {
|
||||
installment = iterator.next();
|
||||
installmentCounter.getAndIncrement();
|
||||
}
|
||||
|
||||
if (installment != null && installment.getDueDate().equals(rm.getDueDate())
|
||||
&& !installment.getDueDate().isBefore(transactionDate)) {
|
||||
installment.updatePrincipal(rm.getDuePrincipal().getAmount());
|
||||
installment.updateInterestCharged(rm.getDueInterest().getAmount());
|
||||
installment.updateObligationsMet(currency, transactionDate);
|
||||
} else {
|
||||
if (loanProductRelatedDetail != null && loanProductRelatedDetail.isAllowFullTermForTranche()
|
||||
&& loanProductRelatedDetail.getNumberOfRepayments() > 0 && !rm.getDueDate().isBefore(transactionDate)) {
|
||||
installmentCounter.getAndIncrement();
|
||||
final LoanRepaymentScheduleInstallment newInstallment = new LoanRepaymentScheduleInstallment(
|
||||
loanTransaction.getLoan(), installmentCounter.get(), rm.getFromDate(), rm.getDueDate(),
|
||||
rm.getDuePrincipal().getAmount(), rm.getDueInterest().getAmount(), null, null, null, null, null, null,
|
||||
false, false, false);
|
||||
|
||||
newInstallment.updateObligationsMet(currency, transactionDate);
|
||||
iterator.add(newInstallment);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+2
-1
@@ -56,7 +56,8 @@ public final class LoanConfigurationDetailsMapper {
|
||||
loanProductRelatedDetail.getRepaymentPeriodFrequencyType(), loanProductRelatedDetail.getRepayEvery(),
|
||||
loanProductRelatedDetail.getNumberOfRepayments(), loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate(),
|
||||
loanProductRelatedDetail.getDaysInYearCustomStrategy(), loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation(),
|
||||
loan.isInterestRecalculationEnabled(), getRestFrequencyType(loan), getPreCloseInterestCalculationStrategy(loan));
|
||||
loan.isInterestRecalculationEnabled(), getRestFrequencyType(loan), getPreCloseInterestCalculationStrategy(loan),
|
||||
loan.isAllowFullTermForTranche());
|
||||
}
|
||||
|
||||
private static RecalculationFrequencyType getRestFrequencyType(Loan loan) {
|
||||
|
||||
+120
-17
@@ -18,6 +18,9 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanproduct.calc;
|
||||
|
||||
import static java.math.BigDecimal.ZERO;
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper.isInPeriodFromInclusiveToExclusive;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
@@ -134,12 +137,112 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
scheduleModel.repaymentPeriods().stream().filter(rp -> !operation.getSubmittedOnDate().isAfter(rp.getFromDate()))
|
||||
.forEach(rp -> rp.setTotalDisbursedAmount(rp.getTotalDisbursedAmount().add(operation.getAmount())));
|
||||
|
||||
scheduleModel
|
||||
.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), operation.getAmount(),
|
||||
scheduleModel.zero(), scheduleModel.zero())
|
||||
.ifPresent((repaymentPeriod) -> calculateEMIValueAndRateFactors(
|
||||
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, operation.getSubmittedOnDate()), scheduleModel,
|
||||
operation));
|
||||
final int numberOfRepayments = scheduleModel.loanProductRelatedDetail().getNumberOfRepayments();
|
||||
if (scheduleModel.loanProductRelatedDetail().isAllowFullTermForTranche() && numberOfRepayments > 0) {
|
||||
addFullTermTrancheDisbursement(scheduleModel, operation);
|
||||
} else {
|
||||
scheduleModel
|
||||
.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), operation.getAmount(),
|
||||
scheduleModel.zero(), scheduleModel.zero())
|
||||
.ifPresent((repaymentPeriod) -> calculateEMIValueAndRateFactors(
|
||||
getEffectiveRepaymentDueDate(scheduleModel, repaymentPeriod, operation.getSubmittedOnDate()), scheduleModel,
|
||||
operation));
|
||||
}
|
||||
}
|
||||
|
||||
private void addFullTermTrancheDisbursement(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final EmiChangeOperation operation) {
|
||||
final MathContext mc = scheduleModel.mc();
|
||||
final LocalDate disbursementDate = operation.getSubmittedOnDate();
|
||||
final Money disbursedAmount = operation.getAmount();
|
||||
final ILoanConfigurationDetails loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail();
|
||||
final Optional<RepaymentPeriod> firstDisbursedPeriod = scheduleModel.repaymentPeriods().stream()
|
||||
.filter(period -> isInPeriodFromInclusiveToExclusive(disbursementDate, period.getFromDate(), period.getDueDate()))
|
||||
.findFirst();
|
||||
final LocalDate firstDisbursedPeriodStartDate = firstDisbursedPeriod.isPresent() ? firstDisbursedPeriod.get().getFromDate()
|
||||
: scheduleModel.getMaturityDate();
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = buildLoanApplicationTerms(loanProductRelatedDetail, firstDisbursedPeriodStartDate,
|
||||
disbursedAmount, mc);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generateTemporaryScheduleModel(loanApplicationTerms, mc,
|
||||
firstDisbursedPeriodStartDate, disbursementDate);
|
||||
|
||||
mergeNewScheduleModelWithExistingOne(scheduleModel, temporaryReAgedScheduleModel, operation);
|
||||
}
|
||||
|
||||
private LoanApplicationTerms buildLoanApplicationTerms(final ILoanConfigurationDetails loanProductRelatedDetail,
|
||||
final LocalDate firstDisbursedPeriodStartDate, final Money disbursedAmount, final MathContext mc) {
|
||||
return new LoanApplicationTerms.Builder()
|
||||
// Loan basics
|
||||
.currency(loanProductRelatedDetail.getCurrencyData()).principal(disbursedAmount)
|
||||
.repaymentsStartingFromDate(firstDisbursedPeriodStartDate).downPaymentPercentage(ZERO)
|
||||
.seedDate(firstDisbursedPeriodStartDate).inArrearsTolerance(Money.zero(loanProductRelatedDetail.getCurrencyData()))
|
||||
// Term & frequency
|
||||
.loanTermFrequency(loanProductRelatedDetail.getNumberOfRepayments())
|
||||
.loanTermPeriodFrequencyType(loanProductRelatedDetail.getRepaymentPeriodFrequencyType())
|
||||
.numberOfRepayments(loanProductRelatedDetail.getNumberOfRepayments())
|
||||
.repaymentEvery(loanProductRelatedDetail.getRepayEvery())
|
||||
.repaymentPeriodFrequencyType(loanProductRelatedDetail.getRepaymentPeriodFrequencyType())
|
||||
// Interest configuration
|
||||
.interestRatePerPeriod(loanProductRelatedDetail.getAnnualNominalInterestRate())
|
||||
.annualNominalInterestRate(loanProductRelatedDetail.getAnnualNominalInterestRate())
|
||||
.interestRatePeriodFrequencyType(loanProductRelatedDetail.getRepaymentPeriodFrequencyType())
|
||||
.interestMethod(loanProductRelatedDetail.getInterestMethod())
|
||||
.interestRecognitionOnDisbursementDate(loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate())
|
||||
// Day count conventions
|
||||
.daysInMonthType(DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType()))
|
||||
.daysInYearType(DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType()))
|
||||
.daysInYearCustomStrategy(loanProductRelatedDetail.getDaysInYearCustomStrategy())
|
||||
// Flags & options
|
||||
.isDownPaymentEnabled(false)
|
||||
.allowPartialPeriodInterestCalculation(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation())
|
||||
// Technical
|
||||
.mc(mc).build();
|
||||
}
|
||||
|
||||
private void mergeNewScheduleModelWithExistingOne(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final ProgressiveLoanInterestScheduleModel temporaryScheduleModel, final EmiChangeOperation operation) {
|
||||
final List<RepaymentPeriod> newPeriods = temporaryScheduleModel.repaymentPeriods();
|
||||
|
||||
if (newPeriods.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<RepaymentPeriod> existingRepaymentPeriods = scheduleModel.repaymentPeriods();
|
||||
|
||||
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(), operation.getAmount(),
|
||||
scheduleModel.zero(), scheduleModel.zero());
|
||||
|
||||
for (final RepaymentPeriod newPeriod : newPeriods) {
|
||||
final Money newPrincipal = newPeriod.getDuePrincipal();
|
||||
final Money newInterest = newPeriod.getDueInterest();
|
||||
|
||||
final Optional<RepaymentPeriod> existingRepaymentPeriod = existingRepaymentPeriods.stream()
|
||||
.filter(ep -> ep.getFromDate().isEqual(newPeriod.getFromDate()) && ep.getDueDate().equals(newPeriod.getDueDate()))
|
||||
.findFirst();
|
||||
|
||||
if (existingRepaymentPeriod.isPresent()) {
|
||||
existingRepaymentPeriod.get().setEmi(existingRepaymentPeriod.get().getEmi().add(newPrincipal.add(newInterest)));
|
||||
existingRepaymentPeriod.get().setOriginalEmi(existingRepaymentPeriod.get().getEmi());
|
||||
calculateRateFactorForRepaymentPeriod(existingRepaymentPeriod.get(), scheduleModel);
|
||||
} else {
|
||||
final RepaymentPeriod rp = RepaymentPeriod.create(
|
||||
!existingRepaymentPeriods.isEmpty() ? existingRepaymentPeriods.getLast() : null,
|
||||
(newPeriod.equals(newPeriods.getFirst()) && !existingRepaymentPeriods.isEmpty())
|
||||
? existingRepaymentPeriods.getLast().getDueDate()
|
||||
: newPeriod.getFromDate(),
|
||||
newPeriod.getDueDate(), newPrincipal.add(newInterest), scheduleModel.mc(),
|
||||
scheduleModel.loanProductRelatedDetail());
|
||||
rp.setTotalDisbursedAmount(scheduleModel.getLastRepaymentPeriod().getTotalDisbursedAmount());
|
||||
|
||||
existingRepaymentPeriods.add(rp);
|
||||
calculateRateFactorForRepaymentPeriod(rp, scheduleModel);
|
||||
}
|
||||
}
|
||||
|
||||
calculateOutstandingBalance(scheduleModel);
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, operation.getSubmittedOnDate());
|
||||
}
|
||||
|
||||
private LocalDate getEffectiveRepaymentDueDate(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
@@ -590,10 +693,10 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
|
||||
moveOutstandingAmountsFromPeriodsBeforeTransactionDate(scheduleModel.repaymentPeriods(), targetDate);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generateTemporaryReAgedScheduleModel(loanApplicationTerms,
|
||||
mc, reAgePeriodStartDate);
|
||||
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generateTemporaryScheduleModel(loanApplicationTerms, mc,
|
||||
reAgePeriodStartDate, reAgePeriodStartDate);
|
||||
|
||||
mergeNewInterestScheduleModelWithExistingOne(scheduleModel, temporaryReAgedScheduleModel, reAgeFirstDueDate, targetDate,
|
||||
attachTemporaryScheduleModelReAgedPeriodsToExistingModel(scheduleModel, temporaryReAgedScheduleModel, reAgeFirstDueDate, targetDate,
|
||||
paidBalancesFromTransactionDate);
|
||||
}
|
||||
|
||||
@@ -853,11 +956,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
}
|
||||
|
||||
/**
|
||||
* * Merging the new temporary model of re-aged repayment periods and existing one together. After that recalculate
|
||||
* the balances of the updated model and also recalculate the EMI if the EMI of the last repayment period differs
|
||||
* significantly from other periods.
|
||||
* * Attaching re-aged repayment periods of the new temporary model to existing model repayment periods. After that
|
||||
* recalculate the balances of the updated model and also recalculate the EMI if the EMI of the last repayment
|
||||
* period differs significantly from other periods.
|
||||
*/
|
||||
private void mergeNewInterestScheduleModelWithExistingOne(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
private void attachTemporaryScheduleModelReAgedPeriodsToExistingModel(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel, final LocalDate reAgeFirstDueDate,
|
||||
final LocalDate targetDate, final OutstandingDetails paidBalancesFromTransactionDate) {
|
||||
final List<RepaymentPeriod> newPeriods = temporaryReAgedScheduleModel.repaymentPeriods();
|
||||
@@ -914,18 +1017,18 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
}
|
||||
|
||||
/**
|
||||
* * Generates temporary interestScheduleModel with re-aged repayment periods
|
||||
* * Generates temporary interestScheduleModel with particular disbursement date
|
||||
*/
|
||||
@NotNull
|
||||
private ProgressiveLoanInterestScheduleModel generateTemporaryReAgedScheduleModel(final LoanApplicationTerms loanApplicationTerms,
|
||||
final MathContext mc, final LocalDate periodStartDate) {
|
||||
private ProgressiveLoanInterestScheduleModel generateTemporaryScheduleModel(final LoanApplicationTerms loanApplicationTerms,
|
||||
final MathContext mc, final LocalDate periodStartDate, final LocalDate disbursementDate) {
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = scheduledDateGenerator.generateRepaymentPeriods(mc,
|
||||
periodStartDate, loanApplicationTerms, null);
|
||||
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanApplicationTerms.toLoanConfigurationDetails(),
|
||||
loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc);
|
||||
|
||||
addDisbursement(temporaryReAgedScheduleModel, EmiChangeOperation.disburse(periodStartDate, loanApplicationTerms.getPrincipal()));
|
||||
addDisbursement(temporaryReAgedScheduleModel, EmiChangeOperation.disburse(disbursementDate, loanApplicationTerms.getPrincipal()));
|
||||
return temporaryReAgedScheduleModel;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -65,7 +65,7 @@ class LoanScheduleGeneratorTest {
|
||||
LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), CURRENCY,
|
||||
DISBURSEMENT_AMOUNT, DISBURSEMENT_DATE, NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE,
|
||||
NOMINAL_INTEREST_RATE, false, DaysInMonthType.DAYS_30, DaysInYearType.DAYS_360, null, null, null, false, null,
|
||||
InterestMethod.DECLINING_BALANCE, true);
|
||||
InterestMethod.DECLINING_BALANCE, true, false);
|
||||
|
||||
ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||
ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator,
|
||||
@@ -103,7 +103,7 @@ class LoanScheduleGeneratorTest {
|
||||
LoanRepaymentScheduleModelData modelData = new LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), CURRENCY,
|
||||
DISBURSEMENT_AMOUNT_100, LocalDate.of(2024, 1, 1), NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE,
|
||||
NOMINAL_INTEREST_RATE, true, DaysInMonthType.DAYS_30, DaysInYearType.DAYS_360, DOWN_PAYMENT_PORTION, null, null, false,
|
||||
null, InterestMethod.DECLINING_BALANCE, true);
|
||||
null, InterestMethod.DECLINING_BALANCE, true, false);
|
||||
|
||||
ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator();
|
||||
ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator,
|
||||
|
||||
+95
@@ -235,6 +235,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -582,6 +583,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
threadLocalContextUtil.when(ThreadLocalContextUtil::getBusinessDate).thenReturn(LocalDate.of(2024, 2, 15));
|
||||
|
||||
@@ -1348,6 +1350,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestModel = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -2476,6 +2479,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -2533,6 +2537,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -2601,6 +2606,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -2953,6 +2959,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -2991,6 +2998,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -3028,6 +3036,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -3068,6 +3077,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -3107,6 +3117,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(2);
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -3143,6 +3154,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInYearCustomStrategy())
|
||||
.thenReturn(DaysInYearCustomStrategyType.FEB_29_PERIOD_ONLY);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -3363,6 +3375,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(4);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue());
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue());
|
||||
@@ -3402,6 +3415,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.WEEKS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(4);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue());
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
|
||||
@@ -3441,6 +3455,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.DAYS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(4);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(30);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue());
|
||||
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
|
||||
@@ -3530,6 +3545,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod())
|
||||
.thenReturn(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
@@ -3558,6 +3574,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(8);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(2);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
@@ -3587,6 +3604,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
|
||||
|
||||
@@ -3620,6 +3638,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(false);
|
||||
|
||||
@@ -3653,6 +3672,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
|
||||
|
||||
@@ -3691,6 +3711,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(20);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
@@ -3732,6 +3753,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(24);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
|
||||
final Integer installmentAmountInMultiplesOf = null;
|
||||
@@ -3806,6 +3828,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(false);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
|
||||
|
||||
@@ -3855,6 +3878,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
|
||||
|
||||
@@ -3892,6 +3916,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
|
||||
|
||||
@@ -3928,6 +3953,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(false);
|
||||
|
||||
@@ -3986,6 +4012,7 @@ class ProgressiveEMICalculatorTest {
|
||||
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
|
||||
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(false);
|
||||
|
||||
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(false);
|
||||
|
||||
@@ -5152,4 +5179,72 @@ class ProgressiveEMICalculatorTest {
|
||||
return interestScheduleModelService.fromJson(json, toCopy.loanProductRelatedDetail(), toCopy.mc(),
|
||||
toCopy.installmentAmountInMultiplesOf());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_fullTermTranche_disbursedAmt200_2ndOnDueDate_dayInYears360_daysInMonth30_repayEvery1Month() {
|
||||
// Create 7 periods (6 original + 1 extension for second tranche)
|
||||
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)));
|
||||
expectedRepaymentPeriods.add(repayment(7, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 8, 1)));
|
||||
|
||||
final BigDecimal interestRate = BigDecimal.valueOf(9.4822);
|
||||
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);
|
||||
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(6);
|
||||
// Enable full term tranche feature
|
||||
Mockito.when(loanProductRelatedDetail.isAllowFullTermForTranche()).thenReturn(true);
|
||||
|
||||
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
|
||||
// First disbursement: 100 on Jan 1 -> should set EMI ~17.13 on periods 0-5
|
||||
final Money disbursedAmount1 = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount1);
|
||||
|
||||
// Check EMI after first disbursement - periods 0-5 should have ~17.13
|
||||
Assertions.assertEquals(17.13, toDouble(interestSchedule.repaymentPeriods().get(0).getEmi()), 0.01,
|
||||
"Period 0 EMI after first disbursement");
|
||||
Assertions.assertEquals(17.13, toDouble(interestSchedule.repaymentPeriods().get(1).getEmi()), 0.01,
|
||||
"Period 1 EMI after first disbursement");
|
||||
Assertions.assertEquals(17.13, toDouble(interestSchedule.repaymentPeriods().get(2).getEmi()), 0.01,
|
||||
"Period 2 EMI after first disbursement");
|
||||
Assertions.assertEquals(17.13, toDouble(interestSchedule.repaymentPeriods().get(3).getEmi()), 0.01,
|
||||
"Period 3 EMI after first disbursement");
|
||||
Assertions.assertEquals(17.13, toDouble(interestSchedule.repaymentPeriods().get(4).getEmi()), 0.01,
|
||||
"Period 4 EMI after first disbursement");
|
||||
Assertions.assertEquals(17.13, toDouble(interestSchedule.repaymentPeriods().get(5).getEmi()), 0.01,
|
||||
"Period 5 EMI after first disbursement");
|
||||
Assertions.assertEquals(0.0, toDouble(interestSchedule.repaymentPeriods().get(6).getEmi()), 0.01,
|
||||
"Period 6 EMI after first disbursement (should be 0)");
|
||||
|
||||
// Second disbursement: 100 on Feb 1 -> should ADD EMI ~17.13 to periods 1-6
|
||||
final Money disbursedAmount2 = toMoney(100.0);
|
||||
emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 2, 1), disbursedAmount2);
|
||||
|
||||
// Verify EMI values:
|
||||
// Period 0: EMI = 17.13 (only first tranche)
|
||||
// Periods 1-5: EMI = 34.26 (both tranches)
|
||||
// Period 6: EMI = 17.13 (only second tranche)
|
||||
Assertions.assertEquals(17.13, toDouble(interestSchedule.repaymentPeriods().get(0).getEmi()), 0.01,
|
||||
"Period 0 EMI (single tranche)");
|
||||
Assertions.assertEquals(34.26, toDouble(interestSchedule.repaymentPeriods().get(1).getEmi()), 0.01, "Period 1 EMI (aggregated)");
|
||||
Assertions.assertEquals(34.26, toDouble(interestSchedule.repaymentPeriods().get(2).getEmi()), 0.01, "Period 2 EMI (aggregated)");
|
||||
Assertions.assertEquals(34.26, toDouble(interestSchedule.repaymentPeriods().get(3).getEmi()), 0.01, "Period 3 EMI (aggregated)");
|
||||
Assertions.assertEquals(34.26, toDouble(interestSchedule.repaymentPeriods().get(4).getEmi()), 0.01, "Period 4 EMI (aggregated)");
|
||||
Assertions.assertEquals(34.26, toDouble(interestSchedule.repaymentPeriods().get(5).getEmi()), 0.01, "Period 5 EMI (aggregated)");
|
||||
Assertions.assertEquals(17.13, toDouble(interestSchedule.repaymentPeriods().get(6).getEmi()), 0.01,
|
||||
"Period 6 EMI (single tranche)");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user