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

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


The following commit(s) were added to refs/heads/develop by this push:
     new 35c04c014 FINERACT-1981: Interest recalculation - Recalculate Interest 
For Loan - Business step for COB - progressive loan
35c04c014 is described below

commit 35c04c0141ee19deb2b8fdb70df909d9b04a8ef9
Author: Soma Sörös <soma.so...@dpc.hu>
AuthorDate: Thu Sep 12 17:40:03 2024 +0200

    FINERACT-1981: Interest recalculation - Recalculate Interest For Loan - 
Business step for COB - progressive loan
---
 ...dvancedPaymentScheduleTransactionProcessor.java | 117 ++-
 .../impl/ProgressiveTransactionCtx.java            |   9 +-
 .../integrationtests/BaseLoanIntegrationTest.java  | 165 ++++
 .../LoanInterestRecalculationCOBTest.java          | 847 +++++++++++++++++++--
 4 files changed, 1073 insertions(+), 65 deletions(-)

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 b08b055d2..16a854be9 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
@@ -60,6 +60,7 @@ import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.MathUtil;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
@@ -180,9 +181,11 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         ProgressiveLoanInterestScheduleModel scheduleModel = 
emiCalculator.generateModel(loanProductRelatedDetail,
                 installmentAmountInMultiplesOf, installments, mc);
 
+        ProgressiveTransactionCtx ctx = new 
ProgressiveTransactionCtx(currency, installments, charges, overpaymentHolder,
+                changedTransactionDetail, scheduleModel);
+
         for (final ChargeOrTransaction chargeOrTransaction : 
chargeOrTransactions) {
-            chargeOrTransaction.getLoanTransaction().ifPresent(loanTransaction 
-> processSingleTransaction(loanTransaction, currency,
-                    installments, charges, changedTransactionDetail, 
overpaymentHolder, scheduleModel));
+            chargeOrTransaction.getLoanTransaction().ifPresent(loanTransaction 
-> processSingleTransaction(loanTransaction, ctx));
             chargeOrTransaction.getLoanCharge()
                     .ifPresent(loanCharge -> processSingleCharge(loanCharge, 
currency, installments, disbursementDate));
         }
@@ -190,6 +193,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                 .map(ChargeOrTransaction::getLoanTransaction) //
                 .filter(Optional::isPresent) //
                 .map(Optional::get).toList();
+        recalculateInterestForDate(ThreadLocalContextUtil.getBusinessDate(), 
ctx, true);
         reprocessInstallments(disbursementDate, txs, installments, currency);
         return Pair.of(changedTransactionDetail, scheduleModel);
     }
@@ -562,11 +566,10 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
     }
 
-    private void processSingleTransaction(LoanTransaction loanTransaction, 
MonetaryCurrency currency,
-            List<LoanRepaymentScheduleInstallment> installments, 
Set<LoanCharge> charges, ChangedTransactionDetail changedTransactionDetail,
-            MoneyHolder overpaymentHolder, 
ProgressiveLoanInterestScheduleModel scheduleModel) {
-        TransactionCtx ctx = new ProgressiveTransactionCtx(currency, 
installments, charges, overpaymentHolder, changedTransactionDetail,
-                scheduleModel);
+    private void processSingleTransaction(LoanTransaction loanTransaction, 
final ProgressiveTransactionCtx ctx) {
+        final MonetaryCurrency currency = ctx.getCurrency();
+        final ChangedTransactionDetail changedTransactionDetail = 
ctx.getChangedTransactionDetail();
+
         if (loanTransaction.getId() == null) {
             processLatestTransaction(loanTransaction, ctx);
             if (loanTransaction.isInterestWaiver()) {
@@ -765,7 +768,107 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         }
     }
 
+    private List<LoanRepaymentScheduleInstallment> 
findOverdueInstallmentsBeforeDateSortedByInstallmentNumber(LocalDate 
currentDate,
+            ProgressiveTransactionCtx transactionCtx) {
+        final LocalDate fromDate = 
transactionCtx.getInstallments().get(0).getLoan().getApprovedOnDate();
+        return transactionCtx.getInstallments().stream().filter(installment -> 
!installment.getFromDate().isBefore(fromDate))
+                .filter(installment -> 
installment.getDueDate().isBefore(currentDate))
+                .filter(installment -> installment.isOverdueOn(currentDate))
+                
.sorted(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).toList();
+    }
+
+    private void recalculateInterestForDate(LocalDate currentDate, 
ProgressiveTransactionCtx ctx, boolean isLastRecalculation) {
+        if (ctx.getInstallments() != null && !ctx.getInstallments().isEmpty()
+                && 
ctx.getInstallments().get(0).getLoan().isInterestRecalculationEnabledForProduct()
+                && !ctx.getInstallments().get(0).getLoan().isNpa() && 
!ctx.getInstallments().get(0).getLoan().isChargedOff()) {
+            List<LoanRepaymentScheduleInstallment> 
overdueInstallmentsSortedByInstallmentNumber = 
findOverdueInstallmentsBeforeDateSortedByInstallmentNumber(
+                    currentDate, ctx);
+            if (!overdueInstallmentsSortedByInstallmentNumber.isEmpty()) {
+                List<LoanRepaymentScheduleInstallment> 
possibleCurrentInstallment = ctx.getInstallments().stream().filter(
+                        installment -> 
installment.getFromDate().isBefore(currentDate) && 
!installment.getDueDate().isBefore(currentDate))
+                        .toList();
+
+                // get DUE installment or last installment
+                LoanRepaymentScheduleInstallment currentInstallment = 
!possibleCurrentInstallment.isEmpty()
+                        ? possibleCurrentInstallment.get(0)
+                        : 
ctx.getInstallments().stream().max(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber))
+                                .orElseThrow();
+
+                Money overDuePrincipal = Money.zero(ctx.getCurrency());
+                for (LoanRepaymentScheduleInstallment processingInstallment : 
overdueInstallmentsSortedByInstallmentNumber) {
+                    // add and subtract outstanding principal
+                    if 
(overDuePrincipal.compareTo(Money.zero(ctx.getCurrency())) != 0) {
+                        adjustOverduePrincipalForInstallment(currentDate, 
isLastRecalculation, processingInstallment, overDuePrincipal,
+                                ctx);
+                    }
+
+                    overDuePrincipal = 
overDuePrincipal.add(processingInstallment.getPrincipalOutstanding(ctx.getCurrency()).getAmount());
+                }
+                adjustOverduePrincipalForInstallment(currentDate, 
isLastRecalculation, currentInstallment, overDuePrincipal, ctx);
+            }
+        }
+    }
+
+    private void adjustOverduePrincipalForInstallment(LocalDate currentDate, 
boolean isLastRecalculation,
+            LoanRepaymentScheduleInstallment currentInstallment, Money 
overduePrincipal, ProgressiveTransactionCtx ctx) {
+
+        LocalDate fromDate = currentInstallment.getFromDate();
+        boolean hasUpdate = false;
+
+        if 
(currentInstallment.getLoan().getLoanInterestRecalculationDetails().getRestFrequencyType().isSameAsRepayment())
 {
+            // if we have same date for fromDate & last overdue balance change 
then it meas we have the up-to-date
+            // model.
+            if (ctx.getLastOverdueBalanceChange() == null || 
fromDate.isAfter(ctx.getLastOverdueBalanceChange())) {
+                emiCalculator.addBalanceCorrection(ctx.getModel(), fromDate, 
overduePrincipal);
+                ctx.setLastOverdueBalanceChange(fromDate);
+                hasUpdate = true;
+            }
+        }
+
+        if 
(currentInstallment.getLoan().getLoanInterestRecalculationDetails().getRestFrequencyType().isDaily()
+                // if we have same date for currentDate & last overdue balance 
change then it meas we have the
+                // up-to-date model.
+                && !currentDate.equals(ctx.getLastOverdueBalanceChange())) {
+            if (ctx.getLastOverdueBalanceChange() == null || 
currentInstallment.getFromDate().isAfter(ctx.getLastOverdueBalanceChange())) {
+                // first overdue hit for installment. setting overdue balance 
correction from instalment from date.
+                emiCalculator.addBalanceCorrection(ctx.getModel(), fromDate, 
overduePrincipal);
+            } else {
+                // not the first balance correction on installment period, 
then setting overdue balance correction from
+                // last balance change's current date. previous interest 
period already has the correct balanec
+                // correction
+                emiCalculator.addBalanceCorrection(ctx.getModel(), 
ctx.getLastOverdueBalanceChange(), overduePrincipal);
+            }
+
+            // setting negative correction for the period from current date, 
expecting the overdue balance's full
+            // repayment on that day.
+            if (currentDate.isAfter(currentInstallment.getFromDate()) && 
currentDate.isBefore(currentInstallment.getDueDate())) {
+                emiCalculator.addBalanceCorrection(ctx.getModel(), 
currentDate, overduePrincipal.negated());
+                ctx.setLastOverdueBalanceChange(currentDate);
+            }
+            hasUpdate = true;
+        }
+
+        if (hasUpdate) {
+            updateInstallmentsPrincipalAndInterestByModel(ctx);
+        }
+    }
+
+    private void 
updateInstallmentsPrincipalAndInterestByModel(ProgressiveTransactionCtx ctx) {
+        ctx.getModel().repayments().forEach(rm -> {
+            LoanRepaymentScheduleInstallment installment = 
ctx.getInstallments().stream()
+                    .filter(ri -> !ri.isDownPayment() && 
Objects.equals(ri.getFromDate(), rm.getFromDate())).findFirst().orElse(null);
+            if (installment != null) {
+                installment.updatePrincipal(rm.getPrincipalDue().getAmount());
+                
installment.updateInterestCharged(rm.getInterestDue().getAmount());
+                installment.setRecalculatedInterestComponent(true);
+            }
+        });
+    }
+
     private void handleRepayment(LoanTransaction loanTransaction, 
TransactionCtx transactionCtx) {
+        if (transactionCtx instanceof ProgressiveTransactionCtx) {
+            recalculateInterestForDate(loanTransaction.getTransactionDate(), 
(ProgressiveTransactionCtx) transactionCtx, false);
+        }
         if (loanTransaction.isRepaymentLikeType() || 
loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
             loanTransaction.resetDerivedComponents();
         }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/ProgressiveTransactionCtx.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/ProgressiveTransactionCtx.java
index c1348f004..a3d7daa08 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/ProgressiveTransactionCtx.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/ProgressiveTransactionCtx.java
@@ -18,8 +18,11 @@
  */
 package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl;
 
+import java.time.LocalDate;
 import java.util.List;
 import java.util.Set;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import 
org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
@@ -28,9 +31,12 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Mon
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel;
 
+@Getter
 public class ProgressiveTransactionCtx extends TransactionCtx {
 
     private final ProgressiveLoanInterestScheduleModel model;
+    @Setter
+    private LocalDate lastOverdueBalanceChange = null;
 
     public ProgressiveTransactionCtx(MonetaryCurrency currency, 
List<LoanRepaymentScheduleInstallment> installments,
             Set<LoanCharge> charges, MoneyHolder overpaymentHolder, 
ChangedTransactionDetail changedTransactionDetail,
@@ -39,7 +45,4 @@ public class ProgressiveTransactionCtx extends TransactionCtx 
{
         this.model = model;
     }
 
-    public ProgressiveLoanInterestScheduleModel getModel() {
-        return model;
-    }
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index 15d65da86..ae7912760 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -21,6 +21,7 @@ package org.apache.fineract.integrationtests;
 import static java.lang.Boolean.FALSE;
 import static java.lang.Boolean.TRUE;
 import static 
org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType.BUSINESS_DATE;
+import static 
org.apache.fineract.integrationtests.BaseLoanIntegrationTest.TransactionProcessingStrategyCode.ADVANCED_PAYMENT_ALLOCATION_STRATEGY;
 import static 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -41,6 +42,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.UUID;
@@ -174,6 +176,24 @@ public abstract class BaseLoanIntegrationTest {
         assertEquals(paidLate, period.getTotalPaidLateForPeriod());
     }
 
+    protected static void 
validateFullyUnpaidRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer 
index, String dueDate,
+            double principalDue, double feeDue, double penaltyDue, double 
interestDue) {
+        validateRepaymentPeriod(loanDetails, index, LocalDate.parse(dueDate, 
DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH)),
+                principalDue, 0, principalDue, feeDue, 0, feeDue, penaltyDue, 
0, penaltyDue, interestDue, 0, interestDue, 0, 0);
+    }
+
+    protected static void 
validateFullyPaidRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer 
index, String dueDate,
+            double principalDue, double feeDue, double penaltyDue, double 
interestDue) {
+        validateRepaymentPeriod(loanDetails, index, LocalDate.parse(dueDate, 
DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH)),
+                principalDue, principalDue, 0, feeDue, feeDue, 0, penaltyDue, 
penaltyDue, 0, interestDue, interestDue, 0, 0, 0);
+    }
+
+    protected static void 
validateFullyPaidRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer 
index, String dueDate,
+            double principalDue, double feeDue, double penaltyDue, double 
interestDue, double paidLate) {
+        validateRepaymentPeriod(loanDetails, index, LocalDate.parse(dueDate, 
DateTimeFormatter.ofPattern(DATETIME_PATTERN, Locale.ENGLISH)),
+                principalDue, principalDue, 0, feeDue, feeDue, 0, penaltyDue, 
penaltyDue, 0, interestDue, interestDue, 0, 0, paidLate);
+    }
+
     protected static void validateRepaymentPeriod(GetLoansLoanIdResponse 
loanDetails, Integer index, LocalDate dueDate, double principalDue,
             double feeDue, double penaltyDue, double interestDue) {
         validateRepaymentPeriod(loanDetails, index, dueDate, principalDue, 0, 
principalDue, feeDue, 0, feeDue, penaltyDue, 0, penaltyDue,
@@ -217,6 +237,106 @@ public abstract class BaseLoanIntegrationTest {
         return createOnePeriod30DaysPeriodicAccrualProduct((double) 0);
     }
 
+    protected PostLoanProductsRequest create4IProgressive() {
+        return new 
PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("4I_PROGRESSIVE_",
 6))//
+                .shortName(Utils.uniqueRandomStringGenerator("", 4))//
+                .description("4 installment product - progressive")//
+                .includeInBorrowerCycle(false)//
+                .useBorrowerCycle(false)//
+                .currencyCode("EUR")//
+                .digitsAfterDecimal(2)//
+                .principal(1000.0)//
+                .minPrincipal(100.0)//
+                .maxPrincipal(10000.0)//
+                .numberOfRepayments(4)//
+                .repaymentEvery(1)//
+                .repaymentFrequencyType(RepaymentFrequencyType.MONTHS_L)//
+                .interestRatePerPeriod(10D)//
+                .minInterestRatePerPeriod(0D)//
+                .maxInterestRatePerPeriod(120D)//
+                .interestRateFrequencyType(InterestRateFrequencyType.YEARS)//
+                .isLinkedToFloatingInterestRates(false)//
+                .isLinkedToFloatingInterestRates(false)//
+                .allowVariableInstallments(false)//
+                .amortizationType(AmortizationType.EQUAL_INSTALLMENTS)//
+                .interestType(InterestType.DECLINING_BALANCE)//
+                
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)//
+                .allowPartialPeriodInterestCalcualtion(false)//
+                
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY)//
+                
.paymentAllocation(List.of(createDefaultPaymentAllocation("NEXT_INSTALLMENT")))//
+                .creditAllocation(List.of())//
+                .overdueDaysForNPA(179)//
+                .daysInMonthType(30)//
+                .daysInYearType(360)//
+                .isInterestRecalculationEnabled(true)//
+                .interestRecalculationCompoundingMethod(0)//
+                
.rescheduleStrategyMethod(RescheduleStrategyMethod.ADJUST_LAST_UNPAID_PERIOD)//
+                
.recalculationRestFrequencyType(RecalculationRestFrequencyType.SAME_AS_REPAYMENT_PERIOD)//
+                .recalculationRestFrequencyInterval(0)//
+                .isArrearsBasedOnOriginalSchedule(false)//
+                .isCompoundingToBePostedAsTransaction(false)//
+                .preClosureInterestCalculationStrategy(1)//
+                .allowCompoundingOnEod(false)//
+                .canDefineInstallmentAmount(true)//
+                .repaymentStartDateType(1)//
+                .supportedInterestRefundTypes(List.of())//
+                .charges(List.of())//
+                .principalVariationsForBorrowerCycle(List.of())//
+                .interestRateVariationsForBorrowerCycle(List.of())//
+                .numberOfRepaymentVariationsForBorrowerCycle(List.of())//
+                .accountingRule(3)//
+                .canUseForTopup(false)//
+                .fundSourceAccountId(fundSource.getAccountID().longValue())//
+                
.loanPortfolioAccountId(loansReceivableAccount.getAccountID().longValue())//
+                
.transfersInSuspenseAccountId(suspenseAccount.getAccountID().longValue())//
+                
.interestOnLoanAccountId(interestIncomeAccount.getAccountID().longValue())//
+                
.incomeFromFeeAccountId(feeIncomeAccount.getAccountID().longValue())//
+                
.incomeFromPenaltyAccountId(penaltyIncomeAccount.getAccountID().longValue())//
+                
.incomeFromRecoveryAccountId(recoveriesAccount.getAccountID().longValue())//
+                
.writeOffAccountId(writtenOffAccount.getAccountID().longValue())//
+                
.overpaymentLiabilityAccountId(overpaymentAccount.getAccountID().longValue())//
+                
.receivableInterestAccountId(interestReceivableAccount.getAccountID().longValue())//
+                
.receivableFeeAccountId(feeReceivableAccount.getAccountID().longValue())//
+                
.receivablePenaltyAccountId(penaltyReceivableAccount.getAccountID().longValue())//
+                
.goodwillCreditAccountId(goodwillExpenseAccount.getAccountID().longValue())//
+                
.incomeFromGoodwillCreditInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
+                
.incomeFromGoodwillCreditFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
+                
.incomeFromGoodwillCreditPenaltyAccountId(feeChargeOffAccount.getAccountID().longValue())//
+                
.incomeFromChargeOffInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
+                
.incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
+                
.incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue())//
+                
.chargeOffExpenseAccountId(chargeOffExpenseAccount.getAccountID().longValue())//
+                
.chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())//
+                .dateFormat(DATETIME_PATTERN)//
+                .locale("en")//
+                .enableAccrualActivityPosting(false)//
+                .multiDisburseLoan(true)//
+                .maxTrancheCount(10)//
+                .outstandingLoanBalance(10000.0)//
+                .disallowExpectedDisbursements(true)//
+                .allowApprovedDisbursedAmountsOverApplied(true)//
+                .overAppliedCalculationType("percentage")//
+                .overAppliedNumber(50)//
+                .principalThresholdForLastInstallment(50)//
+                .holdGuaranteeFunds(false)//
+                .accountMovesOutOfNPAOnlyOnArrearsCompletion(false)//
+                .allowAttributeOverrides(new AllowAttributeOverrides()//
+                        .amortizationType(true)//
+                        .interestType(true)//
+                        .transactionProcessingStrategyCode(true)//
+                        .interestCalculationPeriodType(true)//
+                        .inArrearsTolerance(true)//
+                        .repaymentEvery(true)//
+                        .graceOnPrincipalAndInterestPayment(true)//
+                        .graceOnArrearsAgeing(true)//
+                ).isEqualAmortization(false)//
+                .delinquencyBucketId(1L)//
+                .enableDownPayment(false)//
+                .enableInstallmentLevelDelinquency(false)//
+                .loanScheduleType("PROGRESSIVE")//
+                .loanScheduleProcessingType("HORIZONTAL");//
+    }
+
     // Loan product with proper accounting setup
     protected PostLoanProductsRequest 
createOnePeriod30DaysPeriodicAccrualProduct(double interestRatePerPeriod) {
         return new 
PostLoanProductsRequest().name(Utils.uniqueRandomStringGenerator("LOAN_PRODUCT_",
 6))//
@@ -700,6 +820,23 @@ public abstract class BaseLoanIntegrationTest {
         return postLoansRequest;
     }
 
+    protected PostLoansRequest applyPin4ProgressiveLoanRequest(Long clientId, 
Long loanProductId, String loanDisbursementDate,
+            Double amount, Double interestRate, int numberOfRepayments, 
Consumer<PostLoansRequest> customizer) {
+
+        PostLoansRequest postLoansRequest = new 
PostLoansRequest().clientId(clientId)
+                
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).productId(loanProductId)
+                
.expectedDisbursementDate(loanDisbursementDate).dateFormat(DATETIME_PATTERN).locale("en")
+                
.submittedOnDate(loanDisbursementDate).amortizationType(1).interestRatePerPeriod(BigDecimal.valueOf(interestRate))
+                
.numberOfRepayments(numberOfRepayments).principal(BigDecimal.valueOf(amount)).loanTermFrequency(numberOfRepayments)
+                
.repaymentEvery(1).repaymentFrequencyType(RepaymentFrequencyType.MONTHS)
+                
.loanTermFrequencyType(RepaymentFrequencyType.MONTHS).interestType(InterestType.DECLINING_BALANCE)
+                
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY).loanType("individual");
+        if (customizer != null) {
+            customizer.accept(postLoansRequest);
+        }
+        return postLoansRequest;
+    }
+
     protected PostLoansLoanIdRequest approveLoanRequest(Double amount, String 
approvalDate) {
         return new 
PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(amount)).dateFormat(DATETIME_PATTERN)
                 .approvedOnDate(approvalDate).locale("en");
@@ -726,6 +863,17 @@ public abstract class BaseLoanIntegrationTest {
         return approvedLoanResult.getLoanId();
     }
 
+    protected Long applyAndApproveProgressiveLoan(Long clientId, Long 
loanProductId, String loanDisbursementDate, Double amount,
+            Double interestRate, int numberOfRepayments, 
Consumer<PostLoansRequest> customizer) {
+        PostLoansResponse postLoansResponse = 
loanTransactionHelper.applyLoan(applyPin4ProgressiveLoanRequest(clientId, 
loanProductId,
+                loanDisbursementDate, amount, interestRate, 
numberOfRepayments, customizer));
+
+        PostLoansLoanIdResponse approvedLoanResult = 
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
+                approveLoanRequest(amount, loanDisbursementDate));
+
+        return approvedLoanResult.getLoanId();
+    }
+
     protected Long applyAndApproveLoan(Long clientId, Long loanProductId, 
String loanDisbursementDate, Double amount) {
         return applyAndApproveLoan(clientId, loanProductId, 
loanDisbursementDate, amount, 1);
     }
@@ -1039,11 +1187,18 @@ public abstract class BaseLoanIntegrationTest {
     public static class RepaymentFrequencyType {
 
         public static final Integer MONTHS = 2;
+        public static final Long MONTHS_L = 2L;
         public static final String MONTHS_STRING = "MONTHS";
         public static final Integer DAYS = 0;
         public static final String DAYS_STRING = "DAYS";
     }
 
+    public static class RecalculationRestFrequencyType {
+
+        public static final Integer SAME_AS_REPAYMENT_PERIOD = 1;
+        public static final Integer DAILY = 2;
+    }
+
     public static class InterestCalculationPeriodType {
 
         public static final Integer DAILY = 0;
@@ -1056,6 +1211,16 @@ public abstract class BaseLoanIntegrationTest {
         public static final Integer YEARS = 3;
     }
 
+    public static class TransactionProcessingStrategyCode {
+
+        public static final String ADVANCED_PAYMENT_ALLOCATION_STRATEGY = 
"advanced-payment-allocation-strategy";
+    }
+
+    public static class RescheduleStrategyMethod {
+
+        public static final Integer ADJUST_LAST_UNPAID_PERIOD = 4;
+    }
+
     public static class DaysInYearType {
 
         public static final Integer INVALID = 0;
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java
index dd526c11e..5722592cc 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRecalculationCOBTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.integrationtests;
 
+import static 
org.apache.fineract.integrationtests.BaseLoanIntegrationTest.InterestCalculationPeriodType.SAME_AS_REPAYMENT_PERIOD;
 import static 
org.apache.fineract.integrationtests.BaseLoanIntegrationTest.RepaymentFrequencyType.DAYS;
 import static 
org.apache.fineract.integrationtests.BaseLoanIntegrationTest.RepaymentFrequencyType.MONTHS;
 
@@ -28,19 +29,25 @@ import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Stream;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.PostClientsResponse;
 import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
 import org.apache.fineract.integrationtests.common.BusinessStepHelper;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
 import org.apache.fineract.integrationtests.common.Utils;
-import 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
@@ -74,35 +81,373 @@ public class LoanInterestRecalculationCOBTest extends 
BaseLoanIntegrationTest {
                 "EXTERNAL_ASSET_OWNER_TRANSFER", "CHECK_DUE_INSTALLMENTS", 
"ACCRUAL_ACTIVITY_POSTING", "LOAN_INTEREST_RECALCULATION");
     }
 
+    private void logLoanDetails(GetLoansLoanIdResponse loanDetails) {
+        log.info("index, dueDate, principal, fee, penalty, interest");
+        Assertions.assertNotNull(loanDetails.getRepaymentSchedule());
+        
Assertions.assertNotNull(loanDetails.getRepaymentSchedule().getPeriods());
+        loanDetails.getRepaymentSchedule().getPeriods()
+                .forEach(period -> log.info("{}, \"{}\", {}, {}, {}, {}", 
period.getPeriod(),
+                        DateTimeFormatter.ofPattern(DATETIME_PATTERN, 
Locale.ENGLISH).format(Objects.requireNonNull(period.getDueDate())),
+                        period.getPrincipalDue(), period.getFeeChargesDue(), 
period.getPenaltyChargesDue(), period.getInterestDue()));
+    }
+
     @Test
-    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnProgressiveLoanCOB()
 {
+    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepDaily()
 {
         AtomicReference<Long> loanIdRef = new AtomicReference<>();
         runAt("1 January 2023", () -> {
-            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(
-                    
createOnePeriod30DaysPeriodicAccrualProductWithAdvancedPaymentAllocationAndInterestRecalculation(16.0,
 4)
-                            
.maxInterestRatePerPeriod(120.0).maxPrincipal(10000.0));
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    
.createLoanProduct(create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY)
 //
+            );
 
-            Long loanId = applyAndApproveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 4,
-                    postLoansRequest -> postLoansRequest.loanTermFrequency(4)//
-                            .loanTermFrequencyType(MONTHS)//
-                            
.interestRatePerPeriod(BigDecimal.valueOf(120.0)).interestCalculationPeriodType(DAYS)//
-                            
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)//
-                            .repaymentEvery(1)//
-                            .repaymentFrequencyType(MONTHS)//
-                            
.principal(BigDecimal.valueOf(8000.0)).maxOutstandingLoanBalance(BigDecimal.valueOf(100000.0)));
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
+                    4, null);
             loanIdRef.set(loanId);
 
             disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
 
             GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+
+        });
+
+        runAt("1 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+        });
+        runAt("2 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.05, 0.0, 0.0, 50.79);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2025.55, 0.0, 0.0, 16.88);
+        });
+        runAt("20 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
 
-            loanDetails.getRepaymentSchedule().getPeriods().forEach(p -> 
log.info("validateRepaymentPeriod before: {} {} {}", p.getPeriod(),
-                    p.getPrincipalOriginalDue(), 
p.getInterestOriginalDue()));//
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1980.46, 0.0, 0.0, 61.38);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.14, 0.0, 0.0, 33.7);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2036.23, 0.0, 0.0, 16.97);
 
-            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 
1700.66, 0.0, 0.0, 815.34);
-            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 
1936.12, 0.0, 0.0, 579.88);
-            validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 
2071.31, 0.0, 0.0, 444.69);
-            validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 5, 1), 
2291.91, 0.0, 0.0, 226.05);
+        });
+        runAt("1 March 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+
+        });
+        payoffOnDateAndVerifyStatus("1 February 2023", loanIdRef.get());
+    }
+
+    private void payoffOnDateAndVerifyStatus(final String date, final Long 
loanId) {
+        runAt(date, () -> {
+            HashMap prepayAmount = 
loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, 
loanId.intValue());
+            Assertions.assertNotNull(prepayAmount);
+            Float amount = (Float) prepayAmount.get("amount");
+            PostLoansLoanIdTransactionsResponse response = 
loanTransactionHelper.makeLoanRepayment(date, amount, loanId.intValue());
+            Assertions.assertNotNull(response);
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            Assertions.assertNotNull(loanDetails);
+            Assertions.assertNotNull(loanDetails.getStatus());
+            log.info("Loan status {}", loanDetails.getStatus().getId());
+            Assertions.assertTrue(Stream.of(600).anyMatch(v -> 
loanDetails.getStatus().getId().intValue() == v));
+        });
+    }
+
+    @Test
+    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepLatePayPayOnDuePayLatePayOnDateDaily()
 {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2023", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    
.createLoanProduct(create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY)
 //
+            );
+
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
+                    4, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+
+        });
+        runAt("20 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1980.46, 0.0, 0.0, 61.38);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.14, 0.0, 0.0, 33.7);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2036.23, 0.0, 0.0, 16.97);
+
+        });
+        runAt("1 March 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+
+            loanTransactionHelper.makeLoanRepayment("20 February 2023", 
2041.84f, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment("01 March 2023", 2041.84f, 
loanId.intValue());
+
+            loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
+            validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 
1980.46, 0.0, 0.0, 61.38);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.14, 0.0, 0.0, 33.7);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2036.23, 0.0, 0.0, 16.97);
+        });
+        runAt("10 April 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
+            validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 
1980.46, 0.0, 0.0, 61.38);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.14, 0.0, 0.0, 33.7);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2036.23, 0.0, 0.0, 21.99);
+
+            loanTransactionHelper.makeLoanRepayment("10 April 2023", 2041.84f, 
loanId.intValue());
+
+            loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
+            validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 
1980.46, 0.0, 0.0, 61.38);
+            validateFullyPaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 
2008.14, 0.0, 0.0, 33.7, 2041.84);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2036.23, 0.0, 0.0, 21.99);
+        });
+        runAt("20 April 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
+            validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 
1980.46, 0.0, 0.0, 61.38);
+            validateFullyPaidRepaymentPeriod(loanDetails, 3, "01 April 2023", 
2008.14, 0.0, 0.0, 33.7, 2041.84);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2036.23, 0.0, 0.0, 21.99);
+        });
+        payoffOnDateAndVerifyStatus("20 April 2023", loanIdRef.get());
+    }
+
+    @Test
+    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepLatePartialRepaymentDailyInterestCalculation()
 {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2023", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    
.createLoanProduct(create4IProgressive().recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY)
 //
+            );
+
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
+                    4, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+
+        });
+        runAt("1 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            loanTransactionHelper.makeLoanRepayment("01 February 2023", 
2041.84f, loanId.intValue());
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+
+        });
+        runAt("10 March 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2003.41, 0.0, 0.0, 38.43);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2029.79, 0.0, 0.0, 16.91);
+
+            loanTransactionHelper.makeLoanRepayment("10 March 2023", 500.00f, 
loanId.intValue());
+
+            loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 
1991.63, 500.0, 1491.63, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.21,
+                    0, 50.21, 0, 500.0);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2003.41, 0.0, 0.0, 38.43);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2029.79, 0.0, 0.0, 16.91);
+
+            loanTransactionHelper.makeLoanRepayment("10 March 2023", 541.84f, 
loanId.intValue());
+
+            loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 
1991.63, 1041.84, 949.79, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.21,
+                    0, 50.21, 0, 1041.84);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2003.41, 0.0, 0.0, 38.43);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2029.79, 0.0, 0.0, 16.91);
+        });
+        runAt("20 March 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 
1991.63, 1041.84, 949.79, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.21,
+                    0, 50.21, 0, 1041.84);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2000.85, 0.0, 0.0, 40.99);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2032.35, 0.0, 0.0, 16.94);
+
+            loanTransactionHelper.makeLoanRepayment("20 March 2023", 1000f, 
loanId.intValue());
+
+            loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 
1991.63, 0.0, 0.0, 50.21, 2041.84);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2000.85, 0.0, 0.0, 40.99);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2032.35, 0.0, 0.0, 16.94);
+        });
+        payoffOnDateAndVerifyStatus("1 April 2023", loanIdRef.get());
+    }
+
+    @Test
+    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStep()
 {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2023", () -> {
+            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive());
+
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
+                    4, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+
+        });
+
+        runAt("1 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+        });
+        runAt("2 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+        });
+        runAt("20 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
         });
         runAt("2 March 2023", () -> {
             Long loanId = loanIdRef.get();
@@ -110,16 +455,225 @@ public class LoanInterestRecalculationCOBTest extends 
BaseLoanIntegrationTest {
             inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
 
             GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2074.49, 0.0, 0.0, 17.29);
+        });
+        payoffOnDateAndVerifyStatus("1 February 2023", loanIdRef.get());
+    }
+
+    @Test
+    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepLatePaidPaidOnTimeLatePaidPayoffOnTime()
 {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2023", () -> {
+            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive());
+
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
+                    4, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
 
-            loanDetails.getRepaymentSchedule().getPeriods().forEach(p -> 
log.info("validateRepaymentPeriod after: {} {} {}", p.getPeriod(),
-                    p.getPrincipalOriginalDue(), 
p.getInterestOriginalDue()));//
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
 
-            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 
1700.66, 0.0, 0.0, 815.34);
-            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 
1936.12, 0.0, 0.0, 579.88);
-            validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 
2071.31, 0.0, 0.0, 444.69);
-            validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 5, 1), 
2291.91, 0.0, 0.0, 226.05);
         });
 
+        runAt("2 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+        });
+        runAt("15 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+
+            loanTransactionHelper.makeLoanRepayment("15 February 2023", 
500.0F, loanId.intValue());
+
+            loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 
1975.17, 500, 1475.17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 66.67, 0,
+                    66.67, 0, 500);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+
+            loanTransactionHelper.makeLoanRepayment("15 February 2023", 500f, 
loanId.intValue());
+
+            loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 
1975.17, 1000, 975.17, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 66.67, 0,
+                    66.67, 0, 1000);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+
+        });
+        runAt("20 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            loanTransactionHelper.makeLoanRepayment("20 February 2023", 
1041.84f, loanId.intValue());
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+
+        });
+        runAt("1 March 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            loanTransactionHelper.makeLoanRepayment("01 March 2023", 2041.84f, 
loanId.intValue());
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
+            validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 
1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+        });
+        runAt("2 April 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 
1975.17, 1975.17, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 66.67,
+                    66.67, 0, 0, 2041.84);
+            validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 
1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 33.75);
+        });
+        runAt("1 May 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            loanTransactionHelper.makeLoanRepayment("15 April 2023", 2041.84f, 
loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment("01 May 2023", 2075.32f, 
loanId.intValue());
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 
1975.17, 1975.17, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 66.67,
+                    66.67, 0, 0, 2041.84);
+            validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 
1975.17, 0.0, 0.0, 66.67);
+            validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 
2008.09, 2008.09, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 33.75,
+                    33.75, 0, 0, 2041.84);
+            validateFullyPaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 33.75);
+        });
+    }
+
+    @Test
+    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanCOBStepOnePaid()
 {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2023", () -> {
+            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive());
+
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
+                    4, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+
+        });
+
+        runAt("1 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            loanTransactionHelper.makeLoanRepayment("01 February 2023", 
2041.84f, loanId.intValue());
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+        });
+        runAt("1 March 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+        });
+        runAt("2 March 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+        });
+        runAt("20 March 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+        });
+        payoffOnDateAndVerifyStatus("1 March 2023", loanIdRef.get());
     }
 
     @Test
@@ -162,67 +716,104 @@ public class LoanInterestRecalculationCOBTest extends 
BaseLoanIntegrationTest {
             validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 
4216.94, 0.0, 4216.94, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 403.23,
                     0.0, 403.23, 0.0, 0.0);
         });
-
+        payoffOnDateAndVerifyStatus("1 March 2023", loanIdRef.get());
     }
 
     @Test
     public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnProgressiveLoanJob()
 {
         AtomicReference<Long> loanIdRef = new AtomicReference<>();
         runAt("1 January 2023", () -> {
-            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(
-                    
createOnePeriod30DaysPeriodicAccrualProductWithAdvancedPaymentAllocationAndInterestRecalculation(16.0,
 4)
-                            
.maxInterestRatePerPeriod(120.0).maxPrincipal(10000.0));
+            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive());
 
-            Long loanId = applyAndApproveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 4,
-                    postLoansRequest -> postLoansRequest.loanTermFrequency(4)//
-                            .loanTermFrequencyType(MONTHS)//
-                            
.interestRatePerPeriod(BigDecimal.valueOf(120.0)).interestCalculationPeriodType(DAYS)//
-                            
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)//
-                            .repaymentEvery(1)//
-                            .repaymentFrequencyType(MONTHS)//
-                            
.principal(BigDecimal.valueOf(8000.0)).maxOutstandingLoanBalance(BigDecimal.valueOf(100000.0)));
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
+                    4, null);
             loanIdRef.set(loanId);
 
             disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
 
             GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
 
-            loanDetails.getRepaymentSchedule().getPeriods().forEach(p -> 
log.info("validateRepaymentPeriod before: {} {} {}", p.getPeriod(),
-                    p.getPrincipalOriginalDue(), 
p.getInterestOriginalDue()));//
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
 
-            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 
1700.66, 0.0, 0.0, 815.34);
-            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 
1936.12, 0.0, 0.0, 579.88);
-            validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 
2071.31, 0.0, 0.0, 444.69);
-            validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 5, 1), 
2291.91, 0.0, 0.0, 226.05);
         });
-        runAt("2 March 2023", () -> {
+
+        runAt("1 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            schedulerJobHelper.executeAndAwaitJob("Update Loan Arrears 
Ageing");
+            schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For 
Loans");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+        });
+        runAt("2 February 2023", () -> {
             Long loanId = loanIdRef.get();
 
             schedulerJobHelper.executeAndAwaitJob("Update Loan Arrears 
Ageing");
             schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For 
Loans");
 
             GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
 
-            loanDetails.getRepaymentSchedule().getPeriods().forEach(p -> 
log.info("validateRepaymentPeriod after: {} {} {}", p.getPeriod(),
-                    p.getPrincipalOriginalDue(), 
p.getInterestOriginalDue()));//
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+        });
+        runAt("20 February 2023", () -> {
+            Long loanId = loanIdRef.get();
 
-            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 2, 1), 
1700.66, 0.0, 0.0, 815.34);
-            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 
1936.12, 0.0, 0.0, 579.88);
-            validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 4, 1), 
2071.31, 0.0, 0.0, 444.69);
-            validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 5, 1), 
2291.91, 0.0, 0.0, 226.05);
+            schedulerJobHelper.executeAndAwaitJob("Update Loan Arrears 
Ageing");
+            schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For 
Loans");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
         });
+        runAt("2 March 2023", () -> {
+            Long loanId = loanIdRef.get();
 
+            schedulerJobHelper.executeAndAwaitJob("Update Loan Arrears 
Ageing");
+            schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For 
Loans");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2074.49, 0.0, 0.0, 17.29);
+        });
+        payoffOnDateAndVerifyStatus("1 February 2023", loanIdRef.get());
     }
 
     @Test
     public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnCumulativeLoanJob()
 {
         AtomicReference<Long> loanIdRef = new AtomicReference<>();
         runAt("1 January 2023", () -> {
-            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(10.0)
-                    .isInterestRecalculationEnabled(true)//
-                    
.maxPrincipal(10000.0).minNumberOfRepayments(1).rescheduleStrategyMethod(1).recalculationRestFrequencyType(MONTHS)
-                    
.recalculationRestFrequencyInterval(1).recalculationCompoundingFrequencyType(MONTHS)
-                    
.recalculationCompoundingFrequencyInterval(30).interestRecalculationCompoundingMethod(1));
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    
.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(10.0).isInterestRecalculationEnabled(true)//
+                            .maxPrincipal(10000.0) //
+                            .minNumberOfRepayments(1) //
+                            .rescheduleStrategyMethod(1) //
+                            .recalculationRestFrequencyType(MONTHS) //
+                            .recalculationRestFrequencyInterval(1) //
+                            .recalculationCompoundingFrequencyType(MONTHS) //
+                            .recalculationCompoundingFrequencyInterval(30) //
+                            .interestRecalculationCompoundingMethod(1)); //
 
             Long loanId = applyAndApproveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 2,
                     postLoansRequest -> postLoansRequest.loanTermFrequency(2)//
@@ -255,7 +846,153 @@ public class LoanInterestRecalculationCOBTest extends 
BaseLoanIntegrationTest {
             validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 3, 1), 
4216.94, 0.0, 4216.94, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 403.23,
                     0.0, 403.23, 0.0, 0.0);
         });
+        payoffOnDateAndVerifyStatus("1 March 2023", loanIdRef.get());
+
+    }
+
+    @Test
+    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOn4IProgressiveLoanMultiOverdueSingleRepayment()
 {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2023", () -> {
+            PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive());
+
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 10.0,
+                    4, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1991.63, 0.0, 0.0, 50.21);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.23, 0.0, 0.0, 33.61);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2024.97, 0.0, 0.0, 16.87);
+
+        });
+        runAt("1 March 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            loanTransactionHelper.makeLoanRepayment("01 March 2023", 4083.68f, 
loanId.intValue());
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyPaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1975.17, 0.0, 0.0, 66.67, 2041.84);
+            validateFullyPaidRepaymentPeriod(loanDetails, 2, "01 March 2023", 
1975.17, 0.0, 0.0, 66.67);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2008.09, 0.0, 0.0, 33.75);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2041.57, 0.0, 0.0, 17.01);
+        });
+        payoffOnDateAndVerifyStatus("1 April 2023", loanIdRef.get());
+
+    }
+
+    @Test
+    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnCumulativeLoanJobSameAsRepaymentPeriod()
 {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2023", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    
.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(10.0).isInterestRecalculationEnabled(true)//
+                            .maxPrincipal(10000.0) //
+                            .minNumberOfRepayments(1) //
+                            .maxNumberOfRepayments(10) //
+                            .rescheduleStrategyMethod(1) //
+                            .daysInYearType(360) //
+                            .daysInMonthType(30) //
+                            
.recalculationRestFrequencyType(SAME_AS_REPAYMENT_PERIOD) //
+                            .recalculationRestFrequencyInterval(1) //
+                            .recalculationCompoundingFrequencyType(1) //
+                            .recalculationCompoundingFrequencyInterval(1) //
+                            .interestRecalculationCompoundingMethod(0)); //
+
+            Long loanId = applyAndApproveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 2,
+                    postLoansRequest -> postLoansRequest.loanTermFrequency(4)//
+                            .loanTermFrequencyType(MONTHS)//
+                            
.numberOfRepayments(4).interestRatePerPeriod(BigDecimal.valueOf(10.0)).interestCalculationPeriodType(DAYS)//
+                            .repaymentEvery(1)//
+                            .repaymentFrequencyType(MONTHS)//
+                            
.principal(BigDecimal.valueOf(8000.0)).maxOutstandingLoanBalance(BigDecimal.valueOf(100000.0)));
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1724.0, 0.0, 0.0, 800.0);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1896.4, 0.0, 0.0, 627.6);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2086.04, 0.0, 0.0, 437.96);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2293.56, 0.0, 0.0, 229.36);
+        });
+        runAt("8 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            schedulerJobHelper.executeAndAwaitJob("Update Loan Arrears 
Ageing");
+            schedulerJobHelper.executeAndAwaitJob("Recalculate Interest For 
Loans");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1724.0, 0.0, 0.0, 800.0);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1724.0, 0.0, 0.0, 800.0);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2068.8, 0.0, 0.0, 455.2);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2483.2, 0.0, 0.0, 248.32);
+        });
+        payoffOnDateAndVerifyStatus("1 May 2023", loanIdRef.get());
+    }
 
+    @Test
+    public void 
verifyLoanInstallmentRecalculatedIfThereIsOverdueInstallmentOnCumulativeLoanCOBSameAsRepaymentPeriod()
 {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2023", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    
.createLoanProduct(createOnePeriod30DaysPeriodicAccrualProduct(10.0).isInterestRecalculationEnabled(true)//
+                            .maxPrincipal(10000.0) //
+                            .minNumberOfRepayments(1) //
+                            .maxNumberOfRepayments(10) //
+                            .rescheduleStrategyMethod(1) //
+                            .daysInYearType(360) //
+                            .daysInMonthType(30) //
+                            
.recalculationRestFrequencyType(SAME_AS_REPAYMENT_PERIOD) //
+                            .recalculationRestFrequencyInterval(1) //
+                            .recalculationCompoundingFrequencyType(1) //
+                            .recalculationCompoundingFrequencyInterval(1) //
+                            .interestRecalculationCompoundingMethod(0)); //
+
+            Long loanId = applyAndApproveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2023", 8000.0, 2,
+                    postLoansRequest -> postLoansRequest.loanTermFrequency(4)//
+                            .loanTermFrequencyType(MONTHS)//
+                            
.numberOfRepayments(4).interestRatePerPeriod(BigDecimal.valueOf(10.0)).interestCalculationPeriodType(DAYS)//
+                            .repaymentEvery(1)//
+                            .repaymentFrequencyType(MONTHS)//
+                            
.principal(BigDecimal.valueOf(8000.0)).maxOutstandingLoanBalance(BigDecimal.valueOf(100000.0)));
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(8000), "1 January 2023");
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1724.0, 0.0, 0.0, 800.0);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1896.4, 0.0, 0.0, 627.6);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2086.04, 0.0, 0.0, 437.96);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2293.56, 0.0, 0.0, 229.36);
+        });
+        runAt("8 February 2023", () -> {
+            Long loanId = loanIdRef.get();
+
+            inlineLoanCOBHelper.executeInlineCOB(List.of(loanId));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            logLoanDetails(loanDetails);
+
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 1, "01 February 
2023", 1724.0, 0.0, 0.0, 800.0);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 2, "01 March 
2023", 1724.0, 0.0, 0.0, 800.0);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 3, "01 April 
2023", 2068.8, 0.0, 0.0, 455.2);
+            validateFullyUnpaidRepaymentPeriod(loanDetails, 4, "01 May 2023", 
2483.2, 0.0, 0.0, 248.32);
+        });
+        payoffOnDateAndVerifyStatus("1 May 2023", loanIdRef.get());
     }
 
 }

Reply via email to