This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 0b9687d2a FINERACT-2081: Fix end of month and partial interest 
calculation for 360/30
0b9687d2a is described below

commit 0b9687d2aab6fce1097a8eeb86319879b2f3471c
Author: Adam Saghy <[email protected]>
AuthorDate: Mon Jan 27 20:04:14 2025 +0100

    FINERACT-2081: Fix end of month and partial interest calculation for 360/30
---
 .../loanschedule/domain/LoanApplicationTerms.java  |   8 +-
 .../data/LoanProductRelatedDetailMinimumData.java  |  34 +-
 ...oductMinimumRepaymentScheduleRelatedDetail.java |   5 -
 .../domain/LoanProductRelatedDetail.java           |   2 -
 ...dvancedPaymentScheduleTransactionProcessor.java |  33 +-
 .../data/ProgressiveLoanInterestScheduleModel.java |  23 +-
 .../domain/ProgressiveLoanScheduleGenerator.java   |   8 +-
 .../portfolio/loanproduct/calc/EMICalculator.java  |   6 +-
 .../loanproduct/calc/ProgressiveEMICalculator.java | 152 ++++++-
 .../calc/ProgressiveEMICalculatorTest.java         | 500 ++++++++++++++++++---
 10 files changed, 615 insertions(+), 156 deletions(-)

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

Reply via email to