You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by al...@apache.org on 2025/07/23 09:32:38 UTC
(fineract) 02/02: FINERACT-2327: added e2e testing for validatoin of feature allow to create Interest Refund transaction manually
This is an automated email from the ASF dual-hosted git repository.
aleks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 0b5391169f5f1d2c9a3d9614b6acc244e4122390
Author: Rustam Zeinalov <tr...@gmail.com>
AuthorDate: Tue Jul 22 15:00:44 2025 +0200
FINERACT-2327: added e2e testing for validatoin of feature allow to create Interest Refund transaction manually
---
.../fineract/test/helper/ErrorMessageHelper.java | 4 +
.../fineract/test/stepdef/loan/LoanStepDef.java | 101 +++++++++++++++++++++
.../features/LoanMerchantIssuedRefund.feature | 19 +++-
.../resources/features/LoanPayoutRefund.feature | 53 ++++++++++-
4 files changed, 175 insertions(+), 2 deletions(-)
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
index dd0a911663..91c4f6da74 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
@@ -72,6 +72,10 @@ public final class ErrorMessageHelper {
return "Interest Refund already exists for this transaction";
}
+ public static String addManualInterestRefundIfReversedFailure() {
+ return "Target transaction must be Merchant Issued Refund or Payout Refund";
+ }
+
public static String addDisbursementExceedMaxAppliedAmountFailure(String totalDisbAmount, String maxDisbursalAmount) {
return String.format("Loan disbursal amount can't be greater than maximum applied loan amount calculation. "
+ "Total disbursed amount: %s Maximum disbursal amount: %s", totalDisbAmount, maxDisbursalAmount);
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
index a4c667045f..e61da4f54d 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
@@ -461,6 +461,59 @@ public class LoanStepDef extends AbstractStepDef {
eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
+ @When("Admin manually adds Interest Refund for {string} transaction made on invalid date {string} with {double} EUR interest refund amount")
+ public void addInterestRefundTransactionManuallyWithInvalidDate(final String transactionTypeInput, final String transactionDate,
+ final double amount) throws IOException {
+ final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ final long loanId = loanResponse.body().getLoanId();
+ final TransactionType transactionType = TransactionType.valueOf(transactionTypeInput);
+
+ final Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
+ ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
+
+ assert loanDetailsResponse.body() != null;
+ final List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
+ assert transactions != null;
+ final GetLoansLoanIdTransactions refundTransaction = transactions.stream()
+ .filter(t -> t.getType() != null
+ && (transactionType.equals(TransactionType.PAYOUT_REFUND) ? "Payout Refund" : "Merchant Issued Refund")
+ .equals(t.getType().getValue()))
+ .findFirst().orElseThrow(() -> new IllegalStateException("No refund transaction found for loan " + loanId));
+
+ final Response<PostLoansLoanIdTransactionsResponse> adjustmentResponse = addInterestRefundTransaction(amount,
+ refundTransaction.getId(), transactionDate);
+ testContext().set(TestContextKey.LOAN_INTEREST_REFUND_RESPONSE, adjustmentResponse);
+ ErrorHelper.checkFailedApiCall(adjustmentResponse, 400);
+ }
+
+ @When("Admin fails to add Interest Refund for {string} transaction made on {string} with {double} EUR interest refund amount")
+ public void addInterestRefundTransactionManuallyFailsInNonPayout(final String transactionTypeInput, final String transactionDate,
+ final double amount) throws IOException {
+ final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ final long loanId = loanResponse.body().getLoanId();
+ final TransactionType transactionType = TransactionType.valueOf(transactionTypeInput);
+
+ final Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
+ ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
+
+ assert loanDetailsResponse.body() != null;
+ final List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
+ assert transactions != null;
+
+ final GetLoansLoanIdTransactions moneyTransaction = transactions.stream()
+ .filter(t -> t.getType() != null && transactionType.equals(TransactionType.REPAYMENT) && t.getDate() != null
+ && transactionDate.equals(FORMATTER.format(t.getDate())))
+ .findFirst().orElseThrow(() -> new IllegalStateException("No repayment transaction found"));
+
+ final Response<PostLoansLoanIdTransactionsResponse> adjustmentResponse = addInterestRefundTransaction(amount,
+ moneyTransaction.getId());
+ testContext().set(TestContextKey.LOAN_INTEREST_REFUND_RESPONSE, adjustmentResponse);
+ final ErrorResponse errorDetails = ErrorResponse.from(adjustmentResponse);
+ assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addManualInterestRefundIfReversedFailure()).isEqualTo(403);
+ assertThat(errorDetails.getSingleError().getDeveloperMessage())
+ .isEqualTo(ErrorMessageHelper.addManualInterestRefundIfReversedFailure());
+ }
+
@Then("Admin fails to add duplicate Interest Refund for {string} transaction made on {string} with {double} EUR interest refund amount")
public void failToAddManualInterestRefundIfAlreadyExists(final String transactionTypeInput, final String transactionDate,
final double amount) throws IOException {
@@ -490,6 +543,35 @@ public class LoanStepDef extends AbstractStepDef {
.isEqualTo(ErrorMessageHelper.addManualInterestRefundIfAlreadyExistsFailure());
}
+ @Then("Admin fails to add Interest Refund {string} transaction after reverse made on {string} with {double} EUR interest refund amount")
+ public void failToAddManualInterestRefundIfReversed(final String transactionTypeInput, final String transactionDate,
+ final double amount) throws IOException {
+ final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ final long loanId = loanResponse.body().getLoanId();
+ final TransactionType transactionType = TransactionType.valueOf(transactionTypeInput);
+
+ final Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
+ ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
+
+ assert loanDetailsResponse.body() != null;
+ final List<GetLoansLoanIdTransactions> transactions = loanDetailsResponse.body().getTransactions();
+ assert transactions != null;
+ final GetLoansLoanIdTransactions refundTransaction = transactions.stream()
+ .filter(t -> t.getType() != null
+ && (transactionType.equals(TransactionType.PAYOUT_REFUND) ? "Payout Refund" : "Merchant Issued Refund")
+ .equals(t.getType().getValue())
+ && t.getDate() != null && transactionDate.equals(FORMATTER.format(t.getDate())))
+ .findFirst().orElseThrow(() -> new IllegalStateException("No refund transaction found for loan " + loanId));
+
+ final Response<PostLoansLoanIdTransactionsResponse> adjustmentResponse = addInterestRefundTransaction(amount,
+ refundTransaction.getId());
+ testContext().set(TestContextKey.LOAN_INTEREST_REFUND_RESPONSE, adjustmentResponse);
+ final ErrorResponse errorDetails = ErrorResponse.from(adjustmentResponse);
+ assertThat(errorDetails.getHttpStatusCode()).as(ErrorMessageHelper.addManualInterestRefundIfAlreadyExistsFailure()).isEqualTo(403);
+ assertThat(errorDetails.getSingleError().getDeveloperMessage())
+ .isEqualTo(ErrorMessageHelper.addManualInterestRefundIfReversedFailure());
+ }
+
private void createTransactionWithAutoIdempotencyKeyAndWithExternalOwner(String transactionTypeInput, String transactionPaymentType,
String transactionDate, double transactionAmount, String externalOwnerId) throws IOException {
eventStore.reset();
@@ -5058,4 +5140,23 @@ public class LoanStepDef extends AbstractStepDef {
return loanTransactionsApi.adjustLoanTransaction(loanId, transactionId, interestRefundRequest, "interest-refund").execute();
}
+ private Response<PostLoansLoanIdTransactionsResponse> addInterestRefundTransaction(final double amount, final Long transactionId,
+ final String transactionDate) throws IOException {
+ final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ assert loanResponse.body() != null;
+ final long loanId = loanResponse.body().getLoanId();
+
+ final DefaultPaymentType paymentType = DefaultPaymentType.AUTOPAY;
+ final Long paymentTypeValue = paymentTypeResolver.resolve(paymentType);
+
+ final PostLoansLoanIdTransactionsTransactionIdRequest interestRefundRequest = new PostLoansLoanIdTransactionsTransactionIdRequest()
+ .dateFormat("dd MMMM yyyy").locale("en").transactionAmount(amount).paymentTypeId(paymentTypeValue)
+ .externalId("EXT-INT-REF-" + UUID.randomUUID()).note("");
+
+ if (transactionDate != null) {
+ interestRefundRequest.transactionDate(transactionDate);
+ }
+
+ return loanTransactionsApi.adjustLoanTransaction(loanId, transactionId, interestRefundRequest, "interest-refund").execute();
+ }
}
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 1a9aacd2b8..ebb3adc892 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanMerchantIssuedRefund.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanMerchantIssuedRefund.feature
@@ -531,4 +531,21 @@ Feature: MerchantIssuedRefund
| 10 July 2024 | Repayment | 338.9 | 336.48 | 2.42 | 0.0 | 0.0 | 663.52 | false | false |
| 15 July 2024 | Merchant Issued Refund | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 613.52 | false | false |
| 15 July 2024 | Interest Refund | 0.19 | 0.0 | 0.19 | 0.0 | 0.0 | 613.52 | false | false |
- When Admin fails to add duplicate Interest Refund for "MERCHANT_ISSUED_REFUND" transaction made on "15 July 2024" with 0.19 EUR interest refund amount
\ No newline at end of file
+ When Admin fails to add duplicate Interest Refund for "MERCHANT_ISSUED_REFUND" transaction made on "15 July 2024" with 0.19 EUR interest refund amount
+
+ @TestRailId:C3880
+ Scenario: Prevent manual Interest Refund creation with mismatched transaction date for Merchant Issued Refund
+ When Admin sets the business date to "01 July 2024"
+ 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_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 July 2024 | 1000 | 10 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 July 2024" with "1000" amount and expected disbursement date on "01 July 2024"
+ And Admin successfully disburse the loan on "01 July 2024" with "1000" EUR transaction amount
+ When Admin sets the business date to "10 July 2024"
+ And Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "10 July 2024" with 338.9 EUR transaction amount and system-generated Idempotency key
+ When Admin sets the business date to "15 July 2024"
+ And Customer makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "15 July 2024" with 50 EUR transaction amount and system-generated Idempotency key and interestRefundCalculation false
+ When Admin sets the business date to "16 July 2024"
+ #mismatch date for Interest Refund
+ When Admin manually adds Interest Refund for "MERCHANT_ISSUED_REFUND" transaction made on invalid date "16 July 2024" with 2.42 EUR interest refund amount
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanPayoutRefund.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanPayoutRefund.feature
index 4de834faee..a20b25acc9 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanPayoutRefund.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanPayoutRefund.feature
@@ -390,4 +390,55 @@ Feature: PayoutRefund
| 10 July 2024 | Repayment | 338.9 | 336.48 | 2.42 | 0.0 | 0.0 | 663.52 | false | false |
| 15 July 2024 | Payout Refund | 50.0 | 50.0 | 0.0 | 0.0 | 0.0 | 613.52 | false | false |
| 15 July 2024 | Interest Refund | 0.19 | 0.0 | 0.19 | 0.0 | 0.0 | 613.52 | false | false |
- When Admin fails to add duplicate Interest Refund for "PAYOUT_REFUND" transaction made on "15 July 2024" with 0.19 EUR interest refund amount
\ No newline at end of file
+ When Admin fails to add duplicate Interest Refund for "PAYOUT_REFUND" transaction made on "15 July 2024" with 0.19 EUR interest refund amount
+
+ @TestRailId:C3878
+ Scenario: Prevent manual Interest Refund creation on reversed refund transaction
+ When Admin sets the business date to "01 July 2024"
+ 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_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 July 2024 | 1000 | 10 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 July 2024" with "1000" amount and expected disbursement date on "01 July 2024"
+ And Admin successfully disburse the loan on "01 July 2024" with "1000" EUR transaction amount
+ When Admin sets the business date to "10 July 2024"
+ And Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "10 July 2024" with 338.9 EUR transaction amount and system-generated Idempotency key
+ When Admin sets the business date to "15 July 2024"
+ And Customer makes "PAYOUT_REFUND" transaction with "AUTOPAY" payment type on "15 July 2024" with 50 EUR transaction amount and system-generated Idempotency key and interestRefundCalculation false
+ When Customer undo "1"th transaction made on "15 July 2024"
+ Then Admin fails to add Interest Refund "PAYOUT_REFUND" transaction after reverse made on "15 July 2024" with 2.42 EUR interest refund amount
+
+ @TestRailId:C3879
+ Scenario: Prevent manual Interest Refund creation on non-refund transaction type
+ When Admin sets the business date to "01 July 2024"
+ 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_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 July 2024 | 1000 | 10 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 July 2024" with "1000" amount and expected disbursement date on "01 July 2024"
+ And Admin successfully disburse the loan on "01 July 2024" with "1000" EUR transaction amount
+ Then Loan Repayment schedule has 3 periods, with the following data for periods:
+ | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
+ | | | 01 July 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
+ | 1 | 31 | 01 August 2024 | | 669.43 | 330.57 | 8.33 | 0.0 | 0.0 | 338.9 | 0.0 | 0.0 | 0.0 | 338.9 |
+ | 2 | 31 | 01 September 2024 | | 336.11 | 333.32 | 5.58 | 0.0 | 0.0 | 338.9 | 0.0 | 0.0 | 0.0 | 338.9 |
+ | 3 | 30 | 01 October 2024 | | 0.0 | 336.11 | 2.8 | 0.0 | 0.0 | 338.91 | 0.0 | 0.0 | 0.0 | 338.91 |
+ And Loan Repayment schedule has the following data in Total row:
+ | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
+ | 1000.0 | 16.71 | 0.0 | 0.0 | 1016.71 | 0.0 | 0.0 | 0.0 | 1016.71 |
+ When Admin sets the business date to "10 July 2024"
+ And Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "10 July 2024" with 338.9 EUR transaction amount and system-generated Idempotency key
+ Then Loan Repayment schedule has 3 periods, with the following data for periods:
+ | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
+ | | | 01 July 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
+ | 1 | 31 | 01 August 2024 | 10 July 2024 | 663.52 | 336.48 | 2.42 | 0.0 | 0.0 | 338.9 | 338.9 | 338.9 | 0.0 | 0.0 |
+ | 2 | 31 | 01 September 2024 | | 334.07 | 329.45 | 9.45 | 0.0 | 0.0 | 338.9 | 0.0 | 0.0 | 0.0 | 338.9 |
+ | 3 | 30 | 01 October 2024 | | 0.0 | 334.07 | 2.78 | 0.0 | 0.0 | 336.85 | 0.0 | 0.0 | 0.0 | 336.85 |
+ And Loan Repayment schedule has the following data in Total row:
+ | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
+ | 1000.0 | 14.65 | 0.0 | 0.0 | 1014.65 | 338.9 | 338.9 | 0.0 | 675.75 |
+ And Loan Transactions tab has the following data:
+ | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
+ | 01 July 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
+ | 10 July 2024 | Repayment | 338.9 | 336.48 | 2.42 | 0.0 | 0.0 | 663.52 | false | false |
+ When Admin fails to add Interest Refund for "REPAYMENT" transaction made on "10 July 2024" with 2.42 EUR interest refund amount