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 6286d1f82 FINERACT-2107: Interest Refund - Transaction Amount 
Recalculation
6286d1f82 is described below

commit 6286d1f8211dd9b60a1e372b48f1f060f089d25b
Author: Soma Sörös <[email protected]>
AuthorDate: Thu Nov 7 18:18:11 2024 +0100

    FINERACT-2107: Interest Refund - Transaction Amount Recalculation
---
 .../test/resources/features/LoanRepayment.feature  |   2 +-
 .../portfolio/loanaccount/domain/Loan.java         |   6 +
 .../loanaccount/domain/LoanTransaction.java        |   4 +
 .../loanaccount/service/InterestRefundService.java |  14 ++-
 ...dvancedPaymentScheduleTransactionProcessor.java |  28 ++++-
 .../impl/ProgressiveTransactionCtx.java            |   7 ++
 .../portfolio/loanproduct/calc/EMICalculator.java  |   2 +
 .../loanproduct/calc/ProgressiveEMICalculator.java |  18 +++
 ...cedPaymentScheduleTransactionProcessorTest.java |   2 +-
 .../domain/LoanAccountDomainServiceJpa.java        |  24 ++--
 .../service/InterestRefundServiceDelegate.java     |   2 +
 .../ProgressiveLoanInterestRefundServiceImpl.java  | 116 +++++++++++--------
 .../starter/LoanAccountAutoStarter.java            |   7 +-
 .../integrationtests/LoanInterestRefundTest.java   | 127 ++++++++++++++++++---
 14 files changed, 280 insertions(+), 79 deletions(-)

diff --git 
a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature 
b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
index 81fe59167..5e19a7c79 100644
--- 
a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
+++ 
b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
@@ -3969,7 +3969,7 @@ Feature: LoanRepayment
       | 15 January 2024  | Merchant Issued Refund | 50.0   | 48.9      | 1.1   
   | 0.0  | 0.0       | 126.1        | false    |
       | 15 January 2024  | Interest Refund        | 0.29   | 0.29      | 0.0   
   | 0.0  | 0.0       | 125.81       | false    |
       | 16 January 2024  | Payout Refund          | 50.0   | 49.95     | 0.05  
   | 0.0  | 0.0       | 75.86        | false    |
-      | 16 January 2024  | Interest Refund        | 0.31   | 0.31      | 0.0   
   | 0.0  | 0.0       | 75.55        | false    |
+      | 16 January 2024  | Interest Refund        | 0.3   | 0.3      | 0.0     
 | 0.0  | 0.0       | 75.56        | false    |
     Then In Loan Transactions the "4"th Transaction has relationship 
type=RELATED with the "3"th Transaction
     Then In Loan Transactions the "6"th Transaction has relationship 
type=RELATED with the "5"th Transaction
     When Customer undo "1"th "Merchant Issued Refund" transaction made on "15 
January 2024"
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index e039e505d..4ba2c3b24 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -141,6 +141,7 @@ import 
org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCom
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
 import 
org.apache.fineract.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
+import 
org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
 import 
org.apache.fineract.portfolio.loanproduct.domain.RecalculationFrequencyType;
 import org.apache.fineract.portfolio.loanproduct.domain.RepaymentStartDateType;
 import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
@@ -5452,6 +5453,11 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom<Long> {
         }
     }
 
+    public List<LoanTransactionType> 
getSupportedInterestRefundTransactionTypes() {
+        return 
getLoanProductRelatedDetail().getSupportedInterestRefundTypes().stream()
+                
.map(LoanSupportedInterestRefundTypes::getTransactionType).toList();
+    }
+
     public LoanTransaction getLastUserTransaction() {
         return getLoanTransactions().stream() //
                 .filter(LoanTransaction::isNotReversed) //
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
index 9f14dd045..98b5166c8 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
@@ -1052,6 +1052,10 @@ public class LoanTransaction extends 
AbstractAuditableWithUTCDateTimeCustom<Long
         return getTypeOf().isInterestRefund();
     }
 
+    public void updateAmount(BigDecimal bigDecimal) {
+        this.amount = bigDecimal;
+    }
+
     // TODO missing hashCode(), equals(Object obj), but probably OK as long as
     // this is never stored in a Collection.
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundService.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundService.java
index 266d025bf..ba27cc078 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundService.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundService.java
@@ -18,15 +18,23 @@
  */
 package org.apache.fineract.portfolio.loanaccount.service;
 
-import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.List;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
 
 public interface InterestRefundService {
 
     boolean canHandle(Loan loan);
 
-    BigDecimal calculateInterestRefundAmount(Long loanId, BigDecimal 
relatedRefundTransactionAmount,
-            LocalDate relatedRefundTransactionDate);
+    @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+    Money 
totalInterestByTransactions(LoanRepaymentScheduleTransactionProcessor 
processor, Long loanId,
+            LocalDate relatedRefundTransactionDate, List<LoanTransaction> 
newTransactions, List<Long> oldTransactionIds);
 
+    Money getTotalInterestRefunded(List<LoanTransaction> loanTransactions, 
MonetaryCurrency currency);
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index ab321b70c..dc35c20c3 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
@@ -57,6 +57,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.NotImplementedException;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.tuple.Pair;
+import 
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
@@ -85,6 +86,7 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.Tra
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.PeriodDueDetails;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
+import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService;
 import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
 import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
 import 
org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType;
@@ -102,6 +104,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
     public static final String ADVANCED_PAYMENT_ALLOCATION_STRATEGY_NAME = 
"Advanced payment allocation strategy";
 
     public final EMICalculator emiCalculator;
+    public final InterestRefundService interestRefundService;
 
     @Override
     public String getCode() {
@@ -196,6 +199,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                 LoanTransaction transaction = 
changeOperation.getLoanTransaction().get();
                 processSingleTransaction(transaction, ctx);
                 transaction = 
getProcessedTransaction(changedTransactionDetail, transaction);
+                ctx.getAlreadyProcessedTransactions().add(transaction);
                 if (transaction.isOverPaid() && 
transaction.isRepaymentLikeType()) { // TODO CREDIT, DEBIT
                     overpaidTransactions.add(transaction);
                 }
@@ -268,9 +272,10 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
             case CHARGEBACK -> handleChargeback(loanTransaction, ctx);
             case CREDIT_BALANCE_REFUND ->
                 handleCreditBalanceRefund(loanTransaction, ctx.getCurrency(), 
ctx.getInstallments(), ctx.getOverpaymentHolder());
-            case INTEREST_REFUND, REPAYMENT, MERCHANT_ISSUED_REFUND, 
PAYOUT_REFUND, GOODWILL_CREDIT, CHARGE_REFUND, CHARGE_ADJUSTMENT,
-                    DOWN_PAYMENT, WAIVE_INTEREST, RECOVERY_REPAYMENT, 
INTEREST_PAYMENT_WAIVER ->
+            case REPAYMENT, MERCHANT_ISSUED_REFUND, PAYOUT_REFUND, 
GOODWILL_CREDIT, CHARGE_REFUND, CHARGE_ADJUSTMENT, DOWN_PAYMENT,
+                    WAIVE_INTEREST, RECOVERY_REPAYMENT, 
INTEREST_PAYMENT_WAIVER ->
                 handleRepayment(loanTransaction, ctx);
+            case INTEREST_REFUND -> handleInterestRefund(loanTransaction, ctx);
             case CHARGE_OFF -> handleChargeOff(loanTransaction, ctx);
             case CHARGE_PAYMENT -> handleChargePayment(loanTransaction, ctx);
             case WAIVE_CHARGES -> log.debug("WAIVE_CHARGES transaction will 
not be processed.");
@@ -284,6 +289,25 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         }
     }
 
+    private void handleInterestRefund(LoanTransaction loanTransaction, 
TransactionCtx ctx) {
+
+        if (ctx instanceof ProgressiveTransactionCtx progCtx) {
+            Money interestBeforeRefund = 
emiCalculator.getSumOfDueInterestsOnDate(progCtx.getModel(), 
loanTransaction.getDateOf());
+            List<Long> unmodifiedTransactionIds = 
progCtx.getAlreadyProcessedTransactions().stream().filter(LoanTransaction::isNotReversed)
+                    .map(AbstractPersistableCustom::getId).toList();
+            List<LoanTransaction> modifiedTransactions = new 
ArrayList<>(progCtx.getAlreadyProcessedTransactions().stream()
+                    .filter(LoanTransaction::isNotReversed).filter(tr -> 
tr.getId() == null).toList());
+            if (!modifiedTransactions.isEmpty()) {
+                Money interestAfterRefund = 
interestRefundService.totalInterestByTransactions(this, 
loanTransaction.getLoan().getId(),
+                        loanTransaction.getDateOf(), modifiedTransactions, 
unmodifiedTransactionIds);
+                Money newAmount = 
interestBeforeRefund.minus(progCtx.getSumOfInterestRefundAmount()).minus(interestAfterRefund);
+                loanTransaction.updateAmount(newAmount.getAmount());
+            }
+            
progCtx.setSumOfInterestRefundAmount(progCtx.getSumOfInterestRefundAmount().add(loanTransaction.getAmount()));
+        }
+        handleRepayment(loanTransaction, ctx);
+    }
+
     private void handleReAmortization(LoanTransaction loanTransaction, 
TransactionCtx transactionCtx) {
         LocalDate transactionDate = loanTransaction.getTransactionDate();
         List<LoanRepaymentScheduleInstallment> previousInstallments = 
transactionCtx.getInstallments().stream() //
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 05fe590ff..833311fd2 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
@@ -19,14 +19,17 @@
 package 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl;
 
 import java.time.LocalDate;
+import java.util.ArrayList;
 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.organisation.monetary.domain.Money;
 import 
org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel;
@@ -37,11 +40,15 @@ public class ProgressiveTransactionCtx extends 
TransactionCtx {
     private final ProgressiveLoanInterestScheduleModel model;
     @Setter
     private LocalDate lastOverdueBalanceChange = null;
+    private List<LoanTransaction> alreadyProcessedTransactions = new 
ArrayList<>();
+    @Setter
+    private Money sumOfInterestRefundAmount;
 
     public ProgressiveTransactionCtx(MonetaryCurrency currency, 
List<LoanRepaymentScheduleInstallment> installments,
             Set<LoanCharge> charges, MoneyHolder overpaymentHolder, 
ChangedTransactionDetail changedTransactionDetail,
             ProgressiveLoanInterestScheduleModel model) {
         super(currency, installments, charges, overpaymentHolder, 
changedTransactionDetail);
+        sumOfInterestRefundAmount = model.getZero();
         this.model = model;
     }
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java
index 0f9221174..f701cdfca 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java
@@ -62,4 +62,6 @@ public interface EMICalculator {
             LocalDate targetDate);
 
     OutstandingDetails 
getOutstandingAmountsTillDate(ProgressiveLoanInterestScheduleModel model, 
LocalDate targetDate);
+
+    Money getSumOfDueInterestsOnDate(ProgressiveLoanInterestScheduleModel 
scheduleModel, LocalDate subjectDate);
 }
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 e33c2fec8..e9d21cf85 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
@@ -716,4 +716,22 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
 
         return new ProgressiveLoanInterestScheduleModel(repaymentModels, 
loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
     }
+
+    /**
+     * Calculates the sum of due interests on interest periods.
+     *
+     * @param scheduleModel
+     *            schedule model
+     * @param subjectDate
+     *            the date to calculate the interest for.
+     * @return sum of due interests
+     */
+    @Override
+    public Money 
getSumOfDueInterestsOnDate(ProgressiveLoanInterestScheduleModel scheduleModel, 
LocalDate subjectDate) {
+        return 
scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueDate) //
+                .map(repaymentPeriodDueDate -> getDueAmounts(scheduleModel, 
repaymentPeriodDueDate, subjectDate) //
+                        .getDueInterest()) //
+                .reduce(scheduleModel.getZero(), Money::add); //
+    }
+
 }
diff --git 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java
 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java
index d04ba491e..cb1c285fd 100644
--- 
a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java
+++ 
b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessorTest.java
@@ -109,7 +109,7 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
 
     @BeforeEach
     public void setUp() {
-        underTest = new 
AdvancedPaymentScheduleTransactionProcessor(emiCalculator);
+        underTest = new 
AdvancedPaymentScheduleTransactionProcessor(emiCalculator, null);
 
         ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, 
"default", "Default", "Asia/Kolkata", null));
         ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index dcbba6ed0..af9617b25 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -34,6 +34,7 @@ import 
org.apache.fineract.infrastructure.configuration.domain.ConfigurationDoma
 import org.apache.fineract.infrastructure.core.data.ApiParameterError;
 import 
org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
 import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import 
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
 import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import 
org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
 import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
@@ -143,8 +144,8 @@ public class LoanAccountDomainServiceJpa implements 
LoanAccountDomainService {
     private final DelinquencyEffectivePauseHelper 
delinquencyEffectivePauseHelper;
     private final DelinquencyReadPlatformService 
delinquencyReadPlatformService;
     private final LoanAccrualsProcessingService loanAccrualsProcessingService;
-    private final InterestRefundServiceDelegate interestRefundServiceDelegate;
     private final LoanRepaymentScheduleTransactionProcessorFactory 
transactionProcessorFactory;
+    private final InterestRefundServiceDelegate interestRefundServiceDelegate;
 
     @Transactional
     @Override
@@ -165,17 +166,26 @@ public class LoanAccountDomainServiceJpa implements 
LoanAccountDomainService {
         
this.loanCollateralManagementRepository.saveAll(loanCollateralManagementSet);
     }
 
-    private LoanTransaction createInterestRefundLoanTransaction(Loan loan, 
final LocalDate transactionDate,
-            BigDecimal relatedRefundTransactionAmount) {
+    private LoanTransaction createInterestRefundLoanTransaction(Loan loan, 
LoanTransaction refundTransaction) {
+
         InterestRefundService interestRefundService = 
interestRefundServiceDelegate.lookupInterestRefundService(loan);
         if (interestRefundService == null) {
             return null;
         }
-        BigDecimal interestRefundAmount = 
interestRefundService.calculateInterestRefundAmount(loan.getId(), 
relatedRefundTransactionAmount,
-                transactionDate);
+
+        Money totalInterest = 
interestRefundService.totalInterestByTransactions(null, loan.getId(), 
refundTransaction.getTransactionDate(),
+                List.of(), 
loan.getLoanTransactions().stream().map(AbstractPersistableCustom::getId).toList());
+        Money previouslyRefundedInterests = 
interestRefundService.getTotalInterestRefunded(loan.getLoanTransactions(), 
loan.getCurrency());
+
+        Money newTotalInterest = 
interestRefundService.totalInterestByTransactions(null, loan.getId(),
+                refundTransaction.getTransactionDate(), 
List.of(refundTransaction),
+                
loan.getLoanTransactions().stream().map(AbstractPersistableCustom::getId).toList());
+        BigDecimal interestRefundAmount = 
totalInterest.minus(previouslyRefundedInterests).minus(newTotalInterest).getAmount();
+
         final ExternalId txnExternalId = externalIdFactory.create();
         businessEventNotifierService.notifyPreBusinessEvent(new 
LoanTransactionInterestRefundPreBusinessEvent(loan));
-        return LoanTransaction.interestRefund(loan, interestRefundAmount, 
transactionDate, txnExternalId);
+        return LoanTransaction.interestRefund(loan, interestRefundAmount, 
refundTransaction.getDateOf(), txnExternalId);
+
     }
 
     @Transactional
@@ -867,7 +877,7 @@ public class LoanAccountDomainServiceJpa implements 
LoanAccountDomainService {
         LoanTransaction interestRefundTransaction = null;
 
         if (shouldCreateInterestRefundTransaction) {
-            interestRefundTransaction = 
createInterestRefundLoanTransaction(loan, transactionDate, transactionAmount);
+            interestRefundTransaction = 
createInterestRefundLoanTransaction(loan, refundTransaction);
             if (interestRefundTransaction != null) {
                 
interestRefundTransaction.getLoanTransactionRelations().add(LoanTransactionRelation
                         .linkToTransaction(interestRefundTransaction, 
refundTransaction, LoanTransactionRelationTypeEnum.RELATED));
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundServiceDelegate.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundServiceDelegate.java
similarity index 95%
rename from 
fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundServiceDelegate.java
rename to 
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundServiceDelegate.java
index a4aa2f062..7635b0a59 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundServiceDelegate.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundServiceDelegate.java
@@ -21,12 +21,14 @@ package org.apache.fineract.portfolio.loanaccount.service;
 import java.util.List;
 import lombok.RequiredArgsConstructor;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 
 @Service
 @RequiredArgsConstructor
 public class InterestRefundServiceDelegate {
 
+    @Lazy
     private final List<InterestRefundService> interestRefundService;
 
     public InterestRefundService lookupInterestRefundService(final Loan loan) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java
index 62ec46dce..8dc0ab31b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java
@@ -25,19 +25,22 @@ import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Stream;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.organisation.monetary.domain.Money;
 import 
org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
+import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel;
 import 
org.apache.fineract.portfolio.loanaccount.starter.AdvancedPaymentScheduleTransactionProcessorCondition;
 import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
-import 
org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
 import org.springframework.context.annotation.Conditional;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
@@ -49,18 +52,13 @@ import 
org.springframework.transaction.annotation.Transactional;
 @Service
 public class ProgressiveLoanInterestRefundServiceImpl implements 
InterestRefundService {
 
-    private final AdvancedPaymentScheduleTransactionProcessor processor;
     private final EMICalculator emiCalculator;
     private final LoanAssembler loanAssembler;
 
-    @Override
-    public boolean canHandle(Loan loan) {
-        return loan != null && loan.isInterestBearing() && 
processor.accept(loan.getTransactionProcessingStrategyCode());
-    }
-
     private static void simulateRepaymentForDisbursements(LoanTransaction lt, 
final AtomicReference<BigDecimal> refundFinal,
             List<LoanTransaction> collect) {
-        collect.add(lt);
+        collect.add(new LoanTransaction(lt.getLoan(), 
lt.getLoan().getOffice(), lt.getTypeOf().getValue(), lt.getDateOf(), 
lt.getAmount(),
+                BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, 
BigDecimal.ZERO, BigDecimal.ZERO, false, null, null));
         if (lt.getTypeOf().isDisbursement() && 
refundFinal.get().compareTo(BigDecimal.ZERO) > 0) {
             if (lt.getAmount().compareTo(refundFinal.get()) <= 0) {
                 collect.add(
@@ -76,52 +74,70 @@ public class ProgressiveLoanInterestRefundServiceImpl 
implements InterestRefundS
         }
     }
 
-    private BigDecimal totalInterest(final Loan loan, BigDecimal refundAmount, 
LocalDate relatedRefundTransactionDate) {
-        final AtomicReference<BigDecimal> refundFinal = new 
AtomicReference<>(refundAmount);
-
-        BigDecimal payableInterest = BigDecimal.ZERO;
-        if 
(loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isDisbursement)) 
{
-            List<LoanTransaction> transactionsToReprocess = new ArrayList<>();
-            List<LoanTransactionType> interestRefundTypes = 
loan.getLoanProductRelatedDetail().getSupportedInterestRefundTypes().stream()
-                    
.map(LoanSupportedInterestRefundTypes::getTransactionType).toList();
-            // add already interest refunded amounts to refund amount
-            // it is necessary to avoid multi disbursed refund
-            loan.getLoanTransactions().stream() //
-                    .filter(lt -> !lt.isReversed()) //
-                    .filter(lt -> 
interestRefundTypes.contains(lt.getTypeOf())) //
-                    .forEach(t -> 
refundFinal.set(refundFinal.get().add(t.getAmount()))); //
-            loan.getLoanTransactions().stream() //
-                    .filter(lt -> !lt.isReversed()) //
-                    .filter(lt -> !lt.isAccrual() && !lt.isAccrualActivity() 
&& !lt.isInterestRefund()) //
-                    .filter(loanTransaction -> 
!interestRefundTypes.contains(loanTransaction.getTypeOf())) //
-                    .forEach(lt -> simulateRepaymentForDisbursements(lt, 
refundFinal, transactionsToReprocess)); //
-
-            List<LoanRepaymentScheduleInstallment> installmentsToReprocess = 
new ArrayList<>(
-                    loan.getRepaymentScheduleInstallments().stream().filter(i 
-> !i.isReAged() && !i.isAdditional()).toList());
-
-            Pair<ChangedTransactionDetail, 
ProgressiveLoanInterestScheduleModel> reprocessResult = processor
-                    
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), 
relatedRefundTransactionDate, transactionsToReprocess,
-                            loan.getCurrency(), installmentsToReprocess, 
loan.getActiveCharges());
-            
loan.getLoanTransactions().addAll(reprocessResult.getLeft().getCurrentTransactionToOldId().keySet());
-            ProgressiveLoanInterestScheduleModel modelAfter = 
reprocessResult.getRight();
-
-            payableInterest = installmentsToReprocess.stream() //
-                    .map(installment -> emiCalculator //
-                            .getDueAmounts(modelAfter, 
installment.getDueDate(), relatedRefundTransactionDate) //
-                            .getDueInterest() //
-                            .getAmount()) //
-                    .reduce(BigDecimal.ZERO, BigDecimal::add); //
-        }
-        return payableInterest;
+    private Money 
recalculateTotalInterest(AdvancedPaymentScheduleTransactionProcessor processor, 
Loan loan,
+            LocalDate relatedRefundTransactionDate, List<LoanTransaction> 
transactionsToReprocess) {
+        List<LoanRepaymentScheduleInstallment> installmentsToReprocess = new 
ArrayList<>(
+                loan.getRepaymentScheduleInstallments().stream().filter(i -> 
!i.isReAged() && !i.isAdditional()).toList());
+
+        Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> 
reprocessResult = processor
+                
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), 
relatedRefundTransactionDate, transactionsToReprocess,
+                        loan.getCurrency(), installmentsToReprocess, 
loan.getActiveCharges());
+        
loan.getLoanTransactions().addAll(reprocessResult.getLeft().getCurrentTransactionToOldId().keySet());
+        ProgressiveLoanInterestScheduleModel modelAfter = 
reprocessResult.getRight();
+
+        return emiCalculator.getSumOfDueInterestsOnDate(modelAfter, 
relatedRefundTransactionDate);
+    }
+
+    @Override
+    public boolean canHandle(Loan loan) {
+        String s = loan.getTransactionProcessingStrategyCode();
+        return 
AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY_NAME.equalsIgnoreCase(s)
+                || 
AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY.equalsIgnoreCase(s);
+    }
+
+    private boolean 
isTransactionNeededForInterestRefundCalculations(LoanTransaction lt) {
+        return lt.isNotReversed() && !lt.isAccrual() && 
!lt.isAccrualActivity() && !lt.isInterestRefund();
     }
 
     @Override
     @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
-    public BigDecimal calculateInterestRefundAmount(Long loanId, BigDecimal 
relatedRefundTransactionAmount,
-            LocalDate relatedRefundTransactionDate) {
+    public Money 
totalInterestByTransactions(LoanRepaymentScheduleTransactionProcessor 
processor, final Long loanId,
+            LocalDate relatedRefundTransactionDate, List<LoanTransaction> 
newTransactions, List<Long> oldTransactionIds) {
         Loan loan = loanAssembler.assembleFrom(loanId);
-        BigDecimal totalInterestBeforeRefund = totalInterest(loan, 
BigDecimal.ZERO, relatedRefundTransactionDate);
-        BigDecimal totalInterestAfterRefund = totalInterest(loan, 
relatedRefundTransactionAmount, relatedRefundTransactionDate);
-        return totalInterestBeforeRefund.subtract(totalInterestAfterRefund);
+        if (processor == null) {
+            processor = loan.getTransactionProcessor();
+        }
+        if (!(processor instanceof 
AdvancedPaymentScheduleTransactionProcessor)) {
+            throw new IllegalArgumentException(
+                    "Wrong processor implementation. 
ProgressiveLoanInterestRefundServiceImpl requires 
AdvancedPaymentScheduleTransactionProcessor");
+        }
+
+        List<LoanTransaction> transactionsToReprocess = new ArrayList<>();
+        List<LoanTransactionType> interestRefundTypes = 
loan.getSupportedInterestRefundTransactionTypes();
+
+        List<LoanTransaction> transactions = 
Stream.concat(loan.getLoanTransactions().stream() //
+                .filter(lt -> 
isTransactionNeededForInterestRefundCalculations(lt) //
+                        && oldTransactionIds.contains(lt.getId())), //
+                newTransactions.stream() //
+                        
.filter(this::isTransactionNeededForInterestRefundCalculations) //
+                        .map(LoanTransaction::copyTransactionProperties)) //
+                .toList();
+
+        final AtomicReference<BigDecimal> refundFinal = new AtomicReference<>(
+                transactions.stream().filter(lt -> 
interestRefundTypes.contains(lt.getTypeOf())) //
+                        
.map(LoanTransaction::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
+
+        transactions.stream().filter(loanTransaction -> 
!interestRefundTypes.contains(loanTransaction.getTypeOf())) //
+                .forEach(lt -> simulateRepaymentForDisbursements(lt, 
refundFinal, transactionsToReprocess)); //
+
+        return 
recalculateTotalInterest((AdvancedPaymentScheduleTransactionProcessor) 
processor, loan, relatedRefundTransactionDate,
+                transactionsToReprocess);
+    }
+
+    @Override
+    public Money getTotalInterestRefunded(List<LoanTransaction> 
loanTransactions, MonetaryCurrency currency) {
+        return Money.of(currency, 
loanTransactions.stream().filter(LoanTransaction::isNotReversed).filter(LoanTransaction::isInterestRefund)
+                .map(LoanTransaction::getAmount).reduce(BigDecimal.ZERO, 
BigDecimal::add));
     }
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java
index 47b820906..269452c3e 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountAutoStarter.java
@@ -31,11 +31,13 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.imp
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.PrincipalInterestPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
 import 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor;
+import 
org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanInterestRefundServiceImpl;
 import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
 import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Conditional;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Lazy;
 
 @Configuration
 public class LoanAccountAutoStarter {
@@ -104,8 +106,9 @@ public class LoanAccountAutoStarter {
 
     @Bean
     @Conditional(AdvancedPaymentScheduleTransactionProcessorCondition.class)
-    public AdvancedPaymentScheduleTransactionProcessor 
advancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator) {
-        return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator);
+    public AdvancedPaymentScheduleTransactionProcessor 
advancedPaymentScheduleTransactionProcessor(EMICalculator emiCalculator,
+            @Lazy ProgressiveLoanInterestRefundServiceImpl 
progressiveLoanInterestRefundService) {
+        return new AdvancedPaymentScheduleTransactionProcessor(emiCalculator, 
progressiveLoanInterestRefundService);
     }
 
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
index 5631fc7ba..9047fb7d5 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
@@ -309,7 +309,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
 
             logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"),
-                    transaction(1000.0, "Payout Refund", "09 February 2021"), 
transaction(87.89, "Repayment", "01 February 2021"),
+                    transaction(87.89, "Repayment", "01 February 2021"), 
transaction(1000.0, "Payout Refund", "09 February 2021"),
                     transaction(10.49, "Interest Refund", "09 February 2021"));
         });
     }
@@ -680,12 +680,12 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
             loanTransactionHelper.makeLoanRepayment("PayoutRefund", "22 
January 2021", 500F, loanId.intValue());
-
+            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Merchant Issued Refund", "14 January 
2021"), //
                     transaction(1.78, "Interest Refund", "14 January 2021"), //
                     transaction(500.0, "Payout Refund", "22 January 2021"), //
-                    transaction(2.87, "Interest Refund", "22 January 2021") //
+                    transaction(2.88, "Interest Refund", "22 January 2021") //
             );
         });
     }
@@ -830,6 +830,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         runAt("26 January 2021", () -> {
             Long loanId = loanIdRef.get();
             loanTransactionHelper.makeLoanRepayment("PayoutRefund", "26 
January 2021", 400F, loanId.intValue());
+            logLoanTransactions(loanId);
 
             verifyTransactions(loanId, transaction(200.0, "Disbursement", "01 
January 2021"), //
                     transaction(300.0, "Disbursement", "01 January 2021"), //
@@ -879,11 +880,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("1 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 
2021", 171.50F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 
2021", 171.41F, loanId.intValue());
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
-                    transaction(171.5, "Repayment", "01 February 2021"));
+                    transaction(171.41, "Repayment", "01 February 2021"));
         });
         runAt("13 February 2021", () -> {
             Long loanId = loanIdRef.get();
@@ -891,7 +892,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
-                    transaction(171.5, "Repayment", "01 February 2021"), //
+                    transaction(171.41, "Repayment", "01 February 2021"), //
                     transaction(250.0, "Payout Refund", "13 February 2021"), //
                     transaction(2.96, "Interest Refund", "13 February 2021") //
             );
@@ -899,20 +900,21 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         runAt("24 February 2021", () -> {
             Long loanId = loanIdRef.get();
             loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"24 February 2021", 400F, loanId.intValue());
+            logLoanTransactions(loanId);
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
-                    transaction(171.5, "Repayment", "01 February 2021"), //
+                    transaction(171.41, "Repayment", "01 February 2021"), //
                     transaction(250.0, "Payout Refund", "13 February 2021"), //
                     transaction(2.96, "Interest Refund", "13 February 2021"), 
//
                     transaction(400.0, "Merchant Issued Refund", "24 February 
2021"), //
-                    transaction(5.76, "Interest Refund", "24 February 2021") //
+                    transaction(5.77, "Interest Refund", "24 February 2021") //
             );
         });
         runAt("1 April 2021", () -> {
             Long loanId = loanIdRef.get();
             loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 
2021", 171.41F, loanId.intValue());
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 
2021", 11.17F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 
2021", 11.25F, loanId.intValue());
             GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
 
             Assertions.assertNotNull(loanDetails);
@@ -973,6 +975,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         runAt("6 April 2021", () -> {
             Long loanId = loanIdRef.get();
             loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "6 
April 2021", 400F, loanId.intValue());
+            logLoanTransactions(loanId);
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
@@ -982,14 +985,14 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
                     transaction(250.0, "Payout Refund", "13 February 2021"), //
                     transaction(2.96, "Interest Refund", "13 February 2021"), 
//
                     transaction(400.0, "Merchant Issued Refund", "06 April 
2021"), //
-                    transaction(10.1, "Interest Refund", "06 April 2021") //
+                    transaction(10.11, "Interest Refund", "06 April 2021") //
             );
             GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
 
             Assertions.assertNotNull(loanDetails);
             Assertions.assertNotNull(loanDetails.getStatus());
             Assertions.assertEquals(700, loanDetails.getStatus().getId());
-            Assertions.assertEquals(160.15D, loanDetails.getTotalOverpaid());
+            Assertions.assertEquals(160.16D, loanDetails.getTotalOverpaid());
         });
     }
 
@@ -1042,6 +1045,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         runAt("8 February 2021", () -> {
             Long loanId = loanIdRef.get();
             loanTransactionHelper.makeLoanRepayment("PayoutRefund", "8 
February 2021", 250F, loanId.intValue());
+            logLoanTransactions(loanId);
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(400.0, "Payout Refund", "12 January 2021"), //
@@ -1050,12 +1054,12 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
                     transaction(0.66, "Interest Refund", "17 January 2021"), //
                     transaction(171.5, "Repayment", "01 February 2021"), //
                     transaction(250.0, "Payout Refund", "08 February 2021"), //
-                    transaction(2.60, "Interest Refund", "08 February 2021") //
+                    transaction(2.61, "Interest Refund", "08 February 2021") //
             );
         });
         runAt("1 March 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 
2021", 30.44F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 
2021", 30.43F, loanId.intValue());
             GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
             Assertions.assertNotNull(loanDetails);
             Assertions.assertNotNull(loanDetails.getStatus());
@@ -1063,6 +1067,103 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
     }
 
+    @Test
+    public void verifyUC18S1() {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2021", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL)
 //
+                            .daysInYearType(DaysInYearType.ACTUAL) //
+                            .multiDisburseLoan(true)//
+                            .disallowExpectedDisbursements(true)//
+                            .maxTrancheCount(2)//
+                            
.addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND)
 //
+                            
.addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND)
 //
+                            
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+            );
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.9,
+                    12, null);
+            Assertions.assertNotNull(loanId);
+            loanIdRef.set(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
+        });
+        runAt("22 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"22 January 2021", 1000F, loanId.intValue());
+            logLoanTransactions(loanId);
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
+                    transaction(1000.0, "Merchant Issued Refund", "22 January 
2021"), //
+                    transaction(5.70, "Interest Refund", "22 January 2021"), //
+                    transaction(5.70, "Accrual", "22 January 2021") //
+            );
+            loanTransactionHelper.makeLoanRepayment("Repayment", "10 January 
2021", 85.63F, loanId.intValue());
+            logLoanTransactions(loanId);
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
+                    transaction(85.63, "Repayment", "10 January 2021"), //
+                    transaction(1000.0, "Merchant Issued Refund", "22 January 
2021"), //
+                    transaction(5.42, "Interest Refund", "22 January 2021") //
+            );
+        });
+    }
+
+    @Test
+    public void verifyUC18S2() {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        AtomicReference<Long> repaymentIdRef = new AtomicReference<>();
+        runAt("1 January 2021", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL)
 //
+                            .daysInYearType(DaysInYearType.ACTUAL) //
+                            .multiDisburseLoan(true)//
+                            .disallowExpectedDisbursements(true)//
+                            
.maxTrancheCount(2).addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND)
 //
+                            
.addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND)
 //
+                            
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+            );
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.9,
+                    12, null);
+            Assertions.assertNotNull(loanId);
+            loanIdRef.set(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
+        });
+        runAt("10 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            Long response = 
loanTransactionHelper.makeLoanRepayment("Repayment", "10 January 2021", 85.63F, 
loanId.intValue())
+                    .getResourceId();
+            Assertions.assertNotNull(response);
+            repaymentIdRef.set(response);
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
+                    transaction(85.63, "Repayment", "10 January 2021") //
+            );
+        });
+        runAt("22 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"22 January 2021", 1000F, loanId.intValue());
+            logLoanTransactions(loanId);
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
+                    transaction(85.63, "Repayment", "10 January 2021"), //
+                    transaction(1000.0, "Merchant Issued Refund", "22 January 
2021"), //
+                    transaction(5.42, "Interest Refund", "22 January 2021") //
+            );
+
+            Long repaymentId = repaymentIdRef.get();
+            loanTransactionHelper.reverseLoanTransaction(loanId.intValue(), 
repaymentId, "10 January 2021", responseSpec);
+            logLoanTransactions(loanId);
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
+                    reversedTransaction(85.63, "Repayment", "10 January 
2021"), //
+                    transaction(1000.0, "Merchant Issued Refund", "22 January 
2021"), //
+                    transaction(5.70, "Interest Refund", "22 January 2021"), //
+                    transaction(5.70, "Accrual", "10 January 2021") //
+            );
+
+        });
+    }
+
     private void logInstallmentsOfLoanDetails(GetLoansLoanIdResponse 
loanDetails) {
         log.info("index, dueDate, principal, fee, penalty, interest");
         if (loanDetails != null && loanDetails.getRepaymentSchedule() != null 
&& loanDetails.getRepaymentSchedule().getPeriods() != null) {

Reply via email to