001 /** 002 * Copyright 2004-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.hr.time.timesheet.service; 017 018 import java.math.BigDecimal; 019 import java.sql.Timestamp; 020 import java.util.ArrayList; 021 import java.util.Date; 022 import java.util.LinkedList; 023 import java.util.List; 024 025 import org.apache.commons.lang.StringUtils; 026 import org.apache.log4j.Logger; 027 import org.kuali.hr.job.Job; 028 import org.kuali.hr.time.assignment.Assignment; 029 import org.kuali.hr.time.calendar.CalendarEntries; 030 import org.kuali.hr.time.holidaycalendar.HolidayCalendar; 031 import org.kuali.hr.time.holidaycalendar.HolidayCalendarDateEntry; 032 import org.kuali.hr.time.principal.PrincipalHRAttributes; 033 import org.kuali.hr.time.service.base.TkServiceLocator; 034 import org.kuali.hr.time.timeblock.TimeBlock; 035 import org.kuali.hr.time.timesheet.TimesheetDocument; 036 import org.kuali.hr.time.util.TKContext; 037 import org.kuali.hr.time.util.TKUser; 038 import org.kuali.hr.time.util.TKUtils; 039 import org.kuali.hr.time.util.TkConstants; 040 import org.kuali.hr.time.workflow.TimesheetDocumentHeader; 041 import org.kuali.rice.kew.api.WorkflowDocument; 042 import org.kuali.rice.kew.api.WorkflowDocumentFactory; 043 import org.kuali.rice.kew.api.exception.WorkflowException; 044 import org.kuali.rice.kew.service.KEWServiceLocator; 045 import org.kuali.rice.kim.api.identity.Person; 046 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 047 048 public class TimesheetServiceImpl implements TimesheetService { 049 050 @SuppressWarnings("unused") 051 private static final Logger LOG = Logger.getLogger(TimesheetServiceImpl.class); 052 053 @Override 054 public void routeTimesheet(String principalId, TimesheetDocument timesheetDocument) { 055 routeTimesheet(TkConstants.DOCUMENT_ACTIONS.ROUTE, principalId, timesheetDocument); 056 } 057 058 @Override 059 public void routeTimesheet(String action, String principalId, TimesheetDocument timesheetDocument) { 060 timesheetAction(action, principalId, timesheetDocument); 061 } 062 063 @Override 064 public void approveTimesheet(String principalId, TimesheetDocument timesheetDocument) { 065 timesheetAction(TkConstants.DOCUMENT_ACTIONS.APPROVE, principalId, timesheetDocument); 066 } 067 068 @Override 069 public void approveTimesheet(String principalId, TimesheetDocument timesheetDocument, String action) { 070 timesheetAction(action, principalId, timesheetDocument); 071 } 072 073 @Override 074 public void disapproveTimesheet(String principalId, TimesheetDocument timesheetDocument) { 075 timesheetAction(TkConstants.DOCUMENT_ACTIONS.DISAPPROVE, principalId, timesheetDocument); 076 } 077 078 protected void timesheetAction(String action, String principalId, TimesheetDocument timesheetDocument) { 079 WorkflowDocument wd = null; 080 if (timesheetDocument != null) { 081 String rhid = timesheetDocument.getDocumentId(); 082 wd = WorkflowDocumentFactory.loadDocument(principalId, rhid); 083 084 if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.ROUTE)) { 085 wd.route("Routing for Approval"); 086 } else if (StringUtils.equals(action, TkConstants.BATCH_JOB_ACTIONS.BATCH_JOB_ROUTE)) { 087 wd.route("Batch job routing for Approval"); 088 } else if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.APPROVE)) { 089 if (TKContext.getUser().getCurrentTargetRoles().isSystemAdmin() && 090 !TKContext.getUser().getCurrentTargetRoles().isApproverForTimesheet(timesheetDocument)) { 091 wd.superUserBlanketApprove("Superuser approving timesheet."); 092 } else { 093 wd.approve("Approving timesheet."); 094 } 095 } else if (StringUtils.equals(action, TkConstants.BATCH_JOB_ACTIONS.BATCH_JOB_APPROVE)) { 096 wd.superUserBlanketApprove("Batch job superuser approving timesheet."); 097 } else if (StringUtils.equals(action, TkConstants.DOCUMENT_ACTIONS.DISAPPROVE)) { 098 if (TKContext.getUser().getCurrentTargetRoles().isSystemAdmin() 099 && !TKContext.getUser().getCurrentTargetRoles().isApproverForTimesheet(timesheetDocument)) { 100 wd.superUserDisapprove("Superuser disapproving timesheet."); 101 } else { 102 wd.disapprove("Disapproving timesheet."); 103 } 104 } 105 106 String kewStatus = KEWServiceLocator.getRouteHeaderService().getDocumentStatus(timesheetDocument.getDocumentId()); 107 if (!kewStatus.equals(timesheetDocument.getDocumentHeader().getDocumentStatus())) { 108 timesheetDocument.getDocumentHeader().setDocumentStatus(kewStatus); 109 TkServiceLocator.getTimesheetDocumentHeaderService().saveOrUpdate(timesheetDocument.getDocumentHeader()); 110 } 111 112 } 113 } 114 115 @Override 116 public TimesheetDocument openTimesheetDocument(String principalId, CalendarEntries calendarDates) throws WorkflowException { 117 TimesheetDocument timesheetDocument = null; 118 119 Date begin = calendarDates.getBeginPeriodDateTime(); 120 Date end = calendarDates.getEndPeriodDateTime(); 121 122 TimesheetDocumentHeader header = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, begin, end); 123 124 if (header == null) { 125 List<Assignment> activeAssignments = TkServiceLocator.getAssignmentService().getAssignmentsByPayEntry(principalId, calendarDates); 126 //TkServiceLocator.getAssignmentService().getAssignments(principalId, TKUtils.getTimelessDate(payCalendarDates.getEndPeriodDate())); 127 if (activeAssignments.size() == 0) { 128 throw new RuntimeException("No active assignments for " + principalId + " for " + calendarDates.getEndPeriodDate()); 129 } 130 131 Person person = KimApiServiceLocator.getPersonService().getPerson(principalId); 132 String principalName = person != null ? person.getName() : StringUtils.EMPTY; 133 String endDateString = TKUtils.formatDate(new java.sql.Date(end.getTime())); 134 String timesheetDocumentTitle = TimesheetDocument.TIMESHEET_DOCUMENT_TYPE + " - " + principalName + " (" + principalId + ") - " + endDateString; 135 136 timesheetDocument = this.initiateWorkflowDocument(principalId, begin, end, calendarDates, TimesheetDocument.TIMESHEET_DOCUMENT_TYPE, timesheetDocumentTitle); 137 //timesheetDocument.setPayCalendarEntry(calendarDates); 138 //this.loadTimesheetDocumentData(timesheetDocument, principalId, calendarDates); 139 140 this.loadHolidaysOnTimesheet(timesheetDocument, principalId, begin, end); 141 } else { 142 timesheetDocument = this.getTimesheetDocument(header.getDocumentId()); 143 timesheetDocument.setPayCalendarEntry(calendarDates); 144 } 145 146 timesheetDocument.setTimeSummary(TkServiceLocator.getTimeSummaryService().getTimeSummary(timesheetDocument)); 147 return timesheetDocument; 148 } 149 150 public void loadHolidaysOnTimesheet(TimesheetDocument timesheetDocument, String principalId, Date beginDate, Date endDate) { 151 PrincipalHRAttributes principalCalendar = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, new java.sql.Date(beginDate.getTime())); 152 if (principalCalendar != null) { 153 HolidayCalendar holidayCalendar = TkServiceLocator.getHolidayCalendarService().getHolidayCalendarByGroup(principalCalendar.getHolidayCalendarGroup()); 154 if (holidayCalendar != null) { 155 List<HolidayCalendarDateEntry> lstHolidays = TkServiceLocator.getHolidayCalendarService().getHolidayCalendarDateEntriesForPayPeriod(holidayCalendar.getHrHolidayCalendarId(), 156 beginDate, endDate); 157 for (HolidayCalendarDateEntry holiday : lstHolidays) { 158 Assignment holidayAssign = TkServiceLocator.getHolidayCalendarService().getAssignmentToApplyHolidays(timesheetDocument, TKUtils.getTimelessDate(endDate)); 159 if (holidayAssign != null) { 160 BigDecimal holidayCalcHours = TkServiceLocator.getHolidayCalendarService().calculateHolidayHours(holidayAssign.getJob(), holiday.getHolidayHours()); 161 TimeBlock timeBlock = TkServiceLocator.getTimeBlockService().createTimeBlock(timesheetDocument, new Timestamp(holiday.getHolidayDate().getTime()), 162 new Timestamp(holiday.getHolidayDate().getTime()), holidayAssign, TkConstants.HOLIDAY_EARN_CODE, holidayCalcHours, BigDecimal.ZERO, false, false); 163 timesheetDocument.getTimeBlocks().add(timeBlock); 164 } 165 } 166 167 //If holidays are loaded will need to save them to the database 168 if (!lstHolidays.isEmpty()) { 169 TkServiceLocator.getTimeBlockService().saveTimeBlocks(new LinkedList<TimeBlock>(), timesheetDocument.getTimeBlocks()); 170 } 171 } 172 } 173 174 } 175 176 protected TimesheetDocument initiateWorkflowDocument(String principalId, Date payBeginDate, Date payEndDate, CalendarEntries calendarEntries, String documentType, String title) throws WorkflowException { 177 TimesheetDocument timesheetDocument = null; 178 WorkflowDocument workflowDocument = null; 179 180 workflowDocument = WorkflowDocumentFactory.createDocument(principalId, documentType, title); 181 182 String status = workflowDocument.getStatus().getCode(); 183 TimesheetDocumentHeader documentHeader = new TimesheetDocumentHeader(workflowDocument.getDocumentId(), principalId, payBeginDate, payEndDate, status); 184 185 documentHeader.setDocumentId(workflowDocument.getDocumentId().toString()); 186 documentHeader.setDocumentStatus("I"); 187 188 TkServiceLocator.getTimesheetDocumentHeaderService().saveOrUpdate(documentHeader); 189 timesheetDocument = new TimesheetDocument(documentHeader); 190 timesheetDocument.setPayCalendarEntry(calendarEntries); 191 loadTimesheetDocumentData(timesheetDocument, principalId, calendarEntries); 192 TkServiceLocator.getTkSearchableAttributeService().updateSearchableAttribute(timesheetDocument, payEndDate); 193 194 return timesheetDocument; 195 } 196 197 public List<TimeBlock> getPrevDocumentTimeBlocks(String principalId, Date payBeginDate) { 198 TimesheetDocumentHeader prevTdh = TkServiceLocator.getTimesheetDocumentHeaderService().getPreviousDocumentHeader(principalId, payBeginDate); 199 if (prevTdh == null) { 200 return new ArrayList<TimeBlock>(); 201 } 202 return TkServiceLocator.getTimeBlockService().getTimeBlocks(prevTdh.getDocumentId()); 203 } 204 205 @Override 206 public TimesheetDocument getTimesheetDocument(String documentId) { 207 TimesheetDocument timesheetDocument = null; 208 TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(documentId); 209 210 if (tdh != null) { 211 timesheetDocument = new TimesheetDocument(tdh); 212 CalendarEntries pce = TkServiceLocator.getCalendarService().getCalendarDatesByPayEndDate(tdh.getPrincipalId(), tdh.getPayEndDate(), null); 213 loadTimesheetDocumentData(timesheetDocument, tdh.getPrincipalId(), pce); 214 215 timesheetDocument.setPayCalendarEntry(pce); 216 } else { 217 throw new RuntimeException("Could not find TimesheetDocumentHeader for DocumentID: " + documentId); 218 } 219 return timesheetDocument; 220 } 221 222 protected void loadTimesheetDocumentData(TimesheetDocument tdoc, String principalId, CalendarEntries payCalEntry) { 223 List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByPayEntry(principalId, payCalEntry); 224 List<Job> jobs = TkServiceLocator.getJobService().getJobs(principalId, TKUtils.getTimelessDate(payCalEntry.getEndPeriodDate())); 225 List<TimeBlock> timeBlocks = TkServiceLocator.getTimeBlockService().getTimeBlocks(tdoc.getDocumentHeader().getDocumentId()); 226 227 tdoc.setAssignments(assignments); 228 tdoc.setJobs(jobs); 229 tdoc.setTimeBlocks(timeBlocks); 230 } 231 232 public boolean isSynchronousUser() { 233 List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignments(TKUser.getCurrentTargetPerson().getPrincipalId(), TKUtils.getCurrentDate()); 234 boolean isSynchronousUser = true; 235 for (Assignment assignment : assignments) { 236 isSynchronousUser &= assignment.isSynchronous(); 237 } 238 return isSynchronousUser; 239 } 240 241 //this is an admin function used for testing 242 public void deleteTimesheet(String documentId) { 243 TkServiceLocator.getTimeBlockService().deleteTimeBlocksAssociatedWithDocumentId(documentId); 244 TkServiceLocator.getTimesheetDocumentHeaderService().deleteTimesheetHeader(documentId); 245 } 246 247 public TimeBlock resetWorkedHours(TimeBlock timeBlock) { 248 if (timeBlock.getBeginTime() != null && timeBlock.getEndTime() != null && StringUtils.equals(timeBlock.getEarnCodeType(), TkConstants.EARN_CODE_TIME)) { 249 BigDecimal hours = TKUtils.getHoursBetween(timeBlock.getBeginTime().getTime(), timeBlock.getEndTime().getTime()); 250 timeBlock.setHours(hours); 251 } 252 return timeBlock; 253 } 254 255 @Override 256 public void resetTimeBlock(List<TimeBlock> timeBlocks) { 257 for (TimeBlock tb : timeBlocks) { 258 resetWorkedHours(tb); 259 } 260 TkServiceLocator.getTimeBlockService().resetTimeHourDetail(timeBlocks); 261 } 262 263 }