From 9baf053d0b4269c497e3e289835c59a4d8b2a7bb Mon Sep 17 00:00:00 2001 From: taskain7 Date: Wed, 8 Mar 2023 17:38:24 +0100 Subject: [PATCH] [FINERACT-1751] Add savings account transaction request to Batch API --- .../command/CommandStrategyProvider.java | 8 ++ .../GetSavingsAccountByIdCommandStrategy.java | 81 +++++++++++ ...countAdjustTransactionCommandStrategy.java | 70 +++++++++ ...ingsAccountTransactionCommandStrategy.java | 63 ++++++++ .../integrationtests/BatchApiTest.java | 49 +++++++ .../integrationtests/common/BatchHelper.java | 134 ++++++++++++++++++ 6 files changed, 405 insertions(+) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetSavingsAccountByIdCommandStrategy.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/SavingsAccountAdjustTransactionCommandStrategy.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/SavingsAccountTransactionCommandStrategy.java 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 0560934f1..24a2532f6 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 @@ -128,7 +128,15 @@ public class CommandStrategyProvider { commandStrategies.put( CommandContext.resource("loans\\/external-id\\/" + UUID_PARAM_REGEX + OPTIONAL_QUERY_PARAM_REGEX).method(GET).build(), "getLoanByExternalIdCommandStrategy"); + commandStrategies.put(CommandContext.resource("savingsaccounts\\/" + NUMBER_REGEX + OPTIONAL_QUERY_PARAM_REGEX).method(GET).build(), + "getSavingsAccountByIdCommandStrategy"); commandStrategies.put(CommandContext.resource("savingsaccounts").method(POST).build(), "applySavingsCommandStrategy"); + commandStrategies.put(CommandContext + .resource("savingsaccounts\\/" + NUMBER_REGEX + "\\/transactions" + OPTIONAL_COMMAND_PARAM_REGEX).method(POST).build(), + "savingsAccountTransactionCommandStrategy"); + commandStrategies.put(CommandContext + .resource("savingsaccounts\\/" + NUMBER_REGEX + "\\/transactions\\/" + NUMBER_REGEX + OPTIONAL_COMMAND_PARAM_REGEX) + .method(POST).build(), "savingsAccountAdjustTransactionCommandStrategy"); commandStrategies.put(CommandContext.resource("loans\\/" + NUMBER_REGEX + "\\/charges").method(POST).build(), "createChargeCommandStrategy"); commandStrategies diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetSavingsAccountByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetSavingsAccountByIdCommandStrategy.java new file mode 100644 index 000000000..fb2c28b03 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetSavingsAccountByIdCommandStrategy.java @@ -0,0 +1,81 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.batch.command.internal; + +import java.util.HashMap; +import java.util.Map; +import javax.ws.rs.core.UriInfo; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.fineract.batch.command.CommandStrategy; +import org.apache.fineract.batch.command.CommandStrategyUtils; +import org.apache.fineract.batch.domain.BatchRequest; +import org.apache.fineract.batch.domain.BatchResponse; +import org.apache.fineract.infrastructure.core.api.MutableUriInfo; +import org.apache.fineract.portfolio.savings.api.SavingsAccountsApiResource; +import org.apache.http.HttpStatus; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class GetSavingsAccountByIdCommandStrategy implements CommandStrategy { + + private final SavingsAccountsApiResource savingsAccountsApiResource; + + @Override + public BatchResponse execute(BatchRequest batchRequest, UriInfo uriInfo) { + final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo); + final BatchResponse response = new BatchResponse(); + + response.setRequestId(batchRequest.getRequestId()); + response.setHeaders(batchRequest.getHeaders()); + + final String relativeUrl = batchRequest.getRelativeUrl(); + + final Long savingsAccountId; + Map queryParameters = new HashMap<>(); + if (relativeUrl.indexOf('?') > 0) { + savingsAccountId = Long.parseLong(StringUtils.substringBetween(relativeUrl, "/", "?")); + queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl); + CommandStrategyUtils.addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters); + } else { + savingsAccountId = Long.parseLong(StringUtils.substringAfter(relativeUrl, "/")); + } + + String staffInSelectedOfficeOnly = null; + String chargeStatus = null; + if (!queryParameters.isEmpty()) { + if (queryParameters.containsKey("staffInSelectedOfficeOnly")) { + staffInSelectedOfficeOnly = queryParameters.get("staffInSelectedOfficeOnly"); + } + if (queryParameters.containsKey("chargeStatus")) { + chargeStatus = queryParameters.get("chargeStatus"); + } + } + + final String responseBody = savingsAccountsApiResource.retrieveOne(savingsAccountId, + Boolean.parseBoolean(staffInSelectedOfficeOnly), chargeStatus, uriInfo); + + response.setStatusCode(HttpStatus.SC_OK); + + response.setBody(responseBody); + + return response; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/SavingsAccountAdjustTransactionCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/SavingsAccountAdjustTransactionCommandStrategy.java new file mode 100644 index 000000000..d97b8e32c --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/SavingsAccountAdjustTransactionCommandStrategy.java @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.batch.command.internal; + +import com.google.common.base.Splitter; +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.UriInfo; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.fineract.batch.command.CommandStrategy; +import org.apache.fineract.batch.command.CommandStrategyUtils; +import org.apache.fineract.batch.domain.BatchRequest; +import org.apache.fineract.batch.domain.BatchResponse; +import org.apache.fineract.portfolio.savings.api.SavingsAccountTransactionsApiResource; +import org.apache.http.HttpStatus; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class SavingsAccountAdjustTransactionCommandStrategy implements CommandStrategy { + + private final SavingsAccountTransactionsApiResource savingsAccountTransactionsApiResource; + + @Override + public BatchResponse execute(BatchRequest batchRequest, UriInfo uriInfo) { + final BatchResponse response = new BatchResponse(); + + response.setRequestId(batchRequest.getRequestId()); + response.setHeaders(batchRequest.getHeaders()); + String relativeUrl = batchRequest.getRelativeUrl(); + final List pathParameters; + String command = null; + if (relativeUrl.indexOf('?') > 0) { + String subString = StringUtils.substringBefore(relativeUrl, "?"); + pathParameters = Splitter.on('/').splitToList(subString); + final Map queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl); + command = queryParameters.get("command"); + } else { + pathParameters = Splitter.on('/').splitToList(relativeUrl); + } + + Long savingsAccountId = Long.parseLong(pathParameters.get(1)); + Long transactionId = Long.parseLong(pathParameters.get(3)); + final String responseBody = savingsAccountTransactionsApiResource.adjustTransaction(savingsAccountId, transactionId, command, + batchRequest.getBody()); + + response.setStatusCode(HttpStatus.SC_OK); + + response.setBody(responseBody); + + return response; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/SavingsAccountTransactionCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/SavingsAccountTransactionCommandStrategy.java new file mode 100644 index 000000000..ce200fbee --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/SavingsAccountTransactionCommandStrategy.java @@ -0,0 +1,63 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.batch.command.internal; + +import com.google.common.base.Splitter; +import java.util.List; +import java.util.Map; +import javax.ws.rs.core.UriInfo; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.batch.command.CommandStrategy; +import org.apache.fineract.batch.command.CommandStrategyUtils; +import org.apache.fineract.batch.domain.BatchRequest; +import org.apache.fineract.batch.domain.BatchResponse; +import org.apache.fineract.portfolio.savings.api.SavingsAccountTransactionsApiResource; +import org.apache.http.HttpStatus; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class SavingsAccountTransactionCommandStrategy implements CommandStrategy { + + private final SavingsAccountTransactionsApiResource savingsAccountTransactionsApiResource; + + @Override + public BatchResponse execute(BatchRequest batchRequest, UriInfo uriInfo) { + final BatchResponse response = new BatchResponse(); + + response.setRequestId(batchRequest.getRequestId()); + response.setHeaders(batchRequest.getHeaders()); + String relativeUrl = batchRequest.getRelativeUrl(); + final List pathParameters = Splitter.on('/').splitToList(relativeUrl); + String command = null; + if (relativeUrl.indexOf('?') > 0) { + final Map queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl); + command = queryParameters.get("command"); + } + + Long savingsAccountId = Long.parseLong(pathParameters.get(1)); + final String responseBody = savingsAccountTransactionsApiResource.transaction(savingsAccountId, command, batchRequest.getBody()); + + response.setStatusCode(HttpStatus.SC_OK); + + response.setBody(responseBody); + + return response; + } +} 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 151b302b3..0cf426f88 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 @@ -53,7 +53,9 @@ import org.apache.fineract.integrationtests.common.Utils; import org.apache.fineract.integrationtests.common.charges.ChargesHelper; import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; +import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper; import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper; +import org.apache.fineract.integrationtests.common.savings.SavingsStatusChecker; import org.apache.fineract.integrationtests.common.system.CodeHelper; import org.apache.fineract.integrationtests.common.system.DatatableHelper; import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; @@ -1918,6 +1920,53 @@ public class BatchApiTest { Assertions.assertEquals(date, payoutRefundDate); } + @Test + public void shouldReturnOkStatusOnModifyingSavingAccount() { + final String startDate = "10 April 2022"; + final SavingsProductHelper savingsProductHelper = new SavingsProductHelper(); + final SavingsAccountHelper savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec); + final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec, startDate); + Assertions.assertNotNull(clientID); + final String savingsProductJSON = savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily() + .withInterestPostingPeriodTypeAsDaily().withInterestCalculationPeriodTypeAsDailyBalance().build(); + final Integer savingsProductID = SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec); + Assertions.assertNotNull(savingsProductID); + final Integer savingsId = savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, savingsProductID, "INDIVIDUAL", + startDate); + Assertions.assertNotNull(savingsId); + HashMap savingsStatusHashMap = savingsAccountHelper.approveSavingsOnDate(savingsId, startDate); + SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap); + savingsStatusHashMap = savingsAccountHelper.activateSavingsAccount(savingsId, startDate); + SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap); + + final BatchRequest getSavingAccountRequest = BatchHelper.getSavingAccount(1L, Long.valueOf(savingsId), "chargeStatus=all", null); + final BatchRequest depositSavingAccountRequest = BatchHelper.depositSavingAccount(2L, 1L); + final BatchRequest holdAmountOnSavingAccountRequest = BatchHelper.holdAmountOnSavingAccount(3L, 1L); + + final List batchRequests1 = Arrays.asList(getSavingAccountRequest, depositSavingAccountRequest, + holdAmountOnSavingAccountRequest); + final List responses1 = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, this.responseSpec, + BatchHelper.toJsonString(batchRequests1)); + + Assertions.assertEquals(HttpStatus.SC_OK, responses1.get(1).getStatusCode(), "Verify Status Code 200 for deposit saving account"); + Assertions.assertEquals(HttpStatus.SC_OK, responses1.get(2).getStatusCode(), + "Verify Status Code 200 for hold amount on saving account"); + final FromJsonHelper jsonHelper = new FromJsonHelper(); + final Long holdAmountTransactionId = jsonHelper.parse(responses1.get(2).getBody()).getAsJsonObject().get("resourceId").getAsLong(); + + final BatchRequest releaseAmountOnSavingAccountRequest = BatchHelper.releaseAmountOnSavingAccount(2L, 1L, holdAmountTransactionId); + final BatchRequest withdrawSavingAccountRequest = BatchHelper.withdrawSavingAccount(3L, 1L); + + final List batchRequests2 = Arrays.asList(getSavingAccountRequest, releaseAmountOnSavingAccountRequest, + withdrawSavingAccountRequest); + final List responses2 = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, this.responseSpec, + BatchHelper.toJsonString(batchRequests2)); + + Assertions.assertEquals(HttpStatus.SC_OK, responses2.get(1).getStatusCode(), + "Verify Status Code 200 for release amount on saving account"); + Assertions.assertEquals(HttpStatus.SC_OK, responses2.get(2).getStatusCode(), "Verify Status Code 200 for withdraw saving account"); + } + /** * Test for finding datatable entry by the query API and update its value */ 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 68a04c8ce..6aabe2372 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 @@ -1433,4 +1433,138 @@ public final class BatchHelper { return br; } + + /** + * Creates and returns a batch request to get saving account. + * + * @param accountId + * the saving account id + * @param queryParameter + * the query parameters + * @param referenceId + * the reference id + * @return the {@link BatchRequest} + */ + public static BatchRequest getSavingAccount(final Long requestId, final Long accountId, final String queryParameter, + final Long referenceId) { + final BatchRequest br = new BatchRequest(); + String relativeUrl = String.format("savingsaccounts/%s", accountId); + if (queryParameter != null) { + relativeUrl = relativeUrl + "?" + queryParameter; + } + + br.setRequestId(requestId); + br.setReference(referenceId); + br.setRelativeUrl(relativeUrl); + br.setMethod(HttpMethod.GET); + br.setBody("{}"); + + return br; + } + + /** + * Creates and returns a {@link org.apache.fineract.batch.command.internal.SavingsAccountTransactionCommandStrategy} + * request with given request ID. + * + * + * @param requestId + * the request ID + * @param reference + * teh reference + * @return BatchRequest the created {@link BatchRequest} + */ + public static BatchRequest depositSavingAccount(final Long requestId, final Long reference) { + final BatchRequest br = new BatchRequest(); + + br.setRequestId(requestId); + br.setReference(reference); + br.setRelativeUrl("savingsaccounts/$.id/transactions?command=deposit"); + br.setMethod(HttpMethod.POST); + final LocalDate transactionDate = LocalDate.now(ZoneId.systemDefault()); + final String transactionDateString = transactionDate.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); + br.setBody(String.format("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " + + "\"transactionDate\": \"%s\", \"transactionAmount\": \"100\", \"paymentTypeId\": \"1\"}", transactionDateString)); + + return br; + } + + /** + * Creates and returns a {@link org.apache.fineract.batch.command.internal.SavingsAccountTransactionCommandStrategy} + * request with given request ID. + * + * + * @param requestId + * the request ID + * @param reference + * teh reference + * @return BatchRequest the created {@link BatchRequest} + */ + public static BatchRequest withdrawSavingAccount(final Long requestId, final Long reference) { + final BatchRequest br = new BatchRequest(); + + br.setRequestId(requestId); + br.setReference(reference); + br.setRelativeUrl("savingsaccounts/$.id/transactions?command=withdrawal"); + br.setMethod(HttpMethod.POST); + final LocalDate transactionDate = LocalDate.now(ZoneId.systemDefault()); + final String transactionDateString = transactionDate.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); + br.setBody(String.format("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " + + "\"transactionDate\": \"%s\", \"transactionAmount\": \"80\", \"paymentTypeId\": \"1\"}", transactionDateString)); + + return br; + } + + /** + * Creates and returns a {@link org.apache.fineract.batch.command.internal.SavingsAccountTransactionCommandStrategy} + * request with given request ID. + * + * + * @param requestId + * the request ID + * @param reference + * teh reference + * @return BatchRequest the created {@link BatchRequest} + */ + public static BatchRequest holdAmountOnSavingAccount(final Long requestId, final Long reference) { + final BatchRequest br = new BatchRequest(); + + br.setRequestId(requestId); + br.setReference(reference); + br.setRelativeUrl("savingsaccounts/$.id/transactions?command=holdAmount"); + br.setMethod(HttpMethod.POST); + final LocalDate transactionDate = LocalDate.now(ZoneId.systemDefault()); + final String transactionDateString = transactionDate.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); + br.setBody(String.format( + "{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " + + "\"transactionDate\": \"%s\", \"transactionAmount\": \"10\", \"reasonForBlock\": \"test\"}", + transactionDateString)); + + return br; + } + + /** + * Creates and returns a + * {@link org.apache.fineract.batch.command.internal.SavingsAccountAdjustTransactionCommandStrategy} request with + * given request ID. + * + * + * @param requestId + * the request ID + * @param reference + * the reference + * @param transactionId + * the transactionId + * @return BatchRequest the created {@link BatchRequest} + */ + public static BatchRequest releaseAmountOnSavingAccount(final Long requestId, final Long reference, final Long transactionId) { + final BatchRequest br = new BatchRequest(); + + br.setRequestId(requestId); + br.setReference(reference); + br.setRelativeUrl("savingsaccounts/$.id/transactions/" + transactionId + "?command=releaseAmount"); + br.setMethod(HttpMethod.POST); + br.setBody("{\"isBulk\": \"false\"}"); + + return br; + } }