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/10/30 12:16:47 UTC
(fineract) branch develop updated: FINERACT-2107: Interest Refund - 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 001d586e3 FINERACT-2107: Interest Refund - Partial Repayment
001d586e3 is described below
commit 001d586e30073671a733355cf52743988c921595
Author: Soma Sörös <so...@dpc.hu>
AuthorDate: Tue Oct 29 17:52:41 2024 +0100
FINERACT-2107: Interest Refund - Partial Repayment
---
.../loanproduct/calc/ProgressiveEMICalculator.java | 5 +
.../ProgressiveLoanInterestRefundServiceImpl.java | 69 +++--
.../integrationtests/BaseLoanIntegrationTest.java | 7 +-
.../integrationtests/LoanInterestRefundTest.java | 305 ++++++++++++++++++---
4 files changed, 312 insertions(+), 74 deletions(-)
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 97b1d3bcc..f39e5d391 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
@@ -165,6 +165,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
interestPeriod = repaymentPeriod.getInterestPeriods().stream()
.filter(ip -> targetDate.isAfter(ip.getFromDate()) && !targetDate.isAfter(ip.getDueDate())).findFirst().orElseThrow();
}
+ scheduleModelCopy.repaymentPeriods().stream()
+ .filter(rp -> targetDate.isAfter(rp.getFromDate()) && !targetDate.isAfter(rp.getDueDate())).findFirst()
+ .ifPresent(rp -> rp.getInterestPeriods().stream()
+ .filter(ip -> targetDate.isAfter(ip.getFromDate()) && !targetDate.isAfter(ip.getDueDate()))
+ .reduce((one, two) -> two).ifPresent(ip -> ip.setDueDate(targetDate)));
interestPeriod.setDueDate(adjustedTargetDate);
int index = repaymentPeriod.getInterestPeriods().indexOf(interestPeriod);
repaymentPeriod.getInterestPeriods().subList(index + 1, repaymentPeriod.getInterestPeriods().size()).clear();
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 13253ac7c..16f1cb3ec 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
@@ -18,15 +18,15 @@
*/
package org.apache.fineract.portfolio.loanaccount.service;
+import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REPAYMENT;
+
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
@@ -54,53 +54,48 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
return loan != null && loan.isInterestBearing() && processor.accept(loan.getTransactionProcessingStrategyCode());
}
- private static boolean omitDisbursements(LoanTransaction lt, final AtomicReference<BigDecimal> refundFinal) {
+ private static void simulateRepaymentForDisbursements(LoanTransaction lt, final AtomicReference<BigDecimal> refundFinal,
+ List<LoanTransaction> collect) {
+ collect.add(lt);
if (lt.getTypeOf().isDisbursement() && refundFinal.get().compareTo(BigDecimal.ZERO) > 0) {
if (lt.getAmount().compareTo(refundFinal.get()) <= 0) {
+ collect.add(
+ new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), REPAYMENT.getValue(), lt.getDateOf(), lt.getAmount(),
+ BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, false, null, null));
refundFinal.set(refundFinal.get().subtract(lt.getAmount()));
- return false;
+ } else {
+ collect.add(
+ new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), REPAYMENT.getValue(), lt.getDateOf(), refundFinal.get(),
+ BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, false, null, null));
+ refundFinal.set(BigDecimal.ZERO);
}
}
- return true;
- }
-
- private static LoanTransaction calculateReducedAmountDisbursements(LoanTransaction lt, final AtomicReference<BigDecimal> refundFinal) {
- if (lt.getTypeOf().isDisbursement() && refundFinal.get().compareTo(BigDecimal.ZERO) > 0) {
- LoanTransaction result = new LoanTransaction(lt.getLoan(), lt.getLoan().getOffice(), lt.getTypeOf().getValue(), lt.getDateOf(),
- lt.getAmount().subtract(refundFinal.get()), lt.getPrincipalPortion(), lt.getInterestPortion(),
- lt.getFeeChargesPortion(), lt.getPenaltyChargesPortion(),
- lt.getOverPaymentPortion(lt.getLoan().getCurrency()).getAmount(), lt.isReversed(), lt.getPaymentDetail(),
- lt.getExternalId());
- refundFinal.set(BigDecimal.ZERO);
- return result;
- }
- return lt;
}
private BigDecimal totalInterest(final Loan loan, BigDecimal refundAmount, LocalDate relatedRefundTransactionDate) {
final AtomicReference<BigDecimal> refundFinal = new AtomicReference<>(refundAmount);
- List<LoanTransaction> transactionsToReprocess = loan.getLoanTransactions().stream().filter(lt -> !lt.isReversed()) //
- .filter(lt -> !lt.isAccrual() && !lt.isAccrualActivity()) //
- .filter(lt -> omitDisbursements(lt, refundFinal)) //
- .map(lt -> calculateReducedAmountDisbursements(lt, refundFinal)).toList();
- 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();
BigDecimal payableInterest = BigDecimal.ZERO;
- if (modelAfter != null && loan.getStatus().isActive()) {
- LoanRepaymentScheduleInstallment actualInstallment = loan.getRelatedRepaymentScheduleInstallment(relatedRefundTransactionDate);
- if (actualInstallment == null) {
- actualInstallment = loan.getLastLoanRepaymentScheduleInstallment();
- }
- payableInterest = emiCalculator.getPayableDetails(modelAfter, actualInstallment.getDueDate(), relatedRefundTransactionDate)
- .getPayableInterest().getAmount();
+ 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()) //
+ .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();
+
+ payableInterest = installmentsToReprocess.stream() //
+ .map(installment -> emiCalculator //
+ .getPayableDetails(modelAfter, installment.getDueDate(), relatedRefundTransactionDate) //
+ .getPayableInterest() //
+ .getAmount()) //
+ .reduce(BigDecimal.ZERO, BigDecimal::add); //
}
- BigDecimal paidInterest = installmentsToReprocess.stream().map(i -> i.getInterestPaid(loan.getCurrency())).filter(Objects::nonNull)
- .map(Money::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
- return payableInterest.add(paidInterest);
+ return payableInterest;
}
@Override
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 71dc80b44..3a7cd4ad9 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
@@ -295,7 +295,6 @@ public abstract class BaseLoanIntegrationTest {
.allowCompoundingOnEod(false)//
.canDefineInstallmentAmount(true)//
.repaymentStartDateType(1)//
- .supportedInterestRefundTypes(List.of())//
.charges(List.of())//
.principalVariationsForBorrowerCycle(List.of())//
.interestRateVariationsForBorrowerCycle(List.of())//
@@ -1296,4 +1295,10 @@ public abstract class BaseLoanIntegrationTest {
public static final String LAST_INSTALLMENT = "LAST_INSTALLMENT";
}
+
+ public static class SupportedInterestRefundTypesItem {
+
+ public static final String MERCHANT_ISSUED_REFUND = "MERCHANT_ISSUED_REFUND";
+ public static final String PAYOUT_REFUND = "PAYOUT_REFUND";
+ }
}
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 2cf9cc0d7..35ef80eef 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
@@ -26,7 +26,9 @@ import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.math.BigDecimal;
-import java.util.ArrayList;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
@@ -70,7 +72,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>()) //
+ //
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
);
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.9,
@@ -86,7 +88,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ logLoanTransactions(loanId);
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
transaction(1000.0, "Payout Refund", "22 January 2021"));
});
@@ -99,7 +101,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>())//
+ //
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
);
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.9,
@@ -115,7 +117,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ logLoanTransactions(loanId);
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
transaction(1000.0, "Merchant Issued Refund", "22 January 2021"));
});
@@ -128,7 +130,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
+ .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.PAYOUT_REFUND) //
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
);
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
@@ -144,7 +146,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ logLoanTransactions(loanId);
verifyTransactions(loanId, //
transaction(1000.0, "Disbursement", "01 January 2021"), //
transaction(1000.0, "Payout Refund", "22 January 2021"), //
@@ -185,7 +187,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
+ .addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
);
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 9.99,
@@ -201,7 +203,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ 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"),
transaction(5.75, "Interest Refund", "22 January 2021"));
@@ -215,8 +217,8 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
- .addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
+ .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,
@@ -232,7 +234,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ 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"),
transaction(5.75, "Interest Refund", "22 January 2021"));
@@ -246,8 +248,8 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
- .addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
+ .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,
@@ -263,7 +265,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ 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"),
transaction(8.48, "Interest Refund", "01 February 2021"));
@@ -277,8 +279,8 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
- .addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
+ .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,
@@ -294,7 +296,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ logLoanTransactions(loanId);
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
transaction(87.89, "Repayment", "01 February 2021"));
});
@@ -306,7 +308,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ logLoanTransactions(loanId);
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
transaction(1000.0, "Payout Refund", "09 February 2021"), transaction(87.89, "Repayment", "01 February 2021"),
transaction(10.50, "Interest Refund", "09 February 2021"));
@@ -320,11 +322,11 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>())//
+ //
.disallowExpectedDisbursements(true)//
.multiDisburseLoan(true)//
- .maxTrancheCount(2).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
- .addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
+ .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,
@@ -341,7 +343,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ 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"),
transaction(5.75, "Accrual", "22 January 2021"), transaction(5.75, "Interest Refund", "22 January 2021"));
@@ -355,9 +357,9 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>()).disallowExpectedDisbursements(true).multiDisburseLoan(true)
- .maxTrancheCount(2).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
- .addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
+ .disallowExpectedDisbursements(true).multiDisburseLoan(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,
@@ -377,7 +379,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ 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"),
transaction(5.14, "Accrual", "22 January 2021"), transaction(5.14, "Interest Refund", "22 January 2021"));
@@ -391,9 +393,10 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
- .supportedInterestRefundTypes(new ArrayList<>()).disallowExpectedDisbursements(true).multiDisburseLoan(true)
- .maxTrancheCount(2).addSupportedInterestRefundTypesItem("PAYOUT_REFUND") //
- .addSupportedInterestRefundTypesItem("MERCHANT_ISSUED_REFUND") //
+ .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,
@@ -413,7 +416,7 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ 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"));
});
@@ -425,18 +428,248 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
- logTransactions(loanId);
+ 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"),
transaction(87.82, "Repayment", "01 February 2021"), transaction(9.67, "Interest Refund", "09 February 2021"));
});
}
- private void logTransactions(Long loanId) {
+ @Test
+ public void verifyUC06() {
+ AtomicReference<Long> loanIdRef = new AtomicReference<>();
+ runAt("1 December 2020", () -> {
+ 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 December 2020", 1000.0, 9.99,
+ 6, null);
+ Assertions.assertNotNull(loanId);
+ loanIdRef.set(loanId);
+ disburseLoan(loanId, BigDecimal.valueOf(1000), "1 December 2020");
+
+ });
+ runAt("14 December 2020", () -> {
+ Long loanId = loanIdRef.get();
+ PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
+ .makeLoanRepayment("PayoutRefund", "14 December 2020", 500F, loanId.intValue());
+ 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"));
+ });
+ }
+
+ @Test
+ public void verifyUC07() {
+ 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,
+ 12, null);
+ 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());
+ 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());
+ 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"), //
+ transaction(5.35, "Interest Refund", "09 February 2021"));
+ });
+ }
+
+ @Test
+ public void verifyUC08() {
+ AtomicReference<Long> loanIdRef = new AtomicReference<>();
+ runAt("1 January 2021", () -> {
+ PostLoanProductsResponse loanProduct = loanProductHelper
+ .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+ .daysInYearType(DaysInYearType.ACTUAL) //
+ //
+ .disallowExpectedDisbursements(true)//
+ .multiDisburseLoan(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,
+ 6, 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();
+ PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
+ .makeLoanRepayment("PayoutRefund", "22 January 2021", 500F, loanId.intValue());
+ 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"), //
+ transaction(2.88, "Interest Refund", "22 January 2021"));
+ });
+ }
+
+ @Test
+ public void verifyUC09() {
+ AtomicReference<Long> loanIdRef = new AtomicReference<>();
+ runAt("1 January 2021", () -> {
+ PostLoanProductsResponse loanProduct = loanProductHelper
+ .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+ .daysInYearType(DaysInYearType.ACTUAL) //
+ .disallowExpectedDisbursements(true).multiDisburseLoan(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,
+ 6, null);
+ Assertions.assertNotNull(loanId);
+ loanIdRef.set(loanId);
+ disburseLoan(loanId, BigDecimal.valueOf(250), "1 January 2021");
+ });
+ runAt("7 January 2021", () -> {
+ Long loanId = loanIdRef.get();
+ disburseLoan(loanId, BigDecimal.valueOf(750), "7 January 2021");
+ });
+ runAt("22 January 2021", () -> {
+ Long loanId = loanIdRef.get();
+ PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
+ .makeLoanRepayment("PayoutRefund", "22 January 2021", 500F, loanId.intValue());
+ 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"), //
+ transaction(2.47, "Interest Refund", "22 January 2021"));
+ });
+ }
+
+ @Test
+ public void verifyUC10() {
+ AtomicReference<Long> loanIdRef = new AtomicReference<>();
+ runAt("1 January 2021", () -> {
+ PostLoanProductsResponse loanProduct = loanProductHelper
+ .createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
+ .daysInYearType(DaysInYearType.ACTUAL) //
+ .disallowExpectedDisbursements(true).multiDisburseLoan(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,
+ 6, null);
+ Assertions.assertNotNull(loanId);
+ loanIdRef.set(loanId);
+ disburseLoan(loanId, BigDecimal.valueOf(250), "1 January 2021");
+
+ });
+ 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);
+ verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2021"), //
+ transaction(750.0, "Disbursement", "07 January 2021"), //
+ transaction(171.29, "Repayment", "01 February 2021"), //
+ transaction(171.29, "Repayment", "01 March 2021"), //
+ transaction(171.29, "Repayment", "01 April 2021"), //
+ transaction(171.29, "Repayment", "01 May 2021"), //
+ transaction(171.29, "Repayment", "01 June 2021"), //
+ transaction(171.32, "Repayment", "01 July 2021") //
+ ); //
+ });
+ runAt("11 July 2021", () -> {
+ Long loanId = loanIdRef.get();
+ PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
+ .makeLoanRepayment("PayoutRefund", "11 July 2021", 500.0F, loanId.intValue());
+ 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"), //
+ transaction(171.29, "Repayment", "01 March 2021"), //
+ transaction(171.29, "Repayment", "01 April 2021"), //
+ transaction(171.29, "Repayment", "01 May 2021"), //
+ transaction(171.29, "Repayment", "01 June 2021"), //
+ transaction(171.32, "Repayment", "01 July 2021"), //
+ transaction(500.0, "Payout Refund", "11 July 2021"), //
+ transaction(20.41, "Interest Refund", "11 July 2021")); //
+ });
+ }
+
+ 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()));
+ }
+ }
+
+ private void logLoanTransactions(Long loanId) {
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
- assert loanDetails.getTransactions() != null;
- loanDetails.getTransactions()
- .forEach(tr -> log.info("Transaction {} {} {} ", tr.getType().getValue(), tr.getDate(), tr.getAmount()));
+ if (loanDetails.getTransactions() != null) {
+ loanDetails.getTransactions()
+ .forEach(tr -> log.info("Transaction {} {} {} ", tr.getType().getValue(), tr.getDate(), tr.getAmount()));
+ }
}
}