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 }