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 }