From b2769ff21b057e713e4e4e7b5c65dd343b116046 Mon Sep 17 00:00:00 2001 From: Madhukar Date: Tue, 22 Jul 2014 15:23:05 +0530 Subject: [PATCH] MIFOSX-1325 Attach a group to center --- api-docs/apiLive.htm | 116 +++++++++++++----- ...ategroup_disassociategroup_permissions.sql | 4 + ...e_gl_account_increase_size_of_name_col.sql | 2 + .../glaccount/command/GLAccountCommand.java | 4 +- .../commands/domain/CommandWrapper.java | 8 ++ .../service/CommandWrapperBuilder.java | 18 +++ .../SynchronousCommandProcessingService.java | 4 + .../group/api/CentersApiResource.java | 10 +- .../portfolio/group/domain/Group.java | 48 ++++++++ .../GroupExistsInCenterException.java | 12 ++ .../GroupNotExistsInCenterException.java | 12 ++ ...AssociateGroupsToCenterCommandHandler.java | 27 ++++ ...sociateGroupsFromCenterCommandHandler.java | 27 ++++ .../GroupingTypesDataValidator.java | 40 ++++++ .../GroupingTypesWritePlatformService.java | 4 + ...WritePlatformServiceJpaRepositoryImpl.java | 110 ++++++++++++++++- .../LoanAccrualWritePlatformServiceImpl.java | 4 +- .../service/LoanReadPlatformServiceImpl.java | 3 +- .../loanproduct/domain/LoanProduct.java | 4 +- 19 files changed, 421 insertions(+), 36 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V190__add_associategroup_disassociategroup_permissions.sql create mode 100644 mifosng-db/migrations/core_db/V191__update_gl_account_increase_size_of_name_col.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/GroupExistsInCenterException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/GroupNotExistsInCenterException.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/AssociateGroupsToCenterCommandHandler.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/DisassociateGroupsFromCenterCommandHandler.java diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index e47f73efd..544779f3a 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -1261,6 +1261,22 @@ + + + centers/{centerId}?command=associateGroups + Associate Groups + + + + + + + centers/{centerId}?command=disassociateGroups + Disassociate Groups + + + + centers/{centerId}/accounts @@ -4753,6 +4769,62 @@ Request Body: +   +
+
+

Associate Groups

+

This API allows to associate existing groups to a center.

+

The groups are listed from the office to which the center is associated.

+

If group(s) is already associated with center then API will result in an error.

+
+ +
+ POST https://Domain Name/api/v1/centers/{centerId}?command=associateGroups + POST centers/1?command=associateGroups +Content-Type: application/json +Request Body: +{ + "grouptMembers":[1,2] +} + + +{ + "officeId": 1, + "groupId": 1, + "resourceId": 1, + "grouptMembers": [1,2] +} + +
+
+ +   +
+
+

Disassociate Groups

+

This API allows to disassociate groups from a center.

+
+ +
+ POST https://Domain Name/api/v1/centers/{centerId}?command=disassociateGroups + POST center/1?command=disassociateGroups +Content-Type: application/json +Request Body: +{ + "grouptMembers":[1,2] +} + + +{ + "officeId": 1, + "groupId": 1, + "resourceId": 1, + "grouptMembers": [1,2] +} + +
+
+  
@@ -5542,34 +5614,6 @@ Request Body:
-   -
-
-

Associate Clients

-

This API allows to associate existing clients to a group.

-

The clients are listed from the office to which the group is associated.

-

If client(s) is already associated with group then API will result in an error.

-
- -
- POST https://Domain Name/api/v1/groups/{groupId}?command=associateClients - POST groups/1?command=associateClients -Content-Type: application/json -Request Body: -{ - "clientMembers":[1,2] -} - - -{ - "officeId": 1, - "groupId": 1, - "resourceId": 1, - "clientMembers": [1,2] -} - -
-
 
@@ -31848,6 +31892,22 @@ No Request Body: + + + centers/{centerId}?command=associateGroups + Associate Groups + + + + + + + centers/{centerId}?command=disassociateGroups + Disassociate Groups + + + + centers/{centerId}/accounts diff --git a/mifosng-db/migrations/core_db/V190__add_associategroup_disassociategroup_permissions.sql b/mifosng-db/migrations/core_db/V190__add_associategroup_disassociategroup_permissions.sql new file mode 100644 index 000000000..61edae5e2 --- /dev/null +++ b/mifosng-db/migrations/core_db/V190__add_associategroup_disassociategroup_permissions.sql @@ -0,0 +1,4 @@ +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio_center', 'DISASSOCIATEGROUPS_CENTER', 'CENTER', 'DISASSOCIATEGROUPS', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio_center', 'ASSOCIATEGROUPS_CENTER', 'CENTER', 'ASSOCIATEGROUPS', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio_center', 'DISASSOCIATEGROUPS_CENTER_CHECKER', 'CENTER', 'DISASSOCIATEGROUPS_CHECKER', 0); +INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`) VALUES ('portfolio_center', 'ASSOCIATEGROUPS_CENTER_CHECKER', 'CENTER', 'ASSOCIATEGROUPS_CHECKER', 0); \ No newline at end of file diff --git a/mifosng-db/migrations/core_db/V191__update_gl_account_increase_size_of_name_col.sql b/mifosng-db/migrations/core_db/V191__update_gl_account_increase_size_of_name_col.sql new file mode 100644 index 000000000..e43598590 --- /dev/null +++ b/mifosng-db/migrations/core_db/V191__update_gl_account_increase_size_of_name_col.sql @@ -0,0 +1,2 @@ +ALTER TABLE `acc_gl_account` + CHANGE COLUMN `name` `name` VARCHAR(200) NOT NULL AFTER `id`; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/command/GLAccountCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/command/GLAccountCommand.java index add0aba13..05f1615de 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/command/GLAccountCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/command/GLAccountCommand.java @@ -52,7 +52,7 @@ public class GLAccountCommand { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLAccount"); - baseDataValidator.reset().parameter(GLAccountJsonInputParams.NAME.getValue()).value(this.name).notBlank().notExceedingLengthOf(45); + baseDataValidator.reset().parameter(GLAccountJsonInputParams.NAME.getValue()).value(this.name).notBlank().notExceedingLengthOf(200); baseDataValidator.reset().parameter(GLAccountJsonInputParams.GL_CODE.getValue()).value(this.glCode).notBlank() .notExceedingLengthOf(45); @@ -85,7 +85,7 @@ public class GLAccountCommand { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLAccount"); baseDataValidator.reset().parameter(GLAccountJsonInputParams.NAME.getValue()).value(this.name).ignoreIfNull().notBlank() - .notExceedingLengthOf(45); + .notExceedingLengthOf(200); baseDataValidator.reset().parameter(GLAccountJsonInputParams.GL_CODE.getValue()).ignoreIfNull().value(this.glCode).notBlank() .notExceedingLengthOf(45); 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 45e483fa1..a8e37ef92 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/domain/CommandWrapper.java @@ -327,6 +327,14 @@ public class CommandWrapper { return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("CENTER"); } + public boolean isCenterAssociateGroups() { + return this.actionName.equalsIgnoreCase("ASSOCIATEGROUPS") && this.entityName.equalsIgnoreCase("CENTER"); + } + + public boolean isCenterDisassociateGroups() { + return this.actionName.equalsIgnoreCase("DISASSOCIATEGROUPS") && 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 5b1e533aa..c26deb0db 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -1479,6 +1479,24 @@ public class CommandWrapperBuilder { this.href = "/centers/" + centerId + "?command=close"; return this; } + + public CommandWrapperBuilder associateGroupsToCenter(final Long centerId) { + this.actionName = "ASSOCIATEGROUPS"; + this.entityName = "CENTER"; + this.entityId = centerId; + this.groupId = centerId; + this.href = "/groups/" + centerId + "?command=associateGroups"; + return this; + } + + public CommandWrapperBuilder disassociateGroupsFromCenter(final Long centerId) { + this.actionName = "DISASSOCIATEGROUPS"; + this.entityName = "CENTER"; + this.entityId = centerId; + this.groupId = centerId; + this.href = "/groups/" + centerId + "?command=disassociateGroups"; + 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 8710e21b9..4b61b29bc 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/SynchronousCommandProcessingService.java @@ -679,6 +679,10 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer handler = this.applicationContext.getBean("saveCenterCollectionSheetCommandHandler", NewCommandSourceHandler.class); } else if (wrapper.isCenterClose()) { handler = this.applicationContext.getBean("closeCenterCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isCenterDisassociateGroups()) { + handler = this.applicationContext.getBean("disassociateGroupsFromCenterCommandHandler", NewCommandSourceHandler.class); + } else if (wrapper.isCenterAssociateGroups()) { + handler = this.applicationContext.getBean("associateGroupsToCenterCommandHandler", NewCommandSourceHandler.class); } else { throw new UnsupportedCommandException(wrapper.commandName()); } 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 26eea0a9c..cf045f639 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 @@ -279,9 +279,17 @@ public class CentersApiResource { final CommandWrapper commandRequest = builder.closeCenter(centerId).build(); result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); return this.toApiJsonSerializer.serialize(result); + } else if (is(commandParam, "associateGroups")) { + final CommandWrapper commandRequest = builder.associateGroupsToCenter(centerId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); + } else if (is(commandParam, "disassociateGroups")) { + final CommandWrapper commandRequest = builder.disassociateGroupsFromCenter(centerId).build(); + result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + return this.toApiJsonSerializer.serialize(result); } else { throw new UnrecognizedQueryParamException("command", commandParam, new Object[] { "activate", "generateCollectionSheet", - "createRole" }); + "saveCollectionSheet", "close", "associateGroups", "disassociateGroups" }); } } 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 e3b5ce583..c3ff04abe 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 @@ -35,6 +35,7 @@ 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.GeneralPlatformDomainRuleException; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.organisation.office.domain.Office; @@ -44,6 +45,8 @@ 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.GroupExistsInCenterException; +import org.mifosplatform.portfolio.group.exception.GroupNotExistsInCenterException; import org.mifosplatform.portfolio.group.exception.InvalidGroupStateTransitionException; import org.mifosplatform.useradministration.domain.AppUser; import org.springframework.data.jpa.domain.AbstractPersistable; @@ -518,4 +521,49 @@ public final class Group extends AbstractPersistable { } return false; } + + public boolean hasGroupAsMember(final Group group) { + return this.groupMembers.contains(group); + } + + public List associateGroups(final Set groupMembersSet) { + + final List differences = new ArrayList<>(); + for (final Group group : groupMembersSet) { + + if (group.isCenter()) { + final String defaultUserMessage = "Center can not assigned as a child"; + throw new GeneralPlatformDomainRuleException("error.msg.center.cannot.be.assigned.as.child", defaultUserMessage, + group.getId()); + } + + if (group.isChildGroup()) { + final String defaultUserMessage = "Group is already associated with a center"; + throw new GeneralPlatformDomainRuleException("error.msg.group.already.associated.with.center", defaultUserMessage, group + .getParent().getId(), group.getId()); + } + + if (hasGroupAsMember(group)) { throw new GroupExistsInCenterException(getId(), group.getId()); } + + this.groupMembers.add(group); + differences.add(group.getId().toString()); + } + + return differences; + } + + public List disassociateGroups(Set groupMembersSet) { + + final List differences = new ArrayList<>(); + for (final Group group : groupMembersSet) { + if (hasGroupAsMember(group)) { + this.groupMembers.remove(group); + differences.add(group.getId().toString()); + } else { + throw new GroupNotExistsInCenterException(group.getId(), getId()); + } + } + + return differences; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/GroupExistsInCenterException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/GroupExistsInCenterException.java new file mode 100644 index 000000000..69470c7be --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/GroupExistsInCenterException.java @@ -0,0 +1,12 @@ +package org.mifosplatform.portfolio.group.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +public class GroupExistsInCenterException extends AbstractPlatformDomainRuleException { + + public GroupExistsInCenterException(final Long centerId, final Long groupId) { + super("error.msg.group.is.already.member.of.center", "Group with identifier " + groupId + + " is already exists in Center with identifier " + centerId, groupId, centerId); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/GroupNotExistsInCenterException.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/GroupNotExistsInCenterException.java new file mode 100644 index 000000000..4c6ee32dd --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/exception/GroupNotExistsInCenterException.java @@ -0,0 +1,12 @@ +package org.mifosplatform.portfolio.group.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +public class GroupNotExistsInCenterException extends AbstractPlatformDomainRuleException { + + public GroupNotExistsInCenterException(final Long groupId, final Long centerId) { + super("error.msg.group.not.in.center", "Group with identifier " + groupId + " is not exists in Center with identifier " + centerId, + groupId, centerId); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/AssociateGroupsToCenterCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/AssociateGroupsToCenterCommandHandler.java new file mode 100644 index 000000000..7f95fc86b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/AssociateGroupsToCenterCommandHandler.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 AssociateGroupsToCenterCommandHandler implements NewCommandSourceHandler { + + private final GroupingTypesWritePlatformService writePlatformService; + + @Autowired + public AssociateGroupsToCenterCommandHandler(final GroupingTypesWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + return this.writePlatformService.associateGroupsToCenter(command.entityId(), command); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/DisassociateGroupsFromCenterCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/DisassociateGroupsFromCenterCommandHandler.java new file mode 100644 index 000000000..3ec7b1940 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/handler/DisassociateGroupsFromCenterCommandHandler.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 DisassociateGroupsFromCenterCommandHandler implements NewCommandSourceHandler { + + private final GroupingTypesWritePlatformService writePlatformService; + + @Autowired + public DisassociateGroupsFromCenterCommandHandler(final GroupingTypesWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(JsonCommand command) { + return this.writePlatformService.disassociateGroupsToCenter(command.entityId(), command); + } + +} 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 aa3df65da..3559adf12 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 @@ -476,4 +476,44 @@ public final class GroupingTypesDataValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } + + public void validateForAssociateGroups(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + + final Set supportedParameters = new HashSet<>(Arrays.asList(GroupingTypesApiConstants.groupMembersParamName)); + + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList<>(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("group"); + final String[] groups = this.fromApiJsonHelper.extractArrayNamed(GroupingTypesApiConstants.groupMembersParamName, element); + baseDataValidator.reset().parameter(GroupingTypesApiConstants.groupMembersParamName).value(groups).arrayNotEmpty(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + + } + + public void validateForDisassociateGroups(final String json) { + + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + + final Set supportedParameters = new HashSet<>(Arrays.asList(GroupingTypesApiConstants.groupMembersParamName)); + + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList<>(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("group"); + final String[] groups = this.fromApiJsonHelper.extractArrayNamed(GroupingTypesApiConstants.groupMembersParamName, element); + baseDataValidator.reset().parameter(GroupingTypesApiConstants.groupMembersParamName).value(groups).arrayNotEmpty(); + + throwExceptionIfValidationWarningsExist(dataValidationErrors); + } } \ No newline at end of file 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 18a6f97aa..4d12bb93c 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 @@ -33,4 +33,8 @@ public interface GroupingTypesWritePlatformService { CommandProcessingResult associateClientsToGroup(Long groupId, JsonCommand command); CommandProcessingResult disassociateClientsFromGroup(Long groupId, JsonCommand command); + + CommandProcessingResult associateGroupsToCenter(final Long centerId, final JsonCommand command); + + CommandProcessingResult disassociateGroupsToCenter(final Long centerId, final 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 85ea9519e..08ad471dd 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 @@ -25,6 +25,7 @@ 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; +import org.mifosplatform.infrastructure.core.exception.GeneralPlatformDomainRuleException; import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; import org.mifosplatform.organisation.office.domain.Office; @@ -33,6 +34,11 @@ import org.mifosplatform.organisation.office.exception.InvalidOfficeException; 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.calendar.domain.Calendar; +import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; +import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; +import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository; +import org.mifosplatform.portfolio.calendar.domain.CalendarType; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.client.service.LoanStatusMapper; @@ -81,6 +87,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group private final CodeValueRepositoryWrapper codeValueRepository; private final SavingsAccountRepository savingsRepository; private final CommandProcessingService commandProcessingService; + private final CalendarInstanceRepository calendarInstanceRepository; @Autowired public GroupingTypesWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, @@ -88,7 +95,8 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group final OfficeRepository officeRepository, final StaffRepositoryWrapper staffRepository, final NoteRepository noteRepository, final GroupLevelRepository groupLevelRepository, final GroupingTypesDataValidator fromApiJsonDeserializer, final LoanRepository loanRepository, final SavingsAccountRepository savingsRepository, - final CodeValueRepositoryWrapper codeValueRepository, final CommandProcessingService commandProcessingService) { + final CodeValueRepositoryWrapper codeValueRepository, final CommandProcessingService commandProcessingService, + final CalendarInstanceRepository calendarInstanceRepository) { this.context = context; this.groupRepository = groupRepository; this.clientRepositoryWrapper = clientRepositoryWrapper; @@ -101,6 +109,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group this.savingsRepository = savingsRepository; this.codeValueRepository = codeValueRepository; this.commandProcessingService = commandProcessingService; + this.calendarInstanceRepository = calendarInstanceRepository; } private CommandProcessingResult createGroupingType(final JsonCommand command, final GroupTypes groupingType, final Long centerId) { @@ -579,6 +588,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group final String errorMessage = "Group and child groups must have the same office."; throw new InvalidOfficeException("group", "attach.to.parent.group", errorMessage); } + childGroups.add(group); } } @@ -684,6 +694,60 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group .build(); } + @Transactional + @Override + public CommandProcessingResult associateGroupsToCenter(final Long centerId, final JsonCommand command) { + + this.fromApiJsonDeserializer.validateForAssociateGroups(command.json()); + + final Group centerForUpdate = this.groupRepository.findOneWithNotFoundDetection(centerId); + final Set groupMembers = assembleSetOfChildGroups(centerForUpdate.officeId(), command); + checkGroupMembersMeetingSyncWithCenterMeeting(centerId, groupMembers); + + final Map actualChanges = new HashMap<>(); + final List changes = centerForUpdate.associateGroups(groupMembers); + if (!changes.isEmpty()) { + actualChanges.put(GroupingTypesApiConstants.groupMembersParamName, changes); + } + + this.groupRepository.saveAndFlush(centerForUpdate); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withOfficeId(centerForUpdate.officeId()) // + .withGroupId(centerForUpdate.getId()) // + .withEntityId(centerForUpdate.getId()) // + .with(actualChanges) // + .build(); + } + + @Transactional + @Override + public CommandProcessingResult disassociateGroupsToCenter(final Long centerId, final JsonCommand command) { + this.fromApiJsonDeserializer.validateForDisassociateGroups(command.json()); + + final Group centerForUpdate = this.groupRepository.findOneWithNotFoundDetection(centerId); + final Set groupMembers = assembleSetOfChildGroups(centerForUpdate.officeId(), command); + + final Map actualChanges = new HashMap<>(); + + final List changes = centerForUpdate.disassociateGroups(groupMembers); + if (!changes.isEmpty()) { + actualChanges.put(GroupingTypesApiConstants.clientMembersParamName, changes); + } + + this.groupRepository.saveAndFlush(centerForUpdate); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withOfficeId(centerForUpdate.officeId()) // + .withGroupId(centerForUpdate.getId()) // + .withEntityId(centerForUpdate.getId()) // + .with(actualChanges) // + .build(); + + } + @Transactional private void validateForJLGLoan(final Long groupId, final Set clientMembers) { for (final Client client : clientMembers) { @@ -720,4 +784,48 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group } } + private void checkGroupMembersMeetingSyncWithCenterMeeting(final Long centerId, final Set groupMembers) { + + /** + * Get parent(center) calendar + */ + Calendar ceneterCalendar = null; + final CalendarInstance parentCalendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId( + centerId, CalendarEntityType.CENTERS.getValue(), CalendarType.COLLECTION.getValue()); + if (parentCalendarInstance != null) { + ceneterCalendar = parentCalendarInstance.getCalendar(); + } + + for (final Group group : groupMembers) { + /** + * Get child(group) calendar + */ + Calendar groupCalendar = null; + final CalendarInstance groupCalendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId( + group.getId(), CalendarEntityType.GROUPS.getValue(), CalendarType.COLLECTION.getValue()); + if (groupCalendarInstance != null) { + groupCalendar = groupCalendarInstance.getCalendar(); + } + + /** + * Group shouldn't have a meeting when no meeting attached for + * center + */ + if (ceneterCalendar == null && groupCalendar != null) { + throw new GeneralPlatformDomainRuleException( + "error.msg.center.associating.group.not.allowed.with.meeting.attached.to.group", "Group with id " + group.getId() + + " is already associated with meeting", group.getId()); + } + /** + * Group meeting recurrence should match with center meeting + * recurrence + */ + else if (ceneterCalendar != null && groupCalendar != null) { + + if (!ceneterCalendar.getRecurrence().equalsIgnoreCase(groupCalendar.getRecurrence())) { throw new GeneralPlatformDomainRuleException( + "error.msg.center.associating.group.not.allowed.with.different.meeting", "Group with id " + group.getId() + + " meeting recurrence doesnot matched with center meeting recurrence", group.getId()); } + } + } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java index 020ae737a..142a08126 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java @@ -98,9 +98,9 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat try { if (!loansIds.contains(accrualData.getLoanId())) { if (accrualData.getDueDateAsLocaldate().isAfter(tilldate)) { - if (accruredTill == null) { + //if (accruredTill == null) { accruredTill = accrualData.getAccruedTill(); - } + //} addAccrualTillSpecificDate(tilldate, accrualData, accruredTill); } else { addAccrualAccounting(accrualData); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index dd95c0331..3e021a86a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -1395,7 +1395,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final StringBuilder sqlBuilder = new StringBuilder(400); sqlBuilder .append("loan.id as loanId ,if(loan.client_id is null,mg.office_id,mc.office_id) as officeId,") - .append("loan.accrued_till as accruedTill, loan.repayment_period_frequency_enum as frequencyEnum, ") + .append("ifnull(loan.accrued_till, ifnull(loan.interest_calculated_from_date, loan.disbursedon_date)) as accruedTill, ") + .append("loan.repayment_period_frequency_enum as frequencyEnum, ") .append("loan.repay_every as repayEvery,") .append("(select sum(lc.amount) as dueamount from m_loan_charge lc where lc.loan_id = loan.id and lc.is_active = 1 and lc.waived = 0 and lc.is_penalty = 0 and lc.due_for_collection_as_of_date > ls.fromdate and lc.due_for_collection_as_of_date<= if(:tilldate < ls.duedate, :tilldate , ls.duedate)) as feedueIn,") .append("(select sum(lc.amount) as dueamount from m_loan_charge lc where lc.loan_id = loan.id and lc.is_active = 1 and lc.waived = 0 and lc.is_penalty = 1 and lc.due_for_collection_as_of_date > ls.fromdate and lc.due_for_collection_as_of_date<= if(:tilldate < ls.duedate, :tilldate , ls.duedate)) as penalitydueIn,") diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java index da0eea1d2..f599cbe2c 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProduct.java @@ -668,7 +668,9 @@ public class LoanProduct extends AbstractPersistable { } else { this.productInterestRecalculationDetails = null; } - } else { + } + + if (!isInterestRecalculationEnabledChanged && this.isInterestRecalculationEnabled()) { this.productInterestRecalculationDetails.update(command, actualChanges, localeAsInput); }