/*
* 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;
import java.sql.Timestamp;
import java.util.List;
import org.kuali.kfs.module.purap.PurapPropertyConstants;
import org.kuali.kfs.module.purap.businessobject.AccountsPayableItem;
import org.kuali.kfs.module.purap.businessobject.PurApItemUseTax;
import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
import org.kuali.kfs.module.purap.document.service.AccountsPayableDocumentSpecificService;
import org.kuali.kfs.module.purap.document.service.PurapService;
import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.Bank;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.vnd.businessobject.CampusParameter;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.framework.postprocessor.DocumentRouteLevelChange;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Accounts Payable Document Base
*/
public abstract class AccountsPayableDocumentBase extends PurchasingAccountsPayableDocumentBase implements AccountsPayableDocument {
private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountsPayableDocumentBase.class);
// SHARED FIELDS BETWEEN PAYMENT REQUEST AND CREDIT MEMO
protected Timestamp accountsPayableApprovalTimestamp;
protected String lastActionPerformedByPersonId;
protected String accountsPayableProcessorIdentifier;
protected boolean holdIndicator;
protected Timestamp extractedTimestamp;
protected Integer purchaseOrderIdentifier;
protected String processingCampusCode;
protected String noteLine1Text;
protected String noteLine2Text;
protected String noteLine3Text;
protected boolean continuationAccountIndicator;
protected boolean closePurchaseOrderIndicator;
protected boolean reopenPurchaseOrderIndicator;
protected String bankCode;
protected boolean unmatchedOverride; // not persisted
// NOT PERSISTED IN DB
// BELOW USED BY ROUTING
protected String chartOfAccountsCode;
protected String organizationCode;
// NOT PERSISTED IN DB
// BELOW USED BY GL ENTRY CREATION
protected boolean generateEncumbranceEntries;
protected String debitCreditCodeForGLEntries;
protected PurApItemUseTax offsetUseTax;
// REFERENCE OBJECTS
protected CampusParameter processingCampus;
protected transient PurchaseOrderDocument purchaseOrderDocument;
protected Bank bank;
/**
* Constructs a AccountsPayableDocumentBase
*/
public AccountsPayableDocumentBase() {
super();
setUnmatchedOverride(false);
}
public void setLineItemTotal(KualiDecimal total) {
// do nothing, this is so that the jsp won't complain about lineItemTotal have no setter method.
}
public void setGrandTotal(KualiDecimal total) {
// do nothing, this is so that the jsp won't complain about grandTotal have no setter method.
}
/**
* Overriding to stop the deleting of general ledger entries.
*
* @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#removeGeneralLedgerPendingEntries()
*/
@Override
protected void removeGeneralLedgerPendingEntries() {
// do not delete entries for PREQ or CM (hjs)
}
/**
* @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#requiresAccountsPayableReviewRouting()
*/
@Override
public boolean requiresAccountsPayableReviewRouting() {
return !approvalAtAccountsPayableReviewAllowed();
}
/**
* @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#approvalAtAccountsPayableReviewAllowed()
*/
@Override
public boolean approvalAtAccountsPayableReviewAllowed() {
return !(isAttachmentRequired() && documentHasNoImagesAttached());
}
/**
* Checks whether an attachment is required
*
* @return - true if attachment is required, otherwise false
*/
protected abstract boolean isAttachmentRequired();
/**
* Checks all documents notes for attachments and to be overriden by sub class
*
* @return - true if document does not have an image attached, false otherwise
*/
public abstract boolean documentHasNoImagesAttached();
/**
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#populateDocumentForRouting()
*/
@Override
public void populateDocumentForRouting() {
if (ObjectUtils.isNotNull(getPurchaseOrderDocument())) {
this.setChartOfAccountsCode(getPurchaseOrderDocument().getChartOfAccountsCode());
this.setOrganizationCode(getPurchaseOrderDocument().getOrganizationCode());
if (ObjectUtils.isNull(this.getPurchaseOrderDocument().getDocumentHeader().getDocumentNumber())) {
this.getPurchaseOrderDocument().refreshReferenceObject(KFSPropertyConstants.DOCUMENT_HEADER);
}
}
super.populateDocumentForRouting();
}
/**
* Calls a custom prepare for save method, as the super class does GL entry creation that causes problems with AP documents.
*
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#prepareForSave(org.kuali.rice.krad.rule.event.KualiDocumentEvent)
*/
@Override
public void prepareForSave(KualiDocumentEvent event) {
// copied from super because we can't call super for AP docs
customPrepareForSave(event);
// DO NOT CALL SUPER HERE!! Cannot call super because it will mess up the GL entry creation process (hjs)
// super.prepareForSave(event);
}
/**
* Helper method to be called from custom prepare for save and to be overriden by sub class.
*
* @return - Po Document Type
*/
public abstract String getPoDocumentTypeForAccountsPayableDocumentCancel();
/**
* @see org.kuali.rice.krad.document.DocumentBase#handleRouteLevelChange(org.kuali.rice.kew.clientapp.vo.DocumentRouteLevelChangeDTO)
*/
@Override
public void doRouteLevelChange(DocumentRouteLevelChange levelChangeEvent) {
LOG.debug("handleRouteLevelChange() started");
super.doRouteLevelChange(levelChangeEvent);
//process node change for documents
String newNodeName = levelChangeEvent.getNewNodeName();
processNodeChange(newNodeName, levelChangeEvent.getOldNodeName());
// KFSMI-9715 - need to call this after processNodeChange, otherwise if PO is closed while processing PREQ
// it gets saved before encumbrence is relieved, and the Total Encumbrance Amount Relieved and TotalPaidAmount
// on the PREQ didn't reflect the invoice amount, and the amount paid on the PO wasn't being set correctly.
saveDocumentFromPostProcessing();
}
/**
* Hook to allow processing after a route level is passed.
*
* @param newNodeName - current route level
* @param oldNodeName - previous route level
* @return - true if process completes to valid state
*/
public abstract boolean processNodeChange(String newNodeName, String oldNodeName);
/**
* Retrieves node details object based on name.
*
* @param nodeName - route level
* @return - Information about the supplied route level
*/
//public abstract NodeDetails getNodeDetailEnum(String nodeName);
/**
* Hook point to allow processing after a save.
*/
public abstract void saveDocumentFromPostProcessing();
// GETTERS AND SETTERS
@Override
public Integer getPurchaseOrderIdentifier() {
return purchaseOrderIdentifier;
}
@Override
public void setPurchaseOrderIdentifier(Integer purchaseOrderIdentifier) {
this.purchaseOrderIdentifier = purchaseOrderIdentifier;
}
@Override
public String getAccountsPayableProcessorIdentifier() {
return accountsPayableProcessorIdentifier;
}
@Override
public void setAccountsPayableProcessorIdentifier(String accountsPayableProcessorIdentifier) {
this.accountsPayableProcessorIdentifier = accountsPayableProcessorIdentifier;
}
@Override
public String getLastActionPerformedByPersonId() {
return lastActionPerformedByPersonId;
}
@Override
public void setLastActionPerformedByPersonId(String lastActionPerformedByPersonId) {
this.lastActionPerformedByPersonId = lastActionPerformedByPersonId;
}
@Override
public String getProcessingCampusCode() {
return processingCampusCode;
}
@Override
public void setProcessingCampusCode(String processingCampusCode) {
this.processingCampusCode = processingCampusCode;
}
@Override
public Timestamp getAccountsPayableApprovalTimestamp() {
return accountsPayableApprovalTimestamp;
}
@Override
public void setAccountsPayableApprovalTimestamp(Timestamp accountsPayableApprovalTimestamp) {
this.accountsPayableApprovalTimestamp = accountsPayableApprovalTimestamp;
}
@Override
public Timestamp getExtractedTimestamp() {
return extractedTimestamp;
}
@Override
public void setExtractedTimestamp(Timestamp extractedTimestamp) {
this.extractedTimestamp = extractedTimestamp;
}
@Override
public boolean isHoldIndicator() {
return holdIndicator;
}
@Override
public void setHoldIndicator(boolean holdIndicator) {
this.holdIndicator = holdIndicator;
}
@Override
public String getNoteLine1Text() {
return noteLine1Text;
}
@Override
public void setNoteLine1Text(String noteLine1Text) {
this.noteLine1Text = noteLine1Text;
}
@Override
public String getNoteLine2Text() {
return noteLine2Text;
}
@Override
public void setNoteLine2Text(String noteLine2Text) {
this.noteLine2Text = noteLine2Text;
}
@Override
public String getNoteLine3Text() {
return noteLine3Text;
}
@Override
public void setNoteLine3Text(String noteLine3Text) {
this.noteLine3Text = noteLine3Text;
}
@Override
public CampusParameter getProcessingCampus() {
return processingCampus;
}
public String getChartOfAccountsCode() {
return chartOfAccountsCode;
}
public void setChartOfAccountsCode(String chartOfAccountsCode) {
this.chartOfAccountsCode = chartOfAccountsCode;
}
public String getOrganizationCode() {
return organizationCode;
}
public void setOrganizationCode(String organizationCode) {
this.organizationCode = organizationCode;
}
public boolean isGenerateEncumbranceEntries() {
return generateEncumbranceEntries;
}
public void setGenerateEncumbranceEntries(boolean generateEncumbranceEntries) {
this.generateEncumbranceEntries = generateEncumbranceEntries;
}
/**
* @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getPurchaseOrderDocument()
*/
@Override
public PurchaseOrderDocument getPurchaseOrderDocument() {
if ((ObjectUtils.isNull(purchaseOrderDocument) || ObjectUtils.isNull(purchaseOrderDocument.getPurapDocumentIdentifier())) && (ObjectUtils.isNotNull(getPurchaseOrderIdentifier()))) {
setPurchaseOrderDocument(SpringContext.getBean(PurchaseOrderService.class).getCurrentPurchaseOrder(this.getPurchaseOrderIdentifier()));
}
return purchaseOrderDocument;
}
/**
* @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#setPurchaseOrderDocument(org.kuali.kfs.module.purap.document.PurchaseOrderDocument)
*/
@Override
public void setPurchaseOrderDocument(PurchaseOrderDocument purchaseOrderDocument) {
if (ObjectUtils.isNull(purchaseOrderDocument)) {
// KUALI-PURAP 1185 PO Id not being set to null, instead throwing error on main screen that value is invalid.
// setPurchaseOrderIdentifier(null);
this.purchaseOrderDocument = null;
}
else {
if (ObjectUtils.isNotNull(purchaseOrderDocument.getPurapDocumentIdentifier())) {
setPurchaseOrderIdentifier(purchaseOrderDocument.getPurapDocumentIdentifier());
}
this.purchaseOrderDocument = purchaseOrderDocument;
}
}
public boolean isClosePurchaseOrderIndicator() {
return closePurchaseOrderIndicator;
}
public void setClosePurchaseOrderIndicator(boolean closePurchaseOrderIndicator) {
this.closePurchaseOrderIndicator = closePurchaseOrderIndicator;
}
public boolean isReopenPurchaseOrderIndicator() {
return reopenPurchaseOrderIndicator;
}
public void setReopenPurchaseOrderIndicator(boolean reopenPurchaseOrderIndicator) {
this.reopenPurchaseOrderIndicator = reopenPurchaseOrderIndicator;
}
public String getBankCode() {
return bankCode;
}
public void setBankCode(String bankCode) {
this.bankCode = bankCode;
}
public Bank getBank() {
return bank;
}
public void setBank(Bank bank) {
this.bank = bank;
}
/**
* Sets the processing campus.
* @deprecated
* @param processingCampus
*/
@Deprecated
public void setProcessingCampus(CampusParameter processingCampus) {
this.processingCampus = processingCampus;
}
// Helper methods
/**
* Retrieves the universal user object for the last person to perform an action on the document.
*/
public Person getLastActionPerformedByUser() {
return SpringContext.getBean(org.kuali.rice.kim.api.identity.PersonService.class).getPerson(getLastActionPerformedByPersonId());
}
/**
* Retrieves the person name for the last person to perform an action on the document.
*
* @return - the person's name who last performed an action on the document.
*/
public String getLastActionPerformedByPersonName() {
Person user = getLastActionPerformedByUser();
if (ObjectUtils.isNull(user)) {
return "";
}
else {
return user.getName();
}
}
public String getDebitCreditCodeForGLEntries() {
return debitCreditCodeForGLEntries;
}
public void setDebitCreditCodeForGLEntries(String debitCreditCodeForGLEntries) {
this.debitCreditCodeForGLEntries = debitCreditCodeForGLEntries;
}
@Override
public boolean isUnmatchedOverride() {
return unmatchedOverride;
}
@Override
public void setUnmatchedOverride(boolean unmatchedOverride) {
this.unmatchedOverride = unmatchedOverride;
}
public boolean getExtractedIndicatorForSearching() {
return extractedTimestamp != null;
}
public boolean isHoldIndicatorForSearching() {
return holdIndicator;
}
/**
* @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getGrandTotal()
*/
@Override
public abstract KualiDecimal getGrandTotal();
/**
* @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getInitialAmount()
*/
@Override
public abstract KualiDecimal getInitialAmount();
@Override
public boolean isContinuationAccountIndicator() {
return continuationAccountIndicator;
}
@Override
public void setContinuationAccountIndicator(boolean continuationAccountIndicator) {
this.continuationAccountIndicator = continuationAccountIndicator;
}
@Override
public boolean isExtracted() {
return (ObjectUtils.isNotNull(getExtractedTimestamp()));
}
@Override
public abstract AccountsPayableDocumentSpecificService getDocumentSpecificService();
@Override
public AccountsPayableItem getAPItemFromPOItem(PurchaseOrderItem poi) {
for (AccountsPayableItem preqItem : (List<AccountsPayableItem>) this.getItems()) {
preqItem.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
if (preqItem.getItemType().isLineItemIndicator()) {
if (preqItem.getItemLineNumber().compareTo(poi.getItemLineNumber()) == 0) {
return preqItem;
}
}
else {
return (AccountsPayableItem) SpringContext.getBean(PurapService.class).getBelowTheLineByType(this, poi.getItemType());
}
}
return null;
}
/**
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getItemClass()
*/
@Override
public Class getItemClass() {
return null;
}
/**
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentIfPossible()
*/
@Override
public PurchasingAccountsPayableDocument getPurApSourceDocumentIfPossible() {
return null;
}
/**
* @see org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase#getPurApSourceDocumentLabelIfPossible()
*/
@Override
public String getPurApSourceDocumentLabelIfPossible() {
return null;
}
public void updateExtendedPriceOnItems() {
for (AccountsPayableItem item : (List<AccountsPayableItem>) getItems()) {
item.refreshReferenceObject(PurapPropertyConstants.ITEM_TYPE);
final KualiDecimal itemExtendedPrice = (item.getExtendedPrice()==null)?KualiDecimal.ZERO:item.getExtendedPrice();
if ( ObjectUtils.isNotNull( item.getItemType() ) ) {
if (item.getItemType().isQuantityBasedGeneralLedgerIndicator() && item.getExtendedPrice()==null ) {
KualiDecimal newExtendedPrice = item.calculateExtendedPrice();
item.setExtendedPrice(newExtendedPrice);
}
}
}
}
/**
*
* @see org.kuali.kfs.module.purap.document.AccountsPayableDocument#getTotalRemitAmount()
*/
@Override
public KualiDecimal getTotalRemitTax() {
if(!this.isUseTaxIndicator()) {
return (KualiDecimal.ZERO.equals(this.getTotalTaxAmount()))?null:this.getTotalTaxAmount();
}
return null;
}
@Override
public boolean customizeOffsetGeneralLedgerPendingEntry(GeneralLedgerPendingEntrySourceDetail accountingLine, GeneralLedgerPendingEntry explicitEntry, GeneralLedgerPendingEntry offsetEntry) {
boolean value = super.customizeOffsetGeneralLedgerPendingEntry(accountingLine, explicitEntry, offsetEntry);
if(offsetEntry != null && this.offsetUseTax != null) {
offsetEntry.setChartOfAccountsCode(this.offsetUseTax.getChartOfAccountsCode());
offsetEntry.refreshReferenceObject(KFSPropertyConstants.CHART);
offsetEntry.setAccountNumber(this.offsetUseTax.getAccountNumber());
offsetEntry.refreshReferenceObject(KFSPropertyConstants.ACCOUNT);
offsetEntry.setFinancialObjectCode(this.offsetUseTax.getFinancialObjectCode());
offsetEntry.refreshReferenceObject(KFSPropertyConstants.FINANCIAL_OBJECT);
} else {
value=false;
}
return value;
}
@Override
public boolean generateGeneralLedgerPendingEntries(GeneralLedgerPendingEntrySourceDetail glpeSourceDetail, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, PurApItemUseTax offsetUseTax) {
this.offsetUseTax = offsetUseTax;
boolean value = this.generateGeneralLedgerPendingEntries(glpeSourceDetail, sequenceHelper);
this.offsetUseTax = null;
return value;
}
public String getHoldIndicatorForResult(){
return isHoldIndicator() ? "Yes" : "No";
}
public String getProcessingCampusCodeForSearch(){
return getProcessingCampusCode();
}
public String getDocumentChartOfAccountsCodeForSearching(){
return getPurchaseOrderDocument().getChartOfAccountsCode();
}
public String getDocumentOrganizationCodeForSearching(){
return getPurchaseOrderDocument().getOrganizationCode();
}
/**
* @return workflow document type for the purap document
*/
public String getDocumentType() {
return SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(this.getClass());
}
@Override
public boolean shouldGiveErrorForEmptyAccountsProration() {
return true;
}
}