1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.hr.lm.leavecalendar.validation;
17
18 import java.math.BigDecimal;
19 import java.sql.Date;
20 import java.util.ArrayList;
21 import java.util.Calendar;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.apache.commons.collections.CollectionUtils;
29 import org.apache.commons.lang.StringUtils;
30 import org.joda.time.DateTime;
31 import org.joda.time.DateTimeConstants;
32 import org.joda.time.DateTimeZone;
33 import org.joda.time.Hours;
34 import org.joda.time.Interval;
35 import org.joda.time.LocalDateTime;
36 import org.kuali.hr.lm.LMConstants;
37 import org.kuali.hr.lm.accrual.AccrualCategory;
38 import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
39 import org.kuali.hr.lm.leave.web.LeaveCalendarWSForm;
40 import org.kuali.hr.lm.leaveSummary.LeaveSummary;
41 import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow;
42 import org.kuali.hr.lm.leaveblock.LeaveBlock;
43 import org.kuali.hr.lm.leavecalendar.LeaveCalendarDocument;
44 import org.kuali.hr.time.assignment.Assignment;
45 import org.kuali.hr.time.assignment.AssignmentDescriptionKey;
46 import org.kuali.hr.time.calendar.CalendarEntries;
47 import org.kuali.hr.time.earncode.EarnCode;
48 import org.kuali.hr.time.earncodegroup.EarnCodeGroup;
49 import org.kuali.hr.time.service.base.TkServiceLocator;
50 import org.kuali.hr.time.util.TKContext;
51 import org.kuali.hr.time.util.TKUser;
52 import org.kuali.hr.time.util.TKUtils;
53 import org.kuali.hr.time.util.TkConstants;
54 import org.kuali.rice.kew.api.KewApiServiceLocator;
55 import org.kuali.rice.kew.api.document.DocumentStatus;
56
57 public class LeaveCalendarValidationUtil {
58
59
60 public static List<String> validateLeaveAccrualRuleMaxUsage(LeaveCalendarWSForm lcf) {
61 LeaveBlock updatedLeaveBlock = null;
62 if(lcf.getLeaveBlockId() != null) {
63 updatedLeaveBlock = TkServiceLocator.getLeaveBlockService().getLeaveBlock(lcf.getLeaveBlockId());
64 }
65 return validateLeaveAccrualRuleMaxUsage(lcf.getLeaveSummary(), lcf.getSelectedEarnCode(), lcf.getStartDate(),
66 lcf.getEndDate(), lcf.getLeaveAmount(), updatedLeaveBlock);
67 }
68
69 public static List<String> validateLeaveAccrualRuleMaxUsage(LeaveSummary ls, String selectedEarnCode, String leaveStartDateString,
70 String leaveEndDateString, BigDecimal leaveAmount, LeaveBlock updatedLeaveBlock) {
71 List<String> errors = new ArrayList<String>();
72 String principalId = TKContext.getTargetPrincipalId();
73 long daysSpan = TKUtils.getDaysBetween(TKUtils.formatDateString(leaveStartDateString), TKUtils.formatDateString(leaveEndDateString));
74 if(leaveAmount == null) {
75 leaveAmount = TKUtils.getHoursBetween(TKUtils.formatDateString(leaveStartDateString).getTime(), TKUtils.formatDateString(leaveEndDateString).getTime());
76 }
77 if(ls != null && CollectionUtils.isNotEmpty(ls.getLeaveSummaryRows())) {
78 BigDecimal oldLeaveAmount = null;
79 boolean earnCodeChanged = false;
80 if(updatedLeaveBlock != null) {
81 if(!updatedLeaveBlock.getEarnCode().equals(selectedEarnCode)) {
82 earnCodeChanged = true;
83 }
84 if(!updatedLeaveBlock.getLeaveAmount().equals(leaveAmount)) {
85 oldLeaveAmount = updatedLeaveBlock.getLeaveAmount();
86 }
87 }
88 Date aDate = TKUtils.formatDateString(leaveEndDateString);
89 EarnCode earnCodeObj = TkServiceLocator.getEarnCodeService().getEarnCode(selectedEarnCode, aDate);
90 if(earnCodeObj != null && StringUtils.equals(earnCodeObj.getAccrualBalanceAction(),LMConstants.ACCRUAL_BALANCE_ACTION.USAGE)
91 || StringUtils.equals(earnCodeObj.getUsageLimit(), "I")) {
92 AccrualCategory accrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(earnCodeObj.getAccrualCategory(), aDate);
93 if(accrualCategory != null) {
94 List<LeaveSummaryRow> rows = ls.getLeaveSummaryRows();
95 for(LeaveSummaryRow aRow : rows) {
96 if(aRow.getAccrualCategory().equals(accrualCategory.getAccrualCategory())) {
97
98 List<EmployeeOverride> employeeOverrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId,TKUtils.formatDateString(leaveEndDateString));
99 String leavePlan = accrualCategory.getLeavePlan();
100 BigDecimal maxUsage = aRow.getUsageLimit();
101 for(EmployeeOverride eo : employeeOverrides) {
102 if(eo.getLeavePlan().equals(leavePlan) && eo.getAccrualCategory().equals(aRow.getAccrualCategory())) {
103 if(eo.getOverrideType().equals("MU") && eo.isActive()) {
104 if(eo.getOverrideValue()!=null) {
105 maxUsage = new BigDecimal(eo.getOverrideValue());
106 } else {
107 maxUsage = null;
108 }
109 }
110 }
111 }
112 BigDecimal ytdUsage = aRow.getYtdApprovedUsage();
113 BigDecimal pendingLeaveBalance = aRow.getPendingLeaveRequests();
114 BigDecimal desiredUsage = new BigDecimal(0);
115 if(pendingLeaveBalance!=null) {
116 if(oldLeaveAmount!=null) {
117
118 if(!earnCodeChanged ||
119 updatedLeaveBlock.getAccrualCategory().equals(accrualCategory.getAccrualCategory())) {
120 pendingLeaveBalance = pendingLeaveBalance.subtract(oldLeaveAmount.abs());
121 }
122 }
123
124 desiredUsage = desiredUsage.add(pendingLeaveBalance);
125 }
126
127 desiredUsage = desiredUsage.add(leaveAmount.multiply(new BigDecimal(daysSpan+1)));
128
129 if(ytdUsage!=null) {
130 desiredUsage = desiredUsage.add(ytdUsage);
131 }
132 if(maxUsage!=null) {
133 if(desiredUsage.compareTo(maxUsage) > 0 ) {
134 errors.add("This leave request would exceed the usage limit for " + aRow.getAccrualCategory());
135 }
136 }
137 }
138 }
139 }
140 }
141 }
142 return errors;
143 }
144
145
146
147 public static Map<String, Set<String>> validatePendingTransactions(String principalId, Date fromDate, Date toDate) {
148 Map<String, Set<String>> allMessages = new HashMap<String, Set<String>>();
149
150 Set<String> actionMessages = new HashSet<String>();
151 Set<String> infoMessages = new HashSet<String>();
152 Set<String> warningMessages = new HashSet<String>();
153
154 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(principalId, fromDate, toDate, LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER);
155 Set<String> workflowDocIds = new HashSet<String>();
156 for(LeaveBlock lb : leaveBlocks) {
157 if(lb.getTransactionalDocId() != null) {
158 workflowDocIds.add(lb.getTransactionalDocId());
159 } else {
160 if(StringUtils.contains(lb.getDescription(), "Forfeited balance transfer amount")) {
161 infoMessages.add("A max balance action that forfeited accrued leave occurred on this calendar");
162 }
163 }
164 }
165 for(String workflowDocId : workflowDocIds) {
166 DocumentStatus status = KewApiServiceLocator.getWorkflowDocumentService().getDocumentStatus(workflowDocId);
167
168 if(StringUtils.equals(status.getCode(), TkConstants.ROUTE_STATUS.FINAL)) {
169 infoMessages.add("A transfer action occurred on this calendar");
170 }
171 else if(StringUtils.equals(status.getCode(), TkConstants.ROUTE_STATUS.ENROUTE)) {
172 actionMessages.add("A pending balance transfer exists on this calendar. It must be finalized before this calendar can be approved");
173 }
174 else {
175 warningMessages.add("A balance transfer document exists for this calendar with status neither final nor enroute");
176 }
177 }
178
179 leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(principalId, fromDate, toDate, LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
180 workflowDocIds = new HashSet<String>();
181 for(LeaveBlock lb : leaveBlocks) {
182 if(lb.getTransactionalDocId() != null) {
183 workflowDocIds.add(lb.getTransactionalDocId());
184 }
185 }
186 for(String workflowDocId : workflowDocIds) {
187 DocumentStatus status = KewApiServiceLocator.getWorkflowDocumentService().getDocumentStatus(workflowDocId);
188
189 if(StringUtils.equals(status.getCode(), TkConstants.ROUTE_STATUS.FINAL)) {
190 infoMessages.add("A payout action occurred on this calendar");
191 }
192 else if(StringUtils.equals(status.getCode(), TkConstants.ROUTE_STATUS.ENROUTE)) {
193 actionMessages.add("A pending payout exists on this calendar. It must be finalized before this calendar can be approved");
194 }
195 else {
196 warningMessages.add("A payout document exists for this calendar with status neither final or enroute");
197 }
198 }
199 allMessages.put("actionMessages", actionMessages);
200 allMessages.put("infoMessages", infoMessages);
201 allMessages.put("warningMessages", warningMessages);
202
203 return allMessages;
204 }
205
206
207 public static Map<String, Set<String>> getWarningMessagesForLeaveBlocks(List<LeaveBlock> leaveBlocks) {
208
209 Map<String, Set<String>> allMessages = new HashMap<String, Set<String>>();
210
211 Set<String> actionMessages = new HashSet<String>();
212 Set<String> infoMessages = new HashSet<String>();
213 Set<String> warningMessages = new HashSet<String>();
214
215 if (CollectionUtils.isNotEmpty(leaveBlocks)) {
216 for(LeaveBlock lb : leaveBlocks) {
217 EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(lb.getEarnCode(), lb.getLeaveDate());
218 if(ec != null) {
219 EarnCodeGroup eg = TkServiceLocator.getEarnCodeGroupService().getEarnCodeGroupForEarnCode(lb.getEarnCode(), lb.getLeaveDate());
220 if(eg != null && !StringUtils.isEmpty(eg.getWarningText())) {
221 warningMessages.add(eg.getWarningText());
222 }
223 }
224 }
225 }
226 allMessages.put("actionMessages", actionMessages);
227 allMessages.put("infoMessages", infoMessages);
228 allMessages.put("warningMessages", warningMessages);
229
230
231 return allMessages;
232 }
233
234 public static List<String> validateAvailableLeaveBalance(LeaveCalendarWSForm lcf) {
235 LeaveBlock updatedLeaveBlock = null;
236 if(lcf.getLeaveBlockId() != null) {
237 updatedLeaveBlock = TkServiceLocator.getLeaveBlockService().getLeaveBlock(lcf.getLeaveBlockId());
238 }
239 return validateAvailableLeaveBalanceForUsage(lcf.getSelectedEarnCode(), lcf.getStartDate(), lcf.getEndDate(), lcf.getLeaveAmount(), updatedLeaveBlock);
240 }
241
242 public static List<String> validateAvailableLeaveBalanceForUsage(String earnCode, String leaveStartDateString, String leaveEndDateString,
243 BigDecimal leaveAmount, LeaveBlock updatedLeaveBlock) {
244 List<String> errors = new ArrayList<String>();
245 boolean earnCodeChanged = false;
246 BigDecimal oldAmount = null;
247
248 if(leaveAmount == null) {
249 leaveAmount = TKUtils.getHoursBetween(TKUtils.formatDateString(leaveStartDateString).getTime(), TKUtils.formatDateString(leaveEndDateString).getTime());
250 }
251 if(updatedLeaveBlock != null) {
252 if(!updatedLeaveBlock.getEarnCode().equals(earnCode)) {
253 earnCodeChanged = true;
254 }
255 if(!updatedLeaveBlock.getLeaveAmount().equals(leaveAmount)) {
256 oldAmount = updatedLeaveBlock.getLeaveAmount();
257 }
258 }
259 Date startDate = TKUtils.formatDateString(leaveStartDateString);
260 Date endDate = TKUtils.formatDateString(leaveEndDateString);
261 long daysSpan = TKUtils.getDaysBetween(startDate,endDate);
262 EarnCode earnCodeObj = TkServiceLocator.getEarnCodeService().getEarnCode(earnCode, endDate);
263 if(earnCodeObj != null && earnCodeObj.getAllowNegativeAccrualBalance().equals("N")) {
264 AccrualCategory accrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(earnCodeObj.getAccrualCategory(), endDate);
265 if(accrualCategory != null) {
266 java.util.Date nextIntervalDate = TkServiceLocator.getAccrualService().getNextAccrualIntervalDate(accrualCategory.getAccrualEarnInterval(), endDate);
267
268 java.util.Date usageEndDate = nextIntervalDate;
269 if(nextIntervalDate.compareTo(endDate) > 0) {
270 Calendar aCal = Calendar.getInstance();
271 aCal.setTime(nextIntervalDate);
272 aCal.add(Calendar.DAY_OF_YEAR, -1);
273 usageEndDate = aCal.getTime();
274 }
275
276
277 if(accrualCategory.getAccrualEarnInterval().equals(LMConstants.ACCRUAL_EARN_INTERVAL_CODE.NO_ACCRUAL)) {
278 Calendar aCal = Calendar.getInstance();
279 aCal.setTime(endDate);
280 aCal.set(Calendar.MONTH, Calendar.DECEMBER);
281 aCal.set(Calendar.DAY_OF_MONTH, 31);
282 nextIntervalDate = aCal.getTime();
283 usageEndDate = nextIntervalDate;
284 }
285 BigDecimal availableBalance = TkServiceLocator.getLeaveSummaryService()
286 .getLeaveBalanceForAccrCatUpToDate(TKContext.getTargetPrincipalId(), startDate, endDate, accrualCategory.getAccrualCategory(), usageEndDate);
287
288 if(oldAmount!=null) {
289 if(!earnCodeChanged ||
290 updatedLeaveBlock.getAccrualCategory().equals(accrualCategory.getAccrualCategory())) {
291 availableBalance = availableBalance.add(oldAmount.abs());
292 }
293 }
294
295
296 BigDecimal desiredUsage = leaveAmount.multiply(new BigDecimal(daysSpan+1));
297
298
299
300
301
302
303 if(desiredUsage.compareTo(availableBalance) > 0 ) {
304 errors.add("Requested leave amount " + desiredUsage.toString() + " is greater than available leave balance " + availableBalance.toString());
305 }
306 }
307 }
308
309 return errors;
310 }
311
312 public static List<String> validateDates(String startDateS, String endDateS) {
313 List<String> errors = new ArrayList<String>();
314 if (errors.size() == 0 && StringUtils.isEmpty(startDateS)) errors.add("The start date is blank.");
315 if (errors.size() == 0 && StringUtils.isEmpty(endDateS)) errors.add("The end date is blank.");
316 return errors;
317 }
318
319 public static List<String> validateTimes(String startTimeS, String endTimeS) {
320 List<String> errors = new ArrayList<String>();
321 if (errors.size() == 0 && startTimeS == null) errors.add("The start time is blank.");
322 if (errors.size() == 0 && endTimeS == null) errors.add("The end time is blank.");
323 return errors;
324 }
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383 public static List<String> validateSpanningWeeks(LeaveCalendarWSForm lcf) {
384 List<String> errors = new ArrayList<String>();
385
386 boolean spanningWeeks = lcf.getSpanningWeeks().equalsIgnoreCase("y");
387
388 if (!spanningWeeks) {
389 Date startDate = TKUtils.formatDateString(lcf.getStartDate());
390 EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(lcf.getSelectedEarnCode(), startDate);
391 DateTime startTemp, endTemp;
392
393 if (ec != null && !ec.getRecordMethod().equals(LMConstants.RECORD_METHOD.TIME)) {
394 startTemp = new DateTime(startDate);
395 endTemp = new DateTime(TKUtils.formatDateString(lcf.getEndDate()));
396 } else {
397 startTemp = new DateTime(TKUtils.convertDateStringToTimestamp(lcf.getStartDate()).getTime());
398 endTemp = new DateTime(TKUtils.convertDateStringToTimestamp(lcf.getEndDate()).getTime());
399 }
400
401 boolean isOnlyWeekendSpan = true;
402 while ((startTemp.isBefore(endTemp) || startTemp.isEqual(endTemp)) && isOnlyWeekendSpan) {
403 if (startTemp.getDayOfWeek() != DateTimeConstants.SATURDAY && startTemp.getDayOfWeek() != DateTimeConstants.SUNDAY) {
404 isOnlyWeekendSpan = false;
405 }
406 startTemp = startTemp.plusDays(1);
407 }
408 if (isOnlyWeekendSpan) {
409 errors.add("Weekend day is selected, but include weekends checkbox is not checked");
410 }
411 }
412
413 return errors;
414 }
415
416 public static List<String> validateParametersAccordingToSelectedEarnCodeRecordMethod(LeaveCalendarWSForm lcf) {
417 return validateParametersForLeaveEntry(lcf.getSelectedEarnCode(), lcf.getCalendarEntry(), lcf.getStartDate(), lcf.getEndDate(), lcf.getStartTime(), lcf.getEndTime(), lcf.getSelectedAssignment(), lcf.getLeaveCalendarDocument(), lcf.getLeaveBlockId());
418 }
419
420 public static List<String> validateParametersForLeaveEntry(String selectedEarnCode, CalendarEntries leaveCalEntry, String startDateS, String endDateS, String startTimeS, String endTimeS, String selectedAssignment, LeaveCalendarDocument leaveCalendarDocument, String leaveBlockId) {
421
422 java.sql.Date asOfDate = leaveCalEntry.getEndPeriodDate();
423
424 List<String> errors = new ArrayList<String>();
425 if (StringUtils.isNotBlank(selectedEarnCode)) {
426 EarnCode earnCode = TkServiceLocator.getEarnCodeService().getEarnCode(selectedEarnCode, asOfDate);
427
428 if(earnCode != null && earnCode.getRecordMethod().equalsIgnoreCase(TkConstants.EARN_CODE_TIME)) {
429
430 errors.addAll(LeaveCalendarValidationUtil.validateDates(startDateS, endDateS));
431 errors.addAll(LeaveCalendarValidationUtil.validateTimes(startTimeS, endTimeS));
432 if (errors.size() > 0) return errors;
433
434 Long startTime;
435 Long endTime;
436
437 startTime = TKUtils.convertDateStringToTimestampWithoutZone(startDateS, startTimeS).getTime();
438 endTime = TKUtils.convertDateStringToTimestampWithoutZone(endDateS, endTimeS).getTime();
439
440 errors.addAll(validateInterval(leaveCalEntry, startTime, endTime));
441 if (errors.size() > 0) return errors;
442
443 if (startTimeS == null) errors.add("The start time is blank.");
444 if (endTimeS == null) errors.add("The end time is blank.");
445 if (startTime - endTime == 0) errors.add("Start time and end time cannot be equivalent");
446
447 if (errors.size() > 0) return errors;
448
449 DateTime startTemp = new DateTime(startTime);
450 DateTime endTemp = new DateTime(endTime);
451
452 if (errors.size() == 0) {
453 Hours hrs = Hours.hoursBetween(startTemp, endTemp);
454 if (hrs.getHours() >= 24) errors.add("One leaveblock cannot exceed 24 hours");
455 }
456 if (errors.size() > 0) return errors;
457
458
459 AssignmentDescriptionKey assignKey = TkServiceLocator.getAssignmentService().getAssignmentDescriptionKey(selectedAssignment);
460 Assignment assign = TkServiceLocator.getAssignmentService().getAssignment(assignKey, new Date(startTime));
461
462 if ((startTime.compareTo(endTime) > 0 || endTime.compareTo(startTime) < 0)) {
463 errors.add("The time or date is not valid.");
464 }
465 if (errors.size() > 0) return errors;
466
467
468 boolean isRegularEarnCode = true;
469 errors.addAll(validateOverlap(startTime, endTime, startDateS, endTimeS,startTemp, endTemp, leaveCalEntry, leaveBlockId, isRegularEarnCode, earnCode.getRecordMethod()));
470 if (errors.size() > 0) return errors;
471 }
472 }
473 return errors;
474 }
475
476 public static List<String> validateInterval(CalendarEntries payCalEntry, Long startTime, Long endTime) {
477 List<String> errors = new ArrayList<String>();
478 LocalDateTime pcb_ldt = payCalEntry.getBeginLocalDateTime();
479 LocalDateTime pce_ldt = payCalEntry.getEndLocalDateTime();
480 DateTimeZone utz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
481 DateTime p_cal_b_dt = pcb_ldt.toDateTime(utz);
482 DateTime p_cal_e_dt = pce_ldt.toDateTime(utz);
483
484 Interval payInterval = new Interval(p_cal_b_dt, p_cal_e_dt);
485 if (errors.size() == 0 && !payInterval.contains(startTime)) {
486 errors.add("The start date/time is outside the pay period");
487 }
488 if (errors.size() == 0 && !payInterval.contains(endTime) && p_cal_e_dt.getMillis() != endTime) {
489 errors.add("The end date/time is outside the pay period");
490 }
491 return errors;
492 }
493
494 public static List<String> validateOverlap(Long startTime, Long endTime, String startDateS, String endTimeS, DateTime startTemp, DateTime endTemp, CalendarEntries calendarEntry, String lmLeaveBlockId, boolean isRegularEarnCode, String earnCodeType) {
495 List<String> errors = new ArrayList<String>();
496 Interval addedTimeblockInterval = new Interval(startTime, endTime);
497 List<Interval> dayInt = new ArrayList<Interval>();
498 String viewPrincipal = TKUser.getCurrentTargetPersonId();
499
500 dayInt.add(addedTimeblockInterval);
501 List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(viewPrincipal, calendarEntry);
502 List<String> assignmentKeys = new ArrayList<String>();
503 for(Assignment assign : assignments) {
504 assignmentKeys.add(assign.getAssignmentKey());
505 }
506
507 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForLeaveCalendar(viewPrincipal, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate(), assignmentKeys);
508 for (LeaveBlock leaveBlock : leaveBlocks) {
509 if (errors.size() == 0 && StringUtils.equals(earnCodeType, TkConstants.EARN_CODE_TIME) && leaveBlock.getBeginTimestamp() != null && leaveBlock.getEndTimestamp()!= null) {
510 Interval leaveBlockInterval = new Interval(leaveBlock.getBeginTimestamp().getTime(), leaveBlock.getEndTimestamp().getTime());
511 for (Interval intv : dayInt) {
512 if (isRegularEarnCode && leaveBlockInterval.overlaps(intv) && (lmLeaveBlockId == null || lmLeaveBlockId.compareTo(leaveBlock.getLmLeaveBlockId()) != 0)) {
513 errors.add("The leave block you are trying to add overlaps with an existing time block.");
514 }
515 }
516 }
517 }
518
519 return errors;
520 }
521 }