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.ec.document.validation.impl;
017    
018    import java.util.List;
019    
020    import org.kuali.rice.kns.util.ObjectUtils;
021    import org.apache.commons.lang.StringUtils;
022    import org.kuali.kfs.coa.businessobject.Account;
023    import org.kuali.kfs.integration.ld.LaborModuleService;
024    import org.kuali.kfs.module.ec.EffortConstants;
025    import org.kuali.kfs.module.ec.EffortKeyConstants;
026    import org.kuali.kfs.module.ec.EffortPropertyConstants;
027    import org.kuali.kfs.module.ec.batch.service.EffortCertificationExtractService;
028    import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail;
029    import org.kuali.kfs.module.ec.businessobject.EffortCertificationDocumentBuild;
030    import org.kuali.kfs.module.ec.businessobject.EffortCertificationReportDefinition;
031    import org.kuali.kfs.module.ec.document.EffortCertificationDocument;
032    import org.kuali.kfs.module.ec.document.validation.AddDetailLineRule;
033    import org.kuali.kfs.module.ec.document.validation.CheckDetailLineAmountRule;
034    import org.kuali.kfs.module.ec.document.validation.LoadDetailLineRule;
035    import org.kuali.kfs.module.ec.document.validation.UpdateDetailLineRule;
036    import org.kuali.kfs.module.ec.service.EffortCertificationDocumentService;
037    import org.kuali.kfs.module.ec.service.EffortCertificationReportDefinitionService;
038    import org.kuali.kfs.sys.KFSConstants;
039    import org.kuali.kfs.sys.KFSKeyConstants;
040    import org.kuali.kfs.sys.context.SpringContext;
041    import org.kuali.kfs.sys.document.service.AccountingLineRuleHelperService;
042    import org.kuali.rice.kns.bo.Note;
043    import org.kuali.rice.kns.datadictionary.DataDictionary;
044    import org.kuali.rice.kns.document.Document;
045    import org.kuali.rice.kns.rule.event.ApproveDocumentEvent;
046    import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase;
047    import org.kuali.rice.kns.service.BusinessObjectService;
048    import org.kuali.rice.kns.service.DataDictionaryService;
049    import org.kuali.rice.kns.util.GlobalVariables;
050    import org.kuali.rice.kns.util.KualiDecimal;
051    
052    /**
053     * To define the rules that may be applied to the effort certification document, a transactional document
054     */
055    public class EffortCertificationDocumentRules extends TransactionalDocumentRuleBase implements AddDetailLineRule<EffortCertificationDocument, EffortCertificationDetail>, UpdateDetailLineRule<EffortCertificationDocument, EffortCertificationDetail>, CheckDetailLineAmountRule<EffortCertificationDocument, EffortCertificationDetail>, LoadDetailLineRule<EffortCertificationDocument> {
056        protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(EffortCertificationDocumentRules.class);
057    
058        protected EffortCertificationDocumentService effortCertificationDocumentService = SpringContext.getBean(EffortCertificationDocumentService.class);
059        protected EffortCertificationReportDefinitionService effortCertificationReportDefinitionService = SpringContext.getBean(EffortCertificationReportDefinitionService.class);
060        protected EffortCertificationExtractService effortCertificationExtractService = SpringContext.getBean(EffortCertificationExtractService.class);
061    
062        protected BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
063        protected LaborModuleService laborModuleService = SpringContext.getBean(LaborModuleService.class);
064        protected AccountingLineRuleHelperService accountingLineRuleHelperService = SpringContext.getBean(AccountingLineRuleHelperService.class);
065    
066        /**
067         * @see org.kuali.kfs.module.ec.document.validation.AddDetailLineRule#processAddDetailLineRules(org.kuali.kfs.module.ec.document.EffortCertificationDocument,
068         *      org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail)
069         */
070        public boolean processAddDetailLineRules(EffortCertificationDocument document, EffortCertificationDetail detailLine) {
071            LOG.info("processAddDetailLineRules() start");
072    
073            document.refreshNonUpdateableReferences();
074            detailLine.refreshNonUpdateableReferences();
075    
076            if (!this.checkDetailLineAttributes(detailLine)) {
077                return false;
078            }
079    
080            if(!this.processCheckDetailLineAmountRules(document, detailLine)) {
081                return false;
082            }
083    
084            List<String> comparableFields = EffortConstants.DETAIL_LINES_CONSOLIDATION_FILEDS;
085            if (detailLine.isNewLineIndicator() && EffortCertificationDocumentRuleUtil.hasSameExistingLine(document, detailLine, comparableFields)) {
086                reportError(EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS, EffortKeyConstants.ERROR_LINE_EXISTS);
087                return false;
088            }
089    
090            if (EffortCertificationDocumentRuleUtil.hasClosedAccount(detailLine)) {
091                reportError(EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS, EffortKeyConstants.ERROR_ACCOUNT_CLOSED);
092                return false;
093            }
094    
095            if (detailLine.isNewLineIndicator() && !EffortCertificationDocumentRuleUtil.canExpiredAccountBeUsed(detailLine)) {
096                Account account = detailLine.getAccount();
097                Account continuation = account.getContinuationAccount();
098    
099                reportError(EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS, KFSKeyConstants.ERROR_DOCUMENT_ACCOUNT_EXPIRED, account.getAccountNumber(), continuation.getChartOfAccountsCode(), continuation.getAccountNumber());
100                return false;
101            }
102    
103            return true;
104        }
105    
106        /**
107         * @see org.kuali.kfs.module.ec.document.validation.UpdateDetailLineRule#processUpdateDetailLineRules(org.kuali.kfs.module.ec.document.EffortCertificationDocument,
108         *      org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail)
109         */
110        public boolean processUpdateDetailLineRules(EffortCertificationDocument document, EffortCertificationDetail detailLine) {
111            LOG.info("processUpdateDetailLineRules() start");
112    
113            if (!this.processAddDetailLineRules(document, detailLine)) {
114                return false;
115            }
116    
117            if(!this.processCheckDetailLineAmountRules(document, detailLine)) {
118                return false;
119            }
120    
121            KualiDecimal originalTotalAmount = document.getTotalOriginalPayrollAmount();
122            if (EffortCertificationDocumentRuleUtil.isPayrollAmountOverChanged(detailLine, originalTotalAmount, EffortConstants.PERCENT_LIMIT_OF_LINE_SALARY_CHANGE)) {
123                reportError(EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS, EffortKeyConstants.ERROR_PAYROLL_AMOUNT_OVERCHANGED, (Double.valueOf(EffortConstants.PERCENT_LIMIT_OF_LINE_SALARY_CHANGE)).toString());
124                return false;
125            }
126    
127            return true;
128        }
129    
130        /**
131         * @see org.kuali.rice.kns.rules.DocumentRuleBase#processCustomApproveDocumentBusinessRules(org.kuali.rice.kns.rule.event.ApproveDocumentEvent)
132         */
133        @Override
134        public boolean processCustomApproveDocumentBusinessRules(ApproveDocumentEvent approveEvent) {
135            LOG.info("processAddLineBusinessRules() start");
136    
137            EffortCertificationDocument effortCertificationDocument = (EffortCertificationDocument) (approveEvent.getDocument());
138            if (this.bypassBusinessRuleIfInitiation(effortCertificationDocument)) {
139                return true;
140            }
141    
142            boolean valid = true;
143            for (EffortCertificationDetail detailLine : effortCertificationDocument.getEffortCertificationDetailLines()) {
144                valid &= this.processUpdateDetailLineRules(effortCertificationDocument, detailLine);
145            }
146    
147            valid &= this.processCustomRouteDocumentBusinessRules(effortCertificationDocument);
148    
149            return valid;
150        }
151    
152        /**
153         * @see org.kuali.rice.kns.rules.DocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.Document)
154         */
155        @Override
156        public boolean processCustomRouteDocumentBusinessRules(Document document) {
157            LOG.info("processAddLineBusinessRules() start");
158    
159            EffortCertificationDocument effortCertificationDocument = (EffortCertificationDocument) document;
160    
161            // the docuemnt must have at least one detail line
162            if (!EffortCertificationDocumentRuleUtil.hasDetailLine(effortCertificationDocument)) {
163                reportError(EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS, EffortKeyConstants.ERROR_NOT_HAVE_DETAIL_LINE);
164                return false;
165            }
166    
167            if (this.bypassBusinessRuleIfInitiation(effortCertificationDocument)) {
168                return true;
169            }
170    
171            if (EffortCertificationDocumentRuleUtil.isEffortPercentChangedFromPersisted(effortCertificationDocument)) {
172                List<Note> notes = effortCertificationDocument.getDocumentHeader().getBoNotes();
173                
174                boolean noteHasBeenAdded = false;
175                for(Note note : notes) {
176                    if(note.isNewCollectionRecord()) {
177                        noteHasBeenAdded = true;
178                        break;
179                    }
180                }
181                
182                if (!noteHasBeenAdded) {
183                    reportError(EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS, EffortKeyConstants.ERROR_NOTE_REQUIRED_WHEN_EFFORT_CHANGED);
184                    return false;
185                }
186            }
187    
188            if (EffortCertificationDocumentRuleUtil.isTotalPayrollAmountOverChanged(effortCertificationDocument, EffortConstants.AMOUNT_LIMIT_OF_TOTAL_SALARY_CHANGE)) {
189                reportError(EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS, EffortKeyConstants.ERROR_TOTAL_PAYROLL_AMOUNT_OVERCHANGED, (Double.valueOf(EffortConstants.AMOUNT_LIMIT_OF_TOTAL_SALARY_CHANGE)).toString());
190                return false;
191            }
192    
193            if (!EffortCertificationDocumentRuleUtil.isTotalEffortPercentageAs100(effortCertificationDocument)) {
194                reportError(EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS, EffortKeyConstants.ERROR_TOTAL_EFFORT_PERCENTAGE_NOT_100);
195                return false;
196            }
197    
198            String emplid = effortCertificationDocument.getEmplid();
199            effortCertificationDocument.refreshReferenceObject(EffortPropertyConstants.EFFORT_CERTIFICATION_REPORT_DEFINITION);
200            EffortCertificationReportDefinition reportDefinition = effortCertificationDocument.getEffortCertificationReportDefinition();
201            if (effortCertificationReportDefinitionService.hasApprovedEffortCertification(emplid, reportDefinition)) {
202                List<Note> notes = effortCertificationDocument.getDocumentHeader().getBoNotes();
203                if (notes == null || notes.isEmpty()) {
204                    reportError(EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS, EffortKeyConstants.ERROR_NOTE_REQUIRED_WHEN_APPROVED_EFFORT_CERTIFICATION_EXIST, emplid, reportDefinition.getUniversityFiscalYear().toString(), reportDefinition.getEffortCertificationReportNumber());
205                    return false;
206                }
207            }
208    
209            return true;
210        }
211    
212        /**
213         * @see org.kuali.kfs.module.ec.document.validation.CheckDetailLineAmountRule#processCheckDetailLineAmountRules(org.kuali.kfs.module.ec.document.EffortCertificationDocument,
214         *      org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail)
215         */
216        public boolean processCheckDetailLineAmountRules(EffortCertificationDocument effortCertificationDocument, EffortCertificationDetail effortCertificationDetail) {
217            if (!EffortCertificationDocumentRuleUtil.hasValidEffortPercent(effortCertificationDetail)) {
218                reportError(EffortPropertyConstants.EFFORT_CERTIFICATION_UPDATED_OVERALL_PERCENT, EffortKeyConstants.ERROR_INVALID_EFFORT_PERCENT);
219                return false;
220            }
221    
222            if (!EffortCertificationDocumentRuleUtil.hasNonnegativePayrollAmount(effortCertificationDetail)) {
223                reportError(EffortPropertyConstants.EFFORT_CERTIFICATION_PAYROLL_AMOUNT, EffortKeyConstants.ERROR_NEGATIVE_PAYROLL_AMOUNT);
224                return false;
225            }
226    
227            return true;
228        }
229    
230        /**
231         * @see org.kuali.kfs.module.ec.document.validation.LoadDetailLineRule#processLoadDetailLineRules(org.kuali.kfs.module.ec.document.EffortCertificationDocument)
232         */
233        public boolean processLoadDetailLineRules(EffortCertificationDocument effortCertificationDocument) {
234            LOG.info("processLoadDetailLineRules() start");
235    
236            boolean isValid = true;
237            String emplid = effortCertificationDocument.getEmplid();
238    
239            effortCertificationDocument.refreshReferenceObject(EffortPropertyConstants.EFFORT_CERTIFICATION_REPORT_DEFINITION);
240            EffortCertificationReportDefinition reportDefinition = effortCertificationDocument.getEffortCertificationReportDefinition();
241    
242            if (ObjectUtils.isNull(reportDefinition)) {
243                reportError(EffortConstants.EFFORT_DETAIL_IMPORT_ERRORS, EffortKeyConstants.ERROR_REPORT_DEFINITION_NOT_EXIST);
244                return false;
245            }
246    
247            if (!reportDefinition.isActive()) {
248                reportError(EffortConstants.EFFORT_DETAIL_IMPORT_ERRORS, EffortKeyConstants.ERROR_REPORT_DEFINITION_INACTIVE);
249                return false;
250            }
251    
252            isValid = StringUtils.equals(KFSConstants.PeriodStatusCodes.OPEN, reportDefinition.getEffortCertificationReportPeriodStatusCode());
253            if (!isValid) {
254                reportError(EffortConstants.EFFORT_DETAIL_IMPORT_ERRORS, EffortKeyConstants.ERROR_REPORT_DEFINITION_PERIOD_NOT_OPENED);
255                return false;
256            }
257    
258            isValid = !effortCertificationReportDefinitionService.hasPendingEffortCertification(emplid, reportDefinition);
259            if (!isValid) {
260                reportError(EffortConstants.EFFORT_DETAIL_IMPORT_ERRORS, EffortKeyConstants.ERROR_PENDING_EFFORT_CERTIFICATION_EXIST);
261                return false;
262            }
263    
264            isValid = effortCertificationReportDefinitionService.hasBeenUsedForEffortCertificationGeneration(reportDefinition);
265            if (!isValid) {
266                reportError(EffortConstants.EFFORT_DETAIL_IMPORT_ERRORS, EffortKeyConstants.ERROR_CREATE_PROCESS_HAS_NOT_BEEN_COMPLETED);
267                return false;
268            }
269    
270            isValid = effortCertificationExtractService.isEmployeeEligibleForEffortCertification(emplid, reportDefinition);
271            if (!isValid) {
272                reportError(EffortConstants.EFFORT_DETAIL_IMPORT_ERRORS, EffortKeyConstants.ERROR_EMPLOYEE_NOT_ELIGIBLE, emplid);
273                return false;
274            }
275    
276            int countOfPendingSalaryExpenseTransfer = laborModuleService.countPendingSalaryExpenseTransfer(emplid);
277            if (countOfPendingSalaryExpenseTransfer > 0) {
278                reportError(EffortConstants.EFFORT_DETAIL_IMPORT_ERRORS, EffortKeyConstants.ERROR_PENDING_SALARAY_EXPENSE_TRANSFER_EXIST, emplid, Integer.toString(countOfPendingSalaryExpenseTransfer));
279                return false;
280            }
281    
282            return this.populateEffortCertificationDocument(effortCertificationDocument);
283        }
284    
285        /**
286         * check if the attributes in the detail line are valid for the defintions in data dictionary and have valid references
287         * 
288         * @param detailLine the given effort certification detail line
289         * @return true if the attributes in the detail line are valid for the defintions in data dictionary and have valid references;
290         *         otherwise, false
291         */
292        protected boolean checkDetailLineAttributes(EffortCertificationDetail detailLine) {
293            LOG.debug("checkDetailLine() start");
294    
295            DataDictionary dataDictionary = SpringContext.getBean(DataDictionaryService.class).getDataDictionary();
296    
297            // check if the fields in the detail line are in the correct formats defined in the data dictionary
298            boolean hasValidFormat = EffortCertificationDocumentRuleUtil.hasValidFormat(detailLine);
299    
300            // if the formats of the fields are correct, check if there exist the references of a set of specified fields
301            boolean hasValidReference = true;
302            if (hasValidFormat) {
303                hasValidReference &= accountingLineRuleHelperService.isValidAccount(detailLine.getAccount(), dataDictionary, EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS);
304                hasValidReference &= accountingLineRuleHelperService.isValidChart(detailLine.getChartOfAccounts(), dataDictionary, EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS);
305    
306                if (!KFSConstants.getDashSubAccountNumber().equals(detailLine.getSubAccountNumber()) && StringUtils.isNotBlank(detailLine.getSubAccountNumber())) {
307                    hasValidReference &= accountingLineRuleHelperService.isValidSubAccount(detailLine.getSubAccount(), dataDictionary, EffortConstants.EFFORT_CERTIFICATION_TAB_ERRORS);
308                }
309            }
310    
311            return hasValidFormat && hasValidReference;
312        }
313    
314        /**
315         * determine if the business rule needs to be bypassed. If the given document is in the state of initiation, bypass
316         * 
317         * @param effortCertificationDocument the given document
318         * @return true if the given document is in the state of initiation; otherwise, false
319         */
320        protected boolean bypassBusinessRuleIfInitiation(EffortCertificationDocument effortCertificationDocument) {
321            return effortCertificationDocument.getDocumentHeader().getWorkflowDocument().stateIsInitiated();
322        }
323    
324        /**
325         * determine if the given document can be populated. If so, populate it and return true
326         * 
327         * @param effortCertificationDocument the given document
328         * @return true if the given document can be populated; otherwise, return false and the document is not changed
329         */
330        protected boolean populateEffortCertificationDocument(EffortCertificationDocument effortCertificationDocument) {
331            String emplid = effortCertificationDocument.getEmplid();
332            EffortCertificationReportDefinition reportDefinition = effortCertificationDocument.getEffortCertificationReportDefinition();
333            EffortCertificationDocumentBuild documentBuild = effortCertificationExtractService.extract(emplid, reportDefinition);
334    
335            if (documentBuild == null) {
336                reportError(EffortConstants.EFFORT_DETAIL_IMPORT_ERRORS, EffortKeyConstants.ERROR_EMPLOYEE_NO_ELIGIBLE_LABOR_BALANCE, emplid);
337                return false;
338            }
339    
340            effortCertificationDocumentService.removeEffortCertificationDetailLines(effortCertificationDocument);
341            boolean success = effortCertificationDocumentService.populateEffortCertificationDocument(effortCertificationDocument, documentBuild);
342    
343            if (effortCertificationReportDefinitionService.hasBeenUsedForEffortCertificationGeneration(emplid, reportDefinition)) {
344                effortCertificationDocument.setEffortCertificationDocumentCode(true);
345            }
346    
347            return success;
348        }
349    
350        // record the error into the global error map
351        protected void reportError(String propertyName, String errorKey, String... errorParameters) {
352            GlobalVariables.getMessageMap().putError(propertyName, errorKey, errorParameters);
353        }
354    }