FINERACT-2327: added e2e testing for validatoin of feature allow to create Interest Refund transaction manually

This commit is contained in:
Rustam Zeinalov
2025-07-23 11:32:30 +02:00
committed by Aleksandar Vidakovic
parent c2e19bb68f
commit 0b5391169f
4 changed files with 175 additions and 2 deletions
@@ -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);
@@ -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();
}
}
@@ -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
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
@@ -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
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