FINERACT-1981: Fix interest refund transaction with zero amount
This commit is contained in:
committed by
Adam Saghy
parent
e95b3ff25c
commit
34b165aa0e
+2
-1
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanaccount.service;
|
package org.apache.fineract.portfolio.loanaccount.service;
|
||||||
|
|
||||||
|
import java.math.MathContext;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||||
@@ -36,5 +37,5 @@ public interface InterestRefundService {
|
|||||||
Money totalInterestByTransactions(LoanRepaymentScheduleTransactionProcessor processor, Long loanId,
|
Money totalInterestByTransactions(LoanRepaymentScheduleTransactionProcessor processor, Long loanId,
|
||||||
LocalDate relatedRefundTransactionDate, List<LoanTransaction> newTransactions, List<Long> oldTransactionIds);
|
LocalDate relatedRefundTransactionDate, List<LoanTransaction> newTransactions, List<Long> oldTransactionIds);
|
||||||
|
|
||||||
Money getTotalInterestRefunded(List<LoanTransaction> loanTransactions, MonetaryCurrency currency);
|
Money getTotalInterestRefunded(List<LoanTransaction> loanTransactions, MonetaryCurrency currency, MathContext mc);
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.portfolio.loanaccount.domain;
|
package org.apache.fineract.portfolio.loanaccount.domain;
|
||||||
|
|
||||||
|
import jakarta.annotation.Nullable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
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.exception.PlatformApiDataValidationException;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
|
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.LoanBalanceChangedBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBusinessEvent;
|
||||||
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargePaymentPostBusinessEvent;
|
import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargePaymentPostBusinessEvent;
|
||||||
@@ -166,6 +168,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
this.loanCollateralManagementRepository.saveAll(loanCollateralManagementSet);
|
this.loanCollateralManagementRepository.saveAll(loanCollateralManagementSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private LoanTransaction createInterestRefundLoanTransaction(Loan loan, LoanTransaction refundTransaction) {
|
private LoanTransaction createInterestRefundLoanTransaction(Loan loan, LoanTransaction refundTransaction) {
|
||||||
|
|
||||||
InterestRefundService interestRefundService = interestRefundServiceDelegate.lookupInterestRefundService(loan);
|
InterestRefundService interestRefundService = interestRefundServiceDelegate.lookupInterestRefundService(loan);
|
||||||
@@ -175,13 +178,17 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
|||||||
|
|
||||||
Money totalInterest = interestRefundService.totalInterestByTransactions(null, loan.getId(), refundTransaction.getTransactionDate(),
|
Money totalInterest = interestRefundService.totalInterestByTransactions(null, loan.getId(), refundTransaction.getTransactionDate(),
|
||||||
List.of(), loan.getLoanTransactions().stream().map(AbstractPersistableCustom::getId).toList());
|
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(),
|
Money newTotalInterest = interestRefundService.totalInterestByTransactions(null, loan.getId(),
|
||||||
refundTransaction.getTransactionDate(), List.of(refundTransaction),
|
refundTransaction.getTransactionDate(), List.of(refundTransaction),
|
||||||
loan.getLoanTransactions().stream().map(AbstractPersistableCustom::getId).toList());
|
loan.getLoanTransactions().stream().map(AbstractPersistableCustom::getId).toList());
|
||||||
BigDecimal interestRefundAmount = totalInterest.minus(previouslyRefundedInterests).minus(newTotalInterest).getAmount();
|
BigDecimal interestRefundAmount = totalInterest.minus(previouslyRefundedInterests).minus(newTotalInterest).getAmount();
|
||||||
|
|
||||||
|
if (MathUtil.isZero(interestRefundAmount)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
final ExternalId txnExternalId = externalIdFactory.create();
|
final ExternalId txnExternalId = externalIdFactory.create();
|
||||||
businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionInterestRefundPreBusinessEvent(loan));
|
businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionInterestRefundPreBusinessEvent(loan));
|
||||||
return LoanTransaction.interestRefund(loan, interestRefundAmount, refundTransaction.getDateOf(), txnExternalId);
|
return LoanTransaction.interestRefund(loan, interestRefundAmount, refundTransaction.getDateOf(), txnExternalId);
|
||||||
|
|||||||
+8
-3
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.service;
|
|||||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REPAYMENT;
|
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REPAYMENT;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.math.MathContext;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -135,9 +136,13 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Money getTotalInterestRefunded(List<LoanTransaction> loanTransactions, MonetaryCurrency currency) {
|
public Money getTotalInterestRefunded(List<LoanTransaction> loanTransactions, MonetaryCurrency currency, MathContext mc) {
|
||||||
return Money.of(currency, loanTransactions.stream().filter(LoanTransaction::isNotReversed).filter(LoanTransaction::isInterestRefund)
|
final BigDecimal totalInterestRefunded = loanTransactions.stream() //
|
||||||
.map(LoanTransaction::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
|
.filter(LoanTransaction::isNotReversed) //
|
||||||
|
.filter(LoanTransaction::isInterestRefund) //
|
||||||
|
.map(LoanTransaction::getAmount) //
|
||||||
|
.reduce(BigDecimal.ZERO, BigDecimal::add); //
|
||||||
|
return Money.of(currency, totalInterestRefunded, mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -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
|
@Test
|
||||||
public void verifyUC18S2() {
|
public void verifyUC18S2() {
|
||||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||||
|
|||||||
Reference in New Issue
Block a user