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.tklm.time.clock.web;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.struts.action.ActionForm;
21  import org.apache.struts.action.ActionForward;
22  import org.apache.struts.action.ActionMapping;
23  import org.joda.time.DateTime;
24  import org.joda.time.DateTimeZone;
25  import org.joda.time.Interval;
26  import org.joda.time.LocalDate;
27  import org.joda.time.format.DateTimeFormat;
28  import org.joda.time.format.DateTimeFormatter;
29  import org.json.simple.JSONArray;
30  import org.json.simple.JSONValue;
31  import org.kuali.kpme.core.api.assignment.Assignment;
32  import org.kuali.kpme.core.api.assignment.AssignmentDescriptionKey;
33  import org.kuali.kpme.core.api.calendar.entry.CalendarEntry;
34  import org.kuali.kpme.core.api.earncode.EarnCodeContract;
35  import org.kuali.kpme.core.api.namespace.KPMENamespace;
36  import org.kuali.kpme.core.api.workarea.WorkArea;
37  import org.kuali.kpme.core.document.calendar.CalendarDocument;
38  import org.kuali.kpme.core.role.KPMERole;
39  import org.kuali.kpme.core.service.HrServiceLocator;
40  import org.kuali.kpme.core.util.HrConstants;
41  import org.kuali.kpme.core.util.HrContext;
42  import org.kuali.kpme.core.util.TKUtils;
43  import org.kuali.kpme.tklm.api.common.TkConstants;
44  import org.kuali.kpme.tklm.api.leave.block.LeaveBlock;
45  import org.kuali.kpme.tklm.api.time.clocklog.ClockLog;
46  import org.kuali.kpme.tklm.api.time.timeblock.TimeBlock;
47  import org.kuali.kpme.tklm.time.clocklog.exception.InvalidClockLogException;
48  import org.kuali.kpme.tklm.time.rules.lunch.department.DeptLunchRule;
49  import org.kuali.kpme.tklm.time.service.TkServiceLocator;
50  import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
51  import org.kuali.kpme.tklm.time.timesheet.web.TimesheetAction;
52  import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
53  import org.kuali.rice.core.api.config.property.ConfigContext;
54  import org.kuali.rice.core.api.util.Truth;
55  import org.kuali.rice.krad.exception.AuthorizationException;
56  import org.kuali.rice.krad.util.GlobalVariables;
57  
58  import javax.servlet.http.HttpServletRequest;
59  import javax.servlet.http.HttpServletResponse;
60  import java.math.BigDecimal;
61  import java.text.SimpleDateFormat;
62  import java.util.*;
63  
64  public class ClockAction extends TimesheetAction {
65  
66      public static final SimpleDateFormat SDF = new SimpleDateFormat("EEE, MMMM d yyyy HH:mm:ss, zzzz");
67      public static final String SEPERATOR = "[****]+";
68      public static final String DOCUMENT_NOT_INITIATE_ERROR = "New Timesheet document could not be found. Please initiate the document first.";
69      public static final String TIME_BLOCK_OVERLAP_ERROR = "User has already logged time for this clock period.";
70  
71  
72      @Override
73      protected void checkTKAuthorization(ActionForm form, String methodToCall) throws AuthorizationException {
74          super.checkTKAuthorization(form, methodToCall); // Checks for read access first.
75  
76          ClockActionForm clockActionForm = (ClockActionForm) form;
77  
78          String principalId = GlobalVariables.getUserSession().getPrincipalId();
79      	CalendarDocument timesheetDocument = TkServiceLocator.getTimesheetService().getTimesheetDocument(clockActionForm.getDocumentId());
80          // Check for write access to Timeblock.
81          if (StringUtils.equals(methodToCall, "clockAction") ||
82                  StringUtils.equals(methodToCall, "addTimeBlock") ||
83                  StringUtils.equals(methodToCall, "editTimeBlock") ||
84                  StringUtils.equals(methodToCall, "distributeTimeBlocks") ||
85                  StringUtils.equals(methodToCall, "saveNewTimeBlocks") ||
86                  StringUtils.equals(methodToCall, "deleteTimeBlock")) {
87              if (!HrServiceLocator.getHRPermissionService().canEditCalendarDocument(principalId, timesheetDocument)) {
88                  throw new AuthorizationException(GlobalVariables.getUserSession().getPrincipalId(), "ClockAction", "");
89              }
90          }
91      }
92  
93  
94      @Override
95      public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
96          ActionForward actionForward = super.execute(mapping, form, request, response);
97          
98          ClockActionForm clockActionForm = (ClockActionForm) form;
99          
100         TimesheetDocument timesheetDocument = clockActionForm.getTimesheetDocument();
101         clockActionForm.setShowClockButton(true);
102         if (timesheetDocument != null) {
103         	DateTimeFormatter formatter = DateTimeFormat.forPattern("MM/dd/yyyy");
104         	String fromDateString = formatter.print(timesheetDocument.getCalendarEntry().getBeginPeriodLocalDateTime());
105         	String toDateString = formatter.print(timesheetDocument.getCalendarEntry().getEndPeriodLocalDateTime());
106         	
107         	clockActionForm.setDistributionPeriod(fromDateString +" - "+toDateString);
108 	        if (!timesheetDocument.getDocumentHeader().getDocumentStatus().equals(HrConstants.ROUTE_STATUS.ENROUTE)
109 	                && !timesheetDocument.getDocumentHeader().getDocumentStatus().equals(HrConstants.ROUTE_STATUS.FINAL)) {
110         	
111 		        String targetPrincipalId = HrContext.getTargetPrincipalId();
112 		        if (targetPrincipalId != null) {
113 		            clockActionForm.setPrincipalId(targetPrincipalId);
114 		        }
115 		        
116 		        if (StringUtils.equals(GlobalVariables.getUserSession().getPrincipalId(), HrContext.getTargetPrincipalId())) {
117 		        	clockActionForm.setAssignmentDescriptions(timesheetDocument.getAssignmentDescriptions(true, LocalDate.now()));
118 		        } else {
119 		        	clockActionForm.setAssignmentDescriptions(timesheetDocument.getAssignmentDescriptionsOfApprovals(true));
120 		        }
121 		        
122 		        String ipAddress = TKUtils.getIPAddressFromRequest(request);
123 		        Map<String, String> assignmentMap = timesheetDocument.getAssignmentDescriptions(true, LocalDate.now());
124 		        String principalId = HrContext.getPrincipalId();
125 		        
126 		        if(StringUtils.equals(targetPrincipalId, principalId)){
127 		        	Map<String, String> assignmentDescriptionMap = new TreeMap<String, String>();
128 
129 		        	DateTime currentDateTime = new DateTime();
130 		        	for (Map.Entry<String, String> entry : assignmentMap.entrySet()) {
131 		        		Assignment assignment = timesheetDocument.getAssignment(AssignmentDescriptionKey.get(entry.getKey()), LocalDate.now());
132 		        		String allowActionFromInvalidLocation = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.ALLOW_CLOCKING_EMPLOYEE_FROM_INVALID_LOCATION);
133 		        		if(!Truth.strToBooleanIgnoreCase(allowActionFromInvalidLocation)) {
134 		        			boolean isInValid = TkServiceLocator.getClockLocationRuleService().isInvalidIPClockLocation(assignment.getGroupKeyCode(), assignment.getDept(), assignment.getWorkArea(), assignment.getPrincipalId(), assignment.getJobNumber(), ipAddress, currentDateTime.toLocalDate());
135 		        			if (!isInValid) {
136 		        				assignmentDescriptionMap.put(entry.getKey(), entry.getValue());
137 		        			}
138 		        		} else {
139                             assignmentDescriptionMap.put(entry.getKey(), entry.getValue());
140                         }
141 		        	}
142 		        	clockActionForm.setAssignmentDescriptions(assignmentDescriptionMap);
143 		        }else{
144 		        	clockActionForm.setAssignmentDescriptions(assignmentMap);
145 		        }
146                 
147 		        if (clockActionForm.getEditTimeBlockId() != null) {
148 		            clockActionForm.setCurrentTimeBlock(TkServiceLocator.getTimeBlockService().getTimeBlock(clockActionForm.getEditTimeBlockId()));
149 		        }
150 		        
151 		        ClockLog lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog(targetPrincipalId);
152 		        if (lastClockLog != null) {
153 		            DateTime lastClockDateTime = lastClockLog.getClockDateTime();
154 		            String lastClockZone = lastClockLog.getClockTimestampTimezone();
155 		            if (StringUtils.isEmpty(lastClockZone)) {
156 		                lastClockZone = TKUtils.getSystemTimeZone();
157 		            }
158 		            // zone will not be null. At this point is Valid or Exception.
159 		            // Exception would indicate bad data stored in the system. We can wrap this, but
160 		            // for now, the thrown exception is probably more valuable.
161 		            DateTimeZone zone = DateTimeZone.forID(lastClockZone);
162 		            DateTime clockWithZone = lastClockDateTime.withZone(zone);
163 		            clockActionForm.setLastClockTimeWithZone(clockWithZone.toDate());
164 		            clockActionForm.setLastClockTimestamp(lastClockDateTime.toDate());
165 		            clockActionForm.setLastClockAction(lastClockLog.getClockAction());
166 		        }
167 		        
168 		        if (lastClockLog == null || StringUtils.equals(lastClockLog.getClockAction(), TkConstants.CLOCK_OUT)) {
169 		            clockActionForm.setCurrentClockAction(TkConstants.CLOCK_IN);
170 		        } else {
171 		        	// KPME-3532 comment out the call to System Lunch Rule
172 		            //if (StringUtils.equals(lastClockLog.getClockAction(), TkConstants.LUNCH_OUT) && TkServiceLocator.getSystemLunchRuleService().isShowLunchButton()) {
173 		        	if (StringUtils.equals(lastClockLog.getClockAction(), TkConstants.LUNCH_OUT)) {
174 		                clockActionForm.setCurrentClockAction(TkConstants.LUNCH_IN);
175 		                clockActionForm.setShowClockButton(false);
176 		            } else {
177 		                clockActionForm.setCurrentClockAction(TkConstants.CLOCK_OUT);
178 		            }
179 		            // if the current clock action is clock out, displays only the clocked-in assignment
180 		            String selectedAssignment = new AssignmentDescriptionKey(lastClockLog.getGroupKeyCode(), lastClockLog.getJobNumber(), lastClockLog.getWorkArea(), lastClockLog.getTask()).toAssignmentKeyString();
181 		            clockActionForm.setSelectedAssignment(selectedAssignment);
182 		            Assignment assignment = timesheetDocument.getAssignment(AssignmentDescriptionKey.get(selectedAssignment), LocalDate.now());
183 		            Map<String, String> assignmentDesc = HrServiceLocator.getAssignmentService().getAssignmentDescriptions(assignment);
184 		            clockActionForm.setAssignmentDescriptions(assignmentDesc);
185 		        }
186 		        
187 		        // KPME-2772 This issue happens when target employee is clocked out and there are multiple assignments 
188 		        // because when there are more than one assignment, it uses "default" assignment on the clock action form,
189 		        // which never gets set above.  When target employee is clocked in, there is always one assignment, so it works.
190 		        // The solution is to add else if statement and set clockButtonEnabled flag to true when target employee is clocked out.  
191 		        // Since all the assignments for target employee are already filtered by the time it gets here (i.e, only showing the ones
192 		        // that approver has permission to view for), we will just enable buttons.  When target employee is clocked in, it gets
193 		        // handled in else statement
194 		        if (StringUtils.equals(GlobalVariables.getUserSession().getPrincipalId(), HrContext.getTargetPrincipalId())) {
195 		        	clockActionForm.setClockButtonEnabled(true);
196                     clockActionForm.setProxyClockAction(false);
197 		        } else {
198                     clockActionForm.setProxyClockAction(true);
199                     clockActionForm.setProxyClockActionTargetUser(HrContext.getTargetName());
200 		        	boolean isApproverOrReviewerForCurrentAssignment = false;
201 		        	String selectedAssignment = StringUtils.EMPTY;
202 		        	if (clockActionForm.getAssignmentDescriptions() != null) {
203 		        		if (clockActionForm.getAssignmentDescriptions().size() == 1) {
204 		        			for (String assignment : clockActionForm.getAssignmentDescriptions().keySet()) {
205 		        				selectedAssignment = assignment;
206 		        			}
207 		        		} else {
208 		        			selectedAssignment = clockActionForm.getSelectedAssignment();
209 		        		}
210 		        	}
211 		        	
212 		        	if(StringUtils.isNotBlank(selectedAssignment)) {
213 		        		Assignment assignment = HrServiceLocator.getAssignmentService().getAssignmentForTargetPrincipal(AssignmentDescriptionKey.get(selectedAssignment), LocalDate.now());
214 		        		if (assignment != null) {
215 		        			Long workArea = assignment.getWorkArea();
216                             String dept = assignment.getJob().getDept();
217                             String groupKeyCode = assignment.getJob().getGroupKeyCode();
218                             //String location = assignment.getJob().getGroupKey().getLocation().getLocation();
219 
220 		        			/*String principalId = HrContext.getPrincipalId();*/
221 		        			DateTime startOfToday = LocalDate.now().toDateTimeAtStartOfDay();
222 
223                             String location = HrServiceLocator.getHrGroupKeyService().getHrGroupKey(groupKeyCode, startOfToday.toLocalDate()).getLocationId();
224 
225 
226                             isApproverOrReviewerForCurrentAssignment =
227                                     HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.APPROVER.getRoleName(), workArea, startOfToday)
228 
229                                     || HrServiceLocator.getKPMERoleService().principalHasRole(principalId, KPMENamespace.KPME_TK.getNamespaceCode(), KPMERole.TIME_SYSTEM_ADMINISTRATOR.getRoleName(), startOfToday)
230                                     || HrServiceLocator.getKPMERoleService().principalHasRoleInLocation(principalId, KPMENamespace.KPME_TK.getNamespaceCode(), KPMERole.TIME_LOCATION_ADMINISTRATOR.getRoleName(), location, startOfToday)
231 
232 		        					|| HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.APPROVER_DELEGATE.getRoleName(), workArea, startOfToday)
233 		        					|| HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.REVIEWER.getRoleName(), workArea, startOfToday)
234                                     || HrServiceLocator.getKPMERoleService().principalHasRoleInDepartment(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.PAYROLL_PROCESSOR.getRoleName(), dept, groupKeyCode, startOfToday)
235                                     || HrServiceLocator.getKPMERoleService().principalHasRoleInDepartment(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.PAYROLL_PROCESSOR_DELEGATE.getRoleName(), dept, groupKeyCode, startOfToday);
236 		        		}
237 		        	} else {
238                         if (CollectionUtils.isNotEmpty(clockActionForm.getAssignmentDescriptions().entrySet())) {
239                             //only assignments that target user and logged in user have access to should be in this list
240                             isApproverOrReviewerForCurrentAssignment = true;
241                         }
242 
243                     }
244 		        	clockActionForm.setClockButtonEnabled(isApproverOrReviewerForCurrentAssignment);
245 		        }
246 		        
247 		        // KPME-3532 comment out the call to System Lunch Rule - system lunch is off always now so that the button
248 		        // should never show up - set the show button to false
249 		        //clockActionForm.setShowLunchButton(TkServiceLocator.getSystemLunchRuleService().isShowLunchButton());
250 		        clockActionForm.setShowLunchButton(false);
251 		        assignShowDistributeButton(clockActionForm);
252 		        
253 		        if (clockActionForm.isShowLunchButton()) {
254 		            // We don't need to worry about the assignments and lunch rules
255 		            // if the global lunch rule is turned off.
256 		
257 		            // Check for presence of department lunch rule.
258 		            Map<String, Boolean> assignmentDeptLunchRuleMap = new HashMap<String, Boolean>();
259 		            for (Assignment a : timesheetDocument.getAssignmentMap().get(LocalDate.now())) {
260 	                    String key = AssignmentDescriptionKey.getAssignmentKeyString(a);
261 	                    DeptLunchRule deptLunchRule = TkServiceLocator.getDepartmentLunchRuleService().getDepartmentLunchRule(a.getDept(), a.getWorkArea(), clockActionForm.getPrincipalId(), a.getJobNumber(), a.getGroupKeyCode(), LocalDate.now());
262 	                    assignmentDeptLunchRuleMap.put(key, deptLunchRule != null);
263 	                }
264 		            clockActionForm.setAssignmentLunchMap(assignmentDeptLunchRuleMap);
265 		        }
266 	        } else {
267 	        	clockActionForm.setErrorMessage("Your current timesheet is already submitted for Approval. Clock action is not allowed on this timesheet.");
268 	        }
269         }
270         
271         return actionForward;
272     }
273     
274     public void assignShowDistributeButton(ClockActionForm caf) {
275     	caf.setShowDistrubuteButton(false);
276     	
277     	TimesheetDocument timesheetDocument = caf.getTimesheetDocument();
278     	List<Assignment> listOfAssignments = caf.getTimesheetDocument().getAssignmentMap().get(LocalDate.now());
279     	if(listOfAssignments == null || listOfAssignments.isEmpty()) {
280     		listOfAssignments = caf.getTimesheetDocument().getAssignmentMap().get(timesheetDocument.getDocEndDate());
281     	}
282         if (timesheetDocument != null && listOfAssignments != null) {
283             int eligibleAssignmentCount = 0;
284             for (Assignment a : listOfAssignments) {
285                 WorkArea aWorkArea = HrServiceLocator.getWorkAreaService().getWorkArea(a.getWorkArea(), timesheetDocument.getDocEndDate());
286                 if(aWorkArea != null && aWorkArea.isHrsDistributionF()) {
287                     eligibleAssignmentCount++;
288                 }
289 
290                 // Only show the distribute button if there is more than one eligible assignment
291                 if (eligibleAssignmentCount > 1) {
292                     caf.setShowDistrubuteButton(true);
293                     break;
294                 }
295             }
296         }
297     }
298     
299     public ActionForward clockAction(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception, InvalidClockLogException {
300         ClockActionForm caf = (ClockActionForm) form;
301         DateTime currentDateTime = new DateTime();
302         // TODO: Validate that clock action is valid for this user
303         // TODO: this needs to be integrated with the error tag
304         if (StringUtils.isBlank(caf.getSelectedAssignment())) {
305             caf.setErrorMessage("No assignment selected.");
306             return mapping.findForward("basic");
307         }
308         String pId = HrContext.getTargetPrincipalId();
309         ClockLog previousClockLog = TkServiceLocator.getClockLogService().getLastClockLog(pId);
310         if(previousClockLog != null && StringUtils.equals(caf.getCurrentClockAction(), previousClockLog.getClockAction())){
311         	caf.setErrorMessage("The operation is already performed.");
312             return mapping.findForward("basic");
313         }
314         String ip = TKUtils.getIPAddressFromRequest(request);
315 
316         Assignment assignment = caf.getTimesheetDocument().getAssignment(AssignmentDescriptionKey.get(caf.getSelectedAssignment()), LocalDate.now());
317         Long jbNum = assignment.getJobNumber();
318         Long wa = assignment.getWorkArea();
319         String dept= assignment.getDept();
320         String grpKeyCode = assignment.getGroupKeyCode();
321 
322         try {
323             if (StringUtils.equals(pId, HrContext.getPrincipalId())) {
324                 TkServiceLocator.getClockLogService().invalidIpCheck(grpKeyCode, dept, wa, pId, jbNum, ip, LocalDate.now(), false);
325             }
326         }
327         catch (InvalidClockLogException e)
328         {
329             caf.setErrorMessage("Could not take the action as Action taken from  "+ ip + ",  is not a valid IP address.");
330             return mapping.findForward("basic");
331         }
332 
333         // check if User takes action from Valid location.
334         if (pId.equalsIgnoreCase(HrContext.getPrincipalId())) {
335 	        String allowActionFromInvalidLocaiton = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.ALLOW_CLOCKING_EMPLOYEE_FROM_INVALID_LOCATION);
336 	        if(StringUtils.equals(allowActionFromInvalidLocaiton, "false")) {
337 		        boolean isInValid = TkServiceLocator.getClockLocationRuleService().isInvalidIPClockLocation(assignment.getGroupKeyCode(), assignment.getDept(), assignment.getWorkArea(), assignment.getPrincipalId(), assignment.getJobNumber(), ip, currentDateTime.toLocalDate());
338 		        if(isInValid){
339 		        	caf.setErrorMessage("Could not take the action as Action taken from  "+ ip + ",  is not a valid IP address.");
340 		            return mapping.findForward("basic");
341 		        }
342 	        }
343         }
344         
345         List<Assignment> lstAssingmentAsOfToday = HrServiceLocator.getAssignmentService().getAssignments(pId, LocalDate.now());
346         boolean foundValidAssignment = false;
347         for(Assignment assign : lstAssingmentAsOfToday){
348         	if((assign.getJobNumber().compareTo(assignment.getJobNumber()) ==0) &&
349         		(assign.getWorkArea().compareTo(assignment.getWorkArea()) == 0) &&
350         		(assign.getTask().compareTo(assignment.getTask()) == 0)){
351         		foundValidAssignment = true;
352         		break;
353         	}
354         }
355         
356         if(!foundValidAssignment){
357         	caf.setErrorMessage("Assignment is not effective as of today");
358         	return mapping.findForward("basic");
359         }
360         
361         LocalDate beginDate = LocalDate.now();
362         DateTime clockTimeWithGraceRule = new DateTime(beginDate.toDateTimeAtCurrentTime());
363         clockTimeWithGraceRule = TkServiceLocator.getGracePeriodService().processGracePeriodRule(clockTimeWithGraceRule, beginDate);
364        
365         // validate if there's any overlapping with existing time blocks
366         if (StringUtils.equals(caf.getCurrentClockAction(), TkConstants.CLOCK_IN) || StringUtils.equals(caf.getCurrentClockAction(), TkConstants.LUNCH_IN) || StringUtils.equals(caf.getCurrentClockAction(), TkConstants.CLOCK_OUT) || StringUtils.equals(caf.getCurrentClockAction(), TkConstants.LUNCH_OUT)) {
367             ClockLog lastLog = null;
368             if (StringUtils.equals(caf.getCurrentClockAction(), TkConstants.LUNCH_IN)) {
369                 lastLog = TkServiceLocator.getClockLogService().getLastClockLog(pId, TkConstants.LUNCH_OUT);
370             } else if (StringUtils.equals(caf.getCurrentClockAction(), TkConstants.CLOCK_IN)) {
371                 lastLog = TkServiceLocator.getClockLogService().getLastClockLog(pId);
372             }else if (StringUtils.equals(caf.getCurrentClockAction(), TkConstants.LUNCH_OUT)) {
373                 lastLog = TkServiceLocator.getClockLogService().getLastClockLog(pId, TkConstants.LUNCH_IN);
374             } else if (StringUtils.equals(caf.getCurrentClockAction(), TkConstants.CLOCK_OUT)) {
375                 lastLog = TkServiceLocator.getClockLogService().getLastClockLog(pId);
376             }
377             if (lastLog != null) {
378                 if (lastLog.getClockDateTime().withMillisOfSecond(0).equals(clockTimeWithGraceRule.withMillisOfSecond(0))) {
379                     clockTimeWithGraceRule = clockTimeWithGraceRule.withMillisOfSecond(lastLog.getClockDateTime().getMillisOfSecond() + 1);
380                 }
381             }
382             Set<String> regularEarnCodes = new HashSet<String>();
383              for(Assignment assign : caf.getTimesheetDocument().getAllAssignments()) {
384                  regularEarnCodes.add(assign.getJob().getPayTypeObj().getRegEarnCode());
385              }
386         	 List<TimeBlock> tbList = caf.getTimesheetDocument().getTimeBlocks();
387 	         for(TimeBlock tb : tbList) {
388 	        	 String earnCode = tb.getEarnCode();
389 	        	 boolean isRegularEarnCode = StringUtils.equals(assignment.getJob().getPayTypeObj().getRegEarnCode(), earnCode);
390 	        	 EarnCodeContract earnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(earnCode, caf.getTimesheetDocument().getAsOfDate());
391 	        	 if(earnCodeObj != null && HrConstants.EARN_CODE_TIME.equals(earnCodeObj.getEarnCodeType())) {
392 	        		 Interval clockInterval = new Interval(tb.getBeginDateTime(), tb.getEndDateTime());
393 	        		 if((isRegularEarnCode || regularEarnCodes.contains(earnCodeObj.getEarnCode())) && clockInterval.contains(clockTimeWithGraceRule.getMillis())) {
394 	        			 caf.setErrorMessage(TIME_BLOCK_OVERLAP_ERROR);
395 	        			 return mapping.findForward("basic");
396 	        		 }else if(StringUtils.equals(caf.getCurrentClockAction(),TkConstants.CLOCK_OUT) || StringUtils.equals(caf.getCurrentClockAction(), TkConstants.LUNCH_OUT) && (isRegularEarnCode || regularEarnCodes.contains(earnCodeObj.getEarnCode()))){
397 	        			 Interval currentClockInterval = new Interval(lastLog.getClockDateTime(),clockTimeWithGraceRule);
398 	        			 if(clockInterval.contains(currentClockInterval) || currentClockInterval.contains(clockInterval)){
399 		        			 caf.setErrorMessage(TIME_BLOCK_OVERLAP_ERROR);
400 		        			 return mapping.findForward("basic");
401 	        			 }
402 	        		 }
403 	        	 }
404 	         }
405         }
406         
407         
408         // for clock out and lunch out actions, check if the current time and last clock log time is on two different calendar entries,
409         // if they are, we need to clock out the employee at the endDatTime (in employee's time zone) of the last calendar entry,
410         // and clock employee back in at the beginDateTime (in employee's time zone) of the new calendar entry
411         // then clock him out again at current time. 
412         if (StringUtils.equals(caf.getCurrentClockAction(), TkConstants.CLOCK_OUT) || StringUtils.equals(caf.getCurrentClockAction(), TkConstants.LUNCH_OUT)) {
413             ClockLog lastLog = null;
414             String inAction = "";
415             String outAction = "";
416           	if (StringUtils.equals(caf.getCurrentClockAction(), TkConstants.LUNCH_OUT)) {
417                lastLog = TkServiceLocator.getClockLogService().getLastClockLog(pId, TkConstants.CLOCK_IN);
418                inAction = TkConstants.LUNCH_IN;
419                outAction = TkConstants.LUNCH_OUT;
420             } else if (StringUtils.equals(caf.getCurrentClockAction(), TkConstants.CLOCK_OUT)) {
421                lastLog = TkServiceLocator.getClockLogService().getLastClockLog(pId);
422                inAction = TkConstants.CLOCK_IN;
423                outAction = TkConstants.CLOCK_OUT;
424             }     
425 
426         	TimesheetDocument previousTimeDoc = TkServiceLocator.getTimesheetService().getTimesheetDocument(previousClockLog.getDocumentId());
427 
428             //uses a different method to get previous timesheet if missed punch was used to create a clock log
429             // on the previous timesheet
430             if (StringUtils.equals(((ClockActionForm) form).getDocumentId(),previousTimeDoc.getDocumentId())) {
431                 DateTime currentCalendarEntryBeginDate = ((ClockActionForm) form).getTimesheetDocument().getCalendarEntry().getBeginPeriodFullDateTime();
432                 DateTime currentCalendarEntryEndDate = ((ClockActionForm) form).getTimesheetDocument().getCalendarEntry().getEndPeriodFullDateTime();
433 
434                 Interval currentCalendarInterval = new Interval(currentCalendarEntryBeginDate,currentCalendarEntryEndDate);
435                 DateTimeZone userTimeZone = DateTimeZone.forID(HrServiceLocator.getTimezoneService().getUserTimezone(pId));
436         		DateTimeZone systemTimeZone = TKUtils.getSystemDateTimeZone();
437                 
438                 if (!currentCalendarInterval.contains(TKUtils.convertDateTimeToDifferentTimezone(previousClockLog.getClockDateTime(), systemTimeZone, userTimeZone))) {
439                     TimesheetDocumentHeader prevTdh = TkServiceLocator.getTimesheetDocumentHeaderService().getPreviousDocumentHeader(pId,currentCalendarEntryBeginDate);
440                     if (prevTdh != null) {
441                         previousTimeDoc =  TkServiceLocator.getTimesheetService().getTimesheetDocument(prevTdh.getDocumentId());
442                     }
443                 }
444             }
445 
446         	if(previousTimeDoc != null) {
447                 CalendarEntry previousCalEntry = previousTimeDoc.getCalendarEntry();
448 	        	DateTime previousEndPeriodDateTime = previousCalEntry.getEndPeriodFullDateTime();
449         		// use the user's time zone and the system time zone to figure out the system time of endPeriodDatTime in the user's timezone
450                 DateTimeZone userTimezone = DateTimeZone.forID(HrServiceLocator.getTimezoneService().getUserTimezone(pId));
451         		DateTimeZone systemTimeZone = TKUtils.getSystemDateTimeZone();
452         		// time to use to create the out clock log
453                 DateTime outLogDateTime = TKUtils.convertTimeForDifferentTimeZone(previousEndPeriodDateTime, systemTimeZone, userTimezone);
454 	        	
455 	        	// if current time is after the end time of previous calendar entry, it means the clock action covers two calendar entries
456             	if(currentDateTime.isAfter(outLogDateTime.getMillis())) {
457 	                CalendarEntry nextCalendarEntry = HrServiceLocator.getCalendarEntryService().getNextCalendarEntryByCalendarId(previousCalEntry.getHrCalendarId(), previousCalEntry);
458 	                DateTime beginNextPeriodDateTime = nextCalendarEntry.getBeginPeriodFullDateTime();
459 	                // time to use to create the CI clock log
460 	                DateTime inLogDateTime = TKUtils.convertDateTimeToDifferentTimezone(beginNextPeriodDateTime, userTimezone, systemTimeZone);
461 
462                     TimesheetDocumentHeader nextTdh = TkServiceLocator.getTimesheetDocumentHeaderService()
463 	                		.getDocumentHeader(pId, nextCalendarEntry.getBeginPeriodFullDateTime(), nextCalendarEntry.getEndPeriodFullDateTime());
464 	                if(nextTdh == null) {
465 	                	 caf.setErrorMessage(DOCUMENT_NOT_INITIATE_ERROR);
466 		            	 return mapping.findForward("basic");
467 	                }
468 	                TimesheetDocument nextTimeDoc = TkServiceLocator.getTimesheetService().getTimesheetDocument(nextTdh.getDocumentId());
469 	                if(nextTimeDoc == null) {
470 	                	 caf.setErrorMessage(DOCUMENT_NOT_INITIATE_ERROR);
471 		            	 return mapping.findForward("basic");
472 	                }
473 	        	    // validate if there's any overlapping with existing time blocks
474 	        		if (lastLog != null) {
475 	        			// validation with previous calendar entry
476 	        			// the datetime for the new clock log that's about to be created with grace period rule applied
477 	        			DateTime endDateTime = new DateTime(outLogDateTime.getMillis());
478 	        			endDateTime = TkServiceLocator.getGracePeriodService().processGracePeriodRule(outLogDateTime, previousCalEntry.getBeginPeriodFullDateTime().toLocalDate());
479 //	        			if(gpRuleConfig!=null && StringUtils.equals(gpRuleConfig, TkConstants.GRACE_PERIOD_RULE_CONFIG.CLOCK_ENTRY)){
480 //	        				endDateTime = TkServiceLocator.getGracePeriodService().processGracePeriodRule(outLogDateTime, previousCalEntry.getBeginPeriodFullDateTime().toLocalDate());
481 //	        			}
482 	        			if (lastLog.getClockDateTime().withMillisOfSecond(0).equals(endDateTime.withMillisOfSecond(0))) {
483                             endDateTime = endDateTime.withMillisOfSecond(lastLog.getClockDateTime().getMillisOfSecond() + 1);
484                         }
485                         boolean validation = this.validateOverlapping(previousTimeDoc.getAsOfDate(), previousTimeDoc.getTimeBlocks(), lastLog.getClockDateTime(), endDateTime,assignment);
486 	        			if(!validation) {
487 	        				 caf.setErrorMessage(TIME_BLOCK_OVERLAP_ERROR);
488    		            		 return mapping.findForward("basic");
489 	        			}
490 	        			
491 	        			// validation with the next calendar entry
492 		   	             // the datetime for the new clock log that's about to be created with grace period rule applied
493 	        			endDateTime = currentDateTime;
494 	        			endDateTime = TkServiceLocator.getGracePeriodService().processGracePeriodRule(currentDateTime, nextCalendarEntry.getBeginPeriodFullDateTime().toLocalDate());
495 	        			
496 //	        			if(gpRuleConfig!=null && StringUtils.equals(gpRuleConfig, TkConstants.GRACE_PERIOD_RULE_CONFIG.CLOCK_ENTRY)){
497 //	        				endDateTime = TkServiceLocator.getGracePeriodService().processGracePeriodRule(currentDateTime, nextCalendarEntry.getBeginPeriodFullDateTime().toLocalDate());
498 //	        			}
499                         if (lastLog.getClockDateTime().withMillisOfSecond(0).equals(endDateTime.withMillisOfSecond(0))) {
500                             endDateTime = endDateTime.withMillisOfSecond(lastLog.getClockDateTime().getMillisOfSecond() + 1);
501                         }
502                         validation = this.validateOverlapping(nextTimeDoc.getAsOfDate(), nextTimeDoc.getTimeBlocks(), inLogDateTime, endDateTime,assignment);
503 	        			if(!validation) {
504 	        				 caf.setErrorMessage(TIME_BLOCK_OVERLAP_ERROR);
505   		            		 return mapping.findForward("basic");
506 	        			}
507 		            } 
508 	        		// create co, ci and co clock logs and assign the last co clock log to the form
509 	                // clock out employee at the end of the previous pay period
510 	                ClockLog outLog = TkServiceLocator.getClockLogService().processClockLog(pId, previousTimeDoc.getDocumentId(), outLogDateTime, assignment, previousCalEntry, ip,
511 	                		previousEndPeriodDateTime.toLocalDate(), outAction, true);
512 	                
513 	                // clock in employee at the begin of the next pay period
514 	                ClockLog inLog = TkServiceLocator.getClockLogService().processClockLog(pId, nextTimeDoc.getDocumentId(), inLogDateTime, assignment, nextCalendarEntry, ip,
515 	                		beginNextPeriodDateTime.toLocalDate(), inAction, true);
516 	                
517 	                // finally clock out employee at current time
518 	                ClockLog finalOutLog = TkServiceLocator.getClockLogService().processClockLog(pId, nextTimeDoc.getDocumentId(), currentDateTime, assignment, nextCalendarEntry, ip,
519 	                		currentDateTime.toLocalDate(), caf.getCurrentClockAction(), true);
520 	                
521 	                // add 5 seconds to clock out log's timestamp so it will be found as the latest clock action
522                     ClockLog.Builder clBuilder = ClockLog.Builder.create(finalOutLog);
523                     clBuilder.setCreateTime(finalOutLog.getCreateTime().plusSeconds(5));
524 	                finalOutLog = TkServiceLocator.getClockLogService().saveClockLog(clBuilder.build());
525 	                
526 	                caf.setClockLog(finalOutLog);  
527 	                return mapping.findForward("basic");
528 	                
529 	            } else {	// covers the scenario that user clocks out on the same calendar entry
530 	                if (lastLog != null) {
531 		   	            // the datetime for the new clock log that's about to be created with grace period rule applied
532 	                	DateTime endDateTime  = new DateTime();
533 	                	endDateTime = TkServiceLocator.getGracePeriodService().processGracePeriodRule(endDateTime, caf.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate());
534 	                	if (lastLog.getClockDateTime().withMillisOfSecond(0).equals(endDateTime.withMillisOfSecond(0))) {
535                             endDateTime = endDateTime.withMillisOfSecond(lastLog.getClockDateTime().getMillisOfSecond() + 1);
536                         }
537 	                	
538 //	                	if(gpRuleConfig!=null && gpRuleConfig.equals("CLOCK")){
539 //	                		endDateTime = TkServiceLocator.getGracePeriodService().processGracePeriodRule(endDateTime, caf.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate());
540 //                            if (lastLog.getClockDateTime().withMillisOfSecond(0).equals(endDateTime.withMillisOfSecond(0))) {
541 //                                endDateTime = endDateTime.withMillisOfSecond(lastLog.getClockDateTime().getMillisOfSecond() + 1);
542 //                            }
543 //                        }
544 		   	         	boolean validation = this.validateOverlapping(caf.getTimesheetDocument().getAsOfDate(), caf.getTimesheetDocument().getTimeBlocks(), lastLog.getClockDateTime(), endDateTime,assignment);
545 	        			if(!validation) {
546 	        				 caf.setErrorMessage(TIME_BLOCK_OVERLAP_ERROR);
547 			            		 return mapping.findForward("basic");
548 	        			}
549 	                }
550 	        	}
551     		}
552     	}
553         // create clock log 
554         ClockLog clockLog = TkServiceLocator.getClockLogService().processClockLog(pId, caf.getTimesheetDocument().getDocumentId(), new DateTime(), assignment, caf.getCalendarEntry(), ip,
555         		beginDate, caf.getCurrentClockAction(), true);
556 
557         caf.setClockLog(clockLog);
558         return mapping.findForward("basic"); 
559         
560     }
561 
562     public boolean validateOverlapping(LocalDate asOfDate, List<TimeBlock> tbList, DateTime beginDateTime, DateTime endDateTime, Assignment assignment) {
563     	Interval clockInterval = new Interval(beginDateTime, endDateTime);
564     	if(clockInterval != null) {
565 	    	for(TimeBlock tb : tbList) {
566 	        	 String earnCode = tb.getEarnCode();
567 	        	 boolean isRegularEarnCode = StringUtils.equals(assignment.getJob().getPayTypeObj().getRegEarnCode(),earnCode);
568 	        	 EarnCodeContract earnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(earnCode, asOfDate);
569 	        	 if(isRegularEarnCode && earnCodeObj != null && HrConstants.EARN_CODE_TIME.equals(earnCodeObj.getEarnCodeType())) {
570 	            	 if(clockInterval.overlaps(new Interval(tb.getBeginDateTime(), tb.getEndDateTime()))) {
571 	            		 return false;
572 	            	 }
573 	        	 }
574 	    	}
575 	    	return true;
576     	}
577 		return false;
578     }
579     
580     
581     public ActionForward distributeTimeBlocks(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
582         ClockActionForm caf = (ClockActionForm) form;
583         caf.findTimeBlocksToDistribute();
584         return mapping.findForward("tb");
585     }
586 
587 
588     public ActionForward editTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
589         ClockActionForm caf = (ClockActionForm) form;
590         TimeBlock tb = caf.getCurrentTimeBlock();
591         caf.setCurrentAssignmentKey(tb.getAssignmentKey());
592         caf.populateAssignmentsForSelectedTimeBlock(tb);
593         ActionForward forward = mapping.findForward("et");
594 
595         return new ActionForward(forward.getPath() + "?editTimeBlockId=" + tb.getTkTimeBlockId().toString());
596 
597     }
598     public ActionForward addTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
599         ClockActionForm caf = (ClockActionForm) form;
600         TimeBlock currentTb = caf.getCurrentTimeBlock();
601         List<TimeBlock> newTimeBlocks = caf.getTimesheetDocument().getTimeBlocks();
602         List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(caf.getTimesheetDocument().getTimeBlocks().size());
603         for (TimeBlock tb : caf.getTimesheetDocument().getTimeBlocks()) {
604             referenceTimeBlocks.add(TimeBlock.Builder.create(tb).build());
605         }
606         //call persist method that only saves added/deleted/changed timeblocks
607         TkServiceLocator.getTimeBlockService().saveOrUpdateTimeBlocks(referenceTimeBlocks, newTimeBlocks, HrContext.getPrincipalId());
608 
609         ActionForward forward = mapping.findForward("et");
610 
611         return new ActionForward(forward.getPath() + "?editTimeBlockId=" + currentTb.getTkTimeBlockId().toString());
612     }
613     
614     public ActionForward saveNewTimeBlocks(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response){
615 		ClockActionForm caf = (ClockActionForm)form;
616 		String tbId = caf.getTbId();
617 		String timesheetDocId = caf.getTsDocId();
618 
619 		String[] assignments = caf.getNewAssignDesCol().split(SEPERATOR);
620 		String[] beginDates = caf.getNewBDCol().split(SEPERATOR);
621 		String[] beginTimes = caf.getNewBTCol().split(SEPERATOR);
622 		String[] endDates = caf.getNewEDCol().split(SEPERATOR);
623 		String[] endTimes = caf.getNewETCol().split(SEPERATOR);
624 		String[] hrs = caf.getNewHrsCol().split(SEPERATOR);
625 		String earnCode = TkServiceLocator.getTimeBlockService().getTimeBlock(tbId).getEarnCode();
626 		TimesheetDocument tsDoc = TkServiceLocator.getTimesheetService().getTimesheetDocument(timesheetDocId);
627 		List<TimeBlock> newTbList = new ArrayList<TimeBlock>();
628 		if(tsDoc != null) {
629 			for(TimeBlock oldTB : tsDoc.getTimeBlocks()) {
630 				if(!(oldTB.getTkTimeBlockId().compareTo(tbId) == 0)) {
631 					newTbList.add(oldTB);
632 				}
633 			}
634 		}
635 		for(int i = 0; i < hrs.length; i++) {
636 			BigDecimal hours = new BigDecimal(hrs[i]);
637 			DateTime beginDateTime = TKUtils.convertDateStringToDateTime(beginDates[i], beginTimes[i]);
638 			DateTime endDateTime = TKUtils.convertDateStringToDateTime(endDates[i], endTimes[i]);
639 			String assignString = assignments[i];
640 			Assignment assignment = HrServiceLocator.getAssignmentService().getAssignment(assignString);
641 			
642 			TimeBlock tb = TkServiceLocator.getTimeBlockService().createTimeBlock(tsDoc.getPrincipalId(), tsDoc.getDocumentId(), beginDateTime, endDateTime, assignment, earnCode, hours,BigDecimal.ZERO, false, false, HrContext.getPrincipalId());
643 			newTbList.add(tb);
644 		}
645 		TkServiceLocator.getTimeBlockService().resetTimeHourDetail(newTbList);
646 
647 		TkServiceLocator.getTkRuleControllerService().applyRules(TkConstants.ACTIONS.ADD_TIME_BLOCK, newTbList, Collections.<LeaveBlock>emptyList(), tsDoc.getCalendarEntry(), tsDoc, tsDoc.getPrincipalId());
648 
649         TkServiceLocator.getTimeBlockService().saveTimeBlocks(newTbList);
650 		TimeBlock oldTB = TkServiceLocator.getTimeBlockService().getTimeBlock(tbId);
651 		TkServiceLocator.getTimeBlockService().deleteTimeBlockAndHandleMissedPunch(oldTB, true);
652 		return mapping.findForward("basic");
653 	}
654 	
655 	public ActionForward validateNewTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response){
656 		ClockActionForm caf = (ClockActionForm)form;
657 		String tbId = caf.getTbId();
658 		String[] assignments = caf.getNewAssignDesCol().split(SEPERATOR);
659 		String[] beginDates = caf.getNewBDCol().split(SEPERATOR);
660 		String[] beginTimes = caf.getNewBTCol().split(SEPERATOR);
661 		String[] endDates = caf.getNewEDCol().split(SEPERATOR);
662 		String[] endTimes = caf.getNewETCol().split(SEPERATOR);
663 		String[] hrs = caf.getNewHrsCol().split(SEPERATOR);
664 
665 		List<Interval> newIntervals = new ArrayList<Interval>();
666 		JSONArray errorMsgList = new JSONArray();
667 
668 		// validates that all fields are available
669 		if(assignments.length != beginDates.length ||
670 				assignments.length!= beginTimes.length ||
671 				assignments.length != endDates.length ||
672 				assignments.length != endTimes.length ||
673 				assignments.length != hrs.length) {
674 			errorMsgList.add("All fields are required");
675 		    caf.setOutputString(JSONValue.toJSONString(errorMsgList));
676 		    return mapping.findForward("ws");
677 		}
678 
679 		for(int i = 0; i < hrs.length; i++) {
680 			String index = String.valueOf(i+1);
681 
682 			// validate the hours field
683 			BigDecimal dc = new BigDecimal(hrs[i]);
684 		    if (dc.compareTo(new BigDecimal("0")) == 0) {
685 		        errorMsgList.add("The entered hours for entry " + index + " is not valid.");
686 		        caf.setOutputString(JSONValue.toJSONString(errorMsgList));
687 		        return mapping.findForward("ws");
688 		    }
689 
690 		    // check if the begin / end time are valid
691 		    // should not include time zone in consideration when conparing time intervals
692 		    DateTime beginDateTime = TKUtils.convertDateStringToDateTimeWithoutZone(beginDates[i], beginTimes[i]);
693 			DateTime endDateTime = TKUtils.convertDateStringToDateTimeWithoutZone(endDates[i], endTimes[i]);
694 		    if ((beginDateTime.compareTo(endDateTime) > 0 || endDateTime.compareTo(beginDateTime) < 0)) {
695 		        errorMsgList.add("The time or date for entry " + index + " is not valid.");
696 		        caf.setOutputString(JSONValue.toJSONString(errorMsgList));
697 		        return mapping.findForward("ws");
698 		    }
699 
700 		    // check if new time blocks overlap with existing time blocks
701 		    Interval addedTimeblockInterval = new Interval(beginDateTime, endDateTime);
702 		    newIntervals.add(addedTimeblockInterval);
703 		    for (TimeBlock timeBlock : caf.getTimesheetDocument().getTimeBlocks()) {
704 		    	if(timeBlock.getTkTimeBlockId().equals(tbId)) {	// ignore the original time block
705 		    		continue;
706 		    	}
707 		    	if(timeBlock.getHours().compareTo(BigDecimal.ZERO) == 0) { // ignore time blocks with zero hours
708 		    		continue;
709 		    	}
710 		    	DateTimeZone dateTimeZone = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
711 		    	DateTime timeBlockBeginTimestamp = new DateTime(timeBlock.getBeginDateTime(), dateTimeZone).withZone(TKUtils.getSystemDateTimeZone());
712 		    	DateTime timeBlockEndTimestamp = new DateTime(timeBlock.getEndDateTime(), dateTimeZone).withZone(TKUtils.getSystemDateTimeZone());
713 		    	Interval timeBlockInterval = new Interval(timeBlockBeginTimestamp, timeBlockEndTimestamp);
714 			    if (timeBlockInterval.overlaps(addedTimeblockInterval)) {
715 			        errorMsgList.add("The time block you are trying to add for entry " + index + " overlaps with an existing time block.");
716 			        caf.setOutputString(JSONValue.toJSONString(errorMsgList));
717 			        return mapping.findForward("ws");
718 			    }
719 		    }
720 		}
721 		// check if new time blocks overlap with each other
722 		if(newIntervals.size() > 1 ) {
723 			for(Interval intv1 : newIntervals) {
724 				for(Interval intv2 : newIntervals) {
725 					if(intv1.equals(intv2)) {
726 						continue;
727 					}
728 					if (intv1.overlaps(intv2)) {
729 						errorMsgList.add("There is time overlap between the entries.");
730 				        caf.setOutputString(JSONValue.toJSONString(errorMsgList));
731 				        return mapping.findForward("ws");
732 					}
733 				}
734 			}
735 		}
736 
737 	    caf.setOutputString(JSONValue.toJSONString(errorMsgList));
738 		return mapping.findForward("ws");
739  	}
740 	
741 	 private Boolean isPrincipalAnyProcessorInWorkArea(String principalId, Long tbWorkArea, LocalDate asOfDate) {
742 	    Boolean flag = false;
743 	    Set<Long> workAreas = new HashSet<Long>();
744 	    workAreas.addAll(HrServiceLocator.getKPMERoleService().getWorkAreasForPrincipalInRole(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.PAYROLL_PROCESSOR.getRoleName(), LocalDate.now().toDateTimeAtStartOfDay(), true));
745 	    workAreas.addAll(HrServiceLocator.getKPMERoleService().getWorkAreasForPrincipalInRole(principalId, KPMENamespace.KPME_HR.getNamespaceCode(),  KPMERole.PAYROLL_PROCESSOR_DELEGATE.getRoleName(), LocalDate.now().toDateTimeAtStartOfDay(), true));
746 
747         List<WorkArea> workAreaList = HrServiceLocator.getWorkAreaService().getWorkAreasForList(new ArrayList<Long>(workAreas), asOfDate);
748         for (WorkArea wa : workAreaList) {
749             if (wa.getWorkArea().compareTo(tbWorkArea) == 0) {
750                 return true;
751             }
752         }
753         return false;
754      }
755 }