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 }