FINERACT-2180: Trigger (internal) business event when new datatable entry was added

This commit is contained in:
Jose Alberto Hernandez
2025-03-14 09:46:28 +00:00
committed by Adam Saghy
parent 3198b1b99e
commit e1d820756f
19 changed files with 1081 additions and 471 deletions
@@ -106,8 +106,12 @@ import org.apache.fineract.infrastructure.dataqueries.data.ResultsetRowData;
import org.apache.fineract.infrastructure.dataqueries.exception.DatatableEntryRequiredException; import org.apache.fineract.infrastructure.dataqueries.exception.DatatableEntryRequiredException;
import org.apache.fineract.infrastructure.dataqueries.exception.DatatableNotFoundException; import org.apache.fineract.infrastructure.dataqueries.exception.DatatableNotFoundException;
import org.apache.fineract.infrastructure.dataqueries.exception.DatatableSystemErrorException; import org.apache.fineract.infrastructure.dataqueries.exception.DatatableSystemErrorException;
import org.apache.fineract.infrastructure.event.business.domain.datatable.DatatableEntryCreatedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.datatable.DatatableEntryDeletedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.datatable.DatatableEntryDetails;
import org.apache.fineract.infrastructure.event.business.domain.datatable.DatatableEntryUpdatedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.service.SqlInjectionPreventerService;
import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.portfolio.search.data.AdvancedQueryData; import org.apache.fineract.portfolio.search.data.AdvancedQueryData;
@@ -156,10 +160,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
private final DataTableValidator dataTableValidator; private final DataTableValidator dataTableValidator;
private final ColumnValidator columnValidator; private final ColumnValidator columnValidator;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate; private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private final SqlInjectionPreventerService preventSqlInjectionService;
private final DatatableKeywordGenerator datatableKeywordGenerator; private final DatatableKeywordGenerator datatableKeywordGenerator;
private final SqlValidator sqlValidator; private final SqlValidator sqlValidator;
private final SearchUtil searchUtil; private final SearchUtil searchUtil;
private final BusinessEventNotifierService businessEventNotifierService;
@Override @Override
public List<DatatableData> retrieveDatatableNames(final String appTable) { public List<DatatableData> retrieveDatatableNames(final String appTable) {
@@ -1307,8 +1311,9 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
ArrayList<String> insertColumns = new ArrayList<>( ArrayList<String> insertColumns = new ArrayList<>(
List.of(entityTable.getForeignKeyColumnNameOnDatatable(), CREATEDAT_FIELD_NAME, UPDATEDAT_FIELD_NAME)); List.of(entityTable.getForeignKeyColumnNameOnDatatable(), CREATEDAT_FIELD_NAME, UPDATEDAT_FIELD_NAME));
LocalDateTime auditDateTime = DateUtils.getAuditLocalDateTime(); final LocalDateTime auditDateTime = DateUtils.getAuditLocalDateTime();
ArrayList<Object> params = new ArrayList<>(List.of(appTableId, auditDateTime, auditDateTime)); ArrayList<Object> params = new ArrayList<>(List.of(appTableId, auditDateTime, auditDateTime));
Map<String, Object> dataObjectParams = new HashMap<String, Object>();
for (Map.Entry<String, String> entry : dataParams.entrySet()) { for (Map.Entry<String, String> entry : dataParams.entrySet()) {
if (isTechnicalParam(entry.getKey())) { if (isTechnicalParam(entry.getKey())) {
continue; continue;
@@ -1318,8 +1323,11 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
continue; continue;
} }
insertColumns.add(columnHeader.getColumnName()); insertColumns.add(columnHeader.getColumnName());
params.add(searchUtil.parseJdbcColumnValue(columnHeader, entry.getValue(), dateFormat, dateTimeFormat, locale, false, Object valueParam = searchUtil.parseJdbcColumnValue(columnHeader, entry.getValue(), dateFormat, dateTimeFormat, locale, false,
sqlGenerator)); sqlGenerator);
params.add(valueParam);
dataObjectParams.put(entry.getKey(), valueParam);
} }
if (addScore) { if (addScore) {
List<Object> scoreIds = params.stream().filter(e -> e != null && !String.valueOf(e).isBlank()).toList(); List<Object> scoreIds = params.stream().filter(e -> e != null && !String.valueOf(e).isBlank()).toList();
@@ -1353,6 +1361,11 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
if (isMultirowDatatable(columnHeaders)) { if (isMultirowDatatable(columnHeaders)) {
resourceId = sqlGenerator.fetchPK(keyHolder); resourceId = sqlGenerator.fetchPK(keyHolder);
} }
final DatatableEntryDetails datatableEntryDetails = new DatatableEntryDetails(dataTableName, entityTable, resourceId,
appTableId, dataObjectParams);
businessEventNotifierService.notifyPostBusinessEvent(new DatatableEntryCreatedBusinessEvent(datatableEntryDetails));
return CommandProcessingResult.fromCommandProcessingResult(commandProcessingResult, resourceId); return CommandProcessingResult.fromCommandProcessingResult(commandProcessingResult, resourceId);
} catch (final DataAccessException dve) { } catch (final DataAccessException dve) {
handleDataIntegrityIssues(dataTableName, appTableId, dve.getMostSpecificCause(), dve); handleDataIntegrityIssues(dataTableName, appTableId, dve.getMostSpecificCause(), dve);
@@ -1422,6 +1435,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType(); final Type typeOfMap = new TypeToken<Map<String, String>>() {}.getType();
final Map<String, String> dataParams = fromJsonHelper.extractDataMap(typeOfMap, command.json()); final Map<String, String> dataParams = fromJsonHelper.extractDataMap(typeOfMap, command.json());
final Map<String, Object> dataObjectParams = new HashMap<String, Object>();
final String dateFormat = dataParams.get(API_PARAM_DATE_FORMAT); final String dateFormat = dataParams.get(API_PARAM_DATE_FORMAT);
// fall back to dateFormat to keep backward compatibility // fall back to dateFormat to keep backward compatibility
@@ -1445,6 +1459,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
Object existingValue = valuesByHeader.get(columnHeader); Object existingValue = valuesByHeader.get(columnHeader);
Object columnValue = searchUtil.parseColumnValue(columnHeader, entry.getValue(), dateFormat, dateTimeFormat, locale, false, Object columnValue = searchUtil.parseColumnValue(columnHeader, entry.getValue(), dateFormat, dateTimeFormat, locale, false,
sqlGenerator); sqlGenerator);
dataObjectParams.put(entry.getKey(), columnValue);
if ((columnHeader.getColumnType().isDecimalType() && MathUtil.isEqualTo((BigDecimal) existingValue, (BigDecimal) columnValue)) if ((columnHeader.getColumnType().isDecimalType() && MathUtil.isEqualTo((BigDecimal) existingValue, (BigDecimal) columnValue))
|| (existingValue == null ? columnValue == null : existingValue.equals(columnValue))) { || (existingValue == null ? columnValue == null : existingValue.equals(columnValue))) {
log.debug("Ignore change on update {}:{}", dataTableName, columnName); log.debug("Ignore change on update {}:{}", dataTableName, columnName);
@@ -1457,16 +1472,23 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
Long primaryKey = datatableId == null ? appTableId : datatableId; Long primaryKey = datatableId == null ? appTableId : datatableId;
if (!updateColumns.isEmpty()) { if (!updateColumns.isEmpty()) {
ResultsetColumnHeaderData pkColumn = searchUtil.getFiltered(columnHeaders, ResultsetColumnHeaderData::getIsColumnPrimaryKey); ResultsetColumnHeaderData pkColumn = searchUtil.getFiltered(columnHeaders, ResultsetColumnHeaderData::getIsColumnPrimaryKey);
params.add(primaryKey); if (pkColumn != null) {
final String sql = sqlGenerator.buildUpdate(dataTableName, updateColumns, headersByName) + " WHERE " + pkColumn.getColumnName() params.add(primaryKey);
+ " = ?"; final String sql = sqlGenerator.buildUpdate(dataTableName, updateColumns, headersByName) + " WHERE "
int updated = jdbcTemplate.update(sql, params.toArray(Object[]::new)); // NOSONAR + pkColumn.getColumnName() + " = ?";
if (updated != 1) { int updated = jdbcTemplate.update(sql, params.toArray(Object[]::new)); // NOSONAR
throw new PlatformDataIntegrityException("error.msg.invalid.update", "Expected one updated row."); if (updated != 1) {
throw new PlatformDataIntegrityException("error.msg.invalid.update", "Expected one updated row.");
}
} }
} else { } else {
log.debug("No change on update {}", dataTableName); log.debug("No change on update {}", dataTableName);
} }
final DatatableEntryDetails datatableEntryDetails = new DatatableEntryDetails(dataTableName, entityTable, datatableId, appTableId,
dataObjectParams);
businessEventNotifierService.notifyPostBusinessEvent(new DatatableEntryUpdatedBusinessEvent(datatableEntryDetails));
return new CommandProcessingResultBuilder().withCommandId(command.commandId()) // return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
.withEntityId(primaryKey) // .withEntityId(primaryKey) //
.withOfficeId(commandProcessingResult.getOfficeId()) // .withOfficeId(commandProcessingResult.getOfficeId()) //
@@ -1517,6 +1539,11 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ
+ whereValue; + whereValue;
this.jdbcTemplate.update(sql); // NOSONAR this.jdbcTemplate.update(sql); // NOSONAR
final Map<String, Object> dataParams = null;
final DatatableEntryDetails datatableEntryDetails = new DatatableEntryDetails(dataTableName, entityTable, datatableId, appTableId,
dataParams);
businessEventNotifierService.notifyPostBusinessEvent(new DatatableEntryDeletedBusinessEvent(datatableEntryDetails));
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) // .withCommandId(command.commandId()) //
.withEntityId(whereValue) // .withEntityId(whereValue) //
@@ -29,8 +29,8 @@ import org.apache.fineract.infrastructure.dataqueries.service.DatatableKeywordGe
import org.apache.fineract.infrastructure.dataqueries.service.GenericDataService; import org.apache.fineract.infrastructure.dataqueries.service.GenericDataService;
import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDataService; import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDataService;
import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDataServiceImpl; import org.apache.fineract.infrastructure.dataqueries.service.ReadWriteNonCoreDataServiceImpl;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.service.SqlInjectionPreventerService;
import org.apache.fineract.infrastructure.security.service.SqlValidator; import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.portfolio.search.service.SearchUtil; import org.apache.fineract.portfolio.search.service.SearchUtil;
@@ -51,11 +51,11 @@ public class DataQueriesAutoConfiguration {
final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer, final DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer,
final ConfigurationDomainService configurationDomainService, final CodeReadPlatformService codeReadPlatformService, final ConfigurationDomainService configurationDomainService, final CodeReadPlatformService codeReadPlatformService,
final DataTableValidator dataTableValidator, final ColumnValidator columnValidator, final DataTableValidator dataTableValidator, final ColumnValidator columnValidator,
final NamedParameterJdbcTemplate namedParameterJdbcTemplate, final SqlInjectionPreventerService preventSqlInjectionService, final NamedParameterJdbcTemplate namedParameterJdbcTemplate, DatatableKeywordGenerator datatableKeywordGenerator,
DatatableKeywordGenerator datatableKeywordGenerator, SqlValidator sqlValidator, SearchUtil searchUtil) { SqlValidator sqlValidator, SearchUtil searchUtil, final BusinessEventNotifierService businessEventNotifierService) {
return new ReadWriteNonCoreDataServiceImpl(jdbcTemplate, databaseTypeResolver, sqlGenerator, context, fromJsonHelper, return new ReadWriteNonCoreDataServiceImpl(jdbcTemplate, databaseTypeResolver, sqlGenerator, context, fromJsonHelper,
genericDataService, fromApiJsonDeserializer, configurationDomainService, codeReadPlatformService, dataTableValidator, genericDataService, fromApiJsonDeserializer, configurationDomainService, codeReadPlatformService, dataTableValidator,
columnValidator, namedParameterJdbcTemplate, preventSqlInjectionService, datatableKeywordGenerator, sqlValidator, columnValidator, namedParameterJdbcTemplate, datatableKeywordGenerator, sqlValidator, searchUtil,
searchUtil); businessEventNotifierService);
} }
} }
@@ -0,0 +1,47 @@
/**
* 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.event.business.domain.datatable;
import lombok.Getter;
import org.apache.fineract.infrastructure.event.business.domain.AbstractBusinessEvent;
@Getter
public abstract class DatatableEntryBusinessEvent extends AbstractBusinessEvent<DatatableEntryDetails> {
protected static final String CATEGORY = "Datatable";
public static final String TYPE = "DatatableEntryBusinessEvent";
protected DatatableEntryDetails datatableEntryDetails;
public DatatableEntryBusinessEvent(final DatatableEntryDetails datatableEntryDetails) {
super(datatableEntryDetails);
this.datatableEntryDetails = datatableEntryDetails;
}
@Override
public String getType() {
return TYPE;
}
@Override
public String getCategory() {
return CATEGORY;
}
}
@@ -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.infrastructure.event.business.domain.datatable;
import org.apache.fineract.infrastructure.event.business.domain.NoExternalEvent;
public class DatatableEntryCreatedBusinessEvent extends DatatableEntryBusinessEvent implements NoExternalEvent {
public static final String TYPE = "DatatableEntryCreatedBusinessEvent";
public DatatableEntryCreatedBusinessEvent(final DatatableEntryDetails datatableEntryDetails) {
super(datatableEntryDetails);
}
@Override
public String getType() {
return TYPE;
}
@Override
public Long getAggregateRootId() {
return datatableEntryDetails.getEntityId();
}
}
@@ -0,0 +1,40 @@
/**
* 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.event.business.domain.datatable;
import org.apache.fineract.infrastructure.event.business.domain.NoExternalEvent;
public class DatatableEntryDeletedBusinessEvent extends DatatableEntryBusinessEvent implements NoExternalEvent {
public static final String TYPE = "DatatableEntryDeletedBusinessEvent";
public DatatableEntryDeletedBusinessEvent(final DatatableEntryDetails datatableEntryDetails) {
super(datatableEntryDetails);
}
@Override
public String getType() {
return TYPE;
}
@Override
public Long getAggregateRootId() {
return datatableEntryDetails.getEntityId();
}
}
@@ -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.event.business.domain.datatable;
import java.util.Map;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
@Data
@RequiredArgsConstructor
public class DatatableEntryDetails {
private final String datatableName;
private final EntityTables entityType;
private final Long entityId;
private final Long appTableId;
private final Map<String, Object> data;
}
@@ -0,0 +1,40 @@
/**
* 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.event.business.domain.datatable;
import org.apache.fineract.infrastructure.event.business.domain.NoExternalEvent;
public class DatatableEntryUpdatedBusinessEvent extends DatatableEntryBusinessEvent implements NoExternalEvent {
public static final String TYPE = "DatatableEntryUpdatedBusinessEvent";
public DatatableEntryUpdatedBusinessEvent(final DatatableEntryDetails datatableEntryDetails) {
super(datatableEntryDetails);
}
@Override
public String getType() {
return TYPE;
}
@Override
public Long getAggregateRootId() {
return datatableEntryDetails.getEntityId();
}
}
@@ -68,6 +68,7 @@ import org.apache.fineract.portfolio.accountdetails.service.AccountDetailsReadPl
import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.client.exception.ClientNotFoundException; import org.apache.fineract.portfolio.client.exception.ClientNotFoundException;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.client.service.ClientTemplateReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.guarantor.data.ObligeeData; import org.apache.fineract.portfolio.loanaccount.guarantor.data.ObligeeData;
import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorReadPlatformService; import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorReadPlatformService;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData; import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
@@ -85,6 +86,7 @@ public class ClientsApiResource {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final ClientReadPlatformService clientReadPlatformService; private final ClientReadPlatformService clientReadPlatformService;
private final ClientTemplateReadPlatformService clientTemplateReadPlatformService;
private final ToApiJsonSerializer<ClientData> toApiJsonSerializer; private final ToApiJsonSerializer<ClientData> toApiJsonSerializer;
private final ToApiJsonSerializer<AccountSummaryCollectionData> clientAccountSummaryToApiJsonSerializer; private final ToApiJsonSerializer<AccountSummaryCollectionData> clientAccountSummaryToApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper; private final ApiRequestParameterHelper apiRequestParameterHelper;
@@ -122,7 +124,7 @@ public class ClientsApiResource {
} else if (CommandParameterUtil.is(commandParam, "withdraw")) { } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
clientData = clientReadPlatformService.retrieveAllNarrations(ClientApiConstants.CLIENT_WITHDRAW_REASON); clientData = clientReadPlatformService.retrieveAllNarrations(ClientApiConstants.CLIENT_WITHDRAW_REASON);
} else { } else {
clientData = clientReadPlatformService.retrieveTemplate(officeId, staffInSelectedOfficeOnly); clientData = clientTemplateReadPlatformService.retrieveTemplate(officeId, staffInSelectedOfficeOnly);
} }
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -458,7 +460,8 @@ public class ClientsApiResource {
private ClientData retrieveClientData(final Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) { private ClientData retrieveClientData(final Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
ClientData clientData = clientReadPlatformService.retrieveOne(clientId); ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
if (isTemplate) { if (isTemplate) {
final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly); final ClientData templateData = clientTemplateReadPlatformService.retrieveTemplate(clientData.getOfficeId(),
staffInSelectedOfficeOnly);
clientData = ClientData.templateOnTop(clientData, templateData); clientData = ClientData.templateOnTop(clientData, templateData);
Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null); Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
if (savingAccountOptions != null && savingAccountOptions.size() > 0) { if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
@@ -27,8 +27,6 @@ import org.apache.fineract.portfolio.client.data.ClientData;
public interface ClientReadPlatformService { public interface ClientReadPlatformService {
ClientData retrieveTemplate(Long officeId, boolean staffInSelectedOfficeOnly);
Page<ClientData> retrieveAll(SearchParameters searchParameters); Page<ClientData> retrieveAll(SearchParameters searchParameters);
ClientData retrieveOne(Long clientId); ClientData retrieveOne(Long clientId);
@@ -32,54 +32,36 @@ import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService; import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.organisation.office.data.OfficeData;
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
import org.apache.fineract.portfolio.address.data.AddressData;
import org.apache.fineract.portfolio.address.service.AddressReadPlatformService;
import org.apache.fineract.portfolio.client.api.ClientApiConstants;
import org.apache.fineract.portfolio.client.data.ClientCollateralManagementData; import org.apache.fineract.portfolio.client.data.ClientCollateralManagementData;
import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.client.data.ClientFamilyMembersData;
import org.apache.fineract.portfolio.client.data.ClientNonPersonData; import org.apache.fineract.portfolio.client.data.ClientNonPersonData;
import org.apache.fineract.portfolio.client.data.ClientTimelineData; import org.apache.fineract.portfolio.client.data.ClientTimelineData;
import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientEnumerations; import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.client.domain.ClientStatus; import org.apache.fineract.portfolio.client.domain.ClientStatus;
import org.apache.fineract.portfolio.client.domain.LegalForm;
import org.apache.fineract.portfolio.client.exception.ClientNotFoundException; import org.apache.fineract.portfolio.client.exception.ClientNotFoundException;
import org.apache.fineract.portfolio.client.mapper.ClientMapper; import org.apache.fineract.portfolio.client.mapper.ClientMapper;
import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement; import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepositoryWrapper; import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagementRepositoryWrapper;
import org.apache.fineract.portfolio.group.data.GroupGeneralData; import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
import org.apache.fineract.useradministration.domain.AppUser; import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -87,10 +69,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final OfficeReadPlatformService officeReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService; private final CodeValueReadPlatformService codeValueReadPlatformService;
private final SavingsProductReadPlatformService savingsProductReadPlatformService;
// data mappers // data mappers
private final PaginationHelper paginationHelper; private final PaginationHelper paginationHelper;
private final DatabaseSpecificSQLGenerator sqlGenerator; private final DatabaseSpecificSQLGenerator sqlGenerator;
@@ -99,71 +78,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
private final ClientMembersOfGroupMapper membersOfGroupMapper = new ClientMembersOfGroupMapper(); private final ClientMembersOfGroupMapper membersOfGroupMapper = new ClientMembersOfGroupMapper();
private final ParentGroupsMapper clientGroupsMapper = new ParentGroupsMapper(); private final ParentGroupsMapper clientGroupsMapper = new ParentGroupsMapper();
private final AddressReadPlatformService addressReadPlatformService;
private final ClientFamilyMembersReadPlatformService clientFamilyMembersReadPlatformService;
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
private final ColumnValidator columnValidator; private final ColumnValidator columnValidator;
private final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper; private final ClientCollateralManagementRepositoryWrapper clientCollateralManagementRepositoryWrapper;
private final ConfigurationDomainService configurationDomainService;
private final ClientRepositoryWrapper clientRepositoryWrapper; private final ClientRepositoryWrapper clientRepositoryWrapper;
private final ClientMapper clientMapper; private final ClientMapper clientMapper;
@Override
public ClientData retrieveTemplate(final Long officeId, final boolean staffInSelectedOfficeOnly) {
this.context.authenticatedUser();
final Long defaultOfficeId = defaultToUsersOfficeIfNull(officeId);
AddressData address = null;
final Collection<OfficeData> offices = this.officeReadPlatformService.retrieveAllOfficesForDropdown();
final Collection<SavingsProductData> savingsProductDatas = this.savingsProductReadPlatformService.retrieveAllForLookupByType(null);
final Boolean isAddressEnabled = configurationDomainService.isAddressEnabled();
if (isAddressEnabled) {
address = this.addressReadPlatformService.retrieveTemplate();
}
final ClientFamilyMembersData familyMemberOptions = this.clientFamilyMembersReadPlatformService.retrieveTemplate();
Collection<StaffData> staffOptions = null;
final boolean loanOfficersOnly = false;
if (staffInSelectedOfficeOnly) {
staffOptions = this.staffReadPlatformService.retrieveAllStaffForDropdown(defaultOfficeId);
} else {
staffOptions = this.staffReadPlatformService.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(defaultOfficeId,
loanOfficersOnly);
}
if (CollectionUtils.isEmpty(staffOptions)) {
staffOptions = null;
}
final List<CodeValueData> genderOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.GENDER));
final List<CodeValueData> clientTypeOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_TYPE));
final List<CodeValueData> clientClassificationOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_CLASSIFICATION));
final List<CodeValueData> clientNonPersonConstitutionOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_NON_PERSON_CONSTITUTION));
final List<CodeValueData> clientNonPersonMainBusinessLineOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_NON_PERSON_MAIN_BUSINESS_LINE));
final List<EnumOptionData> clientLegalFormOptions = ClientEnumerations.legalForm(LegalForm.values());
final List<DatatableData> datatableTemplates = this.entityDatatableChecksReadService.retrieveTemplates(StatusEnum.CREATE.getValue(),
EntityTables.CLIENT.getName(), null);
return ClientData.template(defaultOfficeId, LocalDate.now(DateUtils.getDateTimeZoneOfTenant()), offices, staffOptions, null,
genderOptions, savingsProductDatas, clientTypeOptions, clientClassificationOptions, clientNonPersonConstitutionOptions,
clientNonPersonMainBusinessLineOptions, clientLegalFormOptions, familyMemberOptions,
new ArrayList<AddressData>(Arrays.asList(address)), isAddressEnabled, datatableTemplates);
}
@Override @Override
public Page<ClientData> retrieveAll(final SearchParameters searchParameters) { public Page<ClientData> retrieveAll(final SearchParameters searchParameters) {
@@ -643,14 +562,6 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
} }
} }
private Long defaultToUsersOfficeIfNull(final Long officeId) {
Long defaultOfficeId = officeId;
if (defaultOfficeId == null) {
defaultOfficeId = this.context.authenticatedUser().getOffice().getId();
}
return defaultOfficeId;
}
@Override @Override
public ClientData retrieveAllNarrations(final String clientNarrations) { public ClientData retrieveAllNarrations(final String clientNarrations) {
final List<CodeValueData> narrations = new ArrayList<>( final List<CodeValueData> narrations = new ArrayList<>(
@@ -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.service;
import org.apache.fineract.portfolio.client.data.ClientData;
public interface ClientTemplateReadPlatformService {
ClientData retrieveTemplate(Long officeId, boolean staffInSelectedOfficeOnly);
}
@@ -0,0 +1,133 @@
/**
* 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.service;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.office.data.OfficeData;
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
import org.apache.fineract.portfolio.address.data.AddressData;
import org.apache.fineract.portfolio.address.service.AddressReadPlatformService;
import org.apache.fineract.portfolio.client.api.ClientApiConstants;
import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.client.data.ClientFamilyMembersData;
import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
import org.apache.fineract.portfolio.client.domain.LegalForm;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
import org.apache.fineract.portfolio.savings.service.SavingsProductReadPlatformService;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@Service
@RequiredArgsConstructor
public class ClientTemplateReadPlatformServiceImpl implements ClientTemplateReadPlatformService {
private final PlatformSecurityContext context;
private final OfficeReadPlatformService officeReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
private final SavingsProductReadPlatformService savingsProductReadPlatformService;
// data mappers
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
private final AddressReadPlatformService addressReadPlatformService;
private final ClientFamilyMembersReadPlatformService clientFamilyMembersReadPlatformService;
private final ConfigurationDomainService configurationDomainService;
@Override
public ClientData retrieveTemplate(final Long officeId, final boolean staffInSelectedOfficeOnly) {
this.context.authenticatedUser();
final Long defaultOfficeId = defaultToUsersOfficeIfNull(officeId);
AddressData address = null;
final Collection<OfficeData> offices = this.officeReadPlatformService.retrieveAllOfficesForDropdown();
final Collection<SavingsProductData> savingsProductDatas = this.savingsProductReadPlatformService.retrieveAllForLookupByType(null);
final Boolean isAddressEnabled = configurationDomainService.isAddressEnabled();
if (isAddressEnabled) {
address = this.addressReadPlatformService.retrieveTemplate();
}
final ClientFamilyMembersData familyMemberOptions = this.clientFamilyMembersReadPlatformService.retrieveTemplate();
Collection<StaffData> staffOptions = null;
final boolean loanOfficersOnly = false;
if (staffInSelectedOfficeOnly) {
staffOptions = this.staffReadPlatformService.retrieveAllStaffForDropdown(defaultOfficeId);
} else {
staffOptions = this.staffReadPlatformService.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(defaultOfficeId,
loanOfficersOnly);
}
if (CollectionUtils.isEmpty(staffOptions)) {
staffOptions = null;
}
final List<CodeValueData> genderOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.GENDER));
final List<CodeValueData> clientTypeOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_TYPE));
final List<CodeValueData> clientClassificationOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_CLASSIFICATION));
final List<CodeValueData> clientNonPersonConstitutionOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_NON_PERSON_CONSTITUTION));
final List<CodeValueData> clientNonPersonMainBusinessLineOptions = new ArrayList<>(
this.codeValueReadPlatformService.retrieveCodeValuesByCode(ClientApiConstants.CLIENT_NON_PERSON_MAIN_BUSINESS_LINE));
final List<EnumOptionData> clientLegalFormOptions = ClientEnumerations.legalForm(LegalForm.values());
final List<DatatableData> datatableTemplates = this.entityDatatableChecksReadService.retrieveTemplates(StatusEnum.CREATE.getValue(),
EntityTables.CLIENT.getName(), null);
return ClientData.template(defaultOfficeId, LocalDate.now(DateUtils.getDateTimeZoneOfTenant()), offices, staffOptions, null,
genderOptions, savingsProductDatas, clientTypeOptions, clientClassificationOptions, clientNonPersonConstitutionOptions,
clientNonPersonMainBusinessLineOptions, clientLegalFormOptions, familyMemberOptions,
new ArrayList<AddressData>(Arrays.asList(address)), isAddressEnabled, datatableTemplates);
}
private Long defaultToUsersOfficeIfNull(final Long officeId) {
Long defaultOfficeId = officeId;
if (defaultOfficeId == null) {
defaultOfficeId = this.context.authenticatedUser().getOffice().getId();
}
return defaultOfficeId;
}
}
@@ -74,6 +74,7 @@ import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException; import org.apache.fineract.portfolio.savings.exception.SavingsAccountNotFoundException;
import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService; import org.apache.fineract.portfolio.savings.service.SavingsAccountChargeReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService; import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformService;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -86,6 +87,7 @@ import org.springframework.util.CollectionUtils;
public class SavingsAccountsApiResource { public class SavingsAccountsApiResource {
private final SavingsAccountReadPlatformService savingsAccountReadPlatformService; private final SavingsAccountReadPlatformService savingsAccountReadPlatformService;
private final SavingsAccountTemplateReadPlatformService savingsAccountTemplateReadPlatformService;
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer; private final DefaultToApiJsonSerializer<SavingsAccountData> toApiJsonSerializer;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@@ -112,7 +114,7 @@ public class SavingsAccountsApiResource {
context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME); context.authenticatedUser().validateHasReadPermission(SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME);
final SavingsAccountData savingsAccount = savingsAccountReadPlatformService.retrieveTemplate(clientId, groupId, productId, final SavingsAccountData savingsAccount = savingsAccountTemplateReadPlatformService.retrieveTemplate(clientId, groupId, productId,
staffInSelectedOfficeOnly); staffInSelectedOfficeOnly);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
@@ -662,8 +664,8 @@ public class SavingsAccountsApiResource {
SavingsAccountData templateData = null; SavingsAccountData templateData = null;
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (settings.isTemplate()) { if (settings.isTemplate()) {
templateData = savingsAccountReadPlatformService.retrieveTemplate(savingsAccount.getClientId(), savingsAccount.getGroupId(), templateData = savingsAccountTemplateReadPlatformService.retrieveTemplate(savingsAccount.getClientId(),
savingsAccount.getSavingsProductId(), staffInSelectedOfficeOnly); savingsAccount.getGroupId(), savingsAccount.getSavingsProductId(), staffInSelectedOfficeOnly);
} }
return SavingsAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges); return SavingsAccountData.withTemplateOptions(savingsAccount, templateData, transactions, charges);
@@ -42,23 +42,12 @@ import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.PaginationHelper; import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator; import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator; import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
import org.apache.fineract.portfolio.account.data.AccountTransferData; import org.apache.fineract.portfolio.account.data.AccountTransferData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
import org.apache.fineract.portfolio.charge.util.ConvertChargeDataToSpecificChargeData;
import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.group.data.GroupGeneralData; import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData; import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData; import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.apache.fineract.portfolio.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.DepositAccountType;
@@ -91,19 +80,12 @@ import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.CollectionUtils;
public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountReadPlatformService { public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountReadPlatformService {
private final PlatformSecurityContext context; private final PlatformSecurityContext context;
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
private final ClientReadPlatformService clientReadPlatformService;
private final GroupReadPlatformService groupReadPlatformService;
private final SavingsProductReadPlatformService savingsProductReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
private final DatabaseSpecificSQLGenerator sqlGenerator; private final DatabaseSpecificSQLGenerator sqlGenerator;
private final ChargeReadPlatformService chargeReadPlatformService;
// mappers // mappers
private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper; private final SavingsAccountTransactionTemplateMapper transactionTemplateMapper;
@@ -116,35 +98,22 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
// pagination // pagination
private final PaginationHelper paginationHelper; private final PaginationHelper paginationHelper;
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
private final ColumnValidator columnValidator; private final ColumnValidator columnValidator;
private final SavingsAccountAssembler savingAccountAssembler; private final SavingsAccountAssembler savingAccountAssembler;
private final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper; private final SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper;
public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate, public SavingsAccountReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService, final SavingsAccountAssembler savingAccountAssembler, PaginationHelper paginationHelper, ColumnValidator columnValidator,
final SavingsProductReadPlatformService savingProductReadPlatformService,
final StaffReadPlatformService staffReadPlatformService, final SavingsDropdownReadPlatformService dropdownReadPlatformService,
final ChargeReadPlatformService chargeReadPlatformService,
final EntityDatatableChecksReadService entityDatatableChecksReadService, final ColumnValidator columnValidator,
final SavingsAccountAssembler savingAccountAssembler, PaginationHelper paginationHelper,
DatabaseSpecificSQLGenerator sqlGenerator, SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper) { DatabaseSpecificSQLGenerator sqlGenerator, SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper) {
this.context = context; this.context = context;
this.jdbcTemplate = jdbcTemplate; this.jdbcTemplate = jdbcTemplate;
this.clientReadPlatformService = clientReadPlatformService;
this.groupReadPlatformService = groupReadPlatformService;
this.savingsProductReadPlatformService = savingProductReadPlatformService;
this.staffReadPlatformService = staffReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService;
this.sqlGenerator = sqlGenerator; this.sqlGenerator = sqlGenerator;
this.savingsAccountRepositoryWrapper = savingsAccountRepositoryWrapper; this.savingsAccountRepositoryWrapper = savingsAccountRepositoryWrapper;
this.transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper(); this.transactionTemplateMapper = new SavingsAccountTransactionTemplateMapper();
this.transactionsMapper = new SavingsAccountTransactionsMapper(); this.transactionsMapper = new SavingsAccountTransactionsMapper();
this.savingsAccountTransactionsForBatchMapper = new SavingsAccountTransactionsForBatchMapper(); this.savingsAccountTransactionsForBatchMapper = new SavingsAccountTransactionsForBatchMapper();
this.savingAccountMapper = new SavingAccountMapper(); this.savingAccountMapper = new SavingAccountMapper();
this.chargeReadPlatformService = chargeReadPlatformService;
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
this.columnValidator = columnValidator; this.columnValidator = columnValidator;
this.paginationHelper = paginationHelper; this.paginationHelper = paginationHelper;
this.savingAccountMapperForInterestPosting = new SavingAccountMapperForInterestPosting(); this.savingAccountMapperForInterestPosting = new SavingAccountMapperForInterestPosting();
@@ -1033,141 +1002,6 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
} }
} }
@Override
public SavingsAccountData retrieveTemplate(final Long clientId, final Long groupId, final Long productId,
final boolean staffInSelectedOfficeOnly) {
final AppUser loggedInUser = this.context.authenticatedUser();
Long officeId = loggedInUser.getOffice().getId();
ClientData client = null;
if (clientId != null) {
client = this.clientReadPlatformService.retrieveOne(clientId);
officeId = client.getOfficeId();
}
GroupGeneralData group = null;
if (groupId != null) {
group = this.groupReadPlatformService.retrieveOne(groupId);
officeId = group.getOfficeId();
}
final Collection<SavingsProductData> productOptions = this.savingsProductReadPlatformService.retrieveAllForLookup();
SavingsAccountData template = null;
if (productId != null) {
final SavingAccountTemplateMapper mapper = new SavingAccountTemplateMapper(client, group);
final String sql = "select " + mapper.schema() + " where sp.id = ?";
template = this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { productId }); // NOSONAR
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = this.dropdownReadPlatformService
.retrieveCompoundingInterestPeriodTypeOptions();
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = this.dropdownReadPlatformService
.retrieveInterestPostingPeriodTypeOptions();
final Collection<EnumOptionData> interestCalculationTypeOptions = this.dropdownReadPlatformService
.retrieveInterestCalculationTypeOptions();
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService
.retrieveInterestCalculationDaysInYearTypeOptions();
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService
.retrieveLockinPeriodFrequencyTypeOptions();
final Collection<EnumOptionData> withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<ChargeData> productCharges = this.chargeReadPlatformService.retrieveSavingsProductCharges(productId);
// update charges from Product charges
final Collection<SavingsAccountChargeData> charges = fromChargesToSavingsCharges(productCharges);
final boolean feeChargesOnly = false;
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
Collection<StaffData> fieldOfficerOptions = null;
if (officeId != null) {
if (staffInSelectedOfficeOnly) {
// only bring back loan officers in selected branch/office
final Collection<StaffData> fieldOfficersInBranch = this.staffReadPlatformService
.retrieveAllLoanOfficersInOfficeById(officeId);
if (!CollectionUtils.isEmpty(fieldOfficersInBranch)) {
fieldOfficerOptions = new ArrayList<>(fieldOfficersInBranch);
}
} else {
// by default bring back all officers in selected
// branch/office as well as officers in office above
// this office
final boolean restrictToLoanOfficersOnly = true;
final Collection<StaffData> loanOfficersInHierarchy = this.staffReadPlatformService
.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId, restrictToLoanOfficersOnly);
if (!CollectionUtils.isEmpty(loanOfficersInHierarchy)) {
fieldOfficerOptions = new ArrayList<>(loanOfficersInHierarchy);
}
}
}
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions,
charges, chargeOptions);
} else {
String clientName = null;
if (client != null) {
clientName = client.getDisplayName();
}
String groupName = null;
if (group != null) {
groupName = group.getName();
}
template = SavingsAccountData.withClientTemplate(clientId, clientName, groupId, groupName);
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<SavingsAccountChargeData> charges = null;
final boolean feeChargesOnly = false;
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions,
charges, chargeOptions);
}
final List<DatatableData> datatableTemplates = this.entityDatatableChecksReadService.retrieveTemplates(StatusEnum.CREATE.getValue(),
EntityTables.SAVINGS.getName(), productId);
template.setDatatables(datatableTemplates);
return template;
}
private Collection<SavingsAccountChargeData> fromChargesToSavingsCharges(final Collection<ChargeData> productCharges) {
final Collection<SavingsAccountChargeData> savingsCharges = new ArrayList<>();
for (final ChargeData chargeData : productCharges) {
final SavingsAccountChargeData savingsCharge = ConvertChargeDataToSpecificChargeData.toSavingsAccountChargeData(chargeData);
savingsCharges.add(savingsCharge);
}
return savingsCharges;
}
@Override @Override
public SavingsAccountTransactionData retrieveDepositTransactionTemplate(final Long savingsId, public SavingsAccountTransactionData retrieveDepositTransactionTemplate(final Long savingsId,
final DepositAccountType depositAccountType) { final DepositAccountType depositAccountType) {
@@ -1402,188 +1236,6 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
} }
} }
private static final class SavingAccountTemplateMapper implements RowMapper<SavingsAccountData> {
private final ClientData client;
private final GroupGeneralData group;
private final String schemaSql;
SavingAccountTemplateMapper(final ClientData client, final GroupGeneralData group) {
this.client = client;
this.group = group;
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("sp.id as productId, sp.name as productName, ");
sqlBuilder.append(
"sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
sqlBuilder.append("sp.nominal_annual_interest_rate as nominalAnnualIterestRate, ");
sqlBuilder.append("sp.interest_compounding_period_enum as interestCompoundingPeriodType, ");
sqlBuilder.append("sp.interest_posting_period_enum as interestPostingPeriodType, ");
sqlBuilder.append("sp.interest_calculation_type_enum as interestCalculationType, ");
sqlBuilder.append("sp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
sqlBuilder.append("sp.min_required_opening_balance as minRequiredOpeningBalance, ");
sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,");
sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
// sqlBuilder.append("sp.withdrawal_fee_amount as
// withdrawalFeeAmount,");
// sqlBuilder.append("sp.withdrawal_fee_type_enum as
// withdrawalFeeTypeEnum, ");
sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
sqlBuilder.append("sp.allow_overdraft as allowOverdraft, ");
sqlBuilder.append("sp.overdraft_limit as overdraftLimit, ");
sqlBuilder.append("sp.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, ");
sqlBuilder.append("sp.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, ");
sqlBuilder.append("sp.withhold_tax as withHoldTax,");
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
// sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,");
// sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth,
// ");
// sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay ");
sqlBuilder.append("sp.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("sp.enforce_min_required_balance as enforceMinRequiredBalance, ");
sqlBuilder.append("sp.max_allowed_lien_limit as maxAllowedLienLimit, ");
sqlBuilder.append("sp.is_lien_allowed as lienAllowed ");
sqlBuilder.append("from m_savings_product sp ");
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
sqlBuilder.append("left join m_tax_group tg on tg.id = sp.tax_group_id ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public SavingsAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long productId = rs.getLong("productId");
final String productName = rs.getString("productName");
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol,
currencyNameCode);
final BigDecimal nominalAnnualIterestRate = rs.getBigDecimal("nominalAnnualIterestRate");
final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations.compoundingInterestPeriodType(
SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestCompoundingPeriodType")));
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(
SavingsPostingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));
final EnumOptionData interestCalculationType = SavingsEnumerations
.interestCalculationType(SavingsInterestCalculationType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationType")));
final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations.interestCalculationDaysInYearType(
SavingsInterestCalculationDaysInYearType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType")));
final BigDecimal minRequiredOpeningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredOpeningBalance");
final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
EnumOptionData lockinPeriodFrequencyType = null;
final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
if (lockinPeriodFrequencyTypeValue != null) {
final SavingsPeriodFrequencyType lockinPeriodType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType);
}
// final BigDecimal withdrawalFeeAmount =
// rs.getBigDecimal("withdrawalFeeAmount");
/*
* EnumOptionData withdrawalFeeType = null; final Integer withdrawalFeeTypeValue =
* JdbcSupport.getInteger(rs, "withdrawalFeeTypeEnum"); if (withdrawalFeeTypeValue != null) {
* withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); }
*/
final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers");
final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
final BigDecimal overdraftLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit");
final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"nominalAnnualInterestRateOverdraft");
final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"minOverdraftForInterestCalculation");
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
final BigDecimal maxAllowedLienLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "maxAllowedLienLimit");
final boolean lienAllowed = rs.getBoolean("lienAllowed");
final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"minBalanceForInterestCalculation");
// final BigDecimal annualFeeAmount =
// JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
// "annualFeeAmount");
/*
* MonthDay annualFeeOnMonthDay = null; final Integer annualFeeOnMonth = JdbcSupport.getInteger(rs,
* "annualFeeOnMonth"); final Integer annualFeeOnDay = JdbcSupport.getInteger(rs, "annualFeeOnDay"); if
* (annualFeeAmount != null && annualFeeOnDay != null) { annualFeeOnMonthDay = new
* MonthDay(annualFeeOnMonth, annualFeeOnDay); }
*/
final boolean withHoldTax = rs.getBoolean("withHoldTax");
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
final String taxGroupName = rs.getString("taxGroupName");
TaxGroupData taxGroupData = null;
if (taxGroupId != null) {
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
}
Long clientId = null;
String clientName = null;
if (this.client != null) {
clientId = this.client.getId();
clientName = this.client.getDisplayName();
}
Long groupId = null;
String groupName = null;
if (this.group != null) {
groupId = this.group.getId();
groupName = this.group.getName();
}
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
// final LocalDate annualFeeNextDueDate = null;
final SavingsAccountSummaryData summary = null;
final BigDecimal onHoldFunds = null;
final BigDecimal savingsAmountOnHold = null;
final SavingsAccountSubStatusEnumData subStatus = null;
final String reasonForBlock = null;
final LocalDate lastActiveTransactionDate = null;
final boolean isDormancyTrackingActive = false;
final Integer daysToInactive = null;
final Integer daysToDormancy = null;
final Integer daysToEscheat = null;
final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault();
final EnumOptionData depositType = null;
return SavingsAccountData.instance(null, null, depositType, null, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, subStatus, reasonForBlock, timeline, currency,
nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
}
@Override @Override
public Collection<SavingsAccountData> retrieveForLookup(Long clientId, Boolean overdraft) { public Collection<SavingsAccountData> retrieveForLookup(Long clientId, Boolean overdraft) {
@@ -0,0 +1,412 @@
/**
* 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.savings.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksReadService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.organisation.monetary.data.CurrencyData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
import org.apache.fineract.portfolio.charge.util.ConvertChargeDataToSpecificChargeData;
import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.group.service.GroupReadPlatformService;
import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationDaysInYearType;
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
import org.apache.fineract.portfolio.savings.SavingsPeriodFrequencyType;
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
import org.apache.fineract.portfolio.savings.data.SavingsAccountApplicationTimelineData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountStatusEnumData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountSubStatusEnumData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountSummaryData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
import org.apache.fineract.portfolio.tax.data.TaxGroupData;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.CollectionUtils;
public class SavingsAccountTemplateReadPlatformServiceImpl implements SavingsAccountTemplateReadPlatformService {
private final PlatformSecurityContext context;
private final JdbcTemplate jdbcTemplate;
private final ClientReadPlatformService clientReadPlatformService;
private final GroupReadPlatformService groupReadPlatformService;
private final SavingsProductReadPlatformService savingsProductReadPlatformService;
private final StaffReadPlatformService staffReadPlatformService;
private final SavingsDropdownReadPlatformService dropdownReadPlatformService;
private final ChargeReadPlatformService chargeReadPlatformService;
private final EntityDatatableChecksReadService entityDatatableChecksReadService;
public SavingsAccountTemplateReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate,
final ClientReadPlatformService clientReadPlatformService, final GroupReadPlatformService groupReadPlatformService,
final SavingsProductReadPlatformService savingProductReadPlatformService,
final StaffReadPlatformService staffReadPlatformService, final SavingsDropdownReadPlatformService dropdownReadPlatformService,
final ChargeReadPlatformService chargeReadPlatformService,
final EntityDatatableChecksReadService entityDatatableChecksReadService, final ColumnValidator columnValidator) {
this.context = context;
this.jdbcTemplate = jdbcTemplate;
this.clientReadPlatformService = clientReadPlatformService;
this.groupReadPlatformService = groupReadPlatformService;
this.savingsProductReadPlatformService = savingProductReadPlatformService;
this.staffReadPlatformService = staffReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService;
this.chargeReadPlatformService = chargeReadPlatformService;
this.entityDatatableChecksReadService = entityDatatableChecksReadService;
}
@Override
public SavingsAccountData retrieveTemplate(final Long clientId, final Long groupId, final Long productId,
final boolean staffInSelectedOfficeOnly) {
final AppUser loggedInUser = this.context.authenticatedUser();
Long officeId = loggedInUser.getOffice().getId();
ClientData client = null;
if (clientId != null) {
client = this.clientReadPlatformService.retrieveOne(clientId);
officeId = client.getOfficeId();
}
GroupGeneralData group = null;
if (groupId != null) {
group = this.groupReadPlatformService.retrieveOne(groupId);
officeId = group.getOfficeId();
}
final Collection<SavingsProductData> productOptions = this.savingsProductReadPlatformService.retrieveAllForLookup();
SavingsAccountData template = null;
if (productId != null) {
final SavingAccountTemplateMapper mapper = new SavingAccountTemplateMapper(client, group);
final String sql = "select " + mapper.schema() + " where sp.id = ?";
template = this.jdbcTemplate.queryForObject(sql, mapper, new Object[] { productId }); // NOSONAR
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = this.dropdownReadPlatformService
.retrieveCompoundingInterestPeriodTypeOptions();
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = this.dropdownReadPlatformService
.retrieveInterestPostingPeriodTypeOptions();
final Collection<EnumOptionData> interestCalculationTypeOptions = this.dropdownReadPlatformService
.retrieveInterestCalculationTypeOptions();
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = this.dropdownReadPlatformService
.retrieveInterestCalculationDaysInYearTypeOptions();
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = this.dropdownReadPlatformService
.retrieveLockinPeriodFrequencyTypeOptions();
final Collection<EnumOptionData> withdrawalFeeTypeOptions = this.dropdownReadPlatformService.retrievewithdrawalFeeTypeOptions();
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<ChargeData> productCharges = this.chargeReadPlatformService.retrieveSavingsProductCharges(productId);
// update charges from Product charges
final Collection<SavingsAccountChargeData> charges = fromChargesToSavingsCharges(productCharges);
final boolean feeChargesOnly = false;
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
Collection<StaffData> fieldOfficerOptions = null;
if (officeId != null) {
if (staffInSelectedOfficeOnly) {
// only bring back loan officers in selected branch/office
final Collection<StaffData> fieldOfficersInBranch = this.staffReadPlatformService
.retrieveAllLoanOfficersInOfficeById(officeId);
if (!CollectionUtils.isEmpty(fieldOfficersInBranch)) {
fieldOfficerOptions = new ArrayList<>(fieldOfficersInBranch);
}
} else {
// by default bring back all officers in selected
// branch/office as well as officers in office above
// this office
final boolean restrictToLoanOfficersOnly = true;
final Collection<StaffData> loanOfficersInHierarchy = this.staffReadPlatformService
.retrieveAllStaffInOfficeAndItsParentOfficeHierarchy(officeId, restrictToLoanOfficersOnly);
if (!CollectionUtils.isEmpty(loanOfficersInHierarchy)) {
fieldOfficerOptions = new ArrayList<>(loanOfficersInHierarchy);
}
}
}
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions,
charges, chargeOptions);
} else {
String clientName = null;
if (client != null) {
clientName = client.getDisplayName();
}
String groupName = null;
if (group != null) {
groupName = group.getName();
}
template = SavingsAccountData.withClientTemplate(clientId, clientName, groupId, groupName);
final Collection<StaffData> fieldOfficerOptions = null;
final Collection<EnumOptionData> interestCompoundingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestPostingPeriodTypeOptions = null;
final Collection<EnumOptionData> interestCalculationTypeOptions = null;
final Collection<EnumOptionData> interestCalculationDaysInYearTypeOptions = null;
final Collection<EnumOptionData> lockinPeriodFrequencyTypeOptions = null;
final Collection<EnumOptionData> withdrawalFeeTypeOptions = null;
final Collection<SavingsAccountTransactionData> transactions = null;
final Collection<SavingsAccountChargeData> charges = null;
final boolean feeChargesOnly = false;
final Collection<ChargeData> chargeOptions = this.chargeReadPlatformService
.retrieveSavingsProductApplicableCharges(feeChargesOnly);
template = SavingsAccountData.withTemplateOptions(template, productOptions, fieldOfficerOptions,
interestCompoundingPeriodTypeOptions, interestPostingPeriodTypeOptions, interestCalculationTypeOptions,
interestCalculationDaysInYearTypeOptions, lockinPeriodFrequencyTypeOptions, withdrawalFeeTypeOptions, transactions,
charges, chargeOptions);
}
final List<DatatableData> datatableTemplates = this.entityDatatableChecksReadService.retrieveTemplates(StatusEnum.CREATE.getValue(),
EntityTables.SAVINGS.getName(), productId);
template.setDatatables(datatableTemplates);
return template;
}
private static final class SavingAccountTemplateMapper implements RowMapper<SavingsAccountData> {
private final ClientData client;
private final GroupGeneralData group;
private final String schemaSql;
SavingAccountTemplateMapper(final ClientData client, final GroupGeneralData group) {
this.client = client;
this.group = group;
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append("sp.id as productId, sp.name as productName, ");
sqlBuilder.append(
"sp.currency_code as currencyCode, sp.currency_digits as currencyDigits, sp.currency_multiplesof as inMultiplesOf, ");
sqlBuilder.append("curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, ");
sqlBuilder.append("curr.display_symbol as currencyDisplaySymbol, ");
sqlBuilder.append("sp.nominal_annual_interest_rate as nominalAnnualIterestRate, ");
sqlBuilder.append("sp.interest_compounding_period_enum as interestCompoundingPeriodType, ");
sqlBuilder.append("sp.interest_posting_period_enum as interestPostingPeriodType, ");
sqlBuilder.append("sp.interest_calculation_type_enum as interestCalculationType, ");
sqlBuilder.append("sp.interest_calculation_days_in_year_type_enum as interestCalculationDaysInYearType, ");
sqlBuilder.append("sp.min_required_opening_balance as minRequiredOpeningBalance, ");
sqlBuilder.append("sp.lockin_period_frequency as lockinPeriodFrequency,");
sqlBuilder.append("sp.lockin_period_frequency_enum as lockinPeriodFrequencyType, ");
// sqlBuilder.append("sp.withdrawal_fee_amount as
// withdrawalFeeAmount,");
// sqlBuilder.append("sp.withdrawal_fee_type_enum as
// withdrawalFeeTypeEnum, ");
sqlBuilder.append("sp.withdrawal_fee_for_transfer as withdrawalFeeForTransfers, ");
sqlBuilder.append("sp.min_balance_for_interest_calculation as minBalanceForInterestCalculation, ");
sqlBuilder.append("sp.allow_overdraft as allowOverdraft, ");
sqlBuilder.append("sp.overdraft_limit as overdraftLimit, ");
sqlBuilder.append("sp.nominal_annual_interest_rate_overdraft as nominalAnnualInterestRateOverdraft, ");
sqlBuilder.append("sp.min_overdraft_for_interest_calculation as minOverdraftForInterestCalculation, ");
sqlBuilder.append("sp.withhold_tax as withHoldTax,");
sqlBuilder.append("tg.id as taxGroupId, tg.name as taxGroupName, ");
// sqlBuilder.append("sp.annual_fee_amount as annualFeeAmount,");
// sqlBuilder.append("sp.annual_fee_on_month as annualFeeOnMonth,
// ");
// sqlBuilder.append("sp.annual_fee_on_day as annualFeeOnDay ");
sqlBuilder.append("sp.min_required_balance as minRequiredBalance, ");
sqlBuilder.append("sp.enforce_min_required_balance as enforceMinRequiredBalance, ");
sqlBuilder.append("sp.max_allowed_lien_limit as maxAllowedLienLimit, ");
sqlBuilder.append("sp.is_lien_allowed as lienAllowed ");
sqlBuilder.append("from m_savings_product sp ");
sqlBuilder.append("join m_currency curr on curr.code = sp.currency_code ");
sqlBuilder.append("left join m_tax_group tg on tg.id = sp.tax_group_id ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public SavingsAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long productId = rs.getLong("productId");
final String productName = rs.getString("productName");
final String currencyCode = rs.getString("currencyCode");
final String currencyName = rs.getString("currencyName");
final String currencyNameCode = rs.getString("currencyNameCode");
final String currencyDisplaySymbol = rs.getString("currencyDisplaySymbol");
final Integer currencyDigits = JdbcSupport.getInteger(rs, "currencyDigits");
final Integer inMultiplesOf = JdbcSupport.getInteger(rs, "inMultiplesOf");
final CurrencyData currency = new CurrencyData(currencyCode, currencyName, currencyDigits, inMultiplesOf, currencyDisplaySymbol,
currencyNameCode);
final BigDecimal nominalAnnualIterestRate = rs.getBigDecimal("nominalAnnualIterestRate");
final EnumOptionData interestCompoundingPeriodType = SavingsEnumerations.compoundingInterestPeriodType(
SavingsCompoundingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestCompoundingPeriodType")));
final EnumOptionData interestPostingPeriodType = SavingsEnumerations.interestPostingPeriodType(
SavingsPostingInterestPeriodType.fromInt(JdbcSupport.getInteger(rs, "interestPostingPeriodType")));
final EnumOptionData interestCalculationType = SavingsEnumerations
.interestCalculationType(SavingsInterestCalculationType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationType")));
final EnumOptionData interestCalculationDaysInYearType = SavingsEnumerations.interestCalculationDaysInYearType(
SavingsInterestCalculationDaysInYearType.fromInt(JdbcSupport.getInteger(rs, "interestCalculationDaysInYearType")));
final BigDecimal minRequiredOpeningBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredOpeningBalance");
final Integer lockinPeriodFrequency = JdbcSupport.getInteger(rs, "lockinPeriodFrequency");
EnumOptionData lockinPeriodFrequencyType = null;
final Integer lockinPeriodFrequencyTypeValue = JdbcSupport.getInteger(rs, "lockinPeriodFrequencyType");
if (lockinPeriodFrequencyTypeValue != null) {
final SavingsPeriodFrequencyType lockinPeriodType = SavingsPeriodFrequencyType.fromInt(lockinPeriodFrequencyTypeValue);
lockinPeriodFrequencyType = SavingsEnumerations.lockinPeriodFrequencyType(lockinPeriodType);
}
// final BigDecimal withdrawalFeeAmount =
// rs.getBigDecimal("withdrawalFeeAmount");
/*
* EnumOptionData withdrawalFeeType = null; final Integer withdrawalFeeTypeValue =
* JdbcSupport.getInteger(rs, "withdrawalFeeTypeEnum"); if (withdrawalFeeTypeValue != null) {
* withdrawalFeeType = SavingsEnumerations.withdrawalFeeType(withdrawalFeeTypeValue); }
*/
final boolean withdrawalFeeForTransfers = rs.getBoolean("withdrawalFeeForTransfers");
final boolean allowOverdraft = rs.getBoolean("allowOverdraft");
final BigDecimal overdraftLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "overdraftLimit");
final BigDecimal nominalAnnualInterestRateOverdraft = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"nominalAnnualInterestRateOverdraft");
final BigDecimal minOverdraftForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"minOverdraftForInterestCalculation");
final BigDecimal minRequiredBalance = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "minRequiredBalance");
final boolean enforceMinRequiredBalance = rs.getBoolean("enforceMinRequiredBalance");
final BigDecimal maxAllowedLienLimit = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs, "maxAllowedLienLimit");
final boolean lienAllowed = rs.getBoolean("lienAllowed");
final BigDecimal minBalanceForInterestCalculation = JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
"minBalanceForInterestCalculation");
// final BigDecimal annualFeeAmount =
// JdbcSupport.getBigDecimalDefaultToNullIfZero(rs,
// "annualFeeAmount");
/*
* MonthDay annualFeeOnMonthDay = null; final Integer annualFeeOnMonth = JdbcSupport.getInteger(rs,
* "annualFeeOnMonth"); final Integer annualFeeOnDay = JdbcSupport.getInteger(rs, "annualFeeOnDay"); if
* (annualFeeAmount != null && annualFeeOnDay != null) { annualFeeOnMonthDay = new
* MonthDay(annualFeeOnMonth, annualFeeOnDay); }
*/
final boolean withHoldTax = rs.getBoolean("withHoldTax");
final Long taxGroupId = JdbcSupport.getLong(rs, "taxGroupId");
final String taxGroupName = rs.getString("taxGroupName");
TaxGroupData taxGroupData = null;
if (taxGroupId != null) {
taxGroupData = TaxGroupData.lookup(taxGroupId, taxGroupName);
}
Long clientId = null;
String clientName = null;
if (this.client != null) {
clientId = this.client.getId();
clientName = this.client.getDisplayName();
}
Long groupId = null;
String groupName = null;
if (this.group != null) {
groupId = this.group.getId();
groupName = this.group.getName();
}
final Long fieldOfficerId = null;
final String fieldOfficerName = null;
final SavingsAccountStatusEnumData status = null;
// final LocalDate annualFeeNextDueDate = null;
final SavingsAccountSummaryData summary = null;
final BigDecimal onHoldFunds = null;
final BigDecimal savingsAmountOnHold = null;
final SavingsAccountSubStatusEnumData subStatus = null;
final String reasonForBlock = null;
final LocalDate lastActiveTransactionDate = null;
final boolean isDormancyTrackingActive = false;
final Integer daysToInactive = null;
final Integer daysToDormancy = null;
final Integer daysToEscheat = null;
final SavingsAccountApplicationTimelineData timeline = SavingsAccountApplicationTimelineData.templateDefault();
final EnumOptionData depositType = null;
return SavingsAccountData.instance(null, null, depositType, null, groupId, groupName, clientId, clientName, productId,
productName, fieldOfficerId, fieldOfficerName, status, subStatus, reasonForBlock, timeline, currency,
nominalAnnualIterestRate, interestCompoundingPeriodType, interestPostingPeriodType, interestCalculationType,
interestCalculationDaysInYearType, minRequiredOpeningBalance, lockinPeriodFrequency, lockinPeriodFrequencyType,
withdrawalFeeForTransfers, summary, allowOverdraft, overdraftLimit, minRequiredBalance, enforceMinRequiredBalance,
maxAllowedLienLimit, lienAllowed, minBalanceForInterestCalculation, onHoldFunds, nominalAnnualInterestRateOverdraft,
minOverdraftForInterestCalculation, withHoldTax, taxGroupData, lastActiveTransactionDate, isDormancyTrackingActive,
daysToInactive, daysToDormancy, daysToEscheat, savingsAmountOnHold);
}
}
private Collection<SavingsAccountChargeData> fromChargesToSavingsCharges(final Collection<ChargeData> productCharges) {
final Collection<SavingsAccountChargeData> savingsCharges = new ArrayList<>();
for (final ChargeData chargeData : productCharges) {
final SavingsAccountChargeData savingsCharge = ConvertChargeDataToSpecificChargeData.toSavingsAccountChargeData(chargeData);
savingsCharges.add(savingsCharge);
}
return savingsCharges;
}
}
@@ -124,6 +124,8 @@ import org.apache.fineract.portfolio.savings.service.SavingsAccountInterestPosti
import org.apache.fineract.portfolio.savings.service.SavingsAccountInterestPostingServiceImpl; import org.apache.fineract.portfolio.savings.service.SavingsAccountInterestPostingServiceImpl;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService; import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformServiceImpl; import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformServiceImpl;
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountTemplateReadPlatformServiceImpl;
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService; import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformService;
import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformServiceJpaRepositoryImpl; import org.apache.fineract.portfolio.savings.service.SavingsAccountWritePlatformServiceJpaRepositoryImpl;
import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService; import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService;
@@ -335,16 +337,23 @@ public class SavingsConfiguration {
@Bean @Bean
@ConditionalOnMissingBean(SavingsAccountReadPlatformService.class) @ConditionalOnMissingBean(SavingsAccountReadPlatformService.class)
public SavingsAccountReadPlatformService savingsAccountReadPlatformService(PlatformSecurityContext context, JdbcTemplate jdbcTemplate, public SavingsAccountReadPlatformService savingsAccountReadPlatformService(PlatformSecurityContext context, JdbcTemplate jdbcTemplate,
ClientReadPlatformService clientReadPlatformService, GroupReadPlatformService groupReadPlatformService,
SavingsProductReadPlatformService savingProductReadPlatformService, StaffReadPlatformService staffReadPlatformService,
SavingsDropdownReadPlatformService dropdownReadPlatformService, ChargeReadPlatformService chargeReadPlatformService,
EntityDatatableChecksReadService entityDatatableChecksReadService, ColumnValidator columnValidator,
SavingsAccountAssembler savingAccountAssembler, PaginationHelper paginationHelper, DatabaseSpecificSQLGenerator sqlGenerator, SavingsAccountAssembler savingAccountAssembler, PaginationHelper paginationHelper, DatabaseSpecificSQLGenerator sqlGenerator,
SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper) { SavingsAccountRepositoryWrapper savingsAccountRepositoryWrapper, ColumnValidator columnValidator) {
return new SavingsAccountReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService, return new SavingsAccountReadPlatformServiceImpl(context, jdbcTemplate, savingAccountAssembler, paginationHelper, columnValidator,
sqlGenerator, savingsAccountRepositoryWrapper);
}
@Bean
@ConditionalOnMissingBean(SavingsAccountTemplateReadPlatformService.class)
public SavingsAccountTemplateReadPlatformService savingsAccountTemplateReadPlatformService(PlatformSecurityContext context,
JdbcTemplate jdbcTemplate, ClientReadPlatformService clientReadPlatformService,
GroupReadPlatformService groupReadPlatformService, SavingsProductReadPlatformService savingProductReadPlatformService,
StaffReadPlatformService staffReadPlatformService, SavingsDropdownReadPlatformService dropdownReadPlatformService,
ChargeReadPlatformService chargeReadPlatformService, EntityDatatableChecksReadService entityDatatableChecksReadService,
ColumnValidator columnValidator) {
return new SavingsAccountTemplateReadPlatformServiceImpl(context, jdbcTemplate, clientReadPlatformService, groupReadPlatformService,
savingProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService, savingProductReadPlatformService, staffReadPlatformService, dropdownReadPlatformService, chargeReadPlatformService,
entityDatatableChecksReadService, columnValidator, savingAccountAssembler, paginationHelper, sqlGenerator, entityDatatableChecksReadService, columnValidator);
savingsAccountRepositoryWrapper);
} }
@Bean @Bean
@@ -0,0 +1,207 @@
/**
* 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 static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.codes.service.CodeReadPlatformService;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenantConnection;
import org.apache.fineract.infrastructure.core.serialization.DatatableCommandFromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
import org.apache.fineract.infrastructure.dataqueries.data.DataTableValidator;
import org.apache.fineract.infrastructure.dataqueries.data.ResultsetRowData;
import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.datatable.DatatableEntryBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.utils.ColumnValidator;
import org.apache.fineract.infrastructure.security.utils.DefaultSqlValidator;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.portfolio.search.service.SearchUtil;
import org.apache.fineract.useradministration.domain.AppUser;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.jdbc.support.rowset.SqlRowSet;
@ExtendWith(MockitoExtension.class)
public class DatatableBusinessEventTest {
@Mock
private PlatformSecurityContext context;
@Mock
private BusinessEventNotifierService businessEventNotifierService;
@Mock
private JdbcTemplate jdbcTemplate;
@Mock
private GenericDataService genericDataService;
@Mock
private SearchUtil searchUtil;
@Mock
private DatabaseTypeResolver databaseTypeResolver;
@Mock
private DatabaseSpecificSQLGenerator sqlGenerator;
@Mock
private FineractPlatformTenantConnection tenantConnection;
@Mock
private DefaultSqlValidator sqlValidator;
@Mock
private FromJsonHelper fromJsonHelper;
@Mock
private DatatableCommandFromApiJsonDeserializer fromApiJsonDeserializer;
@Mock
private ConfigurationDomainService configurationDomainService;
@Mock
private CodeReadPlatformService codeReadPlatformService;
@Mock
private DataTableValidator dataTableValidator;
@Mock
private ColumnValidator columnValidator;
@Mock
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
@Mock
private DatatableKeywordGenerator datatableKeywordGenerator;
@InjectMocks
private ReadWriteNonCoreDataServiceImpl underTest;
@Captor
private ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor;
private static String DATATABLE_NAME = "test_loan_data";
@BeforeEach
public void setUp() {
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.parse("2024-01-16"),
BusinessDateType.COB_DATE, LocalDate.parse("2024-01-15"))));
SqlRowSet sqlRS = Mockito.mock(SqlRowSet.class);
SqlRowSet sqlRSData = Mockito.mock(SqlRowSet.class);
doNothing().when(sqlValidator).validate(anyString());
when(jdbcTemplate.queryForRowSet(anyString(), anyString())).thenReturn(sqlRS);
when(jdbcTemplate.queryForRowSet(anyString())).thenReturn(sqlRSData);
when(sqlRS.next()).thenReturn(true).thenReturn(false);
when(sqlRS.getString("application_table_name")).thenReturn("m_loan");
when(sqlRSData.next()).thenReturn(true).thenReturn(false);
when(sqlRSData.getObject(anyString())).thenReturn(1L);
AppUser currentUser = Mockito.mock(AppUser.class);
Office office = Mockito.mock(Office.class);
when(context.authenticatedUser()).thenReturn(currentUser);
when(currentUser.getOffice()).thenReturn(office);
when(office.getHierarchy()).thenReturn(".");
}
@AfterEach
public void tearDown() {
ThreadLocalContextUtil.reset();
}
@Test
public void businessEventCreateNewDatatableEntryTest() {
when(jdbcTemplate.update(any(PreparedStatementCreator.class), any(KeyHolder.class))).thenReturn(1);
underTest.createNewDatatableEntry(DATATABLE_NAME, 1L, createJsonCommand("{}", 1L));
ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = verifyBusinessEvents(1);
verifyDatatableBusinessEvent(businessEventArgumentCaptor, 0, 1L);
}
@Test
public void businessEventUpdateDatatableEntryTest() {
List<Object> values = new ArrayList<>();
values.add(1L);
List<ResultsetRowData> result = new ArrayList<>();
result.add(ResultsetRowData.create(values));
when(genericDataService.fillResultsetRowData(anyString(), any())).thenReturn(result);
// ResultsetColumnHeaderData resultSet = Mockito.mock(ResultsetColumnHeaderData.class);
// when(searchUtil.getFiltered(columnHeaders,
// ResultsetColumnHeaderData::getIsColumnPrimaryKey)).thenReturn(resultSet);
underTest.updateDatatableEntryOneToMany(DATATABLE_NAME, 1L, 1L, createJsonCommand("{}", 1L));
ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = verifyBusinessEvents(1);
verifyDatatableBusinessEvent(businessEventArgumentCaptor, 0, 1L);
}
@Test
public void businessEventDeleteDatatableEntryTest() {
underTest.deleteDatatableEntry(DATATABLE_NAME, 1L, 1L, createJsonCommand("{}", 1L));
ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = verifyBusinessEvents(1);
verifyDatatableBusinessEvent(businessEventArgumentCaptor, 0, 1L);
}
@NotNull
private ArgumentCaptor<BusinessEvent<?>> verifyBusinessEvents(int expectedBusinessEvents) {
@SuppressWarnings("unchecked")
ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor = ArgumentCaptor.forClass(BusinessEvent.class);
verify(businessEventNotifierService, times(expectedBusinessEvents)).notifyPostBusinessEvent(businessEventArgumentCaptor.capture());
return businessEventArgumentCaptor;
}
private void verifyDatatableBusinessEvent(ArgumentCaptor<BusinessEvent<?>> businessEventArgumentCaptor, int index, Long entityId) {
assertTrue(businessEventArgumentCaptor.getAllValues().get(index) instanceof DatatableEntryBusinessEvent);
assertEquals(DATATABLE_NAME, ((DatatableEntryBusinessEvent) businessEventArgumentCaptor.getAllValues().get(index))
.getDatatableEntryDetails().getDatatableName());
assertEquals(entityId, ((DatatableEntryBusinessEvent) businessEventArgumentCaptor.getAllValues().get(index)).getAggregateRootId());
}
private JsonCommand createJsonCommand(final String jsonCommand, final Long resourceId) {
return new JsonCommand(null, jsonCommand, null, null, null, resourceId, null, null, null, null, null, null, null, null, null, null,
null, null);
}
}
@@ -40,8 +40,6 @@ public interface SavingsAccountReadPlatformService {
SavingsAccountData retrieveOne(Long savingsId); SavingsAccountData retrieveOne(Long savingsId);
SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly);
SavingsAccountTransactionData retrieveDepositTransactionTemplate(Long savingsId, DepositAccountType depositAccountType); SavingsAccountTransactionData retrieveDepositTransactionTemplate(Long savingsId, DepositAccountType depositAccountType);
Collection<SavingsAccountTransactionData> retrieveAllTransactions(Long savingsId, DepositAccountType depositAccountType); Collection<SavingsAccountTransactionData> retrieveAllTransactions(Long savingsId, DepositAccountType depositAccountType);
@@ -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.savings.service;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
public interface SavingsAccountTemplateReadPlatformService {
SavingsAccountData retrieveTemplate(Long clientId, Long groupId, Long productId, boolean staffInSelectedOfficeOnly);
}