001 /** 002 * Copyright 2004-2013 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.hr.time.assignment.validation; 017 018 import java.sql.Date; 019 import java.util.Calendar; 020 import java.util.Collection; 021 import java.util.HashMap; 022 import java.util.HashSet; 023 import java.util.Iterator; 024 import java.util.List; 025 import java.util.Map; 026 import java.util.Set; 027 028 import org.apache.commons.lang.StringUtils; 029 import org.apache.commons.lang.time.DateUtils; 030 import org.kuali.hr.job.Job; 031 import org.kuali.hr.time.assignment.Assignment; 032 import org.kuali.hr.time.assignment.AssignmentAccount; 033 import org.kuali.hr.time.earncode.EarnCode; 034 import org.kuali.hr.time.paytype.PayType; 035 import org.kuali.hr.time.service.base.TkServiceLocator; 036 import org.kuali.hr.time.task.Task; 037 import org.kuali.hr.time.timeblock.TimeBlock; 038 import org.kuali.hr.time.util.TKUtils; 039 import org.kuali.hr.time.util.ValidationUtils; 040 import org.kuali.kfs.coa.businessobject.Account; 041 import org.kuali.kfs.coa.businessobject.ObjectCode; 042 import org.kuali.kfs.coa.businessobject.SubObjectCode; 043 import org.kuali.rice.kns.document.MaintenanceDocument; 044 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase; 045 import org.kuali.rice.krad.bo.PersistableBusinessObject; 046 import org.kuali.rice.krad.service.KRADServiceLocator; 047 import org.kuali.rice.krad.util.GlobalVariables; 048 049 public class AssignmentRule extends MaintenanceDocumentRuleBase { 050 051 protected boolean validateWorkArea(Assignment assignment) { 052 boolean valid = true; 053 if (assignment.getWorkArea() != null) { 054 if (!ValidationUtils.validateWorkArea(assignment.getWorkArea(), 055 assignment.getEffectiveDate())) { 056 this.putFieldError("workArea", "error.existence", "workArea '" 057 + assignment.getWorkArea() + "'"); 058 valid = false; 059 } else { 060 int count = TkServiceLocator.getWorkAreaService().getWorkAreaCount(assignment.getDept(), assignment.getWorkArea()); 061 valid = (count > 0); 062 if (!valid) { 063 this.putFieldError("workArea", "dept.workarea.invalid.sync"); 064 } 065 } 066 } 067 return valid; 068 } 069 070 protected boolean validateTask(Assignment assignment) { 071 boolean valid = true; 072 //task by default is zero so if non zero validate against existing taskss 073 if (assignment.getTask() != null && !assignment.getTask().equals(0L)) { 074 Task task = TkServiceLocator.getTaskService().getTask(assignment.getTask(), assignment.getEffectiveDate()); 075 if(task != null) { 076 if(task.getWorkArea() == null || !task.getWorkArea().equals(assignment.getWorkArea())) { 077 this.putFieldError("task", "task.workarea.invalid.sync"); 078 valid = false; 079 } 080 } 081 } 082 return valid; 083 } 084 085 protected boolean validateDepartment(Assignment assignment) { 086 boolean valid = true; 087 if (assignment.getDept() != null) { 088 int count = TkServiceLocator.getJobService().getJobCount(null, assignment.getJobNumber(), assignment.getDept()); 089 valid = (count > 0); 090 if (!valid) { 091 this.putFieldError("dept", "dept.jobnumber.invalid.sync"); 092 } 093 094 } 095 return valid; 096 } 097 098 protected boolean validateJob(Assignment assignment) { 099 boolean valid = false; 100 LOG.debug("Validating job: " + assignment.getPrincipalId() +" Job number: "+assignment.getJobNumber()); 101 Job job = TkServiceLocator.getJobService().getJob( 102 assignment.getPrincipalId(), assignment.getJobNumber(), 103 assignment.getEffectiveDate(), false); 104 // Job job = 105 // KNSServiceLocator.getBusinessObjectService().findBySinglePrimaryKey(Job.class, 106 // assignment.getJob().getHrJobId()); 107 if (job != null) { 108 valid = true; 109 110 LOG.debug("found job."); 111 } else { 112 this.putFieldError("jobNumber", "error.existence", "jobNumber '" 113 + assignment.getJobNumber() + "'"); 114 } 115 return valid; 116 } 117 118 protected boolean validatePercentagePerEarnCode(Assignment assignment) { 119 boolean valid = true; 120 LOG.debug("Validating PercentagePerEarnCode: "); 121 List<AssignmentAccount> assignmentAccounts = assignment 122 .getAssignmentAccounts(); 123 Set<String> invalidEarnCodes = null; 124 if (assignmentAccounts != null && assignment.isActive()) { 125 Map<String, Integer> earnCodePercent = new HashMap<String, Integer>(); 126 for (AssignmentAccount account : assignmentAccounts) { 127 if (account.getPercent() != null && account.isActive()) { 128 int percent = 0; 129 if (earnCodePercent.containsKey(account.getEarnCode())) { 130 percent = earnCodePercent.get(account.getEarnCode()); 131 } 132 percent += account.getPercent().toBigInteger().intValue(); 133 earnCodePercent.put(account.getEarnCode(), percent); 134 } 135 } 136 //Iterator<String> itr = earnCodePercent.keySet().iterator(); 137 for (Map.Entry<String, Integer> entry : earnCodePercent.entrySet()) { 138 //while (itr.hasNext()) { 139 String earnCode = entry.getKey(); 140 if (entry.getValue() != 100) { 141 if (invalidEarnCodes == null) { 142 invalidEarnCodes = new HashSet<String>(); 143 } 144 invalidEarnCodes.add(earnCode); 145 valid = false; 146 } 147 } 148 if (!valid) { 149 int index = 0; 150 for (AssignmentAccount account : assignmentAccounts) { 151 if (invalidEarnCodes.contains(account.getEarnCode())) { 152 this.putFieldError("assignmentAccounts[" + index 153 + "].percent", "error.percentage.earncode"); 154 } 155 index++; 156 } 157 } 158 } 159 return valid; 160 } 161 162 protected boolean validateEarnCode(AssignmentAccount assignmentAccount) { 163 boolean valid = false; 164 LOG.debug("Validating EarnCode: " + assignmentAccount.getEarnCode()); 165 Date date = new Date(Calendar.getInstance().getTimeInMillis()); 166 EarnCode earnCode = TkServiceLocator.getEarnCodeService().getEarnCode( 167 assignmentAccount.getEarnCode(), date); 168 if (earnCode != null) { 169 170 valid = true; 171 LOG.debug("found earnCode."); 172 } else { 173 this.putGlobalError("error.existence", "earn code '" 174 + assignmentAccount.getEarnCode() + "'"); 175 } 176 return valid; 177 } 178 179 protected boolean validateRegPayEarnCode(Assignment assignment) { 180 boolean valid = false; 181 int index = 0; 182 LOG.debug("Validating Regular pay EarnCodes: " + assignment.getAssignmentAccounts().size()); 183 for(AssignmentAccount assignmentAccount : assignment.getAssignmentAccounts()){ 184 if(assignment.getJobNumber()!=null && assignment.getPrincipalId()!=null){ 185 Job job = TkServiceLocator.getJobService().getJob(assignment.getPrincipalId(), assignment.getJobNumber(), assignment.getEffectiveDate(), false); 186 if(job !=null){ 187 PayType payType = TkServiceLocator.getPayTypeService().getPayType(job.getHrPayType(), assignment.getEffectiveDate()); 188 if(StringUtils.equals(assignmentAccount.getEarnCode(), payType.getRegEarnCode())){ 189 valid = true; 190 break; 191 } 192 193 } 194 } 195 index++; 196 } 197 if(!valid) { 198 this.putFieldError("assignmentAccounts", "earncode.regular.pay.required"); 199 } 200 return valid; 201 } 202 203 protected boolean validateAccount(AssignmentAccount assignmentAccount) { 204 boolean valid = false; 205 LOG.debug("Validating Account: " + assignmentAccount.getAccountNbr()); 206 Map<String, String> fields = new HashMap<String, String>(); 207 fields.put("accountNumber", assignmentAccount.getAccountNbr()); 208 Collection account = KRADServiceLocator.getBusinessObjectService() 209 .findMatching(Account.class, fields); 210 valid = account.size() > 0; 211 if (!valid) { 212 this.putGlobalError("error.existence", "Account Number '" 213 + assignmentAccount.getAccountNbr() + "'"); 214 } 215 return valid; 216 } 217 218 protected boolean validateObjectCode(AssignmentAccount assignmentAccount) { 219 boolean valid = false; 220 LOG.debug("Validating ObjectCode: " 221 + assignmentAccount.getFinObjectCd()); 222 Map<String, String> fields = new HashMap<String, String>(); 223 fields.put("financialObjectCode", assignmentAccount.getFinObjectCd()); 224 Collection objectCode = KRADServiceLocator.getBusinessObjectService() 225 .findMatching(ObjectCode.class, fields); 226 valid = objectCode.size() > 0; 227 if (!valid) { 228 this.putGlobalError("error.existence", "Object Code '" 229 + assignmentAccount.getFinObjectCd() + "'"); 230 } 231 return valid; 232 } 233 234 protected boolean validateSubObjectCode(AssignmentAccount assignmentAccount) { 235 boolean valid = false; 236 LOG.debug("Validating SubObjectCode: " 237 + assignmentAccount.getFinSubObjCd()); 238 if (assignmentAccount.getFinSubObjCd() != null) { 239 Map<String, String> fields = new HashMap<String, String>(); 240 fields.put("financialSubObjectCode", assignmentAccount 241 .getFinSubObjCd()); 242 Collection subObjectCode = KRADServiceLocator.getBusinessObjectService() 243 .findMatching(SubObjectCode.class, fields); 244 valid = subObjectCode.size() > 0; 245 if (!valid) { 246 this.putGlobalError("error.existence", "SubObject Code '" 247 + assignmentAccount.getFinSubObjCd() + "'"); 248 } 249 } else { 250 valid = true; 251 } 252 return valid; 253 } 254 255 protected boolean validateHasAccounts(Assignment assign){ 256 if(assign.getAssignmentAccounts().isEmpty()){ 257 this.putGlobalError("error.assign.must.have.one.or.more.account"); 258 return false; 259 } 260 return true; 261 } 262 263 protected boolean validateActiveFlag(Assignment assign){ 264 if(!assign.isActive()) { 265 List<TimeBlock> tbList = TkServiceLocator.getTimeBlockService().getTimeBlocksForAssignment(assign); 266 if(!tbList.isEmpty()) { 267 Date tbEndDate = tbList.get(0).getEndDate(); 268 for(TimeBlock tb : tbList) { 269 if(tb.getEndDate().after(tbEndDate)) { 270 tbEndDate = tb.getEndDate(); // get the max end date 271 } 272 } 273 if(tbEndDate.equals(assign.getEffectiveDate()) || tbEndDate.after(assign.getEffectiveDate())) { 274 this.putFieldError("active", "error.assignment.timeblock.existence", tbEndDate.toString()); 275 return false; 276 } 277 } 278 } 279 return true; 280 } 281 282 /** 283 * It looks like the method that calls this class doesn't actually care 284 * about the return type. 285 */ 286 @Override 287 protected boolean processCustomRouteDocumentBusinessRules( 288 MaintenanceDocument document) { 289 boolean valid = false; 290 LOG.debug("entering custom validation for DeptLunchRule"); 291 PersistableBusinessObject pbo = (PersistableBusinessObject) this.getNewBo(); 292 if (pbo instanceof Assignment) { 293 Assignment assignment = (Assignment) pbo; 294 if (assignment != null) { 295 valid = true; 296 valid &= this.validateWorkArea(assignment); 297 valid &= this.validateTask(assignment); 298 valid &= this.validateJob(assignment); 299 valid &= this.validateDepartment(assignment); 300 valid &= this.validatePercentagePerEarnCode(assignment); 301 valid &= this.validateHasAccounts(assignment); 302 valid &= this.validateActiveFlag(assignment); 303 if(!assignment.getAssignmentAccounts().isEmpty()) { 304 valid &= this.validateRegPayEarnCode(assignment); 305 } 306 } 307 } 308 309 return valid; 310 } 311 312 @Override 313 public boolean processCustomAddCollectionLineBusinessRules( 314 MaintenanceDocument document, String collectionName, 315 PersistableBusinessObject line) { 316 boolean valid = false; 317 LOG.debug("entering custom validation for DeptLunchRule"); 318 PersistableBusinessObject pbo = line; 319 if (pbo instanceof AssignmentAccount) { 320 321 AssignmentAccount assignmentAccount = (AssignmentAccount) pbo; 322 if (assignmentAccount != null) { 323 valid = true; 324 valid &= this.validateEarnCode(assignmentAccount); 325 valid &= this.validateAccount(assignmentAccount); 326 valid &= this.validateObjectCode(assignmentAccount); 327 valid &= this.validateSubObjectCode(assignmentAccount); 328 } 329 } 330 return valid; 331 } 332 333 }