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