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

taskain 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 5ce69c19f FINERACT-2024: Fix Overpayment portion miscalculated in CBR 
reverse-replay
5ce69c19f is described below

commit 5ce69c19fe829b19d64c19f93c9ee6cc68e7a214
Author: Adam Saghy <[email protected]>
AuthorDate: Thu Mar 7 16:36:15 2024 +0100

    FINERACT-2024: Fix Overpayment portion miscalculated in CBR reverse-replay
---
 .../portfolio/loanaccount/domain/Loan.java         |   4 +-
 ...tLoanRepaymentScheduleTransactionProcessor.java |   3 +-
 ...PaymentAllocationLoanRepaymentScheduleTest.java | 235 ++++++++++++++++-----
 .../integrationtests/BaseLoanIntegrationTest.java  |  56 +++++
 ...lanceRefundandRepaymentTypeIntegrationTest.java | 184 ++++++++++++++++
 5 files changed, 426 insertions(+), 56 deletions(-)

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 deeb280ce..d9d9c931c 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
@@ -3867,7 +3867,9 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
             if (loanTransaction.isRefund() || 
loanTransaction.isRefundForActiveLoan()) {
                 totalPaidInRepayments = 
totalPaidInRepayments.minus(loanTransaction.getAmount(currency));
             } else if (loanTransaction.isCreditBalanceRefund() || 
loanTransaction.isChargeback()) {
-                totalPaidInRepayments = 
totalPaidInRepayments.minus(loanTransaction.getOverPaymentPortion(currency));
+                if (loanTransaction.getPrincipalPortion(currency).isZero()) {
+                    totalPaidInRepayments = 
totalPaidInRepayments.minus(loanTransaction.getOverPaymentPortion(currency));
+                }
             }
         }
 
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
index 94592fed8..b05c2ae9e 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
@@ -227,7 +227,8 @@ public abstract class 
AbstractLoanRepaymentScheduleTransactionProcessor implemen
                         onLoanOverpayment(loanTransaction, 
transactionAmountUnprocessed);
                         
loanTransaction.setOverPayments(transactionAmountUnprocessed);
                     }
-                    
ctx.getOverpaymentHolder().setMoneyObject(transactionAmountUnprocessed);
+                    ctx.getOverpaymentHolder()
+                            
.setMoneyObject(ctx.getOverpaymentHolder().getMoneyObject().add(transactionAmountUnprocessed));
                 } else {
                     
ctx.getOverpaymentHolder().setMoneyObject(Money.zero(ctx.getCurrency()));
                 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
index aa1bd5e42..7e71d4223 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
@@ -34,7 +34,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Objects;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import org.apache.fineract.client.models.AdvancedPaymentData;
@@ -3283,13 +3283,186 @@ public class 
AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
         });
     }
 
-    private static void validateLoanSummaryBalances(GetLoansLoanIdResponse 
loanDetails, Double totalOutstanding, Double totalRepayment,
-            Double principalOutstanding, Double principalPaid, Double 
totalOverpaid) {
-        assertEquals(totalOutstanding, 
loanDetails.getSummary().getTotalOutstanding());
-        assertEquals(totalRepayment, 
loanDetails.getSummary().getTotalRepayment());
-        assertEquals(principalOutstanding, 
loanDetails.getSummary().getPrincipalOutstanding());
-        assertEquals(principalPaid, 
loanDetails.getSummary().getPrincipalPaid());
-        assertEquals(totalOverpaid, loanDetails.getTotalOverpaid());
+    // UC124: Advanced payment allocation, MIR, CBR and Chargeback on overpaid 
loan
+    // ADVANCED_PAYMENT_ALLOCATION_STRATEGY
+    // 1. Create a Loan product with Adv. Pment. Alloc.
+    // 2. Submit Loan and approve
+    // 3. Disburse only 100
+    // 4. Fully pay the loan
+    // 5. Do MIR 3 times
+    // 6. Do a CBR
+    // 7. Do MIR 2 times
+    // 8. Reverse the 1st MIR
+    // 9. Do Chargeback (lower than overpayment amount)
+    // 10.Do Chargeback again (reactive the loan)
+    @Test
+    public void uc124() {
+        runAt("06 March 2024", () -> {
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+            PostLoanProductsRequest product = 
createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
+                    
.numberOfRepayments(1).repaymentEvery(30).enableDownPayment(false);
+            PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(product);
+            PostLoansRequest applicationRequest = applyLoanRequest(clientId, 
loanProductResponse.getResourceId(), "25 January 2024", 1000.0,
+                    4);
+
+            applicationRequest = 
applicationRequest.numberOfRepayments(1).loanTermFrequency(30)
+                    
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY).repaymentEvery(30);
+
+            PostLoansResponse loanResponse = 
loanTransactionHelper.applyLoan(applicationRequest);
+
+            loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
+                    new 
PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
+                            .approvedOnDate("25 January 2024").locale("en"));
+
+            loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+                    new PostLoansLoanIdRequest().actualDisbursementDate("25 
January 2024").dateFormat(DATETIME_PATTERN)
+                            
.transactionAmount(BigDecimal.valueOf(100.0)).locale("en"));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 100.0, 0.0, 100.0, 0.0, 
null);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 0.0, 100.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getActive());
+
+            String repaymentExternalId = UUID.randomUUID().toString();
+            loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(),
+                    new 
PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("24
 February 2024").locale("en")
+                            
.transactionAmount(100.0).externalId(repaymentExternalId));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
null);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getClosedObligationsMet());
+
+            String mir1ExternalId = UUID.randomUUID().toString();
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(),
+                    new 
PostLoansLoanIdTransactionsRequest().transactionDate("28 February 
2024").dateFormat(DATETIME_PATTERN)
+                            
.transactionAmount(36.99).locale("en").externalId(mir1ExternalId));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
36.99);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("28 February 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(18.94).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
55.93);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("28 February 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(36.99).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
92.92);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("28 February 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(31.99).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
124.91);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeCreditBalanceRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("01 March 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(124.91).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
null);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getClosedObligationsMet());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("02 March 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(19.99).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
19.99);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("02 March 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(19.99).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
39.98);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            verifyTransactions(loanResponse.getLoanId(), //
+                    transaction(100, "Disbursement", "25 January 2024", 100.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(100, "Repayment", "24 February 2024", 0.0, 
100.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(18.94, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 18.94), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 55.93), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 92.92), //
+                    transaction(31.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 124.91), //
+                    transaction(124.91, "Credit Balance Refund", "01 March 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 124.91), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 19.99), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 39.98) //
+            );
+
+            
loanTransactionHelper.reverseLoanTransaction(loanResponse.getLoanId(), 
mir1ExternalId,
+                    new 
PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat(DATETIME_PATTERN).transactionDate("02
 March 2024")
+                            .transactionAmount(0.0).locale("en"));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 224.91, 0.0, 224.91, 
2.99);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 1), 
124.91, 124.91, 0.0, 0.0, 36.99);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            verifyTransactions(loanResponse.getLoanId(), //
+                    transaction(100, "Disbursement", "25 January 2024", 100.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(100, "Repayment", "24 February 2024", 0.0, 
100.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 36.99, true), //
+                    transaction(18.94, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 18.94), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 55.93), //
+                    transaction(31.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(124.91, "Credit Balance Refund", "01 March 
2024", 36.99, 36.99, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 17.0, 19.99, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 17.0, 0.0, 0.0, 0.0, 0.0, 2.99) //
+            );
+
+            
loanTransactionHelper.chargebackLoanTransaction(loanResponse.getLoanId(), 
repaymentExternalId,
+                    new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(2.0));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 224.91, 0.0, 224.91, 
0.99);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 1), 
124.91, 124.91, 0.0, 0.0, 36.99);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            verifyTransactions(loanResponse.getLoanId(), //
+                    transaction(100, "Disbursement", "25 January 2024", 100.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(100, "Repayment", "24 February 2024", 0.0, 
100.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 36.99, true), //
+                    transaction(18.94, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 18.94), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 55.93), //
+                    transaction(31.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(124.91, "Credit Balance Refund", "01 March 
2024", 36.99, 36.99, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 17.0, 19.99, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 17.0, 0.0, 0.0, 0.0, 0.0, 2.99), //
+                    transaction(2.0, "Chargeback", "06 March 2024", 0.0, 0.0, 
0.0, 0.0, 0.0, 0.0, 2.0) //
+            );
+
+            
loanTransactionHelper.chargebackLoanTransaction(loanResponse.getLoanId(), 
repaymentExternalId,
+                    new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(1.0));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.01, 225.90, 0.01, 
225.90, null);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 6), 
125.91, 125.90, 0.01, 0.0, 36.99);
+            assertTrue(loanDetails.getStatus().getActive());
+
+            verifyTransactions(loanResponse.getLoanId(), //
+                    transaction(100, "Disbursement", "25 January 2024", 100.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(100, "Repayment", "24 February 2024", 0.0, 
100.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 36.99, true), //
+                    transaction(18.94, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 18.94), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 55.93), //
+                    transaction(31.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(124.91, "Credit Balance Refund", "01 March 
2024", 36.99, 36.99, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 17.0, 19.99, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 17.0, 0.0, 0.0, 0.0, 0.0, 2.99), //
+                    transaction(2.0, "Chargeback", "06 March 2024", 0.0, 0.0, 
0.0, 0.0, 0.0, 0.0, 2.0), //
+                    transaction(1.0, "Chargeback", "06 March 2024", 0.01, 
0.01, 0.0, 0.0, 0.0, 0.0, 0.99) //
+            );
+
+        });
     }
 
     private static List<PaymentAllocationOrder> 
getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) {
@@ -3430,52 +3603,6 @@ public class 
AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
         assertEquals(paidLate, period.getTotalPaidLateForPeriod());
     }
 
-    private static void validateRepaymentPeriod(GetLoansLoanIdResponse 
loanDetails, Integer index, LocalDate dueDate, double principalDue,
-            double principalPaid, double principalOutstanding, double 
paidInAdvance, double paidLate) {
-        GetLoansLoanIdRepaymentPeriod period = 
loanDetails.getRepaymentSchedule().getPeriods().stream()
-                .filter(p -> Objects.equals(p.getPeriod(), 
index)).findFirst().orElseThrow();
-        assertEquals(dueDate, period.getDueDate());
-        assertEquals(principalDue, period.getPrincipalDue());
-        assertEquals(principalPaid, period.getPrincipalPaid());
-        assertEquals(principalOutstanding, period.getPrincipalOutstanding());
-        assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
-        assertEquals(paidLate, period.getTotalPaidLateForPeriod());
-    }
-
-    private static void validateRepaymentPeriod(GetLoansLoanIdResponse 
loanDetails, Integer index, double principalDue,
-            double principalPaid, double principalOutstanding, double 
paidInAdvance, double paidLate) {
-        GetLoansLoanIdRepaymentPeriod period = 
loanDetails.getRepaymentSchedule().getPeriods().stream()
-                .filter(p -> Objects.equals(p.getPeriod(), 
index)).findFirst().orElseThrow();
-        assertEquals(principalDue, period.getPrincipalDue());
-        assertEquals(principalPaid, period.getPrincipalPaid());
-        assertEquals(principalOutstanding, period.getPrincipalOutstanding());
-        assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
-        assertEquals(paidLate, period.getTotalPaidLateForPeriod());
-    }
-
-    private static void validateRepaymentPeriod(GetLoansLoanIdResponse 
loanDetails, Integer index, LocalDate dueDate, double principalDue,
-            double principalPaid, double principalOutstanding, double feeDue, 
double feePaid, double feeOutstanding, double penaltyDue,
-            double penaltyPaid, double penaltyOutstanding, double interestDue, 
double interestPaid, double interestOutstanding,
-            double paidInAdvance, double paidLate) {
-        GetLoansLoanIdRepaymentPeriod period = 
loanDetails.getRepaymentSchedule().getPeriods().stream()
-                .filter(p -> Objects.equals(p.getPeriod(), 
index)).findFirst().orElseThrow();
-        assertEquals(dueDate, period.getDueDate());
-        assertEquals(principalDue, period.getPrincipalDue());
-        assertEquals(principalPaid, period.getPrincipalPaid());
-        assertEquals(principalOutstanding, period.getPrincipalOutstanding());
-        assertEquals(feeDue, period.getFeeChargesDue());
-        assertEquals(feePaid, period.getFeeChargesPaid());
-        assertEquals(feeOutstanding, period.getFeeChargesOutstanding());
-        assertEquals(penaltyDue, period.getPenaltyChargesDue());
-        assertEquals(penaltyPaid, period.getPenaltyChargesPaid());
-        assertEquals(penaltyOutstanding, 
period.getPenaltyChargesOutstanding());
-        assertEquals(interestDue, period.getInterestDue());
-        assertEquals(interestPaid, period.getInterestPaid());
-        assertEquals(interestOutstanding, period.getInterestOutstanding());
-        assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
-        assertEquals(paidLate, period.getTotalPaidLateForPeriod());
-    }
-
     private static PostLoansResponse applyForLoanApplication(final Long 
clientId, final Integer loanProductId, final BigDecimal principal,
             final int loanTermFrequency, final int repaymentAfterEvery, final 
int numberOfRepayments, final BigDecimal interestRate,
             final String expectedDisbursementDate, final String 
submittedOnDate, String transactionProcessorCode,
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 bcd50f555..7903ca41e 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
@@ -22,6 +22,7 @@ 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.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.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -717,6 +718,61 @@ public abstract class BaseLoanIntegrationTest {
         return new BatchRequestBuilder(requestSpec, responseSpec);
     }
 
+    protected void validateLoanSummaryBalances(GetLoansLoanIdResponse 
loanDetails, Double totalOutstanding, Double totalRepayment,
+            Double principalOutstanding, Double principalPaid, Double 
totalOverpaid) {
+        assertEquals(totalOutstanding, 
loanDetails.getSummary().getTotalOutstanding());
+        assertEquals(totalRepayment, 
loanDetails.getSummary().getTotalRepayment());
+        assertEquals(principalOutstanding, 
loanDetails.getSummary().getPrincipalOutstanding());
+        assertEquals(principalPaid, 
loanDetails.getSummary().getPrincipalPaid());
+        assertEquals(totalOverpaid, loanDetails.getTotalOverpaid());
+    }
+
+    protected static void validateRepaymentPeriod(GetLoansLoanIdResponse 
loanDetails, Integer index, LocalDate dueDate, double principalDue,
+            double principalPaid, double principalOutstanding, double 
paidInAdvance, double paidLate) {
+        GetLoansLoanIdRepaymentPeriod period = 
loanDetails.getRepaymentSchedule().getPeriods().stream()
+                .filter(p -> Objects.equals(p.getPeriod(), 
index)).findFirst().orElseThrow();
+        assertEquals(dueDate, period.getDueDate());
+        assertEquals(principalDue, period.getPrincipalDue());
+        assertEquals(principalPaid, period.getPrincipalPaid());
+        assertEquals(principalOutstanding, period.getPrincipalOutstanding());
+        assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
+        assertEquals(paidLate, period.getTotalPaidLateForPeriod());
+    }
+
+    protected static void validateRepaymentPeriod(GetLoansLoanIdResponse 
loanDetails, Integer index, double principalDue,
+            double principalPaid, double principalOutstanding, double 
paidInAdvance, double paidLate) {
+        GetLoansLoanIdRepaymentPeriod period = 
loanDetails.getRepaymentSchedule().getPeriods().stream()
+                .filter(p -> Objects.equals(p.getPeriod(), 
index)).findFirst().orElseThrow();
+        assertEquals(principalDue, period.getPrincipalDue());
+        assertEquals(principalPaid, period.getPrincipalPaid());
+        assertEquals(principalOutstanding, period.getPrincipalOutstanding());
+        assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
+        assertEquals(paidLate, period.getTotalPaidLateForPeriod());
+    }
+
+    protected static void validateRepaymentPeriod(GetLoansLoanIdResponse 
loanDetails, Integer index, LocalDate dueDate, double principalDue,
+            double principalPaid, double principalOutstanding, double feeDue, 
double feePaid, double feeOutstanding, double penaltyDue,
+            double penaltyPaid, double penaltyOutstanding, double interestDue, 
double interestPaid, double interestOutstanding,
+            double paidInAdvance, double paidLate) {
+        GetLoansLoanIdRepaymentPeriod period = 
loanDetails.getRepaymentSchedule().getPeriods().stream()
+                .filter(p -> Objects.equals(p.getPeriod(), 
index)).findFirst().orElseThrow();
+        assertEquals(dueDate, period.getDueDate());
+        assertEquals(principalDue, period.getPrincipalDue());
+        assertEquals(principalPaid, period.getPrincipalPaid());
+        assertEquals(principalOutstanding, period.getPrincipalOutstanding());
+        assertEquals(feeDue, period.getFeeChargesDue());
+        assertEquals(feePaid, period.getFeeChargesPaid());
+        assertEquals(feeOutstanding, period.getFeeChargesOutstanding());
+        assertEquals(penaltyDue, period.getPenaltyChargesDue());
+        assertEquals(penaltyPaid, period.getPenaltyChargesPaid());
+        assertEquals(penaltyOutstanding, 
period.getPenaltyChargesOutstanding());
+        assertEquals(interestDue, period.getInterestDue());
+        assertEquals(interestPaid, period.getInterestPaid());
+        assertEquals(interestOutstanding, period.getInterestOutstanding());
+        assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
+        assertEquals(paidLate, period.getTotalPaidLateForPeriod());
+    }
+
     @RequiredArgsConstructor
     public static class BatchRequestBuilder {
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
index 3f884f2da..cffaa660c 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
@@ -21,23 +21,34 @@ package org.apache.fineract.integrationtests;
 import static 
org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder.DEFAULT_STRATEGY;
 import static 
org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import io.restassured.builder.RequestSpecBuilder;
 import io.restassured.builder.ResponseSpecBuilder;
 import io.restassured.http.ContentType;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
+import java.math.BigDecimal;
+import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Stream;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.AdvancedPaymentData;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.PaymentAllocationOrder;
+import org.apache.fineract.client.models.PostLoanProductsRequest;
+import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdRequest;
+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.models.PostLoansRequest;
+import org.apache.fineract.client.models.PostLoansResponse;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.CommonConstants;
 import org.apache.fineract.integrationtests.common.Utils;
@@ -55,6 +66,7 @@ import 
org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Named;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
@@ -695,6 +707,178 @@ public class 
ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest extend
                 responseSpec403);
     }
 
+    @Test
+    public void cbrReverseReplayTest() {
+        runAt("06 March 2024", () -> {
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+            PostLoanProductsRequest product = 
createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct().numberOfRepayments(1)
+                    .repaymentEvery(30).enableDownPayment(false);
+            PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(product);
+            PostLoansRequest applicationRequest = applyLoanRequest(clientId, 
loanProductResponse.getResourceId(), "25 January 2024", 1000.0,
+                    4);
+
+            applicationRequest = 
applicationRequest.numberOfRepayments(1).loanTermFrequency(30)
+                    .transactionProcessingStrategyCode(
+                            
LoanProductTestBuilder.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST_STRATEGY)
+                    .repaymentEvery(30);
+
+            PostLoansResponse loanResponse = 
loanTransactionHelper.applyLoan(applicationRequest);
+
+            loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
+                    new 
PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
+                            .approvedOnDate("25 January 2024").locale("en"));
+
+            loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+                    new PostLoansLoanIdRequest().actualDisbursementDate("25 
January 2024").dateFormat(DATETIME_PATTERN)
+                            
.transactionAmount(BigDecimal.valueOf(100.0)).locale("en"));
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 100.0, 0.0, 100.0, 0.0, 
null);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 0.0, 100.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getActive());
+
+            String repaymentExternalId = UUID.randomUUID().toString();
+            loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(),
+                    new 
PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("24
 February 2024").locale("en")
+                            
.transactionAmount(100.0).externalId(repaymentExternalId));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
null);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getClosedObligationsMet());
+
+            String mir1ExternalId = UUID.randomUUID().toString();
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(),
+                    new 
PostLoansLoanIdTransactionsRequest().transactionDate("28 February 
2024").dateFormat(DATETIME_PATTERN)
+                            
.transactionAmount(36.99).locale("en").externalId(mir1ExternalId));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
36.99);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("28 February 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(18.94).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
55.93);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("28 February 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(36.99).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
92.92);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("28 February 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(31.99).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
124.91);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeCreditBalanceRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("01 March 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(124.91).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
null);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getClosedObligationsMet());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("02 March 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(19.99).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
19.99);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            
loanTransactionHelper.makeMerchantIssuedRefund(loanResponse.getLoanId(), new 
PostLoansLoanIdTransactionsRequest()
+                    .transactionDate("02 March 
2024").dateFormat(DATETIME_PATTERN).transactionAmount(19.99).locale("en"));
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 
39.98);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            verifyTransactions(loanResponse.getLoanId(), //
+                    transaction(100, "Disbursement", "25 January 2024", 100.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(100, "Repayment", "24 February 2024", 0.0, 
100.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(18.94, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 18.94), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 55.93), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 92.92), //
+                    transaction(31.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 124.91), //
+                    transaction(124.91, "Credit Balance Refund", "01 March 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 124.91), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 19.99), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 39.98) //
+            );
+
+            
loanTransactionHelper.reverseLoanTransaction(loanResponse.getLoanId(), 
mir1ExternalId,
+                    new 
PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat(DATETIME_PATTERN).transactionDate("02
 March 2024")
+                            .transactionAmount(0.0).locale("en"));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 224.91, 0.0, 224.91, 
2.99);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 1), 
124.91, 124.91, 0.0, 0.0, 36.99);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            verifyTransactions(loanResponse.getLoanId(), //
+                    transaction(100, "Disbursement", "25 January 2024", 100.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(100, "Repayment", "24 February 2024", 0.0, 
100.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 36.99, true), //
+                    transaction(18.94, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 18.94), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 55.93), //
+                    transaction(31.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(124.91, "Credit Balance Refund", "01 March 
2024", 36.99, 36.99, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 17.0, 19.99, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 17.0, 0.0, 0.0, 0.0, 0.0, 2.99) //
+            );
+
+            
loanTransactionHelper.chargebackLoanTransaction(loanResponse.getLoanId(), 
repaymentExternalId,
+                    new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(2.0));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.0, 224.91, 0.0, 224.91, 
0.99);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 1), 
124.91, 124.91, 0.0, 0.0, 36.99);
+            assertTrue(loanDetails.getStatus().getOverpaid());
+
+            verifyTransactions(loanResponse.getLoanId(), //
+                    transaction(100, "Disbursement", "25 January 2024", 100.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(100, "Repayment", "24 February 2024", 0.0, 
100.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 36.99, true), //
+                    transaction(18.94, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 18.94), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 55.93), //
+                    transaction(31.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(124.91, "Credit Balance Refund", "01 March 
2024", 36.99, 36.99, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 17.0, 19.99, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 17.0, 0.0, 0.0, 0.0, 0.0, 2.99), //
+                    transaction(2.0, "Chargeback", "06 March 2024", 0.0, 0.0, 
0.0, 0.0, 0.0, 0.0, 2.0) //
+            );
+
+            
loanTransactionHelper.chargebackLoanTransaction(loanResponse.getLoanId(), 
repaymentExternalId,
+                    new 
PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(1.0));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+            validateLoanSummaryBalances(loanDetails, 0.01, 225.90, 0.01, 
225.90, null);
+            validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 24), 
100.0, 100.0, 0.0, 0.0, 0.0);
+            validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 6), 
125.91, 125.90, 0.01, 0.0, 36.99);
+            assertTrue(loanDetails.getStatus().getActive());
+
+            verifyTransactions(loanResponse.getLoanId(), //
+                    transaction(100, "Disbursement", "25 January 2024", 100.0, 
0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(100, "Repayment", "24 February 2024", 0.0, 
100.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 36.99, true), //
+                    transaction(18.94, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 18.94), //
+                    transaction(36.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 55.93), //
+                    transaction(31.99, "Merchant Issued Refund", "28 February 
2024", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(124.91, "Credit Balance Refund", "01 March 
2024", 36.99, 36.99, 0.0, 0.0, 0.0, 0.0, 87.92), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 17.0, 19.99, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(19.99, "Merchant Issued Refund", "02 March 
2024", 0.0, 17.0, 0.0, 0.0, 0.0, 0.0, 2.99), //
+                    transaction(2.0, "Chargeback", "06 March 2024", 0.0, 0.0, 
0.0, 0.0, 0.0, 0.0, 2.0), //
+                    transaction(1.0, "Chargeback", "06 March 2024", 0.01, 
0.01, 0.0, 0.0, 0.0, 0.0, 0.99) //
+            );
+
+        });
+    }
+
     private static AdvancedPaymentData createRepaymentPaymentAllocation() {
         AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
         advancedPaymentData.setTransactionType("REPAYMENT");


Reply via email to