View Javadoc
1   /**
2    * Copyright 2004-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.kpme.core.assignment.validation;
17  
18  import java.util.HashMap;
19  import java.util.HashSet;
20  import java.util.List;
21  import java.util.ListIterator;
22  import java.util.Map;
23  import java.util.Set;
24  
25  import org.apache.commons.lang.StringUtils;
26  import org.joda.time.DateTime;
27  import org.joda.time.LocalDate;
28  import org.kuali.kpme.core.api.assignment.Assignment;
29  import org.kuali.kpme.core.api.assignment.AssignmentContract;
30  import org.kuali.kpme.core.api.earncode.EarnCodeContract;
31  import org.kuali.kpme.core.api.job.JobContract;
32  import org.kuali.kpme.core.api.paytype.PayType;
33  import org.kuali.kpme.core.api.task.Task;
34  import org.kuali.kpme.core.api.task.TaskContract;
35  import org.kuali.kpme.core.assignment.AssignmentBo;
36  import org.kuali.kpme.core.assignment.account.AssignmentAccountBo;
37  import org.kuali.kpme.core.service.HrServiceLocator;
38  import org.kuali.kpme.core.util.ValidationUtils;
39  import org.kuali.rice.krad.bo.PersistableBusinessObject;
40  import org.kuali.rice.krad.maintenance.MaintenanceDocument;
41  import org.kuali.rice.krad.rules.MaintenanceDocumentRuleBase;
42  import org.kuali.rice.krad.util.GlobalVariables;
43  
44  @SuppressWarnings("deprecation")
45  public class AssignmentRule extends MaintenanceDocumentRuleBase {
46  
47  	protected boolean validateWorkArea(AssignmentBo assignment) {
48  		boolean valid = true;
49  		if (assignment.getWorkArea() != null) {
50  			if (!ValidationUtils.validateWorkArea(assignment.getWorkArea(),
51  					assignment.getEffectiveLocalDate())) {
52  				this.putFieldError("dataObject.workArea", "error.existence", "workArea '"
53  						+ assignment.getWorkArea() + "'");
54  				valid = false;
55  			} else {
56  				int count = HrServiceLocator.getWorkAreaService().getWorkAreaCount(assignment.getDept(), assignment.getWorkArea());
57  				valid = (count > 0);
58  				if (!valid) {
59  					this.putFieldError("dataObject.workArea", "dept.workarea.invalid.sync");
60  				}
61  			}
62  		}
63  		return valid;
64  	}
65  	
66  	protected boolean validateTask(AssignmentBo assignment) {
67  		boolean valid = false;
68  		//task by default is zero so if non zero validate against existing taskss
69  		if (assignment.getTask() != null && !assignment.getTask().equals(0L)) {
70  //			TaskContract task = HrServiceLocator.getTaskService().getTask(assignment.getTask(), assignment.getEffectiveLocalDate());
71  			String workarea = "";
72  			if(assignment.getWorkArea() != null) {
73  				workarea = assignment.getWorkArea().toString();
74  			}
75  			List<Task> tasks = HrServiceLocator.getTaskService().getTasks(assignment.getTask().toString(), null, workarea, null, assignment.getEffectiveLocalDate());
76  			for(Task task : tasks) {
77  				if(workarea.equals(task.getWorkArea().toString())) {
78  					valid = true;
79  					return valid;
80  				}
81  			}
82  		} else {
83  			valid = true;
84  			return valid;
85  		}
86  		this.putFieldError("dataObject.task", "task.workarea.invalid.sync");
87  		return valid;
88  	}
89  
90  	protected boolean validateDepartment(AssignmentBo assignment) {
91  		boolean valid = true;
92  		if (assignment.getDept() != null) {
93  				int count = HrServiceLocator.getJobService().getJobCount(null, assignment.getJobNumber(), assignment.getDept());
94  				valid = (count > 0);
95  				if (!valid) {
96  					this.putFieldError("dataObject.dept", "dept.jobnumber.invalid.sync");
97  				}
98  			 
99  		}
100 		return valid;
101 	}
102 
103 	protected boolean validateJob(AssignmentBo assignment) {
104 		boolean valid = false;
105 		LOG.debug("Validating job: " + assignment.getPrincipalId() +" Job number: "+assignment.getJobNumber());
106 		JobContract job = HrServiceLocator.getJobService().getJob(
107 				assignment.getPrincipalId(), assignment.getJobNumber(),
108 				assignment.getEffectiveLocalDate(), false);
109 		if (job != null) {
110 			valid = true;
111 
112 			LOG.debug("found job.");
113 		} else {
114 			this.putFieldError("dataObject.jobNumber", "error.existence", "jobNumber '"
115 					+ assignment.getJobNumber() + "'");
116 		}
117 		return valid;
118 	}
119 
120 	protected boolean validatePercentagePerEarnCode(AssignmentBo assignment) {
121 		boolean valid = true;
122 		LOG.debug("Validating PercentagePerEarnCode: ");
123 		List<AssignmentAccountBo> assignmentAccounts = assignment
124 				.getAssignmentAccounts();
125 		Set<String> invalidEarnCodes = null;
126 		if (assignmentAccounts != null && assignment.isActive()) {
127 			Map<String, Integer> earnCodePercent = new HashMap<String, Integer>();
128 			for (AssignmentAccountBo account : assignmentAccounts) {
129 				if (account.getPercent() != null && account.isActive()) {
130 					int percent = 0;
131 					if (earnCodePercent.containsKey(account.getEarnCode())) {
132 						percent = earnCodePercent.get(account.getEarnCode());
133 					}
134 					percent += account.getPercent().toBigInteger().intValue();
135 					earnCodePercent.put(account.getEarnCode(), percent);
136 				}
137 			}
138 			//Iterator<String> itr = earnCodePercent.keySet().iterator();
139             for (Map.Entry<String, Integer> entry : earnCodePercent.entrySet()) {
140 			//while (itr.hasNext()) {
141 				String earnCode = entry.getKey();
142 				if (entry.getValue() != 100) {
143 					if (invalidEarnCodes == null) {
144 						invalidEarnCodes = new HashSet<String>();
145 					}
146 					invalidEarnCodes.add(earnCode);
147 					valid = false;
148 				}
149 			}
150 			if (!valid) {
151 				int index = 0;
152 				for (AssignmentAccountBo account : assignmentAccounts) {
153 					if (invalidEarnCodes.contains(account.getEarnCode())) {
154 						this.putFieldError("dataObject.assignmentAccounts[" + index
155 								+ "].percent", "error.percentage.earncode");
156 					}
157 					index++;
158 				}
159 			}
160 		}
161 		return valid;
162 	}
163 
164 	protected boolean validateEarnCode(AssignmentAccountBo assignmentAccount, LocalDate assignmentEffectiveDate) {
165 		boolean valid = false;
166 		LOG.debug("Validating EarnCode: " + assignmentAccount.getEarnCode());
167 		EarnCodeContract earnCode = HrServiceLocator.getEarnCodeService().getEarnCode(
168 				assignmentAccount.getEarnCode(), assignmentEffectiveDate);
169 		if (earnCode != null) {
170 
171 			valid = true;
172 			LOG.debug("found earnCode.");
173 		} else {
174 			this.putGlobalError("error.existence", "earn code '"
175 					+ assignmentAccount.getEarnCode() + "'");
176 		}
177 		return valid;
178 	}
179 	
180 	protected boolean validateRegPayEarnCode(AssignmentBo assignment) {
181 		boolean valid = false;
182 		LOG.debug("Validating Regular pay EarnCodes: " + assignment.getAssignmentAccounts().size());
183 		for(AssignmentAccountBo assignmentAccount : assignment.getAssignmentAccounts()){
184 			if(assignment.getJobNumber()!=null && assignment.getPrincipalId()!=null){
185 				JobContract job = HrServiceLocator.getJobService().getJob(assignment.getPrincipalId(), assignment.getJobNumber(), assignment.getEffectiveLocalDate(), false);
186 				if(job !=null){
187 					PayType payType = HrServiceLocator.getPayTypeService().getPayType(job.getHrPayType(), assignment.getEffectiveLocalDate());
188 					if(StringUtils.equals(assignmentAccount.getEarnCode(), payType.getRegEarnCode())){
189 						valid = true;
190 						break;
191 					}
192 					
193 				}
194 			}
195 		}
196 		if(!valid) {
197 			this.putFieldError("dataObject.assignmentAccounts", "earncode.regular.pay.required");
198 		}
199 		return valid;
200 	}
201 	
202 	// KPME-2780 This is to validate accounts in the collection 
203 	protected boolean validateAccounts(AssignmentBo assignment) {
204 		boolean valid = false;
205 		LOG.debug("Validating Accounts: " + assignment.getAssignmentAccounts().size());
206 		for(AssignmentAccountBo assignmentAccount : assignment.getAssignmentAccounts()){
207 			valid = ValidationUtils.validateAccount(assignmentAccount.getFinCoaCd(), assignmentAccount.getAccountNbr());
208 			if(!valid) {
209 				this.putFieldError("dataObject.assignmentAccounts", "error.existence", "Account Number '" + assignmentAccount.getAccountNbr() + "'");
210 				break;
211 			}
212 		}
213 		return valid;
214 	}
215 
216 	protected boolean validateAccount(AssignmentAccountBo assignmentAccount) {
217 		boolean valid = false;
218 		LOG.debug("Validating Account: " + assignmentAccount.getAccountNbr());
219 		valid = ValidationUtils.validateAccount(assignmentAccount.getFinCoaCd(),assignmentAccount.getAccountNbr());
220 		if (!valid) {
221 			this.putGlobalError("error.existence", "Account Number '"
222 					+ assignmentAccount.getAccountNbr() + "'");
223 		}
224 		return valid;
225 	}
226 
227 	private boolean validateSubAccount(AssignmentAccountBo assignmentAccount) {
228 		boolean valid = false;
229 		LOG.debug("Validating Sub-Account: " + assignmentAccount.getSubAcctNbr());
230 		valid = ValidationUtils.validateSubAccount(assignmentAccount.getSubAcctNbr(),assignmentAccount.getAccountNbr(), assignmentAccount.getFinCoaCd());
231 		if (!valid) {
232 			this.putGlobalError("error.existence", "Sub-Account Number '"
233 					+ assignmentAccount.getSubAcctNbr() + "'");
234 		}
235 		return valid;
236 	}
237 
238 	protected boolean validateObjectCode(AssignmentAccountBo assignmentAccount, LocalDate assignmentEffectiveDate) {
239 		boolean valid = false;
240 		LOG.debug("Validating ObjectCode: "
241 				+ assignmentAccount.getFinObjectCd());
242 		valid = ValidationUtils.validateObjectCode(assignmentAccount.getFinObjectCd(),
243 												assignmentAccount.getFinCoaCd(),
244 												null);
245 		if (!valid) {
246 			this.putGlobalError("error.existence", "Object Code '"
247 					+ assignmentAccount.getFinObjectCd() + "'");
248 		}
249 		return valid;
250 	}
251 
252 	protected boolean validateSubObjectCode(AssignmentAccountBo assignmentAccount, LocalDate assignmentEffectiveDate) {
253 		boolean valid = false;
254 		LOG.debug("Validating SubObjectCode: "
255 				+ assignmentAccount.getFinSubObjCd());
256 		if (assignmentAccount.getFinSubObjCd() != null) {
257 			valid = ValidationUtils.validateSubObjectCode(String.valueOf(assignmentEffectiveDate.getYear()),
258 																		assignmentAccount.getFinCoaCd(),
259 																		assignmentAccount.getAccountNbr(),
260 																		assignmentAccount.getFinObjectCd(),
261 																		assignmentAccount.getFinSubObjCd());
262 			if (!valid) {
263 				this.putGlobalError("error.existence", "SubObject Code '"
264 						+ assignmentAccount.getFinSubObjCd() + "'");
265 			}
266 		} else {
267 			valid = true;
268 		}
269 		return valid;
270 	}
271 	
272 	protected boolean validateHasAccounts(AssignmentBo assign){
273 		if(assign.getAssignmentAccounts().isEmpty()){
274 			this.putGlobalError("error.assign.must.have.one.or.more.account");
275 			return false;
276 		}
277 		return true;
278 	}
279 	
280 	protected boolean validateOnePrimaryAssignment(AssignmentBo assignment, AssignmentBo oldAssignment) {
281 		if (assignment.isPrimaryAssign()) {
282 			//do not block editing of previous primary assignment
283 			if(oldAssignment!=null && oldAssignment.isPrimaryAssign()){
284 				return true;
285 			}
286 			JobContract job = HrServiceLocator.getJobService().getJob(assignment.getPrincipalId(), assignment.getJobNumber(), assignment.getEffectiveLocalDate(), false);
287 			if(job != null && job.isEligibleForLeave()) {
288 				List<Assignment> assignList = HrServiceLocator.getAssignmentService().getActiveAssignmentsForJob(assignment.getPrincipalId(), assignment.getJobNumber(), assignment.getEffectiveLocalDate());
289 				for(Assignment anAssignment : assignList) {
290 					if(anAssignment != null && anAssignment.isPrimaryAssign()) {
291 						this.putFieldError("dataObject.primaryAssign", "error.primary.assignment.exists.for.leaveJob", assignment.getJobNumber().toString());
292 						return false;
293 					}
294 				}
295 			}
296 		}
297 		return true;
298 	}
299 	
300 	protected boolean validateActiveFlag(AssignmentBo assign){
301         if (assign.isActive()) {
302             return true;
303         }
304         Assignment assignment = AssignmentBo.to(assign);
305 		DateTime latestTimeEndTimestamp =  HrServiceLocator.getCalendarBlockService().getLatestEndTimestampForAssignment(assignment,"Time");
306 		
307 		if(latestTimeEndTimestamp != null) {
308 			LocalDate assignmentEffectiveDate = assign.getEffectiveLocalDate();
309 			LocalDate latestTimeEndTimestampLocalDate = latestTimeEndTimestamp.toLocalDate();
310 			 
311 			if ( !assign.isActive() && !assignmentEffectiveDate.isAfter(latestTimeEndTimestampLocalDate) ){
312 				this.putFieldError("active", "error.assignment.timeblock.existence", latestTimeEndTimestampLocalDate.toString());
313 				return false;
314 			}
315 		}
316 		
317 		DateTime latestLeaveEndTimestamp =  HrServiceLocator.getCalendarBlockService().getLatestEndTimestampForAssignment(assignment,"Leave");
318 		if(latestLeaveEndTimestamp!=null) {
319 			LocalDate assignmentEffectiveDate = assign.getEffectiveLocalDate();
320 			LocalDate latestLeaveEndTimestampLocalDate = latestLeaveEndTimestamp.toLocalDate();
321 			 
322 			if ( !assign.isActive() && !assignmentEffectiveDate.isAfter(latestLeaveEndTimestampLocalDate) ){
323 				this.putFieldError("active", "error.assignment.leaveblock.existence", latestLeaveEndTimestampLocalDate.toString());
324 				return false;
325 			}
326 		}
327 		
328 		return true;
329 	}
330 
331 	/**
332 	 * It looks like the method that calls this class doesn't actually care
333 	 * about the return type.
334 	 */
335 	@Override
336 	protected boolean processCustomRouteDocumentBusinessRules(
337 			MaintenanceDocument document) {
338 		boolean valid = false;
339 		LOG.debug("entering custom validation for Assignment");
340 		PersistableBusinessObject pbo = (PersistableBusinessObject) this.getNewDataObject();
341 		if (pbo instanceof AssignmentBo) {
342 			AssignmentBo assignment = (AssignmentBo) pbo;
343 			if (assignment != null) {
344 				valid = true;
345 				valid &= this.validateWorkArea(assignment);
346 				valid &= this.validateTask(assignment);
347 				valid &= this.validateJob(assignment);
348 				valid &= this.validateDepartment(assignment);
349 				valid &= this.validatePercentagePerEarnCode(assignment);
350 				valid &= this.validateHasAccounts(assignment);
351 				valid &= this.validateActiveFlag(assignment);
352 				if(!assignment.getAssignmentAccounts().isEmpty()) {
353 					valid &= this.validateRegPayEarnCode(assignment);
354 //					valid &= this.validateAccounts(assignment); // KPME-2780
355 					// Validate Assignment Accounts
356 					for (ListIterator<AssignmentAccountBo> iterator =  assignment.getAssignmentAccounts().listIterator() ; iterator.hasNext();){
357 						int index = iterator.nextIndex();
358 						AssignmentAccountBo assignmentAccountBo = iterator.next();
359 						valid &= this.validateAssignmentAccount(assignmentAccountBo, assignment, index);
360 					}
361 				}
362 				// only allow one primary assignment for the leave eligible job
363 				if(assignment.isPrimaryAssign()) {
364 					AssignmentBo oldAssignment = (AssignmentBo) this.getOldDataObject();
365 					valid &= this.validateOnePrimaryAssignment(assignment, oldAssignment);
366 				}
367 			}
368 		}
369 
370 		return valid;
371 	}
372 
373 	@Override
374 	public boolean processCustomAddCollectionLineBusinessRules(
375 			MaintenanceDocument document, String collectionName,
376 			PersistableBusinessObject line) {
377 		boolean valid = false;
378 		LOG.debug("entering custom add assignment account business rules");
379 		PersistableBusinessObject assignmentPbo = (PersistableBusinessObject) this.getNewDataObject();
380 		PersistableBusinessObject pbo = line;
381 		if (pbo instanceof AssignmentAccountBo) {
382 			AssignmentAccountBo assignmentAccount = (AssignmentAccountBo) pbo;
383 			if (assignmentAccount != null) {
384 				if(assignmentPbo instanceof AssignmentBo) {
385 					AssignmentBo assignment = (AssignmentBo) assignmentPbo;
386 					valid = true;
387 					valid &= this.validateEarnCode(assignmentAccount, assignment.getEffectiveLocalDate());
388 					valid &= this.validateAccount(assignmentAccount);
389 					valid &= this.validateObjectCode(assignmentAccount, assignment.getEffectiveLocalDate());
390 					valid &= this.validateSubObjectCode(assignmentAccount, assignment.getEffectiveLocalDate());
391 					if(assignmentAccount.getSubAcctNbr() != null && !assignmentAccount.getSubAcctNbr().isEmpty()) {
392 						valid &= this.validateSubAccount(assignmentAccount);
393 					}
394 				}
395 			}
396 		}
397 		return valid;
398 	}
399 	
400 	
401 	private boolean validateAssignmentAccount(AssignmentAccountBo assignmentAccount, AssignmentBo assignmentObj, int index) {
402 		boolean isValid = true;
403 		String prefix = "assignmentAccounts";
404 		String propertyNamePrefix = prefix + "[" + index + "].";
405 		if(StringUtils.isNotEmpty(assignmentAccount.getEarnCode())) {
406 			boolean valid = ValidationUtils.validateEarnCode(assignmentAccount.getEarnCode(), assignmentObj.getEffectiveLocalDate());
407 			if(!valid) {
408 				this.putFieldError(propertyNamePrefix + "earnCode","error.existence", "earn code '"+ assignmentAccount.getEarnCode() + "'");
409 				isValid = false;
410 			}
411 		}
412 		if(StringUtils.isNotEmpty(assignmentAccount.getAccountNbr())) {
413 			boolean valid = ValidationUtils.validateAccount(assignmentAccount.getFinCoaCd(),assignmentAccount.getAccountNbr());
414 			if(!valid) {
415 				this.putFieldError(propertyNamePrefix + "accountNbr","error.existence", "Account Number '"+ assignmentAccount.getAccountNbr() + "'");
416 				isValid = false;
417 			}
418 		}
419 		if(StringUtils.isNotEmpty(assignmentAccount.getFinObjectCd())) {
420 			boolean valid = ValidationUtils.validateObjectCode(assignmentAccount.getFinObjectCd(),assignmentAccount.getFinCoaCd(),null);
421 			if (!valid) {
422 				this.putFieldError(propertyNamePrefix + "finObjectCd","error.existence", "Object Code '"+ assignmentAccount.getFinObjectCd() + "'");
423 				isValid = false;
424 			}			
425 		}
426 		if (StringUtils.isNotEmpty(assignmentAccount.getFinSubObjCd())) {
427 			boolean valid = ValidationUtils.validateSubObjectCode(String.valueOf(assignmentObj.getEffectiveLocalDate().getYear()),assignmentAccount.getFinCoaCd(),
428 					assignmentAccount.getAccountNbr(), assignmentAccount.getFinObjectCd(), assignmentAccount.getFinSubObjCd());
429 			if (!valid) {
430 				this.putFieldError(propertyNamePrefix + "finSubObjCd","error.existence", "SubObject Code '"+ assignmentAccount.getFinSubObjCd() + "'");
431 				isValid = false;
432 			}
433 		} 
434 		if(assignmentAccount.getSubAcctNbr() != null && StringUtils.isNotEmpty(assignmentAccount.getSubAcctNbr())) {
435 			boolean valid = ValidationUtils.validateSubAccount(assignmentAccount.getSubAcctNbr(),assignmentAccount.getAccountNbr(), assignmentAccount.getFinCoaCd());
436 			if (!valid) {
437 				this.putFieldError(propertyNamePrefix + "subAcctNbr", "error.existence", "Sub-Account Number '"+ assignmentAccount.getSubAcctNbr() + "'");
438 				isValid = false;
439 			}
440 		}
441 		
442 		return isValid;
443 	}
444     
445 
446 	
447 }