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 java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.List;
021    import java.util.Map;
022    
023    import org.apache.commons.lang.StringUtils;
024    import org.kuali.kfs.coa.businessobject.ObjectCode;
025    import org.kuali.kfs.coa.service.ObjectCodeService;
026    import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService;
027    import org.kuali.kfs.module.cam.CamsConstants;
028    import org.kuali.kfs.module.cam.CamsKeyConstants;
029    import org.kuali.kfs.module.cam.CamsPropertyConstants;
030    import org.kuali.kfs.module.cam.CamsConstants.DocumentTypeName;
031    import org.kuali.kfs.module.cam.businessobject.Asset;
032    import org.kuali.kfs.module.cam.businessobject.AssetObjectCode;
033    import org.kuali.kfs.module.cam.businessobject.AssetPayment;
034    import org.kuali.kfs.module.cam.businessobject.AssetRetirementGlobal;
035    import org.kuali.kfs.module.cam.businessobject.AssetRetirementGlobalDetail;
036    import org.kuali.kfs.module.cam.document.gl.AssetRetirementGeneralLedgerPendingEntrySource;
037    import org.kuali.kfs.module.cam.document.service.AssetObjectCodeService;
038    import org.kuali.kfs.module.cam.document.service.AssetPaymentService;
039    import org.kuali.kfs.module.cam.document.service.AssetRetirementService;
040    import org.kuali.kfs.module.cam.document.service.AssetService;
041    import org.kuali.kfs.sys.KFSKeyConstants;
042    import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
043    import org.kuali.kfs.sys.context.SpringContext;
044    import org.kuali.kfs.sys.document.authorization.FinancialSystemMaintenanceDocumentAuthorizerBase;
045    import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
046    import org.kuali.rice.kns.bo.PersistableBusinessObject;
047    import org.kuali.rice.kns.document.MaintenanceDocument;
048    import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
049    import org.kuali.rice.kns.service.BusinessObjectService;
050    import org.kuali.rice.kns.service.DataDictionaryService;
051    import org.kuali.rice.kns.service.DocumentHelperService;
052    import org.kuali.rice.kns.service.ParameterService;
053    import org.kuali.rice.kns.util.GlobalVariables;
054    import org.kuali.rice.kns.util.ObjectUtils;
055    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
056    
057    /**
058     * Business rules applicable to AssetLocationGlobal documents.
059     */
060    public class AssetRetirementGlobalRule extends MaintenanceDocumentRuleBase {
061    
062        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetRetirementGlobalRule.class);
063    
064        protected PersistableBusinessObject bo;
065    
066    
067        /**
068         * Constructs a AssetLocationGlobalRule
069         */
070        public AssetRetirementGlobalRule() {
071        }
072    
073        /**
074         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#setupConvenienceObjects()
075         */
076        @Override
077        public void setupConvenienceObjects() {
078            AssetRetirementGlobal newRetirementGlobal = (AssetRetirementGlobal) super.getNewBo();
079            newRetirementGlobal.refreshNonUpdateableReferences();
080            for (AssetRetirementGlobalDetail detail : newRetirementGlobal.getAssetRetirementGlobalDetails()) {
081                detail.refreshNonUpdateableReferences();
082            }
083        }
084    
085        /**
086         * Forces the processing of rules when saving.
087         * 
088         * @param document MaintenanceDocument
089         * @return boolean true when valid; Namely we need to enforce foreign key constraints else
090         *         processCustomSaveDocumentBusinessRules does not force user back to the document for error correction.
091         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#isDocumentValidForSave(org.kuali.rice.kns.document.MaintenanceDocument)
092         */
093        @Override
094        protected boolean isDocumentValidForSave(MaintenanceDocument document) {
095            boolean valid = super.isDocumentValidForSave(document);
096    
097            if (valid) {
098                AssetRetirementGlobal assetRetirementGlobal = (AssetRetirementGlobal) document.getNewMaintainableObject().getBusinessObject();
099    
100                setupConvenienceObjects();
101                valid &= assetRetirementValidation(assetRetirementGlobal, document);
102            }
103    
104            return valid;
105        }
106    
107    
108        /**
109         * Processes rules when saving this global.
110         * 
111         * @param document MaintenanceDocument type of document to be processed.
112         * @return boolean true when success
113         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomSaveDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
114         */
115        @Override
116        protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
117            boolean valid = true;
118            AssetRetirementGlobal assetRetirementGlobal = (AssetRetirementGlobal) document.getNewMaintainableObject().getBusinessObject();
119    
120            setupConvenienceObjects();
121            valid &= assetRetirementValidation(assetRetirementGlobal, document);
122    
123            if ((valid && super.processCustomSaveDocumentBusinessRules(document)) && !getAssetRetirementService().isAssetRetiredByMerged(assetRetirementGlobal) && !allPaymentsFederalOwned(assetRetirementGlobal)) {
124                // Check if Asset Object Code and Object code exists and active.
125                if (valid &= validateObjectCodesForGLPosting(assetRetirementGlobal)) {
126                    // create poster
127                    AssetRetirementGeneralLedgerPendingEntrySource assetRetirementGlPoster = new AssetRetirementGeneralLedgerPendingEntrySource((FinancialSystemDocumentHeader) document.getDocumentHeader());
128                    // create postables
129                    getAssetRetirementService().createGLPostables(assetRetirementGlobal, assetRetirementGlPoster);
130    
131                    if (SpringContext.getBean(GeneralLedgerPendingEntryService.class).generateGeneralLedgerPendingEntries(assetRetirementGlPoster)) {
132                        assetRetirementGlobal.setGeneralLedgerPendingEntries(assetRetirementGlPoster.getPendingEntries());
133                    }
134                    else {
135                        assetRetirementGlPoster.getPendingEntries().clear();
136                    }
137                }
138            }
139    
140            // add doc header description if retirement reason is "MERGED"
141            if (CamsConstants.AssetRetirementReasonCode.MERGED.equals(assetRetirementGlobal.getRetirementReasonCode())) {
142                if (!document.getDocumentHeader().getDocumentDescription().toLowerCase().contains(CamsConstants.AssetRetirementGlobal.MERGE_AN_ASSET_DESCRIPTION.toLowerCase())) {
143                    document.getDocumentHeader().setDocumentDescription(CamsConstants.AssetRetirementGlobal.MERGE_AN_ASSET_DESCRIPTION + " " + document.getDocumentHeader().getDocumentDescription());
144                }
145            }
146            // get asset locks
147            List<Long> capitalAssetNumbers = retrieveAssetNumbersForLocking(assetRetirementGlobal);
148            valid &= !this.getCapitalAssetManagementModuleService().isAssetLocked(capitalAssetNumbers, DocumentTypeName.ASSET_RETIREMENT_GLOBAL, document.getDocumentNumber());
149    
150            return valid;
151        }
152    
153        protected List<Long> retrieveAssetNumbersForLocking(AssetRetirementGlobal assetRetirementGlobal) {
154            List<Long> capitalAssetNumbers = new ArrayList<Long>();
155            if (getAssetRetirementService().isAssetRetiredByMerged(assetRetirementGlobal) && assetRetirementGlobal.getMergedTargetCapitalAssetNumber() != null) {
156                capitalAssetNumbers.add(assetRetirementGlobal.getMergedTargetCapitalAssetNumber());
157            }
158    
159            for (AssetRetirementGlobalDetail retirementDetail : assetRetirementGlobal.getAssetRetirementGlobalDetails()) {
160                if (retirementDetail.getCapitalAssetNumber() != null) {
161                    capitalAssetNumbers.add(retirementDetail.getCapitalAssetNumber());
162                }
163            }
164            return capitalAssetNumbers;
165        }
166    
167        @Override
168        protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
169            boolean valid = super.processCustomRouteDocumentBusinessRules(document);
170    
171            KualiWorkflowDocument workflowDoc = document.getDocumentHeader().getWorkflowDocument();
172            // adding asset locks
173            if (!GlobalVariables.getMessageMap().hasErrors() && (workflowDoc.stateIsInitiated() || workflowDoc.stateIsSaved())) {
174                valid &= setAssetLocks(document);
175    
176            }
177            return valid;
178        }
179    
180        protected boolean setAssetLocks(MaintenanceDocument document) {
181            AssetRetirementGlobal assetRetirementGlobal = (AssetRetirementGlobal) document.getNewMaintainableObject().getBusinessObject();
182            // get asset locks
183            return this.getCapitalAssetManagementModuleService().storeAssetLocks(retrieveAssetNumbersForLocking(assetRetirementGlobal), document.getDocumentNumber(), DocumentTypeName.ASSET_RETIREMENT_GLOBAL, null);
184        }
185    
186        protected CapitalAssetManagementModuleService getCapitalAssetManagementModuleService() {
187            return SpringContext.getBean(CapitalAssetManagementModuleService.class);
188        }
189    
190    
191        /**
192         * Check if all asset payments are federal owned.
193         * 
194         * @param assetRetirementGlobal
195         * @return
196         */
197        protected boolean allPaymentsFederalOwned(AssetRetirementGlobal assetRetirementGlobal) {
198            List<AssetRetirementGlobalDetail> assetRetirementGlobalDetails = assetRetirementGlobal.getAssetRetirementGlobalDetails();
199            for (AssetRetirementGlobalDetail assetRetirementGlobalDetail : assetRetirementGlobalDetails) {
200                for (AssetPayment assetPayment : assetRetirementGlobalDetail.getAsset().getAssetPayments()) {
201                    if (!getAssetPaymentService().isPaymentFederalOwned(assetPayment)) {
202                        return false;
203                    }
204                }
205            }
206            return true;
207        }
208    
209        /**
210         * Validate Asset Object Codes and Fin Object Codes eligible for GL Posting.
211         * 
212         * @param assetRetirementGlobal
213         * @return
214         */
215        protected boolean validateObjectCodesForGLPosting(AssetRetirementGlobal assetRetirementGlobal) {
216            boolean valid = true;
217            List<AssetRetirementGlobalDetail> assetRetirementGlobalDetails = assetRetirementGlobal.getAssetRetirementGlobalDetails();
218            Asset asset = null;
219            List<AssetPayment> assetPayments = null;
220    
221            for (AssetRetirementGlobalDetail assetRetirementGlobalDetail : assetRetirementGlobalDetails) {
222                asset = assetRetirementGlobalDetail.getAsset();
223                assetPayments = asset.getAssetPayments();
224                for (AssetPayment assetPayment : assetPayments) {
225                    if (!getAssetPaymentService().isPaymentFederalOwned(assetPayment)) {
226                        // validate Asset Object Code and Financial Object Codes respectively
227                        if (valid &= validateAssetObjectCode(asset, assetPayment)) {
228                            valid &= validateFinancialObjectCodes(asset, assetPayment);
229                        }
230                    }
231                }
232            }
233    
234            return valid;
235        }
236    
237        /**
238         * Check Financial Object Code for GLPE.
239         * 
240         * @param asset
241         * @param assetPayment
242         * @return
243         */
244        protected boolean validateFinancialObjectCodes(Asset asset, AssetPayment assetPayment) {
245            AssetPaymentService assetPaymentService = getAssetPaymentService();
246            boolean valid = true;
247            ObjectCodeService objectCodeService = (ObjectCodeService) SpringContext.getBean(ObjectCodeService.class);
248            ObjectCode objectCode = objectCodeService.getByPrimaryIdForCurrentYear(assetPayment.getChartOfAccountsCode(), assetPayment.getFinancialObjectCode());
249    
250            AssetObjectCode assetObjectCode = getAssetObjectCodeService().findAssetObjectCode(asset.getOrganizationOwnerChartOfAccountsCode(), objectCode.getFinancialObjectSubTypeCode());
251            if (assetPaymentService.isPaymentEligibleForCapitalizationGLPosting(assetPayment)) {
252                // check for capitalization financial object code existing.
253                assetObjectCode.refreshReferenceObject(CamsPropertyConstants.AssetObjectCode.CAPITALIZATION_FINANCIAL_OBJECT);
254                valid &= validateFinObjectCodeForGLPosting(asset.getOrganizationOwnerChartOfAccountsCode(), assetObjectCode.getCapitalizationFinancialObjectCode(), assetObjectCode.getCapitalizationFinancialObject(), CamsConstants.GLPostingObjectCodeType.CAPITALIZATION);
255            }
256            if (assetPaymentService.isPaymentEligibleForAccumDeprGLPosting(assetPayment)) {
257                // check for accumulate depreciation financial Object Code existing
258                assetObjectCode.refreshReferenceObject(CamsPropertyConstants.AssetObjectCode.ACCUMULATED_DEPRECIATION_FINANCIAL_OBJECT);
259                valid &= validateFinObjectCodeForGLPosting(asset.getOrganizationOwnerChartOfAccountsCode(), assetObjectCode.getAccumulatedDepreciationFinancialObjectCode(), assetObjectCode.getAccumulatedDepreciationFinancialObject(), CamsConstants.GLPostingObjectCodeType.ACCUMMULATE_DEPRECIATION);
260            }
261            if (assetPaymentService.isPaymentEligibleForOffsetGLPosting(assetPayment)) {
262                // check for offset financial object code existing.
263                valid &= validateFinObjectCodeForGLPosting(asset.getOrganizationOwnerChartOfAccountsCode(), SpringContext.getBean(ParameterService.class).getParameterValue(AssetRetirementGlobal.class, CamsConstants.Parameters.DEFAULT_GAIN_LOSS_DISPOSITION_OBJECT_CODE), getAssetRetirementService().getOffsetFinancialObject(asset.getOrganizationOwnerChartOfAccountsCode()), CamsConstants.GLPostingObjectCodeType.OFFSET_AMOUNT);
264            }
265            return valid;
266        }
267    
268        /**
269         * check existence and active status for given financial Object Code BO.
270         * 
271         * @param chartCode
272         * @param finObjectCode
273         * @param finObject
274         * @return
275         */
276        protected boolean validateFinObjectCodeForGLPosting(String chartOfAccountsCode, String finObjectCode, ObjectCode finObject, String glPostingType) {
277            boolean valid = true;
278            // not defined in Asset Object Code table
279            if (StringUtils.isBlank(finObjectCode)) {
280                GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetRetirementGlobal.SECTION_ID_ASSET_DETAIL_INFORMATION, CamsKeyConstants.GLPosting.ERROR_OBJECT_CODE_FROM_ASSET_OBJECT_CODE_NOT_FOUND, new String[] { glPostingType, chartOfAccountsCode });
281                valid = false;
282            }
283            // check Object Code existing
284            else if (ObjectUtils.isNull(finObject)) {
285                GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetRetirementGlobal.SECTION_ID_ASSET_DETAIL_INFORMATION, CamsKeyConstants.GLPosting.ERROR_OBJECT_CODE_FROM_ASSET_OBJECT_CODE_INVALID, new String[] { glPostingType, finObjectCode, chartOfAccountsCode });
286                valid = false;
287            }
288            // check Object Code active
289            else if (!finObject.isActive()) {
290                GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetRetirementGlobal.SECTION_ID_ASSET_DETAIL_INFORMATION, CamsKeyConstants.GLPosting.ERROR_OBJECT_CODE_FROM_ASSET_OBJECT_CODE_INACTIVE, new String[] { glPostingType, finObjectCode, chartOfAccountsCode });
291                valid = false;
292            }
293            return valid;
294        }
295    
296        /**
297         * Asset Object Code must exist as an active status.
298         * 
299         * @param asset
300         * @param assetPayment
301         * @return
302         */
303        protected boolean validateAssetObjectCode(Asset asset, AssetPayment assetPayment) {
304            boolean valid = true;
305            ObjectCodeService objectCodeService = (ObjectCodeService) SpringContext.getBean(ObjectCodeService.class);
306            ObjectCode objectCode = objectCodeService.getByPrimaryIdForCurrentYear(assetPayment.getChartOfAccountsCode(), assetPayment.getFinancialObjectCode());
307    
308            AssetObjectCode assetObjectCode = getAssetObjectCodeService().findAssetObjectCode(asset.getOrganizationOwnerChartOfAccountsCode(), objectCode.getFinancialObjectSubTypeCode());
309            // check Asset Object Code existing.
310            if (ObjectUtils.isNull(assetObjectCode)) {
311                GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetRetirementGlobal.SECTION_ID_ASSET_DETAIL_INFORMATION, CamsKeyConstants.GLPosting.ERROR_ASSET_OBJECT_CODE_NOT_FOUND, new String[] { asset.getOrganizationOwnerChartOfAccountsCode(), objectCode.getFinancialObjectSubTypeCode() });
312                valid = false;
313            }
314            // check Asset Object Code active
315            else if (!assetObjectCode.isActive()) {
316                GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetRetirementGlobal.SECTION_ID_ASSET_DETAIL_INFORMATION, CamsKeyConstants.GLPosting.ERROR_ASSET_OBJECT_CODE_INACTIVE, new String[] { asset.getOrganizationOwnerChartOfAccountsCode(), objectCode.getFinancialObjectSubTypeCode() });
317                valid = false;
318            }
319    
320            return valid;
321        }
322    
323        /**
324         * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomAddCollectionLineBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument,
325         *      java.lang.String, org.kuali.rice.kns.bo.PersistableBusinessObject)
326         */
327        @Override
328        public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject line) {
329            boolean success = true;
330            AssetRetirementGlobalDetail assetRetirementGlobalDetail = (AssetRetirementGlobalDetail) line;
331            AssetRetirementGlobal assetRetirementGlobal = (AssetRetirementGlobal) document.getDocumentBusinessObject();
332    
333            if (!checkEmptyValue(assetRetirementGlobalDetail.getCapitalAssetNumber())) {
334                GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetRetirementGlobalDetail.CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_BLANK_CAPITAL_ASSET_NUMBER);
335                return false;
336            }
337            assetRetirementGlobalDetail.refreshReferenceObject(CamsPropertyConstants.AssetLocationGlobalDetail.ASSET);
338    
339            if (ObjectUtils.isNull(assetRetirementGlobalDetail.getAsset())) {
340                GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetRetirementGlobalDetail.CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_INVALID_CAPITAL_ASSET_NUMBER, assetRetirementGlobalDetail.getCapitalAssetNumber().toString());
341                return false;
342            }
343    
344            if (this.getAssetService().isAssetLoaned(assetRetirementGlobalDetail.getAsset())) {
345                GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetRetirementGlobalDetail.CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_LOANED_ASSET_CANNOT_RETIRED);
346                success = false;
347            }
348            else if (!getAssetService().isDocumentEnrouting(document)) {
349                success &= checkRetirementDetailOneLine(assetRetirementGlobalDetail, assetRetirementGlobal, document);
350                success &= checkRetireMultipleAssets(assetRetirementGlobal.getRetirementReasonCode(), assetRetirementGlobal.getAssetRetirementGlobalDetails(), new Integer(0), document);
351            }
352    
353            // Calculate summary fields in order to show the values even though add new line fails.
354            for (AssetRetirementGlobalDetail detail : assetRetirementGlobal.getAssetRetirementGlobalDetails()) {
355                getAssetService().setAssetSummaryFields(detail.getAsset());
356            }
357            return success & super.processCustomAddCollectionLineBusinessRules(document, collectionName, line);
358        }
359    
360        /**
361         * Check if only single asset is allowed to retire.
362         * 
363         * @param retirementReasonCode
364         * @param assetRetirementDetails
365         * @param maxNumber
366         * @param maintenanceDocument
367         * @return
368         */
369        protected boolean checkRetireMultipleAssets(String retirementReasonCode, List<AssetRetirementGlobalDetail> assetRetirementDetails, Integer maxNumber, MaintenanceDocument maintenanceDocument) {
370            boolean success = true;
371    
372            if (assetRetirementDetails.size() > maxNumber && !getAssetRetirementService().isAllowedRetireMultipleAssets(maintenanceDocument)) {
373                GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetRetirementGlobal.SECTION_ID_ASSET_DETAIL_INFORMATION, CamsKeyConstants.Retirement.ERROR_MULTIPLE_ASSET_RETIRED);
374                success = false;
375            }
376    
377            return success;
378        }
379    
380        /**
381         * This method validates each asset to be retired.
382         * 
383         * @param assetRetirementGlobalDetails
384         * @param maintenanceDocument
385         * @return
386         */
387        protected boolean validateRetirementDetails(AssetRetirementGlobal assetRetirementGlobal, MaintenanceDocument maintenanceDocument) {
388            boolean success = true;
389    
390            List<AssetRetirementGlobalDetail> assetRetirementGlobalDetails = assetRetirementGlobal.getAssetRetirementGlobalDetails();
391    
392            if (assetRetirementGlobalDetails.size() == 0) {
393                success = false;
394                GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetRetirementGlobal.SECTION_ID_ASSET_DETAIL_INFORMATION, CamsKeyConstants.Retirement.ERROR_ASSET_RETIREMENT_GLOBAL_NO_ASSET);
395            }
396            else {
397                // validate each asset retirement detail
398                int index = 0;
399                for (AssetRetirementGlobalDetail detail : assetRetirementGlobalDetails) {
400                    String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetRetirementGlobal.ASSET_RETIREMENT_GLOBAL_DETAILS + "[" + index++ + "]";
401                    GlobalVariables.getMessageMap().addToErrorPath(errorPath);
402    
403                    success &= checkRetirementDetailOneLine(detail, assetRetirementGlobal, maintenanceDocument);
404    
405                    GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
406                }
407            }
408            return success;
409        }
410    
411    
412        /**
413         * This method validates one asset is a valid asset and no duplicate with target asset when merge.
414         * 
415         * @param assetRetirementGlobalDetail
416         * @param assetRetirementGlobal
417         * @param maintenanceDocument
418         * @return
419         */
420        protected boolean checkRetirementDetailOneLine(AssetRetirementGlobalDetail assetRetirementGlobalDetail, AssetRetirementGlobal assetRetirementGlobal, MaintenanceDocument maintenanceDocument) {
421            boolean success = true;
422    
423            assetRetirementGlobalDetail.refreshReferenceObject(CamsPropertyConstants.AssetRetirementGlobalDetail.ASSET);
424    
425            Asset asset = assetRetirementGlobalDetail.getAsset();
426    
427            if (ObjectUtils.isNull(asset)) {
428                success = false;
429                GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetRetirementGlobal.SECTION_ID_ASSET_DETAIL_INFORMATION, CamsKeyConstants.Retirement.ERROR_INVALID_CAPITAL_ASSET_NUMBER, assetRetirementGlobalDetail.getCapitalAssetNumber().toString());
430            }
431            else {
432                success &= validateActiveCapitalAsset(asset);
433                success &= validateNonMoveableAsset(asset, maintenanceDocument);
434    
435                if (getAssetRetirementService().isAssetRetiredByMerged(assetRetirementGlobal)) {
436                    success &= validateDuplicateAssetNumber(assetRetirementGlobal.getMergedTargetCapitalAssetNumber(), assetRetirementGlobalDetail.getCapitalAssetNumber());
437                }
438    
439                // Set asset non persistent fields
440                getAssetService().setAssetSummaryFields(asset);
441            }
442    
443            return success;
444        }
445    
446        /**
447         * Check for Merge Asset, no duplicate capitalAssetNumber between "from" and "to".
448         * 
449         * @param targetAssetNumber
450         * @param sourceAssetNumber
451         * @return
452         */
453        protected boolean validateDuplicateAssetNumber(Long targetAssetNumber, Long sourceAssetNumber) {
454            boolean success = true;
455    
456            if (getAssetService().isCapitalAssetNumberDuplicate(targetAssetNumber, sourceAssetNumber)) {
457                GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetRetirementGlobalDetail.CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_DUPLICATE_CAPITAL_ASSET_NUMBER_WITH_TARGET, sourceAssetNumber.toString());
458                success = false;
459            }
460    
461            return success;
462        }
463    
464        /**
465         * User must be in work group CM_SUPER_USERS to retire a non-moveable asset.
466         * 
467         * @param asset
468         * @param maintenanceDocument
469         * @return
470         */
471        protected boolean validateNonMoveableAsset(Asset asset, MaintenanceDocument maintenanceDocument) {
472            boolean success = true;
473    
474            FinancialSystemMaintenanceDocumentAuthorizerBase documentAuthorizer = (FinancialSystemMaintenanceDocumentAuthorizerBase) SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(maintenanceDocument);
475            boolean isAuthorized = documentAuthorizer.isAuthorized(maintenanceDocument, CamsConstants.CAM_MODULE_CODE, CamsConstants.PermissionNames.RETIRE_NON_MOVABLE_ASSETS, GlobalVariables.getUserSession().getPerson().getPrincipalId());
476    
477            if (!getAssetService().isAssetMovableCheckByAsset(asset) && !isAuthorized) {
478                GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetRetirementGlobalDetail.CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_INVALID_USER_GROUP_FOR_NON_MOVEABLE_ASSET, asset.getCapitalAssetNumber().toString());
479                success = false;
480            }
481    
482            return success;
483        }
484    
485    
486        /**
487         * Validate Asset Retirement Global and Details.
488         * 
489         * @param assetRetirementGlobal
490         * @param maintenanceDocument
491         * @return
492         */
493        protected boolean assetRetirementValidation(AssetRetirementGlobal assetRetirementGlobal, MaintenanceDocument maintenanceDocument) {
494            boolean valid = true;
495    
496            valid &= validateRequiredGlobalFields(assetRetirementGlobal);
497            if (getAssetRetirementService().isAssetRetiredByMerged(assetRetirementGlobal)) {
498                valid &= validateMergeTargetAsset(assetRetirementGlobal);
499            }
500    
501            if (!getAssetService().isDocumentEnrouting(maintenanceDocument)) {
502                valid &= validateRetirementDetails(assetRetirementGlobal, maintenanceDocument);
503                valid &= checkRetireMultipleAssets(assetRetirementGlobal.getRetirementReasonCode(), assetRetirementGlobal.getAssetRetirementGlobalDetails(), new Integer(1), maintenanceDocument);
504            }
505            return valid;
506        }
507    
508    
509        /**
510         * Validate mergedTargetCapitalAsset. Only valid and active capital asset is allowed.
511         * 
512         * @param assetRetirementGlobal
513         * @return
514         */
515        protected boolean validateMergeTargetAsset(AssetRetirementGlobal assetRetirementGlobal) {
516            boolean valid = true;
517            Asset targetAsset = assetRetirementGlobal.getMergedTargetCapitalAsset();
518            Long targetAssetNumber = assetRetirementGlobal.getMergedTargetCapitalAssetNumber();
519    
520            if (!checkEmptyValue(targetAssetNumber)) {
521                putFieldError(CamsPropertyConstants.AssetRetirementGlobal.MERGED_TARGET_CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_BLANK_CAPITAL_ASSET_NUMBER);
522                valid = false;
523            }
524            else if (ObjectUtils.isNull(targetAsset)) {
525                putFieldError(CamsPropertyConstants.AssetRetirementGlobal.MERGED_TARGET_CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_INVALID_MERGED_TARGET_ASSET_NUMBER, targetAssetNumber.toString());
526                valid = false;
527            }
528            else {
529                // Check asset of capital and active
530                if (!getAssetService().isCapitalAsset(targetAsset)) {
531                    putFieldError(CamsPropertyConstants.AssetRetirementGlobal.MERGED_TARGET_CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_NON_CAPITAL_ASSET_RETIREMENT, targetAssetNumber.toString());
532                    valid = false;
533                }
534                if (getAssetService().isAssetRetired(targetAsset)) {
535                    putFieldError(CamsPropertyConstants.AssetRetirementGlobal.MERGED_TARGET_CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_NON_ACTIVE_ASSET_RETIREMENT, targetAssetNumber.toString());
536                    valid = false;
537                }
538            }
539    
540            // Validating the mergeAssetDescription is not blank
541            if (!checkEmptyValue(assetRetirementGlobal.getMergedTargetCapitalAssetDescription())) {
542                String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetRetirementGlobal.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetRetirementGlobal.MERGED_TARGET_CAPITAL_ASSET_DESC).getLabel();
543                putFieldError(CamsPropertyConstants.AssetRetirementGlobal.MERGED_TARGET_CAPITAL_ASSET_DESC, KFSKeyConstants.ERROR_REQUIRED, label);
544                valid = false;
545            }
546    
547            return valid;
548        }
549    
550    
551        /**
552         * Only active capital equipment can be retired using the asset retirement document.
553         * 
554         * @param valid
555         * @param detail
556         * @return
557         */
558        protected boolean validateActiveCapitalAsset(Asset asset) {
559            boolean valid = true;
560    
561            if (!getAssetService().isCapitalAsset(asset)) {
562                GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetRetirementGlobalDetail.CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_NON_CAPITAL_ASSET_RETIREMENT, asset.getCapitalAssetNumber().toString());
563                valid = false;
564            }
565            else if (getAssetService().isAssetRetired(asset)) {
566                GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetRetirementGlobalDetail.CAPITAL_ASSET_NUMBER, CamsKeyConstants.Retirement.ERROR_NON_ACTIVE_ASSET_RETIREMENT, asset.getCapitalAssetNumber().toString());
567                valid = false;
568            }
569            else if (!this.validateAssetOnLoan(asset)) {
570                valid = false;
571            }
572    
573            return valid;
574        }
575    
576    
577        /**
578         * Validate required fields for given retirement reason code
579         * 
580         * @param assetRetirementGlobal
581         * @return
582         */
583        protected boolean validateRequiredGlobalFields(AssetRetirementGlobal assetRetirementGlobal) {
584            boolean valid = true;
585    
586            if (getAssetRetirementService().isAssetRetiredBySold(assetRetirementGlobal)) {
587                if (StringUtils.isBlank(assetRetirementGlobal.getBuyerDescription())) {
588                    putFieldError(CamsPropertyConstants.AssetRetirementGlobalDetail.BUYER_DESCRIPTION, CamsKeyConstants.Retirement.ERROR_RETIREMENT_DETAIL_INFO_NULL, new String[] { CamsConstants.RetirementLabel.BUYER_DESCRIPTION, getAssetRetirementService().getAssetRetirementReasonName(assetRetirementGlobal) });
589                    valid = false;
590                }
591                if (assetRetirementGlobal.getSalePrice() == null) {
592                    putFieldError(CamsPropertyConstants.AssetRetirementGlobalDetail.SALE_PRICE, CamsKeyConstants.Retirement.ERROR_RETIREMENT_DETAIL_INFO_NULL, new String[] { CamsConstants.RetirementLabel.SALE_PRICE, getAssetRetirementService().getAssetRetirementReasonName(assetRetirementGlobal) });
593                    valid = false;
594                }
595                valid &= validateCashReceiptFinancialDocumentNumber(assetRetirementGlobal.getCashReceiptFinancialDocumentNumber());
596            }
597            else if (getAssetRetirementService().isAssetRetiredByAuction(assetRetirementGlobal)) {
598                valid = validateCashReceiptFinancialDocumentNumber(assetRetirementGlobal.getCashReceiptFinancialDocumentNumber());
599            }
600            else if (getAssetRetirementService().isAssetRetiredByExternalTransferOrGift(assetRetirementGlobal) && StringUtils.isBlank(assetRetirementGlobal.getRetirementInstitutionName())) {
601                putFieldError(CamsPropertyConstants.AssetRetirementGlobalDetail.RETIREMENT_INSTITUTION_NAME, CamsKeyConstants.Retirement.ERROR_RETIREMENT_DETAIL_INFO_NULL, new String[] { CamsConstants.RetirementLabel.RETIREMENT_INSTITUTION_NAME, getAssetRetirementService().getAssetRetirementReasonName(assetRetirementGlobal) });
602                valid = false;
603            }
604            else if (getAssetRetirementService().isAssetRetiredByTheft(assetRetirementGlobal) && StringUtils.isBlank(assetRetirementGlobal.getPaidCaseNumber())) {
605                putFieldError(CamsPropertyConstants.AssetRetirementGlobalDetail.PAID_CASE_NUMBER, CamsKeyConstants.Retirement.ERROR_RETIREMENT_DETAIL_INFO_NULL, new String[] { CamsConstants.RetirementLabel.PAID_CASE_NUMBER, getAssetRetirementService().getAssetRetirementReasonName(assetRetirementGlobal) });
606                valid = false;
607            }
608            return valid;
609        }
610    
611        /**
612         * validates Cash Receipt Financial Document Number
613         * 
614         * @param sharedRetirementInfo
615         * @return boolean
616         */
617        protected boolean validateCashReceiptFinancialDocumentNumber(String documentNumber) {
618            boolean valid = true;
619            if (StringUtils.isNotBlank(documentNumber)) {
620                Map retirementInfoMap = new HashMap();
621                retirementInfoMap.put(CamsPropertyConstants.AssetGlobal.DOCUMENT_NUMBER, documentNumber);
622                bo = (FinancialSystemDocumentHeader) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(FinancialSystemDocumentHeader.class, retirementInfoMap);
623                if (ObjectUtils.isNull(bo)) {
624                    putFieldError(CamsPropertyConstants.AssetRetirementGlobalDetail.CASH_RECEIPT_FINANCIAL_DOCUMENT_NUMBER, CamsKeyConstants.Retirement.ERROR_INVALID_RETIREMENT_DETAIL_INFO, new String[] { CamsConstants.RetirementLabel.CASH_RECEIPT_FINANCIAL_DOCUMENT_NUMBER, documentNumber });
625                    valid = false;
626                }
627            }
628            return valid;
629        }
630    
631    
632        /**
633         * Validates whether or not asset is on loan status
634         * 
635         * @param assetRetirementGlobalDetail
636         * @return boolean
637         */
638        protected boolean validateAssetOnLoan(Asset asset) {
639            boolean success = true;
640            if (this.getAssetService().isAssetLoaned(asset)) {
641                GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetRetirementGlobal.SECTION_ID_ASSET_DETAIL_INFORMATION, CamsKeyConstants.Retirement.ERROR_LOANED_ASSET_CANNOT_RETIRED);
642                success = false;
643            }
644            return success;
645        }
646    
647    
648        protected AssetService getAssetService() {
649            return SpringContext.getBean(AssetService.class);
650        }
651    
652        protected AssetRetirementService getAssetRetirementService() {
653            return SpringContext.getBean(AssetRetirementService.class);
654        }
655    
656        protected AssetPaymentService getAssetPaymentService() {
657            return SpringContext.getBean(AssetPaymentService.class);
658        }
659    
660        protected AssetObjectCodeService getAssetObjectCodeService() {
661            return SpringContext.getBean(AssetObjectCodeService.class);
662        }
663    }