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.gl.service.impl;
017    
018    import java.util.ArrayList;
019    import java.util.Iterator;
020    import java.util.List;
021    import java.util.Map;
022    
023    import org.apache.commons.collections.IteratorUtils;
024    import org.kuali.kfs.coa.service.ObjectTypeService;
025    import org.kuali.kfs.gl.GeneralLedgerConstants;
026    import org.kuali.kfs.gl.OJBUtility;
027    import org.kuali.kfs.gl.businessobject.AccountBalance;
028    import org.kuali.kfs.gl.dataaccess.AccountBalanceDao;
029    import org.kuali.kfs.gl.service.AccountBalanceService;
030    import org.kuali.kfs.sys.KFSKeyConstants;
031    import org.kuali.kfs.sys.context.SpringContext;
032    import org.kuali.rice.kns.service.KualiConfigurationService;
033    import org.springframework.transaction.annotation.Transactional;
034    
035    /**
036     * The basic implementation of the AccountBalanceService interface
037     */
038    @Transactional
039    public class AccountBalanceServiceImpl implements AccountBalanceService {
040        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountBalanceServiceImpl.class);
041    
042        AccountBalanceDao accountBalanceDao;
043        KualiConfigurationService kualiConfigurationService;
044    
045        /**
046         * Defers to the DAO to find the consolidated account balances, based on the keys given in the Map parameter
047         * 
048         * @param fieldValues the input fields and values
049         * @return the summary records of account balance entries
050         * @see org.kuali.kfs.gl.service.AccountBalanceService#findConsolidatedAvailableAccountBalance(java.util.Map)
051         */
052        public Iterator findConsolidatedAvailableAccountBalance(Map fieldValues) {
053            LOG.debug("findConsolidatedAvailableAccountBalance() started");
054    
055            return accountBalanceDao.findConsolidatedAvailableAccountBalance(fieldValues);
056        }
057    
058        /**
059         * Given the map of parameters, constructs a query to find all qualifying account balance records
060         * 
061         * @param fieldValues the input fields and values
062         * @param isConsolidated determine whether the search results are consolidated
063         * @return a collection of account balance entries
064         * @see org.kuali.kfs.gl.service.AccountBalanceService#findAvailableAccountBalance(java.util.Map)
065         */
066        public Iterator findAvailableAccountBalance(Map fieldValues) {
067            LOG.debug("findAvailableAccountBalance() started");
068    
069            return accountBalanceDao.findAvailableAccountBalance(fieldValues);
070        }
071    
072        /**
073         * This finds account balances grouped by consolidation
074         * 
075         * @param universityFiscalYear the fiscal year account to find account balances for
076         * @param chartOfAccountsCode the chart of accounts code to find account balances for
077         * @param accountNumber the account number to find account balances for
078         * @param subAccountNumber the sub account number to find account balances for
079         * @param isCostShareExcluded should account balances found have cost share information excluded?
080         * @param isConsolidated should account balances found be consolidated?
081         * @param pendingEntryCode should pending entries be included in the query?
082         * @return a List of qualifying account balance records
083         * @see org.kuali.kfs.gl.service.AccountBalanceService#findAccountBalanceByConsolidation(java.util.Map, boolean, boolean)
084         */
085        public List findAccountBalanceByConsolidation(Integer universityFiscalYear, String chartOfAccountsCode, String accountNumber, String subAccountNumber, boolean isCostShareExcluded, boolean isConsolidated, int pendingEntryCode) {
086            LOG.debug("findAccountBalanceByConsolidation() started");
087    
088            ObjectTypeService objectTypeService = (ObjectTypeService) SpringContext.getBean(ObjectTypeService.class);
089    
090            String[] incomeObjectTypes = objectTypeService.getBasicIncomeObjectTypes(universityFiscalYear).toArray(new String[0]);
091            String[] incomeTransferObjectTypes = { objectTypeService.getIncomeTransferObjectType(universityFiscalYear) };
092            String[] expenseObjectTypes = objectTypeService.getBasicExpenseObjectTypes(universityFiscalYear).toArray(new String[0]);
093            String[] expenseTransferObjectTypes = { objectTypeService.getExpenseTransferObjectType(universityFiscalYear) };
094    
095            // Consolidate all object types into one array (yes I could have used lists, but it was just as many lines of code than
096            // this)
097            String[] allObjectTypes = new String[incomeObjectTypes.length + incomeTransferObjectTypes.length + expenseObjectTypes.length + expenseTransferObjectTypes.length];
098            int count = 0;
099            for (int i = 0; i < incomeObjectTypes.length; i++) {
100                allObjectTypes[count++] = incomeObjectTypes[i];
101            }
102            for (int i = 0; i < incomeTransferObjectTypes.length; i++) {
103                allObjectTypes[count++] = incomeTransferObjectTypes[i];
104            }
105            for (int i = 0; i < expenseObjectTypes.length; i++) {
106                allObjectTypes[count++] = expenseObjectTypes[i];
107            }
108            for (int i = 0; i < expenseTransferObjectTypes.length; i++) {
109                allObjectTypes[count++] = expenseTransferObjectTypes[i];
110            }
111    
112            // Put the total lines at the beginning of the list
113            List results = new ArrayList();
114            AccountBalance income = new AccountBalance(kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.INCOME));
115            AccountBalance incomeTransfers = new AccountBalance(kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.INCOME_FROM_TRANSFERS));
116            AccountBalance incomeTotal = new AccountBalance(kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.INCOME_TOTAL));
117            AccountBalance expense = new AccountBalance(kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.EXPENSE));
118            AccountBalance expenseTransfers = new AccountBalance(kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.EXPENSE_FROM_TRANSFERS));
119            AccountBalance expenseTotal = new AccountBalance(kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.EXPENSE_TOTAL));
120            AccountBalance total = new AccountBalance(kualiConfigurationService.getPropertyString(KFSKeyConstants.AccountBalanceService.TOTAL));
121    
122            results.add(income);
123            results.add(incomeTransfers);
124            results.add(incomeTotal);
125            results.add(expense);
126            results.add(expenseTransfers);
127            results.add(expenseTotal);
128            results.add(total);
129    
130            // If you want a sub account, you can't do consolidated
131            if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
132                subAccountNumber = subAccountNumber.toUpperCase();
133                isConsolidated = false;
134            }
135    
136            // Get the data
137            List balances = accountBalanceDao.findAccountBalanceByConsolidationByObjectTypes(allObjectTypes, universityFiscalYear, chartOfAccountsCode, accountNumber, isCostShareExcluded, isConsolidated, pendingEntryCode);
138    
139            // Convert it to Account Balances
140            for (Iterator iter = balances.iterator(); iter.hasNext();) {
141                Map bal = (Map) iter.next();
142                AccountBalance bbc = new AccountBalance(AccountBalance.TYPE_CONSOLIDATION, bal, universityFiscalYear, chartOfAccountsCode, accountNumber);
143    
144                if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
145                    if (bbc.getSubAccountNumber().equals(subAccountNumber)) {
146                        results.add(bbc);
147                    }
148                }
149                else {
150                    results.add(bbc);
151                }
152            }
153    
154            // Calculate totals
155    
156            // Get balances for these parameters, then based on the object type code, put balances into the correct summary line
157            List data = accountBalanceDao.findAccountBalanceByConsolidationByObjectTypes(incomeObjectTypes, universityFiscalYear, chartOfAccountsCode, accountNumber, isCostShareExcluded, isConsolidated, pendingEntryCode);
158            for (Iterator iter = data.iterator(); iter.hasNext();) {
159                Map bal = (Map) iter.next();
160                AccountBalance bbc = new AccountBalance(AccountBalance.TYPE_CONSOLIDATION, bal, universityFiscalYear, chartOfAccountsCode, accountNumber);
161                if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
162                    if (bbc.getSubAccountNumber().equals(subAccountNumber)) {
163                        income.add(bbc);
164                        incomeTotal.add(bbc);
165                    }
166                }
167                else {
168                    String transferExpenseCode = bbc.getFinancialObject().getFinancialObjectLevel().getFinancialConsolidationObject().getFinConsolidationObjectCode();
169                    if (transferExpenseCode.equals(GeneralLedgerConstants.INCOME_OR_EXPENSE_TRANSFER_CONSOLIDATION_CODE)) {
170                        incomeTransfers.add(bbc);
171                        incomeTotal.add(bbc);                    
172                        }
173                    else {
174                        income.add(bbc);
175                        incomeTotal.add(bbc);
176                    }
177                }
178            }
179    
180            data = accountBalanceDao.findAccountBalanceByConsolidationByObjectTypes(incomeTransferObjectTypes, universityFiscalYear, chartOfAccountsCode, accountNumber, isCostShareExcluded, isConsolidated, pendingEntryCode);
181            for (Iterator iter = data.iterator(); iter.hasNext();) {
182                Map bal = (Map) iter.next();
183                AccountBalance bbc = new AccountBalance(AccountBalance.TYPE_CONSOLIDATION, bal, universityFiscalYear, chartOfAccountsCode, accountNumber);
184                if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
185                    if (bbc.getSubAccountNumber().equals(subAccountNumber)) {
186                        incomeTransfers.add(bbc);
187                        incomeTotal.add(bbc);
188                    }
189                }
190                else {
191                    incomeTransfers.add(bbc);
192                    incomeTotal.add(bbc);
193                }
194            }
195    
196            data = accountBalanceDao.findAccountBalanceByConsolidationByObjectTypes(expenseObjectTypes, universityFiscalYear, chartOfAccountsCode, accountNumber, isCostShareExcluded, isConsolidated, pendingEntryCode);
197            for (Iterator iter = data.iterator(); iter.hasNext();) {
198                Map bal = (Map) iter.next();
199                AccountBalance bbc = new AccountBalance(AccountBalance.TYPE_CONSOLIDATION, bal, universityFiscalYear, chartOfAccountsCode, accountNumber);
200                if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
201                    if (bbc.getSubAccountNumber().equals(subAccountNumber)) {
202                        expense.add(bbc);
203                        expenseTotal.add(bbc);
204                    }
205                }
206                else {
207                    String transferExpenseCode = bbc.getFinancialObject().getFinancialObjectLevel().getFinancialConsolidationObject().getFinConsolidationObjectCode();
208                    if (transferExpenseCode.equals(GeneralLedgerConstants.INCOME_OR_EXPENSE_TRANSFER_CONSOLIDATION_CODE)) {
209                        expenseTransfers.add(bbc);
210                        expenseTotal.add(bbc);                    
211                    }
212                    else {
213                        expense.add(bbc);
214                        expenseTotal.add(bbc);
215                    }
216                }
217            }
218    
219            data = accountBalanceDao.findAccountBalanceByConsolidationByObjectTypes(expenseTransferObjectTypes, universityFiscalYear, chartOfAccountsCode, accountNumber, isCostShareExcluded, isConsolidated, pendingEntryCode);
220            for (Iterator iter = data.iterator(); iter.hasNext();) {
221                Map bal = (Map) iter.next();
222                AccountBalance bbc = new AccountBalance(AccountBalance.TYPE_CONSOLIDATION, bal, universityFiscalYear, chartOfAccountsCode, accountNumber);
223                if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
224                    if (bbc.getSubAccountNumber().equals(subAccountNumber)) {
225                        expenseTransfers.add(bbc);
226                        expenseTotal.add(bbc);
227                    }
228                }
229                else {
230                    expenseTransfers.add(bbc);
231                    expenseTotal.add(bbc);
232                }
233            }
234    
235            // Add up variances
236            income.getDummyBusinessObject().setGenericAmount(income.getAccountLineActualsBalanceAmount().add(income.getAccountLineEncumbranceBalanceAmount()).subtract(income.getCurrentBudgetLineBalanceAmount()));
237            incomeTransfers.getDummyBusinessObject().setGenericAmount(incomeTransfers.getAccountLineActualsBalanceAmount().add(incomeTransfers.getAccountLineEncumbranceBalanceAmount()).subtract(incomeTransfers.getCurrentBudgetLineBalanceAmount()));
238            incomeTotal.getDummyBusinessObject().setGenericAmount(income.getDummyBusinessObject().getGenericAmount().add(incomeTransfers.getDummyBusinessObject().getGenericAmount()));
239    
240            expense.getDummyBusinessObject().setGenericAmount(expense.getCurrentBudgetLineBalanceAmount().subtract(expense.getAccountLineActualsBalanceAmount()).subtract(expense.getAccountLineEncumbranceBalanceAmount()));
241            expenseTransfers.getDummyBusinessObject().setGenericAmount(expenseTransfers.getCurrentBudgetLineBalanceAmount().subtract(expenseTransfers.getAccountLineActualsBalanceAmount()).subtract(expenseTransfers.getAccountLineEncumbranceBalanceAmount()));
242            expenseTotal.getDummyBusinessObject().setGenericAmount(expense.getDummyBusinessObject().getGenericAmount().add(expenseTransfers.getDummyBusinessObject().getGenericAmount()));
243    
244            total.getDummyBusinessObject().setGenericAmount(incomeTotal.getDummyBusinessObject().getGenericAmount().add(expenseTotal.getDummyBusinessObject().getGenericAmount()));
245    
246            return results;
247        }
248    
249        /**
250         * Finds account balances grouped by object level
251         * 
252         * @param universityFiscalYear the fiscal year account to find account balances for
253         * @param chartOfAccountsCode the chart of accounts code to find account balances for
254         * @param accountNumber the account number to find account balances for
255         * @param subAccountNumber the sub account number to find account balances for
256         * @param financialConsolidationCode the consolidation code to find account balances for
257         * @param isCostShareExcluded should account balances found have cost share information excluded?
258         * @param isConsolidated should account balances found be consolidated?
259         * @param pendingEntryCode should pending entries be included in the query?
260         * @return a List of qualifying account balance records
261         * @see org.kuali.kfs.gl.service.AccountBalanceService#findAccountBalanceByLevel(java.lang.Integer, java.lang.String,
262         *      java.lang.String, java.lang.String, java.lang.String, boolean, boolean, int)
263         */
264        public List findAccountBalanceByLevel(Integer universityFiscalYear, String chartOfAccountsCode, String accountNumber, String subAccountNumber, String financialConsolidationObjectCode, boolean isCostShareExcluded, boolean isConsolidated, int pendingEntryCode) {
265            LOG.debug("findAccountBalanceByLevel() started");
266    
267            List results = new ArrayList();
268    
269            // If you want a sub account, you can't do consolidated
270            if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
271                subAccountNumber = subAccountNumber.toUpperCase();
272                isConsolidated = false;
273            }
274    
275            // Get the data
276            List balances = accountBalanceDao.findAccountBalanceByLevel(universityFiscalYear, chartOfAccountsCode, accountNumber, financialConsolidationObjectCode, isCostShareExcluded, isConsolidated, pendingEntryCode);
277    
278            // Convert it to Account Balances
279            for (Iterator iter = balances.iterator(); iter.hasNext();) {
280                Map bal = (Map) iter.next();
281                bal.put(GeneralLedgerConstants.ColumnNames.CONSOLIDATION_OBJECT_CODE, financialConsolidationObjectCode);
282                AccountBalance bbc = new AccountBalance(AccountBalance.TYPE_LEVEL, bal, universityFiscalYear, chartOfAccountsCode, accountNumber);
283                if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
284                    if (bbc.getSubAccountNumber().equals(subAccountNumber)) {
285                        results.add(bbc);
286                    }
287                }
288                else {
289                    results.add(bbc);
290                }
291            }
292    
293            return results;
294        }
295    
296        /**
297         * Finds account balances that match the qualifying parameters, grouped by object code
298         * 
299         * @param universityFiscalYear the fiscal year account to find account balances for
300         * @param chartOfAccountsCode the chart of accounts code to find account balances for
301         * @param accountNumber the account number to find account balances for
302         * @param subAccountNumber the sub account number to find account balances for
303         * @param financialObjectLevelCode the financial object level code to find account balances for
304         * @param financialReportingSortCode the reporting sort code to sort account balances by
305         * @param isCostShareExcluded should account balances found have cost share information excluded?
306         * @param isConsolidated should account balances found be consolidated?
307         * @param pendingEntryCode should pending entries be included in the query?
308         * @return a List of qualifying account balance records
309         * @see org.kuali.kfs.gl.service.AccountBalanceService#findAccountBalanceByObject(java.lang.Integer, java.lang.String,
310         *      java.lang.String, java.lang.String, java.lang.String, java.lang.String, boolean, boolean, int)
311         */
312        public List findAccountBalanceByObject(Integer universityFiscalYear, String chartOfAccountsCode, String accountNumber, String subAccountNumber, String financialObjectLevelCode, String financialReportingSortCode, boolean isCostShareExcluded, boolean isConsolidated, int pendingEntryCode) {
313            LOG.debug("findAccountBalanceByObject() started");
314    
315            List results = new ArrayList();
316    
317            // If you want a sub account, you can't do consolidated
318            if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
319                subAccountNumber = subAccountNumber.toUpperCase();
320                isConsolidated = false;
321            }
322    
323            // Get the data
324            List balances = accountBalanceDao.findAccountBalanceByObject(universityFiscalYear, chartOfAccountsCode, accountNumber, financialObjectLevelCode, financialReportingSortCode, isCostShareExcluded, isConsolidated, pendingEntryCode);
325    
326            // Convert it to Account Balances
327            for (Iterator iter = balances.iterator(); iter.hasNext();) {
328                Map bal = (Map) iter.next();
329                bal.put(GeneralLedgerConstants.ColumnNames.OBJECT_LEVEL_CODE, financialObjectLevelCode);
330                AccountBalance bbc = new AccountBalance(AccountBalance.TYPE_OBJECT, bal, universityFiscalYear, chartOfAccountsCode, accountNumber);
331                if ((subAccountNumber != null) && (subAccountNumber.length() > 0)) {
332                    if (bbc.getSubAccountNumber().equals(subAccountNumber)) {
333                        results.add(bbc);
334                    }
335                }
336                else {
337                    results.add(bbc);
338                }
339            }
340    
341            return results;
342        }
343    
344        /**
345         * Defers to the DAO to save the account balance.
346         * 
347         * @param ab account balance record to save
348         * @see org.kuali.kfs.gl.service.AccountBalanceService#save(org.kuali.kfs.gl.businessobject.AccountBalance)
349         */
350        public void save(AccountBalance ab) {
351            accountBalanceDao.save(ab);
352        }
353    
354        /**
355         * Purge an entire fiscal year for a single chart.
356         * 
357         * @param chartOfAccountsCode the chart of accounts of account balances to purge
358         * @param year the fiscal year of account balances to purge
359         */
360        public void purgeYearByChart(String chartOfAccountsCode, int year) {
361            LOG.debug("purgeYearByChart() started");
362    
363            accountBalanceDao.purgeYearByChart(chartOfAccountsCode, year);
364        }
365    
366        /**
367         * This method gets the number of the available account balances according to input fields and values
368         * 
369         * @param fieldValues the input fields and values
370         * @param isConsolidated determine whether the search results are consolidated
371         * @return the number of the available account balances
372         * @see org.kuali.kfs.gl.service.AccountBalanceService#getAvailableAccountBalanceCount(java.util.Map, boolean)
373         */
374        public Integer getAvailableAccountBalanceCount(Map fieldValues, boolean isConsolidated) {
375            Integer recordCount = null;
376            if (!isConsolidated) {
377                recordCount = OJBUtility.getResultSizeFromMap(fieldValues, new AccountBalance()).intValue();
378            }
379            else {
380                Iterator recordCountIterator = accountBalanceDao.findConsolidatedAvailableAccountBalance(fieldValues);
381                // TODO: WL: why build a list and waste time/memory when we can just iterate through the iterator and do a count?
382                List recordCountList = IteratorUtils.toList(recordCountIterator);
383                recordCount = recordCountList.size();
384            }
385            return recordCount;
386        }
387    
388        public void setKualiConfigurationService(KualiConfigurationService kcs) {
389            kualiConfigurationService = kcs;
390        }
391    
392        public void setAccountBalanceDao(AccountBalanceDao accountBalanceDao) {
393            this.accountBalanceDao = accountBalanceDao;
394        }
395    }