Skip to content

Commit

Permalink
FINERACT-1981: Fix interest refund transaction with zero amount
Browse files Browse the repository at this point in the history
  • Loading branch information
janez89 authored and adamsaghy committed Nov 22, 2024
1 parent e95b3ff commit 34b165a
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -36,5 +37,5 @@ public interface InterestRefundService {
Money totalInterestByTransactions(LoanRepaymentScheduleTransactionProcessor processor, Long loanId,
LocalDate relatedRefundTransactionDate, List<LoanTransaction> newTransactions, List<Long> oldTransactionIds);

Money getTotalInterestRefunded(List<LoanTransaction> loanTransactions, MonetaryCurrency currency);
Money getTotalInterestRefunded(List<LoanTransaction> loanTransactions, MonetaryCurrency currency, MathContext mc);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -40,6 +41,7 @@
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;
Expand Down Expand Up @@ -166,6 +168,7 @@ public void updateLoanCollateralStatus(Set<LoanCollateralManagement> loanCollate
this.loanCollateralManagementRepository.saveAll(loanCollateralManagementSet);
}

@Nullable
private LoanTransaction createInterestRefundLoanTransaction(Loan loan, LoanTransaction refundTransaction) {

InterestRefundService interestRefundService = interestRefundServiceDelegate.lookupInterestRefundService(loan);
Expand All @@ -175,13 +178,17 @@ private LoanTransaction createInterestRefundLoanTransaction(Loan loan, LoanTrans

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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;
Expand Down Expand Up @@ -135,9 +136,13 @@ public Money totalInterestByTransactions(LoanRepaymentScheduleTransactionProcess
}

@Override
public Money getTotalInterestRefunded(List<LoanTransaction> 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<LoanTransaction> 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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,35 @@ public void verifyUC18S1() {
});
}

@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<Long> loanIdRef = new AtomicReference<>();
Expand Down

0 comments on commit 34b165a

Please sign in to comment.