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.dataaccess.impl;
017    
018    import java.util.ArrayList;
019    import java.util.Collection;
020    import java.util.Iterator;
021    import java.util.List;
022    
023    import org.apache.ojb.broker.query.Criteria;
024    import org.apache.ojb.broker.query.QueryFactory;
025    import org.apache.ojb.broker.query.ReportQueryByCriteria;
026    import org.kuali.kfs.coa.businessobject.Account;
027    import org.kuali.kfs.coa.businessobject.AccountDelegate;
028    import org.kuali.kfs.coa.dataaccess.AccountDao;
029    import org.kuali.kfs.sys.KFSConstants;
030    import org.kuali.kfs.sys.KFSPropertyConstants;
031    import org.kuali.kfs.sys.businessobject.AccountResponsibility;
032    import org.kuali.kfs.sys.context.SpringContext;
033    import org.kuali.rice.kim.bo.Person;
034    import org.kuali.rice.kns.dao.impl.PlatformAwareDaoBaseOjb;
035    import org.kuali.rice.kns.service.DateTimeService;
036    import org.kuali.rice.kns.util.KualiDecimal;
037    import org.kuali.rice.kns.util.ObjectUtils;
038    
039    /**
040     * This class is the OJB implementation of the AccountDao interface.
041     */
042    public class AccountDaoOjb extends PlatformAwareDaoBaseOjb implements AccountDao {
043        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountDaoOjb.class);
044    
045        private DateTimeService dateTimeService;
046    
047        /**
048         * Retrieves account business object by primary key
049         * 
050         * @param chartOfAccountsCode - the FIN_COA_CD of the Chart Code that is part of the composite key of Account
051         * @param accountNumber - the ACCOUNT_NBR part of the composite key of Accont
052         * @return Account
053         * @see AccountDao
054         */
055        public Account getByPrimaryId(String chartOfAccountsCode, String accountNumber) {
056            LOG.debug("getByPrimaryId() started");
057    
058            Criteria criteria = new Criteria();
059            criteria.addEqualTo("chartOfAccountsCode", chartOfAccountsCode);
060            criteria.addEqualTo("accountNumber", accountNumber);
061    
062            return (Account) getPersistenceBrokerTemplate().getObjectByQuery(QueryFactory.newQuery(Account.class, criteria));
063        }
064    
065        /**
066         * fetch the accounts that the user is either the fiscal officer or a delegate of the fiscal officer
067         * 
068         * @param kualiUser
069         * @return a list of Accounts that the user has responsibility for
070         */
071        public List getAccountsThatUserIsResponsibleFor(Person person) {
072            LOG.debug("getAccountsThatUserIsResponsibleFor() started");
073    
074            List accountResponsibilities = new ArrayList();
075            accountResponsibilities.addAll(getFiscalOfficerResponsibilities(person));
076            accountResponsibilities.addAll(getDelegatedResponsibilities(person));
077            return accountResponsibilities;
078        }
079    
080        /**
081         * This method determines if the given user has any responsibilities on the given account
082         * 
083         * @param person the user to check responsibilities for
084         * @param account the account to check responsibilities on
085         * @return true if user is somehow responsible for account, false if otherwise
086         */
087        public boolean determineUserResponsibilityOnAccount(Person person, Account account) {
088            boolean result = hasFiscalOfficerResponsibility(person, account);
089            if (!result) {
090                result = hasDelegatedResponsibility(person, account);
091            }
092            return result;
093        }
094    
095        /**
096         * Resolves the Primary Delegate for the given delegate example. If the primary delegate exists for a specific Document Type
097         * Code and for a Document Type Code of "KFS", the delegate for the specific document type code is returned;
098         * 
099         * @see org.kuali.kfs.coa.dataaccess.AccountDao#getPrimaryDelegationByExample(org.kuali.kfs.coa.businessobject.AccountDelegate,
100         *      java.lang.String)
101         */
102        public List getPrimaryDelegationByExample(AccountDelegate delegateExample, String totalDollarAmount) {
103            return new ArrayList(getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(AccountDelegate.class, getDelegateByExampleCriteria(delegateExample, totalDollarAmount, "Y"))));
104        }
105    
106        /**
107         * @see org.kuali.kfs.coa.dataaccess.AccountDao#getSecondaryDelegationsByExample(org.kuali.kfs.coa.businessobject.AccountDelegate,
108         *      java.lang.String)
109         */
110        public List getSecondaryDelegationsByExample(AccountDelegate delegateExample, String totalDollarAmount) {
111            return new ArrayList(getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(AccountDelegate.class, getDelegateByExampleCriteria(delegateExample, totalDollarAmount, "N"))));
112        }
113    
114        /**
115         * This method creates a {@link Criteria} based on {@link Delegate}, dollar amount and whether or not it is the primary
116         * delegate
117         * 
118         * @param delegateExample
119         * @param totalDollarAmount
120         * @param accountsDelegatePrmrtIndicator
121         * @return example {@link Delegate} {@link Criteria}
122         */
123        protected Criteria getDelegateByExampleCriteria(AccountDelegate delegateExample, String totalDollarAmount, String accountsDelegatePrmrtIndicator) {
124            Criteria criteria = new Criteria();
125            criteria.addEqualTo(KFSConstants.CHART_OF_ACCOUNTS_CODE_PROPERTY_NAME, delegateExample.getChartOfAccountsCode());
126            criteria.addEqualTo(KFSConstants.ACCOUNT_NUMBER_PROPERTY_NAME, delegateExample.getAccountNumber());
127            criteria.addEqualTo("active", "Y");
128            criteria.addLessOrEqualThan("accountDelegateStartDate", SpringContext.getBean(DateTimeService.class).getCurrentSqlDate());
129            criteria.addEqualTo("accountsDelegatePrmrtIndicator", accountsDelegatePrmrtIndicator);
130            if (totalDollarAmount != null) {
131                // (toAmt is nullish and (fromAmt is nullish or fromAmt <= total)) or (fromAmt is nullish and (toAmt is nullish or toAmt >= total)) or (fromAmt <= total and toAmount >= total)
132                
133                /* to not active clause: (toAmt is nullish and (fromAmt is nullish or fromAmt <= total)) */
134                Criteria toAmountIsNullish = new Criteria();
135                toAmountIsNullish.addIsNull(KFSPropertyConstants.FIN_DOC_APPROVAL_TO_THIS_AMOUNT);
136                Criteria toAmountIsZero1 = new Criteria();
137                toAmountIsZero1.addEqualTo(KFSPropertyConstants.FIN_DOC_APPROVAL_TO_THIS_AMOUNT, "0");
138                toAmountIsNullish.addOrCriteria(toAmountIsZero1);
139                            
140                Criteria fromMatchesClause = new Criteria();
141                fromMatchesClause.addIsNull(KFSPropertyConstants.FIN_DOC_APPROVAL_FROM_THIS_AMT);
142                Criteria fromAmountIsLessThanTotal = new Criteria();
143                fromAmountIsLessThanTotal.addLessOrEqualThan(KFSPropertyConstants.FIN_DOC_APPROVAL_FROM_THIS_AMT, totalDollarAmount);
144                fromMatchesClause.addOrCriteria(fromAmountIsLessThanTotal);
145                
146                Criteria toNotActiveClause = new Criteria();
147                toNotActiveClause.addAndCriteria(toAmountIsNullish);
148                toNotActiveClause.addAndCriteria(fromMatchesClause);
149                
150                /* from not active clause: (fromAmt is nullish and (toAmt is nullish or toAmt >= total)) */
151                Criteria toMatchesClause = new Criteria();
152                toMatchesClause.addIsNull(KFSPropertyConstants.FIN_DOC_APPROVAL_TO_THIS_AMOUNT);
153                Criteria toAmountIsZero2 = new Criteria();
154                toAmountIsZero2.addEqualTo(KFSPropertyConstants.FIN_DOC_APPROVAL_TO_THIS_AMOUNT, "0");
155                toMatchesClause.addOrCriteria(toAmountIsZero2);
156                Criteria toAmountIsGreaterThanTotal = new Criteria();
157                toAmountIsGreaterThanTotal.addGreaterOrEqualThan(KFSPropertyConstants.FIN_DOC_APPROVAL_TO_THIS_AMOUNT, totalDollarAmount);
158                toMatchesClause.addOrCriteria(toAmountIsGreaterThanTotal);
159                
160                Criteria fromIsNullClause = new Criteria();
161                fromIsNullClause.addIsNull(KFSPropertyConstants.FIN_DOC_APPROVAL_FROM_THIS_AMT);
162                Criteria fromIsZeroClause = new Criteria();
163                fromIsZeroClause.addEqualTo(KFSPropertyConstants.FIN_DOC_APPROVAL_FROM_THIS_AMT, "0");
164                Criteria fromIsNullishClause = new Criteria();
165                fromIsNullishClause.addOrCriteria(fromIsNullClause);
166                fromIsNullishClause.addOrCriteria(fromIsZeroClause);
167                Criteria fromNotActiveClause = new Criteria();
168                fromNotActiveClause.addAndCriteria(fromIsNullishClause);
169                fromNotActiveClause.addAndCriteria(toMatchesClause);
170                
171                Criteria bothActive = new Criteria();
172                bothActive.addLessOrEqualThan(KFSPropertyConstants.FIN_DOC_APPROVAL_FROM_THIS_AMT, totalDollarAmount);
173                bothActive.addGreaterOrEqualThan(KFSPropertyConstants.FIN_DOC_APPROVAL_TO_THIS_AMOUNT, totalDollarAmount);
174                
175                Criteria totalDollarAmountCriteria = new Criteria();
176                totalDollarAmountCriteria.addOrCriteria(toNotActiveClause);
177                totalDollarAmountCriteria.addOrCriteria(fromNotActiveClause);
178                totalDollarAmountCriteria.addOrCriteria(bothActive);
179    
180                criteria.addAndCriteria(totalDollarAmountCriteria);
181            }
182            return criteria;
183        }
184    
185        /**
186         * method to get the fo responsibilities for the account
187         * 
188         * @param person - fiscal officer to check for
189         * @return list of {@link AccountResponsibility} for this fiscal officer
190         */
191        protected List getFiscalOfficerResponsibilities(Person person) {
192            List fiscalOfficerResponsibilities = new ArrayList();
193            Criteria criteria = new Criteria();
194            criteria.addEqualTo("accountFiscalOfficerSystemIdentifier", person.getPrincipalId());
195            Collection accounts = getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(Account.class, criteria));
196            for (Iterator iter = accounts.iterator(); iter.hasNext();) {
197                Account account = (Account) iter.next();
198                AccountResponsibility accountResponsibility = new AccountResponsibility(AccountResponsibility.FISCAL_OFFICER_RESPONSIBILITY, KualiDecimal.ZERO, KualiDecimal.ZERO, "", account);
199                fiscalOfficerResponsibilities.add(accountResponsibility);
200            }
201            return fiscalOfficerResponsibilities;
202        }
203    
204        /**
205         * This method determines if a given user has fiscal officer responsiblity on a given account.
206         * 
207         * @param person the user to check responsibilities for
208         * @param account the account to check responsibilities on
209         * @return true if user does have fiscal officer responsibility on account, false if otherwise
210         */
211        protected boolean hasFiscalOfficerResponsibility(Person person, Account account) {
212            boolean hasFiscalOfficerResponsibility = false;
213            Criteria criteria = new Criteria();
214            criteria.addEqualTo("accountFiscalOfficerSystemIdentifier", person.getPrincipalId());
215            criteria.addEqualTo("chartOfAccountsCode", account.getChartOfAccountsCode());
216            criteria.addEqualTo("accountNumber", account.getAccountNumber());
217            Collection accounts = getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(Account.class, criteria));
218            if (accounts != null && accounts.size() > 0) {
219                Account retrievedAccount = (Account) accounts.iterator().next();
220                if (ObjectUtils.isNotNull(retrievedAccount)) {
221                    hasFiscalOfficerResponsibility = true;
222                }
223            }
224            return hasFiscalOfficerResponsibility;
225        }
226    
227        /**
228         * method to get the fo delegated responsibilities for the account
229         * 
230         * @param person - user to check against
231         * @return a list of {@link AccountResponsibility} objects for a delegate
232         */
233        protected List getDelegatedResponsibilities(Person person) {
234            List delegatedResponsibilities = new ArrayList();
235            Criteria criteria = new Criteria();
236            criteria.addEqualTo("accountDelegateSystemId", person.getPrincipalId());
237            Collection accountDelegates = getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(AccountDelegate.class, criteria));
238            for (Iterator iter = accountDelegates.iterator(); iter.hasNext();) {
239                AccountDelegate accountDelegate = (AccountDelegate) iter.next();
240                if (accountDelegate.isActive()) {
241                    // the start_date should never be null in the real world, but
242                    // there is some test data that
243                    // contains null startDates, therefore this check.
244                    if (ObjectUtils.isNotNull(accountDelegate.getAccountDelegateStartDate())) {
245                        if (!accountDelegate.getAccountDelegateStartDate().after(dateTimeService.getCurrentDate())) {
246                            Account account = getByPrimaryId(accountDelegate.getChartOfAccountsCode(), accountDelegate.getAccount().getAccountNumber());
247                            AccountResponsibility accountResponsibility = new AccountResponsibility(AccountResponsibility.DELEGATED_RESPONSIBILITY, accountDelegate.getFinDocApprovalFromThisAmt(), accountDelegate.getFinDocApprovalToThisAmount(), accountDelegate.getFinancialDocumentTypeCode(), account);
248                            delegatedResponsibilities.add(accountResponsibility);
249                        }
250                    }
251                }
252            }
253            return delegatedResponsibilities;
254        }
255    
256        /**
257         * This method determines if a user has delegated responsibilities on a given account.
258         * 
259         * @param person the user to check responsibilities for
260         * @param account the account to check responsibilities on
261         * @return true if user has delegated responsibilities
262         */
263        protected boolean hasDelegatedResponsibility(Person person, Account account) {
264            boolean hasResponsibility = false;
265            Criteria criteria = new Criteria();
266            criteria.addEqualTo("accountDelegateSystemId", person.getPrincipalId());
267            criteria.addEqualTo("chartOfAccountsCode", account.getChartOfAccountsCode());
268            criteria.addEqualTo("accountNumber", account.getAccountNumber());
269            Collection accountDelegates = getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(AccountDelegate.class, criteria));
270            for (Iterator iter = accountDelegates.iterator(); iter.hasNext() && !hasResponsibility;) {
271                AccountDelegate accountDelegate = (AccountDelegate) iter.next();
272                if (accountDelegate.isActive()) {
273                    // the start_date should never be null in the real world, but
274                    // there is some test data that
275                    // contains null startDates, therefore this check.
276                    if (ObjectUtils.isNotNull(accountDelegate.getAccountDelegateStartDate())) {
277                        if (!accountDelegate.getAccountDelegateStartDate().after(dateTimeService.getCurrentDate())) {
278                            hasResponsibility = true;
279                        }
280                    }
281                }
282            }
283            return hasResponsibility;
284        }
285    
286        /**
287         * @see org.kuali.kfs.coa.dataaccess.AccountDao#getAllAccounts()
288         * @return an iterator for all accounts
289         */
290        public Iterator getAllAccounts() {
291            LOG.debug("getAllAccounts() started");
292    
293            Criteria criteria = new Criteria();
294            return getPersistenceBrokerTemplate().getIteratorByQuery(QueryFactory.newQuery(Account.class, criteria));
295        }
296    
297        /**
298         * @see org.kuali.kfs.coa.dataaccess.AccountDao#getActiveAccountsForAccountSupervisor(java.lang.String)
299         */
300        public Iterator<Account> getActiveAccountsForAccountSupervisor(String principalId) {
301            Criteria criteria = new Criteria();
302            criteria.addEqualTo("accountsSupervisorySystemsIdentifier", principalId);
303            criteria.addEqualTo("active", true);
304            criteria.addAndCriteria(getAccountNotExpiredCriteria());
305            return (Iterator<Account>)getPersistenceBrokerTemplate().getIteratorByQuery(QueryFactory.newQuery(Account.class, criteria));
306        }
307    
308        /**
309         * @see org.kuali.kfs.coa.dataaccess.AccountDao#getActiveAccountsForFiscalOfficer(java.lang.String)
310         */
311        public Iterator<Account> getActiveAccountsForFiscalOfficer(String principalId) {
312            Criteria criteria = new Criteria();
313            criteria.addEqualTo("accountFiscalOfficerSystemIdentifier", principalId);
314            criteria.addEqualTo("active", true);
315            criteria.addAndCriteria(getAccountNotExpiredCriteria());
316            return (Iterator<Account>)getPersistenceBrokerTemplate().getIteratorByQuery(QueryFactory.newQuery(Account.class, criteria));
317        }
318    
319        /**
320         * @see org.kuali.kfs.coa.dataaccess.AccountDao#getExpiredAccountsForAccountSupervisor(java.lang.String)
321         */
322        public Iterator<Account> getExpiredAccountsForAccountSupervisor(String principalId) {
323            Criteria criteria = new Criteria();
324            criteria.addEqualTo("accountsSupervisorySystemsIdentifier", principalId);
325            criteria.addEqualTo("active", true);
326            criteria.addAndCriteria(getAccountExpiredCriteria());
327            return (Iterator<Account>)getPersistenceBrokerTemplate().getIteratorByQuery(QueryFactory.newQuery(Account.class, criteria));
328        }
329    
330        /**
331         * @see org.kuali.kfs.coa.dataaccess.AccountDao#getExpiredAccountsForFiscalOfficer(java.lang.String)
332         */
333        public Iterator<Account> getExpiredAccountsForFiscalOfficer(String principalId) {
334            Criteria criteria = new Criteria();
335            criteria.addEqualTo("accountFiscalOfficerSystemIdentifier", principalId);
336            criteria.addEqualTo("active", true);
337            criteria.addAndCriteria(getAccountExpiredCriteria());
338            return (Iterator<Account>)getPersistenceBrokerTemplate().getIteratorByQuery(QueryFactory.newQuery(Account.class, criteria));
339        }
340        
341        /**
342         * Builds a criteria to find expired accounts
343         * @return a Criteria for expired accounts
344         */
345        protected Criteria getAccountExpiredCriteria() {
346            Criteria criteria = new Criteria();
347            criteria.addNotNull("accountExpirationDate");
348            criteria.addLessOrEqualThan("accountExpirationDate", dateTimeService.getCurrentSqlDate());
349            return criteria;
350        }
351        
352        /**
353         * Builds a criteria to find non-expired accounts
354         * @return a Criteria for non-expired accounts
355         */
356        protected Criteria getAccountNotExpiredCriteria() {
357            Criteria criteria = new Criteria();
358            criteria.addIsNull("accountExpirationDate");
359            
360            Criteria notYetExpiredCriteria = new Criteria();
361            notYetExpiredCriteria.addGreaterThan("accountExpirationDate", dateTimeService.getCurrentSqlDate());
362            
363            criteria.addOrCriteria(notYetExpiredCriteria);
364            return criteria;
365        }
366    
367        /**
368         * @see org.kuali.kfs.coa.dataaccess.AccountDao#isPrincipalInAnyWayShapeOrFormAccountManager(java.lang.String)
369         */
370        public boolean isPrincipalInAnyWayShapeOrFormAccountManager(String principalId) {
371            return queryPrincipalHasAccountRole(principalId, "accountManagerSystemIdentifier");
372        }
373        
374        /**
375         * Determines if any non-closed accounts exist where the principal id is in the role of the role name
376         * @param principalId the principal id to check
377         * @param principalRoleName the name of the field on account to check for the principal id in
378         * @return true if the principal has that account role, false otherwise
379         */
380        protected boolean queryPrincipalHasAccountRole(String principalId, String principalRoleName) {
381            Criteria criteria = new Criteria();
382            criteria.addEqualTo(principalRoleName, principalId);
383            criteria.addEqualTo("active", "Y");
384            
385            ReportQueryByCriteria reportQuery = QueryFactory.newReportQuery(Account.class, criteria);
386            reportQuery.setAttributes(new String[] { "count(*)" });
387            
388            int resultCount = 0;
389            Iterator iter = getPersistenceBrokerTemplate().getReportQueryIteratorByQuery(reportQuery);
390            while (iter.hasNext()) {
391                final Object[] results = (Object[])iter.next();
392                resultCount = (results[0] instanceof Number) ? ((Number)results[0]).intValue() : new Integer(results[0].toString()).intValue();
393            }
394            return resultCount > 0;
395        }
396    
397        
398        /**
399         * @see org.kuali.kfs.coa.dataaccess.AccountDao#isPrincipalInAnyWayShapeOrFormAccountSupervisor(java.lang.String)
400         */
401        public boolean isPrincipalInAnyWayShapeOrFormAccountSupervisor(String principalId) {
402            return queryPrincipalHasAccountRole(principalId, "accountsSupervisorySystemsIdentifier");
403        }
404    
405        /**
406         * @see org.kuali.kfs.coa.dataaccess.AccountDao#isPrincipalInAnyWayShapeOrFormFiscalOfficer(java.lang.String)
407         */
408        public boolean isPrincipalInAnyWayShapeOrFormFiscalOfficer(String principalId) {
409            return queryPrincipalHasAccountRole(principalId, "accountFiscalOfficerSystemIdentifier");
410        }
411    
412        public void setDateTimeService(DateTimeService dateTimeService) {
413            this.dateTimeService = dateTimeService;
414        }
415    
416        /**
417         * @see org.kuali.kfs.coa.dataaccess.AccountDao#getAccountsForAccountNumber(java.lang.String)
418         */
419        public Collection<Account> getAccountsForAccountNumber(String accountNumber) {
420            Criteria criteria = new Criteria();
421            criteria.addEqualTo(KFSPropertyConstants.ACCOUNT_NUMBER, accountNumber);
422    
423            return getPersistenceBrokerTemplate().getCollectionByQuery(QueryFactory.newQuery(Account.class, criteria));
424        }
425    }
426