001 /* 002 * Copyright 2011 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 */ 016 package org.kuali.kfs.module.cam.document; 017 018 import java.util.ArrayList; 019 import java.util.Iterator; 020 import java.util.List; 021 022 import org.apache.commons.lang.StringUtils; 023 import org.apache.log4j.Logger; 024 import org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService; 025 import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService; 026 import org.kuali.kfs.module.cam.CamsConstants; 027 import org.kuali.kfs.module.cam.businessobject.AssetPaymentAccountingLineParser; 028 import org.kuali.kfs.module.cam.businessobject.AssetPaymentAssetDetail; 029 import org.kuali.kfs.module.cam.businessobject.AssetPaymentDetail; 030 import org.kuali.kfs.module.cam.document.service.AssetPaymentService; 031 import org.kuali.kfs.module.cam.document.validation.event.AssetPaymentManuallyAddAccountingLineEvent; 032 import org.kuali.kfs.module.cam.util.AssetPaymentDistributor; 033 import org.kuali.kfs.sys.KFSConstants; 034 import org.kuali.kfs.sys.businessobject.AccountingLine; 035 import org.kuali.kfs.sys.businessobject.AccountingLineParser; 036 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySourceDetail; 037 import org.kuali.kfs.sys.context.SpringContext; 038 import org.kuali.kfs.sys.document.AccountingDocumentBase; 039 import org.kuali.kfs.sys.document.AmountTotaling; 040 import org.kuali.rice.kew.dto.DocumentRouteStatusChangeDTO; 041 import org.kuali.rice.kns.document.Copyable; 042 import org.kuali.rice.kns.exception.ValidationException; 043 import org.kuali.rice.kns.rule.event.KualiDocumentEvent; 044 import org.kuali.rice.kns.rule.event.SaveDocumentEvent; 045 import org.kuali.rice.kns.util.KualiDecimal; 046 import org.kuali.rice.kns.util.TypedArrayList; 047 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 048 049 /** 050 * Capital assets document class for the asset payment document 051 */ 052 public class AssetPaymentDocument extends AccountingDocumentBase implements Copyable, AmountTotaling { 053 protected static Logger LOG = Logger.getLogger(AssetPaymentDocument.class); 054 055 protected List<AssetPaymentAssetDetail> assetPaymentAssetDetail; 056 protected Long capitalAssetNumber; 057 protected boolean capitalAssetBuilderOriginIndicator; 058 059 public AssetPaymentDocument() { 060 super(); 061 this.setAssetPaymentAssetDetail(new TypedArrayList(AssetPaymentAssetDetail.class)); 062 } 063 064 /** 065 * Remove asset from collection for deletion 066 * 067 * @see org.kuali.kfs.sys.document.AccountingDocumentBase#buildListOfDeletionAwareLists() 068 */ 069 @Override 070 public List buildListOfDeletionAwareLists() { 071 List<List> deletionAwareList = super.buildListOfDeletionAwareLists(); 072 deletionAwareList.add(this.getAssetPaymentAssetDetail()); 073 return deletionAwareList; 074 } 075 076 /** 077 * When document save, AddAccountingLineEvent is added by the framework. Also, we need to add 078 * AssetPaymentManuallyAddAccountingLineEvent manually to run all relating validations. 079 * 080 * @see org.kuali.kfs.sys.document.AccountingDocumentBase#generateSaveEvents() 081 */ 082 @Override 083 public List generateSaveEvents() { 084 List subEvents = new ArrayList(); 085 // keep the order of events as for validation will run in the same order. 086 if (!isCapitalAssetBuilderOriginIndicator()) { 087 // Add AssetPaymentManuallyAddAccountingLineEvent for each manually added accounting line. 088 String errorPathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_SOURCE_ACCT_LINE_PROPERTY_NAME; 089 int index = 0; 090 for (Iterator i = getSourceAccountingLines().iterator(); i.hasNext(); index++) { 091 String indexedErrorPathPrefix = errorPathPrefix + "[" + index + "]"; 092 AccountingLine currentLine = (AccountingLine) i.next(); 093 AssetPaymentManuallyAddAccountingLineEvent newSubEvent = new AssetPaymentManuallyAddAccountingLineEvent(indexedErrorPathPrefix, this, currentLine); 094 subEvents.add(newSubEvent); 095 } 096 } 097 098 subEvents.addAll(super.generateSaveEvents()); 099 100 return subEvents; 101 } 102 103 /** 104 * Lock on purchase order document since post processor will update PO document by adding notes. 105 * 106 * @see org.kuali.rice.kns.document.DocumentBase#getWorkflowEngineDocumentIdsToLock() 107 */ 108 @Override 109 public List<Long> getWorkflowEngineDocumentIdsToLock() { 110 if (this.isCapitalAssetBuilderOriginIndicator()) { 111 String poDocId = SpringContext.getBean(CapitalAssetBuilderModuleService.class).getCurrentPurchaseOrderDocumentNumber(this.getDocumentNumber()); 112 if (StringUtils.isNotBlank(poDocId)) { 113 List<Long> documentIds = new ArrayList<Long>(); 114 documentIds.add(new Long(poDocId)); 115 return documentIds; 116 } 117 } 118 return null; 119 } 120 121 /** 122 * Determines if the given AccountingLine (as a GeneralLedgerPostable) is a credit or a debit, in terms of GLPE generation 123 * 124 * @see org.kuali.kfs.sys.document.AccountingDocumentBase#isDebit(org.kuali.kfs.bo.GeneralLedgerPostable) 125 */ 126 @Override 127 public boolean isDebit(GeneralLedgerPendingEntrySourceDetail postable) { 128 return false; 129 } 130 131 public boolean isCapitalAssetBuilderOriginIndicator() { 132 return capitalAssetBuilderOriginIndicator; 133 } 134 135 public void setCapitalAssetBuilderOriginIndicator(boolean capitalAssetBuilderOriginIndicator) { 136 this.capitalAssetBuilderOriginIndicator = capitalAssetBuilderOriginIndicator; 137 } 138 139 /** 140 * This method... 141 * 142 * @param assetPaymentAssetDetail 143 */ 144 public void addAssetPaymentAssetDetail(AssetPaymentAssetDetail assetPaymentAssetDetail) { 145 this.getAssetPaymentAssetDetail().add(assetPaymentAssetDetail); 146 } 147 148 /** 149 * @see org.kuali.rice.kns.document.DocumentBase#postProcessSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent) 150 */ 151 @Override 152 public void postProcessSave(KualiDocumentEvent event) { 153 super.postProcessSave(event); 154 155 if (!(event instanceof SaveDocumentEvent)) { // don't lock until they route 156 ArrayList<Long> capitalAssetNumbers = new ArrayList<Long>(); 157 for (AssetPaymentAssetDetail assetPaymentAssetDetail : this.getAssetPaymentAssetDetail()) { 158 if (assetPaymentAssetDetail.getCapitalAssetNumber() != null) { 159 capitalAssetNumbers.add(assetPaymentAssetDetail.getCapitalAssetNumber()); 160 } 161 } 162 163 String documentTypeForLocking = CamsConstants.DocumentTypeName.ASSET_PAYMENT; 164 if (this.isCapitalAssetBuilderOriginIndicator()) { 165 documentTypeForLocking = CamsConstants.DocumentTypeName.ASSET_PAYMENT_FROM_CAB; 166 } 167 168 if (!this.getCapitalAssetManagementModuleService().storeAssetLocks(capitalAssetNumbers, this.getDocumentNumber(), documentTypeForLocking, null)) { 169 throw new ValidationException("Asset " + capitalAssetNumbers.toString() + " is being locked by other documents."); 170 } 171 } 172 } 173 174 protected CapitalAssetManagementModuleService getCapitalAssetManagementModuleService() { 175 return SpringContext.getBean(CapitalAssetManagementModuleService.class); 176 } 177 178 /** 179 * @see org.kuali.kfs.sys.document.GeneralLedgerPostingDocumentBase#doRouteStatusChange() 180 */ 181 @Override 182 public void doRouteStatusChange(DocumentRouteStatusChangeDTO statusChangeEvent) { 183 super.doRouteStatusChange(statusChangeEvent); 184 KualiWorkflowDocument workflowDocument = getDocumentHeader().getWorkflowDocument(); 185 186 // Update asset payment table with the approved asset detail records. 187 if (workflowDocument.stateIsProcessed()) { 188 SpringContext.getBean(AssetPaymentService.class).processApprovedAssetPayment(this); 189 } 190 191 // Remove asset lock when doc status change. We don't include stateIsFinal since document always go to 'processed' first. 192 if (workflowDocument.stateIsCanceled() || workflowDocument.stateIsDisapproved() || workflowDocument.stateIsProcessed()) { 193 this.getCapitalAssetManagementModuleService().deleteAssetLocks(this.getDocumentNumber(), null); 194 } 195 196 if (isCapitalAssetBuilderOriginIndicator()) { 197 SpringContext.getBean(CapitalAssetBuilderModuleService.class).notifyRouteStatusChange(getDocumentHeader()); 198 } 199 } 200 201 /** 202 * @see org.kuali.kfs.sys.document.AccountingDocumentBase#prepareForSave(org.kuali.rice.kns.rule.event.KualiDocumentEvent) 203 */ 204 @Override 205 public void prepareForSave(KualiDocumentEvent event) { 206 // This is an empty method in order to prevent kuali from generating a gl pending entry record. 207 } 208 209 public List<AssetPaymentAssetDetail> getAssetPaymentAssetDetail() { 210 return assetPaymentAssetDetail; 211 } 212 213 public void setAssetPaymentAssetDetail(List<AssetPaymentAssetDetail> assetPaymentAssetDetail) { 214 this.assetPaymentAssetDetail = assetPaymentAssetDetail; 215 } 216 217 public Long getCapitalAssetNumber() { 218 return capitalAssetNumber; 219 } 220 221 public void setCapitalAssetNumber(Long capitalAssetNumber) { 222 this.capitalAssetNumber = capitalAssetNumber; 223 } 224 225 /** 226 * calculates the total previous cost amount of all the assets in the document 227 * 228 * @return KualiDecimal 229 */ 230 public KualiDecimal getAssetsTotalHistoricalCost() { 231 KualiDecimal total = new KualiDecimal(0); 232 if (this.getAssetPaymentAssetDetail().isEmpty()) 233 return new KualiDecimal(0); 234 235 for (AssetPaymentAssetDetail detail : this.getAssetPaymentAssetDetail()) { 236 KualiDecimal amount = (detail.getPreviousTotalCostAmount() == null ? new KualiDecimal(0) : detail.getPreviousTotalCostAmount()); 237 total = total.add(amount); 238 } 239 return total; 240 } 241 242 /** 243 * Get the asset payment distributor built by AssetPaymentDetails, AssetPaymentAssetDetail and 244 * totalHistoricalCost 245 * 246 * @return AssetPaymentDistributor 247 */ 248 public AssetPaymentDistributor getAssetPaymentDistributor() { 249 250 // instantiate the distributor and calculate the asset payment distributions 251 AssetPaymentDistributor paymentDistributor = new AssetPaymentDistributor(getSourceAccountingLines(), 252 getAssetPaymentAssetDetail(), getAssetsTotalHistoricalCost()); 253 254 return paymentDistributor; 255 } 256 }