001/*
002 * Copyright 2007 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 */
016package org.kuali.ole.module.purap.document.service.impl;
017
018import org.apache.commons.collections.CollectionUtils;
019import org.apache.commons.lang.StringUtils;
020import org.apache.commons.lang.text.StrBuilder;
021import org.kuali.ole.module.purap.PurapConstants;
022import org.kuali.ole.module.purap.PurapConstants.CreditMemoStatuses;
023import org.kuali.ole.module.purap.PurapKeyConstants;
024import org.kuali.ole.module.purap.PurapParameterConstants;
025import org.kuali.ole.module.purap.PurapPropertyConstants;
026import org.kuali.ole.module.purap.businessobject.*;
027import org.kuali.ole.module.purap.document.*;
028import org.kuali.ole.module.purap.document.dataaccess.CreditMemoDao;
029import org.kuali.ole.module.purap.document.service.*;
030import org.kuali.ole.module.purap.document.validation.event.AttributedContinuePurapEvent;
031import org.kuali.ole.module.purap.service.PurapAccountingService;
032import org.kuali.ole.module.purap.service.PurapGeneralLedgerService;
033import org.kuali.ole.module.purap.util.ExpiredOrClosedAccountEntry;
034import org.kuali.ole.module.purap.util.VendorGroupingHelper;
035import org.kuali.ole.select.document.OleVendorCreditMemoDocument;
036import org.kuali.ole.sys.businessobject.Bank;
037import org.kuali.ole.sys.businessobject.SourceAccountingLine;
038import org.kuali.ole.sys.context.SpringContext;
039import org.kuali.ole.sys.service.BankService;
040import org.kuali.ole.vnd.VendorConstants;
041import org.kuali.ole.vnd.VendorUtils;
042import org.kuali.ole.vnd.businessobject.VendorAddress;
043import org.kuali.ole.vnd.businessobject.VendorDetail;
044import org.kuali.ole.vnd.document.service.VendorService;
045import org.kuali.rice.core.api.config.property.ConfigurationService;
046import org.kuali.rice.core.api.util.type.KualiDecimal;
047import org.kuali.rice.coreservice.framework.parameter.ParameterService;
048import org.kuali.rice.kew.api.KewApiServiceLocator;
049import org.kuali.rice.kew.api.WorkflowDocument;
050import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
051import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
052import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
053import org.kuali.rice.kew.api.exception.WorkflowException;
054import org.kuali.rice.kim.api.identity.Person;
055import org.kuali.rice.kns.service.DataDictionaryService;
056import org.kuali.rice.krad.bo.DocumentHeader;
057import org.kuali.rice.krad.bo.Note;
058import org.kuali.rice.krad.exception.ValidationException;
059import org.kuali.rice.krad.service.DocumentService;
060import org.kuali.rice.krad.service.NoteService;
061import org.kuali.rice.krad.util.GlobalVariables;
062import org.kuali.rice.krad.util.KRADPropertyConstants;
063import org.kuali.rice.krad.util.ObjectUtils;
064import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
065import org.springframework.transaction.annotation.Transactional;
066
067import java.math.BigDecimal;
068import java.sql.Date;
069import java.sql.Timestamp;
070import java.util.*;
071
072/**
073 * Provides services to support the creation of a Credit Memo Document.
074 */
075@Transactional
076public class CreditMemoServiceImpl implements CreditMemoService {
077    private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CreditMemoServiceImpl.class);
078
079    private AccountsPayableService accountsPayableService;
080    private CreditMemoDao creditMemoDao;
081    private DataDictionaryService dataDictionaryService;
082    private DocumentService documentService;
083    private ConfigurationService kualiConfigurationService;
084    private NoteService noteService;
085    private PaymentRequestService paymentRequestService;
086    private PurapAccountingService purapAccountingService;
087    private PurapGeneralLedgerService purapGeneralLedgerService;
088    private PurapService purapService;
089    private PurchaseOrderService purchaseOrderService;
090    private VendorService vendorService;
091    private WorkflowDocumentService workflowDocumentService;
092
093
094    public void setAccountsPayableService(AccountsPayableService accountsPayableService) {
095        this.accountsPayableService = accountsPayableService;
096    }
097
098    public void setCreditMemoDao(CreditMemoDao creditMemoDao) {
099        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