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 refundFinal) { + private static void simulateRepaymentForDisbursements(LoanTransaction lt, final AtomicReference refundFinal, + List 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 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 refundFinal = new AtomicReference<>(refundAmount); - List 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 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 transactionsToReprocess = new ArrayList<>(); + loan.getLoanTransactions().stream().filter(lt -> !lt.isReversed()) // + .filter(lt -> !lt.isAccrual() && !lt.isAccrualActivity()) // + .forEach(lt -> simulateRepaymentForDisbursements(lt, refundFinal, transactionsToReprocess)); // + + List 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 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 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 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 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 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())); + } } }