You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ad...@apache.org on 2024/11/04 12:06:41 UTC

(fineract) branch develop updated: FINERACT-2107: Interest Refund - Multiple Partial Repayment

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 174f4178b FINERACT-2107: Interest Refund - Multiple Partial Repayment
174f4178b is described below

commit 174f4178b7465213671517e4f6aaab3fb3137572
Author: Soma Sörös <so...@dpc.hu>
AuthorDate: Wed Oct 30 11:21:11 2024 +0100

    FINERACT-2107: Interest Refund - Multiple Partial Repayment
---
 .../ProgressiveLoanInterestRefundServiceImpl.java  |  25 +-
 .../integrationtests/LoanInterestRefundTest.java   | 418 ++++++++++++++++++++-
 2 files changed, 435 insertions(+), 8 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java
index 16f1cb3ec..f9ceda2e2 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanInterestRefundServiceImpl.java
@@ -27,13 +27,17 @@ import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.ProgressiveLoanInterestScheduleModel;
 import org.apache.fineract.portfolio.loanaccount.starter.AdvancedPaymentScheduleTransactionProcessorCondition;
 import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator;
+import org.apache.fineract.portfolio.loanproduct.domain.LoanSupportedInterestRefundTypes;
 import org.springframework.context.annotation.Conditional;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Propagation;
@@ -78,15 +82,28 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
         BigDecimal payableInterest = BigDecimal.ZERO;
         if (loan.getLoanTransactions().stream().anyMatch(LoanTransaction::isDisbursement)) {
             List<LoanTransaction> transactionsToReprocess = new ArrayList<>();
-            loan.getLoanTransactions().stream().filter(lt -> !lt.isReversed()) //
-                    .filter(lt -> !lt.isAccrual() && !lt.isAccrualActivity()) //
+            List<LoanTransactionType> interestRefundTypes = loan.getLoanProductRelatedDetail().getSupportedInterestRefundTypes().stream()
+                    .map(LoanSupportedInterestRefundTypes::getTransactionType).toList();
+            // add already interest refunded amounts to refund amount
+            // it is necessary to avoid multi disbursed refund
+            loan.getLoanTransactions().stream() //
+                    .filter(lt -> !lt.isReversed()) //
+                    .filter(lt -> interestRefundTypes.contains(lt.getTypeOf())) //
+                    .forEach(t -> refundFinal.set(refundFinal.get().add(t.getAmount()))); //
+            loan.getLoanTransactions().stream() //
+                    .filter(lt -> !lt.isReversed()) //
+                    .filter(lt -> !lt.isAccrual() && !lt.isAccrualActivity() && !lt.isInterestRefund()) //
+                    .filter(loanTransaction -> !interestRefundTypes.contains(loanTransaction.getTypeOf())) //
                     .forEach(lt -> simulateRepaymentForDisbursements(lt, refundFinal, transactionsToReprocess)); //
 
             List<LoanRepaymentScheduleInstallment> installmentsToReprocess = new ArrayList<>(
                     loan.getRepaymentScheduleInstallments().stream().filter(i -> !i.isReAged() && !i.isAdditional()).toList());
 
-            ProgressiveLoanInterestScheduleModel modelAfter = processor.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(),
-                    transactionsToReprocess, loan.getCurrency(), installmentsToReprocess, loan.getActiveCharges()).getRight();
+            Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> reprocessResult = processor
+                    .reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), transactionsToReprocess, loan.getCurrency(),
+                            installmentsToReprocess, loan.getActiveCharges());
+            loan.getLoanTransactions().addAll(reprocessResult.getLeft().getCurrentTransactionToOldId().keySet());
+            ProgressiveLoanInterestScheduleModel modelAfter = reprocessResult.getRight();
 
             payableInterest = installmentsToReprocess.stream() //
                     .map(installment -> emiCalculator //
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 35ef80eef..c720099a8 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
@@ -72,7 +72,6 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             PostLoanProductsResponse loanProduct = loanProductHelper
                     .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
                             .daysInYearType(DaysInYearType.ACTUAL) //
-                            //
                             .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
             );
             Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.9,
@@ -101,7 +100,6 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             PostLoanProductsResponse loanProduct = loanProductHelper
                     .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
                             .daysInYearType(DaysInYearType.ACTUAL) //
-                            //
                             .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
             );
             Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.9,
@@ -158,7 +156,8 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             checkTransactionWasNotReverseReplayed(postLoansLoanIdTransactionsResponse.getLoanId(),
                     postLoansLoanIdTransactionsResponse.getSubResourceId());
 
-            verifyTRJournalEntries(postLoansLoanIdTransactionsResponse.getResourceId(), journalEntry(1000, fundSource, "DEBIT"), //
+            verifyTRJournalEntries(postLoansLoanIdTransactionsResponse.getResourceId(), //
+                    journalEntry(1000, fundSource, "DEBIT"), //
                     journalEntry(5.75, interestReceivableAccount, "CREDIT"), //
                     journalEntry(994.25, loansReceivableAccount, "CREDIT"));
 
@@ -520,7 +519,6 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             PostLoanProductsResponse loanProduct = loanProductHelper
                     .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
                             .daysInYearType(DaysInYearType.ACTUAL) //
-                            //
                             .disallowExpectedDisbursements(true)//
                             .multiDisburseLoan(true)//
                             .maxTrancheCount(2).addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND) //
@@ -653,6 +651,418 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
         });
     }
 
+    @Test
+    public void verifyUC11() {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2021", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+                            .daysInYearType(DaysInYearType.ACTUAL) //
+                            .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND) //
+                            .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
+                            .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+            );
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
+                    6, null);
+            Assertions.assertNotNull(loanId);
+            loanIdRef.set(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
+
+        });
+        runAt("14 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "14 January 2021", 500F, loanId.intValue());
+
+            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"));
+        });
+        runAt("22 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "22 January 2021", 500F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Merchant Issued Refund", "14 January 2021"), //
+                    transaction(1.78, "Interest Refund", "14 January 2021"), //
+                    transaction(500.0, "Payout Refund", "22 January 2021"), //
+                    transaction(2.87, "Interest Refund", "22 January 2021") //
+            );
+        });
+    }
+
+    @Test
+    public void verifyUC12() {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2021", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+                            .daysInYearType(DaysInYearType.ACTUAL) //
+                            .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND) //
+                            .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
+                            .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+            );
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
+                    6, null);
+            Assertions.assertNotNull(loanId);
+            loanIdRef.set(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
+
+        });
+        runAt("1 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 2021", 171.50F, loanId.intValue());
+
+            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());
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(171.5, "Repayment", "01 February 2021"), //
+                    transaction(500.0, "Merchant Issued Refund", "09 February 2021"), //
+                    transaction(5.34, "Interest Refund", "09 February 2021"));
+        });
+        runAt("25 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "25 February 2021", 250F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(171.5, "Repayment", "01 February 2021"), //
+                    transaction(500.0, "Merchant Issued Refund", "09 February 2021"), //
+                    transaction(5.34, "Interest Refund", "09 February 2021"), //
+                    transaction(250.0, "Payout Refund", "25 February 2021"), //
+                    transaction(3.78, "Interest Refund", "25 February 2021") //
+            );
+        });
+    }
+
+    @Test
+    public void verifyUC13() {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2021", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+                            .daysInYearType(DaysInYearType.ACTUAL) //
+                            .multiDisburseLoan(true)//
+                            .disallowExpectedDisbursements(true)//
+                            .maxTrancheCount(2).addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND) //
+                            .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
+                            .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+            );
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
+                    12, null);
+            Assertions.assertNotNull(loanId);
+            loanIdRef.set(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(250), "1 January 2021");
+            disburseLoan(loanId, BigDecimal.valueOf(750), "1 January 2021");
+
+        });
+        runAt("22 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "22 January 2021", 500F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2021"), //
+                    transaction(750.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Merchant Issued Refund", "22 January 2021"), //
+                    transaction(2.88, "Interest Refund", "22 January 2021") //
+            );
+        });
+        runAt("26 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "26 January 2021", 400F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2021"), //
+                    transaction(750.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Merchant Issued Refund", "22 January 2021"), //
+                    transaction(2.88, "Interest Refund", "22 January 2021"), //
+                    transaction(400.0, "Payout Refund", "26 January 2021"), //
+                    transaction(2.74, "Interest Refund", "26 January 2021") //
+            );
+        });
+        runAt("1 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 2021", 100.85F, loanId.intValue());
+            GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            Assertions.assertNotNull(loanDetails);
+            Assertions.assertNotNull(loanDetails.getStatus());
+            Assertions.assertEquals(600, loanDetails.getStatus().getId());
+        });
+    }
+
+    @Test
+    public void verifyUC14() {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2021", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+                            .daysInYearType(DaysInYearType.ACTUAL) //
+                            .multiDisburseLoan(true)//
+                            .disallowExpectedDisbursements(true)//
+                            .maxTrancheCount(3).addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND) //
+                            .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
+                            .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+            );
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
+                    6, null);
+            Assertions.assertNotNull(loanId);
+            loanIdRef.set(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(200), "1 January 2021");
+            disburseLoan(loanId, BigDecimal.valueOf(300), "1 January 2021");
+        });
+        runAt("5 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            disburseLoan(loanId, BigDecimal.valueOf(500), "5 January 2021");
+
+        });
+        runAt("22 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "22 January 2021", 250F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(200.0, "Disbursement", "01 January 2021"), //
+                    transaction(300.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Disbursement", "05 January 2021"), //
+                    transaction(250.0, "Merchant Issued Refund", "22 January 2021"), //
+                    transaction(1.44, "Interest Refund", "22 January 2021") //
+            );
+        });
+        runAt("26 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "26 January 2021", 400F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(200.0, "Disbursement", "01 January 2021"), //
+                    transaction(300.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Disbursement", "05 January 2021"), //
+                    transaction(250.0, "Merchant Issued Refund", "22 January 2021"), //
+                    transaction(1.44, "Interest Refund", "22 January 2021"), //
+                    transaction(400.0, "Payout Refund", "26 January 2021"), //
+                    transaction(2.57, "Interest Refund", "26 January 2021") //
+            );
+        });
+        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.26F, loanId.intValue());
+            GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+
+            Assertions.assertNotNull(loanDetails);
+            Assertions.assertNotNull(loanDetails.getStatus());
+            Assertions.assertEquals(600, loanDetails.getStatus().getId());
+        });
+    }
+
+    @Test
+    public void verifyUC15() {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2021", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+                            .daysInYearType(DaysInYearType.ACTUAL) //
+                            .multiDisburseLoan(true)//
+                            .disallowExpectedDisbursements(true)//
+                            .maxTrancheCount(3).addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND) //
+                            .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
+                            .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+            );
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
+                    6, null);
+            Assertions.assertNotNull(loanId);
+            loanIdRef.set(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(500), "1 January 2021");
+        });
+        runAt("5 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            disburseLoan(loanId, BigDecimal.valueOf(500), "5 January 2021");
+
+        });
+        runAt("1 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 2021", 171.50F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Disbursement", "05 January 2021"), //
+                    transaction(171.5, "Repayment", "01 February 2021"));
+        });
+        runAt("13 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "13 February 2021", 250F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Disbursement", "05 January 2021"), //
+                    transaction(171.5, "Repayment", "01 February 2021"), //
+                    transaction(250.0, "Payout Refund", "13 February 2021"), //
+                    transaction(2.96, "Interest Refund", "13 February 2021") //
+            );
+        });
+        runAt("24 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "24 February 2021", 400F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Disbursement", "05 January 2021"), //
+                    transaction(171.5, "Repayment", "01 February 2021"), //
+                    transaction(250.0, "Payout Refund", "13 February 2021"), //
+                    transaction(2.96, "Interest Refund", "13 February 2021"), //
+                    transaction(400.0, "Merchant Issued Refund", "24 February 2021"), //
+                    transaction(5.76, "Interest Refund", "24 February 2021") //
+            );
+        });
+        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.16F, loanId.intValue());
+            GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+
+            Assertions.assertNotNull(loanDetails);
+            Assertions.assertNotNull(loanDetails.getStatus());
+            Assertions.assertEquals(600, loanDetails.getStatus().getId());
+        });
+    }
+
+    @Test
+    public void verifyUC16() {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2021", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+                            .daysInYearType(DaysInYearType.ACTUAL) //
+                            .multiDisburseLoan(true)//
+                            .disallowExpectedDisbursements(true)//
+                            .maxTrancheCount(3).addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND) //
+                            .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
+                            .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+            );
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
+                    6, null);
+            Assertions.assertNotNull(loanId);
+            loanIdRef.set(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(500), "1 January 2021");
+        });
+        runAt("5 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            disburseLoan(loanId, BigDecimal.valueOf(500), "5 January 2021");
+
+        });
+        runAt("1 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 2021", 171.41F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Disbursement", "05 January 2021"), //
+                    transaction(171.41, "Repayment", "01 February 2021"));
+        });
+        runAt("13 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "13 February 2021", 250F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Disbursement", "05 January 2021"), //
+                    transaction(171.41, "Repayment", "01 February 2021"), //
+                    transaction(250.0, "Payout Refund", "13 February 2021"), //
+                    transaction(2.96, "Interest Refund", "13 February 2021") //
+            );
+        });
+
+        runAt("1 April 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 2021", 171.41F, loanId.intValue());
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 2021", 171.41F, loanId.intValue());
+        });
+        runAt("6 April 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "6 April 2021", 400F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Disbursement", "05 January 2021"), //
+                    transaction(171.41, "Repayment", "01 February 2021"), //
+                    transaction(171.41, "Repayment", "01 March 2021"), //
+                    transaction(171.41, "Repayment", "01 April 2021"), //
+                    transaction(250.0, "Payout Refund", "13 February 2021"), //
+                    transaction(2.96, "Interest Refund", "13 February 2021"), //
+                    transaction(400.0, "Merchant Issued Refund", "06 April 2021"), //
+                    transaction(10.1, "Interest Refund", "06 April 2021") //
+            );
+            GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+
+            Assertions.assertNotNull(loanDetails);
+            Assertions.assertNotNull(loanDetails.getStatus());
+            Assertions.assertEquals(700, loanDetails.getStatus().getId());
+            Assertions.assertEquals(160.15D, loanDetails.getTotalOverpaid());
+        });
+    }
+
+    @Test
+    public void verifyUC17() {
+        AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        runAt("1 January 2021", () -> {
+            PostLoanProductsResponse loanProduct = loanProductHelper
+                    .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+                            .daysInYearType(DaysInYearType.ACTUAL) //
+                            .multiDisburseLoan(true)//
+                            .disallowExpectedDisbursements(true)//
+                            .maxTrancheCount(2).addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND) //
+                            .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
+                            .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
+            );
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
+                    12, null);
+            Assertions.assertNotNull(loanId);
+            loanIdRef.set(loanId);
+            disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
+
+        });
+        runAt("12 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "12 January 2021", 400F, loanId.intValue());
+
+            verifyTransactions(loanId, //
+                    transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(400.0, "Payout Refund", "12 January 2021"), //
+                    transaction(1.20, "Interest Refund", "12 January 2021") //
+            );
+        });
+        runAt("22 January 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "17 January 2021", 150F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(400.0, "Payout Refund", "12 January 2021"), //
+                    transaction(1.20, "Interest Refund", "12 January 2021"), //
+                    transaction(150.0, "Merchant Issued Refund", "17 January 2021"), //
+                    transaction(0.66, "Interest Refund", "17 January 2021") //
+            );
+        });
+
+        runAt("1 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 2021", 171.5F, loanId.intValue());
+        });
+        runAt("8 February 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("PayoutRefund", "8 February 2021", 250F, loanId.intValue());
+
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(400.0, "Payout Refund", "12 January 2021"), //
+                    transaction(1.20, "Interest Refund", "12 January 2021"), //
+                    transaction(150.0, "Merchant Issued Refund", "17 January 2021"), //
+                    transaction(0.66, "Interest Refund", "17 January 2021"), //
+                    transaction(171.5, "Repayment", "01 February 2021"), //
+                    transaction(250.0, "Payout Refund", "08 February 2021"), //
+                    transaction(2.60, "Interest Refund", "08 February 2021") //
+            );
+        });
+        runAt("1 March 2021", () -> {
+            Long loanId = loanIdRef.get();
+            loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 2021", 30.44F, loanId.intValue());
+            GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            Assertions.assertNotNull(loanDetails);
+            Assertions.assertNotNull(loanDetails.getStatus());
+            Assertions.assertEquals(600, loanDetails.getStatus().getId());
+        });
+    }
+
     private void logInstallmentsOfLoanDetails(GetLoansLoanIdResponse loanDetails) {
         log.info("index, dueDate, principal, fee, penalty, interest");
         if (loanDetails != null && loanDetails.getRepaymentSchedule() != null && loanDetails.getRepaymentSchedule().getPeriods() != null) {