Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FINERACT-1981: Fix interest refund transaction with zero amount #4182

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading