From 7a05bb6b6a7465b5c5852d60f816f085baaff924 Mon Sep 17 00:00:00 2001 From: mifoscontributer Date: Thu, 13 Jun 2013 20:31:18 +0530 Subject: [PATCH] Allow linking of staff to Clients Changes required for UI to link staff to client --- .../core_db/v59__add_staff_id_to_m_client.sql | 5 + .../commands/domain/CommandWrapper.java | 4 + .../service/CommandWrapperBuilder.java | 9 ++ .../SynchronousCommandProcessingService.java | 2 + .../client/api/ClientApiConstants.java | 8 +- .../client/api/ClientsApiResource.java | 26 +++- .../portfolio/client/data/ClientData.java | 33 +++-- .../client/data/ClientDataValidator.java | 125 ++++++++++++------ .../portfolio/client/domain/Client.java | 69 ++++++++-- .../exception/ClientHasNoStaffException.java | 11 ++ .../UnassignClientStaffCommandHandler.java | 27 ++++ .../service/ClientReadPlatformService.java | 2 +- .../ClientReadPlatformServiceImpl.java | 46 +++++-- .../service/ClientWritePlatformService.java | 2 + ...WritePlatformServiceJpaRepositoryImpl.java | 73 ++++++++-- 15 files changed, 349 insertions(+), 93 deletions(-) create mode 100644 mifosng-db/migrations/core_db/v59__add_staff_id_to_m_client.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientHasNoStaffException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/UnassignClientStaffCommandHandler.java diff --git a/mifosng-db/migrations/core_db/v59__add_staff_id_to_m_client.sql b/mifosng-db/migrations/core_db/v59__add_staff_id_to_m_client.sql new file mode 100644 index 000000000..af90c29a1 --- /dev/null +++ b/mifosng-db/migrations/core_db/v59__add_staff_id_to_m_client.sql @@ -0,0 +1,5 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio', 'UNASSIGNSTAFF_CLIENT', 'CLIENT', 'UNASSIGNSTAFF', 0); +ALTER TABLE m_client + ADD COLUMN `staff_id` BIGINT(20) NULL DEFAULT NULL AFTER `office_id`, + ADD INDEX `client_staff_id` (`staff_id`), + ADD CONSTRAINT `FK_m_client_m_staff` FOREIGN KEY (`staff_id`) REFERENCES `m_staff` (`id`); \ No newline at end of file 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 dc7b6a09c..13a573728 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 @@ -535,4 +535,8 @@ public class CommandWrapper { public boolean isHolidayResource() { return this.entityName.equalsIgnoreCase("HOLIDAY"); } + + public boolean isClientUnassignStaff() { + return this.actionName.equalsIgnoreCase("UNASSIGNSTAFF") && 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 132a79d8e..5710fd6c4 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 @@ -1026,4 +1026,13 @@ public class CommandWrapperBuilder { this.href = "/holidays/template"; return this; } + + public CommandWrapperBuilder unassignClientStaff(Long clientId) { + this.actionName = "UNASSIGNSTAFF"; + this.entityName = "CLIENT"; + this.entityId = clientId; + this.clientId = clientId; + this.href = "/clients/" + clientId + "?command=unassignStaff"; + return this; + } } \ No newline at end of file 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 692c12bcf..434224655 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 @@ -157,6 +157,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = applicationContext.getBean("deleteClientCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isClientActivation()) { handler = applicationContext.getBean("activateClientCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isClientUnassignStaff()) { + handler = applicationContext.getBean("unassignClientStaffCommandHandler", 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 ac556c38f..0ed6dc3ff 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 @@ -31,6 +31,7 @@ public class ClientApiConstants { public static final String officeIdParamName = "officeId"; public static final String activeParamName = "active"; public static final String activationDateParamName = "activationDate"; + public static final String staffIdParamName = "staffId"; // response parameters public static final String statusParamName = "status"; @@ -45,14 +46,15 @@ public class ClientApiConstants { // template related part of response public static final String officeOptionsParamName = "officeOptions"; + public static final String staffOptionsParamName = "staffOptions"; public static final Set CLIENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, groupIdParamName, accountNoParamName, externalIdParamName, firstnameParamName, middlenameParamName, - lastnameParamName, fullnameParamName, officeIdParamName, activeParamName, activationDateParamName)); + lastnameParamName, fullnameParamName, officeIdParamName, activeParamName, activationDateParamName, staffIdParamName)); public static final Set CLIENT_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, accountNoParamName, externalIdParamName, firstnameParamName, middlenameParamName, - lastnameParamName, fullnameParamName, activeParamName, activationDateParamName)); + lastnameParamName, fullnameParamName, activeParamName, activationDateParamName, staffIdParamName)); /** * These parameters will match the class level parameters of @@ -62,7 +64,7 @@ public class ClientApiConstants { public static final Set CLIENT_RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList(idParamName, accountNoParamName, externalIdParamName, statusParamName, activeParamName, activationDateParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, displayNameParamName, officeIdParamName, officeNameParamName, hierarchyParamName, - imageKeyParamName, imagePresentParamName, groupsParamName, officeOptionsParamName)); + imageKeyParamName, imagePresentParamName, groupsParamName, officeOptionsParamName, staffOptionsParamName)); public static final Set ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, activationDateParamName)); 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 d23ae34c4..e54e7675f 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 @@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.client.api; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -37,6 +38,8 @@ import org.mifosplatform.infrastructure.core.service.Page; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; +import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; @@ -44,6 +47,7 @@ import org.mifosplatform.portfolio.group.service.SearchParameters; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; @Path("/clients") @Component @@ -57,13 +61,15 @@ public class ClientsApiResource { private final ToApiJsonSerializer clientAccountSummaryToApiJsonSerializer; private final ApiRequestParameterHelper apiRequestParameterHelper; private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final StaffReadPlatformService staffReadPlatformService; @Autowired public ClientsApiResource(final PlatformSecurityContext context, final ClientReadPlatformService readPlatformService, final OfficeReadPlatformService officeReadPlatformService, final ToApiJsonSerializer toApiJsonSerializer, final ToApiJsonSerializer clientAccountSummaryToApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, - final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final StaffReadPlatformService staffReadPlatformService) { this.context = context; this.clientReadPlatformService = readPlatformService; this.officeReadPlatformService = officeReadPlatformService; @@ -71,17 +77,18 @@ public class ClientsApiResource { this.clientAccountSummaryToApiJsonSerializer = clientAccountSummaryToApiJsonSerializer; this.apiRequestParameterHelper = apiRequestParameterHelper; this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.staffReadPlatformService = staffReadPlatformService; } @GET @Path("template") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveTemplate(@Context final UriInfo uriInfo) { + public String retrieveTemplate(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId) { context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME); - final ClientData clientData = this.clientReadPlatformService.retrieveTemplate(); + final ClientData clientData = this.clientReadPlatformService.retrieveTemplate(officeId); final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS); @@ -121,7 +128,11 @@ public class ClientsApiResource { ClientData clientData = this.clientReadPlatformService.retrieveOne(clientId); if (settings.isTemplate()) { final List allowedOffices = new ArrayList(officeReadPlatformService.retrieveAllOfficesForDropdown()); - clientData = ClientData.templateOnTop(clientData, allowedOffices); + Collection staffOptions = this.staffReadPlatformService.retrieveAllStaffForDropdown(clientData.officeId()); + if (CollectionUtils.isEmpty(staffOptions)) { + staffOptions = null; + } + clientData = ClientData.templateOnTop(clientData, allowedOffices, staffOptions); } return this.toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS); @@ -186,11 +197,14 @@ public class ClientsApiResource { if (is(commandParam, "activate")) { final CommandWrapper commandRequest = builder.activateClient(clientId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + } else if (is(commandParam, "unassignStaff")) { + final CommandWrapper commandRequest = builder.unassignClientStaff(clientId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); } if (result == null) { - // - throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate" }); + throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "unassignStaff" }); } return this.toApiJsonSerializer.serialize(result); 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 1463f0473..3947a242e 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 @@ -15,6 +15,7 @@ import org.apache.commons.lang.builder.HashCodeBuilder; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.data.EnumOptionData; import org.mifosplatform.organisation.office.data.OfficeData; +import org.mifosplatform.organisation.staff.data.StaffData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; /** @@ -43,28 +44,34 @@ final public class ClientData implements Comparable { private final Long imageId; private final Boolean imagePresent; + private final Long staffId; + private final String staffName; // associations private final Collection groups; // template private final Collection officeOptions; + private final Collection staffOptions; - public static ClientData template(final Long officeId, final LocalDate joinedDate, final Collection officeOptions) { - return new ClientData(null, null, officeId, null, null, null, null, null, null, null, null, joinedDate, null, officeOptions, null); + 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); } - public static ClientData templateOnTop(final ClientData clientData, final List allowedOffices) { + public static ClientData templateOnTop(final ClientData clientData, final List allowedOffices, final Collection staffOptions) { 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, allowedOffices, clientData.groups); + clientData.externalId, clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, + allowedOffices, clientData.groups, staffOptions); } 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.officeOptions, parentGroups); + clientData.externalId, clientData.activationDate, clientData.imageId, clientData.staffId, clientData.staffName, + clientData.officeOptions, parentGroups, clientData.staffOptions); } public static ClientData clientIdentifier(final Long id, final String accountNo, final EnumOptionData status, final String firstname, @@ -72,24 +79,25 @@ 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); } 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); + return new ClientData(null, null, officeId, officeName, id, null, null, null, null, displayName, null, null, null, null, null, null, null, null); } public static ClientData instance(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 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, null, null); + externalId, activationDate, imageId, staffId, staffName, 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 Collection allowedOffices, - final Collection groups) { + final String externalId, final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName, + final Collection allowedOffices, final Collection groups, final Collection staffOptions) { this.accountNo = accountNo; this.status = status; if (status != null) { @@ -113,12 +121,15 @@ final public class ClientData implements Comparable { } else { this.imagePresent = Boolean.FALSE; } + this.staffId = staffId; + this.staffName = staffName; // associations this.groups = groups; // template this.officeOptions = allowedOffices; + this.staffOptions = staffOptions; } 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 af350e5a5..c078986c4 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 @@ -9,8 +9,11 @@ import static org.mifosplatform.portfolio.savings.api.SavingsApiConstants.activa import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; @@ -42,24 +45,29 @@ public final class ClientDataValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken>() {}.getType(); - fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_CREATE_REQUEST_DATA_PARAMETERS); - final JsonElement element = fromApiJsonHelper.parse(json); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_CREATE_REQUEST_DATA_PARAMETERS); + final JsonElement element = this.fromApiJsonHelper.parse(json); final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(ClientApiConstants.CLIENT_RESOURCE_NAME); - final Long officeId = fromApiJsonHelper.extractLongNamed(ClientApiConstants.officeIdParamName, element); + final Long officeId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.officeIdParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.officeIdParamName).value(officeId).notNull().integerGreaterThanZero(); - if (fromApiJsonHelper.parameterExists(ClientApiConstants.groupIdParamName, element)) { - final Long groupId = fromApiJsonHelper.extractLongNamed(ClientApiConstants.groupIdParamName, element); + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.groupIdParamName, element)) { + final Long groupId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.groupIdParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.groupIdParamName).value(groupId).notNull().integerGreaterThanZero(); } - if (fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) { - final String accountNo = fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element); + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.staffIdParamName, element)) { + final Long staffId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.staffIdParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.staffIdParamName).value(staffId).notNull().longGreaterThanZero(); + } + + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) { + final String accountNo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20); } @@ -103,16 +111,17 @@ public final class ClientDataValidator { } } - if (fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) { - final String externalId = fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element); + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) { + final String externalId = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.externalIdParamName).value(externalId).ignoreIfNull() .notExceedingLengthOf(100); } - final Boolean active = fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element); + final Boolean active = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element); if (active != null) { if (active.booleanValue()) { - final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName, element); + final LocalDate joinedDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName, + element); baseDataValidator.reset().parameter(ClientApiConstants.activationDateParamName).value(joinedDate).notNull(); } } else { @@ -123,67 +132,67 @@ public final class ClientDataValidator { } private void validateIndividualNamePartsCannotBeUsedWithFullname(final JsonElement element, final DataValidatorBuilder baseDataValidator) { - final String firstnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element); + final String firstnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element); if (StringUtils.isNotBlank(firstnameParam)) { - final String fullnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); + final String fullnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.fullnameParamName).value(fullnameParam) .mustBeBlankWhenParameterProvided(ClientApiConstants.firstnameParamName, firstnameParam); } - final String middlenameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element); + final String middlenameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element); if (StringUtils.isNotBlank(middlenameParam)) { - final String fullnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); + final String fullnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.fullnameParamName).value(fullnameParam) .mustBeBlankWhenParameterProvided(ClientApiConstants.middlenameParamName, middlenameParam); } - final String lastnameParamName = fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element); + final String lastnameParamName = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element); if (StringUtils.isNotBlank(lastnameParamName)) { - final String fullnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); + final String fullnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.fullnameParamName).value(fullnameParam) .mustBeBlankWhenParameterProvided(ClientApiConstants.lastnameParamName, lastnameParamName); } } private void validateRequiredIndividualNamePartsExist(final JsonElement element, final DataValidatorBuilder baseDataValidator) { - final String firstnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element); + final String firstnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.firstnameParamName).value(firstnameParam).notBlank() .notExceedingLengthOf(50); - final String middlenameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element); + final String middlenameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.middlenameParamName).value(middlenameParam).ignoreIfNull() .notExceedingLengthOf(50); - final String lastnameParamName = fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element); + final String lastnameParamName = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.lastnameParamName).value(lastnameParamName).notBlank() .notExceedingLengthOf(50); } private void fullnameCannotBeBlank(final JsonElement element, final DataValidatorBuilder baseDataValidator) { - final String fullnameParam = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); + final String fullnameParam = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.fullnameParamName).value(fullnameParam).notBlank().notExceedingLengthOf(100); } private boolean isIndividualNamePartParameterPassed(final JsonElement element) { - return fromApiJsonHelper.parameterExists(ClientApiConstants.firstnameParamName, element) - || fromApiJsonHelper.parameterExists(ClientApiConstants.middlenameParamName, element) - || fromApiJsonHelper.parameterExists(ClientApiConstants.lastnameParamName, element); + return this.fromApiJsonHelper.parameterExists(ClientApiConstants.firstnameParamName, element) + || this.fromApiJsonHelper.parameterExists(ClientApiConstants.middlenameParamName, element) + || this.fromApiJsonHelper.parameterExists(ClientApiConstants.lastnameParamName, element); } private boolean isFullnameParameterPassed(final JsonElement element) { - return fromApiJsonHelper.parameterExists(ClientApiConstants.fullnameParamName, element); + return this.fromApiJsonHelper.parameterExists(ClientApiConstants.fullnameParamName, element); } private boolean isIndividualNameProvided(final JsonElement element) { - final String firstname = fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element); - final String middlename = fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element); - final String lastname = fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element); + final String firstname = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.firstnameParamName, element); + final String middlename = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.middlenameParamName, element); + final String lastname = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.lastnameParamName, element); return StringUtils.isNotBlank(firstname) || StringUtils.isNotBlank(middlename) || StringUtils.isNotBlank(lastname); } private boolean isFullnameProvided(final JsonElement element) { - final String fullname = fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); + final String fullname = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.fullnameParamName, element); return StringUtils.isNotBlank(fullname); } @@ -192,8 +201,8 @@ public final class ClientDataValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken>() {}.getType(); - fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_UPDATE_REQUEST_DATA_PARAMETERS); - final JsonElement element = fromApiJsonHelper.parse(json); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.CLIENT_UPDATE_REQUEST_DATA_PARAMETERS); + final JsonElement element = this.fromApiJsonHelper.parse(json); final List dataValidationErrors = new ArrayList(); @@ -201,9 +210,9 @@ public final class ClientDataValidator { .resource(ClientApiConstants.CLIENT_RESOURCE_NAME); boolean atLeastOneParameterPassedForUpdate = false; - if (fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) { + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.accountNoParamName, element)) { atLeastOneParameterPassedForUpdate = true; - final String accountNo = fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element); + final String accountNo = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.accountNoParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.accountNoParamName).value(accountNo).notBlank().notExceedingLengthOf(20); } @@ -247,36 +256,42 @@ public final class ClientDataValidator { } } - if (fromApiJsonHelper.parameterExists(ClientApiConstants.fullnameParamName, element)) { + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.fullnameParamName, element)) { atLeastOneParameterPassedForUpdate = true; } - if (fromApiJsonHelper.parameterExists(ClientApiConstants.lastnameParamName, element)) { + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.lastnameParamName, element)) { atLeastOneParameterPassedForUpdate = true; } - if (fromApiJsonHelper.parameterExists(ClientApiConstants.middlenameParamName, element)) { + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.middlenameParamName, element)) { atLeastOneParameterPassedForUpdate = true; } - if (fromApiJsonHelper.parameterExists(ClientApiConstants.firstnameParamName, element)) { + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.firstnameParamName, element)) { atLeastOneParameterPassedForUpdate = true; } - if (fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) { + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.externalIdParamName, element)) { atLeastOneParameterPassedForUpdate = true; - final String externalId = fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element); + final String externalId = this.fromApiJsonHelper.extractStringNamed(ClientApiConstants.externalIdParamName, element); baseDataValidator.reset().parameter(ClientApiConstants.externalIdParamName).value(externalId).notExceedingLengthOf(100); } - final Boolean active = fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element); + final Boolean active = this.fromApiJsonHelper.extractBooleanNamed(ClientApiConstants.activeParamName, element); if (active != null) { if (active.booleanValue()) { - final LocalDate joinedDate = fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName, element); + final LocalDate joinedDate = this.fromApiJsonHelper.extractLocalDateNamed(ClientApiConstants.activationDateParamName, + element); baseDataValidator.reset().parameter(ClientApiConstants.activationDateParamName).value(joinedDate).notNull(); } } + if (this.fromApiJsonHelper.parameterExists(ClientApiConstants.staffIdParamName, element)) { + final Long staffId = this.fromApiJsonHelper.extractLongNamed(ClientApiConstants.staffIdParamName, element); + baseDataValidator.reset().parameter(ClientApiConstants.staffIdParamName).value(staffId).notNull().longGreaterThanZero(); + } + if (!atLeastOneParameterPassedForUpdate) { final Object forceError = null; baseDataValidator.reset().anyOfNotNull(forceError); @@ -291,7 +306,7 @@ public final class ClientDataValidator { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken>() {}.getType(); - fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.ACTIVATION_REQUEST_DATA_PARAMETERS); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, ClientApiConstants.ACTIVATION_REQUEST_DATA_PARAMETERS); final List dataValidationErrors = new ArrayList(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) @@ -299,7 +314,7 @@ public final class ClientDataValidator { final JsonElement element = command.parsedJson(); - final LocalDate activationDate = fromApiJsonHelper.extractLocalDateNamed(activationDateParamName, element); + final LocalDate activationDate = this.fromApiJsonHelper.extractLocalDateNamed(activationDateParamName, element); baseDataValidator.reset().parameter(activationDateParamName).value(activationDate).notNull(); throwExceptionIfValidationWarningsExist(dataValidationErrors); @@ -311,4 +326,28 @@ public final class ClientDataValidator { throw new PlatformApiDataValidationException(dataValidationErrors); } } + + public void validateForUnassignStaff(final String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + + final Set supportedParametersUnassignStaff = new HashSet(Arrays.asList(ClientApiConstants.staffIdParamName)); + + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParametersUnassignStaff); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(ClientApiConstants.CLIENT_RESOURCE_NAME); + + final String staffIdParameterName = ClientApiConstants.staffIdParamName; + final Long staffId = this.fromApiJsonHelper.extractLongNamed(staffIdParameterName, element); + baseDataValidator.reset().parameter(staffIdParameterName).value(staffId).notNull().longGreaterThanZero(); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(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 b8408c84a..57d1dc7d3 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 @@ -5,6 +5,27 @@ */ package org.mifosplatform.portfolio.client.domain; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.OneToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Transient; +import javax.persistence.UniqueConstraint; + import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; @@ -16,14 +37,11 @@ import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.documentmanagement.domain.Image; import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator; import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.group.domain.Group; import org.springframework.data.jpa.domain.AbstractPersistable; -import javax.persistence.*; - -import java.util.*; - @Entity @Table(name = "m_client", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "account_no_UNIQUE") }) public final class Client extends AbstractPersistable { @@ -67,6 +85,10 @@ public final class Client extends AbstractPersistable { @Column(name = "external_id", length = 100, nullable = true, unique = true) private String externalId; + @ManyToOne + @JoinColumn(name = "staff_id") + private Staff staff; + @ManyToMany @JoinTable(name = "m_group_client", joinColumns = @JoinColumn(name = "client_id"), inverseJoinColumns = @JoinColumn(name = "group_id")) private Set groups; @@ -74,7 +96,7 @@ public final class Client extends AbstractPersistable { @Transient private boolean accountNumberRequiresAutoGeneration = false; - public static Client createNew(final Office clientOffice, final Group clientParentGroup, final JsonCommand command) { + public static Client createNew(final Office clientOffice, final Group clientParentGroup, final Staff staff, final JsonCommand command) { final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName); final String externalId = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName); @@ -97,7 +119,7 @@ public final class Client extends AbstractPersistable { } return new Client(status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname, activationDate, - externalId); + externalId, staff); } protected Client() { @@ -106,7 +128,7 @@ public final class Client extends AbstractPersistable { private Client(final ClientStatus status, final Office office, final Group clientParentGroup, final String accountNo, final String firstname, final String middlename, final String lastname, final String fullname, final LocalDate activationDate, - final String externalId) { + final String externalId, final Staff staff) { if (StringUtils.isBlank(accountNo)) { this.accountNumber = new RandomPasswordGenerator(19).generate(); this.accountNumberRequiresAutoGeneration = true; @@ -152,6 +174,8 @@ public final class Client extends AbstractPersistable { this.groups.add(clientParentGroup); } + this.staff = staff; + deriveDisplayName(); validate(); } @@ -280,6 +304,11 @@ public final class Client extends AbstractPersistable { this.fullname = newValue; } + if (command.isChangeInLongParameterNamed(ClientApiConstants.staffIdParamName, staffId())) { + final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName); + actualChanges.put(ClientApiConstants.staffIdParamName, newValue); + } + final String dateFormatAsInput = command.dateFormat(); final String localeAsInput = command.locale(); @@ -326,7 +355,7 @@ public final class Client extends AbstractPersistable { private void validateActivationDate(final List dataValidationErrors) { if (this.activationDate != null) { - if (office.isOpeningDateAfter(getActivationLocalDate())) { + if (this.office.isOpeningDateAfter(getActivationLocalDate())) { final String defaultUserMessage = "Client activation date cannot be a date before the office opening date."; final ApiParameterError error = ApiParameterError.parameterError( "error.msg.clients.activationDate.cannot.be.before.office.activation.date", defaultUserMessage, @@ -374,7 +403,7 @@ public final class Client extends AbstractPersistable { return this.office.getId(); } - public void setImage(Image image) { + public void setImage(final Image image) { this.image = image; } @@ -386,8 +415,28 @@ public final class Client extends AbstractPersistable { return this.displayName; } - public void setDisplayName(String displayName) { + public void setDisplayName(final String displayName) { this.displayName = displayName; } + private Long staffId() { + Long staffId = null; + if (this.staff != null) { + staffId = this.staff.getId(); + } + return staffId; + } + + public void updateStaff(final Staff staff) { + this.staff = staff; + } + + public Staff getStaff() { + return this.staff; + } + + public void unassignStaff() { + this.staff = null; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientHasNoStaffException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientHasNoStaffException.java new file mode 100644 index 000000000..6b629f309 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/exception/ClientHasNoStaffException.java @@ -0,0 +1,11 @@ +package org.mifosplatform.portfolio.client.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class ClientHasNoStaffException extends AbstractPlatformResourceNotFoundException { + + public ClientHasNoStaffException(final Long clientId) { + super("error.msg.client.has.no.staff", "Client with identifier " + clientId + " does not have staff", clientId); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/UnassignClientStaffCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/UnassignClientStaffCommandHandler.java new file mode 100644 index 000000000..2c447d1c1 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/handler/UnassignClientStaffCommandHandler.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 UnassignClientStaffCommandHandler implements NewCommandSourceHandler { + + private final ClientWritePlatformService clientWritePlatformService; + + @Autowired + public UnassignClientStaffCommandHandler(final ClientWritePlatformService clientWritePlatformService) { + this.clientWritePlatformService = clientWritePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.clientWritePlatformService.unassignClientStaff(command.entityId(), command); + } + +} 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 33d6808f0..937b0d397 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 @@ -16,7 +16,7 @@ import org.mifosplatform.portfolio.group.service.SearchParameters; public interface ClientReadPlatformService { - ClientData retrieveTemplate(); + ClientData retrieveTemplate(Long officeId); Page retrieveAll(SearchParameters searchParameters); 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 9389613a4..6bdf6dfb1 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 @@ -22,6 +22,8 @@ import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSourc import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.office.data.OfficeData; import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; +import org.mifosplatform.organisation.staff.data.StaffData; +import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryCollectionData; import org.mifosplatform.portfolio.client.data.ClientAccountSummaryData; import org.mifosplatform.portfolio.client.data.ClientData; @@ -40,6 +42,7 @@ import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; @Service public class ClientReadPlatformServiceImpl implements ClientReadPlatformService { @@ -47,6 +50,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; private final OfficeReadPlatformService officeReadPlatformService; + private final StaffReadPlatformService staffReadPlatformService; // data mappers private final PaginationHelper paginationHelper = new PaginationHelper(); @@ -57,22 +61,28 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService @Autowired public ClientReadPlatformServiceImpl(final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource, - final OfficeReadPlatformService officeReadPlatformService) { + final OfficeReadPlatformService officeReadPlatformService, final StaffReadPlatformService staffReadPlatformService) { this.context = context; this.officeReadPlatformService = officeReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); + this.staffReadPlatformService = staffReadPlatformService; } @Override - public ClientData retrieveTemplate() { + public ClientData retrieveTemplate(final Long officeId) { - final AppUser currentUser = context.authenticatedUser(); + this.context.authenticatedUser(); + + final Long defaultOfficeId = defaultToUsersOfficeIfNull(officeId); final Collection offices = officeReadPlatformService.retrieveAllOfficesForDropdown(); - final Long officeId = currentUser.getOffice().getId(); - - return ClientData.template(officeId, new LocalDate(), offices); + Collection staffOptions = this.staffReadPlatformService.retrieveAllStaffForDropdown(defaultOfficeId); + if (CollectionUtils.isEmpty(staffOptions)) { + staffOptions = null; + } + + return ClientData.template(defaultOfficeId, new LocalDate(), offices, staffOptions); } @Override @@ -226,10 +236,12 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService sqlBuilder.append("c.office_id as officeId, o.name as officeName, "); sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "); sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, "); - sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId "); + sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, "); + sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName "); sqlBuilder.append("from m_client c "); sqlBuilder.append("join m_office o on o.id = c.office_id "); sqlBuilder.append("join m_group_client pgc on pgc.client_id = c.id"); + sqlBuilder.append("left join m_staff s on s.id = c.staff_id "); this.schema = sqlBuilder.toString(); } @@ -256,9 +268,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); final Long imageId = JdbcSupport.getLong(rs, "imageId"); final String officeName = rs.getString("officeName"); + final Long staffId = JdbcSupport.getLong(rs, "staffId"); + final String staffName = rs.getString("staffName"); return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, - externalId, activationDate, imageId); + externalId, activationDate, imageId, staffId, staffName); } } @@ -274,9 +288,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService builder.append("c.office_id as officeId, o.name as officeName, "); builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, "); builder.append("c.fullname as fullname, c.display_name as displayName, "); - builder.append("c.activation_date as activationDate, c.image_id as imageId "); + builder.append("c.activation_date as activationDate, c.image_id as imageId, "); + builder.append("c.staff_id as staffId, s.display_name as staffName "); builder.append("from m_client c "); builder.append("join m_office o on o.id = c.office_id "); + builder.append("left join m_staff s on s.id = c.staff_id "); this.schema = builder.toString(); } @@ -304,9 +320,11 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final LocalDate activationDate = JdbcSupport.getLocalDate(rs, "activationDate"); final String officeName = rs.getString("officeName"); final Long imageId = JdbcSupport.getLong(rs, "imageId"); + final Long staffId = JdbcSupport.getLong(rs, "staffId"); + final String staffName = rs.getString("staffName"); return ClientData.instance(accountNo, status, officeId, officeName, id, firstname, middlename, lastname, fullname, displayName, - externalId, activationDate, imageId); + externalId, activationDate, imageId, staffId, staffName); } } @@ -560,4 +578,12 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService officeName); } } + + private Long defaultToUsersOfficeIfNull(final Long officeId) { + Long defaultOfficeId = officeId; + if (defaultOfficeId == null) { + defaultOfficeId = this.context.authenticatedUser().getOffice().getId(); + } + return defaultOfficeId; + } } \ 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 bdad04f22..d4da4f6ba 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 @@ -18,4 +18,6 @@ public interface ClientWritePlatformService { CommandProcessingResult deleteClient(Long clientId); + CommandProcessingResult unassignClientStaff(Long clientId, JsonCommand command); + } \ No newline at end of file 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 52cf5ad4b..6766d1888 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 @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.client.service; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -20,12 +21,15 @@ import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.office.domain.OfficeRepository; import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; +import org.mifosplatform.organisation.staff.domain.Staff; +import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; import org.mifosplatform.portfolio.client.api.ClientApiConstants; import org.mifosplatform.portfolio.client.data.ClientDataValidator; 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.exception.ClientHasNoStaffException; import org.mifosplatform.portfolio.client.exception.ClientMustBePendingToBeDeletedException; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.GroupRepository; @@ -51,12 +55,13 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP private final GroupRepository groupRepository; private final ClientDataValidator fromApiJsonDeserializer; private final AccountNumberGeneratorFactory accountIdentifierGeneratorFactory; + private final StaffRepositoryWrapper staffRepository; @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 GroupRepository groupRepository, final StaffRepositoryWrapper staffRepository) { this.context = context; this.clientRepository = clientRepository; this.officeRepository = officeRepository; @@ -64,6 +69,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.accountIdentifierGeneratorFactory = accountIdentifierGeneratorFactory; this.groupRepository = groupRepository; + this.staffRepository = staffRepository; } @Transactional @@ -74,7 +80,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP if (client.isNotPending()) { throw new ClientMustBePendingToBeDeletedException(clientId); } - List relatedNotes = this.noteRepository.findByClientId(clientId); + final List relatedNotes = this.noteRepository.findByClientId(clientId); this.noteRepository.deleteInBatch(relatedNotes); this.clientRepository.delete(client); @@ -92,7 +98,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP */ private void handleDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { - Throwable realCause = dve.getMostSpecificCause(); + final Throwable realCause = dve.getMostSpecificCause(); if (realCause.getMessage().contains("external_id")) { final String externalId = command.stringValueOfParameterNamed("externalId"); @@ -114,7 +120,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP public CommandProcessingResult createClient(final JsonCommand command) { try { - context.authenticatedUser(); + this.context.authenticatedUser(); this.fromApiJsonDeserializer.validateForCreate(command.json()); @@ -131,7 +137,13 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP if (clientParentGroup == null) { throw new GroupNotFoundException(groupId); } } - final Client newClient = Client.createNew(clientOffice, clientParentGroup, command); + Staff staff = null; + final Long staffId = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName); + if (staffId != null) { + staff = this.staffRepository.findByOfficeWithNotFoundDetection(staffId, officeId); + } + + final Client newClient = Client.createNew(clientOffice, clientParentGroup, staff, command); this.clientRepository.save(newClient); @@ -149,7 +161,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP .withGroupId(groupId) // .withEntityId(newClient.getId()) // .build(); - } catch (DataIntegrityViolationException dve) { + } catch (final DataIntegrityViolationException dve) { handleDataIntegrityIssues(command, dve); return CommandProcessingResult.empty(); } @@ -160,7 +172,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP public CommandProcessingResult updateClient(final Long clientId, final JsonCommand command) { try { - context.authenticatedUser(); + this.context.authenticatedUser(); this.fromApiJsonDeserializer.validateForUpdate(command.json()); @@ -168,6 +180,16 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP final Map changes = clientForUpdate.update(command); + if (changes.containsKey(ClientApiConstants.staffIdParamName)) { + + final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName); + Staff newStaff = null; + if (newValue != null) { + newStaff = this.staffRepository.findByOfficeWithNotFoundDetection(newValue, clientForUpdate.officeId()); + } + clientForUpdate.updateStaff(newStaff); + } + if (!changes.isEmpty()) { this.clientRepository.saveAndFlush(clientForUpdate); } @@ -179,7 +201,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP .withEntityId(clientId) // .with(changes) // .build(); - } catch (DataIntegrityViolationException dve) { + } catch (final DataIntegrityViolationException dve) { handleDataIntegrityIssues(command, dve); return CommandProcessingResult.empty(); } @@ -207,7 +229,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP .withClientId(clientId) // .withEntityId(clientId) // .build(); - } catch (DataIntegrityViolationException dve) { + } catch (final DataIntegrityViolationException dve) { handleDataIntegrityIssues(command, dve); return CommandProcessingResult.empty(); } @@ -216,4 +238,37 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP private void logAsErrorUnexpectedDataIntegrityException(final DataIntegrityViolationException dve) { logger.error(dve.getMessage(), dve); } + + @Transactional + @Override + public CommandProcessingResult unassignClientStaff(final Long clientId, final JsonCommand command) { + + this.context.authenticatedUser(); + + final Map actualChanges = new LinkedHashMap(5); + + this.fromApiJsonDeserializer.validateForUnassignStaff(command.json()); + + final Client clientForUpdate = this.clientRepository.findOneWithNotFoundDetection(clientId); + + final Staff presentStaff = clientForUpdate.getStaff(); + Long presentStaffId = null; + if (presentStaff == null) { throw new ClientHasNoStaffException(clientId); } + presentStaffId = presentStaff.getId(); + final String staffIdParamName = ClientApiConstants.staffIdParamName; + if (!command.isChangeInLongParameterNamed(staffIdParamName, presentStaffId)) { + clientForUpdate.unassignStaff(); + } + this.clientRepository.saveAndFlush(clientForUpdate); + + actualChanges.put(staffIdParamName, presentStaffId); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withOfficeId(clientForUpdate.officeId()) // + .withEntityId(clientForUpdate.getId()) // + .withClientId(clientId) // + .with(actualChanges) // + .build(); + } } \ No newline at end of file