From 86bb794f4122cc6c7b179c1de8e9d01b2229fc19 Mon Sep 17 00:00:00 2001 From: goutham-M Date: Fri, 21 Jun 2013 22:15:18 +0530 Subject: [PATCH] close client functionality --- .../V66__client_close_functionality.sql | 8 ++ .../commands/domain/CommandWrapper.java | 3 + .../service/CommandWrapperBuilder.java | 9 +++ .../SynchronousCommandProcessingService.java | 2 + .../client/api/ClientApiConstants.java | 7 +- .../client/api/ClientsApiResource.java | 21 +++-- .../portfolio/client/data/ClientData.java | 24 +++--- .../client/data/ClientDataValidator.java | 25 ++++++ .../portfolio/client/domain/Client.java | 19 +++++ ...InvalidClientStateTransitionException.java | 20 +++++ .../handler/CloseClientCommandHandler.java | 27 +++++++ .../service/ClientReadPlatformService.java | 2 + .../ClientReadPlatformServiceImpl.java | 17 ++++- .../service/ClientWritePlatformService.java | 2 + ...WritePlatformServiceJpaRepositoryImpl.java | 76 ++++++++++++++++++- .../portfolio/loanaccount/domain/Loan.java | 10 ++- .../loanaccount/domain/LoanRepository.java | 3 + .../domain/SavingsAccountRepository.java | 6 ++ 18 files changed, 258 insertions(+), 23 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V66__client_close_functionality.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/InvalidClientStateTransitionException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CloseClientCommandHandler.java diff --git a/mifosng-db/migrations/core_db/V66__client_close_functionality.sql b/mifosng-db/migrations/core_db/V66__client_close_functionality.sql new file mode 100644 index 000000000..09d056371 --- /dev/null +++ b/mifosng-db/migrations/core_db/V66__client_close_functionality.sql @@ -0,0 +1,8 @@ +INSERT INTO `m_code` (`code_name`, `is_system_defined`) VALUES ('ClientClosureReason', 1) + +ALTER TABLE `m_client` ADD COLUMN `closure_reason_cv_id` INT(11) NULL DEFAULT NULL, +ADD COLUMN `closedon_date` DATE NULL DEFAULT NULL, +ADD CONSTRAINT `FK_m_client_m_code` FOREIGN KEY (`closure_reason_cv_id`) REFERENCES `m_code_value` (`id`) + +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`) VALUES ('portfolio', 'CLOSE_CLIENT', 'CLIENT', 'CLOSE') + diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java index 903c40163..2ebe99ad5 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -553,4 +553,7 @@ public class CommandWrapper { return this.actionName.equalsIgnoreCase("ASSIGNSTAFF") && this.entityName.equalsIgnoreCase("CLIENT"); } + public boolean isClientClose() { + return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("CLIENT"); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index 2607098e7..b49d75400 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -336,6 +336,15 @@ public class CommandWrapperBuilder { this.href = "/clients/" + clientId + "?command=activate&template=true"; return this; } + + public CommandWrapperBuilder closeClient(final Long clientId) { + this.actionName = "CLOSE"; + this.entityName = "CLIENT"; + this.entityId = clientId; + this.clientId = clientId; + this.href = "/clients/" + clientId + "?command=close&template=true"; + return this; + } public CommandWrapperBuilder updateClient(final Long clientId) { this.actionName = "UPDATE"; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java index 36825400d..bc60b106e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -161,6 +161,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("unassignClientStaffCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isClientAssignStaff()) { handler = this.applicationContext.getBean("assignClientStaffCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isClientClose()) { + handler = this.applicationContext.getBean("closeClientCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java index 0ed6dc3ff..e7420e4b6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientApiConstants.java @@ -14,6 +14,7 @@ import org.mifosplatform.portfolio.client.data.ClientData; public class ClientApiConstants { public static final String CLIENT_RESOURCE_NAME = "client"; + public static final String CLIENT_CLOSURE_REASON = "ClientClosureReason"; // general public static final String localeParamName = "locale"; @@ -32,7 +33,8 @@ public class ClientApiConstants { public static final String activeParamName = "active"; public static final String activationDateParamName = "activationDate"; public static final String staffIdParamName = "staffId"; - + public static final String closureDateParamName = "closureDate"; + public static final String closureReasonIdParamName = "closureReasonId"; // response parameters public static final String statusParamName = "status"; public static final String hierarchyParamName = "hierarchy"; @@ -68,4 +70,7 @@ public class ClientApiConstants { public static final Set ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, activationDateParamName)); + + public static final Set CLIENT_CLOSE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, closureDateParamName, closureReasonIdParamName)); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java index 86f4a3d94..d9a79f455 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java @@ -84,11 +84,18 @@ public class ClientsApiResource { @Path("template") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveTemplate(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId) { + public String retrieveTemplate(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId, + @QueryParam("commandParam") final String commandParam) { context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME); - final ClientData clientData = this.clientReadPlatformService.retrieveTemplate(officeId); + ClientData clientData = null; + context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME); + if (is(commandParam, "close")) { + clientData = this.clientReadPlatformService.retrieveAllClosureReasons(ClientApiConstants.CLIENT_CLOSURE_REASON); + }else{ + clientData = this.clientReadPlatformService.retrieveTemplate(officeId); + } final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS); @@ -194,17 +201,21 @@ public class ClientsApiResource { final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson); CommandProcessingResult result = null; + CommandWrapper commandRequest = null; if (is(commandParam, "activate")) { - final CommandWrapper commandRequest = builder.activateClient(clientId).build(); + commandRequest = builder.activateClient(clientId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } else if (is(commandParam, "assignStaff")) { - final CommandWrapper commandRequest = builder.assignClientStaff(clientId).build(); + commandRequest = builder.assignClientStaff(clientId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return this.toApiJsonSerializer.serialize(result); } else if (is(commandParam, "unassignStaff")) { - final CommandWrapper commandRequest = builder.unassignClientStaff(clientId).build(); + commandRequest = builder.unassignClientStaff(clientId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return this.toApiJsonSerializer.serialize(result); + } else if(is(commandParam, "close")) { + commandRequest = builder.closeClient(clientId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); } if (result == null) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java index 3947a242e..7e62fd3d0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java @@ -13,6 +13,7 @@ import org.apache.commons.lang.builder.CompareToBuilder; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.staff.data.StaffData; @@ -49,14 +50,15 @@ final public class ClientData implements Comparable { // associations private final Collection groups; - + // template private final Collection officeOptions; private final Collection staffOptions; - + private final Collection closureReasons; + public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection officeOptions, - final Collection staffOptions) { - return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, null, null, officeOptions, null, staffOptions); + final Collection staffOptions, final Collection closureReasons) { + return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, null, null, officeOptions, null, staffOptions, closureReasons); } public static ClientData templateOnTop(final ClientData clientData, final List allowedOffices, final Collection staffOptions) { @@ -64,14 +66,14 @@ final public class ClientData implements Comparable { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, - allowedOffices, clientData.groups, staffOptions); + allowedOffices, clientData.groups, staffOptions, null); } public static ClientData setParentGroups(final ClientData clientData, final Collection parentGroups) { return new ClientData(clientData.accountNo, clientData.status, clientData.officeId, clientData.officeName, clientData.id, clientData.firstname, clientData.middlename, clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, - clientData.officeOptions, parentGroups, clientData.staffOptions); + clientData.officeOptions, parentGroups, clientData.staffOptions, null); } public static ClientData clientIdentifier(final Long id, final String accountNo, final EnumOptionData status, final String firstname, @@ -79,11 +81,11 @@ final public class ClientData implements Comparable { final String officeName) { return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, null, - null, null, null, null, null, null,null); + null, null, null, null, null, null,null, null); } public static ClientData lookup(final Long id, final String displayName, final Long officeId, final String officeName) { - return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, null, null, null); + return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, null, null, null, null); } public static ClientData instance(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, @@ -91,13 +93,14 @@ final public class ClientData implements Comparable { final String displayName, final String externalId, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName) { return new ClientData(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, - externalId, activationDate, imageId, staffId, staffName, null, null, null); + externalId, activationDate, imageId, staffId, staffName, null, null, null, null); } private ClientData(final String accountNo, final EnumOptionData status, final Long officeId, final String officeName, final Long id, final String firstname, final String middlename, final String lastname, final String fullname, final String displayName, final String externalId, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName, - final Collection allowedOffices, final Collection groups, final Collection staffOptions) { + final Collection allowedOffices, final Collection groups, final Collection staffOptions, + final Collection closureReasons) { this.accountNo = accountNo; this.status = status; if (status != null) { @@ -130,6 +133,7 @@ final public class ClientData implements Comparable { // template this.officeOptions = allowedOffices; this.staffOptions = staffOptions; + this.closureReasons = closureReasons; } public Long id() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java index 392527fe3..5f1afbb99 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientDataValidator.java @@ -374,4 +374,29 @@ public final class ClientDataValidator { if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + + public void validateClose(final JsonCommand command) { + + final String json = command.json(); + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_CLOSE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ClientApiConstants.CLIENT_RESOURCE_NAME); + + final JsonElement element = command.parsedJson(); + + final LocalDate closureDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.closureDateParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.closureDateParamName).value(closureDate).notNull(); + + final Long closureReasonId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.closureReasonIdParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.closureReasonIdParamName).value(closureReasonId).notNull().longGreaterThanZero(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java index 002fb9229..e74aefb44 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java @@ -95,6 +95,15 @@ public final class Client extends AbstractPersistable { @Transient private boolean accountNumberRequiresAutoGeneration = false; + + @SuppressWarnings("unused") + @Column(name = "closure_reason_cv_id", nullable = true) + private Long closureReasonId; + + @SuppressWarnings("unused") + @Column(name = "closedon_date", nullable = true) + @Temporal(TemporalType.DATE) + private Date closureDate; public static Client createNew(final Office clientOffice, final Group clientParentGroup, final Staff staff, final JsonCommand command) { @@ -447,4 +456,14 @@ public final class Client extends AbstractPersistable { this.staff = staff; } + + public void close(final Long closureReasonId, final Date closureDate) { + this.closureReasonId = closureReasonId; + this.closureDate = closureDate; + this.status = ClientStatus.CLOSED.getValue(); + } + + public Integer getStatus() { + return status; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/InvalidClientStateTransitionException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/InvalidClientStateTransitionException.java new file mode 100644 index 000000000..b78cabf04 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/InvalidClientStateTransitionException.java @@ -0,0 +1,20 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.client.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * {@link AbstractPlatformDomainRuleException} thrown an action to transition a loan from one state to another violates a domain rule. + */ +public class InvalidClientStateTransitionException extends AbstractPlatformDomainRuleException { + + public InvalidClientStateTransitionException(final String action, String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) { + super("error.msg.client." + action + "." + postFix, defaultUserMessage, defaultUserMessageArgs); + // TODO Auto-generated constructor stub + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CloseClientCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CloseClientCommandHandler.java new file mode 100644 index 000000000..da23f402a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/CloseClientCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.client.handler; + +import org.mifosplatform.commands.handler.NewCommandSourceHandler; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.portfolio.client.service.ClientWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CloseClientCommandHandler implements NewCommandSourceHandler { + + private final ClientWritePlatformService clientWritePlatformService; + + @Autowired + public CloseClientCommandHandler(final ClientWritePlatformService clientWritePlatformService) { + this.clientWritePlatformService = clientWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.clientWritePlatformService.closeClient(command.entityId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java index 937b0d397..29badd023 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformService.java @@ -33,4 +33,6 @@ public interface ClientReadPlatformService { ClientData retrieveClientByIdentifier(Long identifierTypeId, String identifierKey); Collection retrieveClientMembersOfGroup(Long groupId); + + ClientData retrieveAllClosureReasons(String clientClosureReason); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java index 40dd5702a..4ce67813d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -13,6 +13,8 @@ import java.util.List; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService; import org.mifosplatform.infrastructure.core.api.ApiParameterHelper; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; @@ -51,7 +53,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService private final PlatformSecurityContext context; private final OfficeReadPlatformService officeReadPlatformService; private final StaffReadPlatformService staffReadPlatformService; - + private final CodeValueReadPlatformService codeValueReadPlatformService; // data mappers private final PaginationHelper paginationHelper = new PaginationHelper(); private final ClientMapper clientMapper = new ClientMapper(); @@ -61,11 +63,13 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService @Autowired public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource, - final OfficeReadPlatformService officeReadPlatformService, final StaffReadPlatformService staffReadPlatformService) { + final OfficeReadPlatformService officeReadPlatformService, final StaffReadPlatformService staffReadPlatformService, + final CodeValueReadPlatformService codeValueReadPlatformService) { this.context = context; this.officeReadPlatformService = officeReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); this.staffReadPlatformService = staffReadPlatformService; + this.codeValueReadPlatformService = codeValueReadPlatformService; } @Override @@ -82,7 +86,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService staffOptions = null; } - return ClientData.template(defaultOfficeId, new LocalDate(), offices, staffOptions); + return ClientData.template(defaultOfficeId, new LocalDate(), offices, staffOptions, null); } @Override @@ -586,4 +590,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService } return defaultOfficeId; } + + @Override + public ClientData retrieveAllClosureReasons(String clientClosureReason) { + final List closureReasons = new ArrayList(codeValueReadPlatformService.retrieveCodeValuesByCode(clientClosureReason)); + return ClientData.template(null, null, null, null, closureReasons); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java index 6fe9b07b5..eb4f6fe74 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformService.java @@ -19,6 +19,8 @@ public interface ClientWritePlatformService { CommandProcessingResult deleteClient(Long clientId); CommandProcessingResult unassignClientStaff(Long clientId, JsonCommand command); + + CommandProcessingResult closeClient(final Long clientId, final JsonCommand command); CommandProcessingResult assignClientStaff(Long clientId, JsonCommand command); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index d4bf3cafb..773f975f4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -13,6 +13,7 @@ import java.util.Map; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; @@ -29,13 +30,19 @@ import org.mifosplatform.portfolio.client.domain.AccountNumberGenerator; import org.mifosplatform.portfolio.client.domain.AccountNumberGeneratorFactory; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.client.domain.ClientStatus; import org.mifosplatform.portfolio.client.exception.ClientHasNoStaffException; import org.mifosplatform.portfolio.client.exception.ClientMustBePendingToBeDeletedException; +import org.mifosplatform.portfolio.client.exception.InvalidClientStateTransitionException; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.GroupRepository; import org.mifosplatform.portfolio.group.exception.GroupNotFoundException; +import org.mifosplatform.portfolio.loanaccount.domain.Loan; +import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; import org.mifosplatform.portfolio.note.domain.Note; import org.mifosplatform.portfolio.note.domain.NoteRepository; +import org.mifosplatform.portfolio.savings.domain.SavingsAccount; +import org.mifosplatform.portfolio.savings.domain.SavingsAccountRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -56,12 +63,16 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP private final ClientDataValidator fromApiJsonDeserializer; private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory; private final StaffRepositoryWrapper staffRepository; + private final CodeValueRepositoryWrapper codeValueRepository; + private final LoanRepository loanRepository; + private final SavingsAccountRepository savingsRepository; @Autowired public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final ClientRepositoryWrapper clientRepository, final OfficeRepository officeRepository, final NoteRepository noteRepository, final ClientDataValidator fromApiJsonDeserializer, final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory, - final GroupRepository groupRepository, final StaffRepositoryWrapper staffRepository) { + final GroupRepository groupRepository, final StaffRepositoryWrapper staffRepository, final CodeValueRepositoryWrapper codeValueRepository, + final LoanRepository loanRepository, final SavingsAccountRepository savingsRepository) { this.context = context; this.clientRepository = clientRepository; this.officeRepository = officeRepository; @@ -70,6 +81,9 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory; this.groupRepository = groupRepository; this.staffRepository = staffRepository; + this.codeValueRepository = codeValueRepository; + this.loanRepository = loanRepository; + this.savingsRepository = savingsRepository; } @Transactional @@ -305,4 +319,64 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP .with(actualChanges) // .build(); } + + @Transactional + @Override + public CommandProcessingResult closeClient(final Long clientId, final JsonCommand command) { + try { + this.fromApiJsonDeserializer.validateClose(command); + + Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); + final LocalDate closureDate = command.localDateValueOfParameterNamed(ClientApiConstants.closureDateParamName); + final Long closureReasonId = command.longValueOfParameterNamed(ClientApiConstants.closureReasonIdParamName); + + this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection(ClientApiConstants.CLIENT_CLOSURE_REASON, closureReasonId); + + if (ClientStatus.fromInt(client.getStatus()).isClosed()) { + final String errorMessage = "Client is alread closed."; + throw new InvalidClientStateTransitionException("close", "is.already.closed", errorMessage); + } + + if (client.isNotPending() && client.getActivationLocalDate().isAfter(closureDate)) { + final String errorMessage = "The client closureDate cannot be before the client ActivationDate."; + throw new InvalidClientStateTransitionException("close", "date.cannot.before.client.actvation.date", errorMessage, + closureDate, client.getActivationLocalDate()); + } + + List clientLoans = this.loanRepository.findLoanByClientId(clientId); + + for (Loan loan : clientLoans) { + LoanStatusMapper loanStatus = new LoanStatusMapper(loan.status().getValue()); + if (loanStatus.isOpen()) { + final String errorMessage = "Client cannot be closed because of non-closed loans."; + throw new InvalidClientStateTransitionException("close", "loan.non-closed", errorMessage); + } else if (loanStatus.isClosed() && loan.getClosedOnDate().after(closureDate.toDate())) { + final String errorMessage = "The client closureDate cannot be before the loan closedOnDate."; + throw new InvalidClientStateTransitionException("close", "date.cannot.before.loan.closed.date", errorMessage, + closureDate, loan.getClosedOnDate()); + } + } + List clientSavingAccounts = this.savingsRepository.findSavingAccountByClientId(clientId); + + for (SavingsAccount saving : clientSavingAccounts) { + if (saving.isActive()) { + final String errorMessage = "Client cannot be closed because of non-closed savings account."; + throw new InvalidClientStateTransitionException("close", "non-closed.savings.account", errorMessage); + } + } + + client.close(closureReasonId, closureDate.toDate()); + this.clientRepository.saveAndFlush(client); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withClientId(clientId) // + .withEntityId(clientId) // + .build(); + } catch (DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve); + return CommandProcessingResult.empty(); + } + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index 594d2c332..cf195ef33 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -2317,8 +2317,12 @@ public class Loan extends AbstractPersistable { this.transactionProcessorFactory = transactionProcessorFactory; } - public boolean isSyncDisbursementWithMeeting() { - return this.syncDisbursementWithMeeting == null ? false : this.syncDisbursementWithMeeting; - } + public boolean isSyncDisbursementWithMeeting() { + return this.syncDisbursementWithMeeting == null ? false : this.syncDisbursementWithMeeting; + } + + public Date getClosedOnDate() { + return this.closedOnDate; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java index d27ac4ef9..542aa867a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepository.java @@ -16,4 +16,7 @@ public interface LoanRepository extends JpaRepository, JpaSpecificationExecutor { @Query("from Loan loan where loan.client.id = :clientId and loan.group.id = :groupId") List findByClientIdAndGroupId(@Param("clientId") Long clientId, @Param("groupId") Long groupId); + + @Query("from Loan loan where loan.client.id = :clientId") + List findLoanByClientId(@Param("clientId") Long clientId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java index c728382b8..67539b0cb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountRepository.java @@ -5,9 +5,15 @@ */ package org.mifosplatform.portfolio.savings.domain; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface SavingsAccountRepository extends JpaRepository, JpaSpecificationExecutor { + @Query("from SavingsAccount s_acc where s_acc.client.id = :clientId") + List findSavingAccountByClientId(@Param("clientId") Long clientId); } \ No newline at end of file