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.pm.position.validation;
17  
18  import java.math.BigDecimal;
19  import java.util.List;
20  import java.util.ListIterator;
21  import java.util.Set;
22  
23  import org.apache.commons.collections.CollectionUtils;
24  import org.apache.commons.lang.StringUtils;
25  import org.kuali.kpme.core.api.department.Department;
26  import org.kuali.kpme.core.service.HrServiceLocator;
27  import org.kuali.kpme.core.util.HrContext;
28  import org.kuali.kpme.pm.PMConstants;
29  import org.kuali.kpme.pm.api.classification.ClassificationContract;
30  import org.kuali.kpme.pm.classification.ClassificationBo;
31  import org.kuali.kpme.pm.position.PositionBo;
32  import org.kuali.kpme.pm.position.PositionDutyBo;
33  import org.kuali.kpme.pm.position.authorization.PositionDocumentAuthorizer;
34  import org.kuali.kpme.pm.position.funding.PositionFundingBo;
35  import org.kuali.kpme.pm.positiondepartment.PositionDepartmentBo;
36  import org.kuali.kpme.pm.positionresponsibility.PositionResponsibilityBo;
37  import org.kuali.kpme.core.departmentaffiliation.DepartmentAffiliationBo;
38  import org.kuali.kpme.core.util.ValidationUtils;
39  import org.kuali.kpme.pm.service.base.PmServiceLocator;
40  import org.kuali.kpme.pm.util.PmValidationUtils;
41  import org.kuali.rice.krad.document.DocumentAuthorizer;
42  import org.kuali.rice.krad.maintenance.MaintenanceDocument;
43  import org.kuali.rice.krad.rules.MaintenanceDocumentRuleBase;
44  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
45  import org.kuali.rice.krad.util.GlobalVariables;
46  
47  public class PositionValidation extends MaintenanceDocumentRuleBase {
48  	@Override
49  	protected boolean processCustomRouteDocumentBusinessRules(
50  			MaintenanceDocument document) {
51  		boolean valid = false;
52  		LOG.debug("entering custom validation for Position");
53  		PositionBo aPosition = (PositionBo) this.getNewDataObject();
54          PositionBo oldPosition = (PositionBo) this.getOldDataObject();
55  
56  		if (aPosition != null) {
57  			valid = true;
58              valid &= this.canEdit(document);
59  			valid &= this.validateOverviewPage(aPosition);
60  			valid &= this.validateClassificationPage(aPosition);
61  			valid &= this.validateDutyListPercentage(aPosition);
62  			valid &= this.validateResponsibilityListPercentage(aPosition);
63  			valid &= this.validatePrimaryDepartment(aPosition);
64              valid &= this.validatPrimaryDepartmentExistence(aPosition);
65              valid &= this.validateProcess(aPosition, oldPosition);
66              valid &= this.validateFundingLines(aPosition);
67              valid &= this.validateAdditionalDepartments(aPosition);
68  
69  		}
70  		return valid;
71  	}
72  
73      private boolean validateAdditionalDepartments(PositionBo aPosition) {
74  		List<PositionDepartmentBo> posDeptList = aPosition.getDepartmentList();
75  		boolean flag = false;
76  		int i=0;
77  		for (PositionDepartmentBo pd : posDeptList) {
78  			if(pd.getGroupKeyCode().equals(aPosition.getGroupKeyCode())){
79  				if(aPosition.getEffectiveLocalDate()!=null){
80  					Department department = HrServiceLocator.getDepartmentService().getDepartment(pd.getDepartment(), pd.getGroupKeyCode(), aPosition.getEffectiveLocalDate());
81  					if(department == null){
82  						this.putFieldError("document.newMaintainableObject.dataObject.departmentList[" + i + "].groupKeyCode", "error.existence", "Position Department '" + pd.getDepartment() + "'");
83  						return false;
84  					} 
85  				}
86  			}else{
87  				flag = false;
88  				this.putFieldError("document.newMaintainableObject.dataObject.departmentList[" + i + "].groupKeyCode","error.existence", "Position Department '" + pd.getDepartment() + "'");
89  				return flag;
90  			}
91  			i++;
92  		}
93  		return true;
94  	}
95  
96  	protected boolean canEdit(MaintenanceDocument document) {
97          DocumentAuthorizer auth = KRADServiceLocatorWeb.getDocumentDictionaryService().getDocumentAuthorizer(document);
98          boolean valid = true;
99          if (auth != null) {
100             valid = auth.canEdit(document, GlobalVariables.getUserSession().getActualPerson());
101         }
102         if (!valid) {
103             this.putFieldError("dataObject.primaryDepartment", "error.primaryDepartment.invalid");
104         }
105         return valid;
106 
107     }
108 
109 
110     protected boolean validateDutyListPercentage(PositionBo aPosition) {
111 		if (CollectionUtils.isNotEmpty(aPosition.getDutyList())) {
112 			BigDecimal sum = BigDecimal.ZERO;
113 			for (PositionDutyBo aDuty : aPosition.getDutyList()) {
114 				if (aDuty != null && aDuty.getPercentage() != null) {
115 					sum = sum.add(aDuty.getPercentage());
116 				}
117 			}
118 			if (sum.compareTo(new BigDecimal(100)) > 0) {
119 				String[] parameters = new String[1];
120 				parameters[0] = sum.toString();
121 				this.putFieldError("dataObject.dutyList",
122 						"duty.percentage.exceedsMaximum", parameters);
123 				return false;
124 			}
125 		}
126 		return true;
127 	}
128 
129 	protected boolean validateResponsibilityListPercentage(PositionBo aPosition) {
130 		if (CollectionUtils.isNotEmpty(aPosition.getPositionResponsibilityList())) {
131 			BigDecimal sum = BigDecimal.ZERO;
132 			for (PositionResponsibilityBo aResp : aPosition.getPositionResponsibilityList()) {
133 				if (aResp != null && aResp.getPercentTime() != null) {
134 					sum = sum.add(aResp.getPercentTime());
135 				}
136 			}
137 			if (sum.compareTo(new BigDecimal(100)) > 0) {
138 				String[] parameters = new String[1];
139 				parameters[0] = sum.toString();
140 				this.putFieldError("dataObject.positionResponsibilityList",
141 						"responsibility.percenttime.exceedsMaximum", parameters);
142 				return false;
143 			}
144 		}
145 		return true;
146 	}
147 
148 	
149 	// KPME-3016  
150 	// Now each section is its own page that if you want to show errors globally, you have to catch them globally
151 	protected boolean validateOverviewPage(PositionBo aPosition) {
152 
153 		// required fields
154 		if (aPosition.getEffectiveDate() == null
155                 || StringUtils.isEmpty(aPosition.getGroupKeyCode())
156                 || StringUtils.isEmpty(aPosition.getPrimaryDepartment())
157                 || StringUtils.isEmpty(aPosition.getPositionClass())
158                 || StringUtils.isEmpty(aPosition.getDescription())
159 				|| StringUtils.isEmpty(aPosition.getPositionStatus())
160 				|| StringUtils.isEmpty(aPosition.getAppointmentType())
161 				|| StringUtils.isEmpty(aPosition.getTemporary())
162 				|| StringUtils.isEmpty(aPosition.getContract())) {
163 
164 			this.putGlobalError("error.overview.fields.required");
165 			return false;
166 		}
167 
168 		// validate appointment type
169 		if (!StringUtils.isEmpty(aPosition.getAppointmentType())) {
170 			List <PositionDepartmentBo> depts = aPosition.getDepartmentList();
171 			if (depts != null && depts.size() > 0) {
172 				boolean found = false;
173 				for (PositionDepartmentBo aPos : depts) {
174 					if (PmValidationUtils.validatePositionAppointmentType(aPosition.getAppointmentType(), aPos.getGroupKeyCode(), aPosition.getEffectiveLocalDate())) {
175 						found = true;
176 						break;
177 					}
178 				}
179 				if (!found) {
180 					this.putFieldError("dataObject.appointmentType", "error.existence", "Appointment Type '" + aPosition.getAppointmentType() + "'");
181 					return false;						
182 				}
183 			}
184 		}
185 		
186 		// validate contract and contrqact type
187 		if (StringUtils.equals(aPosition.getContract(), "Y")) {
188 			if (StringUtils.isEmpty(aPosition.getContractType())) {
189 				this.putFieldError("dataObject.contractType", "error.overview.fields.required");
190 				return false;
191 			} else {
192 				if (!PmValidationUtils.validatePositionContractType(aPosition.getContractType(), aPosition.getInstitution()+"-"+aPosition.getLocation(), aPosition.getEffectiveLocalDate())) {
193 					this.putFieldError("dataObject.contractType", "error.existence", "Contract Type '" + aPosition.getContractType() + "'");
194 					return false;
195 				}
196 			}
197 		}
198 		
199 		// validate renewal eligible
200 		if (aPosition.getExpectedEndDate() != null) {
201 			if (StringUtils.isEmpty(aPosition.getRenewEligible())) {
202 				this.putFieldError("dataObject.renewEligible", "error.overview.fields.required");
203 				return false;
204 			}
205 		}
206 		
207 		return true;
208 	}
209 
210     protected  boolean validatPrimaryDepartmentExistence(PositionBo aPosition) {
211         Department dept = HrServiceLocator.getDepartmentService().getDepartment(aPosition.getPrimaryDepartment(), aPosition.getGroupKeyCode(), aPosition.getEffectiveLocalDate());
212         if (dept == null) {
213             this.putFieldError("dataObject.primaryDepartment","error.existence", "Primary Department '" + aPosition.getPrimaryDepartment() + "'");
214             return false;
215         }
216         return true;
217     }
218 	
219 	protected boolean validateClassificationPage(PositionBo aPosition) {
220 
221 		if (StringUtils.isEmpty(aPosition.getPmPositionClassId())
222 				|| StringUtils.isEmpty(aPosition.getTenureEligible())
223 				|| StringUtils.isEmpty(aPosition.getBenefitsEligible())
224 				|| StringUtils.isEmpty(aPosition.getLeaveEligible())) {
225 
226 			this.putFieldError("dataObject.pmPositionClassId", "error.classication.fields.required");
227 			return false;
228 		}
229 		// validate leave plan
230 		if (StringUtils.equals(aPosition.getLeaveEligible(), "Y")) {
231 			if (StringUtils.isEmpty(aPosition.getLeavePlan())) {
232 				this.putFieldError("dataObject.leavePlan", "error.classication.fields.required");
233 				return false;
234 			} 
235 		}
236 		
237 		//validate Group Key
238 		ClassificationContract classification = PmServiceLocator.getClassificationService().getClassificationById(aPosition.getPmPositionClassId());
239 		Set<String> groupKeyCodes = classification.getGroupKeyCodeSet();
240 		if(!groupKeyCodes.contains(aPosition.getGroupKeyCode())){
241 			String[] parameters = new String[2];
242 			parameters[0] = classification.getPositionClass();
243 			parameters[1] = aPosition.getGroupKeyCode();
244 			this.putFieldError("dataObject.pmPositionClassId", "error.classication.groupkey.invalid.sync", parameters);
245 			return false;
246 		}
247 		return true;
248 	}
249 	
250 	protected boolean validatePrimaryDepartment(PositionBo aPosition) {
251 
252 		if (CollectionUtils.isNotEmpty(aPosition.getDepartmentList())) {
253 			for (PositionDepartmentBo aDepartment : aPosition.getDepartmentList()) {
254 				if(aDepartment != null && aDepartment.getDeptAfflObj() != null) {
255 					DepartmentAffiliationBo pda = (DepartmentAffiliationBo)aDepartment.getDeptAfflObj();
256 					if (pda.isPrimaryIndicator()) {
257 						return true;
258 					}
259 				}
260 			}
261 		}
262 
263 		this.putFieldError("dataObject.primaryDepartment", "error.primaryDepartment.required");
264 		return false;
265 	}
266 
267 	protected boolean validateProcess(PositionBo newPosition, PositionBo oldPosition) {
268         String process = newPosition.getProcess();
269             if (StringUtils.equals(process, PMConstants.PSTN_PROCESS_REORG)) {
270 
271                 if (StringUtils.equals(newPosition.getPrimaryDepartment(),oldPosition.getPrimaryDepartment())
272                         && StringUtils.equals(newPosition.getReportsToPositionId(),oldPosition.getReportsToPositionId())) {
273                     this.putFieldError("dataObject.primaryDepartment","error.reorganization.noChange");
274                     return false;
275                 }
276 
277             } else if (StringUtils.equals(process,PMConstants.PSTN_PROCESS_RECLASS)) {
278                 if(StringUtils.equals(newPosition.getPmPositionClassId(),oldPosition.getPmPositionClassId())) {
279                     this.putFieldError("dataObject.positionClass","error.reclassification.noChange");
280                     return false;
281                 }
282             } else if (StringUtils.equals(process,PMConstants.PSTN_PROCESS_STATUS)) {
283                 if(StringUtils.equals(newPosition.getPositionStatus(),oldPosition.getPositionStatus())) {
284                     this.putFieldError("dataObject.positionStatus","error.changeStatus.noChange");
285                 }
286             }
287 
288         return true;
289     }
290 
291 	protected boolean validateFundingLines(PositionBo aPosition) {
292     	boolean valid = true;
293     	String prefix = "fundingList";
294     	if(CollectionUtils.isNotEmpty(aPosition.getFundingList())) {
295 	    	for (ListIterator<? extends PositionFundingBo> iterator = aPosition.getFundingList().listIterator(); iterator.hasNext(); ) {
296 				int index = iterator.nextIndex();
297 				PositionFundingBo pf = iterator.next();
298 				valid &= validateAddFundingLine(pf, aPosition, prefix, index);
299 			}
300     	}
301     	return valid;
302     }
303     
304 	protected boolean validateAddFundingLine(PositionFundingBo pf, PositionBo aPosition,String prefix, int index) {
305 		boolean valid = true;
306 		String propertyNamePrefix = prefix + "[" + index + "].";
307     	if(StringUtils.isNotEmpty(pf.getAccount())) {
308     		boolean results = ValidationUtils.validateAccount(pf.getChart(), pf.getAccount());
309     		if(!results) {
310     			this.putFieldError(propertyNamePrefix + "account","error.existence", "Account '" + pf.getAccount() + "'");
311     			valid = false;
312     		}
313     	}
314     	if(StringUtils.isNotEmpty(pf.getSubAccount())) {
315     		boolean results = ValidationUtils.validateSubAccount(pf.getSubAccount(), pf.getAccount(), pf.getChart());
316     		if(!results) {
317 	   			 this.putFieldError(propertyNamePrefix + "subAccount","error.existence", "Sub Account '" + pf.getSubAccount() + "'");
318 	   			 valid = false;
319     		}
320     	}
321     	if(StringUtils.isNotEmpty(pf.getObjectCode())) {
322     		boolean results = ValidationUtils.validateObjectCode(pf.getObjectCode(), pf.getChart(), null);
323     		if(!results) {
324     			 this.putFieldError(propertyNamePrefix + "objectCode","error.existence", "Object Code '" + pf.getObjectCode() + "'");
325     			 valid = false;
326     		}
327     	}
328     	if(StringUtils.isNotEmpty(pf.getSubObjectCode())) {
329     		boolean results = ValidationUtils.validateSubObjectCode(null,
330     				pf.getChart(),
331     				pf.getAccount(),
332     				pf.getObjectCode(),
333     				pf.getSubObjectCode());
334     		if(!results) {
335     			 this.putFieldError(propertyNamePrefix + "subObjectCode","error.existence", "Sub Object Code '" + pf.getSubObjectCode() + "'");
336     			 valid= false;
337     		}
338     	}
339     	return valid;
340     
341 	}
342 
343 
344 }