View Javadoc
1   /*
2    * Copyright 2006 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.module.purap.document;
17  
18  import org.kuali.ole.module.purap.PurapPropertyConstants;
19  import org.kuali.ole.module.purap.businessobject.AccountsPayableItem;
20  import org.kuali.ole.module.purap.businessobject.PurApItemUseTax;
21  import org.kuali.ole.module.purap.businessobject.PurchaseOrderItem;
22  import org.kuali.ole.module.purap.document.service.AccountsPayableDocumentSpecificService;
23  import org.kuali.ole.module.purap.document.service.PurapService;
24  import org.kuali.ole.module.purap.document.service.PurchaseOrderService;
25  import org.kuali.ole.sys.OLEPropertyConstants;
26  import org.kuali.ole.sys.businessobject.Bank;
27  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntry;
28  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
29  import org.kuali.ole.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
30  import org.kuali.ole.sys.context.SpringContext;
31  import org.kuali.ole.vnd.businessobject.CampusParameter;
32  import org.kuali.rice.core.api.util.type.KualiDecimal;
33  import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange;
34  import org.kuali.rice.kim.api.identity.Person;
35  import org.kuali.rice.kns.service.DataDictionaryService;
36  import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
37  import org.kuali.rice.krad.util.ObjectUtils;
38  
39  import java.sql.Timestamp;
40  import java.util.List;
41  
42  /**
43   * Accounts Payable Document Base
44   */
45  public abstract class AccountsPayableDocumentBase extends PurchasingAccountsPayableDocumentBase implements AccountsPayableDocument {
46      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountsPayableDocumentBase.class);
47  
48      // SHARED FIELDS BETWEEN PAYMENT REQUEST AND CREDIT MEMO
49      protected Timestamp accountsPayableApprovalTimestamp;
50      protected String lastActionPerformedByPersonId;
51      protected String accountsPayableProcessorIdentifier;
52      protected boolean holdIndicator;
53      protected Timestamp extractedTimestamp;
54      protected Integer purchaseOrderIdentifier;
55      protected String processingCampusCode;
56      protected String noteLine1Text;
57      protected String noteLine2Text;
58      protected String noteLine3Text;
59      protected boolean continuationAccountIndicator;
60      protected boolean closePurchaseOrderIndicator;
61      protected boolean reopenPurchaseOrderIndicator;
62      protected String bankCode;
63  
64      protected boolean unmatchedOverride; // not persisted
65  
66      // NOT PERSISTED IN DB
67      // BELOW USED BY ROUTING
68      protected String chartOfAccountsCode;
69      protected String organizationCode;
70  
71      // NOT PERSISTED IN DB
72      // BELOW USED BY GL ENTRY CREATION
73      protected boolean generateEncumbranceEntries;
74      protected String debitCreditCodeForGLEntries;
75      protected PurApItemUseTax offsetUseTax;
76  
77      // REFERENCE OBJECTS
78      protected CampusParameter processingCampus;
79      protected transient PurchaseOrderDocument purchaseOrderDocument;
80      protected Bank bank;
81  
82      /**
83       * Constructs a AccountsPayableDocumentBase
84       */
85      public AccountsPayableDocumentBase() {
86          super();
87          setUnmatchedOverride(false);
88      }
89  
90      public void setLineItemTotal(KualiDecimal total) {
91          // do nothing, this is so that the jsp won't complain about lineItemTotal have no setter method.
92      }
93  
94      public void setGrandTotal(KualiDecimal total) {
95          // do nothing, this is so that the jsp won't complain about grandTotal have no setter method.
96      }
97  
98      /**
99       * Overriding to stop the deleting of general ledger entries.
100      *
101      * @see org.kuali.ole.sys.document.GeneralLedgerPostingDocumentBase#removeGeneralLedgerPendingEntries()
102      */
103     @Override
104     protected void removeGeneralLedgerPendingEntries() {
105         // do not delete entries for PREQ or CM (hjs)
106     }
107 
108     /**
109      * @see org.kuali.ole.module.purap.document.AccountsPayableDocument#requiresAccountsPayableReviewRouting()
110      */
111     @Override
112     public boolean requiresAccountsPayableReviewRouting() {
113         return !approvalAtAccountsPayableReviewAllowed();
114     }
115 
116     /**
117      * @see org.kuali.ole.module.purap.document.AccountsPayableDocument#approvalAtAccountsPayableReviewAllowed()
118      */
119     @Override
120     public boolean approvalAtAccountsPayableReviewAllowed() {
121         return !(isAttachmentRequired() && documentHasNoImagesAttached());
122     }
123 
124     /**
125      * Checks whether an attachment is required
126      *
127      * @return - true if attachment is required, otherwise false
128      */
129     protected abstract boolean isAttachmentRequired();
130 
131     /**
132      * Checks all documents notes for attachments and to be overriden by sub class
133      *
134      * @return - true if document does not have an image attached, false otherwise
135      */
136     public abstract boolean documentHasNoImagesAttached();
137 
138     /**
139      * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocumentBase#populateDocumentForRouting()
140      */
141     @Override
142     public void populateDocumentForRouting() {
143         if (ObjectUtils.isNotNull(getPurchaseOrderDocument())) {
144             this.setChartOfAccountsCode(getPurchaseOrderDocument().getChartOfAccountsCode());
145             this.setOrganizationCode(getPurchaseOrderDocument().getOrganizationCode());
146             if (ObjectUtils.isNull(this.getPurchaseOrderDocument().getDocumentHeader().getDocumentNumber())) {
147                 this.getPurchaseOrderDocument().refreshReferenceObject(OLEPropertyConstants.DOCUMENT_HEADER);
148             }
149         }
150         super.populateDocumentForRouting();
151     }
152 
153     /**
154      * Calls a custom prepare for save method, as the super class does GL entry creation that causes problems with AP documents.
155      *
156      * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocumentBase#prepareForSave(org.kuali.rice.krad.rule.event.KualiDocumentEvent)
157      */
158     @Override
159     public void prepareForSave(KualiDocumentEvent event) {
160 
161         // copied from super because we can't call super for AP docs
162         customPrepareForSave(event);
163 
164         // DO NOT CALL SUPER HERE!! Cannot call super because it will mess up the GL entry creation process (hjs)
165         // super.prepareForSave(event);
166     }
167 
168     /**
169      * Helper method to be called from custom prepare for save and to be overriden by sub class.
170      *
171      * @return - Po Document Type
172      */
173     public abstract String getPoDocumentTypeForAccountsPayableDocumentCancel();
174 
175     /**
176      * @see org.kuali.rice.krad.document.DocumentBase#handleRouteLevelChange(org.kuali.rice.kew.clientapp.vo.DocumentRouteLevelChangeDTO)
177      */
178     @Override
179     public void doRouteLevelChange(DocumentRouteLevelChange levelChangeEvent) {
180         LOG.debug("handleRouteLevelChange() started");
181         super.doRouteLevelChange(levelChangeEvent);
182         saveDocumentFromPostProcessing();
183 
184         //process node change for documents
185         String newNodeName = levelChangeEvent.getNewNodeName();
186         processNodeChange(newNodeName, levelChangeEvent.getOldNodeName());
187         /*
188          * FIXME: Should be handled by XML now
189         String newNodeName = levelChangeEvent.getNewNodeName();
190         if (processNodeChange(newNodeName, levelChangeEvent.getOldNodeName())) {
191             if (StringUtils.isNotBlank(newNodeName)) {
192                 NodeDetails nodeDetailEnum = getNodeDetailEnum(newNodeName);
193                 if (ObjectUtils.isNotNull(nodeDetailEnum)) {
194                     String statusCode = nodeDetailEnum.getAwaitingStatusCode();
195                     if (StringUtils.isNotBlank(statusCode)) {
196                         setAppDocStatus(statusCode);
197 
198                     }
199                     else {
200                         if (LOG.isDebugEnabled()) {
201                             LOG.debug("Document with id " + getDocumentNumber() + " will stop in route node '" + newNodeName + "' but no awaiting status found to set");
202                         }
203                     }
204                 }
205             }
206         }*/
207     }
208 
209     /**
210      * Hook to allow processing after a route level is passed.
211      *
212      * @param newNodeName - current route level
213      * @param oldNodeName - previous route level
214      * @return - true if process completes to valid state
215      */
216     public abstract boolean processNodeChange(String newNodeName, String oldNodeName);
217 
218     /**
219      * Retrieves node details object based on name.
220      *
221      * @param nodeName - route level
222      * @return - Information about the supplied route level
223      */
224     //public abstract NodeDetails getNodeDetailEnum(String nodeName);
225 
226     /**
227      * Hook point to allow processing after a save.
228      */
229     public abstract void saveDocumentFromPostProcessing();
230 
231     // GETTERS AND SETTERS
232     @Override
233     public Integer getPurchaseOrderIdentifier() {
234         return purchaseOrderIdentifier;
235     }
236 
237     @Override
238     public void setPurchaseOrderIdentifier(Integer purchaseOrderIdentifier) {
239         this.purchaseOrderIdentifier = purchaseOrderIdentifier;
240     }
241 
242     @Override
243     public String getAccountsPayableProcessorIdentifier() {
244         return accountsPayableProcessorIdentifier;
245     }
246 
247     @Override
248     public void setAccountsPayableProcessorIdentifier(String accountsPayableProcessorIdentifier) {
249         this.accountsPayableProcessorIdentifier = accountsPayableProcessorIdentifier;
250     }
251 
252     @Override
253     public String getLastActionPerformedByPersonId() {
254         return lastActionPerformedByPersonId;
255     }
256 
257     @Override
258     public void setLastActionPerformedByPersonId(String lastActionPerformedByPersonId) {
259         this.lastActionPerformedByPersonId = lastActionPerformedByPersonId;
260     }
261 
262     @Override
263     public String getProcessingCampusCode() {
264         return processingCampusCode;
265     }
266 
267     @Override
268     public void setProcessingCampusCode(String processingCampusCode) {
269         this.processingCampusCode = processingCampusCode;
270     }
271 
272     @Override
273     public Timestamp getAccountsPayableApprovalTimestamp() {
274         return accountsPayableApprovalTimestamp;
275     }
276 
277     @Override
278     public void setAccountsPayableApprovalTimestamp(Timestamp accountsPayableApprovalTimestamp) {
279         this.accountsPayableApprovalTimestamp = accountsPayableApprovalTimestamp;
280     }
281 
282     @Override
283     public Timestamp getExtractedTimestamp() {
284         return extractedTimestamp;
285     }
286 
287     @Override
288     public void setExtractedTimestamp(Timestamp extractedTimestamp) {
289         this.extractedTimestamp = extractedTimestamp;
290     }
291 
292     @Override
293     public boolean isHoldIndicator() {
294         return holdIndicator;
295     }
296 
297     @Override
298     public void setHoldIndicator(boolean holdIndicator) {
299         this.holdIndicator = holdIndicator;
300     }
301 
302     @Override
303     public String getNoteLine1Text() {
304         return noteLine1Text;
305     }
306 
307     @Override
308     public void setNoteLine1Text(String noteLine1Text) {
309         this.noteLine1Text = noteLine1Text;
310     }
311 
312     @Override
313     public String getNoteLine2Text() {
314         return noteLine2Text;
315     }
316 
317     @Override
318     public void setNoteLine2Text(String noteLine2Text) {
319         this.noteLine2Text = noteLine2Text;
320     }
321 
322     @Override
323     public String getNoteLine3Text() {
324         return noteLine3Text;
325     }
326 
327     @Override
328     public void setNoteLine3Text(String noteLine3Text) {
329         this.noteLine3Text = noteLine3Text;
330     }
331 
332     @Override
333     public CampusParameter getProcessingCampus() {
334         return processingCampus;
335     }
336 
337     public String getChartOfAccountsCode() {
338         return chartOfAccountsCode;
339     }
340 
341     public void setChartOfAccountsCode(String chartOfAccountsCode) {
342         this.chartOfAccountsCode = chartOfAccountsCode;
343     }
344 
345     public String getOrganizationCode() {
346         return organizationCode;
347     }
348 
349     public void setOrganizationCode(String organizationCode) {
350         this.organizationCode = organizationCode;
351     }
352 
353     public boolean isGenerateEncumbranceEntries() {
354         return generateEncumbranceEntries;
355     }
356 
357     public void setGenerateEncumbranceEntries(boolean generateEncumbranceEntries) {
358         this.generateEncumbranceEntries = generateEncumbranceEntries;
359     }
360 
361     /**
362      * @see org.kuali.ole.module.purap.document.AccountsPayableDocument#getPurchaseOrderDocument()
363      */
364     @Override
365     public PurchaseOrderDocument getPurchaseOrderDocument() {
366         if ((ObjectUtils.isNull(purchaseOrderDocument) || ObjectUtils.isNull(purchaseOrderDocument.getPurapDocumentIdentifier())) && (ObjectUtils.isNotNull(getPurchaseOrderIdentifier()))) {
367             setPurchaseOrderDocument(SpringContext.getBean(PurchaseOrderService.class).getCurrentPurchaseOrder(this.getPurchaseOrderIdentifier()));
368         }
369         return purchaseOrderDocument;
370     }
371 
372     /**
373      * @see org.kuali.ole.module.purap.document.AccountsPayableDocument#setPurchaseOrderDocument(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
374      */
375     @Override
376     public void setPurchaseOrderDocument(PurchaseOrderDocument purchaseOrderDocument) {
377         if (ObjectUtils.isNull(purchaseOrderDocument)) {
378             // KUALI-PURAP 1185 PO Id not being set to null, instead throwing error on main screen that value is invalid.
379             // setPurchaseOrderIdentifier(null);
380             this.purchaseOrderDocument = null;
381         } else {
382             if (ObjectUtils.isNotNull(purchaseOrderDocument.getPurapDocumentIdentifier())) {
383                 setPurchaseOrderIdentifier(purchaseOrderDocument.getPurapDocumentIdentifier());
384             }
385             this.purchaseOrderDocument = purchaseOrderDocument;
386         }
387     }
388 
389     public boolean isClosePurchaseOrderIndicator() {
390         return closePurchaseOrderIndicator;
391     }
392 
393     public void setClosePurchaseOrderIndicator(boolean closePurchaseOrderIndicator) {
394         this.closePurchaseOrderIndicator = closePurchaseOrderIndicator;
395     }
396 
397     public boolean isReopenPurchaseOrderIndicator() {
398         return reopenPurchaseOrderIndicator;
399     }
400 
401     public void setReopenPurchaseOrderIndicator(boolean reopenPurchaseOrderIndicator) {
402         this.reopenPurchaseOrderIndicator = reopenPurchaseOrderIndicator;
403     }
404 
405     public String getBankCode() {
406         return bankCode;
407     }
408 
409     public void setBankCode(String bankCode) {
410         this.bankCode = bankCode;
411     }
412 
413     public Bank getBank() {
414         return bank;
415     }
416 
417     public void setBank(Bank bank) {
418         this.bank = bank;
419     }
420 
421     /**
422      * Sets the processing campus.
423      *
424      * @param processingCampus
425      * @deprecated
426      */
427     public void setProcessingCampus(CampusParameter processingCampus) {
428         this.processingCampus = processingCampus;
429     }
430 
431     // Helper methods
432 
433     /**
434      * Retrieves the universal user object for the last person to perform an action on the document.
435      */
436     public Person getLastActionPerformedByUser() {
437         return SpringContext.getBean(org.kuali.rice.kim.api.identity.PersonService.class).getPerson(getLastActionPerformedByPersonId());
438     }
439 
440     /**
441      * Retrieves the person name for the last person to perform an action on the document.
442      *
443      * @return - the person's name who last performed an action on the document.
444      */
445     public String getLastActionPerformedByPersonName() {
446         Person user = getLastActionPerformedByUser();
447         if (ObjectUtils.isNull(user)) {
448             return "";
449         } else {
450             return user.getName();
451         }
452     }
453 
454     public String getDebitCreditCodeForGLEntries() {
455         return debitCreditCodeForGLEntries;
456     }
457 
458     public void setDebitCreditCodeForGLEntries(String debitCreditCodeForGLEntries) {
459         this.debitCreditCodeForGLEntries = debitCreditCodeForGLEntries;
460     }
461 
462     @Override
463     public boolean isUnmatchedOverride() {
464         return unmatchedOverride;
465     }
466 
467     @Override
468     public void setUnmatchedOverride(boolean unmatchedOverride) {
469         this.unmatchedOverride = unmatchedOverride;
470     }
471 
472     public boolean getExtractedIndicatorForSearching() {
473         return extractedTimestamp != null;
474     }
475 
476     public boolean isHoldIndicatorForSearching() {
477         return holdIndicator;
478     }
479 
480     /**
481      * @see org.kuali.ole.module.purap.document.AccountsPayableDocument#getGrandTotal()
482      */
483     @Override
484     public abstract KualiDecimal getGrandTotal();
485 
486     /**
487      * @see org.kuali.ole.module.purap.document.AccountsPayableDocument#getInitialAmount()
488      */
489     @Override
490     public abstract KualiDecimal getInitialAmount();
491 
492     @Override
493     public boolean isContinuationAccountIndicator() {
494         return continuationAccountIndicator;
495     }
496 
497     @Override
498     public void setContinuationAccountIndicator(boolean continuationAccountIndicator) {
499         this.continuationAccountIndicator = continuationAccountIndicator;
500     }
501 
502     @Override
503     public boolean isExtracted() {
504         return (ObjectUtils.isNotNull(getExtractedTimestamp()));
505     }
506 
507     @Override
508     public abstract AccountsPayableDocumentSpecificService getDocumentSpecificService();
509 
510     @Override
511     public AccountsPayableItem getAPItemFromPOItem(PurchaseOrderItem poi) {
512         for (AccountsPayableItem preqItem : (List<AccountsPayableItem>) this.getItems()) {
513             if (preqItem.getItemType().isLineItemIndicator()) {
514                 if (preqItem.getItemLineNumber().compareTo(poi.getItemLineNumber()) == 0) {
515                     return preqItem;
516                 }
517             } else {
518                 return (AccountsPayableItem) SpringContext.getBean(PurapService.class).getBelowTheLineByType(this, poi.getItemType());
519             }
520         }
521         return null;
522     }
523 
524     /**
525      * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocumentBase#getItemClass()
526      */
527     @Override
528     public Class getItemClass() {
529         return null;
530     }
531 
532     /**
533      * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentIfPossible()
534      */
535     @Override
536     public PurchasingAccountsPayableDocument getPurApSourceDocumentIfPossible() {
537         return null;
538     }
539 
540     /**
541      * @see org.kuali.ole.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentLabelIfPossible()
542      */
543     @Override
544     public String getPurApSourceDocumentLabelIfPossible() {
545         return null;
546     }
547 
548     public void updateExtendedPriceOnItems() {
549         for (AccountsPayableItem item : (List<AccountsPayableItem>) getItems()) {
550             item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
551 
552             final KualiDecimal itemExtendedPrice = (item.getExtendedPrice() == null) ? KualiDecimal.ZERO : item.getExtendedPrice();
553             if (ObjectUtils.isNotNull(item.getItemType())) {
554                 if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) {
555                     KualiDecimal newExtendedPrice = item.calculateExtendedPrice();
556                     item.setExtendedPrice(newExtendedPrice);
557                 }
558             }
559         }
560     }
561 
562     /**
563      * @see org.kuali.ole.module.purap.document.AccountsPayableDocument#getTotalRemitAmount()
564      */
565     @Override
566     public KualiDecimal getTotalRemitTax() {
567         if (!this.isUseTaxIndicator()) {
568             return (KualiDecimal.ZERO.equals(this.getTotalTaxAmount())) ? null : this.getTotalTaxAmount();
569         }
570         return null;
571     }
572 
573     @Override
574     public boolean customizeOffsetGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail accountingLine, GeneralLedgerPendingEntry explicitEntry, GeneralLedgerPendingEntry offsetEntry) {
575         boolean value = super.customizeOffsetGeneralLedgerPendingEntry(accountingLine, explicitEntry, offsetEntry);
576         if (offsetEntry != null && this.offsetUseTax != null) {
577             offsetEntry.setChartOfAccountsCode(this.offsetUseTax.getChartOfAccountsCode());
578             offsetEntry.refreshReferenceObject(OLEPropertyConstants.CHART);
579             offsetEntry.setAccountNumber(this.offsetUseTax.getAccountNumber());
580             offsetEntry.refreshReferenceObject(OLEPropertyConstants.ACCOUNT);
581             offsetEntry.setFinancialObjectCode(this.offsetUseTax.getFinancialObjectCode());
582             offsetEntry.refreshReferenceObject(OLEPropertyConstants.FINANCIAL_OBJECT);
583         } else {
584             value = false;
585         }
586         return value;
587     }
588 
589     @Override
590     public boolean generateGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, PurApItemUseTax offsetUseTax) {
591         this.offsetUseTax = offsetUseTax;
592         boolean value = this.generateGeneralLedgerPendingEntries(glpeSourceDetail, sequenceHelper);
593         this.offsetUseTax = null;
594         return value;
595     }
596 
597     public String getHoldIndicatorForResult() {
598         return isHoldIndicator() ? "Yes" : "No";
599     }
600 
601     public String getProcessingCampusCodeForSearch() {
602         return getProcessingCampusCode();
603     }
604 
605     public String getDocumentChartOfAccountsCodeForSearching() {
606         return getPurchaseOrderDocument().getChartOfAccountsCode();
607     }
608 
609     public String getDocumentOrganizationCodeForSearching() {
610         return getPurchaseOrderDocument().getOrganizationCode();
611     }
612 
613     /**
614      * @return workflow document type for the purap document
615      */
616     public String getDocumentType() {
617         return SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass());
618     }
619 
620     @Override
621     public boolean shouldGiveErrorForEmptyAccountsProration() {
622         return true;
623     }
624 }
625