From db28fa14634aa35e4b433afc435917715b8e06ea Mon Sep 17 00:00:00 2001 From: viktorpavlenko Date: Wed, 2 Apr 2025 17:51:03 +0300 Subject: [PATCH] FINERACT-2169: Refactor account api controller; --- .../accounts/api/AccountsApiResource.java | 90 +++++++------------ .../data/request/AccountChargesRequest.java | 36 ++++++++ .../accounts/data/request/AccountRequest.java | 58 ++++++++++++ .../api/SelfShareAccountsApiResource.java | 9 +- .../ShareAccountApplicationTimelineData.java | 2 + .../shareaccounts/data/ShareAccountData.java | 1 + .../data/ShareAccountTransactionData.java | 2 + .../shares/ShareAccountIntegrationTests.java | 4 +- 8 files changed, 137 insertions(+), 65 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/request/AccountChargesRequest.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/request/AccountRequest.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java index f2d831150..32d476ebe 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/api/AccountsApiResource.java @@ -49,18 +49,16 @@ import org.apache.fineract.infrastructure.bulkimport.service.BulkImportWorkbookS import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.UploadRequest; -import org.apache.fineract.infrastructure.core.exception.ResourceNotFoundException; import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; -import org.apache.fineract.portfolio.accounts.constants.AccountsApiConstants; import org.apache.fineract.portfolio.accounts.data.AccountData; -import org.apache.fineract.portfolio.accounts.service.AccountReadPlatformService; +import org.apache.fineract.portfolio.accounts.data.request.AccountRequest; +import org.apache.fineract.portfolio.shareaccounts.data.ShareAccountData; +import org.apache.fineract.portfolio.shareaccounts.service.ShareAccountReadPlatformService; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; @Path("/v1/accounts/{type}") @@ -69,13 +67,13 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class AccountsApiResource { - private final ApplicationContext applicationContext; private final ApiRequestParameterHelper apiRequestParameterHelper; private final DefaultToApiJsonSerializer toApiJsonSerializer; private final PlatformSecurityContext platformSecurityContext; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final BulkImportWorkbookService bulkImportWorkbookService; private final BulkImportWorkbookPopulatorService bulkImportWorkbookPopulatorService; + private final ShareAccountReadPlatformService shareAccountReadPlatformService; @GET @Path("template") @@ -86,19 +84,11 @@ public class AccountsApiResource { + "\n" + "\n" + "accounts/share/template?clientId=1&productId=1") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.GetAccountsTypeTemplateResponse.class))) }) - public String template(@PathParam("type") @Parameter(description = "type") final String accountType, + public ShareAccountData template(@PathParam("type") @Parameter(description = "type") final String accountType, @QueryParam("clientId") @Parameter(description = "clientId") final Long clientId, - @QueryParam("productId") @Parameter(description = "productId") final Long productId, @Context final UriInfo uriInfo) { - try { - this.platformSecurityContext.authenticatedUser(); - String serviceName = accountType + AccountsApiConstants.READPLATFORM_NAME; - AccountReadPlatformService service = (AccountReadPlatformService) this.applicationContext.getBean(serviceName); - final AccountData accountData = service.retrieveTemplate(clientId, productId); - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, accountData, service.getResponseDataParams()); - } catch (BeansException e) { - throw new ResourceNotFoundException(e); - } + @QueryParam("productId") @Parameter(description = "productId") final Long productId) { + this.platformSecurityContext.authenticatedUser(); + return shareAccountReadPlatformService.retrieveTemplate(clientId, productId); } @GET @@ -109,17 +99,10 @@ public class AccountsApiResource { + "Example Requests :\n" + "\n" + "shareaccount/1") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.GetAccountsTypeAccountIdResponse.class))) }) - public String retrieveAccount(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId, + public ShareAccountData retrieveAccount(@PathParam("accountId") @Parameter(description = "accountId") final Long accountId, @PathParam("type") @Parameter(description = "type") final String accountType, @Context final UriInfo uriInfo) { - try { - String serviceName = accountType + AccountsApiConstants.READPLATFORM_NAME; - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - AccountReadPlatformService service = (AccountReadPlatformService) this.applicationContext.getBean(serviceName); - AccountData data = service.retrieveOne(accountId, settings.isTemplate()); - return this.toApiJsonSerializer.serialize(settings, data, service.getResponseDataParams()); - } catch (BeansException e) { - throw new ResourceNotFoundException(e); - } + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return shareAccountReadPlatformService.retrieveOne(accountId, settings.isTemplate()); } @GET @@ -129,18 +112,10 @@ public class AccountsApiResource { + "\n" + "shareaccount") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.GetAccountsTypeResponse.class))) }) - public String retrieveAllAccounts(@PathParam("type") @Parameter(description = "type") final String accountType, + public Page retrieveAllAccounts(@PathParam("type") @Parameter(description = "type") final String accountType, @QueryParam("offset") @Parameter(description = "offset") final Integer offset, - @QueryParam("limit") @Parameter(description = "limit") final Integer limit, @Context final UriInfo uriInfo) { - try { - String serviceName = accountType + AccountsApiConstants.READPLATFORM_NAME; - AccountReadPlatformService service = (AccountReadPlatformService) this.applicationContext.getBean(serviceName); - Page data = service.retrieveAll(offset, limit); - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, data, service.getResponseDataParams()); - } catch (BeansException e) { - throw new ResourceNotFoundException(e); - } + @QueryParam("limit") @Parameter(description = "limit") final Integer limit) { + return shareAccountReadPlatformService.retrieveAll(offset, limit); } @POST @@ -150,16 +125,15 @@ public class AccountsApiResource { + "Mandatory Fields: clientId, productId, submittedDate, savingsAccountId, requestedShares, applicationDate\n\n" + "Optional Fields: accountNo, externalId\n\n" + "Inherited from Product (if not provided): minimumActivePeriod, minimumActivePeriodFrequencyType, lockinPeriodFrequency, lockinPeriodFrequencyType") - @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.PostAccountsTypeRequest.class))) + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccountRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.PostAccountsTypeResponse.class))) }) - public String createAccount(@PathParam("type") @Parameter(description = "type") final String accountType, - @Parameter(hidden = true) final String apiRequestBodyAsJson) { - CommandWrapper commandWrapper = null; + public CommandProcessingResult createAccount(@PathParam("type") @Parameter(description = "type") final String accountType, + @Parameter(hidden = true) AccountRequest accountRequest) { this.platformSecurityContext.authenticatedUser(); - commandWrapper = new CommandWrapperBuilder().createAccount(accountType).withJson(apiRequestBodyAsJson).build(); - final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper); - return this.toApiJsonSerializer.serialize(commandProcessingResult); + CommandWrapper commandWrapper = new CommandWrapperBuilder().createAccount(accountType) + .withJson(toApiJsonSerializer.serialize(accountRequest)).build(); + return this.commandsSourceWritePlatformService.logCommandSource(commandWrapper); } @POST @@ -188,16 +162,14 @@ public class AccountsApiResource { @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.PostAccountsTypeAccountIdRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.PostAccountsTypeAccountIdResponse.class))) }) - public String handleCommands(@PathParam("type") @Parameter(description = "type") final String accountType, + public CommandProcessingResult handleCommands(@PathParam("type") @Parameter(description = "type") final String accountType, @PathParam("accountId") @Parameter(description = "accountId") final Long accountId, @QueryParam("command") @Parameter(description = "command") final String commandParam, @Parameter(hidden = true) final String apiRequestBodyAsJson) { - CommandWrapper commandWrapper = null; this.platformSecurityContext.authenticatedUser(); - commandWrapper = new CommandWrapperBuilder().createAccountCommand(accountType, accountId, commandParam) + CommandWrapper commandWrapper = new CommandWrapperBuilder().createAccountCommand(accountType, accountId, commandParam) .withJson(apiRequestBodyAsJson).build(); - final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper); - return this.toApiJsonSerializer.serialize(commandProcessingResult); + return this.commandsSourceWritePlatformService.logCommandSource(commandWrapper); } @PUT @@ -208,14 +180,13 @@ public class AccountsApiResource { @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.PutAccountsTypeAccountIdRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountsApiResourceSwagger.PutAccountsTypeAccountIdResponse.class))) }) - public String updateAccount(@PathParam("type") @Parameter(description = "type") final String accountType, + public CommandProcessingResult updateAccount(@PathParam("type") @Parameter(description = "type") final String accountType, @PathParam("accountId") @Parameter(description = "accountId") final Long accountId, - @Parameter(hidden = true) final String apiRequestBodyAsJson) { + @Parameter(hidden = true) AccountRequest accountRequest) { this.platformSecurityContext.authenticatedUser(); final CommandWrapper commandRequest = new CommandWrapperBuilder().updateAccount(accountType, accountId) - .withJson(apiRequestBodyAsJson).build(); - final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - return this.toApiJsonSerializer.serialize(result); + .withJson(toApiJsonSerializer.serialize(accountRequest)).build(); + return this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } @GET @@ -232,12 +203,11 @@ public class AccountsApiResource { @Consumes(MediaType.MULTIPART_FORM_DATA) @RequestBody(description = "Upload shared accounts template", content = { @Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = UploadRequest.class)) }) - public String postSharedAccountsTemplate(@FormDataParam("file") InputStream uploadedInputStream, + public Long postSharedAccountsTemplate(@FormDataParam("file") InputStream uploadedInputStream, @FormDataParam("file") FormDataContentDisposition fileDetail, @FormDataParam("locale") final String locale, @FormDataParam("dateFormat") final String dateFormat, @PathParam("type") @Parameter(description = "type") final String accountType) { - final Long importDocumentId = this.bulkImportWorkbookService.importWorkbook(GlobalEntityType.SHARE_ACCOUNTS.toString(), - uploadedInputStream, fileDetail, locale, dateFormat); - return this.toApiJsonSerializer.serialize(importDocumentId); + return this.bulkImportWorkbookService.importWorkbook(GlobalEntityType.SHARE_ACCOUNTS.toString(), uploadedInputStream, fileDetail, + locale, dateFormat); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/request/AccountChargesRequest.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/request/AccountChargesRequest.java new file mode 100644 index 000000000..b9d595122 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/request/AccountChargesRequest.java @@ -0,0 +1,36 @@ +/** + * 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.portfolio.accounts.data.request; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class AccountChargesRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long chargeId; + private BigDecimal amount; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/request/AccountRequest.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/request/AccountRequest.java new file mode 100644 index 000000000..7181f7dc8 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/accounts/data/request/AccountRequest.java @@ -0,0 +1,58 @@ +/** + * 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.portfolio.accounts.data.request; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class AccountRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private BigDecimal unitPrice; + private Long clientId; + private Long productId; + private Integer digitsAfterDecimal; + private Long requestedShares; + private String dateFormat; + private Integer minimumActivePeriod; + private Long numberOfShares; + private String allowDividendCalculationForInactiveClients; + private String externalId; + private String minimumActivePeriodFrequencyType; + private Long savingsAccountId; + private String locale; + private String submittedDate; + private String approvedDate; + private List charges; + private String lockinPeriodFrequencyType; + private String inMultiplesOf; + private String purchasedDate; + private Integer lockinPeriodFrequency; + private Long id; + private String currencyCode; + private String applicationDate; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/api/SelfShareAccountsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/api/SelfShareAccountsApiResource.java index 0b5ca5799..f1907825f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/api/SelfShareAccountsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/shareaccounts/api/SelfShareAccountsApiResource.java @@ -42,12 +42,14 @@ import java.util.Collection; import java.util.HashMap; import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.accounts.api.AccountsApiResource; import org.apache.fineract.portfolio.accounts.constants.ShareAccountApiConstants; import org.apache.fineract.portfolio.accounts.data.AccountData; +import org.apache.fineract.portfolio.accounts.data.request.AccountRequest; import org.apache.fineract.portfolio.accounts.exceptions.ShareAccountNotFoundException; import org.apache.fineract.portfolio.charge.data.ChargeData; import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService; @@ -121,12 +123,13 @@ public class SelfShareAccountsApiResource { + "minimumActivePeriod, minimumActivePeriodFrequencyType, lockinPeriodFrequency, lockinPeriodFrequencyType.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = SelfShareAccountsApiResourceSwagger.PostNewShareApplicationResponse.class)))) }) - public String createAccount(final String apiRequestBodyAsJson) { - HashMap attr = selfShareAccountsDataValidator.validateShareAccountApplication(apiRequestBodyAsJson); + public CommandProcessingResult createAccount(AccountRequest accountRequest) { + HashMap attr = selfShareAccountsDataValidator + .validateShareAccountApplication(toApiJsonSerializer.serialize(accountRequest)); final Long clientId = (Long) attr.get(ShareAccountApiConstants.clientid_paramname); validateAppuserClientsMapping(clientId); String accountType = ShareAccountApiConstants.shareEntityType; - return accountsApiResource.createAccount(accountType, apiRequestBodyAsJson); + return accountsApiResource.createAccount(accountType, accountRequest); } @GET diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountApplicationTimelineData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountApplicationTimelineData.java index ba88f75ec..103694dbc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountApplicationTimelineData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountApplicationTimelineData.java @@ -22,9 +22,11 @@ import java.io.Serializable; import java.time.LocalDate; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.apache.fineract.infrastructure.core.jersey.serializer.legacy.JsonLocalDateArrayFormat; @RequiredArgsConstructor @Getter +@JsonLocalDateArrayFormat public class ShareAccountApplicationTimelineData implements Serializable { private final LocalDate submittedOnDate; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountData.java index 2cd96a4bd..0430001bb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountData.java @@ -83,6 +83,7 @@ public class ShareAccountData implements Serializable, AccountData { // import fields private Integer requestedShares; + private LocalDate submittedDate; private Integer minimumActivePeriodFrequencyType; private Integer lockinPeriodFrequency; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountTransactionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountTransactionData.java index 6e54df6aa..180a5fbd9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountTransactionData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/shareaccounts/data/ShareAccountTransactionData.java @@ -24,9 +24,11 @@ import java.time.LocalDate; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.jersey.serializer.legacy.JsonLocalDateArrayFormat; @Getter @RequiredArgsConstructor +@JsonLocalDateArrayFormat public class ShareAccountTransactionData implements Serializable { private final Long id; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/shares/ShareAccountIntegrationTests.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/shares/ShareAccountIntegrationTests.java index 4da6723df..7826136e3 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/shares/ShareAccountIntegrationTests.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/shares/ShareAccountIntegrationTests.java @@ -997,10 +997,10 @@ public class ShareAccountIntegrationTests { private Integer createShareAccount(final Integer clientId, final Integer productId, final Integer savingsAccountId, List> charges) { - String josn = new ShareAccountHelper().withClientId(String.valueOf(clientId)).withProductId(String.valueOf(productId)) + String json = new ShareAccountHelper().withClientId(String.valueOf(clientId)).withProductId(String.valueOf(productId)) .withExternalId("External1").withSavingsAccountId(String.valueOf(savingsAccountId)).withSubmittedDate("01 January 2016") .withApplicationDate("01 January 2016").withRequestedShares("25").withCharges(charges).build(); - return ShareAccountTransactionHelper.createShareAccount(josn, requestSpec, responseSpec); + return ShareAccountTransactionHelper.createShareAccount(json, requestSpec, responseSpec); } private Map createCharge(final Integer chargeId, String amount) {