diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java index 4c722d04e6c..18fd9986f7a 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java @@ -87,6 +87,7 @@ public enum DefaultLoanProduct implements LoanProduct { LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND, // LP1_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, // LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, // + LP2_ADV_PMT_ALLOC_ACTUAL_ACTUAL_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL, // LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_WHOLE_TERM, // LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_IR_DAILY_TILL_PRECLOSE_LAST_INSTALLMENT_STRATEGY, // LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND_INTEREST_RECALCULATION, // diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java index 09fa976f5fd..4ac5c16e8da 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java @@ -4230,6 +4230,27 @@ public void initialize() throws Exception { TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_MULTIDISBURSE_CHARGEBACK, responseLoanProductsRequestLP2AdvancedpaymentInterestEmi36030InterestRecalcDailyMultiDisburseChargeback); + // LP2 + interest recalculation + advanced custom payment allocation + progressive loan schedule + horizontal + // (LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL) + String name152 = DefaultLoanProduct.LP2_ADV_PMT_ALLOC_ACTUAL_ACTUAL_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL.getName(); + + PostLoanProductsRequest loanProductsRequestAdvPaymentAllocationActualActualProgressiveLoanSchedule = loanProductsRequestFactory + .defaultLoanProductsRequestLP2InterestDailyRecalculation()// + .name(name152)// + .supportedInterestRefundTypes(Arrays.asList("MERCHANT_ISSUED_REFUND", "PAYOUT_REFUND"))// + .enableAccrualActivityPosting(true) // + .daysInYearType(DaysInYearType.ACTUAL.value)// + .daysInMonthType(DaysInMonthType.ACTUAL.value)// + .paymentAllocation(List.of(// + createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), // + createPaymentAllocation("PAYOUT_REFUND", "LAST_INSTALLMENT"), // + createPaymentAllocation("MERCHANT_ISSUED_REFUND", "LAST_INSTALLMENT")));// + PostLoanProductsResponse responseLoanProductsRequestAdvPaymentAllocationActualActualProgressiveLoanSchedule = createLoanProductIdempotent( + loanProductsRequestAdvPaymentAllocationActualActualProgressiveLoanSchedule); + TestContext.INSTANCE.set( + TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADVANCED_PAYMENT_ALLOCATION_ACTUAL_ACTUAL_PROGRESSIVE_LOAN_SCHEDULE, + responseLoanProductsRequestAdvPaymentAllocationActualActualProgressiveLoanSchedule); + // LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_PRINCIPAL_FIRST // Same as LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL but with PRINCIPAL before INTEREST // in payment allocation order diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java index 062b6a19d46..e01a9e0104a 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java @@ -88,6 +88,7 @@ public abstract class TestContextKey { public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_AUTO = "loanProductCreateResponseLP2DownPaymentAuto"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE = "loanProductCreateResponseLP2DownPaymentProgressiveLoanSchedule"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADVANCED_CUSTOM_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE = "loanProductCreateResponseLP2ProgressiveLoanScheduleCustomPaymentAllocation"; + public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADVANCED_PAYMENT_ALLOCATION_ACTUAL_ACTUAL_PROGRESSIVE_LOAN_SCHEDULE = "loanProductCreateResponseLP2ProgressiveLoanScheduleActualActualAdvancedPaymentAllocation"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE_VERTICAL = "loanProductCreateResponseLP2DownPaymentProgressiveLoanScheduleVertical"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADVANCED_PAYMENT_ALLOCATION_PROGRESSIVE_LOAN_SCHEDULE_INSTALLMENT_LEVEL_DELINQUENCY = "loanProductCreateResponseLP2DownPaymentProgressiveLoanScheduleInstallmentLevelDelinquency"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROG_SCHEDULE_HOR_INST_LVL_DELINQUENCY_CREDIT_ALLOCATION = "loanProductCreateResponseLP2DownPaymentProgressiveLoanScheduleHorizontalInstallmentLevelDelinquencyCreditAllocation"; diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanMerchantIssuedRefund.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanMerchantIssuedRefund.feature index 9c3952ae646..4ec1477acea 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanMerchantIssuedRefund.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanMerchantIssuedRefund.feature @@ -637,3 +637,62 @@ Feature: MerchantIssuedRefund | 02 October 2025 | Interest Refund | 17.07 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false | | 02 October 2025 | Accrual | 18.33 | 0.0 | 18.33 | 0.0 | 0.0 | 0.0 | false | false | | 02 October 2025 | Accrual Activity | 8.97 | 0.0 | 8.97 | 0.0 | 0.0 | 0.0 | false | false | + + @TestRailId:C4355 + Scenario: Verify manual Interest Refund is recalculated during reprocessing even if no prior transaction was reverse-replayed + When Admin sets the business date to "01 January 2025" + And Admin creates a client with random data + And Admin creates a fully customized loan with the following data: + | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy | + | LP2_ADV_PMT_ALLOC_ACTUAL_ACTUAL_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 January 2025 | 100 | 26 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION | + And Admin successfully approves the loan on "01 January 2025" with "100" amount and expected disbursement date on "01 January 2025" + And Admin successfully disburse the loan on "01 January 2025" with "100" EUR transaction amount + And Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + # MIR with interestRefundCalculation=false (no auto Interest Refund) + When Admin sets the business date to "01 February 2025" + And Customer makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "01 February 2025" with 66.41 EUR transaction amount and system-generated Idempotency key and interestRefundCalculation false + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 01 February 2025 | Merchant Issued Refund | 66.41 | 64.2 | 2.21 | 0.0 | 0.0 | 35.8 | false | false | + # Manually create Interest Refund with arbitrary amount (0.47 EUR) + When Admin manually adds Interest Refund for "MERCHANT_ISSUED_REFUND" transaction made on "01 February 2025" with 0.47 EUR interest refund amount + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 01 February 2025 | Merchant Issued Refund | 66.41 | 64.2 | 2.21 | 0.0 | 0.0 | 35.8 | false | false | + | 01 February 2025 | Interest Refund | 0.47 | 0.47 | 0.0 | 0.0 | 0.0 | 35.33 | false | false | + # Backdated repayment on 20 January (before MIR) - triggers replay of MIR + And Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "20 January 2025" with 17.94 EUR transaction amount and system-generated Idempotency key + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 20 January 2025 | Repayment | 17.94 | 16.59 | 1.35 | 0.0 | 0.0 | 83.41 | false | false | + | 01 February 2025 | Merchant Issued Refund | 66.41 | 66.41 | 0.0 | 0.0 | 0.0 | 17.0 | false | true | + | 01 February 2025 | Interest Refund | 1.48 | 1.48 | 0.0 | 0.0 | 0.0 | 15.52 | false | true | +# Step 4: Make another repayment on 25 January (also before MIR) + And Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "25 January 2025" with 10.94 EUR transaction amount and system-generated Idempotency key + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 20 January 2025 | Repayment | 17.94 | 16.59 | 1.35 | 0.0 | 0.0 | 83.41 | false | false | + | 25 January 2025 | Repayment | 10.94 | 10.94 | 0.0 | 0.0 | 0.0 | 72.47 | false | false | + | 01 February 2025 | Merchant Issued Refund | 66.41 | 66.41 | 0.0 | 0.0 | 0.0 | 6.06 | false | true | + | 01 February 2025 | Interest Refund | 1.47 | 1.47 | 0.0 | 0.0 | 0.0 | 4.59 | false | true | +# Step 5: Reverse the 1st repayment (20 Jan) - this triggers full transaction reprocessing +# Key expectation: Interest Refund should be RECALCULATED even though MIR wasn't modified +# Before fix: Interest Refund kept 1.47 because no txn before it was changed +# After fix: Interest Refund recalculated to correct value + When Customer undo "1"th "Repayment" transaction made on "20 January 2025" + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 01 January 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false | + | 20 January 2025 | Repayment | 17.94 | 16.59 | 1.35 | 0.0 | 0.0 | 83.41 | true | false | + | 25 January 2025 | Repayment | 10.94 | 10.94 | 0.0 | 0.0 | 0.0 | 89.06 | false | false | + | 01 February 2025 | Merchant Issued Refund | 66.41 | 64.26 | 2.15 | 0.0 | 0.0 | 24.8 | false | true | + #Interest Refund value is only slightly different (1.46 vs 1.47) which I believe is wrong + | 01 February 2025 | Interest Refund | 1.46 | 1.46 | 0.0 | 0.0 | 0.0 | 23.34 | false | true | + #following steps will fail if Interest Refund is not recalculated properly + Then Loan has 23.97 outstanding amount diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java index 584eec52f7c..a0b789a7fe0 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java @@ -568,7 +568,7 @@ private void handleInterestRefund(final LoanTransaction loanTransaction, final T .filter(LoanTransaction::isNotReversed).map(AbstractPersistableCustom::getId).toList(); final List modifiedTransactions = new ArrayList<>(progCtx.getAlreadyProcessedTransactions().stream() .filter(LoanTransaction::isNotReversed).filter(tr -> tr.getId() == null).toList()); - if (!modifiedTransactions.isEmpty()) { + if (validateInterestRefundTransactionRelation(loanTransaction)) { final Money interestAfterRefund = interestRefundService.totalInterestByTransactions(this, loan.getId(), targetDate, modifiedTransactions, unmodifiedTransactionIds); final Money newAmount = interestBeforeRefund.minus(progCtx.getSumOfInterestRefundAmount()).minus(interestAfterRefund); @@ -580,6 +580,10 @@ private void handleInterestRefund(final LoanTransaction loanTransaction, final T handleRepayment(loanTransaction, ctx); } + private boolean validateInterestRefundTransactionRelation(final LoanTransaction interestRefundTransaction) { + return interestRefundTransaction.getLoanTransactionRelations(tr -> tr.getToTransaction().getId() != null).isEmpty(); + } + private boolean chargeOffIsInEffect(TransactionCtx ctx, LoanTransaction chargeOffTransaction, LoanTransaction loanTransaction) { if (ctx instanceof ProgressiveTransactionCtx progressiveCtx && progressiveCtx.isChargedOff()) { return true; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java index fa3a9aacc85..e5e064192b4 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java @@ -32,6 +32,7 @@ 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,10 +42,10 @@ 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; -import org.apache.fineract.integrationtests.common.BusinessStepHelper; import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.Utils; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; @@ -61,7 +62,6 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest { private static RequestSpecification requestSpec; private static LoanTransactionHelper loanTransactionHelper; private static PostClientsResponse client; - private static BusinessStepHelper businessStepHelper; @BeforeAll public static void setup() { @@ -1633,4 +1633,63 @@ public void verifyMerchantIssuedRefundInTwoPortion() { 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 + loanTransactionHelper.createManualInterestRefund(loanId, refundResponse.getResourceId(), "1 February 2025", 0.47, null); + + verifyTransactions(loanId, // + transaction(100.0, "Disbursement", "01 January 2025"), // + transaction(66.41, "Merchant Issued Refund", "01 February 2025"), // + transaction(0.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 2025") + .transactionAmount(0.0).locale("en")); + + verifyTransactions(loanId, // + transaction(100.0, "Disbursement", "01 January 2025"), // + reversedTransaction(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") // + ); + + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + verifyLoanStatus(loanDetails, LoanStatus.ACTIVE); + Assertions.assertEquals(23.97, Utils.getDoubleValue(loanDetails.getSummary().getTotalOutstanding())); + }); + } + } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanManualInterestRefundResponseStructureTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanManualInterestRefundResponseStructureTest.java index fe692334987..3b29fae7729 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanManualInterestRefundResponseStructureTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanManualInterestRefundResponseStructureTest.java @@ -36,7 +36,6 @@ import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostLoanProductsResponse; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; -import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest; import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.Utils; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; @@ -96,8 +95,8 @@ public void testManualInterestRefundResponseStructureWithoutExternalIds() { targetTransactionIdRef.set(refundResponse.getResourceId()); // Create manual interest refund via API - PostLoansLoanIdTransactionsResponse interestRefundResponse = createManualInterestRefund(loanId, refundResponse.getResourceId(), - "15 January 2024", 5.0, null); + PostLoansLoanIdTransactionsResponse interestRefundResponse = loanTransactionHelper.createManualInterestRefund(loanId, + refundResponse.getResourceId(), "15 January 2024", 5.0, null); assertNotNull(interestRefundResponse, "Interest refund response should not be null"); assertNotNull(interestRefundResponse.getResourceId(), "Interest refund resource ID should not be null"); @@ -157,8 +156,8 @@ public void testManualInterestRefundResponseStructureWithExternalIds() { // Create manual interest refund with external ID String interestRefundExternalId = UUID.randomUUID().toString(); - PostLoansLoanIdTransactionsResponse interestRefundResponse = createManualInterestRefund(loanId, refundResponse.getResourceId(), - "15 February 2024", 5.0, interestRefundExternalId); + PostLoansLoanIdTransactionsResponse interestRefundResponse = loanTransactionHelper.createManualInterestRefund(loanId, + refundResponse.getResourceId(), "15 February 2024", 5.0, interestRefundExternalId); assertNotNull(interestRefundResponse, "Interest refund response should not be null"); assertNotNull(interestRefundResponse.getResourceId(), "Interest refund resource ID should not be null"); @@ -180,22 +179,6 @@ public void testManualInterestRefundResponseStructureWithExternalIds() { }); } - /** - * 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) */ diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java index 8a55b88eb0c..371c26649d8 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java @@ -1709,6 +1709,22 @@ public GetLoansLoanIdTransactionsResponse getLoanTransactionsByExternalId(final 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)