View Javadoc
1   /*
2    * Copyright 2006 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.gl.service.impl;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.HashMap;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.kuali.ole.coa.businessobject.ObjectCode;
26  import org.kuali.ole.coa.service.AccountService;
27  import org.kuali.ole.coa.service.ObjectLevelService;
28  import org.kuali.ole.coa.service.ObjectTypeService;
29  import org.kuali.ole.fp.document.YearEndDocument;
30  import org.kuali.ole.gl.batch.dataaccess.SufficientFundsDao;
31  import org.kuali.ole.gl.businessobject.SufficientFundBalances;
32  import org.kuali.ole.gl.businessobject.SufficientFundRebuild;
33  import org.kuali.ole.gl.businessobject.Transaction;
34  import org.kuali.ole.gl.dataaccess.SufficientFundBalancesDao;
35  import org.kuali.ole.gl.service.SufficientFundsService;
36  import org.kuali.ole.gl.service.SufficientFundsServiceConstants;
37  import org.kuali.ole.module.purap.businessobject.PaymentRequestAccount;
38  import org.kuali.ole.select.businessobject.OlePaymentRequestItem;
39  import org.kuali.ole.select.businessobject.OleSufficientFundCheck;
40  import org.kuali.ole.select.document.OlePaymentRequestDocument;
41  import org.kuali.ole.sys.OLEConstants;
42  import org.kuali.ole.sys.OLEPropertyConstants;
43  import org.kuali.ole.sys.businessobject.SufficientFundsItem;
44  import org.kuali.ole.sys.businessobject.SystemOptions;
45  import org.kuali.ole.sys.context.SpringContext;
46  import org.kuali.ole.sys.document.GeneralLedgerPostingDocument;
47  import org.kuali.ole.sys.service.GeneralLedgerPendingEntryService;
48  import org.kuali.ole.sys.service.OptionsService;
49  import org.kuali.rice.core.api.config.property.ConfigurationService;
50  import org.kuali.rice.core.api.util.type.KualiDecimal;
51  import org.kuali.rice.krad.service.BusinessObjectService;
52  import org.kuali.rice.krad.util.ObjectUtils;
53  import org.springframework.transaction.annotation.Transactional;
54  
55  /**
56   * The base implementation of SufficientFundsService
57   */
58  @Transactional
59  public class SufficientFundsServiceImpl implements SufficientFundsService, SufficientFundsServiceConstants {
60      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SufficientFundsServiceImpl.class);
61  
62      private AccountService accountService;
63      private ObjectLevelService objectLevelService;
64      private ConfigurationService kualiConfigurationService;
65      private SufficientFundsDao sufficientFundsDao;
66      private SufficientFundBalancesDao sufficientFundBalancesDao;
67      private OptionsService optionsService;
68      private GeneralLedgerPendingEntryService generalLedgerPendingEntryService;
69      private BusinessObjectService businessObjectService;
70  
71      /**
72       * Default constructor
73       */
74      public SufficientFundsServiceImpl() {
75          super();
76      }
77  
78      /**
79       * This operation derives the acct_sf_finobj_cd which is used to populate the General Ledger Pending entry table, so that later
80       * we can do Suff Fund checking against that entry
81       *
82       * @param financialObject the object code being checked against
83       * @param accountSufficientFundsCode the kind of sufficient funds checking turned on in this system
84       * @return the object code that should be used for the sufficient funds inquiry, or a blank String
85       * @see org.kuali.ole.gl.service.SufficientFundsService#getSufficientFundsObjectCode(org.kuali.ole.coa.businessobject.ObjectCode,
86       *      java.lang.String)
87       */
88      @Override
89      public String getSufficientFundsObjectCode(ObjectCode financialObject, String accountSufficientFundsCode) {
90          if ( LOG.isDebugEnabled() ) {
91              LOG.debug("Obtaining SF Object code for: " + accountSufficientFundsCode + " - " + financialObject.getFinancialObjectCode() );
92          }
93          if (OLEConstants.SF_TYPE_NO_CHECKING.equals(accountSufficientFundsCode)) {
94              return OLEConstants.NOT_AVAILABLE_STRING;
95          }
96          else if (OLEConstants.SF_TYPE_ACCOUNT.equals(accountSufficientFundsCode)) {
97              return "    ";
98          }
99          else if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(accountSufficientFundsCode)) {
100             return "    ";
101         }
102         else if (OLEConstants.SF_TYPE_OBJECT.equals(accountSufficientFundsCode)) {
103             return financialObject.getFinancialObjectCode();
104         }
105         else if (OLEConstants.SF_TYPE_LEVEL.equals(accountSufficientFundsCode)) {
106             return financialObject.getFinancialObjectLevelCode();
107         }
108         else if (OLEConstants.SF_TYPE_CONSOLIDATION.equals(accountSufficientFundsCode)) {
109             financialObject.refreshReferenceObject("financialObjectLevel");
110             return financialObject.getFinancialObjectLevel().getFinancialConsolidationObjectCode();
111         }
112         else {
113             throw new IllegalArgumentException("Invalid Sufficient Funds Code: " + accountSufficientFundsCode);
114         }
115     }
116 
117     /**
118      * Checks for sufficient funds on a single document
119      *
120      * @param document document to check
121      * @return Empty List if has sufficient funds for all accounts, List of SufficientFundsItem if not
122      * @see org.kuali.ole.gl.service.SufficientFundsService#checkSufficientFunds(org.kuali.rice.krad.document.FinancialDocument)
123      */
124     @Override
125     public List<SufficientFundsItem> checkSufficientFunds(GeneralLedgerPostingDocument document) {
126         LOG.debug("checkSufficientFunds() started");
127 
128         return checkSufficientFunds(document.getPendingLedgerEntriesForSufficientFundsChecking());
129     }
130 
131     /**
132      * checks to see if a document is a <code>YearEndDocument</code>
133      *
134      * @param documentClass the class of a Document to check
135      * @return true if the class implements <code>YearEndDocument</code>
136      */
137     @SuppressWarnings("unchecked")
138     protected boolean isYearEndDocument(Class documentClass) {
139         return YearEndDocument.class.isAssignableFrom(documentClass);
140     }
141 
142     /**
143      * Checks for sufficient funds on a list of transactions
144      *
145      * @param transactions list of transactions
146      * @return Empty List if has sufficient funds for all accounts, List of SufficientFundsItem if not
147      * @see org.kuali.ole.gl.service.SufficientFundsService#checkSufficientFunds(java.util.List)
148      */
149     @Override
150     @SuppressWarnings("unchecked")
151     public List<SufficientFundsItem> checkSufficientFunds(List<? extends Transaction> transactions) {
152         LOG.debug("checkSufficientFunds() started");
153 
154         for (Transaction e : transactions) {
155             e.refreshNonUpdateableReferences();
156         }
157 
158         List<SufficientFundsItem> summaryItems = summarizeTransactions(transactions);
159         for (Iterator iter = summaryItems.iterator(); iter.hasNext();) {
160             SufficientFundsItem item = (SufficientFundsItem) iter.next();
161             if ( LOG.isDebugEnabled() ) {
162                 LOG.debug("checkSufficientFunds() " + item.toString());
163             }
164             if (hasSufficientFundsOnItem(item)) {
165                 iter.remove();
166             }
167         }
168 
169         if ( LOG.isDebugEnabled() ) {
170             LOG.debug("Sufficient Funds Check Complete.  Returning: " + summaryItems );
171         }
172         return summaryItems;
173     }
174 
175     @Override
176     @SuppressWarnings("unchecked")
177     public List<SufficientFundsItem> checkSufficientFundsForPREQ(List<? extends Transaction> transactions) {
178         LOG.debug("checkSufficientFunds() started");
179 
180         for (Transaction e : transactions) {
181             e.refreshNonUpdateableReferences();
182         }
183 
184         List<SufficientFundsItem> summaryItems = summarizeTransactions(transactions);
185         for (Iterator iter = summaryItems.iterator(); iter.hasNext();) {
186             SufficientFundsItem item = (SufficientFundsItem) iter.next();
187             if (LOG.isDebugEnabled()) {
188                 LOG.debug("checkSufficientFunds() " + item.toString());
189             }
190             if (hasSufficientFundsOnPREQItem(item)) {
191                 iter.remove();
192             }
193         }
194 
195         if (LOG.isDebugEnabled()) {
196             LOG.debug("PREQ Sufficient Funds Check Complete.  Returning: " + summaryItems);
197         }
198         return summaryItems;
199     }
200 
201     @Override
202     @SuppressWarnings("unchecked")
203     public List<SufficientFundsItem> checkSufficientFundsForInvoice(List<? extends Transaction> transactions) {
204         LOG.debug("checkSufficientFunds() for Invoice started");
205 
206         for (Transaction e : transactions) {
207             e.refreshNonUpdateableReferences();
208         }
209 
210         List<SufficientFundsItem> summaryItems = summarizeTransactions(transactions);
211         for (Iterator iter = summaryItems.iterator(); iter.hasNext();) {
212             SufficientFundsItem item = (SufficientFundsItem) iter.next();
213             if (LOG.isDebugEnabled()) {
214                 LOG.debug("checkSufficientFunds() for Invoice " + item.toString());
215             }
216            /* if (hasSufficientFundsOnPREQItem(item)) {
217                 iter.remove();
218             }*/
219         }
220 
221         if (LOG.isDebugEnabled()) {
222             LOG.debug("Invoice Sufficient Funds Check Complete.  Returning: " + summaryItems);
223         }
224         return summaryItems;
225     }
226 
227     /**
228      * For each transaction, fetches the appropriate sufficient funds item to check against
229      *
230      * @param transactions a list of Transactions
231      * @return a List of corresponding SufficientFundsItem
232      */
233     @SuppressWarnings("unchecked")
234     protected List<SufficientFundsItem> summarizeTransactions(List<? extends Transaction> transactions) {
235         Map<String, SufficientFundsItem> items = new HashMap<String, SufficientFundsItem>();
236 
237         SystemOptions currentYear = optionsService.getCurrentYearOptions();
238 
239         // loop over the given transactions, grouping into SufficientFundsItem objects
240         // which are keyed by the appropriate chart/account/SF type, and derived object value
241         // see getSufficientFundsObjectCode() for the "object" used for grouping
242         for (Object element : transactions) {
243             Transaction tran = (Transaction) element;
244 
245             SystemOptions year = tran.getOption();
246             if (year == null) {
247                 year = currentYear;
248             }
249             if (ObjectUtils.isNull(tran.getAccount())) {
250                 throw new IllegalArgumentException("Invalid account: " + tran.getChartOfAccountsCode() + "-" + tran.getAccountNumber());
251             }
252             SufficientFundsItem sfi = new SufficientFundsItem(year, tran, getSufficientFundsObjectCode(tran.getFinancialObject(), tran.getAccount().getAccountSufficientFundsCode()));
253             sfi.setDocumentTypeCode(tran.getFinancialDocumentTypeCode());
254 
255             if (items.containsKey(sfi.getKey())) {
256                 SufficientFundsItem item = items.get(sfi.getKey());
257                 item.add(tran);
258             }
259             else {
260                 items.put(sfi.getKey(), sfi);
261             }
262         }
263 
264         if ( LOG.isDebugEnabled() ) {
265             LOG.debug( "Returning Summarized transactions for sufficient funds checking: " + items );
266         }
267         return new ArrayList<SufficientFundsItem>(items.values());
268     }
269 
270     /**
271      * Given a sufficient funds item record, determines if there are sufficient funds available for the transaction
272      *
273      * @param item the item to check
274      * @return true if there are sufficient funds available, false otherwise
275      */
276     protected boolean hasSufficientFundsOnItem(SufficientFundsItem item) {
277         if (item.getAmount().equals(KualiDecimal.ZERO)) {
278             LOG.debug("hasSufficientFundsOnItem() Transactions with zero amounts shold pass");
279             return true;
280         }
281 
282         if (!item.getYear().isBudgetCheckingOptionsCode()) {
283             LOG.debug("hasSufficientFundsOnItem() No sufficient funds checking");
284             return true;
285         }
286 
287         if (!item.getAccount().isPendingAcctSufficientFundsIndicator()) {
288             if (LOG.isDebugEnabled()) {
289                 LOG.debug("hasSufficientFundsOnItem() No checking on eDocs for account " + item.getAccount().getChartOfAccountsCode() + "-" + item.getAccount().getAccountNumber());
290             }
291             return true;
292         }
293 
294         // exit sufficient funds checking if not enabled for an account
295         if (OLEConstants.SF_TYPE_NO_CHECKING.equals(item.getAccountSufficientFundsCode())) {
296             if (LOG.isDebugEnabled()) {
297                 LOG.debug("hasSufficientFundsOnItem() sufficient funds not enabled for account " + item.getAccount().getChartOfAccountsCode() + "-" + item.getAccount().getAccountNumber());
298             }
299             return true;
300         }
301 
302         ObjectTypeService objectTypeService = SpringContext.getBean(ObjectTypeService.class);
303         List<String> expenseObjectTypes = objectTypeService.getCurrentYearExpenseObjectTypes();
304 
305         if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode())
306                 && !item.getFinancialObject().getChartOfAccounts().getFinancialCashObjectCode()
307                 .equals(item.getFinancialObject().getFinancialObjectCode())) {
308             LOG.debug("hasSufficientFundsOnItem() SF checking is cash and transaction is not cash");
309             return true;
310         }
311 
312         else if (!OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode())
313                 && !expenseObjectTypes.contains(item.getFinancialObjectType().getCode())) {
314             LOG.debug("hasSufficientFundsOnItem() SF checking is budget and transaction is not expense");
315             return true;
316         }
317 
318         Map<String, Object> keys = new HashMap<String, Object>();
319         keys.put(OLEPropertyConstants.UNIVERSITY_FISCAL_YEAR, item.getYear().getUniversityFiscalYear());
320         keys.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, item.getAccount().getChartOfAccountsCode());
321         keys.put(OLEPropertyConstants.ACCOUNT_NUMBER, item.getAccount().getAccountNumber());
322         keys.put(OLEPropertyConstants.FINANCIAL_OBJECT_CODE, item.getSufficientFundsObjectCode());
323         SufficientFundBalances sfBalance = businessObjectService.findByPrimaryKey(SufficientFundBalances.class, keys);
324 
325         if (sfBalance == null) {
326             Map criteria = new HashMap();
327             criteria.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, item.getAccount().getChartOfAccountsCode());
328             criteria.put(OLEPropertyConstants.ACCOUNT_NUMBER_FINANCIAL_OBJECT_CODE, item.getAccount().getAccountNumber());
329 
330             Collection sufficientFundRebuilds = businessObjectService.findMatching(SufficientFundRebuild.class, criteria);
331             if (sufficientFundRebuilds != null && sufficientFundRebuilds.size() > 0) {
332                 LOG.debug("hasSufficientFundsOnItem() No balance record and waiting on rebuild, no sufficient funds");
333                 return false;
334             }
335             else {
336                 sfBalance = new SufficientFundBalances();
337                 sfBalance.setAccountActualExpenditureAmt(KualiDecimal.ZERO);
338                 sfBalance.setAccountEncumbranceAmount(KualiDecimal.ZERO);
339                 sfBalance.setCurrentBudgetBalanceAmount(KualiDecimal.ZERO);
340             }
341         }
342         if (LOG.isDebugEnabled()) {
343             LOG.debug("Current SF Balances: Budget:      " + sfBalance.getCurrentBudgetBalanceAmount());
344             LOG.debug("Current SF Balances: Actuals:     " + sfBalance.getAccountEncumbranceAmount());
345             LOG.debug("Current SF Balances: Encumbrance: " + sfBalance.getAccountActualExpenditureAmt());
346         }
347 
348         KualiDecimal balanceAmount = item.getAmount();
349         if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode())
350                 || item.getYear().getBudgetCheckingBalanceTypeCd().equals(item.getBalanceTyp().getCode())) {
351             // We need to change the sign on the amount because the amount in the item is an increase in cash. We only care
352             // about decreases in cash.
353 
354             // Also, negating if this is a balance type code of budget checking and the transaction is a budget transaction.
355 
356             balanceAmount = balanceAmount.negated();
357         }
358 
359         if (balanceAmount.isNegative()) {
360             LOG.debug("hasSufficientFundsOnItem() balanceAmount is negative, allow transaction to proceed");
361             return true;
362         }
363 
364         PendingAmounts priorYearPending = new PendingAmounts();
365         // if we're checking the CASH_AT_ACCOUNT type, then we need to consider the prior year pending transactions
366         // if the balance forwards have not been run
367         if ((OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode()))
368                 && (!item.getYear().isFinancialBeginBalanceLoadInd())) {
369             priorYearPending = getPriorYearSufficientFundsBalanceAmount(item);
370         }
371 
372         PendingAmounts pending = getPendingBalanceAmount(item);
373 
374         KualiDecimal availableBalance = null;
375         KualiDecimal freeBalance = null;
376         if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode())) {
377             // if the beginning balances have not loaded for the transaction FY, pull the remaining balance from last year
378             if (!item.getYear().isFinancialBeginBalanceLoadInd()) {
379                 availableBalance = sfBalance.getCurrentBudgetBalanceAmount().add(priorYearPending.budget) // add the remaining
380                         // budget from last year
381                         // (assumed to carry to
382                         // this year's)
383                         .add(pending.actual) // any pending expenses (remember sense is negated)
384                         .subtract(sfBalance.getAccountEncumbranceAmount()) // subtract the encumbrances (not reflected in cash yet)
385                         .subtract(priorYearPending.encumbrance);
386             }
387             else { // balance forwards have been run, don't need to consider prior year remaining budget
388                 availableBalance = sfBalance.getCurrentBudgetBalanceAmount().add(pending.actual)
389                         .subtract(sfBalance.getAccountEncumbranceAmount());
390             }
391         }
392         else {
393             availableBalance = sfBalance.getCurrentBudgetBalanceAmount() // current budget balance
394                     .add(pending.budget) // pending budget entries
395                     .subtract(sfBalance.getAccountActualExpenditureAmt()) // minus all current and pending actuals and encumbrances
396                     .subtract(pending.actual).subtract(sfBalance.getAccountEncumbranceAmount())
397                     .subtract(pending.encumbrance);
398             freeBalance = sfBalance.getCurrentBudgetBalanceAmount()
399                     .subtract(sfBalance.getAccountActualExpenditureAmt()).subtract(pending.actual);
400         }
401 
402         if (LOG.isDebugEnabled()) {
403             LOG.debug("hasSufficientFundsOnItem() balanceAmount: " + balanceAmount + " availableBalance: " + availableBalance);
404         }
405         String chart = item.getAccount().getChartOfAccountsCode();
406         String account = item.getAccount().getAccountNumber();
407         String sfCode = item.getAccount().getAccountSufficientFundsCode();
408 
409         Map<String, Object> key = new HashMap<String, Object>();
410         key.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, chart);
411         key.put(OLEPropertyConstants.ACCOUNT_NUMBER, account);
412         OleSufficientFundCheck oleSufficientFundCheck = businessObjectService.findByPrimaryKey(
413                 OleSufficientFundCheck.class, key);
414         String option = "";
415         boolean checkBalance;
416         if (oleSufficientFundCheck != null) {
417             option = oleSufficientFundCheck.getNotificationOption() != null ? oleSufficientFundCheck
418                     .getNotificationOption() : "";
419             if (option.equals(OLEPropertyConstants.BUD_REVIEW)) {
420                 checkBalance = checkEncumbrance(item, balanceAmount, availableBalance, sfBalance);
421                 return checkBalance;
422             }
423             /*
424              * else if (option.equals(OLEPropertyConstants.NOTIFICATION)) { OleNotifyServiceImpl notification = new
425              * OleNotifyServiceImpl(); List user = new ArrayList(); try { user.add(OLEConstants.NOTIFICATION_APPROVER);
426              * notification.notify(user, OLEConstants.ACC_NUM + account + OLEConstants.EXC_BUD_AMT); return true; } catch
427              * (WorkflowException exception) { LOG.debug("Exception occured while notification sent to budget reviewer" +
428              * exception); } }
429              */
430             else if (option.equals(OLEPropertyConstants.WARNING_MSG)) {
431                 return true;
432             }
433             else if (option.equals(OLEPropertyConstants.BLOCK_USE)) {
434                 return true;
435             }
436 
437         }
438         else {
439             checkBalance = checkEncumbrance(item, balanceAmount, availableBalance, sfBalance);
440             return checkBalance;
441         }
442         return false;
443     }
444 
445     public boolean checkEncumbrance(SufficientFundsItem item, KualiDecimal balanceAmount,
446                                     KualiDecimal availableBalance, SufficientFundBalances sfBalance) {
447         String chart = item.getAccount().getChartOfAccountsCode();
448         String account = item.getAccount().getAccountNumber();
449         String sfCode = item.getAccount().getAccountSufficientFundsCode();
450 
451         Map<String, Object> key = new HashMap<String, Object>();
452         key.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, chart);
453         key.put(OLEPropertyConstants.ACCOUNT_NUMBER, account);
454         OleSufficientFundCheck oleSufficientFundCheck = businessObjectService.findByPrimaryKey(
455                 OleSufficientFundCheck.class, key);
456         if (oleSufficientFundCheck != null) {
457             KualiDecimal encumbranceAmount = KualiDecimal.ZERO;
458             KualiDecimal encumAvailableAmount = null;
459             if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_TYP_PERCENTAGE.equals(oleSufficientFundCheck
460                     .getEncumbExpenseConstraintType())) {
461                 encumbranceAmount = new KualiDecimal(oleSufficientFundCheck.getEncumbranceAmount());
462                 if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_OVER.equals(oleSufficientFundCheck
463                         .getEncumbExpenseMethod())) {
464                     encumAvailableAmount = availableBalance.add((sfBalance.getCurrentBudgetBalanceAmount()
465                             .multiply(encumbranceAmount)).divide(new KualiDecimal(100)));
466                 }
467                 else if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_UNDER.equals(oleSufficientFundCheck
468                         .getEncumbExpenseMethod())) {
469                     encumAvailableAmount = availableBalance.subtract((sfBalance.getCurrentBudgetBalanceAmount()
470                             .multiply(encumbranceAmount)).divide(new KualiDecimal(100)));
471                 }
472                 if (balanceAmount.compareTo(encumAvailableAmount) > 0) {
473                     return false;
474                 }
475                 return true;
476             }
477             else if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_TYP_CASH.equals(oleSufficientFundCheck
478                     .getEncumbExpenseConstraintType())) {
479                 encumbranceAmount = new KualiDecimal(oleSufficientFundCheck.getEncumbranceAmount());
480                 if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_OVER.equals(oleSufficientFundCheck
481                         .getEncumbExpenseMethod())) {
482                     encumAvailableAmount = availableBalance.add(encumbranceAmount);
483                 }
484                 else if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_UNDER.equals(oleSufficientFundCheck
485                         .getEncumbExpenseMethod())) {
486                     encumAvailableAmount = availableBalance.subtract(encumbranceAmount);
487                 }
488                 if (balanceAmount.compareTo(encumAvailableAmount) > 0) {
489                     return false;
490                 }
491                 return true;
492             }
493         }
494         if (availableBalance.isGreaterThan(balanceAmount)) {
495             return true;
496         }
497         return false;
498     }
499 
500 
501     protected boolean hasSufficientFundsOnPREQItem(SufficientFundsItem item) {
502 
503 
504         if (item.getAmount().equals(KualiDecimal.ZERO)) {
505             LOG.debug("hasSufficientFundsOnItem() Transactions with zero amounts shold pass");
506             return true;
507         }
508 
509         if (!item.getYear().isBudgetCheckingOptionsCode()) {
510             LOG.debug("hasSufficientFundsOnItem() No sufficient funds checking");
511             return true;
512         }
513 
514         if (!item.getAccount().isPendingAcctSufficientFundsIndicator()) {
515             if (LOG.isDebugEnabled()) {
516                 LOG.debug("hasSufficientFundsOnItem() No checking on eDocs for account "
517                         + item.getAccount().getChartOfAccountsCode() + "-" + item.getAccount().getAccountNumber());
518             }
519             return true;
520         }
521 
522         // exit sufficient funds checking if not enabled for an account
523         if (OLEConstants.SF_TYPE_NO_CHECKING.equals(item.getAccountSufficientFundsCode())) {
524             if (LOG.isDebugEnabled()) {
525                 LOG.debug("hasSufficientFundsOnItem() sufficient funds not enabled for account "
526                         + item.getAccount().getChartOfAccountsCode() + "-" + item.getAccount().getAccountNumber());
527             }
528             return true;
529         }
530 
531         ObjectTypeService objectTypeService = SpringContext.getBean(ObjectTypeService.class);
532         List<String> expenseObjectTypes = objectTypeService.getCurrentYearExpenseObjectTypes();
533 
534         if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode())
535                 && !item.getFinancialObject().getChartOfAccounts().getFinancialCashObjectCode()
536                 .equals(item.getFinancialObject().getFinancialObjectCode())) {
537             LOG.debug("hasSufficientFundsOnItem() SF checking is cash and transaction is not cash");
538             return true;
539         }
540 
541         else if (!OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode())
542                 && !expenseObjectTypes.contains(item.getFinancialObjectType().getCode())) {
543             LOG.debug("hasSufficientFundsOnItem() SF checking is budget and transaction is not expense");
544             return true;
545         }
546 
547         Map<String, Object> keys = new HashMap<String, Object>();
548         keys.put(OLEPropertyConstants.UNIVERSITY_FISCAL_YEAR, item.getYear().getUniversityFiscalYear());
549         keys.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, item.getAccount().getChartOfAccountsCode());
550         keys.put(OLEPropertyConstants.ACCOUNT_NUMBER, item.getAccount().getAccountNumber());
551         keys.put(OLEPropertyConstants.FINANCIAL_OBJECT_CODE, item.getSufficientFundsObjectCode());
552         SufficientFundBalances sfBalance = businessObjectService.findByPrimaryKey(SufficientFundBalances.class, keys);
553         if (sfBalance == null) {
554             Map criteria = new HashMap();
555             criteria.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, item.getAccount().getChartOfAccountsCode());
556             criteria.put(OLEPropertyConstants.ACCOUNT_NUMBER_FINANCIAL_OBJECT_CODE, item.getAccount()
557                     .getAccountNumber());
558 
559             Collection sufficientFundRebuilds = businessObjectService.findMatching(SufficientFundRebuild.class,
560                     criteria);
561             if (sufficientFundRebuilds != null && sufficientFundRebuilds.size() > 0) {
562                 LOG.debug("hasSufficientFundsOnItem() No balance record and waiting on rebuild, no sufficient funds");
563                 return false;
564             }
565             else {
566                 sfBalance = new SufficientFundBalances();
567                 sfBalance.setAccountActualExpenditureAmt(KualiDecimal.ZERO);
568                 sfBalance.setAccountEncumbranceAmount(KualiDecimal.ZERO);
569                 sfBalance.setCurrentBudgetBalanceAmount(KualiDecimal.ZERO);
570             }
571         }
572         if (LOG.isDebugEnabled()) {
573             LOG.debug("Current SF Balances: Budget:      " + sfBalance.getCurrentBudgetBalanceAmount());
574             LOG.debug("Current SF Balances: Actuals:     " + sfBalance.getAccountEncumbranceAmount());
575             LOG.debug("Current SF Balances: Encumbrance: " + sfBalance.getAccountActualExpenditureAmt());
576         }
577 
578         KualiDecimal balanceAmount = item.getAmount();
579         if (balanceAmount.isLessThan(KualiDecimal.ZERO)) {
580             balanceAmount = balanceAmount.negated();
581         }
582         // balanceAmount = balanceAmount.negated();
583         if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode())
584                 || item.getYear().getBudgetCheckingBalanceTypeCd().equals(item.getBalanceTyp().getCode())) {
585             // We need to change the sign on the amount because the amount in the item is an increase in cash. We only care
586             // about decreases in cash.
587 
588             // Also, negating if this is a balance type code of budget checking and the transaction is a budget transaction.
589 
590             balanceAmount = balanceAmount.negated();
591         }
592 
593         /*
594          * if (balanceAmount.isNegative()) {
595          * LOG.debug("hasSufficientFundsOnItem() balanceAmount is negative, allow transaction to proceed"); return true; }
596          */
597 
598         PendingAmounts priorYearPending = new PendingAmounts();
599         // if we're checking the CASH_AT_ACCOUNT type, then we need to consider the prior year pending transactions
600         // if the balance forwards have not been run
601         if ((OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode()))
602                 && (!item.getYear().isFinancialBeginBalanceLoadInd())) {
603             priorYearPending = getPriorYearSufficientFundsBalanceAmount(item);
604         }
605 
606         PendingAmounts pending = getPendingBalanceAmount(item);
607 
608         KualiDecimal availableBalance = KualiDecimal.ZERO;
609         KualiDecimal freeBalance = KualiDecimal.ZERO;
610         KualiDecimal expenditures = KualiDecimal.ZERO;
611 
612         if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode())) {
613             // if the beginning balances have not loaded for the transaction FY, pull the remaining balance from last year
614             if (!item.getYear().isFinancialBeginBalanceLoadInd()) {
615                 availableBalance = sfBalance.getCurrentBudgetBalanceAmount().add(priorYearPending.budget) // add the remaining
616                         // budget from last year
617                         // (assumed to carry to
618                         // this year's)
619                         .add(pending.actual) // any pending expenses (remember sense is negated)
620                         .subtract(sfBalance.getAccountEncumbranceAmount()) // subtract the encumbrances (not reflected in cash yet)
621                         .subtract(priorYearPending.encumbrance);
622             }
623             else { // balance forwards have been run, don't need to consider prior year remaining budget
624                 availableBalance = sfBalance.getCurrentBudgetBalanceAmount().add(pending.actual)
625                         .subtract(sfBalance.getAccountEncumbranceAmount());
626             }
627         }
628         else {
629             availableBalance = sfBalance.getCurrentBudgetBalanceAmount()
630 
631                     // pending budget entries
632                     .subtract(sfBalance.getAccountActualExpenditureAmt())
633                             // minus all current and pending actuals and encumbrances
634                     .subtract(pending.actual);
635             freeBalance = sfBalance.getCurrentBudgetBalanceAmount()
636                     .subtract(sfBalance.getAccountActualExpenditureAmt()).subtract(pending.actual);
637         }
638         /*
639          * Map searchCriteria = new HashMap(); searchCriteria.put("chartOfAccountsCode",
640          * item.getAccount().getChartOfAccountsCode()); searchCriteria.put("accountNumber", item.getAccount().getAccountNumber());
641          * searchCriteria.put("financialObjectCode", item.getSufficientFundsObjectCode());
642          * searchCriteria.put("financialDocumentTypeCode", "OLE_PREQ"); // searchCriteria.put("transactionDebitCreditCode", null);
643          * List<GeneralLedgerPendingEntry> pendingList = (List<GeneralLedgerPendingEntry>) SpringContext.getBean(
644          * BusinessObjectService.class).findMatching(GeneralLedgerPendingEntry.class, searchCriteria); for
645          * (GeneralLedgerPendingEntry pendingAmountEntry : pendingList) { if (pendingAmountEntry.getTransactionDebitCreditCode() ==
646          * null) { expenditures = expenditures.add(pendingAmountEntry.getTransactionLedgerEntryAmount()); } }
647          */
648         if (item.getAccount().getAccountSufficientFundsCode().equals(OLEConstants.SF_TYPE_OBJECT)) {
649             expenditures = getSumPaidInvoicesForObject(item.getAccount().getChartOfAccountsCode(), item.getAccount()
650                     .getAccountNumber(), item.getSufficientFundsObjectCode());
651         }
652         else if (item.getAccount().getAccountSufficientFundsCode().equals(OLEConstants.SF_TYPE_ACCOUNT)) {
653             expenditures = getSumPaidInvoicesForAccount(item.getAccount().getChartOfAccountsCode(), item.getAccount()
654                     .getAccountNumber(), item.getSufficientFundsObjectCode());
655         }
656         availableBalance = availableBalance.subtract(expenditures);
657         availableBalance = availableBalance.subtract(item.getAmount());
658         String chart = item.getAccount().getChartOfAccountsCode();
659         String account = item.getAccount().getAccountNumber();
660         String sfCode = item.getAccount().getAccountSufficientFundsCode();
661         Map<String, Object> key = new HashMap<String, Object>();
662         key.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, chart);
663         key.put(OLEPropertyConstants.ACCOUNT_NUMBER, account);
664         OleSufficientFundCheck oleSufficientFundCheck = businessObjectService.findByPrimaryKey(
665                 OleSufficientFundCheck.class, key);
666         if (oleSufficientFundCheck != null) {
667             String option = oleSufficientFundCheck.getNotificationOption() != null ? oleSufficientFundCheck
668                     .getNotificationOption() : "";
669             boolean checkBalance;
670 
671             if (option.equals(OLEPropertyConstants.BUD_REVIEW)) {
672                 checkBalance = checkExpense(item, balanceAmount, availableBalance, sfBalance, freeBalance);
673                 return checkBalance;
674             }/*
675               * else if (option.equals(OLEPropertyConstants.NOTIFICATION)) { OleNotifyServiceImpl notification = new
676               * OleNotifyServiceImpl(); List user = new ArrayList(); try { user.add(OLEConstants.NOTIFICATION_APPROVER);
677               * notification.notify(user, "Account number " + account + " exceeds budget amount"); return true; } catch
678               * (WorkflowException exception) { LOG.debug("Exception occured while notification sent to budget reviewer" +
679               * exception); } }
680               */
681 
682 
683         }
684         else {
685             if (availableBalance.isGreaterThan(balanceAmount)) {
686                 return true;
687             }
688         }
689         return false;
690     }
691 
692     public boolean checkExpense(SufficientFundsItem item, KualiDecimal balanceAmount, KualiDecimal availableBalance,
693                                 SufficientFundBalances sfBalance, KualiDecimal freeBalance) {
694         String chart = item.getAccount().getChartOfAccountsCode();
695         String account = item.getAccount().getAccountNumber();
696         String sfCode = item.getAccount().getAccountSufficientFundsCode();
697 
698         Map<String, Object> key = new HashMap<String, Object>();
699         key.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, chart);
700         key.put(OLEPropertyConstants.ACCOUNT_NUMBER, account);
701         OleSufficientFundCheck oleSufficientFundCheck = businessObjectService.findByPrimaryKey(
702                 OleSufficientFundCheck.class, key);
703         if (oleSufficientFundCheck != null) {
704             KualiDecimal expenseAvailableAmount = KualiDecimal.ZERO;
705             KualiDecimal expenseAmount = KualiDecimal.ZERO;
706             if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_TYP_PERCENTAGE.equals(oleSufficientFundCheck
707                     .getEncumbExpenseConstraintType())) {
708                 expenseAmount = new KualiDecimal(oleSufficientFundCheck.getExpenseAmount());
709                 if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_OVER.equals(oleSufficientFundCheck
710                         .getEncumbExpenseMethod())) {
711                     expenseAvailableAmount = availableBalance.add(
712                             sfBalance.getCurrentBudgetBalanceAmount().multiply(expenseAmount)).divide(
713                             new KualiDecimal(100));
714                 }
715                 else if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_UNDER.equals(oleSufficientFundCheck
716                         .getEncumbExpenseMethod())) {
717                     expenseAvailableAmount = availableBalance.subtract((sfBalance.getCurrentBudgetBalanceAmount()
718                             .multiply(expenseAmount)).divide(new KualiDecimal(100)));
719                 }
720                 if (balanceAmount.compareTo(expenseAvailableAmount) > 0) {
721                     return false;
722                 }
723                 return true;
724             }
725             else if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_TYP_CASH.equals(oleSufficientFundCheck
726                     .getEncumbExpenseConstraintType())) {
727                 expenseAmount = new KualiDecimal(oleSufficientFundCheck.getExpenseAmount());
728                 if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_OVER.equals(oleSufficientFundCheck
729                         .getEncumbExpenseMethod())) {
730                     expenseAvailableAmount = availableBalance.add(expenseAmount);
731                 }
732                 else if (OLEPropertyConstants.SUFFICIENT_FUND_ENC_UNDER.equals(oleSufficientFundCheck
733                         .getEncumbExpenseMethod())) {
734                     expenseAvailableAmount = availableBalance.subtract(expenseAmount);
735                 }
736                 if (balanceAmount.compareTo(expenseAvailableAmount) > 0) {
737                     return false;
738                 }
739                 return true;
740             }
741         }
742         if (availableBalance.isGreaterThan(balanceAmount)) {
743             return true;
744         }
745         return false;
746     }
747 
748 
749     public KualiDecimal getSumPaidInvoicesForAccount(String chartCode, String accountNo, String objectCode) {
750         KualiDecimal paidInvoices = KualiDecimal.ZERO;
751         List<OlePaymentRequestDocument> payDocList = (List<OlePaymentRequestDocument>) SpringContext.getBean(
752                 BusinessObjectService.class).findAll(OlePaymentRequestDocument.class);
753         if (payDocList.size() > 0) {
754             for (OlePaymentRequestDocument olePaymentRequestDocument : payDocList) {
755                 Integer payReqId = olePaymentRequestDocument.getPurapDocumentIdentifier();
756                 Map docMap = new HashMap();
757                 docMap.put(OLEConstants.PUR_AP_IDEN, payReqId);
758                 docMap.put(OLEConstants.ITM_TYP_CD_KEY, OLEConstants.ITM_TYP_CD);
759                 List<OlePaymentRequestItem> itemList = (List<OlePaymentRequestItem>) SpringContext.getBean(
760                         BusinessObjectService.class).findMatching(OlePaymentRequestItem.class, docMap);
761                 HashMap acctMap = new HashMap();
762                 for (OlePaymentRequestItem olePaymentRequestItem : itemList) {
763                     Integer itemIdentifier = olePaymentRequestItem.getItemIdentifier();
764                     acctMap.put("itemIdentifier", olePaymentRequestItem.getItemIdentifier());
765                     acctMap.put("chartOfAccountsCode", chartCode);
766                     acctMap.put("accountNumber", accountNo);
767                     List<PaymentRequestAccount> olePaymentRequestAccount = (List<PaymentRequestAccount>) SpringContext
768                             .getBean(BusinessObjectService.class).findMatching(PaymentRequestAccount.class, acctMap);
769                     for (PaymentRequestAccount payReqAcct : olePaymentRequestAccount) {
770                         paidInvoices = paidInvoices.add(payReqAcct.getAmount());
771                     }
772                 }
773             }
774         }
775         return paidInvoices;
776     }
777 
778 
779     public KualiDecimal getSumPaidInvoicesForObject(String chartCode, String accountNo, String objectCode) {
780         KualiDecimal paidInvoices = KualiDecimal.ZERO;
781         List<OlePaymentRequestDocument> payDocList = (List<OlePaymentRequestDocument>) SpringContext.getBean(
782                 BusinessObjectService.class).findAll(OlePaymentRequestDocument.class);
783         if (payDocList.size() > 0) {
784             for (OlePaymentRequestDocument olePaymentRequestDocument : payDocList) {
785                 Integer payReqId = olePaymentRequestDocument.getPurapDocumentIdentifier();
786                 Map docMap = new HashMap();
787                 docMap.put(OLEConstants.PUR_AP_IDEN, payReqId);
788                 docMap.put(OLEConstants.ITM_TYP_CD_KEY, OLEConstants.ITM_TYP_CD);
789                 List<OlePaymentRequestItem> itemList = (List<OlePaymentRequestItem>) SpringContext.getBean(
790                         BusinessObjectService.class).findMatching(OlePaymentRequestItem.class, docMap);
791                 HashMap acctMap = new HashMap();
792                 for (OlePaymentRequestItem olePaymentRequestItem : itemList) {
793                     acctMap.put("itemIdentifier", olePaymentRequestItem.getItemIdentifier());
794                     acctMap.put("chartOfAccountsCode", chartCode);
795                     acctMap.put("accountNumber", accountNo);
796                     acctMap.put("financialObjectCode", objectCode);
797                     List<PaymentRequestAccount> olePaymentRequestAccount = (List<PaymentRequestAccount>) SpringContext
798                             .getBean(BusinessObjectService.class).findMatching(PaymentRequestAccount.class, acctMap);
799                     for (PaymentRequestAccount payReqAcct : olePaymentRequestAccount) {
800                         paidInvoices = paidInvoices.add(payReqAcct.getAmount());
801                     }
802                 }
803             }
804         }
805         return paidInvoices;
806     }
807 
808     /**
809      * An inner class to hold summary totals of pending ledger entry amounts
810      */
811     protected class PendingAmounts {
812         public KualiDecimal budget = KualiDecimal.ZERO;
813         public KualiDecimal actual = KualiDecimal.ZERO;
814         public KualiDecimal encumbrance = KualiDecimal.ZERO;
815     }
816 
817     /**
818      * Given a sufficient funds item to check, gets the prior year sufficient funds balance to check against
819      *
820      * @param item the sufficient funds item to check against
821      * @return a PendingAmounts record with the pending budget and encumbrance
822      */
823     protected PendingAmounts getPriorYearSufficientFundsBalanceAmount(SufficientFundsItem item) {
824         PendingAmounts amounts = new PendingAmounts();
825 
826         // This only gets called for sufficient funds type of Cash at Account (H). The object code in the table for this type is
827         // always
828         // 4 spaces.
829         Map<String, Object> keys = new HashMap<String, Object>();
830         keys.put(OLEPropertyConstants.UNIVERSITY_FISCAL_YEAR, Integer.valueOf(item.getYear().getUniversityFiscalYear().intValue() - 1));
831         keys.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, item.getAccount().getChartOfAccountsCode());
832         keys.put(OLEPropertyConstants.ACCOUNT_NUMBER, item.getAccount().getAccountNumber());
833         keys.put(OLEPropertyConstants.FINANCIAL_OBJECT_CODE, "    ");
834         SufficientFundBalances bal = businessObjectService.findByPrimaryKey(SufficientFundBalances.class, keys);
835 
836         if (bal != null) {
837             amounts.budget = bal.getCurrentBudgetBalanceAmount();
838             amounts.encumbrance = bal.getAccountEncumbranceAmount();
839         }
840 
841         if ( LOG.isDebugEnabled() ) {
842             LOG.debug("getPriorYearSufficientFundsBalanceAmount() budget      " + amounts.budget);
843             LOG.debug("getPriorYearSufficientFundsBalanceAmount() encumbrance " + amounts.encumbrance);
844         }
845         return amounts;
846     }
847 
848     /**
849      * Totals the amounts of actual, encumbrance, and budget amounts from related pending entries
850      *
851      * @param item a sufficient funds item to find pending amounts for
852      * @return the totals encapsulated in a PendingAmounts object
853      */
854     @SuppressWarnings("unchecked")
855     protected PendingAmounts getPendingBalanceAmount(SufficientFundsItem item) {
856         LOG.debug("getPendingBalanceAmount() started");
857 
858         Integer fiscalYear = item.getYear().getUniversityFiscalYear();
859         String chart = item.getAccount().getChartOfAccountsCode();
860         String account = item.getAccount().getAccountNumber();
861         String sfCode = item.getAccount().getAccountSufficientFundsCode();
862 
863         PendingAmounts amounts = new PendingAmounts();
864 
865         if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(sfCode)) {
866             // Cash checking
867             List years = new ArrayList();
868             years.add(item.getYear().getUniversityFiscalYear());
869 
870             // If the beginning balance isn't loaded, we need to include cash from
871             // the previous fiscal year
872             if (!item.getYear().isFinancialBeginBalanceLoadInd()) {
873                 years.add(item.getYear().getUniversityFiscalYear() - 1);
874             }
875 
876             // Calculate the pending actual amount
877             // Get Cash (debit amount - credit amount)
878             amounts.actual = generalLedgerPendingEntryService.getCashSummary(years, chart, account, true);
879             amounts.actual = amounts.actual.subtract(generalLedgerPendingEntryService.getCashSummary(years, chart, account, false));
880 
881             // Get Payables (credit amount - debit amount)
882             amounts.actual = amounts.actual.add(generalLedgerPendingEntryService.getActualSummary(years, chart, account, true));
883             amounts.actual = amounts.actual.subtract(generalLedgerPendingEntryService.getActualSummary(years, chart, account, false));
884         }
885         else {
886             // Non-Cash checking
887 
888             // Get expenditure (debit - credit)
889             amounts.actual = generalLedgerPendingEntryService.getExpenseSummary(fiscalYear, chart, account, item.getSufficientFundsObjectCode(), true, item.getDocumentTypeCode().startsWith("YE"));
890             amounts.actual = amounts.actual.subtract(generalLedgerPendingEntryService.getExpenseSummary(fiscalYear, chart, account, item.getSufficientFundsObjectCode(), false, item.getDocumentTypeCode().startsWith("YE")));
891 
892             // Get budget
893             amounts.budget = generalLedgerPendingEntryService.getBudgetSummary(fiscalYear, chart, account, item.getSufficientFundsObjectCode(), item.getDocumentTypeCode().startsWith("YE"));
894 
895             // Get encumbrance (debit - credit)
896             amounts.encumbrance = generalLedgerPendingEntryService.getEncumbranceSummary(fiscalYear, chart, account, item.getSufficientFundsObjectCode(), true, item.getDocumentTypeCode().startsWith("YE"));
897             amounts.encumbrance = amounts.encumbrance.subtract(generalLedgerPendingEntryService.getEncumbranceSummary(fiscalYear, chart, account, item.getSufficientFundsObjectCode(), false, item.getDocumentTypeCode().startsWith("YE")));
898         }
899 
900         if ( LOG.isDebugEnabled() ) {
901             LOG.debug("getPendingBalanceAmount() actual      " + amounts.actual);
902             LOG.debug("getPendingBalanceAmount() budget      " + amounts.budget);
903             LOG.debug("getPendingBalanceAmount() encumbrance " + amounts.encumbrance);
904         }
905         return amounts;
906     }
907 
908     /**
909      * Purge the sufficient funds balance table by year/chart
910      *
911      * @param chart the chart of sufficient fund balances to purge
912      * @param year the fiscal year of sufficient fund balances to purge
913      */
914     @Override
915     public void purgeYearByChart(String chart, int year) {
916         sufficientFundsDao.purgeYearByChart(chart, year);
917     }
918 
919     public void setAccountService(AccountService accountService) {
920         this.accountService = accountService;
921     }
922 
923     public void setGeneralLedgerPendingEntryService(GeneralLedgerPendingEntryService generalLedgerPendingEntryService) {
924         this.generalLedgerPendingEntryService = generalLedgerPendingEntryService;
925     }
926 
927     public void setConfigurationService(ConfigurationService kualiConfigurationService) {
928         this.kualiConfigurationService = kualiConfigurationService;
929     }
930 
931     public void setObjectLevelService(ObjectLevelService objectLevelService) {
932         this.objectLevelService = objectLevelService;
933     }
934 
935     public void setOptionsService(OptionsService optionsService) {
936         this.optionsService = optionsService;
937     }
938 
939     public void setSufficientFundBalancesDao(SufficientFundBalancesDao sufficientFundBalancesDao) {
940         this.sufficientFundBalancesDao = sufficientFundBalancesDao;
941     }
942 
943     public void setSufficientFundsDao(SufficientFundsDao sufficientFundsDao) {
944         this.sufficientFundsDao = sufficientFundsDao;
945     }
946 
947     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
948         this.businessObjectService = businessObjectService;
949     }
950 }