001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.coa.service.impl;
017    
018    import java.util.ArrayList;
019    import java.util.Collection;
020    import java.util.HashSet;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.Set;
024    
025    import org.apache.commons.lang.StringUtils;
026    import org.apache.log4j.Logger;
027    import org.kuali.kfs.coa.businessobject.Account;
028    import org.kuali.kfs.coa.businessobject.AccountDelegate;
029    import org.kuali.kfs.coa.dataaccess.AccountDao;
030    import org.kuali.kfs.coa.service.AccountService;
031    import org.kuali.kfs.sys.KFSConstants;
032    import org.kuali.kfs.sys.KFSConstants.SystemGroupParameterNames;
033    import org.kuali.kfs.sys.businessobject.AccountingLine;
034    import org.kuali.kfs.sys.context.SpringContext;
035    import org.kuali.kfs.sys.service.NonTransactional;
036    import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
037    import org.kuali.rice.kew.service.KEWServiceLocator;
038    import org.kuali.rice.kim.bo.Person;
039    import org.kuali.rice.kim.util.KimCommonUtils;
040    import org.kuali.rice.kns.service.ParameterService;
041    import org.kuali.rice.kns.util.ObjectUtils;
042    import org.kuali.rice.kns.util.spring.Cached;
043    
044    /**
045     * This class is the service implementation for the Account structure. This is the default, Kuali provided implementation.
046     */
047    
048    @NonTransactional
049    public class AccountServiceImpl implements AccountService {
050        private static final Logger LOG = Logger.getLogger(AccountServiceImpl.class);
051    
052        private AccountDao accountDao;
053    
054        /**
055         * Retrieves an Account object based on primary key.
056         * 
057         * @param chartOfAccountsCode - Chart of Accounts Code
058         * @param accountNumber - Account Number
059         * @return Account
060         * @see AccountService
061         */
062        public Account getByPrimaryId(String chartOfAccountsCode, String accountNumber) {
063            if (LOG.isDebugEnabled()) {
064                LOG.debug("retrieving account by primaryId (" + chartOfAccountsCode + "," + accountNumber + ")");
065            }
066    
067            Account account = accountDao.getByPrimaryId(chartOfAccountsCode, accountNumber);
068    
069            if (LOG.isDebugEnabled()) {
070                LOG.debug("retrieved account by primaryId (" + chartOfAccountsCode + "," + accountNumber + ")");
071            }
072            return account;
073        }
074    
075        /**
076         * Method is used by KualiAccountAttribute to enable caching of accounts for routing.
077         * 
078         * @see org.kuali.kfs.coa.service.impl.AccountServiceImpl#getByPrimaryId(java.lang.String, java.lang.String)
079         */
080        @Cached
081        public Account getByPrimaryIdWithCaching(String chartOfAccountsCode, String accountNumber) {
082            return accountDao.getByPrimaryId(chartOfAccountsCode, accountNumber);
083        }
084    
085        /**
086         * @see org.kuali.kfs.coa.service.AccountService#getAccountsThatUserIsResponsibleFor(org.kuali.bo.user.KualiUser)
087         */
088        public List getAccountsThatUserIsResponsibleFor(Person person) {
089            if (LOG.isDebugEnabled()) {
090                LOG.debug("retrieving accountsResponsible list for user " + person.getName());
091            }
092    
093            // gets the list of accounts that the user is the Fiscal Officer of
094            List accountList = accountDao.getAccountsThatUserIsResponsibleFor(person);
095            if (LOG.isDebugEnabled()) {
096                LOG.debug("retrieved accountsResponsible list for user " + person.getName());
097            }
098            return accountList;
099        }
100    
101        /**
102         * @see org.kuali.kfs.coa.service.AccountService#hasResponsibilityOnAccount(org.kuali.rice.kim.bo.Person,
103         *      org.kuali.kfs.coa.businessobject.Account)
104         */
105        public boolean hasResponsibilityOnAccount(Person kualiUser, Account account) {
106            return accountDao.determineUserResponsibilityOnAccount(kualiUser, account);
107        }
108    
109        /**
110         * @see org.kuali.kfs.coa.service.AccountService#getPrimaryDelegationByExample(org.kuali.kfs.coa.businessobject.AccountDelegate,
111         *      java.lang.String)
112         */
113    
114        public AccountDelegate getPrimaryDelegationByExample(AccountDelegate delegateExample, String totalDollarAmount) {
115            String documentTypeName = delegateExample.getFinancialDocumentTypeCode();
116            List primaryDelegations = filterAccountDelegates(delegateExample, accountDao.getPrimaryDelegationByExample(delegateExample, totalDollarAmount));
117            if (primaryDelegations.isEmpty()) {
118                return null;
119            }
120            AccountDelegate delegate;
121            for (Iterator iterator = primaryDelegations.iterator(); iterator.hasNext();) {
122                delegate = (AccountDelegate) iterator.next();
123                if (!KFSConstants.ROOT_DOCUMENT_TYPE.equals(delegate.getFinancialDocumentTypeCode())) {
124                    return delegate;
125                }
126            }
127            return (AccountDelegate)primaryDelegations.iterator().next();
128        }
129    
130        /**
131         * @see org.kuali.kfs.coa.service.AccountService#getSecondaryDelegationsByExample(org.kuali.kfs.coa.businessobject.AccountDelegate,
132         *      java.lang.String)
133         */
134        public List getSecondaryDelegationsByExample(AccountDelegate delegateExample, String totalDollarAmount) {
135            List secondaryDelegations = accountDao.getSecondaryDelegationsByExample(delegateExample, totalDollarAmount);
136            return filterAccountDelegates(delegateExample, secondaryDelegations);
137        }
138    
139        /**
140         * This method filters account delegates by 
141         * 1) performing an exact match on the document type name of delegateExample
142         * 2) if no match is found for 1), then by performing an exact match on 
143         * the closest parent document type name of delegateExample document type name.
144         * 
145         * @param delegateExample
146         * @param accountDelegatesToFilterFrom
147         * @return
148         */
149        protected List<AccountDelegate> filterAccountDelegates(AccountDelegate delegateExample, List<AccountDelegate> accountDelegatesToFilterFrom){
150            String documentTypeName = delegateExample.getFinancialDocumentTypeCode();
151            AccountDelegate delegate;
152            List<AccountDelegate> filteredAccountDelegates = filterAccountDelegates(accountDelegatesToFilterFrom, documentTypeName);
153            if(filteredAccountDelegates.size()==0){
154                Set<String> potentialParentDocumentTypeNames = getPotentialParentDocumentTypeNames(accountDelegatesToFilterFrom);
155                String closestParentDocumentTypeName = KimCommonUtils.getClosestParentDocumentTypeName(
156                        KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName), 
157                        potentialParentDocumentTypeNames);
158                filteredAccountDelegates = filterAccountDelegates(accountDelegatesToFilterFrom, closestParentDocumentTypeName);
159            }
160            return filteredAccountDelegates;
161        }
162        
163        /**
164         * This method filters account delegates by performing an exact match on the document type name passed in.
165         * 
166         * @param delegations
167         * @param documentTypeNameToFilterOn
168         * @return
169         */
170        protected List<AccountDelegate> filterAccountDelegates(List<AccountDelegate> delegations, String documentTypeNameToFilterOn){
171            AccountDelegate delegate;
172            List<AccountDelegate> filteredSecondaryDelegations = new ArrayList<AccountDelegate>();
173            for(Object delegateObject: delegations){
174                delegate = (AccountDelegate)delegateObject;
175                if(StringUtils.equals(delegate.getFinancialDocumentTypeCode(), documentTypeNameToFilterOn)){
176                    filteredSecondaryDelegations.add(delegate);
177                }
178            }
179            return filteredSecondaryDelegations;
180        }
181    
182        /**
183         * This method gets a list of potential parent document type names 
184         * by collecting the unique doc type names from the list of account delegations
185         * 
186         * @param delegations
187         * @return
188         */
189        protected Set<String> getPotentialParentDocumentTypeNames(List<AccountDelegate> delegations){
190            AccountDelegate delegate;
191            Set<String> potentialParentDocumentTypeNames = new HashSet<String>();
192            for(Object delegateObject: delegations){
193                delegate = (AccountDelegate)delegateObject;
194                if(!potentialParentDocumentTypeNames.contains(delegate.getFinancialDocumentTypeCode()))
195                    potentialParentDocumentTypeNames.add(delegate.getFinancialDocumentTypeCode());
196            }
197            return potentialParentDocumentTypeNames;
198        }
199    
200        /**
201         * get all accounts in the system. This is needed by a sufficient funds rebuilder job
202         * 
203         * @return iterator of all accounts
204         */
205        public Iterator getAllAccounts() {
206            LOG.debug("getAllAccounts() started");
207    
208            Iterator accountIter = accountDao.getAllAccounts();
209            List accountList = new ArrayList();
210            while (accountIter.hasNext()) {
211                accountList.add(accountIter.next());
212            }
213            return accountList.iterator();
214        }
215    
216        /**
217         * @see org.kuali.kfs.coa.service.AccountService#getActiveAccountsForAccountSupervisor(java.lang.String)
218         */
219        public Iterator<Account> getActiveAccountsForAccountSupervisor(String principalId) {
220            return accountDao.getActiveAccountsForAccountSupervisor(principalId);
221        }
222    
223        /**
224         * @see org.kuali.kfs.coa.service.AccountService#getActiveAccountsForFiscalOfficer(java.lang.String)
225         */
226        public Iterator<Account> getActiveAccountsForFiscalOfficer(String principalId) {
227            return accountDao.getActiveAccountsForFiscalOfficer(principalId);
228        }
229    
230        /**
231         * @see org.kuali.kfs.coa.service.AccountService#getExpiredAccountsForAccountSupervisor(java.lang.String)
232         */
233        public Iterator<Account> getExpiredAccountsForAccountSupervisor(String principalId) {
234            return accountDao.getExpiredAccountsForAccountSupervisor(principalId);
235        }
236    
237        /**
238         * @see org.kuali.kfs.coa.service.AccountService#getExpiredAccountsForFiscalOfficer(java.lang.String)
239         */
240        public Iterator<Account> getExpiredAccountsForFiscalOfficer(String principalId) {
241            return accountDao.getExpiredAccountsForFiscalOfficer(principalId);
242        }
243    
244        /**
245         * @see org.kuali.kfs.coa.service.AccountService#isPrincipalInAnyWayShapeOrFormAccountManager(java.lang.String)
246         */
247        public boolean isPrincipalInAnyWayShapeOrFormAccountManager(String principalId) {
248            return accountDao.isPrincipalInAnyWayShapeOrFormAccountManager(principalId);
249        }
250    
251        /**
252         * @see org.kuali.kfs.coa.service.AccountService#isPrincipalInAnyWayShapeOrFormAccountSupervisor(java.lang.String)
253         */
254        public boolean isPrincipalInAnyWayShapeOrFormAccountSupervisor(String principalId) {
255            return accountDao.isPrincipalInAnyWayShapeOrFormAccountSupervisor(principalId);
256        }
257    
258        /**
259         * @see org.kuali.kfs.coa.service.AccountService#isPrincipalInAnyWayShapeOrFormFiscalOfficer(java.lang.String)
260         */
261        public boolean isPrincipalInAnyWayShapeOrFormFiscalOfficer(String principalId) {
262            return accountDao.isPrincipalInAnyWayShapeOrFormFiscalOfficer(principalId);
263        }
264    
265        /**
266         * @see org.kuali.kfs.coa.service.AccountService#getAccountsForAccountNumber(java.lang.String)
267         */
268        public Collection<Account> getAccountsForAccountNumber(String accountNumber) {
269            return accountDao.getAccountsForAccountNumber(accountNumber);
270        }
271        
272        /**
273         * @see org.kuali.kfs.coa.service.AccountService#getUniqueAccountForAccountNumber(java.lang.String)
274         */
275        public Account getUniqueAccountForAccountNumber(String accountNumber) {
276            Iterator<Account> accounts = accountDao.getAccountsForAccountNumber(accountNumber).iterator();
277            Account account = null;
278            // there should be only one account in the collection
279            if (accounts.hasNext()) {
280                account = (Account)accounts.next();
281            }
282            return account;
283        }
284        
285        /**
286         * @see org.kuali.kfs.coa.service.AccountService#accountsCanCrossCharts()
287         */
288        public boolean accountsCanCrossCharts() {
289            return SpringContext.getBean(ParameterService.class).getIndicatorParameter(KfsParameterConstants.FINANCIAL_SYSTEM_ALL.class, SystemGroupParameterNames.ACCOUNTS_CAN_CROSS_CHARTS_IND);
290        }
291        
292        /**
293         * @see org.kuali.kfs.coa.service.AccountService#accountsCanCrossCharts()
294         */
295        public void populateAccountingLineChartIfNeeded(AccountingLine line) {
296            if (!accountsCanCrossCharts() /*&& line.getChartOfAccountsCode() == null*/) {
297                Account account = getUniqueAccountForAccountNumber(line.getAccountNumber());
298                if (ObjectUtils.isNotNull(account)) {
299                    line.setChartOfAccountsCode(account.getChartOfAccountsCode());
300                }
301            }
302        }
303        
304        /**
305         * @param accountDao The accountDao to set.
306         */
307        public void setAccountDao(AccountDao accountDao) {
308            this.accountDao = accountDao;
309        }
310    
311    }