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 }