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