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 BigDecimal desiredUsage =null;
296 if(!TkConstants.EARN_CODE_TIME.equals(earnCodeObj.getRecordMethod())) {
297 desiredUsage = leaveAmount.multiply(new BigDecimal(daysSpan+1));
298 } else {
299 desiredUsage = leaveAmount.multiply(new BigDecimal(daysSpan));
300 }
301
302 if(desiredUsage.compareTo(availableBalance) > 0 ) {
303 errors.add("Requested leave amount " + desiredUsage.toString() + " is greater than available leave balance " + availableBalance.toString());
304 }
305 }
306 }
307
308 return errors;
309 }
310
311 public static List<String> validateDates(String startDateS, String endDateS) {
312 List<String> errors = new ArrayList<String>();
313 if (errors.size() == 0 && StringUtils.isEmpty(startDateS)) errors.add("The start date is blank.");
314 if (errors.size() == 0 && StringUtils.isEmpty(endDateS)) errors.add("The end date is blank.");
315 return errors;
316 }
317
318 public static List<String> validateTimes(String startTimeS, String endTimeS) {
319 List<String> errors = new ArrayList<String>();
320 if (errors.size() == 0 && startTimeS == null) errors.add("The start time is blank.");
321 if (errors.size() == 0 && endTimeS == null) errors.add("The end time is blank.");
322 return errors;
323 }
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 public static List<String> validateSpanningWeeks(LeaveCalendarWSForm lcf) {
383 List<String> errors = new ArrayList<String>();
384
385 boolean spanningWeeks = lcf.getSpanningWeeks().equalsIgnoreCase("y");
386
387 if (!spanningWeeks) {
388 Date startDate = TKUtils.formatDateString(lcf.getStartDate());
389 EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(lcf.getSelectedEarnCode(), startDate);
390 DateTime startTemp, endTemp;
391
392 if (ec != null && !ec.getRecordMethod().equals(LMConstants.RECORD_METHOD.TIME)) {
393 startTemp = new DateTime(startDate);
394 endTemp = new DateTime(TKUtils.formatDateString(lcf.getEndDate()));
395 } else {
396 startTemp = new DateTime(TKUtils.convertDateStringToTimestamp(lcf.getStartDate()).getTime());
397 endTemp = new DateTime(TKUtils.convertDateStringToTimestamp(lcf.getEndDate()).getTime());
398 }
399
400 boolean isOnlyWeekendSpan = true;
401 while ((startTemp.isBefore(endTemp) || startTemp.isEqual(endTemp)) && isOnlyWeekendSpan) {
402 if (startTemp.getDayOfWeek() != DateTimeConstants.SATURDAY && startTemp.getDayOfWeek() != DateTimeConstants.SUNDAY) {
403 isOnlyWeekendSpan = false;
404 }
405 startTemp = startTemp.plusDays(1);
406 }
407 if (isOnlyWeekendSpan) {
408 errors.add("Weekend day is selected, but include weekends checkbox is not checked");
409 }
410 }
411
412 return errors;
413 }
414
415 public static List<String> validateParametersAccordingToSelectedEarnCodeRecordMethod(LeaveCalendarWSForm lcf) {
416 return validateParametersForLeaveEntry(lcf.getSelectedEarnCode(), lcf.getCalendarEntry(), lcf.getStartDate(), lcf.getEndDate(), lcf.getStartTime(), lcf.getEndTime(), lcf.getSelectedAssignment(), lcf.getLeaveCalendarDocument(), lcf.getLeaveBlockId());
417 }
418
419 public static List<String> validateParametersForLeaveEntry(String selectedEarnCode, CalendarEntries leaveCalEntry, String startDateS, String endDateS, String startTimeS, String endTimeS, String selectedAssignment, LeaveCalendarDocument leaveCalendarDocument, String leaveBlockId) {
420
421 java.sql.Date asOfDate = leaveCalEntry.getEndPeriodDate();
422
423 List<String> errors = new ArrayList<String>();
424 if (StringUtils.isNotBlank(selectedEarnCode)) {
425 EarnCode earnCode = TkServiceLocator.getEarnCodeService().getEarnCode(selectedEarnCode, asOfDate);
426
427 if(earnCode != null && earnCode.getRecordMethod().equalsIgnoreCase(TkConstants.EARN_CODE_TIME)) {
428
429 errors.addAll(LeaveCalendarValidationUtil.validateDates(startDateS, endDateS));
430 errors.addAll(LeaveCalendarValidationUtil.validateTimes(startTimeS, endTimeS));
431 if (errors.size() > 0) return errors;
432
433 Long startTime;
434 Long endTime;
435
436 startTime = TKUtils.convertDateStringToTimestampWithoutZone(startDateS, startTimeS).getTime();
437 endTime = TKUtils.convertDateStringToTimestampWithoutZone(endDateS, endTimeS).getTime();
438
439 errors.addAll(validateInterval(leaveCalEntry, startTime, endTime));
440 if (errors.size() > 0) return errors;
441
442 if (startTimeS == null) errors.add("The start time is blank.");
443 if (endTimeS == null) errors.add("The end time is blank.");
444 if (startTime - endTime == 0) errors.add("Start time and end time cannot be equivalent");
445
446 if (errors.size() > 0) return errors;
447
448 DateTime startTemp = new DateTime(startTime);
449 DateTime endTemp = new DateTime(endTime);
450
451 if (errors.size() == 0) {
452 Hours hrs = Hours.hoursBetween(startTemp, endTemp);
453 if (hrs.getHours() >= 24) errors.add("One leaveblock cannot exceed 24 hours");
454 }
455 if (errors.size() > 0) return errors;
456
457
458 AssignmentDescriptionKey assignKey = TkServiceLocator.getAssignmentService().getAssignmentDescriptionKey(selectedAssignment);
459 Assignment assign = TkServiceLocator.getAssignmentService().getAssignment(assignKey, new Date(startTime));
460
461 if ((startTime.compareTo(endTime) > 0 || endTime.compareTo(startTime) < 0)) {
462 errors.add("The time or date is not valid.");
463 }
464 if (errors.size() > 0) return errors;
465
466
467 boolean isRegularEarnCode = true;
468 errors.addAll(validateOverlap(startTime, endTime, startDateS, endTimeS,startTemp, endTemp, leaveCalEntry, leaveBlockId, isRegularEarnCode, earnCode.getRecordMethod()));
469 if (errors.size() > 0) return errors;
470 }
471 }
472 return errors;
473 }
474
475 public static List<String> validateInterval(CalendarEntries payCalEntry, Long startTime, Long endTime) {
476 List<String> errors = new ArrayList<String>();
477 LocalDateTime pcb_ldt = payCalEntry.getBeginLocalDateTime();
478 LocalDateTime pce_ldt = payCalEntry.getEndLocalDateTime();
479 DateTimeZone utz = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
480 DateTime p_cal_b_dt = pcb_ldt.toDateTime(utz);
481 DateTime p_cal_e_dt = pce_ldt.toDateTime(utz);
482
483 Interval payInterval = new Interval(p_cal_b_dt, p_cal_e_dt);
484 if (errors.size() == 0 && !payInterval.contains(startTime)) {
485 errors.add("The start date/time is outside the pay period");
486 }
487 if (errors.size() == 0 && !payInterval.contains(endTime) && p_cal_e_dt.getMillis() != endTime) {
488 errors.add("The end date/time is outside the pay period");
489 }
490 return errors;
491 }
492
493 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) {
494 List<String> errors = new ArrayList<String>();
495 Interval addedTimeblockInterval = new Interval(startTime, endTime);
496 List<Interval> dayInt = new ArrayList<Interval>();
497 String viewPrincipal = TKUser.getCurrentTargetPersonId();
498
499 dayInt.add(addedTimeblockInterval);
500 List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(viewPrincipal, calendarEntry);
501 List<String> assignmentKeys = new ArrayList<String>();
502 for(Assignment assign : assignments) {
503 assignmentKeys.add(assign.getAssignmentKey());
504 }
505
506 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForLeaveCalendar(viewPrincipal, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate(), assignmentKeys);
507 for (LeaveBlock leaveBlock : leaveBlocks) {
508 if (errors.size() == 0 && StringUtils.equals(earnCodeType, TkConstants.EARN_CODE_TIME) && leaveBlock.getBeginTimestamp() != null && leaveBlock.getEndTimestamp()!= null) {
509 Interval leaveBlockInterval = new Interval(leaveBlock.getBeginTimestamp().getTime(), leaveBlock.getEndTimestamp().getTime());
510 for (Interval intv : dayInt) {
511 if (isRegularEarnCode && leaveBlockInterval.overlaps(intv) && (lmLeaveBlockId == null || lmLeaveBlockId.compareTo(leaveBlock.getLmLeaveBlockId()) != 0)) {
512 errors.add("The leave block you are trying to add overlaps with an existing time block.");
513 }
514 }
515 }
516 }
517
518 return errors;
519 }
520 }