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 b4b9d3ea714ffefe65a24038f4dd17fbf1fa7dea
Author: Janos Meszaros <[email protected]>
AuthorDate: Wed Feb 19 21:02:22 2025 +0100

    FINERACT-1981: Fix calculation new way when applies multi disbursement
---
 .../loanproduct/calc/ProgressiveEMICalculator.java | 18 +++----
 .../loanproduct/calc/data/EmiChangeOperation.java  |  7 +++
 .../data/ProgressiveLoanInterestScheduleModel.java | 16 +++---
 .../loanproduct/calc/data/RepaymentPeriod.java     | 57 ++++++++++++++--------
 .../calc/ProgressiveEMICalculatorTest.java         | 22 ++++-----
 5 files changed, 74 insertions(+), 46 deletions(-)

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 bc2104758..6cf8d0680 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
@@ -89,7 +89,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 = new RepaymentPeriod(prev.get(), 
from.apply(e), to.apply(e), zero, mc);
+            RepaymentPeriod rp = RepaymentPeriod.create(prev.get(), 
from.apply(e), to.apply(e), zero, mc);
             prev.set(rp);
             return rp;
         }).toList();
@@ -374,14 +374,14 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
             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;
+                || operation.getAction() == 
EmiChangeOperation.Action.INTEREST_RATE_CHANGE || 
scheduleModel.isCopiedForCalculation();
 
         calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
         calculateOutstandingBalance(scheduleModel);
         if (onlyOnActualModelShouldApply) {
             calculateEMIOnActualModel(relatedRepaymentPeriods, scheduleModel);
         } else {
-            calculateEMIOnNewEmptyModelAndMerge(relatedRepaymentPeriods, 
scheduleModel, operation);
+            calculateEMIOnNewModelAndMerge(relatedRepaymentPeriods, 
scheduleModel, operation);
         }
         calculateOutstandingBalance(scheduleModel);
         calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
@@ -733,18 +733,18 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
         });
     }
 
-    private void calculateEMIOnNewEmptyModelAndMerge(List<RepaymentPeriod> 
repaymentPeriods,
-            ProgressiveLoanInterestScheduleModel scheduleModel, final 
EmiChangeOperation operation) {
+    private void calculateEMIOnNewModelAndMerge(List<RepaymentPeriod> 
repaymentPeriods, ProgressiveLoanInterestScheduleModel scheduleModel,
+            final EmiChangeOperation operation) {
         if (repaymentPeriods.isEmpty()) {
             return;
         }
-        final ProgressiveLoanInterestScheduleModel scheduleModelCopy = 
scheduleModel.emptyCopy();
-        addDisbursement(scheduleModelCopy, operation);
+        final ProgressiveLoanInterestScheduleModel scheduleModelCopy = 
scheduleModel.copyWithoutPaidAmounts();
+        addDisbursement(scheduleModelCopy, operation.withZeroAmount());
 
         final LocalDate firstDueDate = repaymentPeriods.get(0).getDueDate();
         scheduleModel.copyPeriodsFrom(firstDueDate, 
scheduleModelCopy.repaymentPeriods(), (newRepaymentPeriod, 
actualRepaymentPeriod) -> {
-            
actualRepaymentPeriod.setEmi(actualRepaymentPeriod.getEmi().plus(newRepaymentPeriod.getEmi()));
-            
actualRepaymentPeriod.setOriginalEmi(actualRepaymentPeriod.getOriginalEmi().plus(newRepaymentPeriod.getOriginalEmi()));
+            actualRepaymentPeriod.setEmi(newRepaymentPeriod.getEmi());
+            
actualRepaymentPeriod.setOriginalEmi(newRepaymentPeriod.getOriginalEmi());
         });
     }
 
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/EmiChangeOperation.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/EmiChangeOperation.java
index c25639bee..ea0f12020 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/EmiChangeOperation.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/EmiChangeOperation.java
@@ -46,4 +46,11 @@ public class EmiChangeOperation {
     public static EmiChangeOperation changeInterestRate(final LocalDate 
newInterestSubmittedOnDate, final BigDecimal newInterestRate) {
         return new 
EmiChangeOperation(EmiChangeOperation.Action.INTEREST_RATE_CHANGE, 
newInterestSubmittedOnDate, null, newInterestRate);
     }
+
+    public EmiChangeOperation withZeroAmount() {
+        if (action == Action.DISBURSEMENT) {
+            return new EmiChangeOperation(action, submittedOnDate, 
amount.zero(), null);
+        }
+        return null;
+    }
 }
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 ded2ef328..3c79fd41a 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
@@ -56,6 +56,7 @@ public class ProgressiveLoanInterestScheduleModel {
     private final Integer installmentAmountInMultiplesOf;
     private final MathContext mc;
     private final Money zero;
+    private final boolean isCopiedForCalculation;
 
     public ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> 
repaymentPeriods,
             final LoanProductMinimumRepaymentScheduleRelatedDetail 
loanProductRelatedDetail,
@@ -67,33 +68,34 @@ public class ProgressiveLoanInterestScheduleModel {
         this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
         this.mc = mc;
         this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
+        this.isCopiedForCalculation = false;
     }
 
     private ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> 
repaymentPeriods, final TreeSet<InterestRate> interestRates,
             final LoanProductMinimumRepaymentScheduleRelatedDetail 
loanProductRelatedDetail,
             final Map<LoanTermVariationType, List<LoanTermVariationsData>> 
loanTermVariations, final Integer installmentAmountInMultiplesOf,
-            final MathContext mc) {
+            final MathContext mc, final boolean isCopiedForCalculation) {
         this.mc = mc;
         this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods,
-                (previousPeriod, repaymentPeriod) -> new 
RepaymentPeriod(previousPeriod, repaymentPeriod, mc));
+                (previousPeriod, repaymentPeriod) -> 
RepaymentPeriod.copy(previousPeriod, repaymentPeriod, mc));
         this.interestRates = new TreeSet<>(interestRates);
         this.loanProductRelatedDetail = loanProductRelatedDetail;
         this.loanTermVariations = loanTermVariations;
         this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
         this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
+        this.isCopiedForCalculation = isCopiedForCalculation;
     }
 
     public ProgressiveLoanInterestScheduleModel deepCopy(MathContext mc) {
         return new ProgressiveLoanInterestScheduleModel(repaymentPeriods, 
interestRates, loanProductRelatedDetail, loanTermVariations,
-                installmentAmountInMultiplesOf, mc);
+                installmentAmountInMultiplesOf, mc, false);
     }
 
-    public ProgressiveLoanInterestScheduleModel emptyCopy() {
+    public ProgressiveLoanInterestScheduleModel copyWithoutPaidAmounts() {
         final List<RepaymentPeriod> repaymentPeriodCopies = 
copyRepaymentPeriods(repaymentPeriods,
-                (previousPeriod, repaymentPeriod) -> new 
RepaymentPeriod(previousPeriod, repaymentPeriod.getFromDate(),
-                        repaymentPeriod.getDueDate(), 
repaymentPeriod.getEmi().zero(), mc));
+                (previousPeriod, repaymentPeriod) -> 
RepaymentPeriod.copyWithoutPaidAmounts(previousPeriod, repaymentPeriod, mc));
         return new ProgressiveLoanInterestScheduleModel(repaymentPeriodCopies, 
interestRates, loanProductRelatedDetail, loanTermVariations,
-                installmentAmountInMultiplesOf, mc);
+                installmentAmountInMultiplesOf, mc, true);
     }
 
     private List<RepaymentPeriod> copyRepaymentPeriods(final 
List<RepaymentPeriod> repaymentPeriods,
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 24edc1144..210ac0a7b 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
@@ -37,7 +37,7 @@ import org.apache.fineract.portfolio.util.Memo;
 
 @ToString(exclude = { "previous" })
 @EqualsAndHashCode(exclude = { "previous" })
-public class RepaymentPeriod {
+public final class RepaymentPeriod {
 
     private final RepaymentPeriod previous;
     @Getter
@@ -59,40 +59,59 @@ public class RepaymentPeriod {
     @Getter
     private Money paidInterest;
 
+    private final MathContext mc;
+
     private Memo<BigDecimal> rateFactorPlus1Calculation;
     private Memo<Money> calculatedDueInterestCalculation;
     private Memo<Money> dueInterestCalculation;
     private Memo<Money> outstandingBalanceCalculation;
-    private final MathContext mc;
 
-    public RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, 
LocalDate dueDate, Money emi, MathContext mc) {
+    private RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, 
LocalDate dueDate, List<InterestPeriod> interestPeriods,
+            Money emi, Money originalEmi, Money paidPrincipal, Money 
paidInterest, MathContext mc) {
         this.previous = previous;
         this.fromDate = fromDate;
         this.dueDate = dueDate;
+        this.interestPeriods = interestPeriods;
         this.emi = emi;
-        this.originalEmi = emi;
+        this.originalEmi = originalEmi;
+        this.paidPrincipal = paidPrincipal;
+        this.paidInterest = paidInterest;
         this.mc = mc;
-        this.interestPeriods = new ArrayList<>();
+    }
+
+    public static RepaymentPeriod create(RepaymentPeriod previous, LocalDate 
fromDate, LocalDate dueDate, Money emi, MathContext mc) {
+        final Money zero = emi.zero();
+        final RepaymentPeriod newRepaymentPeriod = new 
RepaymentPeriod(previous, fromDate, dueDate, new ArrayList<>(), emi, emi, zero, 
zero,
+                mc);
         // There is always at least 1 interest period, by default with same 
from-due date as repayment period
-        getInterestPeriods().add(InterestPeriod.withEmptyAmounts(this, 
getFromDate(), getDueDate()));
-        this.paidInterest = getZero(mc);
-        this.paidPrincipal = getZero(mc);
+        
newRepaymentPeriod.interestPeriods.add(InterestPeriod.withEmptyAmounts(newRepaymentPeriod,
 fromDate, dueDate));
+        return newRepaymentPeriod;
     }
 
-    public RepaymentPeriod(RepaymentPeriod previous, RepaymentPeriod 
repaymentPeriod, MathContext mc) {
-        this.previous = previous;
-        this.fromDate = repaymentPeriod.fromDate;
-        this.dueDate = repaymentPeriod.dueDate;
-        this.emi = repaymentPeriod.emi;
-        this.originalEmi = repaymentPeriod.originalEmi;
-        this.interestPeriods = new ArrayList<>();
-        this.paidPrincipal = repaymentPeriod.paidPrincipal;
-        this.paidInterest = repaymentPeriod.paidInterest;
-        this.mc = mc;
+    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, mc);
+        // 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));
+        }
+        return newRepaymentPeriod;
+    }
+
+    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, mc);
         // There is always at least 1 interest period, by default with same 
from-due date as repayment period
         for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) {
-            interestPeriods.add(InterestPeriod.copy(this, interestPeriod));
+            var interestPeriodCopy = InterestPeriod.copy(newRepaymentPeriod, 
interestPeriod);
+            if (!interestPeriodCopy.getBalanceCorrectionAmount().isZero()) {
+                
interestPeriodCopy.addBalanceCorrectionAmount(interestPeriodCopy.getBalanceCorrectionAmount().negated());
+            }
+            newRepaymentPeriod.interestPeriods.add(interestPeriodCopy);
         }
+        return newRepaymentPeriod;
     }
 
     public Optional<RepaymentPeriod> getPrevious() {
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 9a40cc386..e7068108e 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
@@ -934,16 +934,16 @@ class ProgressiveEMICalculatorTest {
         disbursedAmount = toMoney(25.0);
         emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 
8), disbursedAmount);
 
-        checkTotalInterestDue(interestSchedule, 4.65);
+        checkTotalInterestDue(interestSchedule, 4.64);
 
-        checkPeriod(interestSchedule, 0, 0, 29.93, 0.001019591398, 0.00, 1.15, 
28.78, 146.22);
-        checkPeriod(interestSchedule, 0, 1, 29.93, 0.000764693548, 0.08, 1.15, 
28.78, 146.22);
-        checkPeriod(interestSchedule, 0, 2, 29.93, 0.006117548387, 1.07, 1.15, 
28.78, 146.22);
-        checkPeriod(interestSchedule, 1, 0, 29.93, 0.007901833333, 1.16, 
28.77, 117.45);
-        checkPeriod(interestSchedule, 2, 0, 29.93, 0.007901833333, 0.93, 
29.00, 88.45);
-        checkPeriod(interestSchedule, 3, 0, 29.93, 0.007901833333, 0.70, 
29.23, 59.22);
-        checkPeriod(interestSchedule, 4, 0, 29.93, 0.007901833333, 0.47, 
29.46, 29.76);
-        checkPeriod(interestSchedule, 5, 0, 30.0, 0.007901833333, 0.24, 29.76, 
0.0);
+        checkPeriod(interestSchedule, 0, 0, 29.94, 0.001019591398, 0.00, 1.15, 
28.79, 146.21);
+        checkPeriod(interestSchedule, 0, 1, 29.94, 0.000764693548, 0.08, 1.15, 
28.79, 146.21);
+        checkPeriod(interestSchedule, 0, 2, 29.94, 0.006117548387, 1.07, 1.15, 
28.79, 146.21);
+        checkPeriod(interestSchedule, 1, 0, 29.94, 0.007901833333, 1.16, 
28.78, 117.43);
+        checkPeriod(interestSchedule, 2, 0, 29.94, 0.007901833333, 0.93, 
29.01, 88.42);
+        checkPeriod(interestSchedule, 3, 0, 29.94, 0.007901833333, 0.70, 
29.24, 59.18);
+        checkPeriod(interestSchedule, 4, 0, 29.94, 0.007901833333, 0.47, 
29.47, 29.71);
+        checkPeriod(interestSchedule, 5, 0, 29.94, 0.007901833333, 0.23, 
29.71, 0.0);
     }
 
     @Test
@@ -1024,8 +1024,8 @@ class ProgressiveEMICalculatorTest {
         emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 
1), toMoney(200.0));
         checkTotalInterestDue(interestSchedule, 4.11);
 
-        checkEmi(interestSchedule, 4, 85.05);
-        checkEmi(interestSchedule, 5, 78.86);
+        checkEmi(interestSchedule, 4, 85.04);
+        checkEmi(interestSchedule, 5, 78.91);
     }
 
     @Test

Reply via email to