FINERACT-2389: Reprocess Interest Refund txn amount if no txn was changed
This commit is contained in:
committed by
Adam Saghy
parent
0026aaf1a6
commit
6df78f64c0
+4
-6
@@ -568,12 +568,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
.filter(LoanTransaction::isNotReversed).map(AbstractPersistableCustom::getId).toList();
|
||||
final List<LoanTransaction> modifiedTransactions = new ArrayList<>(progCtx.getAlreadyProcessedTransactions().stream()
|
||||
.filter(LoanTransaction::isNotReversed).filter(tr -> tr.getId() == null).toList());
|
||||
if (!modifiedTransactions.isEmpty()) {
|
||||
final Money interestAfterRefund = interestRefundService.totalInterestByTransactions(this, loan.getId(), targetDate,
|
||||
modifiedTransactions, unmodifiedTransactionIds);
|
||||
final Money newAmount = interestBeforeRefund.minus(progCtx.getSumOfInterestRefundAmount()).minus(interestAfterRefund);
|
||||
loanTransaction.updateAmount(newAmount.getAmount());
|
||||
}
|
||||
final Money interestAfterRefund = interestRefundService.totalInterestByTransactions(this, loan.getId(), targetDate,
|
||||
modifiedTransactions, unmodifiedTransactionIds);
|
||||
final Money newAmount = interestBeforeRefund.minus(progCtx.getSumOfInterestRefundAmount()).minus(interestAfterRefund);
|
||||
loanTransaction.updateAmount(newAmount.getAmount());
|
||||
progCtx.setSumOfInterestRefundAmount(progCtx.getSumOfInterestRefundAmount().add(loanTransaction.getAmount()));
|
||||
}
|
||||
}
|
||||
|
||||
+54
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.Loan.DATE_FORMAT;
|
||||
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum.REPLAYED;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -32,6 +33,7 @@ import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.client.models.AdvancedPaymentData;
|
||||
@@ -41,6 +43,7 @@ import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionId
|
||||
import org.apache.fineract.client.models.PaymentAllocationOrder;
|
||||
import org.apache.fineract.client.models.PostClientsResponse;
|
||||
import org.apache.fineract.client.models.PostLoanProductsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
||||
import org.apache.fineract.client.util.CallFailedRuntimeException;
|
||||
@@ -1633,4 +1636,55 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
|
||||
Assertions.assertEquals(0.36, Utils.getDoubleValue(loanDetails.getTotalOverpaid()));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowToReprocessInterestRefundEvenIfNoTransactionWasChanged() {
|
||||
runAt("1 February 2025", () -> {
|
||||
Long loanProductId = getOrCreateLoanProduct();
|
||||
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductId, "1 January 2025", 100.0, 26.0, 6, null);
|
||||
Assertions.assertNotNull(loanId);
|
||||
disburseLoan(loanId, BigDecimal.valueOf(100.0), "1 January 2025");
|
||||
|
||||
final String transactionExternalId = UUID.randomUUID().toString();
|
||||
final PostLoansLoanIdTransactionsResponse refundResponse = loanTransactionHelper
|
||||
.makeMerchantIssuedRefund(loanId, new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN)
|
||||
.transactionDate("1 February 2025").locale("en").transactionAmount(66.41).externalId(transactionExternalId).interestRefundCalculation(false));
|
||||
Assertions.assertNotNull(refundResponse.getResourceId());
|
||||
|
||||
verifyTransactions(loanId, //
|
||||
transaction(100.0, "Disbursement", "01 January 2025"), //
|
||||
transaction(66.41, "Merchant Issued Refund", "01 February 2025") //
|
||||
);
|
||||
|
||||
// Create manual interest refund via API
|
||||
PostLoansLoanIdTransactionsResponse interestRefundResponse = loanTransactionHelper.createManualInterestRefund(loanId, refundResponse.getResourceId(),
|
||||
"1 February 2025", 1.47, null);
|
||||
|
||||
verifyTransactions(loanId, //
|
||||
transaction(100.0, "Disbursement", "01 January 2025"), //
|
||||
transaction(66.41, "Merchant Issued Refund", "01 February 2025"), //
|
||||
transaction(1.47, "Interest Refund", "01 February 2025") //
|
||||
);
|
||||
|
||||
PostLoansLoanIdTransactionsResponse repaymentResponse = loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "20 January 2025", 17.94);
|
||||
loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "25 January 2025", 10.94);
|
||||
|
||||
verifyTransactions(loanId, //
|
||||
transaction(100.0, "Disbursement", "01 January 2025"), //
|
||||
transaction(17.94, "Repayment", "20 January 2025"), //
|
||||
transaction(10.94, "Repayment", "25 January 2025"), //
|
||||
transaction(66.41, "Merchant Issued Refund", "01 February 2025"), //
|
||||
transaction(1.47, "Interest Refund", "01 February 2025") //
|
||||
);
|
||||
|
||||
loanTransactionHelper.reverseLoanTransaction(loanId, repaymentResponse.getResourceId(),
|
||||
new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat(DATETIME_PATTERN)
|
||||
.transactionDate("25 January 2021").transactionAmount(0.0).locale("en"));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
verifyLoanStatus(loanDetails, LoanStatus.OVERPAID);
|
||||
Assertions.assertEquals(0.36, Utils.getDoubleValue(loanDetails.getTotalOverpaid()));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-18
@@ -96,7 +96,7 @@ public class LoanManualInterestRefundResponseStructureTest extends BaseLoanInteg
|
||||
targetTransactionIdRef.set(refundResponse.getResourceId());
|
||||
|
||||
// Create manual interest refund via API
|
||||
PostLoansLoanIdTransactionsResponse interestRefundResponse = createManualInterestRefund(loanId, refundResponse.getResourceId(),
|
||||
PostLoansLoanIdTransactionsResponse interestRefundResponse = loanTransactionHelper.createManualInterestRefund(loanId, refundResponse.getResourceId(),
|
||||
"15 January 2024", 5.0, null);
|
||||
|
||||
assertNotNull(interestRefundResponse, "Interest refund response should not be null");
|
||||
@@ -157,7 +157,7 @@ public class LoanManualInterestRefundResponseStructureTest extends BaseLoanInteg
|
||||
|
||||
// Create manual interest refund with external ID
|
||||
String interestRefundExternalId = UUID.randomUUID().toString();
|
||||
PostLoansLoanIdTransactionsResponse interestRefundResponse = createManualInterestRefund(loanId, refundResponse.getResourceId(),
|
||||
PostLoansLoanIdTransactionsResponse interestRefundResponse = loanTransactionHelper.createManualInterestRefund(loanId, refundResponse.getResourceId(),
|
||||
"15 February 2024", 5.0, interestRefundExternalId);
|
||||
|
||||
assertNotNull(interestRefundResponse, "Interest refund response should not be null");
|
||||
@@ -180,22 +180,6 @@ public class LoanManualInterestRefundResponseStructureTest extends BaseLoanInteg
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create manual interest refund transaction
|
||||
*/
|
||||
private PostLoansLoanIdTransactionsResponse createManualInterestRefund(Long loanId, Long targetTransactionId, String transactionDate,
|
||||
Double amount, String externalId) {
|
||||
|
||||
PostLoansLoanIdTransactionsTransactionIdRequest request = new PostLoansLoanIdTransactionsTransactionIdRequest()
|
||||
.transactionAmount(amount).dateFormat("dd MMMM yyyy").locale("en");
|
||||
|
||||
if (externalId != null) {
|
||||
request.externalId(externalId);
|
||||
}
|
||||
|
||||
return loanTransactionHelper.manualInterestRefund(loanId, targetTransactionId, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to make loan merchant issued refund (without automatic interest refund)
|
||||
*/
|
||||
|
||||
+16
@@ -1709,6 +1709,22 @@ public class LoanTransactionHelper {
|
||||
excludedTransactionTypes, page, size, sort));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create manual interest refund transaction
|
||||
*/
|
||||
public PostLoansLoanIdTransactionsResponse createManualInterestRefund(Long loanId, Long targetTransactionId, String transactionDate,
|
||||
Double amount, String externalId) {
|
||||
|
||||
PostLoansLoanIdTransactionsTransactionIdRequest request = new PostLoansLoanIdTransactionsTransactionIdRequest()
|
||||
.transactionAmount(amount).dateFormat("dd MMMM yyyy").locale("en");
|
||||
|
||||
if (externalId != null) {
|
||||
request.externalId(externalId);
|
||||
}
|
||||
|
||||
return manualInterestRefund(loanId, targetTransactionId, request);
|
||||
}
|
||||
|
||||
// TODO: Rewrite to use fineract-client instead!
|
||||
// Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
|
||||
// org.apache.fineract.client.models.PostLoansLoanIdRequest)
|
||||
|
||||
Reference in New Issue
Block a user