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.timeblock.service;
17  
18  import java.math.BigDecimal;
19  import java.sql.Date;
20  import java.sql.Timestamp;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.apache.commons.lang.StringUtils;
25  import org.apache.log4j.Logger;
26  import org.joda.time.DateTime;
27  import org.joda.time.DateTimeConstants;
28  import org.joda.time.DateTimeZone;
29  import org.joda.time.Interval;
30  import org.kuali.hr.job.Job;
31  import org.kuali.hr.lm.earncodesec.EarnCodeSecurity;
32  import org.kuali.hr.time.assignment.Assignment;
33  import org.kuali.hr.time.earncode.EarnCode;
34  import org.kuali.hr.time.paytype.PayType;
35  import org.kuali.hr.time.service.base.TkServiceLocator;
36  import org.kuali.hr.time.timeblock.TimeBlock;
37  import org.kuali.hr.time.timeblock.TimeBlockHistory;
38  import org.kuali.hr.time.timeblock.TimeHourDetail;
39  import org.kuali.hr.time.timeblock.dao.TimeBlockDao;
40  import org.kuali.hr.time.timesheet.TimesheetDocument;
41  import org.kuali.hr.time.util.TKContext;
42  import org.kuali.hr.time.util.TKUser;
43  import org.kuali.hr.time.util.TKUtils;
44  import org.kuali.hr.time.util.TkConstants;
45  import org.kuali.rice.krad.service.KRADServiceLocator;
46  import org.kuali.rice.krad.util.GlobalVariables;
47  
48  public class TimeBlockServiceImpl implements TimeBlockService {
49  
50      private static final Logger LOG = Logger.getLogger(TimeBlockServiceImpl.class);
51      private TimeBlockDao timeBlockDao;
52  
53      public void setTimeBlockDao(TimeBlockDao timeBlockDao) {
54          this.timeBlockDao = timeBlockDao;
55      }
56  
57      //This function is used to build timeblocks that span days
58      public List<TimeBlock> buildTimeBlocksSpanDates(Assignment assignment, String earnCode, TimesheetDocument timesheetDocument,
59                                                      Timestamp beginTimestamp, Timestamp endTimestamp, BigDecimal hours, BigDecimal amount, 
60                                                      Boolean isClockLogCreated, Boolean isLunchDeleted, String spanningWeeks, String userPrincipalId) {
61          DateTimeZone zone = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
62          DateTime beginDt = new DateTime(beginTimestamp.getTime(), zone);
63          DateTime endDt = beginDt.toLocalDate().toDateTime((new DateTime(endTimestamp.getTime(), zone)).toLocalTime(), zone);
64          if (endDt.isBefore(beginDt)) endDt = endDt.plusDays(1);
65      	
66          List<Interval> dayInt = TKUtils.getDaySpanForCalendarEntry(timesheetDocument.getCalendarEntry());
67          TimeBlock firstTimeBlock = new TimeBlock();
68          List<TimeBlock> lstTimeBlocks = new ArrayList<TimeBlock>();
69          for (Interval dayIn : dayInt) {
70              if (dayIn.contains(beginDt)) {
71                  if (dayIn.contains(endDt) || dayIn.getEnd().equals(endDt)) {
72                  	// KPME-1446 if "Include weekends" check box is checked, don't add Sat and Sun to the timeblock list
73                  	if (StringUtils.isEmpty(spanningWeeks) && 
74                  		(dayIn.getStart().getDayOfWeek() == DateTimeConstants.SATURDAY ||dayIn.getStart().getDayOfWeek() == DateTimeConstants.SUNDAY)) {
75                  		// do nothing
76                  	} else {
77                          firstTimeBlock = createTimeBlock(timesheetDocument, beginTimestamp, new Timestamp(endDt.getMillis()), assignment, earnCode, hours, amount, false, isLunchDeleted, userPrincipalId);
78                          lstTimeBlocks.add(firstTimeBlock);                		
79                  	}
80                  } else {
81                      //TODO move this to prerule validation
82                      //throw validation error if this case met error
83                  }
84              }
85          }
86  
87          DateTime endTime = new DateTime(endTimestamp.getTime(), zone);
88          DateTime endOfFirstDay = new DateTime(firstTimeBlock.getEndTimestamp(), zone);
89          long diffInMillis = endOfFirstDay.minus(beginDt.getMillis()).getMillis();
90          DateTime currTime = beginDt.plusDays(1);
91          while (currTime.isBefore(endTime) || currTime.isEqual(endTime)) {
92          	// KPME-1446 if "Include weekends" check box is checked, don't add Sat and Sun to the timeblock list
93          	if (StringUtils.isEmpty(spanningWeeks) && 
94          		(currTime.getDayOfWeek() == DateTimeConstants.SATURDAY || currTime.getDayOfWeek() == DateTimeConstants.SUNDAY)) {
95          		// do nothing
96          	} else {
97  	            Timestamp begin = new Timestamp(currTime.getMillis());
98  	            Timestamp end = new Timestamp((currTime.plus(diffInMillis).getMillis()));
99  	            TimeBlock tb = createTimeBlock(timesheetDocument, begin, end, assignment, earnCode, hours, amount, false, isLunchDeleted, userPrincipalId);
100 	            lstTimeBlocks.add(tb);
101         	}
102         	currTime = currTime.plusDays(1);
103         }
104         return lstTimeBlocks;
105     }
106 
107 
108     public List<TimeBlock> buildTimeBlocks(Assignment assignment, String earnCode, TimesheetDocument timesheetDocument,
109                                            Timestamp beginTimestamp, Timestamp endTimestamp, BigDecimal hours, BigDecimal amount, 
110                                            Boolean isClockLogCreated, Boolean isLunchDeleted, String userPrincipalId) {
111 
112         //Create 1 or many timeblocks if the span of timeblocks exceed more than one
113         //day that is determined by pay period day(24 hrs + period begin date)
114         Interval firstDay = null;
115         List<Interval> dayIntervals = TKUtils.getDaySpanForCalendarEntry(timesheetDocument.getCalendarEntry());
116         List<TimeBlock> lstTimeBlocks = new ArrayList<TimeBlock>();
117         Timestamp beginTemp = beginTimestamp;
118 
119         for (Interval dayInt : dayIntervals) {
120         	// the time period spans more than one day
121             if (firstDay != null) {
122             	if(!dayInt.contains(endTimestamp.getTime())){
123             		beginTemp = new Timestamp(dayInt.getStartMillis());
124             	} else if((dayInt.getStartMillis() - endTimestamp.getTime()) != 0){
125             		TimeBlock tb = createTimeBlock(timesheetDocument, new Timestamp(dayInt.getStartMillis()), endTimestamp, assignment, earnCode, hours, amount, isClockLogCreated, isLunchDeleted, userPrincipalId);
126             		lstTimeBlocks.add(tb);
127             		break;
128             	}            		
129             }
130             if (dayInt.contains(beginTemp.getTime())) {
131                 firstDay = dayInt;
132                 // KPME-361
133                 // added a condition to handle the time block which ends at 12a, e.g. a 10p-12a timeblock
134                 // this is the same fix as TkTimeBlockAggregate
135                 if (dayInt.contains(endTimestamp.getTime()) || (endTimestamp.getTime() == dayInt.getEnd().getMillis())) {
136                     //create one timeblock if contained in one day interval
137                 	TimeBlock tb = createTimeBlock(timesheetDocument, beginTemp, endTimestamp, assignment, earnCode, hours, amount, isClockLogCreated, isLunchDeleted, userPrincipalId);
138                     tb.setBeginTimestamp(beginTemp);
139                     tb.setEndTimestamp(endTimestamp);
140                     lstTimeBlocks.add(tb);
141                     break;
142                 } else {
143                     // create a timeblock that wraps the 24 hr day
144                 	TimeBlock tb = createTimeBlock(timesheetDocument, beginTemp, new Timestamp(dayInt.getEndMillis()), assignment, earnCode, hours, amount, isClockLogCreated, isLunchDeleted, userPrincipalId);
145                     tb.setBeginTimestamp(beginTemp);
146                     tb.setEndTimestamp(new Timestamp(firstDay.getEndMillis()));
147                     lstTimeBlocks.add(tb);
148                 }
149             }
150         }
151         return lstTimeBlocks;
152     }
153 
154     public void saveTimeBlocks(List<TimeBlock> oldTimeBlocks, List<TimeBlock> newTimeBlocks, String userPrincipalId) {
155         List<TimeBlock> alteredTimeBlocks = new ArrayList<TimeBlock>();
156         for (TimeBlock tb : newTimeBlocks) {
157             boolean persist = true;
158             for (TimeBlock tbOld : oldTimeBlocks) {
159                 if (tb.equals(tbOld)) {
160                     persist = false;
161                     break;
162                 }
163             }
164             if (persist) {
165                 alteredTimeBlocks.add(tb);
166             }
167         }
168         
169         for (TimeBlock timeBlock : alteredTimeBlocks) {
170             TkServiceLocator.getTimeHourDetailService().removeTimeHourDetails(timeBlock.getTkTimeBlockId());
171             timeBlock.setUserPrincipalId(userPrincipalId);
172         }
173         
174         List<TimeBlock> savedTimeBlocks = (List<TimeBlock>) KRADServiceLocator.getBusinessObjectService().save(alteredTimeBlocks);
175         
176         for (TimeBlock timeBlock : savedTimeBlocks) {
177             timeBlock.setTimeBlockHistories(createTimeBlockHistories(timeBlock, TkConstants.ACTIONS.ADD_TIME_BLOCK));
178             KRADServiceLocator.getBusinessObjectService().save(timeBlock.getTimeBlockHistories());
179         }
180     }
181 
182     public void saveTimeBlocks(List<TimeBlock> tbList) {
183 		 for (TimeBlock tb : tbList) {
184 	         TkServiceLocator.getTimeHourDetailService().removeTimeHourDetails(tb.getTkTimeBlockId());
185 	         timeBlockDao.saveOrUpdate(tb);
186 	         for(TimeBlockHistory tbh : tb.getTimeBlockHistories()){
187 	        	 TkServiceLocator.getTimeBlockHistoryService().saveTimeBlockHistory(tbh);
188 	         }
189 	     }
190     }
191     
192     public void updateTimeBlock(TimeBlock tb) {
193 	         timeBlockDao.saveOrUpdate(tb);
194     }
195 
196 
197     public TimeBlock createTimeBlock(TimesheetDocument timesheetDocument, Timestamp beginTime, Timestamp endTime, Assignment assignment, String earnCode, BigDecimal hours, BigDecimal amount, Boolean clockLogCreated, Boolean lunchDeleted, String userPrincipalId) {
198         DateTimeZone timezone = TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
199         EarnCode earnCodeObj = TkServiceLocator.getEarnCodeService().getEarnCode(earnCode, timesheetDocument.getAsOfDate());
200 
201         TimeBlock tb = new TimeBlock();
202         tb.setDocumentId(timesheetDocument.getDocumentHeader().getDocumentId());
203         tb.setPrincipalId(timesheetDocument.getPrincipalId());
204         tb.setJobNumber(assignment.getJobNumber());
205         tb.setWorkArea(assignment.getWorkArea());
206         tb.setTask(assignment.getTask());
207         tb.setEarnCode(earnCode);
208         tb.setBeginTimestamp(beginTime);
209         tb.setBeginTimestampTimezone(timezone.getID());
210         tb.setEndTimestamp(endTime);
211         tb.setEndTimestampTimezone(timezone.getID());
212         tb.setBeginTimeDisplay(new DateTime(tb.getBeginTimestamp(), timezone));
213         tb.setEndTimeDisplay(new DateTime(tb.getEndTimestamp(), timezone));
214         // only calculate the hours from the time fields if the passed in hour is zero
215         if(hours == null || hours.compareTo(BigDecimal.ZERO) == 0) {
216         	hours = TKUtils.getHoursBetween(beginTime.getTime(), endTime.getTime());
217         }
218         tb.setAmount(amount);
219         //If earn code has an inflate min hours check if it is greater than zero
220         //and compare if the hours specified is less than min hours awarded for this
221         //earn code
222         if (earnCodeObj.getInflateMinHours() != null) {
223             if ((earnCodeObj.getInflateMinHours().compareTo(BigDecimal.ZERO) != 0) &&
224                     earnCodeObj.getInflateMinHours().compareTo(hours) > 0) {
225                 hours = earnCodeObj.getInflateMinHours();
226             }
227         }
228         //If earn code has an inflate factor multiple hours specified by the factor
229         if (earnCodeObj.getInflateFactor() != null) {
230             if ((earnCodeObj.getInflateFactor().compareTo(new BigDecimal(1.0)) != 0)
231             		&& (earnCodeObj.getInflateFactor().compareTo(BigDecimal.ZERO)!= 0) ) {
232                 hours = earnCodeObj.getInflateFactor().multiply(hours, TkConstants.MATH_CONTEXT).setScale(TkConstants.BIG_DECIMAL_SCALE);
233             }
234         }
235 
236         tb.setEarnCodeType(earnCodeObj.getEarnCodeType());
237         tb.setHours(hours);
238         tb.setClockLogCreated(clockLogCreated);
239         tb.setUserPrincipalId(userPrincipalId);
240         tb.setTimestamp(new Timestamp(System.currentTimeMillis()));
241         tb.setLunchDeleted(lunchDeleted);
242 
243         tb.setTimeHourDetails(this.createTimeHourDetails(tb.getEarnCode(), tb.getHours(), tb.getAmount(), tb.getTkTimeBlockId()));
244 
245         return tb;
246     }
247 
248     public TimeBlock getTimeBlock(String tkTimeBlockId) {
249         return timeBlockDao.getTimeBlock(tkTimeBlockId);
250     }
251 
252     @Override
253     public void deleteTimeBlock(TimeBlock timeBlock) {
254         timeBlockDao.deleteTimeBlock(timeBlock);
255 
256     }
257 
258     public void resetTimeHourDetail(List<TimeBlock> origTimeBlocks) {
259         for (TimeBlock tb : origTimeBlocks) {
260             tb.setTimeHourDetails(createTimeHourDetails(tb.getEarnCode(), tb.getHours(), tb.getAmount(), tb.getTkTimeBlockId()));
261             //reset time block history details
262             for(TimeBlockHistory tbh : tb.getTimeBlockHistories()) {
263             	TkServiceLocator.getTimeBlockHistoryService().addTimeBlockHistoryDetails(tbh,tb);
264             }
265         }
266     }
267 
268     private List<TimeHourDetail> createTimeHourDetails(String earnCode, BigDecimal hours, BigDecimal amount, String timeBlockId) {
269         List<TimeHourDetail> timeHourDetails = new ArrayList<TimeHourDetail>();
270 
271         TimeHourDetail timeHourDetail = new TimeHourDetail();
272         timeHourDetail.setEarnCode(earnCode);
273         timeHourDetail.setHours(hours);
274         timeHourDetail.setAmount(amount);
275         timeHourDetail.setTkTimeBlockId(timeBlockId);
276         timeHourDetails.add(timeHourDetail);
277 
278         return timeHourDetails;
279     }
280 
281     public List<TimeBlockHistory> createTimeBlockHistories(TimeBlock tb, String actionHistory) {
282         List<TimeBlockHistory> tbhs = new ArrayList<TimeBlockHistory>();
283 
284         TimeBlockHistory tbh = new TimeBlockHistory(tb);
285         tbh.setActionHistory(actionHistory);
286         // add time block history details to this time block history
287         TkServiceLocator.getTimeBlockHistoryService().addTimeBlockHistoryDetails(tbh, tb);
288         
289         tbhs.add(tbh);
290 
291         return tbhs;
292     }
293     
294     // This method now translates time based on timezone settings.
295     //
296     public List<TimeBlock> getTimeBlocks(String documentId) {
297     	List<TimeBlock> timeBlocks = timeBlockDao.getTimeBlocks(documentId);
298         TkServiceLocator.getTimezoneService().translateForTimezone(timeBlocks);
299         for(TimeBlock tb : timeBlocks) {
300             String earnCodeType = TkServiceLocator.getEarnCodeService().getEarnCodeType(tb.getEarnCode(), new java.sql.Date(tb.getBeginTimestamp().getTime()));
301             tb.setEarnCodeType(earnCodeType);
302         }
303 
304         return timeBlocks;
305     }
306 
307     public List<TimeBlock> getTimeBlocksForAssignment(Assignment assign) {
308     	List<TimeBlock> timeBlocks = new ArrayList<TimeBlock>();
309     	if(assign != null) {
310         	timeBlocks = timeBlockDao.getTimeBlocksForAssignment(assign);
311     	}
312     	TkServiceLocator.getTimezoneService().translateForTimezone(timeBlocks);
313     	 for(TimeBlock tb : timeBlocks) {
314              String earnCodeType = TkServiceLocator.getEarnCodeService().getEarnCodeType(tb.getEarnCode(), new java.sql.Date(tb.getBeginTimestamp().getTime()));
315              tb.setEarnCodeType(earnCodeType);
316          }
317     	return timeBlocks;
318     }
319 
320 
321 	@Override
322 	public void deleteTimeBlocksAssociatedWithDocumentId(String documentId) {
323 		timeBlockDao.deleteTimeBlocksAssociatedWithDocumentId(documentId);
324 	}
325 
326 	@Override
327 	// figure out if the user has permission to edit/delete the time block
328 	public Boolean isTimeBlockEditable(TimeBlock tb) {
329 		String userId = GlobalVariables.getUserSession().getPrincipalId();
330 
331     	if(userId != null) {
332 
333 			if(TKUser.isSystemAdmin()) {
334 				return true;
335 			}
336 
337 			if(TKUser.isTimesheetApprover() && TKUser.getApproverWorkAreas().contains(tb.getWorkArea())
338 					|| TKUser.isTimesheetReviewer() && TKUser.getReviewerWorkAreas().contains(tb.getWorkArea())) {
339 				Job job = TkServiceLocator.getJobService().getJob(TKContext.getTargetPrincipalId(),tb.getJobNumber(), tb.getEndDate());
340 				PayType payType = TkServiceLocator.getPayTypeService().getPayType(job.getHrPayType(), tb.getEndDate());
341 				if(StringUtils.equals(payType.getRegEarnCode(), tb.getEarnCode())){
342 					return true;
343 				}
344 
345 				List<EarnCodeSecurity> deptEarnCodes = TkServiceLocator.getEarnCodeSecurityService().getEarnCodeSecurities(job.getDept(), job.getHrSalGroup(), job.getLocation(), tb.getEndDate());
346 				for(EarnCodeSecurity dec : deptEarnCodes){
347 					if(dec.isApprover() && StringUtils.equals(dec.getEarnCode(), tb.getEarnCode())){
348 						return true;
349 					}
350 				}
351 			}
352 
353 			if(userId.equals(TKContext.getTargetPrincipalId())) {
354 				Job job = TkServiceLocator.getJobService().getJob(TKContext.getTargetPrincipalId(),tb.getJobNumber(), tb.getEndDate());
355 				PayType payType = TkServiceLocator.getPayTypeService().getPayType(job.getHrPayType(), tb.getEndDate());
356 				if(StringUtils.equals(payType.getRegEarnCode(), tb.getEarnCode())){
357 					return true;
358 				}
359 
360 				List<EarnCodeSecurity> deptEarnCodes = TkServiceLocator.getEarnCodeSecurityService().getEarnCodeSecurities(job.getDept(), job.getHrSalGroup(), job.getLocation(), tb.getEndDate());
361 				for(EarnCodeSecurity dec : deptEarnCodes){
362 					if(dec.isEmployee() && StringUtils.equals(dec.getEarnCode(), tb.getEarnCode())){
363 						return true;
364 					}
365 				}
366 				// if the user is the creator of this time block
367 			}
368 
369 
370 		}
371 		return false;
372 	}
373 
374 	@Override
375 	public List<TimeBlock> getTimeBlocksForClockLogEndId(String tkClockLogId) {
376 		return timeBlockDao.getTimeBlocksForClockLogEndId(tkClockLogId);
377 	}
378 	@Override
379 	public List<TimeBlock> getTimeBlocksForClockLogBeginId(String tkClockLogId) {
380 		return timeBlockDao.getTimeBlocksForClockLogBeginId(tkClockLogId);
381 	}
382 
383 	@Override
384 	public List<TimeBlock> getLatestEndTimestampForEarnCode(String earnCode){
385 		return timeBlockDao.getLatestEndTimestampForEarnCode(earnCode);
386 	}
387 
388     @Override
389     public List<TimeBlock> getOvernightTimeBlocks(String clockLogEndId) {
390         return timeBlockDao.getOvernightTimeBlocks(clockLogEndId);
391     }
392     
393     @Override
394     public void deleteLunchDeduction(String tkTimeHourDetailId) {
395         TimeHourDetail thd = TkServiceLocator.getTimeHourDetailService().getTimeHourDetail(tkTimeHourDetailId);
396         TimeBlock tb = getTimeBlock(thd.getTkTimeBlockId());
397         
398         // mark the lunch deleted as Y
399         tb.setLunchDeleted(true);
400         // save the change
401         timeBlockDao.saveOrUpdate(tb);
402         // remove the related time hour detail row with the lunch deduction
403         TkServiceLocator.getTimeHourDetailService().removeTimeHourDetail(thd.getTkTimeHourDetailId());
404     }
405     @Override
406     public List<TimeBlock> getTimeBlocksWithEarnCode(String earnCode, Date effDate) {
407     	return timeBlockDao.getTimeBlocksWithEarnCode(earnCode, effDate);
408     }
409 }