From 2e076ecbf6ca567c561dace31b398c71553fc051 Mon Sep 17 00:00:00 2001 From: Marti Date: Fri, 8 Mar 2019 12:01:34 +0100 Subject: [PATCH 1/5] interoperable - service --- .../api/InteropApiResource.java | 317 +++++++++++++ .../api/InteropWrapperBuilder.java | 82 ++++ .../interoperation/data/ExtensionData.java | 71 +++ .../interoperation/data/GeoCodeData.java | 68 +++ .../data/InteropIdentifierRequestData.java | 86 ++++ .../data/InteropIdentifierResponseData.java | 58 +++ .../data/InteropQuoteRequestData.java | 117 +++++ .../data/InteropQuoteResponseData.java | 88 ++++ .../data/InteropRequestData.java | 193 ++++++++ .../data/InteropResponseData.java | 105 +++++ .../data/InteropTransactionRequestData.java | 71 +++ ...InteropTransactionRequestResponseData.java | 64 +++ .../data/InteropTransactionTypeData.java | 96 ++++ .../data/InteropTransferRequestData.java | 111 +++++ .../data/InteropTransferResponseData.java | 79 ++++ .../interoperation/data/MoneyData.java | 86 ++++ .../domain/InteropActionState.java | 24 + .../domain/InteropAmountType.java | 24 + .../domain/InteropIdentifier.java | 167 +++++++ .../domain/InteropIdentifierRepository.java | 27 ++ .../domain/InteropIdentifierType.java | 31 ++ .../domain/InteropInitiatorType.java | 26 ++ .../domain/InteropTransactionRole.java | 35 ++ .../domain/InteropTransactionScenario.java | 28 ++ .../domain/InteropTransferActionType.java | 26 ++ .../handler/CommitInteropTransferHandler.java | 49 ++ .../CreateInteropIdentifierHandler.java | 56 +++ .../handler/CreateInteropQuoteHandler.java | 48 ++ .../handler/CreateInteropRequestHandler.java | 49 ++ .../handler/DeleteInteropRequestHandler.java | 55 +++ .../PrepareInteropTransferHandler.java | 49 ++ .../serialization/InteropDataValidator.java | 110 +++++ .../service/InteropService.java | 60 +++ .../service/InteropServiceImpl.java | 429 ++++++++++++++++++ .../interoperation/util/InteropUtil.java | 76 ++++ .../interoperation/util/MathUtil.java | 403 ++++++++++++++++ 36 files changed, 3464 insertions(+) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropApiResource.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropWrapperBuilder.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/ExtensionData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/GeoCodeData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropIdentifierRequestData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropIdentifierResponseData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropQuoteRequestData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropQuoteResponseData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropRequestData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropResponseData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionRequestData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionRequestResponseData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionTypeData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransferRequestData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransferResponseData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/data/MoneyData.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropActionState.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropAmountType.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifier.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifierRepository.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifierType.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropInitiatorType.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransactionRole.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransactionScenario.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransferActionType.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CommitInteropTransferHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropIdentifierHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropQuoteHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropRequestHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/DeleteInteropRequestHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/PrepareInteropTransferHandler.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/serialization/InteropDataValidator.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropService.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/util/InteropUtil.java create mode 100644 fineract-provider/src/main/java/org/apache/fineract/interoperation/util/MathUtil.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropApiResource.java new file mode 100644 index 00000000000..1aba6a79cc3 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropApiResource.java @@ -0,0 +1,317 @@ +/** + * 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.interoperation.api; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import org.apache.fineract.commands.domain.CommandWrapper; +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.security.service.PlatformSecurityContext; +import org.apache.fineract.interoperation.data.InteropIdentifierRequestData; +import org.apache.fineract.interoperation.data.InteropIdentifierResponseData; +import org.apache.fineract.interoperation.data.InteropQuoteRequestData; +import org.apache.fineract.interoperation.data.InteropQuoteResponseData; +import org.apache.fineract.interoperation.data.InteropTransactionRequestData; +import org.apache.fineract.interoperation.data.InteropTransactionRequestResponseData; +import org.apache.fineract.interoperation.data.InteropTransferRequestData; +import org.apache.fineract.interoperation.data.InteropTransferResponseData; +import org.apache.fineract.interoperation.domain.InteropIdentifierType; +import org.apache.fineract.interoperation.domain.InteropTransferActionType; +import org.apache.fineract.interoperation.service.InteropService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_QUOTE; +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_REQUEST; +import static org.apache.fineract.interoperation.util.InteropUtil.ROOT_PATH; + +@Path("/interoperation") //api/v1/ +@Component +@Scope +@Api(value = ROOT_PATH, description = "") +public class InteropApiResource { + + private PlatformSecurityContext context; + private ApiRequestParameterHelper apiRequestParameterHelper; + + private DefaultToApiJsonSerializer jsonSerializer; + + private InteropService interopService; + private PortfolioCommandSourceWritePlatformService commandsSourceService; + + @Autowired + public InteropApiResource(PlatformSecurityContext context, + ApiRequestParameterHelper apiRequestParameterHelper, + DefaultToApiJsonSerializer defaultToApiJsonSerializer, + InteropService interopService, + PortfolioCommandSourceWritePlatformService portfolioCommandSourceWritePlatformService) { + this.context = context; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.jsonSerializer = defaultToApiJsonSerializer; + this.interopService = interopService; + this.commandsSourceService = portfolioCommandSourceWritePlatformService; + } + + @GET + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("health") + @ApiOperation(value = "Query Interoperation Health Request", httpMethod = "GET", notes = "") + @ApiResponses({@ApiResponse(code = 200, message = "OK")}) + public String health(@Context UriInfo uriInfo) { + return "OK"; + } + + @GET + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("parties/{idType}/{idValue}") + @ApiOperation(value = "Query Interoperation Account by secondary identifier", httpMethod = "GET", notes = "") + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)}) + public String getAccountByIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType, + @PathParam("idValue") @ApiParam(value = "idValue") String idValue, + @Context UriInfo uriInfo) { + InteropIdentifierResponseData result = interopService.getAccountByIdentifier(idType, idValue, null); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } + + @GET + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("parties/{idType}/{idValue}/{subIdOrType}") + @ApiOperation(value = "Query Interoperation Account by secondary identifier", httpMethod = "GET", notes = "") + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)}) + public String getAccountByIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType, + @PathParam("idValue") @ApiParam(value = "idValue") String idValue, + @PathParam("subIdOrType") @ApiParam(value = "subIdOrType") String subIdOrType, + @Context UriInfo uriInfo) { + InteropIdentifierResponseData result = interopService.getAccountByIdentifier(idType, idValue, subIdOrType); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } + + @POST + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("parties/{idType}/{idValue}") + @ApiOperation(value = "Interoperation Identifier registration", httpMethod = "POST", notes = "") + @ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body", + dataTypeClass = InteropIdentifierRequestData.class)}) + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)}) + public String registerAccountIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType, + @PathParam("idValue") @ApiParam(value = "idValue") String idValue, + @ApiParam(hidden = true) String identifierJson, @Context UriInfo uriInfo) + throws Throwable { + CommandWrapper commandRequest = new InteropWrapperBuilder().registerAccountIdentifier(idType, idValue, null).withJson(identifierJson).build(); + + InteropIdentifierResponseData result = (InteropIdentifierResponseData) commandsSourceService.logCommandSource(commandRequest); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } + + @POST + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("parties/{idType}/{idValue}/{subIdOrType}") + @ApiOperation(value = "Interoperation Identifier registration", httpMethod = "POST", notes = "") + @ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body", + dataTypeClass = InteropIdentifierRequestData.class)}) + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)}) + public String registerAccountIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType, + @PathParam("idValue") @ApiParam(value = "idValue") String idValue, + @PathParam("subIdOrType") @ApiParam(value = "subIdOrType") String subIdOrType, + @ApiParam(hidden = true) String identifierJson, @Context UriInfo uriInfo) + throws Throwable { + CommandWrapper commandRequest = new InteropWrapperBuilder().registerAccountIdentifier(idType, idValue, subIdOrType).withJson(identifierJson).build(); + + InteropIdentifierResponseData result = (InteropIdentifierResponseData) commandsSourceService.logCommandSource(commandRequest); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } + + @DELETE + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("parties/{idType}/{idValue}") + @ApiOperation(value = "Allow Interoperation Identifier registration", httpMethod = "DELETE", notes = "") + @ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body", + dataTypeClass = InteropIdentifierRequestData.class)}) + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)}) + public String deleteAccountIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType, + @PathParam("idValue") @ApiParam(value = "idValue") String idValue, + @Context UriInfo uriInfo) + throws Throwable { + CommandWrapper commandRequest = new InteropWrapperBuilder().deleteAccountIdentifier(idType, idValue, null).build(); + + InteropIdentifierResponseData result = (InteropIdentifierResponseData) commandsSourceService.logCommandSource(commandRequest); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } + + @DELETE + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("parties/{idType}/{idValue}/{subIdOrType}") + @ApiOperation(value = "Allow Interoperation Identifier registration", httpMethod = "DELETE", notes = "") + @ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body", + dataTypeClass = InteropIdentifierRequestData.class)}) + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropIdentifierResponseData.class)}) + public String deleteAccountIdentifier(@PathParam("idType") @ApiParam(value = "idType") InteropIdentifierType idType, + @PathParam("idValue") @ApiParam(value = "idValue") String idValue, + @PathParam("subIdOrType") @ApiParam(value = "subIdOrType") String subIdOrType, + @Context UriInfo uriInfo) + throws Throwable { + CommandWrapper commandRequest = new InteropWrapperBuilder().deleteAccountIdentifier(idType, idValue, subIdOrType).build(); + + InteropIdentifierResponseData result = (InteropIdentifierResponseData) commandsSourceService.logCommandSource(commandRequest); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } + + @GET + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("transactions/{transactionCode}/requests/{requestCode}") + @ApiOperation(value = "Query Interoperation Transaction Request", httpMethod = "GET", notes = "") + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropTransactionRequestResponseData.class)}) + public String getTransactionRequest(@PathParam("transactionCode") @ApiParam(value = "transactionCode") String transactionCode, + @PathParam("requestCode") @ApiParam(value = "requestCode") String requestCode, @Context UriInfo uriInfo) { + context.authenticatedUser().validateHasReadPermission(ENTITY_NAME_REQUEST); + + InteropTransactionRequestResponseData result = interopService.getTransactionRequest(transactionCode, requestCode); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } + + @POST + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("requests") + @ApiOperation(value = "Allow Interoperation Transaction Request", httpMethod = "POST", notes = "") + @ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body", + dataTypeClass = InteropTransactionRequestData.class)}) + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropTransactionRequestResponseData.class)}) + public String createTransactionRequest(@ApiParam(hidden = true) String quotesJson, @Context UriInfo uriInfo) { + CommandWrapper commandRequest = new InteropWrapperBuilder().createTransactionRequest().withJson(quotesJson).build(); + + InteropTransactionRequestResponseData result = (InteropTransactionRequestResponseData) commandsSourceService.logCommandSource(commandRequest); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } + + @GET + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("transactions/{transactionCode}/quotes/{quoteCode}") + @ApiOperation(value = "Query Interoperation Quote", httpMethod = "GET", notes = "") + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropQuoteResponseData.class)}) + public String getQuote(@PathParam("transactionCode") @ApiParam(value = "transactionCode") String transactionCode, + @PathParam("quoteCode") @ApiParam(value = "quoteCode") String quoteCode, + @Context UriInfo uriInfo) { + context.authenticatedUser().validateHasReadPermission(ENTITY_NAME_QUOTE); + + InteropQuoteResponseData result = interopService.getQuote(transactionCode, quoteCode); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return this.jsonSerializer.serialize(settings, result); + } + + @POST + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("quotes") + @ApiOperation(value = "Calculate Interoperation Quote", httpMethod = "POST", notes = "") + @ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body", + dataTypeClass = InteropQuoteRequestData.class)}) + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropQuoteResponseData.class)}) + public String createQuote(@ApiParam(hidden = true) String quotesJson, @Context UriInfo uriInfo) { + CommandWrapper commandRequest = new InteropWrapperBuilder().createQuotes().withJson(quotesJson).build(); + + InteropQuoteResponseData result = (InteropQuoteResponseData) commandsSourceService.logCommandSource(commandRequest); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } + + @GET + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("transactions/{transactionCode}/transfers/{transferCode}") + @ApiOperation(value = "Query Interoperation Transfer", httpMethod = "GET", notes = "") + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropTransferResponseData.class)}) + public String getTransfer(@PathParam("transactionCode") @ApiParam(value = "transactionCode") String transactionCode, + @PathParam("transferCode") @ApiParam(value = "transferCode") String transferCode, + @Context UriInfo uriInfo) { + context.authenticatedUser().validateHasReadPermission(ENTITY_NAME_QUOTE); + + InteropTransferResponseData result = interopService.getTransfer(transactionCode, transferCode); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return this.jsonSerializer.serialize(settings, result); + } + + @POST + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + @Path("transfers") + @ApiOperation(value = "Prepare Interoperation Transfer", httpMethod = "POST", notes = "") + @ApiImplicitParams({@ApiImplicitParam(value = "body", required = true, paramType = "body", dataType = "body", format = "body", + dataTypeClass = InteropTransferRequestData.class)}) + @ApiResponses({@ApiResponse(code = 200, message = "OK", response = InteropTransferResponseData.class)}) + public String performTransfer(@QueryParam("action") @ApiParam(value = "action") String action, + @ApiParam(hidden = true) String quotesJson, @Context UriInfo uriInfo) { + CommandWrapper commandRequest = new InteropWrapperBuilder().performTransfer(InteropTransferActionType.valueOf(action)).withJson(quotesJson).build(); + + InteropTransferResponseData result = (InteropTransferResponseData) commandsSourceService.logCommandSource(commandRequest); + ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + return jsonSerializer.serialize(settings, result); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropWrapperBuilder.java new file mode 100644 index 00000000000..140591ae04e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/api/InteropWrapperBuilder.java @@ -0,0 +1,82 @@ +/* + * 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.interoperation.api; + +import org.apache.fineract.commands.domain.CommandWrapper; +import org.apache.fineract.interoperation.domain.InteropIdentifierType; +import org.apache.fineract.interoperation.domain.InteropTransferActionType; + +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_IDENTIFIER; +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_QUOTE; +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_REQUEST; +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_TRANSFER; +import static org.apache.fineract.interoperation.util.InteropUtil.ROOT_PATH; + +public class InteropWrapperBuilder { + + private String actionName; + private String entityName; + private String href; + private String json = "{}"; + + public CommandWrapper build() { + return new CommandWrapper(null, null, null, null, null, actionName, entityName, null, null, href, json, null, null, + null, null, null); + } + + public InteropWrapperBuilder withJson(final String json) { + this.json = json; + return this; + } + + public InteropWrapperBuilder registerAccountIdentifier(InteropIdentifierType idType, String idValue, String subIdOrType) { + this.actionName = "CREATE"; + this.entityName = ENTITY_NAME_IDENTIFIER; + this.href = "/" + ROOT_PATH + "/parties/" + idType + "/" + idValue + "/" + (subIdOrType == null ? "" : subIdOrType); + return this; + } + + public InteropWrapperBuilder deleteAccountIdentifier(InteropIdentifierType idType, String idValue, String subIdOrType) { + this.actionName = "DELETE"; + this.entityName = ENTITY_NAME_IDENTIFIER; + this.href = "/" + ROOT_PATH + "/parties/" + idType + "/" + idValue + "/" + (subIdOrType == null ? "" : subIdOrType); + return this; + } + + public InteropWrapperBuilder createTransactionRequest() { + this.actionName = "CREATE"; + this.entityName = ENTITY_NAME_REQUEST; + this.href = "/" + ROOT_PATH + "/requests"; + return this; + } + + public InteropWrapperBuilder createQuotes() { + this.actionName = "CREATE"; + this.entityName = ENTITY_NAME_QUOTE; + this.href = "/" + ROOT_PATH + "/quotes"; + return this; + } + + public InteropWrapperBuilder performTransfer(InteropTransferActionType action) { + this.actionName = action.name(); + this.entityName = ENTITY_NAME_TRANSFER; + this.href = "/" + ROOT_PATH + "/transfers"; + return this; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/ExtensionData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/ExtensionData.java new file mode 100644 index 00000000000..e2c49920107 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/ExtensionData.java @@ -0,0 +1,71 @@ +/* + * 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.interoperation.data; + +import com.google.gson.JsonObject; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; + +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_KEY; +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_VALUE; + +public class ExtensionData { + + public static final String[] PARAMS = {PARAM_KEY, PARAM_VALUE}; + + @NotNull + private final String key; + + private String value; + + public ExtensionData(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public static ExtensionData validateAndParse(DataValidatorBuilder dataValidator, JsonObject element, FromJsonHelper jsonHelper) { + if (element == null) + return null; + + jsonHelper.checkForUnsupportedParameters(element, Arrays.asList(PARAMS)); + + String key = jsonHelper.extractStringNamed(PARAM_KEY, element); + DataValidatorBuilder dataValidatorCopy = dataValidator.reset().parameter(PARAM_KEY).value(key).notBlank(); + + String value = jsonHelper.extractStringNamed(PARAM_VALUE, element); + + dataValidator.merge(dataValidatorCopy); + return dataValidator.hasError() ? null : new ExtensionData(key, value); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/GeoCodeData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/GeoCodeData.java new file mode 100644 index 00000000000..5b8eb8f24eb --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/GeoCodeData.java @@ -0,0 +1,68 @@ +/* + * 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.interoperation.data; + +import com.google.gson.JsonObject; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; + +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_LATITUDE; +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_LONGITUDE; + +public class GeoCodeData { + + public static final String[] PARAMS = {PARAM_LATITUDE, PARAM_LONGITUDE}; + + @NotNull + private final String latitude; + @NotNull + private final String longitude; + + public GeoCodeData(String latitude, String longitude) { + this.latitude = latitude; + this.longitude = longitude; + } + + public String getLatitude() { + return latitude; + } + + public String getLongitude() { + return longitude; + } + + public static GeoCodeData validateAndParse(DataValidatorBuilder dataValidator, JsonObject element, FromJsonHelper jsonHelper) { + if (element == null) + return null; + + jsonHelper.checkForUnsupportedParameters(element, Arrays.asList(PARAMS)); + + String latitude = jsonHelper.extractStringNamed(PARAM_LATITUDE, element); + DataValidatorBuilder dataValidatorCopy = dataValidator.reset().parameter(PARAM_LATITUDE).value(latitude).notBlank(); + + String longitude = jsonHelper.extractStringNamed(PARAM_LONGITUDE, element); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_LONGITUDE).value(longitude).notBlank(); + + dataValidator.merge(dataValidatorCopy); + return dataValidator.hasError() ? null : new GeoCodeData(latitude, longitude); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropIdentifierRequestData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropIdentifierRequestData.java new file mode 100644 index 00000000000..0347b53e892 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropIdentifierRequestData.java @@ -0,0 +1,86 @@ +/* + * 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.interoperation.data; + +import com.google.gson.JsonObject; +import org.apache.bval.constraints.NotEmpty; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.interoperation.domain.InteropIdentifierType; +import org.apache.fineract.interoperation.domain.InteropTransactionRole; +import org.joda.time.LocalDateTime; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; + +import static org.apache.fineract.interoperation.util.InteropUtil.*; + +public class InteropIdentifierRequestData { + + static final String[] PARAMS = {PARAM_ACCOUNT_ID}; + + @NotEmpty + private final InteropIdentifierType idType; + @NotEmpty + private final String idValue; + + private final String subIdOrType; + + @NotEmpty + private final String accountId; + + public InteropIdentifierRequestData(@NotNull InteropIdentifierType idType, @NotNull String idValue, String subIdOrType, String accountId) { + this.idType = idType; + this.idValue = idValue; + this.subIdOrType = subIdOrType; + this.accountId = accountId; + } + + public InteropIdentifierType getIdType() { + return idType; + } + + public String getIdValue() { + return idValue; + } + + public String getSubIdOrType() { + return subIdOrType; + } + + public String getAccountId() { + return accountId; + } + + public static InteropIdentifierRequestData validateAndParse(final DataValidatorBuilder dataValidator, @NotNull InteropIdentifierType idType, + @NotNull String idValue, String subIdOrType, JsonObject element, + FromJsonHelper jsonHelper) { + if (element == null) + return null; + + jsonHelper.checkForUnsupportedParameters(element, Arrays.asList(PARAMS)); + + String accountId = jsonHelper.extractStringNamed(PARAM_ACCOUNT_ID, element); + DataValidatorBuilder dataValidatorCopy = dataValidator.reset().parameter(PARAM_ACCOUNT_ID).value(accountId).notBlank(); + + dataValidator.merge(dataValidatorCopy); + return dataValidator.hasError() ? null : new InteropIdentifierRequestData(idType, idValue, subIdOrType, accountId); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropIdentifierResponseData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropIdentifierResponseData.java new file mode 100644 index 00000000000..1f7b74ad728 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropIdentifierResponseData.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.interoperation.data; + +import org.apache.bval.constraints.NotEmpty; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; + +import javax.validation.constraints.NotNull; +import java.util.Map; + +public class InteropIdentifierResponseData extends CommandProcessingResult { + + @NotEmpty + private String accountId; + + + protected InteropIdentifierResponseData(Long resourceId, Long officeId, Long commandId, Map changesOnly, @NotNull String accountId) { + super(resourceId, officeId, commandId, changesOnly); + this.accountId = accountId; + } + + protected static InteropIdentifierResponseData build(Long resourceId, Long officeId, Long commandId, Map changesOnly, @NotNull String accountId) { + return new InteropIdentifierResponseData(resourceId, officeId, commandId, changesOnly, accountId); + } + + protected static InteropIdentifierResponseData build(Long commandId, @NotNull String accountId) { + return build(null, null, commandId, null, accountId); + } + + public static InteropIdentifierResponseData build(@NotNull String accountId) { + return build(null, accountId); + } + + @NotNull + public String getAccountId() { + return accountId; + } + + protected void setAccountId(String accountId) { + this.accountId = accountId; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropQuoteRequestData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropQuoteRequestData.java new file mode 100644 index 00000000000..a5a88cb7d9a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropQuoteRequestData.java @@ -0,0 +1,117 @@ +/* + * 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.interoperation.data; + +import com.google.gson.JsonObject; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.interoperation.domain.InteropAmountType; +import org.apache.fineract.interoperation.domain.InteropTransactionRole; +import org.joda.time.LocalDateTime; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; + +import static org.apache.fineract.interoperation.util.InteropUtil.*; + +public class InteropQuoteRequestData extends InteropRequestData { + + static final String[] PARAMS = {PARAM_TRANSACTION_CODE, PARAM_REQUEST_CODE, PARAM_ACCOUNT_ID, PARAM_AMOUNT, PARAM_TRANSACTION_TYPE, + PARAM_TRANSACTION_ROLE, PARAM_NOTE, PARAM_GEO_CODE, PARAM_EXPIRATION, PARAM_EXTENSION_LIST, PARAM_QUOTE_CODE, + PARAM_AMOUNT_TYPE, PARAM_FEES, PARAM_LOCALE, PARAM_DATE_FORMAT}; + @NotNull + private final String quoteCode; + @NotNull + private final InteropAmountType amountType; + + private final MoneyData fees; // only for disclosed Payer fees on the Payee side + + public InteropQuoteRequestData(@NotNull String transactionCode, String requestCode, @NotNull String accountId, @NotNull MoneyData amount, + @NotNull InteropTransactionRole transactionRole, @NotNull InteropTransactionTypeData transactionType, + String note, GeoCodeData geoCode, LocalDateTime expiration, List extensionList, + @NotNull String quoteCode, @NotNull InteropAmountType amountType, MoneyData fees) { + super(transactionCode, requestCode, accountId, amount, transactionRole, transactionType, note, geoCode, expiration, extensionList); + this.quoteCode = quoteCode; + this.amountType = amountType; + this.fees = fees; + } + + public InteropQuoteRequestData(@NotNull String transactionCode, @NotNull String accountId, @NotNull InteropAmountType amountType, + @NotNull MoneyData amount, @NotNull InteropTransactionRole transactionRole, @NotNull InteropTransactionTypeData transactionType, + @NotNull String quoteCode) { + this(transactionCode, null, accountId, amount, transactionRole, transactionType, null, null, null, null, quoteCode, + amountType, null); + } + + private InteropQuoteRequestData(@NotNull InteropRequestData other, @NotNull String quoteCode, @NotNull InteropAmountType amountType, + MoneyData fees) { + this(other.getTransactionCode(), other.getRequestCode(), other.getAccountId(), other.getAmount(), other.getTransactionRole(), + other.getTransactionType(), other.getNote(), other.getGeoCode(), other.getExpiration(), other.getExtensionList(), + quoteCode, amountType, fees); + } + + public String getQuoteCode() { + return quoteCode; + } + + public InteropAmountType getAmountType() { + return amountType; + } + + public MoneyData getFees() { + return fees; + } + + public void normalizeAmounts(@NotNull MonetaryCurrency currency) { + super.normalizeAmounts(currency); + if (fees != null) + fees.normalizeAmount(currency); + } + + public static InteropQuoteRequestData validateAndParse(final DataValidatorBuilder dataValidator, JsonObject element, FromJsonHelper jsonHelper) { + if (element == null) + return null; + + jsonHelper.checkForUnsupportedParameters(element, Arrays.asList(PARAMS)); + + InteropRequestData interopRequestData = InteropRequestData.validateAndParse(dataValidator, element, jsonHelper); + + String quoteCode = jsonHelper.extractStringNamed(PARAM_QUOTE_CODE, element); + DataValidatorBuilder dataValidatorCopy = dataValidator.reset().parameter(PARAM_QUOTE_CODE).value(quoteCode).notBlank(); + + String amountTypeString = jsonHelper.extractStringNamed(PARAM_AMOUNT_TYPE, element); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_AMOUNT_TYPE).value(amountTypeString).notBlank(); + InteropAmountType amountType = InteropAmountType.valueOf(amountTypeString); + + JsonObject feesElement = jsonHelper.extractJsonObjectNamed(PARAM_FEES, element); + dataValidator.merge(dataValidatorCopy); + MoneyData fees = MoneyData.validateAndParse(dataValidator, feesElement, jsonHelper); + + String transactionRoleString = jsonHelper.extractStringNamed(PARAM_TRANSACTION_ROLE, element); + dataValidatorCopy = dataValidator.reset().parameter(PARAM_TRANSACTION_ROLE).value(transactionRoleString).notNull(); + + JsonObject transactionTypeElement = jsonHelper.extractJsonObjectNamed(PARAM_TRANSACTION_TYPE, element); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_TRANSACTION_TYPE).value(transactionTypeElement).notNull(); + + dataValidator.merge(dataValidatorCopy); + return dataValidator.hasError() ? null : new InteropQuoteRequestData(interopRequestData, quoteCode, amountType, fees); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropQuoteResponseData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropQuoteResponseData.java new file mode 100644 index 00000000000..bd2d7b92d93 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropQuoteResponseData.java @@ -0,0 +1,88 @@ +/* + * 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.interoperation.data; + +import org.apache.fineract.interoperation.domain.InteropActionState; +import org.joda.time.LocalDateTime; + +import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.Map; + +public class InteropQuoteResponseData extends InteropResponseData { + + @NotNull + private final String quoteCode; + + private MoneyData fspFee; + + private MoneyData fspCommission; + + + private InteropQuoteResponseData(Long resourceId, Long officeId, Long commandId, Map changesOnly, + @NotNull String transactionCode, @NotNull InteropActionState state, LocalDateTime expiration, + List extensionList, @NotNull String quoteCode, MoneyData fspFee, MoneyData fspCommission) { + super(resourceId, officeId, commandId, changesOnly, transactionCode, state, expiration, extensionList); + this.quoteCode = quoteCode; + this.fspFee = fspFee; + this.fspCommission = fspCommission; + } + + public static InteropQuoteResponseData build(Long commandId, @NotNull String transactionCode, @NotNull InteropActionState state, + LocalDateTime expiration, List extensionList, @NotNull String quoteCode, + MoneyData fspFee, MoneyData fspCommission) { + return new InteropQuoteResponseData(null, null, commandId, null, transactionCode, state, expiration, extensionList, + quoteCode, fspFee, fspCommission); + } + + public static InteropQuoteResponseData build(@NotNull String transactionCode, @NotNull InteropActionState state, LocalDateTime expiration, + List extensionList, @NotNull String quoteCode, MoneyData fspFee, + MoneyData fspCommission) { + return build(null, transactionCode, state, expiration, extensionList, quoteCode, fspFee, fspCommission); + } + + public static InteropQuoteResponseData build(Long commandId, @NotNull String transactionCode, @NotNull InteropActionState state, + @NotNull String quoteCode) { + return build(commandId, transactionCode, state, null, null, quoteCode, null, null); + } + + public static InteropQuoteResponseData build(@NotNull String transactionCode, @NotNull InteropActionState state, @NotNull String quoteCode) { + return build(null, transactionCode, state, quoteCode); + } + + public String getQuoteCode() { + return quoteCode; + } + + public MoneyData getFspFee() { + return fspFee; + } + + public void setFspFee(MoneyData fspFee) { + this.fspFee = fspFee; + } + + public MoneyData getFspCommission() { + return fspCommission; + } + + public void setFspCommission(MoneyData fspCommission) { + this.fspCommission = fspCommission; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropRequestData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropRequestData.java new file mode 100644 index 00000000000..e05adbcd198 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropRequestData.java @@ -0,0 +1,193 @@ +/* + * 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.interoperation.data; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.interoperation.domain.InteropTransactionRole; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; + +import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; + +import static org.apache.fineract.interoperation.util.InteropUtil.*; + +public class InteropRequestData { + + @NotNull + private final String transactionCode; + + private final String requestCode; + @NotNull + private final String accountId; + @NotNull + private final MoneyData amount; + @NotNull + private final InteropTransactionRole transactionRole; + + private final InteropTransactionTypeData transactionType; + + private String note; + + private GeoCodeData geoCode; + + private LocalDateTime expiration; + + private List extensionList; + + protected InteropRequestData(@NotNull String transactionCode, String requestCode, @NotNull String accountId, @NotNull MoneyData amount, + @NotNull InteropTransactionRole transactionRole, InteropTransactionTypeData transactionType, String note, + GeoCodeData geoCode, LocalDateTime expiration, List extensionList) { + this.transactionCode = transactionCode; + this.requestCode = requestCode; + this.accountId = accountId; + this.amount = amount; + this.transactionType = transactionType; + this.transactionRole = transactionRole; + this.note = note; + this.geoCode = geoCode; + this.expiration = expiration; + this.extensionList = extensionList; + } + + protected InteropRequestData(@NotNull String transactionCode, @NotNull String accountId, @NotNull MoneyData amount, @NotNull InteropTransactionRole transactionRole) { + this(transactionCode, null, accountId, amount, transactionRole, null, null, null, null, null); + } + + @NotNull + public String getTransactionCode() { + return transactionCode; + } + + public String getRequestCode() { + return requestCode; + } + + @NotNull + public String getAccountId() { + return accountId; + } + + @NotNull + public MoneyData getAmount() { + return amount; + } + + public InteropTransactionTypeData getTransactionType() { + return transactionType; + } + + @NotNull + public InteropTransactionRole getTransactionRole() { + return transactionRole; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public GeoCodeData getGeoCode() { + return geoCode; + } + + public void setGeoCode(GeoCodeData geoCode) { + this.geoCode = geoCode; + } + + public LocalDateTime getExpiration() { + return expiration; + } + + public LocalDate getExpirationLocalDate() { + return expiration == null ? null : expiration.toLocalDate(); + } + + public void setExpiration(LocalDateTime expiration) { + this.expiration = expiration; + } + + public List getExtensionList() { + return extensionList; + } + + public void setExtensionList(List extensionList) { + this.extensionList = extensionList; + } + + public void normalizeAmounts(@NotNull MonetaryCurrency currency) { + amount.normalizeAmount(currency); + } + + public static InteropRequestData validateAndParse(final DataValidatorBuilder dataValidator, JsonObject element, FromJsonHelper jsonHelper) { + if (element == null) + return null; + + String transactionCode = jsonHelper.extractStringNamed(PARAM_TRANSACTION_CODE, element); + DataValidatorBuilder dataValidatorCopy = dataValidator.reset().parameter(PARAM_TRANSACTION_CODE).value(transactionCode).notBlank(); + + String requestCode = jsonHelper.extractStringNamed(PARAM_REQUEST_CODE, element); + + String accountId = jsonHelper.extractStringNamed(PARAM_ACCOUNT_ID, element); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_ACCOUNT_ID).value(accountId).notBlank(); + + JsonObject moneyElement = jsonHelper.extractJsonObjectNamed(PARAM_AMOUNT, element); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_AMOUNT).value(moneyElement).notNull(); + dataValidator.merge(dataValidatorCopy); + MoneyData amount = MoneyData.validateAndParse(dataValidator, moneyElement, jsonHelper); + + JsonObject transactionTypeElement = jsonHelper.extractJsonObjectNamed(PARAM_TRANSACTION_TYPE, element); + InteropTransactionTypeData transactionType = InteropTransactionTypeData.validateAndParse(dataValidator, transactionTypeElement, jsonHelper); + + String transactionRoleString = jsonHelper.extractStringNamed(PARAM_TRANSACTION_ROLE, element); + InteropTransactionRole transactionRole = transactionRoleString == null ? InteropTransactionRole.PAYER : InteropTransactionRole.valueOf(transactionRoleString); + + String note = jsonHelper.extractStringNamed(PARAM_NOTE, element); + + JsonObject geoCodeElement = jsonHelper.extractJsonObjectNamed(PARAM_GEO_CODE, element); + GeoCodeData geoCode = GeoCodeData.validateAndParse(dataValidator, geoCodeElement, jsonHelper); + + String locale = jsonHelper.extractStringNamed(PARAM_LOCALE, element); + LocalDateTime expiration = locale == null + ? jsonHelper.extractLocalTimeNamed(PARAM_EXPIRATION, element, ISO8601_DATE_TIME_FORMAT, DEFAULT_LOCALE) + : jsonHelper.extractLocalTimeNamed(PARAM_EXPIRATION, element); // PARAM_DATE_FORMAT also must be set + + JsonArray extensionArray = jsonHelper.extractJsonArrayNamed(PARAM_EXTENSION_LIST, element); + ArrayList extensionList = null; + if (extensionArray != null) { + extensionList = new ArrayList<>(extensionArray.size()); + for (JsonElement jsonElement : extensionArray) { + if (jsonElement.isJsonObject()) + extensionList.add(ExtensionData.validateAndParse(dataValidator, jsonElement.getAsJsonObject(), jsonHelper)); + } + } + + return dataValidator.hasError() ? null : new InteropRequestData(transactionCode, requestCode, accountId, amount, + transactionRole, transactionType, note, geoCode, expiration, extensionList); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropResponseData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropResponseData.java new file mode 100644 index 00000000000..aecf2a03bb8 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropResponseData.java @@ -0,0 +1,105 @@ +/* + * 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.interoperation.data; + +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.interoperation.domain.InteropActionState; +import org.joda.time.LocalDateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; + +import javax.validation.constraints.NotNull; +import java.beans.Transient; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Map; + +public class InteropResponseData extends CommandProcessingResult { + + public static final String ISO_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ssZ"; +// public static final SimpleDateFormat ISO_DATE_TIME_FORMATTER = new SimpleDateFormat(ISO_DATE_TIME_PATTERN); // TODO: not synchronized + public static DateTimeFormatter ISO_DATE_TIME_FORMATTER = DateTimeFormat.forPattern(ISO_DATE_TIME_PATTERN); + + @NotNull + private final String transactionCode; + + @NotNull + private final InteropActionState state; + + private final String expiration; + + private final List extensionList; + + + protected InteropResponseData(Long resourceId, Long officeId, Long commandId, Map changesOnly, @NotNull String transactionCode, + @NotNull InteropActionState state, LocalDateTime expiration, List extensionList) { + super(resourceId, officeId, commandId, changesOnly); + this.transactionCode = transactionCode; + this.state = state; + this.expiration = format(expiration); + this.extensionList = extensionList; + } + + protected static InteropResponseData build(Long commandId, @NotNull String transactionCode, @NotNull InteropActionState state, + LocalDateTime expiration, List extensionList) { + return new InteropResponseData(null, null, commandId, null, transactionCode, state, expiration, extensionList); + } + + public static InteropResponseData build(@NotNull String transactionCode, @NotNull InteropActionState state, + LocalDateTime expiration, List extensionList) { + return build(null, transactionCode, state, expiration, extensionList); + } + + public static InteropResponseData build(Long commandId, @NotNull String transactionCode, @NotNull InteropActionState state) { + return build(commandId, transactionCode, state, null, null); + } + + public static InteropResponseData build(@NotNull String transactionCode, @NotNull InteropActionState state) { + return build(null, transactionCode, state); + } + + public String getTransactionCode() { + return transactionCode; + } + + public InteropActionState getState() { + return state; + } + + public String getExpiration() { + return expiration; + } + + @Transient + public LocalDateTime getExpirationDate() { + return parse(expiration); + } + + public List getExtensionList() { + return extensionList; + } + + protected static LocalDateTime parse(String date) { + return date == null ? null : LocalDateTime.parse(date, ISO_DATE_TIME_FORMATTER); + } + + protected static String format(LocalDateTime date) { + return date == null ? null : date.toString(ISO_DATE_TIME_FORMATTER); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionRequestData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionRequestData.java new file mode 100644 index 00000000000..1912ce1d84b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionRequestData.java @@ -0,0 +1,71 @@ +/* + * 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.interoperation.data; + +import com.google.gson.JsonObject; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.interoperation.domain.InteropTransactionRole; +import org.joda.time.LocalDateTime; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; + +import static org.apache.fineract.interoperation.util.InteropUtil.*; + +public class InteropTransactionRequestData extends InteropRequestData { + + static final String[] PARAMS = {PARAM_TRANSACTION_CODE, PARAM_REQUEST_CODE, PARAM_ACCOUNT_ID, PARAM_AMOUNT, PARAM_TRANSACTION_ROLE, + PARAM_TRANSACTION_TYPE, PARAM_NOTE, PARAM_GEO_CODE, PARAM_EXPIRATION, PARAM_EXTENSION_LIST, PARAM_LOCALE, PARAM_DATE_FORMAT}; + + + public InteropTransactionRequestData(@NotNull String transactionCode, @NotNull String requestCode, @NotNull String accountId, + @NotNull MoneyData amount, @NotNull InteropTransactionTypeData transactionType, String note, + GeoCodeData geoCode, LocalDateTime expiration, List extensionList) { + super(transactionCode, requestCode, accountId, amount, InteropTransactionRole.PAYER, transactionType, note, geoCode, expiration, extensionList); + } + + public InteropTransactionRequestData(@NotNull String transactionCode, @NotNull String requestCode, @NotNull String accountId, + @NotNull MoneyData amount, @NotNull InteropTransactionTypeData transactionType) { + this(transactionCode, requestCode, accountId, amount, transactionType, null, null, null, null); + } + + private InteropTransactionRequestData(InteropRequestData other) { + this(other.getTransactionCode(), other.getRequestCode(), other.getAccountId(), other.getAmount(), other.getTransactionType(), + other.getNote(), other.getGeoCode(), other.getExpiration(), other.getExtensionList()); + } + + public static InteropTransactionRequestData validateAndParse(final DataValidatorBuilder dataValidator, JsonObject element, FromJsonHelper jsonHelper) { + if (element == null) + return null; + + jsonHelper.checkForUnsupportedParameters(element, Arrays.asList(PARAMS)); + + InteropRequestData interopRequestData = InteropRequestData.validateAndParse(dataValidator, element, jsonHelper); + + DataValidatorBuilder dataValidatorCopy = dataValidator.reset().parameter(PARAM_REQUEST_CODE).value(interopRequestData.getRequestCode()).notNull(); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_TRANSACTION_TYPE).value(interopRequestData.getTransactionType()).notNull(); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_TRANSACTION_ROLE).value(interopRequestData.getTransactionRole()).ignoreIfNull() + .isOneOfTheseValues(InteropTransactionRole.PAYER); + + dataValidator.merge(dataValidatorCopy); + return dataValidator.hasError() ? null : new InteropTransactionRequestData(interopRequestData); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionRequestResponseData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionRequestResponseData.java new file mode 100644 index 00000000000..1743aace28e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionRequestResponseData.java @@ -0,0 +1,64 @@ +/* + * 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.interoperation.data; + +import org.apache.fineract.interoperation.domain.InteropActionState; +import org.joda.time.LocalDateTime; + +import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.Map; + +public class InteropTransactionRequestResponseData extends InteropResponseData { + + @NotNull + private final String requestCode; + + + private InteropTransactionRequestResponseData(Long resourceId, Long officeId, Long commandId, Map changesOnly, + @NotNull String transactionCode, @NotNull InteropActionState state, LocalDateTime expiration, + List extensionList, @NotNull String requestCode) { + super(resourceId, officeId, commandId, changesOnly, transactionCode, state, expiration, extensionList); + this.requestCode = requestCode; + } + + public static InteropTransactionRequestResponseData build(Long commandId, @NotNull String transactionCode, @NotNull InteropActionState state, + LocalDateTime expiration, List extensionList, @NotNull String requestCode) { + return new InteropTransactionRequestResponseData(null, null, commandId, null, transactionCode, state, expiration, extensionList, requestCode); + } + + public static InteropTransactionRequestResponseData build(@NotNull String transactionCode, @NotNull InteropActionState state, + LocalDateTime expiration, List extensionList, @NotNull String requestCode) { + return build(null, transactionCode, state, expiration, extensionList, requestCode); + } + + public static InteropTransactionRequestResponseData build(Long commandId, @NotNull String transactionCode, @NotNull InteropActionState state, + @NotNull String requestCode) { + return build(commandId, transactionCode, state, null, null, requestCode); + } + + public static InteropTransactionRequestResponseData build(@NotNull String transactionCode, @NotNull InteropActionState state, + @NotNull String requestCode) { + return build(null, transactionCode, state, requestCode); + } + + public String getRequestCode() { + return requestCode; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionTypeData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionTypeData.java new file mode 100644 index 00000000000..2b9f9999dd6 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransactionTypeData.java @@ -0,0 +1,96 @@ +/* + * 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.interoperation.data; + +import com.google.gson.JsonObject; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.interoperation.domain.InteropInitiatorType; +import org.apache.fineract.interoperation.domain.InteropTransactionRole; +import org.apache.fineract.interoperation.domain.InteropTransactionScenario; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; + +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_INITIATOR; +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_INITIATOR_TYPE; +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_SCENARIO; +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_SUB_SCENARIO; + +public class InteropTransactionTypeData { + + public static final String[] PARAMS = {PARAM_SCENARIO, PARAM_SUB_SCENARIO, PARAM_INITIATOR, PARAM_INITIATOR_TYPE}; + + @NotNull + private final InteropTransactionScenario scenario; + + private final String subScenario; + @NotNull + private final InteropTransactionRole initiator; + @NotNull + private final InteropInitiatorType initiatorType; + + public InteropTransactionTypeData(InteropTransactionScenario scenario, String subScenario, InteropTransactionRole initiator, InteropInitiatorType initiatorType) { + this.scenario = scenario; + this.subScenario = subScenario; + this.initiator = initiator; + this.initiatorType = initiatorType; + } + + public InteropTransactionScenario getScenario() { + return scenario; + } + + public String getSubScenario() { + return subScenario; + } + + public InteropTransactionRole getInitiator() { + return initiator; + } + + public InteropInitiatorType getInitiatorType() { + return initiatorType; + } + + + public static InteropTransactionTypeData validateAndParse(DataValidatorBuilder dataValidator, JsonObject element, FromJsonHelper jsonHelper) { + if (element == null) + return null; + + jsonHelper.checkForUnsupportedParameters(element, Arrays.asList(PARAMS)); + + String scenarioString = jsonHelper.extractStringNamed(PARAM_SCENARIO, element); + DataValidatorBuilder dataValidatorCopy = dataValidator.reset().parameter(PARAM_SCENARIO).value(scenarioString).notBlank(); + InteropTransactionScenario scenario = InteropTransactionScenario.valueOf(scenarioString); + + String subScenario = jsonHelper.extractStringNamed(PARAM_SUB_SCENARIO, element); + + String initiatorString = jsonHelper.extractStringNamed(PARAM_INITIATOR, element); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_INITIATOR).value(initiatorString).notBlank(); + InteropTransactionRole initiator = InteropTransactionRole.valueOf(initiatorString); + + String initiatorTypeString = jsonHelper.extractStringNamed(PARAM_INITIATOR_TYPE, element); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_INITIATOR_TYPE).value(initiatorTypeString).notBlank(); + InteropInitiatorType initiatorType = InteropInitiatorType.valueOf(initiatorTypeString); + + dataValidator.merge(dataValidatorCopy); + return dataValidator.hasError() ? null : new InteropTransactionTypeData(scenario, subScenario, initiator, initiatorType); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransferRequestData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransferRequestData.java new file mode 100644 index 00000000000..849b69d3330 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransferRequestData.java @@ -0,0 +1,111 @@ +/* + * 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.interoperation.data; + +import com.google.gson.JsonObject; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.interoperation.domain.InteropTransactionRole; +import org.joda.time.LocalDateTime; + +import javax.validation.constraints.NotNull; +import java.util.Arrays; +import java.util.List; + +import static org.apache.fineract.interoperation.util.InteropUtil.*; + +public class InteropTransferRequestData extends InteropRequestData { + + static final String[] PARAMS = {PARAM_TRANSACTION_CODE, PARAM_ACCOUNT_ID, PARAM_AMOUNT, PARAM_TRANSACTION_ROLE, PARAM_TRANSACTION_TYPE, + PARAM_NOTE, PARAM_EXPIRATION, PARAM_EXTENSION_LIST, PARAM_TRANSFER_CODE, PARAM_FSP_FEE, PARAM_FSP_COMMISSION, + PARAM_LOCALE, PARAM_DATE_FORMAT}; + + + @NotNull + private final String transferCode; + + // validation: what was specified in quotes step + private MoneyData fspFee; + + private MoneyData fspCommission; + + public InteropTransferRequestData(@NotNull String transactionCode, @NotNull String accountId, @NotNull MoneyData amount, + @NotNull InteropTransactionRole transactionRole, InteropTransactionTypeData transactionType, String note, LocalDateTime expiration, + List extensionList, @NotNull String transferCode, MoneyData fspFee, MoneyData fspCommission) { + super(transactionCode, null, accountId, amount, transactionRole, transactionType, note, null, expiration, extensionList); + this.transferCode = transferCode; + this.fspFee = fspFee; + this.fspCommission = fspCommission; + } + + public InteropTransferRequestData(@NotNull String transactionCode, @NotNull String transferCode, @NotNull String accountId, + @NotNull MoneyData amount, @NotNull InteropTransactionRole transactionRole) { + this(transactionCode, accountId, amount, transactionRole, null, null, null, null, transferCode, null, null); + } + + private InteropTransferRequestData(InteropRequestData other, @NotNull String transferCode, MoneyData fspFee, MoneyData fspCommission) { + this(other.getTransactionCode(), other.getAccountId(), other.getAmount(), other.getTransactionRole(), other.getTransactionType(), + other.getNote(), other.getExpiration(), other.getExtensionList(), transferCode, fspFee, fspCommission); + } + + public String getTransferCode() { + return transferCode; + } + + public MoneyData getFspFee() { + return fspFee; + } + + public MoneyData getFspCommission() { + return fspCommission; + } + + public void normalizeAmounts(@NotNull MonetaryCurrency currency) { + super.normalizeAmounts(currency); + if (fspFee != null) + fspFee.normalizeAmount(currency); + } + + public static InteropTransferRequestData validateAndParse(final DataValidatorBuilder dataValidator, JsonObject element, FromJsonHelper jsonHelper) { + if (element == null) + return null; + + jsonHelper.checkForUnsupportedParameters(element, Arrays.asList(PARAMS)); + + InteropRequestData interopRequestData = InteropRequestData.validateAndParse(dataValidator, element, jsonHelper); + + String transferCode = jsonHelper.extractStringNamed(PARAM_TRANSFER_CODE, element); + DataValidatorBuilder dataValidatorCopy = dataValidator.reset().parameter(PARAM_TRANSFER_CODE).value(transferCode).notBlank(); + + JsonObject fspFeeElement = jsonHelper.extractJsonObjectNamed(PARAM_FSP_FEE, element); + dataValidator.merge(dataValidatorCopy); + MoneyData fspFee = MoneyData.validateAndParse(dataValidator, fspFeeElement, jsonHelper); + + JsonObject fspCommissionElement = jsonHelper.extractJsonObjectNamed(PARAM_FSP_COMMISSION, element); + dataValidator.merge(dataValidatorCopy); + MoneyData fspCommission = MoneyData.validateAndParse(dataValidator, fspCommissionElement, jsonHelper); + + String transactionRoleString = jsonHelper.extractStringNamed(PARAM_TRANSACTION_ROLE, element); + dataValidatorCopy = dataValidator.reset().parameter(PARAM_TRANSACTION_ROLE).value(transactionRoleString).notNull(); + + dataValidator.merge(dataValidatorCopy); + return dataValidator.hasError() ? null : new InteropTransferRequestData(interopRequestData, transferCode, fspFee, fspCommission); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransferResponseData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransferResponseData.java new file mode 100644 index 00000000000..bc89172a65b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/InteropTransferResponseData.java @@ -0,0 +1,79 @@ +/* + * 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.interoperation.data; + +import org.apache.fineract.interoperation.domain.InteropActionState; +import org.joda.time.LocalDateTime; + +import javax.validation.constraints.NotNull; +import java.beans.Transient; +import java.text.ParseException; +import java.util.List; +import java.util.Map; + +public class InteropTransferResponseData extends InteropResponseData { + + @NotNull + private final String transferCode; + + private String completedTimestamp; + + private InteropTransferResponseData(Long resourceId, Long officeId, Long commandId, Map changesOnly, + @NotNull String transactionCode, @NotNull InteropActionState state, LocalDateTime expiration, + List extensionList, @NotNull String transferCode, LocalDateTime completedTimestamp) { + super(resourceId, officeId, commandId, changesOnly, transactionCode, state, expiration, extensionList); + this.transferCode = transferCode; + this.completedTimestamp = format(completedTimestamp); + } + + public static InteropTransferResponseData build(Long commandId, @NotNull String transactionCode, @NotNull InteropActionState state, + LocalDateTime expiration, List extensionList, @NotNull String transferCode, + LocalDateTime completedTimestamp) { + return new InteropTransferResponseData(null, null, commandId, null, transactionCode, state, expiration, extensionList, + transferCode, completedTimestamp); + } + + public static InteropTransferResponseData build(@NotNull String transactionCode, @NotNull InteropActionState state, + List extensionList, @NotNull String transferCode, + LocalDateTime completedTimestamp) { + return build(null, transactionCode, state, null, extensionList, transferCode, completedTimestamp); + } + + public static InteropTransferResponseData build(Long commandId, @NotNull String transactionCode, @NotNull InteropActionState state, + @NotNull String transferCode) { + return build(commandId, transactionCode, state, null, null, transferCode, null); + } + + public static InteropTransferResponseData build(@NotNull String transactionCode, @NotNull InteropActionState state, @NotNull String transferCode) { + return build(null, transactionCode, state, transferCode); + } + + public String getTransferCode() { + return transferCode; + } + + public String getCompletedTimestamp() { + return completedTimestamp; + } + + @Transient + public LocalDateTime getCompletedTimestampDate() throws ParseException { + return parse(completedTimestamp); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/MoneyData.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/MoneyData.java new file mode 100644 index 00000000000..4b48298c3bc --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/data/MoneyData.java @@ -0,0 +1,86 @@ +/* + * 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.interoperation.data; + +import com.google.gson.JsonObject; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.interoperation.util.MathUtil; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.Arrays; + +import static org.apache.fineract.interoperation.util.InteropUtil.DEFAULT_LOCALE; +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_AMOUNT; +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_CURRENCY; +import static org.apache.fineract.interoperation.util.InteropUtil.PARAM_LOCALE; + +public class MoneyData { + + public static final String[] PARAMS = {PARAM_AMOUNT, PARAM_CURRENCY, PARAM_LOCALE}; + + @NotNull + private final BigDecimal amount; + @NotNull + private final String currency; + + MoneyData(BigDecimal amount, String currency) { + this.amount = amount; + this.currency = currency; + } + + public static MoneyData build(BigDecimal amount, String currency) { + return new MoneyData(amount, currency); + } + + public BigDecimal getAmount() { + return amount; + } + + public String getCurrency() { + return currency; + } + + public void normalizeAmount(@NotNull MonetaryCurrency currency) { + if (!currency.getCode().equals(this.currency)) + throw new UnsupportedOperationException("Internal error: Invalid currency " + currency.getCode()); + MathUtil.normalizeAmount(amount, currency); + } + + public static MoneyData validateAndParse(DataValidatorBuilder dataValidator, JsonObject element, FromJsonHelper jsonHelper) { + if (element == null) + return null; + + jsonHelper.checkForUnsupportedParameters(element, Arrays.asList(PARAMS)); + + String locale = jsonHelper.extractStringNamed(PARAM_LOCALE, element); + BigDecimal amount = locale == null + ? jsonHelper.extractBigDecimalNamed(PARAM_AMOUNT, element, DEFAULT_LOCALE) + : jsonHelper.extractBigDecimalWithLocaleNamed(PARAM_AMOUNT, element); + DataValidatorBuilder dataValidatorCopy = dataValidator.reset().parameter(PARAM_AMOUNT).value(amount).notBlank().zeroOrPositiveAmount(); + + String currency = jsonHelper.extractStringNamed(PARAM_CURRENCY, element); + dataValidatorCopy = dataValidatorCopy.reset().parameter(PARAM_CURRENCY).value(currency).notBlank().notExceedingLengthOf(3); + + dataValidator.merge(dataValidatorCopy); + return dataValidator.hasError() ? null : new MoneyData(amount, currency); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropActionState.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropActionState.java new file mode 100644 index 00000000000..73b761debd2 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropActionState.java @@ -0,0 +1,24 @@ +/* + * 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.interoperation.domain; + +public enum InteropActionState { + ACCEPTED, + REJECTED +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropAmountType.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropAmountType.java new file mode 100644 index 00000000000..1c4cab85319 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropAmountType.java @@ -0,0 +1,24 @@ +/* + * 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.interoperation.domain; + +public enum InteropAmountType { + SEND, + RECEIVE +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifier.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifier.java new file mode 100644 index 00000000000..b37c1795ab5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifier.java @@ -0,0 +1,167 @@ +/* + * 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.interoperation.domain; + +import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.Date; + +@Entity +@Table(name = "interop_identifier", uniqueConstraints = { + @UniqueConstraint(name = "uk_hathor_identifier_account", columnNames = {"account_id", "type"}), + @UniqueConstraint(name = "uk_hathor_identifier_value", columnNames = {"type", "a_value", "sub_value_or_type"}) +}) +public class InteropIdentifier extends AbstractPersistableCustom { + + @ManyToOne(optional = false) + @JoinColumn(name = "account_id", nullable = false) + private SavingsAccount account; + + @Column(name = "type", nullable = false, length = 32) + @Enumerated(EnumType.STRING) + private InteropIdentifierType type; + + @Column(name = "a_value", nullable = false, length = 128) + private String value; + + @Column(name = "sub_value_or_type", length = 128) + private String subValueOrType; + + @Column(name = "created_by", nullable = false, length = 32) + private String createdBy; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "created_on", nullable = false) + private Date createdOn; + + @Column(name = "modified_by", length = 32) + private String modifiedBy; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name = "modified_on") + private Date modifiedOn; + + + protected InteropIdentifier() { + } + + public InteropIdentifier(@NotNull SavingsAccount account, @NotNull InteropIdentifierType type, @NotNull String value, + String subValueOrType, @NotNull String createdBy, @NotNull Date createdOn) { + this.account = account; + this.type = type; + this.value = value; + this.subValueOrType = subValueOrType; + this.createdBy = createdBy; + this.createdOn = createdOn; + } + + public InteropIdentifier(@NotNull SavingsAccount account, @NotNull InteropIdentifierType type, @NotNull String createdBy, + @NotNull Date createdOn) { + this(account, type, null, null, createdBy, createdOn); + } + + public SavingsAccount getAccount() { + return account; + } + + private void setAccount(SavingsAccount account) { + this.account = account; + } + + public InteropIdentifierType getType() { + return type; + } + + private void setType(InteropIdentifierType type) { + this.type = type; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getSubValueOrType() { + return subValueOrType; + } + + public void setSubValueOrType(String subValueOrType) { + this.subValueOrType = subValueOrType; + } + + public String getCreatedBy() { + return createdBy; + } + + private void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public Date getCreatedOn() { + return createdOn; + } + + private void setCreatedOn(Date createdOn) { + this.createdOn = createdOn; + } + + public String geModifiedBy() { + return modifiedBy; + } + + public void setModifiedBy(String modifiedBy) { + this.modifiedBy = modifiedBy; + } + + public Date getModifiedOn() { + return modifiedOn; + } + + public void setModifiedOn(Date modifiedOn) { + this.modifiedOn = modifiedOn; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + InteropIdentifier that = (InteropIdentifier) o; + + if (!account.equals(that.account)) return false; + if (type != that.type) return false; + if (!value.equals(that.value)) return false; + return subValueOrType != null ? subValueOrType.equals(that.subValueOrType) : that.subValueOrType == null; + } + + @Override + public int hashCode() { + int result = type.hashCode(); + result = 31 * result + value.hashCode(); + result = 31 * result + (subValueOrType != null ? subValueOrType.hashCode() : 0); + return result; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifierRepository.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifierRepository.java new file mode 100644 index 00000000000..c9f418cd929 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifierRepository.java @@ -0,0 +1,27 @@ +/* + * 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.interoperation.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +@Repository +public interface InteropIdentifierRepository extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifierType.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifierType.java new file mode 100644 index 00000000000..b0e2e989ae0 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropIdentifierType.java @@ -0,0 +1,31 @@ +/* + * 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.interoperation.domain; + +public enum InteropIdentifierType { + + MSISDN, + EMAIL, + PERSONAL_ID, + BUSINESS, + DEVICE, + ACCOUNT_ID, + IBAN, + ALIAS +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropInitiatorType.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropInitiatorType.java new file mode 100644 index 00000000000..fb6ff8c3943 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropInitiatorType.java @@ -0,0 +1,26 @@ +/* + * 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.interoperation.domain; + +public enum InteropInitiatorType { + CONSUMER, + AGENT, + BUSINESS, + DEVICE +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransactionRole.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransactionRole.java new file mode 100644 index 00000000000..232bf787847 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransactionRole.java @@ -0,0 +1,35 @@ +/* + * 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.interoperation.domain; + +import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType; + +public enum InteropTransactionRole { + PAYER, + PAYEE, + ; + + public boolean isWithdraw() { + return this == PAYER; + } + + public SavingsAccountTransactionType getTransactionType() { + return this == PAYER ? SavingsAccountTransactionType.WITHDRAWAL : SavingsAccountTransactionType.DEPOSIT; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransactionScenario.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransactionScenario.java new file mode 100644 index 00000000000..e3db1ad1281 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransactionScenario.java @@ -0,0 +1,28 @@ +/* + * 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.interoperation.domain; + +public enum InteropTransactionScenario { + + DEPOSIT, + WITHDRAWAL, + TRANSFER, + PAYMENT, + REFUND +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransferActionType.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransferActionType.java new file mode 100644 index 00000000000..df4b05db938 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/domain/InteropTransferActionType.java @@ -0,0 +1,26 @@ +/* + * 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.interoperation.domain; + +public enum InteropTransferActionType { + + PREPARE, + CREATE, + ; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CommitInteropTransferHandler.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CommitInteropTransferHandler.java new file mode 100644 index 00000000000..3cf3033f2d8 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CommitInteropTransferHandler.java @@ -0,0 +1,49 @@ +/** + * 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.interoperation.handler; + +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.interoperation.service.InteropService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.apache.fineract.interoperation.util.InteropUtil.ACTION_TRANSFER_COMMIT; +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_TRANSFER; + +@Service +@CommandType(entity = ENTITY_NAME_TRANSFER, action = ACTION_TRANSFER_COMMIT) +public class CommitInteropTransferHandler implements NewCommandSourceHandler { + + private final InteropService interopService; + + @Autowired + public CommitInteropTransferHandler(InteropService interopService) { + this.interopService = interopService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.interopService.commitTransfer(command); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropIdentifierHandler.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropIdentifierHandler.java new file mode 100644 index 00000000000..1b888214d5d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropIdentifierHandler.java @@ -0,0 +1,56 @@ +/** + * 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.interoperation.handler; + +import com.google.common.base.Strings; +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.interoperation.domain.InteropIdentifierType; +import org.apache.fineract.interoperation.service.InteropService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_IDENTIFIER; +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_REQUEST; + +@Service +@CommandType(entity = ENTITY_NAME_IDENTIFIER, action = "CREATE") +public class CreateInteropIdentifierHandler implements NewCommandSourceHandler { + + private final InteropService interopService; + + @Autowired + public CreateInteropIdentifierHandler(InteropService interopService) { + this.interopService = interopService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + String[] split = command.getUrl().split("/"); + int length = split.length; + String subIdOrType = Strings.emptyToNull(split[length]); + String idValue = split[length - 1]; + InteropIdentifierType idType = InteropIdentifierType.valueOf(split[length - 2].toUpperCase()); + return this.interopService.registerAccountIdentifier(idType, idValue, subIdOrType, command); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropQuoteHandler.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropQuoteHandler.java new file mode 100644 index 00000000000..e389d4be4db --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropQuoteHandler.java @@ -0,0 +1,48 @@ +/** + * 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.interoperation.handler; + +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.interoperation.service.InteropService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_QUOTE; + +@Service +@CommandType(entity = ENTITY_NAME_QUOTE, action = "CREATE") +public class CreateInteropQuoteHandler implements NewCommandSourceHandler { + + private final InteropService interopService; + + @Autowired + public CreateInteropQuoteHandler(InteropService interopService) { + this.interopService = interopService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.interopService.createQuote(command); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropRequestHandler.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropRequestHandler.java new file mode 100644 index 00000000000..98e2e2370b0 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/CreateInteropRequestHandler.java @@ -0,0 +1,49 @@ +/** + * 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.interoperation.handler; + +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.interoperation.domain.InteropIdentifierType; +import org.apache.fineract.interoperation.service.InteropService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_REQUEST; + +@Service +@CommandType(entity = ENTITY_NAME_REQUEST, action = "CREATE") +public class CreateInteropRequestHandler implements NewCommandSourceHandler { + + private final InteropService interopService; + + @Autowired + public CreateInteropRequestHandler(InteropService interopService) { + this.interopService = interopService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.interopService.createTransactionRequest(command); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/DeleteInteropRequestHandler.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/DeleteInteropRequestHandler.java new file mode 100644 index 00000000000..dda41c47e91 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/DeleteInteropRequestHandler.java @@ -0,0 +1,55 @@ +/** + * 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.interoperation.handler; + +import com.google.common.base.Strings; +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.interoperation.domain.InteropIdentifierType; +import org.apache.fineract.interoperation.service.InteropService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_REQUEST; + +@Service +@CommandType(entity = ENTITY_NAME_REQUEST, action = "DELETE") +public class DeleteInteropRequestHandler implements NewCommandSourceHandler { + + private final InteropService interopService; + + @Autowired + public DeleteInteropRequestHandler(InteropService interopService) { + this.interopService = interopService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + String[] split = command.getUrl().split("/"); + int length = split.length; + String subIdOrType = Strings.emptyToNull(split[length]); + String idValue = split[length - 1]; + InteropIdentifierType idType = InteropIdentifierType.valueOf(split[length - 2].toUpperCase()); + return this.interopService.deleteAccountIdentifier(idType, idValue, subIdOrType); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/PrepareInteropTransferHandler.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/PrepareInteropTransferHandler.java new file mode 100644 index 00000000000..399464129db --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/handler/PrepareInteropTransferHandler.java @@ -0,0 +1,49 @@ +/** + * 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.interoperation.handler; + +import org.apache.fineract.commands.annotation.CommandType; +import org.apache.fineract.commands.handler.NewCommandSourceHandler; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.interoperation.service.InteropService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import static org.apache.fineract.interoperation.util.InteropUtil.ACTION_TRANSFER_PREPARE; +import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_TRANSFER; + +@Service +@CommandType(entity = ENTITY_NAME_TRANSFER, action = ACTION_TRANSFER_PREPARE) +public class PrepareInteropTransferHandler implements NewCommandSourceHandler { + + private final InteropService interopService; + + @Autowired + public PrepareInteropTransferHandler(InteropService interopService) { + this.interopService = interopService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.interopService.prepareTransfer(command); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/serialization/InteropDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/serialization/InteropDataValidator.java new file mode 100644 index 00000000000..8d90d5a16d5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/serialization/InteropDataValidator.java @@ -0,0 +1,110 @@ +/* + * 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.interoperation.serialization; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.apache.commons.lang.StringUtils; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; +import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.interoperation.data.InteropIdentifierRequestData; +import org.apache.fineract.interoperation.data.InteropQuoteRequestData; +import org.apache.fineract.interoperation.data.InteropTransactionRequestData; +import org.apache.fineract.interoperation.data.InteropTransferRequestData; +import org.apache.fineract.interoperation.domain.InteropIdentifierType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.validation.constraints.NotNull; +import java.util.ArrayList; + +@Component +public class InteropDataValidator { + + private final FromJsonHelper jsonHelper; + + @Autowired + public InteropDataValidator(final FromJsonHelper fromJsonHelper) { + this.jsonHelper = fromJsonHelper; + } + + public InteropIdentifierRequestData validateAndParseCreateIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, + String subIdOrType, JsonCommand command) { + final DataValidatorBuilder dataValidator = new DataValidatorBuilder(new ArrayList<>()).resource("interoperation.identifier"); + JsonObject element = extractJsonObject(command); + + InteropIdentifierRequestData result = InteropIdentifierRequestData.validateAndParse(dataValidator, idType, idValue, subIdOrType, element, jsonHelper); + throwExceptionIfValidationWarningsExist(dataValidator); + + return result; + } + + public InteropTransactionRequestData validateAndParseCreateRequest(JsonCommand command) { + final DataValidatorBuilder dataValidator = new DataValidatorBuilder(new ArrayList<>()).resource("interoperation.request"); + JsonObject element = extractJsonObject(command); + + InteropTransactionRequestData result = InteropTransactionRequestData.validateAndParse(dataValidator, element, jsonHelper); + throwExceptionIfValidationWarningsExist(dataValidator); + + return result; + } + + public InteropQuoteRequestData validateAndParseCreateQuote(JsonCommand command) { + final DataValidatorBuilder dataValidator = new DataValidatorBuilder(new ArrayList<>()).resource("interoperation.quote"); + JsonObject element = extractJsonObject(command); + + InteropQuoteRequestData result = InteropQuoteRequestData.validateAndParse(dataValidator, element, jsonHelper); + throwExceptionIfValidationWarningsExist(dataValidator); + + return result; + } + + public InteropTransferRequestData validateAndParsePrepareTransfer(JsonCommand command) { + return validateAndParseCreateTransfer(command); + } + + public InteropTransferRequestData validateAndParseCreateTransfer(JsonCommand command) { + final DataValidatorBuilder dataValidator = new DataValidatorBuilder(new ArrayList<>()).resource("interoperation.transfer"); + JsonObject element = extractJsonObject(command); + + InteropTransferRequestData result = InteropTransferRequestData.validateAndParse(dataValidator, element, jsonHelper); + throwExceptionIfValidationWarningsExist(dataValidator); + + return result; + } + + private JsonObject extractJsonObject(JsonCommand command) { + String json = command.json(); + if (StringUtils.isBlank(json)) + throw new InvalidJsonException(); + + final JsonElement element = jsonHelper.parse(json); + return element.getAsJsonObject(); + } + + private void throwExceptionIfValidationWarningsExist(DataValidatorBuilder dataValidator) { + if (dataValidator.hasError()) { + throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidator.getDataValidationErrors()); + } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropService.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropService.java new file mode 100644 index 00000000000..e0b0e7fb849 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropService.java @@ -0,0 +1,60 @@ +/** + * 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.interoperation.service; + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.interoperation.data.InteropIdentifierResponseData; +import org.apache.fineract.interoperation.data.InteropQuoteResponseData; +import org.apache.fineract.interoperation.data.InteropTransactionRequestResponseData; +import org.apache.fineract.interoperation.data.InteropTransferResponseData; +import org.apache.fineract.interoperation.domain.InteropIdentifierType; + +import javax.validation.constraints.NotNull; + +public interface InteropService { + + @NotNull + InteropIdentifierResponseData getAccountByIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, String subIdOrType); + + @NotNull + InteropIdentifierResponseData registerAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, + String subIdOrType, @NotNull JsonCommand command); + + @NotNull + InteropIdentifierResponseData deleteAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, + String subIdOrType); + + InteropTransactionRequestResponseData getTransactionRequest(@NotNull String transactionCode, @NotNull String requestCode); + + @NotNull + InteropTransactionRequestResponseData createTransactionRequest(@NotNull JsonCommand command); + + InteropQuoteResponseData getQuote(@NotNull String transactionCode, @NotNull String quoteCode); + + @NotNull + InteropQuoteResponseData createQuote(@NotNull JsonCommand command); + + InteropTransferResponseData getTransfer(@NotNull String transactionCode, @NotNull String transferCode); + + @NotNull + InteropTransferResponseData prepareTransfer(@NotNull JsonCommand command); + + @NotNull + InteropTransferResponseData commitTransfer(@NotNull JsonCommand command); +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java new file mode 100644 index 00000000000..51c0b250cd4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java @@ -0,0 +1,429 @@ +/** + * 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.interoperation.service; + +import org.apache.commons.lang.StringUtils; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.interoperation.data.*; +import org.apache.fineract.interoperation.domain.InteropIdentifier; +import org.apache.fineract.interoperation.domain.InteropIdentifierRepository; +import org.apache.fineract.interoperation.domain.InteropIdentifierType; +import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; +import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository; +import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.interoperation.domain.InteropActionState; +import org.apache.fineract.interoperation.serialization.InteropDataValidator; +import org.apache.fineract.interoperation.util.MathUtil; +import org.apache.fineract.portfolio.note.domain.Note; +import org.apache.fineract.portfolio.note.domain.NoteRepository; +import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; +import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService; +import org.apache.fineract.portfolio.paymenttype.domain.PaymentType; +import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository; +import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType; +import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountDomainService; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionRepository; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionSummaryWrapper; +import org.apache.fineract.portfolio.savings.domain.SavingsHelper; +import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException; +import org.apache.fineract.useradministration.domain.AppUser; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.domain.Specifications; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Path; +import javax.persistence.criteria.Root; +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import static org.apache.fineract.interoperation.util.InteropUtil.DEFAULT_LOCALE; +import static org.apache.fineract.interoperation.util.InteropUtil.DEFAULT_ROUTING_CODE; +import static org.apache.fineract.interoperation.util.InteropUtil.ISO8601_DATE_TIME_FORMAT; + +@Service +public class InteropServiceImpl implements InteropService { + + private final static Logger LOG = LoggerFactory.getLogger(InteropServiceImpl.class); + + private final PlatformSecurityContext securityContext; + private final InteropDataValidator dataValidator; + + private final SavingsAccountRepository savingsAccountRepository; + private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; + private final ApplicationCurrencyRepository currencyRepository; + private final NoteRepository noteRepository; + private final PaymentTypeRepository paymentTypeRepository; + private final InteropIdentifierRepository identifierRepository; + + private final SavingsHelper savingsHelper; + private final SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper; + + private final SavingsAccountDomainService savingsAccountService; + private final PaymentDetailWritePlatformService paymentDetailService; + + @Autowired + public InteropServiceImpl(PlatformSecurityContext securityContext, + InteropDataValidator interopDataValidator, + SavingsAccountRepository savingsAccountRepository, + SavingsAccountTransactionRepository savingsAccountTransactionRepository, + ApplicationCurrencyRepository applicationCurrencyRepository, + NoteRepository noteRepository, + PaymentTypeRepository paymentTypeRepository, + InteropIdentifierRepository identifierRepository, + SavingsHelper savingsHelper, + SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper, + SavingsAccountDomainService savingsAccountService, + PaymentDetailWritePlatformService paymentDetailWritePlatformService) { + this.securityContext = securityContext; + this.dataValidator = interopDataValidator; + this.savingsAccountRepository = savingsAccountRepository; + this.savingsAccountTransactionRepository = savingsAccountTransactionRepository; + this.currencyRepository = applicationCurrencyRepository; + this.noteRepository = noteRepository; + this.paymentTypeRepository = paymentTypeRepository; + this.identifierRepository = identifierRepository; + this.savingsHelper = savingsHelper; + this.savingsAccountTransactionSummaryWrapper = savingsAccountTransactionSummaryWrapper; + this.savingsAccountService = savingsAccountService; + this.paymentDetailService = paymentDetailWritePlatformService; + } + + @NotNull + @Transactional + public InteropIdentifierResponseData getAccountByIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, String subIdOrType) { + InteropIdentifier identifier = findIdentifier(idType, idValue, subIdOrType); + if (identifier == null) + throw new UnsupportedOperationException("Account not found for identifier " + idType + "/" + idValue + (subIdOrType == null ? "" : ("/" + subIdOrType))); + + return InteropIdentifierResponseData.build(identifier.getAccount().getExternalId()); + } + + @NotNull + @Transactional(propagation = Propagation.MANDATORY) + public InteropIdentifierResponseData registerAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, + String subIdOrType, @NotNull JsonCommand command) { + InteropIdentifierRequestData request = dataValidator.validateAndParseCreateIdentifier(idType, idValue, subIdOrType, command); + //TODO: error handling + SavingsAccount savingsAccount = validateAndGetSavingAccount(request.getAccountId()); + + AppUser createdBy = getLoginUser(); + + InteropIdentifier identifier = new InteropIdentifier(savingsAccount, request.getIdType(), request.getIdValue(), + request.getSubIdOrType(), createdBy.getUsername(), DateUtils.getDateOfTenant()); + + identifierRepository.save(identifier); + + return InteropIdentifierResponseData.build(savingsAccount.getExternalId()); + } + + @NotNull + @Transactional(propagation = Propagation.MANDATORY) + public InteropIdentifierResponseData deleteAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, + String subIdOrType) { + InteropIdentifier identifier = findIdentifier(idType, idValue, subIdOrType); + if (identifier == null) + throw new UnsupportedOperationException("Account not found for identifier " + idType + "/" + idValue + (subIdOrType == null ? "" : ("/" + subIdOrType))); + + String accountId = identifier.getAccount().getExternalId(); + + identifierRepository.delete(identifier); + + return InteropIdentifierResponseData.build(accountId); + } + + @Override + public InteropTransactionRequestResponseData getTransactionRequest(@NotNull String transactionCode, @NotNull String requestCode) { + // always REJECTED until request info is stored + return InteropTransactionRequestResponseData.build(transactionCode, InteropActionState.REJECTED, requestCode); + } + + @Override + @NotNull + @Transactional(propagation = Propagation.MANDATORY) + public InteropTransactionRequestResponseData createTransactionRequest(@NotNull JsonCommand command) { + // only when Payee request transaction from Payer, so here role must be always Payer + InteropTransactionRequestData request = dataValidator.validateAndParseCreateRequest(command); + + //TODO: error handling + SavingsAccount savingsAccount = validateAndGetSavingAccount(request); + + return InteropTransactionRequestResponseData.build(command.commandId(), request.getTransactionCode(), InteropActionState.ACCEPTED, + request.getExpiration(), request.getExtensionList(), request.getRequestCode()); + } + + @Override + public InteropQuoteResponseData getQuote(@NotNull String transactionCode, @NotNull String quoteCode) { + return null; + } + + @Override + @NotNull + @Transactional(propagation = Propagation.MANDATORY) + public InteropQuoteResponseData createQuote(@NotNull JsonCommand command) { + InteropQuoteRequestData request = dataValidator.validateAndParseCreateQuote(command); + + //TODO: error handling + SavingsAccount savingsAccount = validateAndGetSavingAccount(request); + SavingsAccountTransactionType transactionType = request.getTransactionRole().getTransactionType(); + + BigDecimal fee = transactionType.isDebit() ? savingsAccount.calculateWithdrawalFee(request.getAmount().getAmount()) : BigDecimal.ZERO; + + return InteropQuoteResponseData.build(command.commandId(), request.getTransactionCode(), InteropActionState.ACCEPTED, + request.getExpiration(), request.getExtensionList(), request.getQuoteCode(), MoneyData.build(fee, savingsAccount.getCurrency().getCode()), + null); + } + + @Override + public InteropTransferResponseData getTransfer(@NotNull String transactionCode, @NotNull String transferCode) { + return null; + } + + @Override + @NotNull + @Transactional(propagation = Propagation.MANDATORY) + public InteropTransferResponseData prepareTransfer(@NotNull JsonCommand command) { + InteropTransferRequestData request = dataValidator.validateAndParsePrepareTransfer(command); + + //TODO: error handling + //TODO: REVERSE + SavingsAccount savingsAccount = validateAndGetSavingAccount(request); + + BigDecimal total = validateTransfer(request, savingsAccount); + + String transferCode = request.getTransferCode(); + LocalDateTime transactionDate = DateUtils.getLocalDateTimeOfTenant(); + if (MathUtil.isGreaterThanZero(total)) { + if (MathUtil.isLessThan(savingsAccount.getWithdrawableBalance(), total)) { + throw new UnsupportedOperationException(); + } + if (findTransaction(savingsAccount, transferCode, SavingsAccountTransactionType.AMOUNT_HOLD) != null) + throw new UnsupportedOperationException("Transfer amount was already put on hold " + transferCode); + + PaymentDetail paymentDetail = PaymentDetail.instance(findPaymentType(), savingsAccount.getExternalId(), null, getRoutingCode(), transferCode, null); + AppUser appUser = getLoginUser(); + SavingsAccountTransaction transaction = SavingsAccountTransaction.holdAmount(savingsAccount, savingsAccount.office(), + paymentDetail, transactionDate.toLocalDate(), Money.of(savingsAccount.getCurrency(), total), new Date(), + appUser); + + savingsAccount.holdAmount(total); + savingsAccount.addTransaction(transaction); + + savingsAccountRepository.save(savingsAccount); + } + + return InteropTransferResponseData.build(command.commandId(), request.getTransactionCode(), InteropActionState.ACCEPTED, request.getExpiration(), + request.getExtensionList(), transferCode, transactionDate); + } + + @Override + @NotNull + @Transactional(propagation = Propagation.MANDATORY) + public InteropTransferResponseData commitTransfer(@NotNull JsonCommand command) { + InteropTransferRequestData request = dataValidator.validateAndParseCreateTransfer(command); + SavingsAccountTransactionType transactionType = request.getTransactionRole().getTransactionType(); + boolean debit = transactionType.isDebit(); + + //TODO: error handling + //TODO: REVERSE + SavingsAccount savingsAccount = validateAndGetSavingAccount(request); + + validateTransfer(request, savingsAccount); + + String transferCode = request.getTransferCode(); + if (findTransaction(savingsAccount, transferCode, debit ? SavingsAccountTransactionType.WITHDRAWAL : SavingsAccountTransactionType.DEPOSIT) != null) + throw new UnsupportedOperationException("Transfer was already committed " + transferCode); + + PaymentDetail paymentDetail = PaymentDetail.instance(findPaymentType(), savingsAccount.getExternalId(), null, getRoutingCode(), transferCode, null); + + LocalDateTime transactionDateTime = DateUtils.getLocalDateTimeOfTenant(); + LocalDate transactionDate = transactionDateTime.toLocalDate(); + Date createdDate = new Date(); + + SavingsAccountTransaction holdTransaction = findTransaction(savingsAccount, transferCode, SavingsAccountTransactionType.AMOUNT_HOLD); + if (holdTransaction != null && holdTransaction.getReleaseIdOfHoldAmountTransaction() == null) { + AppUser appUser = getLoginUser(); + + SavingsAccountTransaction releaseTransaction = SavingsAccountTransaction.releaseAmount(holdTransaction, transactionDate, createdDate, appUser); + releaseTransaction = savingsAccountTransactionRepository.saveAndFlush(releaseTransaction); + holdTransaction.updateReleaseId(releaseTransaction.getId()); + + savingsAccount.releaseAmount(holdTransaction.getAmount()); + savingsAccount.addTransaction(releaseTransaction); + + savingsAccountRepository.save(savingsAccount); + } + + BigDecimal amount = request.getAmount().getAmount(); + DateTimeFormatter fmt = getDateTimeFormatter(command); + + SavingsAccountTransaction transaction; + if (debit) { + SavingsTransactionBooleanValues transactionValues = new SavingsTransactionBooleanValues(false, true, true, false, false); + transaction = savingsAccountService.handleWithdrawal(savingsAccount, fmt, transactionDate, amount, + paymentDetail, transactionValues); + } + else { + transaction = savingsAccountService.handleDeposit(savingsAccount, fmt, transactionDate, amount, + paymentDetail, false, true); + } + + String note = request.getNote(); + if (!StringUtils.isBlank(note)) { + noteRepository.save(Note.savingsTransactionNote(savingsAccount, transaction, note)); + } + + return InteropTransferResponseData.build(command.commandId(), request.getTransactionCode(), InteropActionState.ACCEPTED, + request.getExpiration(), request.getExtensionList(), request.getTransferCode(), transactionDateTime); + } + + // Util + + private SavingsAccount validateAndGetSavingAccount(String accountId) { + SavingsAccount savingsAccount = savingsAccountRepository.findByExternalId(accountId); + if (savingsAccount == null) + throw new SavingsAccountNotFoundException(accountId); + return savingsAccount; + } + + private SavingsAccount validateAndGetSavingAccount(@NotNull InteropRequestData request) { + //TODO: error handling + SavingsAccount savingsAccount = validateAndGetSavingAccount(request.getAccountId()); + savingsAccount.setHelpers(savingsAccountTransactionSummaryWrapper, savingsHelper); + + ApplicationCurrency currency = currencyRepository.findOneByCode(request.getAmount().getCurrency()); + if (!savingsAccount.getCurrency().getCode().equals(currency.getCode())) + throw new UnsupportedOperationException(); + + SavingsAccountTransactionType transactionType = request.getTransactionRole().getTransactionType(); + if (!savingsAccount.isTransactionAllowed(transactionType, request.getExpirationLocalDate())) + throw new UnsupportedOperationException(); + + request.normalizeAmounts(savingsAccount.getCurrency()); + if (transactionType.isDebit() && MathUtil.isLessThan(savingsAccount.getWithdrawableBalance(), request.getAmount().getAmount())) + throw new UnsupportedOperationException(); + + return savingsAccount; + } + + private BigDecimal validateTransfer(@NotNull InteropTransferRequestData request, @NotNull SavingsAccount savingsAccount) { + BigDecimal amount = request.getAmount().getAmount(); + SavingsAccountTransactionType transactionType = request.getTransactionRole().getTransactionType(); + + BigDecimal total = transactionType.isDebit() ? amount : MathUtil.negate(amount); + MoneyData fspFee = request.getFspFee(); + if (fspFee != null) { + if (!savingsAccount.getCurrency().getCode().equals(fspFee.getCurrency())) + throw new UnsupportedOperationException(); + //TODO: compare with calculated quote fee + total = MathUtil.add(total, fspFee.getAmount()); + } + MoneyData fspCommission = request.getFspCommission(); + if (fspCommission != null) { + if (!savingsAccount.getCurrency().getCode().equals(fspCommission.getCurrency())) + throw new UnsupportedOperationException(); + //TODO: compare with calculated quote commission + total = MathUtil.subtractToZero(total, fspCommission.getAmount()); + } + return total; + } + + private DateTimeFormatter getDateTimeFormatter(@NotNull JsonCommand command) { + Locale locale = command.extractLocale(); + if (locale == null) + locale = DEFAULT_LOCALE; + String dateFormat = command.dateFormat(); + if (StringUtils.isEmpty(dateFormat)) + dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"; + + return DateTimeFormat.forPattern(dateFormat).withLocale(locale); + } + + PaymentType findPaymentType() { + List paymentTypes = paymentTypeRepository.findAll(); + for (PaymentType paymentType : paymentTypes) { + if (!paymentType.isCashPayment()) + return paymentType; + //TODO: for now first not cash is retured: + // 1. must be added as initial setup, + // 2. if more than one non-cashe type added then update this code + } + return null; + } + + SavingsAccountTransaction findTransaction(@NotNull SavingsAccount savingsAccount, @NotNull String transactionCode, SavingsAccountTransactionType transactionType) { + String routingCode = getRoutingCode(); + for (SavingsAccountTransaction transaction : savingsAccount.getTransactions()) { + if (transactionType != null && !transactionType.getValue().equals(transaction.getTypeOf())) + continue; + + PaymentDetail detail = transaction.getPaymentDetail(); + if (detail != null && routingCode.equals(detail.getRoutingCode()) && transactionCode.equals(detail.getReceiptNumber())) + return transaction; + } + return null; + } + + public InteropIdentifier findIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, String subIdOrType) { + return identifierRepository.findOne(Specifications.where(idTypeEqual(idType)).and(idValueEqual(idValue)).and(subIdOrTypeEqual(subIdOrType))); + } + + public static Specification idTypeEqual(@NotNull InteropIdentifierType idType) { + return (Root root, CriteriaQuery query, CriteriaBuilder cb) -> cb.and(cb.equal(root.get("type"), idType)); + } + + public static Specification idValueEqual(@NotNull String idValue) { + return (Root root, CriteriaQuery query, CriteriaBuilder cb) -> cb.and(cb.equal(root.get("value"), idValue)); + } + + public static Specification subIdOrTypeEqual(String subIdOrType) { + return (Root root, CriteriaQuery query, CriteriaBuilder cb) -> { + Path path = root.get("subValueOrType"); + return cb.and(subIdOrType == null ? cb.isNull(path) : cb.equal(path, subIdOrType)); + }; + } + + private AppUser getLoginUser() { + return securityContext.getAuthenticatedUserIfPresent(); + } + + @NotNull + String getRoutingCode() { + return DEFAULT_ROUTING_CODE; + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/InteropUtil.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/InteropUtil.java new file mode 100644 index 00000000000..b33adeb3001 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/InteropUtil.java @@ -0,0 +1,76 @@ +/* + * 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.interoperation.util; + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import serp.util.Strings; + +import java.util.Locale; + +public class InteropUtil { + + public static final String ISO8601_DATE_TIME_FORMAT = "yyyy-MM-ddTHH:mm:ss.SSS[-HH:MM]"; + public static final String ISO8601_DATE_FORMAT = "yyyy-MM-dd"; + + public static final Locale DEFAULT_LOCALE = Locale.US; + + public static final String ROOT_PATH = "interoperation"; + public static final String DEFAULT_ROUTING_CODE = "INTEROPERATION"; + + public static final String ENTITY_NAME_IDENTIFIER = "INTERID"; + public static final String ENTITY_NAME_REQUEST = "INTERREQUEST"; + public static final String ENTITY_NAME_QUOTE = "INTERQUOTE"; + public static final String ENTITY_NAME_TRANSFER = "INTERTRANSFER"; + + public static final String ACTION_TRANSFER_PREPARE = "PREPARE"; + public static final String ACTION_TRANSFER_COMMIT = "CREATE"; + + public static final String PARAM_LOCALE = "locale"; + public static final String PARAM_DATE_FORMAT = "dateFormat"; + + public static final String PARAM_TRANSACTION_CODE = "transactionCode"; + public static final String PARAM_REQUEST_CODE = "requestCode"; + public static final String PARAM_QUOTE_CODE = "quoteCode"; + public static final String PARAM_TRANSFER_CODE = "transferCode"; + public static final String PARAM_ACCOUNT_ID = "accountId"; + public static final String PARAM_AMOUNT_TYPE = "amountType"; + public static final String PARAM_AMOUNT = "amount"; + public static final String PARAM_FEES = "fees"; + public static final String PARAM_FSP_FEE = "fspFee"; + public static final String PARAM_FSP_COMMISSION = "fspCommission"; + public static final String PARAM_TRANSACTION_TYPE = "transactionType"; + public static final String PARAM_TRANSACTION_ROLE = "transactionRole"; + public static final String PARAM_NOTE = "note"; + public static final String PARAM_GEO_CODE = "geoCode"; + + public static final String PARAM_CURRENCY = "currency"; + + public static final String PARAM_SCENARIO = "scenario"; + public static final String PARAM_SUB_SCENARIO = "subScenario"; + public static final String PARAM_INITIATOR = "initiator"; + public static final String PARAM_INITIATOR_TYPE = "initiatorType"; + public static final String PARAM_EXPIRATION = "expiration"; + + public static final String PARAM_LATITUDE = "latitude"; + public static final String PARAM_LONGITUDE = "latitude"; + + public static final String PARAM_EXTENSION_LIST = "extensionList"; + public static final String PARAM_KEY = "key"; + public static final String PARAM_VALUE = "value"; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/MathUtil.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/MathUtil.java new file mode 100644 index 00000000000..a5c7a783527 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/util/MathUtil.java @@ -0,0 +1,403 @@ +/* + * 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.interoperation.util; + +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; +import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.organisation.monetary.domain.MoneyHelper; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.math.MathContext; + +public class MathUtil { + + public static Long nullToZero(Long value) { + return nullToDefault(value, 0L); + } + + public static Long nullToDefault(Long value, Long def) { + return value == null ? def : value; + } + + public static Long zeroToNull(Long value) { + return isEmpty(value) ? null : value; + } + + /** @return parameter value or ZERO if it is negative */ + public static Long negativeToZero(Long value) { + return isGreaterThanZero(value) ? value : 0L; + } + + public static boolean isEmpty(Long value) { + return value == null || value.equals(0L); + } + + public static boolean isGreaterThanZero(Long value) { + return value != null && value > 0L; + } + + public static boolean isLessThanZero(Long value) { + return value != null && value < 0L; + } + + public static boolean isZero(Long value) { + return value != null && value.equals(0L); + } + + public static boolean isEqualTo(Long first, Long second) { + return nullToZero(first).equals(nullToZero(second)); + } + + public static boolean isGreaterThan(Long first, Long second) { + return nullToZero(first) > nullToZero(second); + } + + public static boolean isLessThan(Long first, Long second) { + return nullToZero(first) < nullToZero(second); + } + + public static boolean isGreaterThanOrEqualTo(Long first, Long second) { + return nullToZero(first) >= nullToZero(second); + } + + public static boolean isLessThanOrEqualZero(Long value) { + return nullToZero(value) <= 0L; + } + + /** @return parameter value or negated value to positive */ + public static Long abs(Long value) { + return value == null ? 0L : Math.abs(value); + } + + /** @return calculates minimum of the two values considering null values + * @param notNull if true then null parameter is omitted, otherwise returns null */ + public static Long min(Long first, Long second, boolean notNull) { + return first == null + ? (notNull ? second : null) + : second == null ? (notNull ? first : null) : Math.min(first, second); + } + + /** @return calculates minimum of the values considering null values + * @param notNull if true then null parameter is omitted, otherwise returns null */ + public static Long min(Long first, Long second, Long third, boolean notNull) { + return min(min(first, second, notNull), third, notNull); + } + + /** @return sum the two values considering null values */ + public static Long add(Long first, Long second) { + return first == null + ? second + : second == null ? first : Math.addExact(first, second); + } + + /** @return sum the values considering null values */ + public static Long add(Long first, Long second, Long third) { + return add(add(first, second), third); + } + + /** @return sum the values considering null values */ + public static Long add(Long first, Long second, Long third, Long fourth) { + return add(add(add(first, second), third), fourth); + } + + /** @return sum the values considering null values */ + public static Long add(Long first, Long second, Long third, Long fourth, Long fifth) { + return add(add(add(add(first, second), third), fourth), fifth); + } + + /** @return first minus second considering null values, maybe negative */ + public static Long subtract(Long first, Long second) { + return first == null + ? null + : second == null ? first : Math.subtractExact(first, second); + } + + /** @return first minus the others considering null values, maybe negative */ + public static Long subtractToZero(Long first, Long second, Long third) { + return subtractToZero(subtract(first, second), third); + } + + /** @return first minus the others considering null values, maybe negative */ + public static Long subtractToZero(Long first, Long second, Long third, Long fourth) { + return subtractToZero(subtract(subtract(first, second), third), fourth); + } + + /** @return NONE negative first minus second considering null values */ + public static Long subtractToZero(Long first, Long second) { + return negativeToZero(subtract(first, second)); + } + + /** @return BigDecimal null safe negate */ + public static Long negate(Long amount) { + return isEmpty(amount) ? amount : Math.negateExact(amount); + } + + + // ----------------- BigDecimal ----------------- + + public static BigDecimal nullToZero(BigDecimal value) { + return nullToDefault(value, BigDecimal.ZERO); + } + + public static BigDecimal nullToDefault(BigDecimal value, BigDecimal def) { + return value == null ? def : value; + } + + public static BigDecimal zeroToNull(BigDecimal value) { + return isEmpty(value) ? null : value; + } + + /** @return parameter value or ZERO if it is negative */ + public static BigDecimal negativeToZero(BigDecimal value) { + return isGreaterThanZero(value) ? value : BigDecimal.ZERO; + } + + public static boolean isEmpty(BigDecimal value) { + return value == null || BigDecimal.ZERO.compareTo(value) == 0; + } + + public static boolean isGreaterThanZero(BigDecimal value) { + return value != null && value.compareTo(BigDecimal.ZERO) > 0; + } + + public static boolean isLessThanZero(BigDecimal value) { + return value != null && value.compareTo(BigDecimal.ZERO) < 0; + } + + public static boolean isZero(BigDecimal value) { + return value != null && value.compareTo(BigDecimal.ZERO) == 0; + } + + public static boolean isEqualTo(BigDecimal first, BigDecimal second) { + return nullToZero(first).compareTo(nullToZero(second)) == 0; + } + + public static boolean isGreaterThan(BigDecimal first, BigDecimal second) { + return nullToZero(first).compareTo(nullToZero(second)) > 0; + } + + public static boolean isLessThan(BigDecimal first, BigDecimal second) { + return nullToZero(first).compareTo(nullToZero(second)) < 0; + } + + public static boolean isGreaterThanOrEqualTo(BigDecimal first, BigDecimal second) { + return nullToZero(first).compareTo(nullToZero(second)) >= 0; + } + + public static boolean isLessThanOrEqualZero(BigDecimal value) { + return nullToZero(value).compareTo(BigDecimal.ZERO) <= 0; + } + + /** @return parameter value or negated value to positive */ + public static BigDecimal abs(BigDecimal value) { + return value == null ? BigDecimal.ZERO : value.abs(); + } + + /** @return calculates minimum of the two values considering null values + * @param notNull if true then null parameter is omitted, otherwise returns null */ + public static BigDecimal min(BigDecimal first, BigDecimal second, boolean notNull) { + return notNull + ? first == null + ? second + : second == null ? first : min(first, second, false) + : isLessThan(first, second) ? first : second; + } + + /** @return calculates minimum of the values considering null values + * @param notNull if true then null parameter is omitted, otherwise returns null */ + public static BigDecimal min(BigDecimal first, BigDecimal second, BigDecimal third, boolean notNull) { + return min(min(first, second, notNull), third, notNull); + } + + /** @return sum the two values considering null values */ + public static BigDecimal add(BigDecimal first, BigDecimal second) { + return add(first, second, MoneyHelper.getMathContext()); + } + + /** @return sum the two values considering null values */ + public static BigDecimal add(BigDecimal first, BigDecimal second, MathContext mc) { + return first == null + ? second + : second == null ? first : first.add(second, mc); + } + + /** @return sum the values considering null values */ + public static BigDecimal add(BigDecimal first, BigDecimal second, BigDecimal third) { + return add(first, second, third, MoneyHelper.getMathContext()); + } + + /** @return sum the values considering null values */ + public static BigDecimal add(BigDecimal first, BigDecimal second, BigDecimal third, MathContext mc) { + return add(add(first, second, mc), third, mc); + } + + /** @return sum the values considering null values */ + public static BigDecimal add(BigDecimal first, BigDecimal second, BigDecimal third, BigDecimal fourth) { + return add(first, second, third, fourth, MoneyHelper.getMathContext()); + } + + /** @return sum the values considering null values */ + public static BigDecimal add(BigDecimal first, BigDecimal second, BigDecimal third, BigDecimal fourth, MathContext mc) { + return add(add(add(first, second, mc), third, mc), fourth, mc); + } + + /** @return sum the values considering null values */ + public static BigDecimal add(BigDecimal first, BigDecimal second, BigDecimal third, BigDecimal fourth, BigDecimal fifth) { + return add(first, second, third, fourth, fifth, MoneyHelper.getMathContext()); + } + + /** @return sum the values considering null values */ + public static BigDecimal add(BigDecimal first, BigDecimal second, BigDecimal third, BigDecimal fourth, BigDecimal fifth, MathContext mc) { + return add(add(add(add(first, second, mc), third, mc), fourth, mc), fifth, mc); + } + + /** @return first minus second considering null values, maybe negative */ + public static BigDecimal subtract(BigDecimal first, BigDecimal second) { + return first == null + ? null + : second == null ? first : first.subtract(second, MoneyHelper.getMathContext()); + } + + /** @return NONE negative first minus second considering null values */ + public static BigDecimal subtractToZero(BigDecimal first, BigDecimal second) { + return negativeToZero(subtract(first, second)); + } + + /** @return first minus the others considering null values, maybe negative */ + public static BigDecimal subtractToZero(BigDecimal first, BigDecimal second, BigDecimal third) { + MathContext mc = MoneyHelper.getMathContext(); + return subtractToZero(subtract(first, second), third); + } + + /** @return first minus the others considering null values, maybe negative */ + public static BigDecimal subtractToZero(BigDecimal first, BigDecimal second, BigDecimal third, BigDecimal fourth) { + return subtractToZero(subtract(subtract(first, second), third), fourth); + } + + /** @return BigDecimal with scale set to the 'digitsAfterDecimal' of the parameter currency */ + public static BigDecimal normalizeAmount(BigDecimal amount, @NotNull MonetaryCurrency currency) { + return amount == null ? null : amount.setScale(currency.getDigitsAfterDecimal(), MoneyHelper.getRoundingMode()); + } + + /** @return BigDecimal null safe negate */ + public static BigDecimal negate(BigDecimal amount) { + return negate(amount, MoneyHelper.getMathContext()); + } + + /** @return BigDecimal null safe negate */ + public static BigDecimal negate(BigDecimal amount, MathContext mc) { + return isEmpty(amount) ? amount : amount.negate(mc); + } + + + // ----------------- Money ----------------- + + public static Money nullToZero(Money value, @NotNull MonetaryCurrency currency) { + return nullToDefault(value, Money.zero(currency)); + } + + public static Money nullToDefault(Money value, Money def) { + return value == null ? def : value; + } + + public static Money zeroToNull(Money value) { + return isEmpty(value) ? null : value; + } + + /** @return parameter value or ZERO if it is negative */ + public static Money negativeToZero(Money value) { + return value == null || isGreaterThanZero(value) ? value : Money.zero(value.getCurrency()); + } + + public static boolean isEmpty(Money value) { + return value == null || value.isZero(); + } + + public static boolean isGreaterThanZero(Money value) { + return value != null && value.isGreaterThanZero(); + } + + public static boolean isLessThanZero(Money value) { + return value != null && value.isLessThanZero(); + } + + public static boolean isEqualTo(Money first, Money second) { + return first == null ? second == null : (second != null && first.isEqualTo(second)); + } + + public static boolean isGreaterThan(Money first, Money second) { + return second == null || (first != null && first.isGreaterThan(second)); + } + + public static boolean isLessThan(Money first, Money second) { + return first == null || (second != null && first.isLessThan(second)); + } + + public static Money plus(Money first, Money second) { + return first == null + ? second + : second == null ? first : first.plus(second); + } + + public static Money plus(Money first, Money second, Money third) { + return plus(plus(first, second), third); + } + + public static Money plus(Money first, Money second, Money third, Money fourth) { + return plus(plus(plus(first, second), third), fourth); + } + + public static Money minus(Money first, Money second) { + return first == null + ? null + : second == null ? first : first.minus(second); + } + + /** @return first minus the others considering null values, maybe negative */ + public static Money minusToZero(Money first, Money second, Money third) { + return minusToZero(minus(first, second), third); + } + + /** @return first minus the others considering null values, maybe negative */ + public static Money minusToZero(Money first, Money second, Money third, Money fourth) { + return minusToZero(minus(minus(first, second), third), fourth); + } + + /** @return NONE negative first minus second considering null values */ + public static Money minusToZero(Money first, Money second) { + return negativeToZero(minus(first, second)); + } + + /** @return calculates minimum of the two values considering null values + * @param notNull if true then null parameter is omitted, otherwise returns null */ + public static Money min(Money first, Money second, boolean notNull) { + return notNull + ? first == null + ? second + : second == null ? first : min(first, second, false) + : isLessThan(first, second) ? first : second; + } + + /** @return calculates minimum of the values considering null values + * @param notNull if true then null parameter is omitted, otherwise returns null */ + public static Money min(Money first, Money second, Money third, boolean notNull) { + return min(min(first, second, notNull), third, notNull); + } +} From cd3a92931201e2f5ed756f3a4dc40ecc1b362bd8 Mon Sep 17 00:00:00 2001 From: Marti Date: Fri, 8 Mar 2019 12:03:32 +0100 Subject: [PATCH 2/5] interoperable - config, build --- .../bulkTransactions_post_sync_response.json | 7 + .../bulkTransactions_put_async_response.json | 28 +++ .../json/bulk_payer_post_request.json | 60 ++++++ .../json/merchant_payer_post_request.json | 46 ++++ .../json/p2p_payee_post_request.json | 42 ++++ .../json/p2p_payer_post_request.json | 42 ++++ ...payee_quote_notification_post_request.json | 53 +++++ ...payer_quote_notification_post_request.json | 57 +++++ ...yer_request_notification_post_request.json | 39 ++++ .../json/transactions_post_sync_response.json | 7 + .../json/transactions_put_async_response.json | 16 ++ .../resources/META-INF/spring/appContext.xml | 14 +- .../migrations/core_db/V346__interop_init.sql | 83 ++++++++ .../V1__mifos-platform-shared-tenants.sql | 2 + .../sample_data/interop_sample_data.sql | 200 ++++++++++++++++++ .../sample_data/tn03_interop_sample_data.sql | 80 +++++++ .../sample_data/tn04_interop_sample_data.sql | 80 +++++++ 17 files changed, 848 insertions(+), 8 deletions(-) create mode 100644 docs/interoperation/specification/json/bulkTransactions_post_sync_response.json create mode 100644 docs/interoperation/specification/json/bulkTransactions_put_async_response.json create mode 100644 docs/interoperation/specification/json/bulk_payer_post_request.json create mode 100644 docs/interoperation/specification/json/merchant_payer_post_request.json create mode 100644 docs/interoperation/specification/json/p2p_payee_post_request.json create mode 100644 docs/interoperation/specification/json/p2p_payer_post_request.json create mode 100644 docs/interoperation/specification/json/payee_quote_notification_post_request.json create mode 100644 docs/interoperation/specification/json/payer_quote_notification_post_request.json create mode 100644 docs/interoperation/specification/json/payer_request_notification_post_request.json create mode 100644 docs/interoperation/specification/json/transactions_post_sync_response.json create mode 100644 docs/interoperation/specification/json/transactions_put_async_response.json create mode 100644 fineract-provider/src/main/resources/sql/migrations/core_db/V346__interop_init.sql create mode 100644 fineract-provider/src/main/resources/sql/migrations/sample_data/interop_sample_data.sql create mode 100644 fineract-provider/src/main/resources/sql/migrations/sample_data/tn03_interop_sample_data.sql create mode 100644 fineract-provider/src/main/resources/sql/migrations/sample_data/tn04_interop_sample_data.sql diff --git a/docs/interoperation/specification/json/bulkTransactions_post_sync_response.json b/docs/interoperation/specification/json/bulkTransactions_post_sync_response.json new file mode 100644 index 00000000000..1bb95ab40bb --- /dev/null +++ b/docs/interoperation/specification/json/bulkTransactions_post_sync_response.json @@ -0,0 +1,7 @@ +//Request URI: +//POST paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/bulkTransactions + +//Response Body: +{ + "bulkTransactionId": "11436b17-c690-4a30-8505-42a2c4eafb9d" // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ +} diff --git a/docs/interoperation/specification/json/bulkTransactions_put_async_response.json b/docs/interoperation/specification/json/bulkTransactions_put_async_response.json new file mode 100644 index 00000000000..c6899d75e3f --- /dev/null +++ b/docs/interoperation/specification/json/bulkTransactions_put_async_response.json @@ -0,0 +1,28 @@ +//Request URI: +//POST paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/bulkTransactions +//GET paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/bulkTransactions/11436b17-c690-4a30-8505-42a2c4eafb9d + +//Response URI: +//PUT /interoperation/bulkTransactions/11436b17-c690-4a30-8505-42a2c4eafb9d + +//Header: +//X-Tenant-Identifier: T111 + +//Response Body: +{ + "clientRefId": "0f4f8eb4-1d83-11e9-ab14-d663bd873d93", // String(1..36) + "completedTimestamp": "2017-11-16T04:15:35.513+01:00", // optional, ISO 8601 + "bulkTransferState": "COMPLETED", // mandatory, Enum of String(1..32): RECEIVED, PENDING, ACCEPTED, PROCESSING, COMPLETED, REJECTED + "individualTransferResults": [ // mandatory, (1..1000) + { + "transferId": "11436b17-c690-4a30-8505-42a2c4eafb9d", // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "completedTimestamp": "2017-11-16T04:15:35.513+01:00", // optional, ISO 8601 + "transferState": "COMMITTED"// mandatory, Enum of String(1..32): RECEIVED, RESERVED, COMMITTED, ABORTED + }, + { + "transferId": "11436b17-c690-4a30-8505-42a2c4eafb9d", // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "completedTimestamp": "2017-11-16T04:15:35.513+01:00", // optional, ISO 8601 + "transferState": "COMMITTED"// mandatory, Enum of String(1..32): RECEIVED, RESERVED, COMMITTED, ABORTED + } + ] +} diff --git a/docs/interoperation/specification/json/bulk_payer_post_request.json b/docs/interoperation/specification/json/bulk_payer_post_request.json new file mode 100644 index 00000000000..9cfcc9ddd40 --- /dev/null +++ b/docs/interoperation/specification/json/bulk_payer_post_request.json @@ -0,0 +1,60 @@ +//Request URI: +//POST paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/bulkTransactions +// HTTP/1.1 + +//Request Header: +//Accept: application/vnd.interoperability.quotes+json;version=1 +//Content-Type: application/vnd.interoperability.quotes+json;version=1.0 +//Content-Length: 975 +//Date: Tue, 15 Nov 2017 10: 13:40 GMT +//Origin: http://www.example.com +//X-Tenant-Identifier: T111 + +//Request Body: +{ + "clientRefId": "0f4f8eb4-1d83-11e9-ab14-d663bd873d93", // String(1..36) + "payer": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN93116000060000000012345676", // mandatory, String(1..128) + "partySubIdOrType": "something" // optional, String(1..128) + } + }, + "amountType": "RECEIVE", // mandatory, constant, type: Enum of String(1..32): SEND, RECEIVE + "individualTransfers": [ // mandatory, (1..1000) + { + "payee": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN93116000060000000012345671", // mandatory, String(1..128) + "partySubIdOrType": "nothing", // optional, String(1..128) + } + }, + "amount": { // mandatory + "amount": "100", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + } + }, + { + "payee": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN93116000060000000012345672", // mandatory, String(1..128) + "partySubIdOrType": "nothing", // optional, String(1..128) + } + }, + "amount": { // mandatory + "amount": "100", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + } + } + ], + "transactionType": { // mandatory + "scenario": "TRANSFER", // mandatory, constant, type: Enum of String(1..32): DEPOSIT, WITHDRAWAL, TRANSFER, PAYMENT, REFUND + "initiator": "PAYER", // mandatory, constant, type: Enum of String(1..32): PAYER, PAYEE + "initiatorType": "CONSUMER" // mandatory, constant, type: Enum of String(1..32): CONSUMER, AGENT, BUSINESS, DEVICE + }, + "note": "From Mats", // optional, String(1..128) + "expiration": "2017-11-15T22:17:28.985-01:00" // optional, ISO 8601 +} + diff --git a/docs/interoperation/specification/json/merchant_payer_post_request.json b/docs/interoperation/specification/json/merchant_payer_post_request.json new file mode 100644 index 00000000000..ce5c20bc9da --- /dev/null +++ b/docs/interoperation/specification/json/merchant_payer_post_request.json @@ -0,0 +1,46 @@ +//Request URI: +//POST paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/transactions +// HTTP/1.1 + +//Request Header: +//Accept: application/vnd.interoperability.quotes+json;version=1 +//Content-Type: application/vnd.interoperability.quotes+json;version=1.0 +//Content-Length: 975 +//Date: Tue, 15 Nov 2017 10: 13:40 GMT +//Origin: http://www.example.com +//FSPIOP-Callback: http://www.client-server.com/demo +//FSPIOP-Notification: http://www.client-server.com/demo +//X-Tenant-Identifier: T111 + +//Request Body: +{ + "clientRefId": "0f4f8eb4-1d83-11e9-ab14-d663bd873d93", // String(1..36) + "payee": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN93116000060000000012345676", // mandatory, String(1..128) + "partySubIdOrType": "something" // optional, String(1..128) + }, + "merchantClassificationCode": "" // optional, String(1..4 digits) + }, + "payer": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN4550000000058398257466", // mandatory, String(1..128) + "partySubIdOrType": "nothing", // optional, String(1..128) + "fspId": "BankNrOne" // optional, String(1..32) + } + }, + "amountType": "RECEIVE", // mandatory, constant, type: Enum of String(1..32): SEND, RECEIVE + "amount": { // mandatory + "amount": "100", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + }, + "transactionType": { // mandatory + "scenario": "PAYMENT", // mandatory, constant, type: Enum of String(1..32): DEPOSIT, WITHDRAWAL, TRANSFER, PAYMENT, REFUND + "initiator": "PAYER", // mandatory, constant, type: Enum of String(1..32): PAYER, PAYEE + "initiatorType": "CONSUMER" // mandatory, constant, type: Enum of String(1..32): CONSUMER, AGENT, BUSINESS, DEVICE + }, + "note": "From Mats", // optional, String(1..128) + "expiration": "2017-11-15T22:17:28.985-01:00" // optional, ISO 8601 +} diff --git a/docs/interoperation/specification/json/p2p_payee_post_request.json b/docs/interoperation/specification/json/p2p_payee_post_request.json new file mode 100644 index 00000000000..84a96a785e6 --- /dev/null +++ b/docs/interoperation/specification/json/p2p_payee_post_request.json @@ -0,0 +1,42 @@ +//Request URI: +//POST paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/transactions +// HTTP/1.1 + +//Request Header: +//Accept: application/vnd.interoperability.quotes+json;version=1 +//Content-Type: application/vnd.interoperability.quotes+json;version=1.0 +//Content-Length: 975 +//Date: Tue, 15 Nov 2017 10: 13:40 GMT +//Origin: http://www.example.com +//X-Tenant-Identifier: T111 + +//Request Body: +{ + "clientRefId": "0f4f8eb4-1d83-11e9-ab14-d663bd873d93", // String(1..36) + "payee": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN93116000060000000012345676", // mandatory, String(1..128) + "partySubIdOrType": "something" // optional, String(1..128) + } + }, + "payer": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "MSISDN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "+12345678901", // mandatory, String(1..128) + "partySubIdOrType": "nothing", // optional, String(1..128) + } + }, + "amountType": "SEND", // mandatory, constant, type: Enum of String(1..32): SEND, RECEIVE + "amount": { // mandatory + "amount": "100", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + }, + "transactionType": { // mandatory + "scenario": "TRANSFER", // mandatory, constant, type: Enum of String(1..32): DEPOSIT, WITHDRAWAL, TRANSFER, PAYMENT, REFUND + "initiator": "PAYEE", // mandatory, constant, type: Enum of String(1..32): PAYER, PAYEE + "initiatorType": "CONSUMER" // mandatory, constant, type: Enum of String(1..32): CONSUMER, AGENT, BUSINESS, DEVICE + }, + "note": "From Mats", // optional, String(1..128) + "expiration": "2017-11-15T22:17:28.985-01:00" // optional, ISO 8601 +} diff --git a/docs/interoperation/specification/json/p2p_payer_post_request.json b/docs/interoperation/specification/json/p2p_payer_post_request.json new file mode 100644 index 00000000000..848211d371d --- /dev/null +++ b/docs/interoperation/specification/json/p2p_payer_post_request.json @@ -0,0 +1,42 @@ +//Request URI: +//POST paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/transactions +// HTTP/1.1 + +//Request Header: +//Accept: application/vnd.interoperability.quotes+json;version=1 +//Content-Type: application/vnd.interoperability.quotes+json;version=1.0 +//Content-Length: 975 +//Date: Tue, 15 Nov 2017 10: 13:40 GMT +//Origin: http://www.example.com +//X-Tenant-Identifier: T111 + +//Request Body: +{ + "clientRefId": "0f4f8eb4-1d83-11e9-ab14-d663bd873d93", // String(1..36) + "payee": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "MSISDN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "+12345678901", // mandatory, String(1..128) + "partySubIdOrType": "nothing", // optional, String(1..128) + } + }, + "payer": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN93116000060000000012345676", // mandatory, String(1..128) + "partySubIdOrType": "something" // optional, String(1..128) + } + }, + "amountType": "SEND", // mandatory, constant, type: Enum of String(1..32): SEND, RECEIVE + "amount": { // mandatory + "amount": "100", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + }, + "transactionType": { // mandatory + "scenario": "TRANSFER", // mandatory, constant, type: Enum of String(1..32): DEPOSIT, WITHDRAWAL, TRANSFER, PAYMENT, REFUND + "initiator": "PAYER", // mandatory, constant, type: Enum of String(1..32): PAYER, PAYEE + "initiatorType": "CONSUMER" // mandatory, constant, type: Enum of String(1..32): CONSUMER, AGENT, BUSINESS, DEVICE + }, + "note": "From Mats", // optional, String(1..128) + "expiration": "2017-11-15T22:17:28.985-01:00" // optional, ISO 8601 +} diff --git a/docs/interoperation/specification/json/payee_quote_notification_post_request.json b/docs/interoperation/specification/json/payee_quote_notification_post_request.json new file mode 100644 index 00000000000..142166a5b5f --- /dev/null +++ b/docs/interoperation/specification/json/payee_quote_notification_post_request.json @@ -0,0 +1,53 @@ +//Request URI: +//POST /channel/quotes/notification +//HTTP/1.1 +//X-Tenant-Identifier: T111 + +//Request Body: +{ + "clientRefId": "0f4f8eb4-1d83-11e9-ab14-d663bd873d93", // String(1..36) + "transactionId": "11436b17-c690-4a30-8505-42a2c4eafb9d", // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "requestId": "18e43330-187e-11e9-ab14-d663bd873d93", // optional, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "quoteId": "61058cea-187e-11e9-ab14-d663bd873d93", // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "payee": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "MSISDN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "12345678901", // mandatory, String(1..128) + "partySubIdOrType": "nothing", // optional, String(1..128) + "fspId": "BankNrOne" // optional, String(1..32) + } + }, + "payer": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN93116000060000000012345676", // mandatory, String(1..128) + "partySubIdOrType": "something" // optional, String(1..128) + }, + "name": "" // optional, String(1..128) ^(?!\s*$)[\w .,'-]{1,128}$ + }, + "amountType": "SEND", // mandatory, constant, type: Enum of String(1..32): SEND, RECEIVE + "amount": { + "amount": "100", + "currency": "USD" + }, + "transferAmount": { + "amount": "99", + "currency": "USD" + }, + "fspFee": { // optional + "amount": "1", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + }, + "fspCommission": { // optional + "amount": "0", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + }, + "transactionRole": "PAYEE", + "transactionType": { // mandatory + "scenario": "TRANSFER", // mandatory, constant, type: Enum of String(1..32): DEPOSIT, WITHDRAWAL, TRANSFER, PAYMENT, REFUND + "initiator": "PAYER", // mandatory, constant, type: Enum of String(1..32): PAYER, PAYEE + "initiatorType": "CONSUMER" // mandatory, constant, type: Enum of String(1..32): CONSUMER, AGENT, BUSINESS, DEVICE + }, + "note": "From Mats", // optional, String(1..128) + "expiration": "2017-11-15T22:17:28.985-01:00" // optional, ISO 8601 +} diff --git a/docs/interoperation/specification/json/payer_quote_notification_post_request.json b/docs/interoperation/specification/json/payer_quote_notification_post_request.json new file mode 100644 index 00000000000..6b0209488a0 --- /dev/null +++ b/docs/interoperation/specification/json/payer_quote_notification_post_request.json @@ -0,0 +1,57 @@ +//Request URI: +//POST /channel/quotes/notification +// HTTP/1.1 +//X-Tenant-Identifier: T111 + +//Request Body: +{ + "clientRefId": "0f4f8eb4-1d83-11e9-ab14-d663bd873d93", // String(1..36) + "transactionId": "11436b17-c690-4a30-8505-42a2c4eafb9d", // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "requestId": "18e43330-187e-11e9-ab14-d663bd873d93", // optional, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "quoteId": "61058cea-187e-11e9-ab14-d663bd873d93", // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "payee": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "MSISDN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "+12345678901", // mandatory, String(1..128) + "partySubIdOrType": "nothing", // optional, String(1..128) + "fspId": "BankNrOne" // optional, String(1..32) + }, + "name": "" // optional, String(1..128) ^(?!\s*$)[\w .,'-]{1,128}$ + }, + "payer": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN93116000060000000012345676", // mandatory, String(1..128) + "partySubIdOrType": "something" // optional, String(1..128) + } + }, + "amountType": "SEND", // mandatory, constant, type: Enum of String(1..32): SEND, RECEIVE + "amount": { // mandatory + "amount": "100", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + }, + "transferAmount": { + "amount": "99", + "currency": "USD" + }, + "payeeReceiveAmount": { + "amount": "100", + "currency": "USD" + }, + "fspFee": { // optional + "amount": "1", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + }, + "fspCommission": { // optional + "amount": "0", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + }, + "transactionRole": "PAYER", + "transactionType": { // mandatory + "scenario": "TRANSFER", // mandatory, constant, type: Enum of String(1..32): DEPOSIT, WITHDRAWAL, TRANSFER, PAYMENT, REFUND + "initiator": "PAYEE", // mandatory, constant, type: Enum of String(1..32): PAYER, PAYEE + "initiatorType": "CONSUMER" // mandatory, constant, type: Enum of String(1..32): CONSUMER, AGENT, BUSINESS, DEVICE + }, + "note": "From Mats", // optional, String(1..128) + "expiration": "2017-11-15T22:17:28.985-01:00" // optional, ISO 8601 +} diff --git a/docs/interoperation/specification/json/payer_request_notification_post_request.json b/docs/interoperation/specification/json/payer_request_notification_post_request.json new file mode 100644 index 00000000000..3a8d2ad3e0b --- /dev/null +++ b/docs/interoperation/specification/json/payer_request_notification_post_request.json @@ -0,0 +1,39 @@ +//Request URI: +//POST /channel/transactions/{transactionId}/notification +// HTTP/1.1 +//X-Tenant-Identifier: T111 + +//Request Body: +{ + "clientRefId": "0f4f8eb4-1d83-11e9-ab14-d663bd873d93", // String(1..36) + "transactionId": "11436b17-c690-4a30-8505-42a2c4eafb9d", // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "requestId": "18e43330-187e-11e9-ab14-d663bd873d93", // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ + "payee": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "MSISDN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "+12345678901", // mandatory, String(1..128) + "partySubIdOrType": "nothing", // optional, String(1..128) + "fspId": "BankNrOne" // optional, String(1..32) + }, + "name": "" // optional, String(1..128) ^(?!\s*$)[\w .,'-]{1,128}$ + }, + "payer": { // mandatory + "partyIdInfo": { // mandatory + "partyIdType": "IBAN", // mandatory, constant, type: Enum of String(1..32): MSISDN, EMAIL, PERSONAL_ID, BUSINESS, DEVICE, ACCOUNT_ID, IBAN, ALIAS + "partyIdentifier": "IN93116000060000000012345676", // mandatory, String(1..128) + "partySubIdOrType": "something" // optional, String(1..128) + } + }, + "amountType": "SEND", // mandatory, constant, type: Enum of String(1..32): SEND, RECEIVE + "amount": { // mandatory + "amount": "100", // mandatory, Number(22, 4) ^([0]|([1-9][0-9]{0,17}))([.][0-9]{0,3}[1-9])?$ + "currency": "IDR" // mandatory, ISO 4217 (Rupee) + }, + "transactionType": { // mandatory + "scenario": "TRANSFER", // mandatory, constant, type: Enum of String(1..32): DEPOSIT, WITHDRAWAL, TRANSFER, PAYMENT, REFUND + "initiator": "PAYEE", // mandatory, constant, type: Enum of String(1..32): PAYER, PAYEE + "initiatorType": "CONSUMER" // mandatory, constant, type: Enum of String(1..32): CONSUMER, AGENT, BUSINESS, DEVICE + }, + "note": "From Mats", // optional, String(1..128) + "expiration": "2017-11-15T22:17:28.985-01:00" // optional, ISO 8601 +} diff --git a/docs/interoperation/specification/json/transactions_post_sync_response.json b/docs/interoperation/specification/json/transactions_post_sync_response.json new file mode 100644 index 00000000000..c9a89f9a57d --- /dev/null +++ b/docs/interoperation/specification/json/transactions_post_sync_response.json @@ -0,0 +1,7 @@ +//Request URI: +//POST paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/transactions + +//Response Body: +{ + "transactionId": "11436b17-c690-4a30-8505-42a2c4eafb9d" // mandatory, UUID String(36) ^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$ +} diff --git a/docs/interoperation/specification/json/transactions_put_async_response.json b/docs/interoperation/specification/json/transactions_put_async_response.json new file mode 100644 index 00000000000..3ddc178d9aa --- /dev/null +++ b/docs/interoperation/specification/json/transactions_put_async_response.json @@ -0,0 +1,16 @@ +//Request URI: +//POST paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/transactions +//GET paymenthub-schema:// payment-hub-domain:payment-hub-port/channel/transactions/11436b17-c690-4a30-8505-42a2c4eafb9d + +//Response URI: +//PUT /interoperation/transactions/11436b17-c690-4a30-8505-42a2c4eafb9d + +//Header: +//X-Tenant-Identifier: T111 + +//Response Body: +{ + "clientRefId": "0f4f8eb4-1d83-11e9-ab14-d663bd873d93", // String(1..36) + "completedTimestamp": "2017-11-16T04:15:35.513+01:00", // optional, ISO 8601 + "transferState": "COMMITTED" // mandatory, Enum of String(1..32): RECEIVED, RESERVED, COMMITTED, ABORTED +} diff --git a/fineract-provider/src/main/resources/META-INF/spring/appContext.xml b/fineract-provider/src/main/resources/META-INF/spring/appContext.xml index a2631aacc88..f3d07017285 100644 --- a/fineract-provider/src/main/resources/META-INF/spring/appContext.xml +++ b/fineract-provider/src/main/resources/META-INF/spring/appContext.xml @@ -47,11 +47,12 @@ org.apache.fineract.infrastructure.*, org.apache.fineract.scheduledjobs.*, org.apache.fineract.organisation.*, + org.apache.fineract.interoperation.*, org.apache.fineract.portfolio.loanaccount.*, org.apache.fineract.portfolio.savingsaccount.*, org.apache.fineract.portfolio.*, org.apache.fineract.useradministration.*, - org.apache.fineract.mix.*, + org.apache.fineract.mix.*, org.apache.fineract.notification.*, org.apache.fineract.template.*, org.apache.fineract.template.service.*, @@ -72,6 +73,7 @@ + @@ -86,12 +88,11 @@ - - - + - + + @@ -101,8 +102,5 @@ - - - diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V346__interop_init.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V346__interop_init.sql new file mode 100644 index 00000000000..c2b4e5f8f91 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V346__interop_init.sql @@ -0,0 +1,83 @@ +-- +-- 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. +-- + +DROP TABLE IF EXISTS `interop_identifier`; +CREATE TABLE `interop_identifier` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `account_id` BIGINT(20) NOT NULL, + `type` VARCHAR(32) NOT NULL, + `a_value` VARCHAR(128) NOT NULL, + `sub_value_or_type` VARCHAR(128) NULL, + `created_by` VARCHAR(32) NOT NULL, + `created_on` TIMESTAMP NOT NULL, + `modified_by` VARCHAR(32) NULL, + `modified_on` TIMESTAMP NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `uk_interop_identifier_account` (`account_id`, `type`), + UNIQUE INDEX `uk_interop_identifier_value` (`type`, `a_value`, `sub_value_or_type`), + INDEX `fk_interop_identifier_account` (`account_id`), + CONSTRAINT `fk_interop_identifier_account` FOREIGN KEY (`account_id`) REFERENCES `m_savings_account` (`id`) +) + COLLATE = 'utf8_general_ci' + ENGINE = InnoDB; + +-- user+roles + +SET @interop_username = 'interopUser'; +INSERT INTO `m_appuser` +VALUES (NULL, 0, 1, NULL, @interop_username, 'Interop', 'User', '5787039480429368bf94732aacc771cd0a3ea02bcf504ffe1185ab94213bc63a', + 'email@email.com', b'0', b'1', b'1', b'1', b'1', CURDATE(), 0, b'0'); + +INSERT INTO `m_appuser_role` VALUES ((SELECT id FROM m_appuser WHERE username = @interop_username), 1); + +-- Interoperation permissions + +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'READ_INTERID', 'INTERID', 'READ', 0); +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'READ_INTERREQUEST', 'INTERREQUEST', 'READ', 0); +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'READ_INTERQUOTE', 'INTERQUOTE', 'READ', 0); +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'READ_INTERTRANSFER', 'INTERTRANSFER', 'READ', 0); + +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'PREPARE_INTERTRANSFER', 'INTERTRANSFER', 'PREPARE', 0); + +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'CREATE_INTERID', 'INTERID', 'CREATE', 0); +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'CREATE_INTERREQUEST', 'INTERREQUEST', 'CREATE', 0); +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'CREATE_INTERQUOTE', 'INTERQUOTE', 'CREATE', 0); +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'CREATE_INTERTRANSFER', 'INTERTRANSFER', 'CREATE', 0); + +INSERT INTO m_permission (grouping, code, entity_name, action_name, can_maker_checker) +VALUES ('interop', 'DELETE_INTERID', 'INTERID', 'DELETE', 0); + + +INSERT IGNORE INTO m_code (code_name, is_system_defined) VALUES ('PaymentType', 1); + +SET @code_id = -1; +SELECT id INTO @code_id FROM m_code WHERE code_name = 'PaymentType'; + +INSERT IGNORE INTO m_code_value (code_id, code_value, order_position) VALUES (@code_id, 'Money Transfer', 1); + +INSERT IGNORE INTO m_payment_type (value, description, order_position) VALUES ('Money Transfer', 'Money Transfer', 1); \ No newline at end of file diff --git a/fineract-provider/src/main/resources/sql/migrations/list_db/V1__mifos-platform-shared-tenants.sql b/fineract-provider/src/main/resources/sql/migrations/list_db/V1__mifos-platform-shared-tenants.sql index eb7c9c24efa..e9e36f6d19b 100644 --- a/fineract-provider/src/main/resources/sql/migrations/list_db/V1__mifos-platform-shared-tenants.sql +++ b/fineract-provider/src/main/resources/sql/migrations/list_db/V1__mifos-platform-shared-tenants.sql @@ -67,6 +67,8 @@ CREATE TABLE `tenants` ( LOCK TABLES `tenants` WRITE; /*!40000 ALTER TABLE `tenants` DISABLE KEYS */; INSERT INTO `tenants` VALUES (1,'default','Default Demo Tenant','mifostenant-default','Asia/Kolkata',NULL,NULL,NULL,NULL,'localhost','3306','root','mysql',1); +INSERT INTO `tenants` VALUES (2,'tn03','Rhino','tn03','Africa/Bujumbura',NULL,NULL,NULL,NULL,'localhost','3306','root','mysql',1); +INSERT INTO `tenants` VALUES (3,'tn04','Elephant','tn04','Africa/Bujumbura',NULL,NULL,NULL,NULL,'localhost','3306','root','mysql',1); /*!40000 ALTER TABLE `tenants` ENABLE KEYS */; UNLOCK TABLES; diff --git a/fineract-provider/src/main/resources/sql/migrations/sample_data/interop_sample_data.sql b/fineract-provider/src/main/resources/sql/migrations/sample_data/interop_sample_data.sql new file mode 100644 index 00000000000..430b5151f57 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/sample_data/interop_sample_data.sql @@ -0,0 +1,200 @@ +-- +-- 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. +-- + +-- To use: Change saving_account_no before every run! +-- !both tn03, tn04 tenants + +-- saving product, account +SET @last_saving_prod_id = -1; +SELECT COALESCE(max(id), 1) into @last_saving_prod_id from m_savings_product; + +SET @saving_prod_name = concat('Saving Product', @last_saving_prod_id); + +INSERT INTO `m_savings_product` +(`name`, `short_name`, `description`, `deposit_type_enum`, `currency_code`, `currency_digits`, + `currency_multiplesof`, `nominal_annual_interest_rate`, `interest_compounding_period_enum`, + `interest_posting_period_enum`, `interest_calculation_type_enum`, `interest_calculation_days_in_year_type_enum`, + `min_required_opening_balance`, `accounting_type`, `withdrawal_fee_amount`, `withdrawal_fee_type_enum`, + `withdrawal_fee_for_transfer`, `allow_overdraft`, `min_required_balance`, `enforce_min_required_balance`, + `min_balance_for_interest_calculation`, `withhold_tax`, `tax_group_id`, `is_dormancy_tracking_active`) +VALUES (@saving_prod_name, concat('SP', @last_saving_prod_id), 'Saving Product', 100, 'TZS', 2, NULL, 0.000000, 1, + 4, 1, 360, NULL, 2, NULL, NULL, 0, 0, 0.000000, 1, NULL, 0, NULL, 0); + +SET @saving_prod_id = -1; +SELECT id INTO @saving_prod_id FROM m_savings_product WHERE name = @saving_prod_name; + +-- interop_identifier + +-- charge, mapping +-- gl_account, mappings +-- ASSET-1, LIABILITY-2, EQUITY-3, INCOME-4, EXPENSE-5 + +SET @payment_type_id = -1; +SELECT id INTO @payment_type_id FROM m_payment_type WHERE value = 'Money Transfer'; + +SET @saving_gl_name = 'Interoperation Saving'; +INSERT INTO `acc_gl_account` (`name`, `parent_id`, `hierarchy`, `gl_code`, `disabled`, `manual_journal_entries_allowed`, `account_usage`, `classification_enum`, `description`) +VALUES (@saving_gl_name, NULL, NULL, 'Interop_Saving', 0, 1, 1, 1, 'Interoperation Saving Asset'); -- account_usage: DETAIL, classification_enum: ASSET + +INSERT INTO `acc_product_mapping` (`gl_account_id`, `product_id`, `product_type`, `payment_type`, `charge_id`, `financial_account_type`) +VALUES ((SELECT id FROM acc_gl_account WHERE name = @saving_gl_name), @saving_prod_id, 2, @payment_type_id, NULL, 1); -- product_type: SAVING, financial_account_type: ASSET + +SET @nostro_gl_name = 'Interoperation NOSTRO'; +INSERT INTO `acc_gl_account` (`name`, `parent_id`, `hierarchy`, `gl_code`, `disabled`, `manual_journal_entries_allowed`, `account_usage`, `classification_enum`, `description`) +VALUES (@nostro_gl_name, NULL, NULL, 'Interop_Nostro', 0, 0, 1, 2, 'Interoperation NOSTRO Liability'); -- account_usage: DETAIL, classification_enum: LIABILITY + +INSERT INTO `acc_product_mapping` (`gl_account_id`, `product_id`, `product_type`, `payment_type`, `charge_id`, `financial_account_type`) +VALUES ((SELECT id FROM acc_gl_account WHERE name = @nostro_gl_name), @saving_prod_id, 2, NULL, NULL, 2); -- product_type: SAVING, financial_account_type: LIABILITY + +SET @fee_gl_name = 'Interoperation Fee'; +INSERT INTO `acc_gl_account` (`name`, `parent_id`, `hierarchy`, `gl_code`, `disabled`, `manual_journal_entries_allowed`, `account_usage`, `classification_enum`, `description`) +VALUES (@fee_gl_name, NULL, NULL, 'Interop_Fee', 0, 0, 1, 4, 'Interoperation Fee Income'); -- account_usage: DETAIL, classification_enum: INCOME + +SET @fee_gl_id = -1; +SELECT id INTO @fee_gl_id FROM acc_gl_account WHERE name = @fee_gl_name; + +INSERT INTO `acc_product_mapping` (`gl_account_id`, `product_id`, `product_type`, `payment_type`, `charge_id`, `financial_account_type`) +VALUES (@fee_gl_id, @saving_prod_id, 2, NULL, NULL, 4); -- product_type: SAVING, financial_account_type: INCOME + +SET @charge_name = 'Interoperation Withdraw Fee'; +INSERT INTO `m_charge` +(`name`,`currency_code`,`charge_applies_to_enum`,`charge_time_enum`,`charge_calculation_enum`,`charge_payment_mode_enum`, + `amount`,`fee_on_day`,`fee_interval`,`fee_on_month`,`is_penalty`,`is_active`,`is_deleted`,`min_cap`,`max_cap`,`fee_frequency`, + `income_or_liability_account_id`,`tax_group_id`) +VALUES (@charge_name, 'TZS', 2, 5, 1, NULL, 1.000000, NULL, NULL, NULL, 0, 0, 0, NULL, NULL, NULL, @fee_gl_id, NULL); + +-- loan product +/* +SET @last_ext_id = -1; +SELECT COALESCE(max(external_id), 1) INTO @last_ext_id FROM m_product_loan; + +INSERT INTO `m_product_loan` +VALUES +(CONCAT('IP', @last_product_id), 'EUR', 2, 1, 50000.000000, NULL, NULL, NULL, concat('Interoperation Customer Product', @last_product_id), + 'Demo Interoperation Product', NULL, b'0', b'0', 1.000000, 1.000000, NULL, NULL, 3, 1.000000, 0, 1, 1, 1, 2, 1200, NULL, + NULL, NULL, NULL, NULL, 1, 1, 3, @last_ext_id + 1, 0, 0,ADDDATE(curdate(),-100),ADDDATE(curdate(),100), 0, 0, NULL, NULL, + NULL, 1, 30, 0, 0, 0.00, 0, 1, 0, 0, 0); + +SET @product_id = -1; +SELECT id INTO @product_id FROM m_product_loan WHERE name = concat('Interoperation Customer Product', @last_product_id); + +-- charge, mapping +INSERT INTO `m_charge` VALUES ( + NULL, concat('Loan Withdraw Fee_', @product_id), 'TZS', 1, 2, + 1, 0, 1.000000, NULL, NULL, + NULL, 0, 1, 0, NULL, + NULL, NULL, NULL, NULL); + +INSERT INTO `m_product_loan_charge` VALUES + (@product_id, (SELECT id + FROM m_charge + WHERE name = concat('Loan Withdraw Fee_', @product_id))); + +-- gl_account, mappings +-- ASSET-1, LIABILITY-2, EQUITY-3, INCOME-4, EXPENSE-5 +SET @liab_acc_name = concat('Loan Payable Liability_', @product_id); +INSERT INTO `acc_gl_account` VALUES ( + NULL, @liab_acc_name, NULL, NULL, concat('0360009420', @product_id), + 0, 1, 1, 1, NULL, 'Loan Payable Liability'); + +INSERT INTO `acc_product_mapping` VALUES ( + NULL, + (SELECT id + FROM acc_gl_account + WHERE name = @liab_acc_name), + @product_id, + NULL, NULL, NULL, 2); + +SET @nostro_acc_name = concat('Loan NOSTRO_', @product_id); +INSERT INTO `acc_gl_account` VALUES ( + NULL, @nostro_acc_name, NULL, NULL, concat('0360009421', @product_id), + 0, 1, 1, 1, NULL, 'Loan NOSTRO'); + +INSERT INTO `acc_product_mapping` VALUES ( + NULL, + (SELECT id + FROM acc_gl_account + WHERE name = @nostro_acc_name), + @product_id, + NULL, NULL, NULL, 1); + +SET @cash_acc_name = concat('Loan Product Cash_', @product_id); +INSERT INTO `acc_gl_account` VALUES ( + NULL, @cash_acc_name, NULL, NULL, concat('0360009422', @product_id), + 0, 1, 1, 1, NULL, 'Loan Product Cash'); + +INSERT INTO `acc_product_mapping` VALUES ( + NULL, + (SELECT id + FROM acc_gl_account + WHERE name = @cash_acc_name), + @product_id, + NULL, NULL, NULL, 1); + +SET @expen_acc_name = concat('Loan Product Expenses_', @product_id); +INSERT INTO `acc_gl_account` VALUES ( + NULL, @expen_acc_name, NULL, NULL, concat('0360009423', @product_id), + 0, 1, 1, 1, NULL, 'Loan Product Expenses'); + +INSERT INTO `acc_product_mapping` VALUES ( + NULL, + (SELECT id + FROM acc_gl_account + WHERE name = @expen_acc_name), + @product_id, + NULL, NULL, NULL, 5); + +SET @accrue_acc_name = concat('Loan Product Accrue Liability_', @product_id); +INSERT INTO `acc_gl_account` VALUES ( + NULL, @accrue_acc_name, NULL, NULL, concat('0360009424', @product_id), + 0, 1, 1, 1, NULL, 'Loan Product Accrue Liability'); + +INSERT INTO `acc_product_mapping` VALUES ( + NULL, + (SELECT id + FROM acc_gl_account + WHERE name = @accrue_acc_name), + @product_id, + NULL, NULL, NULL, 2); + +SET @equ_acc_name = concat('Loan Product Equity_', @product_id); +INSERT INTO `acc_gl_account` VALUES ( + NULL, @equ_acc_name, NULL, NULL, concat('0360009425', @product_id), + 0, 1, 1, 1, NULL, 'Loan Product Equity'); + +INSERT INTO `acc_product_mapping` VALUES ( + NULL, + (SELECT id + FROM acc_gl_account + WHERE name = @equ_acc_name), + @product_id, + NULL, NULL, NULL, 3); + +SET @feer_acc_name = concat('Loan Product Fees Revenue_', @product_id); +INSERT INTO `acc_gl_account` VALUES ( + NULL, @feer_acc_name, NULL, NULL, concat('0360009426', @product_id), + 0, 1, 1, 1, NULL, 'Loan Product Fees Revenue'); + +INSERT INTO `acc_product_mapping` VALUES ( + NULL, + (SELECT id + FROM acc_gl_account + WHERE name = @feer_acc_name), + @product_id, + NULL, NULL, NULL, 4);*/ \ No newline at end of file diff --git a/fineract-provider/src/main/resources/sql/migrations/sample_data/tn03_interop_sample_data.sql b/fineract-provider/src/main/resources/sql/migrations/sample_data/tn03_interop_sample_data.sql new file mode 100644 index 00000000000..14f3fde3e6a --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/sample_data/tn03_interop_sample_data.sql @@ -0,0 +1,80 @@ +-- +-- 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. +-- + +-- To use: Change saving_account_no before every run! + +USE `tn03`; + +-- data initialization +-- user+roles + +-- client +SET @saving_account_no = '9062b90de19b43989005'; + +INSERT INTO `m_client` (`account_no`, `external_id`, `status_enum`, `sub_status`, `activation_date`, `office_joining_date`, + `office_id`, `transfer_to_office_id`, `staff_id`, `firstname`, `middlename`, `lastname`, `fullname`, + `display_name`, `mobile_no`, `gender_cv_id`, `date_of_birth`, `image_id`, `closure_reason_cv_id`, + `closedon_date`, `updated_by`, `updated_on`, `submittedon_date`, `submittedon_userid`, `activatedon_userid`, + `closedon_userid`, `default_savings_product`, `default_savings_account`, `client_type_cv_id`, `client_classification_cv_id`, + `reject_reason_cv_id`, `rejectedon_date`, `rejectedon_userid`, `withdraw_reason_cv_id`, `withdrawn_on_date`, + `withdraw_on_userid`, `reactivated_on_date`, `reactivated_on_userid`, `legal_form_enum`, `reopened_on_date`, + `reopened_by_userid`) +VALUES (@saving_account_no, NULL, 300, NULL, ADDDATE(curdate(), -100), NULL, 1, NULL, NULL, NULL, NULL, NULL, + 'InteropCustomer', 'InteropCustomer', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ADDDATE(curdate(), -100), + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL, NULL); + +-- saving product, account +SET @saving_prod_name = concat('Saving Product', @last_saving_prod_id); +SET @saving_prod_id = -1; +SELECT id INTO @saving_prod_id FROM m_savings_product WHERE name = @saving_prod_name; + +SET @client_id = -1; +SELECT id INTO @client_id FROM m_client WHERE fullname = 'InteropCustomer'; + +INSERT INTO `m_savings_account` +(`account_no`, `external_id`, `client_id`, `group_id`, `product_id`, `field_officer_id`, `status_enum`, + `sub_status_enum`, `account_type_enum`, `deposit_type_enum`, `submittedon_date`, `submittedon_userid`, + `approvedon_date`, `approvedon_userid`, `activatedon_date`, `activatedon_userid`, + `currency_code`, `currency_digits`, `currency_multiplesof`, `nominal_annual_interest_rate`, + `interest_compounding_period_enum`, `interest_posting_period_enum`, `interest_calculation_type_enum`, + `interest_calculation_days_in_year_type_enum`, `min_required_opening_balance`, `withdrawal_fee_for_transfer`, + `allow_overdraft`, `account_balance_derived`, `min_required_balance`, `enforce_min_required_balance`, + `version`, `withhold_tax`) +VALUES (@saving_account_no, '9062b90de19b43989005d9', @client_id, NULL, @saving_prod_id, NULL, 300, 0, 1, 100, ADDDATE(curdate(), -100), + NULL, ADDDATE(curdate(), -100), NULL, ADDDATE(curdate(), -100), NULL, 'TZS', 2, NULL, 1.000000, 1, 4, 1, -- 29. - 4 + 360, NULL, 1, 1, 100000000.000000, 0.000000, 1, 1, 0); + +-- interop_identifier +SET @saving_acc_id = -1; +SELECT id INTO @saving_acc_id FROM m_savings_account WHERE account_no = @saving_account_no; + +INSERT INTO interop_identifier (id, account_id, type, a_value, sub_value_or_type, created_by, created_on, modified_by, modified_on) +VALUES (NULL, @saving_acc_id, 'IBAN', 'IC11in02tn039062b90de19b43989005d9', NULL, 'operator', CURDATE(), 'operator', + CURDATE()); +INSERT INTO interop_identifier (id, account_id, type, a_value, sub_value_or_type, created_by, created_on, modified_by, modified_on) +VALUES (NULL, @saving_acc_id, 'MSISDN', '27710203999', NULL, 'operator', CURDATE(), 'operator', CURDATE()); + +-- charge, mapping +-- gl_account, mappings +-- ASSET-1, LIABILITY-2, EQUITY-3, INCOME-4, EXPENSE-5 +SET @charge_name = 'Interoperation Withdraw Fee'; + +INSERT INTO `m_savings_account_charge` (`savings_account_id`, `charge_id`, `is_penalty`, `charge_time_enum`, `charge_calculation_enum`, + `amount`, `amount_outstanding_derived`,`is_paid_derived`, `waived`, `is_active`) +VALUES (@saving_acc_id, (SELECT id FROM m_charge WHERE name = @charge_name), 0, 5, 1, 1.000000, 0.000000, 0, 0, 1); diff --git a/fineract-provider/src/main/resources/sql/migrations/sample_data/tn04_interop_sample_data.sql b/fineract-provider/src/main/resources/sql/migrations/sample_data/tn04_interop_sample_data.sql new file mode 100644 index 00000000000..4135d701e18 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/sample_data/tn04_interop_sample_data.sql @@ -0,0 +1,80 @@ +-- +-- 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. +-- + +-- To use: Change saving_account_no before every run! + +USE `tn04`; + +-- data initialization +-- user+roles + +-- client +SET @saving_account_no = '9062b90de19b43989005'; + +INSERT INTO `m_client` (`account_no`, `external_id`, `status_enum`, `sub_status`, `activation_date`, `office_joining_date`, + `office_id`, `transfer_to_office_id`, `staff_id`, `firstname`, `middlename`, `lastname`, `fullname`, + `display_name`, `mobile_no`, `gender_cv_id`, `date_of_birth`, `image_id`, `closure_reason_cv_id`, + `closedon_date`, `updated_by`, `updated_on`, `submittedon_date`, `submittedon_userid`, `activatedon_userid`, + `closedon_userid`, `default_savings_product`, `default_savings_account`, `client_type_cv_id`, `client_classification_cv_id`, + `reject_reason_cv_id`, `rejectedon_date`, `rejectedon_userid`, `withdraw_reason_cv_id`, `withdrawn_on_date`, + `withdraw_on_userid`, `reactivated_on_date`, `reactivated_on_userid`, `legal_form_enum`, `reopened_on_date`, + `reopened_by_userid`) +VALUES (@saving_account_no, NULL, 300, NULL, ADDDATE(curdate(), -100), NULL, 1, NULL, NULL, NULL, NULL, NULL, + 'InteropMerchant', 'InteropMerchant', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ADDDATE(curdate(), -100), + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL, NULL); + +-- saving product, account +SET @saving_prod_name = concat('Saving Product', @last_saving_prod_id); +SET @saving_prod_id = -1; +SELECT id INTO @saving_prod_id FROM m_savings_product WHERE name = @saving_prod_name; + +SET @client_id = -1; +SELECT id INTO @client_id FROM m_client WHERE fullname = 'InteropMerchant'; + +INSERT INTO `m_savings_account` +(`account_no`, `external_id`, `client_id`, `group_id`, `product_id`, `field_officer_id`, `status_enum`, + `sub_status_enum`, `account_type_enum`, `deposit_type_enum`, `submittedon_date`, `submittedon_userid`, + `approvedon_date`, `approvedon_userid`, `activatedon_date`, `activatedon_userid`, + `currency_code`, `currency_digits`, `currency_multiplesof`, `nominal_annual_interest_rate`, + `interest_compounding_period_enum`, `interest_posting_period_enum`, `interest_calculation_type_enum`, + `interest_calculation_days_in_year_type_enum`, `min_required_opening_balance`, `withdrawal_fee_for_transfer`, + `allow_overdraft`, `account_balance_derived`, `min_required_balance`, `enforce_min_required_balance`, + `version`, `withhold_tax`) +VALUES (@saving_account_no, '9062b90de19b43989005d9', @client_id, NULL, @saving_prod_id, NULL, 300, 0, 1, 100, ADDDATE(curdate(), -100), + NULL, ADDDATE(curdate(), -100), NULL, ADDDATE(curdate(), -100), NULL, 'TZS', 2, NULL, 1.000000, 1, 4, 1, -- 29. - 4 + 360, NULL, 1, 1, 100000000.000000, 0.000000, 1, 1, 0); + +-- interop_identifier +SET @saving_acc_id = -1; +SELECT id INTO @saving_acc_id FROM m_savings_account WHERE account_no = @saving_account_no; + +INSERT INTO interop_identifier (id, account_id, type, a_value, sub_value_or_type, created_by, created_on, modified_by, modified_on) +VALUES (NULL, @saving_acc_id, 'IBAN', 'IC11in02tn049062b90de19b43989005d9', NULL, 'operator', CURDATE(), 'operator', + CURDATE()); +INSERT INTO interop_identifier (id, account_id, type, a_value, sub_value_or_type, created_by, created_on, modified_by, modified_on) +VALUES (NULL, @saving_acc_id, 'MSISDN', '27710204999', NULL, 'operator', CURDATE(), 'operator', CURDATE()); + +-- charge, mapping +-- gl_account, mappings +-- ASSET-1, LIABILITY-2, EQUITY-3, INCOME-4, EXPENSE-5 +SET @charge_name = 'Interoperation Withdraw Fee'; + +INSERT INTO `m_savings_account_charge` (`savings_account_id`, `charge_id`, `is_penalty`, `charge_time_enum`, `charge_calculation_enum`, + `amount`, `amount_outstanding_derived`,`is_paid_derived`, `waived`, `is_active`) +VALUES (@saving_acc_id, (SELECT id FROM m_charge WHERE name = @charge_name), 0, 5, 1, 1.000000, 0.000000, 0, 0, 1); From 9957532039e7d3ad83772c3d96607323f76bc715 Mon Sep 17 00:00:00 2001 From: Marti Date: Fri, 8 Mar 2019 12:04:15 +0100 Subject: [PATCH 3/5] interoperable - framework changes --- .../core/data/CommandProcessingResult.java | 2 +- .../core/data/DataValidatorBuilder.java | 12 ++++++ .../core/serialization/FromJsonHelper.java | 22 +++++++--- .../core/serialization/JsonParserHelper.java | 18 ++++++-- .../monetary/domain/MonetaryCurrency.java | 5 +++ .../monetary/domain/MoneyHelper.java | 12 +++++- .../paymentdetail/domain/PaymentDetail.java | 8 +++- .../savings/domain/SavingsAccount.java | 43 ++++++++++++++++++- .../savings/domain/SavingsAccountCharge.java | 11 +++-- .../domain/SavingsAccountRepository.java | 2 + .../domain/SavingsAccountTransaction.java | 2 +- .../savings/domain/SavingsHelper.java | 6 ++- .../SavingsAccountNotFoundException.java | 4 ++ 13 files changed, 127 insertions(+), 20 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResult.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResult.java index d2754c21234..7fd07a54ce0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResult.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/CommandProcessingResult.java @@ -118,7 +118,7 @@ private CommandProcessingResult(final Long commandId, final Long officeId, final this.subResourceId = subResourceId; } - private CommandProcessingResult(final Long resourceId, final Long officeId, final Long commandId, final Map changesOnly) { + protected CommandProcessingResult(final Long resourceId, final Long officeId, final Long commandId, final Map changesOnly) { if (resourceId != null) { this.resourceIdentifier = resourceId.toString(); } else { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/DataValidatorBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/DataValidatorBuilder.java index 1ee13616370..8191cbc9bc9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/DataValidatorBuilder.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/data/DataValidatorBuilder.java @@ -51,6 +51,18 @@ public DataValidatorBuilder reset() { return new DataValidatorBuilder(this.dataValidationErrors).resource(this.resource); } + public void merge(DataValidatorBuilder other) { + dataValidationErrors.addAll(other.dataValidationErrors); + } + + public boolean hasError() { + return !dataValidationErrors.isEmpty(); + } + + public List getDataValidationErrors() { + return dataValidationErrors; + } + public DataValidatorBuilder resource(final String resource) { this.resource = resource; return this; diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/FromJsonHelper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/FromJsonHelper.java index 94eb77cee2c..3718f2e6176 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/FromJsonHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/FromJsonHelper.java @@ -22,6 +22,7 @@ import java.math.BigDecimal; import java.security.InvalidParameterException; import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -82,7 +83,7 @@ public String toJson(final Object object) { return this.gsonConverter.toJson(object); } - public void checkForUnsupportedParameters(final Type typeOfMap, final String json, final Set supportedParams) { + public void checkForUnsupportedParameters(final Type typeOfMap, final String json, final Collection supportedParams) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Map requestMap = this.gsonConverter.fromJson(json, typeOfMap); @@ -97,7 +98,7 @@ public void checkForUnsupportedParameters(final Type typeOfMap, final String jso if (!unsupportedParameterList.isEmpty()) { throw new UnsupportedParameterException(unsupportedParameterList); } } - public void checkForUnsupportedParameters(final JsonObject object, final Set supportedParams) { + public void checkForUnsupportedParameters(final JsonObject object, final Collection supportedParams) { if (object == null) { throw new InvalidParameterException(); } final Set> entries = object.entrySet(); @@ -196,17 +197,26 @@ public MonthDay extractMonthDayNamed(final String parameterName, final JsonObjec } public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element) { - return this.helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet()); + return this.helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet<>()); } public LocalDateTime extractLocalTimeNamed(final String parameterName, final JsonElement element) { - return this.helperDelegator.extractLocalTimeNamed(parameterName, element, new HashSet()); + return this.helperDelegator.extractLocalTimeNamed(parameterName, element, new HashSet<>()); } - + + public LocalDateTime extractLocalTimeNamed(final String parameterName, final JsonElement element, final String dateFormat, + final Locale locale) { + return this.helperDelegator.extractLocalTimeNamed(parameterName, element, dateFormat, locale, new HashSet<>()); + } + + public LocalDateTime extractLocalTimeNamed(final String parameterName, final JsonElement element, String timeFormat) { + return this.helperDelegator.extractLocalTimeNamed(parameterName, element, timeFormat, new HashSet<>()); + } + public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element, final String dateFormat, final Locale locale) { return this.helperDelegator.extractLocalDateNamed(parameterName, element.getAsJsonObject(), dateFormat, locale, - new HashSet()); + new HashSet<>()); } public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element, diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java index a3e489a9d83..460a05a4e19 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/serialization/JsonParserHelper.java @@ -378,15 +378,25 @@ public LocalDateTime extractLocalTimeNamed(final String parameterName, final Jso if (element.isJsonObject()) { final JsonObject object = element.getAsJsonObject(); + value = extractLocalTimeNamed(parameterName, element, extractTimeFormatParameter(object), parametersPassedInCommand); + } + return value; + } - final String timeFormat = extractTimeFormatParameter(object); + public LocalDateTime extractLocalTimeNamed(final String parameterName, final JsonElement element, String timeFormat, + final Set parametersPassedInCommand) { + + LocalDateTime value = null; + + if (element.isJsonObject()) { + final JsonObject object = element.getAsJsonObject(); final Locale clientApplicationLocale = extractLocaleParameter(object); value = extractLocalTimeNamed(parameterName, object, timeFormat, clientApplicationLocale, parametersPassedInCommand); } return value; } - - public LocalDateTime extractLocalTimeNamed(final String parameterName, final JsonObject element, final String timeFormat, + + public LocalDateTime extractLocalTimeNamed(final String parameterName, final JsonElement element, final String timeFormat, final Locale clientApplicationLocale, final Set parametersPassedInCommand) { LocalDateTime value = null; String timeValueAsString=null; @@ -419,7 +429,7 @@ public LocalDateTime extractLocalTimeNamed(final String parameterName, final Jso return value; } - public LocalDate extractLocalDateNamed(final String parameterName, final JsonObject element, final String dateFormat, + public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element, final String dateFormat, final Locale clientApplicationLocale, final Set parametersPassedInCommand) { LocalDate value = null; if (element.isJsonObject()) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java index d91ebc47b9c..f5d0f522118 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MonetaryCurrency.java @@ -49,6 +49,11 @@ public MonetaryCurrency copy() { return new MonetaryCurrency(this.code, this.digitsAfterDecimal, this.inMultiplesOf); } + public static MonetaryCurrency fromApplicationCurrency(ApplicationCurrency applicationCurrency) { + return new MonetaryCurrency(applicationCurrency.getCode(), applicationCurrency.getDecimalPlaces(), + applicationCurrency.getCurrencyInMultiplesOf()); + } + public String getCode() { return this.code; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MoneyHelper.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MoneyHelper.java index 1d370d3d65e..678cb612827 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MoneyHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/monetary/domain/MoneyHelper.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.organisation.monetary.domain; +import java.math.MathContext; import java.math.RoundingMode; import javax.annotation.PostConstruct; @@ -30,8 +31,11 @@ public class MoneyHelper { private static RoundingMode roundingMode = null; + private static MathContext mathContext; + private static final int PRECISION = 12; + private static ConfigurationDomainService staticConfigurationDomainService; - + @Autowired private ConfigurationDomainService configurationDomainService; @@ -48,4 +52,10 @@ public static RoundingMode getRoundingMode() { return roundingMode; } + public static MathContext getMathContext() { + if (mathContext == null) { + mathContext = new MathContext(PRECISION, getRoundingMode()); + } + return mathContext; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/domain/PaymentDetail.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/domain/PaymentDetail.java index be390118d45..c290221ca58 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/domain/PaymentDetail.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/domain/PaymentDetail.java @@ -115,5 +115,11 @@ public PaymentType getPaymentType() { return this.paymentType; } - public String getReceiptNumber() { return this.receiptNumber; } + public String getReceiptNumber() { + return this.receiptNumber; + } + + public String getRoutingCode() { + return routingCode; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java index 3def00ae571..906c6774da8 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java @@ -81,6 +81,7 @@ import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; +import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.staff.domain.Staff; import org.apache.fineract.portfolio.accountdetails.domain.AccountType; @@ -437,6 +438,10 @@ public void setHelpers(final SavingsAccountTransactionSummaryWrapper savingsAcco this.savingsHelper = savingsHelper; } + public String getExternalId() { + return externalId; + } + public boolean isNotActive() { return !isActive(); } @@ -1023,10 +1028,22 @@ public SavingsAccountTransaction withdraw(final SavingsAccountTransactionDTO tra return transaction; } - private void payWithdrawalFee(final BigDecimal transactionAmoount, final LocalDate transactionDate, final AppUser user) { + public BigDecimal calculateWithdrawalFee(final BigDecimal transactionAmount) { + BigDecimal result = BigDecimal.ZERO; + if (isWithdrawalFeeApplicableForTransfer()) { + for (SavingsAccountCharge charge : this.charges()) { + if (charge.isWithdrawalFee() && charge.isActive()) { + result = result.add(charge.calculateWithdralFeeAmount(transactionAmount), MoneyHelper.getMathContext()); + } + } + } + return result; + } + + private void payWithdrawalFee(final BigDecimal transactionAmount, final LocalDate transactionDate, final AppUser user) { for (SavingsAccountCharge charge : this.charges()) { if (charge.isWithdrawalFee() && charge.isActive()) { - charge.updateWithdralFeeAmount(transactionAmoount); + charge.updateWithdralFeeAmount(transactionAmount); this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate, user); } } @@ -2712,6 +2729,28 @@ public boolean isTransactionsAllowed() { return isActive(); } + public boolean isTransactionAllowed(SavingsAccountTransactionType transactionType, LocalDate transactionDate) { + if (!isTransactionsAllowed()) + return false; + + Client client = getClient(); + if (client != null && !client.isActive()) { + return false; + } + Group group = group(); + if (group != null && !group.isActive()) { + return false; + } + + if (transactionDate == null) + return true; + if (DateUtils.isDateInTheFuture(transactionDate) || transactionDate.isBefore(getActivationLocalDate())) + return false; + if (transactionType.isCredit()) + return true; + return !isAccountLocked(transactionDate); + } + public BigDecimal minBalanceForInterestCalculation() { return this.minBalanceForInterestCalculation; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java index 2ef69a5d81b..3be7c47111f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountCharge.java @@ -38,11 +38,13 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import javax.validation.constraints.NotNull; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; +import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; @@ -704,17 +706,20 @@ public boolean equals(final Object obj) { .isEquals(); } - public BigDecimal updateWithdralFeeAmount(final BigDecimal transactionAmount) { + public BigDecimal calculateWithdralFeeAmount(@NotNull BigDecimal transactionAmount) { BigDecimal amountPaybale = BigDecimal.ZERO; if (ChargeCalculationType.fromInt(this.chargeCalculation).isFlat()) { amountPaybale = this.amount; } else if (ChargeCalculationType.fromInt(this.chargeCalculation).isPercentageOfAmount()) { - amountPaybale = transactionAmount.multiply(this.percentage).divide(BigDecimal.valueOf(100l)); + amountPaybale = transactionAmount.multiply(this.percentage).divide(BigDecimal.valueOf(100L), MoneyHelper.getRoundingMode()); } - this.amountOutstanding = amountPaybale; return amountPaybale; } + public BigDecimal updateWithdralFeeAmount(final BigDecimal transactionAmount) { + return amountOutstanding = calculateWithdralFeeAmount(transactionAmount); + } + public void updateToNextDueDateFrom(final LocalDate startingDate) { if (isAnnualFee() || isMonthlyFee() || isWeeklyFee()) { this.dueDate = getNextDueDateFrom(startingDate).toDate(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepository.java index df939f672e1..e5f6d099d65 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountRepository.java @@ -49,4 +49,6 @@ SavingsAccount findByIdAndDepositAccountType(@Param("accountId") Long accountId, @Query("select sa from SavingsAccount sa where sa.accountNumber = :accountNumber and sa.status in (100, 200, 300, 303, 304) ") SavingsAccount findNonClosedAccountByAccountNumber(@Param("accountNumber") String accountNumber); + + SavingsAccount findByExternalId(String externalId); } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java index 2f25e602ad3..0d601a45edc 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccountTransaction.java @@ -71,7 +71,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom Date: Fri, 8 Mar 2019 12:06:24 +0100 Subject: [PATCH 4/5] interoperable - general build - not necessary for interoperable --- fineract-provider/build.gradle | 332 ++++-------------- fineract-provider/dependencies.gradle | 38 +- fineract-provider/dev-dependencies.gradle | 33 +- .../gradle/wrapper/gradle-wrapper.properties | 3 +- fineract-provider/gradlew | 173 +++++++++ fineract-provider/gradlew.bat | 84 +++++ .../basicauth/application.properties | 13 +- .../properties/oauth/application.properties | 13 +- .../apache/fineract/ServerApplication.java | 54 ++- .../core/boot/db/DataSourceConfiguration.java | 24 +- .../core/boot/db/DataSourceProperties.java | 136 ++----- .../core/boot/db/JdbcDriverConfig.java | 87 +++++ .../db/TenantDataSourcePortFixService.java | 44 ++- .../FineractPlatformTenantConnection.java | 8 - .../service/TenantDatabaseUpgradeService.java | 5 +- .../TomcatJdbcDataSourcePerTenantService.java | 4 +- .../META-INF/spring/infrastructure.xml | 14 +- .../META-INF/spring/securityContext.xml | 36 +- .../src/main/resources/application.properties | 13 +- 19 files changed, 572 insertions(+), 542 deletions(-) create mode 100644 fineract-provider/gradlew create mode 100644 fineract-provider/gradlew.bat create mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/JdbcDriverConfig.java diff --git a/fineract-provider/build.gradle b/fineract-provider/build.gradle index 2ddf09c36b8..94f60a44d07 100644 --- a/fineract-provider/build.gradle +++ b/fineract-provider/build.gradle @@ -16,10 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -description = '''\ -Run as: -gradle clean tomcatrunwar -''' buildscript { repositories { jcenter() @@ -31,10 +27,11 @@ buildscript { classpath 'org.gradle.api.plugins:gradle-tomcat-plugin:1.0', 'nl.javadude.gradle.plugins:license-gradle-plugin:0.11.0', 'org.zeroturnaround:gradle-jrebel-plugin:1.1.2', - 'org.springframework.boot:spring-boot-gradle-plugin:1.1.6.RELEASE' // also change springDataJpaVersion below + 'org.springframework.boot:spring-boot-gradle-plugin:1.1.6.RELEASE' classpath 'org.apache.openjpa:openjpa-all:2.4.1' classpath 'at.schmutterer.oss.gradle:gradle-openjpa:0.2.0' classpath 'gradle.plugin.org.nosphere.apache:creadur-rat-gradle:0.2.2' + classpath 'org.drizzle.jdbc:drizzle-jdbc:1.3' } } @@ -45,39 +42,57 @@ apply plugin: 'war' apply plugin: 'spring-boot' apply plugin: 'eclipse' apply plugin: 'idea' -apply plugin: 'tomcat' apply plugin: 'project-report' apply plugin: 'java' apply plugin: 'openjpa' -apply plugin: 'pmd' -apply plugin: 'findbugs' -/* define the valid syntax level for source files */ +group = 'org.apache.fineract' +buildDir = new File(rootProject.projectDir, "../build") + sourceCompatibility = JavaVersion.VERSION_1_8 -/* define binary compatibility version */ targetCompatibility = JavaVersion.VERSION_1_8 project.ext.springBootVersion = '1.1.6.RELEASE' project.ext.springVersion = '4.0.7.RELEASE' project.ext.springOauthVersion = '2.0.4.RELEASE' project.ext.jerseyVersion = '1.17' -project.ext.springDataJpaVersion = '1.7.0.RELEASE' // also change spring-boot-gradle-plugin version above - +project.ext.springDataJpaVersion = '1.7.0.RELEASE' project.ext.mysqlUser='root' project.ext.mysqlPassword='mysql' - -group = 'org.apache.fineract' -buildDir = new File(rootProject.projectDir, "../build") repositories { jcenter() - // mavenLocal() // useful for local dev using MariaDB4j SNAPSHOTs (not needed for real-world non-SNAPHOT builds) + mavenCentral() +} + +dependencies { + compile( + [group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion], + [group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: springBootVersion], + [group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion], + [group: 'org.springframework', name: 'spring-context-support', version: springVersion], + [group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: springOauthVersion]) +} + +springBoot { + mainClass = 'org.apache.fineract.ServerApplication' +} + +bootRepackage { + mainClass = 'org.apache.fineract.ServerApplication' } + +if (project.hasProperty('env') && project.getProperty('env') == 'dev') { + bootRun { + jvmArgs = ['-agentlib:jdwp=transport=dt_socket,server=y,address=5006,suspend=n'] + } +} + openjpa { files = fileTree(sourceSets.main.output.classesDir).matching { include '**/AbstractPersistableCustom.class' include '**/domain/*.class' - + } enforcePropertyRestrictions = true } @@ -86,8 +101,7 @@ rat { xmlOutput = false htmlOutput = false plainOutput = true - verbose = false -// inputDir = './..' + verbose = false reportDir = new File(buildDir,'reports/rat') excludes = [ '**/licenses/**', @@ -155,23 +169,34 @@ rat { '**/assets/html5shiv.js', //BSD License - '**/assets/uglify.js' - ] + '**/assets/uglify.js', + + '**/*.css', + '**/*.js', + '**/*.json', + '**/*Swagger.java', + '**/*.ipr', + '**/*.iws', + '**/*.html', + '**/*.map', + '**/terraform/**', + '**/app/**', + ] } + configurations { - providedRuntime // needed for Spring Boot executable WAR - providedCompile compile() { exclude module: 'hibernate-entitymanager' exclude module: 'hibernate-validator' + exclude module: 'hibernate-core' exclude module: 'activation' exclude module: 'bcmail-jdk14' exclude module: 'bcprov-jdk14' exclude module: 'bctsp-jdk14' exclude module: 'bval-core' - exclude module: 'org.apache.bval.bundle' exclude module: 'bval-jsr303' + exclude module: 'org.apache.bval.bundle' exclude module: 'c3p0' exclude module: 'stax-api' exclude module: 'jaxb-api' @@ -179,21 +204,16 @@ configurations { exclude module: 'jboss-logging' exclude module: 'itext-rtf' exclude module: 'classworlds' - exclude module: 'jcl-over-slf4j' - exclude module: 'jul-to-slf4j' exclude module: 'serp' } - runtime - all*.exclude group: 'commons-logging' } -/* Pick up dependencies based on the environemnt, defaults to production */ + if (project.hasProperty('env') && project.getProperty('env') == 'dev') { apply from: 'dev-dependencies.gradle' } else { apply from: 'dependencies.gradle' } -/* Enable Oauth2 authentication based on environment, default to HTTP basic auth */ if (project.hasProperty('security') && project.getProperty('security') == 'oauth') { if(project.hasProperty('twofactor') && project.getProperty('twofactor') == 'enabled') { copy { @@ -226,7 +246,7 @@ if (project.hasProperty('security') && project.getProperty('security') == 'oauth task dist(type:Zip){ baseName = 'fineractplatform' - version = qualifyVersionIfNecessary(releaseVersion) + version = "${version}" includeEmptyDirs = true from('../') { fileMode = 0755 @@ -264,14 +284,6 @@ task dist(type:Zip){ compileJava{ dependsOn rat } -pmd { - sourceSets = [sourceSets.main] - ignoreFailures = true -} -findbugs { - ignoreFailures = true - sourceSets = [sourceSets.main] -} war { from('../licenses/binary/') { // notice the parens @@ -294,8 +306,21 @@ war { license { header rootProject.file('../APACHE_LICENSETEXT.md') - excludes(["**/*.html", "**/*.mustache", "**/package-info.java", "**/keystore.jks"]) - strictCheck true + excludes([ + "**/*.html", + "**/*.mustache", + "**/package-info.java", + "**/keystore.jks", + '**/*.css', + '**/*.js', + '**/*.json', + '**/*Swagger.java', + '**/*.ipr', + '**/*.iws', + '**/*.html', + '**/*.map' + ]) + strictCheck false } task licenseFormatBuildScripts (type:nl.javadude.gradle.plugins.license.License) { @@ -303,234 +328,11 @@ task licenseFormatBuildScripts (type:nl.javadude.gradle.plugins.license.License) } licenseFormat.dependsOn licenseFormatBuildScripts -tomcatRun { - httpPort = 8080 - httpsPort = 8443 - stopPort = 8081 - stopKey= 'stopKey' - enableSSL = true - configFile = file('src/test/resources/META-INF/context.xml') -} - -tomcatRunWar { - httpPort = 8080 - httpsPort = 8443 - stopPort = 8081 - stopKey= 'stopKey' - enableSSL = true - keystoreFile = file('src/main/resources/keystore.jks') - keystorePass = 'openmf' - configFile = file('src/test/resources/META-INF/context.xml') -} - -/* http://stackoverflow.com/questions/19653311/jpa-repository-works-in-idea-and-production-but-not-in-gradle */ sourceSets.main.output.resourcesDir = sourceSets.main.output.classesDir sourceSets.test.output.resourcesDir = sourceSets.test.output.classesDir -/* Exclude maria db and embedded tomcat related files for non dev builds */ -if (!(project.hasProperty('env') && project.getProperty('env') == 'dev')) { -sourceSets { - main { - java { - exclude '**/Server*' - exclude '**/MariaDB4j*' - exclude '**/EmbeddedTomcatWithSSLConfiguration.java' - } - } - test { - java { - exclude '**/core/boot/tests/**' - } - } -} -} - -sourceSets { - integrationTest { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output - } -} - -configurations { - integrationTestCompile.extendsFrom testCompile - integrationTestRuntime.extendsFrom testRuntime -} - -task integrationTest(type:Test){ - description = "Run integration tests (located in src/integrationTest/java). Starts tomcat in daemon mode before executing the tests." - it.dependsOn war - doFirst { - tomcatRunWar.daemon = true - tomcatRunWar.execute() - } - testClassesDir = project.sourceSets.integrationTest.output.classesDir - classpath = project.sourceSets.integrationTest.runtimeClasspath -} - - -import groovy.sql.Sql - -repositories { - mavenCentral() -} -configurations { - driver -} -dependencies { - driver 'org.drizzle.jdbc:drizzle-jdbc:1.3' -} - test { filter { - //includeTestsMatching "org.apache.fineract.infrastructure.configuration.spring.SpringConfigurationTest.*" includeTestsMatching "org.apache.fineract.template.TemplateMergeServiceTest.*" } -} - -URLClassLoader loader = GroovyObject.class.classLoader -configurations.driver.each {File file -> - loader.addURL(file.toURL()) -} - -task createDB<<{ - description= "Creates the Database. Needs database name to be passed (like: -PdbName=someDBname)" - sql = Sql.newInstance( 'jdbc:mysql:thin://localhost:3306/', mysqlUser, mysqlPassword, 'org.drizzle.jdbc.DrizzleDriver' ) - sql.execute( 'create database '+"`$dbName`" ) -} - -task dropDB<<{ - description= "Drops the specified database. The database name has to be passed (like: -PdbName=someDBname)" - sql = Sql.newInstance( 'jdbc:mysql:thin://localhost:3306/', mysqlUser, mysqlPassword, 'org.drizzle.jdbc.DrizzleDriver' ) - sql.execute( 'DROP DATABASE '+"`$dbName`") -} -task setBlankPassword<<{ - sql = Sql.newInstance( 'jdbc:mysql:thin://localhost:3306/', mysqlUser, mysqlPassword, 'org.drizzle.jdbc.DrizzleDriver' ) - sql.execute('USE `mifosplatform-tenants`') - sql.execute('UPDATE mifosplatform-tenants.tenants SET schema_server = \'localhost\', schema_server_port = \'3306\', schema_username = \'mifos\', schema_password = \'mysql\' WHERE id=1;') -} - - -apply plugin: 'flyway' -buildscript { - repositories { - mavenCentral() - } - - dependencies { - classpath "org.flywaydb:flyway-gradle-plugin:3.0" // version upgraded during Spring Boot & MariaDB4j work, as prev. used v0.2 didn't work well after *.sql moved from fineract-db to fineract-provider/src/main/resources (new version also has clearer errors, e.g. in case of missing DB) - classpath 'org.drizzle.jdbc:drizzle-jdbc:1.3' - } -} - - -flyway { - url = "jdbc:mysql:thin://localhost:3306/mifostenant-default" - driver = "org.drizzle.jdbc.DrizzleDriver" - user = mysqlUser - password = mysqlPassword -} - -task migrateTenantDB<<{ - description="Migrates a Tenant DB. Optionally can pass dbName. Defaults to 'mifostenant-default' (Example: -PdbName=someTenantDBname)" - - def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/core_db' - def tenantDbName = 'mifostenant-default'; - if (rootProject.hasProperty("dbName")) { - tenantDbName = rootProject.getProperty("dbName") - } - - flyway.url= "jdbc:mysql:thin://localhost:3306/$tenantDbName" - flyway.driver = "org.drizzle.jdbc.DrizzleDriver" - flyway.locations= [filePath] - /**We use ${ as the prefix for strecthy reporting, do not want them to be interpreted by Flyway**/ - flyway.placeholderPrefix = "\$\${" - flywayMigrate.execute() -} - -task showTenantDBInfo<<{ - description="Shows the migration info for a Tenant DB. Optionally can pass dbName. Defaults to 'mifostenant-default' (Example: -PdbName=someTenantDBname)" - - def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/core_db' - def tenantDbName = 'mifostenant-default'; - if (rootProject.hasProperty("dbName")) { - tenantDbName = rootProject.getProperty("dbName") - } - - flyway.url= "jdbc:mysql:thin://localhost:3306/$tenantDbName" - flyway.driver = "org.drizzle.jdbc.DrizzleDriver" - flyway.locations= [filePath] - flywayInfo.execute() -} - - -task migrateTenantListDB<<{ - description="Migrates a Tenant List DB. Optionally can pass dbName. Defaults to 'mifosplatform-tenants' (Example: -PdbName=someDBname)" - - def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/list_db' - def tenantsDbName = 'mifosplatform-tenants'; - if (rootProject.hasProperty("dbName")) { - tenantsDbName = rootProject.getProperty("dbName") - } - - flyway.url= "jdbc:mysql:thin://localhost:3306/$tenantsDbName" - flyway.driver = "org.drizzle.jdbc.DrizzleDriver" - flyway.locations= [filePath] - - flywayMigrate.execute() -} - -task showTenantListDBInfo<<{ - description="Shows the migration info for a List DB. Optionally can pass dbName. Defaults to 'mifosplatform-tenants' (Example: -PdbName=someDBname)" - - def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/list_db' - def tenantsDbName = 'mifosplatform-tenants'; - if (rootProject.hasProperty("dbName")) { - tenantsDbName = rootProject.getProperty("dbName") - } - - flyway.url= "jdbc:mysql:thin://localhost:3306/$tenantsDbName" - flyway.driver = "org.drizzle.jdbc.DrizzleDriver" - flyway.locations= [filePath] - flywayInfo.execute() -} - -task repairTenantDB<<{ - description="repair migrate" - - def filePath = "filesystem:$projectDir" + '/src/main/resources/sql/migrations/list_db' - def tenantsDbName = 'mifosplatform-tenants'; - if (rootProject.hasProperty("dbName")) { - tenantsDbName = rootProject.getProperty("dbName") - } - - flyway.url= "jdbc:mysql:thin://localhost:3306/$tenantsDbName" - flyway.driver = "org.drizzle.jdbc.DrizzleDriver" - flyway.locations= [filePath] - flywayRepair.execute() -} - -/* -* Support publication of artifacts versioned by topic branch. -* CI builds supply `-P BRANCH_NAME=` to gradle at build time. -* If starts with 'MIFOSX-', change version -* from BUILD-SNAPSHOT => -SNAPSHOT -* e.g. 1.1.0.BUILD-SNAPSHOT => 1.0.0.MIFOSX-1234-SNAPSHOT -*/ -def qualifyVersionIfNecessary(version) { - - if (rootProject.hasProperty("BRANCH_NAME")) { - def qualifier = rootProject.getProperty("BRANCH_NAME") - if (qualifier.startsWith("MIFOSX-")) { - return version.replace('BUILD', qualifier) - } - } - return version -} - -springBoot { - mainClass = 'org.apache.fineract.ServerWithMariaDB4jApplication' -} -bootRepackage { - mainClass = 'org.apache.fineract.ServerWithMariaDB4jApplication' -} +} \ No newline at end of file diff --git a/fineract-provider/dependencies.gradle b/fineract-provider/dependencies.gradle index 3ceb54df6ea..4a74424702b 100644 --- a/fineract-provider/dependencies.gradle +++ b/fineract-provider/dependencies.gradle @@ -17,34 +17,9 @@ * under the License. */ dependencies { - def tomcatVersion = '7.0.54' - tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}", - "org.apache.tomcat.embed:tomcat-embed-logging-log4j:${tomcatVersion}" // NOT tomcat-embed-logging-juli (http://stackoverflow.com/questions/23963049/classcircularityerror-java-util-logging-logrecord-running-gradle-webapp-with-ja) - tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") { - exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj' - } - tomcat "org.apache.tomcat:tomcat-dbcp:${tomcatVersion}" - - providedRuntime("org.springframework.boot:spring-boot-starter-tomcat") - - providedCompile( -// [group: 'javax.servlet', name: 'servlet-api', version: '2.5'], - ) compile( - // [group: 'ch.vorburger.mariaDB4j', name: 'mariaDB4j', version: '2.1.3'], - - [group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion], - [group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: springBootVersion], - [group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion], - //[group: 'org.eclipse.persistence', name: 'javax.persistence', version: '2.0.0'], - [group: 'org.springframework', name: 'spring-context-support', version: springVersion], - - [group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: springOauthVersion], - - [group: 'org.apache.openjpa', name:'openjpa-all', version:'2.4.1'], - //[group: 'org.apache.openjpa', name:'openjpa-maven-plugin', version:'2.4.1'], - //[group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1'], + [group: 'org.apache.openjpa', name:'openjpa-all', version:'2.4.1'], [group: 'com.sun.jersey', name: 'jersey-core', version: jerseyVersion], [group: 'com.sun.jersey', name: 'jersey-servlet', version: jerseyVersion], [group: 'com.sun.jersey', name: 'jersey-server', version: jerseyVersion], @@ -61,23 +36,16 @@ dependencies { [group: 'com.google.guava', name: 'guava', version: '15.0'], [group: 'joda-time', name: 'joda-time', version: '2.4'], - //[group: 'net.sourceforge.javacsv', name: 'javacsv', version: '2.0'], [group: 'org.apache.commons', name: 'commons-email', version: '1.3.3'], [group: 'org.apache.commons', name: 'commons-lang3', version: '3.3.2'], - // no slf4j & logback here (anymore), as spring-boot-starter-logging already brings this now, better assembled (log4j-over-slf4j was originally forgotten here) - - //[group: 'mysql', name: 'mysql-connector-java', version: '5.1.27'], [group: 'org.drizzle.jdbc', name: 'drizzle-jdbc', version: '1.3'], - // [group: 'org.apache.tomcat', name: 'tomcat-jdbc', version: tomcatVersion], - [group: 'org.apache.poi',name: 'poi', version: '3.9'], [group: 'org.apache.poi',name: 'poi-ooxml', version: '3.9'], [group: 'org.apache.poi',name: 'poi-ooxml-schemas', version: '3.9'], [group: 'com.lowagie', name: 'itext', version: '2.1.7'], - //[group: 'com.lowagie', name: 'itext-rtf', version: '2.1.7'], [group: 'org.mnode.ical4j', name: 'ical4j', version: '1.0.4'], [group: 'com.googlecode.flyway', name: 'flyway-core', version: '2.1.1'], [group: 'org.quartz-scheduler', name: 'quartz', version: '2.1.7'], @@ -86,11 +54,7 @@ dependencies { [group: 'com.github.spullara.mustache.java', name: 'compiler', version: '0.8.12'], [group: 'com.jayway.jsonpath', name: 'json-path', version: '0.9.1'], [group: 'org.apache.tika', name: 'tika-core', version :'1.9'], - // Although fineract (at the time of writing) doesn't have any compile time dep. on this, - // it's useful to have this for the Spring Boot TestRestTemplate http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-rest-templates-test-utility [group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.5'], - // Once we've switched to Java 8 this dep can be removed. - //[group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.0'] [group: 'io.swagger', name: 'swagger-jersey-jaxrs', version: '1.5.15'], [group: 'org.springframework', name:'spring-jms'], diff --git a/fineract-provider/dev-dependencies.gradle b/fineract-provider/dev-dependencies.gradle index 325a1a20a87..e3858e8286a 100644 --- a/fineract-provider/dev-dependencies.gradle +++ b/fineract-provider/dev-dependencies.gradle @@ -17,30 +17,8 @@ * under the License. */ dependencies { - def tomcatVersion = '7.0.54' - tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}", - "org.apache.tomcat.embed:tomcat-embed-logging-log4j:${tomcatVersion}" // NOT tomcat-embed-logging-juli (http://stackoverflow.com/questions/23963049/classcircularityerror-java-util-logging-logrecord-running-gradle-webapp-with-ja) - tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}") { - exclude group: 'org.eclipse.jdt.core.compiler', module: 'ecj' - } - tomcat "org.apache.tomcat:tomcat-dbcp:${tomcatVersion}" - - providedRuntime("org.springframework.boot:spring-boot-starter-tomcat") - - providedCompile( -// [group: 'javax.servlet', name: 'servlet-api', version: '2.5'], - ) compile( - [group: 'ch.vorburger.mariaDB4j', name: 'mariaDB4j', version: '2.1.3'], - - [group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion], - [group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: springBootVersion], - [group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion], - //[group: 'org.eclipse.persistence', name: 'javax.persistence', version: '2.0.0'], - [group: 'org.springframework', name: 'spring-context-support', version: springVersion], - [group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: springOauthVersion], - [group: 'org.apache.openjpa', name:'openjpa-all', version:'2.4.1'], [group: 'org.apache.openjpa', name:'openjpa-maven-plugin', version:'2.4.1'], [group: 'javax.ws.rs', name: 'jsr311-api', version: '1.1.1'], @@ -60,16 +38,10 @@ dependencies { [group: 'com.google.guava', name: 'guava', version: '15.0'], [group: 'joda-time', name: 'joda-time', version: '2.4'], - //[group: 'net.sourceforge.javacsv', name: 'javacsv', version: '2.0'], [group: 'org.apache.commons', name: 'commons-email', version: '1.3.3'], [group: 'org.apache.commons', name: 'commons-lang3', version: '3.3.2'], - // no slf4j & logback here (anymore), as spring-boot-starter-logging already brings this now, better assembled (log4j-over-slf4j was originally forgotten here) - - //[group: 'mysql', name: 'mysql-connector-java', version: '5.1.27'], [group: 'org.drizzle.jdbc', name: 'drizzle-jdbc', version: '1.3'], - [group: 'org.apache.tomcat', name: 'tomcat-jdbc', version: tomcatVersion], - [group: 'org.apache.poi',name: 'poi', version: '3.9'], [group: 'org.apache.poi',name: 'poi-ooxml', version: '3.9'], @@ -85,11 +57,8 @@ dependencies { [group: 'com.github.spullara.mustache.java', name: 'compiler', version: '0.8.12'], [group: 'com.jayway.jsonpath', name: 'json-path', version: '0.9.1'], [group: 'org.apache.tika', name: 'tika-core', version :'1.9'], - // Although fineract (at the time of writing) doesn't have any compile time dep. on this, - // it's useful to have this for the Spring Boot TestRestTemplate http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-rest-templates-test-utility + [group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.5'], - // Once we've switched to Java 8 this dep can be removed. - //[group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.0'] [group: 'io.swagger', name: 'swagger-jersey-jaxrs', version: '1.5.15'], [group: 'org.springframework', name:'spring-jms'], diff --git a/fineract-provider/gradle/wrapper/gradle-wrapper.properties b/fineract-provider/gradle/wrapper/gradle-wrapper.properties index 44cca1e1cd1..f4aa24d48a6 100644 --- a/fineract-provider/gradle/wrapper/gradle-wrapper.properties +++ b/fineract-provider/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jan 29 13:04:36 IST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip diff --git a/fineract-provider/gradlew b/fineract-provider/gradlew new file mode 100644 index 00000000000..22ebe76a08a --- /dev/null +++ b/fineract-provider/gradlew @@ -0,0 +1,173 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" +JAVA_OPTS="-Xms512m -Xmx2048m" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/fineract-provider/gradlew.bat b/fineract-provider/gradlew.bat new file mode 100644 index 00000000000..f9553162f12 --- /dev/null +++ b/fineract-provider/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/fineract-provider/properties/basicauth/application.properties b/fineract-provider/properties/basicauth/application.properties index d1fad00b9b4..a3f08b50b35 100644 --- a/fineract-provider/properties/basicauth/application.properties +++ b/fineract-provider/properties/basicauth/application.properties @@ -17,4 +17,15 @@ # under the License. # -spring.profiles.default=basicauth \ No newline at end of file +spring.profiles.default=basicauth + +server.context-path=/fineract-provider + +fineract.datasource.driver=org.drizzle.jdbc.DrizzleDriver +fineract.datasource.host=localhost +fineract.datasource.port=3306 +fineract.datasource.username=root +fineract.datasource.password=mysql +fineract.datasource.db=mifosplatform-tenants +fineract.datasource.protocol=jdbc +fineract.datasource.subprotocol=mysql:thin \ No newline at end of file diff --git a/fineract-provider/properties/oauth/application.properties b/fineract-provider/properties/oauth/application.properties index 4d266fe424d..0ded8b29f9f 100644 --- a/fineract-provider/properties/oauth/application.properties +++ b/fineract-provider/properties/oauth/application.properties @@ -18,4 +18,15 @@ # spring.profiles.default=basicauth -spring.profiles.active=oauth \ No newline at end of file +spring.profiles.active=oauth + +server.context-path=/fineract-provider + +fineract.datasource.driver=org.drizzle.jdbc.DrizzleDriver +fineract.datasource.host=localhost +fineract.datasource.port=3306 +fineract.datasource.username=root +fineract.datasource.password=mysql +fineract.datasource.db=mifosplatform-tenants +fineract.datasource.protocol=jdbc +fineract.datasource.subprotocol=mysql:thin \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/ServerApplication.java b/fineract-provider/src/main/java/org/apache/fineract/ServerApplication.java index 9f8666624a4..4ddff48efa0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/ServerApplication.java +++ b/fineract-provider/src/main/java/org/apache/fineract/ServerApplication.java @@ -18,39 +18,37 @@ */ package org.apache.fineract; -import org.apache.fineract.infrastructure.core.boot.AbstractApplicationConfiguration; -import org.apache.fineract.infrastructure.core.boot.ApplicationExitUtil; -import org.apache.fineract.infrastructure.core.boot.EmbeddedTomcatWithSSLConfiguration; +import org.apache.fineract.infrastructure.core.boot.WebFrontEndConfiguration; +import org.apache.fineract.infrastructure.core.boot.WebTwoFactorXmlConfiguration; +import org.apache.fineract.infrastructure.core.boot.WebXmlConfiguration; +import org.apache.fineract.infrastructure.core.boot.WebXmlOauthConfiguration; import org.apache.fineract.infrastructure.core.boot.db.DataSourceConfiguration; -import org.apache.fineract.infrastructure.core.boot.db.DataSourceProperties; +import org.apache.fineract.notification.config.MessagingConfiguration; import org.springframework.boot.SpringApplication; -import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportResource; -/** - * Fineract main() application which launches Fineract in an embedded Tomcat HTTP - * (using Spring Boot). - * - * The DataSource used is a to a "normal" external database (not use MariaDB4j). - * This DataSource can be configured with parameters, see {@link DataSourceProperties}. - * - * You can easily launch this via Debug as Java Application in your IDE - - * without needing command line Gradle stuff, no need to build and deploy a WAR, - * remote attachment etc. - * - * It's the old/classic Mifos (non-X) Workspace 2.0 reborn for Fineract! ;-) - * - * @see DataSourceProperties about how to configure the DataSource used - * @see ServerWithMariaDB4jApplication for an alternative with an embedded DB - */ +@EnableAutoConfiguration(exclude = { + DataSourceAutoConfiguration.class, + HibernateJpaAutoConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class, + FlywayAutoConfiguration.class }) +@Import({ + DataSourceConfiguration.class, + WebXmlConfiguration.class, + WebXmlOauthConfiguration.class, + WebFrontEndConfiguration.class, + MessagingConfiguration.class, + WebTwoFactorXmlConfiguration.class }) +@ImportResource({ "classpath*:META-INF/spring/appContext.xml" }) public class ServerApplication { - @Import({ DataSourceConfiguration.class, EmbeddedTomcatWithSSLConfiguration.class }) - private static class Configuration extends AbstractApplicationConfiguration { } - public static void main(String[] args) throws Exception { - ConfigurableApplicationContext ctx = SpringApplication.run(Configuration.class, args); - ApplicationExitUtil.waitForKeyPressToCleanlyExit(ctx); + SpringApplication.run(ServerApplication.class, args); } - -} +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/DataSourceConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/DataSourceConfiguration.java index fe5f9bb09be..74189686019 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/DataSourceConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/DataSourceConfiguration.java @@ -20,11 +20,8 @@ import javax.sql.DataSource; -import org.apache.fineract.infrastructure.core.boot.JDBCDriverConfig; -import org.apache.tomcat.jdbc.pool.PoolConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -34,24 +31,17 @@ */ @Configuration public class DataSourceConfiguration { + private static final Logger logger = LoggerFactory.getLogger(DataSourceConfiguration.class); - - @Autowired JDBCDriverConfig config ; - - @Bean - public DataSourceProperties dataSourceProperties() { - return new DataSourceProperties(config.getDriverClassName(), config.getProtocol(), config.getSubProtocol(), config.getPort()); - } @Bean - public DataSource tenantDataSourceJndi() { - PoolConfiguration p = getProperties(); - org.apache.tomcat.jdbc.pool.DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource(p); - logger.info("Created new DataSource; url=" + p.getUrl()); - return ds; + public DataSourceProperties dataSourceProperties(JdbcDriverConfig config) { + return new DataSourceProperties(config); } - protected DataSourceProperties getProperties() { - return dataSourceProperties(); + @Bean + public DataSource tenantDataSourceJndi(DataSourceProperties dataSourceProperties) { + logger.info("Created new DataSource; url=" + dataSourceProperties.getUrl()); + return new org.apache.tomcat.jdbc.pool.DataSource(dataSourceProperties); } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/DataSourceProperties.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/DataSourceProperties.java index 24276f7828b..f2421491208 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/DataSourceProperties.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/DataSourceProperties.java @@ -18,88 +18,38 @@ */ package org.apache.fineract.infrastructure.core.boot.db; -import javax.validation.constraints.NotNull; - import org.apache.tomcat.jdbc.pool.PoolProperties; -import org.springframework.beans.factory.annotation.Value; import org.springframework.util.StringUtils; -/** - * Configurable DataSource. Properties have sensible defaults, but end-users can - * override those via the Spring Values listed below; i.e. via -D Java System - * properties, or main() command line arguments, OS environment variables, from - * JNDI, or application.properties (thanks Spring Boot). For example: - * -Dfineract.datasource.port=3307. - */ -// NOT a @Component - we do not want this to picked up by component scan, only explicitly declared in DataSourceConfiguration (if that's active) public class DataSourceProperties extends PoolProperties { - public final static String PORT = "fineract.datasource.port"; - public final static String HOST = "fineract.datasource.host"; - public final static String DB = "fineract.datasource.db"; - public final static String UID = "fineract.datasource.username"; - public final static String PWD = "fineract.datasource.password"; - public final static String PROTOCOL = "fineract.datasource.protocol"; - public final static String SUBPROTOCOL = "fineract.datasource.subprotocol"; - - @Value("${" + PORT + ":3306}") - private volatile @NotNull int port; - - @Value("${" + HOST + ":localhost}") - private volatile @NotNull String hostname; - - @Value("${" + DB + ":mifosplatform-tenants}") - private volatile @NotNull String dbName; - - @Value("${" + UID + ":root}") - private volatile @NotNull String username; + private JdbcDriverConfig jdbcConfig; - @Value("${" + PWD + ":mysql}") - private volatile @NotNull String password; - - @Value("${" + PROTOCOL + ":jdbc}") - private volatile @NotNull String jdbcProtocol; - - @Value("${" + SUBPROTOCOL + ":mysql:thin}") - private volatile @NotNull String jdbcSubprotocol; - - public DataSourceProperties(String driverClassName, String protocol, String subProtocol, Integer port) { - super(); - setDriverClassName(driverClassName); - this.jdbcProtocol = protocol ; - this.jdbcSubprotocol = subProtocol ; - this.port = port ; - setDefaults(); - } - - /** - * as per (some of..) INSTALL.md and - * org.apache.fineract.infrastructure.core.service - * .TomcatJdbcDataSourcePerTenantService - * .createNewDataSourceFor(FineractPlatformTenant) - */ - protected void setDefaults() { - setInitialSize(3); - // setMaxIdle(6); -- strange, why? - // setMinIdle(3); -- JavaDoc says default is initialSize.. so shouldn't - // be needed - if (getValidationQuery() == null) setValidationQuery("SELECT 1"); - setTestOnBorrow(true); - setTestOnReturn(true); - setTestWhileIdle(true); - setTimeBetweenEvictionRunsMillis(30000); - setTimeBetweenEvictionRunsMillis(60000); - setLogAbandoned(true); - setSuspectTimeout(60); - - setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;" - + "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport"); - } + public DataSourceProperties(JdbcDriverConfig jdbcConfig) { + super(); + this.jdbcConfig = jdbcConfig; + setDriverClassName(jdbcConfig.getDriverClassName()); + setDefaults(); + } - @Override - public void setUrl(@SuppressWarnings("unused") String url) { - throw new UnsupportedOperationException("Use setHost/Port/DB() instead of setURL()"); - } + protected void setDefaults() { + setInitialSize(3); + // setMaxIdle(6); -- strange, why? + // setMinIdle(3); -- JavaDoc says default is initialSize.. so shouldn't + // be needed + if (getValidationQuery() == null) + setValidationQuery("SELECT 1"); + setTestOnBorrow(true); + setTestOnReturn(true); + setTestWhileIdle(true); + setTimeBetweenEvictionRunsMillis(30000); + setTimeBetweenEvictionRunsMillis(60000); + setLogAbandoned(true); + setSuspectTimeout(60); + + setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;" + + "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport"); + } @Override public String getUrl() { @@ -107,51 +57,43 @@ public String getUrl() { if (StringUtils.hasText(url)) { throw new IllegalStateException(); } - return jdbcProtocol + ":" + jdbcSubprotocol + "://" + getHost() + ":" + getPort() + "/" + getDBName(); + return jdbcConfig.getProtocol() + ":" + jdbcConfig.getSubProtocol() + "://" + getHost() + ":" + getPort() + "/" + getDBName(); } public String getHost() { - return hostname; + return jdbcConfig.getHost(); } public int getPort() { - return port; + return jdbcConfig.getPort(); } public String getDBName() { - return dbName; - } - - public void setPort(int port) { - this.port = port; + return jdbcConfig.getDbName(); } - public void setHost(String hostname) { - this.hostname = hostname; - } - - public void setDBName(String dbName) { - this.dbName = dbName; + @Override + public String getUsername() { + return this.jdbcConfig.getUsername(); } @Override - public String getUsername() { - return this.username; + public String getPassword() { + return this.jdbcConfig.getPassword(); } @Override - public void setUsername(String username) { - this.username = username; + public void setUrl(String url) { + throw new UnsupportedOperationException("Can not change url!"); } @Override - public String getPassword() { - return this.password; + public void setUsername(String username) { + throw new UnsupportedOperationException("Can not change username!"); } @Override public void setPassword(String password) { - this.password = password; + throw new UnsupportedOperationException("Can not change password!"); } - } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/JdbcDriverConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/JdbcDriverConfig.java new file mode 100644 index 00000000000..b08380237c9 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/JdbcDriverConfig.java @@ -0,0 +1,87 @@ +/** + * 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.infrastructure.core.boot.db; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class JdbcDriverConfig { + + @Value("${fineract.datasource.port}") + private int port; + + @Value("${fineract.datasource.driver}") + private String driver; + + @Value("${fineract.datasource.host}") + private String hostname; + + @Value("${fineract.datasource.db}") + private String dbName; + + @Value("${fineract.datasource.username}") + private String username; + + @Value("${fineract.datasource.password}") + private String password; + + @Value("${fineract.datasource.protocol}") + private String jdbcProtocol; + + @Value("${fineract.datasource.subprotocol}") + private String jdbcSubprotocol; + + public Integer getPort() { + return this.port ; + } + + public String getDriverClassName() { + return this.driver ; + } + + public String getHost() { + return hostname; + } + + public String getDbName() { + return dbName; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getProtocol() { + return this.jdbcProtocol ; + } + + public String getSubProtocol() { + return this.jdbcSubprotocol ; + } + + public String constructProtocol(String schemaServer, String schemaServerPort, String schemaName) { + return new StringBuilder(jdbcProtocol).append(":").append(jdbcSubprotocol).append("://").append(schemaServer).append(':').append(schemaServerPort) + .append('/').append(schemaName).toString(); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/TenantDataSourcePortFixService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/TenantDataSourcePortFixService.java index 5ca3dfe75b6..ca264704e46 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/TenantDataSourcePortFixService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/TenantDataSourcePortFixService.java @@ -58,11 +58,8 @@ public class TenantDataSourcePortFixService { @Value("${" + ENABLED + ":true}") private boolean enabled; - // required=false is important here, because in - // WebApplicationInitializerConfiguration for classic WAR there - // is (intentionally) no MariaDB4j nor a DataSourceProperties - // bean (because in the WAR we're using a DS from JNDI) - private @Autowired(required = false) DataSourceProperties dsp; + @Autowired + private JdbcDriverConfig jdbcConfig; private JdbcTemplate jdbcTemplate; @@ -71,26 +68,25 @@ public TenantDataSourcePortFixService(@Qualifier("tenantDataSourceJndi") final D this.jdbcTemplate = new JdbcTemplate(dataSource); } + /** + * This method updates the default 3306 port of the default flyway inserted 'mifostenant-default' tenant overriden by application.properties + */ public void fixUpTenantsSchemaServerPort() { - if (!enabled) { - logger.info("No schema_server_port UPDATE made to tenant_server_connections table of the mifosplatform-tenants schema, because " + ENABLED + " = false"); - return; - } - if (dsp == null) { - // we don't have any generic mechanism to know the DB port, given just a tenant DataSource - logger.debug("No schema_server_port UPDATE made to tenant_server_connections table of the mifosplatform-tenants schema (because neither MariaDB4j nor our own Spring Boot DataSourceConfiguration is used in a traditional WAR)"); - return; - } + if (!enabled) { + logger.info("No schema_server_port UPDATE made to tenant_server_connections table of the mifosplatform-tenants schema, because " + ENABLED + " = false"); + return; + } + int r = jdbcTemplate .update("UPDATE tenant_server_connections SET schema_server = ?, schema_server_port = ?, schema_username = ?, schema_password = ?", - dsp.getHost(), dsp.getPort(), dsp.getUsername(), dsp.getPassword()); - if ( r == 0 ) - logger.warn("UPDATE tenant_server_connections SET ... did not update ANY rows - something is probably wrong"); - else - logger.info("Upated " - + r - + " rows in the tenant_server_connections table of the mifosplatform-tenants schema to the real current host: " - + dsp.getHost() + ", port: " + dsp.getPort()); + jdbcConfig.getHost(), jdbcConfig.getPort(), jdbcConfig.getUsername(), jdbcConfig.getPassword()); + if ( r == 0 ) { + logger.warn("UPDATE tenant_server_connections SET ... did not update ANY rows - something is probably wrong"); + } else { + logger.info("Upated " + + r + + " rows in the tenant_server_connections table of the mifosplatform-tenants schema to the real current host: " + + jdbcConfig.getHost() + ", port: " + jdbcConfig.getPort()); + } } - -} +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/FineractPlatformTenantConnection.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/FineractPlatformTenantConnection.java index af5d493399e..875c42a56ae 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/FineractPlatformTenantConnection.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/domain/FineractPlatformTenantConnection.java @@ -76,14 +76,6 @@ public FineractPlatformTenantConnection(final Long connectionId,final String sch this.maxIntervalBetweenRetries = maxIntervalBetweenRetries; this.testOnBorrow=tesOnBorrow; } - - //The Connection Protocol should be built based on jdbc.properties. We can't hard code this here and also, constructing protocol is not this class - //responsibility - /*public String databaseURL() { - final String url = new StringBuilder("jdbc:mysql:thin://").append(this.schemaServer).append(':').append(this.schemaServerPort) - .append('/').append(this.schemaName).toString(); - return url; - }*/ /** * @return the schemaServer diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/TenantDatabaseUpgradeService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/TenantDatabaseUpgradeService.java index 740dcb57b32..38eb6fa507c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/TenantDatabaseUpgradeService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/TenantDatabaseUpgradeService.java @@ -23,7 +23,7 @@ import javax.annotation.PostConstruct; import javax.sql.DataSource; -import org.apache.fineract.infrastructure.core.boot.JDBCDriverConfig; +import org.apache.fineract.infrastructure.core.boot.db.JdbcDriverConfig; import org.apache.fineract.infrastructure.core.boot.db.TenantDataSourcePortFixService; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection; @@ -47,7 +47,8 @@ public class TenantDatabaseUpgradeService { protected final DataSource tenantDataSource; protected final TenantDataSourcePortFixService tenantDataSourcePortFixService; - @Autowired private JDBCDriverConfig driverConfig ; + @Autowired + private JdbcDriverConfig driverConfig ; @Autowired public TenantDatabaseUpgradeService(final TenantDetailsService detailsService, diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/TomcatJdbcDataSourcePerTenantService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/TomcatJdbcDataSourcePerTenantService.java index 7f9be71994f..2beebc7de5a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/TomcatJdbcDataSourcePerTenantService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/TomcatJdbcDataSourcePerTenantService.java @@ -23,7 +23,7 @@ import javax.sql.DataSource; -import org.apache.fineract.infrastructure.core.boot.JDBCDriverConfig; +import org.apache.fineract.infrastructure.core.boot.db.JdbcDriverConfig; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection; import org.apache.tomcat.jdbc.pool.PoolConfiguration; @@ -47,7 +47,7 @@ public class TomcatJdbcDataSourcePerTenantService implements RoutingDataSourceSe private final DataSource tenantDataSource; @Autowired - private JDBCDriverConfig driverConfig ; + private JdbcDriverConfig driverConfig ; @Autowired public TomcatJdbcDataSourcePerTenantService(final @Qualifier("tenantDataSourceJndi") DataSource tenantDataSource) { diff --git a/fineract-provider/src/main/resources/META-INF/spring/infrastructure.xml b/fineract-provider/src/main/resources/META-INF/spring/infrastructure.xml index e57daaea4f9..fafd581d294 100644 --- a/fineract-provider/src/main/resources/META-INF/spring/infrastructure.xml +++ b/fineract-provider/src/main/resources/META-INF/spring/infrastructure.xml @@ -31,11 +31,23 @@ + + + org.apache.fineract + + - + + + + QuotedNumbersInQueries=true + org.apache.fineract.infrastructure.core.domain.MySQLDictionaryCustom + ForeignKeyDeleteAction=CASCADE + + diff --git a/fineract-provider/src/main/resources/META-INF/spring/securityContext.xml b/fineract-provider/src/main/resources/META-INF/spring/securityContext.xml index a4db7fef550..24120e702c8 100644 --- a/fineract-provider/src/main/resources/META-INF/spring/securityContext.xml +++ b/fineract-provider/src/main/resources/META-INF/spring/securityContext.xml @@ -34,30 +34,18 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/fineract-provider/src/main/resources/application.properties b/fineract-provider/src/main/resources/application.properties index d1fad00b9b4..a3f08b50b35 100644 --- a/fineract-provider/src/main/resources/application.properties +++ b/fineract-provider/src/main/resources/application.properties @@ -17,4 +17,15 @@ # under the License. # -spring.profiles.default=basicauth \ No newline at end of file +spring.profiles.default=basicauth + +server.context-path=/fineract-provider + +fineract.datasource.driver=org.drizzle.jdbc.DrizzleDriver +fineract.datasource.host=localhost +fineract.datasource.port=3306 +fineract.datasource.username=root +fineract.datasource.password=mysql +fineract.datasource.db=mifosplatform-tenants +fineract.datasource.protocol=jdbc +fineract.datasource.subprotocol=mysql:thin \ No newline at end of file From 423243a8ee1d923206f80722be1f75ac8f0752ac Mon Sep 17 00:00:00 2001 From: Marti Date: Fri, 8 Mar 2019 12:07:08 +0100 Subject: [PATCH 5/5] interoperable - build without mariadb - not necessary for interoperable --- fineract-provider/.gitignore | 2 +- .../ServerWithMariaDB4jApplication.java | 105 ----------- .../AbstractApplicationConfiguration.java | 54 ------ .../core/boot/ApplicationExitUtil.java | 46 ----- .../EmbeddedTomcatWithSSLConfiguration.java | 120 ------------- .../core/boot/JDBCDriverConfig.java | 74 -------- .../boot/WarWebApplicationInitializer.java | 53 ------ .../db/MariaDB4jDataSourceConfiguration.java | 62 ------- .../core/boot/db/MariaDB4jSetupService.java | 54 ------ .../resources/META-INF/spring/jdbc.properties | 23 --- .../main/resources/META-INF/spring/jndi.xml | 34 ---- gradlew | 167 ------------------ gradlew.bat | 113 ------------ 13 files changed, 1 insertion(+), 906 deletions(-) delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/ServerWithMariaDB4jApplication.java delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/AbstractApplicationConfiguration.java delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/ApplicationExitUtil.java delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/EmbeddedTomcatWithSSLConfiguration.java delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/JDBCDriverConfig.java delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WarWebApplicationInitializer.java delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/MariaDB4jDataSourceConfiguration.java delete mode 100644 fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/MariaDB4jSetupService.java delete mode 100644 fineract-provider/src/main/resources/META-INF/spring/jdbc.properties delete mode 100644 fineract-provider/src/main/resources/META-INF/spring/jndi.xml delete mode 100755 gradlew delete mode 100644 gradlew.bat diff --git a/fineract-provider/.gitignore b/fineract-provider/.gitignore index e4bae860977..d9d40f220bd 100644 --- a/fineract-provider/.gitignore +++ b/fineract-provider/.gitignore @@ -9,4 +9,4 @@ repos *.log .idea !gradle/wrapper/gradle-wrapper.jar -/gradle + diff --git a/fineract-provider/src/main/java/org/apache/fineract/ServerWithMariaDB4jApplication.java b/fineract-provider/src/main/java/org/apache/fineract/ServerWithMariaDB4jApplication.java deleted file mode 100644 index 0ed31fd2627..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/ServerWithMariaDB4jApplication.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * 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; - -import java.awt.Desktop; -import java.io.IOException; -import java.net.URI; - -import org.apache.fineract.infrastructure.core.boot.AbstractApplicationConfiguration; -import org.apache.fineract.infrastructure.core.boot.ApplicationExitUtil; -import org.apache.fineract.infrastructure.core.boot.EmbeddedTomcatWithSSLConfiguration; -import org.apache.fineract.infrastructure.core.boot.db.MariaDB4jDataSourceConfiguration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Import; -import org.springframework.core.io.Resource; - -/** - * Fineract main() application which launches Fineract in an embedded Tomcat HTTP - * server (using Spring Boot), as well as an embedded database (using - * MariaDB4j). - * - * You can easily launch this via Debug as Java Application in your IDE - - * without needing command line Gradle stuff, no need to build and deploy a WAR, - * remote attachment etc. - * - * It's the old/classic Mifos (non-X) Workspace 2.0 reborn for Fineract! ;-) - * - * @see ServerApplication for the same without the embedded MariaDB4j database - */ -public class ServerWithMariaDB4jApplication { - private final static Logger logger = LoggerFactory.getLogger(ServerWithMariaDB4jApplication.class); - - @Import({ MariaDB4jDataSourceConfiguration.class, EmbeddedTomcatWithSSLConfiguration.class }) - public static class Configuration extends AbstractApplicationConfiguration { } - - public static void main(String[] args) throws Exception { - ConfigurableApplicationContext ctx = SpringApplication.run(Configuration.class, args); - if (!Desktop.isDesktopSupported()) { - logger.info("Not going to open UI homepage in local web browser, because !Desktop.isDesktopSupported()"); - - } else { - // apps/community-app/dist/community-app/index.html - Resource distResource = ctx.getResource("file:" + System.getProperty("user.dir") + - System.getProperty("file.separator") + "apps" + - System.getProperty("file.separator") + "community-app" + - System.getProperty("file.separator") + "dist" + - System.getProperty("file.separator") + "community-app" + - System.getProperty("file.separator") + "index.html"); - URI distURI = URI.create("https://localhost:8443/fineract-provider" + - "/apps/community-app/index.html?baseApiUrl=https://localhost:8443" + - "&tenantIdentifier=default#/"); - - // apps/community-app/app/index.html - Resource devResource = ctx.getResource("file:" + System.getProperty("user.dir") + - System.getProperty("file.separator") + "apps" + - System.getProperty("file.separator") + "community-app" + - System.getProperty("file.separator") + "app" + - System.getProperty("file.separator") + "index.html"); - URI devURI = URI.create("https://localhost:8443/fineract-provider" + - "/apps/community-app/app/index.html?baseApiUrl=https://localhost:8443" + - "&tenantIdentifier=default#/"); - - if (distResource.exists()) { - openWebBrowser(distURI); - } else if (devResource.exists()) { - openWebBrowser(devURI); - } else { - logger.error("Cannot open Fineract UI in browser; not found: " + distResource.toString()); - } - } - - // TODO Tray Icon stuff; dig out my very own old @see https://github.com/mifos/head/tree/hudsonBuild-MIFOS-5157_Launch4j-EXE_NewDist-squash1/server-jetty/src/main/java/org/mifos/server/tray - - ApplicationExitUtil.waitForKeyPressToCleanlyExit(ctx); - } - - private static void openWebBrowser(URI uri) { - try { - logger.info("Opening Fineract UI in browser: " + uri.toString()); - Desktop.getDesktop().browse(uri); - } catch (IOException e) { - logger.error("IOException when opening Fineract UI in browser: " + uri.toString(), e); - } - } - -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/AbstractApplicationConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/AbstractApplicationConfiguration.java deleted file mode 100644 index 53bf33704ae..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/AbstractApplicationConfiguration.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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.infrastructure.core.boot; - -import org.apache.fineract.notification.config.MessagingConfiguration; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.ImportResource; -import org.springframework.context.annotation.PropertySource; - -/** - * Base Spring Configuration with what's common to all Configuration subclasses. - * - * Notably the EnableAutoConfiguration excludes relevant for (and often adjusted - * when upgrading versions of) Spring Boot, the "old" (pre. Spring Boot & - * MariaDB4j) fineract appContext.xml which all configurations need, and the - * web.xml successor WebXmlConfiguration. - * - * Should NOT include Configuration related to embedded Tomcat, data sources, - * and MariaDB4j (because those differ in the subclasses). - */ -@Configuration -@Import({ WebXmlConfiguration.class, WebXmlOauthConfiguration.class, WebFrontEndConfiguration.class, - MessagingConfiguration.class, WebTwoFactorXmlConfiguration.class }) -@ImportResource({ "classpath*:META-INF/spring/appContext.xml" }) -@PropertySource(value="classpath:META-INF/spring/jdbc.properties") -@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, - HibernateJpaAutoConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class, - FlywayAutoConfiguration.class }) -public abstract class AbstractApplicationConfiguration { - -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/ApplicationExitUtil.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/ApplicationExitUtil.java deleted file mode 100644 index 2dac008794b..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/ApplicationExitUtil.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 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.infrastructure.core.boot; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -import org.springframework.context.ConfigurableApplicationContext; - -public abstract class ApplicationExitUtil { - - private ApplicationExitUtil() {} - - public static void waitForKeyPressToCleanlyExit(ConfigurableApplicationContext ctx) throws IOException { - - // NOTE: In Eclipse, the Shutdown Hooks are not invoked on exit (red - // button).. In the case of MariaDB4j that's a problem because then the - // mysqld won't be stopped, so: - // (@see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38016) - System.out.println("\nHit Enter to quit..."); - // NOTE: In Eclipse, System.console() is not available.. so: - // (@see https://bugs.eclipse.org/bugs/show_bug.cgi?id=122429) - BufferedReader d = new BufferedReader(new InputStreamReader(System.in)); - d.readLine(); - - ctx.stop(); - ctx.close(); - } -} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/EmbeddedTomcatWithSSLConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/EmbeddedTomcatWithSSLConfiguration.java deleted file mode 100644 index 7ac996bb542..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/EmbeddedTomcatWithSSLConfiguration.java +++ /dev/null @@ -1,120 +0,0 @@ -/** - * 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.infrastructure.core.boot; - -import java.io.File; -import java.io.IOException; -import java.net.URL; - -import org.apache.catalina.connector.Connector; -import org.apache.commons.io.FileUtils; -import org.apache.coyote.http11.Http11NioProtocol; -import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; -import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; - -@Configuration -public class EmbeddedTomcatWithSSLConfiguration { - - // http://docs.spring.io/spring-boot/docs/1.1.5.RELEASE/reference/htmlsingle/#howto-enable-multiple-connectors-in-tomcat - - @Bean - public EmbeddedServletContainerFactory servletContainer() { - TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory(); - tomcat.setContextPath(getContextPath()); - tomcat.addAdditionalTomcatConnectors(createSslConnector()); - return tomcat; - } - - private String getContextPath() { - return "/fineract-provider"; - } - - protected Connector createSslConnector() { - Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); - Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); - try { - File keystore = getFile(getKeystore()); - File truststore = keystore; - connector.setScheme("https"); - connector.setSecure(true); - connector.setPort(getHTTPSPort()); - protocol.setSSLEnabled(true); - protocol.setKeystoreFile(keystore.getAbsolutePath()); - protocol.setKeystorePass(getKeystorePass()); - protocol.setTruststoreFile(truststore.getAbsolutePath()); - protocol.setTruststorePass(getKeystorePass()); - // ? protocol.setKeyAlias("apitester"); - return connector; - } catch (IOException ex) { - throw new IllegalStateException("can't access keystore: [" + "keystore" + "] or truststore: [" + "keystore" + "]", ex); - } - } - - protected int getHTTPSPort() { - // TODO This shouldn't be hard-coded here, but configurable - return 8443; - } - - protected String getKeystorePass() { - return "openmf"; - } - - protected Resource getKeystore() { - return new ClassPathResource("/keystore.jks"); - } - - public File getFile(Resource resource) throws IOException { - try { - return resource.getFile(); - } catch (IOException e) { - // Uops.. OK, try again (below) - } - - try { - URL url = resource.getURL(); - /** - * // If this creates filenames that are too long on Win, // then - * could just use resource.getFilename(), // even though not unique, - * real risk prob. min.bon String tempDir = - * System.getProperty("java.io.tmpdir"); tempDir = tempDir + "/" + - * getClass().getSimpleName() + "/"; String path = url.getPath(); - * String uniqName = path.replace("file:/", "").replace('!', '_'); - * String tempFullPath = tempDir + uniqName; - **/ - // instead of File.createTempFile(prefix?, suffix?); - File targetFile = new File(resource.getFilename()); - long len = resource.contentLength(); - if (!targetFile.exists() || targetFile.length() != len) { // Only - // copy - // new - // files - FileUtils.copyURLToFile(url, targetFile); - } - return targetFile; - } catch (IOException e) { - // Uops.. erm, give up: - throw new IOException("Cannot obtain a File for Resource: " + resource.toString(), e); - } - - } -} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/JDBCDriverConfig.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/JDBCDriverConfig.java deleted file mode 100644 index 854f1209d8a..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/JDBCDriverConfig.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * 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.infrastructure.core.boot; - -import javax.annotation.PostConstruct; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.core.env.Environment; -import org.springframework.stereotype.Service; - -@Service -public class JDBCDriverConfig { - - private final static String DRIVER_CLASS_PROPERTYNAME = "DRIVERCLASS_NAME" ; - private final static String PROTOCOL_PROPERTYNAME = "PROTOCOL" ; - private final static String SUBPROTOCOL_PROPERTYNAME = "SUB_PROTOCOL" ; - private final static String PORT_PROPERTYNAME = "PORT" ; - - private String driverClassName ; - private String protocol ; - private String subProtocol ; - private Integer port ; - - @Autowired ApplicationContext context ; - - @PostConstruct - protected void init() { - Environment environment = context.getEnvironment() ; - driverClassName = (String)environment.getProperty(DRIVER_CLASS_PROPERTYNAME) ; - protocol = (String) environment.getProperty(PROTOCOL_PROPERTYNAME) ; - subProtocol = (String) environment.getProperty(SUBPROTOCOL_PROPERTYNAME) ; - port = Integer.parseInt((String) environment.getProperty(PORT_PROPERTYNAME)) ; - } - - public String getDriverClassName() { - return this.driverClassName ; - } - - public String getProtocol() { - return this.protocol ; - } - - public String getSubProtocol() { - return this.subProtocol ; - } - - public Integer getPort() { - return this.port ; - } - - public String constructProtocol(String schemaServer, String schemaServerPort, String schemaName) { - final String url = new StringBuilder(protocol).append(":").append(subProtocol).append("://").append(schemaServer).append(':').append(schemaServerPort) - .append('/').append(schemaName).toString(); - return url; - } - -} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WarWebApplicationInitializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WarWebApplicationInitializer.java deleted file mode 100644 index 7d1df1d616f..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/WarWebApplicationInitializer.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * 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.infrastructure.core.boot; - -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.context.web.SpringBootServletInitializer; -import org.springframework.context.annotation.ImportResource; - -/** - * Required to still have a working legacy "classic" WAR. - * - * Configuration just adds the JNDI-based DataSource lookup to its - * AbstractApplicationConfiguration. - * - * This (intentionally) only configures the original (pre-Spring Boot & - * MariaDB4j) fineract Spring Beans, and does NOT include the embedded Tomcat - * (incl. TomcatSSLConfiguration) nor the MariaDB4jSetupService or - * MariaDB4jDataSourceConfiguration, and not even the DataSourceConfiguration - * (as it uses "classic" JNDI) - we want the WAR to "work like before". - * - * @see #howto-convert-an-existing-application-to-spring-boot - */ -public class WarWebApplicationInitializer extends SpringBootServletInitializer { - - @ImportResource({ "classpath*:META-INF/spring/jndi.xml" }) - private static class Configuration extends AbstractApplicationConfiguration { - } - - @Override - protected SpringApplicationBuilder configure( - SpringApplicationBuilder application) { - // let's share Spring Boot Love, so no showBanner(false) - return application.sources(Configuration.class); - } - -} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/MariaDB4jDataSourceConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/MariaDB4jDataSourceConfiguration.java deleted file mode 100644 index ce32ac29612..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/MariaDB4jDataSourceConfiguration.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * 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.infrastructure.core.boot.db; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import ch.vorburger.mariadb4j.springframework.MariaDB4jSpringService; - -@Configuration -public class MariaDB4jDataSourceConfiguration extends DataSourceConfiguration { - - @Bean - public MariaDB4jSetupService mariaDB4jSetUp() { - return new MariaDB4jSetupService(mariaDB4j().getDB()); - } - - @Bean - public MariaDB4jSpringService mariaDB4j() { - MariaDB4jSpringService mariaDB4jSpringService = new MariaDB4jSpringService(); - mariaDB4jSpringService.setDefaultBaseDir("build/db/bin"); - mariaDB4jSpringService.setDefaultDataDir("build/db/data"); - return mariaDB4jSpringService; - } - - @Override - // NOT @Bean @Override dataSourceProperties() - doesn't work :( - protected DataSourceProperties getProperties() { - DataSourceProperties p = super.getProperties(); - String dbName = mariaDB4jSetUp().getTenantDBName(); - // Do not use p.setUrl(mariaDB4j().getConfiguration().getURL(dbName)); - // Because TenantDataSourcePortFixService needs separate - // host/port/db/uid/pwd: - // (DataSourceProperties getUrl() creates the correct JDBC URL from it) - // This intentionally overrides any fineract.datasource.* settings, because - // in this configuration, logically the mariaDB4j settings take - // precedence: - p.setHost("localhost"); - p.setPort(mariaDB4j().getConfiguration().getPort()); - p.setDBName(dbName); - // TODO p.setUsername(mariaDB4j().getConfiguration().getUsername()); - // TODO p.setPassword(mariaDB4j().getConfiguration().getPassword()); - return p; - } - -} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/MariaDB4jSetupService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/MariaDB4jSetupService.java deleted file mode 100644 index 92ca4a509f0..00000000000 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/boot/db/MariaDB4jSetupService.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * 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.infrastructure.core.boot.db; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - -import org.springframework.beans.factory.annotation.Autowired; - -import ch.vorburger.exec.ManagedProcessException; -import ch.vorburger.mariadb4j.DB; - -public class MariaDB4jSetupService { - - private DB db; - - @Autowired - public MariaDB4jSetupService(DB db) { - this.db = db; - } - - @PostConstruct - protected void setUpDBs() throws ManagedProcessException { - db.createDB(getTenantDBName()); - db.createDB("mifostenant-default"); - // Note that we don't need to initialize the DBs, because - // the TenantDatabaseUpgradeService will do this in just a moment. - } - - public String getTenantDBName() { - return "mifosplatform-tenants"; - } - - @PreDestroy - protected void stop() throws ManagedProcessException { - db = null; - } -} \ No newline at end of file diff --git a/fineract-provider/src/main/resources/META-INF/spring/jdbc.properties b/fineract-provider/src/main/resources/META-INF/spring/jdbc.properties deleted file mode 100644 index 8d64dc515ea..00000000000 --- a/fineract-provider/src/main/resources/META-INF/spring/jdbc.properties +++ /dev/null @@ -1,23 +0,0 @@ -# -# 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. -# - -DRIVERCLASS_NAME:org.drizzle.jdbc.DrizzleDriver -PROTOCOL:jdbc -SUB_PROTOCOL:mysql:thin -PORT:3306 \ No newline at end of file diff --git a/fineract-provider/src/main/resources/META-INF/spring/jndi.xml b/fineract-provider/src/main/resources/META-INF/spring/jndi.xml deleted file mode 100644 index ee32adaf052..00000000000 --- a/fineract-provider/src/main/resources/META-INF/spring/jndi.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - diff --git a/gradlew b/gradlew deleted file mode 100755 index 88750cdfa5f..00000000000 --- a/gradlew +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8005,server=y,suspend=n" - -CATALINA_OPTS="-Xms512m -Xmx2048m" -JAVA_OPTS="-Xms512m -Xmx2048m" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - -CLASSPATH=$APP_HOME/fineract-provider/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 0f7cd5ee265..00000000000 --- a/gradlew.bat +++ /dev/null @@ -1,113 +0,0 @@ -@REM -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8005,server=y,suspend=n - -set CATALINA_OPTS=-Xms512m -Xmx512m -set JAVA_OPTS=-Xms512m -Xmx512m - - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\fineract-provider\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega