001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.module.purap.document; 017 018 import java.sql.Timestamp; 019 import java.util.List; 020 021 import org.apache.commons.lang.StringUtils; 022 import org.kuali.kfs.module.purap.PurapPropertyConstants; 023 import org.kuali.kfs.module.purap.PurapWorkflowConstants.NodeDetails; 024 import org.kuali.kfs.module.purap.businessobject.AccountsPayableItem; 025 import org.kuali.kfs.module.purap.businessobject.PurApItemUseTax; 026 import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem; 027 import org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService; 028 import org.kuali.kfs.module.purap.document.service.PurapService; 029 import org.kuali.kfs.module.purap.document.service.PurchaseOrderService; 030 import org.kuali.kfs.sys.KFSPropertyConstants; 031 import org.kuali.kfs.sys.businessobject.Bank; 032 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; 033 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper; 034 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail; 035 import org.kuali.kfs.sys.context.SpringContext; 036 import org.kuali.kfs.vnd.businessobject.CampusParameter; 037 import org.kuali.rice.kew.dto.DocumentRouteLevelChangeDTO; 038 import org.kuali.rice.kim.bo.Person; 039 import org.kuali.rice.kns.rule.event.KualiDocumentEvent; 040 import org.kuali.rice.kns.service.DataDictionaryService; 041 import org.kuali.rice.kns.util.KualiDecimal; 042 import org.kuali.rice.kns.util.ObjectUtils; 043 044 /** 045 * Accounts Payable Document Base 046 */ 047 public abstract class AccountsPayableDocumentBase extends PurchasingAccountsPayableDocumentBase implements AccountsPayableDocument { 048 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountsPayableDocumentBase.class); 049 050 // SHARED FIELDS BETWEEN PAYMENT REQUEST AND CREDIT MEMO 051 protected Timestamp accountsPayableApprovalTimestamp; 052 protected String lastActionPerformedByPersonId; 053 protected String accountsPayableProcessorIdentifier; 054 protected boolean holdIndicator; 055 protected Timestamp extractedTimestamp; 056 protected Integer purchaseOrderIdentifier; 057 protected String processingCampusCode; 058 protected String noteLine1Text; 059 protected String noteLine2Text; 060 protected String noteLine3Text; 061 protected boolean continuationAccountIndicator; 062 protected boolean closePurchaseOrderIndicator; 063 protected boolean reopenPurchaseOrderIndicator; 064 protected String bankCode; 065 066 protected boolean unmatchedOverride; // not persisted 067 068 // NOT PERSISTED IN DB 069 // BELOW USED BY ROUTING 070 protected String chartOfAccountsCode; 071 protected String organizationCode; 072 073 // NOT PERSISTED IN DB 074 // BELOW USED BY GL ENTRY CREATION 075 protected boolean generateEncumbranceEntries; 076 protected String debitCreditCodeForGLEntries; 077 protected PurApItemUseTax offsetUseTax; 078 079 // REFERENCE OBJECTS 080 protected CampusParameter processingCampus; 081 protected transient PurchaseOrderDocument purchaseOrderDocument; 082 protected Bank bank; 083 084 /** 085 * Constructs a AccountsPayableDocumentBase 086 */ 087 public AccountsPayableDocumentBase() { 088 super(); 089 setUnmatchedOverride(false); 090 } 091 092 public void setLineItemTotal(KualiDecimal total) { 093 // do nothing, this is so that the jsp won't complain about lineItemTotal have no setter method. 094 } 095 096 public void setGrandTotal(KualiDecimal total) { 097 // do nothing, this is so that the jsp won't complain about grandTotal have no setter method. 098 } 099 100 /** 101 * Overriding to stop the deleting of general ledger entries. 102 * 103 * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#removeGeneralLedgerPendingEntries() 104 */ 105 @Override 106 protected void removeGeneralLedgerPendingEntries() { 107 // do not delete entries for PREQ or CM (hjs) 108 } 109 110 /** 111 * @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#requiresAccountsPayableReviewRouting() 112 */ 113 public boolean requiresAccountsPayableReviewRouting() { 114 return !approvalAtAccountsPayableReviewAllowed(); 115 } 116 117 /** 118 * @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#approvalAtAccountsPayableReviewAllowed() 119 */ 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.kfs.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(KFSPropertyConstants.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.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#prepareForSave(org.kuali.rice.kns.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.kns.document.DocumentBase#handleRouteLevelChange(org.kuali.rice.kew.clientapp.vo.DocumentRouteLevelChangeDTO) 177 */ 178 @Override 179 public void doRouteLevelChange(DocumentRouteLevelChangeDTO levelChangeEvent) { 180 LOG.debug("handleRouteLevelChange() started"); 181 super.doRouteLevelChange(levelChangeEvent); 182 String newNodeName = levelChangeEvent.getNewNodeName(); 183 if (processNodeChange(newNodeName, levelChangeEvent.getOldNodeName())) { 184 if (StringUtils.isNotBlank(newNodeName)) { 185 NodeDetails nodeDetailEnum = getNodeDetailEnum(newNodeName); 186 if (ObjectUtils.isNotNull(nodeDetailEnum)) { 187 String statusCode = nodeDetailEnum.getAwaitingStatusCode(); 188 if (StringUtils.isNotBlank(statusCode)) { 189 SpringContext.getBean(PurapService.class).updateStatus(this, statusCode); 190 saveDocumentFromPostProcessing(); 191 } 192 else { 193 LOG.debug("Document with id " + getDocumentNumber() + " will stop in route node '" + newNodeName + "' but no awaiting status found to set"); 194 } 195 } 196 } 197 } 198 } 199 200 /** 201 * Hook to allow processing after a route level is passed. 202 * 203 * @param newNodeName - current route level 204 * @param oldNodeName - previous route level 205 * @return - true if process completes to valid state 206 */ 207 public abstract boolean processNodeChange(String newNodeName, String oldNodeName); 208 209 /** 210 * Retrieves node details object based on name. 211 * 212 * @param nodeName - route level 213 * @return - Information about the supplied route level 214 */ 215 public abstract NodeDetails getNodeDetailEnum(String nodeName); 216 217 /** 218 * Hook point to allow processing after a save. 219 */ 220 public abstract void saveDocumentFromPostProcessing(); 221 222 // GETTERS AND SETTERS 223 public Integer getPurchaseOrderIdentifier() { 224 return purchaseOrderIdentifier; 225 } 226 227 public void setPurchaseOrderIdentifier(Integer purchaseOrderIdentifier) { 228 this.purchaseOrderIdentifier = purchaseOrderIdentifier; 229 } 230 231 public String getAccountsPayableProcessorIdentifier() { 232 return accountsPayableProcessorIdentifier; 233 } 234 235 public void setAccountsPayableProcessorIdentifier(String accountsPayableProcessorIdentifier) { 236 this.accountsPayableProcessorIdentifier = accountsPayableProcessorIdentifier; 237 } 238 239 public String getLastActionPerformedByPersonId() { 240 return lastActionPerformedByPersonId; 241 } 242 243 public void setLastActionPerformedByPersonId(String lastActionPerformedByPersonId) { 244 this.lastActionPerformedByPersonId = lastActionPerformedByPersonId; 245 } 246 247 public String getProcessingCampusCode() { 248 return processingCampusCode; 249 } 250 251 public void setProcessingCampusCode(String processingCampusCode) { 252 this.processingCampusCode = processingCampusCode; 253 } 254 255 public Timestamp getAccountsPayableApprovalTimestamp() { 256 return accountsPayableApprovalTimestamp; 257 } 258 259 public void setAccountsPayableApprovalTimestamp(Timestamp accountsPayableApprovalTimestamp) { 260 this.accountsPayableApprovalTimestamp = accountsPayableApprovalTimestamp; 261 } 262 263 public Timestamp getExtractedTimestamp() { 264 return extractedTimestamp; 265 } 266 267 public void setExtractedTimestamp(Timestamp extractedTimestamp) { 268 this.extractedTimestamp = extractedTimestamp; 269 } 270 271 public boolean isHoldIndicator() { 272 return holdIndicator; 273 } 274 275 public void setHoldIndicator(boolean holdIndicator) { 276 this.holdIndicator = holdIndicator; 277 } 278 279 public String getNoteLine1Text() { 280 return noteLine1Text; 281 } 282 283 public void setNoteLine1Text(String noteLine1Text) { 284 this.noteLine1Text = noteLine1Text; 285 } 286 287 public String getNoteLine2Text() { 288 return noteLine2Text; 289 } 290 291 public void setNoteLine2Text(String noteLine2Text) { 292 this.noteLine2Text = noteLine2Text; 293 } 294 295 public String getNoteLine3Text() { 296 return noteLine3Text; 297 } 298 299 public void setNoteLine3Text(String noteLine3Text) { 300 this.noteLine3Text = noteLine3Text; 301 } 302 303 public CampusParameter getProcessingCampus() { 304 return processingCampus; 305 } 306 307 public String getChartOfAccountsCode() { 308 return chartOfAccountsCode; 309 } 310 311 public void setChartOfAccountsCode(String chartOfAccountsCode) { 312 this.chartOfAccountsCode = chartOfAccountsCode; 313 } 314 315 public String getOrganizationCode() { 316 return organizationCode; 317 } 318 319 public void setOrganizationCode(String organizationCode) { 320 this.organizationCode = organizationCode; 321 } 322 323 public boolean isGenerateEncumbranceEntries() { 324 return generateEncumbranceEntries; 325 } 326 327 public void setGenerateEncumbranceEntries(boolean generateEncumbranceEntries) { 328 this.generateEncumbranceEntries = generateEncumbranceEntries; 329 } 330 331 /** 332 * @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getPurchaseOrderDocument() 333 */ 334 public PurchaseOrderDocument getPurchaseOrderDocument() { 335 if ((ObjectUtils.isNull(purchaseOrderDocument) || ObjectUtils.isNull(purchaseOrderDocument.getPurapDocumentIdentifier())) && (ObjectUtils.isNotNull(getPurchaseOrderIdentifier()))) { 336 setPurchaseOrderDocument(SpringContext.getBean(PurchaseOrderService.class).getCurrentPurchaseOrder(this.getPurchaseOrderIdentifier())); 337 } 338 return purchaseOrderDocument; 339 } 340 341 /** 342 * @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#setPurchaseOrderDocument(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 343 */ 344 public void setPurchaseOrderDocument(PurchaseOrderDocument purchaseOrderDocument) { 345 if (ObjectUtils.isNull(purchaseOrderDocument)) { 346 // KUALI-PURAP 1185 PO Id not being set to null, instead throwing error on main screen that value is invalid. 347 // setPurchaseOrderIdentifier(null); 348 this.purchaseOrderDocument = null; 349 } 350 else { 351 if (ObjectUtils.isNotNull(purchaseOrderDocument.getPurapDocumentIdentifier())) { 352 setPurchaseOrderIdentifier(purchaseOrderDocument.getPurapDocumentIdentifier()); 353 } 354 this.purchaseOrderDocument = purchaseOrderDocument; 355 } 356 } 357 358 public boolean isClosePurchaseOrderIndicator() { 359 return closePurchaseOrderIndicator; 360 } 361 362 public void setClosePurchaseOrderIndicator(boolean closePurchaseOrderIndicator) { 363 this.closePurchaseOrderIndicator = closePurchaseOrderIndicator; 364 } 365 366 public boolean isReopenPurchaseOrderIndicator() { 367 return reopenPurchaseOrderIndicator; 368 } 369 370 public void setReopenPurchaseOrderIndicator(boolean reopenPurchaseOrderIndicator) { 371 this.reopenPurchaseOrderIndicator = reopenPurchaseOrderIndicator; 372 } 373 374 public String getBankCode() { 375 return bankCode; 376 } 377 378 public void setBankCode(String bankCode) { 379 this.bankCode = bankCode; 380 } 381 382 public Bank getBank() { 383 return bank; 384 } 385 386 public void setBank(Bank bank) { 387 this.bank = bank; 388 } 389 390 /** 391 * Sets the processing campus. 392 * @deprecated 393 * @param processingCampus 394 */ 395 public void setProcessingCampus(CampusParameter processingCampus) { 396 this.processingCampus = processingCampus; 397 } 398 399 // Helper methods 400 /** 401 * Retrieves the universal user object for the last person to perform an action on the document. 402 */ 403 public Person getLastActionPerformedByUser() { 404 return SpringContext.getBean(org.kuali.rice.kim.service.PersonService.class).getPerson(getLastActionPerformedByPersonId()); 405 } 406 407 /** 408 * Retrieves the person name for the last person to perform an action on the document. 409 * 410 * @return - the person's name who last performed an action on the document. 411 */ 412 public String getLastActionPerformedByPersonName() { 413 Person user = getLastActionPerformedByUser(); 414 if (ObjectUtils.isNull(user)) { 415 return ""; 416 } 417 else { 418 return user.getName(); 419 } 420 } 421 422 public String getDebitCreditCodeForGLEntries() { 423 return debitCreditCodeForGLEntries; 424 } 425 426 public void setDebitCreditCodeForGLEntries(String debitCreditCodeForGLEntries) { 427 this.debitCreditCodeForGLEntries = debitCreditCodeForGLEntries; 428 } 429 430 public boolean isUnmatchedOverride() { 431 return unmatchedOverride; 432 } 433 434 public void setUnmatchedOverride(boolean unmatchedOverride) { 435 this.unmatchedOverride = unmatchedOverride; 436 } 437 438 public boolean getExtractedIndicatorForSearching() { 439 return extractedTimestamp != null; 440 } 441 442 public boolean isHoldIndicatorForSearching() { 443 return holdIndicator; 444 } 445 446 /** 447 * @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getGrandTotal() 448 */ 449 public abstract KualiDecimal getGrandTotal(); 450 451 /** 452 * @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getInitialAmount() 453 */ 454 public abstract KualiDecimal getInitialAmount(); 455 456 public boolean isContinuationAccountIndicator() { 457 return continuationAccountIndicator; 458 } 459 460 public void setContinuationAccountIndicator(boolean continuationAccountIndicator) { 461 this.continuationAccountIndicator = continuationAccountIndicator; 462 } 463 464 public boolean isExtracted() { 465 return (ObjectUtils.isNotNull(getExtractedTimestamp())); 466 } 467 468 public abstract AccountsPayableDocumentSpecificService getDocumentSpecificService(); 469 470 public AccountsPayableItem getAPItemFromPOItem(PurchaseOrderItem poi) { 471 for (AccountsPayableItem preqItem : (List<AccountsPayableItem>) this.getItems()) { 472 if (preqItem.getItemType().isLineItemIndicator()) { 473 if (preqItem.getItemLineNumber().compareTo(poi.getItemLineNumber()) == 0) { 474 return preqItem; 475 } 476 } 477 else { 478 return (AccountsPayableItem) SpringContext.getBean(PurapService.class).getBelowTheLineByType(this, poi.getItemType()); 479 } 480 } 481 return null; 482 } 483 484 /** 485 * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getItemClass() 486 */ 487 public Class getItemClass() { 488 return null; 489 } 490 491 /** 492 * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentIfPossible() 493 */ 494 public PurchasingAccountsPayableDocument getPurApSourceDocumentIfPossible() { 495 return null; 496 } 497 498 /** 499 * @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentLabelIfPossible() 500 */ 501 public String getPurApSourceDocumentLabelIfPossible() { 502 return null; 503 } 504 505 public void updateExtendedPriceOnItems() { 506 for (AccountsPayableItem item : (List<AccountsPayableItem>) getItems()) { 507 item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE); 508 509 final KualiDecimal itemExtendedPrice = (item.getExtendedPrice()==null)?KualiDecimal.ZERO:item.getExtendedPrice();; 510 if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) { 511 KualiDecimal newExtendedPrice = item.calculateExtendedPrice(); 512 item.setExtendedPrice(newExtendedPrice); 513 } 514 } 515 } 516 517 /** 518 * 519 * @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getTotalRemitAmount() 520 */ 521 public KualiDecimal getTotalRemitTax() { 522 if(!this.isUseTaxIndicator()) { 523 return (KualiDecimal.ZERO.equals(this.getTotalTaxAmount()))?null:this.getTotalTaxAmount(); 524 } 525 return null; 526 } 527 528 @Override 529 public boolean customizeOffsetGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail accountingLine, GeneralLedgerPendingEntry explicitEntry, GeneralLedgerPendingEntry offsetEntry) { 530 boolean value = super.customizeOffsetGeneralLedgerPendingEntry(accountingLine, explicitEntry, offsetEntry); 531 if(offsetEntry != null && this.offsetUseTax != null) { 532 offsetEntry.setChartOfAccountsCode(this.offsetUseTax.getChartOfAccountsCode()); 533 offsetEntry.refreshReferenceObject(KFSPropertyConstants.CHART); 534 offsetEntry.setAccountNumber(this.offsetUseTax.getAccountNumber()); 535 offsetEntry.refreshReferenceObject(KFSPropertyConstants.ACCOUNT); 536 offsetEntry.setFinancialObjectCode(this.offsetUseTax.getFinancialObjectCode()); 537 offsetEntry.refreshReferenceObject(KFSPropertyConstants.FINANCIAL_OBJECT); 538 } else { 539 value=false; 540 } 541 return value; 542 } 543 544 public boolean generateGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, PurApItemUseTax offsetUseTax) { 545 this.offsetUseTax = offsetUseTax; 546 boolean value = this.generateGeneralLedgerPendingEntries(glpeSourceDetail, sequenceHelper); 547 this.offsetUseTax = null; 548 return value; 549 } 550 551 public String getHoldIndicatorForResult(){ 552 return isHoldIndicator() ? "Yes" : "No"; 553 } 554 555 public String getProcessingCampusCodeForSearch(){ 556 return getProcessingCampusCode(); 557 } 558 559 public String getDocumentChartOfAccountsCodeForSearching(){ 560 return getPurchaseOrderDocument().getChartOfAccountsCode(); 561 } 562 563 public String getDocumentOrganizationCodeForSearching(){ 564 return getPurchaseOrderDocument().getOrganizationCode(); 565 } 566 567 /** 568 * @return workflow document type for the purap document 569 */ 570 public String getDocumentType() { 571 return SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass()); 572 } 573 574 public boolean shouldGiveErrorForEmptyAccountsProration() { 575 return true; 576 } 577 } 578