FINERACT-706: Interoperation Mojaloop integration 1.0.1

This commit is contained in:
Marti
2019-05-07 14:57:28 +02:00
parent 57b73034a5
commit 2f22da0e6c
76 changed files with 5178 additions and 48 deletions
@@ -118,7 +118,7 @@ public class CommandProcessingResult implements Serializable {
this.subResourceId = subResourceId;
}
private CommandProcessingResult(final Long resourceId, final Long officeId, final Long commandId, final Map<String, Object> changesOnly) {
protected CommandProcessingResult(final Long resourceId, final Long officeId, final Long commandId, final Map<String, Object> changesOnly) {
if (resourceId != null) {
this.resourceIdentifier = resourceId.toString();
} else {
@@ -51,6 +51,18 @@ public class DataValidatorBuilder {
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<ApiParameterError> getDataValidationErrors() {
return dataValidationErrors;
}
public DataValidatorBuilder resource(final String resource) {
this.resource = resource;
return this;
@@ -22,6 +22,7 @@ import java.lang.reflect.Type;
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 class FromJsonHelper {
return this.gsonConverter.toJson(object);
}
public void checkForUnsupportedParameters(final Type typeOfMap, final String json, final Set<String> supportedParams) {
public void checkForUnsupportedParameters(final Type typeOfMap, final String json, final Collection<String> supportedParams) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Map<String, Object> requestMap = this.gsonConverter.fromJson(json, typeOfMap);
@@ -97,7 +98,7 @@ public class FromJsonHelper {
if (!unsupportedParameterList.isEmpty()) { throw new UnsupportedParameterException(unsupportedParameterList); }
}
public void checkForUnsupportedParameters(final JsonObject object, final Set<String> supportedParams) {
public void checkForUnsupportedParameters(final JsonObject object, final Collection<String> supportedParams) {
if (object == null) { throw new InvalidParameterException(); }
final Set<Entry<String, JsonElement>> entries = object.entrySet();
@@ -196,17 +197,26 @@ public class FromJsonHelper {
}
public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element) {
return this.helperDelegator.extractLocalDateNamed(parameterName, element, new HashSet<String>());
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<String>());
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<String>());
new HashSet<>());
}
public LocalDate extractLocalDateNamed(final String parameterName, final JsonElement element,
@@ -378,15 +378,25 @@ public class JsonParserHelper {
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<String> 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<String> parametersPassedInCommand) {
LocalDateTime value = null;
String timeValueAsString=null;
@@ -419,7 +429,7 @@ public class JsonParserHelper {
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<String> parametersPassedInCommand) {
LocalDate value = null;
if (element.isJsonObject()) {
@@ -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<CommandProcessingResult> jsonSerializer;
private InteropService interopService;
private PortfolioCommandSourceWritePlatformService commandsSourceService;
@Autowired
public InteropApiResource(PlatformSecurityContext context,
ApiRequestParameterHelper apiRequestParameterHelper,
DefaultToApiJsonSerializer<CommandProcessingResult> 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);
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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<String, Object> changesOnly, @NotNull String accountId) {
super(resourceId, officeId, commandId, changesOnly);
this.accountId = accountId;
}
protected static InteropIdentifierResponseData build(Long resourceId, Long officeId, Long commandId, Map<String, Object> 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;
}
}
@@ -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<ExtensionData> 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);
}
}
@@ -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<String, Object> changesOnly,
@NotNull String transactionCode, @NotNull InteropActionState state, LocalDateTime expiration,
List<ExtensionData> 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<ExtensionData> 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<ExtensionData> 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;
}
}
@@ -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<ExtensionData> 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<ExtensionData> 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<ExtensionData> getExtensionList() {
return extensionList;
}
public void setExtensionList(List<ExtensionData> 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<ExtensionData> 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);
}
}
@@ -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<ExtensionData> extensionList;
protected InteropResponseData(Long resourceId, Long officeId, Long commandId, Map<String, Object> changesOnly, @NotNull String transactionCode,
@NotNull InteropActionState state, LocalDateTime expiration, List<ExtensionData> 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<ExtensionData> 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<ExtensionData> 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<ExtensionData> 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);
}
}
@@ -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<ExtensionData> 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);
}
}
@@ -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<String, Object> changesOnly,
@NotNull String transactionCode, @NotNull InteropActionState state, LocalDateTime expiration,
List<ExtensionData> 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<ExtensionData> 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<ExtensionData> 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;
}
}
@@ -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);
}
}
@@ -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<ExtensionData> 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);
}
}
@@ -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<String, Object> changesOnly,
@NotNull String transactionCode, @NotNull InteropActionState state, LocalDateTime expiration,
List<ExtensionData> 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<ExtensionData> 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<ExtensionData> 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);
}
}
@@ -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);
}
}
@@ -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
}
@@ -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
}
@@ -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<Long> {
@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;
}
}
@@ -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<InteropIdentifier, Long>, JpaSpecificationExecutor<InteropIdentifier> {
}
@@ -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
}
@@ -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
}
@@ -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;
}
}
@@ -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
}
@@ -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,
;
}
@@ -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);
}
}
@@ -0,0 +1,57 @@
/**
* 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 org.springframework.util.StringUtils;
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(StringUtils.trimWhitespace(split[length - 1]));
String idValue = split[length - 2];
InteropIdentifierType idType = InteropIdentifierType.valueOf(split[length - 3].toUpperCase());
return this.interopService.registerAccountIdentifier(idType, idValue, subIdOrType, command);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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 org.springframework.util.StringUtils;
import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_IDENTIFIER;
@Service
@CommandType(entity = ENTITY_NAME_IDENTIFIER, action = "DELETE")
public class DeleteInteropIdentifierHandler implements NewCommandSourceHandler {
private final InteropService interopService;
@Autowired
public DeleteInteropIdentifierHandler(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(StringUtils.trimWhitespace(split[length - 1]));
String idValue = split[length - 2];
InteropIdentifierType idType = InteropIdentifierType.valueOf(split[length - 3].toUpperCase());
return this.interopService.deleteAccountIdentifier(idType, idValue, subIdOrType);
}
}
@@ -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);
}
}
@@ -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());
}
}
}
@@ -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);
}
@@ -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<PaymentType> 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<InteropIdentifier> idTypeEqual(@NotNull InteropIdentifierType idType) {
return (Root<InteropIdentifier> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> cb.and(cb.equal(root.get("type"), idType));
}
public static Specification<InteropIdentifier> idValueEqual(@NotNull String idValue) {
return (Root<InteropIdentifier> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> cb.and(cb.equal(root.get("value"), idValue));
}
public static Specification<InteropIdentifier> subIdOrTypeEqual(String subIdOrType) {
return (Root<InteropIdentifier> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> {
Path<Object> 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;
}
}
@@ -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";
}
@@ -0,0 +1,407 @@
/**
* 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 subtract(first, second, MoneyHelper.getMathContext());
}
/** @return first minus second considering null values, maybe negative */
public static BigDecimal subtract(BigDecimal first, BigDecimal second, MathContext mc) {
return first == null
? null
: second == null ? first : first.subtract(second, mc);
}
/** @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) {
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);
}
}
@@ -49,6 +49,11 @@ public class MonetaryCurrency {
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;
}
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.organisation.monetary.domain;
import java.math.MathContext;
import java.math.RoundingMode;
import javax.annotation.PostConstruct;
@@ -28,10 +29,13 @@ import org.springframework.stereotype.Component;
@Component
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 class MoneyHelper {
return roundingMode;
}
public static MathContext getMathContext() {
if (mathContext == null) {
mathContext = new MathContext(PRECISION, getRoundingMode());
}
return mathContext;
}
}
@@ -115,5 +115,11 @@ public final class PaymentDetail extends AbstractPersistableCustom<Long> {
return this.paymentType;
}
public String getReceiptNumber() { return this.receiptNumber; }
public String getReceiptNumber() {
return this.receiptNumber;
}
public String getRoutingCode() {
return routingCode;
}
}
@@ -81,6 +81,7 @@ import org.apache.fineract.infrastructure.security.service.RandomPasswordGenerat
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 class SavingsAccount extends AbstractPersistableCustom<Long> {
this.savingsHelper = savingsHelper;
}
public String getExternalId() {
return externalId;
}
public boolean isNotActive() {
return !isActive();
}
@@ -1023,10 +1028,23 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
return transaction;
}
private void payWithdrawalFee(final BigDecimal transactionAmoount, final LocalDate transactionDate, final AppUser user) {
public BigDecimal calculateWithdrawalFee(final BigDecimal transactionAmount) {
BigDecimal result = BigDecimal.ZERO;
System.out.println("isWithdrawalFeeApplicableForTransfer() = " + isWithdrawalFeeApplicableForTransfer());
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 +2730,28 @@ public class SavingsAccount extends AbstractPersistableCustom<Long> {
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;
}
@@ -38,11 +38,13 @@ import javax.persistence.ManyToOne;
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 class SavingsAccountCharge extends AbstractPersistableCustom<Long> {
.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();
@@ -53,4 +53,6 @@ public interface SavingsAccountRepository extends JpaRepository<SavingsAccount,
SavingsAccount findNonClosedAccountByAccountNumber(@Param("accountNumber") String accountNumber);
Page<SavingsAccount> findByStatus(Integer status,Pageable pageable);
SavingsAccount findByExternalId(String externalId);
}
@@ -71,7 +71,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom<L
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@ManyToOne(optional = true)
@ManyToOne(cascade = CascadeType.ALL, optional = true)
@JoinColumn(name = "payment_detail_id", nullable = true)
private PaymentDetail paymentDetail;
@@ -31,11 +31,15 @@ import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.savings.domain.interest.CompoundInterestHelper;
import org.apache.fineract.portfolio.savings.domain.interest.PostingPeriod;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public final class SavingsHelper {
AccountTransfersReadPlatformService accountTransfersReadPlatformService = null;
@Autowired
public SavingsHelper(AccountTransfersReadPlatformService accountTransfersReadPlatformService) {
this.accountTransfersReadPlatformService = accountTransfersReadPlatformService;
}
@@ -25,4 +25,8 @@ public class SavingsAccountNotFoundException extends AbstractPlatformResourceNot
public SavingsAccountNotFoundException(final Long id) {
super("error.msg.saving.account.id.invalid", "Savings account with identifier " + id + " does not exist", id);
}
public SavingsAccountNotFoundException(final String externalId) {
super("error.msg.saving.account.id.invalid", "Savings account with external identifier " + externalId + " does not exist", externalId);
}
}