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

commit 40f8b59ab44d1dc15c844871f4749a68789dd544
Author: Soma Sörös <[email protected]>
AuthorDate: Wed Jun 25 15:30:29 2025 +0200

    FINERACT-1968: Progressive Loan schedule handling - interest handling (FLAT 
interest, Same as repayment period, Equal installments)
---
 .../loanschedule/domain/LoanApplicationTerms.java  |  28 +-
 .../domain/LoanRepaymentScheduleModelData.java     |   4 +-
 .../data/LoanProductRelatedDetailMinimumData.java  |   9 +-
 .../domain/InterestCalculationPeriodMethod.java    |   4 +
 .../loanproduct/domain/InterestMethod.java         |   4 +
 ...oductMinimumRepaymentScheduleRelatedDetail.java |   2 +
 .../domain/LoanProductRelatedDetail.java           |   6 +-
 .../misc/Main.java                                 |   5 +-
 ...ddableProgressiveLoanScheduleGeneratorTest.java |   5 +-
 .../InterestScheduleModelServiceGsonContext.java   |   2 +-
 .../loanproduct/calc/ProgressiveEMICalculator.java | 110 +++++-
 .../loanproduct/calc/data/InterestPeriod.java      |  26 +-
 .../data/ProgressiveLoanInterestScheduleModel.java |  12 +-
 .../loanproduct/calc/data/RepaymentPeriod.java     |  58 ++-
 .../domain/LoanScheduleGeneratorTest.java          |   6 +-
 .../calc/ProgressiveEMICalculatorTest.java         | 406 +++++++++++++++++++++
 .../service/LoanScheduleAssembler.java             |   8 +-
 .../serialization/LoanApplicationValidator.java    |  45 ++-
 .../LoanProductRelatedDetailUpdateUtil.java        |   6 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java |   6 +-
 .../serialization/LoanProductDataValidator.java    |  35 +-
 21 files changed, 710 insertions(+), 77 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 96e13f7452..0ec0d57e8c 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
@@ -290,10 +290,13 @@ public final class LoanApplicationTerms {
         this.buyDownFeeCalculationType = builder.buyDownFeeCalculationType;
         this.buyDownFeeStrategy = builder.buyDownFeeStrategy;
         this.buyDownFeeIncomeType = builder.buyDownFeeIncomeType;
+        this.interestMethod = builder.interestMethod;
+        this.allowPartialPeriodInterestCalcualtion = 
builder.allowPartialPeriodInterestCalculation;
     }
 
     public static class Builder {
 
+        private InterestMethod interestMethod;
         private CurrencyData currency;
         private Integer loanTermFrequency;
         private PeriodFrequencyType loanTermPeriodFrequencyType;
@@ -327,6 +330,12 @@ public final class LoanApplicationTerms {
         private LoanBuyDownFeeCalculationType buyDownFeeCalculationType;
         private LoanBuyDownFeeStrategy buyDownFeeStrategy;
         private LoanBuyDownFeeIncomeType buyDownFeeIncomeType;
+        private boolean allowPartialPeriodInterestCalculation;
+
+        public Builder interestMethod(InterestMethod interestMethod) {
+            this.interestMethod = interestMethod;
+            return this;
+        }
 
         public Builder currency(CurrencyData currency) {
             this.currency = currency;
@@ -491,6 +500,12 @@ public final class LoanApplicationTerms {
             this.daysInYearCustomStrategy = daysInYearCustomStrategy;
             return this;
         }
+
+        public Builder allowPartialPeriodInterestCalculation(boolean 
allowPartialPeriodInterestCalculation) {
+            this.allowPartialPeriodInterestCalculation = 
allowPartialPeriodInterestCalculation;
+            return this;
+        }
+
     }
 
     public static LoanApplicationTerms 
assembleFrom(LoanRepaymentScheduleModelData modelData, MathContext mc) {
@@ -518,7 +533,8 @@ public final class LoanApplicationTerms {
                 
.isDownPaymentEnabled(modelData.downPaymentEnabled()).downPaymentPercentage(downPaymentPercentage)
                 
.submittedOnDate(modelData.scheduleGenerationStartDate()).seedDate(seedDate)
                 
.interestRecognitionOnDisbursementDate(modelData.interestRecognitionOnDisbursementDate())
-                
.daysInYearCustomStrategy(modelData.daysInYearCustomStrategy()).mc(mc).build();
+                
.daysInYearCustomStrategy(modelData.daysInYearCustomStrategy()).interestMethod(modelData.interestMethod())
+                
.allowPartialPeriodInterestCalculation(modelData.allowPartialPeriodInterestCalculation()).mc(mc).build();
     }
 
     public static LoanApplicationTerms assembleFrom(final CurrencyData 
currency, final Integer loanTermFrequency,
@@ -526,7 +542,7 @@ public final class LoanApplicationTerms {
             final PeriodFrequencyType repaymentPeriodFrequencyType, Integer 
nthDay, DayOfWeekType weekDayType,
             final AmortizationMethod amortizationMethod, final InterestMethod 
interestMethod, final BigDecimal interestRatePerPeriod,
             final PeriodFrequencyType interestRatePeriodFrequencyType, final 
BigDecimal annualNominalInterestRate,
-            final InterestCalculationPeriodMethod 
interestCalculationPeriodMethod, final boolean 
allowPartialPeriodInterestCalcualtion,
+            final InterestCalculationPeriodMethod 
interestCalculationPeriodMethod, final boolean 
allowPartialPeriodInterestCalculation,
             final Money principalMoney, final LocalDate 
expectedDisbursementDate, final LocalDate repaymentsStartingFromDate,
             final LocalDate calculatedRepaymentsStartingFromDate, final 
Integer graceOnPrincipalPayment,
             final Integer recurringMoratoriumOnPrincipalPeriods, final Integer 
graceOnInterestPayment, final Integer graceOnInterestCharged,
@@ -561,7 +577,7 @@ public final class LoanApplicationTerms {
         return new LoanApplicationTerms(currency, loanTermFrequency, 
loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery,
                 repaymentPeriodFrequencyType, nthDay, weekDayType, 
amortizationMethod, interestMethod, interestRatePerPeriod,
                 interestRatePeriodFrequencyType, annualNominalInterestRate, 
interestCalculationPeriodMethod,
-                allowPartialPeriodInterestCalcualtion, principalMoney, 
expectedDisbursementDate, repaymentsStartingFromDate,
+                allowPartialPeriodInterestCalculation, principalMoney, 
expectedDisbursementDate, repaymentsStartingFromDate,
                 calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, 
recurringMoratoriumOnPrincipalPeriods,
                 graceOnInterestPayment, graceOnInterestCharged, 
interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
                 disbursementDatas, maxOutstandingBalance, 
graceOnArrearsAgeing, daysInMonthType, daysInYearType,
@@ -608,7 +624,7 @@ public final class LoanApplicationTerms {
         final PeriodFrequencyType interestRatePeriodFrequencyType = 
loanProductRelatedDetail.getInterestPeriodFrequencyType();
         final InterestCalculationPeriodMethod interestCalculationPeriodMethod 
= loanProductRelatedDetail
                 .getInterestCalculationPeriodMethod();
-        final boolean allowPartialPeriodInterestCalcualtion = 
loanProductRelatedDetail.isAllowPartialPeriodInterestCalcualtion();
+        final boolean allowPartialPeriodInterestCalculation = 
loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation();
         final Money principalMoney = loanProductRelatedDetail.getPrincipal();
 
         //
@@ -636,7 +652,7 @@ public final class LoanApplicationTerms {
         return new LoanApplicationTerms(currency, loanTermFrequency, 
loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery,
                 repaymentPeriodFrequencyType, ((nthDay != null) ? 
nthDay.getValue() : null), dayOfWeek, amortizationMethod, interestMethod,
                 interestRatePerPeriod, interestRatePeriodFrequencyType, 
annualNominalInterestRate, interestCalculationPeriodMethod,
-                allowPartialPeriodInterestCalcualtion, principalMoney, 
expectedDisbursementDate, repaymentsStartingFromDate,
+                allowPartialPeriodInterestCalculation, principalMoney, 
expectedDisbursementDate, repaymentsStartingFromDate,
                 calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, 
recurringMoratoriumOnPrincipalPeriods,
                 graceOnInterestPayment, graceOnInterestCharged, 
interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
                 disbursementDatas, maxOutstandingBalance, 
loanProductRelatedDetail.getGraceOnArrearsAgeing(), daysInMonthType,
@@ -1674,7 +1690,7 @@ public final class LoanApplicationTerms {
                 interestCalculationPeriodMethod, daysInYearType, 
daysInMonthType, amortizationMethod, repaymentPeriodFrequencyType,
                 repaymentEvery, numberOfRepayments,
                 isInterestChargedFromDateSameAsDisbursalDateEnabled != null && 
isInterestChargedFromDateSameAsDisbursalDateEnabled,
-                daysInYearCustomStrategy);
+                daysInYearCustomStrategy, 
allowPartialPeriodInterestCalcualtion);
     }
 
     public Integer getLoanTermFrequency() {
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanRepaymentScheduleModelData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanRepaymentScheduleModelData.java
index 286f4d8a04..fe6b1e055a 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanRepaymentScheduleModelData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanRepaymentScheduleModelData.java
@@ -27,11 +27,13 @@ import 
org.apache.fineract.organisation.monetary.data.CurrencyData;
 import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
 import 
org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
 import org.apache.fineract.portfolio.common.domain.DaysInYearType;
+import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
 
 public record LoanRepaymentScheduleModelData(@NotNull LocalDate 
scheduleGenerationStartDate, @NotNull CurrencyData currency,
         @NotNull BigDecimal disbursementAmount, @NotNull LocalDate 
disbursementDate, @NotNull int numberOfRepayments,
         @NotNull int repaymentFrequency, @NotBlank String 
repaymentFrequencyType, @NotNull BigDecimal annualNominalInterestRate,
         @NotNull boolean downPaymentEnabled, @NotNull DaysInMonthType 
daysInMonth, @NotNull DaysInYearType daysInYear,
         BigDecimal downPaymentPercentage, Integer 
installmentAmountInMultiplesOf, Integer fixedLength,
-        @NotNull Boolean interestRecognitionOnDisbursementDate, @Nullable 
DaysInYearCustomStrategyType daysInYearCustomStrategy) {
+        @NotNull Boolean interestRecognitionOnDisbursementDate, @Nullable 
DaysInYearCustomStrategyType daysInYearCustomStrategy,
+        @NotNull InterestMethod interestMethod, @NotNull boolean 
allowPartialPeriodInterestCalculation) {
 }
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 ffdfc47d75..812b158588 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
@@ -48,6 +48,7 @@ public class LoanProductRelatedDetailMinimumData implements 
LoanProductMinimumRe
     private final Integer numberOfRepayments;
     private final boolean interestRecognitionOnDisbursementDate;
     private final DaysInYearCustomStrategyType daysInYearCustomStrategy;
+    private final boolean allowPartialPeriodInterestCalculation;
 
     public LoanProductRelatedDetailMinimumData(CurrencyData currency, 
BigDecimal interestRatePerPeriod,
             BigDecimal annualNominalInterestRate, Integer 
interestChargingGrace, Integer interestPaymentGrace, Integer principalGrace,
@@ -55,7 +56,7 @@ public class LoanProductRelatedDetailMinimumData implements 
LoanProductMinimumRe
             InterestCalculationPeriodMethod interestCalculationPeriodMethod, 
DaysInYearType daysInYearType, DaysInMonthType daysInMonthType,
             AmortizationMethod amortizationMethod, PeriodFrequencyType 
repaymentPeriodFrequencyType, Integer repaymentEvery,
             Integer numberOfRepayments, boolean 
interestRecognitionOnDisbursementDate,
-            DaysInYearCustomStrategyType daysInYearCustomStrategy) {
+            DaysInYearCustomStrategyType daysInYearCustomStrategy, boolean 
allowPartialPeriodInterestCalculation) {
         this.currency = currency;
         this.interestRatePerPeriod = interestRatePerPeriod;
         this.annualNominalInterestRate = annualNominalInterestRate;
@@ -73,6 +74,7 @@ public class LoanProductRelatedDetailMinimumData implements 
LoanProductMinimumRe
         this.numberOfRepayments = numberOfRepayments;
         this.interestRecognitionOnDisbursementDate = 
interestRecognitionOnDisbursementDate;
         this.daysInYearCustomStrategy = daysInYearCustomStrategy;
+        this.allowPartialPeriodInterestCalculation = 
allowPartialPeriodInterestCalculation;
     }
 
     private Integer defaultToNullIfZero(final Integer value) {
@@ -83,6 +85,11 @@ public class LoanProductRelatedDetailMinimumData implements 
LoanProductMinimumRe
         return defaultTo;
     }
 
+    @Override
+    public boolean isAllowPartialPeriodInterestCalculation() {
+        return allowPartialPeriodInterestCalculation;
+    }
+
     @Override
     public CurrencyData getCurrencyData() {
         return currency;
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/InterestCalculationPeriodMethod.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/InterestCalculationPeriodMethod.java
index 1c0237fdb7..1e5ed160c5 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/InterestCalculationPeriodMethod.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/InterestCalculationPeriodMethod.java
@@ -53,4 +53,8 @@ public enum InterestCalculationPeriodMethod {
         return 
this.value.equals(InterestCalculationPeriodMethod.DAILY.getValue());
     }
 
+    public boolean isSameAsRepaymentPeriod() {
+        return 
this.value.equals(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD.getValue());
+    }
+
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/InterestMethod.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/InterestMethod.java
index a511378427..407f7fcc38 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/InterestMethod.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/InterestMethod.java
@@ -52,4 +52,8 @@ public enum InterestMethod {
     public boolean isDecliningBalance() {
         return this.value.equals(InterestMethod.DECLINING_BALANCE.getValue());
     }
+
+    public boolean isFlat() {
+        return this.value.equals(InterestMethod.FLAT.getValue());
+    }
 }
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 08623b60b1..10d33bef0b 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
@@ -48,6 +48,8 @@ public interface 
LoanProductMinimumRepaymentScheduleRelatedDetail {
 
     InterestCalculationPeriodMethod getInterestCalculationPeriodMethod();
 
+    boolean isAllowPartialPeriodInterestCalculation();
+
     Integer getRepayEvery();
 
     PeriodFrequencyType getRepaymentPeriodFrequencyType();
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 c200f657bf..dcbf1a9046 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
@@ -86,7 +86,7 @@ public class LoanProductRelatedDetail implements 
LoanProductMinimumRepaymentSche
     private InterestCalculationPeriodMethod interestCalculationPeriodMethod;
 
     @Column(name = "allow_partial_period_interest_calcualtion", nullable = 
false)
-    private boolean allowPartialPeriodInterestCalcualtion;
+    private boolean allowPartialPeriodInterestCalculation;
 
     @Column(name = "repay_every", nullable = false)
     private Integer repayEvery;
@@ -248,7 +248,7 @@ public class LoanProductRelatedDetail implements 
LoanProductMinimumRepaymentSche
     public LoanProductRelatedDetail(final MonetaryCurrency currency, final 
BigDecimal defaultPrincipal,
             final BigDecimal defaultNominalInterestRatePerPeriod, final 
PeriodFrequencyType interestPeriodFrequencyType,
             final BigDecimal defaultAnnualNominalInterestRate, final 
InterestMethod interestMethod,
-            final InterestCalculationPeriodMethod 
interestCalculationPeriodMethod, final boolean 
allowPartialPeriodInterestCalcualtion,
+            final InterestCalculationPeriodMethod 
interestCalculationPeriodMethod, final boolean 
allowPartialPeriodInterestCalculation,
             final Integer repayEvery, final PeriodFrequencyType 
repaymentFrequencyType, final Integer defaultNumberOfRepayments,
             final Integer graceOnPrincipalPayment, final Integer 
recurringMoratoriumOnPrincipalPeriods,
             final Integer graceOnInterestPayment, final Integer 
graceOnInterestCharged, final AmortizationMethod amortizationMethod,
@@ -272,7 +272,7 @@ public class LoanProductRelatedDetail implements 
LoanProductMinimumRepaymentSche
         this.annualNominalInterestRate = defaultAnnualNominalInterestRate;
         this.interestMethod = interestMethod;
         this.interestCalculationPeriodMethod = interestCalculationPeriodMethod;
-        this.allowPartialPeriodInterestCalcualtion = 
allowPartialPeriodInterestCalcualtion;
+        this.allowPartialPeriodInterestCalculation = 
allowPartialPeriodInterestCalculation;
         this.repayEvery = repayEvery;
         this.repaymentPeriodFrequencyType = repaymentFrequencyType;
         this.numberOfRepayments = defaultNumberOfRepayments;
diff --git 
a/fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java 
b/fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java
index 5e08658f6e..1b7d006eb6 100644
--- a/fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java
+++ b/fineract-progressive-loan-embeddable-schedule-generator/misc/Main.java
@@ -27,6 +27,7 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanRepaymentPeriod;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.EmbeddableProgressiveLoanScheduleGenerator;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleModelData;
+import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
 
 import java.math.BigDecimal;
 import java.math.MathContext;
@@ -58,8 +59,10 @@ public class Main {
         final Integer fixedLength = null;
         final Boolean interestRecognitionOnDisbursementDate = false;
         final DaysInYearCustomStrategyType dasInYearCustomStrategy = null;
+        final InterestMethod interestMethod = InterestMethod.DECLINING_BALANCE;
+        final boolean allowPartialPeriodInterestCalculation = true;
 
-        var config = new LoanRepaymentScheduleModelData(startDate, currency, 
disbursedAmount, disbursementDate, noRepayments, repaymentFrequency, 
repaymentFrequencyType, annualNominalInterestRate, isDownPaymentEnabled, 
daysInMonthType, daysInYearType, downPaymentPercentage, 
installmentAmountInMultiplesOf, fixedLength, 
interestRecognitionOnDisbursementDate, dasInYearCustomStrategy);
+        var config = new LoanRepaymentScheduleModelData(startDate, currency, 
disbursedAmount, disbursementDate, noRepayments, repaymentFrequency, 
repaymentFrequencyType, annualNominalInterestRate, isDownPaymentEnabled, 
daysInMonthType, daysInYearType, downPaymentPercentage, 
installmentAmountInMultiplesOf, fixedLength, 
interestRecognitionOnDisbursementDate, dasInYearCustomStrategy, interestMethod, 
allowPartialPeriodInterestCalculation);
 
         final LoanSchedulePlan plan = calculator.generate(mc, config);
         printPlan(plan);
diff --git 
a/fineract-progressive-loan-embeddable-schedule-generator/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGeneratorTest.java
 
b/fineract-progressive-loan-embeddable-schedule-generator/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGeneratorTest.java
index 2899cdf114..0eea0ff98f 100644
--- 
a/fineract-progressive-loan-embeddable-schedule-generator/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGeneratorTest.java
+++ 
b/fineract-progressive-loan-embeddable-schedule-generator/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGeneratorTest.java
@@ -30,6 +30,7 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanDisbursementPeriod;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanPeriod;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanRepaymentPeriod;
+import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -60,11 +61,13 @@ class EmbeddableProgressiveLoanScheduleGeneratorTest {
         final Integer fixedLength = null;
         final Boolean interestRecognitionOnDisbursementDate = false;
         final DaysInYearCustomStrategyType daysInYearCustomStrategy = null;
+        final InterestMethod interestMethod = InterestMethod.DECLINING_BALANCE;
+        final boolean allowPartialPeriodInterestCalculation = true;
 
         var config = new LoanRepaymentScheduleModelData(startDate, currency, 
disbursedAmount, disbursementDate, noRepayments,
                 repaymentFrequency, repaymentFrequencyType, 
annualNominalInterestRate, isDownPaymentEnabled, daysInMonthType,
                 daysInYearType, downPaymentPercentage, 
installmentAmountInMultiplesOf, fixedLength, 
interestRecognitionOnDisbursementDate,
-                daysInYearCustomStrategy);
+                daysInYearCustomStrategy, interestMethod, 
allowPartialPeriodInterestCalculation);
 
         final LoanSchedulePlan plan = calculator.generate(mc, config);
 
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelServiceGsonContext.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelServiceGsonContext.java
index 39de64dfe1..767c817bca 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelServiceGsonContext.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelServiceGsonContext.java
@@ -42,7 +42,7 @@ public class InterestScheduleModelServiceGsonContext {
     public RepaymentPeriod createRepaymentPeriodInstance(Type type) {
         if (type == RepaymentPeriod.class) {
 
-            setPrev(RepaymentPeriod.empty(getPrev(), getMc()));
+            setPrev(RepaymentPeriod.empty(getPrev(), getMc(), 
getLoanProductRelatedDetail()));
             return getPrev();
         }
         throw new IllegalArgumentException("Unsupported RepaymentPeriod type: 
" + type);
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 f4f84359ce..66aed18cc7 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
@@ -35,6 +35,7 @@ import java.util.function.Predicate;
 import lombok.RequiredArgsConstructor;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.MathUtil;
+import org.apache.fineract.organisation.monetary.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.DaysInYearCustomStrategyType;
@@ -91,7 +92,7 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
         final Money zero = 
Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
         final AtomicReference<RepaymentPeriod> prev = new AtomicReference<>();
         List<RepaymentPeriod> repaymentPeriods = periods.stream().map(e -> {
-            RepaymentPeriod rp = RepaymentPeriod.create(prev.get(), 
from.apply(e), to.apply(e), zero, mc);
+            RepaymentPeriod rp = RepaymentPeriod.create(prev.get(), 
from.apply(e), to.apply(e), zero, mc, loanProductRelatedDetail);
             prev.set(rp);
             return rp;
         }).toList();
@@ -114,10 +115,19 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
     @Override
     public void addDisbursement(final ProgressiveLoanInterestScheduleModel 
scheduleModel, final LocalDate disbursementDueDate,
             final Money disbursedAmount) {
-        addDisbursement(scheduleModel, 
EmiChangeOperation.disburse(disbursementDueDate, disbursedAmount));
+        LocalDate effectiveDueDate = 
scheduleModel.loanProductRelatedDetail().getInterestCalculationPeriodMethod() 
!= null
+                && 
scheduleModel.loanProductRelatedDetail().getInterestCalculationPeriodMethod().isSameAsRepaymentPeriod()
+                && 
!scheduleModel.loanProductRelatedDetail().isAllowPartialPeriodInterestCalculation()
+                        ? scheduleModel.repaymentPeriods().stream().filter(rp 
-> rp.getDueDate().isAfter(disbursementDueDate)).findFirst()
+                                
.map(RepaymentPeriod::getFromDate).orElse(disbursementDueDate)
+                        : disbursementDueDate;
+        addDisbursement(scheduleModel, 
EmiChangeOperation.disburse(effectiveDueDate, disbursedAmount));
     }
 
     private void addDisbursement(final ProgressiveLoanInterestScheduleModel 
scheduleModel, final EmiChangeOperation operation) {
+        scheduleModel.repaymentPeriods().stream().filter(rp -> 
!operation.getSubmittedOnDate().isAfter(rp.getFromDate())).forEach(
+                rp -> 
rp.setTotalDisbursedAmount(MathUtil.nullToZero(rp.getTotalDisbursedAmount()).add(operation.getAmount().getAmount())));
+
         scheduleModel
                 
.changeOutstandingBalanceAndUpdateInterestPeriods(operation.getSubmittedOnDate(),
 operation.getAmount(),
                         scheduleModel.zero(), scheduleModel.zero())
@@ -444,11 +454,33 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
         return scheduleModelCopy;
     }
 
+    private void calculateEMIValueAndRateFactorsForFlatInterestMethod(final 
LocalDate calculateFromRepaymentPeriodDueDate,
+            final ProgressiveLoanInterestScheduleModel scheduleModel, final 
EmiChangeOperation operation) {
+        final List<RepaymentPeriod> relatedRepaymentPeriods = 
scheduleModel.getRelatedRepaymentPeriods(calculateFromRepaymentPeriodDueDate);
+        calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
+        if (relatedRepaymentPeriods.isEmpty()) {
+            return;
+        }
+        
calculateEMIOnActualModelWithFlatInterestMethod(relatedRepaymentPeriods, 
scheduleModel);
+    }
+
     /**
      * Calculate Equal Monthly Installment value and Rate Factor -1 values for 
calculate Interest
      */
     private void calculateEMIValueAndRateFactors(final LocalDate 
calculateFromRepaymentPeriodDueDate,
             final ProgressiveLoanInterestScheduleModel scheduleModel, final 
EmiChangeOperation operation) {
+        switch (scheduleModel.loanProductRelatedDetail().getInterestMethod()) {
+            case FLAT ->
+                
calculateEMIValueAndRateFactorsForFlatInterestMethod(calculateFromRepaymentPeriodDueDate,
 scheduleModel, operation);
+            case DECLINING_BALANCE -> 
calculateEMIValueAndRateFactorsForDecliningBalanceInterestMethod(calculateFromRepaymentPeriodDueDate,
+                    scheduleModel, operation);
+            default -> throw new UnsupportedOperationException(
+                    "Unsupported interest method: " + 
scheduleModel.loanProductRelatedDetail().getInterestMethod());
+        }
+    }
+
+    private void 
calculateEMIValueAndRateFactorsForDecliningBalanceInterestMethod(final 
LocalDate calculateFromRepaymentPeriodDueDate,
+            final ProgressiveLoanInterestScheduleModel scheduleModel, final 
EmiChangeOperation operation) {
         final List<RepaymentPeriod> relatedRepaymentPeriods = 
scheduleModel.getRelatedRepaymentPeriods(calculateFromRepaymentPeriodDueDate);
         final boolean onlyOnActualModelShouldApply = scheduleModel.isEmpty()
                 || operation.getAction() == 
EmiChangeOperation.Action.INTEREST_RATE_CHANGE || scheduleModel.isCopy();
@@ -637,6 +669,20 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
         final boolean partialPeriodCalculationNeeded = daysInYearType == 
DaysInYearType.ACTUAL && numberOfYearsDifferenceInPeriod > 0
                 && 
(!DaysInYearCustomStrategyType.FEB_29_PERIOD_ONLY.equals(daysInYearCustomStrategy)
                         || 
isPeriodContainsFeb29(repaymentPeriod.getFromDate(), 
repaymentPeriod.getDueDate()));
+        final BigDecimal repaymentEvery = 
BigDecimal.valueOf(loanProductRelatedDetail.getRepayEvery());
+
+        if (loanProductRelatedDetail.getInterestCalculationPeriodMethod() != 
null
+                && 
loanProductRelatedDetail.getInterestCalculationPeriodMethod().isSameAsRepaymentPeriod())
 {
+
+            if 
(loanProductRelatedDetail.getRepaymentPeriodFrequencyType().isMonthly()) {
+                return rateFactorByRepaymentPeriod(interestRate, 
BigDecimal.ONE, repaymentEvery, BigDecimal.valueOf(12), actualDaysInPeriod,
+                        calculatedDaysInPeriod, mc);
+            }
+            if 
(loanProductRelatedDetail.getRepaymentPeriodFrequencyType().isWeekly()) {
+                return rateFactorByRepaymentPeriod(interestRate, 
BigDecimal.ONE, repaymentEvery, BigDecimal.valueOf(52), actualDaysInPeriod,
+                        calculatedDaysInPeriod, mc);
+            }
+        }
 
         // TODO check: 
loanApplicationTerms.calculatePeriodsBetweenDates(startDate, endDate); // 
calculate period data
         // TODO review: (repayment frequency: days, weeks, years; validation 
day is month fix 30)
@@ -747,13 +793,26 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
                 repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate());
         final BigDecimal actualDaysInPeriod = BigDecimal
                 .valueOf(DateUtils.getDifferenceInDays(interestPeriodFromDate, 
interestPeriodDueDate));
-        final BigDecimal calculatedDaysInPeriod = BigDecimal
+        final BigDecimal calculatedDaysInRepaymentPeriod = BigDecimal
                 
.valueOf(DateUtils.getDifferenceInDays(repaymentPeriod.getFromDate(), 
repaymentPeriod.getDueDate()));
         final int numberOfYearsDifferenceInPeriod = 
interestPeriodDueDate.getYear() - interestPeriodFromDate.getYear();
         final boolean partialPeriodCalculationNeeded = daysInYearType == 
DaysInYearType.ACTUAL && numberOfYearsDifferenceInPeriod > 0
                 && 
(!DaysInYearCustomStrategyType.FEB_29_PERIOD_ONLY.equals(daysInYearCustomStrategy)
                         || 
isPeriodContainsFeb29(repaymentPeriod.getFromDate(), 
repaymentPeriod.getDueDate()));
-        final BigDecimal daysInMonth = daysInMonthType.isDaysInMonth_30() ? 
BigDecimal.valueOf(30) : calculatedDaysInPeriod;
+        final BigDecimal daysInMonth = daysInMonthType.isDaysInMonth_30() ? 
BigDecimal.valueOf(30) : calculatedDaysInRepaymentPeriod;
+
+        if (loanProductRelatedDetail.getInterestCalculationPeriodMethod() != 
null
+                && 
loanProductRelatedDetail.getInterestCalculationPeriodMethod().isSameAsRepaymentPeriod())
 {
+
+            if 
(loanProductRelatedDetail.getRepaymentPeriodFrequencyType().isMonthly()) {
+                return rateFactorByRepaymentPeriod(interestRate, 
BigDecimal.ONE, repaymentEvery, BigDecimal.valueOf(12), actualDaysInPeriod,
+                        calculatedDaysInRepaymentPeriod, mc);
+            }
+            if 
(loanProductRelatedDetail.getRepaymentPeriodFrequencyType().isWeekly()) {
+                return rateFactorByRepaymentPeriod(interestRate, 
BigDecimal.ONE, repaymentEvery, BigDecimal.valueOf(52), actualDaysInPeriod,
+                        calculatedDaysInRepaymentPeriod, mc);
+            }
+        }
 
         // TODO check: 
loanApplicationTerms.calculatePeriodsBetweenDates(startDate, endDate); // 
calculate period data
         // TODO review: (repayment frequency: days, weeks, years; validation 
day is month fix 30)
@@ -769,7 +828,7 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
             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);
+                    daysInMonth, daysInYear, actualDaysInPeriod, 
calculatedDaysInRepaymentPeriod, mc);
             default -> throw new UnsupportedOperationException("Unsupported 
combination: Days in month: " + daysInMonthType);
         };
     }
@@ -845,10 +904,51 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
         };
     }
 
+    private void 
calculateEMIOnActualModelWithFlatInterestMethod(List<RepaymentPeriod> 
repaymentPeriods,
+            ProgressiveLoanInterestScheduleModel scheduleModel) {
+
+        final MathContext mc = scheduleModel.mc();
+        final CurrencyData currency = 
scheduleModel.loanProductRelatedDetail().getCurrencyData();
+        RepaymentPeriod first = repaymentPeriods.getFirst();
+        RepaymentPeriod last = repaymentPeriods.getLast();
+        Money sumOfInterest = Money.zero(currency);
+        for (RepaymentPeriod rp : repaymentPeriods) {
+            Money interest = rp.calculateCalculatedDueInterest();
+            sumOfInterest = sumOfInterest.add(interest);
+            rp.setEmi(interest);
+        }
+
+        // already repaid principals should be subtracted from total disbursed 
amount to calculate correct EMI.
+        BigDecimal alreadyRepaidPrincipals = first.getPrevious()
+                .map(rp -> 
rp.calculateTotalDisbursedAmountTillGivenPeriod(null).subtract(rp.getOutstandingLoanBalance().getAmount()))
+                .orElse(BigDecimal.ZERO);
+        Money total = Money.of(currency, 
first.calculateTotalDisbursedAmountTillGivenPeriod(first.getLastInterestPeriod()))
+                .plus(sumOfInterest).minus(alreadyRepaidPrincipals);
+
+        Money periodEmi = total.dividedBy(repaymentPeriods.size(), mc);
+        Money remainder = 
total.minus(periodEmi.multipliedBy(repaymentPeriods.size(), mc));
+
+        repaymentPeriods.forEach(rp -> {
+            Money emi = rp.equals(last) ? periodEmi.add(remainder) : periodEmi;
+            rp.setEmi(emi);
+            rp.setOriginalEmi(emi);
+            
rp.getInterestPeriods().forEach(InterestPeriod::updateOutstandingLoanBalance);
+        });
+    }
+
     private void calculateEMIOnActualModel(List<RepaymentPeriod> 
repaymentPeriods, ProgressiveLoanInterestScheduleModel scheduleModel) {
         if (repaymentPeriods.isEmpty()) {
             return;
         }
+        switch (scheduleModel.loanProductRelatedDetail().getInterestMethod()) {
+            case FLAT -> 
calculateEMIOnActualModelWithFlatInterestMethod(repaymentPeriods, 
scheduleModel);
+            case DECLINING_BALANCE -> 
calculateEMIOnActualModelWithDecliningBalanceInterestMethod(repaymentPeriods, 
scheduleModel);
+            default -> throw new UnsupportedOperationException("Unsupported 
interest method");
+        }
+    }
+
+    private void 
calculateEMIOnActualModelWithDecliningBalanceInterestMethod(List<RepaymentPeriod>
 repaymentPeriods,
+            ProgressiveLoanInterestScheduleModel scheduleModel) {
         final MathContext mc = scheduleModel.mc();
         final BigDecimal rateFactorN = 
MathUtil.stripTrailingZeros(calculateRateFactorPlus1N(repaymentPeriods, mc));
         final BigDecimal fnResult = 
MathUtil.stripTrailingZeros(calculateFnResult(repaymentPeriods, mc));
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriod.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriod.java
index d38280f281..4b1be251c8 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriod.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriod.java
@@ -33,6 +33,7 @@ import 
org.apache.fineract.infrastructure.core.serialization.gson.JsonExclude;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
 
 @Getter
 @ToString(exclude = { "repaymentPeriod" })
@@ -55,7 +56,7 @@ public class InterestPeriod implements 
Comparable<InterestPeriod> {
 
     /** Stores credited principals. Related transactions: Chargeback or Credit 
Balance Refound */
     private Money creditedPrincipal;
-    /** Stores credited principals. Related transaction: Chargeback */
+    /** Stores credited interest. Related transaction: Chargeback */
     private Money creditedInterest;
 
     private Money disbursementAmount;
@@ -139,15 +140,26 @@ public class InterestPeriod implements 
Comparable<InterestPeriod> {
         }
 
         long lengthTillPeriodDueDate = getLengthTillPeriodDueDate();
-        final BigDecimal interestDueTillRepaymentDueDate = 
lengthTillPeriodDueDate == 0 //
-                ? BigDecimal.ZERO //
-                : getOutstandingLoanBalance().getAmount() //
-                        .multiply(getRateFactorTillPeriodDueDate(), mc) //
-                        .divide(BigDecimal.valueOf(lengthTillPeriodDueDate), 
mc) //
-                        .multiply(BigDecimal.valueOf(getLength()), mc); //
+        final BigDecimal interestDueTillRepaymentDueDate = 
getCalculatedDueInterest(
+                
getRepaymentPeriod().getLoanProductRelatedDetail().getInterestMethod(), 
lengthTillPeriodDueDate); //
         return MathUtil.negativeToZero(MathUtil.add(mc, 
creditedInterest.getAmount(), interestDueTillRepaymentDueDate));
     }
 
+    public BigDecimal getCalculatedDueInterest(InterestMethod method, long 
lengthTillPeriodDueDate) {
+        if (lengthTillPeriodDueDate == 0) {
+            return BigDecimal.ZERO;
+        }
+        BigDecimal baseAmount = switch (method) {
+            case FLAT -> 
MathUtil.nullToZero(getRepaymentPeriod().calculateTotalDisbursedAmountTillGivenPeriod(this));
+            case DECLINING_BALANCE -> getOutstandingLoanBalance().getAmount();
+            default -> throw new UnsupportedOperationException("Method not 
implemented: " + method);
+        };
+        return baseAmount //
+                .multiply(getRateFactorTillPeriodDueDate(), mc) //
+                .divide(BigDecimal.valueOf(lengthTillPeriodDueDate), mc) //
+                .multiply(BigDecimal.valueOf(getLength()), mc);
+    }
+
     public long getLength() {
         return DateUtils.getDifferenceInDays(fromDate, dueDate);
     }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java
index c7c4410adc..f17b782dbe 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java
@@ -199,18 +199,8 @@ public class ProgressiveLoanInterestScheduleModel {
         if (balanceChangeDate == null) {
             return Optional.empty();
         }
-        // TODO use isInPeriod
         return repaymentPeriods.stream()//
-                .filter(repaymentPeriod -> {
-                    final boolean isFirstPeriod = 
repaymentPeriod.getPrevious().isEmpty();
-                    if (isFirstPeriod) {
-                        return 
!balanceChangeDate.isBefore(repaymentPeriod.getFromDate())
-                                && 
!balanceChangeDate.isAfter(repaymentPeriod.getDueDate());
-                    } else {
-                        return 
balanceChangeDate.isAfter(repaymentPeriod.getFromDate())
-                                && 
!balanceChangeDate.isAfter(repaymentPeriod.getDueDate());
-                    }
-                })//
+                .filter(period -> isInPeriod(balanceChangeDate, 
period.getFromDate(), period.getDueDate(), period.isFirstRepaymentPeriod()))//
                 .findFirst();
     }
 
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java
index 33b94d852c..0778e436e8 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/RepaymentPeriod.java
@@ -34,6 +34,7 @@ import lombok.ToString;
 import org.apache.fineract.infrastructure.core.serialization.gson.JsonExclude;
 import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.organisation.monetary.domain.Money;
+import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
 import org.apache.fineract.portfolio.util.Memo;
 
 @ToString(exclude = { "previous" })
@@ -79,8 +80,17 @@ public final class RepaymentPeriod {
     @Setter
     private boolean isInterestMoved = false;
 
+    @Setter
+    @Getter
+    private BigDecimal totalDisbursedAmount;
+
+    @JsonExclude
+    @Getter
+    private final LoanProductMinimumRepaymentScheduleRelatedDetail 
loanProductRelatedDetail;
+
     private RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, 
LocalDate dueDate, List<InterestPeriod> interestPeriods,
-            Money emi, Money originalEmi, Money paidPrincipal, Money 
paidInterest, Money futureUnrecognizedInterest, MathContext mc) {
+            Money emi, Money originalEmi, Money paidPrincipal, Money 
paidInterest, Money futureUnrecognizedInterest, MathContext mc,
+            LoanProductMinimumRepaymentScheduleRelatedDetail 
loanProductRelatedDetail) {
         this.previous = previous;
         this.fromDate = fromDate;
         this.dueDate = dueDate;
@@ -91,16 +101,19 @@ public final class RepaymentPeriod {
         this.paidInterest = paidInterest;
         this.futureUnrecognizedInterest = futureUnrecognizedInterest;
         this.mc = mc;
+        this.loanProductRelatedDetail = loanProductRelatedDetail;
     }
 
-    public static RepaymentPeriod empty(RepaymentPeriod previous, MathContext 
mc) {
-        return new RepaymentPeriod(previous, null, null, new ArrayList<>(), 
null, null, null, null, null, mc);
+    public static RepaymentPeriod empty(RepaymentPeriod previous, MathContext 
mc,
+            LoanProductMinimumRepaymentScheduleRelatedDetail 
loanProductRelatedDetail) {
+        return new RepaymentPeriod(previous, null, null, new ArrayList<>(), 
null, null, null, null, null, mc, loanProductRelatedDetail);
     }
 
-    public static RepaymentPeriod create(RepaymentPeriod previous, LocalDate 
fromDate, LocalDate dueDate, Money emi, MathContext mc) {
+    public static RepaymentPeriod create(RepaymentPeriod previous, LocalDate 
fromDate, LocalDate dueDate, Money emi, MathContext mc,
+            LoanProductMinimumRepaymentScheduleRelatedDetail 
loanProductRelatedDetail) {
         final Money zero = emi.zero();
         final RepaymentPeriod newRepaymentPeriod = new 
RepaymentPeriod(previous, fromDate, dueDate, new ArrayList<>(), emi, emi, zero, 
zero,
-                zero, mc);
+                zero, mc, loanProductRelatedDetail);
         // There is always at least 1 interest period, by default with same 
from-due date as repayment period
         
newRepaymentPeriod.interestPeriods.add(InterestPeriod.withEmptyAmounts(newRepaymentPeriod,
 fromDate, dueDate));
         return newRepaymentPeriod;
@@ -109,7 +122,8 @@ public final class RepaymentPeriod {
     public static RepaymentPeriod copy(RepaymentPeriod previous, 
RepaymentPeriod repaymentPeriod, MathContext mc) {
         final RepaymentPeriod newRepaymentPeriod = new 
RepaymentPeriod(previous, repaymentPeriod.fromDate, repaymentPeriod.dueDate,
                 new ArrayList<>(), repaymentPeriod.emi, 
repaymentPeriod.originalEmi, repaymentPeriod.paidPrincipal,
-                repaymentPeriod.paidInterest, 
repaymentPeriod.futureUnrecognizedInterest, mc);
+                repaymentPeriod.paidInterest, 
repaymentPeriod.futureUnrecognizedInterest, mc,
+                repaymentPeriod.getLoanProductRelatedDetail());
         // There is always at least 1 interest period, by default with same 
from-due date as repayment period
         for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) {
             
newRepaymentPeriod.interestPeriods.add(InterestPeriod.copy(newRepaymentPeriod, 
interestPeriod, mc));
@@ -120,7 +134,8 @@ public final class RepaymentPeriod {
     public static RepaymentPeriod copyWithoutPaidAmounts(RepaymentPeriod 
previous, RepaymentPeriod repaymentPeriod, MathContext mc) {
         final Money zero = repaymentPeriod.emi.zero();
         final RepaymentPeriod newRepaymentPeriod = new 
RepaymentPeriod(previous, repaymentPeriod.fromDate, repaymentPeriod.dueDate,
-                new ArrayList<>(), repaymentPeriod.emi, 
repaymentPeriod.originalEmi, zero, zero, zero, mc);
+                new ArrayList<>(), repaymentPeriod.emi, 
repaymentPeriod.originalEmi, zero, zero, zero, mc,
+                repaymentPeriod.getLoanProductRelatedDetail());
         // There is always at least 1 interest period, by default with same 
from-due date as repayment period
         for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) {
             var interestPeriodCopy = InterestPeriod.copy(newRepaymentPeriod, 
interestPeriod);
@@ -160,13 +175,13 @@ public final class RepaymentPeriod {
     @NotNull
     public Money getCalculatedDueInterest() {
         if (calculatedDueInterestCalculation == null) {
-            calculatedDueInterestCalculation = 
Memo.of(this::calculateCalculatedDueInterest,
-                    () -> new Object[] { this.previous, this.interestPeriods, 
this.futureUnrecognizedInterest, this.isInterestMoved });
+            calculatedDueInterestCalculation = 
Memo.of(this::calculateCalculatedDueInterest, () -> new Object[] { 
this.previous,
+                    this.interestPeriods, this.futureUnrecognizedInterest, 
this.isInterestMoved, this.totalDisbursedAmount });
         }
         return calculatedDueInterestCalculation.get();
     }
 
-    private Money calculateCalculatedDueInterest() {
+    public Money calculateCalculatedDueInterest() {
         Money calculatedDueInterest = getZero(mc);
         if (!isInterestMoved) {
             calculatedDueInterest = Money.of(emi.getCurrencyData(),
@@ -192,7 +207,8 @@ public final class RepaymentPeriod {
                     () -> 
MathUtil.max(getPaidPrincipal().isGreaterThan(getCalculatedDuePrincipal()) ? 
getPaidInterest()
                             : MathUtil.min(getCalculatedDueInterest(), 
getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest(), false),
                             getPaidInterest(), false),
-                    () -> new Object[] { paidPrincipal, paidInterest, 
interestPeriods, futureUnrecognizedInterest });
+                    () -> new Object[] { paidPrincipal, paidInterest, 
interestPeriods, futureUnrecognizedInterest, totalDisbursedAmount,
+                            emi });
         }
         return dueInterestCalculation.get();
     }
@@ -307,7 +323,7 @@ public final class RepaymentPeriod {
                         .minus(getDuePrincipal(), mc)//
                         .plus(getPaidPrincipal(), mc);//
                 return 
MathUtil.negativeToZero(calculatedOutStandingLoanBalance, mc);
-            }, () -> new Object[] { paidPrincipal, paidInterest, 
interestPeriods });
+            }, () -> new Object[] { paidPrincipal, paidInterest, 
interestPeriods, totalDisbursedAmount });
         }
         return outstandingBalanceCalculation.get();
     }
@@ -369,4 +385,22 @@ public final class RepaymentPeriod {
         this.paidInterest = paidInterest.zero();
         this.paidPrincipal = paidPrincipal.zero();
     }
+
+    /**
+     * @param tillPeriod
+     *            can be null. if null it calculates total disbursement 
including last interest period.
+     * @return disbursed amount til interest period.
+     */
+    public BigDecimal 
calculateTotalDisbursedAmountTillGivenPeriod(InterestPeriod tillPeriod) {
+        BigDecimal res = MathUtil.nullToZero(getTotalDisbursedAmount());
+        for (InterestPeriod interestPeriod : this.interestPeriods) {
+            if (interestPeriod.equals(tillPeriod)) {
+                break;
+            }
+            if (!interestPeriod.getDueDate().equals(getFromDate()) && 
interestPeriod.getDisbursementAmount() != null) {
+                res = 
res.add(interestPeriod.getDisbursementAmount().getAmount());
+            }
+        }
+        return res;
+    }
 }
diff --git 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java
 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java
index 552c52c80d..7e8b93f369 100644
--- 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java
+++ 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java
@@ -36,6 +36,7 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP
 import 
org.apache.fineract.portfolio.loanaccount.service.InterestScheduleModelRepositoryWrapper;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanTransactionProcessingService;
 import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator;
+import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -63,7 +64,8 @@ class LoanScheduleGeneratorTest {
     void testGenerateLoanSchedule() {
         LoanRepaymentScheduleModelData modelData = new 
LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), CURRENCY,
                 DISBURSEMENT_AMOUNT, DISBURSEMENT_DATE, NUMBER_OF_REPAYMENTS, 
REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE,
-                NOMINAL_INTEREST_RATE, false, DaysInMonthType.DAYS_30, 
DaysInYearType.DAYS_360, null, null, null, false, null);
+                NOMINAL_INTEREST_RATE, false, DaysInMonthType.DAYS_30, 
DaysInYearType.DAYS_360, null, null, null, false, null,
+                InterestMethod.DECLINING_BALANCE, true);
 
         ScheduledDateGenerator scheduledDateGenerator = new 
DefaultScheduledDateGenerator();
         ProgressiveLoanScheduleGenerator generator = new 
ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator,
@@ -101,7 +103,7 @@ class LoanScheduleGeneratorTest {
         LoanRepaymentScheduleModelData modelData = new 
LoanRepaymentScheduleModelData(LocalDate.of(2024, 1, 1), CURRENCY,
                 DISBURSEMENT_AMOUNT_100, LocalDate.of(2024, 1, 1), 
NUMBER_OF_REPAYMENTS, REPAYMENT_FREQUENCY, REPAYMENT_FREQUENCY_TYPE,
                 NOMINAL_INTEREST_RATE, true, DaysInMonthType.DAYS_30, 
DaysInYearType.DAYS_360, DOWN_PAYMENT_PORTION, null, null, false,
-                null);
+                null, InterestMethod.DECLINING_BALANCE, true);
 
         ScheduledDateGenerator scheduledDateGenerator = new 
DefaultScheduledDateGenerator();
         ProgressiveLoanScheduleGenerator generator = new 
ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator,
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 207da71ab7..8ee3018bb8 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
@@ -24,6 +24,8 @@ import java.math.RoundingMode;
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.IntStream;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
 import org.apache.fineract.organisation.monetary.data.CurrencyData;
@@ -42,6 +44,8 @@ import 
org.apache.fineract.portfolio.loanproduct.calc.data.InterestPeriod;
 import org.apache.fineract.portfolio.loanproduct.calc.data.PeriodDueDetails;
 import 
org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
 import org.apache.fineract.portfolio.loanproduct.calc.data.RepaymentPeriod;
+import 
org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
+import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
@@ -56,6 +60,7 @@ import org.mockito.Mockito;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.springframework.lang.NonNull;
 
+@Slf4j
 @ExtendWith(MockitoExtension.class)
 class ProgressiveEMICalculatorTest {
 
@@ -99,6 +104,9 @@ class ProgressiveEMICalculatorTest {
     public void setupTestDefaults() {
         
Mockito.when(loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate()).thenReturn(false);
         
Mockito.when(loanProductRelatedDetail.getDaysInYearCustomStrategy()).thenReturn(null);
+        
Mockito.when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.DECLINING_BALANCE);
+        
Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod()).thenReturn(InterestCalculationPeriodMethod.DAILY);
+        
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
     }
 
     private BigDecimal getRateFactorsByMonth(final DaysInYearType 
daysInYearType, final DaysInMonthType daysInMonthType,
@@ -3226,6 +3234,404 @@ class ProgressiveEMICalculatorTest {
 
     }
 
+    @Nested
+    public class InterestTypeFlat {
+
+        @BeforeEach
+        public void setupTestDefaults() {
+            
Mockito.when(loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate()).thenReturn(false);
+            
Mockito.when(loanProductRelatedDetail.getDaysInYearCustomStrategy()).thenReturn(null);
+            
Mockito.when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.FLAT);
+            
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
+            // InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD
+            
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue());
+            
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
+            
Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod())
+                    
.thenReturn(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD);
+            
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
+
+        }
+
+        @Test
+        void test_sameAsRepayment_days_repay_every_6_periods_5() {
+
+            final BigDecimal interestRate = BigDecimal.valueOf(12.2);
+            final LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.DAYS);
+            
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(5);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(6);
+
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
+            final Integer installmentAmountInMultiplesOf = null;
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
List.of(), installmentAmountInMultiplesOf, mc);
+
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(1000.0));
+
+            checkPeriod(interestSchedule, 0, 202.00, 2.00, 200.00, 800.00, 
false); //
+            checkPeriod(interestSchedule, 1, 202.00, 2.00, 200.00, 600.00, 
false); //
+            checkPeriod(interestSchedule, 2, 202.00, 2.00, 200.00, 400.00, 
false); //
+            checkPeriod(interestSchedule, 3, 202.00, 2.00, 200.00, 200.00, 
false); //
+            checkPeriod(interestSchedule, 4, 202.00, 2.00, 200.00, 0.00, 
false); //
+        }
+
+        @Test
+        void test_sameAsRepayment_week_repay_every_1_periods_10() {
+
+            final BigDecimal interestRate = BigDecimal.valueOf(5.2);
+            final LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
+
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = expectedRepaymentWeeks(disbursementDate, 10, 1);
+            final Integer installmentAmountInMultiplesOf = null;
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_364.getValue());
+            
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.WEEKS);
+            
Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod())
+                    
.thenReturn(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
List.of(), installmentAmountInMultiplesOf, mc);
+
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(1000.0));
+
+            checkPeriod(interestSchedule, 0, 101.00, 1.00, 100.00, 900.00, 
false); //
+            checkPeriod(interestSchedule, 1, 101.00, 1.00, 100.00, 800.00, 
false); //
+            checkPeriod(interestSchedule, 2, 101.00, 1.00, 100.00, 700.00, 
false); //
+            checkPeriod(interestSchedule, 3, 101.00, 1.00, 100.00, 600.00, 
false); //
+            checkPeriod(interestSchedule, 4, 101.00, 1.00, 100.00, 500.00, 
false); //
+            checkPeriod(interestSchedule, 5, 101.00, 1.00, 100.00, 400.00, 
false); //
+            checkPeriod(interestSchedule, 6, 101.00, 1.00, 100.00, 300.00, 
false); //
+            checkPeriod(interestSchedule, 7, 101.00, 1.00, 100.00, 200.00, 
false); //
+            checkPeriod(interestSchedule, 8, 101.00, 1.00, 100.00, 100.00, 
false); //
+            checkPeriod(interestSchedule, 9, 101.00, 1.00, 100.00, 0.00, 
false); //
+        }
+
+        @Test
+        void test_sameAsRepayment_month_repay_every_2_periods_8() {
+
+            final BigDecimal interestRate = BigDecimal.valueOf(12.0);
+            final LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+            
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(8);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(2);
+
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
+            final Integer installmentAmountInMultiplesOf = null;
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
List.of(), installmentAmountInMultiplesOf, mc);
+
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(1000.0));
+
+            checkPeriod(interestSchedule, 0, 145.00, 20.00, 125.00, 875.00, 
false); //
+            checkPeriod(interestSchedule, 1, 145.00, 20.00, 125.00, 750.00, 
false); //
+            checkPeriod(interestSchedule, 2, 145.00, 20.00, 125.00, 625.00, 
false); //
+            checkPeriod(interestSchedule, 3, 145.00, 20.00, 125.00, 500.00, 
false); //
+            checkPeriod(interestSchedule, 4, 145.00, 20.00, 125.00, 375.00, 
false); //
+            checkPeriod(interestSchedule, 5, 145.00, 20.00, 125.00, 250.00, 
false); //
+            checkPeriod(interestSchedule, 6, 145.00, 20.00, 125.00, 125.00, 
false); //
+            checkPeriod(interestSchedule, 7, 145.00, 20.00, 125.00, 0.00, 
false); //
+        }
+
+        @Test
+        void 
test_sameAsRepayment_month_repay_every_1_periods_3_second_disbursement_on_repayment_due_date_allow_partial()
 {
+
+            final BigDecimal interestRate = BigDecimal.valueOf(12.0);
+            final LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+            
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+
+            
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
+
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
+
+            final Integer installmentAmountInMultiplesOf = null;
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
List.of(), installmentAmountInMultiplesOf, mc);
+
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(1000.0));
+
+            checkPeriod(interestSchedule, 0, 343.33, 10.00, 333.33, 666.67, 
false); //
+            checkPeriod(interestSchedule, 1, 343.33, 10.00, 333.33, 333.34, 
false); //
+            checkPeriod(interestSchedule, 2, 343.34, 10.00, 333.34, 0.00, 
false); //
+
+            emiCalculator.addDisbursement(interestSchedule, 
disbursementDate.plusMonths(1), toMoney(250.0));
+
+            checkPeriod(interestSchedule, 0, 343.33, 10.00, 333.33, 916.67, 
false); //
+            checkPeriod(interestSchedule, 1, 470.84, 12.50, 458.34, 458.33, 
false); //
+            checkPeriod(interestSchedule, 2, 470.83, 12.50, 458.33, 0.00, 
false); //
+        }
+
+        @Test
+        void 
test_sameAsRepayment_month_repay_every_1_periods_3_second_disbursement_on_repayment_due_date_disallow_partial()
 {
+
+            final BigDecimal interestRate = BigDecimal.valueOf(12.0);
+            final LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+            
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+
+            
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(false);
+
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
+
+            final Integer installmentAmountInMultiplesOf = null;
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
List.of(), installmentAmountInMultiplesOf, mc);
+
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(1000.0));
+
+            checkPeriod(interestSchedule, 0, 343.33, 10.00, 333.33, 666.67, 
false); //
+            checkPeriod(interestSchedule, 1, 343.33, 10.00, 333.33, 333.34, 
false); //
+            checkPeriod(interestSchedule, 2, 343.34, 10.00, 333.34, 0.00, 
false); //
+
+            emiCalculator.addDisbursement(interestSchedule, 
disbursementDate.plusMonths(1), toMoney(250.0));
+
+            checkPeriod(interestSchedule, 0, 343.33, 10.00, 333.33, 916.67, 
false); //
+            checkPeriod(interestSchedule, 1, 470.84, 12.50, 458.34, 458.33, 
false); //
+            checkPeriod(interestSchedule, 2, 470.83, 12.50, 458.33, 0.00, 
false); //
+        }
+
+        @Test
+        void test_sameAsRepayment_month_repay_every_1_periods_3() {
+
+            final BigDecimal interestRate = BigDecimal.valueOf(12.0);
+            final LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+            
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
+
+            final Integer installmentAmountInMultiplesOf = null;
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
List.of(), installmentAmountInMultiplesOf, mc);
+
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(1000.0));
+
+            checkPeriod(interestSchedule, 0, 343.33, 10.00, 333.33, 666.67, 
false); //
+            checkPeriod(interestSchedule, 1, 343.33, 10.00, 333.33, 333.34, 
false); //
+            checkPeriod(interestSchedule, 2, 343.34, 10.00, 333.34, 0.00, 
false); //
+
+            emiCalculator.addDisbursement(interestSchedule, 
disbursementDate.plusDays(4), toMoney(250.0));
+
+            checkPeriod(interestSchedule, 0, 429.06, 12.18, 416.88, 833.12, 
false); //
+            checkPeriod(interestSchedule, 1, 429.06, 12.50, 416.56, 416.56, 
false); //
+            checkPeriod(interestSchedule, 2, 429.06, 12.50, 416.56, 0.00, 
false); //
+
+            emiCalculator.addDisbursement(interestSchedule, 
disbursementDate.plusDays(4), toMoney(250.0));
+
+            checkPeriod(interestSchedule, 0, 514.78, 14.35, 500.43, 999.57, 
false); //
+            checkPeriod(interestSchedule, 1, 514.78, 15.00, 499.78, 499.79, 
false); //
+            checkPeriod(interestSchedule, 2, 514.79, 15.00, 499.79, 0.00, 
false); //
+
+        }
+
+        @Test
+        void test_sameAsRepayment_month_repay_every_1_periods_20() {
+
+            final BigDecimal interestRate = BigDecimal.valueOf(12.0);
+            final LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+            
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(20);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
+            final Integer installmentAmountInMultiplesOf = null;
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
List.of(), installmentAmountInMultiplesOf, mc);
+
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(1000.0));
+
+            checkPeriod(interestSchedule, 0, 60.00, 10.00, 50.00, 950.00, 
false); //
+            checkPeriod(interestSchedule, 1, 60.00, 10.00, 50.00, 900.00, 
false); //
+            checkPeriod(interestSchedule, 2, 60.00, 10.00, 50.00, 850.00, 
false); //
+            checkPeriod(interestSchedule, 3, 60.00, 10.00, 50.00, 800.00, 
false); //
+            checkPeriod(interestSchedule, 4, 60.00, 10.00, 50.00, 750.00, 
false); //
+            checkPeriod(interestSchedule, 5, 60.00, 10.00, 50.00, 700.00, 
false); //
+            checkPeriod(interestSchedule, 6, 60.00, 10.00, 50.00, 650.00, 
false); //
+            checkPeriod(interestSchedule, 7, 60.00, 10.00, 50.00, 600.00, 
false); //
+            checkPeriod(interestSchedule, 8, 60.00, 10.00, 50.00, 550.00, 
false); //
+            checkPeriod(interestSchedule, 9, 60.00, 10.00, 50.00, 500.00, 
false); //
+            checkPeriod(interestSchedule, 10, 60.00, 10.00, 50.00, 450.00, 
false); //
+            checkPeriod(interestSchedule, 11, 60.00, 10.00, 50.00, 400.00, 
false); //
+            checkPeriod(interestSchedule, 12, 60.00, 10.00, 50.00, 350.00, 
false); //
+            checkPeriod(interestSchedule, 13, 60.00, 10.00, 50.00, 300.00, 
false); //
+            checkPeriod(interestSchedule, 14, 60.00, 10.00, 50.00, 250.00, 
false); //
+            checkPeriod(interestSchedule, 15, 60.00, 10.00, 50.00, 200.00, 
false); //
+            checkPeriod(interestSchedule, 16, 60.00, 10.00, 50.00, 150.00, 
false); //
+            checkPeriod(interestSchedule, 17, 60.00, 10.00, 50.00, 100.00, 
false); //
+            checkPeriod(interestSchedule, 18, 60.00, 10.00, 50.00, 50.00, 
false); //
+            checkPeriod(interestSchedule, 19, 60.00, 10.00, 50.00, 0.00, 
false); //
+        }
+
+        @Test
+        void test_sameAsRepayment_month_repay_every_1_periods_24() {
+
+            final BigDecimal interestRate = BigDecimal.valueOf(12.0);
+            final LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+            
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(24);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
+            final Integer installmentAmountInMultiplesOf = null;
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
List.of(), installmentAmountInMultiplesOf, mc);
+
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(7500.0));
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(2500.0));
+
+            checkPeriod(interestSchedule, 0, 516.67, 100.00, 416.67, 9583.33, 
false); //
+            checkPeriod(interestSchedule, 1, 516.67, 100.00, 416.67, 9166.66, 
false); //
+            checkPeriod(interestSchedule, 2, 516.67, 100.00, 416.67, 8749.99, 
false); //
+            checkPeriod(interestSchedule, 3, 516.67, 100.00, 416.67, 8333.32, 
false); //
+            checkPeriod(interestSchedule, 4, 516.67, 100.00, 416.67, 7916.65, 
false); //
+            checkPeriod(interestSchedule, 5, 516.67, 100.00, 416.67, 7499.98, 
false); //
+            checkPeriod(interestSchedule, 6, 516.67, 100.00, 416.67, 7083.31, 
false); //
+            checkPeriod(interestSchedule, 7, 516.67, 100.00, 416.67, 6666.64, 
false); //
+            checkPeriod(interestSchedule, 8, 516.67, 100.00, 416.67, 6249.97, 
false); //
+            checkPeriod(interestSchedule, 9, 516.67, 100.00, 416.67, 5833.30, 
false); //
+            checkPeriod(interestSchedule, 10, 516.67, 100.00, 416.67, 5416.63, 
false); //
+            checkPeriod(interestSchedule, 11, 516.67, 100.00, 416.67, 4999.96, 
false); //
+            checkPeriod(interestSchedule, 12, 516.67, 100.00, 416.67, 4583.29, 
false); //
+            checkPeriod(interestSchedule, 13, 516.67, 100.00, 416.67, 4166.62, 
false); //
+            checkPeriod(interestSchedule, 14, 516.67, 100.00, 416.67, 3749.95, 
false); //
+            checkPeriod(interestSchedule, 15, 516.67, 100.00, 416.67, 3333.28, 
false); //
+            checkPeriod(interestSchedule, 16, 516.67, 100.00, 416.67, 2916.61, 
false); //
+            checkPeriod(interestSchedule, 17, 516.67, 100.00, 416.67, 2499.94, 
false); //
+            checkPeriod(interestSchedule, 18, 516.67, 100.00, 416.67, 2083.27, 
false); //
+            checkPeriod(interestSchedule, 19, 516.67, 100.00, 416.67, 1666.60, 
false); //
+            checkPeriod(interestSchedule, 20, 516.67, 100.00, 416.67, 1249.93, 
false); //
+            checkPeriod(interestSchedule, 21, 516.67, 100.00, 416.67, 833.26, 
false); //
+            checkPeriod(interestSchedule, 22, 516.67, 100.00, 416.67, 416.59, 
false); //
+            checkPeriod(interestSchedule, 23, 516.59, 100.00, 416.59, 0.00, 
false); //
+
+            emiCalculator.addDisbursement(interestSchedule, 
disbursementDate.plusMonths(3).plusDays(4), toMoney(1000.0));
+
+            checkPeriod(interestSchedule, 0, 516.67, 100.00, 416.67, 9583.33, 
false); //
+            checkPeriod(interestSchedule, 1, 516.67, 100.00, 416.67, 9166.66, 
false); //
+            checkPeriod(interestSchedule, 2, 516.67, 100.00, 416.67, 8749.99, 
false); //
+            checkPeriod(interestSchedule, 3, 574.22, 108.67, 465.55, 9284.44, 
false); //
+            checkPeriod(interestSchedule, 4, 574.22, 110.00, 464.22, 8820.22, 
false); //
+            checkPeriod(interestSchedule, 5, 574.22, 110.00, 464.22, 8356.00, 
false); //
+            checkPeriod(interestSchedule, 6, 574.22, 110.00, 464.22, 7891.78, 
false); //
+            checkPeriod(interestSchedule, 7, 574.22, 110.00, 464.22, 7427.56, 
false); //
+            checkPeriod(interestSchedule, 8, 574.22, 110.00, 464.22, 6963.34, 
false); //
+            checkPeriod(interestSchedule, 9, 574.22, 110.00, 464.22, 6499.12, 
false); //
+            checkPeriod(interestSchedule, 10, 574.22, 110.00, 464.22, 6034.90, 
false); //
+            checkPeriod(interestSchedule, 11, 574.22, 110.00, 464.22, 5570.68, 
false); //
+            checkPeriod(interestSchedule, 12, 574.22, 110.00, 464.22, 5106.46, 
false); //
+            checkPeriod(interestSchedule, 13, 574.22, 110.00, 464.22, 4642.24, 
false); //
+            checkPeriod(interestSchedule, 14, 574.22, 110.00, 464.22, 4178.02, 
false); //
+            checkPeriod(interestSchedule, 15, 574.22, 110.00, 464.22, 3713.80, 
false); //
+            checkPeriod(interestSchedule, 16, 574.22, 110.00, 464.22, 3249.58, 
false); //
+            checkPeriod(interestSchedule, 17, 574.22, 110.00, 464.22, 2785.36, 
false); //
+            checkPeriod(interestSchedule, 18, 574.22, 110.00, 464.22, 2321.14, 
false); //
+            checkPeriod(interestSchedule, 19, 574.22, 110.00, 464.22, 1856.92, 
false); //
+            checkPeriod(interestSchedule, 20, 574.22, 110.00, 464.22, 1392.70, 
false); //
+            checkPeriod(interestSchedule, 21, 574.22, 110.00, 464.22, 928.48, 
false); //
+            checkPeriod(interestSchedule, 22, 574.22, 110.00, 464.22, 464.26, 
false); //
+            checkPeriod(interestSchedule, 23, 574.26, 110.00, 464.26, 0.00, 
false); //
+        }
+
+        @Test
+        void 
test_sameAsRepayment_month_repay_every_1_periods_3__not_calculate_exact_days() {
+
+            final BigDecimal interestRate = BigDecimal.valueOf(12.0);
+            final LocalDate disbursementDate = LocalDate.of(2024, 1, 1);
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+            
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+            
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(false);
+
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
+
+            final Integer installmentAmountInMultiplesOf = null;
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
List.of(), installmentAmountInMultiplesOf, mc);
+
+            emiCalculator.addDisbursement(interestSchedule, disbursementDate, 
toMoney(1000.0));
+
+            checkPeriod(interestSchedule, 0, 343.33, 10.00, 333.33, 666.67, 
false); //
+            checkPeriod(interestSchedule, 1, 343.33, 10.00, 333.33, 333.34, 
false); //
+            checkPeriod(interestSchedule, 2, 343.34, 10.00, 333.34, 0.00, 
false); //
+
+            emiCalculator.addDisbursement(interestSchedule, 
disbursementDate.plusDays(4), toMoney(250.0));
+            checkPeriod(interestSchedule, 0, 429.17, 12.50, 416.67, 833.33, 
false); //
+            checkPeriod(interestSchedule, 1, 429.17, 12.50, 416.67, 416.66, 
false); //
+            checkPeriod(interestSchedule, 2, 429.16, 12.50, 416.66, 0.00, 
false); //
+
+            emiCalculator.addDisbursement(interestSchedule, 
disbursementDate.plusDays(17), toMoney(250.0));
+            checkPeriod(interestSchedule, 0, 515.00, 15.00, 500.00, 1000.00, 
false); //
+            checkPeriod(interestSchedule, 1, 515.00, 15.00, 500.00, 500.00, 
false); //
+            checkPeriod(interestSchedule, 2, 515.00, 15.00, 500.00, 0.00, 
false); //
+
+        }
+    }
+
+    // utilities
+    private List<LoanScheduleModelRepaymentPeriod> 
generateExpectedRepaymentPeriods(LocalDate disbursementDate) {
+        return switch 
(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()) {
+            case MONTHS -> expectedRepaymentsMonthly(disbursementDate, 
loanProductRelatedDetail.getNumberOfRepayments(),
+                    loanProductRelatedDetail.getRepayEvery());
+            case WEEKS -> expectedRepaymentWeeks(disbursementDate, 
loanProductRelatedDetail.getNumberOfRepayments(),
+                    loanProductRelatedDetail.getRepayEvery());
+            case DAYS -> expectedRepaymentDays(disbursementDate, 
loanProductRelatedDetail.getNumberOfRepayments(),
+                    loanProductRelatedDetail.getRepayEvery());
+            default -> throw new UnsupportedOperationException();
+        };
+    }
+
+    List<LoanScheduleModelRepaymentPeriod> expectedRepaymentDays(final 
LocalDate disbursementDate, final int periods, final int length) {
+        final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods 
= new ArrayList<>(periods);
+        IntStream.range(0, periods).forEach(i -> expectedRepaymentPeriods
+                .add(repayment(i + 1, disbursementDate.plusDays((long) i * 
length), disbursementDate.plusDays((long) (i + 1) * length))));
+        return expectedRepaymentPeriods;
+    }
+
+    List<LoanScheduleModelRepaymentPeriod> expectedRepaymentsMonthly(final 
LocalDate disbursementDate, final int periods,
+            final int length) {
+        final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods 
= new ArrayList<>(periods);
+        IntStream.range(0, periods).forEach(i -> expectedRepaymentPeriods.add(
+                repayment(i + 1, disbursementDate.plusMonths((long) i * 
length), disbursementDate.plusMonths((long) (i + 1) * length))));
+        return expectedRepaymentPeriods;
+    }
+
+    List<LoanScheduleModelRepaymentPeriod> expectedRepaymentWeeks(final 
LocalDate disbursementDate, final int periods, final int length) {
+        final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods 
= new ArrayList<>(periods);
+        IntStream.range(0, periods).forEach(i -> expectedRepaymentPeriods
+                .add(repayment(i + 1, disbursementDate.plusWeeks((long) i * 
length), disbursementDate.plusWeeks((long) (i + 1) * length))));
+        return expectedRepaymentPeriods;
+    }
+
     private static LoanScheduleModelRepaymentPeriod repayment(int 
periodNumber, LocalDate fromDate, LocalDate dueDate) {
         final Money zeroAmount = Money.zero(currency);
         return LoanScheduleModelRepaymentPeriod.repayment(periodNumber, 
fromDate, dueDate, zeroAmount, zeroAmount, zeroAmount, zeroAmount,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
index 12cab7ea57..7a58ae8b8e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java
@@ -232,7 +232,7 @@ public class LoanScheduleAssembler {
         Boolean allowPartialPeriodInterestCalcualtion = this.fromApiJsonHelper
                 
.extractBooleanNamed(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
 element);
         if (allowPartialPeriodInterestCalcualtion == null) {
-            allowPartialPeriodInterestCalcualtion = 
loanProduct.getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalcualtion();
+            allowPartialPeriodInterestCalcualtion = 
loanProduct.getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalculation();
         }
 
         final BigDecimal interestRatePerPeriod = 
this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod",
 element);
@@ -1359,15 +1359,15 @@ public class LoanScheduleAssembler {
         }
 
         if 
(command.isChangeInBooleanParameterNamed(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
-                
loanProductRelatedDetail.isAllowPartialPeriodInterestCalcualtion())) {
+                
loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation())) {
             final boolean newValue = command
                     
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME);
             
changes.put(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
 newValue);
-            
loanProductRelatedDetail.setAllowPartialPeriodInterestCalcualtion(newValue);
+            
loanProductRelatedDetail.setAllowPartialPeriodInterestCalculation(newValue);
         }
 
         if 
(loanProductRelatedDetail.getInterestCalculationPeriodMethod().isDaily()) {
-            
loanProductRelatedDetail.setAllowPartialPeriodInterestCalcualtion(false);
+            
loanProductRelatedDetail.setAllowPartialPeriodInterestCalculation(false);
         }
 
         final String graceOnPrincipalPaymentParamName = 
"graceOnPrincipalPayment";
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
index c6fc94d417..83b14dbb5d 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java
@@ -1667,14 +1667,20 @@ public final class LoanApplicationValidator {
         }
     }
 
-    public void validateLoanMultiDisbursementDate(final JsonElement element, 
LocalDate expectedDisbursementDate, BigDecimal principal) {
+    public void validateLoanMultiDisbursementDate(final JsonElement element, 
LocalDate expectedDisbursementDate, BigDecimal principal,
+            Loan loan) {
         Validator.validateOrThrow("loan", baseDataValidator -> {
-            validateLoanMultiDisbursementDate(element, baseDataValidator, 
expectedDisbursementDate, principal);
+            validateLoanMultiDisbursementDate(element, baseDataValidator, 
expectedDisbursementDate, principal, loan);
         });
     }
 
     public void validateLoanMultiDisbursementDate(final JsonElement element, 
final DataValidatorBuilder baseDataValidator,
             LocalDate expectedDisbursement, BigDecimal totalPrincipal) {
+        validateLoanMultiDisbursementDate(element, baseDataValidator, 
expectedDisbursement, totalPrincipal, null);
+    }
+
+    public void validateLoanMultiDisbursementDate(final JsonElement element, 
final DataValidatorBuilder baseDataValidator,
+            LocalDate expectedDisbursement, BigDecimal totalPrincipal, Loan 
loan) {
         this.validateDisbursementsAreDatewiseOrdered(element, 
baseDataValidator);
 
         final JsonObject topLevelJsonElement = element.getAsJsonObject();
@@ -1732,11 +1738,31 @@ public final class LoanApplicationValidator {
                     
baseDataValidator.reset().parameter(LoanApiConstants.disbursementPrincipalParameterName)
                             
.failWithCode(LoanApiConstants.APPROVED_AMOUNT_IS_LESS_THAN_SUM_OF_TRANCHES);
                 }
-                final Integer interestType = this.fromApiJsonHelper
-                        
.extractIntegerSansLocaleNamed(LoanApiConstants.interestTypeParameterName, 
element);
-                
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName).value(interestType).ignoreIfNull()
-                        
.integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());
 
+                if (loan == null) {
+                    final String transactionProcessingStrategyCode = 
this.fromApiJsonHelper
+                            
.extractStringNamed(LoanApiConstants.transactionProcessingStrategyCodeParameterName,
 element);
+                    if (transactionProcessingStrategyCode != null) {
+                        final Integer interestType = 
this.fromApiJsonHelper.extractIntegerNamed(LoanApiConstants.interestTypeParameterName,
+                                element, Locale.getDefault());
+                        String processorCode = 
loanRepaymentScheduleTransactionProcessorFactory
+                                
.determineProcessor(transactionProcessingStrategyCode).getCode();
+                        boolean isProgressive = 
"advanced-payment-allocation-strategy".equals(processorCode);
+                        if (isProgressive) {
+                            
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName).value(interestType)
+                                    .ignoreIfNull().inMinMaxRange(0, 1);
+                        } else {
+                            
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName).value(interestType)
+                                    
.ignoreIfNull().integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());
+                        }
+                    }
+                } else {
+                    if (loan.isCumulativeSchedule()) {
+                        
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName)
+                                
.value(loan.getLoanProductRelatedDetail().getInterestMethod()).ignoreIfNull()
+                                .value(InterestMethod.DECLINING_BALANCE);
+                    }
+                }
             }
         }
     }
@@ -1756,7 +1782,7 @@ public final class LoanApplicationValidator {
             final InterestCalculationPeriodMethod 
interestCalculationPeriodMethod = InterestCalculationPeriodMethod
                     .fromInt(interestCalculationPeriodType);
             boolean considerPartialPeriodUpdates = 
interestCalculationPeriodMethod.isDaily() ? 
interestCalculationPeriodMethod.isDaily()
-                    : 
loanProduct.getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalcualtion();
+                    : 
loanProduct.getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalculation();
             if 
(this.fromApiJsonHelper.parameterExists(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
                     element)) {
                 final Boolean considerPartialInterestEnabled = 
this.fromApiJsonHelper
@@ -1780,7 +1806,8 @@ public final class LoanApplicationValidator {
                             
.failWithCode("not.supported.for.selected.interest.calcualtion.type");
                 }
 
-                if (loanProduct.isMultiDisburseLoan()) {
+                if (loanProduct.isMultiDisburseLoan()
+                        && 
!"advanced-payment-allocation-strategy".equals(loanProduct.getTransactionProcessingStrategyCode()))
 {
                     
baseDataValidator.reset().parameter(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME)
                             
.failWithCode("not.supported.for.selected.interest.calcualtion.type");
                 }
@@ -2045,7 +2072,7 @@ public final class LoanApplicationValidator {
 
             LoanProduct loanProduct = loan.loanProduct();
             if (loanProduct.isMultiDisburseLoan()) {
-                validateLoanMultiDisbursementDate(element, 
expectedDisbursementDate, principal);
+                validateLoanMultiDisbursementDate(element, 
expectedDisbursementDate, principal, loan);
 
                 final JsonArray disbursementDataArray = this.fromApiJsonHelper
                         
.extractJsonArrayNamed(LoanApiConstants.disbursementDataParameterName, element);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductRelatedDetailUpdateUtil.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductRelatedDetailUpdateUtil.java
index bc0479b4b7..aefe4f8bfb 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductRelatedDetailUpdateUtil.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanProductRelatedDetailUpdateUtil.java
@@ -201,15 +201,15 @@ public class LoanProductRelatedDetailUpdateUtil {
         }
 
         if 
(command.isChangeInBooleanParameterNamed(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
-                
loanRepaymentScheduleDetail.isAllowPartialPeriodInterestCalcualtion())) {
+                
loanRepaymentScheduleDetail.isAllowPartialPeriodInterestCalculation())) {
             final boolean newValue = command
                     
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME);
             
actualChanges.put(LoanProductConstants.ALLOW_PARTIAL_PERIOD_INTEREST_CALCUALTION_PARAM_NAME,
 newValue);
-            
loanRepaymentScheduleDetail.setAllowPartialPeriodInterestCalcualtion(newValue);
+            
loanRepaymentScheduleDetail.setAllowPartialPeriodInterestCalculation(newValue);
         }
 
         if 
(loanRepaymentScheduleDetail.getInterestCalculationPeriodMethod().isDaily()) {
-            
loanRepaymentScheduleDetail.setAllowPartialPeriodInterestCalcualtion(false);
+            
loanRepaymentScheduleDetail.setAllowPartialPeriodInterestCalculation(false);
         }
 
         final String graceOnPrincipalPaymentParamName = 
"graceOnPrincipalPayment";
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index c78483d507..40284de5ca 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -2232,7 +2232,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
     }
 
     private void validateMultiDisbursementData(final JsonCommand command, 
LocalDate expectedDisbursementDate,
-            boolean isDisallowExpectedDisbursements) {
+            boolean isDisallowExpectedDisbursements, Loan loan) {
         final String json = command.json();
         final JsonElement element = this.fromApiJsonHelper.parse(json);
 
@@ -2254,7 +2254,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
 
         final BigDecimal principal = 
this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("approvedLoanAmount", 
element);
 
-        loanApplicationValidator.validateLoanMultiDisbursementDate(element, 
baseDataValidator, expectedDisbursementDate, principal);
+        loanApplicationValidator.validateLoanMultiDisbursementDate(element, 
baseDataValidator, expectedDisbursementDate, principal, loan);
         if (!dataValidationErrors.isEmpty()) {
             throw new PlatformApiDataValidationException(dataValidationErrors);
         }
@@ -2296,7 +2296,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl 
implements LoanWritePlatf
             final String errorMessage = 
"cannot.modify.tranches.if.loan.is.pendingapproval.closed.overpaid.writtenoff";
             throw new LoanMultiDisbursementException(errorMessage);
         }
-        validateMultiDisbursementData(command, expectedDisbursementDate, 
loan.loanProduct().isDisallowExpectedDisbursements());
+        validateMultiDisbursementData(command, expectedDisbursementDate, 
loan.loanProduct().isDisallowExpectedDisbursements(), loan);
 
         this.validateForAddAndDeleteTranche(loan);
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
index 43f92b019e..bad51f132a 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
@@ -786,7 +786,7 @@ public final class LoanProductDataValidator {
             }
         }
 
-        validateMultiDisburseLoanData(baseDataValidator, element);
+        validateMultiDisburseLoanData(baseDataValidator, element, null);
 
         validateLoanConfigurableAttributes(baseDataValidator, element);
 
@@ -1014,7 +1014,8 @@ public final class LoanProductDataValidator {
         }
     }
 
-    private void validateMultiDisburseLoanData(final DataValidatorBuilder 
baseDataValidator, final JsonElement element) {
+    private void validateMultiDisburseLoanData(final DataValidatorBuilder 
baseDataValidator, final JsonElement element,
+            final LoanProduct loanProduct) {
         Boolean multiDisburseLoan = false;
         if 
(this.fromApiJsonHelper.parameterExists(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME,
 element)) {
             multiDisburseLoan = 
this.fromApiJsonHelper.extractBooleanNamed(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME,
@@ -1045,14 +1046,34 @@ public final class LoanProductDataValidator {
                     .integerGreaterThanZero();
 
             final Integer interestType = 
this.fromApiJsonHelper.extractIntegerNamed(INTEREST_TYPE, element, 
Locale.getDefault());
-            
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull()
-                    
.integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());
+
+            boolean isProgressive = isProgressive(element, loanProduct);
+            if (isProgressive) {
+                
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull().inMinMaxRange(0,
 1);
+            } else {
+                
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull()
+                        
.integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());
+            }
         }
 
         final String overAppliedCalculationType = 
this.fromApiJsonHelper.extractStringNamed(OVER_APPLIED_CALCULATION_TYPE, 
element);
         
baseDataValidator.reset().parameter(OVER_APPLIED_CALCULATION_TYPE).value(overAppliedCalculationType).notExceedingLengthOf(10);
     }
 
+    private boolean isProgressive(JsonElement element, LoanProduct 
loanProduct) {
+        String processorCode = null;
+        if (loanProduct != null) {
+            processorCode = loanProduct.getTransactionProcessingStrategyCode();
+        }
+        final String transactionProcessingStrategyCode = 
this.fromApiJsonHelper.extractStringNamed(TRANSACTION_PROCESSING_STRATEGY_CODE,
+                element);
+        if (transactionProcessingStrategyCode != null) {
+            processorCode = 
loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode)
+                    .getCode();
+        }
+        return "advanced-payment-allocation-strategy".equals(processorCode);
+    }
+
     private void validateInterestRecalculationParams(final JsonElement 
element, final DataValidatorBuilder baseDataValidator,
             final LoanProduct loanProduct) {
 
@@ -1868,7 +1889,7 @@ public final class LoanProductDataValidator {
             }
         }
 
-        validateMultiDisburseLoanData(baseDataValidator, element);
+        validateMultiDisburseLoanData(baseDataValidator, element, loanProduct);
 
         // validateLoanConfigurableAttributes(baseDataValidator,element);
 
@@ -2549,7 +2570,7 @@ public final class LoanProductDataValidator {
                 }
             } else if (loanProduct != null) {
                 if (!interestCalculationPeriodMethod.isDaily()) {
-                    considerPartialPeriodUpdates = 
loanProduct.getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalcualtion();
+                    considerPartialPeriodUpdates = 
loanProduct.getLoanProductRelatedDetail().isAllowPartialPeriodInterestCalculation();
                 }
             }
 
@@ -2574,7 +2595,7 @@ public final class LoanProductDataValidator {
                 } else if (loanProduct != null) {
                     multiDisburseLoan = loanProduct.isMultiDisburseLoan();
                 }
-                if (multiDisburseLoan != null && multiDisburseLoan) {
+                if (multiDisburseLoan != null && multiDisburseLoan && 
!isProgressive(element, loanProduct)) {
                     
baseDataValidator.reset().parameter(LoanProductConstants.MULTI_DISBURSE_LOAN_PARAMETER_NAME)
                             
.failWithCode("not.supported.for.selected.interest.calculation.type");
                 }

Reply via email to