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