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.timesheet.service;
17  
18  import java.math.BigDecimal;
19  import java.util.ArrayList;
20  import java.util.LinkedList;
21  import java.util.List;
22  
23  import org.apache.commons.collections.CollectionUtils;
24  import org.apache.commons.lang.StringUtils;
25  import org.apache.log4j.Logger;
26  import org.joda.time.DateTime;
27  import org.joda.time.LocalDate;
28  import org.kuali.kpme.core.accrualcategory.AccrualCategory;
29  import org.kuali.kpme.core.assignment.Assignment;
30  import org.kuali.kpme.core.batch.BatchJobUtil;
31  import org.kuali.kpme.core.calendar.entry.CalendarEntry;
32  import org.kuali.kpme.core.earncode.EarnCode;
33  import org.kuali.kpme.core.earncode.security.EarnCodeSecurity;
34  import org.kuali.kpme.core.earncode.security.EarnCodeType;
35  import org.kuali.kpme.core.job.Job;
36  import org.kuali.kpme.core.principal.PrincipalHRAttributes;
37  import org.kuali.kpme.core.service.HrServiceLocator;
38  import org.kuali.kpme.core.service.permission.HRPermissionService;
39  import org.kuali.kpme.core.util.HrConstants;
40  import org.kuali.kpme.core.util.HrContext;
41  import org.kuali.kpme.core.util.TKUtils;
42  import org.kuali.kpme.tklm.common.LMConstants;
43  import org.kuali.kpme.tklm.common.TkConstants;
44  import org.kuali.kpme.tklm.leave.block.LeaveBlock;
45  import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
46  import org.kuali.kpme.tklm.leave.timeoff.SystemScheduledTimeOff;
47  import org.kuali.kpme.tklm.time.rules.timecollection.TimeCollectionRule;
48  import org.kuali.kpme.tklm.time.service.TkServiceLocator;
49  import org.kuali.kpme.tklm.time.service.permission.TKPermissionService;
50  import org.kuali.kpme.core.block.CalendarBlockPermissions;
51  import org.kuali.kpme.tklm.time.timeblock.TimeBlock;
52  import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
53  import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
54  import org.kuali.rice.core.api.config.property.ConfigContext;
55  import org.kuali.rice.kew.api.KewApiServiceLocator;
56  import org.kuali.rice.kew.api.WorkflowDocument;
57  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
58  import org.kuali.rice.kew.api.exception.WorkflowException;
59  import org.kuali.rice.kew.api.note.Note;
60  import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
61  import org.kuali.rice.kim.api.identity.principal.Principal;
62  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
63  import org.kuali.rice.krad.util.GlobalVariables;
64  
65  public class TimesheetServiceImpl implements TimesheetService {
66  
67      private static final Logger LOG = Logger.getLogger(TimesheetServiceImpl.class);
68  
69      private HRPermissionService hrPermissionService;
70  
71      @Override
72      public void routeTimesheet(String principalId, TimesheetDocument timesheetDocument) {
73          routeTimesheet(principalId, timesheetDocument, HrConstants.DOCUMENT_ACTIONS.ROUTE);
74      }
75  
76      @Override
77      public void routeTimesheet(String principalId, TimesheetDocument timesheetDocument, String action) {
78          timesheetAction(action, principalId, timesheetDocument);
79      }
80  
81      @Override
82      public void approveTimesheet(String principalId, TimesheetDocument timesheetDocument) {
83          timesheetAction(HrConstants.DOCUMENT_ACTIONS.APPROVE, principalId, timesheetDocument);
84      }
85  
86      @Override
87      public void approveTimesheet(String principalId, TimesheetDocument timesheetDocument, String action) {
88          timesheetAction(action, principalId, timesheetDocument);
89      }
90  
91      @Override
92      public void disapproveTimesheet(String principalId, TimesheetDocument timesheetDocument) {
93          timesheetAction(HrConstants.DOCUMENT_ACTIONS.DISAPPROVE, principalId, timesheetDocument);
94      }
95  
96      protected void timesheetAction(String action, String principalId, TimesheetDocument timesheetDocument) {
97          WorkflowDocument wd = null;
98          if (timesheetDocument != null) {
99              String rhid = timesheetDocument.getDocumentId();
100             wd = WorkflowDocumentFactory.loadDocument(principalId, rhid);
101 
102             if (StringUtils.equals(action, HrConstants.DOCUMENT_ACTIONS.ROUTE)) {
103                 wd.route("Routing for Approval");
104             } else if (StringUtils.equals(action, HrConstants.BATCH_JOB_ACTIONS.BATCH_JOB_ROUTE)) {
105             	Note.Builder builder = Note.Builder.create(rhid, principalId);
106                 builder.setCreateDate(new DateTime());
107                 builder.setText("Routed via Employee Approval batch job");
108             	KewApiServiceLocator.getNoteService().createNote(builder.build());
109             	
110             	wd.route("Batch job routing timesheet");
111             } else if (StringUtils.equals(action, HrConstants.DOCUMENT_ACTIONS.APPROVE)) {
112                 if (HrServiceLocator.getHRPermissionService().canSuperUserAdministerCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), timesheetDocument) 
113                 		&& !HrServiceLocator.getHRPermissionService().canApproveCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), timesheetDocument)) {
114                     wd.superUserBlanketApprove("Superuser approving timesheet.");
115                 } else {
116                     wd.approve("Approving timesheet.");
117                 }
118             } else if (StringUtils.equals(action, HrConstants.BATCH_JOB_ACTIONS.BATCH_JOB_APPROVE)) {
119             	Note.Builder builder = Note.Builder.create(rhid, principalId);
120            	 	builder.setCreateDate(new DateTime());
121            	 	builder.setText("Approved via Supervisor Approval batch job");
122            	 	KewApiServiceLocator.getNoteService().createNote(builder.build());
123             	
124             	wd.superUserBlanketApprove("Batch job approving timesheet.");
125             } else if (StringUtils.equals(action, HrConstants.DOCUMENT_ACTIONS.DISAPPROVE)) {
126                 if (HrServiceLocator.getHRPermissionService().canSuperUserAdministerCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), timesheetDocument) 
127                 		&& !HrServiceLocator.getHRPermissionService().canApproveCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), timesheetDocument)) {
128                     wd.superUserDisapprove("Superuser disapproving timesheet.");
129                 } else {
130                     wd.disapprove("Disapproving timesheet.");
131                 }
132             }
133             clearTimesheetTimeblockPermissions(timesheetDocument);
134         }
135     }
136 
137     @Override
138     public TimesheetDocument openTimesheetDocument(String principalId, CalendarEntry calendarDates) throws WorkflowException {
139         TimesheetDocument timesheetDocument = null;
140 
141         DateTime begin = calendarDates.getBeginPeriodFullDateTime();
142         DateTime end = calendarDates.getEndPeriodFullDateTime();
143 
144         TimesheetDocumentHeader header = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, begin, end);
145 
146         if (header == null) {
147             List<Assignment> activeAssignments = HrServiceLocator.getAssignmentService().getAssignmentsByCalEntryForTimeCalendar(principalId, calendarDates);
148             //HrServiceLocator.getAssignmentService().getAssignments(principalId, TKUtils.getTimelessDate(payCalendarDates.getEndPeriodDate()));
149             if (activeAssignments.size() == 0) {
150                 LOG.warn("No active assignments for " + principalId + " for " + calendarDates.getEndPeriodDate());
151                 return null;
152                 //throw new RuntimeException("No active assignments for " + principalId + " for " + calendarDates.getEndPeriodDate());
153             }
154             
155             EntityNamePrincipalName person = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
156             String principalName = person != null && person.getDefaultName() != null ? person.getDefaultName().getCompositeName() : StringUtils.EMPTY;
157             String endDateString = TKUtils.formatDate(end.toLocalDate());
158             String timesheetDocumentTitle = TimesheetDocument.TIMESHEET_DOCUMENT_TYPE + " - " + principalName + " (" + principalId + ") - " + endDateString;
159             
160             timesheetDocument = this.initiateWorkflowDocument(principalId, begin, end, calendarDates, TimesheetDocument.TIMESHEET_DOCUMENT_TYPE, timesheetDocumentTitle);
161             //timesheetDocument.setPayCalendarEntry(calendarDates);
162             //this.loadTimesheetDocumentData(timesheetDocument, principalId, calendarDates);
163             //TODO switch this to scheduled time offs
164             //this.loadHolidaysOnTimesheet(timesheetDocument, principalId, begin, end);
165         } else {
166             timesheetDocument = this.getTimesheetDocument(header.getDocumentId());
167             if (timesheetDocument != null) {
168             	timesheetDocument.setCalendarEntry(calendarDates);
169             }
170         }
171 
172         //if (timesheetDocument != null) {
173         //	timesheetDocument.setTimeSummary(TkServiceLocator.getTimeSummaryService().getTimeSummary(timesheetDocument));
174         //}
175         
176         return timesheetDocument;
177     }
178 
179     public void loadHolidaysOnTimesheet(TimesheetDocument timesheetDocument, String principalId, LocalDate beginDate, LocalDate endDate) {
180         PrincipalHRAttributes principalCalendar = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, beginDate);
181         if (principalCalendar != null && StringUtils.isNotEmpty(principalCalendar.getLeavePlan())) {
182         	List<SystemScheduledTimeOff> sstoList = LmServiceLocator.getSysSchTimeOffService()
183         		.getSystemScheduledTimeOffForPayPeriod(principalCalendar.getLeavePlan(), beginDate, endDate);
184         	Assignment sstoAssign = getAssignmentToApplyScheduledTimeOff(timesheetDocument.getPrincipalId(), timesheetDocument.getAssignments(), endDate);
185         	if (sstoAssign != null) {
186         		for(SystemScheduledTimeOff ssto : sstoList) {
187                   BigDecimal sstoCalcHours = LmServiceLocator.getSysSchTimeOffService().calculateSysSchTimeOffHours(sstoAssign.getJob(), ssto.getAmountofTime());
188                   TimeBlock timeBlock = TkServiceLocator.getTimeBlockService().createTimeBlock(timesheetDocument, ssto.getScheduledTimeOffLocalDate().toDateTimeAtStartOfDay(),
189                           ssto.getScheduledTimeOffLocalDate().toDateTimeAtStartOfDay(), sstoAssign, HrConstants.HOLIDAY_EARN_CODE, sstoCalcHours, BigDecimal.ZERO, false, false, HrContext.getPrincipalId());
190                   timesheetDocument.getTimeBlocks().add(timeBlock);
191               }
192 	            //If system scheduled time off are loaded will need to save them to the database
193 		        if (CollectionUtils.isNotEmpty(sstoList)) {
194 		           TkServiceLocator.getTimeBlockService().saveTimeBlocks(new LinkedList<TimeBlock>(), timesheetDocument.getTimeBlocks(), HrContext.getPrincipalId());
195 		        }
196         	}
197         }
198     }
199 
200     private Assignment getAssignmentToApplyScheduledTimeOff(String principalId, List<Assignment> assignments, LocalDate endDate) {
201 		Job primaryJob = HrServiceLocator.getJobService().getPrimaryJob(principalId, endDate);
202 		for(Assignment assign : assignments){
203 			if(assign.getJobNumber().equals(primaryJob.getJobNumber())){
204 				return assign;
205 			}
206 		}
207 		return null;
208 	}
209 
210 	protected TimesheetDocument initiateWorkflowDocument(String principalId, DateTime payBeginDate,  DateTime payEndDate, CalendarEntry calendarEntry, String documentType, String title) throws WorkflowException {
211         TimesheetDocument timesheetDocument = null;
212         WorkflowDocument workflowDocument = null;
213 
214         workflowDocument = WorkflowDocumentFactory.createDocument(principalId, documentType, title);
215 
216         String status = workflowDocument.getStatus().getCode();
217         TimesheetDocumentHeader documentHeader = new TimesheetDocumentHeader(workflowDocument.getDocumentId(), principalId, payBeginDate.toDate(), payEndDate.toDate(), status);
218 
219         documentHeader.setDocumentId(workflowDocument.getDocumentId().toString());
220         documentHeader.setDocumentStatus("I");
221 
222         TkServiceLocator.getTimesheetDocumentHeaderService().saveOrUpdate(documentHeader);
223         timesheetDocument = new TimesheetDocument(documentHeader);
224         timesheetDocument.setCalendarEntry(calendarEntry);
225         loadTimesheetDocumentData(timesheetDocument, principalId, calendarEntry);
226         TkServiceLocator.getTkSearchableAttributeService().updateSearchableAttribute(timesheetDocument, payEndDate.toLocalDate());
227 
228         if (LmServiceLocator.getLeaveApprovalService().isActiveAssignmentFoundOnJobFlsaStatus(principalId, HrConstants.FLSA_STATUS_NON_EXEMPT, true)) {
229         	deleteNonApprovedLeaveBlocks(principalId, calendarEntry.getBeginPeriodFullDateTime().toLocalDate(), calendarEntry.getEndPeriodFullDateTime().toLocalDate());
230         }
231         
232         return timesheetDocument;
233     }
234     
235     private void deleteNonApprovedLeaveBlocks(String principalId, LocalDate beginDate, LocalDate endDate) {
236     	String batchUserPrincipalId = BatchJobUtil.getBatchUserPrincipalId();
237         
238         if (batchUserPrincipalId != null) {
239 	    	List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
240 	
241 	    	for (LeaveBlock leaveBlock : leaveBlocks) {
242 	    		if (!StringUtils.equals(leaveBlock.getRequestStatus(), HrConstants.REQUEST_STATUS.APPROVED)) {
243                     LmServiceLocator.getLeaveRequestDocumentService().suCancelLeave(
244                             leaveBlock.getLeaveRequestDocumentId(), batchUserPrincipalId);
245                     LmServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlock.getLmLeaveBlockId(), batchUserPrincipalId);
246 	    		}
247 	    	}
248         } else {
249         	String principalName = BatchJobUtil.getBatchUserPrincipalName();
250         	LOG.error("Could not delete leave request blocks due to missing batch user " + principalName);
251         }
252     }
253 
254 
255     public List<TimeBlock> getPrevDocumentTimeBlocks(String principalId, DateTime payBeginDate) {
256         TimesheetDocumentHeader prevTdh = TkServiceLocator.getTimesheetDocumentHeaderService().getPreviousDocumentHeader(principalId, payBeginDate);
257         if (prevTdh == null) {
258             return new ArrayList<TimeBlock>();
259         }
260         return TkServiceLocator.getTimeBlockService().getTimeBlocks(prevTdh.getDocumentId());
261     }
262 
263     @Override
264     public TimesheetDocument getTimesheetDocument(String documentId) {
265         TimesheetDocument timesheetDocument = null;
266         TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(documentId);
267 
268         if (tdh != null) {
269             timesheetDocument = new TimesheetDocument(tdh);
270             CalendarEntry pce = HrServiceLocator.getCalendarEntryService().getCalendarDatesByPayEndDate(tdh.getPrincipalId(), tdh.getEndDateTime(), HrConstants.PAY_CALENDAR_TYPE);
271             loadTimesheetDocumentData(timesheetDocument, tdh.getPrincipalId(), pce);
272 
273             timesheetDocument.setCalendarEntry(pce);
274         }
275         
276         return timesheetDocument;
277     }
278 
279     protected void loadTimesheetDocumentData(TimesheetDocument tdoc, String principalId, CalendarEntry payCalEntry) {
280     	tdoc.setAssignments(HrServiceLocator.getAssignmentService().getAssignmentsByCalEntryForTimeCalendar(principalId, payCalEntry));
281     	if (payCalEntry != null) {
282     		tdoc.setJobs(HrServiceLocator.getJobService().getJobs(principalId, payCalEntry.getEndPeriodFullDateTime().toLocalDate()));
283     	}
284     	tdoc.setTimeBlocks(TkServiceLocator.getTimeBlockService().getTimeBlocks(tdoc.getDocumentHeader().getDocumentId()));
285     }
286 
287     public boolean isSynchronousUser() {
288         List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignments(HrContext.getTargetPrincipalId(), LocalDate.now());
289         boolean isSynchronousUser = true;
290         for (Assignment assignment : assignments) {
291         	if(assignment.getJob() != null) {
292 	        	TimeCollectionRule tcr = TkServiceLocator.getTimeCollectionRuleService().getTimeCollectionRule(assignment.getDept(), assignment.getWorkArea(), assignment.getJob().getHrPayType(), LocalDate.now());
293 	            isSynchronousUser &= tcr == null || tcr.isClockUserFl();
294         	}
295         }
296         return isSynchronousUser;
297     }
298 
299     //this is an admin function used for testing
300     public void deleteTimesheet(String documentId) {
301         TkServiceLocator.getTimeBlockService().deleteTimeBlocksAssociatedWithDocumentId(documentId);
302         TkServiceLocator.getTimesheetDocumentHeaderService().deleteTimesheetHeader(documentId);
303     }
304 
305     public TimeBlock resetWorkedHours(TimeBlock previousTimeBlock, TimeBlock timeBlock, LocalDate asOfDate) {
306     	EarnCode earnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(timeBlock.getEarnCode(), asOfDate);
307     	
308         if (timeBlock.getBeginTime() != null && timeBlock.getEndTime() != null && StringUtils.equals(timeBlock.getEarnCodeType(), HrConstants.EARN_CODE_TIME)) {
309             BigDecimal hours = TKUtils.getHoursBetween(timeBlock.getBeginTime().getTime(), timeBlock.getEndTime().getTime());
310 
311             //If earn code has an inflate min hours check if it is greater than zero
312             //and compare if the hours specified is less than min hours awarded for this
313             //earn code
314             if (earnCodeObj.getInflateMinHours() != null) {
315             	if ((earnCodeObj.getInflateMinHours().compareTo(BigDecimal.ZERO) != 0) &&
316             			earnCodeObj.getInflateMinHours().compareTo(hours) > 0) {
317                     //if previous timeblock has no gap then assume its one block if the same earn code and divide inflated hours accordingly
318                     if(previousTimeBlock != null && StringUtils.equals(earnCodeObj.getEarnCode(),previousTimeBlock.getEarnCode()) &&
319                             (timeBlock.getBeginTime().getTime() - previousTimeBlock.getEndTime().getTime() == 0L)) {
320                         BigDecimal prevTimeBlockHours = TKUtils.getHoursBetween(previousTimeBlock.getBeginTime().getTime(), previousTimeBlock.getEndTime().getTime());
321                         previousTimeBlock.setHours(prevTimeBlockHours);
322                         if(earnCodeObj.getInflateMinHours().compareTo(prevTimeBlockHours.add(hours,HrConstants.MATH_CONTEXT)) > 0) {
323                         	//hours = earnCodeObj.getInflateMinHours().subtract(prevTimeBlockHours,HrConstants.MATH_CONTEXT);
324                         }
325                     }
326                 }
327             }
328             //If earn code has an inflate factor multiple hours specified by the factor
329 /*            if (earnCodeObj.getInflateFactor() != null) {
330             	if ((earnCodeObj.getInflateFactor().compareTo(new BigDecimal(1.0)) != 0)
331             			&& (earnCodeObj.getInflateFactor().compareTo(BigDecimal.ZERO)!= 0) ) {
332             		hours = earnCodeObj.getInflateFactor().multiply(hours, HrConstants.MATH_CONTEXT).setScale(HrConstants.BIG_DECIMAL_SCALE);
333             	}
334             }*/
335             
336             timeBlock.setHours(hours);
337         }
338         return timeBlock;
339     }
340 
341     @Override
342     public void resetTimeBlock(List<TimeBlock> timeBlocks, LocalDate asOfDate) {
343         TimeBlock previous = null;
344         for (TimeBlock tb : timeBlocks) {
345             resetWorkedHours(previous, tb, asOfDate);
346             previous = tb;
347         }
348         TkServiceLocator.getTimeBlockService().resetTimeHourDetail(timeBlocks);
349     }
350 
351 	@Override
352 	public boolean isReadyToApprove(TimesheetDocument document) {
353         if (document == null) {
354             return false;
355         }
356         List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
357         		document.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate(), document.getCalendarEntry().getEndPeriodFullDateTime().toLocalDate(), LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER);
358         leaveBlocks.addAll(LmServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
359         		document.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate(), document.getCalendarEntry().getEndPeriodFullDateTime().toLocalDate(), LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT));
360         for(LeaveBlock lb : leaveBlocks) {
361         	if(!StringUtils.equals(lb.getRequestStatus(),HrConstants.REQUEST_STATUS.APPROVED) &&
362         			!StringUtils.equals(lb.getRequestStatus(), HrConstants.REQUEST_STATUS.DISAPPROVED))
363         		return false;
364         }
365         return true;
366 	}
367 	
368     public List<EarnCode> getEarnCodesForTime(Assignment a, LocalDate asOfDate, boolean includeRegularEarnCode) {
369         //getEarnCodesForTime and getEarnCodesForLeave have some overlapping logic, but they were separated so that they could follow their own distinct logic, so consolidation of logic is not desirable.
370 
371         if (a == null) {
372         	LOG.error("No assignment parameter.");
373         	return null;
374 //        	throw new RuntimeException("No assignment parameter.");
375         }
376         Job job = a.getJob();
377         if (job == null || job.getPayTypeObj() == null) {
378         	LOG.error("Null job or null job pay type on assignment.");
379         	return null;
380 //        	throw new RuntimeException("Null job or null job pay type on assignment.");
381         }
382 
383         List<EarnCode> earnCodes = new LinkedList<EarnCode>();
384         String earnTypeCode = EarnCodeType.TIME.getCode();
385 
386         TimeCollectionRule tcr = null;
387         if(a.getJob() != null)
388         	tcr = TkServiceLocator.getTimeCollectionRuleService().getTimeCollectionRule(a.getDept(), a.getWorkArea(), a.getJob().getHrPayType(), asOfDate);
389         
390         boolean isClockUser = tcr == null || tcr.isClockUserFl();
391         boolean isUsersTimesheet = StringUtils.equals(HrContext.getPrincipalId(),a.getPrincipalId());
392 
393         // Reg earn codes will typically not be defined in the earn code security table
394         EarnCode regularEarnCode = HrServiceLocator.getEarnCodeService().getEarnCode(job.getPayTypeObj().getRegEarnCode(), asOfDate);
395         if (regularEarnCode == null) {
396         	LOG.error("No regular earn code defined for job pay type.");
397         	return null;
398 //            throw new RuntimeException("No regular earn code defined for job pay type.");
399         } else {
400             //  if you are a clock user and this is your timesheet and you are processing the reg earn code, do not add this earn code. Use the clock in/out mechanism.
401         	if (!isClockUser || !isUsersTimesheet || includeRegularEarnCode) {
402                 earnCodes.add(regularEarnCode);
403             }
404         }
405 
406         List<String> listAccrualCategories = new LinkedList<String>();
407         String accrualCategory;
408 
409         //  first make a list of the accrual categories available to the user's Leave Plan (yes, leave plan), for later comparison.
410         PrincipalHRAttributes principalHRAttributes = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(job.getPrincipalId(), asOfDate);
411         boolean fmlaEligible = principalHRAttributes.isFmlaEligible();
412         boolean workersCompEligible = principalHRAttributes.isWorkersCompEligible();
413 
414         String leavePlan = principalHRAttributes.getLeavePlan();
415         if (leavePlan != null) {
416             for (AccrualCategory accrualCategories : HrServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(leavePlan, asOfDate)) {
417                 accrualCategory = accrualCategories.getAccrualCategory();
418                 if(accrualCategory != null) {
419                     listAccrualCategories.add(accrualCategory);
420                 }
421             }
422         }
423 
424         //  get all earn codes by user security, then we'll filter on accrual category first as we process them.
425         List<EarnCodeSecurity> decs = HrServiceLocator.getEarnCodeSecurityService().getEarnCodeSecurities(job.getDept(), job.getHrSalGroup(), job.getLocation(), asOfDate);
426         for (EarnCodeSecurity dec : decs) {
427 
428             boolean addEarnCode = HrServiceLocator.getEarnCodeService().addEarnCodeBasedOnEmployeeApproverSettings(dec, a, asOfDate);
429             if (addEarnCode) {
430 
431                 //  allow types Time AND Both
432                 if (earnTypeCode.equals(dec.getEarnCodeType()) || EarnCodeType.BOTH.getCode().equals(dec.getEarnCodeType())) {
433                     EarnCode ec = HrServiceLocator.getEarnCodeService().getEarnCode(dec.getEarnCode(), asOfDate);
434 
435                     //  make sure we got something back from the earn code dao
436                     if (ec != null) {
437                     	// make sure the earn code's leave plan matches the user's leave plan
438                     	// if user has a leave plan, we show earn codes with a matching leave plan and all earn codes without a leave plan
439                     	// if user doe not have a leave plan, we show earn codes that don't have a leave plan
440                     	if( (StringUtils.isNotBlank(leavePlan) && StringUtils.isBlank(ec.getLeavePlan()))
441                     			|| (StringUtils.isNotBlank(leavePlan) && StringUtils.isNotBlank(ec.getLeavePlan()) && leavePlan.equals(ec.getLeavePlan()))
442     							|| (StringUtils.isBlank(leavePlan) && StringUtils.isBlank(ec.getLeavePlan()))) {                    	
443 	                        //  if the user's fmla flag is Yes, that means we are not restricting codes based on this flag, so any code is shown.
444 	                        //    if the fmla flag on a code is yes they can see it.    (allow)
445 	                        //    if the fmla flag on a code is no they should see it.  (allow)
446 	                        //  if the user's fmla flag is No,
447 	                        //    they can see any codes which are fmla=no.             (allow)
448 	                        //    they can not see codes with fmla=yes.                 (exclude earn code)
449 	                        //  the fmla earn codes=no do not require any exclusion
450 	                        //  the only action required is if the fmla user flag=no: exclude those codes with fmla=yes.
451 	                        if ( (fmlaEligible || ec.getFmla().equals("N")) ) {
452 	                        	if (ec.getAccrualCategory() == null
453 	                        		|| (listAccrualCategories.contains(ec.getAccrualCategory())
454 	                         	 		&& HrConstants.ACCRUAL_BALANCE_ACTION.USAGE.equals(ec.getAccrualBalanceAction()))) {
455 	                            // go on, we are allowing these three combinations: YY, YN, NN
456 	
457 	                                //  apply the same logic as FMLA to the Worker Compensation flags.
458 	                                if ( (workersCompEligible || ec.getWorkmansComp().equals("N")) ) {
459 	                                    // go on, we are allowing these three combinations: YY, YN, NN.
460 	
461 	                                    //  determine if the holiday earn code should be displayed.
462 	                                    if ( showEarnCodeIfHoliday(ec, dec) ) {
463 	                                        //  non-Holiday earn code will go on, Holiday earn code must meet some requirements in the method.
464 	                                    	// KPME-2556
465 	                                        //if ( !StringUtils.equals(regularEarnCode.toString(), dec.getEarnCode()) ) {
466 	                                    	if (!StringUtils.equals(regularEarnCode.getEarnCode(), dec.getEarnCode()) ) {
467 	                                            //  add earn code if it is not the reg earn code.
468 	                                            earnCodes.add(ec);
469 	                                        }
470 	                                    }
471 	                                }
472 	                            }
473 	                        }
474                     	}
475                     }
476                 }
477             }
478         }
479 
480         return earnCodes;
481     }
482     
483     public List<EarnCode> getEarnCodesForTime(Assignment a, LocalDate asOfDate) {
484     	return getEarnCodesForTime(a, asOfDate, false);
485 	}
486 
487     private boolean showEarnCodeIfHoliday(EarnCode earnCode, EarnCodeSecurity security) {
488         if (earnCode.getEarnCode().equals(HrConstants.HOLIDAY_EARN_CODE)) {
489             if (security.isApprover() || HrContext.isSystemAdmin()) {
490                 return true;
491             } else {
492                 return false;
493             }
494         } else {
495             return true;
496         }
497     }
498 
499     public HRPermissionService getHRPermissionService() {
500         if (hrPermissionService == null) {
501             hrPermissionService = HrServiceLocator.getHRPermissionService();
502         }
503         return hrPermissionService;
504     }
505 
506     private void clearTimesheetTimeblockPermissions(TimesheetDocument doc) {
507         for (TimeBlock tb : doc.getTimeBlocks()) {
508             getHRPermissionService().updateTimeBlockPermissions(CalendarBlockPermissions.newInstance(tb.getTkTimeBlockId()));
509         }
510     }
511 }