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 259eb75d8 FINERACT-2081: Fix Fineract writer hung infinte loop after 
merchant issued refund
259eb75d8 is described below

commit 259eb75d814b795bff7a6f29bba160a3ce0c06b1
Author: Adam Saghy <[email protected]>
AuthorDate: Fri Feb 21 15:12:49 2025 +0100

    FINERACT-2081: Fix Fineract writer hung infinte loop after merchant issued 
refund
---
 .../test/resources/features/LoanChargeOff.feature  |   2 +-
 ...dvancedPaymentScheduleTransactionProcessor.java |   1 +
 .../loanproduct/calc/ProgressiveEMICalculator.java |  24 +-
 .../data/LoanInterestScheduleModelModifiers.java   |  23 ++
 .../data/ProgressiveLoanInterestScheduleModel.java |  20 +-
 .../integrationtests/BaseLoanIntegrationTest.java  |   3 +
 .../integrationtests/LoanInterestRefundTest.java   | 313 +++++++++++----------
 7 files changed, 221 insertions(+), 165 deletions(-)

diff --git 
a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature 
b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature
index ae216afe6..57207a85e 100644
--- 
a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature
+++ 
b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeOff.feature
@@ -3586,7 +3586,7 @@ Feature: Charge-off
     And Global configuration 
"is-principal-compounding-disabled-for-overdue-loans" is disabled
 
   @Skip @TestRailId:C3360 @AdvancedPaymentAllocation
-  Scenario: Verify accelerate maturity to charge-off date when interest 
recalculation is enabled - case when charge-off occurs with adjustment to last 
installment
+  Scenario: SKIPPED-Verify accelerate maturity to charge-off date when 
interest recalculation is enabled - case when charge-off occurs with adjustment 
to last installment
     When Admin sets the business date to "01 January 2024"
     When Admin creates a client with random data
     When Admin creates a fully customized loan with the following data:
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 49df1228c..f7c135710 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
@@ -2250,6 +2250,7 @@ public class AdvancedPaymentScheduleTransactionProcessor 
extends AbstractLoanRep
         
lastPeriod.setEmi(lastPeriod.getDuePrincipal().add(totalPrincipal).add(newInterest));
 
         emiCalculator.calculateRateFactorForRepaymentPeriod(lastPeriod, 
transactionCtx.getModel());
+        transactionCtx.getModel().disableEMIRecalculation();
 
         for (LoanTransaction processTransaction : transactionsToBeReprocessed) 
{
             emiCalculator.addBalanceCorrection(transactionCtx.getModel(), 
processTransaction.getTransactionDate(),
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 a48cd7de3..c941569e7 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
@@ -182,20 +182,25 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
             return;
         }
         Optional<RepaymentPeriod> repaymentPeriod = 
findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate);
+        boolean transactionDateIsBefore = 
transactionDate.isBefore(repaymentPeriod.get().getFromDate());
         repaymentPeriod.ifPresent(rp -> 
rp.addPaidPrincipalAmount(principalAmount));
         // If it is paid late, we need to calculate with the period due date
         LocalDate balanceCorrectionDate = 
DateUtils.isBefore(repaymentPeriodDueDate, transactionDate) ? 
repaymentPeriodDueDate
                 : transactionDate;
         addBalanceCorrection(scheduleModel, balanceCorrectionDate, 
principalAmount.negated());
-
-        repaymentPeriod.ifPresent(rp -> {
-            // If any period total paid > calculated EMI, then set EMI to 
total paid -> effectively it is marked as
-            // fully paid
-            if 
(rp.getTotalPaidAmount().isGreaterThan(rp.getEmiPlusChargeback())) {
-                
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalChargebackAmount()));
+        if (scheduleModel.isEMIRecalculationEnabled()) {
+            repaymentPeriod.ifPresent(rp -> {
+                // If any period total paid > calculated EMI, then set EMI to 
total paid -> effectively it is marked as
+                // fully paid
+                if (transactionDateIsBefore && 
rp.getTotalPaidAmount().isGreaterThan(rp.getEmiPlusChargeback())) {
+                    
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalChargebackAmount()));
+                } else if (transactionDateIsBefore
+                        && 
rp.getTotalPaidAmount().isEqualTo(rp.getOriginalEmi().add(rp.getTotalChargebackAmount())))
 {
+                    
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalChargebackAmount()));
+                }
                 calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
-            }
-        });
+            });
+        }
     }
 
     private void 
addChargebackAmountsToInterestPeriod(ProgressiveLoanInterestScheduleModel 
scheduleModel, LocalDate transactionDate,
@@ -357,7 +362,6 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
             
interestPeriod.addChargebackPrincipalAmount(nextInterestPeriod.getChargebackPrincipal());
             
interestPeriod.addChargebackInterestAmount(nextInterestPeriod.getChargebackInterest());
         }
-
         repaymentPeriod.getInterestPeriods().subList(nextIdx, 
repaymentPeriod.getInterestPeriods().size()).clear();
         scheduleModelCopy.repaymentPeriods().forEach(rp -> 
rp.getInterestPeriods().removeIf(ip -> ip.getDueDate().isAfter(targetDate)));
         calculateRateFactorForPeriods(scheduleModelCopy.repaymentPeriods(), 
scheduleModelCopy);
@@ -374,7 +378,7 @@ public final class ProgressiveEMICalculator implements 
EMICalculator {
             final ProgressiveLoanInterestScheduleModel scheduleModel, final 
EmiChangeOperation operation) {
         final List<RepaymentPeriod> relatedRepaymentPeriods = 
scheduleModel.getRelatedRepaymentPeriods(calculateFromRepaymentPeriodDueDate);
         final boolean onlyOnActualModelShouldApply = scheduleModel.isEmpty()
-                || operation.getAction() == 
EmiChangeOperation.Action.INTEREST_RATE_CHANGE || 
scheduleModel.isCopiedForCalculation();
+                || operation.getAction() == 
EmiChangeOperation.Action.INTEREST_RATE_CHANGE || scheduleModel.isCopy();
 
         calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
         calculateOutstandingBalance(scheduleModel);
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/LoanInterestScheduleModelModifiers.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/LoanInterestScheduleModelModifiers.java
new file mode 100644
index 000000000..e275ac918
--- /dev/null
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/LoanInterestScheduleModelModifiers.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanproduct.calc.data;
+
+public enum LoanInterestScheduleModelModifiers {
+    EMI_RECALCULATION, COPY,
+}
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java
index 279d624ed..7dfa1e075 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java
@@ -19,6 +19,8 @@
 package org.apache.fineract.portfolio.loanproduct.calc.data;
 
 import static 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper.isInPeriod;
+import static 
org.apache.fineract.portfolio.loanproduct.calc.data.LoanInterestScheduleModelModifiers.COPY;
+import static 
org.apache.fineract.portfolio.loanproduct.calc.data.LoanInterestScheduleModelModifiers.EMI_RECALCULATION;
 
 import jakarta.validation.constraints.NotNull;
 import java.math.BigDecimal;
@@ -56,7 +58,7 @@ public class ProgressiveLoanInterestScheduleModel {
     private final Integer installmentAmountInMultiplesOf;
     private final MathContext mc;
     private final Money zero;
-    private final boolean isCopiedForCalculation;
+    private final Map<LoanInterestScheduleModelModifiers, Boolean> modifiers;
 
     public ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> 
repaymentPeriods,
             final LoanProductMinimumRepaymentScheduleRelatedDetail 
loanProductRelatedDetail,
@@ -68,7 +70,7 @@ public class ProgressiveLoanInterestScheduleModel {
         this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
         this.mc = mc;
         this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
-        this.isCopiedForCalculation = false;
+        modifiers = new HashMap<>(Map.of(EMI_RECALCULATION, true, COPY, 
false));
     }
 
     private ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> 
repaymentPeriods, final TreeSet<InterestRate> interestRates,
@@ -83,7 +85,7 @@ public class ProgressiveLoanInterestScheduleModel {
         this.loanTermVariations = loanTermVariations;
         this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
         this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
-        this.isCopiedForCalculation = isCopiedForCalculation;
+        modifiers = new HashMap<>(Map.of(EMI_RECALCULATION, true, COPY, 
isCopiedForCalculation));
     }
 
     public ProgressiveLoanInterestScheduleModel deepCopy(MathContext mc) {
@@ -405,4 +407,16 @@ public class ProgressiveLoanInterestScheduleModel {
         return loanTermVariationsData.stream()
                 .collect(Collectors.groupingBy(ltvd -> 
LoanTermVariationType.fromInt(ltvd.getTermType().getId().intValue())));
     }
+
+    public void disableEMIRecalculation() {
+        this.modifiers.put(EMI_RECALCULATION, false);
+    }
+
+    public boolean isEMIRecalculationEnabled() {
+        return this.modifiers.get(EMI_RECALCULATION);
+    }
+
+    public boolean isCopy() {
+        return this.modifiers.get(COPY);
+    }
 }
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 8b5448f10..f1d3f15fc 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
@@ -767,6 +767,7 @@ public abstract class BaseLoanIntegrationTest extends 
IntegrationTest {
     }
 
     protected void verifyTRJournalEntries(Long transactionId, Journal... 
entries) {
+        Assertions.assertNotNull(transactionId, "transactionId is null");
         GetJournalEntriesTransactionIdResponse journalEntriesForLoan = 
journalEntryHelper.getJournalEntries("L" + transactionId.toString());
         Assertions.assertEquals(entries.length, 
journalEntriesForLoan.getPageItems().size());
         Arrays.stream(entries).forEach(journalEntry -> {
@@ -1425,6 +1426,8 @@ public abstract class BaseLoanIntegrationTest extends 
IntegrationTest {
     public static class FuturePaymentAllocationRule {
 
         public static final String LAST_INSTALLMENT = "LAST_INSTALLMENT";
+        public static final String NEXT_INSTALLMENT = "NEXT_INSTALLMENT";
+
     }
 
     public static class SupportedInterestRefundTypesItem {
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 4f1f91e28..a39b2bd58 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
@@ -29,16 +29,16 @@ import io.restassured.http.ContentType;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
 import java.math.BigDecimal;
-import java.time.format.DateTimeFormatter;
 import java.util.List;
-import java.util.Locale;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
 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.GetLoansLoanIdTransactions;
 import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
+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.PostLoansLoanIdTransactionsResponse;
@@ -48,6 +48,7 @@ import 
org.apache.fineract.integrationtests.common.BusinessStepHelper;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -90,12 +91,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "22 January 2021", 
1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "22 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"),
                     transaction(1000.0, "Payout Refund", "22 January 2021"));
         });
@@ -118,12 +118,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("MerchantIssuedRefund", "22 January 
2021", 1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "MerchantIssuedRefund", "22 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"),
                     transaction(1000.0, "Merchant Issued Refund", "22 January 
2021"));
         });
@@ -143,8 +142,8 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
                     6, null);
             Assertions.assertNotNull(loanId);
             disburseLoan(loanId, BigDecimal.valueOf(600), "1 January 2021");
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("MerchantIssuedRefund", "1 January 
2021", 600F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "MerchantIssuedRefund", "1 January 2021", 600.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
@@ -177,8 +176,8 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
             Assertions.assertNotNull(loanId);
             disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
 
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("MerchantIssuedRefund", "1 January 
2021", 980F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "MerchantIssuedRefund", "1 January 2021", 980.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
@@ -218,8 +217,8 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
             Assertions.assertNotNull(loanId);
             disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
 
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("MerchantIssuedRefund", "1 January 
2021", 1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "MerchantIssuedRefund", "1 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
@@ -261,12 +260,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "22 January 2021", 
1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "22 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, //
                     transaction(1000.0, "Disbursement", "01 January 2021"), //
                     transaction(1000.0, "Payout Refund", "22 January 2021"), //
@@ -319,12 +317,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("MerchantIssuedRefund", "22 January 
2021", 1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "MerchantIssuedRefund", "22 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(1000.0, "Merchant Issued Refund", "22 January 
2021"), //
                     transaction(5.75, "Accrual", "22 January 2021"), //
@@ -351,8 +348,8 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("MerchantIssuedRefund", "22 January 
2021", 1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "MerchantIssuedRefund", "22 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
@@ -391,8 +388,8 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
                     12, null);
             Assertions.assertNotNull(loanId);
             disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("MerchantIssuedRefund", "1 January 
2021", 1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "MerchantIssuedRefund", "1 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
@@ -429,8 +426,8 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
                     12, null);
             Assertions.assertNotNull(loanId);
             disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment("Repayment",
-                    "1 January 2021", 1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "Repayment", "1 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
@@ -473,12 +470,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "22 January 2021", 
1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "22 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(1000.0, "Payout Refund", "22 January 2021"), //
                     transaction(5.75, "Accrual", "22 January 2021"), //
@@ -506,12 +502,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("1 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "1 February 2021", 
1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "1 February 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(1000.0, "Payout Refund", "01 February 2021"), 
//
                     transaction(8.48, "Accrual", "01 February 2021"), //
@@ -538,24 +533,22 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("1 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment("Repayment",
-                    "1 February 2021", 87.89F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "Repayment", "1 February 2021", 87.89);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"),
                     transaction(87.89, "Repayment", "01 February 2021"));
         });
 
         runAt("9 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "9 February 2021", 
1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "9 February 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(87.89, "Repayment", "01 February 2021"), //
                     transaction(1000.0, "Payout Refund", "09 February 2021"), 
//
@@ -588,12 +581,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "22 January 2021", 
1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "22 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(750.0, "Disbursement", "01 
January 2021"), //
                     transaction(250.0, "Disbursement", "01 January 2021"), //
                     transaction(1000.0, "Payout Refund", "22 January 2021"), //
@@ -627,12 +619,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "22 January 2021", 
1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "22 January 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(750.0, "Disbursement", "04 
January 2021"), //
                     transaction(250.0, "Disbursement", "01 January 2021"), //
                     transaction(1000.0, "Payout Refund", "22 January 2021"), //
@@ -667,24 +658,22 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("1 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment("Repayment",
-                    "1 February 2021", 87.82F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "Repayment", "1 February 2021", 87.82);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"),
                     transaction(500.0, "Disbursement", "07 January 2021"), 
transaction(87.82, "Repayment", "01 February 2021"));
         });
 
         runAt("9 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "9 February 2021", 
1000F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "9 February 2021", 1000.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "07 January 2021"), //
                     transaction(1000.0, "Payout Refund", "09 February 2021"), 
//
@@ -715,12 +704,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("14 December 2020", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "14 December 2020", 
500F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "14 December 2020", 500.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
December 2020"), //
                     transaction(500.0, "Payout Refund", "14 December 2020"), //
                     transaction(1.78, "Interest Refund", "14 December 2020"));
@@ -743,29 +731,24 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
             Assertions.assertNotNull(loanId);
             loanIdRef.set(loanId);
             disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
-
-            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
-            logInstallmentsOfLoanDetails(loanDetails);
         });
         runAt("1 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment("Repayment",
-                    "1 February 2021", 87.89F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "Repayment", "1 February 2021", 87.89);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(87.89, "Repayment", "01 February 2021"));
         });
         runAt("9 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "09 February 2021", 
500F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "09 February 2021", 500.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(87.89, "Repayment", "01 February 2021"), //
                     transaction(500.0, "Payout Refund", "09 February 2021"), //
@@ -795,12 +778,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "22 January 2021", 
500F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "22 January 2021", 500.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 
January 2021"), //
                     transaction(750.0, "Disbursement", "01 January 2021"), //
                     transaction(500.0, "Payout Refund", "22 January 2021"), //
@@ -832,12 +814,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "22 January 2021", 
500F, loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "22 January 2021", 500.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 
January 2021"), //
                     transaction(750.0, "Disbursement", "07 January 2021"), //
                     transaction(500.0, "Payout Refund", "22 January 2021"), //
@@ -867,20 +848,16 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         runAt("7 January 2021", () -> {
             Long loanId = loanIdRef.get();
             disburseLoan(loanId, BigDecimal.valueOf(750), "7 January 2021");
-
-            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
-            logInstallmentsOfLoanDetails(loanDetails);
         });
         runAt("1 July 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 
2021", 171.29F, loanId.intValue());
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 
2021", 171.29F, loanId.intValue());
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 
2021", 171.29F, loanId.intValue());
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 May 2021", 
171.29F, loanId.intValue());
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 June 
2021", 171.29F, loanId.intValue());
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 July 
2021", 171.32F, loanId.intValue());
-
-            logLoanTransactions(loanId);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
February 2021", 171.29);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
March 2021", 171.29);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
April 2021", 171.29);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
May 2021", 171.29);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
June 2021", 171.29);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
July 2021", 171.32);
+
             verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 
January 2021"), //
                     transaction(750.0, "Disbursement", "07 January 2021"), //
                     transaction(171.29, "Repayment", "01 February 2021"), //
@@ -894,12 +871,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("11 July 2021", () -> {
             Long loanId = loanIdRef.get();
-            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = loanTransactionHelper
-                    .makeLoanRepayment("PayoutRefund", "11 July 2021", 500.0F, 
loanId.intValue());
+            PostLoansLoanIdTransactionsResponse 
postLoansLoanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(loanId,
+                    "PayoutRefund", "11 July 2021", 500.0);
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
             
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
-            logLoanTransactions(loanId);
             verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 
January 2021"), //
                     transaction(750.0, "Disbursement", "07 January 2021"), //
                     transaction(171.29, "Repayment", "01 February 2021"), //
@@ -935,7 +911,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("14 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"14 January 2021", 500F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "14 January 2021", 500.0);
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Merchant Issued Refund", "14 January 
2021"), //
@@ -943,8 +919,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "22 
January 2021", 500F, loanId.intValue());
-            logLoanTransactions(loanId);
+            loanTransactionHelper.makeLoanRepayment(loanId, "PayoutRefund", 
"22 January 2021", 500.0);
             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"), //
@@ -975,14 +950,14 @@ 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(loanId, "Repayment", "1 
February 2021", 171.50);
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(171.5, "Repayment", "01 February 2021"));
         });
         runAt("9 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "9 
February 2021", 500F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "9 February 2021", 500.0);
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(171.5, "Repayment", "01 February 2021"), //
@@ -991,7 +966,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("25 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "25 
February 2021", 250F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "PayoutRefund", 
"25 February 2021", 250.0);
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(171.5, "Repayment", "01 February 2021"), //
@@ -1026,7 +1001,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"22 January 2021", 500F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "22 January 2021", 500.0);
 
             verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 
January 2021"), //
                     transaction(750.0, "Disbursement", "01 January 2021"), //
@@ -1036,7 +1011,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("26 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "26 
January 2021", 400F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "PayoutRefund", 
"26 January 2021", 400.0);
 
             verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 
January 2021"), //
                     transaction(750.0, "Disbursement", "01 January 2021"), //
@@ -1048,7 +1023,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("1 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 
2021", 100.84F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
February 2021", 100.84);
 
             verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 
January 2021"), //
                     transaction(750.0, "Disbursement", "01 January 2021"), //
@@ -1093,7 +1068,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"22 January 2021", 250F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "22 January 2021", 250.0);
 
             verifyTransactions(loanId, transaction(200.0, "Disbursement", "01 
January 2021"), //
                     transaction(300.0, "Disbursement", "01 January 2021"), //
@@ -1104,8 +1079,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);
+            loanTransactionHelper.makeLoanRepayment(loanId, "PayoutRefund", 
"26 January 2021", 400.0);
 
             verifyTransactions(loanId, transaction(200.0, "Disbursement", "01 
January 2021"), //
                     transaction(300.0, "Disbursement", "01 January 2021"), //
@@ -1118,9 +1092,9 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("1 April 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 
2021", 171.41F, loanId.intValue());
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 
2021", 171.41F, loanId.intValue());
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 
2021", 11.24F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
February 2021", 171.41);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
March 2021", 171.41);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
April 2021", 11.24);
 
             verifyTransactions(loanId, transaction(200.0, "Disbursement", "01 
January 2021"), //
                     transaction(300.0, "Disbursement", "01 January 2021"), //
@@ -1168,7 +1142,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("1 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 
2021", 171.41F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
February 2021", 171.41);
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
@@ -1176,7 +1150,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("13 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "13 
February 2021", 250F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "PayoutRefund", 
"13 February 2021", 250.0);
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
@@ -1187,8 +1161,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("24 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"24 February 2021", 400F, loanId.intValue());
-            logLoanTransactions(loanId);
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "24 February 2021", 400.0);
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
@@ -1201,8 +1174,8 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         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.25F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
March 2021", 171.41);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
April 2021", 11.25);
             GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
 
             Assertions.assertNotNull(loanDetails);
@@ -1237,7 +1210,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("1 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 
2021", 171.41F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
February 2021", 171.41);
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
@@ -1245,7 +1218,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("13 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "13 
February 2021", 250F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "PayoutRefund", 
"13 February 2021", 250.0);
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
@@ -1257,13 +1230,12 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
 
         runAt("1 April 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 
2021", 171.41F, loanId.intValue());
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 
2021", 171.41F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
March 2021", 171.41);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
April 2021", 171.41);
         });
         runAt("6 April 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "6 
April 2021", 400F, loanId.intValue());
-            logLoanTransactions(loanId);
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "6 April 2021", 400.0);
 
             verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 
January 2021"), //
                     transaction(500.0, "Disbursement", "05 January 2021"), //
@@ -1307,7 +1279,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("12 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "12 
January 2021", 400F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "PayoutRefund", 
"12 January 2021", 400.0);
 
             verifyTransactions(loanId, //
                     transaction(1000.0, "Disbursement", "01 January 2021"), //
@@ -1317,7 +1289,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"17 January 2021", 150F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "17 January 2021", 150.0);
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(400.0, "Payout Refund", "12 January 2021"), //
@@ -1329,12 +1301,11 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
 
         runAt("1 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 
2021", 171.5F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
February 2021", 171.5);
         });
         runAt("8 February 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "8 
February 2021", 250F, loanId.intValue());
-            logLoanTransactions(loanId);
+            loanTransactionHelper.makeLoanRepayment(loanId, "PayoutRefund", "8 
February 2021", 250.0);
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(400.0, "Payout Refund", "12 January 2021"), //
@@ -1348,7 +1319,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("1 March 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 
2021", 30.43F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "1 
March 2021", 30.43);
             GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
             Assertions.assertNotNull(loanDetails);
             Assertions.assertNotNull(loanDetails.getStatus());
@@ -1378,16 +1349,14 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"22 January 2021", 1000F, loanId.intValue());
-            logLoanTransactions(loanId);
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "22 January 2021", 1000.0);
 
             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);
+            loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "10 
January 2021", 85.63);
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(85.63, "Repayment", "10 January 2021"), //
@@ -1417,14 +1386,12 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
             Assertions.assertNotNull(loanId);
 
             disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "1 
January 2021", 1000F, loanId.intValue());
-            logLoanTransactions(loanId);
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "1 January 2021", 1000.0);
 
             verifyTransactions(loanId, //
                     transaction(1000.0, "Disbursement", "01 January 2021"), //
                     transaction(1000.0, "Merchant Issued Refund", "01 January 
2021") //
             );
-            logLoanTransactions(loanId);
         });
     }
 
@@ -1450,8 +1417,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("10 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            Long response = 
loanTransactionHelper.makeLoanRepayment("Repayment", "10 January 2021", 85.63F, 
loanId.intValue())
-                    .getResourceId();
+            Long response = loanTransactionHelper.makeLoanRepayment(loanId, 
"Repayment", "10 January 2021", 85.63).getResourceId();
             Assertions.assertNotNull(response);
             repaymentIdRef.set(response);
 
@@ -1461,8 +1427,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"22 January 2021", 1000F, loanId.intValue());
-            logLoanTransactions(loanId);
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "22 January 2021", 1000.0);
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     transaction(85.63, "Repayment", "10 January 2021"), //
@@ -1472,8 +1437,7 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
             );
 
             Long repaymentId = repaymentIdRef.get();
-            loanTransactionHelper.reverseLoanTransaction(loanId.intValue(), 
repaymentId, "10 January 2021", responseSpec);
-            logLoanTransactions(loanId);
+            loanTransactionHelper.reverseLoanTransaction(loanId, repaymentId, 
"10 January 2021");
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 
January 2021"), //
                     reversedTransaction(85.63, "Repayment", "10 January 
2021"), //
@@ -1515,12 +1479,14 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
         });
         runAt("22 January 2021", () -> {
             Long loanId = loanIdRef.get();
-            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", 
"22 January 2021", 1000F, loanId.intValue());
-            logLoanTransactions(loanId);
-            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
-            Optional<GetLoansLoanIdTransactions> optInterestRefundTransaction 
= loanDetails.getTransactions().stream()
-                    .filter(item -> Objects.equals(item.getType().getValue(), 
"Interest Refund")).findFirst();
-            final Long interestRefundTransactionId = 
optInterestRefundTransaction.get().getId();
+            loanTransactionHelper.makeLoanRepayment(loanId, 
"MerchantIssuedRefund", "22 January 2021", 1000.0);
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId);
+            Assertions.assertNotNull(loanDetails.getTransactions());
+            Optional<GetLoansLoanIdTransactions> optInterestRefundTransaction 
= loanDetails.getTransactions().stream().filter(item -> {
+                Assertions.assertNotNull(item.getType());
+                return Objects.equals(item.getType().getValue(), "Interest 
Refund");
+            }).findFirst();
+            final Long interestRefundTransactionId = 
optInterestRefundTransaction.orElseThrow().getId();
 
             CallFailedRuntimeException exception = 
assertThrows(CallFailedRuntimeException.class,
                     () -> loanTransactionHelper.reverseLoanTransaction(loanId, 
interestRefundTransactionId,
@@ -1531,36 +1497,81 @@ public class LoanInterestRefundTest extends 
BaseLoanIntegrationTest {
 
             Optional<GetLoansLoanIdTransactions> optMerchantIssuedTransaction 
= loanDetails.getTransactions().stream()
                     .filter(item -> Objects.equals(item.getType().getValue(), 
"Merchant Issued Refund")).findFirst();
-            final Long merchantIssuedTransactionId = 
optMerchantIssuedTransaction.get().getId();
+            final Long merchantIssuedTransactionId = 
optMerchantIssuedTransaction.orElseThrow().getId();
 
             loanTransactionHelper.reverseLoanTransaction(loanId, 
merchantIssuedTransactionId,
                     new 
PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat(DATETIME_PATTERN).transactionDate("22
 January 2021")
                             .transactionAmount(0.0).locale("en"));
 
-            loanDetails = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId.intValue());
+            loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            Assertions.assertNotNull(loanDetails.getTransactions());
             optInterestRefundTransaction = 
loanDetails.getTransactions().stream()
                     .filter(item -> Objects.equals(item.getType().getValue(), 
"Interest Refund")).findFirst();
-            assertEquals(Boolean.TRUE, 
optInterestRefundTransaction.get().getManuallyReversed());
+            assertEquals(Boolean.TRUE, 
optInterestRefundTransaction.orElseThrow().getManuallyReversed());
         });
     }
 
-    private void logInstallmentsOfLoanDetails(GetLoansLoanIdResponse 
loanDetails) {
-        log.info("index, dueDate, principal, fee, penalty, interest");
-        if (loanDetails != null && loanDetails.getRepaymentSchedule() != null 
&& loanDetails.getRepaymentSchedule().getPeriods() != null) {
-            loanDetails.getRepaymentSchedule().getPeriods()
-                    .forEach(period -> log.info("{}, \"{}\", {}, {}, {}, {}", 
period.getPeriod(),
-                            DateTimeFormatter.ofPattern(DATETIME_PATTERN, 
Locale.ENGLISH)
-                                    
.format(Objects.requireNonNull(period.getDueDate())),
-                            period.getPrincipalDue(), 
period.getFeeChargesDue(), period.getPenaltyChargesDue(), 
period.getInterestDue()));
-        }
+    AdvancedPaymentData 
createPaymentAllocationInterestPrincipalPenaltyFee(String transactionType, 
String futureInstallmentAllocationRule) {
+        AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
+        advancedPaymentData.setTransactionType(transactionType);
+        
advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule);
+
+        List<PaymentAllocationOrder> paymentAllocationOrders = 
getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_INTEREST,
+                PaymentAllocationType.PAST_DUE_PRINCIPAL, 
PaymentAllocationType.PAST_DUE_PENALTY, PaymentAllocationType.PAST_DUE_FEE,
+                PaymentAllocationType.DUE_INTEREST, 
PaymentAllocationType.DUE_PRINCIPAL, PaymentAllocationType.DUE_PENALTY,
+                PaymentAllocationType.DUE_FEE, 
PaymentAllocationType.IN_ADVANCE_INTEREST, 
PaymentAllocationType.IN_ADVANCE_PRINCIPAL,
+                PaymentAllocationType.IN_ADVANCE_PENALTY, 
PaymentAllocationType.IN_ADVANCE_FEE);
+
+        advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders);
+        return advancedPaymentData;
     }
 
-    private void logLoanTransactions(Long loanId) {
-        GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
-        if (loanDetails.getTransactions() != null) {
-            loanDetails.getTransactions()
-                    .forEach(tr -> log.info("Transaction {}  {}  {} ", 
tr.getType().getValue(), tr.getDate(), tr.getAmount()));
+    private Long createLoanProduct() {
+        PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(create4IProgressive() //
+                .daysInMonthType(DaysInMonthType.ACTUAL) //
+                .daysInYearType(DaysInYearType.ACTUAL) //
+                .isInterestRecalculationEnabled(true) //
+                
.addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND)
 //
+                
.addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND)
 //
+                
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+                .paymentAllocation(List.of(//
+                        
createPaymentAllocationInterestPrincipalPenaltyFee("DEFAULT", 
FuturePaymentAllocationRule.NEXT_INSTALLMENT), //
+                        
createPaymentAllocationInterestPrincipalPenaltyFee("PAYOUT_REFUND", 
FuturePaymentAllocationRule.LAST_INSTALLMENT), //
+                        
createPaymentAllocationInterestPrincipalPenaltyFee("MERCHANT_ISSUED_REFUND",
+                                
FuturePaymentAllocationRule.LAST_INSTALLMENT))) //
+        );
+        Assertions.assertNotNull(loanProduct.getResourceId());
+        return loanProduct.getResourceId();
+    }
+
+    private Long loanProductId = null;
+
+    private Long getOrCreateLoanProduct() {
+        if (loanProductId == null) {
+            loanProductId = createLoanProduct();
         }
+        return loanProductId;
     }
 
+    @Test
+    public void verifyMerchantIssuedRefundPostingForBackdatedLoan() {
+        runAt("29 January 2025", () -> {
+            Long loanProductId = getOrCreateLoanProduct();
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProductId, "29 August 2024", 450.0, 26.0, 12, null);
+            Assertions.assertNotNull(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(450.0), "29 August 2024");
+
+            PostLoansLoanIdTransactionsResponse repayment = 
loanTransactionHelper.makeLoanRepayment(loanId, "MerchantIssuedRefund",
+                    "29 January 2025", 500.0);
+            Assertions.assertNotNull(repayment);
+            Assertions.assertNotNull(repayment.getResourceId());
+
+            verifyTransactions(loanId, //
+                    transaction(450.0, "Disbursement", "29 August 2024"), //
+                    transaction(500.0, "Merchant Issued Refund", "29 January 
2025"), //
+                    transaction(48.94, "Interest Refund", "29 January 2025"), 
//
+                    transaction(48.94, "Accrual", "29 January 2025") //
+            ); //
+        });
+    }
 }

Reply via email to