1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
73
74 public SufficientFundsServiceImpl() {
75 super();
76 }
77
78
79
80
81
82
83
84
85
86
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
119
120
121
122
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
133
134
135
136
137 @SuppressWarnings("unchecked")
138 protected boolean isYearEndDocument(Class documentClass) {
139 return YearEndDocument.class.isAssignableFrom(documentClass);
140 }
141
142
143
144
145
146
147
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
217
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
229
230
231
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
240
241
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
272
273
274
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
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
352
353
354
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
366
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
378 if (!item.getYear().isFinancialBeginBalanceLoadInd()) {
379 availableBalance = sfBalance.getCurrentBudgetBalanceAmount().add(priorYearPending.budget)
380
381
382
383 .add(pending.actual)
384 .subtract(sfBalance.getAccountEncumbranceAmount())
385 .subtract(priorYearPending.encumbrance);
386 }
387 else {
388 availableBalance = sfBalance.getCurrentBudgetBalanceAmount().add(pending.actual)
389 .subtract(sfBalance.getAccountEncumbranceAmount());
390 }
391 }
392 else {
393 availableBalance = sfBalance.getCurrentBudgetBalanceAmount()
394 .add(pending.budget)
395 .subtract(sfBalance.getAccountActualExpenditureAmt())
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
425
426
427
428
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
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
583 if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(item.getAccount().getAccountSufficientFundsCode())
584 || item.getYear().getBudgetCheckingBalanceTypeCd().equals(item.getBalanceTyp().getCode())) {
585
586
587
588
589
590 balanceAmount = balanceAmount.negated();
591 }
592
593
594
595
596
597
598 PendingAmounts priorYearPending = new PendingAmounts();
599
600
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
614 if (!item.getYear().isFinancialBeginBalanceLoadInd()) {
615 availableBalance = sfBalance.getCurrentBudgetBalanceAmount().add(priorYearPending.budget)
616
617
618
619 .add(pending.actual)
620 .subtract(sfBalance.getAccountEncumbranceAmount())
621 .subtract(priorYearPending.encumbrance);
622 }
623 else {
624 availableBalance = sfBalance.getCurrentBudgetBalanceAmount().add(pending.actual)
625 .subtract(sfBalance.getAccountEncumbranceAmount());
626 }
627 }
628 else {
629 availableBalance = sfBalance.getCurrentBudgetBalanceAmount()
630
631
632 .subtract(sfBalance.getAccountActualExpenditureAmt())
633
634 .subtract(pending.actual);
635 freeBalance = sfBalance.getCurrentBudgetBalanceAmount()
636 .subtract(sfBalance.getAccountActualExpenditureAmt()).subtract(pending.actual);
637 }
638
639
640
641
642
643
644
645
646
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
676
677
678
679
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
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
819
820
821
822
823 protected PendingAmounts getPriorYearSufficientFundsBalanceAmount(SufficientFundsItem item) {
824 PendingAmounts amounts = new PendingAmounts();
825
826
827
828
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
850
851
852
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
867 List years = new ArrayList();
868 years.add(item.getYear().getUniversityFiscalYear());
869
870
871
872 if (!item.getYear().isFinancialBeginBalanceLoadInd()) {
873 years.add(item.getYear().getUniversityFiscalYear() - 1);
874 }
875
876
877
878 amounts.actual = generalLedgerPendingEntryService.getCashSummary(years, chart, account, true);
879 amounts.actual = amounts.actual.subtract(generalLedgerPendingEntryService.getCashSummary(years, chart, account, false));
880
881
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
887
888
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
893 amounts.budget = generalLedgerPendingEntryService.getBudgetSummary(fiscalYear, chart, account, item.getSufficientFundsObjectCode(), item.getDocumentTypeCode().startsWith("YE"));
894
895
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
910
911
912
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 }