From 6dad5aee4ae94835271355f51a229f9757c4c22f Mon Sep 17 00:00:00 2001 From: ashok-conflux Date: Fri, 30 Aug 2013 18:29:17 +0530 Subject: [PATCH] MIFOSX-625 - support ability to close group --- .../core_db/V110__group_center_close.sql | 8 ++ .../commands/domain/CommandWrapper.java | 8 ++ .../service/CommandWrapperBuilder.java | 20 +++- .../SynchronousCommandProcessingService.java | 4 + .../portfolio/client/domain/Client.java | 11 +- .../domain/ClientRepositoryWrapper.java | 2 +- .../group/api/CentersApiResource.java | 13 +- .../group/api/GroupingTypesApiConstants.java | 11 +- .../group/api/GroupsApiResource.java | 11 ++ .../portfolio/group/data/CenterData.java | 37 +++++- .../group/data/GroupGeneralData.java | 48 ++++++-- .../portfolio/group/domain/Group.java | 46 +++++++ .../domain/GroupingTypeEnumerations.java | 8 ++ .../handler/CloseCenterCommandHandler.java | 27 +++++ .../handler/CloseGroupCommandHandler.java | 27 +++++ .../GroupRolesDataValidator.java | 2 +- .../GroupingTypesDataValidator.java | 48 ++++++++ .../service/CenterReadPlatformService.java | 2 + .../CenterReadPlatformServiceImpl.java | 16 ++- .../service/GroupReadPlatformService.java | 2 + .../service/GroupReadPlatformServiceImpl.java | 9 ++ .../GroupRolesWritePlatformService.java | 2 +- ...WritePlatformServiceJpaRepositoryImpl.java | 4 +- .../GroupingTypesWritePlatformService.java | 4 + ...WritePlatformServiceJpaRepositoryImpl.java | 113 ++++++++++++++++-- .../loanaccount/domain/LoanRepository.java | 5 +- .../savings/domain/SavingsAccount.java | 10 +- .../domain/SavingsAccountRepository.java | 3 + 28 files changed, 468 insertions(+), 33 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V110__group_center_close.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/CloseCenterCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/CloseGroupCommandHandler.java diff --git a/mifosng-db/migrations/core_db/V110__group_center_close.sql b/mifosng-db/migrations/core_db/V110__group_center_close.sql new file mode 100644 index 000000000..70541fbb0 --- /dev/null +++ b/mifosng-db/migrations/core_db/V110__group_center_close.sql @@ -0,0 +1,8 @@ +INSERT INTO `m_code` (`code_name`, `is_system_defined`) VALUES ('GroupClosureReason', 1); + +ALTER TABLE `m_group` ADD COLUMN `closure_reason_cv_id` INT(11) NULL DEFAULT NULL, +ADD COLUMN `closedon_date` DATE NULL DEFAULT NULL, +ADD CONSTRAINT `FK_m_group_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_GROUP', 'GROUP', 'CLOSE'); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`) VALUES ('portfolio', 'CLOSE_CENTER', 'CENTER', '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 111a8f4c4..1146fbd65 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 @@ -288,11 +288,19 @@ public class CommandWrapper { public boolean isGroupActivation() { return this.actionName.equalsIgnoreCase("ACTIVATE") && this.entityName.equalsIgnoreCase("GROUP"); } + + public boolean isGroupClose() { + return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("GROUP"); + } public boolean isCenterActivation() { return this.actionName.equalsIgnoreCase("ACTIVATE") && this.entityName.equalsIgnoreCase("CENTER"); } + public boolean isCenterClose() { + return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("CENTER"); + } + public boolean isClientIdentifierResource() { return this.entityName.equals("CLIENTIDENTIFIER"); } 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 fad90439d..b6a6c4752 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 @@ -1076,7 +1076,16 @@ public class CommandWrapperBuilder { this.href = "/groups/" + groupId + "?command=assignStaff"; return this; } - + + public CommandWrapperBuilder closeGroup(final Long groupId) { + this.actionName = "CLOSE"; + this.entityName = "GROUP"; + this.entityId = groupId; + this.groupId = groupId; + this.href = "/groups/" + groupId + "?command=close"; + return this; + } + public CommandWrapperBuilder createCollateral(final Long loanId) { this.actionName = "CREATE"; this.entityName = "COLLATERAL"; @@ -1153,6 +1162,15 @@ public class CommandWrapperBuilder { this.href = "/centers/" + centerId + "?command=saveCollectionSheet"; return this; } + + public CommandWrapperBuilder closeCenter(final Long centerId) { + this.actionName = "CLOSE"; + this.entityName = "CENTER"; + this.entityId = centerId; + this.groupId = centerId; + this.href = "/centers/" + centerId + "?command=close"; + return this; + } public CommandWrapperBuilder createAccountingRule() { this.actionName = "CREATE"; 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 6de109abb..3872e32d8 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 @@ -464,6 +464,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("assignGroupStaffCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isTransferClientsBetweenGroups()) { handler = this.applicationContext.getBean("transferClientsBetweenGroupsCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isGroupClose()) { + handler = this.applicationContext.getBean("closeGroupCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } @@ -478,6 +480,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("activateCenterCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isSaveCenterCollectionSheet()) { handler = this.applicationContext.getBean("saveCenterCollectionSheetCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isCenterClose()) { + handler = this.applicationContext.getBean("closeCenterCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } 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 b66e308c5..10fe12c77 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 @@ -28,6 +28,8 @@ import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import org.apache.commons.lang.StringUtils; +import org.hibernate.annotations.LazyCollection; +import org.hibernate.annotations.LazyCollectionOption; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; import org.mifosplatform.infrastructure.codes.domain.CodeValue; @@ -99,6 +101,7 @@ public final class Client extends AbstractPersistable { @JoinColumn(name = "staff_id") private Staff staff; + @LazyCollection(LazyCollectionOption.FALSE) @ManyToMany @JoinTable(name = "m_group_client", joinColumns = @JoinColumn(name = "client_id"), inverseJoinColumns = @JoinColumn(name = "group_id")) private Set groups; @@ -270,11 +273,15 @@ public final class Client extends AbstractPersistable { return ClientStatus.fromInt(this.status).isActive(); } + public boolean isClosed() { + return ClientStatus.fromInt(this.status).isClosed(); + } + public boolean isTransferInProgress() { return ClientStatus.fromInt(this.status).isTransferInProgress(); } - - public boolean isTransferOnHold() { + + public boolean isTransferOnHold () { return ClientStatus.fromInt(this.status).isTransferOnHold(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientRepositoryWrapper.java index 5c26af704..b60fca8bf 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientRepositoryWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientRepositoryWrapper.java @@ -30,7 +30,7 @@ public class ClientRepositoryWrapper { if (client == null) { throw new ClientNotFoundException(id); } return client; } - + public void save(final Client client) { this.repository.save(client); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java index 8d9c8aa8e..e10324171 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java @@ -96,10 +96,17 @@ public class CentersApiResource { @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("command") final String commandParam, + @QueryParam("officeId") final Long officeId) { this.context.authenticatedUser().validateHasReadPermission(GroupingTypesApiConstants.CENTER_RESOURCE_NAME); + if (is(commandParam, "close")) { + final CenterData centerClosureTemplate = this.centerReadPlatformService.retrieveCenterWithClosureReasons(); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.centerApiJsonSerializer.serialize(settings, centerClosureTemplate, GroupingTypesApiConstants.CENTER_RESPONSE_DATA_PARAMETERS); + } + final CenterData template = this.centerReadPlatformService.retrieveTemplate(officeId); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); @@ -229,6 +236,10 @@ public class CentersApiResource { final CommandWrapper commandRequest = builder.saveCenterCollectionSheet(centerId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return this.toApiJsonSerializer.serialize(result); + } else if (is(commandParam, "close")) { + final CommandWrapper commandRequest = builder.closeCenter(centerId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); } else { throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet", "createRole" }); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java index 935ee0c00..38eb9e9bb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupingTypesApiConstants.java @@ -21,11 +21,14 @@ public class GroupingTypesApiConstants { public static final String GROUP_ROLE_RESOURCE_NAME = "grouprole"; public static final String GROUP_ROLE_NAME = "GROUPROLE"; + public static final String GROUP_CLOSURE_REASON = "GroupClosureReason"; + public static final String roleParamName = "role"; public static final String groupIdParamName = "groupId"; public static final String clientIdParamName = "clientId"; public static final String groupRolesParamName = "groupRoles"; + // general public static final String localeParamName = "locale"; public static final String dateFormatParamName = "dateFormat"; @@ -53,6 +56,10 @@ public class GroupingTypesApiConstants { public static final String staffOptionsParamName = "staffOptions"; public static final String clientOptionsParamName = "clientOptions"; public static final String collectionMeetingCalendar = "collectionMeetingCalendar"; + + //group close parameters + public static final String closureDateParamName = "closureDate"; + public static final String closureReasonIdParamName = "closureReasonId"; public static final Set CENTER_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, dateFormatParamName, idParamName, nameParamName, externalIdParamName, officeIdParamName, staffIdParamName, activeParamName, @@ -86,5 +93,7 @@ public class GroupingTypesApiConstants { public static final Set COLLECTIONSHEET_DATA_PARAMETERS = new HashSet(Arrays.asList("dueDate", "loanProducts", "groups")); - + + public static final Set GROUP_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/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index 286f70f05..1bf6993b7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -117,10 +117,17 @@ public class GroupsApiResource { @Produces({ MediaType.APPLICATION_JSON }) public String retrieveTemplate(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId, @QueryParam("center") final boolean isCenterGroup, @QueryParam("centerId") final Long centerId, + @QueryParam("command") final String commandParam, @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) { this.context.authenticatedUser().validateHasReadPermission(GroupingTypesApiConstants.GROUP_RESOURCE_NAME); + if (is(commandParam, "close")) { + final GroupGeneralData groupClosureTemplate = this.groupReadPlatformService.retrieveGroupWithClosureReasons(); + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.groupGeneralApiJsonSerializer.serialize(settings, groupClosureTemplate, GroupingTypesApiConstants.GROUP_RESPONSE_DATA_PARAMETERS); + } + if (centerId != null) { final GroupGeneralData centerGroupTemplate = this.centerReadPlatformService.retrieveCenterGroupTemplate(centerId); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); @@ -344,6 +351,10 @@ public class GroupsApiResource { final CommandWrapper commandRequest = builder.transferClientsBetweenGroups(groupId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return this.toApiJsonSerializer.serialize(result); + } else if (is(commandParam, "close")) { + final CommandWrapper commandRequest = builder.closeGroup(groupId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); } else { throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet", "saveCollectionSheet", "unassignStaff", "assignRole", "unassignRole", "updateassignRole" }); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/CenterData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/CenterData.java index 8de5f2068..b77e8ddbe 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/CenterData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/CenterData.java @@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.group.data; import java.util.Collection; 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; @@ -42,18 +43,21 @@ public class CenterData { private final Collection staffOptions; private final Collection groupMembersOptions; private final CalendarData collectionMeetingCalendar; + private final Collection closureReasons; public static CenterData template(final Long officeId, final LocalDate activationDate, final Collection officeOptions, final Collection staffOptions, final Collection groupMembersOptions) { final CalendarData collectionMeetingCalendar = null; + final Collection closureReasons = null; return new CenterData(null, null, null, null, activationDate, officeId, null, null, null, null, null, officeOptions, staffOptions, - groupMembersOptions, collectionMeetingCalendar); + groupMembersOptions, collectionMeetingCalendar, closureReasons); } public static CenterData withTemplate(final CenterData templateCenter, final CenterData center) { return new CenterData(center.id, center.name, center.externalId, center.status, center.activationDate, center.officeId, center.officeName, center.staffId, center.staffName, center.hierarchy, center.groupMembers, templateCenter.officeOptions, - templateCenter.staffOptions, templateCenter.groupMembersOptions, templateCenter.collectionMeetingCalendar); + templateCenter.staffOptions, templateCenter.groupMembersOptions, templateCenter.collectionMeetingCalendar, + templateCenter.closureReasons); } public static CenterData instance(final Long id, final String name, final String externalId, final EnumOptionData status, @@ -65,21 +69,43 @@ public class CenterData { final Collection staffOptions = null; final Collection groupMembersOptions = null; final CalendarData collectionMeetingCalendar = null; + final Collection closureReasons = null; return new CenterData(id, name, externalId, status, activationDate, officeId, officeName, staffId, staffName, hierarchy, - groupMembers, officeOptions, staffOptions, groupMembersOptions, collectionMeetingCalendar); + groupMembers, officeOptions, staffOptions, groupMembersOptions, collectionMeetingCalendar, closureReasons); } public static CenterData withAssociations(final CenterData centerData, final Collection groupMembers, final CalendarData collectionMeetingCalendar) { return new CenterData(centerData.id, centerData.name, centerData.externalId, centerData.status, centerData.activationDate, centerData.officeId, centerData.officeName, centerData.staffId, centerData.staffName, centerData.hierarchy, groupMembers, - centerData.officeOptions, centerData.staffOptions, centerData.groupMembersOptions, collectionMeetingCalendar); + centerData.officeOptions, centerData.staffOptions, centerData.groupMembersOptions, collectionMeetingCalendar, centerData.closureReasons); } + public static CenterData withClosureReasons(final Collection closureReasons) { + final Long id = null; + final String name = null; + final String externalId = null; + final EnumOptionData status = null; + final LocalDate activationDate = null; + final Long officeId = null; + final String officeName = null; + final Long staffId = null; + final String staffName = null; + final String hierarchy = null; + final Collection groupMembers = null; + final Collection officeOptions = null; + final Collection staffOptions = null; + final Collection groupMembersOptions = null; + final CalendarData collectionMeetingCalendar = null; + return new CenterData(id, name, externalId, status, activationDate, officeId, officeName, staffId, staffName, hierarchy, + groupMembers, officeOptions, staffOptions, groupMembersOptions, collectionMeetingCalendar, closureReasons); + } + private CenterData(final Long id, final String name, final String externalId, final EnumOptionData status, final LocalDate activationDate, final Long officeId, final String officeName, final Long staffId, final String staffName, final String hierarchy, final Collection groupMembers, final Collection officeOptions, - final Collection staffOptions, final Collection groupMembersOptions, final CalendarData collectionMeetingCalendar) { + final Collection staffOptions, final Collection groupMembersOptions, + final CalendarData collectionMeetingCalendar, final Collection closureReasons) { this.id = id; this.name = name; this.externalId = externalId; @@ -103,6 +129,7 @@ public class CenterData { this.staffOptions = staffOptions; this.groupMembersOptions = groupMembersOptions; this.collectionMeetingCalendar = collectionMeetingCalendar; + this.closureReasons = closureReasons; } public Long officeId() { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java index 5c78d5c3b..9de2c69ba 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/data/GroupGeneralData.java @@ -51,13 +51,14 @@ public class GroupGeneralData { private final Collection clientOptions; private final Collection availableRoles; private final GroupRoleData selectedRole; + private final Collection closureReasons; public static GroupGeneralData lookup(final Long groupId, final String groupName) { final Collection clientMembers = null; final Collection groupRoles = null; - + final Collection closureReasons = null; return new GroupGeneralData(groupId, groupName, null, null, null, null, null, null, null, null, null, null, clientMembers, null, - null, null, null, groupRoles, null, null, null, null); + null, null, null, groupRoles, null, null, null, null, closureReasons); } public static GroupGeneralData template(final Long officeId, final Long centerId, final String centerName, final Long staffId, @@ -67,9 +68,10 @@ public class GroupGeneralData { final Collection clientMembers = null; final Collection groupRoles = null; + final Collection closureReasons = null; return new GroupGeneralData(null, null, null, null, null, officeId, null, centerId, centerName, staffId, staffName, null, - clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, null, null, null); + clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, null, null, null, closureReasons); } public static GroupGeneralData withTemplate(final GroupGeneralData templatedGrouping, final GroupGeneralData grouping) { @@ -77,7 +79,7 @@ public class GroupGeneralData { grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName, grouping.hierarchy, grouping.clientMembers, templatedGrouping.centerOptions, templatedGrouping.officeOptions, templatedGrouping.staffOptions, templatedGrouping.clientOptions, grouping.groupRoles, templatedGrouping.availableRoles, - grouping.selectedRole, grouping.calendarsData, grouping.collectionMeetingCalendar); + grouping.selectedRole, grouping.calendarsData, grouping.collectionMeetingCalendar, grouping.closureReasons); } public static GroupGeneralData withAssocations(final GroupGeneralData grouping, final Collection membersOfGroup, @@ -85,7 +87,7 @@ public class GroupGeneralData { return new GroupGeneralData(grouping.id, grouping.name, grouping.externalId, grouping.status, grouping.activationDate, grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName, grouping.hierarchy, membersOfGroup, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions, - grouping.clientOptions, groupRoles, grouping.availableRoles, grouping.selectedRole, calendarsData, collectionMeetingCalendar); + grouping.clientOptions, groupRoles, grouping.availableRoles, grouping.selectedRole, calendarsData, collectionMeetingCalendar, grouping.closureReasons); } public static GroupGeneralData instance(final Long id, final String name, final String externalId, final EnumOptionData status, @@ -102,10 +104,11 @@ public class GroupGeneralData { final GroupRoleData role = null; final Collection calendarsData = null; final CalendarData collectionMeetingCalendar = null; + final Collection closureReasons = null; return new GroupGeneralData(id, name, externalId, status, activationDate, officeId, officeName, centerId, centerName, staffId, staffName, hierarchy, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, - role, calendarsData, collectionMeetingCalendar); + role, calendarsData, collectionMeetingCalendar, closureReasons); } private GroupGeneralData(final Long id, final String name, final String externalId, final EnumOptionData status, @@ -114,7 +117,7 @@ public class GroupGeneralData { final Collection centerOptions, final Collection officeOptions, final Collection staffOptions, final Collection clientOptions, final Collection groupRoles, final Collection availableRoles, final GroupRoleData role, - final Collection calendarsData, final CalendarData collectionMeetingCalendar) { + final Collection calendarsData, final CalendarData collectionMeetingCalendar, final Collection closureReasons) { this.id = id; this.name = name; this.externalId = externalId; @@ -151,6 +154,7 @@ public class GroupGeneralData { this.selectedRole = role; this.calendarsData = calendarsData; this.collectionMeetingCalendar = collectionMeetingCalendar; + this.closureReasons = closureReasons; } public Long getId() { @@ -182,7 +186,35 @@ public class GroupGeneralData { return new GroupGeneralData(grouping.id, grouping.name, grouping.externalId, grouping.status, grouping.activationDate, grouping.officeId, grouping.officeName, grouping.centerId, grouping.centerName, grouping.staffId, grouping.staffName, grouping.hierarchy, grouping.clientMembers, grouping.centerOptions, grouping.officeOptions, grouping.staffOptions, - grouping.clientOptions, grouping.groupRoles, grouping.availableRoles, selectedRole, grouping.calendarsData, grouping.collectionMeetingCalendar); + grouping.clientOptions, grouping.groupRoles, grouping.availableRoles, selectedRole, grouping.calendarsData, grouping.collectionMeetingCalendar, grouping.closureReasons); } + public static GroupGeneralData withClosureReasons(final Collection closureReasons) { + final Long id = null; + final String name = null; + final String externalId = null; + final EnumOptionData status = null; + final LocalDate activationDate = null; + final Long officeId = null; + final String officeName = null; + final Long centerId = null; + final String centerName = null; + final Long staffId = null; + final String staffName = null; + final String hierarchy = null; + final Collection clientMembers = null; + final Collection centerOptions = null; + final Collection officeOptions = null; + final Collection staffOptions = null; + final Collection clientOptions = null; + final Collection groupRoles = null; + final Collection availableRoles = null; + final GroupRoleData role = null; + final Collection calendarsData = null; + final CalendarData collectionMeetingCalendar = null; + + return new GroupGeneralData(id, name, externalId, status, activationDate, officeId, officeName, centerId, centerName, staffId, + staffName, hierarchy, clientMembers, centerOptions, officeOptions, staffOptions, clientOptions, groupRoles, availableRoles, + role, calendarsData, collectionMeetingCalendar, closureReasons); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java index d730814af..0f0dfe515 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/Group.java @@ -31,6 +31,7 @@ import javax.persistence.TemporalType; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.codes.domain.CodeValue; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; @@ -41,6 +42,7 @@ import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants; import org.mifosplatform.portfolio.group.exception.ClientExistInGroupException; import org.mifosplatform.portfolio.group.exception.ClientNotInGroupException; +import org.mifosplatform.portfolio.group.exception.InvalidGroupStateTransitionException; import org.springframework.data.jpa.domain.AbstractPersistable; import com.google.common.collect.Sets; @@ -95,6 +97,14 @@ public final class Group extends AbstractPersistable { @ManyToMany @JoinTable(name = "m_group_client", joinColumns = @JoinColumn(name = "group_id"), inverseJoinColumns = @JoinColumn(name = "client_id")) private Set clientMembers; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "closure_reason_cv_id", nullable = true) + private CodeValue closureReason; + + @Column(name = "closedon_date", nullable = true) + @Temporal(TemporalType.DATE) + private Date closureDate; public Group() { this.name = null; @@ -409,5 +419,41 @@ public final class Group extends AbstractPersistable { public boolean isChildGroup(){ return (this.parent == null) ? false : true; + + } + + public boolean isClosed(){ + return GroupingTypeStatus.fromInt(this.status).isClosed(); + } + + public void close(final CodeValue closureReason, final LocalDate closureDate){ + + if (this.isClosed()) { + final String errorMessage = "Group with identifier " + this.getId() + " is alread closed."; + throw new InvalidGroupStateTransitionException(this.groupLevel.getLevelName(), "close", "already.closed", errorMessage, this.getId()); + } + + if (this.isNotPending() && this.getActivationLocalDate().isAfter(closureDate)) { + final String errorMessage = "The Group closure Date " + closureDate + " cannot be before the group Activation Date " + this.getActivationLocalDate() + "."; + throw new InvalidGroupStateTransitionException(this.groupLevel.getLevelName(), "close", "date.cannot.before.group.actvation.date", errorMessage, closureDate, this.getActivationLocalDate()); + } + + this.closureReason = closureReason; + this.closureDate = closureDate.toDate(); + this.status = GroupingTypeStatus.CLOSED.getValue(); + } + + public boolean hasActiveClients(){ + for (Client client : this.clientMembers) { + if(!client.isClosed()) return true; + } + return false; + } + + public boolean hasActiveGroups(){ + for(Group group : this.groupMembers){ + if(!group.isClosed()) return true; + } + return false; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupingTypeEnumerations.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupingTypeEnumerations.java index b3ef8d8c3..a82e3df2d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupingTypeEnumerations.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/domain/GroupingTypeEnumerations.java @@ -33,6 +33,14 @@ public class GroupingTypeEnumerations { optionData = new EnumOptionData(GroupingTypeStatus.CLOSED.getValue().longValue(), GroupingTypeStatus.CLOSED.getCode(), "Closed"); break; + case TRANSFER_IN_PROGRESS: + optionData = new EnumOptionData(GroupingTypeStatus.TRANSFER_IN_PROGRESS.getValue().longValue(), GroupingTypeStatus.TRANSFER_IN_PROGRESS.getCode(), + "Transfer in progress"); + break; + case TRANSFER_ON_HOLD: + optionData = new EnumOptionData(GroupingTypeStatus.TRANSFER_ON_HOLD.getValue().longValue(), GroupingTypeStatus.TRANSFER_ON_HOLD.getCode(), + "Transfer on hold"); + break; } return optionData; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/CloseCenterCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/CloseCenterCommandHandler.java new file mode 100644 index 000000000..854e984cf --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/CloseCenterCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.group.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.group.service.GroupingTypesWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CloseCenterCommandHandler implements NewCommandSourceHandler { + + private final GroupingTypesWritePlatformService groupingTypesWritePlatformService; + + @Autowired + public CloseCenterCommandHandler(final GroupingTypesWritePlatformService groupingTypesWritePlatformService) { + this.groupingTypesWritePlatformService = groupingTypesWritePlatformService; + } + + @Override + @Transactional + public CommandProcessingResult processCommand(JsonCommand command) { + return this.groupingTypesWritePlatformService.closeCenter(command.entityId(), command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/CloseGroupCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/CloseGroupCommandHandler.java new file mode 100644 index 000000000..714ce10ff --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/CloseGroupCommandHandler.java @@ -0,0 +1,27 @@ +package org.mifosplatform.portfolio.group.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.group.service.GroupingTypesWritePlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class CloseGroupCommandHandler implements NewCommandSourceHandler { + + private final GroupingTypesWritePlatformService groupingTypesWritePlatformService; + + @Autowired + public CloseGroupCommandHandler(final GroupingTypesWritePlatformService groupingTypesWritePlatformService) { + this.groupingTypesWritePlatformService = groupingTypesWritePlatformService; + } + + @Override + @Transactional + public CommandProcessingResult processCommand(JsonCommand command) { + return this.groupingTypesWritePlatformService.closeGroup(command.entityId(), command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupRolesDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupRolesDataValidator.java index fdaa183a7..01ca0ccf6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupRolesDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupRolesDataValidator.java @@ -85,5 +85,5 @@ public class GroupRolesDataValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } - + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java index 60813c17a..4a519758c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/serialization/GroupingTypesDataValidator.java @@ -406,4 +406,52 @@ public final class GroupingTypesDataValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } + + public void validateForGroupClose(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, GroupingTypesApiConstants.GROUP_CLOSE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(GroupingTypesApiConstants.GROUP_RESOURCE_NAME); + + final JsonElement element = command.parsedJson(); + + final LocalDate closureDate = this.fromApiJsonHelper.extractLocalDateNamed(GroupingTypesApiConstants.closureDateParamName, element); + baseDataValidator.reset().parameter(GroupingTypesApiConstants.closureDateParamName).value(closureDate).notNull(); + + final Long closureReasonId = this.fromApiJsonHelper.extractLongNamed(GroupingTypesApiConstants.closureReasonIdParamName, element); + baseDataValidator.reset().parameter(GroupingTypesApiConstants.closureReasonIdParamName).value(closureReasonId).notNull().longGreaterThanZero(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + + } + + public void validateForCenterClose(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, GroupingTypesApiConstants.GROUP_CLOSE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) + .resource(GroupingTypesApiConstants.CENTER_RESOURCE_NAME); + + final JsonElement element = command.parsedJson(); + + final LocalDate closureDate = this.fromApiJsonHelper.extractLocalDateNamed(GroupingTypesApiConstants.closureDateParamName, element); + baseDataValidator.reset().parameter(GroupingTypesApiConstants.closureDateParamName).value(closureDate).notNull(); + + final Long closureReasonId = this.fromApiJsonHelper.extractLongNamed(GroupingTypesApiConstants.closureReasonIdParamName, element); + baseDataValidator.reset().parameter(GroupingTypesApiConstants.closureReasonIdParamName).value(closureReasonId).notNull().longGreaterThanZero(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformService.java index 8cd9073f1..1535879b2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformService.java @@ -24,4 +24,6 @@ public interface CenterReadPlatformService { GroupGeneralData retrieveCenterGroupTemplate(Long centerId); Collection retrieveAssociatedGroups(Long centerId); + + CenterData retrieveCenterWithClosureReasons(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformServiceImpl.java index 245ead56e..327cea4fc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/CenterReadPlatformServiceImpl.java @@ -7,11 +7,15 @@ package org.mifosplatform.portfolio.group.service; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +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; @@ -26,6 +30,7 @@ import org.mifosplatform.organisation.staff.service.StaffReadPlatformService; import org.mifosplatform.portfolio.client.data.ClientData; import org.mifosplatform.portfolio.client.domain.ClientEnumerations; import org.mifosplatform.portfolio.client.service.ClientReadPlatformService; +import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants; import org.mifosplatform.portfolio.group.data.CenterData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.domain.GroupTypes; @@ -47,6 +52,7 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService private final ClientReadPlatformService clientReadPlatformService; private final OfficeReadPlatformService officeReadPlatformService; private final StaffReadPlatformService staffReadPlatformService; + private final CodeValueReadPlatformService codeValueReadPlatformService; // data mappers private final AllGroupTypesDataMapper allGroupTypesDataMapper = new AllGroupTypesDataMapper(); @@ -60,12 +66,13 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService @Autowired public CenterReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, - final StaffReadPlatformService staffReadPlatformService) { + final StaffReadPlatformService staffReadPlatformService, final CodeValueReadPlatformService codeValueReadPlatformService) { this.context = context; this.clientReadPlatformService = clientReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); this.officeReadPlatformService = officeReadPlatformService; this.staffReadPlatformService = staffReadPlatformService; + this.codeValueReadPlatformService = codeValueReadPlatformService; } // 'g.' preffix because of ERROR 1052 (23000): Column 'column_name' in where @@ -331,4 +338,11 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService String sql = "select " + this.groupDataMapper.schema() + " where g.parent_id = ? "; return this.jdbcTemplate.query(sql, this.groupDataMapper, new Object[] { centerId }); } + + @Override + public CenterData retrieveCenterWithClosureReasons() { + final List closureReasons = new ArrayList( + codeValueReadPlatformService.retrieveCodeValuesByCode(GroupingTypesApiConstants.GROUP_CLOSURE_REASON)); + return CenterData.withClosureReasons(closureReasons); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java index 9eeb0acab..e35d37375 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformService.java @@ -19,4 +19,6 @@ public interface GroupReadPlatformService { GroupGeneralData retrieveOne(Long groupId); Collection retrieveGroupsForLookup(Long officeId); + + GroupGeneralData retrieveGroupWithClosureReasons(); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java index 06733d8d9..6d80cb3b2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupReadPlatformServiceImpl.java @@ -7,7 +7,9 @@ package org.mifosplatform.portfolio.group.service; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import org.apache.commons.lang.StringUtils; import org.mifosplatform.infrastructure.codes.data.CodeValueData; @@ -227,4 +229,11 @@ public class GroupReadPlatformServiceImpl implements GroupReadPlatformService { } } + @Override + public GroupGeneralData retrieveGroupWithClosureReasons() { + final List closureReasons = new ArrayList( + codeValueReadPlatformService.retrieveCodeValuesByCode(GroupingTypesApiConstants.GROUP_CLOSURE_REASON)); + return GroupGeneralData.withClosureReasons(closureReasons); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupRolesWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupRolesWritePlatformService.java index eb0e0aba8..b2fbfe1ee 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupRolesWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupRolesWritePlatformService.java @@ -11,5 +11,5 @@ public interface GroupRolesWritePlatformService { CommandProcessingResult updateRole(JsonCommand command); CommandProcessingResult deleteRole(Long ruleId); - + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupRolesWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupRolesWritePlatformServiceJpaRepositoryImpl.java index 691f8528d..01cef1d85 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupRolesWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupRolesWritePlatformServiceJpaRepositoryImpl.java @@ -35,7 +35,7 @@ public class GroupRolesWritePlatformServiceJpaRepositoryImpl implements GroupRol private final CodeValueRepositoryWrapper codeValueRepository; private final ClientRepositoryWrapper clientRepository; private final GroupRoleRepositoryWrapper groupRoleRepository; - + @Autowired public GroupRolesWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final GroupRepositoryWrapper groupRepository, final GroupRolesDataValidator fromApiJsonDeserializer, @@ -143,5 +143,5 @@ public class GroupRolesWritePlatformServiceJpaRepositoryImpl implements GroupRol this.groupRoleRepository.delete(groupRole); return new CommandProcessingResultBuilder().withEntityId(groupRole.getId()).build(); } - + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformService.java index 928379deb..14505b84e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformService.java @@ -21,6 +21,10 @@ public interface GroupingTypesWritePlatformService { CommandProcessingResult updateGroup(Long groupId, JsonCommand command); CommandProcessingResult deleteGroup(Long groupId); + + CommandProcessingResult closeGroup(final Long groupId, final JsonCommand command); + + CommandProcessingResult closeCenter(final Long centerId, final JsonCommand command); CommandProcessingResult unassignGroupOrCenterStaff(Long groupId, JsonCommand command); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java index d65b348bd..20e290471 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/service/GroupingTypesWritePlatformServiceJpaRepositoryImpl.java @@ -17,6 +17,8 @@ import java.util.Set; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; +import org.mifosplatform.infrastructure.codes.domain.CodeValue; +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; @@ -30,6 +32,7 @@ import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; +import org.mifosplatform.portfolio.client.service.LoanStatusMapper; import org.mifosplatform.portfolio.group.api.GroupingTypesApiConstants; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.GroupLevel; @@ -64,24 +67,26 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group private final PlatformSecurityContext context; private final GroupRepositoryWrapper groupRepository; - private final ClientRepositoryWrapper clientRepository; + private final ClientRepositoryWrapper clientRepositoryWrapper; private final OfficeRepository officeRepository; private final StaffRepositoryWrapper staffRepository; private final NoteRepository noteRepository; private final GroupLevelRepository groupLevelRepository; private final GroupingTypesDataValidator fromApiJsonDeserializer; private final LoanRepository loanRepository; + private final CodeValueRepositoryWrapper codeValueRepository; private final SavingsAccountRepository savingsRepository; @Autowired public GroupingTypesWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, - final GroupRepositoryWrapper groupRepository, final ClientRepositoryWrapper clientRepository, - final OfficeRepository officeRepository, final StaffRepositoryWrapper staffRepository, final NoteRepository noteRepository, - final GroupLevelRepository groupLevelRepository, final GroupingTypesDataValidator fromApiJsonDeserializer, - final LoanRepository loanRepository, final SavingsAccountRepository savingsRepository) { + final GroupRepositoryWrapper groupRepository, final ClientRepositoryWrapper clientRepositoryWrapper, + final OfficeRepository officeRepository, final StaffRepositoryWrapper staffRepository, + final NoteRepository noteRepository, final GroupLevelRepository groupLevelRepository, + final GroupingTypesDataValidator fromApiJsonDeserializer, final LoanRepository loanRepository, + final SavingsAccountRepository savingsRepository, final CodeValueRepositoryWrapper codeValueRepository) { this.context = context; this.groupRepository = groupRepository; - this.clientRepository = clientRepository; + this.clientRepositoryWrapper = clientRepositoryWrapper; this.officeRepository = officeRepository; this.staffRepository = staffRepository; this.noteRepository = noteRepository; @@ -89,6 +94,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.loanRepository = loanRepository; this.savingsRepository = savingsRepository; + this.codeValueRepository = codeValueRepository; } private CommandProcessingResult createGroupingType(final JsonCommand command, final GroupTypes groupingType, final Long centerId) { @@ -403,6 +409,99 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group .build(); } + @Override + public CommandProcessingResult closeGroup(Long groupId, JsonCommand command) { + this.fromApiJsonDeserializer.validateForGroupClose(command); + final Group group = this.groupRepository.findOneWithNotFoundDetection(groupId); + final LocalDate closureDate = command.localDateValueOfParameterNamed(GroupingTypesApiConstants.closureDateParamName); + final Long closureReasonId = command.longValueOfParameterNamed(GroupingTypesApiConstants.closureReasonIdParamName); + + CodeValue closureReason = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection( + GroupingTypesApiConstants.GROUP_CLOSURE_REASON, closureReasonId); + + if(group.hasActiveClients()){ + final String errorMessage = group.getGroupLevel().getLevelName() + " cannot be closed because of active clients associated with it."; + throw new InvalidGroupStateTransitionException(group.getGroupLevel().getLevelName(), "close", "active.clients.exist", + errorMessage); + } + + validateLoansAndSavingsForGroupOrCenterClose(group, closureDate); + + group.close(closureReason, closureDate); + + this.groupRepository.saveAndFlush(group); + + return new CommandProcessingResultBuilder() // + .withGroupId(groupId) // + .withEntityId(groupId) // + .build(); + } + + private void validateLoansAndSavingsForGroupOrCenterClose(final Group groupOrCenter, final LocalDate closureDate) { + Collection groupLoans = this.loanRepository.findByGroupId(groupOrCenter.getId()); + for (Loan loan : groupLoans) { + LoanStatusMapper loanStatus = new LoanStatusMapper(loan.status().getValue()); + if (loanStatus.isOpen()) { + final String errorMessage = groupOrCenter.getGroupLevel().getLevelName() + " cannot be closed because of non-closed loans."; + throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", "loan.not.closed", + errorMessage); + } else if (loanStatus.isClosed() && loan.getClosedOnDate().after(closureDate.toDate())) { + final String errorMessage = groupOrCenter.getGroupLevel().getLevelName() + "closureDate cannot be before the loan closedOnDate."; + throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", + "date.cannot.before.loan.closed.date", errorMessage, closureDate, loan.getClosedOnDate()); + } else if (loanStatus.isPendingApproval()) { + final String errorMessage = groupOrCenter.getGroupLevel().getLevelName() + " cannot be closed because of non-closed loans."; + throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", "loan.not.closed", + errorMessage); + } else if (loanStatus.isAwaitingDisbursal()) { + final String errorMessage = "Group cannot be closed because of non-closed loans."; + throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", "loan.not.closed", + errorMessage); + } + } + + List groupSavingAccounts = this.savingsRepository.findByGroupId(groupOrCenter.getId()); + + for (SavingsAccount saving : groupSavingAccounts) { + if (saving.isActive() || saving.isSubmittedAndPendingApproval() || saving.isApproved()) { + final String errorMessage = groupOrCenter.getGroupLevel().getLevelName() + " cannot be closed with active savings accounts associated."; + throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", "savings.account.not.closed", + errorMessage); + } else if (saving.isClosed() && saving.getClosedOnDate().isAfter(closureDate)) { + final String errorMessage = groupOrCenter.getGroupLevel().getLevelName() + " closureDate cannot be before the loan closedOnDate."; + throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", + "date.cannot.before.loan.closed.date", errorMessage, closureDate, saving.getClosedOnDate()); + } + } + } + + @Override + public CommandProcessingResult closeCenter(Long centerId, JsonCommand command) { + this.fromApiJsonDeserializer.validateForCenterClose(command); + final Group center = this.groupRepository.findOneWithNotFoundDetection(centerId); + final LocalDate closureDate = command.localDateValueOfParameterNamed(GroupingTypesApiConstants.closureDateParamName); + final Long closureReasonId = command.longValueOfParameterNamed(GroupingTypesApiConstants.closureReasonIdParamName); + + CodeValue closureReason = this.codeValueRepository.findOneByCodeNameAndIdWithNotFoundDetection( + GroupingTypesApiConstants.GROUP_CLOSURE_REASON, closureReasonId); + + if(center.hasActiveGroups()){ + final String errorMessage = center.getGroupLevel().getLevelName() + " cannot be closed because of active groups associated with it."; + throw new InvalidGroupStateTransitionException(center.getGroupLevel().getLevelName(), "close", "active.groups.exist", + errorMessage); + } + + validateLoansAndSavingsForGroupOrCenterClose(center, closureDate); + + center.close(closureReason, closureDate); + + this.groupRepository.saveAndFlush(center); + + return new CommandProcessingResultBuilder() // + .withEntityId(centerId) // + .build(); + } + private Set assembleSetOfClients(final Long groupOfficeId, final JsonCommand command) { final Set clientMembers = new HashSet(); @@ -411,7 +510,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group if (!ObjectUtils.isEmpty(clientMembersArray)) { for (final String clientId : clientMembersArray) { final Long id = Long.valueOf(clientId); - final Client client = this.clientRepository.findOneWithNotFoundDetection(id); + final Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(id); if (!client.isOfficeIdentifiedBy(groupOfficeId)) { final String errorMessage = "Client with identifier " + clientId + " must have the same office as group."; throw new InvalidOfficeException("client", "attach.to.group", errorMessage, clientId, groupOfficeId); 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 a58c10f45..66fc8c2a6 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 @@ -91,10 +91,13 @@ public interface LoanRepository extends JpaRepository, JpaSpecificat @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); + @Query("from Loan loan where loan.group.id = :groupId and loan.client.id is null") + List findByGroupId(@Param("groupId") Long groupId); + @Query("from Loan loan where loan.id IN :ids and loan.loanStatus IN :loanStatuses and loan.loanType IN :loanTypes") List findByIdsAndLoanStatusAndLoanType(@Param("ids") Collection ids, @Param("loanStatuses") Collection loanStatuses, @Param("loanTypes") Collection loanTypes); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java index 86a788339..7a9f625ed 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccount.java @@ -41,6 +41,7 @@ import javax.persistence.TemporalType; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; +import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; @@ -349,6 +350,10 @@ public class SavingsAccount extends AbstractPersistable { return SavingsAccountStatusType.fromInt(this.status).isApproved(); } + public boolean isClosed(){ + return SavingsAccountStatusType.fromInt(this.status).isClosed(); + } + public void postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final List existingTransactionIds, final List existingReversedTransactionIds) { @@ -1758,9 +1763,12 @@ public class SavingsAccount extends AbstractPersistable { public Group group() { return this.group; } - public boolean isWithdrawalFeeApplicableForTransfer() { return this.withdrawalFeeApplicableForTransfer; } + + public LocalDate getClosedOnDate() { + return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.closedOnDate), null); + } } \ 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 9e65b5d44..49d714bd2 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 @@ -25,4 +25,7 @@ public interface SavingsAccountRepository extends JpaRepository 0) then true else false end from SavingsAccount saving where saving.client.id = :clientId and saving.status in (100,200,300,303,304)") boolean doNonClosedSavingAccountsExistForClient(@Param("clientId") Long clientId); + + @Query("from SavingsAccount sa where sa.client.id is null and sa.group.id = :groupId") + List findByGroupId(@Param("groupId") Long groupId); } \ No newline at end of file