cleaning up group validations

This commit is contained in:
Vishwas Babu A J
2014-11-03 21:09:02 +05:30
parent 7d0ac6bf29
commit 702ecd480f
9 changed files with 179 additions and 146 deletions
@@ -39,4 +39,8 @@ public interface ConfigurationDomainService {
Integer retrieveFinancialYearBeginningMonth(); Integer retrieveFinancialYearBeginningMonth();
public Integer retrieveMinAllowedClientsInGroup();
public Integer retrieveMaxAllowedClientsInGroup();
} }
@@ -121,7 +121,6 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
return property.getValue(); return property.getValue();
} }
@Override @Override
public boolean isPasswordForcedResetEnable() { public boolean isPasswordForcedResetEnable() {
final String propertyName = "force-password-reset-days"; final String propertyName = "force-password-reset-days";
@@ -147,9 +146,24 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
public Integer retrieveFinancialYearBeginningMonth() { public Integer retrieveFinancialYearBeginningMonth() {
final String propertyName = "financial-year-beginning-month"; final String propertyName = "financial-year-beginning-month";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName); final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
if(property.isEnabled()) if (property.isEnabled()) return property.getValue().intValue();
return property.getValue().intValue(); return 1;
return 1; }
@Override
public Integer retrieveMinAllowedClientsInGroup() {
final String propertyName = "min-clients-in-group";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
if (property.isEnabled()) { return property.getValue().intValue(); }
return null;
}
@Override
public Integer retrieveMaxAllowedClientsInGroup() {
final String propertyName = "max-clients-in-group";
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName);
if (property.isEnabled()) { return property.getValue().intValue(); }
return null;
} }
} }
@@ -298,7 +298,6 @@ public final class Client extends AbstractPersistable<Long> {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
validateNameParts(dataValidationErrors); validateNameParts(dataValidationErrors);
validateActivationDate(dataValidationErrors); validateActivationDate(dataValidationErrors);
validateClientsGroupRules(dataValidationErrors);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
@@ -449,7 +448,6 @@ public final class Client extends AbstractPersistable<Long> {
actualChanges.put(ClientApiConstants.savingsProductIdParamName, newValue); actualChanges.put(ClientApiConstants.savingsProductIdParamName, newValue);
} }
if (command.isChangeInLongParameterNamed(ClientApiConstants.genderIdParamName, genderId())) { if (command.isChangeInLongParameterNamed(ClientApiConstants.genderIdParamName, genderId())) {
final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.genderIdParamName); final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.genderIdParamName);
actualChanges.put(ClientApiConstants.genderIdParamName, newValue); actualChanges.put(ClientApiConstants.genderIdParamName, newValue);
@@ -465,7 +463,6 @@ public final class Client extends AbstractPersistable<Long> {
actualChanges.put(ClientApiConstants.clientClassificationIdParamName, newValue); actualChanges.put(ClientApiConstants.clientClassificationIdParamName, newValue);
} }
final String dateFormatAsInput = command.dateFormat(); final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale(); final String localeAsInput = command.locale();
@@ -490,7 +487,7 @@ public final class Client extends AbstractPersistable<Long> {
this.dateOfBirth = newValue.toDate(); this.dateOfBirth = newValue.toDate();
} }
if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.submittedOnDateParamName,getSubmittedOnDate())) { if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.submittedOnDateParamName, getSubmittedOnDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName); final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName);
actualChanges.put(ClientApiConstants.submittedOnDateParamName, valueAsInput); actualChanges.put(ClientApiConstants.submittedOnDateParamName, valueAsInput);
actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput); actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
@@ -507,7 +504,6 @@ public final class Client extends AbstractPersistable<Long> {
return actualChanges; return actualChanges;
} }
private void validateNameParts(final List<ApiParameterError> dataValidationErrors) { private void validateNameParts(final List<ApiParameterError> dataValidationErrors) {
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client"); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("client");
@@ -572,18 +568,6 @@ public final class Client extends AbstractPersistable<Long> {
} }
} }
/*
* To become a part of a group, group may have set of criteria to be met
* before client can become member of it. This method is placeholder for
* such validation.
*/
private void validateClientsGroupRules(final List<ApiParameterError> dataValidationErrors) {
for (Group group : this.groups) {
group.validateGroupHasMoreThanRequiredNumberOfClientsAngLogError(dataValidationErrors);
}
}
private void deriveDisplayName() { private void deriveDisplayName() {
StringBuilder nameBuilder = new StringBuilder(); StringBuilder nameBuilder = new StringBuilder();
@@ -18,6 +18,7 @@ import org.mifosplatform.commands.service.CommandProcessingService;
import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.infrastructure.codes.domain.CodeValue; import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper; import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
@@ -42,6 +43,7 @@ import org.mifosplatform.portfolio.client.exception.InvalidClientSavingProductEx
import org.mifosplatform.portfolio.client.exception.InvalidClientStateTransitionException; import org.mifosplatform.portfolio.client.exception.InvalidClientStateTransitionException;
import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.domain.Group;
import org.mifosplatform.portfolio.group.domain.GroupRepository; import org.mifosplatform.portfolio.group.domain.GroupRepository;
import org.mifosplatform.portfolio.group.exception.GroupMemberCountNotInPermissibleRangeException;
import org.mifosplatform.portfolio.group.exception.GroupNotFoundException; import org.mifosplatform.portfolio.group.exception.GroupNotFoundException;
import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
@@ -82,6 +84,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
private final SavingsProductRepository savingsProductRepository; private final SavingsProductRepository savingsProductRepository;
private final SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService; private final SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService;
private final CommandProcessingService commandProcessingService; private final CommandProcessingService commandProcessingService;
private final ConfigurationDomainService configurationDomainService;
@Autowired @Autowired
public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -91,7 +94,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final CodeValueRepositoryWrapper codeValueRepository, final LoanRepository loanRepository, final CodeValueRepositoryWrapper codeValueRepository, final LoanRepository loanRepository,
final SavingsAccountRepository savingsRepository, final SavingsProductRepository savingsProductRepository, final SavingsAccountRepository savingsRepository, final SavingsProductRepository savingsProductRepository,
final SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService, final SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService,
final CommandProcessingService commandProcessingService) { final CommandProcessingService commandProcessingService, final ConfigurationDomainService configurationDomainService) {
this.context = context; this.context = context;
this.clientRepository = clientRepository; this.clientRepository = clientRepository;
this.officeRepository = officeRepository; this.officeRepository = officeRepository;
@@ -106,6 +109,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
this.savingsProductRepository = savingsProductRepository; this.savingsProductRepository = savingsProductRepository;
this.savingsApplicationProcessWritePlatformService = savingsApplicationProcessWritePlatformService; this.savingsApplicationProcessWritePlatformService = savingsApplicationProcessWritePlatformService;
this.commandProcessingService = commandProcessingService; this.commandProcessingService = commandProcessingService;
this.configurationDomainService = configurationDomainService;
} }
@Transactional @Transactional
@@ -215,6 +219,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
clientType, clientClassification, command); clientType, clientClassification, command);
boolean rollbackTransaction = false; boolean rollbackTransaction = false;
if (newClient.isActive()) { if (newClient.isActive()) {
validateParentGroupRulesBeforeClientActivation(newClient);
final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateClient(null).build(); final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateClient(null).build();
rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, currentUser); rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, currentUser);
} }
@@ -351,6 +356,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
this.fromApiJsonDeserializer.validateActivation(command); this.fromApiJsonDeserializer.validateActivation(command);
final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId); final Client client = this.clientRepository.findOneWithNotFoundDetection(clientId);
validateParentGroupRulesBeforeClientActivation(client);
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
@@ -568,4 +574,22 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
.build(); .build();
} }
/*
* To become a part of a group, group may have set of criteria to be met
* before client can become member of it.
*/
private void validateParentGroupRulesBeforeClientActivation(Client client) {
Integer minNumberOfClients = configurationDomainService.retrieveMinAllowedClientsInGroup();
Integer maxNumberOfClients = configurationDomainService.retrieveMaxAllowedClientsInGroup();
for (Group group : client.getGroups()) {
/**
* Since this Client has not yet been associated with the group,
* reduce maxNumberOfClients by 1
**/
final boolean validationsuccess = group.isGroupsClientCountWithinMaxRange(maxNumberOfClients - 1);
if (!validationsuccess) { throw new GroupMemberCountNotInPermissibleRangeException(group.getId(), minNumberOfClients,
maxNumberOfClients); }
}
}
} }
@@ -118,18 +118,19 @@ public final class Group extends AbstractPersistable<Long> {
@JoinColumn(name = "submittedon_userid", nullable = true) @JoinColumn(name = "submittedon_userid", nullable = true)
private AppUser submittedBy; private AppUser submittedBy;
// JPA default constructor for entity // JPA default constructor for entity
protected Group() { protected Group() {
this.name = null; this.name = null;
this.externalId = null; this.externalId = null;
this.clientMembers = new HashSet<>(); this.clientMembers = new HashSet<>();
} }
public static Group newGroup(final Office office, final Staff staff, final Group parent, final GroupLevel groupLevel, public static Group newGroup(final Office office, final Staff staff, final Group parent, final GroupLevel groupLevel,
final String name, final String externalId, final boolean active, final LocalDate activationDate, final String name, final String externalId, final boolean active, final LocalDate activationDate,
final Set<Client> clientMembers, final Set<Group> groupMembers, final LocalDate submittedOnDate, final AppUser currentUser) { final Set<Client> clientMembers, final Set<Group> groupMembers, final LocalDate submittedOnDate, final AppUser currentUser) {
// By default new group is created in PENDING status, unless explicitly status is set to active // By default new group is created in PENDING status, unless explicitly
// status is set to active
GroupingTypeStatus status = GroupingTypeStatus.PENDING; GroupingTypeStatus status = GroupingTypeStatus.PENDING;
LocalDate groupActivationDate = null; LocalDate groupActivationDate = null;
if (active) { if (active) {
@@ -177,7 +178,8 @@ public final class Group extends AbstractPersistable<Long> {
associateClients(clientMembers); associateClients(clientMembers);
/* /*
* Always keep status change at the bottom, as status change rule depends on the attribute's value * Always keep status change at the bottom, as status change rule
* depends on the attribute's value
*/ */
setStatus(activationDate, currentUser, status, dataValidationErrors); setStatus(activationDate, currentUser, status, dataValidationErrors);
@@ -185,14 +187,12 @@ public final class Group extends AbstractPersistable<Long> {
throwExceptionIfErrors(dataValidationErrors); throwExceptionIfErrors(dataValidationErrors);
} }
private void setStatus(final LocalDate activationDate, final AppUser loginUser, final GroupingTypeStatus status, private void setStatus(final LocalDate activationDate, final AppUser loginUser, final GroupingTypeStatus status,
final List<ApiParameterError> dataValidationErrors) { final List<ApiParameterError> dataValidationErrors) {
if (status.isActive()) { if (status.isActive()) {
activate(loginUser, activationDate, dataValidationErrors); activate(loginUser, activationDate, dataValidationErrors);
} } else {
else{
this.status = status.getValue(); this.status = status.getValue();
} }
@@ -201,8 +201,6 @@ public final class Group extends AbstractPersistable<Long> {
private void activate(final AppUser currentUser, final LocalDate activationLocalDate, final List<ApiParameterError> dataValidationErrors) { private void activate(final AppUser currentUser, final LocalDate activationLocalDate, final List<ApiParameterError> dataValidationErrors) {
validateStatusNotEqualToActiveAndLogError(dataValidationErrors); validateStatusNotEqualToActiveAndLogError(dataValidationErrors);
validateRequiredNumberOfClientPerGroupAngLogError(dataValidationErrors);
if (dataValidationErrors.isEmpty()) { if (dataValidationErrors.isEmpty()) {
this.status = GroupingTypeStatus.ACTIVE.getValue(); this.status = GroupingTypeStatus.ACTIVE.getValue();
setActivationDate(activationLocalDate.toDate(), currentUser, dataValidationErrors); setActivationDate(activationLocalDate.toDate(), currentUser, dataValidationErrors);
@@ -218,7 +216,7 @@ public final class Group extends AbstractPersistable<Long> {
} }
private void setActivationDate(final Date activationDate, final AppUser loginUser, final List<ApiParameterError> dataValidationErrors){ private void setActivationDate(final Date activationDate, final AppUser loginUser, final List<ApiParameterError> dataValidationErrors) {
if (activationDate != null) { if (activationDate != null) {
this.activationDate = activationDate; this.activationDate = activationDate;
@@ -238,7 +236,7 @@ public final class Group extends AbstractPersistable<Long> {
} }
public boolean isActive() { public boolean isActive() {
return this.status != null ? GroupingTypeStatus.fromInt(this.status).isActive(): false; return this.status != null ? GroupingTypeStatus.fromInt(this.status).isActive() : false;
} }
private boolean isDateInTheFuture(final LocalDate localDate) { private boolean isDateInTheFuture(final LocalDate localDate) {
@@ -313,15 +311,6 @@ public final class Group extends AbstractPersistable<Long> {
} }
public List<String> associateClients(final Set<Client> clientMembersSet) { public List<String> associateClients(final Set<Client> clientMembersSet) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final List<String> newlyAssociatedClients = associateClients(clientMembersSet, dataValidationErrors);
throwExceptionIfErrors(dataValidationErrors);
return newlyAssociatedClients;
}
public List<String> associateClients(final Set<Client> clientMembersSet, final List<ApiParameterError> dataValidationErrors) {
final List<String> differences = new ArrayList<>(); final List<String> differences = new ArrayList<>();
for (final Client client : clientMembersSet) { for (final Client client : clientMembersSet) {
if (hasClientAsMember(client)) { throw new ClientExistInGroupException(client.getId(), getId()); } if (hasClientAsMember(client)) { throw new ClientExistInGroupException(client.getId(), getId()); }
@@ -329,13 +318,6 @@ public final class Group extends AbstractPersistable<Long> {
differences.add(client.getId().toString()); differences.add(client.getId().toString());
} }
/*
* Minimum and maximum number of client rule is applicable only for active group
*/
if (isActive()) {
validateGroupHasMoreThanRequiredNumberOfClientsAngLogError(dataValidationErrors);
}
return differences; return differences;
} }
@@ -425,6 +407,10 @@ public final class Group extends AbstractPersistable<Long> {
return this.groupLevel.isCenter(); return this.groupLevel.isCenter();
} }
public boolean isGroup() {
return this.groupLevel.isGroup();
}
public boolean isTransferInProgress() { public boolean isTransferInProgress() {
return GroupingTypeStatus.fromInt(this.status).isTransferInProgress(); return GroupingTypeStatus.fromInt(this.status).isTransferInProgress();
} }
@@ -512,7 +498,6 @@ public final class Group extends AbstractPersistable<Long> {
.getParent().getId(), group.getId()); .getParent().getId(), group.getId());
} }
this.groupMembers.add(group); this.groupMembers.add(group);
differences.add(group.getId().toString()); differences.add(group.getId().toString());
} }
@@ -535,66 +520,45 @@ public final class Group extends AbstractPersistable<Long> {
return differences; return differences;
} }
public void validateRequiredNumberOfClientPerGroupAngLogError(final List<ApiParameterError> dataValidationErrors) { public Boolean isGroupsClientCountWithinMinMaxRange(Integer minClients, Integer maxClients) {
if (!hasGroupRequiredNumberOfClients()) { if (maxClients == null && minClients == null) { return true; }
// This validation not strictly related to a parameter, that is the // set minClients or maxClients to 0 if null
// reason parameterName is set null
final String parameterName = null; if (minClients == null) {
final String globalisationMessageCode = "error.msg.group.activation.without.required.number.of.members"; minClients = 0;
final String defaultUserMessage = "Group should have number of members between " + this.groupLevel.getMinClients() + " and " }
+ this.groupLevel.getMaxClients();
final ApiParameterError error = ApiParameterError.parameterError(globalisationMessageCode, defaultUserMessage, parameterName);
dataValidationErrors.add(error); if (maxClients == null) {
maxClients = Integer.MAX_VALUE;
}
Set<Client> activeClientMembers = getActiveClientMembers();
if (activeClientMembers.size() >= minClients && activeClientMembers.size() <= maxClients) { return true; }
return false;
}
public Boolean isGroupsClientCountWithinMaxRange(Integer maxClients) {
Set<Client> activeClientMembers = getActiveClientMembers();
if (maxClients == null) {
return true;
} else if (activeClientMembers.size() <= maxClients) {
return true;
} else {
return false;
} }
} }
private Boolean hasGroupRequiredNumberOfClients() { public Set<Client> getActiveClientMembers() {
Set<Client> activeClientMembers = new HashSet<>();
/* for (Client client : this.clientMembers) {
* If maximum clients required per group is set to zero or less then zero if (client.isActive()) {
* then irrespective of minimum required number, below condition returns true activeClientMembers.add(client);
* that means this is rule is not validated }
*/
if (this.clientMembers.size() >= this.groupLevel.getMinClients()
&& (this.clientMembers.size() < this.groupLevel.getMaxClients() || this.groupLevel.getMaxClients() <= 0)) { return true; }
return false;
}
public void validateGroupHasMoreThanRequiredNumberOfClientsAngLogError(final List<ApiParameterError> dataValidationErrors) {
if (hasGroupMoreThanRequiredNumberOfClients()) {
// This validation not strictly related to a parameter, that is the
// reason parameterName is set null
final String parameterName = null;
final String globalisationMessageCode = "error.msg.group.can.not.have.more.than.required.number.of.members";
final String defaultUserMessage = "Group should have number of members between " + this.groupLevel.getMinClients() + " and "
+ this.groupLevel.getMaxClients();
final ApiParameterError error = ApiParameterError.parameterError(globalisationMessageCode, defaultUserMessage, parameterName);
dataValidationErrors.add(error);
} }
} return activeClientMembers;
private Boolean hasGroupMoreThanRequiredNumberOfClients(){
/*
* If maximum clients required per group is set to zero or less then zero
* then below condition returns always false
* that means this is rule is not validated and group can have unlimited number of clients.
*/
if (this.clientMembers.size() > this.groupLevel.getMaxClients() && this.groupLevel.getMaxClients() > 0) { return true; }
return false;
} }
private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) { private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) {
@@ -603,8 +567,8 @@ public final class Group extends AbstractPersistable<Long> {
final String defaultUserMessage = "Submitted on date cannot be in the future."; final String defaultUserMessage = "Submitted on date cannot be in the future.";
final String globalisationMessageCode = "error.msg.group.submittedOnDate.in.the.future"; final String globalisationMessageCode = "error.msg.group.submittedOnDate.in.the.future";
final ApiParameterError error = ApiParameterError.parameterError(globalisationMessageCode , final ApiParameterError error = ApiParameterError.parameterError(globalisationMessageCode, defaultUserMessage,
defaultUserMessage, GroupingTypesApiConstants.submittedOnDateParamName, this.submittedOnDate); GroupingTypesApiConstants.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
@@ -638,7 +602,7 @@ public final class Group extends AbstractPersistable<Long> {
} }
} }
private void validateStatusNotEqualToActiveAndLogError(final List<ApiParameterError> dataValidationErrors){ private void validateStatusNotEqualToActiveAndLogError(final List<ApiParameterError> dataValidationErrors) {
if (isActive()) { if (isActive()) {
final String defaultUserMessage = "Cannot activate group. Group is already active."; final String defaultUserMessage = "Cannot activate group. Group is already active.";
@@ -30,12 +30,6 @@ public class GroupLevel extends AbstractPersistable<Long> {
@Column(name = "can_have_clients", nullable = false) @Column(name = "can_have_clients", nullable = false)
private boolean canHaveClients = false; private boolean canHaveClients = false;
@Column(name = "min_clients", nullable = false)
private final Long minClients;
@Column(name = "max_clients", nullable = false)
private final Long maxClients;
public GroupLevel() { public GroupLevel() {
this.parentId = null; this.parentId = null;
@@ -43,22 +37,17 @@ public class GroupLevel extends AbstractPersistable<Long> {
this.levelName = null; this.levelName = null;
this.recursable = false; this.recursable = false;
this.canHaveClients = false; this.canHaveClients = false;
this.minClients = null;
this.maxClients = null;
} }
public GroupLevel(final Long parentId, final boolean isSuperParent, final String levelName, final boolean recursable, public GroupLevel(final Long parentId, final boolean isSuperParent, final String levelName, final boolean recursable,
final boolean canHaveClients, final Long minClients, final Long maxClients) { final boolean canHaveClients) {
this.superParent = isSuperParent; this.superParent = isSuperParent;
this.parentId = parentId; this.parentId = parentId;
this.levelName = levelName; this.levelName = levelName;
this.recursable = recursable; this.recursable = recursable;
this.canHaveClients = canHaveClients; this.canHaveClients = canHaveClients;
this.minClients = minClients;
this.maxClients = maxClients;
} }
public Long getParentId() { public Long getParentId() {
@@ -89,11 +78,8 @@ public class GroupLevel extends AbstractPersistable<Long> {
return this.levelName.equalsIgnoreCase("Center"); return this.levelName.equalsIgnoreCase("Center");
} }
public Long getMinClients(){ public boolean isGroup() {
return this.minClients; return this.levelName.equalsIgnoreCase("Group");
} }
public Long getMaxClients(){
return this.maxClients;
}
} }
@@ -0,0 +1,17 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.portfolio.group.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class GroupMemberCountNotInPermissibleRangeException extends AbstractPlatformDomainRuleException {
public GroupMemberCountNotInPermissibleRangeException(final Long groupId, final Integer minClients, final Integer maxClients) {
super("error.msg.group.members.count.must.be.in.permissible.range", "Number of members in the group with Id " + groupId
+ " should be between " + minClients + " and " + maxClients, groupId, minClients, maxClients);
}
}
@@ -19,6 +19,7 @@ import org.mifosplatform.commands.service.CommandProcessingService;
import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.infrastructure.codes.domain.CodeValue; import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper; import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
@@ -47,6 +48,7 @@ import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
import org.mifosplatform.portfolio.group.domain.GroupTypes; import org.mifosplatform.portfolio.group.domain.GroupTypes;
import org.mifosplatform.portfolio.group.exception.GroupAccountExistsException; import org.mifosplatform.portfolio.group.exception.GroupAccountExistsException;
import org.mifosplatform.portfolio.group.exception.GroupHasNoStaffException; import org.mifosplatform.portfolio.group.exception.GroupHasNoStaffException;
import org.mifosplatform.portfolio.group.exception.GroupMemberCountNotInPermissibleRangeException;
import org.mifosplatform.portfolio.group.exception.GroupMustBePendingToBeDeletedException; import org.mifosplatform.portfolio.group.exception.GroupMustBePendingToBeDeletedException;
import org.mifosplatform.portfolio.group.exception.InvalidGroupLevelException; import org.mifosplatform.portfolio.group.exception.InvalidGroupLevelException;
import org.mifosplatform.portfolio.group.exception.InvalidGroupStateTransitionException; import org.mifosplatform.portfolio.group.exception.InvalidGroupStateTransitionException;
@@ -85,6 +87,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
private final SavingsAccountRepository savingsRepository; private final SavingsAccountRepository savingsRepository;
private final CommandProcessingService commandProcessingService; private final CommandProcessingService commandProcessingService;
private final CalendarInstanceRepository calendarInstanceRepository; private final CalendarInstanceRepository calendarInstanceRepository;
private final ConfigurationDomainService configurationDomainService;
@Autowired @Autowired
public GroupingTypesWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, public GroupingTypesWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
@@ -93,7 +96,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final GroupLevelRepository groupLevelRepository, final GroupingTypesDataValidator fromApiJsonDeserializer, final GroupLevelRepository groupLevelRepository, final GroupingTypesDataValidator fromApiJsonDeserializer,
final LoanRepository loanRepository, final SavingsAccountRepository savingsRepository, final LoanRepository loanRepository, final SavingsAccountRepository savingsRepository,
final CodeValueRepositoryWrapper codeValueRepository, final CommandProcessingService commandProcessingService, final CodeValueRepositoryWrapper codeValueRepository, final CommandProcessingService commandProcessingService,
final CalendarInstanceRepository calendarInstanceRepository) { final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService) {
this.context = context; this.context = context;
this.groupRepository = groupRepository; this.groupRepository = groupRepository;
this.clientRepositoryWrapper = clientRepositoryWrapper; this.clientRepositoryWrapper = clientRepositoryWrapper;
@@ -107,6 +110,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
this.codeValueRepository = codeValueRepository; this.codeValueRepository = codeValueRepository;
this.commandProcessingService = commandProcessingService; this.commandProcessingService = commandProcessingService;
this.calendarInstanceRepository = calendarInstanceRepository; this.calendarInstanceRepository = calendarInstanceRepository;
this.configurationDomainService = configurationDomainService;
} }
private CommandProcessingResult createGroupingType(final JsonCommand command, final GroupTypes groupingType, final Long centerId) { private CommandProcessingResult createGroupingType(final JsonCommand command, final GroupTypes groupingType, final Long centerId) {
@@ -157,6 +161,11 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
boolean rollbackTransaction = false; boolean rollbackTransaction = false;
if (newGroup.isActive()) { if (newGroup.isActive()) {
// validate Group creation rules for Group
if (newGroup.isGroup()) {
validateGroupRulesBeforeActivation(newGroup);
}
if (newGroup.isCenter()) { if (newGroup.isCenter()) {
final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateCenter(null).build(); final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateCenter(null).build();
rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, currentUser); rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, currentUser);
@@ -226,6 +235,10 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final Group group = this.groupRepository.findOneWithNotFoundDetection(groupId); final Group group = this.groupRepository.findOneWithNotFoundDetection(groupId);
if (group.isGroup()) {
validateGroupRulesBeforeActivation(group);
}
final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate"); final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate");
validateOfficeOpeningDateisAfterGroupOrCenterOpeningDate(group.getOffice(), group.getGroupLevel(), activationDate); validateOfficeOpeningDateisAfterGroupOrCenterOpeningDate(group.getOffice(), group.getGroupLevel(), activationDate);
@@ -245,6 +258,20 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
} }
} }
private void validateGroupRulesBeforeActivation(final Group group) {
Integer minClients = configurationDomainService.retrieveMinAllowedClientsInGroup();
Integer maxClients = configurationDomainService.retrieveMaxAllowedClientsInGroup();
boolean isGroupClientCountValid = group.isGroupsClientCountWithinMinMaxRange(minClients, maxClients);
if (!isGroupClientCountValid) { throw new GroupMemberCountNotInPermissibleRangeException(group.getId(), minClients, maxClients); }
}
public void validateGroupRulesBeforeClientAssociation(final Group group) {
Integer minClients = configurationDomainService.retrieveMinAllowedClientsInGroup();
Integer maxClients = configurationDomainService.retrieveMaxAllowedClientsInGroup();
boolean isGroupClientCountValid = group.isGroupsClientCountWithinMaxRange(maxClients);
if (!isGroupClientCountValid) { throw new GroupMemberCountNotInPermissibleRangeException(group.getId(), minClients, maxClients); }
}
@Transactional @Transactional
@Override @Override
public CommandProcessingResult updateCenter(final Long centerId, final JsonCommand command) { public CommandProcessingResult updateCenter(final Long centerId, final JsonCommand command) {
@@ -645,6 +672,10 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final Map<String, Object> actualChanges = new HashMap<>(); final Map<String, Object> actualChanges = new HashMap<>();
final List<String> changes = groupForUpdate.associateClients(clientMembers); final List<String> changes = groupForUpdate.associateClients(clientMembers);
if (groupForUpdate.isGroup()) {
validateGroupRulesBeforeClientAssociation(groupForUpdate);
}
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
actualChanges.put(GroupingTypesApiConstants.clientMembersParamName, changes); actualChanges.put(GroupingTypesApiConstants.clientMembersParamName, changes);
} }
@@ -0,0 +1,9 @@
INSERT INTO `c_configuration` (`id`, `name`,`value`, `enabled`, `description`)
VALUES (NULL, 'min-clients-in-group', '5', '0',"Minimum number of Clients that a Group should have");
INSERT INTO `c_configuration` (`id`, `name`, `value`, `enabled`, `description`)
VALUES (NULL, 'max-clients-in-group', '5', '0', "Maximum number of Clients that a Group can have");
ALTER TABLE `m_group_level`
DROP COLUMN `min_clients`,
DROP COLUMN `max_clients`;