1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.kpme.tklm.time.timesheet.service;
17
18 import org.apache.commons.collections.CollectionUtils;
19 import org.apache.commons.collections.MapUtils;
20 import org.apache.commons.lang.StringUtils;
21 import org.apache.log4j.Logger;
22 import org.joda.time.DateTime;
23 import org.joda.time.DateTimeZone;
24 import org.joda.time.Interval;
25 import org.joda.time.LocalDate;
26 import org.joda.time.format.DateTimeFormat;
27 import org.kuali.kpme.core.api.accrualcategory.AccrualCategory;
28 import org.kuali.kpme.core.api.assignment.Assignment;
29 import org.kuali.kpme.core.api.block.CalendarBlockPermissions;
30 import org.kuali.kpme.core.api.calendar.entry.CalendarEntry;
31 import org.kuali.kpme.core.api.earncode.EarnCode;
32 import org.kuali.kpme.core.api.earncode.EarnCodeContract;
33 import org.kuali.kpme.core.api.earncode.security.EarnCodeSecurity;
34 import org.kuali.kpme.core.api.earncode.security.EarnCodeSecurityContract;
35 import org.kuali.kpme.core.api.job.JobContract;
36 import org.kuali.kpme.core.api.permission.HRPermissionService;
37 import org.kuali.kpme.core.api.principal.PrincipalHRAttributes;
38 import org.kuali.kpme.core.batch.BatchJobUtil;
39 import org.kuali.kpme.core.earncode.security.EarnCodeType;
40 import org.kuali.kpme.core.role.KPMERole;
41 import org.kuali.kpme.core.service.HrServiceLocator;
42 import org.kuali.kpme.core.util.HrConstants;
43 import org.kuali.kpme.core.util.HrContext;
44 import org.kuali.kpme.core.util.TKUtils;
45 import org.kuali.kpme.tklm.api.common.TkConstants;
46 import org.kuali.kpme.tklm.api.leave.block.LeaveBlock;
47 import org.kuali.kpme.tklm.api.leave.timeoff.SystemScheduledTimeOffContract;
48 import org.kuali.kpme.tklm.api.time.timeblock.TimeBlock;
49 import org.kuali.kpme.tklm.common.LMConstants;
50 import org.kuali.kpme.tklm.common.WorkflowTagSupport;
51 import org.kuali.kpme.tklm.leave.block.LeaveBlockAggregate;
52 import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
53 import org.kuali.kpme.tklm.time.flsa.FlsaDay;
54 import org.kuali.kpme.tklm.time.flsa.FlsaWeek;
55 import org.kuali.kpme.tklm.time.rules.timecollection.TimeCollectionRule;
56 import org.kuali.kpme.tklm.time.service.TkServiceLocator;
57 import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
58 import org.kuali.kpme.tklm.time.util.TkTimeBlockAggregate;
59 import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
60 import org.kuali.rice.core.api.mo.ModelObjectUtils;
61 import org.kuali.rice.kew.api.KewApiServiceLocator;
62 import org.kuali.rice.kew.api.WorkflowDocument;
63 import org.kuali.rice.kew.api.WorkflowDocumentFactory;
64 import org.kuali.rice.kew.api.action.ActionRequest;
65 import org.kuali.rice.kew.api.exception.WorkflowException;
66 import org.kuali.rice.kew.api.note.Note;
67 import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
68 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
69 import org.kuali.rice.krad.util.GlobalVariables;
70
71 import java.math.BigDecimal;
72 import java.util.*;
73
74 public class TimesheetServiceImpl implements TimesheetService {
75
76 private static final Logger LOG = Logger.getLogger(TimesheetServiceImpl.class);
77 private static final ModelObjectUtils.Transformer<TimeBlock, TimeBlock.Builder> toTimeBlockBuilder =
78 new ModelObjectUtils.Transformer<TimeBlock, TimeBlock.Builder>() {
79 public TimeBlock.Builder transform(TimeBlock input) {
80 return TimeBlock.Builder.create(input);
81 };
82 };
83 private HRPermissionService hrPermissionService;
84
85 @Override
86 public void routeTimesheet(String principalId, TimesheetDocument timesheetDocument) {
87 routeTimesheet(principalId, timesheetDocument, HrConstants.DOCUMENT_ACTIONS.ROUTE);
88 }
89
90 @Override
91 public void routeTimesheet(String principalId, TimesheetDocument timesheetDocument, String action) {
92 timesheetAction(action, principalId, timesheetDocument);
93 }
94
95 @Override
96 public void approveTimesheet(String principalId, TimesheetDocument timesheetDocument) {
97 timesheetAction(HrConstants.DOCUMENT_ACTIONS.APPROVE, principalId, timesheetDocument);
98 }
99
100 @Override
101 public void approveTimesheet(String principalId, TimesheetDocument timesheetDocument, String action) {
102 timesheetAction(action, principalId, timesheetDocument);
103 }
104
105 @Override
106 public void disapproveTimesheet(String principalId, TimesheetDocument timesheetDocument) {
107 timesheetAction(HrConstants.DOCUMENT_ACTIONS.DISAPPROVE, principalId, timesheetDocument);
108 }
109
110 protected void timesheetAction(String action, String principalId, TimesheetDocument timesheetDocument) {
111 WorkflowDocument wd = null;
112 if (timesheetDocument != null) {
113
114 String rhid = timesheetDocument.getDocumentId();
115 wd = WorkflowDocumentFactory.loadDocument(principalId, rhid);
116 List<ActionRequest> actionRequests = KewApiServiceLocator.getWorkflowDocumentService().getPendingActionRequests(rhid);
117 if (StringUtils.equals(action, HrConstants.DOCUMENT_ACTIONS.ROUTE)) {
118 wd.route("Routing for Approval");
119 } else if (StringUtils.equals(action, HrConstants.BATCH_JOB_ACTIONS.BATCH_JOB_ROUTE)) {
120 Note.Builder builder = Note.Builder.create(rhid, principalId);
121 builder.setCreateDate(new DateTime());
122 builder.setText("Routed via Employee Approval batch job");
123 KewApiServiceLocator.getNoteService().createNote(builder.build());
124
125 wd.route("Batch job routing timesheet");
126 } else if (StringUtils.equals(action, HrConstants.DOCUMENT_ACTIONS.APPROVE)) {
127 if (HrServiceLocator.getHRPermissionService().canSuperUserAdministerCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), timesheetDocument)
128 && !HrServiceLocator.getHRPermissionService().canApproveCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), timesheetDocument)) {
129 wd.superUserBlanketApprove("Superuser approving timesheet.");
130 } else {
131 wd.approve("Approving timesheet.");
132 }
133 } else if (StringUtils.equals(action, HrConstants.BATCH_JOB_ACTIONS.BATCH_JOB_APPROVE)) {
134 boolean approverFlag = false;
135 for (ActionRequest ar : actionRequests) {
136 if(StringUtils.equals(ar.getQualifiedRoleNameLabel(), KPMERole.APPROVER.getRoleName())) {
137 approverFlag = true;
138 break;
139 }
140 }
141
142
143 if(approverFlag) {
144
145 Note.Builder builder = Note.Builder.create(rhid, principalId);
146 builder.setCreateDate(new DateTime());
147 builder.setText("Approved via Supervisor Approval batch job");
148 KewApiServiceLocator.getNoteService().createNote(builder.build());
149 wd.approve("Supervisor Batch job approving timesheet on behalf of approvers.");
150 }
151 } else if (StringUtils.equals(action, HrConstants.BATCH_JOB_ACTIONS.PAYROLL_JOB_APPROVE)) {
152 boolean payrollProcessorFlag = false;
153 for (ActionRequest ar : actionRequests) {
154 if(StringUtils.equals(ar.getQualifiedRoleNameLabel(), KPMERole.PAYROLL_PROCESSOR.getRoleName())) {
155 payrollProcessorFlag = true;
156 break;
157 }
158 }
159 if(payrollProcessorFlag) {
160 Note.Builder builder = Note.Builder.create(rhid, principalId);
161 builder.setCreateDate(new DateTime());
162 builder.setText("Approved via Payroll Processor Approval batch job");
163 KewApiServiceLocator.getNoteService().createNote(builder.build());
164 wd.approve("Payroll Processor Batch job approving timesheet on behalf of payroll processors.");
165 }
166 } else if (StringUtils.equals(action, HrConstants.DOCUMENT_ACTIONS.DISAPPROVE)) {
167 if (HrServiceLocator.getHRPermissionService().canSuperUserAdministerCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), timesheetDocument)
168 && !HrServiceLocator.getHRPermissionService().canApproveCalendarDocument(GlobalVariables.getUserSession().getPrincipalId(), timesheetDocument)) {
169 wd.superUserDisapprove("Superuser disapproving timesheet.");
170 } else {
171 wd.disapprove("Disapproving timesheet.");
172 }
173 }
174 clearTimesheetTimeblockPermissions(timesheetDocument);
175 }
176 }
177
178 @Override
179 public TimesheetDocument openTimesheetDocument(String principalId, CalendarEntry calendarDates) throws WorkflowException {
180 TimesheetDocument timesheetDocument = null;
181
182 DateTime begin = calendarDates.getBeginPeriodFullDateTime();
183 DateTime end = calendarDates.getEndPeriodFullDateTime();
184
185 TimesheetDocumentHeader header = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, begin, end);
186
187 if (header == null) {
188 List<Assignment> activeAssignments = HrServiceLocator.getAssignmentService().getAllAssignmentsByCalEntryForTimeCalendar(principalId, calendarDates);
189
190 if (CollectionUtils.isEmpty(activeAssignments)) {
191 LOG.warn("No active assignments for " + principalId + " for " + calendarDates.getEndPeriodFullDateTime());
192 return null;
193
194 }
195
196 EntityNamePrincipalName person = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
197 String principalName = person != null && person.getDefaultName() != null ? person.getDefaultName().getCompositeName() : StringUtils.EMPTY;
198 String endDateString = TKUtils.formatDate(end.toLocalDate());
199 String timesheetDocumentTitle = TimesheetDocument.TIMESHEET_DOCUMENT_TYPE + " - " + principalName + " (" + principalId + ") - " + endDateString;
200
201 timesheetDocument = this.initiateWorkflowDocument(principalId, begin, end, calendarDates, TimesheetDocument.TIMESHEET_DOCUMENT_TYPE, timesheetDocumentTitle);
202
203
204
205
206 } else {
207 timesheetDocument = this.getTimesheetDocument(header.getDocumentId());
208 if (timesheetDocument != null) {
209 timesheetDocument.setCalendarEntry(calendarDates);
210 }
211 }
212
213
214
215
216
217 return timesheetDocument;
218 }
219
220 public void loadHolidaysOnTimesheet(TimesheetDocument timesheetDocument, String principalId, LocalDate beginDate, LocalDate endDate) {
221 PrincipalHRAttributes principalCalendar = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, beginDate);
222 if (principalCalendar != null && StringUtils.isNotEmpty(principalCalendar.getLeavePlan())) {
223 List<? extends SystemScheduledTimeOffContract> sstoList = LmServiceLocator.getSysSchTimeOffService()
224 .getSystemScheduledTimeOffForPayPeriod(principalCalendar.getLeavePlan(), beginDate, endDate);
225 Assignment sstoAssign = getAssignmentToApplyScheduledTimeOff(timesheetDocument.getPrincipalId(), timesheetDocument.getAllAssignments(), endDate);
226 if (sstoAssign != null) {
227 for(SystemScheduledTimeOffContract ssto : sstoList) {
228 BigDecimal sstoCalcHours = LmServiceLocator.getSysSchTimeOffService().calculateSysSchTimeOffHours(sstoAssign.getJob(), ssto.getAmountofTime());
229 TimeBlock timeBlock = TkServiceLocator.getTimeBlockService().createTimeBlock(timesheetDocument.getPrincipalId(), timesheetDocument.getDocumentId(), ssto.getScheduledTimeOffLocalDate().toDateTimeAtStartOfDay(),
230 ssto.getScheduledTimeOffLocalDate().toDateTimeAtStartOfDay(), sstoAssign, HrConstants.HOLIDAY_EARN_CODE, sstoCalcHours, BigDecimal.ZERO, false, false, HrContext.getPrincipalId());
231 timesheetDocument.getTimeBlocks().add(timeBlock);
232 }
233
234 if (CollectionUtils.isNotEmpty(sstoList)) {
235 TkServiceLocator.getTimeBlockService().saveOrUpdateTimeBlocks(Collections.<TimeBlock>emptyList(), timesheetDocument.getTimeBlocks(), HrContext.getPrincipalId());
236 }
237 }
238 }
239 }
240
241 private Assignment getAssignmentToApplyScheduledTimeOff(String principalId, List<Assignment> assignments, LocalDate endDate) {
242 JobContract primaryJob = HrServiceLocator.getJobService().getPrimaryJob(principalId, endDate);
243 for(Assignment assign : assignments){
244 if(assign.getJobNumber().equals(primaryJob.getJobNumber())){
245 return assign;
246 }
247 }
248 return null;
249 }
250
251 protected TimesheetDocument initiateWorkflowDocument(String principalId, DateTime payBeginDate, DateTime payEndDate, CalendarEntry calendarEntry, String documentType, String title) throws WorkflowException {
252 TimesheetDocument timesheetDocument = null;
253 WorkflowDocument workflowDocument = null;
254
255 workflowDocument = WorkflowDocumentFactory.createDocument(principalId, documentType, title);
256
257 String status = workflowDocument.getStatus().getCode();
258 TimesheetDocumentHeader documentHeader = new TimesheetDocumentHeader(workflowDocument.getDocumentId(), principalId, payBeginDate.toDate(), payEndDate.toDate(), status);
259
260 documentHeader.setDocumentId(workflowDocument.getDocumentId());
261 documentHeader.setDocumentStatus("I");
262
263 TkServiceLocator.getTimesheetDocumentHeaderService().saveOrUpdate(documentHeader);
264 timesheetDocument = new TimesheetDocument(documentHeader);
265 timesheetDocument.setCalendarEntry(calendarEntry);
266 loadTimesheetDocumentData(timesheetDocument, principalId, calendarEntry);
267 TkServiceLocator.getTkSearchableAttributeService().updateSearchableAttribute(timesheetDocument, payEndDate.toLocalDate());
268
269 if (LmServiceLocator.getLeaveApprovalService().isActiveAssignmentFoundOnJobFlsaStatus(principalId, HrConstants.FLSA_STATUS_NON_EXEMPT, true)) {
270 deleteNonApprovedLeaveBlocks(principalId, calendarEntry.getBeginPeriodFullDateTime().toLocalDate(), calendarEntry.getEndPeriodFullDateTime().toLocalDate());
271 }
272
273 return timesheetDocument;
274 }
275
276 private void deleteNonApprovedLeaveBlocks(String principalId, LocalDate beginDate, LocalDate endDate) {
277 String batchUserPrincipalId = BatchJobUtil.getBatchUserPrincipalId();
278
279 if (batchUserPrincipalId != null) {
280 List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
281
282 for (LeaveBlock leaveBlock : leaveBlocks) {
283 if (!StringUtils.equals(leaveBlock.getRequestStatus(), HrConstants.REQUEST_STATUS.APPROVED)) {
284 LmServiceLocator.getLeaveRequestDocumentService().suCancelLeave(
285 leaveBlock.getLeaveRequestDocumentId(), batchUserPrincipalId);
286 LmServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlock.getLmLeaveBlockId(), batchUserPrincipalId);
287 }
288 }
289 } else {
290 String principalName = BatchJobUtil.getBatchUserPrincipalName();
291 LOG.error("Could not delete leave request blocks due to missing batch user " + principalName);
292 }
293 }
294
295
296 public List<TimeBlock> getPrevDocumentTimeBlocks(String principalId, DateTime payBeginDate) {
297 TimesheetDocumentHeader prevTdh = TkServiceLocator.getTimesheetDocumentHeaderService().getPreviousDocumentHeader(principalId, payBeginDate);
298 if (prevTdh == null) {
299 return new ArrayList<TimeBlock>();
300 }
301 return TkServiceLocator.getTimeBlockService().getTimeBlocks(prevTdh.getDocumentId());
302 }
303
304 @Override
305 public TimesheetDocument getTimesheetDocument(String documentId) {
306 TimesheetDocument timesheetDocument = null;
307 TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(documentId);
308
309 if (tdh != null) {
310 timesheetDocument = new TimesheetDocument(tdh);
311 CalendarEntry pce = HrServiceLocator.getCalendarEntryService().getCalendarDatesByPayEndDate(tdh.getPrincipalId(), tdh.getEndDateTime(), HrConstants.PAY_CALENDAR_TYPE);
312 loadTimesheetDocumentData(timesheetDocument, tdh.getPrincipalId(), pce);
313
314 timesheetDocument.setCalendarEntry(pce);
315 }
316
317 return timesheetDocument;
318 }
319
320 protected void loadTimesheetDocumentData(TimesheetDocument tdoc, String principalId, CalendarEntry payCalEntry) {
321
322 tdoc.setAssignments(HrServiceLocator.getAssignmentService().getAssignmentHistoryForCalendarEntry(principalId, payCalEntry));
323 if (payCalEntry != null) {
324 tdoc.setJobs(HrServiceLocator.getJobService().getJobs(principalId, payCalEntry.getEndPeriodFullDateTime().toLocalDate()));
325 }
326 tdoc.setTimeBlocks(TkServiceLocator.getTimeBlockService().getTimeBlocks(tdoc.getDocumentHeader().getDocumentId()));
327 }
328
329 public boolean isSynchronousUser() {
330 List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignments(HrContext.getTargetPrincipalId(), LocalDate.now());
331 boolean isSynchronousUser = true;
332 for (Assignment assignment : assignments) {
333 if(assignment.getJob() != null) {
334 TimeCollectionRule tcr = TkServiceLocator.getTimeCollectionRuleService().getTimeCollectionRule(assignment.getDept(), assignment.getWorkArea(), assignment.getJob().getHrPayType(), assignment.getGroupKeyCode(), LocalDate.now());
335 isSynchronousUser &= tcr == null || tcr.isClockUserFl();
336 }
337 }
338 return isSynchronousUser;
339 }
340
341
342 public void deleteTimesheet(String documentId) {
343 TkServiceLocator.getTimeBlockService().deleteTimeBlocksAssociatedWithDocumentId(documentId);
344 TkServiceLocator.getTimesheetDocumentHeaderService().deleteTimesheetHeader(documentId);
345 }
346
347 protected void resetWorkedHours(TimeBlock.Builder previousTimeBlock, TimeBlock.Builder timeBlock, LocalDate asOfDate) {
348 EarnCodeContract earnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(timeBlock.getEarnCode(), asOfDate);
349 if (timeBlock.getBeginTime() != null && timeBlock.getEndTime() != null && StringUtils.equals(timeBlock.getEarnCodeType(), HrConstants.EARN_CODE_TIME)) {
350 BigDecimal hours = TKUtils.getHoursBetween(timeBlock.getBeginDateTime().getMillis(), timeBlock.getEndDateTime().getMillis());
351
352
353
354
355 if (earnCodeObj.getInflateMinHours() != null) {
356 if ((earnCodeObj.getInflateMinHours().compareTo(BigDecimal.ZERO) != 0) &&
357 earnCodeObj.getInflateMinHours().compareTo(hours) > 0) {
358
359 if(previousTimeBlock != null && StringUtils.equals(earnCodeObj.getEarnCode(),previousTimeBlock.getEarnCode()) &&
360 (timeBlock.getBeginDateTime().getMillis() - previousTimeBlock.getEndDateTime().getMillis() == 0L)) {
361 BigDecimal prevTimeBlockHours = TKUtils.getHoursBetween(previousTimeBlock.getBeginDateTime().getMillis(), previousTimeBlock.getEndDateTime().getMillis());
362 previousTimeBlock.setHours(prevTimeBlockHours);
363 }
364 }
365 }
366
367 timeBlock.setHours(hours);
368 }
369 }
370
371 @Override
372 public List<TimeBlock> resetTimeBlock(List<TimeBlock> timeBlocks, LocalDate asOfDate) {
373 TimeBlock.Builder previous = null;
374 List<TimeBlock.Builder> builders = ModelObjectUtils.transform(timeBlocks, toTimeBlockBuilder);
375 for (TimeBlock.Builder tb : builders) {
376 resetWorkedHours(previous, tb, asOfDate);
377 previous = tb;
378 }
379 return TkServiceLocator.getTimeBlockService().resetTimeHourDetail(ModelObjectUtils.<TimeBlock>buildImmutableCopy(builders));
380 }
381
382 @Override
383 public boolean isReadyToApprove(TimesheetDocument document) {
384 if (document == null) {
385 return false;
386 }
387 List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
388 document.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate(), document.getCalendarEntry().getEndPeriodFullDateTime().toLocalDate(), LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER);
389 leaveBlocks.addAll(LmServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
390 document.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate(), document.getCalendarEntry().getEndPeriodFullDateTime().toLocalDate(), LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT));
391 for(LeaveBlock lb : leaveBlocks) {
392 if(!StringUtils.equals(lb.getRequestStatus(),HrConstants.REQUEST_STATUS.APPROVED) &&
393 !StringUtils.equals(lb.getRequestStatus(), HrConstants.REQUEST_STATUS.DISAPPROVED))
394 return false;
395 }
396 return true;
397 }
398
399 @Override
400 public List<EarnCode> getEarnCodesForTime(Assignment a, LocalDate asOfDate, boolean includeRegularEarnCode) {
401
402
403 if (a == null) {
404 LOG.error("No assignment parameter.");
405 return null;
406
407 }
408 JobContract job = a.getJob();
409 if (job == null || job.getPayTypeObj() == null) {
410 LOG.error("Null job or null job pay type on assignment.");
411 return null;
412
413 }
414
415 List<EarnCode> earnCodes = new LinkedList<EarnCode>();
416 String earnTypeCode = EarnCodeType.TIME.getCode();
417
418 TimeCollectionRule tcr = null;
419 if(a.getJob() != null) {
420 tcr = TkServiceLocator.getTimeCollectionRuleService().getTimeCollectionRule(a.getDept(), a.getWorkArea(), a.getJob().getHrPayType(), a.getGroupKeyCode(), asOfDate);
421 }
422 boolean isClockUser = tcr == null || tcr.isClockUserFl();
423 boolean isUsersTimesheet = StringUtils.equals(HrContext.getPrincipalId(),a.getPrincipalId());
424
425
426 EarnCode regularEarnCode = HrServiceLocator.getEarnCodeService().getEarnCode(job.getPayTypeObj().getRegEarnCode(), asOfDate);
427 if (regularEarnCode == null) {
428 LOG.error("No regular earn code defined for job pay type.");
429 return null;
430
431 } else {
432
433 if (!isClockUser || !isUsersTimesheet || includeRegularEarnCode) {
434 earnCodes.add(regularEarnCode);
435 }
436 }
437
438 List<String> listAccrualCategories = new LinkedList<String>();
439 String accrualCategory;
440
441
442 PrincipalHRAttributes principalHRAttributes = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(job.getPrincipalId(), asOfDate);
443 boolean fmlaEligible = principalHRAttributes.isFmlaEligible();
444 boolean workersCompEligible = principalHRAttributes.isWorkersCompEligible();
445
446 String leavePlan = principalHRAttributes.getLeavePlan();
447 if (leavePlan != null) {
448 for (AccrualCategory accrualCategories : HrServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(leavePlan, asOfDate)) {
449 accrualCategory = accrualCategories.getAccrualCategory();
450 if(accrualCategory != null) {
451 listAccrualCategories.add(accrualCategory);
452 }
453 }
454 }
455
456
457 List<EarnCodeSecurity> decs = HrServiceLocator.getEarnCodeSecurityService().getEarnCodeSecurities(job.getDept(), job.getHrSalGroup(), asOfDate, job.getGroupKey().getGroupKeyCode());
458 for (EarnCodeSecurity dec : decs) {
459
460 boolean addEarnCode = HrServiceLocator.getEarnCodeService().addEarnCodeBasedOnEmployeeApproverSettings(dec, a, asOfDate);
461 if (addEarnCode) {
462
463
464 if (earnTypeCode.equals(dec.getEarnCodeType()) || EarnCodeType.BOTH.getCode().equals(dec.getEarnCodeType())) {
465 EarnCode ec = HrServiceLocator.getEarnCodeService().getEarnCode(dec.getEarnCode(), asOfDate);
466
467
468 if (ec != null) {
469
470
471
472 if( (StringUtils.isNotBlank(leavePlan) && StringUtils.isBlank(ec.getLeavePlan()))
473 || (StringUtils.isNotBlank(leavePlan) && StringUtils.isNotBlank(ec.getLeavePlan()) && StringUtils.equals(leavePlan, ec.getLeavePlan()))
474 || (StringUtils.isBlank(leavePlan) && StringUtils.isBlank(ec.getLeavePlan()))) {
475
476
477
478
479
480
481
482
483 if ( (fmlaEligible || ec.getFmla().equals("N")) ) {
484 if (StringUtils.isEmpty(ec.getAccrualCategory())
485 || (listAccrualCategories.contains(ec.getAccrualCategory())
486 && HrConstants.ACCRUAL_BALANCE_ACTION.USAGE.equals(ec.getAccrualBalanceAction()))) {
487
488
489
490 if ( (workersCompEligible || ec.getWorkmansComp().equals("N")) ) {
491
492
493
494 if ( showEarnCodeIfHoliday(ec, dec) ) {
495
496
497
498 if (!StringUtils.equals(regularEarnCode.getEarnCode(), dec.getEarnCode()) ) {
499
500 earnCodes.add(ec);
501 }
502 }
503 }
504 }
505 }
506 }
507 }
508 }
509 }
510 }
511
512 return earnCodes;
513 }
514
515 public List<EarnCode> getEarnCodesForTime(Assignment a, LocalDate asOfDate) {
516 return getEarnCodesForTime(a, asOfDate, false);
517 }
518
519 private boolean showEarnCodeIfHoliday(EarnCode earnCode, EarnCodeSecurityContract security) {
520 if (earnCode.getEarnCode().equals(HrConstants.HOLIDAY_EARN_CODE)) {
521 if (security.isApprover() || HrContext.isSystemAdmin()) {
522 return true;
523 } else {
524 return false;
525 }
526 } else {
527 return true;
528 }
529 }
530
531 public HRPermissionService getHRPermissionService() {
532 if (hrPermissionService == null) {
533 hrPermissionService = HrServiceLocator.getHRPermissionService();
534 }
535 return hrPermissionService;
536 }
537
538 private void clearTimesheetTimeblockPermissions(TimesheetDocument doc) {
539 for (TimeBlock tb : doc.getTimeBlocks()) {
540 getHRPermissionService().updateTimeBlockPermissions(CalendarBlockPermissions.newInstance(tb.getTkTimeBlockId()));
541 }
542 }
543
544 public boolean isTimesheetValid(TimesheetDocument td) {
545 boolean isTimeSheetValid = true;
546
547 if (WorkflowTagSupport.isTimesheetApprovalButtonsDisplaying(td.getDocumentId())) {
548 if (CollectionUtils.isNotEmpty(validateHours(td)) || CollectionUtils.isNotEmpty(validateHours(td))) {
549 isTimeSheetValid = false ;
550 }
551 }
552 return isTimeSheetValid;
553 }
554
555 public List<String> validateTimeBlock(TimesheetDocument td) {
556 List<String> errors = new ArrayList<String>();
557 if (td != null) {
558
559 Map<String, String> earnCodeTypeMap = new HashMap<String, String>();
560
561
562 for (TimeBlock timeBlock : td.getTimeBlocks()) {
563 String earnCode = timeBlock.getEarnCode();
564 if (earnCodeTypeMap.containsKey(earnCode)) {
565 continue;
566 } else {
567 EarnCodeContract earnCodeObj = HrServiceLocator.getEarnCodeService().getEarnCode(earnCode, td.getAsOfDate());
568 if (earnCodeObj != null) {
569 earnCodeTypeMap.put(earnCodeObj.getEarnCode(),earnCodeObj.getEarnCodeType());
570 }
571 }
572 }
573
574 for (TimeBlock timeBlock : td.getTimeBlocks()) {
575 DateTime beginDate = timeBlock.getBeginDateTime();
576 String earnCodeType = earnCodeTypeMap.get(timeBlock.getEarnCode());
577 if (earnCodeType != null && HrConstants.EARN_CODE_TIME.equals(earnCodeType)) {
578 String timeBlockDesc = "TimeBlock (" + timeBlock.getTkTimeBlockId() + ") on " + DateTimeFormat.forPattern("EEE MMM d").print(timeBlock.getBeginDateTime()) + " from " + timeBlock.getBeginTimeDisplayTimeOnlyString() + " - " + timeBlock.getEndTimeDisplayTimeOnlyString();
579 for (TimeBlock compareTimeBlock : td.getTimeBlocks()) {
580 if(compareTimeBlock.getTkTimeBlockId()!=null && timeBlock.getTkTimeBlockId()!=null){
581 if (compareTimeBlock.getTkTimeBlockId().equals(timeBlock.getTkTimeBlockId())) {
582 continue;
583 }
584 }
585 String compareEarnCodeType = earnCodeTypeMap.get(compareTimeBlock.getEarnCode());
586 if (compareEarnCodeType != null && HrConstants.EARN_CODE_TIME.equals(compareEarnCodeType)) {
587 String compareTimeBlockDesc = "TimeBlock (" + compareTimeBlock.getTkTimeBlockId() + ") on " + DateTimeFormat.forPattern("EEE MMM d").print(compareTimeBlock.getBeginDateTime()) + " from " + compareTimeBlock.getBeginTimeDisplayTimeOnlyString() + " - " + compareTimeBlock.getEndTimeDisplayTimeOnlyString();
588 Interval compareTimeBlockInterval = new Interval(compareTimeBlock.getBeginDateTime(), compareTimeBlock.getEndDateTime());
589 if (compareTimeBlockInterval.contains(beginDate.getMillis())) {
590 errors.add("Error : [" + timeBlockDesc + " overlaps with " + compareTimeBlockDesc + ".]" );
591 }
592 }
593 }
594
595 List<String> assignmentKeyList = new ArrayList<String>();
596
597
598 for (Assignment assignment : td.getAssignmentMap().get(timeBlock.getBeginDateTime().toLocalDate())) {
599 assignmentKeyList.add(assignment.getAssignmentKey());
600 }
601 if (!assignmentKeyList.contains(timeBlock.getAssignmentKey())) {
602 errors.add("Error: [" + timeBlockDesc + " contains an invalid assignment.]");
603 }
604 }
605 }
606 }
607 return errors;
608 }
609
610 public List<String> validateHours(TimesheetDocument timesheetDocument) {
611 List<String> errors = new ArrayList<String>();
612
613
614 if (timesheetDocument != null && WorkflowTagSupport.isTimesheetApprovalButtonsDisplaying(timesheetDocument.getDocumentId())) {
615 DateTimeZone userTimeZone = DateTimeZone.forID(HrServiceLocator.getTimezoneService().getUserTimezone(timesheetDocument.getPrincipalId()));
616
617 if (userTimeZone == null) {
618 userTimeZone = HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback();
619 }
620 String assignmentDesc = "";
621 for (Assignment assignment : timesheetDocument.getAllAssignments()) {
622
623 BigDecimal standardHours = assignment.getJob().getStandardHours();
624 LocalDate jobStartDate = assignment.getJob().getEffectiveLocalDate();
625
626 if (standardHours.compareTo(new BigDecimal(0)) == 0) {
627 continue;
628 }
629
630
631 List<TimeBlock> assignmentTimeBlocks = new ArrayList<TimeBlock>();
632 for (TimeBlock timeBlock : timesheetDocument.getTimeBlocks()) {
633 if (timeBlock.getAssignmentKey().equals(assignment.getAssignmentKey())) {
634 assignmentTimeBlocks.add(timeBlock);
635 }
636 }
637
638 TkTimeBlockAggregate tkTimeBlockAggregate = new TkTimeBlockAggregate(assignmentTimeBlocks, timesheetDocument.getCalendarEntry(), HrServiceLocator.getCalendarService().getCalendar(timesheetDocument.getCalendarEntry().getHrCalendarId()), true);
639
640
641 List<String> assigmentKeyList = new ArrayList<String>();
642 assigmentKeyList.add(assignment.getAssignmentKey());
643
644 List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(timesheetDocument.getPrincipalId(),
645 timesheetDocument.getCalendarEntry().getBeginPeriodFullDateTime().toLocalDate(), timesheetDocument.getCalendarEntry().getEndPeriodFullDateTime().toLocalDate(), assigmentKeyList);
646 LeaveBlockAggregate leaveBlockAggregate = new LeaveBlockAggregate(leaveBlocks, timesheetDocument.getCalendarEntry());
647
648
649 tkTimeBlockAggregate = TkTimeBlockAggregate.combineTimeAndLeaveAggregates(tkTimeBlockAggregate, leaveBlockAggregate);
650
651 Map<String, BigDecimal> flsaWeekTotal = getAssignmentHoursToFlsaWeekMap(tkTimeBlockAggregate, timesheetDocument.getPrincipalId(), assignment.getAssignmentKey(), jobStartDate, userTimeZone);
652
653 for (Map.Entry<String, BigDecimal> entry : flsaWeekTotal.entrySet()) {
654 if (standardHours.compareTo(entry.getValue()) > 0) {
655 errors.add("Error: [" + assignment.getAssignmentDescription() + " expected " + standardHours + " hours for " + entry.getKey() + " only " + entry.getValue() + " hours were entered.]");
656 }
657 }
658 }
659
660 }
661 return errors;
662 }
663
664 private Map<String, BigDecimal> getAssignmentHoursToFlsaWeekMap(TkTimeBlockAggregate tkTimeBlockAggregate, String principalId, String assignmentKey, LocalDate jobStartDate, DateTimeZone userTimeZone) {
665
666 Map<String, BigDecimal> hoursToFlsaWeekMap = new LinkedHashMap<String, BigDecimal>();
667 List<List<FlsaWeek>> flsaWeeks = tkTimeBlockAggregate.getFlsaWeeks(userTimeZone, principalId);
668
669
670 int weekCount = 1;
671 for (List<FlsaWeek> flsaWeekParts : flsaWeeks) {
672 boolean printWeek = true;
673 BigDecimal weekTotal = new BigDecimal(0.00);
674 FlsaWeek lastWeekPart = CollectionUtils.isNotEmpty(flsaWeekParts) ? flsaWeekParts.get(flsaWeekParts.size() - 1) : null;
675 for (FlsaWeek flsaWeekPart : flsaWeekParts) {
676
677
678 if (flsaWeekPart == lastWeekPart) {
679 Integer lastFlsaDayOfWeek = flsaWeekPart.getFlsaDays().get(flsaWeekPart.getFlsaDays().size() - 1).getFlsaDate().getDayOfWeek();
680
681 Integer flsaWeekEndDayOfWeek = TkConstants.FLSA_WEEK_END_DAY.get(tkTimeBlockAggregate.getPayCalendar().getFlsaBeginDay());
682
683 if (lastFlsaDayOfWeek.compareTo(flsaWeekEndDayOfWeek) != 0) {
684 printWeek = false;
685 weekCount++;
686 continue;
687 }
688 }
689
690
691 if (flsaWeekPart.getFlsaDays().get(0).getFlsaDate().toLocalDate().isBefore(jobStartDate) ) {
692 printWeek = false;
693 weekCount++;
694 continue;
695 }
696
697 for (FlsaDay flsaDay : flsaWeekPart.getFlsaDays()) {
698
699 for (TimeBlock timeBlock : flsaDay.getAppliedTimeBlocks()) {
700 if (assignmentKey != null) {
701 if (timeBlock.getAssignmentKey().compareTo(assignmentKey) == 0) {
702 weekTotal = weekTotal.add(timeBlock.getHours(), HrConstants.MATH_CONTEXT);
703 } else {
704 weekTotal = weekTotal.add(new BigDecimal("0"), HrConstants.MATH_CONTEXT);
705 }
706 } else {
707 weekTotal = weekTotal.add(timeBlock.getHours(), HrConstants.MATH_CONTEXT);
708 }
709 }
710 }
711 }
712
713 if (printWeek) {
714 hoursToFlsaWeekMap.put("Week " + weekCount++, weekTotal);
715 }
716 }
717
718 return hoursToFlsaWeekMap;
719 }
720 }