/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.module.purap.document.service.impl;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.module.purap.PurapConstants;
import org.kuali.kfs.module.purap.PurapConstants.CreditMemoStatuses;
import org.kuali.kfs.module.purap.PurapKeyConstants;
import org.kuali.kfs.module.purap.PurapParameterConstants;
import org.kuali.kfs.module.purap.PurapPropertyConstants;
import org.kuali.kfs.module.purap.businessobject.CreditMemoAccount;
import org.kuali.kfs.module.purap.businessobject.CreditMemoItem;
import org.kuali.kfs.module.purap.businessobject.PaymentRequestItem;
import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
import org.kuali.kfs.module.purap.businessobject.PurchasingCapitalAssetItem;
import org.kuali.kfs.module.purap.document.AccountsPayableDocument;
import org.kuali.kfs.module.purap.document.PaymentRequestDocument;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument;
import org.kuali.kfs.module.purap.document.VendorCreditMemoDocument;
import org.kuali.kfs.module.purap.document.dataaccess.CreditMemoDao;
import org.kuali.kfs.module.purap.document.service.AccountsPayableService;
import org.kuali.kfs.module.purap.document.service.CreditMemoService;
import org.kuali.kfs.module.purap.document.service.PaymentRequestService;
import org.kuali.kfs.module.purap.document.service.PurapService;
import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
import org.kuali.kfs.module.purap.document.validation.event.AttributedContinuePurapEvent;
import org.kuali.kfs.module.purap.service.PurapAccountingService;
import org.kuali.kfs.module.purap.service.PurapGeneralLedgerService;
import org.kuali.kfs.module.purap.util.ExpiredOrClosedAccountEntry;
import org.kuali.kfs.module.purap.util.VendorGroupingHelper;
import org.kuali.kfs.sys.businessobject.Bank;
import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.service.FinancialSystemDocumentService;
import org.kuali.kfs.sys.service.BankService;
import org.kuali.kfs.vnd.VendorConstants;
import org.kuali.kfs.vnd.VendorUtils;
import org.kuali.kfs.vnd.businessobject.VendorAddress;
import org.kuali.kfs.vnd.businessobject.VendorDetail;
import org.kuali.kfs.vnd.document.service.VendorService;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.bo.DocumentHeader;
import org.kuali.rice.krad.bo.Note;
import org.kuali.rice.krad.exception.ValidationException;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.service.NoteService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADPropertyConstants;
import org.kuali.rice.krad.util.ObjectUtils;
import org.kuali.rice.krad.workflow.service.WorkflowDocumentService;
import org.springframework.transaction.annotation.Transactional;
/**
* Provides services to support the creation of a Credit Memo Document.
*/
@Transactional
public class CreditMemoServiceImpl implements CreditMemoService {
private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CreditMemoServiceImpl.class);
private AccountsPayableService accountsPayableService;
private CreditMemoDao creditMemoDao;
private DataDictionaryService dataDictionaryService;
private DocumentService documentService;
private ConfigurationService kualiConfigurationService;
private NoteService noteService;
private PaymentRequestService paymentRequestService;
private PurapAccountingService purapAccountingService;
private PurapGeneralLedgerService purapGeneralLedgerService;
private PurapService purapService;
private PurchaseOrderService purchaseOrderService;
private VendorService vendorService;
private WorkflowDocumentService workflowDocumentService;
private FinancialSystemDocumentService financialSystemDocumentService;
public void setAccountsPayableService(AccountsPayableService accountsPayableService) {
this.accountsPayableService = accountsPayableService;
}
public void setCreditMemoDao(CreditMemoDao creditMemoDao) {
this.creditMemoDao = creditMemoDao;
}
public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
this.dataDictionaryService = dataDictionaryService;
}
public void setDocumentService(DocumentService documentService) {
this.documentService = documentService;
}
public void setConfigurationService(ConfigurationService kualiConfigurationService) {
this.kualiConfigurationService = kualiConfigurationService;
}
public void setNoteService(NoteService noteService) {
this.noteService = noteService;
}
public void setPaymentRequestService(PaymentRequestService paymentRequestService) {
this.paymentRequestService = paymentRequestService;
}
public void setPurapAccountingService(PurapAccountingService purapAccountingService) {
this.purapAccountingService = purapAccountingService;
}
public void setPurapGeneralLedgerService(PurapGeneralLedgerService purapGeneralLedgerService) {
this.purapGeneralLedgerService = purapGeneralLedgerService;
}
public void setPurapService(PurapService purapService) {
this.purapService = purapService;
}
public void setPurchaseOrderService(PurchaseOrderService purchaseOrderService) {
this.purchaseOrderService = purchaseOrderService;
}
public void setVendorService(VendorService vendorService) {
this.vendorService = vendorService;
}
public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService){
this.workflowDocumentService = workflowDocumentService;
}
public void setFinancialSystemDocumentService(FinancialSystemDocumentService financialSystemDocumentService) {
this.financialSystemDocumentService = financialSystemDocumentService;
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#getCreditMemosToExtract(java.lang.String)
*/
@Override
public List<VendorCreditMemoDocument> getCreditMemosToExtract(String chartCode) {
LOG.debug("getCreditMemosToExtract() started");
List<VendorCreditMemoDocument> docs = creditMemoDao.getCreditMemosToExtract(chartCode);
docs = (List<VendorCreditMemoDocument>) filterCreditMemoByAppDocStatus(docs, CreditMemoStatuses.STATUSES_ALLOWED_FOR_EXTRACTION);
return docs;
}
@Override
public Collection<VendorCreditMemoDocument> getCreditMemosToExtractByVendor(String chartCode, VendorGroupingHelper vendor ) {
LOG.debug("getCreditMemosToExtractByVendor() started");
Collection<VendorCreditMemoDocument> docs = creditMemoDao.getCreditMemosToExtractByVendor(chartCode,vendor);
docs = filterCreditMemoByAppDocStatus(docs, CreditMemoStatuses.STATUSES_ALLOWED_FOR_EXTRACTION);
return docs;
}
@Override
public Set<VendorGroupingHelper> getVendorsOnCreditMemosToExtract(String chartCode) {
LOG.debug("getVendorsOnCreditMemosToExtract() started");
HashSet<VendorGroupingHelper> vendors = new HashSet<VendorGroupingHelper>();
List<VendorCreditMemoDocument> docs = this.getCreditMemosToExtract(chartCode);
for (VendorCreditMemoDocument doc: docs) {
vendors.add( new VendorGroupingHelper( doc ) );
}
return vendors;
}
/**
* Query appDocStatus using financialSystemDocumentService and filter against the provided status list
*
* @param lookupDocNumbers
* @param appDocStatus
* @return
*/
@Deprecated
protected List<String> filterCreditMemoByAppDocStatus(List<String> lookupDocNumbers, String... appDocStatus) {
List<String> creditMemoDocNumbers = new ArrayList<String>();
List<String> appDocStatusList = Arrays.asList(appDocStatus);
for (String docNumber : lookupDocNumbers) {
if(appDocStatusList.contains(financialSystemDocumentService.findByDocumentNumber(docNumber).getApplicationDocumentStatus())) {
creditMemoDocNumbers.add(docNumber);
}
}
return creditMemoDocNumbers;
}
/**
* This method queries financialSystemDocumentHeader and filter credit memos against the provided status.
*
* @param creditMemoDocuments
* @param appDocStatus
* @return
*/
protected Collection<VendorCreditMemoDocument> filterCreditMemoByAppDocStatus(Collection<VendorCreditMemoDocument> creditMemoDocuments, String... appDocStatus) {
List<String> appDocStatusList = Arrays.asList(appDocStatus);
Collection<VendorCreditMemoDocument> filteredCreditMemoDocuments = new ArrayList<VendorCreditMemoDocument>();
//add to filtered collection if the app doc list contains credit memo's app doc status
for (VendorCreditMemoDocument creditMemo : creditMemoDocuments){
if(appDocStatusList.contains(creditMemo.getApplicationDocumentStatus())) {
filteredCreditMemoDocuments.add(creditMemo);
}
}
return filteredCreditMemoDocuments;
}
/**
* Wrapper class to the filterPaymentRequestByAppDocStatus (Collection<PaymentRequestDocument>)
*
* This class first construct the Payment Request Collection from the iterator, and then process through
* filterPaymentRequestByAppDocStatus
*
* @param paymentRequestDocuments
* @param appDocStatus
* @return
*/
private Iterator<VendorCreditMemoDocument> filterCreditMemoByAppDocStatus(Iterator<VendorCreditMemoDocument> creditMemoIterator, String... appDocStatus) {
Collection<VendorCreditMemoDocument> creditMemoDocuments = new ArrayList<VendorCreditMemoDocument>();
for (;creditMemoIterator.hasNext();){
creditMemoDocuments.add(creditMemoIterator.next());
}
return filterCreditMemoByAppDocStatus(creditMemoDocuments, appDocStatus).iterator();
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#creditMemoDuplicateMessages(org.kuali.kfs.module.purap.document.CreditMemoDocument)
*/
@Override
public String creditMemoDuplicateMessages(VendorCreditMemoDocument cmDocument) {
String duplicateMessage = null;
String vendorNumber = cmDocument.getVendorNumber();
if (StringUtils.isEmpty(vendorNumber)) {
PurchasingAccountsPayableDocument sourceDocument = cmDocument.getPurApSourceDocumentIfPossible();
if (ObjectUtils.isNotNull(sourceDocument)) {
vendorNumber = sourceDocument.getVendorNumber();
}
}
if (StringUtils.isNotEmpty(vendorNumber)) {
// check for existence of another credit memo with the same vendor and vendor credit memo number
if (creditMemoDao.duplicateExists(VendorUtils.getVendorHeaderId(vendorNumber), VendorUtils.getVendorDetailId(vendorNumber), cmDocument.getCreditMemoNumber())) {
duplicateMessage = kualiConfigurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_CREDIT_MEMO_VENDOR_NUMBER);
}
// check for existence of another credit memo with the same vendor and credit memo date
if (creditMemoDao.duplicateExists(VendorUtils.getVendorHeaderId(vendorNumber), VendorUtils.getVendorDetailId(vendorNumber), cmDocument.getCreditMemoDate(), cmDocument.getCreditMemoAmount())) {
duplicateMessage = kualiConfigurationService.getPropertyValueAsString(PurapKeyConstants.MESSAGE_DUPLICATE_CREDIT_MEMO_VENDOR_NUMBER_DATE_AMOUNT);
}
}
return duplicateMessage;
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#getPOInvoicedItems(org.kuali.kfs.module.purap.document.PurchaseOrderDocument)
*/
@Override
public List<PurchaseOrderItem> getPOInvoicedItems(PurchaseOrderDocument poDocument) {
List<PurchaseOrderItem> invoicedItems = new ArrayList<PurchaseOrderItem>();
for (Iterator iter = poDocument.getItems().iterator(); iter.hasNext();) {
PurchaseOrderItem poItem = (PurchaseOrderItem) iter.next();
poItem.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalQuantity().isGreaterThan(KualiDecimal.ZERO)) {
invoicedItems.add(poItem);
}
else {
BigDecimal unitPrice = (poItem.getItemUnitPrice() == null ? new BigDecimal(0) : poItem.getItemUnitPrice());
if (unitPrice.doubleValue() > poItem.getItemOutstandingEncumberedAmount().doubleValue()) {
invoicedItems.add(poItem);
}
}
}
return invoicedItems;
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#calculateCreditMemo(org.kuali.kfs.module.purap.document.CreditMemoDocument)
*/
@Override
public void calculateCreditMemo(VendorCreditMemoDocument cmDocument) {
cmDocument.updateExtendedPriceOnItems();
for (CreditMemoItem item : (List<CreditMemoItem>) cmDocument.getItems()) {
// make sure restocking fee is negative
if (StringUtils.equals(PurapConstants.ItemTypeCodes.ITEM_TYPE_RESTCK_FEE_CODE, item.getItemTypeCode())) {
if (item.getItemUnitPrice() != null) {
item.setExtendedPrice(item.getExtendedPrice().abs().negated());
item.setItemUnitPrice(item.getItemUnitPrice().abs().negate());
}
}
}
//calculate tax if cm not based on vendor
if (cmDocument.isSourceVendor() == false) {
purapService.calculateTax(cmDocument);
}
// proration
if (cmDocument.isSourceVendor()) {
// no proration on vendor
return;
}
for (CreditMemoItem item : (List<CreditMemoItem>) cmDocument.getItems()) {
// skip above the line
item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
if (item.getItemType().isLineItemIndicator()) {
continue;
}
if ((item.getSourceAccountingLines().isEmpty()) && (ObjectUtils.isNotNull(item.getExtendedPrice())) && (KualiDecimal.ZERO.compareTo(item.getExtendedPrice()) != 0)) {
KualiDecimal totalAmount = KualiDecimal.ZERO;
List<PurApAccountingLine> distributedAccounts = null;
List<SourceAccountingLine> summaryAccounts = null;
totalAmount = cmDocument.getPurApSourceDocumentIfPossible().getTotalDollarAmount();
// this should do nothing on preq which is fine
purapAccountingService.updateAccountAmounts(cmDocument.getPurApSourceDocumentIfPossible());
summaryAccounts = purapAccountingService.generateSummary(cmDocument.getPurApSourceDocumentIfPossible().getItems());
distributedAccounts = purapAccountingService.generateAccountDistributionForProration(summaryAccounts, totalAmount, PurapConstants.PRORATION_SCALE, CreditMemoAccount.class);
if (CollectionUtils.isNotEmpty(distributedAccounts) && CollectionUtils.isEmpty(item.getSourceAccountingLines())) {
item.setSourceAccountingLines(distributedAccounts);
}
}
}
// end proration
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#getCreditMemoByDocumentNumber(java.lang.String)
*/
@Override
public VendorCreditMemoDocument getCreditMemoByDocumentNumber(String documentNumber) {
LOG.debug("getCreditMemoByDocumentNumber() started");
if (ObjectUtils.isNotNull(documentNumber)) {
try {
VendorCreditMemoDocument doc = (VendorCreditMemoDocument) documentService.getByDocumentHeaderId(documentNumber);
return doc;
}
catch (WorkflowException e) {
String errorMessage = "Error getting credit memo document from document service";
LOG.error("getCreditMemoByDocumentNumber() " + errorMessage, e);
throw new RuntimeException(errorMessage, e);
}
}
return null;
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#getCreditMemoDocumentById(java.lang.Integer)
*/
@Override
public VendorCreditMemoDocument getCreditMemoDocumentById(Integer purchasingDocumentIdentifier) {
return getCreditMemoByDocumentNumber(creditMemoDao.getDocumentNumberByCreditMemoId(purchasingDocumentIdentifier));
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#saveDocument(org.kuali.kfs.module.purap.document.CreditMemoDocument)
*/
@Override
public void populateAndSaveCreditMemo(VendorCreditMemoDocument document) {
try {
// document.setApplicationDocumentStatus(PurapConstants.CreditMemoStatuses.APPDOC_IN_PROCESS);
// document.getDocumentHeader().getWorkflowDocument().setApplicationDocumentStatus(PurapConstants.CreditMemoStatuses.APPDOC_IN_PROCESS);
document.updateAndSaveAppDocStatus(PurapConstants.CreditMemoStatuses.APPDOC_IN_PROCESS);
if (document.isSourceDocumentPaymentRequest()) {
document.setBankCode(document.getPaymentRequestDocument().getBankCode());
document.setBank(document.getPaymentRequestDocument().getBank());
}
else {
// set bank code to default bank code in the system parameter
Bank defaultBank = SpringContext.getBean(BankService.class).getDefaultBankByDocType(document.getClass());
if (defaultBank != null) {
document.setBankCode(defaultBank.getBankCode());
document.setBank(defaultBank);
}
}
documentService.saveDocument(document, AttributedContinuePurapEvent.class);
}
catch (ValidationException ve) {
// set the status back to initiate
try {
document.updateAndSaveAppDocStatus(PurapConstants.CreditMemoStatuses.APPDOC_INITIATE);
}
catch (WorkflowException workflowException) {
}
}
catch (WorkflowException we) {
// set the status back to initiate
try {
document.updateAndSaveAppDocStatus(PurapConstants.CreditMemoStatuses.APPDOC_INITIATE);
}
catch (WorkflowException workflowException) {
}
String errorMsg = "Error saving document # " + document.getDocumentNumber() + " " + we.getMessage();
LOG.error(errorMsg, we);
throw new RuntimeException(errorMsg, we);
}
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#reopenClosedPO(org.kuali.kfs.module.purap.document.CreditMemoDocument)
*/
@Override
public void reopenClosedPO(VendorCreditMemoDocument cmDocument) {
// reopen PO if closed
Integer purchaseOrderDocumentId = cmDocument.getPurchaseOrderIdentifier();
if (cmDocument.isSourceDocumentPaymentRequest() && ObjectUtils.isNull(purchaseOrderDocumentId)) {
PaymentRequestDocument paymentRequestDocument = paymentRequestService.getPaymentRequestById(cmDocument.getPaymentRequestIdentifier());
purchaseOrderDocumentId = paymentRequestDocument.getPurchaseOrderIdentifier();
}
// if we found a valid po id number then check it for reopening
if (ObjectUtils.isNotNull(purchaseOrderDocumentId)) {
PurchaseOrderDocument purchaseOrderDocument = purchaseOrderService.getCurrentPurchaseOrder(purchaseOrderDocumentId);
// only reopen if the po is not null, it does not have a pending change already scheduled, and it is in closed status
if (ObjectUtils.isNotNull(purchaseOrderDocument) && (!purchaseOrderDocument.isPendingActionIndicator()) && PurapConstants.PurchaseOrderStatuses.APPDOC_CLOSED.equals(purchaseOrderDocument.getApplicationDocumentStatus())) {
}
}
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#addHoldOnPaymentRequest(org.kuali.kfs.module.purap.document.CreditMemoDocument,
* java.lang.String)
*/
@Override
public VendorCreditMemoDocument addHoldOnCreditMemo(VendorCreditMemoDocument cmDocument, String note) throws Exception {
// save the note
Note noteObj = documentService.createNoteFromDocument(cmDocument, note);
cmDocument.addNote(noteObj);
noteService.save(noteObj);
// retrieve and save with hold indicator set to true
VendorCreditMemoDocument cmDoc = getCreditMemoDocumentById(cmDocument.getPurapDocumentIdentifier());
cmDoc.setHoldIndicator(true);
cmDoc.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
purapService.saveDocumentNoValidation(cmDoc);
// must also save it on the incoming document
cmDocument.setHoldIndicator(true);
cmDocument.setLastActionPerformedByPersonId(GlobalVariables.getUserSession().getPerson().getPrincipalId());
return cmDoc;
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#removeHoldOnCreditMemo(org.kuali.kfs.module.purap.document.CreditMemoDocument,
* java.lang.String)
*/
@Override
public VendorCreditMemoDocument removeHoldOnCreditMemo(VendorCreditMemoDocument cmDocument, String note) throws Exception {
// save the note
Note noteObj = documentService.createNoteFromDocument(cmDocument, note);
cmDocument.addNote(noteObj);
noteService.save(noteObj);
// retrieve and save with hold indicator set to false
VendorCreditMemoDocument cmDoc = getCreditMemoDocumentById(cmDocument.getPurapDocumentIdentifier());
cmDoc.setHoldIndicator(false);
cmDoc.setLastActionPerformedByPersonId(null);
purapService.saveDocumentNoValidation(cmDoc);
// must also save it on the incoming document
cmDocument.setHoldIndicator(false);
cmDocument.setLastActionPerformedByPersonId(null);
return cmDoc;
}
/**
* @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#updateStatusByNode(java.lang.String, org.kuali.kfs.module.purap.document.AccountsPayableDocument)
*/
@Override
public String updateStatusByNode(String currentNodeName, AccountsPayableDocument apDoc) {
return updateStatusByNode(currentNodeName, (VendorCreditMemoDocument) apDoc);
}
/**
* Updates the status of a credit memo document, currently this is used by the cancel action
*
* @param currentNodeName The string representing the current node to be used to obtain the canceled status code.
* @param cmDoc The credit memo document to be updated.
* @return The string representing the canceledStatusCode, if empty it is assumed to be not from workflow.
*/
protected String updateStatusByNode(String currentNodeName, VendorCreditMemoDocument cmDoc) {
// update the status on the document
String cancelledStatusCode = "";
if (StringUtils.isEmpty(currentNodeName)) {
cancelledStatusCode = PurapConstants.CreditMemoStatuses.APPDOC_CANCELLED_POST_AP_APPROVE;
}
else {
cancelledStatusCode = CreditMemoStatuses.getCreditMemoAppDocDisapproveStatuses().get(currentNodeName);
}
if (StringUtils.isNotBlank(cancelledStatusCode)) {
try {
cmDoc.updateAndSaveAppDocStatus(cancelledStatusCode);
}
catch (WorkflowException we) {
throw new RuntimeException("Unable to save the workflow document with document id: " + cmDoc.getDocumentNumber());
}
purapService.saveDocumentNoValidation(cmDoc);
return cancelledStatusCode;
}
else {
logAndThrowRuntimeException("No status found to set for document being disapproved in node '" + currentNodeName + "'");
}
return cancelledStatusCode;
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#cancelExtractedCreditMemo(org.kuali.kfs.module.purap.document.CreditMemoDocument,
* java.lang.String)
*/
@Override
public void cancelExtractedCreditMemo(VendorCreditMemoDocument cmDocument, String note) {
LOG.debug("cancelExtractedCreditMemo() started");
if (CreditMemoStatuses.CANCELLED_STATUSES.contains(cmDocument.getApplicationDocumentStatus())) {
LOG.debug("cancelExtractedCreditMemo() ended");
return;
}
try {
Note noteObj = documentService.createNoteFromDocument(cmDocument, note);
cmDocument.addNote(noteObj);
}
catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
accountsPayableService.cancelAccountsPayableDocument(cmDocument, "");
if (LOG.isDebugEnabled()) {
LOG.debug("cancelExtractedCreditMemo() CM " + cmDocument.getPurapDocumentIdentifier() + " Cancelled Without Workflow");
}
LOG.debug("cancelExtractedCreditMemo() ended");
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#resetExtractedCreditMemo(org.kuali.kfs.module.purap.document.CreditMemoDocument,
* java.lang.String)
*/
@Override
public void resetExtractedCreditMemo(VendorCreditMemoDocument cmDocument, String note) {
LOG.debug("resetExtractedCreditMemo() started");
if (CreditMemoStatuses.CANCELLED_STATUSES.contains(cmDocument.getApplicationDocumentStatus())) {
LOG.debug("resetExtractedCreditMemo() ended");
return;
}
cmDocument.setExtractedTimestamp(null);
cmDocument.setCreditMemoPaidTimestamp(null);
Note noteObj;
try {
noteObj = documentService.createNoteFromDocument(cmDocument, note);
cmDocument.addNote(noteObj);
}
catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
purapService.saveDocumentNoValidation(cmDocument);
if (LOG.isDebugEnabled()) {
LOG.debug("resetExtractedCreditMemo() CM " + cmDocument.getPurapDocumentIdentifier() + " Cancelled Without Workflow");
}
LOG.debug("resetExtractedCreditMemo() ended");
}
/**
* @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#shouldPurchaseOrderBeReversed(org.kuali.kfs.module.purap.document.AccountsPayableDocument)
*/
@Override
public boolean shouldPurchaseOrderBeReversed(AccountsPayableDocument apDoc) {
// always return false, never reverse
return false;
}
/**
* @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#getPersonForCancel(org.kuali.kfs.module.purap.document.AccountsPayableDocument)
*/
@Override
public Person getPersonForCancel(AccountsPayableDocument apDoc) {
// return null, since superuser is fine for CM
return null;
}
/**
* @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#takePurchaseOrderCancelAction(org.kuali.kfs.module.purap.document.AccountsPayableDocument)
*/
@Override
public void takePurchaseOrderCancelAction(AccountsPayableDocument apDoc) {
VendorCreditMemoDocument cmDocument = (VendorCreditMemoDocument) apDoc;
if (cmDocument.isReopenPurchaseOrderIndicator()) {
String docType = PurapConstants.PurchaseOrderDocTypes.PURCHASE_ORDER_CLOSE_DOCUMENT;
purchaseOrderService.createAndRoutePotentialChangeDocument(cmDocument.getPurchaseOrderDocument().getDocumentNumber(), docType, "reopened by Payment Request " + apDoc.getPurapDocumentIdentifier() + "cancel", new ArrayList(), PurapConstants.PurchaseOrderStatuses.APPDOC_PENDING_CLOSE);
}
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#markPaid(org.kuali.kfs.module.purap.document.CreditMemoDocument,
* java.sql.Date)
*/
@Override
public void markPaid(VendorCreditMemoDocument cm, Date processDate) {
LOG.debug("markPaid() started");
cm.setCreditMemoPaidTimestamp(new Timestamp(processDate.getTime()));
purapService.saveDocumentNoValidation(cm);
}
/**
* @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#poItemEligibleForAp(org.kuali.kfs.module.purap.document.AccountsPayableDocument, org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem)
*/
@Override
public boolean poItemEligibleForAp(AccountsPayableDocument apDoc, PurchaseOrderItem poItem) {
poItem.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
// if the po item is not active... skip it
if (!poItem.isItemActiveIndicator()) {
return false;
}
if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalQuantity().isGreaterThan(KualiDecimal.ZERO)) {
return true;
}
else {
BigDecimal unitPrice = (poItem.getItemUnitPrice() == null ? new BigDecimal(0) : poItem.getItemUnitPrice());
if (unitPrice.doubleValue() > poItem.getItemOutstandingEncumberedAmount().doubleValue()) {
return true;
}
}
return false;
}
/**
* The given document here needs to be a Credit Memo.
*
* @see org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService#generateGLEntriesCreateAccountsPayableDocument(org.kuali.kfs.module.purap.document.AccountsPayableDocument)
*/
@Override
public void generateGLEntriesCreateAccountsPayableDocument(AccountsPayableDocument apDocument) {
VendorCreditMemoDocument creditMemo = (VendorCreditMemoDocument)apDocument;
purapGeneralLedgerService.generateEntriesCreateCreditMemo(creditMemo);
}
/**
* Records the specified error message into the Log file and throws a runtime exception.
*
* @param errorMessage the error message to be logged.
*/
protected void logAndThrowRuntimeException(String errorMessage) {
this.logAndThrowRuntimeException(errorMessage, null);
}
/**
* Records the specified error message into the Log file and throws the specified runtime exception.
*
* @param errorMessage the specified error message.
* @param e the specified runtime exception.
*/
protected void logAndThrowRuntimeException(String errorMessage, Exception e) {
if (ObjectUtils.isNotNull(e)) {
LOG.error(errorMessage, e);
throw new RuntimeException(errorMessage, e);
}
else {
LOG.error(errorMessage);
throw new RuntimeException(errorMessage);
}
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoService#hasActiveCreditMemosForPurchaseOrder(java.lang.Integer)
*/
@Override
public boolean hasActiveCreditMemosForPurchaseOrder(Integer purchaseOrderIdentifier){
boolean hasActiveCreditMemos = false;
WorkflowDocument workflowDocument = null;
List<String> docNumbers= creditMemoDao.getActiveCreditMemoDocumentNumbersForPurchaseOrder(purchaseOrderIdentifier);
List<FinancialSystemDocumentHeader> docHeaders = new ArrayList<FinancialSystemDocumentHeader>();
for (String appDocStatus : CreditMemoStatuses.STATUSES_POTENTIALLY_ACTIVE) {
docHeaders.addAll(financialSystemDocumentService.findByApplicationDocumentStatus(appDocStatus));
}
for (FinancialSystemDocumentHeader docHeader : docHeaders) {
if (docNumbers.contains(docHeader.getDocumentNumber())) {
try{
workflowDocument = workflowDocumentService.loadWorkflowDocument(docHeader.getDocumentNumber(), GlobalVariables.getUserSession().getPerson());
}catch(WorkflowException we){
throw new RuntimeException(we);
}
//if the document is not in a non-active status then return true and stop evaluation
if(!(workflowDocument.isCanceled() ||
workflowDocument.isException() ||
workflowDocument.isFinal()) ){
hasActiveCreditMemos = true;
break;
}
}
}
return hasActiveCreditMemos;
}
/**
* @see org.kuali.kfs.module.purap.document.service.CreditMemoCreateService#populateDocumentAfterInit(org.kuali.kfs.module.purap.document.CreditMemoDocument)
*/
@Override
public void populateDocumentAfterInit(VendorCreditMemoDocument cmDocument) {
// make a call to search for expired/closed accounts
HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList = accountsPayableService.getExpiredOrClosedAccountList(cmDocument);
if (cmDocument.isSourceDocumentPaymentRequest()) {
populateDocumentFromPreq(cmDocument, expiredOrClosedAccountList);
}
else if (cmDocument.isSourceDocumentPurchaseOrder()) {
populateDocumentFromPO(cmDocument, expiredOrClosedAccountList);
}
else {
populateDocumentFromVendor(cmDocument);
}
populateDocumentDescription(cmDocument);
// write a note for expired/closed accounts if any exist and add a message stating there were expired/closed accounts at the
// top of the document
accountsPayableService.generateExpiredOrClosedAccountNote(cmDocument, expiredOrClosedAccountList);
// set indicator so a message is displayed for accounts that were replaced due to expired/closed status
if (ObjectUtils.isNotNull(expiredOrClosedAccountList) && !expiredOrClosedAccountList.isEmpty()) {
cmDocument.setContinuationAccountIndicator(true);
}
}
/**
* Populate Credit Memo of type Payment Request.
*
* @param cmDocument - Credit Memo Document to Populate
*/
protected void populateDocumentFromPreq(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
PaymentRequestDocument paymentRequestDocument = paymentRequestService.getPaymentRequestById(cmDocument.getPaymentRequestIdentifier());
cmDocument.getDocumentHeader().setOrganizationDocumentNumber(paymentRequestDocument.getDocumentHeader().getOrganizationDocumentNumber());
cmDocument.setPaymentRequestDocument(paymentRequestDocument);
cmDocument.setPurchaseOrderDocument(paymentRequestDocument.getPurchaseOrderDocument());
cmDocument.setUseTaxIndicator(paymentRequestDocument.isUseTaxIndicator());
// credit memo address taken directly from payment request
cmDocument.setVendorHeaderGeneratedIdentifier(paymentRequestDocument.getVendorHeaderGeneratedIdentifier());
cmDocument.setVendorDetailAssignedIdentifier(paymentRequestDocument.getVendorDetailAssignedIdentifier());
cmDocument.setVendorAddressGeneratedIdentifier(paymentRequestDocument.getVendorAddressGeneratedIdentifier());
cmDocument.setVendorCustomerNumber(paymentRequestDocument.getVendorCustomerNumber());
cmDocument.setVendorName(paymentRequestDocument.getVendorName());
cmDocument.setVendorLine1Address(paymentRequestDocument.getVendorLine1Address());
cmDocument.setVendorLine2Address(paymentRequestDocument.getVendorLine2Address());
cmDocument.setVendorCityName(paymentRequestDocument.getVendorCityName());
cmDocument.setVendorStateCode(paymentRequestDocument.getVendorStateCode());
cmDocument.setVendorPostalCode(paymentRequestDocument.getVendorPostalCode());
cmDocument.setVendorCountryCode(paymentRequestDocument.getVendorCountryCode());
cmDocument.setVendorAttentionName(paymentRequestDocument.getVendorAttentionName());
cmDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(paymentRequestDocument.getAccountsPayablePurchasingDocumentLinkIdentifier());
// prep the item lines (also collect warnings for later display) this is only done on paymentRequest
purapAccountingService.convertMoneyToPercent(paymentRequestDocument);
populateItemLinesFromPreq(cmDocument, expiredOrClosedAccountList);
}
/**
* Populates the credit memo items from the payment request items.
*
* @param cmDocument - Credit Memo Document to Populate
*/
protected void populateItemLinesFromPreq(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
PaymentRequestDocument preqDocument = cmDocument.getPaymentRequestDocument();
for (PaymentRequestItem preqItemToTemplate : (List<PaymentRequestItem>) preqDocument.getItems()) {
preqItemToTemplate.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
if (preqItemToTemplate.getItemType().isLineItemIndicator() && ((preqItemToTemplate.getItemType().isQuantityBasedGeneralLedgerIndicator() && preqItemToTemplate.getItemQuantity().isNonZero())
|| (preqItemToTemplate.getItemType().isAmountBasedGeneralLedgerIndicator() && preqItemToTemplate.getTotalAmount().isNonZero()))) {
cmDocument.getItems().add(new CreditMemoItem(cmDocument, preqItemToTemplate, preqItemToTemplate.getPurchaseOrderItem(), expiredOrClosedAccountList));
}
}
// add below the line items
purapService.addBelowLineItems(cmDocument);
cmDocument.fixItemReferences();
}
/**
* Populate Credit Memo of type Purchase Order.
*
* @param cmDocument - Credit Memo Document to Populate
*/
protected void populateDocumentFromPO(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
PurchaseOrderDocument purchaseOrderDocument = purchaseOrderService.getCurrentPurchaseOrder(cmDocument.getPurchaseOrderIdentifier());
cmDocument.setPurchaseOrderDocument(purchaseOrderDocument);
cmDocument.getDocumentHeader().setOrganizationDocumentNumber(purchaseOrderDocument.getDocumentHeader().getOrganizationDocumentNumber());
cmDocument.setUseTaxIndicator(cmDocument.isUseTaxIndicator());
cmDocument.setVendorHeaderGeneratedIdentifier(purchaseOrderDocument.getVendorHeaderGeneratedIdentifier());
cmDocument.setVendorDetailAssignedIdentifier(purchaseOrderDocument.getVendorDetailAssignedIdentifier());
cmDocument.setVendorCustomerNumber(purchaseOrderDocument.getVendorCustomerNumber());
cmDocument.setVendorName(purchaseOrderDocument.getVendorName());
cmDocument.setAccountsPayablePurchasingDocumentLinkIdentifier(purchaseOrderDocument.getAccountsPayablePurchasingDocumentLinkIdentifier());
// populate cm vendor address with the default remit address type for the vendor if found
String userCampus = GlobalVariables.getUserSession().getPerson().getCampusCode();
VendorAddress vendorAddress = vendorService.getVendorDefaultAddress(purchaseOrderDocument.getVendorHeaderGeneratedIdentifier(), purchaseOrderDocument.getVendorDetailAssignedIdentifier(), VendorConstants.AddressTypes.REMIT, userCampus,false);
if (vendorAddress != null) {
cmDocument.templateVendorAddress(vendorAddress);
cmDocument.setVendorAddressGeneratedIdentifier(vendorAddress.getVendorAddressGeneratedIdentifier());
cmDocument.setVendorAttentionName(StringUtils.defaultString(vendorAddress.getVendorAttentionName()));
}
else {
// set address from PO
cmDocument.setVendorAddressGeneratedIdentifier(purchaseOrderDocument.getVendorAddressGeneratedIdentifier());
cmDocument.setVendorLine1Address(purchaseOrderDocument.getVendorLine1Address());
cmDocument.setVendorLine2Address(purchaseOrderDocument.getVendorLine2Address());
cmDocument.setVendorCityName(purchaseOrderDocument.getVendorCityName());
cmDocument.setVendorStateCode(purchaseOrderDocument.getVendorStateCode());
cmDocument.setVendorPostalCode(purchaseOrderDocument.getVendorPostalCode());
cmDocument.setVendorCountryCode(purchaseOrderDocument.getVendorCountryCode());
boolean blankAttentionLine = StringUtils.equalsIgnoreCase("Y",SpringContext.getBean(ParameterService.class).getParameterValueAsString(PurapConstants.PURAP_NAMESPACE, "Document", PurapParameterConstants.BLANK_ATTENTION_LINE_FOR_PO_TYPE_ADDRESS));
if (blankAttentionLine){
cmDocument.setVendorAttentionName(StringUtils.EMPTY);
}else{
cmDocument.setVendorAttentionName(StringUtils.defaultString(purchaseOrderDocument.getVendorAttentionName()));
}
}
populateItemLinesFromPO(cmDocument, expiredOrClosedAccountList);
}
/**
* Populates the credit memo items from the payment request items.
*
* @param cmDocument - Credit Memo Document to Populate
*/
protected void populateItemLinesFromPO(VendorCreditMemoDocument cmDocument, HashMap<String, ExpiredOrClosedAccountEntry> expiredOrClosedAccountList) {
List<PurchaseOrderItem> invoicedItems = getPOInvoicedItems(cmDocument.getPurchaseOrderDocument());
for (PurchaseOrderItem poItem : invoicedItems) {
poItem.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
if ((poItem.getItemType().isQuantityBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalQuantity().isNonZero())
|| (poItem.getItemType().isAmountBasedGeneralLedgerIndicator() && poItem.getItemInvoicedTotalAmount().isNonZero())) {
CreditMemoItem creditMemoItem = new CreditMemoItem(cmDocument, poItem, expiredOrClosedAccountList);
cmDocument.getItems().add(creditMemoItem);
PurchasingCapitalAssetItem purchasingCAMSItem = cmDocument.getPurchaseOrderDocument().getPurchasingCapitalAssetItemByItemIdentifier(poItem.getItemIdentifier());
if (purchasingCAMSItem != null) {
creditMemoItem.setCapitalAssetTransactionTypeCode(purchasingCAMSItem.getCapitalAssetTransactionTypeCode());
}
}
}
// add below the line items
purapService.addBelowLineItems(cmDocument);
cmDocument.fixItemReferences();
}
/**
* Populate Credit Memo of type Vendor.
*
* @param cmDocument - Credit Memo Document to Populate
*/
protected void populateDocumentFromVendor(VendorCreditMemoDocument cmDocument) {
Integer vendorHeaderId = VendorUtils.getVendorHeaderId(cmDocument.getVendorNumber());
Integer vendorDetailId = VendorUtils.getVendorDetailId(cmDocument.getVendorNumber());
VendorDetail vendorDetail = vendorService.getVendorDetail(vendorHeaderId, vendorDetailId);
cmDocument.setVendorDetail(vendorDetail);
cmDocument.setVendorHeaderGeneratedIdentifier(vendorDetail.getVendorHeaderGeneratedIdentifier());
cmDocument.setVendorDetailAssignedIdentifier(vendorDetail.getVendorDetailAssignedIdentifier());
cmDocument.setVendorCustomerNumber(vendorDetail.getVendorNumber());
cmDocument.setVendorName(vendorDetail.getVendorName());
// credit memo type vendor uses the default remit type address for the vendor if found
String userCampus = GlobalVariables.getUserSession().getPerson().getCampusCode();
VendorAddress vendorAddress = vendorService.getVendorDefaultAddress(vendorHeaderId, vendorDetailId, VendorConstants.AddressTypes.REMIT, userCampus,false);
if (vendorAddress == null) {
// pick up the default vendor po address type
vendorAddress = vendorService.getVendorDefaultAddress(vendorHeaderId, vendorDetailId, VendorConstants.AddressTypes.PURCHASE_ORDER, userCampus,false);
}
cmDocument.setVendorAddressGeneratedIdentifier(vendorAddress.getVendorAddressGeneratedIdentifier());
cmDocument.templateVendorAddress(vendorAddress);
// add below the line items
purapService.addBelowLineItems(cmDocument);
}
/**
* Defaults the document description based on the credit memo source type.
*
* @param cmDocument - Credit Memo Document to Populate
*/
protected void populateDocumentDescription(VendorCreditMemoDocument cmDocument) {
String description = "";
if (cmDocument.isSourceVendor()) {
description = "Vendor: " + cmDocument.getVendorName();
}
else {
description = "PO: " + cmDocument.getPurchaseOrderDocument().getPurapDocumentIdentifier() + " Vendor: " + cmDocument.getVendorName();
}
// trim description if longer than whats specified in the data dictionary
int noteTextMaxLength = dataDictionaryService.getAttributeMaxLength(DocumentHeader.class, KRADPropertyConstants.DOCUMENT_DESCRIPTION).intValue();
if (noteTextMaxLength < description.length()) {
description = description.substring(0, noteTextMaxLength);
}
cmDocument.getDocumentHeader().setDocumentDescription(description);
}
}