View Javadoc
1   /*
2    * The Kuali Financial System, a comprehensive financial management system for higher education.
3    * 
4    * Copyright 2005-2014 The Kuali Foundation
5    * 
6    * This program is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Affero General Public License as
8    * published by the Free Software Foundation, either version 3 of the
9    * License, or (at your option) any later version.
10   * 
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Affero General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Affero General Public License
17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package org.kuali.kfs.module.purap.document;
20  
21  import java.math.BigDecimal;
22  import java.text.MessageFormat;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import org.apache.commons.collections.CollectionUtils;
31  import org.apache.commons.lang.ArrayUtils;
32  import org.apache.commons.lang.StringUtils;
33  import org.kuali.kfs.module.purap.PurapConstants;
34  import org.kuali.kfs.module.purap.PurapParameterConstants;
35  import org.kuali.kfs.module.purap.PurapPropertyConstants;
36  import org.kuali.kfs.module.purap.businessobject.ItemType;
37  import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
38  import org.kuali.kfs.module.purap.businessobject.PurApItem;
39  import org.kuali.kfs.module.purap.businessobject.PurApItemBase;
40  import org.kuali.kfs.module.purap.businessobject.PurApItemUseTax;
41  import org.kuali.kfs.module.purap.businessobject.PurchaseOrderView;
42  import org.kuali.kfs.module.purap.businessobject.SensitiveData;
43  import org.kuali.kfs.module.purap.document.service.PurapService;
44  import org.kuali.kfs.module.purap.document.service.impl.PurapServiceImpl;
45  import org.kuali.kfs.module.purap.service.PurapAccountingService;
46  import org.kuali.kfs.module.purap.service.SensitiveDataService;
47  import org.kuali.kfs.module.purap.util.PurApRelatedViews;
48  import org.kuali.kfs.sys.KFSConstants.AdHocPaymentIndicator;
49  import org.kuali.kfs.sys.businessobject.AccountingLine;
50  import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
51  import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
52  import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
53  import org.kuali.kfs.sys.context.SpringContext;
54  import org.kuali.kfs.sys.document.AccountingDocumentBase;
55  import org.kuali.kfs.sys.document.AmountTotaling;
56  import org.kuali.kfs.sys.service.UniversityDateService;
57  import org.kuali.kfs.vnd.businessobject.VendorAddress;
58  import org.kuali.kfs.vnd.businessobject.VendorDetail;
59  import org.kuali.kfs.vnd.document.service.VendorService;
60  import org.kuali.rice.core.api.util.type.KualiDecimal;
61  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
62  import org.kuali.rice.kew.api.WorkflowDocument;
63  import org.kuali.rice.krad.rules.rule.event.ApproveDocumentEvent;
64  import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
65  import org.kuali.rice.krad.rules.rule.event.RouteDocumentEvent;
66  import org.kuali.rice.krad.service.KualiModuleService;
67  import org.kuali.rice.krad.service.ModuleService;
68  import org.kuali.rice.krad.util.NoteType;
69  import org.kuali.rice.krad.util.ObjectUtils;
70  import org.kuali.rice.location.api.LocationConstants;
71  import org.kuali.rice.location.framework.country.CountryEbo;
72  
73  /**
74   * Base class for Purchasing-Accounts Payable Documents.
75   */
76  public abstract class PurchasingAccountsPayableDocumentBase extends AccountingDocumentBase implements PurchasingAccountsPayableDocument, AmountTotaling {
77  
78      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PurchasingAccountsPayableDocumentBase.class);
79  
80      // SHARED FIELDS BETWEEN REQUISITION, PURCHASE ORDER, PAYMENT REQUEST, AND CREDIT MEMO
81      protected Integer purapDocumentIdentifier;
82      protected Integer vendorHeaderGeneratedIdentifier;
83      protected Integer vendorDetailAssignedIdentifier;
84      protected String vendorCustomerNumber;
85      protected String vendorName;
86      protected String vendorLine1Address;
87      protected String vendorLine2Address;
88      protected String vendorCityName;
89      protected String vendorStateCode;
90      protected String vendorAddressInternationalProvinceName;
91      protected String vendorPostalCode;
92      protected String vendorCountryCode;
93      protected Integer accountsPayablePurchasingDocumentLinkIdentifier;
94      protected boolean useTaxIndicator;
95      protected String vendorAttentionName;
96      protected String accountDistributionMethod;  //code for account distribution method
97  
98      // NOT PERSISTED IN DB
99      protected String vendorNumber;
100     protected Integer vendorAddressGeneratedIdentifier;
101     protected Boolean overrideWorkflowButtons = null;
102     protected transient PurApRelatedViews relatedViews;
103     protected boolean sensitive;
104 
105     protected boolean calculated;
106 
107     // COLLECTIONS
108     protected List<PurApItem> items;
109     protected List<SourceAccountingLine> accountsForRouting; // don't use me for anything else!!
110 
111     // REFERENCE OBJECTS
112     protected VendorDetail vendorDetail;
113     protected CountryEbo vendorCountry;
114 
115     // STATIC
116     public transient String[] belowTheLineTypes;
117 
118     // workaround for purapOjbCollectionHelper - remove when merged into rice
119     public boolean allowDeleteAwareCollection = true;
120 
121     /**
122      * Default constructor to be overridden.
123      */
124     public PurchasingAccountsPayableDocumentBase() {
125         items = new ArrayList();
126     }
127 
128     protected GeneralLedgerPendingEntry getFirstPendingGLEntry() {
129         if (ObjectUtils.isNotNull(getGeneralLedgerPendingEntries()) && !getGeneralLedgerPendingEntries().isEmpty()) {
130             return getGeneralLedgerPendingEntries().get(0);
131         }
132         return null;
133     }
134 
135     public Integer getPostingYearFromPendingGLEntries() {
136         GeneralLedgerPendingEntry glpe = getFirstPendingGLEntry();
137         if (ObjectUtils.isNotNull(glpe)) {
138             return glpe.getUniversityFiscalYear();
139         }
140         return null;
141     }
142 
143     public String getPostingPeriodCodeFromPendingGLEntries() {
144         GeneralLedgerPendingEntry glpe = getFirstPendingGLEntry();
145         if (ObjectUtils.isNotNull(glpe)) {
146             return glpe.getUniversityFiscalPeriodCode();
147         }
148         return null;
149     }
150 
151     public List<SourceAccountingLine> getAccountsForRouting() {
152         if (accountsForRouting == null) {
153             populateAccountsForRouting();
154         }
155         return accountsForRouting;
156     }
157 
158     public void setAccountsForRouting(List<SourceAccountingLine> accountsForRouting) {
159         this.accountsForRouting = accountsForRouting;
160     }
161 
162     /**
163      * Makes sure that accounts for routing has been generated, so that other information can be retrieved from that
164      */
165     protected void populateAccountsForRouting() {
166         SpringContext.getBean(PurapAccountingService.class).updateAccountAmounts(this);
167         setAccountsForRouting(SpringContext.getBean(PurapAccountingService.class).generateSummary(getItems()));
168         // need to refresh to get the references for the searchable attributes (ie status) and for invoking route levels (ie account
169         // objects) -hjs
170         refreshNonUpdateableReferences();
171         for (SourceAccountingLine sourceLine : getAccountsForRouting()) {
172             sourceLine.refreshNonUpdateableReferences();
173         }
174     }
175 
176     public boolean isSensitive() {
177         List<SensitiveData> sensitiveData = SpringContext.getBean(SensitiveDataService.class).getSensitiveDatasAssignedByRelatedDocId(getAccountsPayablePurchasingDocumentLinkIdentifier());
178         if (ObjectUtils.isNotNull(sensitiveData) && !sensitiveData.isEmpty()) {
179             return true;
180         }
181         return false;
182     }
183 
184     /**
185      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#isInquiryRendered()
186      */
187     @Override
188     public boolean isInquiryRendered() {
189         return isPostingYearPrior();
190     }
191 
192     /**
193      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#isPostingYearNext()
194      */
195     @Override
196     public boolean isPostingYearNext() {
197         Integer currentFY = SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
198         return (getPostingYear().compareTo(currentFY) > 0);
199     }
200 
201     /**
202      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#isPostingYearPrior()
203      */
204     @Override
205     public boolean isPostingYearPrior() {
206         Integer currentFY = SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
207         return (getPostingYear().compareTo(currentFY) < 0);
208     }
209 
210     /**
211      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getPostingYearNextOrCurrent()
212      */
213     @Override
214     public Integer getPostingYearNextOrCurrent() {
215         if (isPostingYearNext()) {
216             //FY is set to next; use it
217             return getPostingYear();
218         }
219         //FY is NOT set to next; use CURRENT
220         return SpringContext.getBean(UniversityDateService.class).getCurrentFiscalYear();
221     }
222 
223     /**
224      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getItemClass()
225      */
226     @Override
227     @SuppressWarnings("rawtypes")
228     public abstract Class getItemClass();
229 
230     @SuppressWarnings("rawtypes")
231     public abstract Class getItemUseTaxClass();
232 
233     /**
234      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getPurApSourceDocumentIfPossible()
235      */
236     @Override
237     public abstract PurchasingAccountsPayableDocument getPurApSourceDocumentIfPossible();
238 
239     /**
240      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getPurApSourceDocumentLabelIfPossible()
241      */
242     @Override
243     public abstract String getPurApSourceDocumentLabelIfPossible();
244 
245     /**
246      * @see org.kuali.rice.krad.document.DocumentBase#prepareForSave()
247      */
248 
249     @Override
250     public void prepareForSave(KualiDocumentEvent event) {
251         customPrepareForSave(event);
252         super.prepareForSave(event);
253         fixItemReferences();
254     }
255 
256     /**
257      * PURAP documents are all overriding this method to return false because sufficient funds checking should not be performed on
258      * route of any PURAP documents. Only the Purchase Order performs a sufficient funds check and it is manually forced during
259      * routing.
260      *
261      * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#documentPerformsSufficientFundsCheck()
262      */
263     @Override
264     public boolean documentPerformsSufficientFundsCheck() {
265         return false;
266     }
267 
268     // for app doc status
269     @Override
270     public boolean isDocumentStoppedInRouteNode(String nodeName) {
271         WorkflowDocument workflowDocument = this.getFinancialSystemDocumentHeader().getWorkflowDocument();
272 
273         Set<String> names = workflowDocument.getCurrentNodeNames();
274         if (CollectionUtils.isNotEmpty(names)) {
275             List<String> currentRouteLevels = new ArrayList<String>(names);
276             if (currentRouteLevels.contains(nodeName) && workflowDocument.isApprovalRequested()) {
277                 return true;
278             }
279         }
280         return false;
281     }
282 
283     /**
284      * Records the specified error message into the Log file and throws a runtime exception.
285      *
286      * @param errorMessage the error message to be logged.
287      */
288     protected void logAndThrowRuntimeException(String errorMessage) {
289         this.logAndThrowRuntimeException(errorMessage, null);
290     }
291 
292     /**
293      * Records the specified error message into the Log file and throws the specified runtime exception.
294      *
295      * @param errorMessage the specified error message.
296      * @param e the specified runtime exception.
297      */
298     protected void logAndThrowRuntimeException(String errorMessage, Exception e) {
299         if (ObjectUtils.isNotNull(e)) {
300             LOG.error(errorMessage, e);
301             throw new RuntimeException(errorMessage, e);
302         }
303         else {
304             LOG.error(errorMessage);
305             throw new RuntimeException(errorMessage);
306         }
307     }
308 
309     /**
310      * Allows child PO classes to customize the prepareForSave method. Most of the subclasses need to call the super's method to get
311      * the GL entry creation, but they each need to do different things to prepare for those entries to be created. This is only for
312      * PO since it has children classes that need different prep work for GL creation.
313      *
314      * @param event the event involved in this action.
315      */
316     public void customPrepareForSave(KualiDocumentEvent event) {
317         // Need this here so that it happens before the GL work is done
318         SpringContext.getBean(PurapAccountingService.class).updateAccountAmounts(this);
319 
320         if (event instanceof RouteDocumentEvent || event instanceof ApproveDocumentEvent) {
321             if (this instanceof VendorCreditMemoDocument && ((VendorCreditMemoDocument)this).isSourceVendor()){
322                 return;
323             }
324             SpringContext.getBean(PurapServiceImpl.class).calculateTax(this);
325         }
326         // These next 5 lines are temporary changes so that we can use PurApOjbCollectionHelper for release 2.
327         // But these 5 lines will not be necessary anymore if the changes in PurApOjbCollectionHelper is
328         // merge into Rice.
329 //        this.allowDeleteAwareCollection = true;
330 //        DocumentDaoOjb docDao = SpringContext.getBean(DocumentDaoOjb.class);
331 //        PurchasingAccountsPayableDocumentBase retrievedDocument = (PurchasingAccountsPayableDocumentBase) docDao.findByDocumentHeaderId(this.getClass(), this.getDocumentNumber());
332 //        if (retrievedDocument != null) {
333 //            retrievedDocument.allowDeleteAwareCollection = true;
334 //        }
335 //
336 //        SpringContext.getBean(PurApOjbCollectionHelper.class).processCollections(docDao, this, retrievedDocument);
337 //        this.allowDeleteAwareCollection = false;
338 //        if (retrievedDocument != null) {
339 //            retrievedDocument.allowDeleteAwareCollection = false;
340 //        }
341     }
342 
343     /**
344      * @see org.kuali.kfs.sys.document.AccountingDocumentBase#buildListOfDeletionAwareLists()
345      */
346     @SuppressWarnings("rawtypes")
347     @Override
348     public List buildListOfDeletionAwareLists() {
349         List managedLists = new ArrayList<List>();
350         managedLists.add(getDeletionAwareAccountingLines());
351         if (allowDeleteAwareCollection) {
352             //From now on, the list of accounting lines would have been added when the
353             //super.buildListOfDeletionAwareLists() is executed when it calls getSourceAccountingLines().
354             //So we can remove the old codes that used to exist here to add the accounts to the
355             //managedLists and just use the one from the super.buildListOfDeletionAwareLists()
356             managedLists.add(this.getItems());
357             managedLists.add(getDeletionAwareUseTaxItems());
358         }
359         return managedLists;
360     }
361 
362     /**
363      * Build deletion list of accounting lines for PurAp generic use.
364      *
365      * @return
366      */
367     @SuppressWarnings("rawtypes")
368     protected List getDeletionAwareAccountingLines() {
369         List<PurApAccountingLine> deletionAwareAccountingLines = new ArrayList<PurApAccountingLine>();
370         for (Object itemAsObject : this.getItems()) {
371             final PurApItem item = (PurApItem)itemAsObject;
372             for (PurApAccountingLine accountingLine : item.getSourceAccountingLines()) {
373                 deletionAwareAccountingLines.add(accountingLine);
374             }
375         }
376         return deletionAwareAccountingLines;
377     }
378 
379     /**
380      * Build deletion list of use tax items for PurAp generic use.
381      *
382      * @return
383      */
384     @SuppressWarnings("rawtypes")
385     protected List getDeletionAwareUseTaxItems() {
386         List<PurApItemUseTax> deletionAwareUseTaxItems = new ArrayList<PurApItemUseTax>();
387 
388         List<PurApItemBase> subManageList = this.getItems();
389         for (PurApItemBase subManage : subManageList) {
390             deletionAwareUseTaxItems.addAll(subManage.getUseTaxItems());
391         }
392 
393         return deletionAwareUseTaxItems;
394     }
395 
396     /**
397      * @see org.kuali.kfs.sys.document.AccountingDocumentBase#buildListOfDeletionAwareLists()
398      *
399     @Override
400     public List buildListOfDeletionAwareLists() {
401         List managedLists = new ArrayList();
402         if (allowDeleteAwareCollection) {
403             List<PurApAccountingLine> purapAccountsList = new ArrayList<PurApAccountingLine>();
404             for (Object itemAsObject : this.getItems()) {
405                 final PurApItem item = (PurApItem)itemAsObject;
406                 purapAccountsList.addAll(item.getSourceAccountingLines());
407             }
408             managedLists.add(purapAccountsList);
409             managedLists.add(this.getItems());
410         }
411         return managedLists;
412     }
413     */
414 
415     @Override
416     public void processAfterRetrieve() {
417         super.processAfterRetrieve();
418 
419         refreshNonUpdateableReferences();
420     }
421 
422     /**
423      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#addItem(PurApItem item)
424      */
425     @Override
426     public void addItem(PurApItem item) {
427         int itemLinePosition = getItemLinePosition();
428         if (ObjectUtils.isNotNull(item.getItemLineNumber()) && (item.getItemLineNumber() > 0) && (item.getItemLineNumber() <= itemLinePosition)) {
429             itemLinePosition = item.getItemLineNumber().intValue() - 1;
430         }
431 
432         item.setPurapDocumentIdentifier(this.purapDocumentIdentifier);
433         item.setPurapDocument(this);
434 
435         items.add(itemLinePosition, item);
436         renumberItems(itemLinePosition);
437     }
438 
439     /**
440      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#deleteItem(int lineNum)
441      */
442     @Override
443     public void deleteItem(int lineNum) {
444         if (items.remove(lineNum) == null) {
445             // throw error here
446         }
447         renumberItems(lineNum);
448     }
449 
450     /**
451      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#renumberItems(int start)
452      */
453     @Override
454     public void renumberItems(int start) {
455         for (int i = start; i < items.size(); i++) {
456             PurApItem item = items.get(i);
457             item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
458 
459             // only set the item line number for above the line items
460             if (item.getItemType().isLineItemIndicator()) {
461                 item.setItemLineNumber(new Integer(i + 1));
462             }
463         }
464     }
465 
466     /**
467      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#itemSwap(int positionFrom, int positionTo)
468      */
469     @Override
470     public void itemSwap(int positionFrom, int positionTo) {
471         // if out of range do nothing
472         if ((positionTo < 0) || (positionTo >= getItemLinePosition())) {
473             return;
474         }
475         PurApItem item1 = this.getItem(positionFrom);
476         PurApItem item2 = this.getItem(positionTo);
477         Integer oldFirstPos = item1.getItemLineNumber();
478         // swap line numbers
479         item1.setItemLineNumber(item2.getItemLineNumber());
480         item2.setItemLineNumber(oldFirstPos);
481         // fix ordering in list
482         items.remove(positionFrom);
483         items.add(positionTo, item1);
484     }
485 
486     /**
487      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getItemLinePosition()
488      */
489     @Override
490     public int getItemLinePosition() {
491         int belowTheLineCount = 0;
492         for (PurApItem item : items) {
493             item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
494             if (item.getItemType().isAdditionalChargeIndicator()) {
495                 belowTheLineCount++;
496             }
497         }
498         return items.size() - belowTheLineCount;
499     }
500 
501     /**
502      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getItem(int pos)
503      */
504     @Override
505     public PurApItem getItem(int pos) {
506         return items.get(pos);
507     }
508 
509     /**
510      * Iterates through the items of the document and returns the item with the line number equal to the number given, or null if a
511      * match is not found.
512      *
513      * @param lineNumber line number to match on.
514      * @return the PurchasingAp Item if a match is found, else null.
515      */
516     @SuppressWarnings("rawtypes")
517     public PurApItem getItemByLineNumber(int lineNumber) {
518         for (Iterator iter = items.iterator(); iter.hasNext();) {
519             PurApItem item = (PurApItem) iter.next();
520             if (item.getItemLineNumber().intValue() == lineNumber) {
521                 return item;
522             }
523         }
524         return null;
525     }
526 
527     /**
528      * Find the item in the document via its string identifier.
529      * @param itemStrID the string identifier of the item being searched for
530      * @return the item being searched for
531      */
532     @SuppressWarnings("rawtypes")
533     public PurApItem getItemByStringIdentifier(String itemStrID) {
534         for (Iterator iter = items.iterator(); iter.hasNext();) {
535             PurApItem item = (PurApItem) iter.next();
536             if (StringUtils.equalsIgnoreCase(item.getItemIdentifierString(), itemStrID)) {
537                 return item;
538             }
539         }
540         return null;
541     }
542 
543     /**
544      * Find the item in the document via its identifier.
545      * @param itemID the string identifier of the item being searched for
546      * @return the item being searched for
547      */
548     @SuppressWarnings("rawtypes")
549     public PurApItem getItemByItemIdentifier(Integer itemID) {
550         for (Iterator iter = items.iterator(); iter.hasNext();) {
551             PurApItem item = (PurApItem) iter.next();
552             if (item.getItemIdentifier() == itemID) {
553                 return item;
554             }
555         }
556         return null;
557     }
558 
559     /**
560      * Overriding the parent method so that we can just set the posting year without the other stuff that the parent does to the
561      * accounting period. We only store the posting year on the doc and don't want the other stuff.
562      *
563      * @see org.kuali.kfs.sys.document.LedgerPostingDocumentBase#setPostingYear(java.lang.Integer)
564      */
565     @Override
566     public void setPostingYear(Integer postingYear) {
567         this.postingYear = postingYear;
568     }
569 
570     /**
571      * @see org.kuali.kfs.sys.document.AccountingDocumentBase#getTotalDollarAmount()
572      */
573     @Override
574     public KualiDecimal getTotalDollarAmount() {
575         return getTotalDollarAmountAllItems(null);
576     }
577 
578     /**
579      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#setTotalDollarAmount(KualiDecimal amount)
580      */
581     @Override
582     public void setTotalDollarAmount(KualiDecimal amount) {
583         // do nothing, this is so that the jsp won't complain about totalDollarAmount have no setter method.
584     }
585 
586     /**
587      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getTotalDollarAmountAllItems(String[] excludedTypes)
588      */
589     @Override
590     public KualiDecimal getTotalDollarAmountAllItems(String[] excludedTypes) {
591         return getTotalDollarAmountWithExclusions(excludedTypes, true);
592     }
593 
594     /**
595      * Computes the total dollar amount of all above the line items.
596      *
597      * @return the total dollar amount of all above the line items.
598      */
599     @Override
600     public KualiDecimal getTotalDollarAmountAboveLineItems() {
601         return getTotalDollarAmountAboveLineItems(null);
602     }
603 
604     /**
605      * Computes the total dollar amount of all above the line items with the specified item types excluded.
606      *
607      * @param excludedTypes the types of items to be excluded.
608      * @return the total dollar amount of all above the line items with the specified item types excluded..
609      */
610     public KualiDecimal getTotalDollarAmountAboveLineItems(String[] excludedTypes) {
611         return getTotalDollarAmountWithExclusions(excludedTypes, false);
612     }
613 
614     /**
615      * Computes the total dollar amount with the specified item types and possibly below the line items excluded.
616      *
617      * @param excludedTypes the types of items to be excluded.
618      * @param includeBelowTheLine indicates whether below the line items shall be included.
619      * @return the total dollar amount with the specified item types excluded.
620      */
621     public KualiDecimal getTotalDollarAmountWithExclusions(String[] excludedTypes, boolean includeBelowTheLine) {
622         List<PurApItem> itemsForTotal = getItems();
623 
624         return getTotalDollarAmountWithExclusionsSubsetItems(excludedTypes, includeBelowTheLine, itemsForTotal);
625     }
626 
627     /**
628      * This method...
629      * @param excludedTypes
630      * @param includeBelowTheLine
631      * @param itemsForTotal
632      * @return
633      */
634     protected KualiDecimal getTotalDollarAmountWithExclusionsSubsetItems(String[] excludedTypes, boolean includeBelowTheLine, List<PurApItem> itemsForTotal) {
635         if (excludedTypes == null) {
636             excludedTypes = new String[] {};
637         }
638 
639         KualiDecimal total = new KualiDecimal(BigDecimal.ZERO);
640         for (PurApItem item : itemsForTotal) {
641             item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
642             ItemType it = item.getItemType();
643             if ((includeBelowTheLine || it.isLineItemIndicator()) && !ArrayUtils.contains(excludedTypes, it.getItemTypeCode())) {
644                 KualiDecimal totalAmount = item.getTotalAmount();
645                 KualiDecimal itemTotal = (totalAmount != null) ? totalAmount : KualiDecimal.ZERO;
646                 total = total.add(itemTotal);
647             }
648         }
649         return total;
650     }
651 
652     @Override
653     public KualiDecimal getTotalDollarAmountForTradeIn() {
654         List<PurApItem> tradeInItems = getTradeInItems();
655         return getTotalDollarAmountWithExclusionsSubsetItems(null,false,tradeInItems);
656     }
657 
658     /**
659      * This method...
660      * @param tradeInItems
661      */
662     @Override
663     public List<PurApItem> getTradeInItems() {
664         List<PurApItem> tradeInItems = new ArrayList<PurApItem>();
665         for (PurApItem purApItem : (List<PurApItem>)getItems()) {
666             if(purApItem.getItemAssignedToTradeInIndicator()) {
667                 tradeInItems.add(purApItem);
668             }
669         }
670         return tradeInItems;
671     }
672 
673     /**
674      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getTotalPreTaxDollarAmount()
675      */
676     @Override
677     public KualiDecimal getTotalPreTaxDollarAmount() {
678         return getTotalPreTaxDollarAmountAllItems(null);
679     }
680 
681     /**
682      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#setTotalPreTaxDollarAmount(org.kuali.rice.core.api.util.type.KualiDecimal)
683      */
684     @Override
685     public void setTotalPreTaxDollarAmount(KualiDecimal amount) {
686         // do nothing, this is so that the jsp won't complain about totalDollarAmount have no setter method.
687     }
688 
689     /**
690      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getTotalPreTaxDollarAmountAllItems(java.lang.String[])
691      */
692     @Override
693     public KualiDecimal getTotalPreTaxDollarAmountAllItems(String[] excludedTypes) {
694         return getTotalPreTaxDollarAmountWithExclusions(excludedTypes, true);
695     }
696 
697     /**
698      * Computes the total dollar amount of all above the line items.
699      *
700      * @return the total dollar amount of all above the line items.
701      */
702     public KualiDecimal getTotalPreTaxDollarAmountAboveLineItems() {
703         return getTotalPreTaxDollarAmountAboveLineItems(null);
704     }
705 
706     /**
707      * Computes the total dollar amount of all above the line items with the specified item types excluded.
708      *
709      * @param excludedTypes the types of items to be excluded.
710      * @return the total dollar amount of all above the line items with the specified item types excluded..
711      */
712     public KualiDecimal getTotalPreTaxDollarAmountAboveLineItems(String[] excludedTypes) {
713         return getTotalPreTaxDollarAmountWithExclusions(excludedTypes, false);
714     }
715 
716     /**
717      * Computes the total dollar amount with the specified item types and possibly below the line items excluded.
718      *
719      * @param excludedTypes the types of items to be excluded.
720      * @param includeBelowTheLine indicates whether below the line items shall be included.
721      * @return the total dollar amount with the specified item types excluded.
722      */
723     public KualiDecimal getTotalPreTaxDollarAmountWithExclusions(String[] excludedTypes, boolean includeBelowTheLine) {
724         if (excludedTypes == null) {
725             excludedTypes = new String[] {};
726         }
727 
728         KualiDecimal total = new KualiDecimal(BigDecimal.ZERO);
729         for (PurApItem item : (List<PurApItem>) getItems()) {
730             item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
731             ItemType it = item.getItemType();
732             if ((includeBelowTheLine || it.isLineItemIndicator()) && !ArrayUtils.contains(excludedTypes, it.getItemTypeCode())) {
733                 KualiDecimal extendedPrice = item.getExtendedPrice();
734                 KualiDecimal itemTotal = (extendedPrice != null) ? extendedPrice : KualiDecimal.ZERO;
735                 total = total.add(itemTotal);
736             }
737         }
738         return total;
739     }
740 
741     @Override
742     public KualiDecimal getTotalTaxAmount() {
743         return getTotalTaxAmountAllItems(null);
744     }
745 
746     @Override
747     public void setTotalTaxAmount(KualiDecimal amount) {
748         // do nothing, this is so that the jsp won't complain about totalTaxAmount have no setter method.
749     }
750 
751     @Override
752     public KualiDecimal getTotalTaxAmountAllItems(String[] excludedTypes) {
753         return getTotalTaxAmountWithExclusions(excludedTypes, true);
754     }
755 
756     @Override
757     public KualiDecimal getTotalTaxAmountAboveLineItems() {
758         return getTotalTaxAmountAboveLineItems(null);
759     }
760 
761     @Override
762     public KualiDecimal getTotalTaxAmountAboveLineItems(String[] excludedTypes) {
763         return getTotalTaxAmountWithExclusions(excludedTypes, false);
764     }
765 
766     @Override
767     public KualiDecimal getTotalTaxAmountWithExclusions(String[] excludedTypes, boolean includeBelowTheLine) {
768         if (excludedTypes == null) {
769             excludedTypes = new String[] {};
770         }
771 
772         KualiDecimal total = new KualiDecimal(BigDecimal.ZERO);
773         for (PurApItem item : (List<PurApItem>) getItems()) {
774             item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
775             ItemType it = item.getItemType();
776             if ((includeBelowTheLine || it.isLineItemIndicator()) && !ArrayUtils.contains(excludedTypes, it.getItemTypeCode())) {
777                 KualiDecimal taxAmount = item.getItemTaxAmount();
778                 KualiDecimal itemTotal = (taxAmount != null) ? taxAmount : KualiDecimal.ZERO;
779                 total = total.add(itemTotal);
780             }
781         }
782         return total;
783     }
784 
785     @Override
786     public boolean isUseTaxIndicator() {
787         return useTaxIndicator;
788     }
789 
790     @Override
791     public void setUseTaxIndicator(boolean useTaxIndicator) {
792         this.useTaxIndicator = useTaxIndicator;
793     }
794 
795     /**
796      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#templateVendorAddress(VendorAddress)
797      */
798     @Override
799     public void templateVendorAddress(VendorAddress vendorAddress) {
800         if (vendorAddress == null) {
801             return;
802         }
803         this.setVendorLine1Address(vendorAddress.getVendorLine1Address());
804         this.setVendorLine2Address(vendorAddress.getVendorLine2Address());
805         this.setVendorCityName(vendorAddress.getVendorCityName());
806         this.setVendorStateCode(vendorAddress.getVendorStateCode());
807         this.setVendorPostalCode(vendorAddress.getVendorZipCode());
808         this.setVendorCountryCode(vendorAddress.getVendorCountryCode());
809     }
810 
811     /**
812      * Returns the vendor number for this document.
813      *
814      * @return the vendor number for this document.
815      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument#getVendorNumber()
816      */
817     @Override
818     public String getVendorNumber() {
819         if (StringUtils.isNotEmpty(vendorNumber)) {
820             return vendorNumber;
821         }
822         else if (ObjectUtils.isNotNull(vendorDetail)) {
823             return vendorDetail.getVendorNumber();
824         }
825         else {
826             return "";
827         }
828     }
829 
830     @Override
831     public void setVendorNumber(String vendorNumber) {
832         this.vendorNumber = vendorNumber;
833     }
834 
835     public Boolean getOverrideWorkflowButtons() {
836         return overrideWorkflowButtons;
837     }
838 
839     public void setOverrideWorkflowButtons(Boolean overrideWorkflowButtons) {
840         this.overrideWorkflowButtons = overrideWorkflowButtons;
841     }
842 
843     @Override
844     public Integer getVendorHeaderGeneratedIdentifier() {
845         return vendorHeaderGeneratedIdentifier;
846     }
847 
848     @Override
849     public void setVendorHeaderGeneratedIdentifier(Integer vendorHeaderGeneratedIdentifier) {
850         this.vendorHeaderGeneratedIdentifier = vendorHeaderGeneratedIdentifier;
851     }
852 
853     @Override
854     public Integer getVendorDetailAssignedIdentifier() {
855         return vendorDetailAssignedIdentifier;
856     }
857 
858     @Override
859     public void setVendorDetailAssignedIdentifier(Integer vendorDetailAssignedIdentifier) {
860         this.vendorDetailAssignedIdentifier = vendorDetailAssignedIdentifier;
861     }
862 
863     @Override
864     public String getVendorCustomerNumber() {
865         return vendorCustomerNumber;
866     }
867 
868     @Override
869     public void setVendorCustomerNumber(String vendorCustomerNumber) {
870         this.vendorCustomerNumber = vendorCustomerNumber;
871     }
872 
873     @Override
874     public Integer getPurapDocumentIdentifier() {
875         return purapDocumentIdentifier;
876     }
877 
878     @Override
879     public void setPurapDocumentIdentifier(Integer identifier) {
880         this.purapDocumentIdentifier = identifier;
881     }
882 
883     @Override
884     public VendorDetail getVendorDetail() {
885         return vendorDetail;
886     }
887 
888     public void setVendorDetail(VendorDetail vendorDetail) {
889         this.vendorDetail = vendorDetail;
890     }
891 
892     @Override
893     @SuppressWarnings("rawtypes")
894     public List getItems() {
895         return items;
896     }
897 
898     @Override
899     @SuppressWarnings("rawtypes")
900     public void setItems(List items) {
901         this.items = items;
902     }
903 
904     @Override
905     public String getVendorCityName() {
906         return vendorCityName;
907     }
908 
909     @Override
910     public void setVendorCityName(String vendorCityName) {
911         this.vendorCityName = vendorCityName;
912     }
913 
914     @Override
915     public String getVendorCountryCode() {
916         return vendorCountryCode;
917     }
918 
919     @Override
920     public void setVendorCountryCode(String vendorCountryCode) {
921         this.vendorCountryCode = vendorCountryCode;
922     }
923 
924     @Override
925     public String getVendorLine1Address() {
926         return vendorLine1Address;
927     }
928 
929     @Override
930     public void setVendorLine1Address(String vendorLine1Address) {
931         this.vendorLine1Address = vendorLine1Address;
932     }
933 
934     @Override
935     public String getVendorLine2Address() {
936         return vendorLine2Address;
937     }
938 
939     @Override
940     public void setVendorLine2Address(String vendorLine2Address) {
941         this.vendorLine2Address = vendorLine2Address;
942     }
943 
944     @Override
945     public String getVendorName() {
946         return vendorName;
947     }
948 
949     @Override
950     public void setVendorName(String vendorName) {
951         this.vendorName = vendorName;
952     }
953 
954     @Override
955     public String getVendorPostalCode() {
956         return vendorPostalCode;
957     }
958 
959     @Override
960     public void setVendorPostalCode(String vendorPostalCode) {
961         this.vendorPostalCode = vendorPostalCode;
962     }
963 
964     @Override
965     public String getVendorStateCode() {
966         return vendorStateCode;
967     }
968 
969     @Override
970     public void setVendorStateCode(String vendorStateCode) {
971         this.vendorStateCode = vendorStateCode;
972     }
973 
974     @Override
975     public String getVendorAddressInternationalProvinceName() {
976         return vendorAddressInternationalProvinceName;
977     }
978 
979     @Override
980     public void setVendorAddressInternationalProvinceName(String vendorAddressInternationalProvinceName) {
981         this.vendorAddressInternationalProvinceName = vendorAddressInternationalProvinceName;
982     }
983 
984     @Override
985     public Integer getVendorAddressGeneratedIdentifier() {
986         return vendorAddressGeneratedIdentifier;
987     }
988 
989     @Override
990     public void setVendorAddressGeneratedIdentifier(Integer vendorAddressGeneratedIdentifier) {
991         this.vendorAddressGeneratedIdentifier = vendorAddressGeneratedIdentifier;
992     }
993 
994     @Override
995     public Integer getAccountsPayablePurchasingDocumentLinkIdentifier() {
996         return accountsPayablePurchasingDocumentLinkIdentifier;
997     }
998 
999     @Override
1000     public void setAccountsPayablePurchasingDocumentLinkIdentifier(Integer accountsPayablePurchasingDocumentLinkIdentifier) {
1001         this.accountsPayablePurchasingDocumentLinkIdentifier = accountsPayablePurchasingDocumentLinkIdentifier;
1002     }
1003 
1004     @Override
1005     public String[] getBelowTheLineTypes() {
1006         if (this.belowTheLineTypes == null) {
1007             this.belowTheLineTypes = SpringContext.getBean(PurapService.class).getBelowTheLineForDocument(this);
1008         }
1009         return belowTheLineTypes;
1010     }
1011 
1012     @Override
1013     public CountryEbo getVendorCountry() {
1014         if ( StringUtils.isBlank(vendorCountryCode) ) {
1015             vendorCountry = null;
1016         } else {
1017             if ( vendorCountry == null || !StringUtils.equals( vendorCountry.getCode(),vendorCountryCode) ) {
1018                 ModuleService moduleService = SpringContext.getBean(KualiModuleService.class).getResponsibleModuleService(CountryEbo.class);
1019                 if ( moduleService != null ) {
1020                     Map<String,Object> keys = new HashMap<String, Object>(1);
1021                     keys.put(LocationConstants.PrimaryKeyConstants.CODE, vendorCountryCode);
1022                     vendorCountry = moduleService.getExternalizableBusinessObject(CountryEbo.class, keys);
1023                 } else {
1024                     throw new RuntimeException( "CONFIGURATION ERROR: No responsible module found for EBO class.  Unable to proceed." );
1025                 }
1026             }
1027         }
1028         return vendorCountry;
1029     }
1030 
1031     /**
1032      * Added only to allow for {@link org.kuali.kfs.module.purap.util.PurApObjectUtils} class to work correctly.
1033      *
1034      * @deprecated
1035      */
1036     @Deprecated
1037     public void setVendorCountry(CountryEbo vendorCountry) {
1038         this.vendorCountry = vendorCountry;
1039     }
1040 
1041     public String getVendorAttentionName() {
1042         return vendorAttentionName;
1043     }
1044 
1045     public void setVendorAttentionName(String vendorAttentionName) {
1046         this.vendorAttentionName = vendorAttentionName;
1047     }
1048 
1049     /**
1050      * Gets the accountDistributionMethod attribute.
1051      *
1052      * @return Returns the accountDistributionMethod
1053      */
1054 
1055     public String getAccountDistributionMethod() {
1056         return accountDistributionMethod;
1057     }
1058 
1059     /**
1060      * Sets the accountDistributionMethod attribute.
1061      *
1062      * @param accountDistributionMethod The accountDistributionMethod to set.
1063      */
1064     public void setAccountDistributionMethod(String accountDistributionMethod) {
1065         this.accountDistributionMethod = accountDistributionMethod;
1066     }
1067 
1068     /**
1069      * Determines whether the account is debit. It always returns false.
1070      *
1071      * @param financialDocument The document containing the account to be validated.
1072      * @param accountingLine The account to be validated.
1073      * @return boolean false.
1074      * @see org.kuali.kfs.sys.document.validation.AccountingLineRule#isDebit(org.kuali.kfs.sys.document.AccountingDocument,
1075      *      org.kuali.kfs.sys.businessobject.AccountingLine)
1076      */
1077     @Override
1078     public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) {
1079         return false;
1080     }
1081 
1082     public PurApRelatedViews getRelatedViews() {
1083         if (relatedViews == null) {
1084             relatedViews = new PurApRelatedViews(this.documentNumber, this.accountsPayablePurchasingDocumentLinkIdentifier);
1085         }
1086         return relatedViews;
1087     }
1088 
1089     public void setRelatedViews(PurApRelatedViews relatedViews) {
1090         this.relatedViews = relatedViews;
1091     }
1092 
1093     @Override
1094     public void refreshNonUpdateableReferences() {
1095         super.refreshNonUpdateableReferences();
1096 
1097         for (PurApItem item : (List<PurApItem>)this.getItems()) {
1098             //refresh the accounts if they do exist...
1099             for (PurApAccountingLine account : item.getSourceAccountingLines()) {
1100                 account.refreshNonUpdateableReferences();
1101             }
1102         }
1103 
1104         fixItemReferences();
1105     }
1106 
1107     /**
1108      * This method fixes the item references in this document if it's new
1109      */
1110     @Override
1111     public void fixItemReferences() {
1112         //fix item and account references in case this is a new doc (since they will be lost)
1113         if(ObjectUtils.isNull(this.purapDocumentIdentifier)) {
1114             for (PurApItem item : (List<PurApItem>)this.getItems()) {
1115                 item.setPurapDocument(this);
1116                 item.fixAccountReferences();
1117             }
1118         }
1119     }
1120 
1121     /**
1122      * Returns the trade in item of the document.
1123      *
1124      * @return
1125      */
1126     @Override
1127     public PurApItem getTradeInItem() {
1128         for (PurApItem item : (List<PurApItem>)getItems()) {
1129             if (item.getItemTypeCode().equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_TRADE_IN_CODE)) {
1130                 return item;
1131             }
1132         }
1133         return null;
1134     }
1135 
1136     /**
1137      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument.getIsATypeOfPurAPRecDoc().
1138      */
1139     @Override
1140     public boolean getIsATypeOfPurAPRecDoc() {
1141         return true;
1142     }
1143 
1144     /**
1145      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument.getIsATypeOfPurDoc().
1146      */
1147     @Override
1148     public boolean getIsATypeOfPurDoc() {
1149         if (this instanceof PurchasingDocumentBase) {
1150             return true;
1151         }
1152         else {
1153             return false;
1154         }
1155     }
1156 
1157     /**
1158      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument.getIsATypeOfPODoc().
1159      */
1160     @Override
1161     public boolean getIsATypeOfPODoc() {
1162         if (this instanceof PurchaseOrderDocument) {
1163             return true;
1164         }
1165         else {
1166             return false;
1167         }
1168     }
1169 
1170     /**
1171      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument.getIsPODoc().
1172      */
1173     @Override
1174     public boolean getIsPODoc() {
1175         if ( (this instanceof PurchaseOrderDocument) &&
1176             !(this instanceof PurchaseOrderAmendmentDocument) &&
1177             !(this instanceof PurchaseOrderCloseDocument) &&
1178             !(this instanceof PurchaseOrderPaymentHoldDocument) &&
1179             !(this instanceof PurchaseOrderRemoveHoldDocument) &&
1180             !(this instanceof PurchaseOrderReopenDocument) &&
1181             !(this instanceof PurchaseOrderRetransmitDocument) &&
1182             !(this instanceof PurchaseOrderSplitDocument) &&
1183             !(this instanceof PurchaseOrderVoidDocument)) {
1184             return true;
1185         }
1186         else {
1187             return false;
1188         }
1189     }
1190 
1191     /**
1192      * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument.getIsATypeOfREQSDoc().
1193      */
1194     @Override
1195     public boolean getIsReqsDoc() {
1196         if (this instanceof RequisitionDocument) {
1197             return true;
1198         }
1199         else {
1200             return false;
1201         }
1202     }
1203 
1204     /**
1205      * build document title based on the properties of current document
1206      *
1207      * @param the default document title
1208      * @return the combine information of the given title and additional payment indicators
1209      */
1210     protected String buildDocumentTitle(String title) {
1211         if(this.getVendorDetail() == null) {
1212            return title;
1213         }
1214 
1215         Integer vendorHeaderGeneratedIdentifier = this.getVendorDetail().getVendorHeaderGeneratedIdentifier();
1216         VendorService vendorService = SpringContext.getBean(VendorService.class);
1217 
1218         Object[] indicators = new String[2];
1219 
1220         boolean isEmployeeVendor = vendorService.isVendorInstitutionEmployee(vendorHeaderGeneratedIdentifier);
1221         indicators[0] = isEmployeeVendor ? AdHocPaymentIndicator.EMPLOYEE_VENDOR : AdHocPaymentIndicator.OTHER;
1222 
1223         boolean isVendorForeign = vendorService.isVendorForeign(vendorHeaderGeneratedIdentifier);
1224         indicators[1] = isVendorForeign ? AdHocPaymentIndicator.ALIEN_VENDOR : AdHocPaymentIndicator.OTHER;
1225 
1226         for(Object indicator : indicators) {
1227             if(!AdHocPaymentIndicator.OTHER.equals(indicator)) {
1228                 String titlePattern = title + " [{0}:{1}]";
1229                 return MessageFormat.format(titlePattern, indicators);
1230             }
1231         }
1232 
1233         return title;
1234     }
1235 
1236     /**
1237      * Overridden to return the source lines of all of the items
1238      * @see org.kuali.kfs.sys.document.AccountingDocumentBase#getSourceAccountingLines()
1239      */
1240     @SuppressWarnings("rawtypes")
1241     @Override
1242     public List getSourceAccountingLines() {
1243         if (ObjectUtils.isNotNull(sourceAccountingLines) && !sourceAccountingLines.isEmpty()) {
1244             // do nothing because acct lines have already been set
1245             return sourceAccountingLines;
1246         }
1247         else {
1248             /*
1249             SpringContext.getBean(PurapAccountingService.class).updateAccountAmounts(this);
1250             return SpringContext.getBean(PurapAccountingService.class).generateSummary(getItems());
1251             */
1252             List<AccountingLine> sourceAccountingLines = new ArrayList<AccountingLine>();
1253             for (Object itemAsObject : this.getItems()) {
1254                 final PurApItem item = (PurApItem)itemAsObject;
1255                 for (PurApAccountingLine accountingLine : item.getSourceAccountingLines()) {
1256                     //KFSMI-9053: check if the accounting line does not already exist in the list
1257                     //and if so then add to the list.  Preventing duplicates
1258                     if (!isDuplicateAccountingLine(sourceAccountingLines, accountingLine)) {
1259                         sourceAccountingLines.add(accountingLine);
1260                     }
1261                 }
1262             }
1263             return sourceAccountingLines;
1264         }
1265     }
1266 
1267     /**
1268      * Helper method to check if the source accounting line is already in the list and if so return true
1269      *
1270      * @param sourceAccountingLines
1271      * @param accountingLine
1272      * @return true if it is a duplicate else return false.
1273      */
1274     protected boolean isDuplicateAccountingLine(List<AccountingLine> sourceAccountingLines, PurApAccountingLine accountingLine) {
1275         for (AccountingLine sourceLine : sourceAccountingLines) {
1276             PurApAccountingLine purapAccountLine = (PurApAccountingLine) sourceLine;
1277 
1278             if (purapAccountLine.accountStringsAreEqual(accountingLine)) {
1279                 return true;
1280             }
1281         }
1282         return false;
1283     }
1284 
1285     /**
1286      * Helper method to find the matching accountingLines in the list of sourceAccountingLines and sum up the
1287      * lines amounts.
1288      *
1289      * @param accountingLine
1290      * @return accountTotalGLEntryAmount
1291      */
1292     protected KualiDecimal getAccountTotalGLEntryAmount(AccountingLine matchingAccountingLine) {
1293         KualiDecimal accountTotalGLEntryAmount = KualiDecimal.ZERO;
1294 
1295         for (Object itemAsObject : this.getItems()) {
1296             final PurApItem item = (PurApItem)itemAsObject;
1297             for (PurApAccountingLine accountingLine : item.getSourceAccountingLines()) {
1298                 //KFSMI-9053: check if the accounting line is a duplicate then add the total
1299                 if (accountingLine.accountStringsAreEqual((SourceAccountingLine)matchingAccountingLine)) {
1300                     accountTotalGLEntryAmount = accountTotalGLEntryAmount.add(accountingLine.getAmount());
1301                 }
1302             }
1303         }
1304 
1305         return accountTotalGLEntryAmount;
1306     }
1307     /**
1308      * Checks whether the related purchase order views need a warning to be displayed,
1309      * i.e. if at least one of the purchase orders has never been opened.
1310      * @return true if at least one related purchase order needs a warning; false otherwise
1311      */
1312     public boolean getNeedWarningRelatedPOs() {
1313         List<PurchaseOrderView> poViews = getRelatedViews().getRelatedPurchaseOrderViews();
1314         for (PurchaseOrderView poView : poViews) {
1315             if (poView.getNeedWarning()) {
1316                 return true;
1317             }
1318         }
1319         return false;
1320     }
1321 
1322     /**
1323      * Accounting lines that are read-only should skip validation
1324      * @see org.kuali.kfs.sys.document.AccountingDocumentBase#getPersistedSourceAccountingLinesForComparison()
1325      */
1326     @SuppressWarnings("rawtypes")
1327     @Override
1328     protected List getPersistedSourceAccountingLinesForComparison() {
1329         LOG.info("Checking persisted source accounting lines for read-only fields");
1330         List<String> restrictedItemTypesList = new ArrayList<String>();
1331         try {
1332             restrictedItemTypesList = new ArrayList<String>( SpringContext.getBean(ParameterService.class).getParameterValuesAsString(this.getClass(), PurapParameterConstants.PURAP_ITEM_TYPES_RESTRICTING_ACCOUNT_EDIT) );
1333         } catch (IllegalArgumentException iae) {
1334             // do nothing, not a problem if no restricted types are defined
1335         }
1336 
1337         PurapAccountingService purApAccountingService = SpringContext.getBean(PurapAccountingService.class);
1338         List persistedSourceLines = new ArrayList();
1339 
1340         for (PurApItem item : (List<PurApItem>) this.getItems()) {
1341             // only check items that already have been persisted since last save
1342             if (ObjectUtils.isNotNull(item.getItemIdentifier())) {
1343                 // Disable validation if the item is read-only
1344                 final boolean isNotReadOnly = !((restrictedItemTypesList != null) && restrictedItemTypesList.contains(item.getItemTypeCode()));
1345                 if (isNotReadOnly) {
1346                     persistedSourceLines.addAll(purApAccountingService.getAccountsFromItem(item));
1347                 }
1348             }
1349         }
1350         return persistedSourceLines;
1351     }
1352 
1353     /**
1354      * Accounting lines that are read-only should skip validation
1355      * @see org.kuali.kfs.sys.document.AccountingDocumentBase#getSourceAccountingLinesForComparison()
1356      */
1357     @SuppressWarnings("rawtypes")
1358     @Override
1359     protected List getSourceAccountingLinesForComparison() {
1360         LOG.info("Checking source accounting lines for read-only fields");
1361         List<String> restrictedItemTypesList = new ArrayList<String>();
1362         try {
1363             restrictedItemTypesList = new ArrayList<String>( SpringContext.getBean(ParameterService.class).getParameterValuesAsString(this.getClass(), PurapParameterConstants.PURAP_ITEM_TYPES_RESTRICTING_ACCOUNT_EDIT) );
1364         } catch (IllegalArgumentException iae) {
1365             // do nothing, not a problem if no restricted types are defined
1366         }
1367         PurapAccountingService purApAccountingService = SpringContext.getBean(PurapAccountingService.class);
1368         List currentSourceLines = new ArrayList();
1369         for (PurApItem item : (List<PurApItem>) this.getItems()) {
1370             // Disable validation if the item is read-only
1371             final boolean isNotReadOnly = !((restrictedItemTypesList != null) && restrictedItemTypesList.contains(item.getItemTypeCode()));
1372             if (isNotReadOnly) {
1373                 currentSourceLines.addAll(item.getSourceAccountingLines());
1374             }
1375         }
1376         return currentSourceLines;
1377     }
1378 
1379     /**
1380      * Gets the calculated attribute.
1381      *
1382      * @return Returns the calculated
1383      */
1384 
1385     @Override
1386     public boolean isCalculated() {
1387         return calculated;
1388     }
1389 
1390     /**
1391      * Sets the calculated attribute.
1392      *
1393      * @param calculated The calculated to set.
1394      */
1395     @Override
1396     public void setCalculated(boolean calculated) {
1397         this.calculated = calculated;
1398     }
1399 
1400     @Override
1401     public NoteType getNoteType() {
1402         return NoteType.BUSINESS_OBJECT;
1403     }
1404 
1405 }