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 fa5be3f5a53a6f8180099f68f52545de26ce5daa
Author: Soma Sörös <[email protected]>
AuthorDate: Wed Nov 26 13:46:58 2025 +0100

    FINERACT-2354: [BE] chargeback handling with backdated re-age for last 
adjustment strategy with equal amortization re-aging behaviour
---
 .../domain/LoanRepaymentScheduleInstallment.java   |   6 +-
 ...dvancedPaymentScheduleTransactionProcessor.java |  66 +++----
 .../loanproduct/calc/ProgressiveEMICalculator.java |  65 +++----
 .../calc/data/EqualAmortizationValues.java         |  27 ++-
 .../loanproduct/calc/data/RepaymentPeriod.java     |  22 ++-
 .../calc/ProgressiveEMICalculatorTest.java         | 201 ++++++++++++++++++++-
 6 files changed, 293 insertions(+), 94 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
index 97999777f6..40590d9cdc 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java
@@ -28,6 +28,7 @@ import jakarta.persistence.OneToMany;
 import jakarta.persistence.Table;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -518,8 +519,9 @@ public class LoanRepaymentScheduleInstallment extends 
AbstractAuditableWithUTCDa
         return this.installmentNumber.compareTo(o.installmentNumber);
     }
 
-    public int compareToByDueDate(LoanRepaymentScheduleInstallment o) {
-        return this.dueDate.compareTo(o.dueDate);
+    public int compareToByFromDueDate(LoanRepaymentScheduleInstallment o) {
+        return 
Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate)
+                
.thenComparing(LoanRepaymentScheduleInstallment::getFromDate).compare(this, o);
     }
 
     public boolean isPrincipalNotCompleted(final MonetaryCurrency currency) {
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index 6362eeea8b..accaa6da30 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -3171,7 +3171,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
     private void reprocessInstallments(final 
List<LoanRepaymentScheduleInstallment> installments) {
         final AtomicInteger counter = new AtomicInteger(1);
         final AtomicReference<LocalDate> previousDueDate = new 
AtomicReference<>(null);
-        
installments.stream().sorted(LoanRepaymentScheduleInstallment::compareToByDueDate).forEachOrdered(i
 -> {
+        
installments.stream().sorted(LoanRepaymentScheduleInstallment::compareToByFromDueDate).forEachOrdered(i
 -> {
             i.updateInstallmentNumber(counter.getAndIncrement());
             final LocalDate prev = previousDueDate.get();
             if (prev != null && (i.isAdditional() || i.isReAged())) {
@@ -3179,6 +3179,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             }
             previousDueDate.set(i.getDueDate());
         });
+        
installments.sort(LoanRepaymentScheduleInstallment::compareToByFromDueDate);
     }
 
     private LocalDate calculateReAgedInstallmentDueDate(final 
LoanReAgeParameter reAgeParameter, final LocalDate dueDate) {
@@ -3479,12 +3480,8 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         BalancesWithPaidInAdvance paidInAdvanceBalances = 
liftEarlyRepaidBalances(installments, transactionDate, currency,
                 ctx.getAlreadyProcessedTransactions());
 
-        // TODO add as Parameter here: 
paidInAdvanceBalances.getAggregatedFeeChargesPortion().isGreaterThanZero() ||
-        // 
paidInAdvanceBalances.getAggregatedPenaltyChargesPortion().isGreaterThanZero()
         emiCalculator.reAgeEqualAmortization(model, transactionDate, 
loanReAgeParameter,
-                outstandingBalances.fees.add(outstandingBalances.penalties),
-                new 
EqualAmortizationValues(calculatedFees.value().add(calculatedPenalties.value()),
-                        
calculatedFees.adjustment().add(calculatedPenalties.adjustment())));
+                outstandingBalances.fees.add(outstandingBalances.penalties), 
calculatedFees.add(calculatedPenalties));
 
         installments.removeIf(i -> (i.getInstallmentNumber() != null && 
!i.isDownPayment() && !i.getDueDate().isBefore(transactionDate)
                 && !i.isAdditional()) || 
(!i.getDueDate().isAfter(model.getMaturityDate()) && i.isAdditional()));
@@ -3494,6 +3491,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             i.setInstallmentNumber(model.repaymentPeriods().size());
         });
 
+        int reAgedInstallmentIndex = 0;
         for (int index = 0; index < model.repaymentPeriods().size(); index++) {
             RepaymentPeriod rp = model.repaymentPeriods().get(index);
             if (rp.getDueDate().isBefore(transactionDate)) {
@@ -3504,9 +3502,9 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                 installment.setInterestCharged(installment.getInterestPaid());
                 
installment.setPrincipal(installment.getPrincipalCompleted(currency).getAmount());
                 installment.setInstallmentNumber(index + 1);
+                
installment.setCreditedPrincipal(rp.getCreditedPrincipal().getAmount());
 
                 installment.updateObligationsMet(currency, transactionDate);
-                // TODO add remaining components
             } else {
                 LoanRepaymentScheduleInstallment created = 
LoanRepaymentScheduleInstallment.newReAgedInstallment(loanTransaction.getLoan(),
                         index + 1, rp.getFromDate(), rp.getDueDate(), 
rp.getDuePrincipal().getAmount(), rp.getDueInterest().getAmount(),
@@ -3525,16 +3523,17 @@ public class 
AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
 
                     
paidInAdvanceBalances.loanTransactionToRepaymentScheduleMappings.forEach(m -> 
m.setInstallment(created));
                 } else {
-                    boolean isLastRepaymentPeriod = 
model.isLastRepaymentPeriod(rp);
-                    
created.setFeeChargesCharged(calculatedFees.calculateValueBigDecimal(isLastRepaymentPeriod));
-                    
created.setPenaltyCharges(calculatedPenalties.calculateValueBigDecimal(isLastRepaymentPeriod));
+                    
created.setFeeChargesCharged(calculatedFees.calculateValueBigDecimal(reAgedInstallmentIndex));
+                    
created.setPenaltyCharges(calculatedPenalties.calculateValueBigDecimal(reAgedInstallmentIndex));
 
-                    
created.setInterestAccrued(calculatedInterestAccrued.calculateValueBigDecimal(isLastRepaymentPeriod));
-                    
created.setFeeAccrued(calculatedFeeAccrued.calculateValueBigDecimal(isLastRepaymentPeriod));
-                    
created.setPenaltyAccrued(calculatedPenaltyAccrued.calculateValueBigDecimal(isLastRepaymentPeriod));
+                    
created.setInterestAccrued(calculatedInterestAccrued.calculateValueBigDecimal(reAgedInstallmentIndex));
+                    
created.setFeeAccrued(calculatedFeeAccrued.calculateValueBigDecimal(reAgedInstallmentIndex));
+                    
created.setPenaltyAccrued(calculatedPenaltyAccrued.calculateValueBigDecimal(reAgedInstallmentIndex));
 
-                    createChargeMappingsForInstallment(created, 
calculatedCharges, isLastRepaymentPeriod);
+                    createChargeMappingsForInstallment(created, 
calculatedCharges, reAgedInstallmentIndex);
+                    reAgedInstallmentIndex++;
                 }
+                
created.setCreditedPrincipal(rp.getCreditedPrincipal().getAmount());
                 created.updateObligationsMet(currency, transactionDate);
                 installments.add(created);
             }
@@ -3549,6 +3548,9 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         List<LoanRepaymentScheduleInstallment> installments = 
ctx.getInstallments();
         LoanReAgeParameter loanReAgeParameter = 
loanTransaction.getLoanReAgeParameter();
         LocalDate transactionDate = loanTransaction.getTransactionDate();
+        LocalDate originalMaturityDate = installments.stream()
+                .filter(i -> !i.isDownPayment() && !i.isAdditional() && 
i.getDueDate() != null)
+                
.map(LoanRepaymentScheduleInstallment::getDueDate).max(LocalDate::compareTo).orElseThrow();
 
         Integer numberOfReAgeInstallments = 
loanReAgeParameter.getNumberOfInstallments();
         Integer installmentAmountInMultiplesOf = 
loanTransaction.getLoan().getLoanProductRelatedDetail()
@@ -3613,8 +3615,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                     return res;
                 }).reduce(new BalancesWithPaidInAdvance(currency), 
BalancesWithPaidInAdvance::summarizerAccumulator);
 
-        if (!balances.getPrincipal().isZero() || 
!balances.getInterest().isZero() || !balances.getFee().isZero()
-                || !balances.getPenalty().isZero()) {
+        if (!transactionDate.isAfter(originalMaturityDate)) {
 
             final LoanRepaymentScheduleInstallment earlyRepaidInstallment = 
LoanRepaymentScheduleInstallment.newReAgedInstallment(loan,
                     firstReAgeInstallmentProps.reAgedInstallmentNumber(), 
firstReAgeInstallmentProps.fromDate(), transactionDate,
@@ -3634,9 +3635,11 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
 
             
InstallmentProcessingHelper.addOneToInstallmentNumberFromInstallment(installments,
                     earlyRepaidInstallment.getInstallmentNumber());
-            
loan.getRepaymentScheduleInstallments().add(earlyRepaidInstallment);
+            installments.add(earlyRepaidInstallment);
         }
 
+        // installment index which excludes earlyRepaidInstallment intallment 
index.
+        Integer reAgedInstallmentIndex = 0;
         LoanRepaymentScheduleInstallment reAgedInstallment = 
LoanRepaymentScheduleInstallment.newReAgedInstallment(loan,
                 firstReAgeInstallmentProps.reAgedInstallmentNumber, 
firstReAgeInstallmentProps.fromDate, loanReAgeParameter.getStartDate(),
                 calculatedPrincipal.value().getAmount(), 
calculatedInterest.value().getAmount(), calculatedFees.value().getAmount(),
@@ -3644,27 +3647,27 @@ public class 
AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
                 calculatedFeeAccrued.value().getAmount(), 
calculatedPenaltyAccrued.value().getAmount());
 
         reAgedInstallment = insertOrReplaceRelatedInstallment(installments, 
reAgedInstallment, currency, transactionDate);
-        createChargeMappingsForInstallment(reAgedInstallment, 
calculatedCharges, false);
-
+        createChargeMappingsForInstallment(reAgedInstallment, 
calculatedCharges, reAgedInstallmentIndex);
+        reAgedInstallmentIndex++;
         for (int i = 1; i < numberOfReAgeInstallments; i++) {
             LocalDate calculatedDueDate = 
scheduledDateGenerator.getRepaymentPeriodDate(loanReAgeParameter.getFrequencyType(),
                     loanReAgeParameter.getFrequencyNumber(), 
reAgedInstallment.getDueDate());
             calculateReAgedInstallmentDueDate(loanReAgeParameter, 
reAgedInstallment.getDueDate());
             int nextReAgedInstallmentNumber = 
firstReAgeInstallmentProps.reAgedInstallmentNumber + i;
-            boolean isLastInstallment = i + 1 == numberOfReAgeInstallments;
 
             reAgedInstallment = 
LoanRepaymentScheduleInstallment.newReAgedInstallment(reAgedInstallment.getLoan(),
                     nextReAgedInstallmentNumber, 
reAgedInstallment.getDueDate(), calculatedDueDate,
-                    
calculatedPrincipal.calculateValueBigDecimal(isLastInstallment),
-                    
calculatedInterest.calculateValueBigDecimal(isLastInstallment),
-                    calculatedFees.calculateValueBigDecimal(isLastInstallment),
-                    
calculatedPenalties.calculateValueBigDecimal(isLastInstallment),
-                    
calculatedInterestAccrued.calculateValueBigDecimal(isLastInstallment),
-                    
calculatedFeeAccrued.calculateValueBigDecimal(isLastInstallment),
-                    
calculatedPenaltyAccrued.calculateValueBigDecimal(isLastInstallment));
+                    
calculatedPrincipal.calculateValueBigDecimal(reAgedInstallmentIndex),
+                    
calculatedInterest.calculateValueBigDecimal(reAgedInstallmentIndex),
+                    
calculatedFees.calculateValueBigDecimal(reAgedInstallmentIndex),
+                    
calculatedPenalties.calculateValueBigDecimal(reAgedInstallmentIndex),
+                    
calculatedInterestAccrued.calculateValueBigDecimal(reAgedInstallmentIndex),
+                    
calculatedFeeAccrued.calculateValueBigDecimal(reAgedInstallmentIndex),
+                    
calculatedPenaltyAccrued.calculateValueBigDecimal(reAgedInstallmentIndex));
 
             reAgedInstallment = 
insertOrReplaceRelatedInstallment(installments, reAgedInstallment, currency, 
transactionDate);
-            createChargeMappingsForInstallment(reAgedInstallment, 
calculatedCharges, isLastInstallment);
+            createChargeMappingsForInstallment(reAgedInstallment, 
calculatedCharges, reAgedInstallmentIndex);
+            reAgedInstallmentIndex++;
         }
         int lastReAgedInstallmentNumber = 
reAgedInstallment.getInstallmentNumber();
         List<LoanRepaymentScheduleInstallment> toRemove = installments.stream()
@@ -3676,11 +3679,10 @@ public class 
AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
     }
 
     private void createChargeMappingsForInstallment(final 
LoanRepaymentScheduleInstallment installment,
-            List<ReAgedChargeEqualAmortizationValues> 
reAgedChargeEqualAmortizationValues, boolean isLastInstallment) {
+            List<ReAgedChargeEqualAmortizationValues> 
reAgedChargeEqualAmortizationValues, Integer index) {
         reAgedChargeEqualAmortizationValues.forEach(amortizationValue -> {
-            installment.getInstallmentCharges()
-                    .add(new 
LoanInstallmentCharge(amortizationValue.equalAmortizationValues.calculateValueBigDecimal(isLastInstallment),
-                            amortizationValue.charge, installment));
+            installment.getInstallmentCharges().add(new LoanInstallmentCharge(
+                    
amortizationValue.equalAmortizationValues.calculateValueBigDecimal(index), 
amortizationValue.charge, installment));
         });
     }
 
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 21a9af44c6..581198918a 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
@@ -857,7 +857,7 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
                 
lastInterestPeriod.addBalanceCorrectionAmount(rp.getOutstandingPrincipal().negated());
             }
             rp.setEmi(rp.getTotalPaidAmount());
-            rp.setOutstandingMoved(true);
+            rp.moveOutstandingDueToReAging();
         });
     }
 
@@ -1697,15 +1697,14 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
         EqualAmortizationValues principalEAV = 
calculateAdjustedEqualAmortizationValues(principal,
                 principal.add(interest).add(feesPenaltiesOutstanding),
                 
interestEAV.value().add(feesPenaltiesEqualAmortizationValues.value()), 
repaymentPeriods.size(), null, currency);
-        RepaymentPeriod last = repaymentPeriods.getLast();
         EqualAmortizationValues emiAEV = principalEAV.add(interestEAV);
-        repaymentPeriods.forEach(rp -> {
-            boolean isLast = last.equals(rp);
-            rp.setReAgedInterest(interestEAV.calculateValue(isLast));
-            Money emi = emiAEV.calculateValue(isLast);
+        for (int i = 0; i < repaymentPeriods.size(); i++) {
+            RepaymentPeriod rp = repaymentPeriods.get(i);
+            rp.setReAgedInterest(interestEAV.calculateValue(i));
+            Money emi = emiAEV.calculateValue(i);
             rp.setEmi(emi);
             rp.setOriginalEmi(emi);
-        });
+        }
     }
 
     @Override
@@ -1722,40 +1721,46 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
             LoanReAgeParameter reageParameter, Money feesPenaltiesOutstanding,
             EqualAmortizationValues feesPenaltiesEqualAmortizationValues) {
         LocalDate originalMaturityDate = interestSchedule.getMaturityDate();
-        boolean isAfterOriginalMaturityDate = 
transactionDate.isAfter(originalMaturityDate);
         List<RepaymentPeriod> reAgedRepaymentPeriods = new 
ArrayList<>(reageParameter.getNumberOfInstallments());
         OutstandingDetails reAgeingAmounts = 
precalculateReAgeEqualAmortizationAmount(interestSchedule, transactionDate, 
reageParameter);
+        Money zero = interestSchedule.zero();
 
         // calculate already paid balances from transaction date
         OutstandingDetails paidBalancesFromTransactionDate = 
calculatePaidBalancesAfterDate(interestSchedule, transactionDate);
 
+        // find future chargebacks.
+        // find future balance corrections
+        Money futureCreditedPrincipals = 
interestSchedule.repaymentPeriods().stream()
+                .filter(rp -> 
!rp.getFromDate().isBefore(transactionDate)).filter(rp -> 
rp.getDueDate().isAfter(transactionDate))
+                .map(RepaymentPeriod::getCreditedPrincipal).reduce(zero, 
Money::add);
+
         // set maturity date to transaction date and remove all repayment 
periods after it.
         accelerateMaturityDateTo(interestSchedule, transactionDate);
 
+        addCredit(interestSchedule, transactionDate, futureCreditedPrincipals, 
zero);
+
         // close all open repayment period while keep paid amounts
         interestSchedule.repaymentPeriods().forEach(rp -> {
             rp.getInterestPeriods().getLast()
                     
.addCreditedInterestAmount(MathUtil.min(rp.getOutstandingInterest(), 
rp.getCreditedInterest(), false).negated());
             rp.setEmi(rp.getTotalPaidAmount());
-            rp.setOutstandingMoved(true);
+            rp.moveOutstandingDueToReAging();
         });
 
         // stop calculate unrecognised interest at this point because all
         
interestSchedule.getLastRepaymentPeriod().setNoUnrecognisedInterest(true);
 
-        if (!paidBalancesFromTransactionDate.getOutstandingInterest().isZero()
-                || 
!paidBalancesFromTransactionDate.getOutstandingPrincipal().isZero()) {
+        if (!originalMaturityDate.isBefore(transactionDate)) {
             
createRepaymentPeriodForEarlyRepaidAmountsDuringReAgeing(interestSchedule,
                     paidBalancesFromTransactionDate.getOutstandingPrincipal(), 
paidBalancesFromTransactionDate.getOutstandingInterest(),
                     true);
-            addFirstReAgedPeriod(interestSchedule, 
interestSchedule.getLastRepaymentPeriod());
         }
 
-        updateModelForReageEqualAmortization(interestSchedule, reageParameter, 
reAgedRepaymentPeriods, isAfterOriginalMaturityDate);
+        updateModelForReageEqualAmortization(interestSchedule, reageParameter, 
reAgedRepaymentPeriods);
 
         updateEMIForReAgeEqualAmortization(reAgedRepaymentPeriods, 
reAgeingAmounts.getOutstandingPrincipal(),
                 reAgeingAmounts.getOutstandingInterest(), 
feesPenaltiesOutstanding, feesPenaltiesEqualAmortizationValues,
-                interestSchedule.zero().getCurrency());
+                zero.getCurrency());
 
         calculateOutstandingBalance(interestSchedule);
 
@@ -1767,31 +1772,17 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
     }
 
     private void 
updateModelForReageEqualAmortization(ProgressiveLoanInterestScheduleModel 
interestSchedule,
-            LoanReAgeParameter reageParameter, List<RepaymentPeriod> 
reAgedRepaymentPeriods, boolean isAfterOriginalMaturityDate) {
+            LoanReAgeParameter reageParameter, List<RepaymentPeriod> 
reAgedRepaymentPeriods) {
         int numberOfInstallmentsToAdd = 
reageParameter.getNumberOfInstallments();
         LocalDate toDate = reageParameter.getStartDate();
         RepaymentPeriod previous = interestSchedule.getLastRepaymentPeriod();
         int frequency = reageParameter.getFrequencyNumber();
         PeriodFrequencyType frequencyType = reageParameter.getFrequencyType();
 
-        if (!isAfterOriginalMaturityDate) {
-            // merge first reaged period
-            RepaymentPeriod firstReAgedPeriod = 
interestSchedule.getLastRepaymentPeriod();
-            firstReAgedPeriod.setDueDate(toDate);
-            firstReAgedPeriod.getLastInterestPeriod().setDueDate(toDate);
-            firstReAgedPeriod.setReAged(true);
-            firstReAgedPeriod.getPrevious().ifPresent(prev -> 
prev.setNoUnrecognisedInterest(true));
-            reAgedRepaymentPeriods.add(firstReAgedPeriod);
-
-            // update params for next reage repayment period calculation
-            numberOfInstallmentsToAdd--;
-            toDate = 
scheduledDateGenerator.getRepaymentPeriodDate(frequencyType, frequency, toDate);
-        }
-
         // insert new reaged repayment periods
         for (int i = 0; i < numberOfInstallmentsToAdd; i++) {
             RepaymentPeriod repaymentPeriod = RepaymentPeriod.create(previous, 
previous.getDueDate(), toDate, interestSchedule.zero(),
-                    previous.getMc(), previous.getLoanProductRelatedDetail());
+                    interestSchedule.mc(), 
previous.getLoanProductRelatedDetail());
             
repaymentPeriod.setTotalCapitalizedIncomeAmount(previous.getTotalCapitalizedIncomeAmount());
             
repaymentPeriod.setTotalDisbursedAmount(previous.getTotalDisbursedAmount());
             repaymentPeriod.setReAged(true);
@@ -1818,14 +1809,6 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
         calculateRateFactorForRepaymentPeriod(targetPeriod, interestSchedule);
     }
 
-    private static void 
addFirstReAgedPeriod(ProgressiveLoanInterestScheduleModel interestSchedule, 
RepaymentPeriod targetPeriod) {
-        RepaymentPeriod repaymentPeriodToInsert = 
RepaymentPeriod.create(targetPeriod, targetPeriod.getDueDate(),
-                interestSchedule.getMaturityDate(), interestSchedule.zero(), 
interestSchedule.mc(),
-                interestSchedule.loanProductRelatedDetail());
-        repaymentPeriodToInsert.setReAged(true);
-        interestSchedule.repaymentPeriods().add(repaymentPeriodToInsert);
-    }
-
     private OutstandingDetails 
calculatePaidBalancesAfterDate(ProgressiveLoanInterestScheduleModel 
interestSchedule,
             LocalDate transactionDate) {
         Money principal = 
interestSchedule.repaymentPeriods().stream().filter(rp -> 
!rp.getDueDate().isBefore(transactionDate))
@@ -1844,9 +1827,9 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
                 equalMonthlyValue = 
Money.roundToMultiplesOf(equalMonthlyValue, installmentAmountInMultiplesOf);
             }
             Money adjustmentForLastInstallment = 
totalOutstanding.minus(equalMonthlyValue.multipliedBy(numberOfInstallments));
-            return new EqualAmortizationValues(equalMonthlyValue, 
adjustmentForLastInstallment);
+            return new EqualAmortizationValues(totalOutstanding, 
numberOfInstallments, equalMonthlyValue, adjustmentForLastInstallment);
         }
-        return new EqualAmortizationValues(Money.zero(currency), 
Money.zero(currency));
+        return new EqualAmortizationValues(totalOutstanding, 
numberOfInstallments, Money.zero(currency), Money.zero(currency));
     }
 
     @Override
@@ -1857,6 +1840,6 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
                 installmentAmountInMultiplesOf, currency);
         Money value = 
calculatedEMI.value().minus(sumOfOtherEqualAmortizationValues);
         Money adjust = 
outstanding.minus(value.multipliedBy(numberOfInstallments));
-        return new EqualAmortizationValues(value, adjust);
+        return new EqualAmortizationValues(outstanding, numberOfInstallments, 
value, adjust);
     }
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/EqualAmortizationValues.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/EqualAmortizationValues.java
index b97b99e20b..1d4331969c 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/EqualAmortizationValues.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/EqualAmortizationValues.java
@@ -19,23 +19,38 @@
 package org.apache.fineract.portfolio.loanproduct.calc.data;
 
 import java.math.BigDecimal;
+import java.util.Objects;
 import org.apache.fineract.organisation.monetary.domain.Money;
 
-public record EqualAmortizationValues(Money value, Money adjustment) {
+public record EqualAmortizationValues(Money totalOutstanding, Integer 
numberOfInstallments, Money value, Money adjustment) {
 
     public Money getAdjustedValue() {
         return value.add(adjustment);
     }
 
-    public Money calculateValue(boolean isLast) {
-        return (isLast ? getAdjustedValue() : value);
+    /**
+     * calculates value according to the index of the installments
+     *
+     * @param index
+     *            index accepted 0 to number of (installments - 1)
+     * @return calculated value for the given index
+     */
+    public Money calculateValue(Integer index) {
+        if (getAdjustedValue().isLessThanZero()) {
+            return totalOutstanding.minus(value.multipliedBy(index + 
1)).isLessThanZero() ? value.zero() : value;
+        }
+        return (index == numberOfInstallments - 1 ? getAdjustedValue() : 
value);
     }
 
-    public BigDecimal calculateValueBigDecimal(boolean isLast) {
-        return calculateValue(isLast).getAmount();
+    public BigDecimal calculateValueBigDecimal(Integer index) {
+        return calculateValue(index).getAmount();
     }
 
     public EqualAmortizationValues add(EqualAmortizationValues other) {
-        return new EqualAmortizationValues(value.add(other.value), 
adjustment.add(other.adjustment));
+        if (!Objects.equals(numberOfInstallments, other.numberOfInstallments)) 
{
+            throw new RuntimeException("Incompatible EqualAmortizationValues. 
numberOfInstallments parameter should match.");
+        }
+        return new 
EqualAmortizationValues(totalOutstanding.add(other.totalOutstanding), 
numberOfInstallments, value.add(other.value),
+                adjustment.add(other.adjustment));
     }
 }
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 61b9bb7d6d..755f47a74d 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
@@ -91,8 +91,10 @@ public class RepaymentPeriod {
 
     @Getter
     @Setter
-    private boolean isOutstandingMoved = false;
-
+    private Money creditedPrincipalMovedDueReAge;
+    @Getter
+    @Setter
+    private Money creditedInterestMovedDueReAge;
     @Setter
     @Getter
     private boolean noUnrecognisedInterest;
@@ -125,6 +127,8 @@ public class RepaymentPeriod {
         this.reAged = reAged;
         this.reAgedEarlyRepaymentHolder = reAgedEarlyRepaymentHolder;
         this.reAgedInterest = reAgedInterest;
+        this.creditedInterestMovedDueReAge = 
Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
+        this.creditedInterestMovedDueReAge = 
Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
     }
 
     public static RepaymentPeriod empty(RepaymentPeriod previous, MathContext 
mc, ILoanConfigurationDetails loanProductRelatedDetail) {
@@ -148,7 +152,8 @@ public class RepaymentPeriod {
                 repaymentPeriod.getPaidPrincipal(), 
repaymentPeriod.getPaidInterest(), 
repaymentPeriod.getFutureUnrecognizedInterest(), mc,
                 repaymentPeriod.getLoanProductRelatedDetail(), 
repaymentPeriod.isNoUnrecognisedInterest(), repaymentPeriod.isReAged(),
                 repaymentPeriod.isReAgedEarlyRepaymentHolder(), 
repaymentPeriod.getReAgedInterest());
-        
newRepaymentPeriod.setOutstandingMoved(repaymentPeriod.isOutstandingMoved());
+        
newRepaymentPeriod.setCreditedPrincipalMovedDueReAge(repaymentPeriod.getCreditedPrincipalMovedDueReAge());
+        
newRepaymentPeriod.setCreditedInterestMovedDueReAge(repaymentPeriod.getCreditedInterestMovedDueReAge());
         // There is always at least 1 interest period, by default with same 
from-due date as repayment period
         for (InterestPeriod interestPeriod : 
repaymentPeriod.getInterestPeriods()) {
             
newRepaymentPeriod.getInterestPeriods().add(InterestPeriod.copy(newRepaymentPeriod,
 interestPeriod, mc));
@@ -162,7 +167,8 @@ public class RepaymentPeriod {
                 repaymentPeriod.getDueDate(), new ArrayList<>(), 
repaymentPeriod.getEmi(), repaymentPeriod.getOriginalEmi(), zero, zero,
                 zero, mc, repaymentPeriod.getLoanProductRelatedDetail(), 
repaymentPeriod.isNoUnrecognisedInterest(),
                 repaymentPeriod.isReAged(), 
repaymentPeriod.isReAgedEarlyRepaymentHolder(), 
repaymentPeriod.getReAgedInterest());
-        
newRepaymentPeriod.setOutstandingMoved(repaymentPeriod.isOutstandingMoved());
+        
newRepaymentPeriod.setCreditedPrincipalMovedDueReAge(repaymentPeriod.getCreditedPrincipalMovedDueReAge());
+        
newRepaymentPeriod.setCreditedInterestMovedDueReAge(repaymentPeriod.getCreditedInterestMovedDueReAge());
         // There is always at least 1 interest period, by default with same 
from-due date as repayment period
         for (InterestPeriod interestPeriod : 
repaymentPeriod.getInterestPeriods()) {
             var interestPeriodCopy = InterestPeriod.copy(newRepaymentPeriod, 
interestPeriod);
@@ -311,7 +317,8 @@ public class RepaymentPeriod {
      * @return
      */
     public Money getTotalCreditedAmount() {
-        return isOutstandingMoved ? Money.zero(getCurrency(), getMc()) : 
getCreditedPrincipal().plus(getCreditedInterest(), getMc());
+        return getCreditedPrincipal().plus(getCreditedInterest(), 
getMc()).minus(getCreditedInterestMovedDueReAge(), getMc())
+                .minus(getCreditedPrincipalMovedDueReAge(), getMc());
     }
 
     /**
@@ -480,4 +487,9 @@ public class RepaymentPeriod {
     public Money getTotalCapitalizedIncomeAmount() {
         return MathUtil.nullToZero(totalCapitalizedIncomeAmount, 
getCurrency(), getMc());
     }
+
+    public void moveOutstandingDueToReAging() {
+        setCreditedPrincipalMovedDueReAge(getCreditedPrincipal());
+        setCreditedInterestMovedDueReAge(getCreditedInterest());
+    }
 }
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 eef9f3d00b..819eb52b1f 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
@@ -31,6 +31,7 @@ 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;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
 import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
@@ -4239,6 +4240,147 @@ class ProgressiveEMICalculatorTest {
     @Nested
     public class ReAgeEqualAmortization {
 
+        private ProgressiveLoanInterestScheduleModel generateSchedule() {
+            final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = new ArrayList<>();
+
+            expectedRepaymentPeriods.add(repayment(1, LocalDate.of(2024, 1, 
1), LocalDate.of(2024, 2, 1)));
+            expectedRepaymentPeriods.add(repayment(2, LocalDate.of(2024, 2, 
1), LocalDate.of(2024, 3, 1)));
+            expectedRepaymentPeriods.add(repayment(3, LocalDate.of(2024, 3, 
1), LocalDate.of(2024, 4, 1)));
+            expectedRepaymentPeriods.add(repayment(4, LocalDate.of(2024, 4, 
1), LocalDate.of(2024, 5, 1)));
+            expectedRepaymentPeriods.add(repayment(5, LocalDate.of(2024, 5, 
1), LocalDate.of(2024, 6, 1)));
+            expectedRepaymentPeriods.add(repayment(6, LocalDate.of(2024, 6, 
1), LocalDate.of(2024, 7, 1)));
+
+            final BigDecimal interestRate = BigDecimal.valueOf(15.678);
+            final Integer installmentAmountInMultiplesOf = null;
+
+            
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
+            
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_365.getValue());
+            
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
+            
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
+            
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
+            
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
+
+            final ProgressiveLoanInterestScheduleModel interestSchedule = 
emiCalculator.generatePeriodInterestScheduleModel(
+                    expectedRepaymentPeriods, loanProductRelatedDetail, 
installmentAmountInMultiplesOf, mc);
+
+            final Money disbursedAmount = toMoney(100.0);
+            emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 
1, 1), disbursedAmount);
+
+            checkPeriod(interestSchedule, 0, 0, 17.43, 0.0, 0.0, 1.33, 16.1, 
83.9);
+            checkPeriod(interestSchedule, 0, 1, 17.43, 0.013315561644, 
1.3315561644, 1.33, 16.1, 83.9);
+            checkPeriod(interestSchedule, 1, 0, 17.43, 0.012456493151, 
1.04509977537, 1.05, 16.38, 67.52);
+            checkPeriod(interestSchedule, 2, 0, 17.43, 0.013315561644, 
0.899066722202, 0.90, 16.53, 50.99);
+            checkPeriod(interestSchedule, 3, 0, 17.43, 0.012886027397, 
0.657058536972, 0.66, 16.77, 34.22);
+            checkPeriod(interestSchedule, 4, 0, 17.43, 0.013315561644, 
0.455658519458, 0.46, 16.97, 17.25);
+            checkPeriod(interestSchedule, 5, 0, 17.47, 0.012886027397, 
0.222283972598, 0.22, 17.25, 0.0);
+            return interestSchedule;
+        }
+
+        @Test
+        public void 
test_chargeBackOn2ndRP_ReAgeingOn1stRPsDueDate_EQUAL_AMORTIZATION_FULL_INTEREST()
 {
+            ProgressiveLoanInterestScheduleModel interestSchedule = 
generateSchedule();
+
+            emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 
1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
+                    Money.of(currency, BigDecimal.valueOf(16.1)));
+            emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 
1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
+                    Money.of(currency, BigDecimal.valueOf(1.33)));
+
+            emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 
2, 1), Money.of(currency, BigDecimal.valueOf(17.43)));
+
+            // No repayment no interest recalculation
+            LocalDate reAgingStartDate = LocalDate.of(2024, 2, 1);
+            LocalDate transactionDate = LocalDate.of(2024, 2, 1);
+
+            OutstandingDetails outstandingAmountsTillDate = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
+                    interestSchedule.getMaturityDate());
+
+            LoanReAgeParameter reageParameter = new LoanReAgeParameter(null, 
PeriodFrequencyType.MONTHS, 1, reAgingStartDate, 6,
+                    
LoanReAgeInterestHandlingType.EQUAL_AMORTIZATION_FULL_INTEREST, null);
+
+            // Update the existing model with re-aged periods
+            emiCalculator.reAgeEqualAmortization(interestSchedule, 
transactionDate, reageParameter, Money.zero(currency),
+                    new EqualAmortizationValues(Money.zero(currency), 6, 
Money.zero(currency), Money.zero(currency)));
+
+            OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
+                    interestSchedule.getMaturityDate());
+
+            
Assertions.assertEquals(outstandingAmountsTillDate.getOutstandingInterest().getAmount(),
+                    
outstandingAmountsTillDateAfterReage.getOutstandingInterest().getAmount());
+            
Assertions.assertEquals(outstandingAmountsTillDate.getOutstandingPrincipal().getAmount(),
+                    
outstandingAmountsTillDateAfterReage.getOutstandingPrincipal().getAmount());
+
+        }
+
+        @Test
+        public void 
test_chargeBackOn2ndRP_ReAgeingOn2stRPsDueDate_EQUAL_AMORTIZATION_FULL_INTEREST()
 {
+            ProgressiveLoanInterestScheduleModel interestSchedule = 
generateSchedule();
+
+            emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 
1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
+                    Money.of(currency, BigDecimal.valueOf(16.1)));
+            emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 
1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
+                    Money.of(currency, BigDecimal.valueOf(1.33)));
+
+            emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 
2, 1), Money.of(currency, BigDecimal.valueOf(17.43)));
+
+            // No repayment no interest recalculation
+            LocalDate reAgingStartDate = LocalDate.of(2024, 3, 1);
+            LocalDate transactionDate = LocalDate.of(2024, 3, 1);
+
+            OutstandingDetails outstandingAmountsTillDate = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
+                    interestSchedule.getMaturityDate());
+
+            LoanReAgeParameter reageParameter = new LoanReAgeParameter(null, 
PeriodFrequencyType.MONTHS, 1, reAgingStartDate, 6,
+                    
LoanReAgeInterestHandlingType.EQUAL_AMORTIZATION_FULL_INTEREST, null);
+
+            // Update the existing model with re-aged periods
+            emiCalculator.reAgeEqualAmortization(interestSchedule, 
transactionDate, reageParameter, Money.zero(currency),
+                    new EqualAmortizationValues(Money.zero(currency), 6, 
Money.zero(currency), Money.zero(currency)));
+
+            OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
+                    interestSchedule.getMaturityDate());
+
+            
Assertions.assertEquals(outstandingAmountsTillDate.getOutstandingInterest().getAmount(),
+                    
outstandingAmountsTillDateAfterReage.getOutstandingInterest().getAmount());
+            
Assertions.assertEquals(outstandingAmountsTillDate.getOutstandingPrincipal().getAmount(),
+                    
outstandingAmountsTillDateAfterReage.getOutstandingPrincipal().getAmount());
+
+        }
+
+        @Test
+        public void 
test_chargeBackOn2ndRP_ReAgeingDuring2stRP_EQUAL_AMORTIZATION_FULL_INTEREST() {
+            ProgressiveLoanInterestScheduleModel interestSchedule = 
generateSchedule();
+
+            emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 1, 
1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
+                    Money.of(currency, BigDecimal.valueOf(16.1)));
+            emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 1, 
1), LocalDate.of(2024, 2, 1), LocalDate.of(2024, 2, 1),
+                    Money.of(currency, BigDecimal.valueOf(1.33)));
+
+            emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 
2, 1), Money.of(currency, BigDecimal.valueOf(17.43)));
+
+            // No repayment no interest recalculation
+            LocalDate reAgingStartDate = LocalDate.of(2024, 2, 10);
+            LocalDate transactionDate = LocalDate.of(2024, 2, 10);
+
+            OutstandingDetails outstandingAmountsTillDate = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
+                    interestSchedule.getMaturityDate());
+
+            LoanReAgeParameter reageParameter = new LoanReAgeParameter(null, 
PeriodFrequencyType.MONTHS, 1, reAgingStartDate, 6,
+                    
LoanReAgeInterestHandlingType.EQUAL_AMORTIZATION_FULL_INTEREST, null);
+
+            // Update the existing model with re-aged periods
+            emiCalculator.reAgeEqualAmortization(interestSchedule, 
transactionDate, reageParameter, Money.zero(currency),
+                    new EqualAmortizationValues(Money.zero(currency), 6, 
Money.zero(currency), Money.zero(currency)));
+
+            OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
+                    interestSchedule.getMaturityDate());
+
+            
Assertions.assertEquals(outstandingAmountsTillDate.getOutstandingInterest().getAmount(),
+                    
outstandingAmountsTillDateAfterReage.getOutstandingInterest().getAmount());
+            
Assertions.assertEquals(outstandingAmountsTillDate.getOutstandingPrincipal().getAmount(),
+                    
outstandingAmountsTillDateAfterReage.getOutstandingPrincipal().getAmount());
+
+        }
+
         @Test
         public void 
test_transactionInMiddleOfPeriod_EQUAL_AMORTIZATION_FULL_INTEREST_noTransactionTilDate_noInterestRecalc()
 {
             final List<LoanScheduleModelRepaymentPeriod> 
expectedRepaymentPeriods = new ArrayList<>();
@@ -4291,7 +4433,7 @@ class ProgressiveEMICalculatorTest {
 
             // Update the existing model with re-aged periods
             emiCalculator.reAgeEqualAmortization(interestSchedule, 
loanTransaction.getTransactionDate(), reageParameter,
-                    Money.zero(currency), new 
EqualAmortizationValues(Money.zero(currency), Money.zero(currency)));
+                    Money.zero(currency), new 
EqualAmortizationValues(Money.zero(currency), 6, Money.zero(currency), 
Money.zero(currency)));
 
             OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
                     interestSchedule.getMaturityDate());
@@ -4380,7 +4522,7 @@ class ProgressiveEMICalculatorTest {
 
             // Update the existing model with re-aged periods
             emiCalculator.reAgeEqualAmortization(interestSchedule, 
loanTransaction.getTransactionDate(), reageParameter,
-                    Money.zero(currency), new 
EqualAmortizationValues(Money.zero(currency), Money.zero(currency)));
+                    Money.zero(currency), new 
EqualAmortizationValues(Money.zero(currency), 6, Money.zero(currency), 
Money.zero(currency)));
 
             OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
                     interestSchedule.getMaturityDate());
@@ -4439,7 +4581,7 @@ class ProgressiveEMICalculatorTest {
 
             // Update the existing model with re-aged periods
             emiCalculator.reAgeEqualAmortization(interestSchedule, 
transactionDate, reageParameter, Money.zero(currency),
-                    new EqualAmortizationValues(Money.zero(currency), 
Money.zero(currency)));
+                    new EqualAmortizationValues(Money.zero(currency), 6, 
Money.zero(currency), Money.zero(currency)));
 
             OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
                     interestSchedule.getMaturityDate());
@@ -4499,7 +4641,7 @@ class ProgressiveEMICalculatorTest {
 
             // Update the existing model with re-aged periods
             emiCalculator.reAgeEqualAmortization(interestSchedule, 
transactionDate, reageParameter, Money.zero(currency),
-                    new EqualAmortizationValues(Money.zero(currency), 
Money.zero(currency)));
+                    new EqualAmortizationValues(Money.zero(currency), 6, 
Money.zero(currency), Money.zero(currency)));
 
             OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
                     interestSchedule.getMaturityDate());
@@ -4582,7 +4724,7 @@ class ProgressiveEMICalculatorTest {
 
             // Update the existing model with re-aged periods
             emiCalculator.reAgeEqualAmortization(interestSchedule, 
transactionDate, reageParameter, Money.zero(currency),
-                    new EqualAmortizationValues(Money.zero(currency), 
Money.zero(currency)));
+                    new EqualAmortizationValues(Money.zero(currency), 6, 
Money.zero(currency), Money.zero(currency)));
 
             OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
                     interestSchedule.getMaturityDate());
@@ -4665,7 +4807,7 @@ class ProgressiveEMICalculatorTest {
 
             // Update the existing model with re-aged periods
             emiCalculator.reAgeEqualAmortization(interestSchedule, 
transactionDate, reageParameter, Money.zero(currency),
-                    new EqualAmortizationValues(Money.zero(currency), 
Money.zero(currency)));
+                    new EqualAmortizationValues(Money.zero(currency), 6, 
Money.zero(currency), Money.zero(currency)));
 
             OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
                     interestSchedule.getMaturityDate());
@@ -4738,7 +4880,7 @@ class ProgressiveEMICalculatorTest {
 
             // Update the existing model with re-aged periods
             emiCalculator.reAgeEqualAmortization(interestSchedule, 
transactionDate, reageParameter, Money.zero(currency),
-                    new EqualAmortizationValues(Money.zero(currency), 
Money.zero(currency)));
+                    new EqualAmortizationValues(Money.zero(currency), 6, 
Money.zero(currency), Money.zero(currency)));
 
             OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
                     interestSchedule.getMaturityDate());
@@ -4811,7 +4953,7 @@ class ProgressiveEMICalculatorTest {
 
             // Update the existing model with re-aged periods
             emiCalculator.reAgeEqualAmortization(interestSchedule, 
transactionDate, reageParameter, Money.zero(currency),
-                    new EqualAmortizationValues(Money.zero(currency), 
Money.zero(currency)));
+                    new EqualAmortizationValues(Money.zero(currency), 6, 
Money.zero(currency), Money.zero(currency)));
 
             OutstandingDetails outstandingAmountsTillDateAfterReage = 
emiCalculator.getOutstandingAmountsTillDate(interestSchedule,
                     interestSchedule.getMaturityDate());
@@ -4824,6 +4966,49 @@ class ProgressiveEMICalculatorTest {
         }
     }
 
+    @Nested
+    public class EqualAmortizationValue {
+
+        @Test
+        public void test_AmortizationTotalIsLessThanInstallmentNumber() {
+            EqualAmortizationValues actual = 
emiCalculator.calculateEqualAmortizationValues(Money.of(currency, 
BigDecimal.valueOf(0.04)), 6,
+                    null, MonetaryCurrency.fromCurrencyData(currency));
+            Assertions.assertEquals(0.01, 
actual.calculateValueBigDecimal(0).doubleValue());
+            Assertions.assertEquals(0.01, 
actual.calculateValueBigDecimal(1).doubleValue());
+            Assertions.assertEquals(0.01, 
actual.calculateValueBigDecimal(2).doubleValue());
+            Assertions.assertEquals(0.01, 
actual.calculateValueBigDecimal(3).doubleValue());
+            Assertions.assertEquals(0.0, 
actual.calculateValueBigDecimal(4).doubleValue());
+            Assertions.assertEquals(0.0, 
actual.calculateValueBigDecimal(5).doubleValue());
+
+        }
+
+        @Test
+        public void test_AmortizationIsJustBiggerThanInstallmentNumber() {
+            EqualAmortizationValues actual = 
emiCalculator.calculateEqualAmortizationValues(Money.of(currency, 
BigDecimal.valueOf(0.07)), 6,
+                    null, MonetaryCurrency.fromCurrencyData(currency));
+            Assertions.assertEquals(0.01, 
actual.calculateValueBigDecimal(0).doubleValue());
+            Assertions.assertEquals(0.01, 
actual.calculateValueBigDecimal(1).doubleValue());
+            Assertions.assertEquals(0.01, 
actual.calculateValueBigDecimal(2).doubleValue());
+            Assertions.assertEquals(0.01, 
actual.calculateValueBigDecimal(3).doubleValue());
+            Assertions.assertEquals(0.01, 
actual.calculateValueBigDecimal(4).doubleValue());
+            Assertions.assertEquals(0.02, 
actual.calculateValueBigDecimal(5).doubleValue());
+
+        }
+
+        @Test
+        public void test_AmortizationNonEdgeCase() {
+            EqualAmortizationValues actual = 
emiCalculator.calculateEqualAmortizationValues(Money.of(currency, 
BigDecimal.valueOf(0.59)), 6,
+                    null, MonetaryCurrency.fromCurrencyData(currency));
+            Assertions.assertEquals(0.1, 
actual.calculateValueBigDecimal(0).doubleValue());
+            Assertions.assertEquals(0.1, 
actual.calculateValueBigDecimal(1).doubleValue());
+            Assertions.assertEquals(0.1, 
actual.calculateValueBigDecimal(2).doubleValue());
+            Assertions.assertEquals(0.1, 
actual.calculateValueBigDecimal(3).doubleValue());
+            Assertions.assertEquals(0.1, 
actual.calculateValueBigDecimal(4).doubleValue());
+            Assertions.assertEquals(0.09, 
actual.calculateValueBigDecimal(5).doubleValue());
+
+        }
+    }
+
     // utilities
     private List<LoanScheduleModelRepaymentPeriod> 
generateExpectedRepaymentPeriods(LocalDate disbursementDate) {
         return switch 
(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()) {

Reply via email to