This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 8dcb711e4 [FINERACT-1760] Client transactions APIs with external 
client ID
8dcb711e4 is described below

commit 8dcb711e4449d569c8c544802f0e5eef1087c8d0
Author: taskain7 <[email protected]>
AuthorDate: Mon Jul 10 15:51:29 2023 +0200

    [FINERACT-1760] Client transactions APIs with external client ID
---
 .../client/api/ClientTransactionsApiResource.java  | 116 ++++++++++++++++++---
 .../client/ClientTransactionTest.java              |  94 +++++++++++++++++
 .../integrationtests/common/ClientHelper.java      |  17 +++
 3 files changed, 214 insertions(+), 13 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientTransactionsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientTransactionsApiResource.java
index 6126f0892..f70e8c1a3 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientTransactionsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientTransactionsApiResource.java
@@ -35,6 +35,7 @@ import jakarta.ws.rs.QueryParam;
 import jakarta.ws.rs.core.Context;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.UriInfo;
+import java.util.Objects;
 import lombok.RequiredArgsConstructor;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.commands.domain.CommandWrapper;
@@ -45,14 +46,17 @@ import 
org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import 
org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
 import 
org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
 import 
org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
 import org.apache.fineract.infrastructure.core.service.Page;
 import org.apache.fineract.infrastructure.core.service.SearchParameters;
 import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.portfolio.client.data.ClientTransactionData;
+import org.apache.fineract.portfolio.client.exception.ClientNotFoundException;
+import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
 import 
org.apache.fineract.portfolio.client.service.ClientTransactionReadPlatformService;
 import org.springframework.stereotype.Component;
 
-@Path("/v1/clients/{clientId}/transactions")
+@Path("/v1/clients")
 @Component
 @Tag(name = "Client Transaction", description = "Client Transactions refer to 
transactions made directly against a Client's internal account. Currently, 
these transactions are only created as a result of charge payments/waivers. You 
are allowed to undo a transaction, however you cannot explicitly create one. ")
 @RequiredArgsConstructor
@@ -63,8 +67,10 @@ public class ClientTransactionsApiResource {
     private final DefaultToApiJsonSerializer<ClientTransactionData> 
toApiJsonSerializer;
     private final ApiRequestParameterHelper apiRequestParameterHelper;
     private final PortfolioCommandSourceWritePlatformService 
commandsSourceWritePlatformService;
+    private final ClientReadPlatformService clientReadPlatformService;
 
     @GET
+    @Path("{clientId}/transactions")
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @Operation(summary = "List Client Transactions", description = "The list 
capability of client transaction can support pagination."
@@ -76,16 +82,11 @@ public class ClientTransactionsApiResource {
             @QueryParam("limit") @Parameter(description = "limit") final 
Integer limit) {
         
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
 
-        SearchParameters searchParameters = 
SearchParameters.forPagination(offset, limit);
-        final Page<ClientTransactionData> clientTransactions = 
clientTransactionReadPlatformService.retrieveAllTransactions(clientId,
-                searchParameters);
-
-        final ApiRequestJsonSerializationSettings settings = 
apiRequestParameterHelper.process(uriInfo.getQueryParameters());
-        return toApiJsonSerializer.serialize(settings, clientTransactions, 
ClientApiConstants.CLIENT_TRANSACTION_RESPONSE_DATA_PARAMETERS);
+        return getAllClientTransactions(clientId, uriInfo, offset, limit);
     }
 
     @GET
-    @Path("{transactionId}")
+    @Path("{clientId}/transactions/{transactionId}")
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @Operation(summary = "Retrieve a Client Transaction", description = 
"Example Requests:\n" + "clients/1/transactions/1\n" + "\n" + "\n"
@@ -98,14 +99,11 @@ public class ClientTransactionsApiResource {
 
         
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
 
-        final ClientTransactionData clientTransaction = 
clientTransactionReadPlatformService.retrieveTransaction(clientId, 
transactionId);
-
-        final ApiRequestJsonSerializationSettings settings = 
apiRequestParameterHelper.process(uriInfo.getQueryParameters());
-        return toApiJsonSerializer.serialize(settings, clientTransaction, 
ClientApiConstants.CLIENT_TRANSACTION_RESPONSE_DATA_PARAMETERS);
+        return getClientTransaction(clientId, transactionId, uriInfo);
     }
 
     @POST
-    @Path("{transactionId}")
+    @Path("{clientId}/transactions/{transactionId}")
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @Operation(summary = "Undo a Client Transaction", description = "Undoes a 
Client Transaction")
@@ -116,6 +114,94 @@ public class ClientTransactionsApiResource {
             @QueryParam("command") @Parameter(description = "command") final 
String commandParam,
             @Parameter(hidden = true) final String apiRequestBodyAsJson) {
 
+        return undoTransaction(clientId, transactionId, commandParam, 
apiRequestBodyAsJson);
+    }
+
+    @GET
+    @Path("external-id/{clientExternalId}/transactions")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "List Client Transactions", description = "The list 
capability of client transaction can support pagination."
+            + "\n\n" + "Example Requests:\n\n" + 
"clients/189/transactions\n\n" + "clients/189/transactions?offset=10&limit=50")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsResponse.class)))
 })
+    public String retrieveAllClientTransactions(
+            @PathParam("clientExternalId") @Parameter(description = 
"clientExternalId") final String clientExternalId,
+            @Context final UriInfo uriInfo, @QueryParam("offset") 
@Parameter(description = "offset") final Integer offset,
+            @QueryParam("limit") @Parameter(description = "limit") final 
Integer limit) {
+        
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
+
+        Long clientId = resolveClientId(clientExternalId);
+        if (Objects.isNull(clientId)) {
+            throw new ClientNotFoundException();
+        }
+
+        return getAllClientTransactions(clientId, uriInfo, offset, limit);
+    }
+
+    @GET
+    @Path("external-id/{clientExternalId}/transactions/{transactionId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Retrieve a Client Transaction", description = 
"Example Requests:\n" + "clients/1/transactions/1\n" + "\n" + "\n"
+            + "clients/1/transactions/1?fields=id,officeName")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsTransactionIdResponse.class)))
 })
+    public String retrieveClientTransaction(
+            @PathParam("clientExternalId") @Parameter(description = 
"clientExternalId") final String clientExternalId,
+            @PathParam("transactionId") @Parameter(description = 
"transactionId") final Long transactionId,
+            @Context final UriInfo uriInfo) {
+
+        
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
+
+        Long clientId = resolveClientId(clientExternalId);
+
+        if (Objects.isNull(clientId)) {
+            throw new ClientNotFoundException();
+        }
+
+        return getClientTransaction(clientId, transactionId, uriInfo);
+    }
+
+    @POST
+    @Path("external-id/{clientExternalId}/transactions/{transactionId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Undo a Client Transaction", description = "Undoes a 
Client Transaction")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(schema = @Schema(implementation = 
ClientTransactionsApiResourceSwagger.PostClientsClientIdTransactionsTransactionIdResponse.class)))
 })
+    public String undoClientTransaction(
+            @PathParam("clientExternalId") @Parameter(description = 
"clientExternalId") final String clientExternalId,
+            @PathParam("transactionId") @Parameter(description = 
"transactionId") final Long transactionId,
+            @QueryParam("command") @Parameter(description = "command") final 
String commandParam,
+            @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+
+        Long clientId = resolveClientId(clientExternalId);
+        if (Objects.isNull(clientId)) {
+            throw new ClientNotFoundException();
+        }
+
+        return undoTransaction(clientId, transactionId, commandParam, 
apiRequestBodyAsJson);
+    }
+
+    private String getClientTransaction(Long clientId, Long transactionId, 
UriInfo uriInfo) {
+        final ClientTransactionData clientTransaction = 
clientTransactionReadPlatformService.retrieveTransaction(clientId, 
transactionId);
+
+        final ApiRequestJsonSerializationSettings settings = 
apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+        return toApiJsonSerializer.serialize(settings, clientTransaction, 
ClientApiConstants.CLIENT_TRANSACTION_RESPONSE_DATA_PARAMETERS);
+    }
+
+    private String getAllClientTransactions(Long clientId, UriInfo uriInfo, 
Integer offset, Integer limit) {
+
+        SearchParameters searchParameters = 
SearchParameters.forPagination(offset, limit);
+        final Page<ClientTransactionData> clientTransactions = 
clientTransactionReadPlatformService.retrieveAllTransactions(clientId,
+                searchParameters);
+
+        final ApiRequestJsonSerializationSettings settings = 
apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+        return toApiJsonSerializer.serialize(settings, clientTransactions, 
ClientApiConstants.CLIENT_TRANSACTION_RESPONSE_DATA_PARAMETERS);
+    }
+
+    private String undoTransaction(Long clientId, Long transactionId, String 
commandParam, String apiRequestBodyAsJson) {
         if (is(commandParam, 
ClientApiConstants.CLIENT_TRANSACTION_COMMAND_UNDO)) {
             final CommandWrapper commandRequest = new 
CommandWrapperBuilder().undoClientTransaction(clientId, transactionId)
                     .withJson(apiRequestBodyAsJson).build();
@@ -128,6 +214,10 @@ public class ClientTransactionsApiResource {
         }
     }
 
+    private Long resolveClientId(String clientExternalId) {
+        return 
clientReadPlatformService.retrieveClientIdByExternalId(ExternalIdFactory.produce(clientExternalId));
+    }
+
     private boolean is(final String commandParam, final String commandValue) {
         return StringUtils.isNotBlank(commandParam) && 
commandParam.trim().equalsIgnoreCase(commandValue);
     }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientTransactionTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientTransactionTest.java
new file mode 100644
index 000000000..925c5dbce
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientTransactionTest.java
@@ -0,0 +1,94 @@
+/**
+ * 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.integrationtests.client;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.UUID;
+import 
org.apache.fineract.client.models.GetClientsClientIdTransactionsResponse;
+import 
org.apache.fineract.client.models.GetClientsClientIdTransactionsTransactionIdResponse;
+import 
org.apache.fineract.client.models.PostClientsClientIdTransactionsTransactionIdResponse;
+import org.apache.fineract.client.models.PostClientsRequest;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class ClientTransactionTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private ClientHelper clientHelper;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+        clientHelper = new ClientHelper(requestSpec, responseSpec);
+    }
+
+    @Test
+    public void testClientTransactions() {
+        PostClientsRequest createClientRequest = 
ClientHelper.defaultClientCreationRequest();
+        String clientExternalId = UUID.randomUUID().toString();
+        createClientRequest.setExternalId(clientExternalId);
+        PostClientsResponse client = 
clientHelper.createClient(createClientRequest);
+        Long clientId = client.getClientId();
+        assertNotNull(clientId);
+
+        final Integer chargeId = ChargesHelper.createCharges(requestSpec, 
responseSpec, ChargesHelper.getChargeSpecifiedDueDateJSON());
+        Assertions.assertNotNull(chargeId);
+        final Integer clientChargeId1 = 
ClientHelper.addChargesForClient(requestSpec, responseSpec, clientId.intValue(),
+                
ClientHelper.getSpecifiedDueDateChargesClientAsJSON(chargeId.toString(), "29 
October 2011"));
+        Assertions.assertNotNull(clientChargeId1);
+        final String clientChargePaidTransactionId1 = 
ClientHelper.payChargesForClients(requestSpec, responseSpec, 
clientId.intValue(),
+                clientChargeId1, ClientHelper.getPayChargeJSON("25 AUGUST 
2015", "10"));
+        assertNotNull(clientChargePaidTransactionId1);
+
+        final Integer clientChargeId2 = 
ClientHelper.addChargesForClient(requestSpec, responseSpec, clientId.intValue(),
+                
ClientHelper.getSpecifiedDueDateChargesClientAsJSON(chargeId.toString(), "29 
October 2011"));
+        Assertions.assertNotNull(clientChargeId2);
+        final String clientChargePaidTransactionId2 = 
ClientHelper.payChargesForClients(requestSpec, responseSpec, 
clientId.intValue(),
+                clientChargeId2, ClientHelper.getPayChargeJSON("25 AUGUST 
2015", "10"));
+        assertNotNull(clientChargePaidTransactionId2);
+
+        GetClientsClientIdTransactionsResponse 
allClientTransactionsByExternalId = clientHelper
+                .getAllClientTransactionsByExternalId(clientExternalId);
+        assertEquals(2, 
allClientTransactionsByExternalId.getTotalFilteredRecords());
+
+        GetClientsClientIdTransactionsTransactionIdResponse 
clientTransactionByExternalId = clientHelper
+                .getClientTransactionByExternalId(clientExternalId, 
clientChargePaidTransactionId1);
+        assertEquals(Integer.parseInt(clientChargePaidTransactionId1), 
clientTransactionByExternalId.getId());
+
+        PostClientsClientIdTransactionsTransactionIdResponse 
undoTransactionResponse = clientHelper
+                .undoClientTransactionByExternalId(clientExternalId, 
clientChargePaidTransactionId2);
+        assertNotNull(undoTransactionResponse.getResourceId());
+    }
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ClientHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ClientHelper.java
index 5ef262046..7d9fa2cab 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ClientHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ClientHelper.java
@@ -44,12 +44,15 @@ import 
org.apache.fineract.client.models.GetClientClientIdAddressesResponse;
 import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
 import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
 import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import 
org.apache.fineract.client.models.GetClientsClientIdTransactionsResponse;
+import 
org.apache.fineract.client.models.GetClientsClientIdTransactionsTransactionIdResponse;
 import org.apache.fineract.client.models.GetObligeeData;
 import org.apache.fineract.client.models.PageClientSearchData;
 import org.apache.fineract.client.models.PagedRequestClientTextSearch;
 import org.apache.fineract.client.models.PostClientClientIdAddressesRequest;
 import org.apache.fineract.client.models.PostClientClientIdAddressesResponse;
 import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import 
org.apache.fineract.client.models.PostClientsClientIdTransactionsTransactionIdResponse;
 import org.apache.fineract.client.models.PostClientsRequest;
 import org.apache.fineract.client.models.PostClientsResponse;
 import org.apache.fineract.client.models.PutClientsClientIdResponse;
@@ -786,6 +789,20 @@ public class ClientHelper extends IntegrationTest {
         return Utils.performServerGet(requestSpec, responseSpec, CHARGES_URL, 
"reversed");
     }
 
+    public GetClientsClientIdTransactionsResponse 
getAllClientTransactionsByExternalId(final String externalId) {
+        return 
ok(fineract().clientTransactions.retrieveAllClientTransactions1(externalId, 0, 
100));
+    }
+
+    public GetClientsClientIdTransactionsTransactionIdResponse 
getClientTransactionByExternalId(final String externalId,
+            final String transactionId) {
+        return 
ok(fineract().clientTransactions.retrieveClientTransaction1(externalId, 
Long.parseLong(transactionId)));
+    }
+
+    public PostClientsClientIdTransactionsTransactionIdResponse 
undoClientTransactionByExternalId(final String externalId,
+            final String transactionId) {
+        return 
ok(fineract().clientTransactions.undoClientTransaction1(externalId, 
Long.parseLong(transactionId), "undo"));
+    }
+
     public Workbook getClientEntityWorkbook(GlobalEntityType clientsEntity, 
String dateFormat) throws IOException {
         requestSpec.header(HttpHeaders.CONTENT_TYPE, 
"application/vnd.ms-excel");
         byte[] byteArray = Utils.performGetBinaryResponse(requestSpec, 
responseSpec, CLIENT_URL + "/downloadtemplate" + "?"

Reply via email to