diff --git a/mifosng-db/0001a-mifos-platform-base-ddl-schema.sql b/mifosng-db/0001a-mifos-platform-base-ddl-schema.sql index 6649d703a..708e83be0 100644 --- a/mifosng-db/0001a-mifos-platform-base-ddl-schema.sql +++ b/mifosng-db/0001a-mifos-platform-base-ddl-schema.sql @@ -196,6 +196,7 @@ CREATE TABLE `admin_appuser_role` ( -- DDL for portfolio tables CREATE TABLE `portfolio_client` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, + `is_deleted` tinyint(1) NOT NULL DEFAULT 0, `office_id` bigint(20) NOT NULL, `external_id` varchar(100) DEFAULT NULL, `firstname` varchar(50) DEFAULT NULL, diff --git a/mifosng-db/patches-post-multi-tenant/patch-0005-add-soft-deleted-flat-for-client.sql b/mifosng-db/patches-post-multi-tenant/patch-0005-add-soft-deleted-flat-for-client.sql new file mode 100644 index 000000000..abc1226fa --- /dev/null +++ b/mifosng-db/patches-post-multi-tenant/patch-0005-add-soft-deleted-flat-for-client.sql @@ -0,0 +1,3 @@ +-- add soft delete flag for clients + +ALTER TABLE `portfolio_client` ADD COLUMN `is_deleted` tinyint(1) NOT NULL DEFAULT 0; diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java index 7087e324c..2e36fe06e 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/ApiDataConversionServiceImpl.java @@ -708,8 +708,8 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { throw new InvalidJsonException(); } - Type typeOfMap = new TypeToken>(){}.getType(); - Map requestMap = gsonConverter.fromJson(json, typeOfMap); + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); Set supportedParams = new HashSet( Arrays.asList("username", "firstname", "lastname", "password", "repeatPassword", "email", "officeId", "notSelectedRoles", "roles") @@ -765,8 +765,8 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { throw new InvalidJsonException(); } - Type typeOfMap = new TypeToken>(){}.getType(); - Map requestMap = gsonConverter.fromJson(json, typeOfMap); + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); Set supportedParams = new HashSet( Arrays.asList("fromOfficeId", "toOfficeId", "transactionDate", "currencyCode", "transactionAmount", "description", "locale", "dateFormat") @@ -792,8 +792,8 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { throw new InvalidJsonException(); } - Type typeOfMap = new TypeToken>(){}.getType(); - Map requestMap = gsonConverter.fromJson(json, typeOfMap); + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); Set supportedParams = new HashSet( Arrays.asList("externalId", "firstname", "lastname", "clientOrBusinessName", "officeId", "joiningDate", "dateFormat") @@ -863,8 +863,8 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { throw new InvalidJsonException(); } - Type typeOfMap = new TypeToken>(){}.getType(); - Map requestMap = gsonConverter.fromJson(json, typeOfMap); + Type typeOfMap = new TypeToken>(){}.getType(); + Map requestMap = gsonConverter.fromJson(json, typeOfMap); Set supportedParams = new HashSet( Arrays.asList("clientId", "productId", "externalId", "fundId", @@ -1089,12 +1089,12 @@ public class ApiDataConversionServiceImpl implements ApiDataConversionService { return paramValue; } - private Long extractLongParameter(final String paramName, final Map requestMap, final Set modifiedParameters) { + private Long extractLongParameter(final String paramName, final Map requestMap, final Set modifiedParameters) { Long paramValue = null; if (requestMap.containsKey(paramName)) { - String valueAsString = (String) requestMap.get(paramName); + String valueAsString = requestMap.get(paramName); if (StringUtils.isNotBlank(valueAsString)) { - paramValue = Long.valueOf(valueAsString); + paramValue = Long.valueOf(Double.valueOf(valueAsString).longValue()); } modifiedParameters.add(paramName); } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java index 79b29b5a3..ced06f577 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java @@ -42,6 +42,9 @@ public class Client extends AbstractAuditableCustom { @Column(name = "external_id", length=100, unique=true) private String externalId; + @Column(name = "is_deleted", nullable=false) + private boolean deleted = false; + public static Client newClient(Office clientOffice, String firstname, String lastname, LocalDate joiningDate, String externalId) { return new Client(clientOffice, firstname, lastname, joiningDate, externalId); } @@ -112,4 +115,18 @@ public class Client extends AbstractAuditableCustom { this.externalId = externalId.trim(); } } + + /** + * Delete is a soft delete. Updates flag on client so it wont appear in query/report results. + * + * Any fields with unique constraints and prepended with id of record. + */ + public void delete() { + this.deleted = true; + this.externalId = this.getId() + "_" + this.externalId; + } + + public boolean isDeleted() { + return deleted; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java index 2bbcfe46d..129108c9c 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java @@ -74,7 +74,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService ClientMapper rm = new ClientMapper(offices); - String sql = "select " + rm.clientSchema() + " where c.office_id in (" + officeIdsList + ") "; + String sql = "select " + rm.clientSchema() + " where c.office_id in (" + officeIdsList + ") and c.is_deleted=0"; if (StringUtils.isNotBlank(extraCriteria)) sql += " and " + extraCriteria; @@ -95,7 +95,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService officeReadPlatformService.retrieveAllOffices()); ClientMapper rm = new ClientMapper(offices); - String sql = "select " + rm.clientSchema() + " where c.id = ? "; + String sql = "select " + rm.clientSchema() + " where c.id = ? and c.is_deleted=0"; return this.jdbcTemplate.queryForObject(sql, rm, new Object[] {clientId}); } catch (EmptyResultDataAccessException e) { @@ -166,6 +166,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService try { this.context.authenticatedUser(); + + // Check if client exists + retrieveIndividualClient(clientId); List pendingApprovalLoans = new ArrayList(); List awaitingDisbursalLoans = new ArrayList(); @@ -241,6 +244,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService try { context.authenticatedUser(); + + // Check if client exists + retrieveIndividualClient(clientId); // FIXME - use join on sql query to fetch user information for note // rather than fetching users? @@ -262,6 +268,9 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService public Collection retrieveAllClientNotes(Long clientId) { context.authenticatedUser(); + + // Check if client exists + retrieveIndividualClient(clientId); // FIXME - use join on sql query to fetch user information for note // rather than fetching users? diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformService.java index d2a852ddb..6e1c362d5 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformService.java @@ -20,5 +20,5 @@ public interface ClientWritePlatformService { EntityIdentifier updateNote(NoteCommand command); @PreAuthorize(value = "hasAnyRole('PORTFOLIO_MANAGEMENT_SUPER_USER_ROLE', 'CAN_DELETE_CLIENT_ROLE')") - void deleteClient(Long clientId); + EntityIdentifier deleteClient(Long clientId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index 99e4ae2ed..6cd59f88b 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -1,7 +1,5 @@ package org.mifosng.platform.client.service; -import java.util.List; - import org.apache.commons.lang.StringUtils; import org.mifosng.platform.api.commands.ClientCommand; import org.mifosng.platform.api.commands.NoteCommand; @@ -39,12 +37,17 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP @Transactional @Override - public void deleteClient(final Long clientId) { + public EntityIdentifier deleteClient(final Long clientId) { - List relatedNotes = this.noteRepository.findByClientId(clientId); - this.noteRepository.deleteInBatch(relatedNotes); + Client client = this.clientRepository.findOne(clientId); + if (client == null || client.isDeleted()) { + throw new ClientNotFoundException(clientId); + } - this.clientRepository.delete(clientId); + client.delete(); + this.clientRepository.save(client); + + return new EntityIdentifier(client.getId()); } @Transactional @@ -101,7 +104,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP } Client clientForUpdate = this.clientRepository.findOne(command.getId()); - if (clientForUpdate == null) { + if (clientForUpdate == null || clientForUpdate.isDeleted()) { throw new ClientNotFoundException(command.getId()); } clientForUpdate.update(clientOffice, firstname, lastname, command.getExternalId(), command.getJoiningDate());