View Javadoc
1   /*
2    * Copyright 2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.module.purap.document.service.impl;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.commons.lang.text.StrBuilder;
21  import org.kuali.ole.module.purap.PurapConstants;
22  import org.kuali.ole.module.purap.PurapConstants.CreditMemoStatuses;
23  import org.kuali.ole.module.purap.PurapKeyConstants;
24  import org.kuali.ole.module.purap.PurapParameterConstants;
25  import org.kuali.ole.module.purap.PurapPropertyConstants;
26  import org.kuali.ole.module.purap.businessobject.*;
27  import org.kuali.ole.module.purap.document.*;
28  import org.kuali.ole.module.purap.document.dataaccess.CreditMemoDao;
29  import org.kuali.ole.module.purap.document.service.*;
30  import org.kuali.ole.module.purap.document.validation.event.AttributedContinuePurapEvent;
31  import org.kuali.ole.module.purap.service.PurapAccountingService;
32  import org.kuali.ole.module.purap.service.PurapGeneralLedgerService;
33  import org.kuali.ole.module.purap.util.ExpiredOrClosedAccountEntry;
34  import org.kuali.ole.module.purap.util.VendorGroupingHelper;
35  import org.kuali.ole.select.document.OleVendorCreditMemoDocument;
36  import org.kuali.ole.sys.businessobject.Bank;
37  import org.kuali.ole.sys.businessobject.SourceAccountingLine;
38  import org.kuali.ole.sys.context.SpringContext;
39  import org.kuali.ole.sys.service.BankService;
40  import org.kuali.ole.vnd.VendorConstants;
41  import org.kuali.ole.vnd.VendorUtils;
42  import org.kuali.ole.vnd.businessobject.VendorAddress;
43  import org.kuali.ole.vnd.businessobject.VendorDetail;
44  import org.kuali.ole.vnd.document.service.VendorService;
45  import org.kuali.rice.core.api.config.property.ConfigurationService;
46  import org.kuali.rice.core.api.util.type.KualiDecimal;
47  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
48  import org.kuali.rice.kew.api.KewApiServiceLocator;
49  import org.kuali.rice.kew.api.WorkflowDocument;
50  import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
51  import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
52  import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
53  import org.kuali.rice.kew.api.exception.WorkflowException;
54  import org.kuali.rice.kim.api.identity.Person;
55  import org.kuali.rice.kns.service.DataDictionaryService;
56  import org.kuali.rice.krad.bo.DocumentHeader;
57  import org.kuali.rice.krad.bo.Note;
58  import org.kuali.rice.krad.exception.ValidationException;
59  import org.kuali.rice.krad.service.DocumentService;
60  import org.kuali.rice.krad.service.NoteService;
61  import org.kuali.rice.krad.util.GlobalVariables;
62  import org.kuali.rice.krad.util.KRADPropertyConstants;
63  import org.kuali.rice.krad.util.ObjectUtils;
64  import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
65  import org.springframework.transaction.annotation.Transactional;
66  
67  import java.math.BigDecimal;
68  import java.sql.Date;
69  import java.sql.Timestamp;
70  import java.util.*;
71  
72  /**
73   * Provides services to support the creation of a Credit Memo Document.
74   */
75  @Transactional
76  public class CreditMemoServiceImpl implements CreditMemoService {
77      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CreditMemoServiceImpl.class);
78  
79      private AccountsPayableService accountsPayableService;
80      private CreditMemoDao creditMemoDao;
81      private DataDictionaryService dataDictionaryService;
82      private DocumentService documentService;
83      private ConfigurationService kualiConfigurationService;
84      private NoteService noteService;
85      private PaymentRequestService paymentRequestService;
86      private PurapAccountingService purapAccountingService;
87      private PurapGeneralLedgerService purapGeneralLedgerService;
88      private PurapService purapService;
89      private PurchaseOrderService purchaseOrderService;
90      private VendorService vendorService;
91      private WorkflowDocumentService workflowDocumentService;
92  
93  
94      public void setAccountsPayableService(AccountsPayableService accountsPayableService) {
95          this.accountsPayableService = accountsPayableService;
96      }
97  
98      public void setCreditMemoDao(CreditMemoDao creditMemoDao) {
99          this.creditMemoDao = creditMemoDao;
100     }
101 
102     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
103         this.dataDictionaryService = dataDictionaryService;
104     }
105 
106     public void setDocumentService(DocumentService documentService) {
107         this.documentService = documentService;
108     }
109 
110     public void setConfigurationService(ConfigurationService kualiConfigurationService) {
111         this.kualiConfigurationService = kualiConfigurationService;
112     }
113 
114     public void setNoteService(NoteService noteService) {
115         this.noteService = noteService;
116     }
117 
118     public void setPaymentRequestService(PaymentRequestService paymentRequestService) {
119         this.paymentRequestService = paymentRequestService;
120     }
121 
122     public void setPurapAccountingService(PurapAccountingService purapAccountingService) {
123         this.purapAccountingService = purapAccountingService;
124     }
125 
126     public void setPurapGeneralLedgerService(PurapGeneralLedgerService purapGeneralLedgerService) {
127         this.purapGeneralLedgerService = purapGeneralLedgerService;
128     }
129 
130     public void setPurapService(PurapService purapService) {
131         this.purapService = purapService;
132     }
133 
134     public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
135         this.purchaseOrderService = purchaseOrderService;
136     }
137 
138     public void setVendorService(VendorService vendorService) {
139         this.vendorService = vendorService;
140     }
141 
142     public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
143         this.workflowDocumentService = workflowDocumentService;
144     }
145 
146 
147     /**
148      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#getCreditMemosToExtract(java.lang.String)
149      */
150     @Override
151     public List<VendorCreditMemoDocument> getCreditMemosToExtract(String chartCode) {
152         LOG.debug("getCreditMemosToExtract() started");
153 
154         List<VendorCreditMemoDocument> docs = creditMemoDao.getCreditMemosToExtract(chartCode);
155         docs = (List<VendorCreditMemoDocument>) filterCreditMemoByAppDocStatus(docs, CreditMemoStatuses.STATUSES_ALLOWED_FOR_EXTRACTION);
156 
157         return docs;
158 
159     }
160 
161     @Override
162     public Collection<VendorCreditMemoDocument> getCreditMemosToExtractByVendor(String chartCode, VendorGroupingHelper vendor) {
163         LOG.debug("getCreditMemosToExtractByVendor() started");
164 
165         Collection<VendorCreditMemoDocument> docs = creditMemoDao.getCreditMemosToExtractByVendor(chartCode, vendor);
166         docs = filterCreditMemoByAppDocStatus(docs, CreditMemoStatuses.STATUSES_ALLOWED_FOR_EXTRACTION);
167         return docs;
168 
169     }
170 
171     @Override
172     public Set<VendorGroupingHelper> getVendorsOnCreditMemosToExtract(String chartCode) {
173         LOG.debug("getVendorsOnCreditMemosToExtract() started");
174         HashSet<VendorGroupingHelper> vendors = new HashSet<VendorGroupingHelper>();
175 
176         List<VendorCreditMemoDocument> docs = this.getCreditMemosToExtract(chartCode);
177 
178         for(VendorCreditMemoDocument vendorCreditMemoDocument : docs) {
179             vendors.add(new VendorGroupingHelper(vendorCreditMemoDocument));
180         }
181 
182 //        while ( docs.hasNext() ) {
183         //           VendorCreditMemoDocument doc = docs.next();
184         //           vendors.add( new VendorGroupingHelper( doc ) );
185         //      }
186         return vendors;
187     }
188 
189     /**
190      * Since PaymentRequest does not have the app doc status, perform an additional lookup
191      * through doc search by using list of PaymentRequest Doc numbers.  Query appDocStatus
192      * from workflow document and filter against the provided status
193      * <p/>
194      * DocumentSearch allows for multiple docNumber lookup by docId|docId|docId conversion
195      *
196      * @param lookupDocNumbers
197      * @param appDocStatus
198      * @return
199      */
200     private List<String> filterCreditMemoByAppDocStatus(List<String> lookupDocNumbers, String... appDocStatus) {
201         boolean valid = false;
202 
203         final String DOC_NUM_DELIM = "|";
204         StrBuilder routerHeaderIdBuilder = new StrBuilder().appendWithSeparators(lookupDocNumbers, DOC_NUM_DELIM);
205 
206         List<String> creditMemoDocNumbers = new ArrayList<String>();
207 
208         DocumentSearchCriteria.Builder documentSearchCriteriaDTO = DocumentSearchCriteria.Builder.create();
209         documentSearchCriteriaDTO.setDocumentId(routerHeaderIdBuilder.toString());
210         documentSearchCriteriaDTO.setDocumentTypeName(PurapConstants.PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT);
211 
212         DocumentSearchResults creditMemoDocumentsList = KewApiServiceLocator.getWorkflowDocumentService().documentSearch(
213                 GlobalVariables.getUserSession().getPrincipalId(), documentSearchCriteriaDTO.build());
214 
215         for (DocumentSearchResult creditMemoDocument : creditMemoDocumentsList.getSearchResults()) {
216             ///use the appDocStatus from the KeyValueDTO result to look up custom status
217             if (Arrays.asList(appDocStatus).contains(creditMemoDocument.getDocument().getApplicationDocumentStatus())) {
218                 //found the matching status, retrieve the routeHeaderId and add to the list
219                 creditMemoDocNumbers.add(creditMemoDocument.getDocument().getDocumentId());
220             }
221         }
222 
223         return creditMemoDocNumbers;
224     }
225 
226     /**
227      * Wrapper class to the filterPaymentRequestByAppDocStatus
228      * <p/>
229      * This class first extract the payment request document numbers from the Payment Request Collections,
230      * then perform the filterPaymentRequestByAppDocStatus function.  Base on the filtered payment request
231      * doc number, reconstruct the filtered Payment Request Collection
232      *
233      * @param paymentRequestDocuments
234      * @param appDocStatus
235      * @return
236      */
237     private Collection<VendorCreditMemoDocument> filterCreditMemoByAppDocStatus(Collection<VendorCreditMemoDocument> creditMemoDocuments, String... appDocStatus) {
238         List<String> creditMemoDocNumbers = new ArrayList<String>();
239         for (VendorCreditMemoDocument creditMemo : creditMemoDocuments) {
240             creditMemoDocNumbers.add(creditMemo.getDocumentNumber());
241         }
242 
243         List<String> filteredCreditMemoDocNumbers = filterCreditMemoByAppDocStatus(creditMemoDocNumbers, appDocStatus);
244 
245         Collection<VendorCreditMemoDocument> filteredCreditMemoDocuments = new ArrayList<VendorCreditMemoDocument>();
246         //add to filtered collection if it is in the filtered payment request doc number list
247         for (VendorCreditMemoDocument creditMemo : creditMemoDocuments) {
248             if (filteredCreditMemoDocNumbers.contains(creditMemo.getDocumentNumber())) {
249                 filteredCreditMemoDocuments.add(creditMemo);
250             }
251         }
252         return filteredCreditMemoDocuments;
253     }
254 
255 
256     /**
257      * Wrapper class to the filterPaymentRequestByAppDocStatus (Collection<PaymentRequestDocument>)
258      * <p/>
259      * This class first construct the Payment Request Collection from the iterator, and then process through
260      * filterPaymentRequestByAppDocStatus
261      *
262      * @param paymentRequestDocuments
263      * @param appDocStatus
264      * @return
265      */
266     private Iterator<VendorCreditMemoDocument> filterCreditMemoByAppDocStatus(Iterator<VendorCreditMemoDocument> creditMemoIterator, String... appDocStatus) {
267         Collection<VendorCreditMemoDocument> creditMemoDocuments = new ArrayList<VendorCreditMemoDocument>();
268         for (; creditMemoIterator.hasNext(); ) {
269             creditMemoDocuments.add(creditMemoIterator.next());
270         }
271 
272         return filterCreditMemoByAppDocStatus(creditMemoDocuments, appDocStatus).iterator();
273     }
274 
275     /**
276      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#creditMemoDuplicateMessages(org.kuali.ole.module.purap.document.CreditMemoDocument)
277      */
278     @Override
279     public String creditMemoDuplicateMessages(VendorCreditMemoDocument cmDocument) {
280         String duplicateMessage = null;
281 
282         String vendorNumber = cmDocument.getVendorNumber();
283         if (StringUtils.isEmpty(vendorNumber)) {
284             PurchasingAccountsPayableDocument sourceDocument = cmDocument.getPurApSourceDocumentIfPossible();
285             if (ObjectUtils.isNotNull(sourceDocument)) {
286                 vendorNumber = sourceDocument.getVendorNumber();
287             }
288         }
289 
290         if (StringUtils.isNotEmpty(vendorNumber)) {
291             // check for existence of another credit memo with the same vendor and vendor credit memo number
292             if (creditMemoDao.duplicateExists(VendorUtils.getVendorHeaderId(vendorNumber), VendorUtils.getVendorDetailId(vendorNumber), cmDocument.getCreditMemoNumber())) {
293                 duplicateMessage = kualiConfigurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_CREDIT_MEMO_VENDOR_NUMBER);
294             }
295 
296             // check for existence of another credit memo with the same vendor and credit memo date
297             if (creditMemoDao.duplicateExists(VendorUtils.getVendorHeaderId(vendorNumber), VendorUtils.getVendorDetailId(vendorNumber), cmDocument.getCreditMemoDate(), cmDocument.getCreditMemoAmount())) {
298                 duplicateMessage = kualiConfigurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_CREDIT_MEMO_VENDOR_NUMBER_DATE_AMOUNT);
299             }
300         }
301 
302         return duplicateMessage;
303     }
304 
305     /**
306      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#getPOInvoicedItems(org.kuali.ole.module.purap.document.PurchaseOrderDocument)
307      */
308     @Override
309     public List<PurchaseOrderItem> getPOInvoicedItems(PurchaseOrderDocument poDocument) {
310         List<PurchaseOrderItem> invoicedItems = new ArrayList<PurchaseOrderItem>();
311 
312         for (Iterator iter = poDocument.getItems().iterator(); iter.hasNext(); ) {
313             PurchaseOrderItem poItem = (PurchaseOrderItem) iter.next();
314 
315             // only items of type above the line can be considered for being invoiced
316             if (poItem.getItemType().isAdditionalChargeIndicator()) {
317                 continue;
318             }
319 
320             if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalQuantity().isGreaterThan(KualiDecimal.ZERO)) {
321                 invoicedItems.add(poItem);
322             } else {
323                 BigDecimal unitPrice = (poItem.getItemUnitPrice() == null ? new BigDecimal(0) : poItem.getItemUnitPrice());
324                 if (unitPrice.doubleValue() > poItem.getItemOutstandingEncumberedAmount().doubleValue()) {
325                     invoicedItems.add(poItem);
326                 }
327             }
328         }
329 
330         return invoicedItems;
331     }
332 
333 
334     /**
335      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#calculateCreditMemo(org.kuali.ole.module.purap.document.CreditMemoDocument)
336      */
337     @Override
338     public void calculateCreditMemo(VendorCreditMemoDocument cmDocument) {
339 
340         cmDocument.updateExtendedPriceOnItems();
341 
342         for (CreditMemoItem item : (List<CreditMemoItem>) cmDocument.getItems()) {
343             // make sure restocking fee is negative
344             if (StringUtils.equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_RESTCK_FEE_CODE, item.getItemTypeCode())) {
345                 if (item.getItemUnitPrice() != null) {
346                     item.setExtendedPrice(item.getExtendedPrice().abs().negated());
347                     item.setItemUnitPrice(item.getItemUnitPrice().abs().negate());
348                 }
349             }
350         }
351 
352         //calculate tax if cm not based on vendor
353         if (cmDocument.isSourceVendor() == false) {
354             purapService.calculateTax(cmDocument);
355         }
356 
357         // proration
358         if (cmDocument.isSourceVendor()) {
359             // no proration on vendor
360             return;
361         }
362 
363         for (CreditMemoItem item : (List<CreditMemoItem>) cmDocument.getItems()) {
364 
365             // skip above the line
366             if (item.getItemType().isLineItemIndicator()) {
367                 continue;
368             }
369 
370             if ((item.getSourceAccountingLines().isEmpty()) && (ObjectUtils.isNotNull(item.getExtendedPrice())) && (KualiDecimal.ZERO.compareTo(item.getExtendedPrice()) != 0)) {
371 
372                 KualiDecimal totalAmount = KualiDecimal.ZERO;
373                 List<PurApAccountingLine> distributedAccounts = null;
374                 List<SourceAccountingLine> summaryAccounts = null;
375 
376                 totalAmount = cmDocument.getPurApSourceDocumentIfPossible().getTotalDollarAmount();
377                 // this should do nothing on preq which is fine
378                 purapAccountingService.updateAccountAmounts(cmDocument.getPurApSourceDocumentIfPossible());
379                 summaryAccounts = purapAccountingService.generateSummary(cmDocument.getPurApSourceDocumentIfPossible().getItems());
380                 distributedAccounts = purapAccountingService.generateAccountDistributionForProration(summaryAccounts, totalAmount, PurapConstants.PRORATION_SCALE, CreditMemoAccount.class);
381 
382                 if (CollectionUtils.isNotEmpty(distributedAccounts) && CollectionUtils.isEmpty(item.getSourceAccountingLines())) {
383                     item.setSourceAccountingLines(distributedAccounts);
384                 }
385             }
386         }
387         // end proration
388     }
389 
390     /**
391      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#getCreditMemoByDocumentNumber(java.lang.String)
392      */
393     @Override
394     public VendorCreditMemoDocument getCreditMemoByDocumentNumber(String documentNumber) {
395         LOG.debug("getCreditMemoByDocumentNumber() started");
396 
397         if (ObjectUtils.isNotNull(documentNumber)) {
398             try {
399                 VendorCreditMemoDocument doc = (VendorCreditMemoDocument) documentService.getByDocumentHeaderId(documentNumber);
400                 return doc;
401             } catch (WorkflowException e) {
402                 String errorMessage = "Error getting credit memo document from document service";
403                 LOG.error("getCreditMemoByDocumentNumber() " + errorMessage, e);
404                 throw new RuntimeException(errorMessage, e);
405             }
406         }
407         return null;
408     }
409 
410     /**
411      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#getCreditMemoDocumentById(java.lang.Integer)
412      */
413     @Override
414     public VendorCreditMemoDocument getCreditMemoDocumentById(Integer purchasingDocumentIdentifier) {
415         return getCreditMemoByDocumentNumber(creditMemoDao.getDocumentNumberByCreditMemoId(purchasingDocumentIdentifier));
416     }
417 
418     /**
419      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#saveDocument(org.kuali.ole.module.purap.document.CreditMemoDocument)
420      */
421     @Override
422     public void populateAndSaveCreditMemo(VendorCreditMemoDocument document) {
423         try {
424             //   document.setApplicationDocumentStatus(PurapConstants.CreditMemoStatuses.APPDOC_IN_PROCESS);
425             //   document.getDocumentHeader().getWorkflowDocument().setApplicationDocumentStatus(PurapConstants.CreditMemoStatuses.APPDOC_IN_PROCESS);
426             document.updateAndSaveAppDocStatus(PurapConstants.CreditMemoStatuses.APPDOC_IN_PROCESS);
427 
428             if (document.isSourceDocumentPaymentRequest()) {
429                 document.setBankCode(document.getPaymentRequestDocument().getBankCode());
430                 document.setBank(document.getPaymentRequestDocument().getBank());
431             } else {
432                 // set bank code to default bank code in the system parameter
433                 Bank defaultBank = SpringContext.getBean(BankService.class).getDefaultBankByDocType(document.getClass());
434                 if (defaultBank != null) {
435                     document.setBankCode(defaultBank.getBankCode());
436                     document.setBank(defaultBank);
437                 }
438             }
439 
440             documentService.saveDocument(document, AttributedContinuePurapEvent.class);
441         } catch (ValidationException ve) {
442             // set the status back to initiate
443             try {
444                 document.updateAndSaveAppDocStatus(PurapConstants.CreditMemoStatuses.APPDOC_INITIATE);
445             } catch (WorkflowException workflowException) {
446 
447             }
448         } catch (WorkflowException we) {
449             // set the status back to initiate
450             try {
451                 document.updateAndSaveAppDocStatus(PurapConstants.CreditMemoStatuses.APPDOC_INITIATE);
452             } catch (WorkflowException workflowException) {
453 
454             }
455             String errorMsg = "Error saving document # " + document.getDocumentNumber() + " " + we.getMessage();
456             LOG.error(errorMsg, we);
457             throw new RuntimeException(errorMsg, we);
458         }
459     }
460 
461     /**
462      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#reopenClosedPO(org.kuali.ole.module.purap.document.CreditMemoDocument)
463      */
464     @Override
465     public void reopenClosedPO(VendorCreditMemoDocument cmDocument) {
466         // reopen PO if closed
467         Integer purchaseOrderDocumentId = cmDocument.getPurchaseOrderIdentifier();
468         if (cmDocument.isSourceDocumentPaymentRequest() && ObjectUtils.isNull(purchaseOrderDocumentId)) {
469             PaymentRequestDocument paymentRequestDocument = paymentRequestService.getPaymentRequestById(cmDocument.getPaymentRequestIdentifier());
470             purchaseOrderDocumentId = paymentRequestDocument.getPurchaseOrderIdentifier();
471         }
472         // if we found a valid po id number then check it for reopening
473         if (ObjectUtils.isNotNull(purchaseOrderDocumentId)) {
474             PurchaseOrderDocument purchaseOrderDocument = purchaseOrderService.getCurrentPurchaseOrder(purchaseOrderDocumentId);
475             // only reopen if the po is not null, it does not have a pending change already scheduled, and it is in closed status
476             if (ObjectUtils.isNotNull(purchaseOrderDocument) && (!purchaseOrderDocument.isPendingActionIndicator()) && PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED.equals(purchaseOrderDocument.getApplicationDocumentStatus())) {
477 
478             }
479         }
480     }
481 
482     /**
483      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#addHoldOnPaymentRequest(org.kuali.ole.module.purap.document.CreditMemoDocument,
484      *      java.lang.String)
485      */
486     @Override
487     public VendorCreditMemoDocument addHoldOnCreditMemo(VendorCreditMemoDocument cmDocument, String note) throws Exception {
488         // save the note
489         Note noteObj = documentService.createNoteFromDocument(cmDocument, note);
490         cmDocument.addNote(noteObj);
491         noteService.save(noteObj);
492 
493         // retrieve and save with hold indicator set to true
494         VendorCreditMemoDocument cmDoc = getCreditMemoDocumentById(cmDocument.getPurapDocumentIdentifier());
495         cmDoc.setHoldIndicator(true);
496         cmDoc.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
497         purapService.saveDocumentNoValidation(cmDoc);
498 
499         // must also save it on the incoming document
500         cmDocument.setHoldIndicator(true);
501         cmDocument.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
502 
503         return cmDoc;
504     }
505 
506     /**
507      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#removeHoldOnCreditMemo(org.kuali.ole.module.purap.document.CreditMemoDocument,
508      *      java.lang.String)
509      */
510     @Override
511     public VendorCreditMemoDocument removeHoldOnCreditMemo(VendorCreditMemoDocument cmDocument, String note) throws Exception {
512         // save the note
513         Note noteObj = documentService.createNoteFromDocument(cmDocument, note);
514         cmDocument.addNote(noteObj);
515         noteService.save(noteObj);
516 
517         // retrieve and save with hold indicator set to false
518         VendorCreditMemoDocument cmDoc = getCreditMemoDocumentById(cmDocument.getPurapDocumentIdentifier());
519         cmDoc.setHoldIndicator(false);
520         cmDoc.setLastActionPerformedByPersonId(null);
521         purapService.saveDocumentNoValidation(cmDoc);
522 
523         // must also save it on the incoming document
524         cmDocument.setHoldIndicator(false);
525         cmDocument.setLastActionPerformedByPersonId(null);
526 
527         return cmDoc;
528     }
529 
530     /**
531      * @see org.kuali.ole.module.purap.document.service.AccountsPayableDocumentSpecificService#updateStatusByNode(java.lang.String, org.kuali.ole.module.purap.document.AccountsPayableDocument)
532      */
533     @Override
534     public String updateStatusByNode(String currentNodeName, AccountsPayableDocument apDoc) {
535         return updateStatusByNode(currentNodeName, (VendorCreditMemoDocument) apDoc);
536     }
537 
538     /**
539      * Updates the status of a credit memo document, currently this is used by the cancel action
540      *
541      * @param currentNodeName The string representing the current node to be used to obtain the canceled status code.
542      * @param cmDoc           The credit memo document to be updated.
543      * @return The string representing the canceledStatusCode, if empty it is assumed to be not from workflow.
544      */
545     protected String updateStatusByNode(String currentNodeName, VendorCreditMemoDocument cmDoc) {
546         // update the status on the document
547 
548         String cancelledStatusCode = "";
549         if (StringUtils.isEmpty(currentNodeName)) {
550             cancelledStatusCode = PurapConstants.CreditMemoStatuses.APPDOC_CANCELLED_POST_AP_APPROVE;
551         } else {
552             cancelledStatusCode = CreditMemoStatuses.getCreditMemoAppDocDisapproveStatuses().get(currentNodeName);
553         }
554 
555         if (StringUtils.isNotBlank(cancelledStatusCode)) {
556             try {
557                 cmDoc.updateAndSaveAppDocStatus(cancelledStatusCode);
558             } catch (WorkflowException we) {
559                 throw new RuntimeException("Unable to save the workflow document with document id: " + cmDoc.getDocumentNumber());
560             }
561             purapService.saveDocumentNoValidation(cmDoc);
562             return cancelledStatusCode;
563         } else {
564             logAndThrowRuntimeException("No status found to set for document being disapproved in node '" + currentNodeName + "'");
565         }
566         return cancelledStatusCode;
567     }
568 
569     /**
570      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#cancelExtractedCreditMemo(org.kuali.ole.module.purap.document.CreditMemoDocument,
571      *      java.lang.String)
572      */
573     @Override
574     public void cancelExtractedCreditMemo(VendorCreditMemoDocument cmDocument, String note) {
575         LOG.debug("cancelExtractedCreditMemo() started");
576         if (CreditMemoStatuses.CANCELLED_STATUSES.contains(cmDocument.getApplicationDocumentStatus())) {
577             LOG.debug("cancelExtractedCreditMemo() ended");
578             return;
579         }
580 
581         try {
582             Note noteObj = documentService.createNoteFromDocument(cmDocument, note);
583             cmDocument.addNote(noteObj);
584         } catch (Exception e) {
585             throw new RuntimeException(e.getMessage());
586         }
587 
588         accountsPayableService.cancelAccountsPayableDocument(cmDocument, "");
589         if (LOG.isDebugEnabled()) {
590             LOG.debug("cancelExtractedCreditMemo() CM " + cmDocument.getPurapDocumentIdentifier() + " Cancelled Without Workflow");
591         }
592         LOG.debug("cancelExtractedCreditMemo() ended");
593 
594     }
595 
596     /**
597      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#resetExtractedCreditMemo(org.kuali.ole.module.purap.document.CreditMemoDocument,
598      *      java.lang.String)
599      */
600     @Override
601     public void resetExtractedCreditMemo(VendorCreditMemoDocument cmDocument, String note) {
602         LOG.debug("resetExtractedCreditMemo() started");
603         if (CreditMemoStatuses.CANCELLED_STATUSES.contains(cmDocument.getApplicationDocumentStatus())) {
604             LOG.debug("resetExtractedCreditMemo() ended");
605             return;
606         }
607         cmDocument.setExtractedTimestamp(null);
608         cmDocument.setCreditMemoPaidTimestamp(null);
609 
610         Note noteObj;
611         try {
612             noteObj = documentService.createNoteFromDocument(cmDocument, note);
613             cmDocument.addNote(noteObj);
614         } catch (Exception e) {
615             throw new RuntimeException(e.getMessage());
616         }
617         purapService.saveDocumentNoValidation(cmDocument);
618 
619         if (LOG.isDebugEnabled()) {
620             LOG.debug("resetExtractedCreditMemo() CM " + cmDocument.getPurapDocumentIdentifier() + " Cancelled Without Workflow");
621         }
622         LOG.debug("resetExtractedCreditMemo() ended");
623     }
624 
625     /**
626      * @see org.kuali.ole.module.purap.document.service.AccountsPayableDocumentSpecificService#shouldPurchaseOrderBeReversed(org.kuali.ole.module.purap.document.AccountsPayableDocument)
627      */
628     @Override
629     public boolean shouldPurchaseOrderBeReversed(AccountsPayableDocument apDoc) {
630         // always return false, never reverse
631         return false;
632     }
633 
634     /**
635      * @see org.kuali.ole.module.purap.document.service.AccountsPayableDocumentSpecificService#getPersonForCancel(org.kuali.ole.module.purap.document.AccountsPayableDocument)
636      */
637     @Override
638     public Person getPersonForCancel(AccountsPayableDocument apDoc) {
639         // return null, since superuser is fine for CM
640         return null;
641     }
642 
643     /**
644      * @see org.kuali.ole.module.purap.document.service.AccountsPayableDocumentSpecificService#takePurchaseOrderCancelAction(org.kuali.ole.module.purap.document.AccountsPayableDocument)
645      */
646     @Override
647     public void takePurchaseOrderCancelAction(AccountsPayableDocument apDoc) {
648         VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) apDoc;
649         if (cmDocument.isReopenPurchaseOrderIndicator()) {
650             String docType = PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT;
651             purchaseOrderService.createAndRoutePotentialChangeDocument(cmDocument.getPurchaseOrderDocument().getDocumentNumber(), docType, "reopened by Payment Request " + apDoc.getPurapDocumentIdentifier() + "cancel", new ArrayList(), PurapConstants.PurchaseOrderStatuses.APPDOC_PENDING_CLOSE);
652         }
653     }
654 
655     /**
656      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#markPaid(org.kuali.ole.module.purap.document.CreditMemoDocument,
657      *      java.sql.Date)
658      */
659     @Override
660     public void markPaid(VendorCreditMemoDocument cm, Date processDate) {
661         LOG.debug("markPaid() started");
662 
663         cm.setCreditMemoPaidTimestamp(new Timestamp(processDate.getTime()));
664         purapService.saveDocumentNoValidation(cm);
665     }
666 
667     /**
668      * @see org.kuali.ole.module.purap.document.service.AccountsPayableDocumentSpecificService#poItemEligibleForAp(org.kuali.ole.module.purap.document.AccountsPayableDocument, org.kuali.ole.module.purap.businessobject.PurchaseOrderItem)
669      */
670     @Override
671     public boolean poItemEligibleForAp(AccountsPayableDocument apDoc, PurchaseOrderItem poItem) {
672         // if the po item is not active... skip it
673         if (!poItem.isItemActiveIndicator()) {
674             return false;
675         }
676 
677         if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalQuantity().isGreaterThan(KualiDecimal.ZERO)) {
678             return true;
679         } else {
680             BigDecimal unitPrice = (poItem.getItemUnitPrice() == null ? new BigDecimal(0) : poItem.getItemUnitPrice());
681             if (unitPrice.doubleValue() > poItem.getItemOutstandingEncumberedAmount().doubleValue()) {
682                 return true;
683             }
684         }
685         return false;
686     }
687 
688     /**
689      * The given document here needs to be a Credit Memo.
690      *
691      * @see org.kuali.ole.module.purap.document.service.AccountsPayableDocumentSpecificService#generateGLEntriesCreateAccountsPayableDocument(org.kuali.ole.module.purap.document.AccountsPayableDocument)
692      */
693     @Override
694     public void generateGLEntriesCreateAccountsPayableDocument(AccountsPayableDocument apDocument) {
695         VendorCreditMemoDocument creditMemo = (VendorCreditMemoDocument) apDocument;
696         purapGeneralLedgerService.generateEntriesCreateCreditMemo(creditMemo);
697     }
698 
699     /**
700      * Records the specified error message into the Log file and throws a runtime exception.
701      *
702      * @param errorMessage the error message to be logged.
703      */
704     protected void logAndThrowRuntimeException(String errorMessage) {
705         this.logAndThrowRuntimeException(errorMessage, null);
706     }
707 
708     /**
709      * Records the specified error message into the Log file and throws the specified runtime exception.
710      *
711      * @param errorMessage the specified error message.
712      * @param e            the specified runtime exception.
713      */
714     protected void logAndThrowRuntimeException(String errorMessage, Exception e) {
715         if (ObjectUtils.isNotNull(e)) {
716             LOG.error(errorMessage, e);
717             throw new RuntimeException(errorMessage, e);
718         } else {
719             LOG.error(errorMessage);
720             throw new RuntimeException(errorMessage);
721         }
722     }
723 
724     /**
725      * @see org.kuali.ole.module.purap.document.service.CreditMemoService#hasActiveCreditMemosForPurchaseOrder(java.lang.Integer)
726      */
727     @Override
728     public boolean hasActiveCreditMemosForPurchaseOrder(Integer purchaseOrderIdentifier) {
729 
730         boolean hasActiveCreditMemos = false;
731         List<String> docNumbers = null;
732         WorkflowDocument workflowDocument = null;
733 
734         docNumbers = creditMemoDao.getActiveCreditMemoDocumentNumbersForPurchaseOrder(purchaseOrderIdentifier);
735         docNumbers = filterCreditMemoByAppDocStatus(docNumbers, CreditMemoStatuses.STATUSES_POTENTIALLY_ACTIVE);
736 
737         for (String docNumber : docNumbers) {
738             try {
739                 workflowDocument = workflowDocumentService.loadWorkflowDocument(docNumber, GlobalVariables.getUserSession().getPerson());
740             } catch (WorkflowException we) {
741                 throw new RuntimeException(we);
742             }
743 
744             //if the document is not in a non-active status then return true and stop evaluation
745             if (!(workflowDocument.isCanceled() ||
746                     workflowDocument.isException() ||
747                     workflowDocument.isFinal())) {
748                 hasActiveCreditMemos = true;
749                 break;
750             }
751 
752         }
753 
754         return hasActiveCreditMemos;
755     }
756 
757     /**
758      * @see org.kuali.ole.module.purap.document.service.CreditMemoCreateService#populateDocumentAfterInit(org.kuali.ole.module.purap.document.CreditMemoDocument)
759      */
760     @Override
761     public void populateDocumentAfterInit(VendorCreditMemoDocument cmDocument) {
762 
763         OleVendorCreditMemoDocument vendorCreditMemoDocument = (OleVendorCreditMemoDocument) cmDocument;
764         // make a call to search for expired/closed accounts
765         HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList = accountsPayableService.getExpiredOrClosedAccountList(cmDocument);
766 
767         if (vendorCreditMemoDocument.isSourceDocumentPaymentRequest() && vendorCreditMemoDocument.getInvoiceIdentifier() == null) {
768             populateDocumentFromPreq(vendorCreditMemoDocument, expiredOrClosedAccountList);
769         } else if (vendorCreditMemoDocument.isSourceDocumentPurchaseOrder() && vendorCreditMemoDocument.getInvoiceIdentifier() == null) {
770             populateDocumentFromPO(vendorCreditMemoDocument, expiredOrClosedAccountList);
771         } else if (vendorCreditMemoDocument.getInvoiceIdentifier() != null) {
772             populateDocumentFromVendor(vendorCreditMemoDocument);
773         }
774 
775         populateDocumentDescription(vendorCreditMemoDocument);
776 
777         // write a note for expired/closed accounts if any exist and add a message stating there were expired/closed accounts at the
778         // top of the document
779         accountsPayableService.generateExpiredOrClosedAccountNote(cmDocument, expiredOrClosedAccountList);
780 
781         // set indicator so a message is displayed for accounts that were replaced due to expired/closed status
782         if (ObjectUtils.isNotNull(expiredOrClosedAccountList) && !expiredOrClosedAccountList.isEmpty()) {
783             cmDocument.setContinuationAccountIndicator(true);
784         }
785 
786     }
787 
788     /**
789      * Populate Credit Memo of type Payment Request.
790      *
791      * @param cmDocument - Credit Memo Document to Populate
792      */
793     protected void populateDocumentFromPreq(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
794         PaymentRequestDocument paymentRequestDocument = paymentRequestService.getPaymentRequestById(cmDocument.getPaymentRequestIdentifier());
795         cmDocument.getDocumentHeader().setOrganizationDocumentNumber(paymentRequestDocument.getDocumentHeader().getOrganizationDocumentNumber());
796         cmDocument.setPaymentRequestDocument(paymentRequestDocument);
797         cmDocument.setPurchaseOrderDocument(paymentRequestDocument.getPurchaseOrderDocument());
798         cmDocument.setUseTaxIndicator(paymentRequestDocument.isUseTaxIndicator());
799 
800         // credit memo address taken directly from payment request
801         cmDocument.setVendorHeaderGeneratedIdentifier(paymentRequestDocument.getVendorHeaderGeneratedIdentifier());
802         cmDocument.setVendorDetailAssignedIdentifier(paymentRequestDocument.getVendorDetailAssignedIdentifier());
803         cmDocument.setVendorAddressGeneratedIdentifier(paymentRequestDocument.getVendorAddressGeneratedIdentifier());
804         cmDocument.setVendorCustomerNumber(paymentRequestDocument.getVendorCustomerNumber());
805         cmDocument.setVendorName(paymentRequestDocument.getVendorName());
806         cmDocument.setVendorLine1Address(paymentRequestDocument.getVendorLine1Address());
807         cmDocument.setVendorLine2Address(paymentRequestDocument.getVendorLine2Address());
808         cmDocument.setVendorCityName(paymentRequestDocument.getVendorCityName());
809         cmDocument.setVendorStateCode(paymentRequestDocument.getVendorStateCode());
810         cmDocument.setVendorPostalCode(paymentRequestDocument.getVendorPostalCode());
811         cmDocument.setVendorCountryCode(paymentRequestDocument.getVendorCountryCode());
812         cmDocument.setVendorAttentionName(paymentRequestDocument.getVendorAttentionName());
813         cmDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(paymentRequestDocument.getAccountsPayablePurchasingDocumentLinkIdentifier());
814         cmDocument.setPaymentMethodId(paymentRequestDocument.getVendorDetail().getPaymentMethodId());
815         // prep the item lines (also collect warnings for later display) this is only done on paymentRequest
816         purapAccountingService.convertMoneyToPercent(paymentRequestDocument);
817         populateItemLinesFromPreq(cmDocument, expiredOrClosedAccountList);
818     }
819 
820     /**
821      * Populates the credit memo items from the payment request items.
822      *
823      * @param cmDocument - Credit Memo Document to Populate
824      */
825     protected void populateItemLinesFromPreq(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
826         PaymentRequestDocument preqDocument = cmDocument.getPaymentRequestDocument();
827 
828         for (PaymentRequestItem preqItemToTemplate : (List<PaymentRequestItem>) preqDocument.getItems()) {
829             preqItemToTemplate.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
830 
831             if (preqItemToTemplate.getItemType().isLineItemIndicator() && ((preqItemToTemplate.getItemType().isQuantityBasedGeneralLedgerIndicator() && preqItemToTemplate.getItemQuantity().isNonZero())
832                     || (preqItemToTemplate.getItemType().isAmountBasedGeneralLedgerIndicator() && preqItemToTemplate.getTotalAmount().isNonZero()))) {
833                 cmDocument.getItems().add(new CreditMemoItem(cmDocument, preqItemToTemplate, preqItemToTemplate.getPurchaseOrderItem(), expiredOrClosedAccountList));
834             }
835         }
836 
837         // add below the line items
838         purapService.addBelowLineItems(cmDocument);
839 
840         cmDocument.fixItemReferences();
841     }
842 
843     /**
844      * Populate Credit Memo of type Purchase Order.
845      *
846      * @param cmDocument - Credit Memo Document to Populate
847      */
848     protected void populateDocumentFromPO(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
849         PurchaseOrderDocument purchaseOrderDocument = purchaseOrderService.getCurrentPurchaseOrder(cmDocument.getPurchaseOrderIdentifier());
850         cmDocument.setPurchaseOrderDocument(purchaseOrderDocument);
851         cmDocument.getDocumentHeader().setOrganizationDocumentNumber(purchaseOrderDocument.getDocumentHeader().getOrganizationDocumentNumber());
852         cmDocument.setUseTaxIndicator(cmDocument.isUseTaxIndicator());
853 
854         cmDocument.setVendorHeaderGeneratedIdentifier(purchaseOrderDocument.getVendorHeaderGeneratedIdentifier());
855         cmDocument.setVendorDetailAssignedIdentifier(purchaseOrderDocument.getVendorDetailAssignedIdentifier());
856         cmDocument.setVendorCustomerNumber(purchaseOrderDocument.getVendorCustomerNumber());
857         cmDocument.setVendorName(purchaseOrderDocument.getVendorName());
858         cmDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(purchaseOrderDocument.getAccountsPayablePurchasingDocumentLinkIdentifier());
859         cmDocument.setPaymentMethodId(purchaseOrderDocument.getVendorDetail().getPaymentMethodId());
860         // populate cm vendor address with the default remit address type for the vendor if found
861         String userCampus = GlobalVariables.getUserSession().getPerson().getCampusCode();
862         VendorAddress vendorAddress = vendorService.getVendorDefaultAddress(purchaseOrderDocument.getVendorHeaderGeneratedIdentifier(), purchaseOrderDocument.getVendorDetailAssignedIdentifier(), VendorConstants.AddressTypes.REMIT, userCampus);
863         if (vendorAddress != null) {
864             cmDocument.templateVendorAddress(vendorAddress);
865             cmDocument.setVendorAddressGeneratedIdentifier(vendorAddress.getVendorAddressGeneratedIdentifier());
866             cmDocument.setVendorAttentionName(StringUtils.defaultString(vendorAddress.getVendorAttentionName()));
867         } else {
868             // set address from PO
869             cmDocument.setVendorAddressGeneratedIdentifier(purchaseOrderDocument.getVendorAddressGeneratedIdentifier());
870             cmDocument.setVendorLine1Address(purchaseOrderDocument.getVendorLine1Address());
871             cmDocument.setVendorLine2Address(purchaseOrderDocument.getVendorLine2Address());
872             cmDocument.setVendorCityName(purchaseOrderDocument.getVendorCityName());
873             cmDocument.setVendorStateCode(purchaseOrderDocument.getVendorStateCode());
874             cmDocument.setVendorPostalCode(purchaseOrderDocument.getVendorPostalCode());
875             cmDocument.setVendorCountryCode(purchaseOrderDocument.getVendorCountryCode());
876 
877             boolean blankAttentionLine = StringUtils.equalsIgnoreCase("Y", SpringContext.getBean(ParameterService.class).getParameterValueAsString(PurapConstants.PURAP_NAMESPACE, "Document", PurapParameterConstants.BLANK_ATTENTION_LINE_FOR_PO_TYPE_ADDRESS));
878             if (blankAttentionLine) {
879                 cmDocument.setVendorAttentionName(StringUtils.EMPTY);
880             } else {
881                 cmDocument.setVendorAttentionName(StringUtils.defaultString(purchaseOrderDocument.getVendorAttentionName()));
882             }
883         }
884 
885         populateItemLinesFromPO(cmDocument, expiredOrClosedAccountList);
886     }
887 
888     /**
889      * Populates the credit memo items from the payment request items.
890      *
891      * @param cmDocument - Credit Memo Document to Populate
892      */
893     protected void populateItemLinesFromPO(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
894         List<PurchaseOrderItem> invoicedItems = getPOInvoicedItems(cmDocument.getPurchaseOrderDocument());
895         for (PurchaseOrderItem poItem : invoicedItems) {
896             if ((poItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalQuantity().isNonZero())
897                     || (poItem.getItemType().isAmountBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalAmount().isNonZero())) {
898                 CreditMemoItem creditMemoItem = new CreditMemoItem(cmDocument, poItem, expiredOrClosedAccountList);
899                 cmDocument.getItems().add(creditMemoItem);
900                 PurchasingCapitalAssetItem purchasingCAMSItem = cmDocument.getPurchaseOrderDocument().getPurchasingCapitalAssetItemByItemIdentifier(poItem.getItemIdentifier());
901                 if (purchasingCAMSItem != null) {
902                     creditMemoItem.setCapitalAssetTransactionTypeCode(purchasingCAMSItem.getCapitalAssetTransactionTypeCode());
903                 }
904             }
905         }
906 
907         // add below the line items
908         purapService.addBelowLineItems(cmDocument);
909 
910         cmDocument.fixItemReferences();
911     }
912 
913     /**
914      * Populate Credit Memo of type Vendor.
915      *
916      * @param cmDocument - Credit Memo Document to Populate
917      */
918     protected void populateDocumentFromVendor(VendorCreditMemoDocument cmDocument) {
919         Integer vendorHeaderId = VendorUtils.getVendorHeaderId(cmDocument.getVendorNumber());
920         Integer vendorDetailId = VendorUtils.getVendorDetailId(cmDocument.getVendorNumber());
921 
922         VendorDetail vendorDetail = vendorService.getVendorDetail(vendorHeaderId, vendorDetailId);
923         cmDocument.setVendorDetail(vendorDetail);
924 
925         cmDocument.setVendorHeaderGeneratedIdentifier(vendorDetail.getVendorHeaderGeneratedIdentifier());
926         cmDocument.setVendorDetailAssignedIdentifier(vendorDetail.getVendorDetailAssignedIdentifier());
927         cmDocument.setVendorCustomerNumber(vendorDetail.getVendorNumber());
928         cmDocument.setVendorName(vendorDetail.getVendorName());
929         cmDocument.setPaymentMethodId(vendorDetail.getPaymentMethodId());
930 
931         // credit memo type vendor uses the default remit type address for the vendor if found
932         String userCampus = GlobalVariables.getUserSession().getPerson().getCampusCode();
933         VendorAddress vendorAddress = vendorService.getVendorDefaultAddress(vendorHeaderId, vendorDetailId, VendorConstants.AddressTypes.REMIT, userCampus);
934         if (vendorAddress == null) {
935             // pick up the default vendor po address type
936             vendorAddress = vendorService.getVendorDefaultAddress(vendorHeaderId, vendorDetailId, VendorConstants.AddressTypes.PURCHASE_ORDER, userCampus);
937         }
938 
939         cmDocument.setVendorAddressGeneratedIdentifier(vendorAddress.getVendorAddressGeneratedIdentifier());
940         cmDocument.templateVendorAddress(vendorAddress);
941 
942         // add below the line items
943         purapService.addBelowLineItems(cmDocument);
944     }
945 
946     /**
947      * Defaults the document description based on the credit memo source type.
948      *
949      * @param cmDocument - Credit Memo Document to Populate
950      */
951     protected void populateDocumentDescription(VendorCreditMemoDocument cmDocument) {
952         String description = "";
953         if (cmDocument.isSourceVendor()) {
954             description = "Vendor: " + cmDocument.getVendorName();
955         } else {
956             description = "PO: " + cmDocument.getPurchaseOrderDocument().getPurapDocumentIdentifier() + " Vendor: " + cmDocument.getVendorName();
957         }
958 
959         // trim description if longer than whats specified in the data dictionary
960         int noteTextMaxLength = dataDictionaryService.getAttributeMaxLength(DocumentHeader.class, KRADPropertyConstants.DOCUMENT_DESCRIPTION).intValue();
961         if (noteTextMaxLength < description.length()) {
962             description = description.substring(0, noteTextMaxLength);
963         }
964 
965         cmDocument.getDocumentHeader().setDocumentDescription(description);
966     }
967 
968 }
969