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 017 package org.kuali.kfs.module.cam.document.validation.impl; 018 019 import java.util.ArrayList; 020 import java.util.HashSet; 021 import java.util.List; 022 import java.util.Set; 023 024 import org.apache.commons.lang.StringUtils; 025 import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService; 026 import org.kuali.kfs.module.cam.CamsConstants; 027 import org.kuali.kfs.module.cam.CamsKeyConstants; 028 import org.kuali.kfs.module.cam.CamsPropertyConstants; 029 import org.kuali.kfs.module.cam.CamsConstants.DocumentTypeName; 030 import org.kuali.kfs.module.cam.businessobject.Asset; 031 import org.kuali.kfs.module.cam.businessobject.AssetLocationGlobal; 032 import org.kuali.kfs.module.cam.businessobject.AssetLocationGlobalDetail; 033 import org.kuali.kfs.module.cam.document.service.AssetService; 034 import org.kuali.kfs.sys.context.SpringContext; 035 import org.kuali.kfs.sys.identity.KfsKimAttributes; 036 import org.kuali.rice.kim.bo.types.dto.AttributeSet; 037 import org.kuali.rice.kim.service.IdentityManagementService; 038 import org.kuali.rice.kim.util.KimCommonUtils; 039 import org.kuali.rice.kns.bo.PersistableBusinessObject; 040 import org.kuali.rice.kns.document.MaintenanceDocument; 041 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase; 042 import org.kuali.rice.kns.util.GlobalVariables; 043 import org.kuali.rice.kns.util.ObjectUtils; 044 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 045 046 /** 047 * Business rules applicable to AssetLocationGlobal documents. 048 */ 049 public class AssetLocationGlobalRule extends MaintenanceDocumentRuleBase { 050 051 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetLocationGlobalRule.class); 052 protected AssetService assetService = SpringContext.getBean(AssetService.class); 053 054 /** 055 * Constructs an AssetLocationGlobalRule 056 */ 057 public AssetLocationGlobalRule() { 058 } 059 060 /** 061 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 062 */ 063 @Override 064 protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) { 065 boolean valid = super.processCustomSaveDocumentBusinessRules(document); 066 AssetLocationGlobal assetLocationGlobal = (AssetLocationGlobal) document.getNewMaintainableObject().getBusinessObject(); 067 valid &= !getCapitalAssetManagementModuleService().isAssetLocked(retrieveAssetNumberForLocking(assetLocationGlobal), DocumentTypeName.ASSET_LOCATION_GLOBAL, document.getDocumentNumber()); 068 return valid; 069 } 070 071 /** 072 * Processes rules when routing this global. 073 * 074 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) 075 */ 076 @Override 077 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument documentCopy) { 078 boolean success = true; 079 080 AssetLocationGlobal assetLocationGlobal = (AssetLocationGlobal) documentCopy.getNewMaintainableObject().getBusinessObject(); 081 List<AssetLocationGlobalDetail> oldAssetLocationGlobalDetail = assetLocationGlobal.getAssetLocationGlobalDetails(); 082 083 // validate multi add w/red highlight (collection) 084 int index = 0; 085 086 if (hasAssetLocationGlobalDetails(oldAssetLocationGlobalDetail)) { 087 Set<String> tags = new HashSet<String>(); 088 int pos = 0; 089 for (AssetLocationGlobalDetail detail : assetLocationGlobal.getAssetLocationGlobalDetails()) { 090 String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetLocationGlobal.ASSET_LOCATION_GLOBAL_DETAILS + "[" + index + "]"; 091 GlobalVariables.getMessageMap().addToErrorPath(errorPath); 092 success &= validateActiveCapitalAsset(detail); 093 success &= validateCampusCode(detail); 094 success &= validateBuildingCode(detail); 095 success &= validateBuildingRoomNumber(detail); 096 success &= validateTagNumber(detail); 097 success &= validateTagDuplicationWithinDocument(detail, tags); 098 success &= validateTagDuplication(detail.getCapitalAssetNumber(), detail.getCampusTagNumber()); 099 success &= checkRequiredFieldsAfterAdd(detail); 100 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath); 101 index++; 102 } 103 } 104 success &= super.processCustomRouteDocumentBusinessRules(documentCopy); 105 106 KualiWorkflowDocument workflowDoc = documentCopy.getDocumentHeader().getWorkflowDocument(); 107 // adding asset locks 108 if (!GlobalVariables.getMessageMap().hasErrors() && (workflowDoc.stateIsInitiated() || workflowDoc.stateIsSaved())) { 109 success &= setAssetLocks(documentCopy); 110 } 111 112 return success; 113 } 114 115 /** 116 * retrieve asset numbers need to be locked. 117 * 118 * @param assetLocationGlobal 119 * @return 120 */ 121 protected List<Long> retrieveAssetNumberForLocking(AssetLocationGlobal assetLocationGlobal) { 122 List<Long> capitalAssetNumbers = new ArrayList<Long>(); 123 for (AssetLocationGlobalDetail locationDetail : assetLocationGlobal.getAssetLocationGlobalDetails()) { 124 if (locationDetail.getCapitalAssetNumber() != null) { 125 capitalAssetNumbers.add(locationDetail.getCapitalAssetNumber()); 126 } 127 } 128 return capitalAssetNumbers; 129 } 130 131 /** 132 * Locking for asset numbers. 133 * 134 * @param documentCopy 135 * @param assetLocationGlobal 136 * @return 137 */ 138 private boolean setAssetLocks(MaintenanceDocument document) { 139 AssetLocationGlobal assetLocationGlobal = (AssetLocationGlobal) document.getNewMaintainableObject().getBusinessObject(); 140 141 return this.getCapitalAssetManagementModuleService().storeAssetLocks(retrieveAssetNumberForLocking(assetLocationGlobal), document.getDocumentNumber(), DocumentTypeName.ASSET_LOCATION_GLOBAL, null); 142 } 143 144 protected CapitalAssetManagementModuleService getCapitalAssetManagementModuleService() { 145 return SpringContext.getBean(CapitalAssetManagementModuleService.class); 146 } 147 148 /** 149 * Process rules for any new {@link AssetLocationGlobalDetail} that is added to this global. 150 * 151 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument, 152 * java.lang.String, org.kuali.rice.kns.bo.PersistableBusinessObject) 153 */ 154 @Override 155 public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument documentCopy, String collectionName, PersistableBusinessObject bo) { 156 boolean success = true; 157 AssetLocationGlobal assetLocationGlobal = (AssetLocationGlobal) documentCopy.getNewMaintainableObject().getBusinessObject(); 158 Set<String> tags = new HashSet<String>(); 159 for (AssetLocationGlobalDetail detail : assetLocationGlobal.getAssetLocationGlobalDetails()) { 160 if (detail.getCampusTagNumber() != null) { 161 tags.add(detail.getCampusTagNumber()); 162 } 163 } 164 165 AssetLocationGlobalDetail newLineDetail = (AssetLocationGlobalDetail) bo; 166 success = validateActiveCapitalAsset(newLineDetail); 167 if (success) { 168 success &= authorizeCapitalAsset(newLineDetail); 169 success &= validateCampusCode(newLineDetail); 170 success &= validateBuildingCode(newLineDetail); 171 success &= validateBuildingRoomNumber(newLineDetail); 172 success &= validateTagDuplicationWithinDocument(newLineDetail, tags); 173 if (success) { 174 success &= validateTagDuplication(newLineDetail.getCapitalAssetNumber(), newLineDetail.getCampusTagNumber()); 175 } 176 } 177 return success & super.processCustomAddCollectionLineBusinessRules(documentCopy, collectionName, bo); 178 } 179 180 /** 181 * Asset user authorization. 182 * 183 * @param assetLocationGlobalDetail 184 * @return boolean 185 */ 186 protected boolean authorizeCapitalAsset(AssetLocationGlobalDetail assetLocationGlobalDetail) { 187 boolean success = true; 188 189 if (ObjectUtils.isNotNull(assetLocationGlobalDetail.getCapitalAssetNumber())) { 190 assetLocationGlobalDetail.refreshReferenceObject(CamsPropertyConstants.AssetLocationGlobalDetail.ASSET); 191 assetLocationGlobalDetail.getAsset().refreshReferenceObject(CamsPropertyConstants.Asset.ORGANIZATION_OWNER_ACCOUNT); 192 193 AttributeSet qualification = new AttributeSet(); 194 qualification.put(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE, assetLocationGlobalDetail.getAsset().getOrganizationOwnerChartOfAccountsCode()); 195 qualification.put(KfsKimAttributes.ORGANIZATION_CODE, assetLocationGlobalDetail.getAsset().getOrganizationOwnerAccount().getOrganizationCode()); 196 AttributeSet permissionDetails = new AttributeSet(); 197 permissionDetails.putAll(KimCommonUtils.getNamespaceAndComponentSimpleName(Asset.class)); 198 if (!SpringContext.getBean(IdentityManagementService.class).isAuthorized(GlobalVariables.getUserSession().getPerson().getPrincipalId(), CamsConstants.CAM_MODULE_CODE, CamsConstants.PermissionNames.MAINTAIN_ASSET_LOCATION, permissionDetails, qualification)) { 199 success &= false; 200 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAPITAL_ASSET_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_ASSET_AUTHORIZATION, new String[] { GlobalVariables.getUserSession().getPerson().getPrincipalName(), assetLocationGlobalDetail.getCapitalAssetNumber().toString() }); 201 } 202 } 203 204 return success; 205 } 206 207 /** 208 * Validate if any {@link AssetLocationGlobalDetail} exist. 209 * 210 * @param assetLocationGlobal 211 * @return boolean 212 */ 213 protected boolean hasAssetLocationGlobalDetails(List<AssetLocationGlobalDetail> assetLocationGlobalDetails) { 214 boolean success = true; 215 216 if (assetLocationGlobalDetails.size() == 0) { 217 success = false; 218 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAPITAL_ASSET_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_ASSET_LOCATION_GLOBAL_NO_ASSET_DETAIL); 219 } 220 221 return success; 222 } 223 224 /** 225 * Validate the capital {@link Asset}. This method also calls {@link AssetService} while validating retired {@link Asset}. 226 * 227 * @param assetLocationGlobalDetail 228 * @return boolean 229 */ 230 protected boolean validateActiveCapitalAsset(AssetLocationGlobalDetail assetLocationGlobalDetail) { 231 boolean success = true; 232 233 if (ObjectUtils.isNotNull(assetLocationGlobalDetail.getCapitalAssetNumber())) { 234 assetLocationGlobalDetail.refreshReferenceObject(CamsPropertyConstants.AssetLocationGlobalDetail.ASSET); 235 236 if (ObjectUtils.isNull(assetLocationGlobalDetail.getAsset())) { 237 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAPITAL_ASSET_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_CAPITAL_ASSET_NUMBER, new String[] { assetLocationGlobalDetail.getCapitalAssetNumber().toString() }); 238 success = false; 239 } 240 else if (assetService.isAssetRetired(assetLocationGlobalDetail.getAsset())) { 241 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_NON_ACTIVE_ASSET_RETIREMENT, new String[] { assetLocationGlobalDetail.getCapitalAssetNumber().toString() }); 242 success = false; 243 } 244 } 245 246 return success; 247 } 248 249 /** 250 * Validate {@link Campus} code. 251 * 252 * @param assetLocationGlobalDetail 253 * @return boolean 254 */ 255 protected boolean validateCampusCode(AssetLocationGlobalDetail assetLocationGlobalDetail) { 256 boolean success = true; 257 258 if (StringUtils.isNotBlank(assetLocationGlobalDetail.getCampusCode())) { 259 // assetLocationGlobalDetail.refreshReferenceObject("campus"); 260 261 if (ObjectUtils.isNull(assetLocationGlobalDetail.getCampus())) { 262 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_CODE, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_CAMPUS_CODE, new String[] { assetLocationGlobalDetail.getCampusCode(), assetLocationGlobalDetail.getCapitalAssetNumber().toString() }); 263 success = false; 264 } 265 } 266 267 return success; 268 } 269 270 /** 271 * Validate {@link Building} code. 272 * 273 * @param assetLocationGlobalDetail 274 * @return boolean 275 */ 276 protected boolean validateBuildingCode(AssetLocationGlobalDetail assetLocationGlobalDetail) { 277 boolean success = true; 278 279 if (StringUtils.isNotBlank(assetLocationGlobalDetail.getCampusCode()) && StringUtils.isNotBlank(assetLocationGlobalDetail.getBuildingCode())) { 280 assetLocationGlobalDetail.refreshReferenceObject("building"); 281 282 if (ObjectUtils.isNull(assetLocationGlobalDetail.getBuilding())) { 283 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.BUILDING_CODE, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_BUILDING_CODE, new String[] { assetLocationGlobalDetail.getBuildingCode(), assetLocationGlobalDetail.getCampusCode() }); 284 success = false; 285 } 286 } 287 288 return success; 289 } 290 291 /** 292 * Validate building {@link Room} number. 293 * 294 * @param assetLocationGlobalDetail 295 * @return boolean 296 */ 297 protected boolean validateBuildingRoomNumber(AssetLocationGlobalDetail assetLocationGlobalDetail) { 298 boolean success = true; 299 300 if (StringUtils.isNotBlank(assetLocationGlobalDetail.getBuildingRoomNumber())) { 301 assetLocationGlobalDetail.refreshReferenceObject("buildingRoom"); 302 303 if (ObjectUtils.isNull(assetLocationGlobalDetail.getBuildingRoom())) { 304 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.BUILDING_ROOM_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_ROOM_NUMBER, new String[] { assetLocationGlobalDetail.getBuildingCode(), assetLocationGlobalDetail.getBuildingRoomNumber(), assetLocationGlobalDetail.getCampusCode() }); 305 success = false; 306 } 307 } 308 309 return success; 310 } 311 312 /** 313 * Validate tag number. 314 * 315 * @param assetLocationGlobalDetail 316 * @return boolean 317 */ 318 protected boolean validateTagNumber(AssetLocationGlobalDetail assetLocationGlobalDetail) { 319 boolean success = true; 320 321 if (StringUtils.isBlank(assetLocationGlobalDetail.getCampusTagNumber())) { 322 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_TAG_NUMBER_REQUIRED); 323 success = false; 324 } 325 326 return success; 327 } 328 329 /** 330 * Validate duplicate tag number. This method also calls {@link AssetService}. 331 * 332 * @param assetLocationGlobalDetail 333 * @return boolean 334 */ 335 protected boolean validateTagDuplication(Long capitalAssetNumber, String campusTagNumber) { 336 boolean success = true; 337 if (capitalAssetNumber != null && ObjectUtils.isNotNull(campusTagNumber) && !CamsConstants.Asset.NON_TAGGABLE_ASSET.equalsIgnoreCase(campusTagNumber)) { 338 // call AssetService, get Assets from doc, gather all assets matching this tag number 339 List<Asset> activeAssetsMatchingTagNumber = assetService.findActiveAssetsMatchingTagNumber(campusTagNumber); 340 for (Asset asset : activeAssetsMatchingTagNumber) { 341 // compare asset numbers 342 if (!asset.getCapitalAssetNumber().equals(capitalAssetNumber)) { 343 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_DUPLICATE_TAG_NUMBER_FOUND, new String[] { campusTagNumber, String.valueOf(asset.getCapitalAssetNumber()), capitalAssetNumber.toString() }); 344 success = false; 345 break; 346 } 347 } 348 } 349 350 return success; 351 } 352 353 protected boolean validateTagDuplicationWithinDocument(AssetLocationGlobalDetail assetLocationGlobalDetail, Set<String> tagsList) { 354 boolean success = true; 355 Long capitalAssetNumber = assetLocationGlobalDetail.getCapitalAssetNumber(); 356 String campusTagNumber = assetLocationGlobalDetail.getCampusTagNumber(); 357 if (capitalAssetNumber != null && ObjectUtils.isNotNull(campusTagNumber) && !CamsConstants.Asset.NON_TAGGABLE_ASSET.equalsIgnoreCase(campusTagNumber)) { 358 // if duplicate within list 359 if (!tagsList.add(campusTagNumber)) { 360 success &= false; 361 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_DUPLICATE_TAG_NUMBER_WITHIN_DOCUMENT, new String[] { campusTagNumber, capitalAssetNumber.toString() }); 362 } 363 } 364 return success; 365 } 366 367 /** 368 * Check required fields after a new asset location has been added. 369 * 370 * @param assetLocationGlobalDetail 371 * @return boolean 372 */ 373 protected boolean checkRequiredFieldsAfterAdd(AssetLocationGlobalDetail assetLocationGlobalDetail) { 374 boolean success = true; 375 376 if (StringUtils.isBlank(assetLocationGlobalDetail.getCampusCode())) { 377 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_CODE, CamsKeyConstants.AssetLocationGlobal.ERROR_CAMPUS_CODE_REQUIRED); 378 success = false; 379 } 380 381 if (StringUtils.isBlank(assetLocationGlobalDetail.getBuildingCode())) { 382 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.BUILDING_CODE, CamsKeyConstants.AssetLocationGlobal.ERROR_BUILDING_CODE_REQUIRED); 383 success = false; 384 } 385 386 if (StringUtils.isBlank(assetLocationGlobalDetail.getBuildingRoomNumber())) { 387 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.BUILDING_ROOM_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_ROOM_NUMBER_REQUIRED); 388 success = false; 389 } 390 391 return success; 392 } 393 }