nl.strohalm.cyclos.services.accounts.AccountTypeServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.services.accounts.AccountTypeServiceImpl.java

Source

/*
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.accounts;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import nl.strohalm.cyclos.access.AdminSystemPermission;
import nl.strohalm.cyclos.dao.accounts.AccountDAO;
import nl.strohalm.cyclos.dao.accounts.AccountLimitLogDAO;
import nl.strohalm.cyclos.dao.accounts.AccountTypeDAO;
import nl.strohalm.cyclos.dao.groups.GroupDAO;
import nl.strohalm.cyclos.entities.Relationship;
import nl.strohalm.cyclos.entities.access.Channel;
import nl.strohalm.cyclos.entities.accounts.AccountLimitLog;
import nl.strohalm.cyclos.entities.accounts.AccountOwner;
import nl.strohalm.cyclos.entities.accounts.AccountType;
import nl.strohalm.cyclos.entities.accounts.AccountTypeQuery;
import nl.strohalm.cyclos.entities.accounts.MemberAccountType;
import nl.strohalm.cyclos.entities.accounts.MemberGroupAccountSettings;
import nl.strohalm.cyclos.entities.accounts.SystemAccount;
import nl.strohalm.cyclos.entities.accounts.SystemAccountOwner;
import nl.strohalm.cyclos.entities.accounts.SystemAccountType;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferType;
import nl.strohalm.cyclos.entities.accounts.transactions.TransferTypeQuery;
import nl.strohalm.cyclos.entities.groups.AdminGroup;
import nl.strohalm.cyclos.entities.groups.Group;
import nl.strohalm.cyclos.entities.groups.MemberGroup;
import nl.strohalm.cyclos.entities.groups.OperatorGroup;
import nl.strohalm.cyclos.entities.members.Administrator;
import nl.strohalm.cyclos.entities.members.Element;
import nl.strohalm.cyclos.entities.members.Member;
import nl.strohalm.cyclos.services.fetch.FetchServiceLocal;
import nl.strohalm.cyclos.services.permissions.PermissionServiceLocal;
import nl.strohalm.cyclos.services.transactions.TransactionContext;
import nl.strohalm.cyclos.services.transfertypes.TransferTypeServiceLocal;
import nl.strohalm.cyclos.utils.access.LoggedUser;
import nl.strohalm.cyclos.utils.cache.Cache;
import nl.strohalm.cyclos.utils.cache.CacheCallback;
import nl.strohalm.cyclos.utils.cache.CacheManager;
import nl.strohalm.cyclos.utils.query.PageHelper;
import nl.strohalm.cyclos.utils.validation.Validator;

import org.apache.commons.collections.CollectionUtils;

/**
 * Implementation for account type service
 * @author luis
 */
public class AccountTypeServiceImpl implements AccountTypeServiceLocal {

    private static final String ALL_KEY = "_ALL_";
    private TransferTypeServiceLocal transferTypeService;
    private AccountDAO accountDao;
    private AccountTypeDAO accountTypeDao;
    private AccountLimitLogDAO accountLimitLogDao;
    private GroupDAO groupDao;
    private FetchServiceLocal fetchService;
    private CacheManager cacheManager;
    private PermissionServiceLocal permissionService;

    @Override
    public void clearCache() {
        getCache().clear();
    }

    @Override
    public MemberAccountType getDefault(MemberGroup group, final Relationship... fetch) {
        group = fetchService.fetch(group, MemberGroup.Relationships.ACCOUNT_SETTINGS);
        Collection<MemberGroupAccountSettings> accountSettings = group.getAccountSettings();
        MemberGroupAccountSettings defaultAccount = null;
        if (CollectionUtils.isNotEmpty(accountSettings)) {
            accountSettings = fetchService.fetch(accountSettings,
                    MemberGroupAccountSettings.Relationships.ACCOUNT_TYPE);
            for (final MemberGroupAccountSettings current : accountSettings) {
                if (current.isDefault()) {
                    // Found the default account
                    defaultAccount = current;
                    break;
                }
            }
            if (defaultAccount == null) {
                // None found: get the first one
                defaultAccount = accountSettings.iterator().next();
            }
        }
        return defaultAccount == null ? null : fetchService.fetch(defaultAccount.getAccountType(), fetch);
    }

    @Override
    public Map<MemberAccountType, BigDecimal> getMemberAccountTypesBalance(
            final Collection<MemberAccountType> types, final Collection<MemberGroup> groups,
            final Calendar timePoint) {
        Map<MemberAccountType, BigDecimal> balances = new TreeMap<MemberAccountType, BigDecimal>();
        for (final MemberAccountType type : types) {
            final BigDecimal balance = accountTypeDao.getBalance(type, groups, timePoint);
            balances.put(fetchService.fetch(type, AccountType.Relationships.CURRENCY), balance);
        }
        return balances;
    }

    @Override
    public Map<SystemAccountType, BigDecimal> getSystemAccountTypesBalance(
            final Collection<SystemAccountType> types, final Calendar timePoint) {
        Map<SystemAccountType, BigDecimal> balances = new TreeMap<SystemAccountType, BigDecimal>();
        for (final SystemAccountType type : types) {
            final BigDecimal balance = accountTypeDao.getBalance(type, timePoint);
            balances.put(fetchService.fetch(type, AccountType.Relationships.CURRENCY), balance);
        }
        return balances;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Collection<AccountType> getVisibleAccountTypes() {
        if (LoggedUser.isSystem()) {
            return (Collection<AccountType>) listAll();
        }
        if (!LoggedUser.hasUser()) {
            // Not system and no user - nothing is visible
            return Collections.emptyList();
        }
        final Group group = LoggedUser.group();
        return getCache().get("_VISIBLE_" + group.getId(), new CacheCallback() {
            @Override
            public Object retrieve() {
                Collection<AccountType> result;
                if (permissionService.permission().admin(AdminSystemPermission.ACCOUNTS_VIEW).hasPermission()) {
                    result = (Collection<AccountType>) listAll();
                } else {
                    if (LoggedUser.isOperator()) {
                        OperatorGroup group = LoggedUser.group();
                        result = fetchService.fetch(group, OperatorGroup.Relationships.CAN_VIEW_INFORMATION_OF)
                                .getCanViewInformationOf();
                    } else {
                        MemberAccountTypeQuery memberQuery = new MemberAccountTypeQuery();
                        memberQuery.setRelatedToGroups(permissionService.getManagedMemberGroups());
                        result = (Collection<AccountType>) search(memberQuery);

                        // A logged admin can see both system and member account types
                        if (LoggedUser.isAdministrator()) {
                            AdminGroup group = LoggedUser.group();
                            Collection<SystemAccountType> systemTypes = fetchService
                                    .fetch(group, AdminGroup.Relationships.VIEW_INFORMATION_OF)
                                    .getViewInformationOf();
                            result = CollectionUtils.union(result, systemTypes);
                        }
                    }
                }
                return fetchService.fetch(result, AccountType.Relationships.CURRENCY);
            }
        });
    }

    @Override
    public boolean hasAuthorizedPayments(AccountType accountType) {
        accountType = fetchService.fetch(accountType, AccountType.Relationships.FROM_TRANSFER_TYPES);
        for (final TransferType transferType : accountType.getFromTransferTypes()) {
            if (transferType.isRequiresAuthorization()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public List<? extends AccountType> listAll() {
        return getCache().get(ALL_KEY, new CacheCallback() {
            @Override
            public Object retrieve() {
                return accountTypeDao.listAll();
            }
        });
    }

    @Override
    public Collection<AccountType> load(final Collection<Long> ids) {
        Collection<AccountType> accountTypes = new ArrayList<AccountType>(ids.size());
        for (Long id : ids) {
            accountTypes.add(load(id));
        }
        return accountTypes;
    }

    @Override
    public AccountType load(final Long id) {
        return getCache().get(id, new CacheCallback() {
            @Override
            public Object retrieve() {
                final AccountType accountType = accountTypeDao.load(id, AccountType.Relationships.CURRENCY,
                        SystemAccountType.Relationships.EXTERNAL_ACCOUNTS);
                fillSystemLimits(accountType);
                return accountType;
            }
        });
    }

    @Override
    public int remove(final Long... ids) {
        for (final Long id : ids) {
            final AccountType accountType = accountTypeDao.load(id);
            if (accountType instanceof SystemAccountType) {
                final SystemAccountType systemAccountType = ((SystemAccountType) accountType);
                final SystemAccount account = systemAccountType.getAccount();
                systemAccountType.setAccount(null);
                accountTypeDao.update(systemAccountType);
                accountDao.delete(account.getId());
            }
        }
        getCache().clear();
        return accountTypeDao.delete(ids);
    }

    @Override
    public <AT extends AccountType> AT save(final AT accountType) {
        AT saved = null;
        validate(accountType);
        SystemAccount systemAccount = null;
        if (accountType.isTransient()) {
            saved = accountTypeDao.insert(accountType);
            if (saved instanceof SystemAccountType) {
                // Create the system account now
                final SystemAccountType systemAccountType = ((SystemAccountType) accountType);
                systemAccount = new SystemAccount();
                systemAccount.setCreationDate(Calendar.getInstance());
                systemAccount.setCreditLimit(systemAccountType.getCreditLimit());
                systemAccount.setUpperCreditLimit(systemAccountType.getUpperCreditLimit());
                systemAccount.setType(saved);
                systemAccount.setOwnerName(saved.getName());
                systemAccount = accountDao.insert(systemAccount);

                // Add permission to the admin group
                AdminGroup group = (AdminGroup) LoggedUser.group();
                group = groupDao.load(group.getId(), AdminGroup.Relationships.VIEW_INFORMATION_OF);
                final Collection<SystemAccountType> systemAccountTypes = group.getViewInformationOf();
                systemAccountTypes.add(systemAccountType);
                groupDao.update(group);
            }
            // Member accounts are created when an account type gets related to a group
        } else {
            if (accountType instanceof SystemAccountType) {
                final SystemAccountType currentAccountType = (SystemAccountType) accountTypeDao
                        .load(accountType.getId(), SystemAccountType.Relationships.VIEWED_BY_GROUPS);
                final Collection<AdminGroup> viewedByGroups = new ArrayList<AdminGroup>();
                if (currentAccountType.getViewedByGroups() != null) {
                    viewedByGroups.addAll(currentAccountType.getViewedByGroups());
                }

                final SystemAccountType systemAccountType = (SystemAccountType) accountType;
                systemAccountType.setViewedByGroups(viewedByGroups);

                // When updating a system account type, should update it's account too
                systemAccount = (SystemAccount) accountDao.load(SystemAccountOwner.instance(), systemAccountType);

                final BigDecimal oldLimit = systemAccount.getCreditLimit() == null ? null
                        : systemAccount.getCreditLimit().abs();
                final BigDecimal oldUpperLimit = systemAccount.getUpperCreditLimit();

                // When there was a credit limit, and it has changed, we must update the account
                final BigDecimal newLimit = systemAccountType.getCreditLimit() == null ? null
                        : systemAccountType.getCreditLimit().abs();
                final BigDecimal newUpperLimit = systemAccountType.getUpperCreditLimit();
                final boolean updateLimit = (newLimit != null && !newLimit.equals(oldLimit))
                        || ((newUpperLimit != null) && !newUpperLimit.equals(oldUpperLimit));
                if (updateLimit) {
                    systemAccount.setCreditLimit(newLimit);
                    systemAccount.setUpperCreditLimit(newUpperLimit);
                }
                systemAccount.setOwnerName(systemAccountType.getName());
                systemAccountType.setAccount(systemAccount);
                accountDao.update(systemAccount);

                if (updateLimit) {
                    // Generate the log
                    AccountLimitLog log = new AccountLimitLog();
                    log.setAccount(systemAccount);
                    log.setBy((Administrator) LoggedUser.element());
                    log.setDate(Calendar.getInstance());
                    log.setCreditLimit(newLimit);
                    log.setUpperCreditLimit(newUpperLimit);
                    accountLimitLogDao.insert(log);
                }
            }
            saved = accountTypeDao.update(accountType);
        }
        if (systemAccount != null) {
            ((SystemAccountType) saved).setAccount(systemAccount);
            saved = accountTypeDao.update(saved);
        }
        getCache().clear();
        return saved;
    }

    @Override
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public List<? extends AccountType> search(final AccountTypeQuery query) {
        if (query instanceof MemberAccountTypeQuery) {
            final MemberAccountTypeQuery memberQuery = (MemberAccountTypeQuery) query;
            final AccountOwner canPay = memberQuery.getCanPay();
            if (canPay != null) {
                Group group = null;
                if (canPay instanceof Member) {
                    final Member member = fetchService.fetch((Member) canPay, Element.Relationships.GROUP);
                    group = member.getGroup();
                }
                Member owner = memberQuery.getOwner();
                if (owner == null && LoggedUser.hasUser() && LoggedUser.isMember()) {
                    owner = LoggedUser.element();
                }
                // Can pay is handled differently: let's reuse the TransferTypeService to check which accounts have possible payment types
                final List<MemberAccountType> accountTypes = new ArrayList<MemberAccountType>();
                // I know: double casts looks awful, but...
                for (final MemberAccountType accountType : (List<MemberAccountType>) (List) accountTypeDao
                        .search(new MemberAccountTypeQuery())) {
                    final TransferTypeQuery transferTypeQuery = new TransferTypeQuery();
                    transferTypeQuery.setPageForCount();
                    transferTypeQuery.setContext(TransactionContext.PAYMENT);
                    transferTypeQuery.setChannel(Channel.WEB);
                    transferTypeQuery.setUsePriority(true);
                    transferTypeQuery.setToAccountType(accountType);
                    transferTypeQuery.setToOwner(owner);
                    transferTypeQuery.setFromOwner(canPay);
                    transferTypeQuery.setGroup(group);
                    if (PageHelper.getTotalCount(transferTypeService.search(transferTypeQuery)) > 0) {
                        accountTypes.add(accountType);
                    }
                }
                return accountTypes;
            }
        }
        return accountTypeDao.search(query);
    }

    public void setAccountDao(final AccountDAO accountDao) {
        this.accountDao = accountDao;
    }

    public void setAccountLimitLogDao(final AccountLimitLogDAO accountLimitLogDao) {
        this.accountLimitLogDao = accountLimitLogDao;
    }

    public void setAccountTypeDao(final AccountTypeDAO accountTypeDao) {
        this.accountTypeDao = accountTypeDao;
    }

    public void setCacheManager(final CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }

    public void setFetchServiceLocal(final FetchServiceLocal fetchService) {
        this.fetchService = fetchService;
    }

    public void setGroupDao(final GroupDAO groupDao) {
        this.groupDao = groupDao;
    }

    public void setPermissionServiceLocal(final PermissionServiceLocal permissionService) {
        this.permissionService = permissionService;
    }

    public void setTransferTypeServiceLocal(final TransferTypeServiceLocal transferTypeService) {
        this.transferTypeService = transferTypeService;
    }

    @Override
    public void validate(final AccountType accountType) {
        getValidator().validate(accountType);
    }

    private void fillSystemLimits(final AccountType accountType) {
        if (accountType instanceof SystemAccountType) {
            final SystemAccountType sat = (SystemAccountType) accountType;
            final SystemAccount account = sat.getAccount();
            if (account != null) {
                BigDecimal creditLimit = account.getCreditLimit();
                if (creditLimit != null) {
                    sat.setCreditLimit(creditLimit.abs().negate());
                }
                sat.setUpperCreditLimit(account.getUpperCreditLimit());
            }
        }
    }

    private Cache getCache() {
        return cacheManager.getCache("cyclos.AccountTypes");
    }

    private Validator getValidator() {
        final Validator validator = new Validator("accountType");
        validator.property("name").required().maxLength(100);
        validator.property("description").maxLength(1000);
        validator.property("currency").required();
        return validator;
    }
}