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 }