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.validation.impl;
017    
018    import static org.kuali.kfs.module.cam.CamsKeyConstants.ERROR_INVALID_ASSET_WARRANTY_NO;
019    import static org.kuali.kfs.module.cam.CamsPropertyConstants.Asset.ASSET_WARRANTY_WARRANTY_NUMBER;
020    
021    import java.sql.Timestamp;
022    import java.util.ArrayList;
023    import java.util.Collection;
024    import java.util.Date;
025    import java.util.HashMap;
026    import java.util.HashSet;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Set;
030    
031    import org.apache.commons.lang.StringUtils;
032    import org.kuali.kfs.coa.businessobject.Account;
033    import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService;
034    import org.kuali.kfs.module.cam.CamsConstants;
035    import org.kuali.kfs.module.cam.CamsKeyConstants;
036    import org.kuali.kfs.module.cam.CamsPropertyConstants;
037    import org.kuali.kfs.module.cam.businessobject.Asset;
038    import org.kuali.kfs.module.cam.businessobject.AssetComponent;
039    import org.kuali.kfs.module.cam.businessobject.AssetFabrication;
040    import org.kuali.kfs.module.cam.businessobject.AssetLocation;
041    import org.kuali.kfs.module.cam.businessobject.AssetRepairHistory;
042    import org.kuali.kfs.module.cam.businessobject.AssetWarranty;
043    import org.kuali.kfs.module.cam.businessobject.defaultvalue.NextAssetNumberFinder;
044    import org.kuali.kfs.module.cam.document.service.AssetComponentService;
045    import org.kuali.kfs.module.cam.document.service.AssetDateService;
046    import org.kuali.kfs.module.cam.document.service.AssetLocationService;
047    import org.kuali.kfs.module.cam.document.service.AssetLocationService.LocationField;
048    import org.kuali.kfs.module.cam.document.service.AssetService;
049    import org.kuali.kfs.module.cam.document.service.EquipmentLoanOrReturnService;
050    import org.kuali.kfs.module.cam.document.service.PaymentSummaryService;
051    import org.kuali.kfs.module.cam.document.service.RetirementInfoService;
052    import org.kuali.kfs.sys.context.SpringContext;
053    import org.kuali.kfs.sys.service.UniversityDateService;
054    import org.kuali.rice.kns.bo.PersistableBusinessObject;
055    import org.kuali.rice.kns.document.MaintenanceDocument;
056    import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
057    import org.kuali.rice.kns.service.DateTimeService;
058    import org.kuali.rice.kns.service.ParameterService;
059    import org.kuali.rice.kns.util.DateUtils;
060    import org.kuali.rice.kns.util.GlobalVariables;
061    import org.kuali.rice.kns.util.KualiDecimal;
062    import org.kuali.rice.kns.util.ObjectUtils;
063    import org.kuali.rice.kns.util.TypedArrayList;
064    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
065    
066    /**
067     * AssetRule for Asset edit.
068     */
069    public class AssetRule extends MaintenanceDocumentRuleBase {
070        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetRule.class);
071        protected static final Map<LocationField, String> LOCATION_FIELD_MAP = new HashMap<LocationField, String>();
072        static {
073            LOCATION_FIELD_MAP.put(LocationField.CAMPUS_CODE, CamsPropertyConstants.Asset.CAMPUS_CODE);
074            LOCATION_FIELD_MAP.put(LocationField.BUILDING_CODE, CamsPropertyConstants.Asset.BUILDING_CODE);
075            LOCATION_FIELD_MAP.put(LocationField.ROOM_NUMBER, CamsPropertyConstants.Asset.BUILDING_ROOM_NUMBER);
076            LOCATION_FIELD_MAP.put(LocationField.SUB_ROOM_NUMBER, CamsPropertyConstants.Asset.BUILDING_SUB_ROOM_NUMBER);
077            LOCATION_FIELD_MAP.put(LocationField.CONTACT_NAME, CamsPropertyConstants.Asset.AssetLocation.CONTACT_NAME);
078            LOCATION_FIELD_MAP.put(LocationField.STREET_ADDRESS, CamsPropertyConstants.Asset.AssetLocation.STREET_ADDRESS);
079            LOCATION_FIELD_MAP.put(LocationField.CITY_NAME, CamsPropertyConstants.Asset.AssetLocation.CITY_NAME);
080            LOCATION_FIELD_MAP.put(LocationField.STATE_CODE, CamsPropertyConstants.Asset.AssetLocation.STATE_CODE);
081            LOCATION_FIELD_MAP.put(LocationField.ZIP_CODE, CamsPropertyConstants.Asset.AssetLocation.ZIP_CODE);
082            LOCATION_FIELD_MAP.put(LocationField.COUNTRY_CODE, CamsPropertyConstants.Asset.AssetLocation.COUNTRY_CODE);
083        }
084    
085        // protected AgencyService agencyService = SpringContext.getBean(AgencyService.class);
086        protected AssetService assetService = SpringContext.getBean(AssetService.class);
087        protected ParameterService parameterService = SpringContext.getBean(ParameterService.class);
088        protected PaymentSummaryService paymentSummaryService = SpringContext.getBean(PaymentSummaryService.class);
089        protected RetirementInfoService retirementInfoService = SpringContext.getBean(RetirementInfoService.class);
090        protected EquipmentLoanOrReturnService equipmentLoanOrReturnService = SpringContext.getBean(EquipmentLoanOrReturnService.class);
091        protected AssetDateService assetDateService = SpringContext.getBean(AssetDateService.class);
092        protected AssetComponentService assetComponentService = SpringContext.getBean(AssetComponentService.class);
093        protected UniversityDateService universityDateService = SpringContext.getBean(UniversityDateService.class);
094        protected AssetLocationService assetLocationService = SpringContext.getBean(AssetLocationService.class);
095        protected DateTimeService dateTimeService = SpringContext.getBean(DateTimeService.class);
096    
097        protected Asset newAsset;
098        protected Asset oldAsset;
099        protected boolean isFabrication;
100    
101    
102        /**
103         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
104         */
105        @Override
106        protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
107            initializeAttributes(document);
108            boolean valid = true;
109            if (SpringContext.getBean(AssetService.class).isAssetFabrication(document)) {
110                this.isFabrication = true;
111                valid &= validateAccount();
112                valid &= validateLocation();
113                valid &= validateFabricationDetails();
114            }
115            else {
116                setAssetComponentNumbers(newAsset);
117                paymentSummaryService.calculateAndSetPaymentSummary(oldAsset);
118                paymentSummaryService.calculateAndSetPaymentSummary(newAsset);
119    
120                assetService.setSeparateHistory(oldAsset);
121                assetService.setSeparateHistory(newAsset);
122    
123                retirementInfoService.setRetirementInfo(oldAsset);
124                retirementInfoService.setRetirementInfo(newAsset);
125    
126                equipmentLoanOrReturnService.setEquipmentLoanInfo(oldAsset);
127                equipmentLoanOrReturnService.setEquipmentLoanInfo(newAsset);
128    
129                valid &= processAssetValidation(document);
130                valid &= validateWarrantyInformation(newAsset);
131                valid &= validateDepreciationData(newAsset);
132    
133                valid &= super.processCustomSaveDocumentBusinessRules(document);
134                if (valid) {
135                    assetDateService.checkAndUpdateLastInventoryDate(oldAsset, newAsset);
136                    assetDateService.checkAndUpdateDepreciationDate(oldAsset, newAsset);
137                    assetDateService.checkAndUpdateFiscalYearAndPeriod(oldAsset, newAsset);
138                }
139    
140                valid &= checkAssetLocked(document);
141            }
142            return valid;
143        }
144    
145        /**
146         * Check if asset is locked by other document.
147         * 
148         * @param document
149         * @param valid
150         * @return
151         */
152        protected boolean checkAssetLocked(MaintenanceDocument document) {
153            Asset asset = (Asset) document.getNewMaintainableObject().getBusinessObject();
154            return !getCapitalAssetManagementModuleService().isAssetLocked(retrieveAssetNumberForLocking(asset), CamsConstants.DocumentTypeName.ASSET_EDIT, document.getDocumentNumber());
155        }
156    
157        /**
158         * Retrieve asset numbers need to be locked.
159         * 
160         * @return
161         */
162        protected List<Long> retrieveAssetNumberForLocking(Asset asset) {
163            List<Long> capitalAssetNumbers = new ArrayList<Long>();
164            if (asset.getCapitalAssetNumber() != null) {
165                capitalAssetNumbers.add(asset.getCapitalAssetNumber());
166            }
167            return capitalAssetNumbers;
168        }
169    
170        /**
171         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument,
172         *      java.lang.String, org.kuali.rice.kns.bo.PersistableBusinessObject)
173         */
174        @Override
175        public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument documentCopy, String collectionName, PersistableBusinessObject bo) {
176            boolean success = true;
177    
178            // get all incidentDates from AssetRepairHistory collection and check for duplicate dates.
179            if (collectionName.equals(CamsConstants.Asset.COLLECTION_ID_ASSET_REPAIR_HISTORY)) {
180    
181                Asset asset = (Asset) documentCopy.getNewMaintainableObject().getBusinessObject();
182                Set<Date> incidentDateSet = new HashSet<Date>();
183    
184                for (AssetRepairHistory assetRepairHistory : asset.getAssetRepairHistory()) {
185                    if (assetRepairHistory.getIncidentDate() != null) {
186                        incidentDateSet.add(assetRepairHistory.getIncidentDate());
187                    }
188                }
189    
190                AssetRepairHistory assetRepairHistoryDetails = (AssetRepairHistory) bo;
191    
192                success &= checkDuplicateIncidentDate(assetRepairHistoryDetails, incidentDateSet);
193    
194                return success & super.processCustomAddCollectionLineBusinessRules(documentCopy, collectionName, bo);
195            }
196    
197            return success;
198        }
199    
200        /**
201         * Check for duplicate incident dates within the Repair History section
202         * 
203         * @param assetRepairHistory
204         * @param incidentDateSet
205         * @return boolean
206         */
207        protected boolean checkDuplicateIncidentDate(AssetRepairHistory assetRepairHistory, Set<Date> incidentDateSet) {
208            boolean success = true;
209    
210            if (!incidentDateSet.add(assetRepairHistory.getIncidentDate())) {
211                GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetRepairHistory.INCIDENT_DATE, CamsKeyConstants.AssetRepairHistory.ERROR_DUPLICATE_INCIDENT_DATE);
212                success &= false;
213            }
214    
215            return success;
216        }
217    
218        /**
219         * Validate fabrication details
220         * 
221         * @return boolean
222         */
223        protected boolean validateFabricationDetails() {
224            /**
225             * Please don't remove this validation, forcing required fields from DD file is not possible and will break asset edit
226             * screen, so please leave this validation here.
227             */
228            boolean valid = true;
229            if (newAsset.getFabricationEstimatedTotalAmount() != null && newAsset.getFabricationEstimatedTotalAmount().isNegative()) {
230                putFieldError(CamsPropertyConstants.Asset.FABRICATION_ESTIMATED_TOTAL_AMOUNT, CamsKeyConstants.ERROR_FABRICATION_ESTIMATED_TOTAL_AMOUNT_NEGATIVE);
231                valid &= false;
232            }
233            if (newAsset.getEstimatedFabricationCompletionDate() != null && newAsset.getEstimatedFabricationCompletionDate().before(DateUtils.clearTimeFields(dateTimeService.getCurrentDate()))) {
234                putFieldError(CamsPropertyConstants.Asset.ESTIMATED_FABRICATION_COMPLETION_DATE, CamsKeyConstants.ERROR_ESTIMATED_FABRICATION_COMPLETION_DATE_PAST);
235                valid &= false;
236            }
237            if (newAsset.getFabricationEstimatedRetentionYears() != null && newAsset.getFabricationEstimatedRetentionYears().intValue() < 0) {
238                putFieldError(CamsPropertyConstants.Asset.FABRICATION_ESTIMATED_RETENTION_YEARS, CamsKeyConstants.ERROR_ESTIMATED_FABRICATION_LIFE_LIMIT_NEGATIVE);
239                valid &= false;
240            }
241            return valid;
242        }
243    
244        /**
245         * Validate account
246         * 
247         * @return boolean
248         */
249        protected boolean validateAccount() {
250            boolean valid = true;
251            Account currentOwnerAccount = newAsset.getOrganizationOwnerAccount();
252            Account previoudOwnerAccount = oldAsset.getOrganizationOwnerAccount();
253    
254            // Account is valid?
255            if (ObjectUtils.isNull(currentOwnerAccount) && StringUtils.isNotBlank(newAsset.getOrganizationOwnerAccountNumber())) {
256                putFieldError(CamsPropertyConstants.Asset.ORGANIZATION_OWNER_ACCOUNT_NUMBER, CamsKeyConstants.ORGANIZATION_OWNER_ACCOUNT_INVALID);
257                valid &= false;
258            }
259    
260            // check if values changed, if not return
261            if (ObjectUtils.isNotNull(previoudOwnerAccount) && ObjectUtils.isNotNull(currentOwnerAccount) && previoudOwnerAccount.getChartOfAccountsCode().equals(currentOwnerAccount.getChartOfAccountsCode()) && previoudOwnerAccount.getAccountNumber().equals(currentOwnerAccount.getAccountNumber())) {
262                return valid;
263    
264            }
265            else if (ObjectUtils.isNotNull(currentOwnerAccount) && (currentOwnerAccount.isExpired() || !currentOwnerAccount.isActive())) {
266                // Account is not active
267                putFieldError(CamsPropertyConstants.Asset.ORGANIZATION_OWNER_ACCOUNT_NUMBER, CamsKeyConstants.ORGANIZATION_OWNER_ACCOUNT_INACTIVE);
268                valid &= false;
269            }
270            return valid;
271        }
272    
273        /**
274         * Set asset component numbers
275         * 
276         * @param asset
277         */
278        protected void setAssetComponentNumbers(Asset asset) {
279            List<AssetComponent> assetComponents = asset.getAssetComponents();
280            Integer maxNo = null;
281            for (AssetComponent assetComponent : assetComponents) {
282                assetComponent.setCapitalAssetNumber(asset.getCapitalAssetNumber());
283                if (maxNo == null) {
284                    maxNo = assetComponentService.getMaxSequenceNumber(assetComponent);
285                }
286                if (assetComponent.getComponentNumber() == null) {
287                    assetComponent.setComponentNumber(++maxNo);
288                }
289            }
290        }
291    
292        /**
293         * Validates Asset document.
294         * 
295         * @param document MaintenanceDocument instance
296         * @return boolean false or true
297         */
298        protected boolean processAssetValidation(MaintenanceDocument document) {
299            boolean valid = true;
300    
301            // validate Inventory Status Code.
302            if (!StringUtils.equalsIgnoreCase(oldAsset.getInventoryStatusCode(), newAsset.getInventoryStatusCode())) {
303                valid &= validateInventoryStatusCode(oldAsset.getInventoryStatusCode(), newAsset.getInventoryStatusCode());
304            }
305    
306            // validate Organization Owner Account Number
307            if (!StringUtils.equalsIgnoreCase(oldAsset.getOrganizationOwnerAccountNumber(), newAsset.getOrganizationOwnerAccountNumber())) {
308                valid &= validateAccount();
309            }
310    
311            // validate Vendor Name.
312            if (!StringUtils.equalsIgnoreCase(oldAsset.getVendorName(), newAsset.getVendorName())) {
313                valid &= validateVendorName();
314            }
315    
316            // validate Tag Number.
317            if (!StringUtils.equalsIgnoreCase(oldAsset.getCampusTagNumber(), newAsset.getCampusTagNumber())) {
318                valid &= validateTagNumber();
319            }
320    
321            // validate location.
322            valid &= validateLocation();
323    
324            // validate In-service Date
325            if (assetService.isInServiceDateChanged(oldAsset, newAsset)) {
326                valid &= validateInServiceDate();
327            }
328            return valid;
329        }
330    
331    
332        /**
333         * Check if the new In-service Date is a valid University Date
334         * 
335         * @return
336         */
337        protected boolean validateInServiceDate() {
338            boolean valid = true;
339            // if asset already starts depreciation, the user can't blank in-service date.
340            if (ObjectUtils.isNull(newAsset.getCapitalAssetInServiceDate()) && assetService.isAssetDepreciationStarted(oldAsset)) {
341                putFieldError(CamsPropertyConstants.Asset.ASSET_DATE_OF_SERVICE, CamsKeyConstants.ERROR_BLANK_IN_SERVICE_DATE_DISALLOWED);
342                valid = false;
343            }
344    
345            else if (ObjectUtils.isNotNull(newAsset.getCapitalAssetInServiceDate()) && universityDateService.getFiscalYear(newAsset.getCapitalAssetInServiceDate()) == null) {
346                putFieldError(CamsPropertyConstants.Asset.ASSET_DATE_OF_SERVICE, CamsKeyConstants.ERROR_INVALID_IN_SERVICE_DATE);
347                valid = false;
348            }
349            return valid;
350        }
351    
352    
353        /**
354         * Check if off campus fields has changed.
355         * 
356         * @return
357         */
358        protected boolean isOffCampusLocationChanged() {
359            boolean changed = false;
360            AssetLocation oldLocation = oldAsset.getOffCampusLocation();
361            AssetLocation newLocation = newAsset.getOffCampusLocation();
362    
363            if (!StringUtils.equalsIgnoreCase(newLocation.getAssetLocationContactName(), oldLocation.getAssetLocationContactName()) || !StringUtils.equalsIgnoreCase(newLocation.getAssetLocationStreetAddress(), oldLocation.getAssetLocationStreetAddress()) || !StringUtils.equalsIgnoreCase(newLocation.getAssetLocationCityName(), oldLocation.getAssetLocationCityName()) || !StringUtils.equalsIgnoreCase(newLocation.getAssetLocationStateCode(), oldLocation.getAssetLocationStateCode()) || !StringUtils.equalsIgnoreCase(newLocation.getAssetLocationZipCode(), oldLocation.getAssetLocationZipCode()) || !StringUtils.equalsIgnoreCase(newLocation.getAssetLocationCountryCode(), oldLocation.getAssetLocationCountryCode())) {
364                changed = true;
365            }
366            return changed;
367        }
368    
369        /**
370         * Validate Inventory Status Code Change
371         */
372        protected boolean validateInventoryStatusCode(String oldInventoryStatusCode, String newInventoryStatusCode) {
373            boolean valid = true;
374            if (assetService.isCapitalAsset(oldAsset) && assetService.isAssetRetired(newAsset)) {
375                // disallow retire capital asset.
376                putFieldError(CamsPropertyConstants.Asset.ASSET_INVENTORY_STATUS, CamsKeyConstants.ERROR_ASSET_RETIRED_CAPITAL);
377                valid = false;
378            }
379            else {
380                // validate inventory status change per system parameter.
381                GlobalVariables.getMessageMap().addToErrorPath(MAINTAINABLE_ERROR_PATH);
382                valid &= parameterService.getParameterEvaluator(Asset.class, CamsConstants.Parameters.VALID_INVENTROY_STATUS_CODE_CHANGE, CamsConstants.Parameters.INVALID_INVENTROY_STATUS_CODE_CHANGE, oldAsset.getInventoryStatusCode(), newAsset.getInventoryStatusCode()).evaluateAndAddError(newAsset.getClass(), CamsPropertyConstants.Asset.ASSET_INVENTORY_STATUS);
383                GlobalVariables.getMessageMap().removeFromErrorPath(MAINTAINABLE_ERROR_PATH);
384            }
385            return valid;
386        }
387    
388        protected void initializeAttributes(MaintenanceDocument document) {
389            if (newAsset == null) {
390                newAsset = (Asset) document.getNewMaintainableObject().getBusinessObject();
391            }
392            if (oldAsset == null) {
393                oldAsset = (Asset) document.getOldMaintainableObject().getBusinessObject();
394            }
395            // for fabrication
396            if (oldAsset == null) {
397                oldAsset = newAsset;
398            }
399        }
400    
401        /**
402         * If the tag number has not been assigned, the departmental user will be able to update the tag number. The Tag Number shall be
403         * verified that the tag number does not exist on another asset.
404         * 
405         * @param asset
406         * @return
407         */
408        protected boolean validateTagNumber() {
409            boolean valid = true;
410            boolean anyFound = false;
411    
412            if (!assetService.isTagNumberCheckExclude(newAsset)) {
413    
414                Map<String, Object> fieldValues = new HashMap<String, Object>();
415                fieldValues.put(CamsPropertyConstants.Asset.CAMPUS_TAG_NUMBER, newAsset.getCampusTagNumber().toUpperCase());
416    
417                Collection<Asset> results = getBoService().findMatching(Asset.class, fieldValues);
418    
419                for (Asset asset : results) {
420                    if (!asset.getCapitalAssetNumber().equals(newAsset.getCapitalAssetNumber())) {
421                        // KFSMI-6149 - do not invalidate if the asset from the database is retired
422                        if (StringUtils.isBlank(asset.getRetirementReasonCode())) {
423                            putFieldError(CamsPropertyConstants.Asset.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetLocationGlobal.ERROR_DUPLICATE_TAG_NUMBER_FOUND, new String[] { newAsset.getCampusTagNumber(), asset.getCapitalAssetNumber().toString(), newAsset.getCapitalAssetNumber().toString() });
424                            valid &= false;
425                            LOG.info("The asset tag number [" + newAsset.getCampusTagNumber().toUpperCase() + "] is a duplicate of asset number [" + asset.getCapitalAssetNumber().toString() + "]'s tag number");
426                        } else {
427                            LOG.info("Although the asset tag number [" + newAsset.getCampusTagNumber().toUpperCase() + "] is a duplicate of asset number [" + asset.getCapitalAssetNumber().toString() + "]'s tag number, the old asset has already been retired");
428                        }
429                    }
430                }
431            }
432    
433            return valid;
434        }
435    
436        /**
437         * The Vendor Name is required for capital equipment and not required for non-capital assets.
438         * 
439         * @param asset
440         * @return
441         */
442        protected boolean validateVendorName() {
443            boolean valid = true;
444    
445            if (assetService.isCapitalAsset(newAsset) && StringUtils.isBlank(newAsset.getVendorName())) {
446                putFieldError(CamsPropertyConstants.Asset.VENDOR_NAME, CamsKeyConstants.ERROR_CAPITAL_ASSET_VENDOR_NAME_REQUIRED);
447                valid &= false;
448    
449            }
450            return valid;
451        }
452    
453    
454        /**
455         * Validate Asset Location fields
456         * 
457         * @param asset
458         * @return
459         */
460        protected boolean validateLocation() {
461            GlobalVariables.getMessageMap().addToErrorPath("document.newMaintainableObject");
462            boolean isCapitalAsset = assetService.isCapitalAsset(newAsset);
463            boolean valid = assetLocationService.validateLocation(LOCATION_FIELD_MAP, newAsset, isCapitalAsset, newAsset.getCapitalAssetType());
464            GlobalVariables.getMessageMap().removeFromErrorPath("document.newMaintainableObject");
465    
466            if (valid && (this.isFabrication || isOffCampusLocationChanged())) {
467                assetLocationService.updateOffCampusLocation(newAsset);
468            }
469            return valid;
470        }
471    
472        /**
473         * Validate warranty information if user enters value
474         * 
475         * @param asset Asset
476         * @return validation result
477         */
478        protected boolean validateWarrantyInformation(Asset asset) {
479            AssetWarranty warranty = asset.getAssetWarranty();
480            if (warranty != null) {
481                if (!StringUtils.isEmpty(warranty.getWarrantyContactName()) || !StringUtils.isEmpty(warranty.getWarrantyPhoneNumber()) || !StringUtils.isEmpty(warranty.getWarrantyText()) || warranty.getWarrantyBeginningDate() != null || warranty.getWarrantyEndingDate() != null) {
482                    if (StringUtils.isEmpty(warranty.getWarrantyNumber())) {
483                        // warranty number is mandatory when any other related info is known
484                        putFieldError(ASSET_WARRANTY_WARRANTY_NUMBER, ERROR_INVALID_ASSET_WARRANTY_NO);
485                        return false;
486                    }
487                }
488            }
489            return true;
490        }
491    
492        /**
493         * validates depreciation data
494         * 
495         * @param asset
496         * @return boolean
497         */
498        protected boolean validateDepreciationData(Asset asset) {
499            if (asset.getSalvageAmount() == null) {
500                asset.setSalvageAmount(KualiDecimal.ZERO);
501            }
502    
503            if (asset.getBaseAmount() == null) {
504                asset.setBaseAmount(KualiDecimal.ZERO);
505            }
506    
507            // If the salvage amount is greater than the base amount, then data is invalid
508            if (asset.getSalvageAmount().compareTo(asset.getBaseAmount()) > 0) {
509                putFieldError(CamsPropertyConstants.Asset.SALVAGE_AMOUNT, CamsKeyConstants.Asset.ERROR_INVALID_SALVAGE_AMOUNT);
510                return false;
511            }
512            else {
513                return true;
514            }
515        }
516    
517        @Override
518        protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
519            boolean valid = super.processCustomRouteDocumentBusinessRules(document);
520            initializeAttributes(document);
521            if (SpringContext.getBean(AssetService.class).isAssetFabrication(document) && newAsset.getCapitalAssetNumber() == null) {
522                newAsset.setCapitalAssetNumber(NextAssetNumberFinder.getLongValue());
523                oldAsset.setCapitalAssetNumber(newAsset.getCapitalAssetNumber());
524                newAsset.setLastInventoryDate(new Timestamp(SpringContext.getBean(DateTimeService.class).getCurrentSqlDate().getTime()));
525                oldAsset.setLastInventoryDate(new Timestamp(SpringContext.getBean(DateTimeService.class).getCurrentSqlDate().getTime()));
526            }
527    
528            // check for change
529            valid &= checkAcquisitionTypeCodeChange();
530            valid &= checkConditionCodeChange();
531            valid &= checkAssetDepreciationMethodChange();
532            valid &= checkAssetStatusCodeChange();
533            valid &= checkAssetTypeCodeChange();
534            valid &= checkFinancialObjectSubtypeCodeChange();
535            valid &= validateAccount();
536    
537            KualiWorkflowDocument workflowDoc = document.getDocumentHeader().getWorkflowDocument();
538            // adding asset locks for asset edit only
539            if (newAsset instanceof Asset && !(newAsset instanceof AssetFabrication) && !GlobalVariables.getMessageMap().hasErrors() && (workflowDoc.stateIsInitiated() || workflowDoc.stateIsSaved())) {
540                valid &= setAssetLock(document);
541            }
542            return valid;
543        }
544    
545        /**
546         * Locking asset number
547         * 
548         * @param document
549         * @return
550         */
551        protected boolean setAssetLock(MaintenanceDocument document) {
552            Asset asset = (Asset) document.getNewMaintainableObject().getBusinessObject();
553            return this.getCapitalAssetManagementModuleService().storeAssetLocks(retrieveAssetNumberForLocking(asset), document.getDocumentNumber(), CamsConstants.DocumentTypeName.ASSET_EDIT, null);
554        }
555    
556        protected CapitalAssetManagementModuleService getCapitalAssetManagementModuleService() {
557            return SpringContext.getBean(CapitalAssetManagementModuleService.class);
558        }
559    
560        /**
561         * Convenience method to append the path prefix
562         */
563        public TypedArrayList putError(String propertyName, String errorKey, String... errorParameters) {
564            return GlobalVariables.getMessageMap().putError(CamsConstants.DOCUMENT_PATH + "." + propertyName, errorKey, errorParameters);
565        }
566    
567        /**
568         * Check if the Acquisition Type Code is valid or is inactive.
569         * 
570         * @param oldAcquisitionTypeCode
571         * @param newAcquisitionTypeCode
572         * @return boolean
573         */
574        protected boolean checkAcquisitionTypeCodeChange() {
575            if (ObjectUtils.isNull(newAsset.getAcquisitionType())) {
576                putFieldError(CamsPropertyConstants.Asset.ACQUISITION_TYPE_CODE, CamsKeyConstants.Asset.ERROR_ACQUISITION_TYPE_CODE_INVALID);
577                return false;
578            }
579            else if (!StringUtils.equalsIgnoreCase(newAsset.getAcquisitionTypeCode(), oldAsset.getAcquisitionTypeCode())) {
580                newAsset.refreshReferenceObject(CamsPropertyConstants.Asset.REF_ACQUISITION_TYPE);
581                if (ObjectUtils.isNotNull(newAsset.getAcquisitionType())) {
582                    if (!newAsset.getAcquisitionType().isActive()) {
583                        putFieldError(CamsPropertyConstants.Asset.ACQUISITION_TYPE_CODE, CamsKeyConstants.Asset.ERROR_ACQUISITION_TYPE_CODE_INACTIVE);
584                        return false;
585                    }
586                }
587            }
588            return true;
589        }
590    
591        /**
592         * Check if the Asset Condition is valid or is inactive.
593         * 
594         * @param oldConditionCode
595         * @param newConditionCode
596         * @return boolean
597         */
598        protected boolean checkConditionCodeChange() {
599            if (ObjectUtils.isNull(newAsset.getCondition())) {
600                putFieldError(CamsPropertyConstants.Asset.CONDITION_CODE, CamsKeyConstants.Asset.ERROR_ASSET_CONDITION_INVALID);
601                return false;
602            }
603            else if (!StringUtils.equalsIgnoreCase(newAsset.getConditionCode(), oldAsset.getConditionCode())) {
604                newAsset.refreshReferenceObject(CamsPropertyConstants.Asset.REF_ASSET_CONDITION);
605                if (ObjectUtils.isNotNull(newAsset.getCondition())) {
606                    if (!newAsset.getCondition().isActive()) {
607                        putFieldError(CamsPropertyConstants.Asset.CONDITION_CODE, CamsKeyConstants.Asset.ERROR_ASSET_CONDITION_INACTIVE);
608                        return false;
609                    }
610                }
611            }
612            return true;
613        }
614    
615        /**
616         * Check if the Asset Depreciation Method is valid or is inactive.
617         * 
618         * @param oldAssetDepreciationMethod
619         * @param newAssetDepreciationMethod
620         * @return boolean
621         */
622        protected boolean checkAssetDepreciationMethodChange() {
623            if (ObjectUtils.isNull(newAsset.getAssetPrimaryDepreciationMethod())) {
624                putFieldError(CamsPropertyConstants.Asset.PRIMARY_DEPRECIATION_METHOD, CamsKeyConstants.Asset.ERROR_DEPRECATION_METHOD_CODE_INVALID);
625                return false;
626            }
627            else if (!StringUtils.equalsIgnoreCase(newAsset.getPrimaryDepreciationMethodCode(), oldAsset.getPrimaryDepreciationMethodCode())) {
628                newAsset.refreshReferenceObject(CamsPropertyConstants.Asset.REF_ASSET_DEPRECATION_METHOD);
629                if (ObjectUtils.isNotNull(newAsset.getAssetPrimaryDepreciationMethod())) {
630                    if (!newAsset.getAssetPrimaryDepreciationMethod().isActive()) {
631                        putFieldError(CamsPropertyConstants.Asset.PRIMARY_DEPRECIATION_METHOD, CamsKeyConstants.Asset.ERROR_DEPRECATION_METHOD_CODE_INACTIVE);
632                        return false;
633                    }
634                }
635            }
636            return true;
637        }
638    
639        /**
640         * Check if the Asset Status Code is valid or is inactive.
641         * 
642         * @param oldAssetStatus
643         * @param newAssetStatus
644         * @return boolean
645         */
646        protected boolean checkAssetStatusCodeChange() {
647            if (ObjectUtils.isNull(newAsset.getInventoryStatus())) {
648                putFieldError(CamsPropertyConstants.Asset.ASSET_INVENTORY_STATUS, CamsKeyConstants.Asset.ERROR_ASSET_STATUS_INVALID);
649                return false;
650            }
651            else if (!StringUtils.equalsIgnoreCase(newAsset.getInventoryStatusCode(), oldAsset.getInventoryStatusCode())) {
652                newAsset.refreshReferenceObject(CamsPropertyConstants.Asset.REF_ASSET_STATUS);
653                if (ObjectUtils.isNotNull(newAsset.getInventoryStatus())) {
654                    if (!newAsset.getInventoryStatus().isActive()) {
655                        putFieldError(CamsPropertyConstants.Asset.ASSET_INVENTORY_STATUS, CamsKeyConstants.Asset.ERROR_ASSET_STATUS_INACTIVE);
656                        return false;
657                    }
658                }
659            }
660            return true;
661        }
662    
663        /**
664         * Check if the Asset Type Code is valid or is inactive.
665         * 
666         * @param oldAssetType
667         * @param newAssetType
668         * @return boolean
669         */
670        protected boolean checkAssetTypeCodeChange() {
671            if (ObjectUtils.isNull(newAsset.getCapitalAssetType())) {
672                putFieldError(CamsPropertyConstants.Asset.CAPITAL_ASSET_TYPE_CODE, CamsKeyConstants.Asset.ERROR_TYPE_CODE_INVALID);
673                return false;
674            }
675            else if (!StringUtils.equalsIgnoreCase(newAsset.getCapitalAssetTypeCode(), oldAsset.getCapitalAssetTypeCode())) {
676                newAsset.refreshReferenceObject(CamsPropertyConstants.Asset.REF_ASSET_TYPE);
677                if (ObjectUtils.isNotNull(newAsset.getCapitalAssetType())) {
678                    if (!newAsset.getCapitalAssetType().isActive()) {
679                        putFieldError(CamsPropertyConstants.AssetType.CAPITAL_ASSET_TYPE_CODE, CamsKeyConstants.Asset.ERROR_TYPE_CODE_INACTIVE);
680                        return false;
681                    }
682                }
683            }
684            return true;
685        }
686    
687        /**
688         * Check if the Financial Object Sub-Type Code is valid or is inactive.
689         * 
690         * @param oldObjectSubType
691         * @param newObjectSubType
692         * @return boolean
693         */
694        protected boolean checkFinancialObjectSubtypeCodeChange() {
695            if (ObjectUtils.isNotNull(newAsset.getFinancialObjectSubType()) || StringUtils.isNotBlank(newAsset.getFinancialObjectSubTypeCode())) {
696                newAsset.refreshReferenceObject(CamsPropertyConstants.Asset.REF_OBJECT_SUB_TYPE);
697                if (ObjectUtils.isNull(newAsset.getFinancialObjectSubType())) {
698                    putFieldError(CamsPropertyConstants.Asset.FINANCIAL_OBJECT_SUB_TYP_CODE, CamsKeyConstants.Asset.ERROR_FINANCIAL_OBJECT_SUBTYPE_CODE_INVALID);
699                    return false;
700                }
701                else if (!newAsset.getFinancialObjectSubType().isActive()) {
702                    putFieldError(CamsPropertyConstants.Asset.FINANCIAL_OBJECT_SUB_TYP_CODE, CamsKeyConstants.Asset.ERROR_FINANCIAL_OBJECT_SUBTYPE_CODE_INACTIVE);
703                    return false;
704                }
705            }
706            return true;
707        }
708    }