/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
Cyclos is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Cyclos is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package nl.strohalm.cyclos.services.groups;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import nl.strohalm.cyclos.access.Permission;
import nl.strohalm.cyclos.dao.accounts.AccountDAO;
import nl.strohalm.cyclos.dao.accounts.AccountLimitLogDAO;
import nl.strohalm.cyclos.dao.accounts.MemberGroupAccountSettingsDAO;
import nl.strohalm.cyclos.dao.accounts.fee.account.AccountFeeDAO;
import nl.strohalm.cyclos.dao.accounts.fee.transaction.TransactionFeeDAO;
import nl.strohalm.cyclos.dao.accounts.transactions.PaymentFilterDAO;
import nl.strohalm.cyclos.dao.customizations.CustomFieldDAO;
import nl.strohalm.cyclos.dao.customizations.CustomizedFileDAO;
import nl.strohalm.cyclos.dao.groups.GroupDAO;
import nl.strohalm.cyclos.dao.members.ElementDAO;
import nl.strohalm.cyclos.dao.members.MemberRecordTypeDAO;
import nl.strohalm.cyclos.entities.Relationship;
import nl.strohalm.cyclos.entities.access.Channel;
import nl.strohalm.cyclos.entities.access.Channel.Credentials;
import nl.strohalm.cyclos.entities.accounts.AccountType;
import nl.strohalm.cyclos.entities.accounts.MemberAccountType;
import nl.strohalm.cyclos.entities.accounts.MemberGroupAccountSettings;
import nl.strohalm.cyclos.entities.accounts.SystemAccountType;
import nl.strohalm.cyclos.entities.accounts.cards.CardType;
import nl.strohalm.cyclos.entities.accounts.fees.account.AccountFee;
import nl.strohalm.cyclos.entities.accounts.fees.transaction.TransactionFee;
import nl.strohalm.cyclos.entities.accounts.guarantees.GuaranteeType;
import nl.strohalm.cyclos.entities.accounts.transactions.PaymentFilter;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferType;
import nl.strohalm.cyclos.entities.customization.documents.Document;
import nl.strohalm.cyclos.entities.customization.fields.AdminCustomField;
import nl.strohalm.cyclos.entities.customization.fields.CustomField;
import nl.strohalm.cyclos.entities.customization.files.CustomizedFile;
import nl.strohalm.cyclos.entities.exceptions.EntityNotFoundException;
import nl.strohalm.cyclos.entities.exceptions.UnexpectedEntityException;
import nl.strohalm.cyclos.entities.groups.AdminGroup;
import nl.strohalm.cyclos.entities.groups.BasicGroupSettings;
import nl.strohalm.cyclos.entities.groups.BasicGroupSettings.PasswordPolicy;
import nl.strohalm.cyclos.entities.groups.BrokerGroup;
import nl.strohalm.cyclos.entities.groups.Group;
import nl.strohalm.cyclos.entities.groups.GroupFilter;
import nl.strohalm.cyclos.entities.groups.GroupQuery;
import nl.strohalm.cyclos.entities.groups.MemberGroup;
import nl.strohalm.cyclos.entities.groups.MemberGroupSettings;
import nl.strohalm.cyclos.entities.groups.OperatorGroup;
import nl.strohalm.cyclos.entities.groups.SystemGroup;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.RegistrationAgreement;
import nl.strohalm.cyclos.entities.members.messages.Message;
import nl.strohalm.cyclos.entities.members.messages.Message.Type;
import nl.strohalm.cyclos.entities.members.messages.MessageCategory;
import nl.strohalm.cyclos.entities.members.records.MemberRecordType;
import nl.strohalm.cyclos.entities.settings.AccessSettings;
import nl.strohalm.cyclos.exceptions.PermissionDeniedException;
import nl.strohalm.cyclos.scheduling.polling.AccountActivationPollingTask;
import nl.strohalm.cyclos.services.access.ChannelServiceLocal;
import nl.strohalm.cyclos.services.accounts.AccountServiceLocal;
import nl.strohalm.cyclos.services.accounts.AccountTypeServiceLocal;
import nl.strohalm.cyclos.services.accounts.BulkUpdateAccountDTO;
import nl.strohalm.cyclos.services.accounts.MemberAccountHandler;
import nl.strohalm.cyclos.services.application.ApplicationServiceLocal;
import nl.strohalm.cyclos.services.customization.AdminCustomFieldServiceLocal;
import nl.strohalm.cyclos.services.customization.MemberCustomFieldServiceLocal;
import nl.strohalm.cyclos.services.customization.OperatorCustomFieldServiceLocal;
import nl.strohalm.cyclos.services.elements.ElementServiceLocal;
import nl.strohalm.cyclos.services.fetch.FetchServiceLocal;
import nl.strohalm.cyclos.services.permissions.PermissionServiceLocal;
import nl.strohalm.cyclos.services.settings.SettingsServiceLocal;
import nl.strohalm.cyclos.services.sms.ISmsContext;
import nl.strohalm.cyclos.utils.PropertyHelper;
import nl.strohalm.cyclos.utils.RelationshipHelper;
import nl.strohalm.cyclos.utils.TimePeriod;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.validation.GeneralValidation;
import nl.strohalm.cyclos.utils.validation.InvalidError;
import nl.strohalm.cyclos.utils.validation.PropertyValidation;
import nl.strohalm.cyclos.utils.validation.RequiredError;
import nl.strohalm.cyclos.utils.validation.RequiredValidation;
import nl.strohalm.cyclos.utils.validation.ValidationError;
import nl.strohalm.cyclos.utils.validation.Validator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
/**
* Implementation for group service.
* @author rafael
* @author luis
* @author Jefferson Magno
*/
public class GroupServiceImpl implements GroupServiceLocal {
/**
* After group creation its nature never change.
* @author ameyer
*/
private final class ChangeNatureValidation implements PropertyValidation {
private static final long serialVersionUID = 1L;
@Override
public ValidationError validate(final Object object, final Object property, final Object value) {
Group group = (Group) object;
if (group.isPersistent()) {
Group current = load(group.getId());
Group.Nature nature = (Group.Nature) value;
if (nature != current.getNature()) {
return new ValidationError("group.invalidNature");
}
}
return null;
}
}
/**
* A custom validation that process the given validation only when the member group is set to expire members after a given time period
* @author luis
*/
private final class ExpirationValidation implements PropertyValidation {
private static final long serialVersionUID = 7237205242667756245L;
private final PropertyValidation validation;
private ExpirationValidation(final PropertyValidation validation) {
this.validation = validation;
}
@Override
public ValidationError validate(final Object object, final Object property, final Object value) {
final MemberGroup group = (MemberGroup) object;
final TimePeriod expireMembersAfter = group.getMemberSettings().getExpireMembersAfter();
if (expireMembersAfter != null && expireMembersAfter.getNumber() > 0) {
return validation.validate(object, property, value);
}
return null;
}
}
/**
* Ensures that a password policy is consistent with the access settings
* @author luis
*/
private final class PasswordPolicyValidation implements PropertyValidation {
private static final long serialVersionUID = 5513735809903251255L;
@Override
public ValidationError validate(final Object object, final Object property, final Object value) {
final PasswordPolicy policy = (PasswordPolicy) value;
if (policy == null) {
return null;
}
final Group group = (Group) object;
final AccessSettings accessSettings = settingsService.getAccessSettings();
final boolean virtualKeyboard = accessSettings.isVirtualKeyboard();
final boolean numericPassword = accessSettings.isNumericPassword();
if (policy == PasswordPolicy.AVOID_OBVIOUS_LETTERS_NUMBERS_SPECIAL && virtualKeyboard) {
// Special chars cannot be typed with the virtual keyboard
return new ValidationError("group.error.passwordPolicySpecialVirtualKeyboard");
} else if (group.getNature() != Group.Nature.ADMIN && policy.isForceCharacters() && numericPassword) {
// Admin groups don't use this, but ensure for other groups that if numeric password, there are no characters enforcements
return new ValidationError("group.error.passwordPolicyNumeric");
}
return null;
}
}
private final class PasswordTrialsValidation implements PropertyValidation {
private static final long serialVersionUID = -5445950747956604765L;
@Override
public ValidationError validate(final Object object, final Object property, final Object value) {
final Group group = (Group) object;
if (((Integer) value == 0) && (group.getBasicSettings().getMaxPasswordWrongTries() > 0)) {
return new InvalidError();
}
return null;
}
}
private final class PINBlockTimeValidation implements PropertyValidation {
private static final long serialVersionUID = 7237205242667756245L;
private final PropertyValidation validation;
private PINBlockTimeValidation(final PropertyValidation validation) {
this.validation = validation;
}
@Override
public ValidationError validate(final Object object, final Object property, final Object value) {
final MemberGroup group = (MemberGroup) object;
final TimePeriod expireMembersAfter = group.getMemberSettings().getPinBlockTimeAfterMaxTries();
if (expireMembersAfter != null && expireMembersAfter.getNumber() > 0) {
return validation.validate(object, property, value);
}
return null;
}
}
private final class PinTrialsValidation implements PropertyValidation {
private static final long serialVersionUID = -5445950747956604765L;
@Override
public ValidationError validate(final Object object, final Object property, final Object value) {
final MemberGroup group = (MemberGroup) object;
if ((value == null || (Integer) value == 0) && (group.getMemberSettings().getMaxPinWrongTries() > 0)) {
return new InvalidError();
}
return null;
}
}
private static final Relationship[] FETCH_TO_KEEP_DATA = { Group.Relationships.PERMISSIONS, Group.Relationships.TRANSFER_TYPES, Group.Relationships.CONVERSION_SIMULATION_TTS, BrokerGroup.Relationships.BROKER_CONVERSION_SIMULATION_TTS, SystemGroup.Relationships.DOCUMENTS, SystemGroup.Relationships.MESSAGE_CATEGORIES, BrokerGroup.Relationships.BROKER_DOCUMENTS, BrokerGroup.Relationships.BROKER_MEMBER_RECORD_TYPES, AdminGroup.Relationships.MANAGES_GROUPS, SystemGroup.Relationships.CHARGEBACK_TRANSFER_TYPES, AdminGroup.Relationships.TRANSFER_TYPES_AS_MEMBER, AdminGroup.Relationships.VIEW_INFORMATION_OF, AdminGroup.Relationships.VIEW_CONNECTED_ADMINS_OF, AdminGroup.Relationships.VIEW_ADMIN_RECORD_TYPES, MemberGroup.Relationships.CAN_VIEW_ADS_OF_GROUPS, MemberGroup.Relationships.CAN_VIEW_PROFILE_OF_GROUPS, MemberGroup.Relationships.MANAGED_BY_GROUPS, Group.Relationships.GUARANTEE_TYPES };
private static final Relationship[] FETCH_TO_REMOVE = { Group.Relationships.PAYMENT_FILTERS, AdminGroup.Relationships.CONNECTED_ADMINS_VIEWED_BY, AdminGroup.Relationships.ADMIN_CUSTOM_FIELDS, MemberGroup.Relationships.ACCOUNT_FEES, MemberGroup.Relationships.MANAGED_BY_GROUPS, MemberGroup.Relationships.CUSTOM_FIELDS, MemberGroup.Relationships.FROM_TRANSACTION_FEES, MemberGroup.Relationships.TO_TRANSACTION_FEES, MemberGroup.Relationships.MEMBER_RECORD_TYPES };
private AccountFeeDAO accountFeeDao;
private AccountTypeServiceLocal accountTypeService;
private AccountServiceLocal accountService;
private CustomFieldDAO customFieldDao;
private CustomizedFileDAO customizedFileDao;
private GroupDAO groupDao;
private ElementDAO elementDao;
private ElementServiceLocal elementService;
private MemberGroupAccountSettingsDAO memberGroupAccountSettingsDao;
private MemberRecordTypeDAO memberRecordTypeDao;
private PaymentFilterDAO paymentFilterDao;
private TransactionFeeDAO transactionFeeDao;
private MemberAccountHandler memberAccountHandler;
private PermissionServiceLocal permissionService;
private FetchServiceLocal fetchService;
private AdminCustomFieldServiceLocal adminCustomFieldService;
private MemberCustomFieldServiceLocal memberCustomFieldService;
private OperatorCustomFieldServiceLocal operatorCustomFieldService;
private SettingsServiceLocal settingsService;
private ChannelServiceLocal channelService;
private ApplicationServiceLocal applicationService;
private AccountDAO accountDao;
private AccountLimitLogDAO accountLimitLogDao;
@Override
public int countPendingAccounts(final MemberGroup group, final MemberAccountType accountType) {
return accountService.countPendingActivation(group, accountType);
}
@Override
public SystemGroup findByLoginPageName(final String loginPageName) {
return groupDao.findByLoginPageName(loginPageName);
}
public CustomFieldDAO getCustomFieldDao() {
return customFieldDao;
}
public CustomizedFileDAO getCustomizedFileDao() {
return customizedFileDao;
}
public GroupDAO getGroupDao() {
return groupDao;
}
public MemberAccountHandler getMemberAccountHandler() {
return memberAccountHandler;
}
public MemberGroupAccountSettingsDAO getMemberGroupAccountSettingsDao() {
return memberGroupAccountSettingsDao;
}
public MemberRecordTypeDAO getMemberRecordTypeDao() {
return memberRecordTypeDao;
}
public PaymentFilterDAO getPaymentFilterDao() {
return paymentFilterDao;
}
@Override
public List<MemberGroup> getPossibleInitialGroups(GroupFilter groupFilter) {
if (LoggedUser.hasUser() && LoggedUser.isBroker()) {
BrokerGroup brokerGroup = LoggedUser.group();
brokerGroup = fetchService.fetch(brokerGroup, BrokerGroup.Relationships.POSSIBLE_INITIAL_GROUPS);
return (List<MemberGroup>) brokerGroup.getPossibleInitialGroups();
}
groupFilter = fetchService.fetch(groupFilter, GroupFilter.Relationships.GROUPS);
final GroupQuery query = new GroupQuery();
query.setNatures(Group.Nature.BROKER, Group.Nature.MEMBER);
final List<MemberGroup> initialGroups = new ArrayList<MemberGroup>();
for (Group group : search(query)) {
final MemberGroup memberGroup = (MemberGroup) fetchService.fetch(group);
if (memberGroup.isInitialGroup()) {
initialGroups.add(memberGroup);
}
}
if (groupFilter != null) {
// If group filter is used, only return groups in that group filter
initialGroups.retainAll(groupFilter.getGroups());
}
return initialGroups;
}
@Override
public boolean hasGroupsWhichRequiresSpecialOnPassword() {
final GroupQuery query = new GroupQuery();
query.setStatus(Group.Status.NORMAL);
for (final Group group : search(query)) {
if (group.getBasicSettings().getPasswordPolicy() == PasswordPolicy.AVOID_OBVIOUS_LETTERS_NUMBERS_SPECIAL) {
return true;
}
}
return false;
}
@Override
public boolean hasMemberGroupsWhichEnforcesCharactersOnPassword() {
final GroupQuery query = new GroupQuery();
query.setStatus(Group.Status.NORMAL);
query.setNatures(Group.Nature.MEMBER, Group.Nature.BROKER);
for (final Group group : search(query)) {
if (group.getBasicSettings().getPasswordPolicy().isForceCharacters()) {
return true;
}
}
return false;
}
@Override
public <G extends Group> G insert(G group, final G baseGroup) {
if (baseGroup != null) {
// Copy settings from base group
group = copyBaseGroupSettings(group, baseGroup);
} else {
group.setBasicSettings(new BasicGroupSettings());
if (group instanceof MemberGroup) {
MemberGroup memberGroup = (MemberGroup) group;
memberGroup.setMemberSettings(new MemberGroupSettings());
}
}
// Validate and save
validate(group);
group = save(group);
// Copy inverse collections from base group
if (baseGroup != null) {
copyInverseCollections(group, baseGroup);
}
if (group instanceof MemberGroup) {
MemberGroup memberGroup = (MemberGroup) group;
// Add permission to admin group over the member group
AdminGroup adminGroup = LoggedUser.group();
adminGroup = fetchService.fetch(adminGroup, AdminGroup.Relationships.MANAGES_GROUPS);
final Collection<MemberGroup> managesGroups = adminGroup.getManagesGroups();
managesGroups.add(memberGroup);
groupDao.update(adminGroup);
}
clearRelatedCaches(group);
return group;
}
@Override
public MemberGroupAccountSettings insertAccountSettings(final MemberGroupAccountSettings settings) {
// Check if the admin group has the permission to manage the member group
final MemberGroup memberGroup = fetchService.fetch(settings.getGroup());
AdminGroup adminGroup = LoggedUser.group();
adminGroup = fetchService.fetch(adminGroup, AdminGroup.Relationships.MANAGES_GROUPS);
final Collection<MemberGroup> managesGroups = adminGroup.getManagesGroups();
if (!managesGroups.contains(memberGroup)) {
throw new PermissionDeniedException();
}
validate(settings);
// Check if the account type isn't already related to the group
try {
memberGroupAccountSettingsDao.load(settings.getGroup().getId(), settings.getAccountType().getId());
throw new UnexpectedEntityException();
} catch (final EntityNotFoundException e) {
// Ok, the account is not related to the group, so we can go on
}
final MemberAccountType currentDefault = accountTypeService.getDefault(memberGroup);
if (currentDefault == null) {
// When there's no current default, set this one as default
settings.setDefault(true);
} else if (settings.isDefault()) {
// When there was a default already and this one is marked as default, unmark the previous one
final MemberGroupAccountSettings defaultSettings = memberGroupAccountSettingsDao.load(memberGroup.getId(), currentDefault.getId());
defaultSettings.setDefault(false);
memberGroupAccountSettingsDao.update(defaultSettings);
}
final MemberGroupAccountSettings saved = memberGroupAccountSettingsDao.insert(settings);
// When inserting an account to an inactive group, activate it
if (!memberGroup.isActive()) {
memberGroup.setActive(true);
groupDao.update(memberGroup);
elementDao.activateMembersOfGroup(memberGroup);
}
// create and mark the member accounts for activation so MemberAccountHandler.ActivateAccountThread can pick them up
BulkUpdateAccountDTO dto = new BulkUpdateAccountDTO();
dto.setGroup(saved.getGroup());
dto.setType(saved.getAccountType());
dto.setCreditLimit(saved.getDefaultCreditLimit());
dto.setUpperCreditLimit(saved.getDefaultUpperCreditLimit());
accountDao.markForActivation(dto);
applicationService.awakePollingTaskOnTransactionCommit(AccountActivationPollingTask.class);
return saved;
}
@Override
public List<OperatorGroup> iterateOperatorGroups(final MemberGroup memberGroup) {
return groupDao.iterateOperatorGroups(memberGroup);
}
@Override
public <T extends Group> Collection<T> load(final Collection<Long> ids, final Relationship... fetch) {
return groupDao.<T> load(ids, fetch);
}
@Override
public <T extends Group> T load(final Long id, final Relationship... fetch) {
return groupDao.<T> load(id, fetch);
}
@Override
public MemberGroupAccountSettings loadAccountSettings(final long groupId, final long accountTypeId, final Relationship... fetch) {
return memberGroupAccountSettingsDao.load(groupId, accountTypeId, fetch);
}
@Override
public <T extends Group> T reload(final Long id, final Relationship... fetch) {
return groupDao.<T> reload(id, fetch);
}
@Override
public void remove(final Long id) throws EntityNotFoundException {
final Group group = load(id, FETCH_TO_REMOVE);
if (!(group instanceof OperatorGroup)) {
removeFromInverseCollections(group);
}
// Ensure the permissions cache for this group is evicted
permissionService.evictCache(group);
groupDao.delete(id);
}
@Override
public void removeAccountTypeRelationship(final MemberGroup group, final MemberAccountType type) {
// Remove the account settings
memberGroupAccountSettingsDao.delete(group.getId(), type.getId());
// mark all accounts for deactivation
accountDao.markForDeactivation(type, group);
applicationService.awakePollingTaskOnTransactionCommit(AccountActivationPollingTask.class);
}
@Override
public List<? extends Group> search(final GroupQuery query) {
if (!query.isIgnoreManagedBy() && LoggedUser.hasUser() && LoggedUser.isAdministrator()) {
final AdminGroup adminGroup = LoggedUser.group();
query.setManagedBy(adminGroup);
}
return groupDao.search(query);
}
public void setAccountDao(final AccountDAO accountDao) {
this.accountDao = accountDao;
}
public void setAccountFeeDao(final AccountFeeDAO accountFeeDao) {
this.accountFeeDao = accountFeeDao;
}
public void setAccountLimitLogDao(final AccountLimitLogDAO accountLimitLogDao) {
this.accountLimitLogDao = accountLimitLogDao;
}
public void setAccountServiceLocal(final AccountServiceLocal accountService) {
this.accountService = accountService;
}
public void setAccountTypeServiceLocal(final AccountTypeServiceLocal accountTypeService) {
this.accountTypeService = accountTypeService;
}
public void setAdminCustomFieldServiceLocal(final AdminCustomFieldServiceLocal adminCustomFieldService) {
this.adminCustomFieldService = adminCustomFieldService;
}
public void setApplicationServiceLocal(final ApplicationServiceLocal applicationService) {
this.applicationService = applicationService;
}
public void setChannelServiceLocal(final ChannelServiceLocal channelService) {
this.channelService = channelService;
}
public void setCustomFieldDao(final CustomFieldDAO customFieldDao) {
this.customFieldDao = customFieldDao;
}
public void setCustomizedFileDao(final CustomizedFileDAO customizedFileDao) {
this.customizedFileDao = customizedFileDao;
}
public void setElementDao(final ElementDAO elementDao) {
this.elementDao = elementDao;
}
public void setElementServiceLocal(final ElementServiceLocal elementService) {
this.elementService = elementService;
}
public void setFetchServiceLocal(final FetchServiceLocal fetchService) {
this.fetchService = fetchService;
}
public void setGroupDao(final GroupDAO groupDao) {
this.groupDao = groupDao;
}
public void setMemberAccountHandler(final MemberAccountHandler memberAccountHandler) {
this.memberAccountHandler = memberAccountHandler;
}
public void setMemberCustomFieldServiceLocal(final MemberCustomFieldServiceLocal memberCustomFieldService) {
this.memberCustomFieldService = memberCustomFieldService;
}
public void setMemberGroupAccountSettingsDao(final MemberGroupAccountSettingsDAO memberGroupAccountSettingsDao) {
this.memberGroupAccountSettingsDao = memberGroupAccountSettingsDao;
}
public void setMemberRecordTypeDao(final MemberRecordTypeDAO memberRecordTypeDao) {
this.memberRecordTypeDao = memberRecordTypeDao;
}
public void setOperatorCustomFieldServiceLocal(final OperatorCustomFieldServiceLocal operatorCustomFieldService) {
this.operatorCustomFieldService = operatorCustomFieldService;
}
public void setPaymentFilterDao(final PaymentFilterDAO paymentFilterDao) {
this.paymentFilterDao = paymentFilterDao;
}
@Override
public <G extends Group> G setPermissions(final GroupPermissionsDTO<G> dto) {
G group = fetchService.fetch(dto.getGroup());
dto.update(group);
group = groupDao.update(group);
permissionService.evictCache(group);
return group;
}
public void setPermissionServiceLocal(final PermissionServiceLocal permissionService) {
this.permissionService = permissionService;
}
public void setSettingsServiceLocal(final SettingsServiceLocal settingsService) {
this.settingsService = settingsService;
}
public void setTransactionFeeDao(final TransactionFeeDAO transactionFeeDao) {
this.transactionFeeDao = transactionFeeDao;
}
@Override
public <G extends Group> G update(G group, final boolean forceMembersToAcceptAgreement) {
validate(group);
final Group currentGroup = load(group.getId());
if (group.isRemoved()) { // only take into account name and description
currentGroup.setName(group.getName());
currentGroup.setDescription(group.getDescription());
return groupDao.update(group);
}
// Those variables are only used for member groups
boolean wasTPUsed = false;
boolean wasActive = false;
boolean isTPUsed = false;
RegistrationAgreement oldAgreement = null;
if (group instanceof MemberGroup) {
// Member groups have special handling
MemberGroup memberGroup = (MemberGroup) group;
final MemberGroup currentMemberGroup = (MemberGroup) load(memberGroup.getId());
oldAgreement = fetchService.fetch(currentMemberGroup.getRegistrationAgreement());
try {
wasTPUsed = currentMemberGroup.getBasicSettings().getTransactionPassword().isUsed();
} catch (final Exception e) {
wasTPUsed = false;
}
try {
isTPUsed = memberGroup.getBasicSettings().getTransactionPassword().isUsed();
} catch (final Exception e) {
isTPUsed = false;
}
wasActive = currentMemberGroup.isActive();
}
group = save(group);
if (group instanceof MemberGroup) {
// Member groups have special handling
MemberGroup memberGroup = (MemberGroup) group;
// When the transaction password was not used and now is, or was used and now is not, update the accounts
if ((wasTPUsed && !isTPUsed) || (!wasTPUsed && isTPUsed)) {
memberGroup = fetchService.reload(memberGroup, MemberGroup.Relationships.ACCOUNT_SETTINGS);
for (final MemberGroupAccountSettings mgas : memberGroup.getAccountSettings()) {
mgas.setTransactionPasswordRequired(isTPUsed);
memberGroupAccountSettingsDao.update(mgas);
}
}
// When the group is becoming active, activate all members on it
if (!wasActive && memberGroup.isActive()) {
elementDao.activateMembersOfGroup(memberGroup);
}
// Check if the registration agreement has changed
final RegistrationAgreement registrationAgreement = fetchService.fetch(memberGroup.getRegistrationAgreement());
if (registrationAgreement != null && !registrationAgreement.equals(oldAgreement)) {
// It did change. When not forcing members to accept it again, we should create all accepting logs
if (!forceMembersToAcceptAgreement) {
elementService.createAgreementForAllMembers(registrationAgreement, memberGroup);
}
}
}
return group;
}
@Override
public MemberGroupAccountSettings updateAccountSettings(final MemberGroupAccountSettings settings, final boolean updateAccountLimits) {
// Check if the admin group has the permission to manage the member group
final MemberGroup memberGroup = settings.getGroup();
AdminGroup adminGroup = LoggedUser.group();
adminGroup = fetchService.fetch(adminGroup, AdminGroup.Relationships.MANAGES_GROUPS);
final Collection<MemberGroup> managesGroups = adminGroup.getManagesGroups();
if (!managesGroups.contains(memberGroup)) {
throw new PermissionDeniedException();
}
validate(settings);
final MemberAccountType currentDefault = accountTypeService.getDefault(memberGroup);
if (currentDefault == null || currentDefault.equals(settings.getAccountType())) {
// When there's no current default, or is this one, set as default
settings.setDefault(true);
} else if (settings.isDefault()) {
// When there was a default already and this one is marked as default, unmark the previous one
final MemberGroupAccountSettings defaultSettings = memberGroupAccountSettingsDao.load(memberGroup.getId(), currentDefault.getId());
defaultSettings.setDefault(false);
memberGroupAccountSettingsDao.update(defaultSettings);
}
final MemberGroupAccountSettings saved = memberGroupAccountSettingsDao.update(settings);
if (updateAccountLimits) {
BulkUpdateAccountDTO dto = new BulkUpdateAccountDTO();
dto.setType(saved.getAccountType());
dto.setGroup(settings.getGroup());
dto.setCreditLimit(saved.getDefaultCreditLimit());
dto.setUpperCreditLimit(saved.getDefaultUpperCreditLimit());
accountDao.bulkUpdateCreditLimites(dto);
accountLimitLogDao.insertAfterCreditLimitBulkUpdate(saved.getAccountType(), settings.getGroup());
}
return saved;
}
@Override
public boolean usesPin(MemberGroup group) {
group = fetchService.fetch(group, MemberGroup.Relationships.CHANNELS);
final Collection<Channel> channels = group.getChannels();
for (final Channel channel : channels) {
if (channel.getCredentials() == Credentials.PIN) {
return true;
}
}
return false;
}
@Override
public void validate(final Group group) {
if (group.isTransient()) {
getInsertValidator().validate(group);
} else if (Group.Status.REMOVED.equals(group.getStatus())) {
getRemovedValidator().validate(group);
} else if (group instanceof AdminGroup) {
getAdminValidator().validate(group);
} else if (group instanceof BrokerGroup) {
getBrokerValidator().validate(group);
} else if (group instanceof MemberGroup) {
getMemberValidator().validate(group);
} else if (group instanceof OperatorGroup) {
getOperatorValidator().validate(group);
}
}
@Override
public void validate(final MemberGroupAccountSettings settings) {
getAccountSettingsValidator().validate(settings);
}
private void clearRelatedCaches(final Group group) {
if (group instanceof AdminGroup) {
adminCustomFieldService.clearCache();
} else if (group instanceof MemberGroup) {
memberCustomFieldService.clearCache();
} else if (group instanceof OperatorGroup) {
operatorCustomFieldService.clearCache();
}
accountTypeService.clearCache();
}
private <G extends Group> G copyBaseGroupSettings(final G group, G baseGroup) {
baseGroup = fetchService.fetch(baseGroup, Group.Relationships.PAYMENT_FILTERS, Group.Relationships.PERMISSIONS, Group.Relationships.TRANSFER_TYPES, SystemGroup.Relationships.DOCUMENTS, Group.Relationships.CUSTOMIZED_FILES, SystemGroup.Relationships.MESSAGE_CATEGORIES, AdminGroup.Relationships.TRANSFER_TYPES_AS_MEMBER, AdminGroup.Relationships.MANAGES_GROUPS, AdminGroup.Relationships.VIEW_INFORMATION_OF, AdminGroup.Relationships.VIEW_CONNECTED_ADMINS_OF, AdminGroup.Relationships.CONNECTED_ADMINS_VIEWED_BY, AdminGroup.Relationships.ADMIN_CUSTOM_FIELDS, MemberGroup.Relationships.ACCOUNT_SETTINGS, MemberGroup.Relationships.CAN_VIEW_PROFILE_OF_GROUPS, MemberGroup.Relationships.CAN_VIEW_ADS_OF_GROUPS, MemberGroup.Relationships.CAN_VIEW_INFORMATION_OF, MemberGroup.Relationships.ACCOUNT_FEES, MemberGroup.Relationships.MANAGED_BY_GROUPS, MemberGroup.Relationships.CUSTOM_FIELDS, MemberGroup.Relationships.FROM_TRANSACTION_FEES, MemberGroup.Relationships.TO_TRANSACTION_FEES, BrokerGroup.Relationships.TRANSFER_TYPES_AS_MEMBER, BrokerGroup.Relationships.BROKER_DOCUMENTS, BrokerGroup.Relationships.BROKER_CAN_VIEW_INFORMATION_OF, OperatorGroup.Relationships.MEMBER, OperatorGroup.Relationships.MAX_AMOUNT_PER_DAY_BY_TRANSFER_TYPE, OperatorGroup.Relationships.CAN_VIEW_INFORMATION_OF, Group.Relationships.GUARANTEE_TYPES, MemberGroup.Relationships.CAN_ISSUE_CERTIFICATION_TO_GROUPS, MemberGroup.Relationships.CAN_BUY_WITH_PAYMENT_OBLIGATIONS_FROM_GROUPS, MemberGroup.Relationships.CARD_TYPE);
// Copy permissions
final Set<Permission> permissions = new HashSet<Permission>(baseGroup.getPermissions());
group.setPermissions(permissions);
// Status
group.setStatus(baseGroup.getStatus());
// Transfer types
final List<TransferType> transferTypes = new ArrayList<TransferType>(baseGroup.getTransferTypes());
group.setTransferTypes(transferTypes);
// Conversion Simulation TTs
final List<TransferType> conversionSimulationTTs = new ArrayList<TransferType>(baseGroup.getConversionSimulationTTs());
group.setConversionSimulationTTs(conversionSimulationTTs);
// Basic settings
if (!(group instanceof OperatorGroup)) {
group.setBasicSettings((BasicGroupSettings) baseGroup.getBasicSettings().clone());
}
// Guarantee types
final List<GuaranteeType> guaranteeTypes = new ArrayList<GuaranteeType>(baseGroup.getGuaranteeTypes());
group.setGuaranteeTypes(guaranteeTypes);
if (group instanceof SystemGroup) {
final SystemGroup systemGroup = (SystemGroup) group;
final SystemGroup baseSystemGroup = (SystemGroup) baseGroup;
// Documents
final List<Document> documents = new ArrayList<Document>(baseSystemGroup.getDocuments());
systemGroup.setDocuments(documents);
// Message categories
final List<MessageCategory> messageCategories = new ArrayList<MessageCategory>(baseSystemGroup.getMessageCategories());
systemGroup.setMessageCategories(messageCategories);
// Chargeback transfer types
final List<TransferType> chargebackTransferTypes = new ArrayList<TransferType>(baseSystemGroup.getChargebackTransferTypes());
systemGroup.setChargebackTransferTypes(chargebackTransferTypes);
}
if (group instanceof AdminGroup) {
final AdminGroup adminGroup = (AdminGroup) group;
final AdminGroup baseAdminGroup = (AdminGroup) baseGroup;
// Transfer types as member
final List<TransferType> transferTypesAsMember = new ArrayList<TransferType>(baseAdminGroup.getTransferTypesAsMember());
adminGroup.setTransferTypesAsMember(transferTypesAsMember);
// Manages groups
final List<MemberGroup> managesGroups = new ArrayList<MemberGroup>(baseAdminGroup.getManagesGroups());
adminGroup.setManagesGroups(managesGroups);
// View information of
final List<SystemAccountType> viewInformationOf = new ArrayList<SystemAccountType>(baseAdminGroup.getViewInformationOf());
adminGroup.setViewInformationOf(viewInformationOf);
// View connected admins of
final List<AdminGroup> viewConnectedAdminsOf = new ArrayList<AdminGroup>(baseAdminGroup.getViewConnectedAdminsOf());
adminGroup.setViewConnectedAdminsOf(viewConnectedAdminsOf);
// View member record types
final List<MemberRecordType> viewMemberRecordTypes = new ArrayList<MemberRecordType>(baseAdminGroup.getViewMemberRecordTypes());
adminGroup.setViewMemberRecordTypes(viewMemberRecordTypes);
// Create member record types
final List<MemberRecordType> createMemberRecordTypes = new ArrayList<MemberRecordType>(baseAdminGroup.getCreateMemberRecordTypes());
adminGroup.setCreateMemberRecordTypes(createMemberRecordTypes);
// Modify member record types
final List<MemberRecordType> modifyMemberRecordTypes = new ArrayList<MemberRecordType>(baseAdminGroup.getModifyMemberRecordTypes());
adminGroup.setModifyMemberRecordTypes(modifyMemberRecordTypes);
// Delete member record types
final List<MemberRecordType> deleteMemberRecordTypes = new ArrayList<MemberRecordType>(baseAdminGroup.getDeleteMemberRecordTypes());
adminGroup.setDeleteMemberRecordTypes(deleteMemberRecordTypes);
// View admin record types
final List<MemberRecordType> viewAdminRecordTypes = new ArrayList<MemberRecordType>(baseAdminGroup.getViewAdminRecordTypes());
adminGroup.setViewAdminRecordTypes(viewAdminRecordTypes);
// Create admin record types
final List<MemberRecordType> createAdminRecordTypes = new ArrayList<MemberRecordType>(baseAdminGroup.getCreateAdminRecordTypes());
adminGroup.setCreateAdminRecordTypes(createAdminRecordTypes);
// Modify admin record types
final List<MemberRecordType> modifyAdminRecordTypes = new ArrayList<MemberRecordType>(baseAdminGroup.getModifyAdminRecordTypes());
adminGroup.setModifyAdminRecordTypes(modifyAdminRecordTypes);
// Delete admin record types
final List<MemberRecordType> deleteAdminRecordTypes = new ArrayList<MemberRecordType>(baseAdminGroup.getDeleteAdminRecordTypes());
adminGroup.setDeleteAdminRecordTypes(deleteAdminRecordTypes);
}
if (group instanceof MemberGroup) {
final MemberGroup memberGroup = (MemberGroup) group;
final MemberGroup baseMemberGroup = (MemberGroup) baseGroup;
// Member group settings
memberGroup.setMemberSettings((MemberGroupSettings) baseMemberGroup.getMemberSettings().clone());
// Copy the e-mail validation
memberGroup.getMemberSettings().setEmailValidation(new HashSet<MemberGroupSettings.EmailValidation>(baseMemberGroup.getMemberSettings().getEmailValidation()));
// Initial group & agreement
memberGroup.setInitialGroup(baseMemberGroup.isInitialGroup());
memberGroup.setRegistrationAgreement(baseMemberGroup.getRegistrationAgreement());
// Active
memberGroup.setActive(baseMemberGroup.isActive());
// Can view profile of groups
final List<MemberGroup> canViewProfileOfGroups = new ArrayList<MemberGroup>(baseMemberGroup.getCanViewProfileOfGroups());
canViewProfileOfGroups.add(baseMemberGroup);
memberGroup.setCanViewProfileOfGroups(canViewProfileOfGroups);
// Can view ads of groups
final List<MemberGroup> canViewAdsOfGroups = new ArrayList<MemberGroup>(baseMemberGroup.getCanViewAdsOfGroups());
canViewAdsOfGroups.add(baseMemberGroup);
memberGroup.setCanViewAdsOfGroups(canViewAdsOfGroups);
// Can view information of
final List<AccountType> canViewInformationOf = new ArrayList<AccountType>(baseMemberGroup.getCanViewInformationOf());
memberGroup.setCanViewInformationOf(canViewInformationOf);
// Default mail messages
final List<Message.Type> defaultMailMessages = new ArrayList<Message.Type>(baseMemberGroup.getDefaultMailMessages());
memberGroup.setDefaultMailMessages(defaultMailMessages);
// SMS messages
final List<Message.Type> smsMessages = new ArrayList<Message.Type>(baseMemberGroup.getSmsMessages());
memberGroup.setSmsMessages(smsMessages);
// Default SMS messages
final List<Message.Type> defaultSmsMessages = new ArrayList<Message.Type>(baseMemberGroup.getDefaultSmsMessages());
memberGroup.setDefaultSmsMessages(defaultSmsMessages);
// Channels
final List<Channel> channels = new ArrayList<Channel>(baseMemberGroup.getChannels());
memberGroup.setChannels(channels);
// Default channels
final List<Channel> defaultChannels = new ArrayList<Channel>(baseMemberGroup.getDefaultChannels());
memberGroup.setDefaultChannels(defaultChannels);
// Request payment by channels
final List<Channel> requestPaymentByChannels = new ArrayList<Channel>(baseMemberGroup.getRequestPaymentByChannels());
memberGroup.setRequestPaymentByChannels(requestPaymentByChannels);
// Can issue certification to groups
final List<MemberGroup> canIssueCertificationToGroups = new ArrayList<MemberGroup>(baseMemberGroup.getCanIssueCertificationToGroups());
memberGroup.setCanIssueCertificationToGroups(canIssueCertificationToGroups);
// Can buy with payment obligations from groups
final List<MemberGroup> canBuyWithPaymentObligationsFromGroups = new ArrayList<MemberGroup>(baseMemberGroup.getCanBuyWithPaymentObligationsFromGroups());
memberGroup.setCanBuyWithPaymentObligationsFromGroups(canBuyWithPaymentObligationsFromGroups);
// Card type
final CardType cardType = baseMemberGroup.getCardType();
memberGroup.setCardType(cardType);
}
if (group instanceof BrokerGroup) {
final BrokerGroup brokerGroup = (BrokerGroup) group;
final BrokerGroup baseBrokerGroup = (BrokerGroup) baseGroup;
// Transfer types as member
final List<TransferType> transferTypesAsMember = new ArrayList<TransferType>(baseBrokerGroup.getTransferTypesAsMember());
brokerGroup.setTransferTypesAsMember(transferTypesAsMember);
final List<TransferType> brokerConversionSimulationTTs = new ArrayList<TransferType>(baseBrokerGroup.getBrokerConversionSimulationTTs());
brokerGroup.setBrokerConversionSimulationTTs(brokerConversionSimulationTTs);
// Broker documents
final List<Document> brokerDocuments = new ArrayList<Document>(baseBrokerGroup.getBrokerDocuments());
brokerGroup.setBrokerDocuments(brokerDocuments);
// Broker can view information of
final List<AccountType> brokerCanViewInformationOf = new ArrayList<AccountType>(baseBrokerGroup.getBrokerCanViewInformationOf());
brokerGroup.setBrokerCanViewInformationOf(brokerCanViewInformationOf);
// Broker view member record types
final List<MemberRecordType> brokerMemberRecordTypes = new ArrayList<MemberRecordType>(baseBrokerGroup.getBrokerMemberRecordTypes());
brokerGroup.setBrokerMemberRecordTypes(brokerMemberRecordTypes);
// Broker create member record types
final List<MemberRecordType> brokerCreateMemberRecordTypes = new ArrayList<MemberRecordType>(baseBrokerGroup.getBrokerCreateMemberRecordTypes());
brokerGroup.setBrokerCreateMemberRecordTypes(brokerCreateMemberRecordTypes);
// Broker modify member record types
final List<MemberRecordType> brokerModifyMemberRecordTypes = new ArrayList<MemberRecordType>(baseBrokerGroup.getBrokerModifyMemberRecordTypes());
brokerGroup.setBrokerModifyMemberRecordTypes(brokerModifyMemberRecordTypes);
// Broker delete member record types
final List<MemberRecordType> brokerDeleteMemberRecordTypes = new ArrayList<MemberRecordType>(baseBrokerGroup.getBrokerDeleteMemberRecordTypes());
brokerGroup.setBrokerDeleteMemberRecordTypes(brokerDeleteMemberRecordTypes);
// Possible initial groups
final List<MemberGroup> possibleInitialGroups = new ArrayList<MemberGroup>(baseBrokerGroup.getPossibleInitialGroups());
brokerGroup.setPossibleInitialGroups(possibleInitialGroups);
}
if (group instanceof OperatorGroup) {
final OperatorGroup operatorGroup = (OperatorGroup) group;
final OperatorGroup baseOperatorGroup = (OperatorGroup) baseGroup;
// Member
operatorGroup.setMember(baseOperatorGroup.getMember());
// Max amount per day by transfer type
final Map<TransferType, BigDecimal> maxAmountPerDayByTransferType = new HashMap<TransferType, BigDecimal>(baseOperatorGroup.getMaxAmountPerDayByTransferType());
operatorGroup.setMaxAmountPerDayByTransferType(maxAmountPerDayByTransferType);
// Can view information of
final List<AccountType> canViewInformationOf = new ArrayList<AccountType>(baseOperatorGroup.getCanViewInformationOf());
operatorGroup.setCanViewInformationOf(canViewInformationOf);
}
return group;
}
@SuppressWarnings("unchecked")
private Group copyInverseCollections(final Group group, final Group baseGroup) {
// Payment filters
final Collection<PaymentFilter> paymentFilters = baseGroup.getPaymentFilters();
for (final PaymentFilter paymentFilter : paymentFilters) {
final Collection<Group> groups = (Collection<Group>) paymentFilter.getGroups();
groups.add(group);
paymentFilterDao.update(paymentFilter);
}
// Customized files
final Collection<CustomizedFile> customizedFiles = baseGroup.getCustomizedFiles();
for (final CustomizedFile baseGroupCustomizedFile : customizedFiles) {
final CustomizedFile customizedFile = (CustomizedFile) baseGroupCustomizedFile.clone();
customizedFile.setId(null);
customizedFile.setGroup(group);
customizedFileDao.insert(customizedFile);
}
// Record types
final Collection<MemberRecordType> recordTypes = baseGroup.getMemberRecordTypes();
for (final MemberRecordType recordType : recordTypes) {
recordType.getGroups().add(group);
memberRecordTypeDao.update(recordType);
}
if (group instanceof AdminGroup) {
final AdminGroup adminGroup = (AdminGroup) group;
final AdminGroup baseAdminGroup = (AdminGroup) baseGroup;
// Connected admins viewed by
final Collection<AdminGroup> connectedAdminsViewedBy = baseAdminGroup.getConnectedAdminsViewedBy();
for (final AdminGroup viewerAdminGroup : connectedAdminsViewedBy) {
viewerAdminGroup.getViewConnectedAdminsOf().add(adminGroup);
groupDao.update(viewerAdminGroup);
}
// Admin custom fields
final Collection<AdminCustomField> adminCustomFields = baseAdminGroup.getAdminCustomFields();
for (final AdminCustomField adminCustomField : adminCustomFields) {
adminCustomField.getGroups().add(adminGroup);
customFieldDao.update(adminCustomField);
}
} else if (group instanceof MemberGroup) {
final MemberGroup memberGroup = (MemberGroup) group;
final MemberGroup baseMemberGroup = (MemberGroup) baseGroup;
// Managed by groups
final Collection<AdminGroup> managedByGroups = baseMemberGroup.getManagedByGroups();
for (final AdminGroup adminGroup : managedByGroups) {
adminGroup.getManagesGroups().add(memberGroup);
groupDao.update(adminGroup);
}
// Account settings
final Collection<MemberGroupAccountSettings> baseMemberGroupAccountSettings = baseMemberGroup.getAccountSettings();
for (final MemberGroupAccountSettings baseAccountSettings : baseMemberGroupAccountSettings) {
final MemberGroupAccountSettings accountSettings = (MemberGroupAccountSettings) baseAccountSettings.clone();
accountSettings.setId(null);
accountSettings.setGroup(memberGroup);
insertAccountSettings(accountSettings);
}
// Account fees
final Collection<AccountFee> accountFees = baseMemberGroup.getAccountFees();
for (final AccountFee accountFee : accountFees) {
final Collection<MemberGroup> groups = accountFee.getGroups();
groups.add(memberGroup);
accountFeeDao.update(accountFee);
}
// Member custom fields and general remark custom fields)
final Collection<CustomField> customFields = baseMemberGroup.getCustomFields();
for (final CustomField customField : customFields) {
// Get the groups using reflection
final Collection<MemberGroup> groups = PropertyHelper.get(customField, "groups");
groups.add(memberGroup);
customFieldDao.update(customField);
}
// From transaction fees
final Collection<TransactionFee> fromTransactionFees = baseMemberGroup.getFromTransactionFees();
for (final TransactionFee transactionFee : fromTransactionFees) {
transactionFee.getFromGroups().add(memberGroup);
transactionFeeDao.update(transactionFee);
}
// To transaction fees
final Collection<TransactionFee> toTransactionFees = baseMemberGroup.getToTransactionFees();
for (final TransactionFee transactionFee : toTransactionFees) {
transactionFee.getToGroups().add(memberGroup);
transactionFeeDao.update(transactionFee);
}
// View profile of
for (final MemberGroup other : memberGroup.getCanViewProfileOfGroups()) {
other.getCanViewProfileOfGroups().add(memberGroup);
}
memberGroup.getCanViewProfileOfGroups().add(memberGroup);
// View ads of
for (final MemberGroup other : memberGroup.getCanViewAdsOfGroups()) {
other.getCanViewAdsOfGroups().add(memberGroup);
}
memberGroup.getCanViewAdsOfGroups().add(memberGroup);
// Group filters
for (final GroupFilter groupFilter : baseMemberGroup.getGroupFilters()) {
groupFilter.getGroups().add(memberGroup);
}
for (final GroupFilter groupFilter : baseMemberGroup.getCanViewGroupFilters()) {
groupFilter.getViewableBy().add(memberGroup);
}
}
return group;
}
private Validator getAccountSettingsValidator() {
final Validator accountSettingsValidator = new Validator("account");
accountSettingsValidator.property("group").displayName("group").required();
accountSettingsValidator.property("accountType").displayName("account type").required();
accountSettingsValidator.property("initialCredit").positive();
accountSettingsValidator.general(new GeneralValidation() {
private static final long serialVersionUID = 1L;
@Override
public ValidationError validate(final Object object) {
final MemberGroupAccountSettings mgas = (MemberGroupAccountSettings) object;
if (mgas.getInitialCreditTransferType() != null) {
TransferType tt = fetchService.fetch(mgas.getInitialCreditTransferType());
BigDecimal minAmount = tt.getMinAmount();
BigDecimal initialCredit = mgas.getInitialCredit();
boolean initialCreditIsPossitive = initialCredit != null && initialCredit.compareTo(new BigDecimal(0)) > 0;
if (initialCreditIsPossitive && minAmount != null && (initialCredit.compareTo(minAmount) < 0)) {
return new ValidationError("group.account.error.minInitialCredit", initialCredit, minAmount);
}
}
return null;
}
});
accountSettingsValidator.property("initialCreditTransferType").add(new PropertyValidation() {
private static final long serialVersionUID = 8284432136349418154L;
@Override
public ValidationError validate(final Object object, final Object name, final Object value) {
final MemberGroupAccountSettings mgas = (MemberGroupAccountSettings) object;
final TransferType tt = fetchService.fetch((TransferType) value, TransferType.Relationships.FROM, TransferType.Relationships.TO);
final BigDecimal initialCredit = mgas.getInitialCredit();
// When there is initial credit, there must be a transfer type
if (initialCredit != null && (initialCredit.compareTo(BigDecimal.ZERO) == 1) && tt == null) {
return new RequiredError();
}
// Must be from system to member
if (tt != null && !(tt.isFromSystem() && !tt.isToSystem())) {
return new InvalidError();
}
return null;
}
});
accountSettingsValidator.property("defaultCreditLimit").required().positive();
accountSettingsValidator.property("defaultUpperCreditLimit").positive();
accountSettingsValidator.property("lowUnits").positive();
accountSettingsValidator.property("lowUnitsMessage").add(new PropertyValidation() {
private static final long serialVersionUID = -6086632981851357180L;
@Override
public ValidationError validate(final Object object, final Object name, final Object value) {
final MemberGroupAccountSettings mgas = (MemberGroupAccountSettings) object;
final BigDecimal lowUnits = mgas.getLowUnits();
// When there are low units, the message is required
if (lowUnits != null && (lowUnits.compareTo(BigDecimal.ZERO) == 1) && StringUtils.isEmpty(mgas.getLowUnitsMessage())) {
return new RequiredError();
}
return null;
}
});
return accountSettingsValidator;
}
private Validator getAdminValidator() {
final Validator adminValidator = new Validator("group");
initSystem(adminValidator, true);
return adminValidator;
}
private Validator getBrokerValidator() {
final Validator brokerValidator = new Validator("group");
initSystem(brokerValidator, true);
initMember(brokerValidator);
return brokerValidator;
}
private Validator getInsertValidator() {
final Validator insertValidator = new Validator("group");
insertValidator.property("name").required().maxLength(100);
insertValidator.property("description").maxLength(2000);
insertValidator.property("nature").required();
insertValidator.property("status").required();
return insertValidator;
}
private Validator getMemberValidator() {
final Validator memberValidator = new Validator("group");
initSystem(memberValidator, true);
initMember(memberValidator);
return memberValidator;
}
private Validator getOperatorValidator() {
final Validator operatorValidator = new Validator("group");
initBasic(operatorValidator, false);
return operatorValidator;
}
private Validator getRemovedValidator() {
final Validator removedValidator = new Validator("group");
removedValidator.property("name").required().maxLength(100);
removedValidator.property("description").maxLength(2000);
removedValidator.property("nature").add(new ChangeNatureValidation());
return removedValidator;
}
private void initBasic(final Validator validator, final boolean addSettings) {
validator.property("name").required().maxLength(100);
validator.property("description").maxLength(2000);
validator.property("nature").add(new ChangeNatureValidation());
if (addSettings) {
validator.property("basicSettings.passwordLength.min").key("group.settings.passwordLength.min").between(1, 32);
validator.property("basicSettings.passwordLength.max").key("group.settings.passwordLength.max").between(1, 32);
validator.property("basicSettings.maxPasswordWrongTries").key("group.settings.passwordTries.maximum").between(0, 99);
validator.property("basicSettings.deactivationAfterMaxPasswordTries.number").key("group.settings.passwordTries.deactivationTime.number").between(0, 999);
validator.property("basicSettings.deactivationAfterMaxPasswordTries.field").key("group.settings.passwordTries.deactivationTime.field").required();
validator.property("basicSettings.passwordExpiresAfter.number").key("group.settings.passwordExpiresAfter.number").between(0, 999);
validator.property("basicSettings.passwordExpiresAfter.field").key("group.settings.passwordExpiresAfter.field").required();
validator.property("basicSettings.transactionPassword").key("group.settings.transactionPassword").required();
validator.property("basicSettings.transactionPasswordLength").key("group.settings.transactionPassword.length").between(1, 32);
validator.property("basicSettings.maxTransactionPasswordWrongTries").key("group.settings.maxTransactionPasswordWrongTries").between(0, 99);
validator.property("basicSettings.deactivationAfterMaxPasswordTries.number").key("group.settings.passwordTries.deactivationTime.number").add(new PasswordTrialsValidation());
validator.property("basicSettings.passwordPolicy").key("group.settings.passwordPolicy").add(new PasswordPolicyValidation());
}
}
private void initMember(final Validator validator) {
validator.property("memberSettings.defaultAdPublicationTime.number").key("group.settings.defaultAdPublicationTime.number").between(1, 999);
validator.property("memberSettings.defaultAdPublicationTime.field").key("group.settings.defaultAdPublicationTime.field").required();
validator.property("memberSettings.maxAdPublicationTime.number").key("group.settings.maxAdPublicationTime.number").between(1, 999);
validator.property("memberSettings.maxAdPublicationTime.field").key("group.settings.maxAdPublicationTime.field").required();
validator.property("memberSettings.maxAdDescriptionSize").key("group.settings.maxAdDescriptionSize").required().between(16, 16000);
validator.property("memberSettings.maxAdsPerMember").key("group.settings.maxAdsPerMember").between(0, 999);
validator.property("memberSettings.maxAdImagesPerMember").key("group.settings.maxAdImagesPerMember").between(0, 999);
validator.property("memberSettings.maxImagesPerMember").key("group.settings.maxImagesPerMember").between(0, 999);
validator.property("memberSettings.expireMembersAfter.number").key("group.settings.expireMembersAfter").between(0, 999);
validator.property("memberSettings.expireMembersAfter.field").key("group.settings.expireMembersAfter").add(new ExpirationValidation(RequiredValidation.instance()));
validator.property("memberSettings.groupAfterExpiration").key("group.settings.groupAfterExpiration").add(new ExpirationValidation(RequiredValidation.instance()));
validator.property("memberSettings.pinBlockTimeAfterMaxTries.number").key("group.settings.pinBlockTimeAfterMaxTries.number").between(0, 999);
validator.property("memberSettings.pinBlockTimeAfterMaxTries.field").key("group.settings.pinBlockTimeAfterMaxTries.field").add(new PINBlockTimeValidation(RequiredValidation.instance()));
validator.property("memberSettings.smsContextClassName").key("group.settings.smsContextClassName").instanceOf(ISmsContext.class);
validator.property("memberSettings.pinBlockTimeAfterMaxTries.number").key("group.settings.pinBlockTimeAfterMaxTries.number").add(new PinTrialsValidation());
}
private void initSystem(final Validator validator, final boolean addSettings) {
initBasic(validator, addSettings);
validator.property("rootUrl").maxLength(100).url();
validator.property("loginPageName").maxLength(20);
validator.property("containerUrl").maxLength(100).url();
}
@SuppressWarnings("unchecked")
private void removeFromInverseCollections(final Group group) {
// Payment filters
final Collection<PaymentFilter> paymentFilters = group.getPaymentFilters();
for (final PaymentFilter paymentFilter : paymentFilters) {
final Collection<Group> groups = (Collection<Group>) paymentFilter.getGroups();
groups.remove(group);
paymentFilterDao.update(paymentFilter);
}
// Record types
final Collection<MemberRecordType> recordTypes = group.getMemberRecordTypes();
for (final MemberRecordType recordType : recordTypes) {
recordType.getGroups().remove(group);
memberRecordTypeDao.update(recordType);
}
if (group instanceof AdminGroup) {
final AdminGroup adminGroup = (AdminGroup) group;
// Connected admins viewed by
final Collection<AdminGroup> connectedAdminsViewedBy = adminGroup.getConnectedAdminsViewedBy();
for (final AdminGroup viewerAdminGroup : connectedAdminsViewedBy) {
viewerAdminGroup.getViewConnectedAdminsOf().remove(adminGroup);
groupDao.update(viewerAdminGroup);
}
// Admin custom fields
final Collection<AdminCustomField> adminCustomFields = adminGroup.getAdminCustomFields();
for (final AdminCustomField adminCustomField : adminCustomFields) {
adminCustomField.getGroups().remove(adminGroup);
customFieldDao.update(adminCustomField);
}
}
if (group instanceof MemberGroup) {
final MemberGroup memberGroup = (MemberGroup) group;
// Account fees
final Collection<AccountFee> accountFees = memberGroup.getAccountFees();
for (final AccountFee accountFee : accountFees) {
final Collection<MemberGroup> groups = accountFee.getGroups();
groups.remove(memberGroup);
accountFeeDao.update(accountFee);
}
// Managed by groups
final Collection<AdminGroup> managedByGroups = memberGroup.getManagedByGroups();
for (final AdminGroup adminGroup : managedByGroups) {
adminGroup.getManagesGroups().remove(memberGroup);
groupDao.update(adminGroup);
}
// Member custom fields and general remark custom fields)
final Collection<CustomField> customFields = memberGroup.getCustomFields();
for (final CustomField customField : customFields) {
// Get the groups using reflection
final Collection<MemberGroup> groups = PropertyHelper.get(customField, "groups");
groups.remove(memberGroup);
customFieldDao.update(customField);
}
// From transaction fees
final Collection<TransactionFee> fromTransactionFees = memberGroup.getFromTransactionFees();
for (final TransactionFee transactionFee : fromTransactionFees) {
transactionFee.getFromGroups().remove(memberGroup);
transactionFeeDao.update(transactionFee);
}
// To transaction fees
final Collection<TransactionFee> toTransactionFees = memberGroup.getToTransactionFees();
for (final TransactionFee transactionFee : toTransactionFees) {
transactionFee.getToGroups().remove(memberGroup);
transactionFeeDao.update(transactionFee);
}
}
}
@SuppressWarnings("unchecked")
private <G extends Group> G save(G group) {
if (group.isTransient()) {
group = groupDao.insert(group);
} else {
// We must keep the many-to-many relationships, or they would be cleared...
final Group currentGroup = load(group.getId(), FETCH_TO_KEEP_DATA);
group.setPermissions(new HashSet<Permission>(currentGroup.getPermissions()));
group.setTransferTypes(new ArrayList<TransferType>(currentGroup.getTransferTypes()));
group.setConversionSimulationTTs(new ArrayList<TransferType>(currentGroup.getConversionSimulationTTs()));
group.setGuaranteeTypes(new ArrayList<GuaranteeType>(currentGroup.getGuaranteeTypes()));
if (group instanceof SystemGroup) {
final SystemGroup systemGroup = (SystemGroup) group;
final SystemGroup currentSystemGroup = ((SystemGroup) currentGroup);
systemGroup.setDocuments(new ArrayList<Document>(currentSystemGroup.getDocuments()));
systemGroup.setMessageCategories(new ArrayList<MessageCategory>(currentSystemGroup.getMessageCategories()));
systemGroup.setChargebackTransferTypes(new ArrayList<TransferType>(currentSystemGroup.getChargebackTransferTypes()));
}
if (group instanceof AdminGroup) {
final AdminGroup adminGroup = (AdminGroup) group;
final AdminGroup currentAdminGroup = ((AdminGroup) currentGroup);
adminGroup.setTransferTypesAsMember(new ArrayList<TransferType>(currentAdminGroup.getTransferTypesAsMember()));
adminGroup.setManagesGroups(new ArrayList<MemberGroup>(currentAdminGroup.getManagesGroups()));
adminGroup.setViewConnectedAdminsOf(new ArrayList<AdminGroup>(currentAdminGroup.getViewConnectedAdminsOf()));
adminGroup.setViewInformationOf(new ArrayList<SystemAccountType>(currentAdminGroup.getViewInformationOf()));
adminGroup.setViewAdminRecordTypes(new ArrayList<MemberRecordType>(currentAdminGroup.getViewAdminRecordTypes()));
adminGroup.setCreateAdminRecordTypes(new ArrayList<MemberRecordType>(currentAdminGroup.getCreateAdminRecordTypes()));
adminGroup.setModifyAdminRecordTypes(new ArrayList<MemberRecordType>(currentAdminGroup.getModifyAdminRecordTypes()));
adminGroup.setDeleteAdminRecordTypes(new ArrayList<MemberRecordType>(currentAdminGroup.getDeleteAdminRecordTypes()));
adminGroup.setViewMemberRecordTypes(new ArrayList<MemberRecordType>(currentAdminGroup.getViewMemberRecordTypes()));
adminGroup.setCreateMemberRecordTypes(new ArrayList<MemberRecordType>(currentAdminGroup.getCreateMemberRecordTypes()));
adminGroup.setModifyMemberRecordTypes(new ArrayList<MemberRecordType>(currentAdminGroup.getModifyMemberRecordTypes()));
adminGroup.setDeleteMemberRecordTypes(new ArrayList<MemberRecordType>(currentAdminGroup.getDeleteMemberRecordTypes()));
}
if (group instanceof BrokerGroup) {
final BrokerGroup brokerGroup = (BrokerGroup) group;
final BrokerGroup currentBrokerGroup = (BrokerGroup) currentGroup;
final List<Document> brokerDocuments = new ArrayList<Document>();
if (currentBrokerGroup.getBrokerDocuments() != null) {
brokerDocuments.addAll(currentBrokerGroup.getBrokerDocuments());
}
brokerGroup.setBrokerDocuments(brokerDocuments);
final List<AccountType> brokerCanViewInformationOf = new ArrayList<AccountType>();
if (brokerGroup.getBrokerCanViewInformationOf() != null) {
brokerCanViewInformationOf.addAll(brokerGroup.getBrokerCanViewInformationOf());
}
brokerGroup.setBrokerCanViewInformationOf(brokerCanViewInformationOf);
brokerGroup.setTransferTypesAsMember(new ArrayList<TransferType>(currentBrokerGroup.getTransferTypesAsMember()));
brokerGroup.setBrokerConversionSimulationTTs(new ArrayList<TransferType>(currentBrokerGroup.getBrokerConversionSimulationTTs()));
brokerGroup.setBrokerMemberRecordTypes(new ArrayList<MemberRecordType>(currentBrokerGroup.getBrokerMemberRecordTypes()));
brokerGroup.setBrokerCreateMemberRecordTypes(new ArrayList<MemberRecordType>(currentBrokerGroup.getBrokerCreateMemberRecordTypes()));
brokerGroup.setBrokerModifyMemberRecordTypes(new ArrayList<MemberRecordType>(currentBrokerGroup.getBrokerModifyMemberRecordTypes()));
brokerGroup.setBrokerDeleteMemberRecordTypes(new ArrayList<MemberRecordType>(currentBrokerGroup.getBrokerDeleteMemberRecordTypes()));
// "possibleInitialGroups" is updated at edit group screen, so it doesn't need to be copied
}
if (group instanceof MemberGroup) {
final MemberGroup memberGroup = (MemberGroup) group;
final MemberGroup currentMemberGroup = (MemberGroup) currentGroup;
memberGroup.setAccountSettings(currentMemberGroup.getAccountSettings());
// Ensure that no channel will be set by default if it's not accessible
memberGroup.getDefaultChannels().retainAll(memberGroup.getChannels());
// Ensure the removedChannels collection contains the channels which were removed
final Collection<Channel> removedChannels = new HashSet<Channel>();
removedChannels.addAll(currentMemberGroup.getChannels());
removedChannels.removeAll(memberGroup.getChannels());
final List<MemberGroup> viewProfile = new ArrayList<MemberGroup>();
if (currentMemberGroup.getCanViewProfileOfGroups() != null) {
viewProfile.addAll(currentMemberGroup.getCanViewProfileOfGroups());
}
memberGroup.setCanViewProfileOfGroups(viewProfile);
final List<AccountType> canViewInformationOf = new ArrayList<AccountType>();
if (currentMemberGroup.getCanViewInformationOf() != null) {
canViewInformationOf.addAll(currentMemberGroup.getCanViewInformationOf());
}
memberGroup.setCanViewInformationOf(canViewInformationOf);
final List<MemberGroup> viewAds = new ArrayList<MemberGroup>();
if (currentMemberGroup.getCanViewAdsOfGroups() != null) {
viewAds.addAll(currentMemberGroup.getCanViewAdsOfGroups());
}
memberGroup.setCanViewAdsOfGroups(viewAds);
final List<AdminGroup> managedByGroups = new ArrayList<AdminGroup>();
if (currentMemberGroup.getManagedByGroups() != null) {
managedByGroups.addAll(currentMemberGroup.getManagedByGroups());
}
memberGroup.setManagedByGroups(managedByGroups);
// "defaultMailMessages" is updated at edit group screen, so it doesn't need to be copied
// "smsMessages" is updated at edit group screen, so it doesn't need to be copied
// "defaultSmsMessages" is updated at edit group screen, so it doesn't need to be copied
// "channels" is updated at edit group screen, so it doesn't need to be copied
// "defaultChannels" is updated at edit group screen, so it doesn't need to be copied
// Add the main web access to default and accessible channels.
final Channel webChannel = channelService.loadByInternalName(Channel.WEB);
memberGroup.setChannels(CollectionUtils.union(memberGroup.getChannels(), Collections.singleton(webChannel)));
memberGroup.setDefaultChannels(CollectionUtils.union(memberGroup.getDefaultChannels(), Collections.singleton(webChannel)));
final List<Channel> requestPaymentByChannels = new ArrayList<Channel>();
if (currentMemberGroup.getRequestPaymentByChannels() != null) {
requestPaymentByChannels.addAll(currentMemberGroup.getRequestPaymentByChannels());
}
memberGroup.setRequestPaymentByChannels(requestPaymentByChannels);
final MemberGroupSettings memberSettings = memberGroup.getMemberSettings();
memberSettings.setGroupAfterExpiration(fetchService.fetch(memberSettings.getGroupAfterExpiration()));
// Update the basic settings of operator groups for members in this group
final GroupQuery operatorQuery = new GroupQuery();
operatorQuery.setNature(Group.Nature.OPERATOR);
operatorQuery.fetch(RelationshipHelper.nested(OperatorGroup.Relationships.MEMBER, Element.Relationships.GROUP));
final List<OperatorGroup> operatorGroups = (List<OperatorGroup>) groupDao.search(operatorQuery);
for (final OperatorGroup operatorGroup : operatorGroups) {
if (operatorGroup.getMember().getGroup().equals(memberGroup)) {
groupDao.update(operatorGroup);
}
}
final List<MemberGroup> canIssueCertificationToGroups = new ArrayList<MemberGroup>();
if (currentMemberGroup.getCanIssueCertificationToGroups() != null) {
canIssueCertificationToGroups.addAll(currentMemberGroup.getCanIssueCertificationToGroups());
}
memberGroup.setCanIssueCertificationToGroups(canIssueCertificationToGroups);
final List<MemberGroup> canBuyWithPaymentObligationsFromGroups = new ArrayList<MemberGroup>();
if (currentMemberGroup.getCanBuyWithPaymentObligationsFromGroups() != null) {
canBuyWithPaymentObligationsFromGroups.addAll(currentMemberGroup.getCanBuyWithPaymentObligationsFromGroups());
}
memberGroup.setCanBuyWithPaymentObligationsFromGroups(canBuyWithPaymentObligationsFromGroups);
// Ensure the message notification types are not present on the group for SMS
final Collection<Message.Type> smsMessages = memberGroup.getSmsMessages();
if (smsMessages != null) {
smsMessages.remove(Message.Type.FROM_MEMBER);
smsMessages.remove(Message.Type.FROM_ADMIN_TO_GROUP);
smsMessages.remove(Message.Type.FROM_ADMIN_TO_MEMBER);
}
final Collection<Type> defaultSmsMessages = memberGroup.getDefaultSmsMessages();
if (defaultSmsMessages != null) {
defaultSmsMessages.remove(Message.Type.FROM_MEMBER);
defaultSmsMessages.remove(Message.Type.FROM_ADMIN_TO_GROUP);
defaultSmsMessages.remove(Message.Type.FROM_ADMIN_TO_MEMBER);
}
// Remove from all members channels which are no longer accessible
elementDao.removeChannelsFromMembers(memberGroup, removedChannels);
// ensure activation status
if (memberGroup.isRemoved()) {
memberGroup.setActive(false);
}
}
if (group instanceof OperatorGroup) {
final OperatorGroup operatorGroup = (OperatorGroup) group;
final OperatorGroup currentOperatorGroup = (OperatorGroup) currentGroup;
// Check the account types
final List<AccountType> canViewInformationOf = new ArrayList<AccountType>();
if (currentOperatorGroup.getCanViewInformationOf() != null) {
canViewInformationOf.addAll(currentOperatorGroup.getCanViewInformationOf());
}
operatorGroup.setCanViewInformationOf(canViewInformationOf);
}
group = groupDao.update(group);
}
// Ensure the permissions cache for this group is evicted
permissionService.evictCache(group);
return group;
}
}