001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.kuali.module.purap.service.impl;
017:
018: import java.util.ArrayList;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.Set;
024:
025: import org.kuali.core.bo.Note;
026: import org.kuali.core.bo.user.UniversalUser;
027: import org.kuali.core.service.DateTimeService;
028: import org.kuali.core.service.DocumentService;
029: import org.kuali.core.util.GlobalVariables;
030: import org.kuali.core.util.KualiDecimal;
031: import org.kuali.core.util.ObjectUtils;
032: import org.kuali.kfs.bo.SourceAccountingLine;
033: import org.kuali.kfs.context.SpringContext;
034: import org.kuali.kfs.service.ParameterService;
035: import org.kuali.kfs.service.impl.ParameterConstants;
036: import org.kuali.module.chart.bo.Account;
037: import org.kuali.module.chart.service.AccountService;
038: import org.kuali.module.purap.PurapConstants;
039: import org.kuali.module.purap.PurapKeyConstants;
040: import org.kuali.module.purap.PurapPropertyConstants;
041: import org.kuali.module.purap.PurapConstants.PaymentRequestStatuses;
042: import org.kuali.module.purap.bo.CreditMemoItem;
043: import org.kuali.module.purap.bo.ItemType;
044: import org.kuali.module.purap.bo.PaymentRequestItem;
045: import org.kuali.module.purap.bo.PurApAccountingLineBase;
046: import org.kuali.module.purap.bo.PurApItem;
047: import org.kuali.module.purap.bo.PurchaseOrderItem;
048: import org.kuali.module.purap.document.AccountsPayableDocument;
049: import org.kuali.module.purap.document.CreditMemoDocument;
050: import org.kuali.module.purap.document.PaymentRequestDocument;
051: import org.kuali.module.purap.document.PurchaseOrderDocument;
052: import org.kuali.module.purap.service.AccountsPayableDocumentSpecificService;
053: import org.kuali.module.purap.service.AccountsPayableService;
054: import org.kuali.module.purap.service.PurapAccountingService;
055: import org.kuali.module.purap.service.PurapGeneralLedgerService;
056: import org.kuali.module.purap.service.PurapService;
057: import org.kuali.module.purap.service.PurchaseOrderService;
058: import org.kuali.module.purap.util.ExpiredOrClosedAccount;
059: import org.kuali.module.purap.util.ExpiredOrClosedAccountEntry;
060: import org.springframework.transaction.annotation.Transactional;
061:
062: @Transactional
063: public class AccountsPayableServiceImpl implements
064: AccountsPayableService {
065:
066: private PurapAccountingService purapAccountingService;
067: private PurapGeneralLedgerService purapGeneralLedgerService;
068: private DocumentService documentService;
069: private PurapService purapService;
070: private ParameterService parameterService;
071:
072: public void setParameterService(ParameterService parameterService) {
073: this .parameterService = parameterService;
074: }
075:
076: public void setPurapService(PurapService purapService) {
077: this .purapService = purapService;
078: }
079:
080: public void setPurapAccountingService(
081: PurapAccountingService purapAccountingService) {
082: this .purapAccountingService = purapAccountingService;
083: }
084:
085: public void setPurapGeneralLedgerService(
086: PurapGeneralLedgerService purapGeneralLedgerService) {
087: this .purapGeneralLedgerService = purapGeneralLedgerService;
088: }
089:
090: public void setDocumentService(DocumentService documentService) {
091: this .documentService = documentService;
092: }
093:
094: /**
095: * @see org.kuali.module.purap.service.AccountsPayableService#getExpiredOrClosedAccountList(org.kuali.module.purap.document.AccountsPayableDocument)
096: */
097: public HashMap<String, ExpiredOrClosedAccountEntry> getExpiredOrClosedAccountList(
098: AccountsPayableDocument document) {
099:
100: // Retrieve a list of accounts and replacement accounts, where accounts or closed or expired.
101: HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccounts = expiredOrClosedAccountsList(document);
102:
103: return expiredOrClosedAccounts;
104: }
105:
106: /**
107: * @see org.kuali.module.purap.service.AccountsPayableService#generateExpiredOrClosedAccountNote(org.kuali.module.purap.document.AccountsPayableDocument,
108: * java.util.HashMap)
109: */
110: public void generateExpiredOrClosedAccountNote(
111: AccountsPayableDocument document,
112: HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
113:
114: // create a note of all the replacement accounts
115: if (!expiredOrClosedAccountList.isEmpty()) {
116: addContinuationAccountsNote(document,
117: expiredOrClosedAccountList);
118: }
119:
120: }
121:
122: /**
123: * @see org.kuali.module.purap.service.AccountsPayableService#generateExpiredOrClosedAccountWarning(org.kuali.module.purap.document.AccountsPayableDocument)
124: */
125: public void generateExpiredOrClosedAccountWarning(
126: AccountsPayableDocument document) {
127:
128: // get user
129: UniversalUser user = GlobalVariables.getUserSession()
130: .getUniversalUser();
131:
132: // get parameter to see if fiscal officers may see the continuation account warning
133: String showContinuationAccountWaringFO = parameterService
134: .getParameterValue(
135: ParameterConstants.PURCHASING_DOCUMENT.class,
136: PurapConstants.PURAP_AP_SHOW_CONTINUATION_ACCOUNT_WARNING_FISCAL_OFFICERS);
137:
138: // get parameter to see if ap users may see the continuation account warning
139: String showContinuationAccountWaringAP = parameterService
140: .getParameterValue(
141: ParameterConstants.PURCHASING_DOCUMENT.class,
142: PurapConstants.PURAP_AP_SHOW_CONTINUATION_ACCOUNT_WARNING_AP_USERS);
143:
144: // versus doing it in their respective documents (preq, credit memo)
145: // document is past full entry and
146: // user is a fiscal officer and a system parameter is set to allow viewing
147: // and if the continuation account indicator is set
148: if (purapService.isFullDocumentEntryCompleted(document)
149: && (isFiscalUser(document, user) && "Y"
150: .equals(showContinuationAccountWaringFO))
151: && (document.isContinuationAccountIndicator())) {
152:
153: GlobalVariables
154: .getMessageList()
155: .add(
156: PurapKeyConstants.MESSAGE_CLOSED_OR_EXPIRED_ACCOUNTS_REPLACED);
157: }
158: }
159:
160: /**
161: * @see org.kuali.module.purap.service.AccountsPayableService#processExpiredOrClosedAccount(org.kuali.module.purap.bo.PurApAccountingLineBase,
162: * java.util.HashMap)
163: */
164: public void processExpiredOrClosedAccount(
165: PurApAccountingLineBase acctLineBase,
166: HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
167:
168: ExpiredOrClosedAccountEntry accountEntry = null;
169: String acctKey = acctLineBase.getChartOfAccountsCode() + "-"
170: + acctLineBase.getAccountNumber();
171:
172: if (expiredOrClosedAccountList.containsKey(acctKey)) {
173:
174: accountEntry = expiredOrClosedAccountList.get(acctKey);
175:
176: if (accountEntry.getOriginalAccount()
177: .isContinuationAccountMissing() == false) {
178: acctLineBase.setChartOfAccountsCode(accountEntry
179: .getReplacementAccount()
180: .getChartOfAccountsCode());
181: acctLineBase.setAccountNumber(accountEntry
182: .getReplacementAccount().getAccountNumber());
183: acctLineBase.refreshReferenceObject("chart");
184: acctLineBase.refreshReferenceObject("account");
185: }
186: }
187: }
188:
189: /**
190: * Creates and adds a note indicating accounts replaced and what they replaced and attaches it to the document.
191: *
192: * @param document The accounts payable document to which we're adding the note.
193: * @param accounts The HashMap where the keys are the string representations of the chart and account of the
194: * original account and the values are the ExpiredOrClosedAccountEntry.
195: */
196: private void addContinuationAccountsNote(
197: AccountsPayableDocument document,
198: HashMap<String, ExpiredOrClosedAccountEntry> accounts) {
199: String noteText;
200: StringBuffer sb = new StringBuffer("");
201: ExpiredOrClosedAccountEntry accountEntry = null;
202: ExpiredOrClosedAccount originalAccount = null;
203: ExpiredOrClosedAccount replacementAccount = null;
204:
205: // List the entries using entrySet()
206: Set entries = accounts.entrySet();
207: Iterator it = entries.iterator();
208:
209: // loop through the accounts found to be expired/closed and add if they have a continuation account
210: while (it.hasNext()) {
211: Map.Entry entry = (Map.Entry) it.next();
212: accountEntry = (ExpiredOrClosedAccountEntry) entry
213: .getValue();
214: originalAccount = accountEntry.getOriginalAccount();
215: replacementAccount = accountEntry.getReplacementAccount();
216:
217: // only print out accounts that were replaced and not missing a continuation account
218: if (originalAccount.isContinuationAccountMissing() == false) {
219: sb
220: .append(" Account "
221: + originalAccount.getAccountString()
222: + " was replaced with account "
223: + replacementAccount.getAccountString()
224: + " ; ");
225: }
226:
227: }
228:
229: // if a note was created, add it to the document
230: if (sb.toString().length() > 0) {
231: try {
232: DocumentService documentService = SpringContext
233: .getBean(DocumentService.class);
234: Note resetNote = documentService
235: .createNoteFromDocument(document, sb.toString());
236: documentService.addNoteToDocument(document, resetNote);
237: } catch (Exception e) {
238: throw new RuntimeException(
239: PurapConstants.REQ_UNABLE_TO_CREATE_NOTE + " "
240: + e);
241: }
242: }
243: }
244:
245: /**
246: * Generates a list of replacement accounts for expired or closed accounts, as well as expired/closed accounts
247: * without a continuation account.
248: *
249: * @param document The accounts payable document from which we're obtaining the purchase order id to be used
250: * to obtain the purchase order document, whose accounts we'll use to generate the list of
251: * replacement accounts for expired or closed accounts.
252: * @return The HashMap where the keys are the string representations of the chart
253: * and account of the original account and the values are the ExpiredOrClosedAccountEntry.
254: */
255: private HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountsList(
256: AccountsPayableDocument document) {
257:
258: HashMap<String, ExpiredOrClosedAccountEntry> list = new HashMap<String, ExpiredOrClosedAccountEntry>();
259: ExpiredOrClosedAccountEntry entry = null;
260: ExpiredOrClosedAccount originalAcct = null;
261: ExpiredOrClosedAccount replaceAcct = null;
262: String chartAccount = null;
263: Integer POID = document.getPurchaseOrderIdentifier();
264:
265: PurchaseOrderDocument po = document.getPurchaseOrderDocument();
266:
267: if (po != null) {
268: // get list of active accounts
269: PurapAccountingService pas = SpringContext
270: .getBean(PurapAccountingService.class);
271: List<SourceAccountingLine> accountList = pas
272: .generateSummary(po.getItemsActiveOnly());
273:
274: // loop through accounts
275: for (SourceAccountingLine poAccountingLine : accountList) {
276:
277: AccountService as = SpringContext
278: .getBean(AccountService.class);
279: Account account = as.getByPrimaryId(poAccountingLine
280: .getChartOfAccountsCode(), poAccountingLine
281: .getAccountNumber());
282:
283: entry = new ExpiredOrClosedAccountEntry();
284:
285: originalAcct = new ExpiredOrClosedAccount(
286: poAccountingLine.getChartOfAccountsCode(),
287: poAccountingLine.getAccountNumber(),
288: poAccountingLine.getSubAccountNumber());
289:
290: if (account.isAccountClosedIndicator()) {
291:
292: // 1. if the account is closed, get the continuation account and add it to the list
293: Account continuationAccount = as.getByPrimaryId(
294: account.getContinuationFinChrtOfAcctCd(),
295: account.getContinuationAccountNumber());
296:
297: if (continuationAccount == null) {
298: replaceAcct = new ExpiredOrClosedAccount();
299: originalAcct
300: .setContinuationAccountMissing(true);
301:
302: entry.setOriginalAccount(originalAcct);
303: entry.setReplacementAccount(replaceAcct);
304:
305: list.put(
306: createChartAccountString(originalAcct),
307: entry);
308: } else {
309: replaceAcct = new ExpiredOrClosedAccount(
310: continuationAccount
311: .getChartOfAccountsCode(),
312: continuationAccount.getAccountNumber(),
313: poAccountingLine.getSubAccountNumber());
314:
315: entry.setOriginalAccount(originalAcct);
316: entry.setReplacementAccount(replaceAcct);
317:
318: list.put(
319: createChartAccountString(originalAcct),
320: entry);
321: }
322: // 2. if the account is expired and the current date is <= 90 days from the expiration date, do nothing
323: // 3. if the account is expired and the current date is > 90 days from the expiration date, get the continuation
324: // account and add it to the list
325: } else if (account.isExpired()) {
326: Account continuationAccount = as.getByPrimaryId(
327: account.getContinuationFinChrtOfAcctCd(),
328: account.getContinuationAccountNumber());
329:
330: // if account is C&G and expired then add to list.
331: if ((account.isForContractsAndGrants() && SpringContext
332: .getBean(DateTimeService.class).dateDiff(
333: account.getAccountExpirationDate(),
334: SpringContext.getBean(
335: DateTimeService.class)
336: .getCurrentDate(), true) > 90)) {
337:
338: if (continuationAccount == null) {
339: replaceAcct = new ExpiredOrClosedAccount();
340: originalAcct
341: .setContinuationAccountMissing(true);
342:
343: entry.setOriginalAccount(originalAcct);
344: entry.setReplacementAccount(replaceAcct);
345:
346: list
347: .put(
348: createChartAccountString(originalAcct),
349: entry);
350: } else {
351: replaceAcct = new ExpiredOrClosedAccount(
352: continuationAccount
353: .getChartOfAccountsCode(),
354: continuationAccount
355: .getAccountNumber(),
356: poAccountingLine
357: .getSubAccountNumber());
358:
359: entry.setOriginalAccount(originalAcct);
360: entry.setReplacementAccount(replaceAcct);
361:
362: list
363: .put(
364: createChartAccountString(originalAcct),
365: entry);
366: }
367: }
368:
369: // if account is not C&G, use the same account, do not replace
370: }
371: }
372: }
373: return list;
374: }
375:
376: /**
377: * Creates a chart-account string.
378: *
379: * @param ecAccount The account whose chart and account number we're going to use to create the resulting String for this method.
380: * @return The string representing the chart and account number of the given ecAccount.
381: */
382: private String createChartAccountString(
383: ExpiredOrClosedAccount ecAccount) {
384: StringBuffer buff = new StringBuffer("");
385:
386: buff.append(ecAccount.getChartOfAccountsCode());
387: buff.append("-");
388: buff.append(ecAccount.getAccountNumber());
389:
390: return buff.toString();
391: }
392:
393: /**
394: * Determines if the user is a fiscal officer. Currently this only checks the doc and workflow status for approval requested
395: *
396: * @param document The document to be used to check the status code and whether the workflow approval is requested.
397: * @param user The current user.
398: * @return boolean true if the user is a fiscal officer.
399: */
400: private boolean isFiscalUser(AccountsPayableDocument document,
401: UniversalUser user) {
402: boolean isFiscalUser = false;
403:
404: if (PaymentRequestStatuses.AWAITING_FISCAL_REVIEW
405: .equals(document.getStatusCode())
406: && document.getDocumentHeader().getWorkflowDocument()
407: .isApprovalRequested()) {
408: isFiscalUser = true;
409: }
410:
411: return isFiscalUser;
412: }
413:
414: /**
415: * @see org.kuali.module.purap.service.AccountsPayableService#cancelAccountsPayableDocument(org.kuali.module.purap.document.AccountsPayableDocument, java.lang.String)
416: */
417: public void cancelAccountsPayableDocument(
418: AccountsPayableDocument apDocument, String currentNodeName) {
419: if (purapService.isFullDocumentEntryCompleted(apDocument)) {
420: purapGeneralLedgerService
421: .generateEntriesCancelAccountsPayableDocument(apDocument);
422: }
423: AccountsPayableDocumentSpecificService accountsPayableDocumentSpecificService = apDocument
424: .getDocumentSpecificService();
425: accountsPayableDocumentSpecificService.updateStatusByNode(
426: currentNodeName, apDocument);
427: apDocument
428: .refreshReferenceObject(PurapPropertyConstants.STATUS);
429:
430: // close/reopen purchase order.
431: accountsPayableDocumentSpecificService
432: .takePurchaseOrderCancelAction(apDocument);
433: }
434:
435: /**
436: * @see org.kuali.module.purap.service.AccountsPayableService#updateItemList(org.kuali.module.purap.document.AccountsPayableDocument)
437: */
438: public void updateItemList(AccountsPayableDocument apDocument) {
439: // don't run the following if past full entry
440: if (purapService.isFullDocumentEntryCompleted(apDocument)) {
441: return;
442: }
443: if (apDocument instanceof CreditMemoDocument) {
444: CreditMemoDocument cm = (CreditMemoDocument) apDocument;
445: if (cm.isSourceDocumentPaymentRequest()) {
446: // just update encumberances, items shouldn't change, get to them through po (or through preq)
447: List<PaymentRequestItem> items = cm
448: .getPaymentRequestDocument().getItems();
449: for (PaymentRequestItem preqItem : items) {
450: // skip inactive and below the line
451: if (!preqItem.getItemType()
452: .isItemTypeAboveTheLineIndicator()) {
453: continue;
454: }
455: PurchaseOrderItem poItem = preqItem
456: .getPurchaseOrderItem();
457: CreditMemoItem cmItem = (CreditMemoItem) cm
458: .getAPItemFromPOItem(poItem);
459: // take invoiced quantities from the lower of the preq and po if different
460: updateEncumberances(preqItem, poItem, cmItem);
461: }
462:
463: } else if (cm.isSourceDocumentPurchaseOrder()) {
464: PurchaseOrderDocument po = SpringContext
465: .getBean(PurchaseOrderService.class)
466: .getCurrentPurchaseOrder(
467: apDocument.getPurchaseOrderIdentifier());
468: List<PurchaseOrderItem> poItems = po.getItems();
469: List<CreditMemoItem> cmItems = cm.getItems();
470: // iterate through the above the line poItems to find matching
471: for (PurchaseOrderItem purchaseOrderItem : poItems) {
472: // skip inactive and below the line
473: if (!purchaseOrderItem.getItemType()
474: .isItemTypeAboveTheLineIndicator()) {
475: continue;
476: }
477:
478: CreditMemoItem cmItem = (CreditMemoItem) cm
479: .getAPItemFromPOItem(purchaseOrderItem);
480: // check if any action needs to be taken on the items (i.e. add for new eligible items or remove for ineligible)
481: if (apDocument.getDocumentSpecificService()
482: .poItemEligibleForAp(apDocument,
483: purchaseOrderItem)) {
484: // if eligible and not there - add
485: if (ObjectUtils.isNull(cmItem)) {
486: cmItems.add(new CreditMemoItem(cm,
487: purchaseOrderItem));
488: } else {
489: // is eligible and on doc, update encumberances
490: // (this is only qty and amount for now NOTE we should also update other key fields, like description
491: // etc in case ammendment modified a line
492: updateEncumberance(purchaseOrderItem,
493: cmItem);
494: }
495: } else { // if not eligible and there - remove
496: if (ObjectUtils.isNotNull(cmItem)) {
497: cmItems.remove(cmItem);
498: // don't update encumberance
499: continue;
500: }
501: }
502:
503: }
504: } // else do nothing
505: return;
506:
507: // finally update encumbrances
508: } else if (apDocument instanceof PaymentRequestDocument) {
509:
510: // get a fresh purchase order
511: PurchaseOrderDocument po = SpringContext.getBean(
512: PurchaseOrderService.class)
513: .getCurrentPurchaseOrder(
514: apDocument.getPurchaseOrderIdentifier());
515: PaymentRequestDocument preq = (PaymentRequestDocument) apDocument;
516:
517: List<PurchaseOrderItem> poItems = po.getItems();
518: List<PaymentRequestItem> preqItems = preq.getItems();
519: // iterate through the above the line poItems to find matching
520: for (PurchaseOrderItem purchaseOrderItem : poItems) {
521: // skip below the line
522: if (!purchaseOrderItem.getItemType()
523: .isItemTypeAboveTheLineIndicator()) {
524: continue;
525: }
526: PaymentRequestItem preqItem = (PaymentRequestItem) preq
527: .getAPItemFromPOItem(purchaseOrderItem);
528: // check if any action needs to be taken on the items (i.e. add for new eligible items or remove for ineligible)
529: if (apDocument.getDocumentSpecificService()
530: .poItemEligibleForAp(apDocument,
531: purchaseOrderItem)) {
532: // if eligible and not there - add
533: if (ObjectUtils.isNull(preqItem)) {
534: preqItems.add(new PaymentRequestItem(
535: purchaseOrderItem, preq));
536: } else {
537: updatePossibleAmmendedFields(purchaseOrderItem,
538: preqItem);
539: }
540: } else { // if not eligible and there - remove
541: if (ObjectUtils.isNotNull(preqItem)) {
542: preqItems.remove(preqItem);
543: }
544: }
545:
546: }
547: }
548: }
549:
550: /**
551: * Updates fields that could've been changed on amendment.
552: *
553: * @param sourceItem The purchase order item from which we're getting the unit price, catalog number and description to be set in the destItem.
554: * @param destItem The payment request item to which we're setting the unit price, catalog number and description.
555: */
556: private void updatePossibleAmmendedFields(
557: PurchaseOrderItem sourceItem, PaymentRequestItem destItem) {
558: destItem.setPurchaseOrderItemUnitPrice(sourceItem
559: .getItemUnitPrice());
560: destItem
561: .setItemCatalogNumber(sourceItem.getItemCatalogNumber());
562: destItem.setItemDescription(sourceItem.getItemDescription());
563: }
564:
565: /**
566: * Updates encumberances.
567: *
568: * @param preqItem The payment request item from which we're obtaining the item quantity, unit price and extended price.
569: * @param poItem The purchase order item from which we're obtaining the invoice total quantity, unit price and invoice total amount.
570: * @param cmItem The credit memo item whose invoice total quantity, unit price and extended price are to be updated.
571: */
572: private void updateEncumberances(PaymentRequestItem preqItem,
573: PurchaseOrderItem poItem, CreditMemoItem cmItem) {
574: if (poItem.getItemInvoicedTotalQuantity() != null
575: && preqItem.getItemQuantity() != null
576: && poItem.getItemInvoicedTotalQuantity().isLessThan(
577: preqItem.getItemQuantity())) {
578: cmItem.setPreqInvoicedTotalQuantity(poItem
579: .getItemInvoicedTotalQuantity());
580: cmItem.setPreqUnitPrice(poItem.getItemUnitPrice());
581: cmItem.setPreqExtendedPrice(poItem
582: .getItemInvoicedTotalAmount());
583: } else {
584: cmItem.setPreqInvoicedTotalQuantity(preqItem
585: .getItemQuantity());
586: cmItem.setPreqUnitPrice(preqItem.getItemUnitPrice());
587: cmItem.setPreqExtendedPrice(preqItem.getExtendedPrice());
588: }
589: }
590:
591: /**
592: * Updates the encumberance related fields.
593: *
594: * @param purchaseOrderItem The purchase order item from which we're obtaining the invoice total quantity, unit price and invoice total amount.
595: * @param cmItem The credit memo item whose invoice total quantity, unit price and extended price are to be updated.
596: */
597: private void updateEncumberance(
598: PurchaseOrderItem purchaseOrderItem, CreditMemoItem cmItem) {
599: cmItem.setPoInvoicedTotalQuantity(purchaseOrderItem
600: .getItemInvoicedTotalQuantity());
601: cmItem.setPreqUnitPrice(purchaseOrderItem.getItemUnitPrice());
602: cmItem.setPoExtendedPrice(purchaseOrderItem
603: .getItemInvoicedTotalAmount());
604: }
605:
606: /**
607: * @see org.kuali.module.purap.service.AccountsPayableService#purchaseOrderItemEligibleForPayment(org.kuali.module.purap.bo.PurchaseOrderItem)
608: */
609: public boolean purchaseOrderItemEligibleForPayment(
610: PurchaseOrderItem poi) {
611: if (ObjectUtils.isNull(poi)) {
612: throw new RuntimeException(
613: "item null in purchaseOrderItemEligibleForPayment ... this should never happen");
614: }
615:
616: // if the po item is not active... skip it
617: if (!poi.isItemActiveIndicator()) {
618: return false;
619: }
620:
621: ItemType poiType = poi.getItemType();
622:
623: if (poiType.isQuantityBasedGeneralLedgerIndicator()) {
624: if (poi.getItemQuantity().isGreaterThan(
625: poi.getItemInvoicedTotalQuantity())) {
626: return true;
627: }
628: return false;
629: } else { // not quantity based
630: if (poi.getItemOutstandingEncumberedAmount().isGreaterThan(
631: KualiDecimal.ZERO)) {
632: return true;
633: }
634: return false;
635: }
636: }
637:
638: }
|