FINERACT-1932: Fineract modularization - custom modules

This commit is contained in:
Marta Jankovics
2024-06-10 15:39:13 +02:00
committed by Adam Saghy
parent 92e291a9e4
commit 655466785c
101 changed files with 239 additions and 194 deletions
@@ -23,8 +23,8 @@ import java.util.List;
import org.apache.fineract.accounting.glaccount.domain.GLAccountType;
import org.apache.fineract.accounting.glaccount.domain.GLAccountUsage;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.PortfolioProductType;
public final class AccountingEnumerations {
@@ -0,0 +1,45 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.codes.data;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* Immutable data object representing a code.
*/
@Data
@NoArgsConstructor
@Accessors(chain = true)
public final class CodeData implements Serializable {
private Long id;
@SuppressWarnings("unused")
private String name;
@SuppressWarnings("unused")
private boolean systemDefined;
public static CodeData instance(final Long id, final String name, final boolean systemDefined) {
return new CodeData().setId(id).setName(name).setSystemDefined(systemDefined);
}
}
@@ -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.infrastructure.codes.service;
import java.util.Collection;
import org.apache.fineract.infrastructure.codes.data.CodeData;
public interface CodeReadPlatformService {
Collection<CodeData> retrieveAllCodes();
CodeData retrieveCode(Long codeId);
CodeData retriveCode(String codeName);
}
@@ -0,0 +1,36 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.core.exception;
import org.springframework.beans.BeansException;
public class ResourceNotFoundException extends AbstractPlatformResourceNotFoundException {
public ResourceNotFoundException(String globalisationMessageCode, String defaultUserMessage, Object[] defaultUserMessageArgs) {
super(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs);
}
public ResourceNotFoundException() {
super("", "", "");
}
public ResourceNotFoundException(BeansException e) {
super("", "", "", e);
}
}
@@ -0,0 +1,382 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.core.serialization;
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.BOOLEAN;
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.DATE;
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.DATETIME;
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.DECIMAL;
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.INTEGER;
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.JSON;
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.TEXT;
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.TIMESTAMP;
import static org.apache.fineract.infrastructure.core.service.database.JdbcJavaType.VARCHAR;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_AFTER;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_CODE;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_INDEXED;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_LENGTH;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_MANDATORY;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_NAME;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_NEWCODE;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_NEWNAME;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_BOOLEAN;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_DATE;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_DATETIME;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_DECIMAL;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_DROPDOWN;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_JSON;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_NUMBER;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_STRING;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_TEXT;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_TYPE_TIMESTAMP;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_FIELD_UNIQUE;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_ADDCOLUMNS;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_APPTABLE_NAME;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_CHANGECOLUMNS;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_COLUMNS;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_DATATABLE_NAME;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_DROPCOLUMNS;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_MULTIROW;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.API_PARAM_SUBTYPE;
import static org.apache.fineract.infrastructure.dataqueries.api.DataTableApiConstant.TABLE_FIELD_ID;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import jakarta.validation.constraints.NotNull;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
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.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
import org.apache.fineract.infrastructure.core.service.database.JdbcJavaType;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class DatatableCommandFromApiJsonDeserializer {
public static final String DATATABLE_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,48}[a-zA-Z0-9]$";
public static final String DATATABLE_COLUMN_NAME_REGEX_PATTERN = "^[a-zA-Z][a-zA-Z0-9\\-_\\s]{0,}[a-zA-Z0-9]$";
/**
* The parameters supported for this command.
*/
private static final Set<String> SUPPORTED_PARAMETERS_FOR_CREATE = Set.of(API_PARAM_DATATABLE_NAME, API_PARAM_SUBTYPE,
API_PARAM_APPTABLE_NAME, API_PARAM_MULTIROW, API_PARAM_COLUMNS);
private static final Set<String> SUPPORTED_PARAMETERS_FOR_CREATE_COLUMNS = Set.of(API_FIELD_NAME, API_FIELD_TYPE, API_FIELD_LENGTH,
API_FIELD_MANDATORY, API_FIELD_CODE, API_FIELD_UNIQUE, API_FIELD_INDEXED);
private static final Set<String> SUPPORTED_PARAMETERS_FOR_UPDATE = Set.of(API_PARAM_APPTABLE_NAME, API_PARAM_SUBTYPE,
API_PARAM_CHANGECOLUMNS, API_PARAM_ADDCOLUMNS, API_PARAM_DROPCOLUMNS);
private static final Set<String> SUPPORTED_PARAMETERS_FOR_ADD_COLUMNS = Set.of(API_FIELD_NAME, API_FIELD_TYPE, API_FIELD_LENGTH,
API_FIELD_MANDATORY, API_FIELD_AFTER, API_FIELD_CODE, API_FIELD_UNIQUE, API_FIELD_INDEXED);
private static final Set<String> SUPPORTED_PARAMETERS_FOR_CHANGE_COLUMNS = Set.of(API_FIELD_NAME, API_FIELD_NEWNAME, API_FIELD_LENGTH,
API_FIELD_MANDATORY, API_FIELD_AFTER, API_FIELD_CODE, API_FIELD_NEWCODE, API_FIELD_UNIQUE, API_FIELD_INDEXED);
private static final Set<String> SUPPORTED_PARAMETERS_FOR_DROP_COLUMNS = Set.of(API_FIELD_NAME);
private static final Object[] SUPPORTED_COLUMN_TYPES = { API_FIELD_TYPE_STRING, API_FIELD_TYPE_NUMBER, API_FIELD_TYPE_BOOLEAN,
API_FIELD_TYPE_DECIMAL, API_FIELD_TYPE_DATE, API_FIELD_TYPE_DATETIME, API_FIELD_TYPE_TEXT, API_FIELD_TYPE_JSON,
API_FIELD_TYPE_DROPDOWN };
private final FromJsonHelper fromApiJsonHelper;
private final DatabaseTypeResolver databaseTypeResolver;
@Autowired
public DatatableCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper, DatabaseTypeResolver databaseTypeResolver) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.databaseTypeResolver = databaseTypeResolver;
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_PARAMETERS_FOR_CREATE);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable");
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String datatableName = this.fromApiJsonHelper.extractStringNamed(API_PARAM_DATATABLE_NAME, element);
baseDataValidator.reset().parameter(API_PARAM_DATATABLE_NAME).value(datatableName).notBlank().notExceedingLengthOf(50)
.matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
final String apptableName = this.fromApiJsonHelper.extractStringNamed(API_PARAM_APPTABLE_NAME, element);
baseDataValidator.reset().parameter(API_PARAM_APPTABLE_NAME).value(apptableName).notBlank().notExceedingLengthOf(50)
.isOneOfTheseStringValues(EntityTables.getEntityNames());
EntityTables entityTable = EntityTables.fromEntityName(apptableName);
validateEntitySubType(baseDataValidator, element, entityTable);
final String fkColumnName = entityTable == null ? null : entityTable.getForeignKeyColumnNameOnDatatable();
final Boolean multiRow = this.fromApiJsonHelper.extractBooleanNamed(API_PARAM_MULTIROW, element);
baseDataValidator.reset().parameter(API_PARAM_MULTIROW).value(multiRow).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
final JsonArray columns = this.fromApiJsonHelper.extractJsonArrayNamed(API_PARAM_COLUMNS, element);
baseDataValidator.reset().parameter(API_PARAM_COLUMNS).value(columns).notNull().jsonArrayNotEmpty();
if (columns != null) {
for (final JsonElement column : columns) {
this.fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), SUPPORTED_PARAMETERS_FOR_CREATE_COLUMNS);
final String name = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NAME, column);
baseDataValidator.reset().parameter(API_FIELD_NAME).value(name).notBlank()
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
validateType(baseDataValidator, column);
final Boolean mandatory = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_MANDATORY, column);
final Boolean unique = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_UNIQUE, column);
final Boolean indexed = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_INDEXED, column);
baseDataValidator.reset().parameter(API_FIELD_MANDATORY).value(mandatory).ignoreIfNull().notBlank().isOneOfTheseValues(true,
false);
baseDataValidator.reset().parameter(API_FIELD_UNIQUE).value(unique).ignoreIfNull().notBlank().isOneOfTheseValues(true,
false);
baseDataValidator.reset().parameter(API_FIELD_INDEXED).value(indexed).ignoreIfNull().notBlank().isOneOfTheseValues(true,
false);
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
// Because all parameters are optional, a check to see if at least one
// parameter
// has been specified is necessary in order to avoid JSON requests with
// no parameters
if (!json.matches("(?s)\\A\\{.*?(\\\".*?\\\"\\s*?:\\s*?)+.*?\\}\\z")) { // NOSONAR
throw new PlatformDataIntegrityException("error.msg.invalid.request.body.no.parameters",
"Provided JSON request body does not have any parameters.");
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_PARAMETERS_FOR_UPDATE);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("datatable");
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String apptableName = this.fromApiJsonHelper.extractStringNamed(API_PARAM_APPTABLE_NAME, element);
baseDataValidator.reset().parameter(API_PARAM_APPTABLE_NAME).value(apptableName).ignoreIfNull().notBlank()
.isOneOfTheseStringValues(EntityTables.getEntityNames());
EntityTables entityTable = EntityTables.fromEntityName(apptableName);
validateEntitySubType(baseDataValidator, element, entityTable);
final String fkColumnName = entityTable.getForeignKeyColumnNameOnDatatable();
final JsonArray changeColumns = this.fromApiJsonHelper.extractJsonArrayNamed(API_PARAM_CHANGECOLUMNS, element);
baseDataValidator.reset().parameter(API_PARAM_CHANGECOLUMNS).value(changeColumns).ignoreIfNull().jsonArrayNotEmpty();
if (changeColumns != null) {
for (final JsonElement column : changeColumns) {
this.fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), SUPPORTED_PARAMETERS_FOR_CHANGE_COLUMNS);
final String name = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NAME, column);
baseDataValidator.reset().parameter(API_FIELD_NAME).value(name).notBlank()
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
final String newName = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NEWNAME, column);
baseDataValidator.reset().parameter(API_FIELD_NEWNAME).value(newName).ignoreIfNull().notBlank().notExceedingLengthOf(50)
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
if (this.fromApiJsonHelper.parameterExists(API_FIELD_LENGTH, column)) {
final String lengthStr = this.fromApiJsonHelper.extractStringNamed(API_FIELD_LENGTH, column);
if (StringUtils.isWhitespace(lengthStr) || !StringUtils.isNumeric(lengthStr) || StringUtils.isBlank(lengthStr)) {
baseDataValidator.reset().parameter(API_FIELD_LENGTH).failWithCode("not.greater.than.zero");
} else {
final Integer length = Integer.parseInt(lengthStr);
baseDataValidator.reset().parameter(API_FIELD_LENGTH).value(length).ignoreIfNull().notBlank().positiveAmount();
}
}
final String code = this.fromApiJsonHelper.extractStringNamed(API_FIELD_CODE, column);
baseDataValidator.reset().parameter(API_FIELD_CODE).value(code).ignoreIfNull().notBlank().notExceedingLengthOf(100)
.matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
final String newCode = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NEWCODE, column);
baseDataValidator.reset().parameter(API_FIELD_NEWCODE).value(newCode).ignoreIfNull().notBlank().notExceedingLengthOf(100)
.matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
if (StringUtils.isBlank(code) && StringUtils.isNotBlank(newCode)) {
baseDataValidator.reset().parameter(API_FIELD_CODE).value(code).cantBeBlankWhenParameterProvidedIs(API_FIELD_NEWCODE,
newCode);
}
final Boolean mandatory = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_MANDATORY, column);
baseDataValidator.reset().parameter(API_FIELD_MANDATORY).value(mandatory).ignoreIfNull().notBlank().isOneOfTheseValues(true,
false);
final Boolean after = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_AFTER, column);
baseDataValidator.reset().parameter(API_FIELD_AFTER).value(after).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
final Boolean unique = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_UNIQUE, column);
baseDataValidator.reset().parameter(API_FIELD_UNIQUE).value(unique).ignoreIfNull().notBlank().isOneOfTheseValues(true,
false);
final Boolean indexed = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_INDEXED, column);
baseDataValidator.reset().parameter(API_FIELD_INDEXED).value(indexed).ignoreIfNull().notBlank().isOneOfTheseValues(true,
false);
}
}
final JsonArray addColumns = this.fromApiJsonHelper.extractJsonArrayNamed(API_PARAM_ADDCOLUMNS, element);
baseDataValidator.reset().parameter(API_PARAM_ADDCOLUMNS).value(addColumns).ignoreIfNull().jsonArrayNotEmpty();
if (addColumns != null) {
for (final JsonElement column : addColumns) {
this.fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), SUPPORTED_PARAMETERS_FOR_ADD_COLUMNS);
final String name = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NAME, column);
baseDataValidator.reset().parameter(API_FIELD_NAME).value(name).notBlank()
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
validateType(baseDataValidator, column);
final Boolean mandatory = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_MANDATORY, column);
baseDataValidator.reset().parameter(API_FIELD_MANDATORY).value(mandatory).ignoreIfNull().notBlank().isOneOfTheseValues(true,
false);
final Boolean after = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_AFTER, column);
baseDataValidator.reset().parameter(API_FIELD_AFTER).value(after).ignoreIfNull().notBlank().isOneOfTheseValues(true, false);
final Boolean unique = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_UNIQUE, column);
baseDataValidator.reset().parameter(API_FIELD_UNIQUE).value(unique).ignoreIfNull().notBlank().isOneOfTheseValues(true,
false);
final Boolean indexed = this.fromApiJsonHelper.extractBooleanNamed(API_FIELD_INDEXED, column);
baseDataValidator.reset().parameter(API_FIELD_INDEXED).value(indexed).ignoreIfNull().notBlank().isOneOfTheseValues(true,
false);
}
}
final JsonArray dropColumns = this.fromApiJsonHelper.extractJsonArrayNamed(API_PARAM_DROPCOLUMNS, element);
baseDataValidator.reset().parameter(API_PARAM_DROPCOLUMNS).value(dropColumns).ignoreIfNull().jsonArrayNotEmpty();
if (dropColumns != null) {
for (final JsonElement column : dropColumns) {
this.fromApiJsonHelper.checkForUnsupportedParameters(column.getAsJsonObject(), SUPPORTED_PARAMETERS_FOR_DROP_COLUMNS);
final String name = this.fromApiJsonHelper.extractStringNamed(API_FIELD_NAME, column);
baseDataValidator.reset().parameter(API_FIELD_NAME).value(name).notBlank()
.isNotOneOfTheseValues(TABLE_FIELD_ID, fkColumnName).matchesRegularExpression(DATATABLE_COLUMN_NAME_REGEX_PATTERN);
}
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void validateType(DataValidatorBuilder validator, final JsonElement column) {
final String type = this.fromApiJsonHelper.extractStringNamed(API_FIELD_TYPE, column);
validator.reset().parameter(API_FIELD_TYPE).value(type).notBlank().isOneOfTheseStringValues(SUPPORTED_COLUMN_TYPES);
if (type == null) {
return;
}
JdbcJavaType jdbcType = mapApiTypeToJdbcType(type, false);
if (jdbcType != null) {
final Integer length = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(API_FIELD_LENGTH, column);
validator = validator.reset().parameter(API_FIELD_LENGTH).value(length);
if (jdbcType.hasPrecision(databaseTypeResolver.databaseType())) {
if (jdbcType.isStringType() && length == null) {
validator.failWithCode("must.be.provided.when.type.is.String");
}
validator.ignoreIfNull().positiveAmount();
} // else, the precision is ignored
}
final String code = this.fromApiJsonHelper.extractStringNamed(API_FIELD_CODE, column);
if (type.equalsIgnoreCase(API_FIELD_TYPE_DROPDOWN)) {
if (code != null) {
validator.reset().parameter(API_FIELD_CODE).value(code).notBlank().matchesRegularExpression(DATATABLE_NAME_REGEX_PATTERN);
} else {
validator.reset().parameter(API_FIELD_CODE).value(code).cantBeBlankWhenParameterProvidedIs(API_FIELD_TYPE, type);
}
} else {
validator.reset().parameter(API_FIELD_CODE).value(code).mustBeBlankWhenParameterProvided(API_FIELD_TYPE, type);
}
}
private static JdbcJavaType mapApiTypeToJdbcType(@NotNull String apiType, boolean fail) {
switch (apiType.toLowerCase()) {
case API_FIELD_TYPE_STRING:
return VARCHAR;
case API_FIELD_TYPE_NUMBER, API_FIELD_TYPE_DROPDOWN:
return INTEGER;
case API_FIELD_TYPE_BOOLEAN:
return BOOLEAN;
case API_FIELD_TYPE_DECIMAL:
return DECIMAL;
case API_FIELD_TYPE_DATE:
return DATE;
case API_FIELD_TYPE_DATETIME:
return DATETIME;
case API_FIELD_TYPE_TIMESTAMP:
return TIMESTAMP;
case API_FIELD_TYPE_TEXT:
return TEXT;
case API_FIELD_TYPE_JSON:
return JSON;
default: {
if (fail) {
throw new PlatformDataIntegrityException("error.msg.datatable.column.type.invalid",
"Column type " + apiType + " is not supported.");
} else {
return null;
}
}
}
}
@NotNull
public static JdbcJavaType mapApiTypeToJdbcType(@NotNull String apiType) {
return mapApiTypeToJdbcType(apiType, true);
}
private void validateEntitySubType(final DataValidatorBuilder baseDataValidator, final JsonElement element,
final EntityTables entityTable) {
if (entityTable == EntityTables.CLIENT) {
String entitySubType = this.fromApiJsonHelper.extractStringNamed(API_PARAM_SUBTYPE, element);
baseDataValidator.reset().parameter(API_PARAM_SUBTYPE).value(entitySubType).notBlank(); // Person or Entity
}
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
}
}
@@ -0,0 +1,38 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.dataqueries.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link AbstractPlatformDomainRuleException} thrown when datatable resources are not found.
*/
public class DatatableEntryRequiredException extends AbstractPlatformDomainRuleException {
public DatatableEntryRequiredException(String datatableName) {
super("error.msg.entry.required.in.datatable." + datatableName,
"The datatable " + datatableName + " needs to be filled in before the current action can be proceeded", datatableName);
}
public DatatableEntryRequiredException(String datatableName, Long appTableId) {
super("error.msg.entry.cannot.be.deleted.datatable." + datatableName + ".attached.to.entity.datatable.check",
"The entry cannot be deleted, due to datatable " + datatableName + " is attached to an Entity-Datatable check",
datatableName, appTableId);
}
}
@@ -0,0 +1,44 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.dataqueries.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
/**
* A {@link RuntimeException} thrown when datatable resources are not found.
*/
public class DatatableNotFoundException extends AbstractPlatformResourceNotFoundException {
public DatatableNotFoundException(final String datatable, final Long id) {
super("error.msg.datatable.data.not.found", "Data not found for datatable: ", datatable + " Id:" + id);
}
public DatatableNotFoundException(final String datatable) {
super("error.msg.datatable.not.found", "Datatable not found.", datatable);
}
public DatatableNotFoundException(final EntityTables entityTable, final Long id) {
this(entityTable.getName(), id);
}
public DatatableNotFoundException(final EntityTables entityTable) {
this(entityTable.getName());
}
}
@@ -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.infrastructure.dataqueries.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class DatatableSystemErrorException extends AbstractPlatformDomainRuleException {
public DatatableSystemErrorException(final String msg) {
super("error.msg.datatable.system.error", msg, msg);
}
}
@@ -0,0 +1,33 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.dataqueries.service;
import org.springframework.stereotype.Component;
@Component
public class DatatableKeywordGenerator {
public String generateIndexName(String tableName, String columnName) {
return "idx_%s_%s".formatted(tableName, columnName);
}
public String generateUniqueKeyName(String tableName, String columnName) {
return "uk_%s_%s".formatted(tableName, columnName);
}
}
@@ -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.infrastructure.security.service;
public interface SqlInjectionPreventerService {
String encodeSql(String literal);
}
@@ -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.domain;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.Getter;
@Getter
public enum InteropIdentifierType {
MSISDN("interopIdentifierType.msisdn"), //
EMAIL("interopIdentifierType.email"), //
PERSONAL_ID("interopIdentifierType.personalId", "PERSONALID", "PERSONALID"), //
BUSINESS("interopIdentifierType.business"), //
DEVICE("interopIdentifierType.device"), //
ACCOUNT_ID("interopIdentifierType.accountId", "ACCOUNTID", "ACCOUNTID"), //
IBAN("interopIdentifierType.iban"), //
ALIAS("interopIdentifierType.alias"), //
BBAN("interopIdentifierType.bban"), //
; //
public static final InteropIdentifierType[] VALUES = values();
private static final Map<String, InteropIdentifierType> BY_ALIAS = Arrays.stream(VALUES)
.collect(Collectors.toMap(InteropIdentifierType::getAlias, v -> v));
private static final Map<String, InteropIdentifierType> BY_NAME = Arrays.stream(VALUES)
.collect(Collectors.toMap(InteropIdentifierType::name, v -> v));
private final String code;
private final String description;
private final String alias;
InteropIdentifierType(String code, String description, String alias) {
this.code = code;
this.description = description == null ? name() : description;
this.alias = alias == null ? name() : alias;
}
InteropIdentifierType(String code) {
this(code, null, null);
}
public static InteropIdentifierType resolveName(String name) {
if (name == null) {
return null;
}
InteropIdentifierType idType = BY_ALIAS.get(name);
return idType == null ? BY_NAME.get(name) : idType;
}
}
@@ -0,0 +1,50 @@
/**
* 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.organisation.monetary.data;
import java.math.BigDecimal;
/**
* Immutable data object representing currency.
*/
public class MoneyData {
private final String code;
private final BigDecimal amount;
private final int decimalPlaces;
public MoneyData(final String code, final BigDecimal amount, final int decimalPlaces) {
this.code = code;
this.amount = amount;
this.decimalPlaces = decimalPlaces;
}
public String getCode() {
return this.code;
}
public BigDecimal getAmount() {
return this.amount;
}
public int getDecimalPlaces() {
return this.decimalPlaces;
}
}
@@ -23,7 +23,6 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.office.domain.OrganisationCurrency;
@Entity
@Table(name = "m_currency")
@@ -0,0 +1,43 @@
/**
* 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.organisation.monetary.domain;
import java.util.List;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface ApplicationCurrencyRepository
extends JpaRepository<ApplicationCurrency, Long>, JpaSpecificationExecutor<ApplicationCurrency> {
String FIND_CURRENCY_DETAILS = "SELECT new org.apache.fineract.organisation.monetary.data.CurrencyData(ac.code, ac.name, ac.decimalPlaces, ac.inMultiplesOf, ac.displaySymbol, ac.nameCode) FROM ApplicationCurrency ac ";
ApplicationCurrency findOneByCode(String currencyCode);
@Query(FIND_CURRENCY_DETAILS + " WHERE ac.code = :code")
CurrencyData findCurrencyDataByCode(@Param("code") String currencyCode);
@Query(FIND_CURRENCY_DETAILS)
List<CurrencyData> findAllSorted(Sort sort);
}
@@ -0,0 +1,69 @@
/**
* 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.organisation.monetary.domain;
import org.apache.fineract.organisation.monetary.exception.CurrencyNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link ApplicationCurrencyRepository} that is responsible for checking if {@link ApplicationCurrency} is
* returned when using <code>findOne</code> repository method and throwing an appropriate not found exception.
* </p>
*
* <p>
* This is to avoid need for checking and throwing in multiple areas of code base where
* {@link ApplicationCurrencyRepository} is required.
* </p>
*/
@Service
public class ApplicationCurrencyRepositoryWrapper {
private final ApplicationCurrencyRepository repository;
@Autowired
public ApplicationCurrencyRepositoryWrapper(final ApplicationCurrencyRepository repository) {
this.repository = repository;
}
public ApplicationCurrency findOneWithNotFoundDetection(final MonetaryCurrency currency) {
final ApplicationCurrency defaultApplicationCurrency = this.repository.findOneByCode(currency.getCode());
if (defaultApplicationCurrency == null) {
throw new CurrencyNotFoundException(currency.getCode());
}
final ApplicationCurrency applicationCurrency = ApplicationCurrency.from(defaultApplicationCurrency,
currency.getDigitsAfterDecimal(), currency.getCurrencyInMultiplesOf());
return applicationCurrency;
}
/**
* Used when its not needed for {@link ApplicationCurrency} to inherit decimal place settings of existing currency.
*/
public ApplicationCurrency findOneWithNotFoundDetection(final String currencyCode) {
final ApplicationCurrency applicationCurrency = this.repository.findOneByCode(currencyCode);
if (applicationCurrency == null) {
throw new CurrencyNotFoundException(currencyCode);
}
return applicationCurrency;
}
}
@@ -16,13 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.organisation.office.domain;
package org.apache.fineract.organisation.monetary.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
/**
* Represents currencies allowed for this MFI/organisation.
@@ -0,0 +1,39 @@
/**
* 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.organisation.monetary.domain;
import java.util.List;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
@Repository
public interface OrganisationCurrencyRepository
extends JpaRepository<OrganisationCurrency, Long>, JpaSpecificationExecutor<OrganisationCurrency> {
String FIND_CURRENCY_DETAILS = "SELECT new org.apache.fineract.organisation.monetary.data.CurrencyData(oc.code, oc.name, oc.decimalPlaces, oc.inMultiplesOf, oc.displaySymbol, oc.nameCode) FROM OrganisationCurrency oc ";
OrganisationCurrency findOneByCode(String currencyCode);
@Query(FIND_CURRENCY_DETAILS)
List<CurrencyData> findAllSorted(Sort sort);
}
@@ -0,0 +1,53 @@
/**
* 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.organisation.monetary.domain;
import org.apache.fineract.organisation.monetary.exception.OrganizationalCurrencyNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link OrganisationCurrency} that is responsible for checking if {@link OrganisationCurrency} is returned
* when using <code>findOne</code> repository method and throwing an appropriate not found exception.
* </p>
*
* <p>
* This is to avoid need for checking and throwing in multiple areas of code base where {@link OrganisationCurrency} is
* required.
* </p>
*/
@Service
public class OrganisationCurrencyRepositoryWrapper {
private final OrganisationCurrencyRepository repository;
@Autowired
public OrganisationCurrencyRepositoryWrapper(final OrganisationCurrencyRepository repository) {
this.repository = repository;
}
public OrganisationCurrency findOneWithNotFoundDetection(final String currencyCode) {
final OrganisationCurrency organisationCurrency = this.repository.findOneByCode(currencyCode);
if (organisationCurrency == null) {
throw new OrganizationalCurrencyNotFoundException(currencyCode);
}
return organisationCurrency;
}
}
@@ -0,0 +1,33 @@
/**
* 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.organisation.monetary.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when trying to delete a currency that is still in use.
*/
public class CurrencyInUseException extends AbstractPlatformDomainRuleException {
public CurrencyInUseException(final String currencyCode) {
super("error.msg.currency.currencyCode.inUse",
"Cannot remove currency with identifier " + currencyCode + " while it is still in use", currencyCode);
}
}
@@ -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.organisation.monetary.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when loan resources are not found.
*/
public class CurrencyNotFoundException extends AbstractPlatformResourceNotFoundException {
public CurrencyNotFoundException(final String currencyCode) {
super("error.msg.currency.currencyCode.invalid", "Currency with identifier " + currencyCode + " does not exist", currencyCode);
}
}
@@ -0,0 +1,32 @@
/**
* 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.organisation.monetary.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when urrency is not supported by an Organization.
*/
public class OrganizationalCurrencyNotFoundException extends AbstractPlatformResourceNotFoundException {
public OrganizationalCurrencyNotFoundException(final String currencyCode) {
super("error.msg.currency.currencyCode.invalid.or.not.supported",
"Currency with identifier " + currencyCode + " does not exist or is not supported by the Organization", currencyCode);
}
}
@@ -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.organisation.monetary.serialization;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public final class CurrencyCommandFromApiJsonDeserializer {
public static final String CURRENCIES = "currencies";
/**
* The parameters supported for this command.
*/
private static final Set<String> SUPPORTED_PARAMETERS = new HashSet<>(List.of(CURRENCIES));
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public CurrencyCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SUPPORTED_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(CURRENCIES);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final String[] currencies = this.fromApiJsonHelper.extractArrayNamed(CURRENCIES, element);
baseDataValidator.reset().parameter(CURRENCIES).value(currencies).arrayNotEmpty();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
}
}
@@ -0,0 +1,83 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.organisation.monetary.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@RequiredArgsConstructor
public class CurrencyReadPlatformServiceImpl implements CurrencyReadPlatformService {
private final PlatformSecurityContext context;
private final JdbcTemplate jdbcTemplate;
private final CurrencyMapper currencyRowMapper = new CurrencyMapper();
@Override
public Collection<CurrencyData> retrieveAllowedCurrencies() {
this.context.authenticatedUser();
final String sql = "select " + this.currencyRowMapper.schema() + " from m_organisation_currency c order by c.name";
return this.jdbcTemplate.query(sql, this.currencyRowMapper); // NOSONAR
}
@Override
public Collection<CurrencyData> retrieveAllPlatformCurrencies() {
final String sql = "select " + this.currencyRowMapper.schema() + " from m_currency c order by c.name";
return this.jdbcTemplate.query(sql, this.currencyRowMapper); // NOSONAR
}
@Override
public CurrencyData retrieveCurrency(final String code) {
final String sql = "select " + this.currencyRowMapper.schema() + " from m_currency c where c.code = ? order by c.name";
return this.jdbcTemplate.queryForObject(sql, this.currencyRowMapper, new Object[] { code }); // NOSONAR
}
private static final class CurrencyMapper implements RowMapper<CurrencyData> {
@Override
public CurrencyData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final String code = rs.getString("code");
final String name = rs.getString("name");
final int decimalPlaces = JdbcSupport.getInteger(rs, "decimalPlaces");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final String displaySymbol = rs.getString("displaySymbol");
final String nameCode = rs.getString("nameCode");
return new CurrencyData(code, name, decimalPlaces, inMultiplesOf, displaySymbol, nameCode);
}
public String schema() {
return " c.code as code, c.name as name, c.decimal_places as decimalPlaces,c.currency_multiplesof as inMultiplesOf, c.display_symbol as displaySymbol, c.internationalized_name_code as nameCode ";
}
}
}
@@ -0,0 +1,42 @@
/**
* 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.organisation.monetary.service;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.organisation.monetary.data.ApplicationCurrencyConfigurationData;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
@RequiredArgsConstructor
public class OrganisationCurrencyReadPlatformServiceImpl implements OrganisationCurrencyReadPlatformService {
private final CurrencyReadPlatformService currencyReadPlatformService;
@Override
public ApplicationCurrencyConfigurationData retrieveCurrencyConfiguration() {
final Collection<CurrencyData> selectedCurrencyOptions = this.currencyReadPlatformService.retrieveAllowedCurrencies();
final Collection<CurrencyData> currencyOptions = this.currencyReadPlatformService.retrieveAllPlatformCurrencies();
// remove selected currency options
currencyOptions.removeAll(selectedCurrencyOptions);
return new ApplicationCurrencyConfigurationData(currencyOptions, selectedCurrencyOptions);
}
}
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.producttoaccountmapping.domain;
package org.apache.fineract.portfolio;
public enum PortfolioProductType {
@@ -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.portfolio.client.domain;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.portfolio.client.domain.search.SearchingClientRepository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface ClientRepository extends JpaRepository<Client, Long>, JpaSpecificationExecutor<Client>, SearchingClientRepository {
String FIND_CLIENT_BY_ACCOUNT_NUMBER = "select client from Client client where client.accountNumber = :accountNumber";
@Query(FIND_CLIENT_BY_ACCOUNT_NUMBER)
Client getClientByAccountNumber(@Param("accountNumber") String accountNumber);
@Query("""
SELECT client
FROM Client client
JOIN client.office office
LEFT JOIN client.transferToOffice transferToOffice
WHERE client.id = :clientId
AND (office.hierarchy LIKE :officeHierarchy OR transferToOffice.hierarchy LIKE :transferToOfficeHierarchy)
""")
Client fetchByClientIdAndHierarchy(@Param("clientId") Long clientId, @Param("officeHierarchy") String officeHierarchy,
@Param("transferToOfficeHierarchy") String transferToOfficeHierarchy);
@Query("SELECT c.id FROM Client c WHERE c.externalId = :externalId")
Long findIdByExternalId(@Param("externalId") ExternalId externalId);
}
@@ -0,0 +1,41 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.client.domain.search;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
@Getter
@RequiredArgsConstructor
public class SearchedClient {
private final Long id;
private final String displayName;
private final ExternalId externalId;
private final String accountNumber;
private final Long officeId;
private final String officeName;
private final String mobileNo;
private final Integer status;
private final LocalDate activationDate;
private final OffsetDateTime createdDate;
}
@@ -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.portfolio.client.domain.search;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
public interface SearchingClientRepository {
Page<SearchedClient> searchByText(String searchText, Pageable pageable, String officeHierarchy);
}
@@ -0,0 +1,93 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.client.domain.search;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Order;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.jpa.CriteriaQueryFactory;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientIdentifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class SearchingClientRepositoryImpl implements SearchingClientRepository {
private final EntityManager entityManager;
private final CriteriaQueryFactory criteriaQueryFactory;
@Override
public Page<SearchedClient> searchByText(String searchText, Pageable pageable, String officeHierarchy) {
/*
* this whole thing can be replaced with Spring Data JPA 3+ with a findBy(Specification, Pageable) call but at
* this point the upgrade is too costly
*
* https://github.com/spring-projects/spring-data-jpa/issues/2499
*/
String hierarchyLikeValue = officeHierarchy + "%";
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<SearchedClient> query = cb.createQuery(SearchedClient.class);
Root<Client> root = query.from(Client.class);
Path<Office> office = root.get("office");
Specification<Client> spec = (r, q, builder) -> {
Path<Office> o = r.get("office");
Join<Client, ClientIdentifier> identity = r.join("identifiers", JoinType.LEFT);
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.like(o.get("hierarchy"), hierarchyLikeValue));
String searchLikeValue = "%" + searchText + "%";
predicates.add(cb.or(cb.like(r.get("accountNumber"), searchLikeValue), cb.like(r.get("displayName"), searchLikeValue),
cb.like(r.get("externalId"), searchLikeValue), cb.like(r.get("mobileNo"), searchLikeValue),
cb.like(identity.get("documentKey"), searchLikeValue)));
return cb.and(predicates.toArray(new Predicate[0]));
};
criteriaQueryFactory.applySpecificationToCriteria(root, spec, query);
List<Order> orders = criteriaQueryFactory.ordersFromPageable(pageable, cb, root, () -> cb.desc(root.get("id")));
query.orderBy(orders);
query.select(cb.construct(SearchedClient.class, root.get("id"), root.get("displayName"), root.get("externalId"),
root.get("accountNumber"), office.get("id"), office.get("name"), root.get("mobileNo"), root.get("status"),
root.get("activationDate"), root.get("createdDate")));
TypedQuery<SearchedClient> queryToExecute = entityManager.createQuery(query);
return criteriaQueryFactory.readPage(queryToExecute, Client.class, pageable, spec);
}
}
@@ -0,0 +1,29 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.client.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class ClientNotActiveException extends AbstractPlatformDomainRuleException {
public ClientNotActiveException(final Long clientId) {
super("error.msg.client.not.active.exception", "The Client with id `" + clientId + "` is not active", clientId);
}
}
@@ -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.portfolio.client.exception;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException;
/**
* A {@link RuntimeException} thrown when client resources are not found.
*/
public class ClientNotFoundException extends AbstractPlatformResourceNotFoundException {
public ClientNotFoundException(final Long id) {
super("error.msg.client.id.invalid", "Client with identifier " + id + " does not exist", id);
}
public ClientNotFoundException(final ExternalId externalId) {
super("error.msg.client.id.invalid", "Client with identifier " + externalId + " does not exist", externalId);
}
public ClientNotFoundException() {
super("error.msg.client.not.found.with.basic.details", "Client not found with basic details.");
}
public ClientNotFoundException(String value, String valueType) {
super("error.msg.client.not.found.with." + valueType, "Client not found with valuer " + value + ".", value);
}
public ClientNotFoundException(Long id, EmptyResultDataAccessException e) {
super("error.msg.client.id.invalid", "Client with identifier " + id + " does not exist", id, e);
}
}
@@ -0,0 +1,81 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.note.domain;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
public enum NoteType {
CLIENT(100, "noteType.client", "clients", "Client note"), //
LOAN(200, "noteType.loan", "loans", "Loan note"), //
LOAN_TRANSACTION(300, "noteType.loan.transaction", "loanTransactions", "Loan transaction note"), //
SAVING_ACCOUNT(500, "noteType.saving", "savings", " account note"), //
GROUP(600, "noteType.group", "groups", "Group note"), //
SHARE_ACCOUNT(700, "noteType.shares", "accounts/share", "Share account note"), //
SAVINGS_TRANSACTION(800, "noteType.savings.transaction", "savingsTransactions", "Savings transaction note"), //
;
public static final NoteType[] VALUES = values();
private static final Map<Integer, NoteType> BY_ID = Arrays.stream(VALUES).collect(Collectors.toMap(NoteType::getValue, v -> v));
private static final Map<String, NoteType> BY_API = Arrays.stream(VALUES).collect(Collectors.toMap(NoteType::getApiUrl, v -> v));
private final Integer value;
private final String code;
private final String apiUrl;
private final String description;
NoteType(final Integer value, final String code, final String apiUrl, String description) {
this.value = value;
this.code = code;
this.apiUrl = apiUrl;
this.description = description;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
public String getApiUrl() {
return this.apiUrl;
}
public String getDescription() {
return description;
}
public static NoteType fromInt(int i) {
return BY_ID.get(i);
}
public static NoteType fromApiUrl(final String url) {
return BY_API.get(url);
}
public EnumOptionData toEnumOptionData() {
return new EnumOptionData(getValue().longValue(), getCode(), getDescription());
}
}
@@ -0,0 +1,29 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.note.service;
import java.util.Collection;
import org.apache.fineract.portfolio.note.data.NoteData;
public interface NoteReadPlatformService {
NoteData retrieveNote(Long noteId, Long resourceId, Integer noteTypeId);
Collection<NoteData> retrieveNotesByResource(Long resourceId, Integer noteTypeId);
}
@@ -0,0 +1,34 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.note.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.portfolio.client.domain.Client;
public interface NoteWritePlatformService {
CommandProcessingResult createNote(JsonCommand command);
CommandProcessingResult updateNote(JsonCommand command);
CommandProcessingResult deleteNote(JsonCommand command);
void createAndPersistClientNote(Client client, JsonCommand 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.portfolio.paymentdetail.domain;
import com.google.gson.JsonObject;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.portfolio.paymentdetail.PaymentDetailConstants;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PaymentDetailAssembler {
private final FromJsonHelper fromApiJsonHelper;
private final PaymentTypeRepositoryWrapper repositoryWrapper;
@Autowired
public PaymentDetailAssembler(final FromJsonHelper fromApiJsonHelper, final PaymentTypeRepositoryWrapper repositoryWrapper) {
this.fromApiJsonHelper = fromApiJsonHelper;
this.repositoryWrapper = repositoryWrapper;
}
public PaymentDetail fetchPaymentDetail(final JsonObject json) {
final Long paymentTypeId = this.fromApiJsonHelper.extractLongNamed(PaymentDetailConstants.paymentTypeParamName, json);
if (paymentTypeId == null) {
return null;
}
final PaymentType paymentType = this.repositoryWrapper.findOneWithNotFoundDetection(paymentTypeId);
final String accountNumber = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.accountNumberParamName, json);
final String checkNumber = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.checkNumberParamName, json);
final String routingCode = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.routingCodeParamName, json);
final String receiptNumber = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.receiptNumberParamName, json);
final String bankNumber = this.fromApiJsonHelper.extractStringNamed(PaymentDetailConstants.bankNumberParamName, json);
return PaymentDetail.instance(paymentType, accountNumber, checkNumber, routingCode, receiptNumber, bankNumber);
}
}
@@ -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.portfolio.paymentdetail.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface PaymentDetailRepository extends JpaRepository<PaymentDetail, Long>, JpaSpecificationExecutor<PaymentDetail> {
// no added behaviour
}
@@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymentdetail.service;
import java.util.Map;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
public interface PaymentDetailWritePlatformService {
PaymentDetail createAndPersistPaymentDetail(JsonCommand command, Map<String, Object> changes);
PaymentDetail createPaymentDetail(JsonCommand command, Map<String, Object> changes);
PaymentDetail persistPaymentDetail(PaymentDetail paymentDetail);
}
@@ -0,0 +1,66 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymentdetail.service;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.portfolio.paymentdetail.PaymentDetailConstants;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetailRepository;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
public class PaymentDetailWritePlatformServiceJpaRepositoryImpl implements PaymentDetailWritePlatformService {
private final PaymentDetailRepository paymentDetailRepository;
// private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
private final PaymentTypeRepositoryWrapper paymentTyperepositoryWrapper;
@Override
public PaymentDetail createPaymentDetail(final JsonCommand command, final Map<String, Object> changes) {
final Long paymentTypeId = command.longValueOfParameterNamed(PaymentDetailConstants.paymentTypeParamName);
if (paymentTypeId == null) {
return null;
}
final PaymentType paymentType = this.paymentTyperepositoryWrapper.findOneWithNotFoundDetection(paymentTypeId);
final PaymentDetail paymentDetail = PaymentDetail.generatePaymentDetail(paymentType, command, changes);
return paymentDetail;
}
@Override
@Transactional
public PaymentDetail persistPaymentDetail(final PaymentDetail paymentDetail) {
return this.paymentDetailRepository.saveAndFlush(paymentDetail);
}
@Override
@Transactional
public PaymentDetail createAndPersistPaymentDetail(final JsonCommand command, final Map<String, Object> changes) {
final PaymentDetail paymentDetail = createPaymentDetail(command, changes);
if (paymentDetail != null) {
return persistPaymentDetail(paymentDetail);
}
return paymentDetail;
}
}
@@ -0,0 +1,38 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymentdetail.starter;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetailRepository;
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService;
import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformServiceJpaRepositoryImpl;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PaymentDetailConfiguration {
@Bean
@ConditionalOnMissingBean(PaymentDetailWritePlatformService.class)
PaymentDetailWritePlatformService paymentDetailWritePlatformService(PaymentDetailRepository paymentDetailRepository,
PaymentTypeRepositoryWrapper paymentTyperepositoryWrapper) {
return new PaymentDetailWritePlatformServiceJpaRepositoryImpl(paymentDetailRepository, paymentTyperepositoryWrapper);
}
}
@@ -0,0 +1,157 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import java.util.Collection;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
import org.springframework.stereotype.Component;
@Path("/v1/paymenttypes")
@Component
@Tag(name = "Payment Type", description = "This defines the payment type")
@RequiredArgsConstructor
public class PaymentTypeApiResource {
private final PlatformSecurityContext securityContext;
private final DefaultToApiJsonSerializer<PaymentTypeData> jsonSerializer;
private final PaymentTypeReadPlatformService readPlatformService;
private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper;
@GET
@Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Retrieve all Payment Types", description = "Retrieve list of payment types")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = PaymentTypeApiResourceSwagger.GetPaymentTypesResponse.class)))) })
public String getAllPaymentTypes(@Context final UriInfo uriInfo,
@QueryParam("onlyWithCode") @Parameter(description = "onlyWithCode") final boolean onlyWithCode) {
this.securityContext.authenticatedUser().validateHasReadPermission(PaymentTypeApiResourceConstants.ENTITY_NAME);
Collection<PaymentTypeData> paymentTypes = null;
if (onlyWithCode) {
paymentTypes = this.readPlatformService.retrieveAllPaymentTypesWithCode();
} else {
paymentTypes = this.readPlatformService.retrieveAllPaymentTypes();
}
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.jsonSerializer.serialize(settings, paymentTypes, PaymentTypeApiResourceConstants.RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{paymentTypeId}")
@Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Retrieve a Payment Type", description = "Retrieves a payment type")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = PaymentTypeApiResourceSwagger.GetPaymentTypesPaymentTypeIdResponse.class))) })
public String retrieveOnePaymentType(@PathParam("paymentTypeId") @Parameter(description = "paymentTypeId") final Long paymentTypeId,
@Context final UriInfo uriInfo) {
this.securityContext.authenticatedUser().validateHasReadPermission(PaymentTypeApiResourceConstants.ENTITY_NAME);
this.paymentTypeRepositoryWrapper.findOneWithNotFoundDetection(paymentTypeId);
final PaymentTypeData paymentTypes = this.readPlatformService.retrieveOne(paymentTypeId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.jsonSerializer.serialize(settings, paymentTypes, PaymentTypeApiResourceConstants.RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create a Payment Type", description = "Creates a new Payment type\n\n" + "Mandatory Fields: name\n\n"
+ "Optional Fields: Description, isCashPayment,Position")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = PaymentTypeApiResourceSwagger.PostPaymentTypesRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = PaymentTypeApiResourceSwagger.PostPaymentTypesResponse.class))) })
public String createPaymentType(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createPaymentType().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandWritePlatformService.logCommandSource(commandRequest);
return this.jsonSerializer.serialize(result);
}
@PUT
@Path("{paymentTypeId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update a Payment Type", description = "Updates a Payment Type")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = PaymentTypeApiResourceSwagger.PutPaymentTypesPaymentTypeIdRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = PaymentTypeApiResourceSwagger.PutPaymentTypesPaymentTypeIdResponse.class))) })
public String updatePaymentType(@PathParam("paymentTypeId") @Parameter(description = "paymentTypeId") final Long paymentTypeId,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updatePaymentType(paymentTypeId).withJson(apiRequestBodyAsJson)
.build();
final CommandProcessingResult result = this.commandWritePlatformService.logCommandSource(commandRequest);
return this.jsonSerializer.serialize(result);
}
@DELETE
@Path("{paymentTypeId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Delete a Payment Type", description = "Deletes payment type")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = PaymentTypeApiResourceSwagger.DeletePaymentTypesPaymentTypeIdResponse.class))) })
public String deleteCode(@PathParam("paymentTypeId") @Parameter(description = "paymentTypeId") final Long paymentTypeId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deletePaymentType(paymentTypeId).build();
final CommandProcessingResult result = this.commandWritePlatformService.logCommandSource(commandRequest);
return this.jsonSerializer.serialize(result);
}
}
@@ -0,0 +1,136 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.api;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* Created by Chirag Gupta on 01/01/18.
*/
public final class PaymentTypeApiResourceSwagger {
private PaymentTypeApiResourceSwagger() {}
@Schema(description = "GetPaymentTypesResponse")
public static final class GetPaymentTypesResponse {
private GetPaymentTypesResponse() {}
@Schema(example = "24")
public Long id;
@Schema(example = "PTC")
public String name;
@Schema(example = "Cash")
public String description;
@Schema(example = "true")
public Boolean isCashPayment;
@Schema(example = "0")
public Integer position;
@Schema(example = "REPAYMENT_REFUND")
public String codeName;
@Schema(example = "false")
public Boolean isSystemDefined;
}
@Schema(description = "GetPaymentTypesPaymentTypeIdResponse")
public static final class GetPaymentTypesPaymentTypeIdResponse {
private GetPaymentTypesPaymentTypeIdResponse() {}
@Schema(example = "13")
public Long id;
@Schema(example = "cash")
public String name;
@Schema(example = "cash Payment")
public String description;
@Schema(example = "true")
public Boolean isCashPayment;
@Schema(example = "1")
public Integer position;
@Schema(example = "REPAYMENT_REFUND")
public String codeName;
@Schema(example = "false")
public Boolean isSystemDefined;
}
@Schema(description = "PostPaymentTypesRequest")
public static final class PostPaymentTypesRequest {
private PostPaymentTypesRequest() {}
@Schema(example = "cash")
public String name;
@Schema(example = "cash payment type")
public String description;
@Schema(example = "true")
public Boolean isCashPayment;
@Schema(example = "1")
public Integer position;
@Schema(example = "REPAYMENT_REFUND")
public String codeName;
@Schema(example = "false")
public Boolean isSystemDefined;
}
@Schema(description = "PostPaymentTypesResponse")
public static final class PostPaymentTypesResponse {
private PostPaymentTypesResponse() {}
@Schema(example = "1")
public Long resourceId;
}
@Schema(description = "PutPaymentTypesPaymentTypeIdRequest")
public static final class PutPaymentTypesPaymentTypeIdRequest {
private PutPaymentTypesPaymentTypeIdRequest() {}
@Schema(example = "mPay")
public String name;
@Schema(example = "not a cash payment type")
public String description;
@Schema(example = "false")
public Boolean isCashPayment;
@Schema(example = "3")
public Integer position;
@Schema(example = "REPAYMENT_REFUND")
public String codeName;
@Schema(example = "false")
public Boolean isSystemDefined;
}
@Schema(description = "PutPaymentTypesPaymentTypeIdResponse")
public static final class PutPaymentTypesPaymentTypeIdResponse {
private PutPaymentTypesPaymentTypeIdResponse() {}
@Schema(example = "13")
public Long resourceId;
}
@Schema(description = "DeletePaymentTypesPaymentTypeIdResponse")
public static final class DeletePaymentTypesPaymentTypeIdResponse {
private DeletePaymentTypesPaymentTypeIdResponse() {}
@Schema(example = "13")
public Long resourceId;
}
}
@@ -0,0 +1,176 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.data;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
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.portfolio.paymenttype.api.PaymentTypeApiResourceConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PaymentTypeDataValidator {
private final FromJsonHelper fromApiJsonHelper;
private static final Set<String> CREATE_PAYMENT_TYPE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(PaymentTypeApiResourceConstants.NAME, PaymentTypeApiResourceConstants.DESCRIPTION,
PaymentTypeApiResourceConstants.ISCASHPAYMENT, PaymentTypeApiResourceConstants.POSITION,
PaymentTypeApiResourceConstants.CODE_NAME, PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED));
private static final Set<String> UPDATE_PAYMENT_TYPE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(PaymentTypeApiResourceConstants.NAME, PaymentTypeApiResourceConstants.DESCRIPTION,
PaymentTypeApiResourceConstants.ISCASHPAYMENT, PaymentTypeApiResourceConstants.POSITION,
PaymentTypeApiResourceConstants.CODE_NAME, PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED));
@Autowired
public PaymentTypeDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, CREATE_PAYMENT_TYPE_REQUEST_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(PaymentTypeApiResourceConstants.ENTITY_NAME);
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.NAME, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(PaymentTypeApiResourceConstants.NAME, element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.NAME).value(name).notBlank();
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.DESCRIPTION, element)) {
final String description = this.fromApiJsonHelper.extractStringNamed(PaymentTypeApiResourceConstants.DESCRIPTION, element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.DESCRIPTION).value(description).ignoreIfNull()
.notExceedingLengthOf(500);
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.ISCASHPAYMENT, element)) {
final Boolean isCashPayment = this.fromApiJsonHelper.extractBooleanNamed(PaymentTypeApiResourceConstants.ISCASHPAYMENT,
element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.ISCASHPAYMENT).value(isCashPayment)
.validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.POSITION, element)) {
final Long position = this.fromApiJsonHelper.extractLongNamed(PaymentTypeApiResourceConstants.POSITION, element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.POSITION).value(position).ignoreIfNull()
.integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.CODE_NAME, element)) {
final String codeName = this.fromApiJsonHelper.extractStringNamed(PaymentTypeApiResourceConstants.CODE_NAME, element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.CODE_NAME).value(codeName).ignoreIfNull()
.notExceedingLengthOf(100);
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED, element)) {
final Boolean isSystemDefined = this.fromApiJsonHelper.extractBooleanNamed(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED,
element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED).value(isSystemDefined)
.validateForBooleanValue();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
//
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
public void validateForUpdate(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, UPDATE_PAYMENT_TYPE_REQUEST_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(PaymentTypeApiResourceConstants.ENTITY_NAME);
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.NAME, element)) {
final String name = this.fromApiJsonHelper.extractStringNamed(PaymentTypeApiResourceConstants.NAME, element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.NAME).value(name);
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.DESCRIPTION, element)) {
final String description = this.fromApiJsonHelper.extractStringNamed(PaymentTypeApiResourceConstants.DESCRIPTION, element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.DESCRIPTION).value(description).ignoreIfNull()
.notExceedingLengthOf(500);
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.ISCASHPAYMENT, element)) {
final Boolean isCashPayment = this.fromApiJsonHelper.extractBooleanNamed(PaymentTypeApiResourceConstants.ISCASHPAYMENT,
element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.ISCASHPAYMENT).value(isCashPayment)
.validateForBooleanValue();
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.POSITION, element)) {
final Long position = this.fromApiJsonHelper.extractLongNamed(PaymentTypeApiResourceConstants.POSITION, element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.POSITION).value(position).ignoreIfNull()
.integerZeroOrGreater();
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.CODE_NAME, element)) {
final String codeName = this.fromApiJsonHelper.extractStringNamed(PaymentTypeApiResourceConstants.CODE_NAME, element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.CODE_NAME).value(codeName).ignoreIfNull()
.notExceedingLengthOf(100);
}
if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED, element)) {
final Boolean isSystemDefined = this.fromApiJsonHelper.extractBooleanNamed(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED,
element);
baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED).value(isSystemDefined)
.validateForBooleanValue();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
}
@@ -0,0 +1,46 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.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.portfolio.paymenttype.service.PaymentTypeWriteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PAYMENTTYPE", action = "CREATE")
public class CreatePaymentTypeCommandHandler implements NewCommandSourceHandler {
private final PaymentTypeWriteService paymentTypeWriteService;
@Autowired
public CreatePaymentTypeCommandHandler(final PaymentTypeWriteService paymentTypeWriteService) {
this.paymentTypeWriteService = paymentTypeWriteService;
}
@Override
@Transactional
public CommandProcessingResult processCommand(JsonCommand command) {
return this.paymentTypeWriteService.createPaymentType(command);
}
}
@@ -0,0 +1,46 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.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.portfolio.paymenttype.service.PaymentTypeWriteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PAYMENTTYPE", action = "DELETE")
public class DeletePaymentTypeCommandHandler implements NewCommandSourceHandler {
private final PaymentTypeWriteService paymentTypeWriteService;
@Autowired
public DeletePaymentTypeCommandHandler(final PaymentTypeWriteService paymentTypeWriteService) {
this.paymentTypeWriteService = paymentTypeWriteService;
}
@Override
@Transactional
public CommandProcessingResult processCommand(JsonCommand command) {
return this.paymentTypeWriteService.deletePaymentType(command.entityId());
}
}
@@ -0,0 +1,46 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.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.portfolio.paymenttype.service.PaymentTypeWriteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PAYMENTTYPE", action = "UPDATE")
public class UpdatePaymentTypeCommandHandler implements NewCommandSourceHandler {
private final PaymentTypeWriteService paymentTypeWriteService;
@Autowired
public UpdatePaymentTypeCommandHandler(final PaymentTypeWriteService paymentTypeWriteService) {
this.paymentTypeWriteService = paymentTypeWriteService;
}
@Override
@Transactional
public CommandProcessingResult processCommand(JsonCommand command) {
return this.paymentTypeWriteService.updatePaymentType(command.entityId(), command);
}
}
@@ -0,0 +1,34 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.mapper;
import java.util.List;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
import org.mapstruct.Mapper;
@Mapper(config = MapstructMapperConfig.class)
public interface PaymentTypeMapper {
PaymentTypeData map(PaymentType source);
List<PaymentTypeData> map(List<PaymentType> sources);
}
@@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.service;
import java.util.Collection;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
public interface PaymentTypeReadPlatformService {
Collection<PaymentTypeData> retrieveAllPaymentTypes();
Collection<PaymentTypeData> retrieveAllPaymentTypesWithCode();
PaymentTypeData retrieveOne(Long paymentTypeId);
}
@@ -0,0 +1,67 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.service;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.apache.fineract.portfolio.paymenttype.mapper.PaymentTypeMapper;
import org.springframework.cache.annotation.Cacheable;
@RequiredArgsConstructor
public class PaymentTypeReadPlatformServiceImpl implements PaymentTypeReadPlatformService {
private final PlatformSecurityContext context;
private final PaymentTypeMapper paymentTypeMapper;
private final PaymentTypeRepositoryWrapper paymentTypeRepository;
@Override
@Cacheable(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
public Collection<PaymentTypeData> retrieveAllPaymentTypes() {
// TODO Auto-generated method stub
this.context.authenticatedUser();
List<PaymentType> paymentType = this.paymentTypeRepository.findAll();
return this.paymentTypeMapper.map(paymentType);
}
@Override
@Cacheable(value = "paymentTypesWithCode", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
public Collection<PaymentTypeData> retrieveAllPaymentTypesWithCode() {
// TODO Auto-generated method stub
this.context.authenticatedUser();
List<PaymentType> paymentType = this.paymentTypeRepository.findAllWithCodeName();
return this.paymentTypeMapper.map(paymentType);
}
@Override
public PaymentTypeData retrieveOne(Long paymentTypeId) {
// TODO Auto-generated method stub
this.context.authenticatedUser();
final PaymentType paymentType = this.paymentTypeRepository.findOneWithNotFoundDetection(paymentTypeId);
return this.paymentTypeMapper.map(paymentType);
}
}
@@ -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.portfolio.paymenttype.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface PaymentTypeWriteService {
CommandProcessingResult createPaymentType(JsonCommand command);
CommandProcessingResult updatePaymentType(Long id, JsonCommand command);
CommandProcessingResult deletePaymentType(Long id);
}
@@ -0,0 +1,103 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.service;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.portfolio.paymenttype.api.PaymentTypeApiResourceConstants;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeDataValidator;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException;
@RequiredArgsConstructor
public class PaymentTypeWriteServiceImpl implements PaymentTypeWriteService {
private final PaymentTypeRepository repository;
private final PaymentTypeRepositoryWrapper repositoryWrapper;
private final PaymentTypeDataValidator fromApiJsonDeserializer;
@Override
@CacheEvict(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
public CommandProcessingResult createPaymentType(JsonCommand command) {
this.fromApiJsonDeserializer.validateForCreate(command.json());
String name = command.stringValueOfParameterNamed(PaymentTypeApiResourceConstants.NAME);
String description = command.stringValueOfParameterNamed(PaymentTypeApiResourceConstants.DESCRIPTION);
Boolean isCashPayment = command.booleanObjectValueOfParameterNamed(PaymentTypeApiResourceConstants.ISCASHPAYMENT);
Long position = command.longValueOfParameterNamed(PaymentTypeApiResourceConstants.POSITION);
String codeName = command.stringValueOfParameterNamed(PaymentTypeApiResourceConstants.CODE_NAME);
Boolean isSystemDefined = command.booleanObjectValueOfParameterNamed(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED);
if (isSystemDefined == null) {
isSystemDefined = false;
}
PaymentType newPaymentType = new PaymentType(name, description, isCashPayment, position, codeName, isSystemDefined);
this.repository.saveAndFlush(newPaymentType);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(newPaymentType.getId()).build();
}
@Override
@CacheEvict(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
public CommandProcessingResult updatePaymentType(Long paymentTypeId, JsonCommand command) {
this.fromApiJsonDeserializer.validateForUpdate(command.json());
final PaymentType paymentType = this.repositoryWrapper.findOneWithNotFoundDetection(paymentTypeId);
final Map<String, Object> changes = paymentType.update(command);
if (!changes.isEmpty()) {
this.repository.save(paymentType);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(command.entityId()).build();
}
@Override
@CacheEvict(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
public CommandProcessingResult deletePaymentType(Long paymentTypeId) {
final PaymentType paymentType = this.repositoryWrapper.findOneWithNotFoundDetection(paymentTypeId);
try {
this.repository.delete(paymentType);
this.repository.flush();
} catch (final JpaSystemException | DataIntegrityViolationException e) {
final Throwable throwable = e.getMostSpecificCause();
handleDataIntegrityIssues(throwable, e);
}
return new CommandProcessingResultBuilder().withEntityId(paymentType.getId()).build();
}
private void handleDataIntegrityIssues(final Throwable realCause, final Exception dve) {
if (realCause.getMessage().contains("acc_product_mapping")) {
throw new PlatformDataIntegrityException("error.msg.payment.type.association.exist",
"cannot.delete.payment.type.with.association");
} else if (realCause.getMessage().contains("payment_type_id")) {
throw new PlatformDataIntegrityException("error.msg.payment.type.association.exist",
"cannot.delete.payment.type.with.association");
}
throw ErrorHandler.getMappable(dve, "error.msg.paymenttypes.unknown.data.integrity.issue",
"Unknown data integrity issue with resource.");
}
}
@@ -0,0 +1,50 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.paymenttype.starter;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeDataValidator;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.apache.fineract.portfolio.paymenttype.mapper.PaymentTypeMapper;
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformServiceImpl;
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeWriteService;
import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeWriteServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PaymentTypeConfiguration {
@Bean
@ConditionalOnMissingBean(PaymentTypeReadPlatformService.class)
PaymentTypeReadPlatformService paymentTypeReadPlatformService(PlatformSecurityContext context, PaymentTypeMapper paymentTypeMapper,
PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper) {
return new PaymentTypeReadPlatformServiceImpl(context, paymentTypeMapper, paymentTypeRepositoryWrapper);
}
@Bean
@ConditionalOnMissingBean(PaymentTypeWriteService.class)
PaymentTypeWriteService paymentTypeWriteService(PaymentTypeRepository repository, PaymentTypeRepositoryWrapper repositoryWrapper,
PaymentTypeDataValidator fromApiJsonDeserializer) {
return new PaymentTypeWriteServiceImpl(repository, repositoryWrapper, fromApiJsonDeserializer);
}
}