From bb5078b5da62c274d42881f2e10e893c511ed248 Mon Sep 17 00:00:00 2001 From: viktorpavlenko Date: Tue, 25 Mar 2025 10:21:35 +0200 Subject: [PATCH] FINERACT-2169: Refactor account transfer api controller; --- .../api/RunreportsApiResource.java | 4 +- .../api/AccountTransfersApiResource.java | 106 ++++-------- .../data/request/AccountTransSearchParam.java | 52 ++++++ .../data/request/AccountTransferRequest.java | 46 ++++++ .../mapper/AccountTransfersMapper.java | 153 ++++++++++++++++++ ...countTransfersReadPlatformServiceImpl.java | 146 +---------------- .../account/starter/AccountConfiguration.java | 7 +- .../api/SelfAccountTransferApiResource.java | 9 +- 8 files changed, 305 insertions(+), 218 deletions(-) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/AccountTransSearchParam.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/AccountTransferRequest.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/portfolio/account/mapper/AccountTransfersMapper.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java index a81171b28..1c1b967cc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/RunreportsApiResource.java @@ -53,7 +53,7 @@ import org.springframework.stereotype.Component; @Path("/v1/runreports") @Component -@Tag(name = "Run Reports", description = "") +@Tag(name = "Run Reports") @RequiredArgsConstructor public class RunreportsApiResource { @@ -69,7 +69,7 @@ public class RunreportsApiResource { @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Return all available export types for the specific report", description = "Returns the list of all available export types.") @ApiResponses({ - @ApiResponse(responseCode = "200", description = "", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ReportExportType.class)))) }) + @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ReportExportType.class)))) }) public Response retrieveAllAvailableExports(@PathParam("reportName") @Parameter(description = "reportName") final String reportName, @Context final UriInfo uriInfo, @DefaultValue("false") @QueryParam(IS_SELF_SERVICE_USER_REPORT_PARAMETER) @Parameter(description = IS_SELF_SERVICE_USER_REPORT_PARAMETER) final boolean isSelfServiceUserReport) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/AccountTransfersApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/AccountTransfersApiResource.java index fd17748c7..dbe71e6b3 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/AccountTransfersApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/api/AccountTransfersApiResource.java @@ -26,6 +26,7 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; @@ -33,22 +34,19 @@ import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; -import jakarta.ws.rs.core.Context; import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.UriInfo; import lombok.RequiredArgsConstructor; import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; -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.core.service.Page; import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; -import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.portfolio.account.data.AccountTransferData; +import org.apache.fineract.portfolio.account.data.request.AccountTransSearchParam; +import org.apache.fineract.portfolio.account.data.request.AccountTransferRequest; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; import org.springframework.stereotype.Component; @@ -61,9 +59,7 @@ public class AccountTransfersApiResource { private final PlatformSecurityContext context; private final DefaultToApiJsonSerializer toApiJsonSerializer; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; - private final ApiRequestParameterHelper apiRequestParameterHelper; private final AccountTransfersReadPlatformService accountTransfersReadPlatformService; - private final SqlValidator sqlValidator; @GET @Path("template") @@ -76,38 +72,28 @@ public class AccountTransfersApiResource { + "accounttransfers/template?fromClientId=1&fromAccountType=2&fromAccountId=1") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountTransfersApiResourceSwagger.GetAccountTransfersTemplateResponse.class))) }) - public String template(@QueryParam("fromOfficeId") @Parameter(description = "fromOfficeId") final Long fromOfficeId, - @QueryParam("fromClientId") @Parameter(description = "fromClientId") final Long fromClientId, - @QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccountId, - @QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType, - @QueryParam("toOfficeId") @Parameter(description = "toOfficeId") final Long toOfficeId, - @QueryParam("toClientId") @Parameter(description = "toClientId") final Long toClientId, - @QueryParam("toAccountId") @Parameter(description = "toAccountId") final Long toAccountId, - @QueryParam("toAccountType") @Parameter(description = "toAccountType") final Integer toAccountType, - @Context final UriInfo uriInfo) { + public AccountTransferData template(@BeanParam AccountTransSearchParam accountTransSearchParam) { - this.context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); - final AccountTransferData transferData = this.accountTransfersReadPlatformService.retrieveTemplate(fromOfficeId, fromClientId, - fromAccountId, fromAccountType, toOfficeId, toClientId, toAccountId, toAccountType); - - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, transferData, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + return accountTransfersReadPlatformService.retrieveTemplate(accountTransSearchParam.getFromAccountId(), + accountTransSearchParam.getFromClientId(), accountTransSearchParam.getFromAccountId(), + accountTransSearchParam.getFromAccountType(), accountTransSearchParam.getToOfficeId(), + accountTransSearchParam.getToClientId(), accountTransSearchParam.getToAccountId(), + accountTransSearchParam.getToAccountType()); } @POST @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Create new Transfer", description = "Ability to create new transfer of monetary funds from one account to another.") - @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccountTransfersApiResourceSwagger.PostAccountTransfersRequest.class))) + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccountTransferRequest.class))) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountTransfersApiResourceSwagger.PostAccountTransfersResponse.class))) - public String create(@Parameter(hidden = true) final String apiRequestBodyAsJson) { + public CommandProcessingResult create(@Parameter(hidden = true) AccountTransferRequest accountTransferRequest) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().createAccountTransfer() + .withJson(toApiJsonSerializer.serialize(accountTransferRequest)).build(); - final CommandWrapper commandRequest = new CommandWrapperBuilder().createAccountTransfer().withJson(apiRequestBodyAsJson).build(); - - final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - - return this.toApiJsonSerializer.serialize(result); + return commandsSourceWritePlatformService.logCommandSource(commandRequest); } @GET @@ -117,26 +103,19 @@ public class AccountTransfersApiResource { + "accounttransfers") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountTransfersApiResourceSwagger.GetAccountTransfersResponse.class))) }) - public String retrieveAll(@Context final UriInfo uriInfo, - @QueryParam("externalId") @Parameter(description = "externalId") final String externalId, + public Page retrieveAll(@QueryParam("externalId") @Parameter(description = "externalId") final String externalId, @QueryParam("offset") @Parameter(description = "offset") final Integer offset, @QueryParam("limit") @Parameter(example = "limit") final Integer limit, @QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy, @QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder, @QueryParam("accountDetailId") @Parameter(description = "accountDetailId") final Long accountDetailId) { - this.context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); - sqlValidator.validate(orderBy); - sqlValidator.validate(sortOrder); - sqlValidator.validate(externalId); final SearchParameters searchParameters = SearchParameters.builder().limit(limit).externalId(externalId).offset(offset) .orderBy(orderBy).sortOrder(sortOrder).build(); - final Page transfers = this.accountTransfersReadPlatformService.retrieveAll(searchParameters, accountDetailId); - - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, transfers, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + return accountTransfersReadPlatformService.retrieveAll(searchParameters, accountDetailId); } @GET @@ -147,15 +126,9 @@ public class AccountTransfersApiResource { + "accounttransfers/1") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountTransfersApiResourceSwagger.GetAccountTransfersResponse.GetAccountTransfersPageItems.class))) }) - public String retrieveOne(@PathParam("transferId") @Parameter(description = "transferId") final Long transferId, - @Context final UriInfo uriInfo) { - - this.context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); - - final AccountTransferData transfer = this.accountTransfersReadPlatformService.retrieveOne(transferId); - - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, transfer, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + public AccountTransferData retrieveOne(@PathParam("transferId") @Parameter(description = "transferId") final Long transferId) { + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + return accountTransfersReadPlatformService.retrieveOne(transferId); } @GET @@ -167,23 +140,13 @@ public class AccountTransfersApiResource { + "accounttransfers/templateRefundByTransfer?fromAccountId=2&fromAccountType=1& toAccountId=1&toAccountType=2&toClientId=1&toOfficeId=1") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountTransfersApiResourceSwagger.GetAccountTransfersTemplateRefundByTransferResponse.class))) }) - public String templateRefundByTransfer(@QueryParam("fromOfficeId") @Parameter(description = "fromOfficeId") final Long fromOfficeId, - @QueryParam("fromClientId") @Parameter(description = "fromClientId") final Long fromClientId, - @QueryParam("fromAccountId") @Parameter(description = "fromAccountId") final Long fromAccountId, - @QueryParam("fromAccountType") @Parameter(description = "fromAccountType") final Integer fromAccountType, - @QueryParam("toOfficeId") @Parameter(description = "toOfficeId") final Long toOfficeId, - @QueryParam("toClientId") @Parameter(description = "toClientId") final Long toClientId, - @QueryParam("toAccountId") @Parameter(description = "toAccountId") final Long toAccountId, - @QueryParam("toAccountType") @Parameter(description = "toAccountType") final Integer toAccountType, - @Context final UriInfo uriInfo) { - - this.context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); - - final AccountTransferData transferData = this.accountTransfersReadPlatformService.retrieveRefundByTransferTemplate(fromOfficeId, - fromClientId, fromAccountId, fromAccountType, toOfficeId, toClientId, toAccountId, toAccountType); - - final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); - return this.toApiJsonSerializer.serialize(settings, transferData, AccountTransfersApiConstants.RESPONSE_DATA_PARAMETERS); + public AccountTransferData templateRefundByTransfer(@BeanParam AccountTransSearchParam accountTransSearchParam) { + context.authenticatedUser().validateHasReadPermission(AccountTransfersApiConstants.ACCOUNT_TRANSFER_RESOURCE_NAME); + return accountTransfersReadPlatformService.retrieveRefundByTransferTemplate(accountTransSearchParam.getFromAccountId(), + accountTransSearchParam.getFromClientId(), accountTransSearchParam.getFromAccountId(), + accountTransSearchParam.getFromAccountType(), accountTransSearchParam.getToOfficeId(), + accountTransSearchParam.getToClientId(), accountTransSearchParam.getToAccountId(), + accountTransSearchParam.getToAccountType()); } @POST @@ -191,15 +154,12 @@ public class AccountTransfersApiResource { @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "Refund of an Active Loan by Transfer", description = "Ability to refund an active loan by transferring to a savings account.") - @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccountTransfersApiResourceSwagger.PostAccountTransfersRefundByTransferRequest.class))) + @RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccountTransferRequest.class))) @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountTransfersApiResourceSwagger.PostAccountTransfersRefundByTransferResponse.class))) }) - public String templateRefundByTransferPost(@Parameter(hidden = true) final String apiRequestBodyAsJson) { - - final CommandWrapper commandRequest = new CommandWrapperBuilder().refundByTransfer().withJson(apiRequestBodyAsJson).build(); - - final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); - - return this.toApiJsonSerializer.serialize(result); + public CommandProcessingResult templateRefundByTransferPost(@Parameter(hidden = true) AccountTransferRequest accountTransferRequest) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().refundByTransfer() + .withJson(toApiJsonSerializer.serialize(accountTransferRequest)).build(); + return commandsSourceWritePlatformService.logCommandSource(commandRequest); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/AccountTransSearchParam.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/AccountTransSearchParam.java new file mode 100644 index 000000000..b802e72de --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/AccountTransSearchParam.java @@ -0,0 +1,52 @@ +/** + * 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.account.data.request; + +import jakarta.ws.rs.QueryParam; +import java.io.Serial; +import java.io.Serializable; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AccountTransSearchParam implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @QueryParam("fromOfficeId") + private Long fromOfficeId; + @QueryParam("fromClientId") + private Long fromClientId; + @QueryParam("fromAccountId") + private Long fromAccountId; + @QueryParam("fromAccountType") + private Integer fromAccountType; + @QueryParam("toOfficeId") + private Long toOfficeId; + @QueryParam("toClientId") + private Long toClientId; + @QueryParam("toAccountId") + private Long toAccountId; + @QueryParam("toAccountType") + private Integer toAccountType; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/AccountTransferRequest.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/AccountTransferRequest.java new file mode 100644 index 000000000..39f1b5f01 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/data/request/AccountTransferRequest.java @@ -0,0 +1,46 @@ +/** + * 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.account.data.request; + +import java.io.Serial; +import java.io.Serializable; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class AccountTransferRequest implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private String transferDescription; + private String toOfficeId; + private String toAccountType; + private String dateFormat; + private String transferAmount; + private String toAccountId; + private String fromClientId; + private String locale; + private String transferDate; + private String fromAccountType; + private String toClientId; + private String fromAccountId; + private String fromOfficeId; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/mapper/AccountTransfersMapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/mapper/AccountTransfersMapper.java new file mode 100644 index 000000000..4263b4e4d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/mapper/AccountTransfersMapper.java @@ -0,0 +1,153 @@ +/** + * 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.account.mapper; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.organisation.monetary.data.CurrencyData; +import org.apache.fineract.organisation.office.data.OfficeData; +import org.apache.fineract.portfolio.account.PortfolioAccountType; +import org.apache.fineract.portfolio.account.data.AccountTransferData; +import org.apache.fineract.portfolio.account.data.PortfolioAccountData; +import org.apache.fineract.portfolio.account.service.AccountTransferEnumerations; +import org.apache.fineract.portfolio.client.data.ClientData; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Component; + +@Component +public final class AccountTransfersMapper implements RowMapper { + + private final String schemaSql; + + public AccountTransfersMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("att.id as id, att.is_reversed as isReversed,"); + sqlBuilder.append("att.transaction_date as transferDate, att.amount as transferAmount,"); + sqlBuilder.append("att.description as transferDescription,"); + sqlBuilder.append("att.currency_code as currencyCode, att.currency_digits as currencyDigits,"); + sqlBuilder.append("att.currency_multiplesof as inMultiplesOf, "); + sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); + sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); + sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); + sqlBuilder.append("tooff.id as toOfficeId, tooff.name as toOfficeName,"); + sqlBuilder.append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,"); + sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); + sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); + sqlBuilder.append("fromloanacc.id as fromLoanAccountId, fromloanacc.account_no as fromLoanAccountNo,"); + sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); + sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo,"); + sqlBuilder.append("fromsavtran.id as fromSavingsAccountTransactionId,"); + sqlBuilder.append("fromsavtran.transaction_type_enum as fromSavingsAccountTransactionType,"); + sqlBuilder.append("tosavtran.id as toSavingsAccountTransactionId,"); + sqlBuilder.append("tosavtran.transaction_type_enum as toSavingsAccountTransactionType"); + sqlBuilder.append(" FROM m_account_transfer_transaction att "); + sqlBuilder.append("left join m_account_transfer_details atd on atd.id = att.account_transfer_details_id "); + sqlBuilder.append("join m_currency curr on curr.code = att.currency_code "); + sqlBuilder.append("join m_office fromoff on fromoff.id = atd.from_office_id "); + sqlBuilder.append("join m_office tooff on tooff.id = atd.to_office_id "); + sqlBuilder.append("join m_client fromclient on fromclient.id = atd.from_client_id "); + sqlBuilder.append("join m_client toclient on toclient.id = atd.to_client_id "); + sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = atd.from_savings_account_id "); + sqlBuilder.append("left join m_loan fromloanacc on fromloanacc.id = atd.from_loan_account_id "); + sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = atd.to_savings_account_id "); + sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = atd.to_loan_account_id "); + sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = att.from_savings_transaction_id "); + sqlBuilder.append("left join m_savings_account_transaction tosavtran on tosavtran.id = att.to_savings_transaction_id "); + sqlBuilder.append("left join m_loan_transaction fromloantran on fromloantran.id = att.from_savings_transaction_id "); + sqlBuilder.append("left join m_loan_transaction toloantran on toloantran.id = att.to_savings_transaction_id "); + + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public AccountTransferData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final boolean reversed = rs.getBoolean("isReversed"); + + final LocalDate transferDate = JdbcSupport.getLocalDate(rs, "transferDate"); + final BigDecimal transferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "transferAmount"); + final String transferDescription = rs.getString("transferDescription"); + + final String currencyCode = rs.getString("currencyCode"); + final String currencyName = rs.getString("currencyName"); + final String currencyNameCode = rs.getString("currencyNameCode"); + final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); + final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); + final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); + final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, + currencyNameCode); + + final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); + final String fromOfficeName = rs.getString("fromOfficeName"); + final OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); + + final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); + final String toOfficeName = rs.getString("toOfficeName"); + final OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); + + final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId"); + final String fromClientName = rs.getString("fromClientName"); + final ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); + + final Long toClientId = JdbcSupport.getLong(rs, "toClientId"); + final String toClientName = rs.getString("toClientName"); + final ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); + + final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); + final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); + final Long fromLoanAccountId = JdbcSupport.getLong(rs, "fromLoanAccountId"); + final String fromLoanAccountNo = rs.getString("fromLoanAccountNo"); + PortfolioAccountData fromAccount = null; + EnumOptionData fromAccountType = null; + if (fromSavingsAccountId != null) { + fromAccount = PortfolioAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); + fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); + } else if (fromLoanAccountId != null) { + fromAccount = PortfolioAccountData.lookup(fromLoanAccountId, fromLoanAccountNo); + fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); + } + + PortfolioAccountData toAccount = null; + EnumOptionData toAccountType = null; + final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId"); + final String toSavingsAccountNo = rs.getString("toSavingsAccountNo"); + final Long toLoanAccountId = JdbcSupport.getLong(rs, "toLoanAccountId"); + final String toLoanAccountNo = rs.getString("toLoanAccountNo"); + + if (toSavingsAccountId != null) { + toAccount = PortfolioAccountData.lookup(toSavingsAccountId, toSavingsAccountNo); + toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); + } else if (toLoanAccountId != null) { + toAccount = PortfolioAccountData.lookup(toLoanAccountId, toLoanAccountNo); + toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); + } + + return AccountTransferData.instance(id, reversed, transferDate, currency, transferAmount, transferDescription, fromOffice, toOffice, + fromClient, toClient, fromAccountType, fromAccount, toAccountType, toAccount); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java index 619da2981..5ad7d0436 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersReadPlatformServiceImpl.java @@ -19,22 +19,21 @@ package org.apache.fineract.portfolio.account.service; import java.math.BigDecimal; -import java.sql.ResultSet; -import java.sql.SQLException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.stream.Stream; +import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.data.EnumOptionData; -import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; +import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; -import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.office.data.OfficeData; import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; import org.apache.fineract.portfolio.account.PortfolioAccountType; @@ -43,13 +42,14 @@ import org.apache.fineract.portfolio.account.data.PortfolioAccountDTO; import org.apache.fineract.portfolio.account.data.PortfolioAccountData; import org.apache.fineract.portfolio.account.domain.AccountTransferType; import org.apache.fineract.portfolio.account.exception.AccountTransferNotFoundException; +import org.apache.fineract.portfolio.account.mapper.AccountTransfersMapper; import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; import org.springframework.util.CollectionUtils; +@RequiredArgsConstructor public class AccountTransfersReadPlatformServiceImpl implements AccountTransfersReadPlatformService { private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @@ -63,20 +63,7 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers private final AccountTransfersMapper accountTransfersMapper; // pagination private final PaginationHelper paginationHelper; - - public AccountTransfersReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate, - final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, - final PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, final ColumnValidator columnValidator, - DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { - this.jdbcTemplate = jdbcTemplate; - this.clientReadPlatformService = clientReadPlatformService; - this.officeReadPlatformService = officeReadPlatformService; - this.portfolioAccountReadPlatformService = portfolioAccountReadPlatformService; - this.columnValidator = columnValidator; - this.sqlGenerator = sqlGenerator; - this.accountTransfersMapper = new AccountTransfersMapper(); - this.paginationHelper = paginationHelper; - } + private final SqlValidator sqlValidator; @Override public AccountTransferData retrieveTemplate(final Long fromOfficeId, final Long fromClientId, final Long fromAccountId, @@ -208,7 +195,8 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers @Override public Page retrieveAll(final SearchParameters searchParameters, final Long accountDetailId) { - + Stream.of(searchParameters.getOrderBy(), searchParameters.getSortOrder(), searchParameters.getExternalId()) + .forEach(sqlValidator::validate); final StringBuilder sqlBuilder = new StringBuilder(200); sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " "); sqlBuilder.append(this.accountTransfersMapper.schema()); @@ -434,122 +422,4 @@ public class AccountTransfersReadPlatformServiceImpl implements AccountTransfers return this.jdbcTemplate.queryForObject(sqlBuilder.toString(), BigDecimal.class, DATE_TIME_FORMATTER.format(transactionDate), accountType, accountId, accountId); } - - private static final class AccountTransfersMapper implements RowMapper { - - private final String schemaSql; - - AccountTransfersMapper() { - final StringBuilder sqlBuilder = new StringBuilder(400); - sqlBuilder.append("att.id as id, att.is_reversed as isReversed,"); - sqlBuilder.append("att.transaction_date as transferDate, att.amount as transferAmount,"); - sqlBuilder.append("att.description as transferDescription,"); - sqlBuilder.append("att.currency_code as currencyCode, att.currency_digits as currencyDigits,"); - sqlBuilder.append("att.currency_multiplesof as inMultiplesOf, "); - sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, "); - sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, "); - sqlBuilder.append("fromoff.id as fromOfficeId, fromoff.name as fromOfficeName,"); - sqlBuilder.append("tooff.id as toOfficeId, tooff.name as toOfficeName,"); - sqlBuilder.append("fromclient.id as fromClientId, fromclient.display_name as fromClientName,"); - sqlBuilder.append("toclient.id as toClientId, toclient.display_name as toClientName,"); - sqlBuilder.append("fromsavacc.id as fromSavingsAccountId, fromsavacc.account_no as fromSavingsAccountNo,"); - sqlBuilder.append("fromloanacc.id as fromLoanAccountId, fromloanacc.account_no as fromLoanAccountNo,"); - sqlBuilder.append("tosavacc.id as toSavingsAccountId, tosavacc.account_no as toSavingsAccountNo,"); - sqlBuilder.append("toloanacc.id as toLoanAccountId, toloanacc.account_no as toLoanAccountNo,"); - sqlBuilder.append("fromsavtran.id as fromSavingsAccountTransactionId,"); - sqlBuilder.append("fromsavtran.transaction_type_enum as fromSavingsAccountTransactionType,"); - sqlBuilder.append("tosavtran.id as toSavingsAccountTransactionId,"); - sqlBuilder.append("tosavtran.transaction_type_enum as toSavingsAccountTransactionType"); - sqlBuilder.append(" FROM m_account_transfer_transaction att "); - sqlBuilder.append("left join m_account_transfer_details atd on atd.id = att.account_transfer_details_id "); - sqlBuilder.append("join m_currency curr on curr.code = att.currency_code "); - sqlBuilder.append("join m_office fromoff on fromoff.id = atd.from_office_id "); - sqlBuilder.append("join m_office tooff on tooff.id = atd.to_office_id "); - sqlBuilder.append("join m_client fromclient on fromclient.id = atd.from_client_id "); - sqlBuilder.append("join m_client toclient on toclient.id = atd.to_client_id "); - sqlBuilder.append("left join m_savings_account fromsavacc on fromsavacc.id = atd.from_savings_account_id "); - sqlBuilder.append("left join m_loan fromloanacc on fromloanacc.id = atd.from_loan_account_id "); - sqlBuilder.append("left join m_savings_account tosavacc on tosavacc.id = atd.to_savings_account_id "); - sqlBuilder.append("left join m_loan toloanacc on toloanacc.id = atd.to_loan_account_id "); - sqlBuilder.append("left join m_savings_account_transaction fromsavtran on fromsavtran.id = att.from_savings_transaction_id "); - sqlBuilder.append("left join m_savings_account_transaction tosavtran on tosavtran.id = att.to_savings_transaction_id "); - sqlBuilder.append("left join m_loan_transaction fromloantran on fromloantran.id = att.from_savings_transaction_id "); - sqlBuilder.append("left join m_loan_transaction toloantran on toloantran.id = att.to_savings_transaction_id "); - - this.schemaSql = sqlBuilder.toString(); - } - - public String schema() { - return this.schemaSql; - } - - @Override - public AccountTransferData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { - - final Long id = rs.getLong("id"); - final boolean reversed = rs.getBoolean("isReversed"); - - final LocalDate transferDate = JdbcSupport.getLocalDate(rs, "transferDate"); - final BigDecimal transferAmount = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "transferAmount"); - final String transferDescription = rs.getString("transferDescription"); - - final String currencyCode = rs.getString("currencyCode"); - final String currencyName = rs.getString("currencyName"); - final String currencyNameCode = rs.getString("currencyNameCode"); - final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol"); - final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits"); - final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf"); - final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol, - currencyNameCode); - - final Long fromOfficeId = JdbcSupport.getLong(rs, "fromOfficeId"); - final String fromOfficeName = rs.getString("fromOfficeName"); - final OfficeData fromOffice = OfficeData.dropdown(fromOfficeId, fromOfficeName, null); - - final Long toOfficeId = JdbcSupport.getLong(rs, "toOfficeId"); - final String toOfficeName = rs.getString("toOfficeName"); - final OfficeData toOffice = OfficeData.dropdown(toOfficeId, toOfficeName, null); - - final Long fromClientId = JdbcSupport.getLong(rs, "fromClientId"); - final String fromClientName = rs.getString("fromClientName"); - final ClientData fromClient = ClientData.lookup(fromClientId, fromClientName, fromOfficeId, fromOfficeName); - - final Long toClientId = JdbcSupport.getLong(rs, "toClientId"); - final String toClientName = rs.getString("toClientName"); - final ClientData toClient = ClientData.lookup(toClientId, toClientName, toOfficeId, toOfficeName); - - final Long fromSavingsAccountId = JdbcSupport.getLong(rs, "fromSavingsAccountId"); - final String fromSavingsAccountNo = rs.getString("fromSavingsAccountNo"); - final Long fromLoanAccountId = JdbcSupport.getLong(rs, "fromLoanAccountId"); - final String fromLoanAccountNo = rs.getString("fromLoanAccountNo"); - PortfolioAccountData fromAccount = null; - EnumOptionData fromAccountType = null; - if (fromSavingsAccountId != null) { - fromAccount = PortfolioAccountData.lookup(fromSavingsAccountId, fromSavingsAccountNo); - fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); - } else if (fromLoanAccountId != null) { - fromAccount = PortfolioAccountData.lookup(fromLoanAccountId, fromLoanAccountNo); - fromAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); - } - - PortfolioAccountData toAccount = null; - EnumOptionData toAccountType = null; - final Long toSavingsAccountId = JdbcSupport.getLong(rs, "toSavingsAccountId"); - final String toSavingsAccountNo = rs.getString("toSavingsAccountNo"); - final Long toLoanAccountId = JdbcSupport.getLong(rs, "toLoanAccountId"); - final String toLoanAccountNo = rs.getString("toLoanAccountNo"); - - if (toSavingsAccountId != null) { - toAccount = PortfolioAccountData.lookup(toSavingsAccountId, toSavingsAccountNo); - toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.SAVINGS); - } else if (toLoanAccountId != null) { - toAccount = PortfolioAccountData.lookup(toLoanAccountId, toLoanAccountNo); - toAccountType = AccountTransferEnumerations.accountType(PortfolioAccountType.LOAN); - } - - return AccountTransferData.instance(id, reversed, transferDate, currency, transferAmount, transferDescription, fromOffice, - toOffice, fromClient, toClient, fromAccountType, fromAccount, toAccountType, toAccount); - } - } - } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/starter/AccountConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/starter/AccountConfiguration.java index f02d0326a..412b78faa 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/starter/AccountConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/starter/AccountConfiguration.java @@ -23,6 +23,7 @@ import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; +import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.organisation.office.service.OfficeReadPlatformService; import org.apache.fineract.portfolio.account.data.AccountTransfersDataValidator; @@ -32,6 +33,7 @@ import org.apache.fineract.portfolio.account.domain.AccountTransferDetailReposit import org.apache.fineract.portfolio.account.domain.AccountTransferRepository; import org.apache.fineract.portfolio.account.domain.StandingInstructionAssembler; import org.apache.fineract.portfolio.account.domain.StandingInstructionRepository; +import org.apache.fineract.portfolio.account.mapper.AccountTransfersMapper; import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformService; import org.apache.fineract.portfolio.account.service.AccountAssociationsReadPlatformServiceImpl; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; @@ -74,9 +76,10 @@ public class AccountConfiguration { public AccountTransfersReadPlatformService accountTransfersReadPlatformService(JdbcTemplate jdbcTemplate, ClientReadPlatformService clientReadPlatformService, OfficeReadPlatformService officeReadPlatformService, PortfolioAccountReadPlatformService portfolioAccountReadPlatformService, ColumnValidator columnValidator, - DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) { + DatabaseSpecificSQLGenerator sqlGenerator, AccountTransfersMapper accountTransfersMapper, PaginationHelper paginationHelper, + SqlValidator sqlValidator) { return new AccountTransfersReadPlatformServiceImpl(jdbcTemplate, clientReadPlatformService, officeReadPlatformService, - portfolioAccountReadPlatformService, columnValidator, sqlGenerator, paginationHelper); + portfolioAccountReadPlatformService, columnValidator, sqlGenerator, accountTransfersMapper, paginationHelper, sqlValidator); } @Bean diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfAccountTransferApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfAccountTransferApiResource.java index 2e6e3fea6..963f87b6d 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfAccountTransferApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/self/account/api/SelfAccountTransferApiResource.java @@ -43,10 +43,12 @@ import java.util.Map; import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; 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.account.api.AccountTransfersApiResource; +import org.apache.fineract.portfolio.account.data.request.AccountTransferRequest; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService; import org.apache.fineract.portfolio.self.account.data.SelfAccountTemplateData; import org.apache.fineract.portfolio.self.account.data.SelfAccountTransferData; @@ -105,13 +107,14 @@ public class SelfAccountTransferApiResource { + "\n" + "\n" + "Example Requests:\n" + "\n" + " self/accounttransfers/\n") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = SelfAccountTransferApiResourceSwagger.PostNewTransferResponse.class)))) }) - public String create(@DefaultValue("") @QueryParam("type") @Parameter(name = "type") final String type, - final String apiRequestBodyAsJson) { + public CommandProcessingResult create(@DefaultValue("") @QueryParam("type") @Parameter(name = "type") final String type, + AccountTransferRequest accountTransferRequest) { + final String apiRequestBodyAsJson = toApiJsonSerializer.serialize(accountTransferRequest); Map params = this.dataValidator.validateCreate(type, apiRequestBodyAsJson); if (type.equals("tpt")) { checkForLimits(params); } - return this.accountTransfersApiResource.create(apiRequestBodyAsJson); + return this.accountTransfersApiResource.create(accountTransferRequest); } private void checkForLimits(Map params) {