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.sql.Date;
019 import java.util.ArrayList;
020 import java.util.HashMap;
021 import java.util.List;
022 import java.util.Map;
023
024 import org.apache.commons.lang.StringUtils;
025 import org.kuali.kfs.coa.businessobject.ObjectCode;
026 import org.kuali.kfs.coa.service.ObjectCodeService;
027 import org.kuali.kfs.integration.cab.CapitalAssetBuilderModuleService;
028 import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService;
029 import org.kuali.kfs.module.cab.CabPropertyConstants;
030 import org.kuali.kfs.module.cab.businessobject.PretagDetail;
031 import org.kuali.kfs.module.cam.CamsConstants;
032 import org.kuali.kfs.module.cam.CamsPropertyConstants;
033 import org.kuali.kfs.module.cam.businessobject.Asset;
034 import org.kuali.kfs.module.cam.businessobject.AssetDepreciationConvention;
035 import org.kuali.kfs.module.cam.businessobject.AssetGlobal;
036 import org.kuali.kfs.module.cam.businessobject.AssetGlobalDetail;
037 import org.kuali.kfs.module.cam.businessobject.AssetOrganization;
038 import org.kuali.kfs.module.cam.businessobject.AssetPayment;
039 import org.kuali.kfs.module.cam.businessobject.AssetPaymentDetail;
040 import org.kuali.kfs.module.cam.businessobject.AssetType;
041 import org.kuali.kfs.module.cam.businessobject.defaultvalue.NextAssetNumberFinder;
042 import org.kuali.kfs.module.cam.document.gl.AssetGlobalGeneralLedgerPendingEntrySource;
043 import org.kuali.kfs.module.cam.document.service.AssetDateService;
044 import org.kuali.kfs.module.cam.document.service.AssetGlobalService;
045 import org.kuali.kfs.module.cam.document.validation.impl.AssetGlobalRule;
046 import org.kuali.kfs.module.cam.util.KualiDecimalUtils;
047 import org.kuali.kfs.sys.KFSConstants;
048 import org.kuali.kfs.sys.KFSPropertyConstants;
049 import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
050 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
051 import org.kuali.kfs.sys.context.SpringContext;
052 import org.kuali.kfs.sys.document.LedgerPostingMaintainable;
053 import org.kuali.rice.kns.bo.DocumentHeader;
054 import org.kuali.rice.kns.bo.PersistableBusinessObject;
055 import org.kuali.rice.kns.document.MaintenanceDocument;
056 import org.kuali.rice.kns.document.MaintenanceLock;
057 import org.kuali.rice.kns.service.BusinessObjectService;
058 import org.kuali.rice.kns.service.DateTimeService;
059 import org.kuali.rice.kns.util.KNSConstants;
060 import org.kuali.rice.kns.util.KualiDecimal;
061 import org.kuali.rice.kns.util.ObjectUtils;
062 import org.kuali.rice.kns.util.TypedArrayList;
063 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
064
065 /**
066 * This class overrides the base {@link KualiGlobalMaintainableImpl} to generate the specific maintenance locks for Global assets
067 */
068 public class AssetGlobalMaintainableImpl extends LedgerPostingMaintainable {
069 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetGlobalMaintainableImpl.class);
070
071 protected static final String REQUIRES_REVIEW = "RequiresReview";
072
073 /**
074 * Lock on purchase order document since post processor will update PO document by adding notes.
075 *
076 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#getWorkflowEngineDocumentIdsToLock()
077 */
078 @Override
079 public List<Long> getWorkflowEngineDocumentIdsToLock() {
080 AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
081 if (ObjectUtils.isNotNull(assetGlobal) && assetGlobal.isCapitalAssetBuilderOriginIndicator()) {
082 String poDocId = SpringContext.getBean(CapitalAssetBuilderModuleService.class).getCurrentPurchaseOrderDocumentNumber(this.documentNumber);
083 if (StringUtils.isNotBlank(poDocId)) {
084 List<Long> documentIds = new ArrayList<Long>();
085 documentIds.add(new Long(poDocId));
086 return documentIds;
087 }
088 }
089 return null;
090 }
091
092 /**
093 * If the Add Asset Global document is submit from CAB, bypass all the approvers.
094 */
095 @Override
096 protected boolean answerSplitNodeQuestion(String nodeName) throws UnsupportedOperationException {
097 if (REQUIRES_REVIEW.equals(nodeName))
098 return !isAccountAndOrganizationReviewRequired();
099 throw new UnsupportedOperationException("Cannot answer split question for this node you call \"" + nodeName + "\"");
100 }
101
102 /**
103 * check whether or not isCapitalAssetBuilderOriginIndicator
104 */
105 protected boolean isAccountAndOrganizationReviewRequired() {
106 return ((AssetGlobal) getBusinessObject()).isCapitalAssetBuilderOriginIndicator();
107 }
108
109
110 /**
111 * Get Asset from AssetGlobal
112 *
113 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterNew(org.kuali.rice.kns.document.MaintenanceDocument,
114 * java.util.Map)
115 */
116 @Override
117 public void processAfterNew(MaintenanceDocument document, Map<String, String[]> parameters) {
118
119 AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
120
121 // set "asset number" and "type code" from URL
122 setSeparateSourceCapitalAssetParameters(assetGlobal, parameters);
123 setFinancialDocumentTypeCode(assetGlobal, parameters);
124
125 // populate required fields for "Asset Separate" doc
126 if (getAssetGlobalService().isAssetSeparate(assetGlobal)) {
127 Asset asset = getAsset(assetGlobal);
128 AssetOrganization assetOrganization = getAssetOrganization(assetGlobal);
129 populateAssetSeparateAssetDetails(assetGlobal, asset, assetOrganization);
130 populateAssetSeparatePaymentDetails(assetGlobal, asset);
131 populateAssetLocationTabInformation(asset);
132 AssetGlobalRule.validateAssetTotalCostMatchesPaymentTotalCost(assetGlobal);
133
134 if (getAssetGlobalService().isAssetSeparateByPayment(assetGlobal)) {
135 AssetGlobalRule.validateAssetAlreadySeparated(assetGlobal.getSeparateSourceCapitalAssetNumber());
136 }
137 // populate doc header description with the doc type
138 document.getDocumentHeader().setDocumentDescription(CamsConstants.AssetSeparate.SEPARATE_AN_ASSET_DESCRIPTION);
139 }
140
141 super.processAfterNew(document, parameters);
142 }
143
144 @Override
145 public void setGenerateDefaultValues(String docTypeName) {
146 }
147
148 @Override
149 public void setupNewFromExisting(MaintenanceDocument document, Map<String, String[]> parameters) {
150 super.setupNewFromExisting(document, parameters);
151
152 AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
153
154 assetGlobal.setLastInventoryDate(SpringContext.getBean(DateTimeService.class).getCurrentSqlDate());
155 }
156
157 /**
158 * Get Asset from AssetGlobal
159 *
160 * @param assetGlobal
161 * @return Asset
162 */
163 private Asset getAsset(AssetGlobal assetGlobal) {
164 HashMap<Object, Object> map = new HashMap<Object, Object>();
165 map.put(CamsPropertyConstants.Asset.CAPITAL_ASSET_NUMBER, assetGlobal.getSeparateSourceCapitalAssetNumber());
166 Asset asset = (Asset) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(Asset.class, map);
167 return asset;
168 }
169
170 /**
171 * Get AssetOrganization from AssetGlobal
172 *
173 * @param assetGlobal
174 * @return AssetOrganization
175 */
176 private AssetOrganization getAssetOrganization(AssetGlobal assetGlobal) {
177 HashMap<Object, Object> map = new HashMap<Object, Object>();
178 map.put(CamsPropertyConstants.Asset.CAPITAL_ASSET_NUMBER, assetGlobal.getSeparateSourceCapitalAssetNumber());
179 AssetOrganization assetOrganization = (AssetOrganization) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(AssetOrganization.class, map);
180 return assetOrganization;
181 }
182
183 /**
184 * Populate Asset Details for Asset Separate document
185 *
186 * @param assetGlobal
187 * @param asset
188 * @param assetOrganization
189 */
190 private void populateAssetSeparateAssetDetails(AssetGlobal assetGlobal, Asset asset, AssetOrganization assetOrganization) {
191 assetGlobal.setOrganizationOwnerAccountNumber(asset.getOrganizationOwnerAccountNumber());
192 assetGlobal.setOrganizationOwnerChartOfAccountsCode(asset.getOrganizationOwnerChartOfAccountsCode());
193 assetGlobal.setAgencyNumber(asset.getAgencyNumber());
194 assetGlobal.setAcquisitionTypeCode(asset.getAcquisitionTypeCode());
195 assetGlobal.setInventoryStatusCode(asset.getInventoryStatusCode());
196 assetGlobal.setConditionCode(asset.getConditionCode());
197 assetGlobal.setCapitalAssetDescription(asset.getCapitalAssetDescription());
198 assetGlobal.setCapitalAssetTypeCode(asset.getCapitalAssetTypeCode());
199 assetGlobal.setVendorName(asset.getVendorName());
200 assetGlobal.setManufacturerName(asset.getManufacturerName());
201 assetGlobal.setManufacturerModelNumber(asset.getManufacturerModelNumber());
202 if (ObjectUtils.isNotNull(assetOrganization)) {
203 assetGlobal.setOrganizationText(assetOrganization.getOrganizationText());
204 }
205 // added in case of NULL date in DB
206 if (asset.getLastInventoryDate() == null) {
207 assetGlobal.setLastInventoryDate(SpringContext.getBean(DateTimeService.class).getCurrentSqlDate());
208 }
209 else {
210 assetGlobal.setLastInventoryDate(new java.sql.Date(asset.getLastInventoryDate().getTime()));
211 }
212 assetGlobal.setCreateDate(asset.getCreateDate());
213 assetGlobal.setCapitalAssetInServiceDate(asset.getCapitalAssetInServiceDate());
214 assetGlobal.setLandCountyName(asset.getLandCountyName());
215 assetGlobal.setLandAcreageSize(asset.getLandAcreageSize());
216 assetGlobal.setLandParcelNumber(asset.getLandParcelNumber());
217
218 assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.ORGANIZATION_OWNER_ACCOUNT);
219 }
220
221 /**
222 * Populate Asset Payment Details for Asset Separate document. It will do this whether we are separating by asset or payment. If
223 * it is by asset it picks up all the payments and sets the total amount on the document of that per the asset. If it is by
224 * payment it picks only the payment out we are interested in and set the document total amount to that payment only.
225 *
226 * @param assetGlobal
227 * @param asset
228 */
229 private void populateAssetSeparatePaymentDetails(AssetGlobal assetGlobal, Asset asset) {
230 // clear and create temp AssetPaymentDetail list
231 assetGlobal.getAssetPaymentDetails().clear();
232 List<AssetPaymentDetail> newAssetPaymentDetailList = assetGlobal.getAssetPaymentDetails();
233
234 if (!getAssetGlobalService().isAssetSeparateByPayment(assetGlobal)) {
235 // Separate by Asset. Pick all payments up
236
237 for (AssetPayment assetPayment : asset.getAssetPayments()) {
238
239 // create new AssetPaymentDetail
240 AssetPaymentDetail assetPaymentDetail = new AssetPaymentDetail(assetPayment);
241
242 // add assetPaymentDetail to AssetPaymentDetail list
243 newAssetPaymentDetailList.add(assetPaymentDetail);
244 }
245
246 // Set total amount per asset
247 assetGlobal.setTotalCostAmount(asset.getTotalCostAmount());
248 assetGlobal.setSeparateSourceRemainingAmount(asset.getTotalCostAmount());
249 }
250 else {
251 for (AssetPayment assetPayment : asset.getAssetPayments()) {
252 // Separate by Payment. Pick only the appropriate payment up and then break
253
254 if (assetPayment.getPaymentSequenceNumber().equals(assetGlobal.getSeparateSourcePaymentSequenceNumber())) {
255 // create new AssetPaymentDetail
256 AssetPaymentDetail assetPaymentDetail = new AssetPaymentDetail(assetPayment);
257
258 // add assetPaymentDetail to AssetPaymentDetail list
259 newAssetPaymentDetailList.add(assetPaymentDetail);
260
261 // Set total amount per payment
262 assetGlobal.setTotalCostAmount(assetPayment.getAccountChargeAmount());
263 assetGlobal.setSeparateSourceRemainingAmount(assetPayment.getAccountChargeAmount());
264
265 break;
266 }
267 }
268 }
269 assetGlobal.setSeparateSourceTotalAmount(KualiDecimal.ZERO);
270
271 // set AssetGlobal payment details with new payment details
272 assetGlobal.setAssetPaymentDetails(newAssetPaymentDetailList);
273 }
274
275 /**
276 * Set capital asset number and payment sequence number from URL on the AssetGlobal BO. It only does so if each is available.
277 *
278 * @see org.kuali.module.cams.lookup.AssetLookupableHelperServiceImpl#getSeparateUrl(BusinessObject)
279 * @see org.kuali.module.cams.lookup.AssetPaymentLookupableHelperServiceImpl#getSeparateUrl(BusinessObject)
280 * @param assetGlobal
281 * @param parameters
282 */
283 private void setSeparateSourceCapitalAssetParameters(AssetGlobal assetGlobal, Map<String, String[]> parameters) {
284 String[] separateSourceCapitalAssetNumber = parameters.get(CamsPropertyConstants.AssetGlobal.SEPARATE_SOURCE_CAPITAL_ASSET_NUMBER);
285 if (separateSourceCapitalAssetNumber != null) {
286 assetGlobal.setSeparateSourceCapitalAssetNumber(Long.parseLong(separateSourceCapitalAssetNumber[0].toString()));
287 }
288
289 String[] separateSourcePaymentSequenceNumber = parameters.get(CamsPropertyConstants.AssetGlobal.SEPERATE_SOURCE_PAYMENT_SEQUENCE_NUMBER);
290 if (separateSourcePaymentSequenceNumber != null) {
291 assetGlobal.setSeparateSourcePaymentSequenceNumber(Integer.parseInt(separateSourcePaymentSequenceNumber[0].toString()));
292 }
293 }
294
295 /**
296 * Set document type code from URL.
297 *
298 * @see org.kuali.module.cams.lookup.AssetLookupableHelperServiceImpl#getSeparateUrl(BusinessObject)
299 * @param assetGlobal
300 * @param parameters
301 */
302 private void setFinancialDocumentTypeCode(AssetGlobal assetGlobal, Map<String, String[]> parameters) {
303 String[] financialDocumentTypeCode = parameters.get(KFSPropertyConstants.FINANCIAL_DOCUMENT_TYPE_CODE);
304 if (financialDocumentTypeCode != null) {
305 assetGlobal.setFinancialDocumentTypeCode(financialDocumentTypeCode[0].toString());
306 }
307 }
308
309 /**
310 * Hook for quantity and setting asset numbers.
311 *
312 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#addNewLineToCollection(java.lang.String)
313 */
314 @Override
315 public void addNewLineToCollection(String collectionName) {
316 AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
317
318 if (CamsPropertyConstants.AssetGlobal.ASSET_PAYMENT_DETAILS.equalsIgnoreCase(collectionName)) {
319 handAssetPaymentsCollection(collectionName, assetGlobal);
320 }
321 if (CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS.equalsIgnoreCase(collectionName)) {
322 handleAssetSharedDetailsCollection(collectionName, assetGlobal);
323 }
324 int sharedDetailsIndex = assetGlobal.getAssetSharedDetails().size() - 1;
325 if (sharedDetailsIndex > -1 && (CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS + "[" + sharedDetailsIndex + "]." + CamsPropertyConstants.AssetGlobalDetail.ASSET_UNIQUE_DETAILS).equalsIgnoreCase(collectionName)) {
326 handleAssetUniqueCollection(collectionName, assetGlobal);
327 }
328 super.addNewLineToCollection(collectionName);
329 }
330
331 /**
332 * Sets required fields with specific values when an individual unique asset added.
333 *
334 * @param collectionName
335 */
336 private void handleAssetUniqueCollection(String collectionName, AssetGlobal assetGlobal) {
337 AssetGlobalDetail assetGlobalDetail = (AssetGlobalDetail) newCollectionLines.get(collectionName);
338
339 if (ObjectUtils.isNotNull(assetGlobalDetail)) {
340 assetGlobalDetail.setCapitalAssetNumber(NextAssetNumberFinder.getLongValue());
341
342 // if not set, populate unique asset fields using original asset data. "Asset Separate" doc (location tab)
343 if (ObjectUtils.isNotNull(assetGlobal)) {
344 if (getAssetGlobalService().isAssetSeparate(assetGlobal)) {
345 if (assetGlobalDetail.getCapitalAssetTypeCode() == null) {
346 assetGlobalDetail.setCapitalAssetTypeCode(assetGlobal.getCapitalAssetTypeCode());
347 }
348 if (assetGlobalDetail.getCapitalAssetDescription() == null) {
349 assetGlobalDetail.setCapitalAssetDescription(assetGlobal.getCapitalAssetDescription());
350 }
351 if (assetGlobalDetail.getManufacturerName() == null) {
352 assetGlobalDetail.setManufacturerName(assetGlobal.getManufacturerName());
353 }
354 if (assetGlobalDetail.getSeparateSourceAmount() == null) {
355 assetGlobalDetail.setSeparateSourceAmount(KualiDecimal.ZERO);
356 }
357 }
358 }
359 }
360 }
361
362 /**
363 * Sets required fields with specific values when multiple unique assets added (i.e. field "Quantity Of Assets To Be Created").
364 *
365 * @param collectionName
366 * @param assetGlobal
367 */
368 private void handleAssetSharedDetailsCollection(String collectionName, AssetGlobal assetGlobal) {
369 AssetGlobalDetail assetGlobalDetail = (AssetGlobalDetail) newCollectionLines.get(collectionName);
370 Integer locationQuantity = assetGlobalDetail.getLocationQuantity();
371 while (locationQuantity != null && locationQuantity > 0) {
372 AssetGlobalDetail newAssetUnique = new AssetGlobalDetail();
373 newAssetUnique.setCapitalAssetNumber(NextAssetNumberFinder.getLongValue());
374
375 // populate unique asset fields using original asset data. "Asset Separate" doc (location tab)
376 if (getAssetGlobalService().isAssetSeparate(assetGlobal)) {
377 newAssetUnique.setCapitalAssetTypeCode(assetGlobal.getCapitalAssetTypeCode());
378 newAssetUnique.setCapitalAssetDescription(assetGlobal.getCapitalAssetDescription());
379 newAssetUnique.setManufacturerName(assetGlobal.getManufacturerName());
380 newAssetUnique.setOrganizationInventoryName(this.getAsset(assetGlobal).getOrganizationInventoryName());
381 newAssetUnique.setSeparateSourceAmount(KualiDecimal.ZERO);
382 }
383 assetGlobalDetail.getAssetGlobalUniqueDetails().add(newAssetUnique);
384 newAssetUnique.setNewCollectionRecord(true);
385 locationQuantity--;
386 }
387 }
388
389 /**
390 * Sets the default values in some of the fields of the asset payment section
391 *
392 * @param collectionName
393 * @param assetGlobal
394 */
395 private void handAssetPaymentsCollection(String collectionName, AssetGlobal assetGlobal) {
396 AssetPaymentDetail assetPaymentDetail = (AssetPaymentDetail) newCollectionLines.get(collectionName);
397 if (assetPaymentDetail != null) {
398 assetPaymentDetail.setSequenceNumber(assetGlobal.incrementFinancialDocumentLineNumber());
399 // Set for document number and document type code
400 if (getAssetGlobalService().existsInGroup(getAssetGlobalService().getNonNewAcquisitionCodeGroup(), assetGlobal.getAcquisitionTypeCode())) {
401 assetPaymentDetail.setExpenditureFinancialDocumentNumber(documentNumber);
402 assetPaymentDetail.setExpenditureFinancialDocumentTypeCode(CamsConstants.DocumentTypeName.ASSET_ADD_GLOBAL);
403 assetPaymentDetail.setExpenditureFinancialSystemOriginationCode(KFSConstants.ORIGIN_CODE_KUALI);
404 }
405 }
406 }
407
408
409 /**
410 * We are using a substitute mechanism for asset locking which can lock on assets when rule check passed. Return empty list from
411 * this method.
412 *
413 * @see org.kuali.rice.kns.maintenance.Maintainable#generateMaintenanceLocks()
414 */
415 @Override
416 public List<MaintenanceLock> generateMaintenanceLocks() {
417 return new ArrayList<MaintenanceLock>();
418 }
419
420 protected CapitalAssetManagementModuleService getCapitalAssetManagementModuleService() {
421 return SpringContext.getBean(CapitalAssetManagementModuleService.class);
422 }
423
424
425 /**
426 * @see org.kuali.rice.kns.maintenance.KualiGlobalMaintainableImpl#prepareForSave()
427 */
428 @Override
429 public void prepareForSave() {
430 super.prepareForSave();
431 AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
432 List<AssetGlobalDetail> assetSharedDetails = assetGlobal.getAssetSharedDetails();
433 List<AssetGlobalDetail> newDetails = new TypedArrayList(AssetGlobalDetail.class);
434 AssetGlobalDetail newAssetGlobalDetail = null;
435 if (!assetSharedDetails.isEmpty() && !assetSharedDetails.get(0).getAssetGlobalUniqueDetails().isEmpty()) {
436
437 for (AssetGlobalDetail locationDetail : assetSharedDetails) {
438 List<AssetGlobalDetail> assetGlobalUniqueDetails = locationDetail.getAssetGlobalUniqueDetails();
439
440 for (AssetGlobalDetail detail : assetGlobalUniqueDetails) {
441 // read from location and set it to detail
442 if (ObjectUtils.isNotNull(locationDetail.getCampusCode())) {
443 detail.setCampusCode(locationDetail.getCampusCode().toUpperCase());
444 }
445 else {
446 detail.setCampusCode(locationDetail.getCampusCode());
447 }
448 if (ObjectUtils.isNotNull(locationDetail.getBuildingCode())) {
449 detail.setBuildingCode(locationDetail.getBuildingCode().toUpperCase());
450 }
451 else {
452 detail.setBuildingCode(locationDetail.getBuildingCode());
453 }
454 detail.setBuildingRoomNumber(locationDetail.getBuildingRoomNumber());
455 detail.setBuildingSubRoomNumber(locationDetail.getBuildingSubRoomNumber());
456 detail.setOffCampusName(locationDetail.getOffCampusName());
457 detail.setOffCampusAddress(locationDetail.getOffCampusAddress());
458 detail.setOffCampusCityName(locationDetail.getOffCampusCityName());
459 detail.setOffCampusStateCode(locationDetail.getOffCampusStateCode());
460 detail.setOffCampusCountryCode(locationDetail.getOffCampusCountryCode());
461 detail.setOffCampusZipCode(locationDetail.getOffCampusZipCode());
462 newDetails.add(detail);
463 }
464 }
465 }
466
467 if (assetGlobal.getCapitalAssetTypeCode() != null) {
468 assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.CAPITAL_ASSET_TYPE);
469 AssetType capitalAssetType = assetGlobal.getCapitalAssetType();
470 if (ObjectUtils.isNotNull(capitalAssetType)) {
471 if (capitalAssetType.getDepreciableLifeLimit() != null && capitalAssetType.getDepreciableLifeLimit().intValue() != 0) {
472 assetGlobal.setCapitalAssetInServiceDate(assetGlobal.getCreateDate() == null ? SpringContext.getBean(DateTimeService.class).getCurrentSqlDate() : assetGlobal.getCreateDate());
473 }
474 else {
475 assetGlobal.setCapitalAssetInServiceDate(null);
476 }
477 computeDepreciationDate(assetGlobal);
478 }
479 }
480 assetGlobal.getAssetGlobalDetails().clear();
481 assetGlobal.setAssetGlobalDetails(newDetails);
482 }
483
484 /**
485 * computes depreciation date
486 *
487 * @param assetGlobal
488 */
489 private void computeDepreciationDate(AssetGlobal assetGlobal) {
490 List<AssetPaymentDetail> assetPaymentDetails = assetGlobal.getAssetPaymentDetails();
491 if (assetPaymentDetails != null && !assetPaymentDetails.isEmpty()) {
492
493 LOG.debug("Compute depreciation date based on asset type, depreciation convention and in-service date");
494 AssetPaymentDetail firstAssetPaymentDetail = assetPaymentDetails.get(0);
495 ObjectCode objectCode = SpringContext.getBean(ObjectCodeService.class).getByPrimaryId(firstAssetPaymentDetail.getPostingYear(), firstAssetPaymentDetail.getChartOfAccountsCode(), firstAssetPaymentDetail.getFinancialObjectCode());
496 if (ObjectUtils.isNotNull(objectCode)) {
497 Map<String, String> primaryKeys = new HashMap<String, String>();
498 primaryKeys.put(CamsPropertyConstants.AssetDepreciationConvention.FINANCIAL_OBJECT_SUB_TYPE_CODE, objectCode.getFinancialObjectSubTypeCode());
499 AssetDepreciationConvention depreciationConvention = (AssetDepreciationConvention) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(AssetDepreciationConvention.class, primaryKeys);
500 Date depreciationDate = SpringContext.getBean(AssetDateService.class).computeDepreciationDate(assetGlobal.getCapitalAssetType(), depreciationConvention, assetGlobal.getCapitalAssetInServiceDate());
501 assetGlobal.setCapitalAssetDepreciationDate(depreciationDate);
502 }
503 }
504 }
505
506 /**
507 * @see org.kuali.rice.kns.maintenance.KualiGlobalMaintainableImpl#processAfterRetrieve()
508 */
509 @Override
510 public void processAfterRetrieve() {
511 super.processAfterRetrieve();
512 AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
513 assetGlobal.refresh();
514 assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.SEPARATE_SOURCE_CAPITAL_ASSET);
515 if (ObjectUtils.isNotNull(assetGlobal.getSeparateSourceCapitalAsset())) {
516 assetGlobal.setLastInventoryDate(new java.sql.Date(assetGlobal.getSeparateSourceCapitalAsset().getLastInventoryDate().getTime()));
517 }
518 else {
519 assetGlobal.setLastInventoryDate(SpringContext.getBean(DateTimeService.class).getCurrentSqlDate());
520 }
521 List<AssetGlobalDetail> assetGlobalDetails = assetGlobal.getAssetGlobalDetails();
522 AssetGlobalDetail currLocationDetail = null;
523 HashMap<String, AssetGlobalDetail> locationMap = new HashMap<String, AssetGlobalDetail>();
524 AssetGlobalDetail copyValue = null;
525 for (AssetGlobalDetail detail : assetGlobalDetails) {
526 copyValue = (AssetGlobalDetail) ObjectUtils.deepCopy(detail);
527 copyValue.getAssetGlobalUniqueDetails().clear();
528 String key = generateLocationKey(copyValue);
529 if ((currLocationDetail = locationMap.get(key)) == null) {
530 currLocationDetail = copyValue;
531 locationMap.put(key, currLocationDetail);
532 }
533 currLocationDetail.getAssetGlobalUniqueDetails().add(copyValue);
534 currLocationDetail.setLocationQuantity(currLocationDetail.getAssetGlobalUniqueDetails().size());
535 }
536 assetGlobal.getAssetSharedDetails().clear();
537 assetGlobal.getAssetSharedDetails().addAll(locationMap.values());
538
539 // When document starts routing, FO won't allow to change asset total amount which is a derived value from Asset payments
540 // and the quantity of assets. To compare asset total amount , we need to calculate and save the value before FO made
541 // changes. No handle to the workflow document and see if it starts routing. Otherwise, we can add if condition here.
542 setAssetTotalAmountFromPersistence(assetGlobal);
543 }
544
545 private void setAssetTotalAmountFromPersistence(AssetGlobal assetGlobal) {
546 KualiDecimal minAssetTotalAmount = getAssetGlobalService().totalPaymentByAsset(assetGlobal, false);
547 KualiDecimal maxAssetTotalAmount = getAssetGlobalService().totalPaymentByAsset(assetGlobal, true);
548 if (minAssetTotalAmount.isGreaterThan(maxAssetTotalAmount)) {
549 // swap min and max
550 KualiDecimal totalPayment = minAssetTotalAmount;
551 minAssetTotalAmount = maxAssetTotalAmount;
552 maxAssetTotalAmount = totalPayment;
553 }
554 assetGlobal.setMinAssetTotalAmount(minAssetTotalAmount);
555 assetGlobal.setMaxAssetTotalAmount(maxAssetTotalAmount);
556 }
557
558 /**
559 * Generates a unique using location fields to keep track of user changes
560 *
561 * @param location Location
562 * @return Key String
563 */
564 private String generateLocationKey(AssetGlobalDetail location) {
565 StringBuilder builder = new StringBuilder();
566 builder.append(location.getCampusCode() == null ? "" : location.getCampusCode().trim().toLowerCase());
567 builder.append(location.getBuildingCode() == null ? "" : location.getBuildingCode().trim().toLowerCase());
568 builder.append(location.getBuildingRoomNumber() == null ? "" : location.getBuildingRoomNumber().trim().toLowerCase());
569 builder.append(location.getBuildingSubRoomNumber() == null ? "" : location.getBuildingSubRoomNumber().trim().toLowerCase());
570 builder.append(location.getOffCampusName() == null ? "" : location.getOffCampusName().trim().toLowerCase());
571 builder.append(location.getOffCampusAddress() == null ? "" : location.getOffCampusAddress().trim().toLowerCase());
572 builder.append(location.getOffCampusCityName() == null ? "" : location.getOffCampusCityName().trim().toLowerCase());
573 builder.append(location.getOffCampusStateCode() == null ? "" : location.getOffCampusStateCode().trim().toLowerCase());
574 builder.append(location.getOffCampusZipCode() == null ? "" : location.getOffCampusZipCode().trim().toLowerCase());
575 builder.append(location.getOffCampusCountryCode() == null ? "" : location.getOffCampusCountryCode().trim().toLowerCase());
576 return builder.toString();
577 }
578
579 /**
580 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterPost(org.kuali.rice.kns.document.MaintenanceDocument,
581 * java.util.Map)
582 */
583 @Override
584 public void processAfterPost(MaintenanceDocument document, Map<String, String[]> parameters) {
585 super.processAfterPost(document, parameters);
586 // adjust the quantity
587 AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
588 List<AssetGlobalDetail> sharedDetailsList = assetGlobal.getAssetSharedDetails();
589
590 // each shared detail is a group of new assets to be created.
591 // so to equally split the source amount into all new assets (all groups),
592 // we need to get the total of ALL location quantities from each shared detail group
593 int locationQtyTotal = 0;
594 if (!sharedDetailsList.isEmpty()) {
595 for (AssetGlobalDetail sharedDetail : sharedDetailsList) {
596 sharedDetail.setLocationQuantity(sharedDetail.getAssetGlobalUniqueDetails().size());
597 locationQtyTotal += sharedDetail.getLocationQuantity();
598 }
599 }
600
601 // button actions for Asset Separate document
602 if (getAssetGlobalService().isAssetSeparate(assetGlobal) && sharedDetailsList.size() >= 1) {
603 String[] customAction = parameters.get(KNSConstants.CUSTOM_ACTION);
604
605 // calculate equal source total amounts and set separate source amount fields
606 if (customAction != null && CamsConstants.AssetSeparate.CALCULATE_EQUAL_SOURCE_AMOUNTS_BUTTON.equals(customAction[0])) {
607 KualiDecimalUtils kualiDecimalService = new KualiDecimalUtils(assetGlobal.getTotalCostAmount(), CamsConstants.CURRENCY_USD);
608 // add source asset to the current location quantity
609 KualiDecimal[] equalSourceAmountsArray = kualiDecimalService.allocateByQuantity(locationQtyTotal + 1);
610 setEqualSeparateSourceAmounts(equalSourceAmountsArray, assetGlobal);
611
612 recalculateTotalAmount(assetGlobal);
613 }
614
615 // calculate source asset remaining amount
616 if (customAction != null && (CamsConstants.AssetSeparate.CALCULATE_SEPARATE_SOURCE_REMAINING_AMOUNT_BUTTON.equals(customAction[0]))) {
617 // Don't do anything because we are anyway recalculating always below
618 }
619
620 // Do recalculate every time even if button (CamsConstants.CALCULATE_SEPARATE_SOURCE_REMAINING_AMOUNT_BUTTON) wasn't
621 // pressed. We do that so that it also happens on add / delete lines.
622 recalculateTotalAmount(assetGlobal);
623 }
624 }
625
626 /**
627 * Recalculate amounts in the Recalculate Total Amount Tab
628 *
629 * @param assetGlobal
630 */
631 protected void recalculateTotalAmount(AssetGlobal assetGlobal) {
632 // set Less Additions
633 assetGlobal.setSeparateSourceTotalAmount(getAssetGlobalService().getUniqueAssetsTotalAmount(assetGlobal));
634 // set Remaining Total Amount
635 assetGlobal.setSeparateSourceRemainingAmount(assetGlobal.getTotalCostAmount().subtract(getAssetGlobalService().getUniqueAssetsTotalAmount(assetGlobal)));
636 }
637
638 /**
639 * Separates the current asset amount equally into new unique assets.
640 *
641 * @param kualiDecimalArray
642 * @param assetGlobal
643 */
644 public void setEqualSeparateSourceAmounts(KualiDecimal[] equalSourceAmountsArray, AssetGlobal assetGlobal) {
645 int i = 0;
646 for (AssetGlobalDetail assetSharedDetail : assetGlobal.getAssetSharedDetails()) {
647 for (AssetGlobalDetail assetGlobalUniqueDetail : assetSharedDetail.getAssetGlobalUniqueDetails()) {
648 assetGlobalUniqueDetail.setSeparateSourceAmount(equalSourceAmountsArray[i]);
649 i++;
650 }
651 }
652 }
653
654 /**
655 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#doRouteStatusChange(org.kuali.rice.kns.bo.DocumentHeader)
656 */
657 @Override
658 public void doRouteStatusChange(DocumentHeader documentHeader) {
659 super.doRouteStatusChange(documentHeader);
660 AssetGlobal assetGlobal = (AssetGlobal) getBusinessObject();
661 List<GeneralLedgerPendingEntry> generalLedgerPendingEntries = assetGlobal.getGeneralLedgerPendingEntries();
662 new AssetGlobalGeneralLedgerPendingEntrySource((FinancialSystemDocumentHeader) documentHeader).doRouteStatusChange(generalLedgerPendingEntries);
663
664 KualiWorkflowDocument workflowDoc = documentHeader.getWorkflowDocument();
665
666 // force pretagDetail active indicators back to true
667 if (workflowDoc.stateIsCanceled()) {
668 if (ObjectUtils.isNotNull(assetGlobal)) {
669 List<AssetGlobalDetail> assetGlobalDetailsList = assetGlobal.getAssetGlobalDetails();
670 if (ObjectUtils.isNotNull(assetGlobalDetailsList)) {
671 for (AssetGlobalDetail assetGlobaldetails : assetGlobalDetailsList) {
672 if (assetGlobaldetails.getCampusTagNumber() != null && !assetGlobaldetails.getCampusTagNumber().isEmpty()) {
673 HashMap<Object, Object> map = new HashMap<Object, Object>();
674 map.put(CabPropertyConstants.PretagDetail.CAMPUS_TAG_NUMBER, assetGlobaldetails.getCampusTagNumber());
675 List<PretagDetail> pretagDetailList = (List<PretagDetail>) SpringContext.getBean(BusinessObjectService.class).findMatching(PretagDetail.class, map);
676 if (ObjectUtils.isNotNull(pretagDetailList)) {
677 for (PretagDetail pretagDetail : pretagDetailList) {
678 pretagDetail.setActive(true);
679 SpringContext.getBean(BusinessObjectService.class).save(pretagDetail);
680 }
681 }
682 }
683 }
684 }
685 }
686 }
687
688 // release lock for separate source asset...We don't include stateIsFinal since document always go to 'processed' first.
689 AssetGlobalService assetGlobalService = SpringContext.getBean(AssetGlobalService.class);
690 if (assetGlobalService.isAssetSeparate(assetGlobal) && (workflowDoc.stateIsCanceled() || workflowDoc.stateIsDisapproved() || workflowDoc.stateIsProcessed())) {
691 this.getCapitalAssetManagementModuleService().deleteAssetLocks(documentNumber, null);
692 }
693
694 // notify CAB of document status change
695 if (((AssetGlobal) getBusinessObject()).isCapitalAssetBuilderOriginIndicator()) {
696 SpringContext.getBean(CapitalAssetBuilderModuleService.class).notifyRouteStatusChange(documentHeader);
697 }
698 }
699
700 /**
701 * @see org.kuali.rice.kns.maintenance.KualiGlobalMaintainableImpl#getPrimaryEditedBusinessObjectClass()
702 */
703 @Override
704 public Class<? extends PersistableBusinessObject> getPrimaryEditedBusinessObjectClass() {
705 return Asset.class;
706 }
707
708 /**
709 * Returns the AssetGlobalService from context
710 *
711 * @return AssetGlobalService
712 */
713 private AssetGlobalService getAssetGlobalService() {
714 return SpringContext.getBean(AssetGlobalService.class);
715 }
716
717 /**
718 * populates the asset location information (add new section)
719 *
720 * @param asset
721 */
722 private void populateAssetLocationTabInformation(Asset asset) {
723 AssetGlobalDetail assetSharedDetail = (AssetGlobalDetail) this.getNewCollectionLine(CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS);
724 assetSharedDetail.setCampusCode(asset.getCampusCode());
725 assetSharedDetail.setBuildingCode(asset.getBuildingCode());
726 assetSharedDetail.setBuildingRoomNumber(asset.getBuildingRoomNumber());
727 }
728 }