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

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

commit 6df78f64c083a05876178e08f911116ef194824d
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Wed Dec 10 10:42:37 2025 -0500

    FINERACT-2389: Reprocess Interest Refund txn amount if no txn was changed
---
 ...dvancedPaymentScheduleTransactionProcessor.java | 10 ++--
 .../integrationtests/LoanInterestRefundTest.java   | 54 ++++++++++++++++++++++
 ...nManualInterestRefundResponseStructureTest.java | 20 +-------
 .../common/loans/LoanTransactionHelper.java        | 16 +++++++
 4 files changed, 76 insertions(+), 24 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 584eec52f7..121172ee17 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
@@ -568,12 +568,10 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
                         
.filter(LoanTransaction::isNotReversed).map(AbstractPersistableCustom::getId).toList();
                 final List<LoanTransaction> modifiedTransactions = new 
ArrayList<>(progCtx.getAlreadyProcessedTransactions().stream()
                         .filter(LoanTransaction::isNotReversed).filter(tr -> 
tr.getId() == null).toList());
-                if (!modifiedTransactions.isEmpty()) {
-                    final Money interestAfterRefund = 
interestRefundService.totalInterestByTransactions(this, loan.getId(), 
targetDate,
-                            modifiedTransactions, unmodifiedTransactionIds);
-                    final Money newAmount = 
interestBeforeRefund.minus(progCtx.getSumOfInterestRefundAmount()).minus(interestAfterRefund);
-                    loanTransaction.updateAmount(newAmount.getAmount());
-                }
+                final Money interestAfterRefund = 
interestRefundService.totalInterestByTransactions(this, loan.getId(), 
targetDate,
+                        modifiedTransactions, unmodifiedTransactionIds);
+                final Money newAmount = 
interestBeforeRefund.minus(progCtx.getSumOfInterestRefundAmount()).minus(interestAfterRefund);
+                loanTransaction.updateAmount(newAmount.getAmount());
                 
progCtx.setSumOfInterestRefundAmount(progCtx.getSumOfInterestRefundAmount().add(loanTransaction.getAmount()));
             }
         }
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 fa3a9aacc8..02c8acfe8b 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
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.integrationtests;
 
+import static 
org.apache.fineract.portfolio.loanaccount.domain.Loan.DATE_FORMAT;
 import static 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum.REPLAYED;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -32,6 +33,7 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.AdvancedPaymentData;
@@ -41,6 +43,7 @@ import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionId
 import org.apache.fineract.client.models.PaymentAllocationOrder;
 import org.apache.fineract.client.models.PostClientsResponse;
 import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
 import 
org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
 import org.apache.fineract.client.util.CallFailedRuntimeException;
@@ -1633,4 +1636,55 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
             Assertions.assertEquals(0.36, 
Utils.getDoubleValue(loanDetails.getTotalOverpaid()));
         });
     }
+
+    @Test
+    public void allowToReprocessInterestRefundEvenIfNoTransactionWasChanged() {
+        runAt("1 February 2025", () -> {
+            Long loanProductId = getOrCreateLoanProduct();
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProductId, "1 January 2025", 100.0, 26.0, 6, null);
+            Assertions.assertNotNull(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(100.0), "1 January 2025");
+
+            final String transactionExternalId = UUID.randomUUID().toString();
+            final PostLoansLoanIdTransactionsResponse refundResponse = 
loanTransactionHelper
+                    .makeMerchantIssuedRefund(loanId, new 
PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN)
+                        .transactionDate("1 February 
2025").locale("en").transactionAmount(66.41).externalId(transactionExternalId).interestRefundCalculation(false));
+            Assertions.assertNotNull(refundResponse.getResourceId());
+
+            verifyTransactions(loanId, //
+                    transaction(100.0, "Disbursement", "01 January 2025"), //
+                    transaction(66.41, "Merchant Issued Refund", "01 February 
2025") //
+            );
+
+            // Create manual interest refund via API
+            PostLoansLoanIdTransactionsResponse interestRefundResponse = 
loanTransactionHelper.createManualInterestRefund(loanId, 
refundResponse.getResourceId(),
+                    "1 February 2025", 1.47, null);
+
+            verifyTransactions(loanId, //
+                    transaction(100.0, "Disbursement", "01 January 2025"), //
+                    transaction(66.41, "Merchant Issued Refund", "01 February 
2025"), //
+                    transaction(1.47, "Interest Refund", "01 February 2025") //
+            );
+
+            PostLoansLoanIdTransactionsResponse repaymentResponse = 
loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "20 January 2025", 
17.94);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "25 
January 2025", 10.94);
+
+            verifyTransactions(loanId, //
+                    transaction(100.0, "Disbursement", "01 January 2025"), //
+                    transaction(17.94, "Repayment", "20 January 2025"), //
+                    transaction(10.94, "Repayment", "25 January 2025"), //
+                    transaction(66.41, "Merchant Issued Refund", "01 February 
2025"), //
+                    transaction(1.47, "Interest Refund", "01 February 2025") //
+            );
+
+            loanTransactionHelper.reverseLoanTransaction(loanId, 
repaymentResponse.getResourceId(),
+                    new 
PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat(DATETIME_PATTERN)
+                    .transactionDate("25 January 
2021").transactionAmount(0.0).locale("en"));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            verifyLoanStatus(loanDetails, LoanStatus.OVERPAID);
+            Assertions.assertEquals(0.36, 
Utils.getDoubleValue(loanDetails.getTotalOverpaid()));
+        });
+    }
+
 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanManualInterestRefundResponseStructureTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanManualInterestRefundResponseStructureTest.java
index fe69233498..abd5d4144e 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanManualInterestRefundResponseStructureTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanManualInterestRefundResponseStructureTest.java
@@ -96,7 +96,7 @@ public class LoanManualInterestRefundResponseStructureTest 
extends BaseLoanInteg
             targetTransactionIdRef.set(refundResponse.getResourceId());
 
             // Create manual interest refund via API
-            PostLoansLoanIdTransactionsResponse interestRefundResponse = 
createManualInterestRefund(loanId, refundResponse.getResourceId(),
+            PostLoansLoanIdTransactionsResponse interestRefundResponse = 
loanTransactionHelper.createManualInterestRefund(loanId, 
refundResponse.getResourceId(),
                     "15 January 2024", 5.0, null);
 
             assertNotNull(interestRefundResponse, "Interest refund response 
should not be null");
@@ -157,7 +157,7 @@ public class LoanManualInterestRefundResponseStructureTest 
extends BaseLoanInteg
 
             // Create manual interest refund with external ID
             String interestRefundExternalId = UUID.randomUUID().toString();
-            PostLoansLoanIdTransactionsResponse interestRefundResponse = 
createManualInterestRefund(loanId, refundResponse.getResourceId(),
+            PostLoansLoanIdTransactionsResponse interestRefundResponse = 
loanTransactionHelper.createManualInterestRefund(loanId, 
refundResponse.getResourceId(),
                     "15 February 2024", 5.0, interestRefundExternalId);
 
             assertNotNull(interestRefundResponse, "Interest refund response 
should not be null");
@@ -180,22 +180,6 @@ public class LoanManualInterestRefundResponseStructureTest 
extends BaseLoanInteg
         });
     }
 
-    /**
-     * Helper method to create manual interest refund transaction
-     */
-    private PostLoansLoanIdTransactionsResponse 
createManualInterestRefund(Long loanId, Long targetTransactionId, String 
transactionDate,
-            Double amount, String externalId) {
-
-        PostLoansLoanIdTransactionsTransactionIdRequest request = new 
PostLoansLoanIdTransactionsTransactionIdRequest()
-                .transactionAmount(amount).dateFormat("dd MMMM 
yyyy").locale("en");
-
-        if (externalId != null) {
-            request.externalId(externalId);
-        }
-
-        return loanTransactionHelper.manualInterestRefund(loanId, 
targetTransactionId, request);
-    }
-
     /**
      * Helper method to make loan merchant issued refund (without automatic 
interest refund)
      */
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 8a55b88eb0..371c26649d 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -1709,6 +1709,22 @@ public class LoanTransactionHelper {
                 excludedTransactionTypes, page, size, sort));
     }
 
+    /**
+     * Helper method to create manual interest refund transaction
+     */
+    public PostLoansLoanIdTransactionsResponse createManualInterestRefund(Long 
loanId, Long targetTransactionId, String transactionDate,
+            Double amount, String externalId) {
+
+        PostLoansLoanIdTransactionsTransactionIdRequest request = new 
PostLoansLoanIdTransactionsTransactionIdRequest()
+                .transactionAmount(amount).dateFormat("dd MMMM 
yyyy").locale("en");
+
+        if (externalId != null) {
+            request.externalId(externalId);
+        }
+
+        return manualInterestRefund(loanId, targetTransactionId, request);
+    }
+
     // TODO: Rewrite to use fineract-client instead!
     // Example: 
org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
     // org.apache.fineract.client.models.PostLoansLoanIdRequest)

Reply via email to