View Javadoc

1   /**
2    * Copyright 2004-2013 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.hr.time.missedpunch.service;
17  
18  import java.math.BigDecimal;
19  import java.sql.Timestamp;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.joda.time.DateTime;
25  import org.joda.time.DateTimeZone;
26  import org.joda.time.LocalTime;
27  import org.kuali.hr.time.assignment.Assignment;
28  import org.kuali.hr.time.batch.BatchJobEntry;
29  import org.kuali.hr.time.clocklog.ClockLog;
30  import org.kuali.hr.time.missedpunch.MissedPunchDocument;
31  import org.kuali.hr.time.missedpunch.dao.MissedPunchDao;
32  import org.kuali.hr.time.service.base.TkServiceLocator;
33  import org.kuali.hr.time.timeblock.TimeBlock;
34  import org.kuali.hr.time.timesheet.TimesheetDocument;
35  import org.kuali.hr.time.util.TKContext;
36  import org.kuali.hr.time.util.TKUtils;
37  import org.kuali.hr.time.util.TkConstants;
38  import org.kuali.rice.kew.api.WorkflowDocument;
39  import org.kuali.rice.kew.api.WorkflowDocumentFactory;
40  import org.kuali.rice.krad.service.KRADServiceLocator;
41  import org.kuali.rice.krad.util.GlobalVariables;
42  
43  public class MissedPunchServiceImpl implements MissedPunchService {
44  
45      MissedPunchDao missedPunchDao;
46  
47      @Override
48      public MissedPunchDocument getMissedPunchByRouteHeader(String headerId) {
49          return missedPunchDao.getMissedPunchByRouteHeader(headerId);
50      }
51  
52      public void setMissedPunchDao(MissedPunchDao missedPunchDao) {
53          this.missedPunchDao = missedPunchDao;
54      }
55  
56      @Override
57      public void updateClockLogAndTimeBlockIfNecessary(MissedPunchDocument missedPunch) {
58          java.util.Date actionDate = missedPunch.getActionDate();
59          java.sql.Time actionTime = missedPunch.getActionTime();
60  
61          LocalTime actionTimeLocal = new LocalTime(actionTime.getTime());
62          DateTime actionDateTime = new DateTime(actionDate.getTime());
63  
64          actionDateTime = actionDateTime.plus(actionTimeLocal.getMillisOfDay());
65  
66          ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(missedPunch.getTkClockLogId());
67          // in case the missedpunch doc has an valication error but the clockLog has been changed at certain time
68          // need to reset the clock log back to the original one
69          if(cl == null) {
70          	MissedPunchDocument mpd = TkServiceLocator.getMissedPunchService().getMissedPunchByRouteHeader(missedPunch.getDocumentNumber());
71          	if(mpd != null) {
72          		missedPunch.setTkClockLogId(mpd.getTkClockLogId());
73          		cl = TkServiceLocator.getClockLogService().getClockLog(missedPunch.getTkClockLogId());
74          	}
75          }
76  
77          if(cl.getClockTimestamp().compareTo(new Timestamp(actionDateTime.getMillis())) != 0){
78          	//change occurred between the initial save and the approver
79          	//inactivate all the previous timeblocks and delete clock logs
80          	String logEndId = null;
81          	String logBeginId = null;
82          	List<TimeBlock> timeBlocks = TkServiceLocator.getTimeBlockService().getTimeBlocksForClockLogEndId(cl.getTkClockLogId());
83          	if(timeBlocks.isEmpty()) {
84          		// get timeBlock with the Clock Log as the clock_log_begin_id
85          		timeBlocks = TkServiceLocator.getTimeBlockService().getTimeBlocksForClockLogBeginId(cl.getTkClockLogId());
86          		if(!timeBlocks.isEmpty()) {
87          			logEndId = timeBlocks.get(0).getClockLogEndId();
88          		}
89          	} else {
90          		logBeginId = timeBlocks.get(0).getClockLogBeginId();	// new time blocks should keep the original clockLogBeginId
91          	}
92          	
93          	//delete existing time blocks
94          	for(TimeBlock tb : timeBlocks){
95          		TkServiceLocator.getTimeBlockService().deleteTimeBlock(tb);
96          	}
97          	KRADServiceLocator.getBusinessObjectService().delete(cl);
98          	// delete the existing clock log and add new time blocks
99          	addClockLogForMissedPunch(missedPunch, logEndId, logBeginId);
100         }
101     }
102 
103     @Override
104     public void addClockLogForMissedPunch(MissedPunchDocument missedPunch) {
105         java.util.Date actionDate = missedPunch.getActionDate();
106         java.sql.Time actionTime = missedPunch.getActionTime();
107 
108         LocalTime actionTimeLocal = new LocalTime(actionTime.getTime());
109         DateTime actionDateTime = new DateTime(actionDate.getTime());
110 
111         actionDateTime = actionDateTime.plus(actionTimeLocal.getMillisOfDay());
112         missedPunch.setActionDate(new java.util.Date(actionDateTime.getMillis()));
113         TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().getTimesheetDocument(missedPunch.getTimesheetDocumentId());
114         Assignment assign = TkServiceLocator.getAssignmentService().getAssignment(tdoc, missedPunch.getAssignment());
115         // Need to build a clock log entry.
116         //Timestamp clockTimestamp, String selectedAssign, TimesheetDocument timesheetDocument, String clockAction, String ip) {
117         Timestamp ts = new Timestamp(missedPunch.getActionDate().getTime());
118         ClockLog lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog(missedPunch.getPrincipalId());
119         Long zoneOffset = TkServiceLocator.getTimezoneService().getTimezoneOffsetFromServerTime(DateTimeZone.forID(lastClockLog.getClockTimestampTimezone()));
120         Timestamp clockLogTime = new Timestamp(ts.getTime() - zoneOffset); // convert the action time to the system zone time
121 
122         ClockLog clockLog = TkServiceLocator.getClockLogService().buildClockLog(clockLogTime, clockLogTime,
123                 assign,
124                 tdoc,
125                 missedPunch.getClockAction(),
126                 TKUtils.getIPAddressFromRequest(TKContext.getHttpServletRequest()));
127 
128         TkServiceLocator.getClockLogService().saveClockLog(clockLog);
129         missedPunch.setTkClockLogId(clockLog.getTkClockLogId());
130 
131         if (StringUtils.equals(clockLog.getClockAction(), TkConstants.CLOCK_OUT) ||
132                 StringUtils.equals(clockLog.getClockAction(), TkConstants.LUNCH_OUT)) {
133             String earnCode = assign.getJob().getPayTypeObj().getRegEarnCode();
134             this.buildTimeBlockRunRules(lastClockLog, clockLog, tdoc, assign, earnCode, lastClockLog.getClockTimestamp(), clockLog.getClockTimestamp());
135         }
136     }
137 
138     @Override
139     // is called by updateClockLogAndTimeBlockIfNecessary when approver changes time on approving an existing missed punch doc
140 
141     public void addClockLogForMissedPunch(MissedPunchDocument missedPunch, String logEndId, String logBeginId) {
142         java.util.Date actionDate = missedPunch.getActionDate();
143         java.sql.Time actionTime = missedPunch.getActionTime();
144 
145         LocalTime actionTimeLocal = new LocalTime(actionTime.getTime());
146         DateTime actionDateTime = new DateTime(actionDate.getTime());
147 
148         actionDateTime = actionDateTime.plus(actionTimeLocal.getMillisOfDay());
149         missedPunch.setActionDate(new java.util.Date(actionDateTime.getMillis()));
150         TimesheetDocument tdoc = TkServiceLocator.getTimesheetService().getTimesheetDocument(missedPunch.getTimesheetDocumentId());
151         Assignment assign = TkServiceLocator.getAssignmentService().getAssignment(tdoc, missedPunch.getAssignment());
152         // Need to build a clock log entry.
153         Timestamp ts = new Timestamp(missedPunch.getActionDate().getTime());
154         ClockLog lastLog = TkServiceLocator.getClockLogService().getLastClockLog(missedPunch.getPrincipalId());
155         Long zoneOffset = TkServiceLocator.getTimezoneService().getTimezoneOffsetFromServerTime(DateTimeZone.forID(lastLog.getClockTimestampTimezone()));
156         Timestamp clockLogTime = new Timestamp(ts.getTime() - zoneOffset); // convert the action time to the system zone time
157 
158         ClockLog clockLog = TkServiceLocator.getClockLogService().buildClockLog(clockLogTime, clockLogTime,
159                 assign,
160                 tdoc,
161                 missedPunch.getClockAction(),
162                 TKUtils.getIPAddressFromRequest(TKContext.getHttpServletRequest()));
163         TkServiceLocator.getClockLogService().saveClockLog(clockLog);
164         missedPunch.setTkClockLogId(clockLog.getTkClockLogId());
165 //        MissedPunchDocument doc = TkServiceLocator.getMissedPunchService().getMissedPunchByRouteHeader(missedPunch.getDocumentNumber());
166 //        doc.setTkClockLogId(clockLog.getTkClockLogId());
167 //        KNSServiceLocator.getBusinessObjectService().save(doc);
168         
169         // if both clock log ids are null, no need to create new time blocks
170         if(!(logEndId == null && logBeginId == null)) {
171 	        ClockLog endLog = null;
172 	        ClockLog beginLog = null;
173 	       if(logEndId != null) {
174 	    	   endLog = TkServiceLocator.getClockLogService().getClockLog(logEndId);
175 	       } else {
176 	    	   endLog = clockLog; 
177 	       }
178 	       if (logBeginId != null) {
179 	           beginLog = TkServiceLocator.getClockLogService().getClockLog(logBeginId);
180 	       } else {
181 	    	   beginLog = clockLog;
182 	       }
183 	        
184 	       if (beginLog != null && endLog != null && beginLog.getClockTimestamp().before(endLog.getClockTimestamp())) {
185 	           String earnCode = assign.getJob().getPayTypeObj().getRegEarnCode();
186 	           this.buildTimeBlockRunRules(beginLog, endLog, tdoc, assign, earnCode, beginLog.getClockTimestamp(), endLog.getClockTimestamp());
187 	       } else {
188 	        	// error
189 	    	   GlobalVariables.getMessageMap().putError("document.actionTime", "clock.mp.invalid.datetime");
190 	       }
191         }
192     }
193 
194     /**
195      * Helper method to build time blocks and fire the rules processing. This
196      * should be called only if there was a CLOCK_OUT action.
197      */
198     private void buildTimeBlockRunRules(ClockLog beginClockLog, ClockLog endClockLog, TimesheetDocument tdoc, Assignment assignment, String earnCode, Timestamp beginTimestamp, Timestamp endTimestamp) {
199         // New Time Blocks, pointer reference
200         List<TimeBlock> newTimeBlocks = tdoc.getTimeBlocks();
201         List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(newTimeBlocks);
202         for (TimeBlock tb : newTimeBlocks) {
203             referenceTimeBlocks.add(tb.copy());
204         }
205 
206         // Add TimeBlocks after we store our reference object!
207         List<TimeBlock> blocks = TkServiceLocator.getTimeBlockService().buildTimeBlocks(
208                 assignment, earnCode, tdoc, beginTimestamp,
209                 endTimestamp, BigDecimal.ZERO, BigDecimal.ZERO, true, false);
210 
211 
212         // Add the clock log IDs to the time blocks that were just created.
213         for (TimeBlock block : blocks) {
214             block.setClockLogBeginId(beginClockLog.getTkClockLogId());
215             block.setClockLogEndId(endClockLog.getTkClockLogId());
216         }
217 
218         newTimeBlocks.addAll(blocks);
219 
220         //reset time block
221         TkServiceLocator.getTimesheetService().resetTimeBlock(newTimeBlocks);
222         //apply any rules for this action
223         TkServiceLocator.getTkRuleControllerService().applyRules(
224                 TkConstants.ACTIONS.CLOCK_OUT, newTimeBlocks,
225                 tdoc.getPayCalendarEntry(),
226                 tdoc, tdoc.getPrincipalId()
227         );
228 
229         TkServiceLocator.getTimeBlockService().saveTimeBlocks(referenceTimeBlocks, newTimeBlocks);
230     }
231     public MissedPunchDocument getMissedPunchByClockLogId(String clockLogId){
232     	return missedPunchDao.getMissedPunchByClockLogId(clockLogId);
233 
234     }
235 
236     @Override
237     public void approveMissedPunch(MissedPunchDocument document) {
238             String rhid = document.getDocumentNumber();
239             WorkflowDocument wd = WorkflowDocumentFactory.loadDocument(TkConstants.BATCH_JOB_USER_PRINCIPAL_ID, rhid);
240             wd.superUserBlanketApprove("Batch job superuser approving missed punch document.");
241     }
242 
243     @Override
244     public List<MissedPunchDocument> getMissedPunchDocsByBatchJobEntry(BatchJobEntry batchJobEntry) {
245         return missedPunchDao.getMissedPunchDocsByBatchJobEntry(batchJobEntry);
246     }
247     
248     @Override
249     public List<MissedPunchDocument> getMissedPunchDocsByTimesheetDocumentId(String timesheetDocumentId) {
250         return missedPunchDao.getMissedPunchDocsByTimesheetDocumentId(timesheetDocumentId);
251     }
252 
253 }