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.clocklog.service; 017 018 import java.math.BigDecimal; 019 import java.sql.Timestamp; 020 import java.util.ArrayList; 021 import java.util.HashSet; 022 import java.util.List; 023 import java.util.Set; 024 025 import org.apache.commons.collections.CollectionUtils; 026 import org.apache.commons.lang.StringUtils; 027 import org.kuali.hr.lm.leaveblock.LeaveBlock; 028 import org.kuali.hr.time.assignment.Assignment; 029 import org.kuali.hr.time.calendar.CalendarEntries; 030 import org.kuali.hr.time.clocklog.ClockLog; 031 import org.kuali.hr.time.clocklog.dao.ClockLogDao; 032 import org.kuali.hr.time.service.base.TkServiceLocator; 033 import org.kuali.hr.time.timeblock.TimeBlock; 034 import org.kuali.hr.time.timesheet.TimesheetDocument; 035 import org.kuali.hr.time.util.TKContext; 036 import org.kuali.hr.time.util.TkConstants; 037 import org.kuali.hr.time.workflow.TimesheetDocumentHeader; 038 import org.kuali.rice.krad.service.KRADServiceLocator; 039 040 public class ClockLogServiceImpl implements ClockLogService { 041 042 private ClockLogDao clockLogDao; 043 044 public ClockLogServiceImpl() { 045 } 046 047 public ClockLog saveClockLog(ClockLog clockLog) { 048 return KRADServiceLocator.getBusinessObjectService().save(clockLog); 049 } 050 051 @Override 052 public ClockLog processClockLog(Timestamp clockTimeStamp, Assignment assignment,CalendarEntries pe, String ip, java.sql.Date asOfDate, TimesheetDocument td, String clockAction, boolean runRules, String principalId) { 053 return processClockLog(clockTimeStamp, assignment, pe, ip, asOfDate, td, clockAction, runRules, principalId, TKContext.getPrincipalId()); 054 } 055 056 @Override 057 public ClockLog processClockLog(Timestamp clockTimeStamp, Assignment assignment,CalendarEntries pe, String ip, java.sql.Date asOfDate, TimesheetDocument td, String clockAction, boolean runRules, String principalId, String userPrincipalId) { 058 // process rules 059 Timestamp roundedClockTimestamp = TkServiceLocator.getGracePeriodService().processGracePeriodRule(clockTimeStamp, new java.sql.Date(pe.getBeginPeriodDateTime().getTime())); 060 061 ClockLog clockLog = buildClockLog(roundedClockTimestamp, new Timestamp(System.currentTimeMillis()), assignment, td, clockAction, ip, userPrincipalId); 062 063 if (runRules) { 064 TkServiceLocator.getClockLocationRuleService().processClockLocationRule(clockLog, asOfDate); 065 } 066 067 // If the clock action is clock out or lunch out, create a time block besides the clock log 068 if (StringUtils.equals(clockAction, TkConstants.CLOCK_OUT) || StringUtils.equals(clockAction, TkConstants.LUNCH_OUT)) { 069 processTimeBlock(clockLog, assignment, pe, td, clockAction, principalId, userPrincipalId); 070 } else { 071 //Save current clock log to get id for timeblock building 072 KRADServiceLocator.getBusinessObjectService().save(clockLog); 073 } 074 075 return clockLog; 076 } 077 078 private void processTimeBlock(ClockLog clockLog, Assignment currentAssignment, CalendarEntries pe, TimesheetDocument td, String clockAction, String principalId, String userPrincipalId) { 079 ClockLog lastLog = null; 080 Timestamp lastClockTimestamp = null; 081 String beginClockLogId = null; 082 String endClockLogId = null; 083 084 if (StringUtils.equals(clockAction, TkConstants.LUNCH_OUT)) { 085 lastLog = TkServiceLocator.getClockLogService().getLastClockLog(principalId, TkConstants.CLOCK_IN); 086 } else if (StringUtils.equals(clockAction, TkConstants.CLOCK_OUT)) { 087 lastLog = TkServiceLocator.getClockLogService().getLastClockLog(principalId); 088 } 089 if (lastLog != null) { 090 lastClockTimestamp = lastLog.getClockTimestamp(); 091 beginClockLogId = lastLog.getTkClockLogId(); 092 } 093 //Save current clock log to get id for timeblock building 094 KRADServiceLocator.getBusinessObjectService().save(clockLog); 095 endClockLogId = clockLog.getTkClockLogId(); 096 097 long beginTime = lastClockTimestamp.getTime(); 098 Timestamp beginTimestamp = new Timestamp(beginTime); 099 Timestamp endTimestamp = clockLog.getClockTimestamp(); 100 101 // New Time Blocks, pointer reference 102 List<TimeBlock> newTimeBlocks = td.getTimeBlocks(); 103 List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(td.getTimeBlocks().size()); 104 for (TimeBlock tb : td.getTimeBlocks()) { 105 referenceTimeBlocks.add(tb.copy()); 106 } 107 108 // Add TimeBlocks after we store our reference object! 109 List<TimeBlock> aList = TkServiceLocator.getTimeBlockService().buildTimeBlocks(currentAssignment, currentAssignment.getJob().getPayTypeObj().getRegEarnCode(), td, beginTimestamp, endTimestamp, BigDecimal.ZERO, BigDecimal.ZERO, true, false, userPrincipalId); 110 for (TimeBlock tb : aList) { 111 tb.setClockLogBeginId(beginClockLogId); 112 tb.setClockLogEndId(endClockLogId); 113 } 114 newTimeBlocks.addAll(aList); 115 116 List<Assignment> assignments = td.getAssignments(); 117 List<String> assignmentKeys = new ArrayList<String>(); 118 for (Assignment assignment : assignments) { 119 assignmentKeys.add(assignment.getAssignmentKey()); 120 } 121 List<LeaveBlock> leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(principalId, td.getAsOfDate(), td.getDocEndDate(), assignmentKeys); 122 123 //reset time block 124 TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks, td.getAsOfDate()); 125 126 //apply any rules for this action 127 TkServiceLocator.getTkRuleControllerService().applyRules(TkConstants.ACTIONS.CLOCK_OUT, newTimeBlocks, leaveBlocks, pe, td, principalId); 128 129 //call persist method that only saves added/deleted/changed timeblocks 130 TkServiceLocator.getTimeBlockService().saveTimeBlocks(referenceTimeBlocks, newTimeBlocks, userPrincipalId); 131 } 132 133 private ClockLog buildClockLog(Timestamp clockTimestamp, Timestamp originalTimestamp, Assignment assignment, TimesheetDocument timesheetDocument, String clockAction, String ip, String userPrincipalId) { 134 ClockLog clockLog = new ClockLog(); 135 clockLog.setDocumentId(timesheetDocument.getDocumentId()); 136 clockLog.setPrincipalId(timesheetDocument.getPrincipalId()); 137 clockLog.setJob(timesheetDocument.getJob(assignment.getJobNumber())); 138 clockLog.setJobNumber(assignment.getJobNumber()); 139 clockLog.setWorkArea(assignment.getWorkArea()); 140 clockLog.setTask(assignment.getTask()); 141 clockLog.setClockTimestampTimezone(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback().getID()); 142 clockLog.setClockTimestamp(clockTimestamp); 143 clockLog.setClockAction(clockAction); 144 clockLog.setIpAddress(ip); 145 clockLog.setUserPrincipalId(userPrincipalId); 146 // timestamp should be the original time without Grace Period rule applied 147 clockLog.setTimestamp(originalTimestamp); 148 149 return clockLog; 150 } 151 152 public void setClockLogDao(ClockLogDao clockLogDao) { 153 this.clockLogDao = clockLogDao; 154 } 155 156 public ClockLog getLastClockLog(String principalId) { 157 return clockLogDao.getLastClockLog(principalId); 158 } 159 160 public ClockLog getLastClockLog(String principalId, String clockAction) { 161 return clockLogDao.getLastClockLog(principalId, clockAction); 162 } 163 164 public ClockLog getLastClockLog(String principalId, String jobNumber, String workArea, String task, CalendarEntries calendarEntry) { 165 //return clockLogDao.getLastClockLog(principalId, jobNumber, workArea, task, calendarEntry); 166 TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, calendarEntry.getBeginPeriodDateTime(), calendarEntry.getEndPeriodDateTime()); 167 if (tdh == null) { 168 return null; 169 } 170 return clockLogDao.getLastClockLog(principalId, jobNumber, workArea, task, tdh.getDocumentId()); 171 } 172 173 @Override 174 public ClockLog getClockLog(String tkClockLogId) { 175 return clockLogDao.getClockLog(tkClockLogId); 176 } 177 178 @Override 179 public void deleteClockLogsForDocumentId(String documentId) { 180 clockLogDao.deleteClockLogsForDocumentId(documentId); 181 } 182 183 public List<String> getUnapprovedIPWarning(List<TimeBlock> timeBlocks) { 184 List<String> warningMessages = new ArrayList<String>(); 185 if (CollectionUtils.isNotEmpty(timeBlocks)) { 186 Set<String> aSet = new HashSet<String>(); 187 for(TimeBlock tb : timeBlocks) { 188 if(tb.getClockLogCreated()) { 189 if(StringUtils.isNotEmpty(tb.getClockLogBeginId())){ 190 ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(tb.getClockLogBeginId()); 191 if(cl.getUnapprovedIP()) { 192 aSet.add(buildUnapprovedIPWarning(cl)); 193 } 194 } 195 if(StringUtils.isNotEmpty(tb.getClockLogEndId())){ 196 ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(tb.getClockLogEndId()); 197 if(cl.getUnapprovedIP()) { 198 aSet.add(buildUnapprovedIPWarning(cl)); 199 } 200 } 201 } 202 } 203 warningMessages.addAll(aSet); 204 } 205 206 return warningMessages; 207 } 208 209 public String buildUnapprovedIPWarning(ClockLog cl) { 210 String warning = "Warning: Action '" + TkConstants.CLOCK_ACTION_STRINGS.get(cl.getClockAction()) + "' taken at " 211 + cl.getClockTimestamp() + " was from an unapproved IP address - " + cl.getIpAddress(); 212 return warning; 213 } 214 215 }