/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.module.cam.document.validation.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService; import org.kuali.kfs.module.cam.CamsConstants; import org.kuali.kfs.module.cam.CamsConstants.DocumentTypeName; import org.kuali.kfs.module.cam.CamsKeyConstants; import org.kuali.kfs.module.cam.CamsPropertyConstants; import org.kuali.kfs.module.cam.businessobject.Asset; import org.kuali.kfs.module.cam.businessobject.AssetLocationGlobal; import org.kuali.kfs.module.cam.businessobject.AssetLocationGlobalDetail; import org.kuali.kfs.module.cam.document.service.AssetService; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.identity.KfsKimAttributes; import org.kuali.rice.kew.api.WorkflowDocument; import org.kuali.rice.kim.api.services.IdentityManagementService; import org.kuali.rice.kns.document.MaintenanceDocument; import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase; import org.kuali.rice.krad.bo.PersistableBusinessObject; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.ObjectUtils; /** * Business rules applicable to AssetLocationGlobal documents. */ public class AssetLocationGlobalRule extends MaintenanceDocumentRuleBase { protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetLocationGlobalRule.class); protected AssetService assetService = SpringContext.getBean(AssetService.class); /** * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) */ @Override protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) { boolean valid = super.processCustomSaveDocumentBusinessRules(document); AssetLocationGlobal assetLocationGlobal = (AssetLocationGlobal) document.getNewMaintainableObject().getBusinessObject(); valid &= !getCapitalAssetManagementModuleService().isAssetLocked(retrieveAssetNumberForLocking(assetLocationGlobal), DocumentTypeName.ASSET_LOCATION_GLOBAL, document.getDocumentNumber()); return valid; } /** * Processes rules when routing this global. * * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument) */ @Override protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument documentCopy) { boolean success = true; AssetLocationGlobal assetLocationGlobal = (AssetLocationGlobal) documentCopy.getNewMaintainableObject().getBusinessObject(); List<AssetLocationGlobalDetail> oldAssetLocationGlobalDetail = assetLocationGlobal.getAssetLocationGlobalDetails(); // validate multi add w/red highlight (collection) int index = 0; boolean hasCapitalAssetChanged = false; if (hasAssetLocationGlobalDetails(oldAssetLocationGlobalDetail)) { Set<String> tags = new HashSet<String>(); int pos = 0; for (AssetLocationGlobalDetail detail : assetLocationGlobal.getAssetLocationGlobalDetails()) { String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetLocationGlobal.ASSET_LOCATION_GLOBAL_DETAILS + "[" + index + "]"; GlobalVariables.getMessageMap().addToErrorPath(errorPath); success &= validateActiveCapitalAsset(detail); success &= validateCampusCode(detail); success &= validateBuildingCode(detail); success &= validateBuildingRoomNumber(detail); success &= validateTagNumber(detail); success &= validateTagDuplicationWithinDocument(detail, tags); success &= validateTagDuplication(detail.getCapitalAssetNumber(), detail.getCampusTagNumber()); success &= checkRequiredFieldsAfterAdd(detail); // no need to fail if nothing has changed on the asset, just issue a warning hasCapitalAssetChanged(detail); GlobalVariables.getMessageMap().removeFromErrorPath(errorPath); index++; } } success &= super.processCustomRouteDocumentBusinessRules(documentCopy); WorkflowDocument workflowDoc = documentCopy.getDocumentHeader().getWorkflowDocument(); // adding asset locks if (!GlobalVariables.getMessageMap().hasErrors() && (workflowDoc.isInitiated() || workflowDoc.isSaved())) { success &= setAssetLocks(documentCopy); } return success; } /** * retrieve asset numbers need to be locked. * * @param assetLocationGlobal * @return */ protected List<Long> retrieveAssetNumberForLocking(AssetLocationGlobal assetLocationGlobal) { List<Long> capitalAssetNumbers = new ArrayList<Long>(); for (AssetLocationGlobalDetail locationDetail : assetLocationGlobal.getAssetLocationGlobalDetails()) { if (locationDetail.getCapitalAssetNumber() != null) { capitalAssetNumbers.add(locationDetail.getCapitalAssetNumber()); } } return capitalAssetNumbers; } /** * Locking for asset numbers. * * @param documentCopy * @param assetLocationGlobal * @return */ private boolean setAssetLocks(MaintenanceDocument document) { AssetLocationGlobal assetLocationGlobal = (AssetLocationGlobal) document.getNewMaintainableObject().getBusinessObject(); return this.getCapitalAssetManagementModuleService().storeAssetLocks(retrieveAssetNumberForLocking(assetLocationGlobal), document.getDocumentNumber(), DocumentTypeName.ASSET_LOCATION_GLOBAL, null); } protected CapitalAssetManagementModuleService getCapitalAssetManagementModuleService() { return SpringContext.getBean(CapitalAssetManagementModuleService.class); } /** * Process rules for any new {@link AssetLocationGlobalDetail} that is added to this global. * * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument, * java.lang.String, org.kuali.rice.krad.bo.PersistableBusinessObject) */ @Override public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument documentCopy, String collectionName, PersistableBusinessObject bo) { boolean success = true; AssetLocationGlobal assetLocationGlobal = (AssetLocationGlobal) documentCopy.getNewMaintainableObject().getBusinessObject(); Set<String> tags = new HashSet<String>(); for (AssetLocationGlobalDetail detail : assetLocationGlobal.getAssetLocationGlobalDetails()) { if (detail.getCampusTagNumber() != null) { tags.add(detail.getCampusTagNumber()); } } AssetLocationGlobalDetail newLineDetail = (AssetLocationGlobalDetail) bo; success = validateActiveCapitalAsset(newLineDetail); if (success) { success &= authorizeCapitalAsset(newLineDetail); success &= validateCampusCode(newLineDetail); success &= validateBuildingCode(newLineDetail); success &= validateBuildingRoomNumber(newLineDetail); success &= validateTagDuplicationWithinDocument(newLineDetail, tags); if (success) { success &= validateTagDuplication(newLineDetail.getCapitalAssetNumber(), newLineDetail.getCampusTagNumber()); } } return success & super.processCustomAddCollectionLineBusinessRules(documentCopy, collectionName, bo); } /** * Asset user authorization. * * @param assetLocationGlobalDetail * @return boolean */ protected boolean authorizeCapitalAsset(AssetLocationGlobalDetail assetLocationGlobalDetail) { boolean success = true; if (ObjectUtils.isNotNull(assetLocationGlobalDetail.getCapitalAssetNumber())) { assetLocationGlobalDetail.refreshReferenceObject(CamsPropertyConstants.AssetLocationGlobalDetail.ASSET); assetLocationGlobalDetail.getAsset().refreshReferenceObject(CamsPropertyConstants.Asset.ORGANIZATION_OWNER_ACCOUNT); Map<String,String> qualification = new HashMap<String,String>(); qualification.put(KfsKimAttributes.CHART_OF_ACCOUNTS_CODE, assetLocationGlobalDetail.getAsset().getOrganizationOwnerChartOfAccountsCode()); qualification.put(KfsKimAttributes.ORGANIZATION_CODE, assetLocationGlobalDetail.getAsset().getOrganizationOwnerAccount().getOrganizationCode()); if (!SpringContext.getBean(IdentityManagementService.class).isAuthorized(GlobalVariables.getUserSession().getPerson().getPrincipalId(), CamsConstants.CAM_MODULE_CODE, CamsConstants.PermissionNames.MAINTAIN_ASSET_LOCATION, qualification)) { success &= false; GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAPITAL_ASSET_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_ASSET_AUTHORIZATION, new String[] { GlobalVariables.getUserSession().getPerson().getPrincipalName(), assetLocationGlobalDetail.getCapitalAssetNumber().toString() }); } } return success; } /** * Check if the values for the asset on the detail were changed. * * @param assetLocationGlobalDetail * @return boolean */ protected boolean hasCapitalAssetChanged(AssetLocationGlobalDetail assetLocationGlobalDetail) { boolean success = false; if (ObjectUtils.isNotNull(assetLocationGlobalDetail.getCapitalAssetNumber())) { assetLocationGlobalDetail.refreshReferenceObject(CamsPropertyConstants.AssetLocationGlobalDetail.ASSET); assetLocationGlobalDetail.refreshReferenceObject(CamsPropertyConstants.AssetLocationGlobalDetail.ASSET); Asset asset = assetLocationGlobalDetail.getAsset(); //assetLocationGlobalDetail.getAsset().refreshReferenceObject(CamsPropertyConstants.Asset.ORGANIZATION_OWNER_ACCOUNT); if (ObjectUtils.isNotNull(assetLocationGlobalDetail.getAsset())) { if (!StringUtils.equalsIgnoreCase(asset.getCampusCode(), assetLocationGlobalDetail.getCampusCode()) ) { success = true; } if (!StringUtils.equalsIgnoreCase(asset.getBuildingCode(), assetLocationGlobalDetail.getBuildingCode()) ) { success = true; } if (!StringUtils.equalsIgnoreCase(asset.getBuildingRoomNumber(), assetLocationGlobalDetail.getBuildingRoomNumber()) ) { success = true; } if (!StringUtils.equalsIgnoreCase(asset.getBuildingSubRoomNumber(), assetLocationGlobalDetail.getBuildingSubRoomNumber()) ) { success = true; } if (!StringUtils.equalsIgnoreCase(asset.getCampusTagNumber(), assetLocationGlobalDetail.getCampusTagNumber())) { success = true; } // if nothing changed- issue a warning if(!success) { GlobalVariables.getMessageMap().putWarning(CamsPropertyConstants.AssetLocationGlobal.CAPITAL_ASSET_NUMBER, CamsKeyConstants.AssetLocationGlobal.WARNING_ASSET_NOT_CHANGED, new String[] { assetLocationGlobalDetail.getCapitalAssetNumber().toString() }); } } } return success; } /** * Validate if any {@link AssetLocationGlobalDetail} exist. * * @param assetLocationGlobal * @return boolean */ protected boolean hasAssetLocationGlobalDetails(List<AssetLocationGlobalDetail> assetLocationGlobalDetails) { boolean success = true; if (assetLocationGlobalDetails.size() == 0) { success = false; GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAPITAL_ASSET_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_ASSET_LOCATION_GLOBAL_NO_ASSET_DETAIL); } return success; } /** * Validate the capital {@link Asset}. This method also calls {@link AssetService} while validating retired {@link Asset}. * * @param assetLocationGlobalDetail * @return boolean */ protected boolean validateActiveCapitalAsset(AssetLocationGlobalDetail assetLocationGlobalDetail) { boolean success = true; if (ObjectUtils.isNotNull(assetLocationGlobalDetail.getCapitalAssetNumber())) { assetLocationGlobalDetail.refreshReferenceObject(CamsPropertyConstants.AssetLocationGlobalDetail.ASSET); if (ObjectUtils.isNull(assetLocationGlobalDetail.getAsset())) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAPITAL_ASSET_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_CAPITAL_ASSET_NUMBER, new String[] { assetLocationGlobalDetail.getCapitalAssetNumber().toString() }); success = false; } else if (assetService.isAssetRetired(assetLocationGlobalDetail.getAsset())) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_NON_ACTIVE_ASSET_RETIREMENT, new String[] { assetLocationGlobalDetail.getCapitalAssetNumber().toString() }); success = false; } } return success; } /** * Validate {@link Campus} code. * * @param assetLocationGlobalDetail * @return boolean */ protected boolean validateCampusCode(AssetLocationGlobalDetail assetLocationGlobalDetail) { boolean success = true; if (StringUtils.isNotBlank(assetLocationGlobalDetail.getCampusCode())) { // assetLocationGlobalDetail.refreshReferenceObject("campus"); if (ObjectUtils.isNull(assetLocationGlobalDetail.getCampus())) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_CODE, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_CAMPUS_CODE, new String[] { assetLocationGlobalDetail.getCampusCode(), assetLocationGlobalDetail.getCapitalAssetNumber().toString() }); success = false; } } return success; } /** * Validate {@link Building} code. * * @param assetLocationGlobalDetail * @return boolean */ protected boolean validateBuildingCode(AssetLocationGlobalDetail assetLocationGlobalDetail) { boolean success = true; if (StringUtils.isNotBlank(assetLocationGlobalDetail.getCampusCode()) && StringUtils.isNotBlank(assetLocationGlobalDetail.getBuildingCode())) { assetLocationGlobalDetail.refreshReferenceObject("building"); if (ObjectUtils.isNull(assetLocationGlobalDetail.getBuilding())) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.BUILDING_CODE, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_BUILDING_CODE, new String[] { assetLocationGlobalDetail.getBuildingCode(), assetLocationGlobalDetail.getCampusCode() }); success = false; } } return success; } /** * Validate building {@link Room} number. * * @param assetLocationGlobalDetail * @return boolean */ protected boolean validateBuildingRoomNumber(AssetLocationGlobalDetail assetLocationGlobalDetail) { boolean success = true; if (StringUtils.isNotBlank(assetLocationGlobalDetail.getBuildingRoomNumber())) { assetLocationGlobalDetail.refreshReferenceObject("buildingRoom"); if (ObjectUtils.isNull(assetLocationGlobalDetail.getBuildingRoom())) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.BUILDING_ROOM_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_INVALID_ROOM_NUMBER, new String[] { assetLocationGlobalDetail.getBuildingCode(), assetLocationGlobalDetail.getBuildingRoomNumber(), assetLocationGlobalDetail.getCampusCode() }); success = false; } } return success; } /** * Validate tag number. * * @param assetLocationGlobalDetail * @return boolean */ protected boolean validateTagNumber(AssetLocationGlobalDetail assetLocationGlobalDetail) { boolean success = true; if (StringUtils.isBlank(assetLocationGlobalDetail.getCampusTagNumber())) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_TAG_NUMBER_REQUIRED); success = false; } return success; } /** * Validate duplicate tag number. This method also calls {@link AssetService}. * * @param assetLocationGlobalDetail * @return boolean */ protected boolean validateTagDuplication(Long capitalAssetNumber, String campusTagNumber) { boolean success = true; if (capitalAssetNumber != null && ObjectUtils.isNotNull(campusTagNumber) && !CamsConstants.Asset.NON_TAGGABLE_ASSET.equalsIgnoreCase(campusTagNumber)) { // call AssetService, get Assets from doc, gather all assets matching this tag number List<Asset> activeAssetsMatchingTagNumber = assetService.findActiveAssetsMatchingTagNumber(campusTagNumber); for (Asset asset : activeAssetsMatchingTagNumber) { // compare asset numbers if (!asset.getCapitalAssetNumber().equals(capitalAssetNumber)) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_DUPLICATE_TAG_NUMBER_FOUND, new String[] { campusTagNumber, String.valueOf(asset.getCapitalAssetNumber()), capitalAssetNumber.toString() }); success = false; break; } } } return success; } protected boolean validateTagDuplicationWithinDocument(AssetLocationGlobalDetail assetLocationGlobalDetail, Set<String> tagsList) { boolean success = true; Long capitalAssetNumber = assetLocationGlobalDetail.getCapitalAssetNumber(); String campusTagNumber = assetLocationGlobalDetail.getCampusTagNumber(); if (capitalAssetNumber != null && ObjectUtils.isNotNull(campusTagNumber) && !CamsConstants.Asset.NON_TAGGABLE_ASSET.equalsIgnoreCase(campusTagNumber)) { // if duplicate within list if (!tagsList.add(campusTagNumber)) { success &= false; GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_DUPLICATE_TAG_NUMBER_WITHIN_DOCUMENT, new String[] { campusTagNumber, capitalAssetNumber.toString() }); } } return success; } /** * Check required fields after a new asset location has been added. * * @param assetLocationGlobalDetail * @return boolean */ protected boolean checkRequiredFieldsAfterAdd(AssetLocationGlobalDetail assetLocationGlobalDetail) { boolean success = true; if (StringUtils.isBlank(assetLocationGlobalDetail.getCampusCode())) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.CAMPUS_CODE, CamsKeyConstants.AssetLocationGlobal.ERROR_CAMPUS_CODE_REQUIRED); success = false; } if (StringUtils.isBlank(assetLocationGlobalDetail.getBuildingCode())) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.BUILDING_CODE, CamsKeyConstants.AssetLocationGlobal.ERROR_BUILDING_CODE_REQUIRED); success = false; } if (StringUtils.isBlank(assetLocationGlobalDetail.getBuildingRoomNumber())) { GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetLocationGlobal.BUILDING_ROOM_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_ROOM_NUMBER_REQUIRED); success = false; } return success; } }