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.service.impl; 017 018 import java.math.BigDecimal; 019 import java.sql.Date; 020 import java.sql.Timestamp; 021 import java.util.ArrayList; 022 import java.util.Collection; 023 import java.util.HashMap; 024 import java.util.HashSet; 025 import java.util.Iterator; 026 import java.util.List; 027 import java.util.Set; 028 029 import org.apache.commons.collections.CollectionUtils; 030 import org.apache.commons.lang.StringUtils; 031 import org.kuali.kfs.module.purap.PurapConstants; 032 import org.kuali.kfs.module.purap.PurapKeyConstants; 033 import org.kuali.kfs.module.purap.PurapParameterConstants; 034 import org.kuali.kfs.module.purap.PurapConstants.CreditMemoStatuses; 035 import org.kuali.kfs.module.purap.PurapWorkflowConstants.NodeDetails; 036 import org.kuali.kfs.module.purap.PurapWorkflowConstants.CreditMemoDocument.NodeDetailEnum; 037 import org.kuali.kfs.module.purap.businessobject.CreditMemoAccount; 038 import org.kuali.kfs.module.purap.businessobject.CreditMemoItem; 039 import org.kuali.kfs.module.purap.businessobject.PaymentRequestItem; 040 import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine; 041 import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem; 042 import org.kuali.kfs.module.purap.businessobject.PurchasingCapitalAssetItem; 043 import org.kuali.kfs.module.purap.document.AccountsPayableDocument; 044 import org.kuali.kfs.module.purap.document.PaymentRequestDocument; 045 import org.kuali.kfs.module.purap.document.PurchaseOrderDocument; 046 import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument; 047 import org.kuali.kfs.module.purap.document.VendorCreditMemoDocument; 048 import org.kuali.kfs.module.purap.document.dataaccess.CreditMemoDao; 049 import org.kuali.kfs.module.purap.document.service.AccountsPayableService; 050 import org.kuali.kfs.module.purap.document.service.CreditMemoService; 051 import org.kuali.kfs.module.purap.document.service.PaymentRequestService; 052 import org.kuali.kfs.module.purap.document.service.PurapService; 053 import org.kuali.kfs.module.purap.document.service.PurchaseOrderService; 054 import org.kuali.kfs.module.purap.document.validation.event.AttributedContinuePurapEvent; 055 import org.kuali.kfs.module.purap.service.PurapAccountingService; 056 import org.kuali.kfs.module.purap.service.PurapGeneralLedgerService; 057 import org.kuali.kfs.module.purap.util.ExpiredOrClosedAccountEntry; 058 import org.kuali.kfs.module.purap.util.VendorGroupingHelper; 059 import org.kuali.kfs.sys.businessobject.Bank; 060 import org.kuali.kfs.sys.businessobject.SourceAccountingLine; 061 import org.kuali.kfs.sys.context.SpringContext; 062 import org.kuali.kfs.sys.service.BankService; 063 import org.kuali.kfs.vnd.VendorConstants; 064 import org.kuali.kfs.vnd.VendorUtils; 065 import org.kuali.kfs.vnd.businessobject.VendorAddress; 066 import org.kuali.kfs.vnd.businessobject.VendorDetail; 067 import org.kuali.kfs.vnd.document.service.VendorService; 068 import org.kuali.rice.kew.exception.WorkflowException; 069 import org.kuali.rice.kim.bo.Person; 070 import org.kuali.rice.kns.bo.DocumentHeader; 071 import org.kuali.rice.kns.bo.Note; 072 import org.kuali.rice.kns.exception.ValidationException; 073 import org.kuali.rice.kns.service.DataDictionaryService; 074 import org.kuali.rice.kns.service.DocumentService; 075 import org.kuali.rice.kns.service.KualiConfigurationService; 076 import org.kuali.rice.kns.service.NoteService; 077 import org.kuali.rice.kns.util.GlobalVariables; 078 import org.kuali.rice.kns.util.KNSPropertyConstants; 079 import org.kuali.rice.kns.util.KualiDecimal; 080 import org.kuali.rice.kns.util.ObjectUtils; 081 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 082 import org.kuali.rice.kns.workflow.service.WorkflowDocumentService; 083 import org.springframework.transaction.annotation.Transactional; 084 085 /** 086 * Provides services to support the creation of a Credit Memo Document. 087 */ 088 @Transactional 089 public class CreditMemoServiceImpl implements CreditMemoService { 090 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CreditMemoServiceImpl.class); 091 092 private AccountsPayableService accountsPayableService; 093 private CreditMemoDao creditMemoDao; 094 private DataDictionaryService dataDictionaryService; 095 private DocumentService documentService; 096 private KualiConfigurationService kualiConfigurationService; 097 private NoteService noteService; 098 private PaymentRequestService paymentRequestService; 099 private PurapAccountingService purapAccountingService; 100 private PurapGeneralLedgerService purapGeneralLedgerService; 101 private PurapService purapService; 102 private PurchaseOrderService purchaseOrderService; 103 private VendorService vendorService; 104 private WorkflowDocumentService workflowDocumentService; 105 106 107 public void setAccountsPayableService(AccountsPayableService accountsPayableService) { 108 this.accountsPayableService = accountsPayableService; 109 } 110 111 public void setCreditMemoDao(CreditMemoDao creditMemoDao) { 112 this.creditMemoDao = creditMemoDao; 113 } 114 115 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 116 this.dataDictionaryService = dataDictionaryService; 117 } 118 119 public void setDocumentService(DocumentService documentService) { 120 this.documentService = documentService; 121 } 122 123 public void setKualiConfigurationService(KualiConfigurationService kualiConfigurationService) { 124 this.kualiConfigurationService = kualiConfigurationService; 125 } 126 127 public void setNoteService(NoteService noteService) { 128 this.noteService = noteService; 129 } 130 131 public void setPaymentRequestService(PaymentRequestService paymentRequestService) { 132 this.paymentRequestService = paymentRequestService; 133 } 134 135 public void setPurapAccountingService(PurapAccountingService purapAccountingService) { 136 this.purapAccountingService = purapAccountingService; 137 } 138 139 public void setPurapGeneralLedgerService(PurapGeneralLedgerService purapGeneralLedgerService) { 140 this.purapGeneralLedgerService = purapGeneralLedgerService; 141 } 142 143 public void setPurapService(PurapService purapService) { 144 this.purapService = purapService; 145 } 146 147 public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) { 148 this.purchaseOrderService = purchaseOrderService; 149 } 150 151 public void setVendorService(VendorService vendorService) { 152 this.vendorService = vendorService; 153 } 154 155 public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService){ 156 this.workflowDocumentService = workflowDocumentService; 157 } 158 159 160 161 /** 162 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#getCreditMemosToExtract(java.lang.String) 163 */ 164 public Iterator<VendorCreditMemoDocument> getCreditMemosToExtract(String chartCode) { 165 LOG.debug("getCreditMemosToExtract() started"); 166 167 return creditMemoDao.getCreditMemosToExtract(chartCode); 168 } 169 170 public Collection<VendorCreditMemoDocument> getCreditMemosToExtractByVendor(String chartCode, VendorGroupingHelper vendor ) { 171 LOG.debug("getCreditMemosToExtractByVendor() started"); 172 173 return creditMemoDao.getCreditMemosToExtractByVendor(chartCode,vendor); 174 } 175 176 public Set<VendorGroupingHelper> getVendorsOnCreditMemosToExtract(String chartCode) { 177 LOG.debug("getVendorsOnCreditMemosToExtract() started"); 178 HashSet<VendorGroupingHelper> vendors = new HashSet<VendorGroupingHelper>(); 179 180 Iterator<VendorCreditMemoDocument> docs = getCreditMemosToExtract(chartCode); 181 while ( docs.hasNext() ) { 182 VendorCreditMemoDocument doc = docs.next(); 183 vendors.add( new VendorGroupingHelper( doc ) ); 184 } 185 return vendors; 186 } 187 188 /** 189 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#creditMemoDuplicateMessages(org.kuali.kfs.module.purap.document.CreditMemoDocument) 190 */ 191 public String creditMemoDuplicateMessages(VendorCreditMemoDocument cmDocument) { 192 String duplicateMessage = null; 193 194 String vendorNumber = cmDocument.getVendorNumber(); 195 if (StringUtils.isEmpty(vendorNumber)) { 196 PurchasingAccountsPayableDocument sourceDocument = cmDocument.getPurApSourceDocumentIfPossible(); 197 if (ObjectUtils.isNotNull(sourceDocument)) { 198 vendorNumber = sourceDocument.getVendorNumber(); 199 } 200 } 201 202 if (StringUtils.isNotEmpty(vendorNumber)) { 203 // check for existence of another credit memo with the same vendor and vendor credit memo number 204 if (creditMemoDao.duplicateExists(VendorUtils.getVendorHeaderId(vendorNumber), VendorUtils.getVendorDetailId(vendorNumber), cmDocument.getCreditMemoNumber())) { 205 duplicateMessage = kualiConfigurationService.getPropertyString(PurapKeyConstants.MESSAGE_DUPLICATE_CREDIT_MEMO_VENDOR_NUMBER); 206 } 207 208 // check for existence of another credit memo with the same vendor and credit memo date 209 if (creditMemoDao.duplicateExists(VendorUtils.getVendorHeaderId(vendorNumber), VendorUtils.getVendorDetailId(vendorNumber), cmDocument.getCreditMemoDate(), cmDocument.getCreditMemoAmount())) { 210 duplicateMessage = kualiConfigurationService.getPropertyString(PurapKeyConstants.MESSAGE_DUPLICATE_CREDIT_MEMO_VENDOR_NUMBER_DATE_AMOUNT); 211 } 212 } 213 214 return duplicateMessage; 215 } 216 217 /** 218 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#getPOInvoicedItems(org.kuali.kfs.module.purap.document.PurchaseOrderDocument) 219 */ 220 public List<PurchaseOrderItem> getPOInvoicedItems(PurchaseOrderDocument poDocument) { 221 List<PurchaseOrderItem> invoicedItems = new ArrayList<PurchaseOrderItem>(); 222 223 for (Iterator iter = poDocument.getItems().iterator(); iter.hasNext();) { 224 PurchaseOrderItem poItem = (PurchaseOrderItem) iter.next(); 225 226 // only items of type above the line can be considered for being invoiced 227 if (poItem.getItemType().isAdditionalChargeIndicator()) { 228 continue; 229 } 230 231 if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalQuantity().isGreaterThan(KualiDecimal.ZERO)) { 232 invoicedItems.add(poItem); 233 } 234 else { 235 BigDecimal unitPrice = (poItem.getItemUnitPrice() == null ? new BigDecimal(0) : poItem.getItemUnitPrice()); 236 if (unitPrice.doubleValue() > poItem.getItemOutstandingEncumberedAmount().doubleValue()) { 237 invoicedItems.add(poItem); 238 } 239 } 240 } 241 242 return invoicedItems; 243 } 244 245 246 /** 247 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#calculateCreditMemo(org.kuali.kfs.module.purap.document.CreditMemoDocument) 248 */ 249 public void calculateCreditMemo(VendorCreditMemoDocument cmDocument) { 250 251 cmDocument.updateExtendedPriceOnItems(); 252 253 for (CreditMemoItem item : (List<CreditMemoItem>) cmDocument.getItems()) { 254 // make sure restocking fee is negative 255 if (StringUtils.equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_RESTCK_FEE_CODE, item.getItemTypeCode())) { 256 if (item.getItemUnitPrice() != null) { 257 item.setExtendedPrice(item.getExtendedPrice().abs().negated()); 258 item.setItemUnitPrice(item.getItemUnitPrice().abs().negate()); 259 } 260 } 261 } 262 263 //calculate tax if cm not based on vendor 264 if (cmDocument.isSourceVendor() == false) { 265 purapService.calculateTax(cmDocument); 266 } 267 268 // proration 269 if (cmDocument.isSourceVendor()) { 270 // no proration on vendor 271 return; 272 } 273 274 for (CreditMemoItem item : (List<CreditMemoItem>) cmDocument.getItems()) { 275 276 // skip above the line 277 if (item.getItemType().isLineItemIndicator()) { 278 continue; 279 } 280 281 if ((item.getSourceAccountingLines().isEmpty()) && (ObjectUtils.isNotNull(item.getExtendedPrice())) && (KualiDecimal.ZERO.compareTo(item.getExtendedPrice()) != 0)) { 282 283 KualiDecimal totalAmount = KualiDecimal.ZERO; 284 List<PurApAccountingLine> distributedAccounts = null; 285 List<SourceAccountingLine> summaryAccounts = null; 286 287 totalAmount = cmDocument.getPurApSourceDocumentIfPossible().getTotalDollarAmount(); 288 // this should do nothing on preq which is fine 289 purapAccountingService.updateAccountAmounts(cmDocument.getPurApSourceDocumentIfPossible()); 290 summaryAccounts = purapAccountingService.generateSummary(cmDocument.getPurApSourceDocumentIfPossible().getItems()); 291 distributedAccounts = purapAccountingService.generateAccountDistributionForProration(summaryAccounts, totalAmount, PurapConstants.PRORATION_SCALE, CreditMemoAccount.class); 292 293 if (CollectionUtils.isNotEmpty(distributedAccounts) && CollectionUtils.isEmpty(item.getSourceAccountingLines())) { 294 item.setSourceAccountingLines(distributedAccounts); 295 } 296 } 297 } 298 // end proration 299 } 300 301 /** 302 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#getCreditMemoByDocumentNumber(java.lang.String) 303 */ 304 public VendorCreditMemoDocument getCreditMemoByDocumentNumber(String documentNumber) { 305 LOG.debug("getCreditMemoByDocumentNumber() started"); 306 307 if (ObjectUtils.isNotNull(documentNumber)) { 308 try { 309 VendorCreditMemoDocument doc = (VendorCreditMemoDocument) documentService.getByDocumentHeaderId(documentNumber); 310 return doc; 311 } 312 catch (WorkflowException e) { 313 String errorMessage = "Error getting credit memo document from document service"; 314 LOG.error("getCreditMemoByDocumentNumber() " + errorMessage, e); 315 throw new RuntimeException(errorMessage, e); 316 } 317 } 318 return null; 319 } 320 321 /** 322 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#getCreditMemoDocumentById(java.lang.Integer) 323 */ 324 public VendorCreditMemoDocument getCreditMemoDocumentById(Integer purchasingDocumentIdentifier) { 325 return getCreditMemoByDocumentNumber(creditMemoDao.getDocumentNumberByCreditMemoId(purchasingDocumentIdentifier)); 326 } 327 328 /** 329 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#saveDocument(org.kuali.kfs.module.purap.document.CreditMemoDocument) 330 */ 331 public void populateAndSaveCreditMemo(VendorCreditMemoDocument document) { 332 try { 333 document.setStatusCode(PurapConstants.CreditMemoStatuses.IN_PROCESS); 334 335 if (document.isSourceDocumentPaymentRequest()) { 336 document.setBankCode(document.getPaymentRequestDocument().getBankCode()); 337 document.setBank(document.getPaymentRequestDocument().getBank()); 338 } 339 else { 340 // set bank code to default bank code in the system parameter 341 Bank defaultBank = SpringContext.getBean(BankService.class).getDefaultBankByDocType(document.getClass()); 342 if (defaultBank != null) { 343 document.setBankCode(defaultBank.getBankCode()); 344 document.setBank(defaultBank); 345 } 346 } 347 348 documentService.saveDocument(document, AttributedContinuePurapEvent.class); 349 } 350 catch (ValidationException ve) { 351 document.setStatusCode(PurapConstants.CreditMemoStatuses.INITIATE); 352 } 353 catch (WorkflowException we) { 354 // set the status back to initiate 355 document.setStatusCode(PurapConstants.CreditMemoStatuses.INITIATE); 356 String errorMsg = "Error saving document # " + document.getDocumentHeader().getDocumentNumber() + " " + we.getMessage(); 357 LOG.error(errorMsg, we); 358 throw new RuntimeException(errorMsg, we); 359 } 360 } 361 362 /** 363 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#reopenClosedPO(org.kuali.kfs.module.purap.document.CreditMemoDocument) 364 */ 365 public void reopenClosedPO(VendorCreditMemoDocument cmDocument) { 366 // reopen PO if closed 367 Integer purchaseOrderDocumentId = cmDocument.getPurchaseOrderIdentifier(); 368 if (cmDocument.isSourceDocumentPaymentRequest() && ObjectUtils.isNull(purchaseOrderDocumentId)) { 369 PaymentRequestDocument paymentRequestDocument = paymentRequestService.getPaymentRequestById(cmDocument.getPaymentRequestIdentifier()); 370 purchaseOrderDocumentId = paymentRequestDocument.getPurchaseOrderIdentifier(); 371 } 372 // if we found a valid po id number then check it for reopening 373 if (ObjectUtils.isNotNull(purchaseOrderDocumentId)) { 374 PurchaseOrderDocument purchaseOrderDocument = purchaseOrderService.getCurrentPurchaseOrder(purchaseOrderDocumentId); 375 // only reopen if the po is not null, it does not have a pending change already scheduled, and it is in closed status 376 if (ObjectUtils.isNotNull(purchaseOrderDocument) && (!purchaseOrderDocument.isPendingActionIndicator()) && PurapConstants.PurchaseOrderStatuses.CLOSED.equals(purchaseOrderDocument.getStatusCode())) { 377 378 } 379 } 380 } 381 382 /** 383 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#addHoldOnPaymentRequest(org.kuali.kfs.module.purap.document.CreditMemoDocument, 384 * java.lang.String) 385 */ 386 public VendorCreditMemoDocument addHoldOnCreditMemo(VendorCreditMemoDocument cmDocument, String note) throws Exception { 387 // save the note 388 Note noteObj = documentService.createNoteFromDocument(cmDocument, note); 389 documentService.addNoteToDocument(cmDocument, noteObj); 390 noteService.save(noteObj); 391 392 // retrieve and save with hold indicator set to true 393 VendorCreditMemoDocument cmDoc = getCreditMemoDocumentById(cmDocument.getPurapDocumentIdentifier()); 394 cmDoc.setHoldIndicator(true); 395 cmDoc.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId()); 396 purapService.saveDocumentNoValidation(cmDoc); 397 398 // must also save it on the incoming document 399 cmDocument.setHoldIndicator(true); 400 cmDocument.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId()); 401 402 return cmDoc; 403 } 404 405 /** 406 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#removeHoldOnCreditMemo(org.kuali.kfs.module.purap.document.CreditMemoDocument, 407 * java.lang.String) 408 */ 409 public VendorCreditMemoDocument removeHoldOnCreditMemo(VendorCreditMemoDocument cmDocument, String note) throws Exception { 410 // save the note 411 Note noteObj = documentService.createNoteFromDocument(cmDocument, note); 412 documentService.addNoteToDocument(cmDocument, noteObj); 413 noteService.save(noteObj); 414 415 // retrieve and save with hold indicator set to false 416 VendorCreditMemoDocument cmDoc = getCreditMemoDocumentById(cmDocument.getPurapDocumentIdentifier()); 417 cmDoc.setHoldIndicator(false); 418 cmDoc.setLastActionPerformedByPersonId(null); 419 purapService.saveDocumentNoValidation(cmDoc); 420 421 // must also save it on the incoming document 422 cmDocument.setHoldIndicator(false); 423 cmDocument.setLastActionPerformedByPersonId(null); 424 425 return cmDoc; 426 } 427 428 /** 429 * @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#updateStatusByNode(java.lang.String, org.kuali.kfs.module.purap.document.AccountsPayableDocument) 430 */ 431 public String updateStatusByNode(String currentNodeName, AccountsPayableDocument apDoc) { 432 return updateStatusByNode(currentNodeName, (VendorCreditMemoDocument) apDoc); 433 } 434 435 /** 436 * Updates the status of a credit memo document, currently this is used by the cancel action 437 * 438 * @param currentNodeName The string representing the current node to be used to obtain the canceled status code. 439 * @param cmDoc The credit memo document to be updated. 440 * @return The string representing the canceledStatusCode, if empty it is assumed to be not from workflow. 441 */ 442 protected String updateStatusByNode(String currentNodeName, VendorCreditMemoDocument cmDoc) { 443 // update the status on the document 444 445 String cancelledStatusCode = ""; 446 if (StringUtils.isEmpty(currentNodeName)) { 447 cancelledStatusCode = PurapConstants.CreditMemoStatuses.CANCELLED_POST_AP_APPROVE; 448 } 449 else { 450 NodeDetails currentNode = NodeDetailEnum.getNodeDetailEnumByName(currentNodeName); 451 if (ObjectUtils.isNotNull(currentNode)) { 452 cancelledStatusCode = currentNode.getDisapprovedStatusCode(); 453 } 454 } 455 456 if (StringUtils.isNotBlank(cancelledStatusCode)) { 457 purapService.updateStatus(cmDoc, cancelledStatusCode); 458 purapService.saveDocumentNoValidation(cmDoc); 459 return cancelledStatusCode; 460 } 461 else { 462 logAndThrowRuntimeException("No status found to set for document being disapproved in node '" + currentNodeName + "'"); 463 } 464 return cancelledStatusCode; 465 } 466 467 /** 468 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#cancelExtractedCreditMemo(org.kuali.kfs.module.purap.document.CreditMemoDocument, 469 * java.lang.String) 470 */ 471 public void cancelExtractedCreditMemo(VendorCreditMemoDocument cmDocument, String note) { 472 LOG.debug("cancelExtractedCreditMemo() started"); 473 if (CreditMemoStatuses.CANCELLED_STATUSES.contains(cmDocument.getStatusCode())) { 474 LOG.debug("cancelExtractedCreditMemo() ended"); 475 return; 476 } 477 478 try { 479 Note noteObj = documentService.createNoteFromDocument(cmDocument, note); 480 documentService.addNoteToDocument(cmDocument, noteObj); 481 } 482 catch (Exception e) { 483 throw new RuntimeException(e.getMessage()); 484 } 485 486 accountsPayableService.cancelAccountsPayableDocument(cmDocument, ""); 487 LOG.debug("cancelExtractedCreditMemo() CM " + cmDocument.getPurapDocumentIdentifier() + " Cancelled Without Workflow"); 488 LOG.debug("cancelExtractedCreditMemo() ended"); 489 490 } 491 492 /** 493 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#resetExtractedCreditMemo(org.kuali.kfs.module.purap.document.CreditMemoDocument, 494 * java.lang.String) 495 */ 496 public void resetExtractedCreditMemo(VendorCreditMemoDocument cmDocument, String note) { 497 LOG.debug("resetExtractedCreditMemo() started"); 498 if (CreditMemoStatuses.CANCELLED_STATUSES.contains(cmDocument.getStatusCode())) { 499 LOG.debug("resetExtractedCreditMemo() ended"); 500 return; 501 } 502 cmDocument.setExtractedTimestamp(null); 503 cmDocument.setCreditMemoPaidTimestamp(null); 504 505 Note noteObj; 506 try { 507 noteObj = documentService.createNoteFromDocument(cmDocument, note); 508 documentService.addNoteToDocument(cmDocument, noteObj); 509 } 510 catch (Exception e) { 511 throw new RuntimeException(e.getMessage()); 512 } 513 purapService.saveDocumentNoValidation(cmDocument); 514 515 LOG.debug("resetExtractedCreditMemo() CM " + cmDocument.getPurapDocumentIdentifier() + " Cancelled Without Workflow"); 516 LOG.debug("resetExtractedCreditMemo() ended"); 517 } 518 519 /** 520 * @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#shouldPurchaseOrderBeReversed(org.kuali.kfs.module.purap.document.AccountsPayableDocument) 521 */ 522 public boolean shouldPurchaseOrderBeReversed(AccountsPayableDocument apDoc) { 523 // always return false, never reverse 524 return false; 525 } 526 527 /** 528 * @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#getPersonForCancel(org.kuali.kfs.module.purap.document.AccountsPayableDocument) 529 */ 530 public Person getPersonForCancel(AccountsPayableDocument apDoc) { 531 // return null, since superuser is fine for CM 532 return null; 533 } 534 535 /** 536 * @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#takePurchaseOrderCancelAction(org.kuali.kfs.module.purap.document.AccountsPayableDocument) 537 */ 538 public void takePurchaseOrderCancelAction(AccountsPayableDocument apDoc) { 539 VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) apDoc; 540 if (cmDocument.isReopenPurchaseOrderIndicator()) { 541 String docType = PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT; 542 purchaseOrderService.createAndRoutePotentialChangeDocument(cmDocument.getPurchaseOrderDocument().getDocumentNumber(), docType, "reopened by Payment Request " + apDoc.getPurapDocumentIdentifier() + "cancel", new ArrayList(), PurapConstants.PurchaseOrderStatuses.PENDING_CLOSE); 543 } 544 } 545 546 /** 547 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#markPaid(org.kuali.kfs.module.purap.document.CreditMemoDocument, 548 * java.sql.Date) 549 */ 550 public void markPaid(VendorCreditMemoDocument cm, Date processDate) { 551 LOG.debug("markPaid() started"); 552 553 cm.setCreditMemoPaidTimestamp(new Timestamp(processDate.getTime())); 554 purapService.saveDocumentNoValidation(cm); 555 } 556 557 /** 558 * @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#poItemEligibleForAp(org.kuali.kfs.module.purap.document.AccountsPayableDocument, org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem) 559 */ 560 public boolean poItemEligibleForAp(AccountsPayableDocument apDoc, PurchaseOrderItem poItem) { 561 // if the po item is not active... skip it 562 if (!poItem.isItemActiveIndicator()) { 563 return false; 564 } 565 566 if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalQuantity().isGreaterThan(KualiDecimal.ZERO)) { 567 return true; 568 } 569 else { 570 BigDecimal unitPrice = (poItem.getItemUnitPrice() == null ? new BigDecimal(0) : poItem.getItemUnitPrice()); 571 if (unitPrice.doubleValue() > poItem.getItemOutstandingEncumberedAmount().doubleValue()) { 572 return true; 573 } 574 } 575 return false; 576 } 577 578 /** 579 * The given document here needs to be a Credit Memo. 580 * 581 * @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#generateGLEntriesCreateAccountsPayableDocument(org.kuali.kfs.module.purap.document.AccountsPayableDocument) 582 */ 583 public void generateGLEntriesCreateAccountsPayableDocument(AccountsPayableDocument apDocument) { 584 VendorCreditMemoDocument creditMemo = (VendorCreditMemoDocument)apDocument; 585 purapGeneralLedgerService.generateEntriesCreateCreditMemo(creditMemo); 586 } 587 588 /** 589 * Records the specified error message into the Log file and throws a runtime exception. 590 * 591 * @param errorMessage the error message to be logged. 592 */ 593 protected void logAndThrowRuntimeException(String errorMessage) { 594 this.logAndThrowRuntimeException(errorMessage, null); 595 } 596 597 /** 598 * Records the specified error message into the Log file and throws the specified runtime exception. 599 * 600 * @param errorMessage the specified error message. 601 * @param e the specified runtime exception. 602 */ 603 protected void logAndThrowRuntimeException(String errorMessage, Exception e) { 604 if (ObjectUtils.isNotNull(e)) { 605 LOG.error(errorMessage, e); 606 throw new RuntimeException(errorMessage, e); 607 } 608 else { 609 LOG.error(errorMessage); 610 throw new RuntimeException(errorMessage); 611 } 612 } 613 614 /** 615 * @see org.kuali.kfs.module.purap.document.service.CreditMemoService#hasActiveCreditMemosForPurchaseOrder(java.lang.Integer) 616 */ 617 public boolean hasActiveCreditMemosForPurchaseOrder(Integer purchaseOrderIdentifier){ 618 619 boolean hasActiveCreditMemos = false; 620 List<String> docNumbers= null; 621 KualiWorkflowDocument workflowDocument = null; 622 623 docNumbers= creditMemoDao.getActiveCreditMemoDocumentNumbersForPurchaseOrder(purchaseOrderIdentifier); 624 625 for (String docNumber : docNumbers) { 626 try{ 627 workflowDocument = workflowDocumentService.createWorkflowDocument(Long.valueOf(docNumber), GlobalVariables.getUserSession().getPerson()); 628 }catch(WorkflowException we){ 629 throw new RuntimeException(we); 630 } 631 632 //if the document is not in a non-active status then return true and stop evaluation 633 if(!(workflowDocument.stateIsCanceled() || 634 workflowDocument.stateIsException() || 635 workflowDocument.stateIsFinal()) ){ 636 hasActiveCreditMemos = true; 637 break; 638 } 639 640 } 641 642 return hasActiveCreditMemos; 643 } 644 645 /** 646 * @see org.kuali.kfs.module.purap.document.service.CreditMemoCreateService#populateDocumentAfterInit(org.kuali.kfs.module.purap.document.CreditMemoDocument) 647 */ 648 public void populateDocumentAfterInit(VendorCreditMemoDocument cmDocument) { 649 650 // make a call to search for expired/closed accounts 651 HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList = accountsPayableService.getExpiredOrClosedAccountList(cmDocument); 652 653 if (cmDocument.isSourceDocumentPaymentRequest()) { 654 populateDocumentFromPreq(cmDocument, expiredOrClosedAccountList); 655 } 656 else if (cmDocument.isSourceDocumentPurchaseOrder()) { 657 populateDocumentFromPO(cmDocument, expiredOrClosedAccountList); 658 } 659 else { 660 populateDocumentFromVendor(cmDocument); 661 } 662 663 populateDocumentDescription(cmDocument); 664 665 // write a note for expired/closed accounts if any exist and add a message stating there were expired/closed accounts at the 666 // top of the document 667 accountsPayableService.generateExpiredOrClosedAccountNote(cmDocument, expiredOrClosedAccountList); 668 669 // set indicator so a message is displayed for accounts that were replaced due to expired/closed status 670 if (ObjectUtils.isNotNull(expiredOrClosedAccountList) && !expiredOrClosedAccountList.isEmpty()) { 671 cmDocument.setContinuationAccountIndicator(true); 672 } 673 674 } 675 676 /** 677 * Populate Credit Memo of type Payment Request. 678 * 679 * @param cmDocument - Credit Memo Document to Populate 680 */ 681 protected void populateDocumentFromPreq(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) { 682 PaymentRequestDocument paymentRequestDocument = paymentRequestService.getPaymentRequestById(cmDocument.getPaymentRequestIdentifier()); 683 cmDocument.getDocumentHeader().setOrganizationDocumentNumber(paymentRequestDocument.getDocumentHeader().getOrganizationDocumentNumber()); 684 cmDocument.setPaymentRequestDocument(paymentRequestDocument); 685 cmDocument.setPurchaseOrderDocument(paymentRequestDocument.getPurchaseOrderDocument()); 686 cmDocument.setUseTaxIndicator(paymentRequestDocument.isUseTaxIndicator()); 687 688 // credit memo address taken directly from payment request 689 cmDocument.setVendorHeaderGeneratedIdentifier(paymentRequestDocument.getVendorHeaderGeneratedIdentifier()); 690 cmDocument.setVendorDetailAssignedIdentifier(paymentRequestDocument.getVendorDetailAssignedIdentifier()); 691 cmDocument.setVendorAddressGeneratedIdentifier(paymentRequestDocument.getVendorAddressGeneratedIdentifier()); 692 cmDocument.setVendorCustomerNumber(paymentRequestDocument.getVendorCustomerNumber()); 693 cmDocument.setVendorName(paymentRequestDocument.getVendorName()); 694 cmDocument.setVendorLine1Address(paymentRequestDocument.getVendorLine1Address()); 695 cmDocument.setVendorLine2Address(paymentRequestDocument.getVendorLine2Address()); 696 cmDocument.setVendorCityName(paymentRequestDocument.getVendorCityName()); 697 cmDocument.setVendorStateCode(paymentRequestDocument.getVendorStateCode()); 698 cmDocument.setVendorPostalCode(paymentRequestDocument.getVendorPostalCode()); 699 cmDocument.setVendorCountryCode(paymentRequestDocument.getVendorCountryCode()); 700 cmDocument.setVendorAttentionName(paymentRequestDocument.getVendorAttentionName()); 701 cmDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(paymentRequestDocument.getAccountsPayablePurchasingDocumentLinkIdentifier()); 702 703 // prep the item lines (also collect warnings for later display) this is only done on paymentRequest 704 purapAccountingService.convertMoneyToPercent(paymentRequestDocument); 705 populateItemLinesFromPreq(cmDocument, expiredOrClosedAccountList); 706 } 707 708 /** 709 * Populates the credit memo items from the payment request items. 710 * 711 * @param cmDocument - Credit Memo Document to Populate 712 */ 713 protected void populateItemLinesFromPreq(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) { 714 PaymentRequestDocument preqDocument = cmDocument.getPaymentRequestDocument(); 715 716 for (PaymentRequestItem preqItemToTemplate : (List<PaymentRequestItem>) preqDocument.getItems()) { 717 if (preqItemToTemplate.getItemType().isLineItemIndicator() && ((preqItemToTemplate.getItemType().isQuantityBasedGeneralLedgerIndicator() && preqItemToTemplate.getItemQuantity().isNonZero()) 718 || (preqItemToTemplate.getItemType().isAmountBasedGeneralLedgerIndicator() && preqItemToTemplate.getTotalAmount().isNonZero()))) { 719 cmDocument.getItems().add(new CreditMemoItem(cmDocument, preqItemToTemplate, preqItemToTemplate.getPurchaseOrderItem(), expiredOrClosedAccountList)); 720 } 721 } 722 723 // add below the line items 724 purapService.addBelowLineItems(cmDocument); 725 726 cmDocument.fixItemReferences(); 727 } 728 729 /** 730 * Populate Credit Memo of type Purchase Order. 731 * 732 * @param cmDocument - Credit Memo Document to Populate 733 */ 734 protected void populateDocumentFromPO(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) { 735 PurchaseOrderDocument purchaseOrderDocument = purchaseOrderService.getCurrentPurchaseOrder(cmDocument.getPurchaseOrderIdentifier()); 736 cmDocument.setPurchaseOrderDocument(purchaseOrderDocument); 737 cmDocument.getDocumentHeader().setOrganizationDocumentNumber(purchaseOrderDocument.getDocumentHeader().getOrganizationDocumentNumber()); 738 cmDocument.setUseTaxIndicator(cmDocument.isUseTaxIndicator()); 739 740 cmDocument.setVendorHeaderGeneratedIdentifier(purchaseOrderDocument.getVendorHeaderGeneratedIdentifier()); 741 cmDocument.setVendorDetailAssignedIdentifier(purchaseOrderDocument.getVendorDetailAssignedIdentifier()); 742 cmDocument.setVendorCustomerNumber(purchaseOrderDocument.getVendorCustomerNumber()); 743 cmDocument.setVendorName(purchaseOrderDocument.getVendorName()); 744 cmDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(purchaseOrderDocument.getAccountsPayablePurchasingDocumentLinkIdentifier()); 745 746 // populate cm vendor address with the default remit address type for the vendor if found 747 String userCampus = GlobalVariables.getUserSession().getPerson().getCampusCode(); 748 VendorAddress vendorAddress = vendorService.getVendorDefaultAddress(purchaseOrderDocument.getVendorHeaderGeneratedIdentifier(), purchaseOrderDocument.getVendorDetailAssignedIdentifier(), VendorConstants.AddressTypes.REMIT, userCampus); 749 if (vendorAddress != null) { 750 cmDocument.templateVendorAddress(vendorAddress); 751 cmDocument.setVendorAddressGeneratedIdentifier(vendorAddress.getVendorAddressGeneratedIdentifier()); 752 cmDocument.setVendorAttentionName(StringUtils.defaultString(vendorAddress.getVendorAttentionName())); 753 } 754 else { 755 // set address from PO 756 cmDocument.setVendorAddressGeneratedIdentifier(purchaseOrderDocument.getVendorAddressGeneratedIdentifier()); 757 cmDocument.setVendorLine1Address(purchaseOrderDocument.getVendorLine1Address()); 758 cmDocument.setVendorLine2Address(purchaseOrderDocument.getVendorLine2Address()); 759 cmDocument.setVendorCityName(purchaseOrderDocument.getVendorCityName()); 760 cmDocument.setVendorStateCode(purchaseOrderDocument.getVendorStateCode()); 761 cmDocument.setVendorPostalCode(purchaseOrderDocument.getVendorPostalCode()); 762 cmDocument.setVendorCountryCode(purchaseOrderDocument.getVendorCountryCode()); 763 764 boolean blankAttentionLine = StringUtils.equalsIgnoreCase("Y",SpringContext.getBean(KualiConfigurationService.class).getParameterValue(PurapConstants.PURAP_NAMESPACE, "Document", PurapParameterConstants.BLANK_ATTENTION_LINE_FOR_PO_TYPE_ADDRESS)); 765 if (blankAttentionLine){ 766 cmDocument.setVendorAttentionName(StringUtils.EMPTY); 767 }else{ 768 cmDocument.setVendorAttentionName(StringUtils.defaultString(purchaseOrderDocument.getVendorAttentionName())); 769 } 770 } 771 772 populateItemLinesFromPO(cmDocument, expiredOrClosedAccountList); 773 } 774 775 /** 776 * Populates the credit memo items from the payment request items. 777 * 778 * @param cmDocument - Credit Memo Document to Populate 779 */ 780 protected void populateItemLinesFromPO(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) { 781 List<PurchaseOrderItem> invoicedItems = getPOInvoicedItems(cmDocument.getPurchaseOrderDocument()); 782 for (PurchaseOrderItem poItem : invoicedItems) { 783 if ((poItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalQuantity().isNonZero()) 784 || (poItem.getItemType().isAmountBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalAmount().isNonZero())) { 785 CreditMemoItem creditMemoItem = new CreditMemoItem(cmDocument, poItem, expiredOrClosedAccountList); 786 cmDocument.getItems().add(creditMemoItem); 787 PurchasingCapitalAssetItem purchasingCAMSItem = cmDocument.getPurchaseOrderDocument().getPurchasingCapitalAssetItemByItemIdentifier(poItem.getItemIdentifier()); 788 if (purchasingCAMSItem != null) { 789 creditMemoItem.setCapitalAssetTransactionTypeCode(purchasingCAMSItem.getCapitalAssetTransactionTypeCode()); 790 } 791 } 792 } 793 794 // add below the line items 795 purapService.addBelowLineItems(cmDocument); 796 797 cmDocument.fixItemReferences(); 798 } 799 800 /** 801 * Populate Credit Memo of type Vendor. 802 * 803 * @param cmDocument - Credit Memo Document to Populate 804 */ 805 protected void populateDocumentFromVendor(VendorCreditMemoDocument cmDocument) { 806 Integer vendorHeaderId = VendorUtils.getVendorHeaderId(cmDocument.getVendorNumber()); 807 Integer vendorDetailId = VendorUtils.getVendorDetailId(cmDocument.getVendorNumber()); 808 809 VendorDetail vendorDetail = vendorService.getVendorDetail(vendorHeaderId, vendorDetailId); 810 cmDocument.setVendorDetail(vendorDetail); 811 812 cmDocument.setVendorHeaderGeneratedIdentifier(vendorDetail.getVendorHeaderGeneratedIdentifier()); 813 cmDocument.setVendorDetailAssignedIdentifier(vendorDetail.getVendorDetailAssignedIdentifier()); 814 cmDocument.setVendorCustomerNumber(vendorDetail.getVendorNumber()); 815 cmDocument.setVendorName(vendorDetail.getVendorName()); 816 817 818 // credit memo type vendor uses the default remit type address for the vendor if found 819 String userCampus = GlobalVariables.getUserSession().getPerson().getCampusCode(); 820 VendorAddress vendorAddress = vendorService.getVendorDefaultAddress(vendorHeaderId, vendorDetailId, VendorConstants.AddressTypes.REMIT, userCampus); 821 if (vendorAddress == null) { 822 // pick up the default vendor po address type 823 vendorAddress = vendorService.getVendorDefaultAddress(vendorHeaderId, vendorDetailId, VendorConstants.AddressTypes.PURCHASE_ORDER, userCampus); 824 } 825 826 cmDocument.setVendorAddressGeneratedIdentifier(vendorAddress.getVendorAddressGeneratedIdentifier()); 827 cmDocument.templateVendorAddress(vendorAddress); 828 829 // add below the line items 830 purapService.addBelowLineItems(cmDocument); 831 } 832 833 /** 834 * Defaults the document description based on the credit memo source type. 835 * 836 * @param cmDocument - Credit Memo Document to Populate 837 */ 838 protected void populateDocumentDescription(VendorCreditMemoDocument cmDocument) { 839 String description = ""; 840 if (cmDocument.isSourceVendor()) { 841 description = "Vendor: " + cmDocument.getVendorName(); 842 } 843 else { 844 description = "PO: " + cmDocument.getPurchaseOrderDocument().getPurapDocumentIdentifier() + " Vendor: " + cmDocument.getVendorName(); 845 } 846 847 // trim description if longer than whats specified in the data dictionary 848 int noteTextMaxLength = dataDictionaryService.getAttributeMaxLength(DocumentHeader.class, KNSPropertyConstants.DOCUMENT_DESCRIPTION).intValue(); 849 if (noteTextMaxLength < description.length()) { 850 description = description.substring(0, noteTextMaxLength); 851 } 852 853 cmDocument.getDocumentHeader().setDocumentDescription(description); 854 } 855 856 } 857