From e0236efa00f23829dfba2aee0e711de21e4d84ee Mon Sep 17 00:00:00 2001 From: Arnold Galovics Date: Mon, 22 Aug 2022 15:35:34 +0200 Subject: [PATCH] Added refund support for Batch API --- .../command/CommandStrategyProvider.java | 7 + .../service/LoanReadPlatformServiceImpl.java | 2 +- .../command/CommandStrategyProviderTest.java | 6 + .../integrationtests/BatchApiTest.java | 134 ++++++++++++++++++ .../integrationtests/common/BatchHelper.java | 67 ++++++--- 5 files changed, 195 insertions(+), 21 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java index 5b8d37d38..ea96f5c50 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java @@ -96,6 +96,13 @@ public class CommandStrategyProvider { this.commandStrategies.put( CommandContext.resource("loans\\/\\d+\\/transactions\\?command=creditBalanceRefund").method("POST").build(), "createTransactionLoanCommandStrategy"); + this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\?command=goodwillCredit").method("POST").build(), + "createTransactionLoanCommandStrategy"); + this.commandStrategies.put( + CommandContext.resource("loans\\/\\d+\\/transactions\\?command=merchantIssuedRefund").method("POST").build(), + "createTransactionLoanCommandStrategy"); + this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\?command=payoutRefund").method("POST").build(), + "createTransactionLoanCommandStrategy"); this.commandStrategies.put(CommandContext.resource("clients\\/\\d+\\?command=activate").method("POST").build(), "activateClientCommandStrategy"); this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\?command=approve").method("POST").build(), diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 385c944f3..b87cb4fb6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -2419,7 +2419,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { sqlBuilder.append( "left join (select lt.loan_id, lt.transaction_date as transactionDate, lt.amount as amount from m_loan_transaction lt "); sqlBuilder.append( - "where lt.loan_id =? and lt.is_reversed = false and lt.transaction_type_enum=2 order by lt.transaction_date desc limit 1) lre on lre.loan_id = l.id "); + "where lt.loan_id =? and lt.is_reversed = false and lt.transaction_type_enum=2 order by lt.transaction_date desc, lt.id desc limit 1) lre on lre.loan_id = l.id "); sqlBuilder.append("where l.id=? "); return sqlBuilder.toString(); } diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java index acf818ce3..c435ca22c 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java @@ -72,6 +72,12 @@ public class CommandStrategyProviderTest { mock(CreateTransactionLoanCommandStrategy.class)), Arguments.of("loans/123/transactions?command=creditBalanceRefund", HttpMethod.POST, "createTransactionLoanCommandStrategy", mock(CreateTransactionLoanCommandStrategy.class)), + Arguments.of("loans/123/transactions?command=goodwillCredit", HttpMethod.POST, "createTransactionLoanCommandStrategy", + mock(CreateTransactionLoanCommandStrategy.class)), + Arguments.of("loans/123/transactions?command=merchantIssuedRefund", HttpMethod.POST, "createTransactionLoanCommandStrategy", + mock(CreateTransactionLoanCommandStrategy.class)), + Arguments.of("loans/123/transactions?command=payoutRefund", HttpMethod.POST, "createTransactionLoanCommandStrategy", + mock(CreateTransactionLoanCommandStrategy.class)), Arguments.of("clients/456?command=activate", HttpMethod.POST, "activateClientCommandStrategy", mock(ActivateClientCommandStrategy.class)), Arguments.of("loans/123?command=approve", HttpMethod.POST, "approveLoanCommandStrategy", diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java index 0123dd4be..20bbf03fe 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java @@ -524,6 +524,75 @@ public class BatchApiTest { "Verify Status Code 200 for Credit Balance Refund"); } + /** + * Tests successful run of batch goodwill credit for loans. 200(OK) status is returned for successful responses. It + * first creates a new loan, approves and disburses the loan. Then a goodwill credit request is made + * + * @see CreateTransactionLoanCommandStrategy + */ + @Test + public void shouldReturnOkStatusForBatchGoodwillCredit() { + + final String loanProductJSON = new LoanProductTestBuilder() // + .withPrincipal("1000.00") // + .withNumberOfRepayments("24") // + .withRepaymentAfterEvery("1") // + .withRepaymentTypeAsMonth() // + .withinterestRatePerPeriod("2") // + .withInterestRateFrequencyTypeAsMonths() // + .withAmortizationTypeAsEqualPrincipalPayment() // + .withInterestTypeAsDecliningBalance() // + .currencyDetails("0", "100").build(null); + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec); + ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID); + + final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec); + Assertions.assertNotNull(collateralId); + final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec, + clientID.toString(), collateralId); + Assertions.assertNotNull(clientCollateralId); + + final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON); + + final Long createActiveClientRequestId = 4730L; + final Long applyLoanRequestId = createActiveClientRequestId + 1; + final Long approveLoanRequestId = applyLoanRequestId + 1; + final Long disburseLoanRequestId = approveLoanRequestId + 1; + final Long goodwillCreditRequestId = disburseLoanRequestId + 1; + + // Create a createClient Request + final BatchRequest br1 = BatchHelper.createActiveClientRequest(createActiveClientRequestId, ""); + + // Create a ApplyLoan Request + final BatchRequest br2 = BatchHelper.applyLoanRequest(applyLoanRequestId, createActiveClientRequestId, productId, + clientCollateralId); + + // Create a approveLoan Request + final BatchRequest br3 = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId); + + // Create a disburseLoan Request + final BatchRequest br4 = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId); + + // Create a good will credit request. + final BatchRequest br5 = BatchHelper.goodwillCreditRequest(goodwillCreditRequestId, disburseLoanRequestId, "500"); + + final List batchRequests = new ArrayList<>(); + + batchRequests.add(br1); + batchRequests.add(br2); + batchRequests.add(br3); + batchRequests.add(br4); + batchRequests.add(br5); + + final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests); + + final List response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec, + jsonifiedRequest); + + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(4).getStatusCode(), "Verify Status Code 200 for Goodwill credit"); + } + /** * Test for the successful activation of a pending client using 'ActivateClientCommandStrategy'. A '200' status code * is expected on successful activation. @@ -1032,6 +1101,71 @@ public class BatchApiTest { } + /** + * Test for the successful merchant issued and payout refund transaction. A '200' status code is expected on + * successful responses. + * + * @see org.apache.fineract.batch.command.internal.ApplyLoanCommandStrategy + * @see org.apache.fineract.batch.command.internal.ApproveLoanCommandStrategy + * @see org.apache.fineract.batch.command.internal.DisburseLoanCommandStrategy + * @see org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy + */ + @Test + public void shouldReturnOkStatusOnSuccessfulTransactionMerchantIssuedAndPayoutRefund() { + final String loanProductJSON = new LoanProductTestBuilder() // + .withPrincipal("10000000.00") // + .withNumberOfRepayments("24") // + .withRepaymentAfterEvery("1") // + .withRepaymentTypeAsMonth() // + .withinterestRatePerPeriod("2") // + .withInterestRateFrequencyTypeAsMonths() // + .withAmortizationTypeAsEqualPrincipalPayment() // + .withInterestTypeAsDecliningBalance() // + .currencyDetails("0", "100").build(null); + + final Long applyLoanRequestId = 5730L; + final Long approveLoanRequestId = 5731L; + final Long disburseLoanRequestId = 5732L; + final Long merchantIssuedRefundRequestId = 5733L; + final Long payoutRefundRequestId = 5734L; + + // Create product + final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON); + + // Create client + final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec); + ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientId); + + // Create an ApplyLoan Request + final BatchRequest applyLoanRequest = BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, productId); + + // Create an approveLoan Request + final BatchRequest approveLoanRequest = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId); + + // Create a disbursement request + final BatchRequest disburseLoanRequest = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId, + LocalDate.now(ZoneId.systemDefault()).minusDays(1)); + + // Create a merchant issued refund request + final BatchRequest merchantIssuedRefundRequest = BatchHelper.merchantIssuedRefundRequest(merchantIssuedRefundRequestId, + applyLoanRequestId, "10"); + + // Create a payout refund request + final BatchRequest payoutRefundRequest = BatchHelper.payoutRefundRequest(payoutRefundRequestId, applyLoanRequestId, "10"); + + final List batchRequests = Arrays.asList(applyLoanRequest, approveLoanRequest, disburseLoanRequest, + merchantIssuedRefundRequest, payoutRefundRequest); + + final List responses = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, this.responseSpec, + BatchHelper.toJsonString(batchRequests)); + + Assertions.assertEquals(HttpStatus.SC_OK, responses.get(0).getStatusCode(), "Verify Status Code 200 for Apply Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, responses.get(1).getStatusCode(), "Verify Status Code 200 for Approve Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, responses.get(2).getStatusCode(), "Verify Status Code 200 for Disburse loan"); + Assertions.assertEquals(HttpStatus.SC_OK, responses.get(3).getStatusCode(), "Verify Status Code 200 for merchant issued refund"); + Assertions.assertEquals(HttpStatus.SC_OK, responses.get(4).getStatusCode(), "Verify Status Code 200 for payout refund"); + } + /** * Delete datatable * diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java index 3284f2785..2d5fb2963 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java @@ -505,7 +505,7 @@ public final class BatchHelper { * @return BatchRequest the batch request */ public static BatchRequest repayLoanRequest(final Long requestId, final Long reference, final String amount) { - return repayLoanRequest(requestId, reference, amount, LocalDate.now(ZoneId.systemDefault())); + return createTransactionRequest(requestId, reference, "repayment", amount, LocalDate.now(ZoneId.systemDefault())); } /** @@ -521,12 +521,13 @@ public final class BatchHelper { * the transaction date * @return BatchRequest the batch request */ - public static BatchRequest repayLoanRequest(final Long requestId, final Long reference, final String amount, final LocalDate date) { + public static BatchRequest createTransactionRequest(final Long requestId, final Long reference, final String transactionCommand, + final String amount, final LocalDate date) { final BatchRequest br = new BatchRequest(); br.setRequestId(requestId); br.setReference(reference); - br.setRelativeUrl("loans/$.loanId/transactions?command=repayment"); + br.setRelativeUrl(String.format("loans/$.loanId/transactions?command=%s", transactionCommand)); br.setMethod("POST"); String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); br.setBody(String.format( @@ -543,38 +544,64 @@ public final class BatchHelper { * @param requestId * the request ID * @param reference - * teh reference + * the reference + * @param amount + * the amount * @return BatchRequest the created {@link BatchRequest} */ public static BatchRequest creditBalanceRefundRequest(final Long requestId, final Long reference, final String amount) { - return creditBalanceRefundRequest(requestId, reference, amount, LocalDate.now(ZoneId.systemDefault())); + return createTransactionRequest(requestId, reference, "creditBalanceRefund", amount, LocalDate.now(ZoneId.systemDefault())); } /** - * Creates and returns a {@link CreateTransactionLoanCommandStrategy} request with given request ID. + * Creates and returns a {@link CreateTransactionLoanCommandStrategy} request with given request ID for goodwill + * credit transaction. * * * @param requestId * the request ID * @param reference - * teh reference - * @param date - * the transaction date + * the reference + * @param amount + * the amount * @return BatchRequest the created {@link BatchRequest} */ - public static BatchRequest creditBalanceRefundRequest(final Long requestId, final Long reference, final String amount, LocalDate date) { - final BatchRequest br = new BatchRequest(); + public static BatchRequest goodwillCreditRequest(final Long requestId, final Long reference, final String amount) { + return createTransactionRequest(requestId, reference, "goodwillCredit", amount, LocalDate.now(ZoneId.systemDefault())); + } - br.setRequestId(requestId); - br.setReference(reference); - br.setRelativeUrl("loans/$.loanId/transactions?command=creditBalanceRefund"); - br.setMethod("POST"); - String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); - br.setBody(String.format( - "{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " + "\"transactionDate\": \"%s\", \"transactionAmount\": %s}", - dateString, amount)); + /** + * Creates and returns a {@link CreateTransactionLoanCommandStrategy} request with given request ID for merchant + * issued refund transaction. + * + * + * @param requestId + * the request ID + * @param reference + * the reference + * @param amount + * the amount + * @return BatchRequest the created {@link BatchRequest} + */ + public static BatchRequest merchantIssuedRefundRequest(final Long requestId, final Long reference, final String amount) { + return createTransactionRequest(requestId, reference, "merchantIssuedRefund", amount, LocalDate.now(ZoneId.systemDefault())); + } - return br; + /** + * Creates and returns a {@link CreateTransactionLoanCommandStrategy} request with given request ID for payout + * refund transaction. + * + * + * @param requestId + * the request ID + * @param reference + * the reference + * @param amount + * the amount + * @return BatchRequest the created {@link BatchRequest} + */ + public static BatchRequest payoutRefundRequest(final Long requestId, final Long reference, final String amount) { + return createTransactionRequest(requestId, reference, "payoutRefund", amount, LocalDate.now(ZoneId.systemDefault())); } /**