diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestRateChange.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestRateChange.feature index 4baf17b8b..d298a486f 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestRateChange.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanInterestRateChange.feature @@ -1701,3 +1701,112 @@ Feature: Loan interest rate change on repayment schedule Then Loan Repayment schedule has the following data in Total row: | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | 1000.0 | 3.68 | 0.0 | 0.0 | 1003.68 | 0.0 | 0.0 | 0.0 | 1003.68 | + + @TestRailId:C4625 + Scenario: Verify loan closure after MIR, backdated interest rate change and repayment reversal + When Admin sets the business date to "03 October 2025" + And Admin creates a client with random data + And Admin set "LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule + And Admin creates a fully customized loan with the following data: + | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy | + | LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 03 October 2025 | 231.59 | 35.99 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION | + And Admin successfully approves the loan on "03 October 2025" with "231.59" amount and expected disbursement date on "03 October 2025" + And Admin successfully disburse the loan on "03 October 2025" with "231.59" EUR transaction amount + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 03 October 2025 | | 231.59 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 03 November 2025 | | 195.79 | 35.8 | 6.95 | 0.0 | 0.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | + | 2 | 30 | 03 December 2025 | | 158.91 | 36.88 | 5.87 | 0.0 | 0.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | + | 3 | 31 | 03 January 2026 | | 120.93 | 37.98 | 4.77 | 0.0 | 0.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | + | 4 | 31 | 03 February 2026 | | 81.81 | 39.12 | 3.63 | 0.0 | 0.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | + | 5 | 28 | 03 March 2026 | | 41.51 | 40.3 | 2.45 | 0.0 | 0.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | + | 6 | 31 | 03 April 2026 | | 0.0 | 41.51 | 1.24 | 0.0 | 0.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 231.59 | 24.91 | 0.0 | 0.0 | 256.5 | 0.0 | 0.0 | 0.0 | 256.5 | + When Admin sets the business date to "15 October 2025" + And Customer makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "15 October 2025" with 220.83 EUR transaction amount and system-generated Idempotency key + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 03 October 2025 | | 231.59 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 03 November 2025 | | 213.75 | 17.84 | 2.89 | 0.0 | 0.0 | 20.73 | 9.65 | 9.65 | 0.0 | 11.08 | + | 2 | 30 | 03 December 2025 | 15 October 2025 | 171.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + | 3 | 31 | 03 January 2026 | 15 October 2025 | 128.25 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + | 4 | 31 | 03 February 2026 | 15 October 2025 | 85.5 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + | 5 | 28 | 03 March 2026 | 15 October 2025 | 42.75 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + | 6 | 31 | 03 April 2026 | 15 October 2025 | 0.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 231.59 | 2.89 | 0.0 | 0.0 | 234.48 | 223.4 | 223.4 | 0.0 | 11.08 | + When Admin sets the business date to "30 October 2025" + And Customer makes "AUTOPAY" repayment on "30 October 2025" with 11.04 EUR transaction amount + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 03 October 2025 | | 231.59 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 03 November 2025 | 30 October 2025 | 213.75 | 17.84 | 2.84 | 0.0 | 0.0 | 20.68 | 20.68 | 20.68 | 0.0 | 0.0 | + | 2 | 30 | 03 December 2025 | 15 October 2025 | 171.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + | 3 | 31 | 03 January 2026 | 15 October 2025 | 128.25 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + | 4 | 31 | 03 February 2026 | 15 October 2025 | 85.5 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + | 5 | 28 | 03 March 2026 | 15 October 2025 | 42.75 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + | 6 | 31 | 03 April 2026 | 15 October 2025 | 0.0 | 42.75 | 0.0 | 0.0 | 0.0 | 42.75 | 42.75 | 42.75 | 0.0 | 0.0 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 231.59 | 2.84 | 0.0 | 0.0 | 234.43 | 234.43 | 234.43 | 0.0 | 0.0 | + When Admin creates and approves Loan reschedule with the following data: + | rescheduleFromDate | submittedOnDate | adjustedDueDate | graceOnPrincipal | graceOnInterest | extraTerms | newInterestRate | + | 04 October 2025 | 30 October 2025 | | | | | 25.99 | + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 03 October 2025 | | 231.59 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 03 November 2025 | 30 October 2025 | 207.9 | 23.69 | 2.05 | 0.0 | 0.0 | 25.74 | 25.74 | 25.74 | 0.0 | 0.0 | + | 2 | 30 | 03 December 2025 | 15 October 2025 | 166.32 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 3 | 31 | 03 January 2026 | 15 October 2025 | 124.74 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 4 | 31 | 03 February 2026 | 15 October 2025 | 83.16 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 5 | 28 | 03 March 2026 | 15 October 2025 | 41.58 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 6 | 31 | 03 April 2026 | 15 October 2025 | 0.0 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 231.59 | 2.05 | 0.0 | 0.0 | 233.64 | 233.64 | 233.64 | 0.0 | 0.0 | + And Admin sets the business date to "06 November 2025" + And Admin makes Credit Balance Refund transaction on "06 November 2025" with 0.04 EUR transaction amount + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 03 October 2025 | | 231.59 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 03 November 2025 | 30 October 2025 | 207.9 | 23.69 | 2.05 | 0.0 | 0.0 | 25.74 | 25.74 | 25.74 | 0.0 | 0.0 | + | 2 | 30 | 03 December 2025 | 15 October 2025 | 166.32 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 3 | 31 | 03 January 2026 | 15 October 2025 | 124.74 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 4 | 31 | 03 February 2026 | 15 October 2025 | 83.16 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 5 | 28 | 03 March 2026 | 15 October 2025 | 41.58 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 6 | 31 | 03 April 2026 | 15 October 2025 | 0.0 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 231.59 | 2.05 | 0.0 | 0.0 | 233.64 | 233.64 | 233.64 | 0.0 | 0.0 | + When Admin sets the business date to "07 November 2025" + And Customer undo "1"th "Repayment" transaction made on "30 October 2025" + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 03 October 2025 | | 231.59 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 03 November 2025 | | 207.9 | 23.69 | 2.1 | 0.0 | 0.0 | 25.79 | 14.78 | 14.78 | 0.0 | 11.01 | + | 2 | 30 | 03 December 2025 | | 166.32 | 41.62 | 0.03 | 0.0 | 0.0 | 41.65 | 41.58 | 41.58 | 0.0 | 0.07 | + | 3 | 31 | 03 January 2026 | 15 October 2025 | 124.74 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 4 | 31 | 03 February 2026 | 15 October 2025 | 83.16 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 5 | 28 | 03 March 2026 | 15 October 2025 | 41.58 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 6 | 31 | 03 April 2026 | 15 October 2025 | 0.0 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 231.63 | 2.13 | 0.0 | 0.0 | 233.76 | 222.68 | 222.68 | 0.0 | 11.08 | + When Admin sets the business date to "10 November 2025" + And Customer makes "AUTOPAY" repayment on "10 November 2025" with 11.05 EUR transaction amount + Then Loan Repayment schedule has 6 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 03 October 2025 | | 231.59 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 03 November 2025 | 10 November 2025 | 207.9 | 23.69 | 2.1 | 0.0 | 0.0 | 25.79 | 25.79 | 14.78 | 11.01 | 0.0 | + | 2 | 30 | 03 December 2025 | 10 November 2025 | 166.32 | 41.62 | 0.0 | 0.0 | 0.0 | 41.62 | 41.62 | 41.62 | 0.0 | 0.0 | + | 3 | 31 | 03 January 2026 | 15 October 2025 | 124.74 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 4 | 31 | 03 February 2026 | 15 October 2025 | 83.16 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 5 | 28 | 03 March 2026 | 15 October 2025 | 41.58 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + | 6 | 31 | 03 April 2026 | 15 October 2025 | 0.0 | 41.58 | 0.0 | 0.0 | 0.0 | 41.58 | 41.58 | 41.58 | 0.0 | 0.0 | + And Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 231.63 | 2.1 | 0.0 | 0.0 | 233.73 | 233.73 | 222.72 | 11.01 | 0.0 | + And Loan is closed with zero outstanding balance and it's all installments have obligations met 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 a39748da3..e6b9d6b7c 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 @@ -368,6 +368,18 @@ public final class ProgressiveEMICalculator implements EMICalculator { }); } + public void addOverdueBalanceCorrection(final ProgressiveLoanInterestScheduleModel scheduleModel, final LocalDate correctionDate, + final Money overdueAmount) { + scheduleModel + .changeOutstandingBalanceAndUpdateInterestPeriods(correctionDate, scheduleModel.zero(), overdueAmount, scheduleModel.zero()) + .ifPresent(repaymentPeriod -> { + scheduleModel.recordOverdueCorrection(correctionDate, overdueAmount, repaymentPeriod.getDueDate()); + calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel); + calculateOutstandingBalance(scheduleModel); + calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, correctionDate); + }); + } + @Override public void payInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodFromDate, LocalDate repaymentPeriodDueDate, LocalDate transactionDate, Money interestAmount) { @@ -919,9 +931,9 @@ public final class ProgressiveEMICalculator implements EMICalculator { if (!currentDate.equals(model.lastOverdueBalanceChange())) { if (model.lastOverdueBalanceChange() == null || currentInstallment.getFromDate().isAfter(model.lastOverdueBalanceChange())) { - addBalanceCorrection(model, fromDate, overduePrincipal); + addOverdueBalanceCorrection(model, fromDate, overduePrincipal); } else { - addBalanceCorrection(model, model.lastOverdueBalanceChange(), overduePrincipal); + addOverdueBalanceCorrection(model, model.lastOverdueBalanceChange(), overduePrincipal); } if (currentDate.isAfter(fromDate) && !currentDate.isAfter(toDate)) { @@ -931,7 +943,7 @@ public final class ProgressiveEMICalculator implements EMICalculator { } else { lastOverdueBalanceChange = currentDate; } - addBalanceCorrection(model, lastOverdueBalanceChange, aggregatedOverDuePrincipal.negated()); + addOverdueBalanceCorrection(model, lastOverdueBalanceChange, aggregatedOverDuePrincipal.negated()); model.lastOverdueBalanceChange(lastOverdueBalanceChange); } return true; @@ -1125,7 +1137,24 @@ public final class ProgressiveEMICalculator implements EMICalculator { RepaymentPeriod repaymentPeriod, LocalDate tillDate) { MathContext mc = scheduleModel.mc(); ProgressiveLoanInterestScheduleModel scheduleModelCopy = scheduleModel.deepCopy(mc); + + // Reverse overdue corrections on the copy when they exist ON the target period + // but NOT beyond it. Beyond-target corrections provide legitimate cascading interest. + // Without reversal, overdue-inflated IPs create phantom futureUnrecognizedInterest. + final boolean hasOverdueCorrectionsBeyondTarget = scheduleModelCopy.hasOverdueCorrectionsBeyondDate(repaymentPeriod.getDueDate()); + final boolean hasOverdueCorrectionsOnTarget = scheduleModelCopy.hasOverdueCorrectionsOnDate(repaymentPeriod.getDueDate()); + final boolean shouldResetOverdue = !hasOverdueCorrectionsBeyondTarget && hasOverdueCorrectionsOnTarget; + + if (shouldResetOverdue) { + scheduleModelCopy.reverseOverdueCorrections(); + } + calculateRateFactorForScheduleTillDateInclusive(scheduleModelCopy, tillDate); + + if (shouldResetOverdue) { + calculateOutstandingBalance(scheduleModelCopy); + } + Optional futureUnrecognizedInterestPeriod = getPeriodWithUnrecognizedInterest(repaymentPeriod, scheduleModelCopy); futureUnrecognizedInterestPeriod.ifPresent(period -> { diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/OverdueBalanceCorrection.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/OverdueBalanceCorrection.java new file mode 100644 index 000000000..c6ab29a54 --- /dev/null +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/OverdueBalanceCorrection.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.portfolio.loanproduct.calc.data; + +import java.time.LocalDate; +import org.apache.fineract.organisation.monetary.domain.Money; + +/** + * Records an overdue balance correction applied to the schedule model. Used for detecting and reversing overdue + * corrections on model copies to prevent phantom futureUnrecognizedInterest. + * + * @param correctionDate + * the date where the correction was applied (matches an InterestPeriod's dueDate after IP split) + * @param amount + * the correction amount (+X for inflation, -X for deflation) + * @param affectedRpDueDate + * the dueDate of the RepaymentPeriod that received this correction (for beyond/onTarget detection) + */ +public record OverdueBalanceCorrection(LocalDate correctionDate, Money amount, LocalDate affectedRpDueDate) { +} 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 c479d6d3a..c02a23e2b 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 @@ -68,6 +68,7 @@ public class ProgressiveLoanInterestScheduleModel { @Setter private LocalDate lastOverdueBalanceChange; + private List overdueCorrections = new ArrayList<>(); public ProgressiveLoanInterestScheduleModel(final List repaymentPeriods, final ILoanConfigurationDetails loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, final MathContext mc) { @@ -95,9 +96,35 @@ public class ProgressiveLoanInterestScheduleModel { loanProductRelatedDetail.isInterestRecalculationEnabled())); } - public ProgressiveLoanInterestScheduleModel deepCopy(MathContext mc) { - return new ProgressiveLoanInterestScheduleModel(repaymentPeriods, interestRates, loanProductRelatedDetail, - installmentAmountInMultiplesOf, mc, false); + public void recordOverdueCorrection(final LocalDate correctionDate, final Money amount, final LocalDate affectedRpDueDate) { + overdueCorrections.add(new OverdueBalanceCorrection(correctionDate, amount, affectedRpDueDate)); + } + + public boolean hasOverdueCorrectionsBeyondDate(final LocalDate targetDueDate) { + return overdueCorrections.stream().anyMatch(oc -> oc.affectedRpDueDate().isAfter(targetDueDate)); + } + + public boolean hasOverdueCorrectionsOnDate(final LocalDate targetDueDate) { + return overdueCorrections.stream().anyMatch(oc -> oc.affectedRpDueDate().isEqual(targetDueDate)); + } + + /** + * Reverses all recorded overdue corrections on this model by subtracting each correction's amount from the + * corresponding InterestPeriod's balanceCorrectionAmount. + */ + public void reverseOverdueCorrections() { + for (final OverdueBalanceCorrection oc : overdueCorrections) { + changeOutstandingBalanceAndUpdateInterestPeriods(oc.correctionDate(), zero(), oc.amount().negated(), zero()); + } + overdueCorrections.clear(); + this.lastOverdueBalanceChange = null; + } + + public ProgressiveLoanInterestScheduleModel deepCopy(final MathContext mc) { + final ProgressiveLoanInterestScheduleModel copy = new ProgressiveLoanInterestScheduleModel(repaymentPeriods, interestRates, + loanProductRelatedDetail, installmentAmountInMultiplesOf, mc, false); + copy.overdueCorrections = new ArrayList<>(this.overdueCorrections); + return copy; } public ProgressiveLoanInterestScheduleModel copyWithoutPaidAmounts() {