001/* 002 * Copyright 2006 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 */ 016package org.kuali.ole.gl.service.impl; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Map; 024 025import org.kuali.ole.coa.businessobject.ObjectCode; 026import org.kuali.ole.coa.service.AccountService; 027import org.kuali.ole.coa.service.ObjectLevelService; 028import org.kuali.ole.coa.service.ObjectTypeService; 029import org.kuali.ole.fp.document.YearEndDocument; 030import org.kuali.ole.gl.batch.dataaccess.SufficientFundsDao; 031import org.kuali.ole.gl.businessobject.SufficientFundBalances; 032import org.kuali.ole.gl.businessobject.SufficientFundRebuild; 033import org.kuali.ole.gl.businessobject.Transaction; 034import org.kuali.ole.gl.dataaccess.SufficientFundBalancesDao; 035import org.kuali.ole.gl.service.SufficientFundsService; 036import org.kuali.ole.gl.service.SufficientFundsServiceConstants; 037import org.kuali.ole.module.purap.businessobject.PaymentRequestAccount; 038import org.kuali.ole.select.businessobject.OlePaymentRequestItem; 039import org.kuali.ole.select.businessobject.OleSufficientFundCheck; 040import org.kuali.ole.select.document.OlePaymentRequestDocument; 041import org.kuali.ole.sys.OLEConstants; 042import org.kuali.ole.sys.OLEPropertyConstants; 043import org.kuali.ole.sys.businessobject.SufficientFundsItem; 044import org.kuali.ole.sys.businessobject.SystemOptions; 045import org.kuali.ole.sys.context.SpringContext; 046import org.kuali.ole.sys.document.GeneralLedgerPostingDocument; 047import org.kuali.ole.sys.service.GeneralLedgerPendingEntryService; 048import org.kuali.ole.sys.service.OptionsService; 049import org.kuali.rice.core.api.config.property.ConfigurationService; 050import org.kuali.rice.core.api.util.type.KualiDecimal; 051import org.kuali.rice.krad.service.BusinessObjectService; 052import org.kuali.rice.krad.util.ObjectUtils; 053import org.springframework.transaction.annotation.Transactional; 054 055/** 056 * The base implementation of SufficientFundsService 057 */ 058@Transactional 059public class SufficientFundsServiceImpl implements SufficientFundsService, SufficientFundsServiceConstants { 060 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SufficientFundsServiceImpl.class); 061 062 private AccountService accountService; 063 private ObjectLevelService objectLevelService; 064 private ConfigurationService kualiConfigurationService; 065 private SufficientFundsDao sufficientFundsDao; 066 private SufficientFundBalancesDao sufficientFundBalancesDao; 067 private OptionsService optionsService; 068 private GeneralLedgerPendingEntryService generalLedgerPendingEntryService; 069 private BusinessObjectService businessObjectService; 070 071 /** 072 * Default constructor 073 */ 074 public SufficientFundsServiceImpl() { 075 super(); 076 } 077 078 /** 079 * This operation derives the acct_sf_finobj_cd which is used to populate the General Ledger Pending entry table, so that later 080 * we can do Suff Fund checking against that entry 081 * 082 * @param financialObject the object code being checked against 083 * @param accountSufficientFundsCode the kind of sufficient funds checking turned on in this system 084 * @return the object code that should be used for the sufficient funds inquiry, or a blank String 085 * @see org.kuali.ole.gl.service.SufficientFundsService#getSufficientFundsObjectCode(org.kuali.ole.coa.businessobject.ObjectCode, 086 * java.lang.String) 087 */ 088 @Override 089 public String getSufficientFundsObjectCode(ObjectCode financialObject, String accountSufficientFundsCode) { 090 if ( LOG.isDebugEnabled() ) { 091 LOG.debug("Obtaining SF Object code for: " + accountSufficientFundsCode + " - " + financialObject.getFinancialObjectCode() ); 092 } 093 if (OLEConstants.SF_TYPE_NO_CHECKING.equals(accountSufficientFundsCode)) { 094 return OLEConstants.NOT_AVAILABLE_STRING; 095 } 096 else if (OLEConstants.SF_TYPE_ACCOUNT.equals(accountSufficientFundsCode)) { 097 return " "; 098 } 099 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}