From 34b165aa0e745f5700582f7b7b37cf1295a1bb1c Mon Sep 17 00:00:00 2001 From: Janos Meszaros Date: Fri, 22 Nov 2024 13:27:37 +0100 Subject: [PATCH] FINERACT-1981: Fix interest refund transaction with zero amount --- .../service/InterestRefundService.java | 3 +- .../domain/LoanAccountDomainServiceJpa.java | 9 +++++- ...gressiveLoanInterestRefundServiceImpl.java | 11 +++++-- .../LoanInterestRefundTest.java | 29 +++++++++++++++++++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundService.java index ba27cc078..8d3e663e3 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestRefundService.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.portfolio.loanaccount.service; +import java.math.MathContext; import java.time.LocalDate; import java.util.List; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; @@ -36,5 +37,5 @@ public interface InterestRefundService { Money totalInterestByTransactions(LoanRepaymentScheduleTransactionProcessor processor, Long loanId, LocalDate relatedRefundTransactionDate, List newTransactions, List oldTransactionIds); - Money getTotalInterestRefunded(List loanTransactions, MonetaryCurrency currency); + Money getTotalInterestRefunded(List loanTransactions, MonetaryCurrency currency, MathContext mc); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index af9617b25..abe81fc1f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.portfolio.loanaccount.domain; +import jakarta.annotation.Nullable; import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; @@ -40,6 +41,7 @@ import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRu import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; +import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargePaymentPostBusinessEvent; @@ -166,6 +168,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { this.loanCollateralManagementRepository.saveAll(loanCollateralManagementSet); } + @Nullable private LoanTransaction createInterestRefundLoanTransaction(Loan loan, LoanTransaction refundTransaction) { InterestRefundService interestRefundService = interestRefundServiceDelegate.lookupInterestRefundService(loan); @@ -175,13 +178,17 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { Money totalInterest = interestRefundService.totalInterestByTransactions(null, loan.getId(), refundTransaction.getTransactionDate(), List.of(), loan.getLoanTransactions().stream().map(AbstractPersistableCustom::getId).toList()); - Money previouslyRefundedInterests = interestRefundService.getTotalInterestRefunded(loan.getLoanTransactions(), loan.getCurrency()); + Money previouslyRefundedInterests = interestRefundService.getTotalInterestRefunded(loan.getLoanTransactions(), loan.getCurrency(), + totalInterest.getMc()); Money newTotalInterest = interestRefundService.totalInterestByTransactions(null, loan.getId(), refundTransaction.getTransactionDate(), List.of(refundTransaction), loan.getLoanTransactions().stream().map(AbstractPersistableCustom::getId).toList()); BigDecimal interestRefundAmount = totalInterest.minus(previouslyRefundedInterests).minus(newTotalInterest).getAmount(); + if (MathUtil.isZero(interestRefundAmount)) { + return null; + } final ExternalId txnExternalId = externalIdFactory.create(); businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionInterestRefundPreBusinessEvent(loan)); return LoanTransaction.interestRefund(loan, interestRefundAmount, refundTransaction.getDateOf(), txnExternalId); 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 8dc0ab31b..8b9490f07 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 @@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.service; import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REPAYMENT; import java.math.BigDecimal; +import java.math.MathContext; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; @@ -135,9 +136,13 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS } @Override - public Money getTotalInterestRefunded(List loanTransactions, MonetaryCurrency currency) { - return Money.of(currency, loanTransactions.stream().filter(LoanTransaction::isNotReversed).filter(LoanTransaction::isInterestRefund) - .map(LoanTransaction::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add)); + public Money getTotalInterestRefunded(List loanTransactions, MonetaryCurrency currency, MathContext mc) { + final BigDecimal totalInterestRefunded = loanTransactions.stream() // + .filter(LoanTransaction::isNotReversed) // + .filter(LoanTransaction::isInterestRefund) // + .map(LoanTransaction::getAmount) // + .reduce(BigDecimal.ZERO, BigDecimal::add); // + return Money.of(currency, totalInterestRefunded, mc); } } 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 9047fb7d5..f3c754508 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 @@ -1108,6 +1108,35 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest { }); } + @Test + public void verifyNoEmptyInterestRefundTransaction() { + 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.9, + 12, null); + Assertions.assertNotNull(loanId); + + disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021"); + loanTransactionHelper.makeLoanRepayment("MerchantIssuedRefund", "1 January 2021", 1000F, loanId.intValue()); + logLoanTransactions(loanId); + + verifyTransactions(loanId, // + transaction(1000.0, "Disbursement", "01 January 2021"), // + transaction(1000.0, "Merchant Issued Refund", "01 January 2021") // + ); + logLoanTransactions(loanId); + }); + } + @Test public void verifyUC18S2() { AtomicReference loanIdRef = new AtomicReference<>();