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    }