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