View Javadoc

1   /**
2    * Copyright 2004-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.kpme.tklm.time.clocklog.service;
17  
18  import java.math.BigDecimal;
19  import java.sql.Timestamp;
20  import java.util.ArrayList;
21  import java.util.HashSet;
22  import java.util.List;
23  import java.util.Set;
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.joda.time.LocalDate;
30  import org.kuali.kpme.core.assignment.Assignment;
31  import org.kuali.kpme.core.calendar.entry.CalendarEntry;
32  import org.kuali.kpme.core.service.HrServiceLocator;
33  import org.kuali.kpme.core.util.HrContext;
34  import org.kuali.kpme.core.util.TKUtils;
35  import org.kuali.kpme.tklm.common.TkConstants;
36  import org.kuali.kpme.tklm.leave.block.LeaveBlock;
37  import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
38  import org.kuali.kpme.tklm.time.batch.EndPayPeriodJob;
39  import org.kuali.kpme.tklm.time.clocklog.ClockLog;
40  import org.kuali.kpme.tklm.time.clocklog.dao.ClockLogDao;
41  import org.kuali.kpme.tklm.time.service.TkServiceLocator;
42  import org.kuali.kpme.tklm.time.timeblock.TimeBlock;
43  import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
44  import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
45  import org.kuali.rice.krad.service.KRADServiceLocator;
46  
47  public class ClockLogServiceImpl implements ClockLogService {
48  	
49  	private static final Logger LOG = Logger.getLogger(ClockLogServiceImpl.class);
50  	
51      private ClockLogDao clockLogDao;
52  
53      public ClockLogServiceImpl() {
54      }
55  
56      public ClockLog saveClockLog(ClockLog clockLog) {
57      	return KRADServiceLocator.getBusinessObjectService().save(clockLog);
58      }
59  
60      @Override
61      public ClockLog processClockLog(DateTime clockDateTime, Assignment assignment,CalendarEntry pe, String ip, LocalDate asOfDate, TimesheetDocument td, String clockAction, boolean runRules, String principalId) {
62          return processClockLog(clockDateTime, assignment, pe, ip, asOfDate, td, clockAction, runRules, principalId, HrContext.getPrincipalId());
63      }
64  
65      @Override
66      public synchronized ClockLog processClockLog(DateTime clockDateTime, Assignment assignment,CalendarEntry pe, String ip, LocalDate asOfDate, TimesheetDocument td, String clockAction, boolean runRules, String principalId, String userPrincipalId) {
67          // process rules
68          DateTime roundedClockDateTime = TkServiceLocator.getGracePeriodService().processGracePeriodRule(clockDateTime, pe.getBeginPeriodFullDateTime().toLocalDate());
69  
70          ClockLog lastClockLog = null;
71          if (StringUtils.equals(clockAction, TkConstants.LUNCH_OUT)) {
72              lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog(assignment.getPrincipalId(), TkConstants.CLOCK_IN);
73          } else if (StringUtils.equals(clockAction, TkConstants.CLOCK_OUT)
74                     || StringUtils.equals(clockAction, TkConstants.CLOCK_IN)) {
75              lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog(assignment.getPrincipalId());
76          }
77  
78          DateTime lastClockLogTime = lastClockLog != null ? lastClockLog.getClockDateTime() : null;
79          if (lastClockLog != null
80                  && lastClockLogTime.withMillisOfSecond(0).equals(roundedClockDateTime.withMillisOfSecond(0))) {
81              roundedClockDateTime = roundedClockDateTime.withMillisOfSecond(lastClockLogTime.getMillisOfSecond() + 1);
82          }
83  
84  
85          ClockLog clockLog = buildClockLog(roundedClockDateTime, new Timestamp(System.currentTimeMillis()), assignment, td, clockAction, ip, userPrincipalId);
86  
87          if (runRules) {
88          	TkServiceLocator.getClockLocationRuleService().processClockLocationRule(clockLog, asOfDate);
89          }
90          
91          // If the clock action is clock out or lunch out, create a time block besides the clock log
92          if (StringUtils.equals(clockAction, TkConstants.CLOCK_OUT) || StringUtils.equals(clockAction, TkConstants.LUNCH_OUT)) {
93              processTimeBlock(clockLog, assignment, pe, td, clockAction, principalId, userPrincipalId);
94          } else {
95              //Save current clock log to get id for timeblock building
96              KRADServiceLocator.getBusinessObjectService().save(clockLog);
97          }
98          return clockLog;
99      }
100 
101     private void processTimeBlock(ClockLog clockLog, Assignment currentAssignment, CalendarEntry pe, TimesheetDocument td, String clockAction, String principalId, String userPrincipalId) {
102 LOG.info("in ClockLogServiceImpl.processTimeBlock");    	
103         ClockLog lastLog = null;
104         DateTime lastClockDateTime = null;
105         String beginClockLogId = null;
106         String endClockLogId = null;
107 
108         if (StringUtils.equals(clockAction, TkConstants.LUNCH_OUT)) {
109             lastLog = TkServiceLocator.getClockLogService().getLastClockLog(principalId, TkConstants.CLOCK_IN);
110         } else if (StringUtils.equals(clockAction, TkConstants.CLOCK_OUT)) {
111             lastLog = TkServiceLocator.getClockLogService().getLastClockLog(principalId);
112         }
113         if (lastLog != null) {
114         	lastClockDateTime = lastLog.getClockDateTime();
115             beginClockLogId = lastLog.getTkClockLogId();
116         }
117         //Save current clock log to get id for timeblock building
118         KRADServiceLocator.getBusinessObjectService().save(clockLog);
119         endClockLogId = clockLog.getTkClockLogId();
120 
121         DateTime beginDateTime = lastClockDateTime;
122         DateTime endDateTime = clockLog.getClockDateTime();
123 
124         // KPME-2680 : chk if hours is not zero then and then create TimeBlock
125     	BigDecimal hours = TKUtils.getHoursBetween(beginDateTime.getMillis(), endDateTime.getMillis());
126     	if(hours.compareTo(BigDecimal.ZERO) > 0) {
127 	        // New Time Blocks, pointer reference
128 	        List<TimeBlock> newTimeBlocks = td.getTimeBlocks();
129 	        List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(td.getTimeBlocks().size());
130 	        for (TimeBlock tb : td.getTimeBlocks()) {
131 	            referenceTimeBlocks.add(tb.copy());
132 	        }
133 	
134 	        // Add TimeBlocks after we store our reference object!
135 	        List<TimeBlock> aList = TkServiceLocator.getTimeBlockService().buildTimeBlocks(currentAssignment,
136                     currentAssignment.getJob().getPayTypeObj().getRegEarnCode(), td, beginDateTime, endDateTime,
137                     BigDecimal.ZERO, BigDecimal.ZERO, true, false, userPrincipalId, beginClockLogId, endClockLogId);
138 
139 	        newTimeBlocks.addAll(aList);
140 	        
141 	        List<Assignment> assignments = td.getAssignments();
142 	        List<String> assignmentKeys = new ArrayList<String>();
143 	        for (Assignment assignment : assignments) {
144 	        	assignmentKeys.add(assignment.getAssignmentKey());
145 	        }
146 	        List<LeaveBlock> leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksForTimeCalendar(principalId, td.getAsOfDate(), td.getDocEndDate(), assignmentKeys);
147 	
148 	        //reset time block
149 	        TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks, td.getAsOfDate());
150 	
151 	        //apply any rules for this action
152 	        TkServiceLocator.getTkRuleControllerService().applyRules(TkConstants.ACTIONS.CLOCK_OUT, newTimeBlocks, leaveBlocks, pe, td, principalId);
153 	
154 	        //call persist method that only saves added/deleted/changed timeblocks
155 	        TkServiceLocator.getTimeBlockService().saveTimeBlocks(referenceTimeBlocks, newTimeBlocks, userPrincipalId);
156 	        
157 LOG.info("in ClockLogServiceImpl.processTimeBlock, after saving time blocks, the size of the time blocks is " + newTimeBlocks.size()); 
158     	}
159     }
160 
161     private ClockLog buildClockLog(DateTime clockDateTime, Timestamp originalTimestamp, Assignment assignment, TimesheetDocument timesheetDocument, String clockAction, String ip, String userPrincipalId) {
162         ClockLog clockLog = new ClockLog();
163         clockLog.setDocumentId(timesheetDocument.getDocumentId());
164         clockLog.setPrincipalId(timesheetDocument.getPrincipalId());
165         clockLog.setJob(timesheetDocument.getJob(assignment.getJobNumber()));
166         clockLog.setJobNumber(assignment.getJobNumber());
167         clockLog.setWorkArea(assignment.getWorkArea());
168         clockLog.setTask(assignment.getTask());
169         clockLog.setClockTimestampTimezone(HrServiceLocator.getTimezoneService().getTargetUserTimezoneWithFallback().getID());
170         clockLog.setClockDateTime(clockDateTime);
171         clockLog.setClockAction(clockAction);
172         clockLog.setIpAddress(ip);
173         clockLog.setUserPrincipalId(userPrincipalId);
174         // timestamp should be the original time without Grace Period rule applied
175         clockLog.setTimestamp(originalTimestamp);
176 
177         return clockLog;
178     }
179 
180     public void setClockLogDao(ClockLogDao clockLogDao) {
181         this.clockLogDao = clockLogDao;
182     }
183 
184     public ClockLog getLastClockLog(String principalId) {
185         return clockLogDao.getLastClockLog(principalId);
186     }
187 
188     public ClockLog getLastClockLog(String principalId, String clockAction) {
189         return clockLogDao.getLastClockLog(principalId, clockAction);
190     }
191 
192     public ClockLog getLastClockLog(String principalId, String jobNumber, String workArea, String task, CalendarEntry calendarEntry) {
193         TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(principalId, calendarEntry.getBeginPeriodFullDateTime(), calendarEntry.getEndPeriodFullDateTime());
194         if(tdh == null)
195         	return null;
196         return clockLogDao.getLastClockLog(principalId, jobNumber, workArea, task, tdh.getDocumentId());
197         //return clockLogDao.getLastClockLog(principalId, jobNumber, workArea, task, calendarEntry);
198     }
199 
200     @Override
201     public ClockLog getClockLog(String tkClockLogId) {
202         return clockLogDao.getClockLog(tkClockLogId);
203     }
204     
205     @Override
206 	public void deleteClockLogsForDocumentId(String documentId) {
207     	clockLogDao.deleteClockLogsForDocumentId(documentId);
208 	}
209 
210     public List<String> getUnapprovedIPWarning(List<TimeBlock> timeBlocks) {
211 		 List<String> warningMessages = new ArrayList<String>();
212 		 if (CollectionUtils.isNotEmpty(timeBlocks)) {
213 		     Set<String> aSet = new HashSet<String>();
214 		     for(TimeBlock tb : timeBlocks) {
215 		    	 if(tb.getClockLogCreated()) {
216 		    		 if(StringUtils.isNotEmpty(tb.getClockLogBeginId())){
217 		    			 ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(tb.getClockLogBeginId());
218 		    			 if(cl == null || cl.isUnapprovedIP()) {
219 		    				 aSet.add(buildUnapprovedIPWarning(cl));
220 		    			 }
221 		    		 }
222 		    		 if(StringUtils.isNotEmpty(tb.getClockLogEndId())){
223 		    			 ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(tb.getClockLogEndId());
224 		    			 if(cl == null || cl.isUnapprovedIP()) {
225 		    				 aSet.add(buildUnapprovedIPWarning(cl));
226 		    			 }
227 		    		 }		
228 		    	 }
229 		     }
230 		     warningMessages.addAll(aSet);
231 		}
232 		
233 		return warningMessages;
234     }
235 
236 	public String buildUnapprovedIPWarning(ClockLog cl) {
237 		String warning = "Warning: Action '" + TkConstants.CLOCK_ACTION_STRINGS.get(cl.getClockAction()) + "' taken at " 
238 			+ cl.getClockTimestamp() + " was from an unapproved IP address - " + cl.getIpAddress();
239 		return warning;
240 	}
241 
242 }