/*
* 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.cam.document;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService;
import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService;
import org.kuali.kfs.module.cam.CamsConstants;
import org.kuali.kfs.module.cam.CamsPropertyConstants;
import org.kuali.kfs.module.cam.businessobject.AssetPaymentAllocationType;
import org.kuali.kfs.module.cam.businessobject.AssetPaymentAssetDetail;
import org.kuali.kfs.module.cam.businessobject.AssetPaymentDetail;
import org.kuali.kfs.module.cam.document.service.AssetPaymentService;
import org.kuali.kfs.module.cam.document.validation.event.AssetPaymentManuallyAddAccountingLineEvent;
import org.kuali.kfs.module.cam.util.distribution.AssetDistribution;
import org.kuali.kfs.module.cam.util.distribution.AssetDistributionEvenly;
import org.kuali.kfs.module.cam.util.distribution.AssetDistributionManual;
import org.kuali.kfs.module.cam.util.distribution.AssetDistributionPercent;
import org.kuali.kfs.module.cam.util.distribution.AssetPaymentDistributionByTotalCost;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.AccountingDocumentBase;
import org.kuali.kfs.sys.document.AmountTotaling;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.kew.api.WorkflowDocument;
import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
import org.kuali.rice.krad.document.Copyable;
import org.kuali.rice.krad.exception.ValidationException;
import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
import org.kuali.rice.krad.rules.rule.event.SaveDocumentEvent;
import org.kuali.rice.krad.util.ObjectUtils;
/**
* Capital assets document class for the asset payment document
*/
public class AssetPaymentDocument extends AccountingDocumentBase implements Copyable, AmountTotaling {
protected static Logger LOG = Logger.getLogger(AssetPaymentDocument.class);
protected List<AssetPaymentAssetDetail> assetPaymentAssetDetail;
protected Long capitalAssetNumber;
protected boolean capitalAssetBuilderOriginIndicator;
protected AssetPaymentAllocationType assetPaymentAllocationType;
protected String assetPaymentAllocationTypeCode;
protected boolean allocationFromFPDocuments;
public AssetPaymentDocument() {
super();
this.setAllocationFromFPDocuments(false);
assetPaymentAllocationTypeCode = CamsPropertyConstants.AssetPaymentAllocation.ASSET_DISTRIBUTION_DEFAULT_CODE;
this.setAssetPaymentAssetDetail(new ArrayList<AssetPaymentAssetDetail>());
}
/**
* Remove asset from collection for deletion
*
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#buildListOfDeletionAwareLists()
*/
@Override
public List buildListOfDeletionAwareLists() {
List<List> deletionAwareList = super.buildListOfDeletionAwareLists();
deletionAwareList.add(this.getAssetPaymentAssetDetail());
return deletionAwareList;
}
/**
* When document save, AddAccountingLineEvent is added by the framework.
* Also, we need to add AssetPaymentManuallyAddAccountingLineEvent manually
* to run all relating validations.
*
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#generateSaveEvents()
*/
@Override
public List generateSaveEvents() {
List subEvents = new ArrayList();
// keep the order of events as for validation will run in the same
// order.
if (!isCapitalAssetBuilderOriginIndicator()) {
// Add AssetPaymentManuallyAddAccountingLineEvent for each manually
// added accounting line.
String errorPathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_SOURCE_ACCT_LINE_PROPERTY_NAME;
int index = 0;
for (Iterator i = getSourceAccountingLines().iterator(); i.hasNext(); index++) {
String indexedErrorPathPrefix = errorPathPrefix + "[" + index + "]";
AccountingLine currentLine = (AccountingLine) i.next();
AssetPaymentManuallyAddAccountingLineEvent newSubEvent = new AssetPaymentManuallyAddAccountingLineEvent(indexedErrorPathPrefix, this, currentLine);
subEvents.add(newSubEvent);
}
}
subEvents.addAll(super.generateSaveEvents());
return subEvents;
}
/**
* Lock on purchase order document since post processor will update PO
* document by adding notes.
*
* @see org.kuali.rice.krad.document.DocumentBase#getWorkflowEngineDocumentIdsToLock()
*/
@Override
public List<String> getWorkflowEngineDocumentIdsToLock() {
List<String> documentIds = null;
if (this.isCapitalAssetBuilderOriginIndicator()) {
String poDocId = SpringContext.getBean(CapitalAssetBuilderModuleService.class).getCurrentPurchaseOrderDocumentNumber(this.getDocumentNumber());
if (StringUtils.isNotBlank(poDocId)) {
documentIds = new ArrayList<String>();
documentIds.add(poDocId);
}
}
return documentIds;
}
/**
* Determines if the given AccountingLine (as a GeneralLedgerPostable) is a
* credit or a debit, in terms of GLPE generation
*
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#isDebit(org.kuali.kfs.bo.GeneralLedgerPostable)
*/
@Override
public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) {
return false;
}
public boolean isCapitalAssetBuilderOriginIndicator() {
return capitalAssetBuilderOriginIndicator;
}
public void setCapitalAssetBuilderOriginIndicator(boolean capitalAssetBuilderOriginIndicator) {
this.capitalAssetBuilderOriginIndicator = capitalAssetBuilderOriginIndicator;
}
/**
* This method...
*
* @param assetPaymentAssetDetail
*/
public void addAssetPaymentAssetDetail(AssetPaymentAssetDetail assetPaymentAssetDetail) {
this.getAssetPaymentAssetDetail().add(assetPaymentAssetDetail);
}
/**
* @see org.kuali.rice.krad.document.DocumentBase#postProcessSave(org.kuali.rice.krad.rule.event.KualiDocumentEvent)
*/
@Override
public void postProcessSave(KualiDocumentEvent event) {
super.postProcessSave(event);
if (!(event instanceof SaveDocumentEvent)) { // don't lock until they
// route
ArrayList<Long> capitalAssetNumbers = new ArrayList<Long>();
for (AssetPaymentAssetDetail assetPaymentAssetDetail : this.getAssetPaymentAssetDetail()) {
if (assetPaymentAssetDetail.getCapitalAssetNumber() != null) {
capitalAssetNumbers.add(assetPaymentAssetDetail.getCapitalAssetNumber());
}
}
String documentTypeForLocking = CamsConstants.DocumentTypeName.ASSET_PAYMENT;
if (this.isCapitalAssetBuilderOriginIndicator()) {
documentTypeForLocking = CamsConstants.DocumentTypeName.ASSET_PAYMENT_FROM_CAB;
}
if (!this.getCapitalAssetManagementModuleService().storeAssetLocks(capitalAssetNumbers, this.getDocumentNumber(), documentTypeForLocking, null)) {
throw new ValidationException("Asset " + capitalAssetNumbers.toString() + " is being locked by other documents.");
}
}
}
protected CapitalAssetManagementModuleService getCapitalAssetManagementModuleService() {
return SpringContext.getBean(CapitalAssetManagementModuleService.class);
}
/**
* @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#doRouteStatusChange()
*/
@Override
public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
super.doRouteStatusChange(statusChangeEvent);
WorkflowDocument workflowDocument = getDocumentHeader().getWorkflowDocument();
// Update asset payment table with the approved asset detail records.
if (workflowDocument.isProcessed()) {
SpringContext.getBean(AssetPaymentService.class).processApprovedAssetPayment(this);
}
// Remove asset lock when doc status change. We don't include
// isFinal since document always go to 'processed' first.
if (workflowDocument.isCanceled() || workflowDocument.isDisapproved() || workflowDocument.isProcessed()) {
this.getCapitalAssetManagementModuleService().deleteAssetLocks(this.getDocumentNumber(), null);
}
if (isCapitalAssetBuilderOriginIndicator()) {
SpringContext.getBean(CapitalAssetBuilderModuleService.class).notifyRouteStatusChange(getDocumentHeader());
}
}
/**
* @see org.kuali.kfs.sys.document.AccountingDocumentBase#prepareForSave(org.kuali.rice.krad.rule.event.KualiDocumentEvent)
*/
@Override
public void prepareForSave(KualiDocumentEvent event) {
// This method prevents kuali from generating a
// gl pending entry record.
for (AssetPaymentAssetDetail assetDetail : this.getAssetPaymentAssetDetail()) {
assetDetail.refreshReferenceObject(CamsPropertyConstants.AssetPaymentAssetDetail.ASSET);
if (ObjectUtils.isNotNull(assetDetail.getAsset()) && assetDetail.getAsset().getTotalCostAmount() != null) {
assetDetail.setPreviousTotalCostAmount(assetDetail.getAsset().getTotalCostAmount());
}
// CSU 6702 BEGIN Inferred change
List<AssetPaymentDetail> apdList = assetDetail.getAssetPaymentDetails();
for (AssetPaymentDetail apd : apdList) {
String accountingPeriodCompositeString = getAccountingPeriodCompositeString();
apd.setPostingYear(new Integer(StringUtils.right(accountingPeriodCompositeString, 4)));
apd.setPostingPeriodCode(StringUtils.left(accountingPeriodCompositeString, 2));
}
// CSU 6702 END Inferred change
}
}
public List<AssetPaymentAssetDetail> getAssetPaymentAssetDetail() {
return assetPaymentAssetDetail;
}
public void setAssetPaymentAssetDetail(List<AssetPaymentAssetDetail> assetPaymentAssetDetail) {
this.assetPaymentAssetDetail = assetPaymentAssetDetail;
}
public Long getCapitalAssetNumber() {
return capitalAssetNumber;
}
public void setCapitalAssetNumber(Long capitalAssetNumber) {
this.capitalAssetNumber = capitalAssetNumber;
}
/**
* calculates the total previous cost amount of all the assets in the
* document
*
* @return KualiDecimal
*/
public KualiDecimal getAssetsTotalHistoricalCost() {
KualiDecimal total = new KualiDecimal(0);
if (this.getAssetPaymentAssetDetail().isEmpty())
return new KualiDecimal(0);
for (AssetPaymentAssetDetail detail : this.getAssetPaymentAssetDetail()) {
KualiDecimal amount = (detail.getPreviousTotalCostAmount() == null ? new KualiDecimal(0) : detail.getPreviousTotalCostAmount());
total = total.add(amount);
}
return total;
}
/**
* Get the asset payment distributor built by AssetPaymentDetails,
* AssetPaymentAssetDetail and totalHistoricalCost
*
* @return AssetPaymentDistributor
*/
public AssetDistribution getAssetPaymentDistributor() {
if (CamsPropertyConstants.AssetPaymentAllocation.ASSET_DISTRIBUTION_BY_AMOUNT_CODE.equals(getAssetPaymentAllocationTypeCode())) {
return new AssetDistributionManual(this);
} else if (CamsPropertyConstants.AssetPaymentAllocation.ASSET_DISTRIBUTION_BY_PERCENTAGE_CODE.equals(getAssetPaymentAllocationTypeCode())) {
return new AssetDistributionPercent(this);
} else if (CamsPropertyConstants.AssetPaymentAllocation.ASSET_DISTRIBUTION_BY_TOTAL_COST_CODE.equals(getAssetPaymentAllocationTypeCode())) {
return new AssetPaymentDistributionByTotalCost(this);
}
return new AssetDistributionEvenly(this);
}
/**
* Get the allocation code value
*/
public String getAssetPaymentAllocationTypeCode() {
return assetPaymentAllocationTypeCode;
}
/**
* Set the allocation code value
*/
public void setAssetPaymentAllocationTypeCode(String code) {
assetPaymentAllocationTypeCode = code;
refreshReferenceObject("assetPaymentAllocationType");
}
/**
* Sets the asset allocation type
*/
public void setAssetPaymentAllocationType(AssetPaymentAllocationType assetPaymentAllocationType) {
this.assetPaymentAllocationType = assetPaymentAllocationType;
}
/**
* Get the asset allocation type
*/
public AssetPaymentAllocationType getAssetPaymentAllocationType() {
return assetPaymentAllocationType;
}
/**
* Gets the allocationFromFPDocuments attribute.
*
* @return Returns the allocationFromFPDocuments
*/
public boolean isAllocationFromFPDocuments() {
return allocationFromFPDocuments;
}
/**
* Sets the allocationFromFPDocuments attribute.
*
* @param allocationFromFPDocuments The allocationFromFPDocuments to set.
*/
public void setAllocationFromFPDocuments(boolean allocationFromFPDocuments) {
this.allocationFromFPDocuments = allocationFromFPDocuments;
}
}