/* 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.elements; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Iterator; import java.util.List; import nl.strohalm.cyclos.access.AdminMemberPermission; import nl.strohalm.cyclos.access.BrokerPermission; import nl.strohalm.cyclos.dao.members.brokerings.BrokerCommissionContractDAO; import nl.strohalm.cyclos.dao.members.brokerings.BrokeringCommissionStatusDAO; import nl.strohalm.cyclos.dao.members.brokerings.DefaultBrokerCommissionDAO; import nl.strohalm.cyclos.entities.Relationship; import nl.strohalm.cyclos.entities.accounts.AccountType.Nature; import nl.strohalm.cyclos.entities.accounts.fees.transaction.BrokerCommission; import nl.strohalm.cyclos.entities.accounts.fees.transaction.TransactionFeeQuery; import nl.strohalm.cyclos.entities.groups.BrokerGroup; import nl.strohalm.cyclos.entities.groups.Group; import nl.strohalm.cyclos.entities.groups.GroupQuery; import nl.strohalm.cyclos.entities.groups.MemberGroup; import nl.strohalm.cyclos.entities.members.BrokeringQuery; import nl.strohalm.cyclos.entities.members.Element; import nl.strohalm.cyclos.entities.members.Member; import nl.strohalm.cyclos.entities.members.brokerings.BrokerCommissionContract; import nl.strohalm.cyclos.entities.members.brokerings.BrokerCommissionContract.Status; import nl.strohalm.cyclos.entities.members.brokerings.BrokerCommissionContractQuery; import nl.strohalm.cyclos.entities.members.brokerings.Brokering; import nl.strohalm.cyclos.entities.members.brokerings.BrokeringCommissionStatus; import nl.strohalm.cyclos.entities.members.brokerings.BrokeringCommissionStatusQuery; import nl.strohalm.cyclos.entities.members.brokerings.DefaultBrokerCommission; import nl.strohalm.cyclos.entities.members.brokerings.DefaultBrokerCommissionQuery; import nl.strohalm.cyclos.entities.settings.LocalSettings; import nl.strohalm.cyclos.services.InitializingService; import nl.strohalm.cyclos.services.fetch.FetchServiceLocal; import nl.strohalm.cyclos.services.groups.GroupServiceLocal; import nl.strohalm.cyclos.services.permissions.PermissionServiceLocal; import nl.strohalm.cyclos.services.settings.SettingsServiceLocal; import nl.strohalm.cyclos.services.transactions.TransactionSummaryVO; import nl.strohalm.cyclos.services.transfertypes.TransactionFeeServiceLocal; import nl.strohalm.cyclos.utils.Amount; import nl.strohalm.cyclos.utils.CacheCleaner; import nl.strohalm.cyclos.utils.DataIteratorHelper; import nl.strohalm.cyclos.utils.DateHelper; import nl.strohalm.cyclos.utils.Period; import nl.strohalm.cyclos.utils.TimePeriod; import nl.strohalm.cyclos.utils.TimePeriod.Field; import nl.strohalm.cyclos.utils.access.LoggedUser; import nl.strohalm.cyclos.utils.notifications.MemberNotificationHandler; import nl.strohalm.cyclos.utils.query.IteratorList; import nl.strohalm.cyclos.utils.query.PageHelper; import nl.strohalm.cyclos.utils.query.QueryParameters.ResultType; import nl.strohalm.cyclos.utils.validation.GeneralValidation; import nl.strohalm.cyclos.utils.validation.PropertyValidation; import nl.strohalm.cyclos.utils.validation.ValidationError; import nl.strohalm.cyclos.utils.validation.ValidationException; import nl.strohalm.cyclos.utils.validation.Validator; import org.apache.commons.collections.CollectionUtils; /** * Implementation for commission service * @author Jefferson Magno */ public class CommissionServiceImpl implements CommissionServiceLocal, InitializingService { /** * Validates if there is an active or pending contract with conflicting period * @author Jefferson Magno */ public class ConflictingContractValidation implements GeneralValidation { private static final long serialVersionUID = -2265778870845933590L; @Override public ValidationError validate(final Object object) { final BrokerCommissionContract brokerCommissionContract = (BrokerCommissionContract) object; if (brokerCommissionContractDao.isConflictingContract(brokerCommissionContract)) { return new ValidationError("brokerCommissionContract.error.conflictingContract"); } return null; } } /** * Validates the default broker commission * @author Jefferson Magno */ public class DefaultBrokerCommissionValidation implements GeneralValidation { private static final long serialVersionUID = -7493901526206665521L; @Override public ValidationError validate(final Object object) { final DefaultBrokerCommission defaultBrokerCommission = (DefaultBrokerCommission) object; if (defaultBrokerCommission.getBrokerCommission() != null) { final BrokerCommission brokerCommission = fetchService.fetch(defaultBrokerCommission.getBrokerCommission()); final Amount defaultAmount = defaultBrokerCommission.getAmount(); final Amount.Type type = defaultAmount.getType(); final BigDecimal defaultValue = defaultAmount.getValue(); BigDecimal feeValue = null; if (type == Amount.Type.FIXED) { feeValue = brokerCommission.getMaxFixedValue(); } else { feeValue = brokerCommission.getMaxPercentageValue(); } if (defaultValue != null && feeValue != null && defaultValue.compareTo(feeValue) > 0) { final LocalSettings localSettings = settingsService.getLocalSettings(); final String brokerCommissionName = brokerCommission.getName(); final Amount feeAmount = new Amount(feeValue, type); final String formattedFeeAmount = localSettings.getAmountConverter().toString(feeAmount); return new ValidationError("defaultBrokerCommission.error.maxValueExceeded", brokerCommissionName, formattedFeeAmount); } } return null; } } /** * Validates if the status of the contract is pending before saving it * @author Jefferson Magno */ public class StatusValidation implements PropertyValidation { private static final long serialVersionUID = 1390235157926363221L; @Override public ValidationError validate(final Object object, final Object property, final Object value) { final BrokerCommissionContract.Status status = (BrokerCommissionContract.Status) value; if (status != BrokerCommissionContract.Status.PENDING) { return new ValidationError("brokerCommissionContract.error.notPendingStatus"); } return null; } } private PermissionServiceLocal permissionService; private BrokerCommissionContractDAO brokerCommissionContractDao; private BrokeringCommissionStatusDAO brokeringCommissionStatusDao; private BrokeringServiceLocal brokeringService; private DefaultBrokerCommissionDAO defaultBrokerCommissionDao; private FetchServiceLocal fetchService; private GroupServiceLocal groupService; private MemberServiceLocal memberService; private SettingsServiceLocal settingsService; private TransactionFeeServiceLocal transactionFeeService; private MemberNotificationHandler memberNotificationHandler; @Override public BrokerCommissionContract acceptBrokerCommissionContract(final Long brokerCommissionContractId) { BrokerCommissionContract brokerCommissionContract = loadBrokerCommissionContract(brokerCommissionContractId); final Period period = brokerCommissionContract.getPeriod(); final boolean alreadyActive = period != null && period.includes(Calendar.getInstance()); brokerCommissionContract.setStatus(alreadyActive ? Status.ACTIVE : Status.ACCEPTED); brokerCommissionContract = brokerCommissionContractDao.update(brokerCommissionContract); memberNotificationHandler.commissionContractAcceptedNotification(brokerCommissionContract); return brokerCommissionContract; } @Override public void activateAcceptedBrokerCommissionContracts(final Calendar time) { final Period today = Period.endingAt(DateHelper.truncate(time)); // Activate accepted contracts that begin today final BrokerCommissionContractQuery query = new BrokerCommissionContractQuery(); query.setResultType(ResultType.ITERATOR); query.setStatus(BrokerCommissionContract.Status.ACCEPTED); query.setStartPeriod(today); final List<BrokerCommissionContract> contractsToActivate = brokerCommissionContractDao.search(query); try { for (final BrokerCommissionContract contractToActivate : contractsToActivate) { contractToActivate.setStatus(BrokerCommissionContract.Status.ACTIVE); brokerCommissionContractDao.update(contractToActivate); } } finally { DataIteratorHelper.close(contractsToActivate); } } @Override public BrokerCommissionContract cancelBrokerCommissionContract(final Long brokerCommissionContractId) { final Element cancelledBy = LoggedUser.element(); BrokerCommissionContract brokerCommissionContract = loadBrokerCommissionContract(brokerCommissionContractId); brokerCommissionContract.setStatus(Status.CANCELLED); brokerCommissionContract.setCancelledBy(cancelledBy); brokerCommissionContract = brokerCommissionContractDao.update(brokerCommissionContract); memberNotificationHandler.commissionContractCancelledNotification(brokerCommissionContract); return brokerCommissionContract; } @Override public BrokeringCommissionStatus closeBrokeringCommissionStatus(BrokeringCommissionStatus brokeringCommissionStatus) { brokeringCommissionStatus.getPeriod().setEnd(Calendar.getInstance()); brokeringCommissionStatus = brokeringCommissionStatusDao.update(brokeringCommissionStatus); return brokeringCommissionStatus; } @Override @SuppressWarnings("unchecked") public void createBrokeringCommissionStatus(final BrokerCommission brokerCommission) { Collection<BrokerGroup> brokerGroups = null; if (brokerCommission.isAllBrokerGroups()) { final GroupQuery query = new GroupQuery(); query.setNature(Group.Nature.BROKER); query.setStatus(Group.Status.NORMAL); query.setOnlyActive(true); brokerGroups = (Collection<BrokerGroup>) groupService.search(query); } else { brokerGroups = fetchService.fetch(brokerCommission.getBrokerGroups()); } for (final BrokerGroup brokerGroup : brokerGroups) { createBrokeringCommissionStatus(brokerGroup, brokerCommission); } } @Override public BrokeringCommissionStatus createBrokeringCommissionStatus(final Brokering brokering, final BrokerCommission brokerCommission) { // Create new instance final BrokeringCommissionStatus brokeringCommissionStatus = new BrokeringCommissionStatus(); brokeringCommissionStatus.setBrokering(brokering); brokeringCommissionStatus.setBrokerCommission(brokerCommission); brokeringCommissionStatus.setCreationDate(Calendar.getInstance()); boolean fillFromCommission = true; if (brokerCommission.isFromMember()) { // Get the data from the default broker commission final DefaultBrokerCommission defaultBrokerCommission = getDefaultBrokerCommission(brokering.getBroker(), brokerCommission); if (defaultBrokerCommission != null) { brokeringCommissionStatus.setAmount(defaultBrokerCommission.getAmount()); brokeringCommissionStatus.setWhen(defaultBrokerCommission.getWhen()); brokeringCommissionStatus.setMaxCount(defaultBrokerCommission.getCount()); fillFromCommission = false; } } if (fillFromCommission) { // Get the data from the broker commission brokeringCommissionStatus.setAmount(brokerCommission.getAmount()); brokeringCommissionStatus.setWhen(brokerCommission.getWhen()); brokeringCommissionStatus.setMaxCount(brokerCommission.getCount()); } // Set total final TransactionSummaryVO total = new TransactionSummaryVO(); total.setCount(0); total.setAmount(new BigDecimal(0)); brokeringCommissionStatus.setTotal(total); // Period final Period period = new Period(); period.setBegin(brokering.getStartDate()); // If the commission has a validity (number of days), set expiration date on the commission status and check if it is not expired if (brokeringCommissionStatus.getWhen() == BrokerCommission.When.DAYS) { // Calculate and set expiration date final Calendar brokeringStartDate = brokering.getStartDate(); final int daysCount = brokeringCommissionStatus.getMaxCount(); final Calendar expirationDate = new TimePeriod(daysCount, Field.DAYS).add(brokeringStartDate); brokeringCommissionStatus.setExpiryDate(expirationDate); // Check if the brokering commission status is expired final Calendar now = Calendar.getInstance(); if (now.compareTo(expirationDate) >= 0) { period.setEnd(expirationDate); } } // Set period brokeringCommissionStatus.setPeriod(period); return brokeringCommissionStatusDao.insert(brokeringCommissionStatus); } @Override @SuppressWarnings("unchecked") public void createDefaultBrokerCommissions(final BrokerCommission brokerCommission) { Collection<BrokerGroup> brokerGroups = null; if (brokerCommission.isAllBrokerGroups()) { final GroupQuery query = new GroupQuery(); query.setNature(Group.Nature.BROKER); query.setStatus(Group.Status.NORMAL); query.setOnlyActive(true); brokerGroups = (Collection<BrokerGroup>) groupService.search(query); } else { brokerGroups = fetchService.fetch(brokerCommission.getBrokerGroups()); } for (final BrokerGroup brokerGroup : brokerGroups) { createDefaultBrokerCommissions(brokerCommission, brokerGroup); } } @Override public BrokerCommissionContract denyBrokerCommissionContract(final Long brokerCommissionContractId) { BrokerCommissionContract brokerCommissionContract = loadBrokerCommissionContract(brokerCommissionContractId); brokerCommissionContract.setStatus(Status.DENIED); brokerCommissionContract = brokerCommissionContractDao.update(brokerCommissionContract); memberNotificationHandler.commissionContractDeniedNotification(brokerCommissionContract); return brokerCommissionContract; } @Override public void expireBrokerCommissionContracts(final Calendar time) { final Period endOfYesterday = Period.endingAt(DateHelper.truncatePreviosDay(time)); // Close contracts that expired at the end of yesterday BrokerCommissionContractQuery query = new BrokerCommissionContractQuery(); query.setResultType(ResultType.ITERATOR); query.setStatus(BrokerCommissionContract.Status.ACTIVE); query.setEndPeriod(endOfYesterday); final List<BrokerCommissionContract> contractsToClose = brokerCommissionContractDao.search(query); try { for (final BrokerCommissionContract contractToClose : contractsToClose) { contractToClose.setStatus(BrokerCommissionContract.Status.CLOSED); brokerCommissionContractDao.update(contractToClose); } } finally { DataIteratorHelper.close(contractsToClose); } // Expire contracts that were not accepted until it´s beginning query = new BrokerCommissionContractQuery(); query.setResultType(ResultType.ITERATOR); query.setStatus(BrokerCommissionContract.Status.PENDING); query.setStartPeriod(endOfYesterday); final List<BrokerCommissionContract> contractsToExpire = brokerCommissionContractDao.search(query); try { for (final BrokerCommissionContract contractToExpire : contractsToExpire) { contractToExpire.setStatus(BrokerCommissionContract.Status.EXPIRED); brokerCommissionContractDao.update(contractToExpire); } } finally { DataIteratorHelper.close(contractsToExpire); } } @Override public void expireBrokeringCommissionStatus(final Calendar date) { brokeringCommissionStatusDao.expireBrokeringCommissionStatus(date); } @Override public BrokerCommissionContract getActiveBrokerCommissionContract(final Brokering brokering, final BrokerCommission brokerCommission) { final BrokerCommissionContractQuery query = new BrokerCommissionContractQuery(); query.setBroker(brokering.getBroker()); query.setMember(brokering.getBrokered()); query.setBrokerCommission(brokerCommission); query.setStatus(Status.ACTIVE); query.setUniqueResult(); final List<BrokerCommissionContract> contracts = brokerCommissionContractDao.search(query); if (CollectionUtils.isEmpty(contracts)) { return null; } else { return contracts.iterator().next(); } } @Override public BrokeringCommissionStatus getBrokeringCommissionStatus(final Brokering brokering, final BrokerCommission brokerCommission) { return brokeringCommissionStatusDao.load(brokering, brokerCommission); } @Override @SuppressWarnings("unchecked") public List<CommissionChargeStatusDTO> getCommissionChargeStatus(final Member member) { final List<CommissionChargeStatusDTO> commissionChargeStatusList = new ArrayList<CommissionChargeStatusDTO>(); final Brokering brokering = brokeringService.getActiveBrokering(member); if (brokering != null) { final Member broker = fetchService.fetch(brokering.getBroker(), Element.Relationships.GROUP); // Get broker commission transaction fees related to the member group and to the broker group final TransactionFeeQuery transactionFeeQuery = new TransactionFeeQuery(); transactionFeeQuery.setGeneratedTransferTypeFromNature(Nature.MEMBER); transactionFeeQuery.setEntityType(BrokerCommission.class); transactionFeeQuery.setBrokerGroup((BrokerGroup) broker.getGroup()); transactionFeeQuery.setMemberGroup((MemberGroup) member.getGroup()); final List<BrokerCommission> brokerCommissions = (List<BrokerCommission>) transactionFeeService.search(transactionFeeQuery); // For each broker commission get the commission charge status dto for (final BrokerCommission brokerCommission : brokerCommissions) { final CommissionChargeStatusDTO commissionChargeStatusDto = new CommissionChargeStatusDTO(); commissionChargeStatusDto.setBrokerCommission(brokerCommission); final BrokerCommissionContract contract = getBrokerCommissionContract(member, broker, brokerCommission); if (contract != null) { commissionChargeStatusDto.setBrokerCommissionContract(contract); commissionChargeStatusDto.setChargeStatus(CommissionChargeStatusDTO.ChargeStatus.CONTRACT); } else { final BrokeringCommissionStatus status = getBrokeringCommissionStatus(brokering, brokerCommission); final DefaultBrokerCommission defaultBrokerCommission = getDefaultBrokerCommission(broker, brokerCommission); if (defaultBrokerCommission != null && status.getPeriod().getEnd() == null) { commissionChargeStatusDto.setBrokeringCommissionStatus(status); commissionChargeStatusDto.setChargeStatus(CommissionChargeStatusDTO.ChargeStatus.DEFAULT_COMMISSION); } else { commissionChargeStatusDto.setChargeStatus(CommissionChargeStatusDTO.ChargeStatus.NONE); } } commissionChargeStatusList.add(commissionChargeStatusDto); } } return commissionChargeStatusList; } @Override public DefaultBrokerCommission getDefaultBrokerCommission(final Member broker, final BrokerCommission brokerCommission) { final DefaultBrokerCommissionQuery query = new DefaultBrokerCommissionQuery(); query.setBroker(broker); query.setBrokerCommission(brokerCommission); query.setUniqueResult(); final List<DefaultBrokerCommission> defaultBrokerCommissions = defaultBrokerCommissionDao.search(query); if (CollectionUtils.isEmpty(defaultBrokerCommissions)) { return null; } else { return defaultBrokerCommissions.iterator().next(); } } @Override public BrokeringCommissionStatus getOrUpdateBrokeringCommissionStatus(final Brokering brokering, final BrokerCommission brokerCommission) { return getOrUpdateBrokeringCommissionStatus(brokering, brokerCommission, brokerCommission.getAmount(), brokerCommission.getWhen(), brokerCommission.getCount()); } @Override public boolean hasAllPermissions() { return permissionService.permission() .admin(AdminMemberPermission.BROKERINGS_MANAGE_COMMISSIONS) .broker(BrokerPermission.MEMBERS_MANAGE_CONTRACTS) .member() .hasPermission(); } @Override public boolean hasBrokerCommissionContracts() { if (!hasAllPermissions()) { return false; } final BrokerCommissionContractQuery commissionQuery = new BrokerCommissionContractQuery(); commissionQuery.setPageForCount(); commissionQuery.setMember(LoggedUser.member()); return PageHelper.hasResults(brokerCommissionContractDao.search(commissionQuery)); } @Override public void initializeService() { Calendar now = Calendar.getInstance(); activateAcceptedBrokerCommissionContracts(now); expireBrokerCommissionContracts(now); expireBrokeringCommissionStatus(now); } @Override public List<BrokerCommission> listPossibleCommissionsForNewContract(Member brokered) { brokered = fetchService.fetch(brokered, Element.Relationships.GROUP); final BrokerGroup brokerGroup = LoggedUser.group(); final MemberGroup memberGroup = brokered.getMemberGroup(); // Get broker commission transaction fees related to the broker group final TransactionFeeQuery transactionFeeQuery = new TransactionFeeQuery(); transactionFeeQuery.setGeneratedTransferTypeFromNature(Nature.MEMBER); transactionFeeQuery.setEntityType(BrokerCommission.class); transactionFeeQuery.setBrokerGroup(brokerGroup); transactionFeeQuery.setMemberGroup(memberGroup); @SuppressWarnings("unchecked") final List<BrokerCommission> brokerCommissions = new ArrayList<BrokerCommission>((List<BrokerCommission>) transactionFeeService.search(transactionFeeQuery)); // Get the existing contracts BrokerCommissionContractQuery query = new BrokerCommissionContractQuery(); query.setBroker(LoggedUser.member()); query.setMember(brokered); query.setStatusList(Arrays.asList(BrokerCommissionContract.Status.ACCEPTED, BrokerCommissionContract.Status.ACTIVE, BrokerCommissionContract.Status.PENDING)); List<BrokerCommissionContract> contracts = searchBrokerCommissionContracts(query); // Filter out the commissions for (Iterator<BrokerCommission> it = brokerCommissions.iterator(); it.hasNext();) { BrokerCommission brokerCommission = it.next(); // Commissions already used by existing contracts are not returned for (BrokerCommissionContract contract : contracts) { if (contract.getBrokerCommission().equals(brokerCommission)) { it.remove(); break; } } } return brokerCommissions; } @Override public BrokerCommissionContract loadBrokerCommissionContract(final Long id, final Relationship... fetch) { return brokerCommissionContractDao.load(id, fetch); } public BrokeringCommissionStatus loadBrokeringCommissionStatus(final Long id, final Relationship... fetch) { return brokeringCommissionStatusDao.load(id, fetch); } @Override public List<DefaultBrokerCommission> loadDefaultBrokerCommissions(final Member broker, final Relationship... fetch) { return defaultBrokerCommissionDao.load(broker, fetch); } @Override public int removeBrokerCommissionContracts(final Long... ids) { return brokerCommissionContractDao.delete(ids); } @Override public BrokerCommissionContract saveBrokerCommissionContract(BrokerCommissionContract brokerCommissionContract) { validateBrokerCommissionContract(brokerCommissionContract); if (brokerCommissionContract.isTransient()) { brokerCommissionContract = brokerCommissionContractDao.insert(brokerCommissionContract); memberNotificationHandler.newCommissionContractNotification(brokerCommissionContract); return brokerCommissionContract; } else { return brokerCommissionContractDao.update(brokerCommissionContract); } } @Override public List<DefaultBrokerCommission> saveDefaultBrokerCommissions(final List<DefaultBrokerCommission> commissions) { final List<DefaultBrokerCommission> savedList = new ArrayList<DefaultBrokerCommission>(); for (DefaultBrokerCommission defaultBrokerCommission : commissions) { // Skip suspended default broker commissions if (!defaultBrokerCommission.isSuspended()) { defaultBrokerCommission.setSetByBroker(true); defaultBrokerCommission = saveDefaultBrokerCommission(defaultBrokerCommission); savedList.add(defaultBrokerCommission); } } return savedList; } @Override public List<BrokerCommissionContract> searchBrokerCommissionContracts(final BrokerCommissionContractQuery query) { return brokerCommissionContractDao.search(query); } public void setBrokerCommissionContractDao(final BrokerCommissionContractDAO brokerCommissionContractDao) { this.brokerCommissionContractDao = brokerCommissionContractDao; } public void setBrokeringCommissionStatusDao(final BrokeringCommissionStatusDAO brokeringCommissionStatusDao) { this.brokeringCommissionStatusDao = brokeringCommissionStatusDao; } public void setBrokeringServiceLocal(final BrokeringServiceLocal brokeringService) { this.brokeringService = brokeringService; } public void setDefaultBrokerCommissionDao(final DefaultBrokerCommissionDAO defaultBrokerCommissionDao) { this.defaultBrokerCommissionDao = defaultBrokerCommissionDao; } public void setFetchServiceLocal(final FetchServiceLocal fetchService) { this.fetchService = fetchService; } public void setGroupServiceLocal(final GroupServiceLocal groupService) { this.groupService = groupService; } public void setMemberNotificationHandler(final MemberNotificationHandler memberNotificationHandler) { this.memberNotificationHandler = memberNotificationHandler; } public void setMemberServiceLocal(final MemberServiceLocal memberService) { this.memberService = memberService; } public void setPermissionServiceLocal(final PermissionServiceLocal permissionService) { this.permissionService = permissionService; } public void setSettingsServiceLocal(final SettingsServiceLocal settingsService) { this.settingsService = settingsService; } public void setTransactionFeeServiceLocal(final TransactionFeeServiceLocal transactionFeeService) { this.transactionFeeService = transactionFeeService; } public void stopCommissions(final BrokerCommission brokerCommission, final BrokerGroup brokerGroup) { CacheCleaner cacheCleaner = new CacheCleaner(fetchService); final IteratorList<Member> brokers = memberService.iterateByGroup(brokerGroup); try { for (final Member broker : brokers) { stopCommissions(brokerCommission, broker, true); cacheCleaner.clearCache(); } } finally { DataIteratorHelper.close(brokers); } } @Override public void stopCommissions(BrokerCommission brokerCommission, final Member broker, final boolean removeDefaultBrokerCommission) { brokerCommission = fetchService.fetch(brokerCommission); if (brokerCommission.isFromMember()) { final DefaultBrokerCommission defaultBrokerCommission = getDefaultBrokerCommission(broker, brokerCommission); if (defaultBrokerCommission != null) { if (removeDefaultBrokerCommission) { // Remove default broker commission defaultBrokerCommissionDao.delete(defaultBrokerCommission.getId()); } else { // Set default broker commission value to 0 final Amount zeroAmount = new Amount(); zeroAmount.setType(defaultBrokerCommission.getAmount().getType()); zeroAmount.setValue(new BigDecimal(0)); defaultBrokerCommission.setAmount(zeroAmount); defaultBrokerCommissionDao.update(defaultBrokerCommission); } } // Close broker commission contracts closeBrokerCommissionContracts(broker, brokerCommission); } // Close brokering commission status closeBrokeringCommissionStatus(broker, brokerCommission); } @Override public void suspendCommissions(BrokerCommission brokerCommission, final Member broker) { brokerCommission = fetchService.fetch(brokerCommission); if (brokerCommission.isFromMember()) { final DefaultBrokerCommission defaultBrokerCommission = getDefaultBrokerCommission(broker, brokerCommission); // Suspend default broker commission if (defaultBrokerCommission != null) { defaultBrokerCommission.setSuspended(true); defaultBrokerCommissionDao.update(defaultBrokerCommission); } // Suspend broker commission contracts suspendBrokerCommissionContracts(broker, brokerCommission); } } @Override public void unsuspendCommissions(BrokerCommission brokerCommission, final Member broker) { brokerCommission = fetchService.fetch(brokerCommission); if (brokerCommission.isFromMember()) { final DefaultBrokerCommission defaultBrokerCommission = getDefaultBrokerCommission(broker, brokerCommission); // Unsuspend default broker commission if (defaultBrokerCommission != null) { defaultBrokerCommission.setSuspended(false); defaultBrokerCommissionDao.update(defaultBrokerCommission); } // Unsuspend broker commission contracts unsuspendBrokerCommissionContracts(broker, brokerCommission); } } @Override @SuppressWarnings("unchecked") public void updateBrokerCommissions(final Member broker, final MemberGroup oldGroup, final MemberGroup newGroup) { List<BrokerCommission> oldBrokerCommissions = new ArrayList<BrokerCommission>(); List<BrokerCommission> newBrokerCommissions = new ArrayList<BrokerCommission>(); if (oldGroup instanceof BrokerGroup) { final BrokerGroup oldBrokerGroup = (BrokerGroup) oldGroup; final TransactionFeeQuery query = new TransactionFeeQuery(); query.setEntityType(BrokerCommission.class); query.setBrokerGroup(oldBrokerGroup); query.setReturnDisabled(true); oldBrokerCommissions = (List<BrokerCommission>) transactionFeeService.search(query); } if (newGroup instanceof BrokerGroup) { final BrokerGroup newBrokerGroup = (BrokerGroup) newGroup; final TransactionFeeQuery query = new TransactionFeeQuery(); query.setEntityType(BrokerCommission.class); query.setBrokerGroup(newBrokerGroup); query.setReturnDisabled(true); newBrokerCommissions = (List<BrokerCommission>) transactionFeeService.search(query); } // Stop charging commissions that are not applicable anymore and remove default broker commissions for (final BrokerCommission oldBrokerCommission : oldBrokerCommissions) { if (!newBrokerCommissions.contains(oldBrokerCommission)) { stopCommissions(oldBrokerCommission, broker, true); } } // Create commissions that now are applicable for (final BrokerCommission newBrokerCommission : newBrokerCommissions) { if (!oldBrokerCommissions.contains(newBrokerCommission)) { if (newBrokerCommission.isFromMember()) { createDefaultBrokerCommission(newBrokerCommission, broker); } createBrokeringCommissionStatus(broker, newBrokerCommission); } } } @Override @SuppressWarnings("unchecked") public void updateBrokeringCommissionStatus(final BrokerCommission brokerCommission, final BrokerCommission savedBrokerCommission) { Collection<BrokerGroup> allBrokerGroups = null; if (brokerCommission.isAllBrokerGroups() || savedBrokerCommission.isAllBrokerGroups()) { final GroupQuery query = new GroupQuery(); query.setNature(Group.Nature.BROKER); query.setStatus(Group.Status.NORMAL); query.setOnlyActive(true); allBrokerGroups = (Collection<BrokerGroup>) groupService.search(query); } Collection<BrokerGroup> brokerGroups = null; Collection<BrokerGroup> savedBrokerGroups = null; // Current groups of brokers if (brokerCommission.isAllBrokerGroups()) { brokerGroups = allBrokerGroups; } else { brokerGroups = fetchService.fetch(brokerCommission.getBrokerGroups()); } // Previous groups of brokers if (savedBrokerCommission.isAllBrokerGroups()) { savedBrokerGroups = allBrokerGroups; } else { savedBrokerGroups = savedBrokerCommission.getBrokerGroups(); } // Create brokering commission status for groups added to the commission for (final BrokerGroup brokerGroup : brokerGroups) { if (!savedBrokerGroups.contains(brokerGroup)) { createBrokeringCommissionStatus(brokerGroup, brokerCommission); } else { updateBrokeringCommissionStatus(brokerGroup, brokerCommission); } } // Stop commissions charging for groups removed from the commission for (final BrokerGroup savedBrokerGroup : savedBrokerGroups) { if (!brokerGroups.contains(savedBrokerGroup)) { stopCommissions(brokerCommission, savedBrokerGroup); } } } @Override public BrokeringCommissionStatus updateBrokeringCommissionStatus(final BrokeringCommissionStatus brokeringCommissionStatus) { return brokeringCommissionStatusDao.update(brokeringCommissionStatus); } @Override @SuppressWarnings("unchecked") public void updateDefaultBrokerCommissions(final BrokerCommission brokerCommission, final BrokerCommission savedBrokerCommission) { Collection<BrokerGroup> allBrokerGroups = null; if (brokerCommission.isAllBrokerGroups() || savedBrokerCommission.isAllBrokerGroups()) { final GroupQuery query = new GroupQuery(); query.setNature(Group.Nature.BROKER); query.setStatus(Group.Status.NORMAL); query.setOnlyActive(true); allBrokerGroups = (Collection<BrokerGroup>) groupService.search(query); } Collection<BrokerGroup> brokerGroups = null; Collection<BrokerGroup> savedBrokerGroups = null; // Current groups of brokers if (brokerCommission.isAllBrokerGroups()) { brokerGroups = allBrokerGroups; } else { brokerGroups = fetchService.fetch(brokerCommission.getBrokerGroups()); } // Previous groups of brokers if (savedBrokerCommission.isAllBrokerGroups()) { savedBrokerGroups = allBrokerGroups; } else { savedBrokerGroups = savedBrokerCommission.getBrokerGroups(); } for (final BrokerGroup brokerGroup : brokerGroups) { if (!savedBrokerGroups.contains(brokerGroup)) { // Create default broker commissions for groups added to the commission createDefaultBrokerCommissions(brokerCommission, brokerGroup); } else { // If the broker commission was already applicable to the broker group, update default broker commissions that were not customized by // the broker and that had not been charged yet updateDefaultBrokerCommissions(brokerCommission, brokerGroup); } } // Stop commissions charging for groups removed from the commission for (final BrokerGroup savedBrokerGroup : savedBrokerGroups) { if (!brokerGroups.contains(savedBrokerGroup)) { stopCommissions(brokerCommission, savedBrokerGroup); } } } @Override public void validateBrokerCommissionContract(final BrokerCommissionContract brokerCommissionContract) throws ValidationException { getBrokerCommissionContractValidator().validate(brokerCommissionContract); } @Override public void validateDefaultBrokerCommissions(final List<DefaultBrokerCommission> defaultBrokerCommissions) throws ValidationException { final Validator defaultBrokerCommissionValidator = getDefaultBrokerCommissionValidator(); for (final DefaultBrokerCommission defaultBrokerCommission : defaultBrokerCommissions) { defaultBrokerCommissionValidator.validate(defaultBrokerCommission); } } private void closeBrokerCommissionContracts(final Member broker, final BrokerCommission brokerCommission) { final List<BrokerCommissionContract.Status> statusList = new ArrayList<BrokerCommissionContract.Status>(); statusList.add(BrokerCommissionContract.Status.PENDING); statusList.add(BrokerCommissionContract.Status.ACTIVE); final BrokerCommissionContractQuery query = new BrokerCommissionContractQuery(); query.setBroker(broker); query.setBrokerCommission(brokerCommission); query.setStatusList(statusList); final List<BrokerCommissionContract> brokerCommissionContracts = brokerCommissionContractDao.search(query); for (final BrokerCommissionContract brokerCommissionContract : brokerCommissionContracts) { brokerCommissionContract.setStatus(BrokerCommissionContract.Status.CLOSED); brokerCommissionContractDao.update(brokerCommissionContract); } } private void closeBrokeringCommissionStatus(final Member broker, final BrokerCommission brokerCommission) { final Calendar now = Calendar.getInstance(); final BrokeringCommissionStatusQuery query = new BrokeringCommissionStatusQuery(); query.setBroker(broker); query.setBrokerCommission(brokerCommission); query.setOnlyActive(true); final List<BrokeringCommissionStatus> statusList = brokeringCommissionStatusDao.search(query); for (final BrokeringCommissionStatus status : statusList) { status.getPeriod().setEnd(now); brokeringCommissionStatusDao.update(status); } } private void createBrokeringCommissionStatus(final BrokerGroup brokerGroup, final BrokerCommission brokerCommission) { CacheCleaner cacheCleaner = new CacheCleaner(fetchService); final IteratorList<Member> brokers = memberService.iterateByGroup(brokerGroup); try { for (final Member broker : brokers) { createBrokeringCommissionStatus(broker, brokerCommission); cacheCleaner.clearCache(); } } finally { DataIteratorHelper.close(brokers); } } private void createBrokeringCommissionStatus(final Member broker, final BrokerCommission brokerCommission) { final BrokeringQuery brokeringQuery = new BrokeringQuery(); brokeringQuery.setBroker(broker); final List<Brokering> brokerings = brokeringService.search(brokeringQuery); for (final Brokering brokering : brokerings) { getOrUpdateBrokeringCommissionStatus(brokering, brokerCommission); } } private DefaultBrokerCommission createDefaultBrokerCommission(final BrokerCommission brokerCommission, final Member broker) { final DefaultBrokerCommission defaultBrokerCommission = new DefaultBrokerCommission(); defaultBrokerCommission.setBroker(broker); defaultBrokerCommission.setBrokerCommission(brokerCommission); defaultBrokerCommission.setAmount(brokerCommission.getAmount()); defaultBrokerCommission.setWhen(brokerCommission.getWhen()); defaultBrokerCommission.setCount(brokerCommission.getCount()); saveDefaultBrokerCommission(defaultBrokerCommission); return defaultBrokerCommission; } private void createDefaultBrokerCommissions(final BrokerCommission brokerCommission, final BrokerGroup brokerGroup) { CacheCleaner cacheCleaner = new CacheCleaner(fetchService); final IteratorList<Member> brokers = memberService.iterateByGroup(brokerGroup); try { for (final Member broker : brokers) { createDefaultBrokerCommission(brokerCommission, broker); cacheCleaner.clearCache(); } } finally { DataIteratorHelper.close(brokers); } } private BrokerCommissionContract getBrokerCommissionContract(final Member member, final Member broker, final BrokerCommission brokerCommission) { // First, check if there is an active contract ... final BrokerCommissionContractQuery query = new BrokerCommissionContractQuery(); query.setMember(member); query.setBroker(broker); query.setBrokerCommission(brokerCommission); query.setStatus(BrokerCommissionContract.Status.ACTIVE); query.setUniqueResult(); Collection<BrokerCommissionContract> contracts = searchBrokerCommissionContracts(query); if (CollectionUtils.isNotEmpty(contracts)) { return contracts.iterator().next(); } // ... if there is not, check if there is a pending contract ... query.setStatus(BrokerCommissionContract.Status.PENDING); contracts = searchBrokerCommissionContracts(query); if (CollectionUtils.isNotEmpty(contracts)) { return contracts.iterator().next(); } // ... else, return null return null; } private Validator getBrokerCommissionContractValidator() { final Validator brokerCommissionContractValidator = new Validator("brokerCommissionContract"); brokerCommissionContractValidator.property("brokering").required(); brokerCommissionContractValidator.property("brokerCommission").required(); brokerCommissionContractValidator.property("period.begin").key("brokerCommissionContract.startDate").required().futureOrToday(); brokerCommissionContractValidator.property("amount.type").key("brokerCommissionContract.amount").required(); brokerCommissionContractValidator.property("amount.value").key("brokerCommissionContract.amount").required(); brokerCommissionContractValidator.property("status").required().add(new StatusValidation()); brokerCommissionContractValidator.general(new ConflictingContractValidation()); return brokerCommissionContractValidator; } private Validator getDefaultBrokerCommissionValidator() { final Validator defaultBrokerCommissionValidator = new Validator("defaultBrokerCommission"); defaultBrokerCommissionValidator.property("broker").required(); defaultBrokerCommissionValidator.property("brokerCommission").required(); defaultBrokerCommissionValidator.property("amount.type").key("transactionFee.amount").required(); defaultBrokerCommissionValidator.property("amount.value").key("transactionFee.amount").required(); defaultBrokerCommissionValidator.property("when").required(); defaultBrokerCommissionValidator.general(new DefaultBrokerCommissionValidation()); return defaultBrokerCommissionValidator; } private BrokeringCommissionStatus getOrUpdateBrokeringCommissionStatus(final Brokering brokering, final BrokerCommission brokerCommission, final Amount amount, final BrokerCommission.When when, final Integer count) { BrokeringCommissionStatus brokeringCommissionStatus = getBrokeringCommissionStatus(brokering, brokerCommission); if (brokeringCommissionStatus == null) { brokeringCommissionStatus = createBrokeringCommissionStatus(brokering, brokerCommission); } else { // If the commission had not been charged yet and the brokering commission status is not closed, the brokering commission status can be // updated if (brokeringCommissionStatus.getTotal().getCount() == 0 && brokeringCommissionStatus.getPeriod().getEnd() == null) { brokeringCommissionStatus.setAmount(amount); brokeringCommissionStatus.setWhen(when); brokeringCommissionStatus.setMaxCount(count); brokeringCommissionStatus = brokeringCommissionStatusDao.update(brokeringCommissionStatus); } } return brokeringCommissionStatus; } /** * Saves the default broker commission, returning the updated instance */ private DefaultBrokerCommission saveDefaultBrokerCommission(DefaultBrokerCommission defaultBrokerCommission) { if (defaultBrokerCommission.getWhen() == BrokerCommission.When.ALWAYS) { defaultBrokerCommission.setCount(null); } if (defaultBrokerCommission.isTransient()) { defaultBrokerCommission = defaultBrokerCommissionDao.insert(defaultBrokerCommission); } else { defaultBrokerCommission = defaultBrokerCommissionDao.update(defaultBrokerCommission); } updateBrokeringCommissionStatus(defaultBrokerCommission); return defaultBrokerCommission; } private void suspendBrokerCommissionContracts(final Member broker, final BrokerCommission brokerCommission) { final BrokerCommissionContractQuery query = new BrokerCommissionContractQuery(); query.setBroker(broker); query.setBrokerCommission(brokerCommission); query.setStatusList(Arrays.asList(BrokerCommissionContract.Status.PENDING, BrokerCommissionContract.Status.ACCEPTED, BrokerCommissionContract.Status.ACTIVE)); final List<BrokerCommissionContract> brokerCommissionContracts = brokerCommissionContractDao.search(query); for (final BrokerCommissionContract brokerCommissionContract : brokerCommissionContracts) { brokerCommissionContract.setStatusBeforeSuspension(brokerCommissionContract.getStatus()); brokerCommissionContract.setStatus(BrokerCommissionContract.Status.SUSPENDED); brokerCommissionContractDao.update(brokerCommissionContract); } } private void unsuspendBrokerCommissionContracts(final Member broker, final BrokerCommission brokerCommission) { final Calendar today = DateHelper.truncate(Calendar.getInstance()); final BrokerCommissionContractQuery query = new BrokerCommissionContractQuery(); query.setBroker(broker); query.setBrokerCommission(brokerCommission); query.setStatus(BrokerCommissionContract.Status.SUSPENDED); final List<BrokerCommissionContract> brokerCommissionContracts = brokerCommissionContractDao.search(query); for (final BrokerCommissionContract brokerCommissionContract : brokerCommissionContracts) { final BrokerCommissionContract.Status statusBeforeSuspension = brokerCommissionContract.getStatusBeforeSuspension(); final Calendar beginOfContract = brokerCommissionContract.getPeriod().getBegin(); final Calendar endOfContract = brokerCommissionContract.getPeriod().getEnd(); if (statusBeforeSuspension == BrokerCommissionContract.Status.ACTIVE) { // If the contract is expired (ends before the beginning of today), close it if (endOfContract.compareTo(today) < 0) { brokerCommissionContract.setStatus(BrokerCommissionContract.Status.CLOSED); } else { brokerCommissionContract.setStatus(BrokerCommissionContract.Status.ACTIVE); } } else if (statusBeforeSuspension == BrokerCommissionContract.Status.ACCEPTED) { // If the contract is expired (ends before the beginning of today), close it if (endOfContract.compareTo(today) < 0) { brokerCommissionContract.setStatus(BrokerCommissionContract.Status.CLOSED); } else if (beginOfContract.compareTo(today) <= 0) { // If the contract begins before today and it was already accepted, activate it brokerCommissionContract.setStatus(BrokerCommissionContract.Status.ACTIVE); } else { // The contract goes back to "ACCEPTED" status brokerCommissionContract.setStatus(BrokerCommissionContract.Status.ACCEPTED); } } else { // If the contract begins before today and was not accepted yet, expire it if (beginOfContract.compareTo(today) <= 0) { brokerCommissionContract.setStatus(BrokerCommissionContract.Status.EXPIRED); } else { brokerCommissionContract.setStatus(BrokerCommissionContract.Status.PENDING); } } brokerCommissionContract.setStatusBeforeSuspension(null); brokerCommissionContractDao.update(brokerCommissionContract); } } private void updateBrokeringCommissionStatus(final BrokerGroup brokerGroup, final BrokerCommission brokerCommission) { CacheCleaner cacheCleaner = new CacheCleaner(fetchService); final IteratorList<Member> brokers = memberService.iterateByGroup(brokerGroup); try { for (final Member broker : brokers) { updateBrokeringCommissionStatus(broker, brokerCommission); cacheCleaner.clearCache(); } } finally { DataIteratorHelper.close(brokers); } } /* * Create a new brokering commission status (for each brokering) if it does not exist and update the existing ones */ private void updateBrokeringCommissionStatus(final DefaultBrokerCommission defaultBrokerCommission) { final Member broker = defaultBrokerCommission.getBroker(); final BrokerCommission brokerCommission = defaultBrokerCommission.getBrokerCommission(); final BrokeringQuery brokeringQuery = new BrokeringQuery(); brokeringQuery.setBroker(broker); final List<Brokering> brokerings = brokeringService.search(brokeringQuery); for (final Brokering brokering : brokerings) { getOrUpdateBrokeringCommissionStatus(brokering, brokerCommission, defaultBrokerCommission.getAmount(), defaultBrokerCommission.getWhen(), defaultBrokerCommission.getCount()); } } private void updateBrokeringCommissionStatus(final Member broker, final BrokerCommission brokerCommission) { final BrokeringQuery brokeringQuery = new BrokeringQuery(); brokeringQuery.setBroker(broker); final List<Brokering> brokerings = brokeringService.search(brokeringQuery); for (final Brokering brokering : brokerings) { getOrUpdateBrokeringCommissionStatus(brokering, brokerCommission); } } private void updateDefaultBrokerCommission(final BrokerCommission brokerCommission, final Member broker) { DefaultBrokerCommission defaultBrokerCommission = getDefaultBrokerCommission(broker, brokerCommission); if (defaultBrokerCommission == null) { defaultBrokerCommission = createDefaultBrokerCommission(brokerCommission, broker); } if (!defaultBrokerCommission.isSetByBroker()) { final BrokeringCommissionStatusQuery query = new BrokeringCommissionStatusQuery(); query.setBroker(broker); query.setBrokerCommission(brokerCommission); final List<BrokeringCommissionStatus> statusList = brokeringCommissionStatusDao.search(query); boolean canUpdate = true; for (final BrokeringCommissionStatus status : statusList) { if (status.getTotal().getAmount().compareTo(new BigDecimal(0)) > 0) { canUpdate = false; break; } } if (canUpdate) { defaultBrokerCommission.setAmount(brokerCommission.getAmount()); defaultBrokerCommissionDao.update(defaultBrokerCommission); updateBrokeringCommissionStatus(defaultBrokerCommission); } } } private void updateDefaultBrokerCommissions(final BrokerCommission brokerCommission, final BrokerGroup brokerGroup) { CacheCleaner cacheCleaner = new CacheCleaner(fetchService); final IteratorList<Member> brokers = memberService.iterateByGroup(brokerGroup); try { for (final Member broker : brokers) { updateDefaultBrokerCommission(brokerCommission, broker); cacheCleaner.clearCache(); } } finally { DataIteratorHelper.close(brokers); } } }