FINERACT-2210: fix previously refunded interest calculation during interest refund
This commit is contained in:
-4
@@ -18,10 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
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.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
@@ -36,6 +34,4 @@ public interface InterestRefundService {
|
|||||||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
|
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
|
||||||
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, MathContext mc);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-4
@@ -190,13 +190,10 @@ 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(),
|
|
||||||
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(newTotalInterest).getAmount();
|
||||||
|
|
||||||
if (MathUtil.isZero(interestRefundAmount)) {
|
if (MathUtil.isZero(interestRefundAmount)) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
-13
@@ -21,7 +21,6 @@ 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;
|
||||||
@@ -31,7 +30,6 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
|
||||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||||
import org.apache.fineract.portfolio.loanaccount.data.TransactionChangeData;
|
import org.apache.fineract.portfolio.loanaccount.data.TransactionChangeData;
|
||||||
@@ -145,15 +143,4 @@ public class ProgressiveLoanInterestRefundServiceImpl implements InterestRefundS
|
|||||||
return recalculateTotalInterest((AdvancedPaymentScheduleTransactionProcessor) processor, loan, relatedRefundTransactionDate,
|
return recalculateTotalInterest((AdvancedPaymentScheduleTransactionProcessor) processor, loan, relatedRefundTransactionDate,
|
||||||
transactionsToReprocess);
|
transactionsToReprocess);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-13
@@ -71,7 +71,7 @@ public class LoanRefundTransactionTest extends BaseLoanIntegrationTest {
|
|||||||
public void testMerchantIssuedRefundCreatesAndReversesInterestRefund() {
|
public void testMerchantIssuedRefundCreatesAndReversesInterestRefund() {
|
||||||
runAt("01 July 2024", () -> {
|
runAt("01 July 2024", () -> {
|
||||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||||
final Long loanId = createLoanForMerchantIssuedRefundWithInterestRefund(clientId);
|
final Long loanId = createAndDisburseLoanForMerchantIssuedRefundWithInterestRefund(clientId);
|
||||||
final PostLoansLoanIdTransactionsResponse merchantIssuedRefundResponse = loanTransactionHelper.makeMerchantIssuedRefund(loanId,
|
final PostLoansLoanIdTransactionsResponse merchantIssuedRefundResponse = loanTransactionHelper.makeMerchantIssuedRefund(loanId,
|
||||||
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("01 July 2024").locale("en")
|
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("01 July 2024").locale("en")
|
||||||
.transactionAmount(100.0));
|
.transactionAmount(100.0));
|
||||||
@@ -103,7 +103,7 @@ public class LoanRefundTransactionTest extends BaseLoanIntegrationTest {
|
|||||||
public void testPayoutRefundCreatesAndReversesInterestRefund() {
|
public void testPayoutRefundCreatesAndReversesInterestRefund() {
|
||||||
runAt("01 July 2024", () -> {
|
runAt("01 July 2024", () -> {
|
||||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||||
final Long loanId = createLoanForPayoutRefundWithInterestRefund(clientId);
|
final Long loanId = createAndDisburseLoanForPayoutRefundWithInterestRefund(clientId);
|
||||||
final PostLoansLoanIdTransactionsResponse payoutRefundResponse = loanTransactionHelper.makePayoutRefund(loanId,
|
final PostLoansLoanIdTransactionsResponse payoutRefundResponse = loanTransactionHelper.makePayoutRefund(loanId,
|
||||||
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("01 July 2024").locale("en")
|
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("01 July 2024").locale("en")
|
||||||
.transactionAmount(100.0));
|
.transactionAmount(100.0));
|
||||||
@@ -129,22 +129,52 @@ public class LoanRefundTransactionTest extends BaseLoanIntegrationTest {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long createLoanForMerchantIssuedRefundWithInterestRefund(Long clientId) {
|
@Test
|
||||||
return createLoanForRefundWithInterestRefund(clientId, "MERCHANT_ISSUED_REFUND");
|
public void testMerchantIssuedRefundDoesNotCreateInterestRefundWithLessThanOrEqualToZeroInterest() {
|
||||||
|
runAt("20 April 2025", () -> {
|
||||||
|
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||||
|
Long loanId = createLoanForRefundWithInterestRefund(clientId, "MERCHANT_ISSUED_REFUND", "05 April 2025", 500.0, 20.99, 6);
|
||||||
|
disburseLoan(loanId, BigDecimal.valueOf(265.91), "05 April 2025");
|
||||||
|
disburseLoan(loanId, BigDecimal.valueOf(1.99), "05 April 2025");
|
||||||
|
disburseLoan(loanId, BigDecimal.valueOf(20.00), "05 April 2025");
|
||||||
|
|
||||||
|
final PostLoansLoanIdTransactionsResponse merchantIssuedRefundResponse1 = loanTransactionHelper.makeMerchantIssuedRefund(loanId,
|
||||||
|
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("06 April 2025").locale("en")
|
||||||
|
.transactionAmount(6.29));
|
||||||
|
|
||||||
|
final PostLoansLoanIdTransactionsResponse merchantIssuedRefundResponse2 = loanTransactionHelper.makeMerchantIssuedRefund(loanId,
|
||||||
|
new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("07 April 2025").locale("en")
|
||||||
|
.transactionAmount(1.99));
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
Assertions.assertTrue(loanDetails.getTransactions().stream()
|
||||||
|
.filter(transaction -> transaction.getType().getCode().equals("loanTransactionType.interestRefund"))
|
||||||
|
.allMatch(transaction -> transaction.getAmount().compareTo(0.0) > 0));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long createLoanForPayoutRefundWithInterestRefund(Long clientId) {
|
private Long createAndDisburseLoanForMerchantIssuedRefundWithInterestRefund(Long clientId) {
|
||||||
return createLoanForRefundWithInterestRefund(clientId, "PAYOUT_REFUND");
|
return createAndDisburseLoanForRefundWithInterestRefund(clientId, "MERCHANT_ISSUED_REFUND");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long createLoanForRefundWithInterestRefund(Long clientId, String refundType) {
|
private Long createAndDisburseLoanForPayoutRefundWithInterestRefund(Long clientId) {
|
||||||
final PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(
|
return createAndDisburseLoanForRefundWithInterestRefund(clientId, "PAYOUT_REFUND");
|
||||||
create4IProgressive().supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem(refundType));
|
}
|
||||||
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(
|
|
||||||
applyLP2ProgressiveLoanRequest(clientId, loanProductsResponse.getResourceId(), "01 June 2024", 1000.0, 10.0, 4, null));
|
private Long createAndDisburseLoanForRefundWithInterestRefund(Long clientId, String refundType) {
|
||||||
Long loanId = postLoansResponse.getLoanId();
|
Long loanId = createLoanForRefundWithInterestRefund(clientId, refundType, "01 June 2024", 1000.0, 10.0, 4);
|
||||||
loanTransactionHelper.approveLoan(loanId, approveLoanRequest(1000.0, "01 June 2024"));
|
|
||||||
disburseLoan(loanId, BigDecimal.valueOf(1000.0), "01 June 2024");
|
disburseLoan(loanId, BigDecimal.valueOf(1000.0), "01 June 2024");
|
||||||
return loanId;
|
return loanId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Long createLoanForRefundWithInterestRefund(Long clientId, String refundType, String date, double amount, double interestRate,
|
||||||
|
int numRepayments) {
|
||||||
|
final PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(
|
||||||
|
create4IProgressive().supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem(refundType));
|
||||||
|
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId,
|
||||||
|
loanProductsResponse.getResourceId(), date, amount, interestRate, numRepayments, null));
|
||||||
|
Long loanId = postLoansResponse.getLoanId();
|
||||||
|
loanTransactionHelper.approveLoan(loanId, approveLoanRequest(amount, date));
|
||||||
|
return loanId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user