1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.hr.time.timesheet.service;
17
18 import java.math.BigDecimal;
19 import java.sql.Timestamp;
20 import java.util.ArrayList;
21 import java.util.Date;
22 import java.util.LinkedList;
23 import java.util.List;
24
25 import org.apache.commons.collections.CollectionUtils;
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.log4j.Logger;
28 import org.joda.time.DateTime;
29 import org.kuali.hr.job.Job;
30 import org.kuali.hr.lm.LMConstants;
31 import org.kuali.hr.lm.balancetransfer.BalanceTransfer;
32 import org.kuali.hr.lm.leaveblock.LeaveBlock;
33 import org.kuali.hr.lm.leavepayout.LeavePayout;
34 import org.kuali.hr.lm.timeoff.SystemScheduledTimeOff;
35 import org.kuali.hr.time.assignment.Assignment;
36 import org.kuali.hr.time.calendar.CalendarEntries;
37 import org.kuali.hr.time.principal.PrincipalHRAttributes;
38 import org.kuali.hr.time.service.base.TkServiceLocator;
39 import org.kuali.hr.time.timeblock.TimeBlock;
40 import org.kuali.hr.time.timesheet.TimesheetDocument;
41 import org.kuali.hr.time.util.TKContext;
42 import org.kuali.hr.time.util.TKUser;
43 import org.kuali.hr.time.util.TKUtils;
44 import org.kuali.hr.time.util.TkConstants;
45 import org.kuali.hr.time.workflow.TimesheetDocumentHeader;
46 import org.kuali.rice.core.api.config.property.ConfigContext;
47 import org.kuali.rice.kew.api.KewApiServiceLocator;
48 import org.kuali.rice.kew.api.WorkflowDocument;
49 import org.kuali.rice.kew.api.WorkflowDocumentFactory;
50 import org.kuali.rice.kew.api.exception.WorkflowException;
51 import org.kuali.rice.kew.api.note.Note;
52 import org.kuali.rice.kim.api.identity.Person;
53 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
54
55 public class TimesheetServiceImpl implements TimesheetService {
56
57 @SuppressWarnings("unused")
58 private static final Logger LOG = Logger.getLogger(TimesheetServiceImpl.class);
59
60 @Override
61 public void routeTimesheet(String principalId, TimesheetDocument timesheetDocument) {
62 routeTimesheet(principalId, timesheetDocument, TkConstants.DOCUMENT_ACTIONS.ROUTE);
63 }
64
65 @Override
66 public void routeTimesheet(String principalId, TimesheetDocument timesheetDocument, String action) {
67 timesheetAction(action, principalId, timesheetDocument);
68 }
69
70 @Override
71 public void approveTimesheet(String principalId, TimesheetDocument timesheetDocument) {
72 timesheetAction(TkConstants.DOCUMENT_ACTIONS.APPROVE, principalId, timesheetDocument);
73 }
74
75 @Override
76 public void approveTimesheet(String principalId, TimesheetDocument timesheetDocument, String action) {
77 timesheetAction(action, principalId, timesheetDocument);
78 }
79
80 @Override
81 public void disapproveTimesheet(String principalId, TimesheetDocument timesheetDocument) {
82 timesheetAction(TkConstants.DOCUMENT_ACTIONS.DISAPPROVE, principalId, timesheetDocument);
83 }
84
85 protected void timesheetAction(String action, String principalId, TimesheetDocument timesheetDocument) {
86 WorkflowDocument wd = null;
87 if (timesheetDocument != null) {
88 String rhid = timesheetDocument.getDocumentId();
89 wd = WorkflowDocumentFactory.loadDocument(principalId, rhid);
90
91 if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.ROUTE)) {
92 wd.route("Routing for Approval");
93 } else if (StringUtils.equals(action, TkConstants.BATCH_JOB_ACTIONS.BATCH_JOB_ROUTE)) {
94 Note.Builder builder = Note.Builder.create(rhid, principalId);
95 builder.setCreateDate(new DateTime());
96 builder.setText("Routed via Employee Approval batch job");
97 KewApiServiceLocator.getNoteService().createNote(builder.build());
98
99 wd.route("Batch job routing timesheet");
100 } else if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.APPROVE)) {
101 if (TKContext.getUser().getCurrentTargetRoles().isSystemAdmin() &&
102 !TKContext.getUser().getCurrentTargetRoles().isApproverForTimesheet(timesheetDocument)) {
103 wd.superUserBlanketApprove("Superuser approving timesheet.");
104 } else {
105 wd.approve("Approving timesheet.");
106 }
107 } else if (StringUtils.equals(action, TkConstants.BATCH_JOB_ACTIONS.BATCH_JOB_APPROVE)) {
108 Note.Builder builder = Note.Builder.create(rhid, principalId);
109 builder.setCreateDate(new DateTime());
110 builder.setText("Approved via Supervisor Approval batch job");
111 KewApiServiceLocator.getNoteService().createNote(builder.build());
112
113 wd.superUserBlanketApprove("Batch job approving timesheet.");
114 } else if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.DISAPPROVE)) {
115 if (TKContext.getUser().getCurrentTargetRoles().isSystemAdmin()
116 && !TKContext.getUser().getCurrentTargetRoles().isApproverForTimesheet(timesheetDocument)) {
117 wd.superUserDisapprove("Superuser disapproving timesheet.");
118 } else {
119 wd.disapprove("Disapproving timesheet.");
120 }
121 }
122 }
123 }
124
125 @Override
126 public TimesheetDocument openTimesheetDocument(String principalId, CalendarEntries calendarDates) throws WorkflowException {
127 TimesheetDocument timesheetDocument = null;
128
129 Date begin = calendarDates.getBeginPeriodDateTime();
130 Date end = calendarDates.getEndPeriodDateTime();
131
132 TimesheetDocumentHeader header = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, begin, end);
133
134 if (header == null) {
135 List<Assignment> activeAssignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForTimeCalendar(principalId, calendarDates);
136
137 if (activeAssignments.size() == 0) {
138 LOG.warn("No active assignments for " + principalId + " for " + calendarDates.getEndPeriodDate());
139 return null;
140
141 }
142
143 Person person = KimApiServiceLocator.getPersonService().getPerson(principalId);
144 String principalName = person != null ? person.getName() : StringUtils.EMPTY;
145 String endDateString = TKUtils.formatDate(new java.sql.Date(end.getTime()));
146 String timesheetDocumentTitle = TimesheetDocument.TIMESHEET_DOCUMENT_TYPE + " - " + principalName + " (" + principalId + ") - " + endDateString;
147
148 timesheetDocument = this.initiateWorkflowDocument(principalId, begin, end, calendarDates, TimesheetDocument.TIMESHEET_DOCUMENT_TYPE, timesheetDocumentTitle);
149
150
151
152
153 } else {
154 timesheetDocument = this.getTimesheetDocument(header.getDocumentId());
155 timesheetDocument.setCalendarEntry(calendarDates);
156 }
157
158 timesheetDocument.setTimeSummary(TkServiceLocator.getTimeSummaryService().getTimeSummary(timesheetDocument));
159 return timesheetDocument;
160 }
161
162 public void loadHolidaysOnTimesheet(TimesheetDocument timesheetDocument, String principalId, Date beginDate, Date endDate) {
163 PrincipalHRAttributes principalCalendar = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, new java.sql.Date(beginDate.getTime()));
164 if (principalCalendar != null && StringUtils.isNotEmpty(principalCalendar.getLeavePlan())) {
165 List<SystemScheduledTimeOff> sstoList = TkServiceLocator.getSysSchTimeOffService()
166 .getSystemScheduledTimeOffForPayPeriod(principalCalendar.getLeavePlan(), beginDate, endDate);
167 Assignment sstoAssign = TkServiceLocator.getAssignmentService().getAssignmentToApplyScheduledTimeOff(timesheetDocument, TKUtils.getTimelessDate(endDate));
168 if (sstoAssign != null) {
169 for(SystemScheduledTimeOff ssto : sstoList) {
170 BigDecimal sstoCalcHours = TkServiceLocator.getSysSchTimeOffService().calculateSysSchTimeOffHours(sstoAssign.getJob(), ssto.getAmountofTime());
171 TimeBlock timeBlock = TkServiceLocator.getTimeBlockService().createTimeBlock(timesheetDocument, new Timestamp(ssto.getScheduledTimeOffDate().getTime()),
172 new Timestamp(ssto.getScheduledTimeOffDate().getTime()), sstoAssign, TkConstants.HOLIDAY_EARN_CODE, sstoCalcHours, BigDecimal.ZERO, false, false, TKContext.getPrincipalId());
173 timesheetDocument.getTimeBlocks().add(timeBlock);
174 }
175
176 if (CollectionUtils.isNotEmpty(sstoList)) {
177 TkServiceLocator.getTimeBlockService().saveTimeBlocks(new LinkedList<TimeBlock>(), timesheetDocument.getTimeBlocks(), TKContext.getPrincipalId());
178 }
179 }
180 }
181 }
182
183 protected TimesheetDocument initiateWorkflowDocument(String principalId, Date payBeginDate, Date payEndDate, CalendarEntries calendarEntries, String documentType, String title) throws WorkflowException {
184 TimesheetDocument timesheetDocument = null;
185 WorkflowDocument workflowDocument = null;
186
187 workflowDocument = WorkflowDocumentFactory.createDocument(principalId, documentType, title);
188
189 String status = workflowDocument.getStatus().getCode();
190 TimesheetDocumentHeader documentHeader = new TimesheetDocumentHeader(workflowDocument.getDocumentId(), principalId, payBeginDate, payEndDate, status);
191
192 documentHeader.setDocumentId(workflowDocument.getDocumentId().toString());
193 documentHeader.setDocumentStatus("I");
194
195 TkServiceLocator.getTimesheetDocumentHeaderService().saveOrUpdate(documentHeader);
196 timesheetDocument = new TimesheetDocument(documentHeader);
197 timesheetDocument.setCalendarEntry(calendarEntries);
198 loadTimesheetDocumentData(timesheetDocument, principalId, calendarEntries);
199 TkServiceLocator.getTkSearchableAttributeService().updateSearchableAttribute(timesheetDocument, payEndDate);
200
201 if (TkServiceLocator.getLeaveApprovalService().isActiveAssignmentFoundOnJobFlsaStatus(principalId, TkConstants.FLSA_STATUS_NON_EXEMPT, true)) {
202 deleteNonApprovedLeaveBlocks(principalId, calendarEntries.getBeginPeriodDate(), calendarEntries.getEndPeriodDate());
203 }
204
205 return timesheetDocument;
206 }
207
208 private void deleteNonApprovedLeaveBlocks(String principalId, Date beginDate, Date endDate) {
209 String batchUserPrincipalId = getBatchUserPrincipalId();
210
211 if (batchUserPrincipalId != null) {
212 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocks(principalId, beginDate, endDate);
213
214 for (LeaveBlock leaveBlock : leaveBlocks) {
215 if (!StringUtils.equals(leaveBlock.getRequestStatus(), LMConstants.REQUEST_STATUS.APPROVED)) {
216 TkServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlock.getLmLeaveBlockId(), batchUserPrincipalId);
217 }
218 }
219 } else {
220 String principalName = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.BATCH_USER_PRINCIPAL_NAME);
221 LOG.error("Could not delete leave request blocks due to missing batch user " + principalName);
222 }
223 }
224
225 private String getBatchUserPrincipalId() {
226 String principalId = null;
227
228 String principalName = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.BATCH_USER_PRINCIPAL_NAME);
229 Person person = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(principalName);
230 if (person != null) {
231 principalId = person.getPrincipalId();
232 }
233
234 return principalId;
235 }
236
237 public List<TimeBlock> getPrevDocumentTimeBlocks(String principalId, Date payBeginDate) {
238 TimesheetDocumentHeader prevTdh = TkServiceLocator.getTimesheetDocumentHeaderService().getPreviousDocumentHeader(principalId, payBeginDate);
239 if (prevTdh == null) {
240 return new ArrayList<TimeBlock>();
241 }
242 return TkServiceLocator.getTimeBlockService().getTimeBlocks(prevTdh.getDocumentId());
243 }
244
245 @Override
246 public TimesheetDocument getTimesheetDocument(String documentId) {
247 TimesheetDocument timesheetDocument = null;
248 TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(documentId);
249
250 if (tdh != null) {
251 timesheetDocument = new TimesheetDocument(tdh);
252 CalendarEntries pce = TkServiceLocator.getCalendarService().getCalendarDatesByPayEndDate(tdh.getPrincipalId(), tdh.getEndDate(), TkConstants.PAY_CALENDAR_TYPE);
253 loadTimesheetDocumentData(timesheetDocument, tdh.getPrincipalId(), pce);
254
255 timesheetDocument.setCalendarEntry(pce);
256 } else {
257 throw new RuntimeException("Could not find TimesheetDocumentHeader for DocumentID: " + documentId);
258 }
259 return timesheetDocument;
260 }
261
262 protected void loadTimesheetDocumentData(TimesheetDocument tdoc, String principalId, CalendarEntries payCalEntry) {
263 List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForTimeCalendar(principalId, payCalEntry);
264 List<Job> jobs = TkServiceLocator.getJobService().getJobs(principalId, TKUtils.getTimelessDate(payCalEntry.getEndPeriodDate()));
265 List<TimeBlock> timeBlocks = TkServiceLocator.getTimeBlockService().getTimeBlocks(tdoc.getDocumentHeader().getDocumentId());
266
267 tdoc.setAssignments(assignments);
268 tdoc.setJobs(jobs);
269 tdoc.setTimeBlocks(timeBlocks);
270 }
271
272 public boolean isSynchronousUser() {
273 List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignments(TKUser.getCurrentTargetPerson().getPrincipalId(), TKUtils.getCurrentDate());
274 boolean isSynchronousUser = true;
275 for (Assignment assignment : assignments) {
276 isSynchronousUser &= assignment.isSynchronous();
277 }
278 return isSynchronousUser;
279 }
280
281
282 public void deleteTimesheet(String documentId) {
283 TkServiceLocator.getTimeBlockService().deleteTimeBlocksAssociatedWithDocumentId(documentId);
284 TkServiceLocator.getTimesheetDocumentHeaderService().deleteTimesheetHeader(documentId);
285 }
286
287 public TimeBlock resetWorkedHours(TimeBlock timeBlock) {
288 if (timeBlock.getBeginTime() != null && timeBlock.getEndTime() != null && StringUtils.equals(timeBlock.getEarnCodeType(), TkConstants.EARN_CODE_TIME)) {
289 BigDecimal hours = TKUtils.getHoursBetween(timeBlock.getBeginTime().getTime(), timeBlock.getEndTime().getTime());
290 timeBlock.setHours(hours);
291 }
292 return timeBlock;
293 }
294
295 @Override
296 public void resetTimeBlock(List<TimeBlock> timeBlocks) {
297 for (TimeBlock tb : timeBlocks) {
298 resetWorkedHours(tb);
299 }
300 TkServiceLocator.getTimeBlockService().resetTimeHourDetail(timeBlocks);
301 }
302
303 @Override
304 public boolean isReadyToApprove(TimesheetDocument document) {
305 if (document == null) {
306 return false;
307 }
308 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
309 document.getCalendarEntry().getBeginPeriodDate(), document.getCalendarEntry().getEndPeriodDate(), LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER);
310 leaveBlocks.addAll(TkServiceLocator.getLeaveBlockService().getLeaveBlocksWithType(document.getPrincipalId(),
311 document.getCalendarEntry().getBeginPeriodDate(), document.getCalendarEntry().getEndPeriodDate(), LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT));
312 for(LeaveBlock lb : leaveBlocks) {
313 if(!StringUtils.equals(lb.getRequestStatus(),LMConstants.REQUEST_STATUS.APPROVED) &&
314 !StringUtils.equals(lb.getRequestStatus(), LMConstants.REQUEST_STATUS.DISAPPROVED))
315 return false;
316 }
317 return true;
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345 }
346
347 }