001: /*
002: * Copyright 2006-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.gl.service.impl;
017:
018: import java.util.ArrayList;
019: import java.util.Arrays;
020: import java.util.Collection;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025:
026: import org.apache.commons.collections.IteratorUtils;
027: import org.kuali.core.util.KualiDecimal;
028: import org.kuali.kfs.bo.Options;
029: import org.kuali.kfs.context.SpringContext;
030: import org.kuali.kfs.service.OptionsService;
031: import org.kuali.module.chart.bo.Account;
032: import org.kuali.module.financial.service.UniversityDateService;
033: import org.kuali.module.gl.bo.Balance;
034: import org.kuali.module.gl.bo.GlSummary;
035: import org.kuali.module.gl.dao.BalanceDao;
036: import org.kuali.module.gl.service.BalanceService;
037: import org.kuali.module.gl.util.OJBUtility;
038: import org.springframework.transaction.annotation.Transactional;
039:
040: /**
041: * This class is the OJB implementation of the Balance Service
042: */
043: @Transactional
044: public class BalanceServiceImpl implements BalanceService {
045: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
046: .getLogger(BalanceServiceImpl.class);
047:
048: protected BalanceDao balanceDao;
049: protected OptionsService optionsService;
050:
051: // must have no asset, liability or fund balance balances other than object code 9899
052:
053: String[] assetLiabilityFundBalanceObjectTypeCodes = null;
054: String[] encumbranceBaseBudgetBalanceTypeCodes = null;
055: String[] actualBalanceCodes = null;
056: String[] incomeObjectTypeCodes = null;
057: String[] expenseObjectTypeCodes = null;
058:
059: /**
060: * Turns an array of Strings into a List of Strings
061: *
062: * @param s an array of Strings
063: * @return an implementation of Collection (a List) of Strings
064: */
065: private Collection wrap(String[] s) {
066: return Arrays.asList(s);
067: }
068:
069: /**
070: * @param universityFiscalYear the fiscal year to find balances for
071: * @param balanceTypeCodes the balance types to summarize
072: * @return a list of summarized GL balances
073: * @see org.kuali.module.gl.service.BalanceService#getGlSummary(int, java.util.List)
074: */
075: public List getGlSummary(int universityFiscalYear,
076: List<String> balanceTypeCodes) {
077: LOG.debug("getGlSummary() started");
078:
079: List sum = new ArrayList();
080:
081: Iterator i = balanceDao.getGlSummary(universityFiscalYear,
082: balanceTypeCodes);
083: while (i.hasNext()) {
084: Object[] data = (Object[]) i.next();
085: sum.add(new GlSummary(data));
086: }
087: return sum;
088: }
089:
090: /**
091: * Defers to the DAO to find all balances in the fiscal year.
092: *
093: * @param fiscalYear the fiscal year to find balances for
094: * @return an Iterator full of balances from the given fiscal year
095: * @see org.kuali.module.gl.service.BalanceService#findBalancesForFiscalYear(java.lang.Integer)
096: */
097: public Iterator<Balance> findBalancesForFiscalYear(
098: Integer fiscalYear) {
099:
100: return (Iterator<Balance>) balanceDao
101: .findBalancesForFiscalYear(fiscalYear);
102: }
103:
104: /**
105: * Checks the given account to see if there are any non zero asset fund liability fund balances for them
106: *
107: * @param account an account to find balances for
108: * @return true if there are non zero asset liability fund balances, false if otherwise
109: * @see org.kuali.module.gl.service.BalanceService#hasAssetLiabilityFundBalanceBalances(org.kuali.module.chart.bo.Account)
110: */
111: public boolean hasAssetLiabilityFundBalanceBalances(Account account) {
112:
113: /*
114: * Here is an excerpt from the original Oracle trigger: SELECT fin_object_cd FROM gl_balance_t WHERE
115: * univ_fiscal_yr = p_univ_fiscal_yr AND fin_coa_cd = p_fin_coa_cd AND account_nbr = p_account_nbr AND fin_object_cd != '9899'
116: * AND fin_obj_typ_cd IN ('AS', 'LI', 'FB') AND fin_balance_typ_cd = 'AC' GROUP BY fin_object_cd HAVING
117: * ABS(SUM(fin_beg_bal_ln_amt + acln_annl_bal_amt)) > 0); added absolute value function to sum--prevents the case of 2 entries
118: * (1 pos and 1 neg) from canceling each other out and allowing the acct to be closed when it shouldn't be.
119: */
120:
121: Integer fiscalYear = SpringContext.getBean(
122: UniversityDateService.class).getCurrentFiscalYear();
123: ArrayList fundBalanceObjectCodes = new ArrayList();
124: fundBalanceObjectCodes
125: .add(null == account.getChartOfAccounts() ? null
126: : account.getChartOfAccounts()
127: .getFundBalanceObjectCode());
128: Iterator balances = balanceDao.findBalances(account,
129: fiscalYear, null, fundBalanceObjectCodes,
130: wrap(getAssetLiabilityFundBalanceBalanceTypeCodes()),
131: wrap(getActualBalanceCodes()));
132:
133: KualiDecimal begin;
134: KualiDecimal annual;
135:
136: // TODO KULCOA-335 - is absolute value necessary to prevent obscure sets of values
137: // from masking accounts that should remain open?
138:
139: Map groups = new HashMap();
140:
141: while (balances.hasNext()) {
142: Balance balance = (Balance) balances.next();
143: begin = balance.getBeginningBalanceLineAmount();
144: annual = balance.getAccountLineAnnualBalanceAmount();
145:
146: String objectCode = balance.getObjectCode();
147:
148: KualiDecimal runningTotal = (KualiDecimal) groups
149: .get(objectCode);
150:
151: if (runningTotal == null) {
152: runningTotal = new KualiDecimal(0);
153: }
154:
155: runningTotal = runningTotal.add(begin);
156: runningTotal = runningTotal.add(annual);
157:
158: groups.put(objectCode, runningTotal);
159:
160: }
161:
162: boolean success = false;
163:
164: Iterator iter = groups.keySet().iterator();
165: while (iter.hasNext()) {
166: success |= ((KualiDecimal) groups.get(iter.next()))
167: .isNonZero();
168: }
169:
170: return success;
171:
172: }
173:
174: /**
175: * Given an iterator of balances, this returns the sum of each balance's beginning balance line amount + annual account linge balance amount
176: *
177: * @param balances an Iterator of balances to sum
178: * @return the sum of all of those balances
179: */
180: private KualiDecimal sumBalances(Iterator balances) {
181: KualiDecimal runningTotal = new KualiDecimal(0);
182:
183: KualiDecimal begin;
184: KualiDecimal annual;
185:
186: while (balances.hasNext()) {
187: Balance balance = (Balance) balances.next();
188: begin = balance.getBeginningBalanceLineAmount();
189: annual = balance.getAccountLineAnnualBalanceAmount();
190:
191: runningTotal = runningTotal.add(begin);
192: runningTotal = runningTotal.add(annual);
193: }
194:
195: return runningTotal;
196:
197: }
198:
199: /**
200: * Returns the sum of balances considered as income for the given account
201: *
202: * @param account the account to find income balances for
203: * @return the sum of income balances
204: */
205: protected KualiDecimal incomeBalances(Account account) {
206:
207: /*
208: * SELECT SUM(fin_beg_bal_ln_amt + acln_annl_bal_amt) INTO v_y FROM gl_balance_t WHERE univ_fiscal_yr = p_univ_fiscal_yr AND
209: * fin_coa_cd = p_fin_coa_cd AND account_nbr = p_account_nbr AND (fin_object_cd = '9899' OR fin_obj_typ_cd IN ('CH', 'IC', 'IN',
210: * 'TI')) AND fin_balance_typ_cd = 'AC';
211: *
212: * @return
213: */
214:
215: Integer fiscalYear = SpringContext.getBean(
216: UniversityDateService.class).getCurrentFiscalYear();
217:
218: ArrayList fundBalanceObjectCodes = new ArrayList();
219: fundBalanceObjectCodes.add(account.getChartOfAccounts()
220: .getFundBalanceObjectCode());
221: Iterator balances = balanceDao.findBalances(account,
222: fiscalYear, fundBalanceObjectCodes, null,
223: wrap(getIncomeObjectTypeCodes()),
224: wrap(getActualBalanceCodes()));
225:
226: return sumBalances(balances);
227:
228: }
229:
230: /**
231: * Sums all the balances associated with a given account that would be considered "expense" balances
232: *
233: * @param account an account to find expense balances for
234: * @return the sum of those balances
235: */
236: protected KualiDecimal expenseBalances(Account account) {
237: /*
238: * Here is an excerpt from the original Oracle Trigger: SELECT SUM(fin_beg_bal_ln_amt || acln_annl_bal_amt) INTO v_x FROM
239: * gl_balance_t WHERE univ_fiscal_yr = p_univ_fiscal_yr AND fin_coa_cd = p_fin_coa_cd AND account_nbr = p_account_nbr AND
240: * fin_obj_typ_cd IN ('EE', 'ES', 'EX', 'TE') AND fin_balance_typ_cd = 'AC'; This method...
241: */
242:
243: Integer fiscalYear = SpringContext.getBean(
244: UniversityDateService.class).getCurrentFiscalYear();
245: Iterator balances = balanceDao.findBalances(account,
246: fiscalYear, null, null,
247: wrap(getExpenseObjectTypeCodes()),
248: wrap(getActualBalanceCodes()));
249:
250: return sumBalances(balances);
251:
252: }
253:
254: /**
255: * Checks to see if the total income balances for the given account equal the total expense balances for the given account
256: *
257: * @param an account to find balances for
258: * @return true if income balances equal expense balances, false otherwise
259: * @see org.kuali.module.gl.service.BalanceService#fundBalanceWillNetToZero(org.kuali.module.chart.bo.Account)
260: */
261: public boolean fundBalanceWillNetToZero(Account account) {
262: KualiDecimal income = incomeBalances(account);
263: KualiDecimal expense = expenseBalances(account);
264:
265: return income.equals(expense);
266: }
267:
268: /**
269: * Finds all of the encumbrance balances for the given account, and figures out if those encumbrances will have a net impact on the budget
270: *
271: * @param account an account to find balances for
272: * @return true if summed encumbrances for the account are not zero (meaning encumbrances will have a net impact on the budget), false if otherwise
273: * @see org.kuali.module.gl.service.BalanceService#hasEncumbrancesOrBaseBudgets(org.kuali.module.chart.bo.Account)
274: */
275: public boolean hasEncumbrancesOrBaseBudgets(Account account) {
276:
277: /*
278: * check for Encumbrances and base budgets Here is an excerpt from the original Oracle Trigger: SELECT SUM(fin_beg_bal_ln_amt +
279: * acln_annl_bal_amt) INTO v_y FROM gl_balance_t WHERE univ_fiscal_yr = p_univ_fiscal_yr AND fin_coa_cd = p_fin_coa_cd AND
280: * account_nbr = p_account_nbr AND fin_balance_typ_cd IN ('EX', 'IE', 'PE', 'BB'); v_rowcnt := SQL%ROWCOUNT;
281: */
282:
283: Integer fiscalYear = SpringContext.getBean(
284: UniversityDateService.class).getCurrentFiscalYear();
285: Iterator balances = balanceDao.findBalances(account,
286: fiscalYear, null, null, null,
287: wrap(getEncumbranceBaseBudgetBalanceTypeCodes()));
288:
289: return sumBalances(balances).isNonZero();
290: }
291:
292: /**
293: * Returns whether or not the beginning budget is loaded for the given account. Of course, it doesn't
294: * really check the account...just the options for the current year to see if all the beginning balances
295: * have been loaded
296: *
297: * @param an account to check whether the beginning balance is loaded for
298: * @return true if the beginning balance is loaded, false otherwise
299: * @see org.kuali.module.gl.service.BalanceService#beginningBalanceLoaded(org.kuali.module.chart.bo.Account)
300: */
301: public boolean beginningBalanceLoaded(Account account) {
302: return optionsService.getCurrentYearOptions()
303: .isFinancialBeginBalanceLoadInd();
304: }
305:
306: /**
307: * Determines if the account has asset/liability balances associated with it that will have a net impact
308: *
309: * @param account an account to check balances for
310: * @return true if the account has an asset liability balance, false otherwise
311: * @see org.kuali.module.gl.service.BalanceService#hasAssetLiabilityOrFundBalance(org.kuali.module.chart.bo.Account)
312: */
313: public boolean hasAssetLiabilityOrFundBalance(Account account) {
314: return hasAssetLiabilityFundBalanceBalances(account)
315: || !fundBalanceWillNetToZero(account)
316: || hasEncumbrancesOrBaseBudgets(account);
317: }
318:
319: /**
320: * Saves the balance in a no-nonsense, straight away, three piece suit sort of way
321: *
322: * @param b the balance to save
323: * @see org.kuali.module.gl.service.BalanceService#save(org.kuali.module.gl.bo.Balance)
324: */
325: public void save(Balance b) {
326: balanceDao.save(b);
327: }
328:
329: public void setBalanceDao(BalanceDao balanceDao) {
330: this .balanceDao = balanceDao;
331: }
332:
333: public void setOptionsService(OptionsService optionsService) {
334: this .optionsService = optionsService;
335: }
336:
337: /**
338: * This method finds the summary records of balance entries according to input fields an values, using the DAO
339: *
340: * @param fieldValues the input fields an values
341: * @param isConsolidated consolidation option is applied or not
342: * @return the summary records of balance entries
343: * @see org.kuali.module.gl.service.BalanceService#findCashBalance(java.util.Map, boolean)
344: */
345: public Iterator findCashBalance(Map fieldValues,
346: boolean isConsolidated) {
347: LOG.debug("findCashBalance() started");
348:
349: return balanceDao.findCashBalance(fieldValues, isConsolidated);
350: }
351:
352: /**
353: * This method gets the size of cash balance entries according to input fields and values
354: *
355: * @param fieldValues the input fields and values
356: * @param isConsolidated consolidation option is applied or not
357: * @return the count of cash balance entries
358: * @see org.kuali.module.gl.service.BalanceService#getCashBalanceRecordCount(java.util.Map, boolean)
359: */
360: public Integer getCashBalanceRecordCount(Map fieldValues,
361: boolean isConsolidated) {
362: LOG.debug("getCashBalanceRecordCount() started");
363:
364: Integer recordCount = new Integer(0);
365: if (!isConsolidated) {
366: recordCount = balanceDao
367: .getDetailedCashBalanceRecordCount(fieldValues);
368: } else {
369: Iterator recordCountIterator = balanceDao
370: .getConsolidatedCashBalanceRecordCount(fieldValues);
371: // TODO: WL: why build a list and waste time/memory when we can just iterate through the iterator and do a count?
372: List recordCountList = IteratorUtils
373: .toList(recordCountIterator);
374: recordCount = recordCountList.size();
375: }
376: return recordCount;
377: }
378:
379: /**
380: * This method gets the size of balance entries according to input fields and values
381: *
382: * @param fieldValues the input fields and values
383: * @param isConsolidated consolidation option is applied or not
384: * @return the size of balance entries
385: * @see org.kuali.module.gl.service.BalanceService#findBalance(java.util.Map, boolean)
386: */
387: public Iterator findBalance(Map fieldValues, boolean isConsolidated) {
388: LOG.debug("findBalance() started");
389: return balanceDao.findBalance(fieldValues, isConsolidated);
390: }
391:
392: /**
393: * This method finds the summary records of balance entries according to input fields and values
394: *
395: * @param fieldValues the input fields and values
396: * @param isConsolidated consolidation option is applied or not
397: * @return the summary records of balance entries
398: * @see org.kuali.module.gl.service.BalanceService#getBalanceRecordCount(java.util.Map, boolean)
399: */
400: public Integer getBalanceRecordCount(Map fieldValues,
401: boolean isConsolidated) {
402: LOG.debug("getBalanceRecordCount() started");
403:
404: Integer recordCount = null;
405: if (!isConsolidated) {
406: recordCount = OJBUtility.getResultSizeFromMap(fieldValues,
407: new Balance()).intValue();
408: } else {
409: Iterator recordCountIterator = balanceDao
410: .getConsolidatedBalanceRecordCount(fieldValues);
411: // TODO: WL: why build a list and waste time/memory when we can just iterate through the iterator and do a count?
412: List recordCountList = IteratorUtils
413: .toList(recordCountIterator);
414: recordCount = recordCountList.size();
415: }
416: return recordCount;
417: }
418:
419: /**
420: * Purge the balance table by year/chart
421: *
422: * @param chart the chart of balances to purge
423: * @param year the year of balances to purge
424: */
425: public void purgeYearByChart(String chart, int year) {
426: LOG.debug("purgeYearByChart() started");
427:
428: balanceDao.purgeYearByChart(chart, year);
429: }
430:
431: /**
432: * Private method to load the values from the system options service and store them locally for later use.
433: */
434: private void loadConstantsFromOptions() {
435: LOG.debug("loadConstantsFromOptions() started");
436: Options options = optionsService.getCurrentYearOptions();
437: // String[] actualBalanceCodes = new String[] { "AC" };
438: actualBalanceCodes = new String[] { options
439: .getActualFinancialBalanceTypeCd() }; // AC
440: // String[] incomeObjectTypeCodes = new String[] { "CH", "IC", "IN", "TI" };
441: incomeObjectTypeCodes = new String[] {
442: options.getFinObjTypeIncomeNotCashCd(), // IC
443: options.getFinObjectTypeIncomecashCode(), // IN
444: options.getFinObjTypeCshNotIncomeCd(), // CH
445: options.getFinancialObjectTypeTransferIncomeCd() // TI
446: };
447: // String[] expenseObjectTypeCodes = new String[] { "EE", "ES", "EX", "TE" };
448: expenseObjectTypeCodes = new String[] {
449: options.getFinObjTypeExpendNotExpCode(), // EE?
450: options.getFinObjTypeExpenditureexpCd(), // ES
451: options.getFinObjTypeExpNotExpendCode(), // EX?
452: options.getFinancialObjectTypeTransferExpenseCd() // TE
453: };
454: // String[] assetLiabilityFundBalanceBalanceTypeCodes = new String[] { "AS", "LI", "FB" };
455: assetLiabilityFundBalanceObjectTypeCodes = new String[] {
456: options.getFinancialObjectTypeAssetsCd(), // AS
457: options.getFinObjectTypeLiabilitiesCode(), // LI
458: options.getFinObjectTypeFundBalanceCd() // FB
459: };
460: // String[] encumbranceBaseBudgetBalanceTypeCodes = new String[] { "EX", "IE", "PE", "BB" };
461: encumbranceBaseBudgetBalanceTypeCodes = new String[] {
462: options.getExtrnlEncumFinBalanceTypCd(), // EX
463: options.getIntrnlEncumFinBalanceTypCd(), // IE
464: options.getPreencumbranceFinBalTypeCd(), // PE
465: options.getBaseBudgetFinancialBalanceTypeCd() // BB
466: };
467: }
468:
469: /**
470: * Use the options table to get a list of all the balance type codes associated with actual balances
471: *
472: * @return an array of balance type codes for actual balances
473: */
474: private String[] getActualBalanceCodes() {
475: if (actualBalanceCodes == null) {
476: loadConstantsFromOptions();
477: }
478: return actualBalanceCodes;
479: }
480:
481: /**
482: * Uses the options table to find all the balance type codes associated with income
483: *
484: * @return an array of income balance type codes
485: */
486: private String[] getIncomeObjectTypeCodes() {
487: if (incomeObjectTypeCodes == null) {
488: loadConstantsFromOptions();
489: }
490: return incomeObjectTypeCodes;
491: }
492:
493: /**
494: * Uses the options table to find all the balance type codes associated with expenses
495: *
496: * @return an array of expense option type codes
497: */
498: private String[] getExpenseObjectTypeCodes() {
499: if (expenseObjectTypeCodes == null) {
500: loadConstantsFromOptions();
501: }
502: return expenseObjectTypeCodes;
503: }
504:
505: /**
506: * Uses the options table to find all the balance type codes associated with asset/liability
507: *
508: * @return an array of asset/liability balance type codes
509: */
510: private String[] getAssetLiabilityFundBalanceBalanceTypeCodes() {
511: if (assetLiabilityFundBalanceObjectTypeCodes == null) {
512: loadConstantsFromOptions();
513: }
514: return assetLiabilityFundBalanceObjectTypeCodes;
515: }
516:
517: /**
518: * Uses the options table to create a list of all the balance type codes associated with encumbrances
519: *
520: * @return an array of encumbrance balance type codes
521: */
522: private String[] getEncumbranceBaseBudgetBalanceTypeCodes() {
523: if (encumbranceBaseBudgetBalanceTypeCodes == null) {
524: loadConstantsFromOptions();
525: }
526: return encumbranceBaseBudgetBalanceTypeCodes;
527: }
528:
529: /**
530: * Uses the DAO to count the number of balances associated with the given fiscal year
531: *
532: * @param fiscal year a fiscal year to count balances for
533: * @return an integer with the number of balances
534: * @see org.kuali.module.gl.service.BalanceService#countBalancesForFiscalYear(java.lang.Integer)
535: */
536: public int countBalancesForFiscalYear(Integer year) {
537: return balanceDao.countBalancesForFiscalYear(year);
538: }
539:
540: /**
541: * This method returns all of the balances specifically for the nominal activity closing job
542: * @param year year to find balances for
543: * @return an Iterator of nominal activity balances
544: * @see org.kuali.module.gl.service.BalanceService#findNominalActivityBalancesForFiscalYear(java.lang.Integer)
545: */
546: public Iterator<Balance> findNominalActivityBalancesForFiscalYear(
547: Integer year) {
548: return balanceDao
549: .findNominalActivityBalancesForFiscalYear(year);
550: }
551:
552: /**
553: * Returns all the balances to be forwarded for the "cumulative" rule
554: * @param year the fiscal year to find balances for
555: * @return an Iterator of balances to process for the cumulative/active balance forward process
556: * @see org.kuali.module.gl.service.BalanceService#findCumulativeBalancesToForwardForFiscalYear(java.lang.Integer)
557: */
558: public Iterator<Balance> findCumulativeBalancesToForwardForFiscalYear(
559: Integer year) {
560: return balanceDao
561: .findCumulativeBalancesToForwardForFiscalYear(year);
562: }
563:
564: /**
565: * Returns all the balances specifically to be processed by the balance forwards job for the "general" rule
566: * @param year the fiscal year to find balances for
567: * @return an Iterator of balances to process for the general balance forward process
568: * @see org.kuali.module.gl.service.BalanceService#findGeneralBalancesToForwardForFiscalYear(java.lang.Integer)
569: */
570: public Iterator<Balance> findGeneralBalancesToForwardForFiscalYear(
571: Integer year) {
572: return balanceDao
573: .findGeneralBalancesToForwardForFiscalYear(year);
574: }
575:
576: /**
577: * Returns all of the balances to be forwarded for the organization reversion process
578: * @param year the year of balances to find
579: * @param endOfYear whether the organization reversion process is running end of year (before the fiscal year change over) or beginning of year (after the fiscal year change over)
580: * @return an iterator of balances to put through the strenuous organization reversion process
581: * @see org.kuali.module.gl.service.BalanceService#findOrganizationReversionBalancesForFiscalYear(java.lang.Integer, boolean)
582: */
583: public Iterator<Balance> findOrganizationReversionBalancesForFiscalYear(
584: Integer year, boolean endOfYear) {
585: return balanceDao
586: .findOrganizationReversionBalancesForFiscalYear(year,
587: endOfYear);
588: }
589:
590: }
|