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;
17  
18  import java.math.BigDecimal;
19  import java.sql.Time;
20  import java.sql.Timestamp;
21  import java.util.ArrayList;
22  import java.util.Date;
23  import java.util.List;
24  
25  import javax.persistence.Transient;
26  
27  import org.apache.commons.lang.builder.EqualsBuilder;
28  import org.apache.commons.lang.builder.HashCodeBuilder;
29  import org.joda.time.DateTime;
30  import org.kuali.hr.time.assignment.Assignment;
31  import org.kuali.hr.time.assignment.AssignmentDescriptionKey;
32  import org.kuali.hr.time.clocklog.ClockLog;
33  import org.kuali.hr.time.service.base.TkServiceLocator;
34  import org.kuali.hr.time.util.TkConstants;
35  import org.kuali.hr.time.workflow.TimesheetDocumentHeader;
36  import org.kuali.rice.kim.api.identity.Person;
37  import org.kuali.rice.krad.bo.PersistableBusinessObjectBase;
38  
39  public class TimeBlock extends PersistableBusinessObjectBase implements Comparable {
40  
41      /**
42       *
43       */
44      private static final long serialVersionUID = -4164042707879641855L;
45  
46      private String tkTimeBlockId;
47      private String documentId;
48      private Long jobNumber;
49      private Long workArea;
50      private Long task;
51      private String earnCode;
52      private String earnCodeType;
53      private Timestamp beginTimestamp;
54      private Timestamp endTimestamp;
55  
56      @Transient
57      private java.sql.Date beginDate;
58      @Transient
59      private java.sql.Date endDate;
60      @Transient
61      private Time beginTime;
62      @Transient
63      private Time endTime;
64  
65      private Boolean clockLogCreated;
66      private BigDecimal hours = TkConstants.BIG_DECIMAL_SCALED_ZERO;
67      private BigDecimal amount = TkConstants.BIG_DECIMAL_SCALED_ZERO;
68      private String principalId;
69      private String userPrincipalId;
70      private Timestamp timestamp;
71      private String beginTimestampTimezone;
72      private String endTimestampTimezone;
73      private DateTime beginTimeDisplay;
74      private DateTime endTimeDisplay;
75      private String clockLogBeginId;
76      private String clockLogEndId;
77      private String assignmentKey;
78      private String overtimePref;
79      private boolean lunchDeleted;
80      
81      @Transient
82      private Boolean deleteable;
83      
84      @Transient
85      private Boolean overtimeEditable;
86      
87      @Transient
88      private Boolean regEarnCodeEditable;
89  
90  
91      // the two variables below are used to determine if a time block needs to be visually pushed forward / backward
92      @Transient
93      private Boolean pushBackward = false;
94  
95      private TimesheetDocumentHeader timesheetDocumentHeader;
96      private Person user;
97      
98      private List<TimeHourDetail> timeHourDetails = new ArrayList<TimeHourDetail>();
99      private List<TimeBlockHistory> timeBlockHistories = new ArrayList<TimeBlockHistory>();
100 
101     public TimeBlock() {
102     }
103 
104     public String getDocumentId() {
105         return documentId;
106     }
107 
108     public void setDocumentId(String documentId) {
109         this.documentId = documentId;
110     }
111 
112     public Long getJobNumber() {
113         return jobNumber;
114     }
115 
116     public void setJobNumber(Long jobNumber) {
117         this.jobNumber = jobNumber;
118     }
119 
120     public String getEarnCode() {
121         return earnCode;
122     }
123 
124     public void setEarnCode(String earnCode) {
125         this.earnCode = earnCode;
126     }
127 
128     public Timestamp getBeginTimestamp() {
129         return beginTimestamp;
130     }
131 
132     public void setBeginTimestamp(Timestamp beginTimestamp) {
133         this.beginTimestamp = beginTimestamp;
134     }
135 
136     public Timestamp getEndTimestamp() {
137         return endTimestamp;
138     }
139 
140     public void setEndTimestamp(Timestamp endTimestamp) {
141         this.endTimestamp = endTimestamp;
142     }
143 
144     public java.sql.Date getBeginDate() {
145         if (beginDate == null && this.getBeginTimestamp() != null) {
146             setBeginDate(new java.sql.Date(this.getBeginTimestamp().getTime()));
147         }
148         return beginDate;
149     }
150 
151     public void setBeginDate(java.sql.Date beginDate) {
152         this.beginDate = beginDate;
153     }
154 
155     public java.sql.Date getEndDate() {
156         if (endDate == null && this.getEndTimestamp() != null) {
157             setEndDate(new java.sql.Date(this.getEndTimestamp().getTime()));
158         }
159         return endDate;
160     }
161 
162     public void setEndDate(java.sql.Date endDate) {
163         this.endDate = endDate;
164     }
165 
166     public Time getBeginTime() {
167         if (beginTime == null && this.getBeginTimestamp() != null) {
168             setBeginTime(new java.sql.Time(this.getBeginTimestamp().getTime()));
169         }
170         return beginTime;
171     }
172 
173     public void setBeginTime(Time beginTime) {
174         this.beginTime = beginTime;
175     }
176 
177     public Time getEndTime() {
178         if (endTime == null && this.getEndTimestamp() != null) {
179             setEndTime(new java.sql.Time(this.getEndTimestamp().getTime()));
180         }
181         return endTime;
182     }
183 
184     public void setEndTime(Time endTime) {
185         this.endTime = endTime;
186     }
187 
188 
189     public Boolean getClockLogCreated() {
190         return clockLogCreated;
191     }
192 
193     public void setClockLogCreated(Boolean clockLogCreated) {
194         this.clockLogCreated = clockLogCreated;
195     }
196 
197     public BigDecimal getHours() {
198         return hours;
199     }
200 
201     public void setHours(BigDecimal hours) {
202         if (hours != null) {
203             this.hours = hours.setScale(TkConstants.BIG_DECIMAL_SCALE, TkConstants.BIG_DECIMAL_SCALE_ROUNDING);
204         } else {
205             this.hours = hours;
206         }
207     }
208 
209     public BigDecimal getAmount() {
210         return amount;
211     }
212 
213     public void setAmount(BigDecimal amount) {
214         if (amount != null) {
215             this.amount = amount.setScale(TkConstants.BIG_DECIMAL_SCALE, TkConstants.BIG_DECIMAL_SCALE_ROUNDING);
216         } else {
217             this.amount = amount;
218         }
219     }
220 
221     public String getUserPrincipalId() {
222         return userPrincipalId;
223     }
224 
225     public void setUserPrincipalId(String userPrincipalId) {
226         this.userPrincipalId = userPrincipalId;
227     }
228 
229     public Timestamp getTimestamp() {
230         return timestamp;
231     }
232 
233     public void setTimestamp(Timestamp timestamp) {
234         this.timestamp = timestamp;
235     }
236 
237     public String getBeginTimestampTimezone() {
238         return beginTimestampTimezone;
239     }
240 
241     public void setBeginTimestampTimezone(String beginTimestampTimezone) {
242         this.beginTimestampTimezone = beginTimestampTimezone;
243     }
244 
245     public String getEndTimestampTimezone() {
246         return endTimestampTimezone;
247     }
248 
249     public void setEndTimestampTimezone(String endTimestampTimezone) {
250         this.endTimestampTimezone = endTimestampTimezone;
251     }
252 
253     public String toCSVString() {
254         StringBuffer sb = new StringBuffer();
255         sb.append(this.beginTimestampTimezone + ",");
256         sb.append(this.earnCode + ",");
257         sb.append(this.endTimestampTimezone + ",");
258         sb.append(this.userPrincipalId + ",");
259         sb.append(this.amount + ",");
260         sb.append(this.beginTimestamp + ",");
261         sb.append(this.clockLogCreated + ",");
262         sb.append(this.endTimestamp + ",");
263         sb.append(this.hours + ",");
264         sb.append(this.jobNumber + ",");
265         sb.append(this.task + ",");
266         sb.append(this.tkTimeBlockId + ",");
267         sb.append(this.timestamp + ",");
268         sb.append(this.workArea + System.getProperty("line.separator"));
269         return sb.toString();
270     }
271 
272     public String getTkTimeBlockId() {
273         return tkTimeBlockId;
274     }
275 
276     public void setTkTimeBlockId(String tkTimeBlockId) {
277         this.tkTimeBlockId = tkTimeBlockId;
278     }
279 
280     public Long getWorkArea() {
281         return workArea;
282     }
283 
284     public void setWorkArea(Long workArea) {
285         this.workArea = workArea;
286     }
287 
288     public Long getTask() {
289         return task;
290     }
291 
292     public void setTask(Long task) {
293         this.task = task;
294     }
295 
296     public List<TimeHourDetail> getTimeHourDetails() {
297         return timeHourDetails;
298     }
299     
300     public void addTimeHourDetail(TimeHourDetail timeHourDetail) {
301     	timeHourDetails.add(timeHourDetail);
302     }
303     
304     public void removeTimeHourDetail(TimeHourDetail timeHourDetail) {
305     	timeHourDetails.remove(timeHourDetail);
306     }
307 
308     public void setTimeHourDetails(List<TimeHourDetail> timeHourDetails) {
309         this.timeHourDetails = timeHourDetails;
310     }
311 
312     public Boolean isPushBackward() {
313         return pushBackward;
314     }
315 
316     public void setPushBackward(Boolean pushBackward) {
317         this.pushBackward = pushBackward;
318     }
319 
320     /**
321      * Use this call for all GUI/Display related rendering of the BEGIN
322      * timestamp of the given time block. Timeblocks require pre-processing
323      * before there will be a non-null return value here.
324      *
325      * @return The Timeblock Begin time to display, with the Users Timezone
326      *         taken into account and applied to this DateTime object.
327      */
328     public DateTime getBeginTimeDisplay() {
329         return beginTimeDisplay;
330     }
331 
332     /**
333      * Helper to call DateTime.toDate().
334      *
335      * @return a java.util.Date representing the getBeginTimeDisplay() DateTime.
336      */
337     public Date getBeginTimeDisplayDate() {
338         return getBeginTimeDisplay().toDate();
339     }
340 
341     /*
342     *   fix timezone issues caused by JScript, for GUI use only,
343     */
344     public String getBeginTimeDisplayDateOnlyString() {
345         return this.getBeginTimeDisplay().toString(TkConstants.DT_BASIC_DATE_FORMAT);
346     }
347 
348     public String getBeginTimeDisplayTimeOnlyString() {
349         return this.getBeginTimeDisplay().toString(TkConstants.DT_BASIC_TIME_FORMAT);
350     }
351 
352     public String getEndTimeDisplayDateOnlyString() {
353         return this.getEndTimeDisplay().toString(TkConstants.DT_BASIC_DATE_FORMAT);
354     }
355 
356     public String getEndTimeDisplayTimeOnlyString() {
357         return this.getEndTimeDisplay().toString(TkConstants.DT_BASIC_TIME_FORMAT);
358     }
359 
360     /**
361      * Set this value with a DateTime that is in the current users Timezone. This
362      * should happen as a pre processing step for display purposes. Do not use these
363      * values for server-side computation.
364      *
365      * @param beginTimeDisplay
366      */
367     public void setBeginTimeDisplay(DateTime beginTimeDisplay) {
368         this.beginTimeDisplay = beginTimeDisplay;
369     }
370 
371     /**
372      * Use this call for all GUI/Display related rendering of the END
373      * timestamp of the given time block. Timeblocks require pre-processing
374      * before there will be a non-null return value here.
375      *
376      * @return The Timeblock end time to display, with the Users Timezone
377      *         taken into account and applied to this DateTime object.
378      */
379     public DateTime getEndTimeDisplay() {
380         return endTimeDisplay;
381     }
382 
383     /**
384      * Helper to call DateTime.toDate().
385      *
386      * @return a java.util.Date representing the getEndTimeDisplay() DateTime.
387      */
388     public Date getEndTimeDisplayDate() {
389         return getEndTimeDisplay().toDate();
390     }
391 
392     /**
393      * Set this value with a DateTime that is in the current users Timezone. This
394      * should happen as a pre processing step for display purposes. Do not use these
395      * values for server-side computation.
396      *
397      * @param endTimeDisplay
398      */
399     public void setEndTimeDisplay(DateTime endTimeDisplay) {
400         this.endTimeDisplay = endTimeDisplay;
401     }
402 
403     public TimesheetDocumentHeader getTimesheetDocumentHeader() {
404         if (timesheetDocumentHeader == null && this.getDocumentId() != null) {
405             setTimesheetDocumentHeader(TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(this.getDocumentId()));
406         }
407         return timesheetDocumentHeader;
408     }
409 
410     public void setTimesheetDocumentHeader(
411             TimesheetDocumentHeader timesheetDocumentHeader) {
412         this.timesheetDocumentHeader = timesheetDocumentHeader;
413     }
414 
415     public List<TimeBlockHistory> getTimeBlockHistories() {
416         return timeBlockHistories;
417     }
418 
419     public void setTimeBlockHistories(List<TimeBlockHistory> timeBlockHistories) {
420         this.timeBlockHistories = timeBlockHistories;
421     }
422 
423     public String getClockLogBeginId() {
424         return clockLogBeginId;
425     }
426 
427     public void setClockLogBeginId(String clockLogBeginId) {
428         this.clockLogBeginId = clockLogBeginId;
429     }
430 
431     public String getClockLogEndId() {
432         return clockLogEndId;
433     }
434 
435     public void setClockLogEndId(String clockLogEndId) {
436         this.clockLogEndId = clockLogEndId;
437     }
438 
439     public String getAssignmentKey() {
440         if (assignmentKey == null) {
441             AssignmentDescriptionKey adk = new AssignmentDescriptionKey(this.getJobNumber().toString(), this.getWorkArea().toString(), this.getTask().toString());
442             this.setAssignmentKey(adk.toAssignmentKeyString());
443         }
444         return assignmentKey;
445     }
446 
447     public void setAssignmentKey(String assignmentDescription) {
448         this.assignmentKey = assignmentDescription;
449     }
450 
451     public String getAssignmentDescription() {
452         AssignmentDescriptionKey adk = new AssignmentDescriptionKey(this.getJobNumber().toString(), this.getWorkArea().toString(), this.getTask().toString());
453         Assignment anAssignment = TkServiceLocator.getAssignmentService().getAssignment(adk, this.getBeginDate());
454         return anAssignment == null ? this.getAssignmentKey() : anAssignment.getAssignmentDescription();
455     }
456 
457 
458     /**
459      * Word on the street is that Object.clone() is a POS. We only need some
460      * basics for comparison, so we'll implement a simple copy constructor
461      * instead.
462      * <p/>
463      * TODO: Check whether or not it matters if the History is copied, this
464      * operation needs to be as inexpensive as possible.
465      *
466      * @param b The TimeBlock to copy values from when creating this instance.
467      */
468     protected TimeBlock(TimeBlock b) {
469         // TODO : Implement "copy" constructor.
470         this.tkTimeBlockId = b.tkTimeBlockId;
471         this.documentId = b.documentId;
472         this.jobNumber = b.jobNumber;
473         this.workArea = b.workArea;
474         this.task = b.task;
475         this.earnCode = b.earnCode;
476         this.beginTimestamp = new Timestamp(b.beginTimestamp.getTime());
477         this.endTimestamp = new Timestamp(b.endTimestamp.getTime());
478         this.clockLogCreated = b.clockLogCreated;
479         this.hours = b.hours;
480         this.amount = b.amount;
481         this.userPrincipalId = b.userPrincipalId;
482         this.timestamp = new Timestamp(b.timestamp.getTime());
483         this.beginTimeDisplay = b.beginTimeDisplay;
484         this.endTimeDisplay = b.endTimeDisplay;
485         this.pushBackward = b.pushBackward;
486         this.clockLogBeginId = b.clockLogBeginId;
487         this.clockLogEndId = b.clockLogEndId;
488         this.principalId = b.principalId;
489 
490         // We just set the reference for this object, since splitting the
491         // TimeBlock would be abnormal behavior.
492         this.timesheetDocumentHeader = b.timesheetDocumentHeader;
493 
494         //private List<TimeHourDetail> timeHourDetails = new ArrayList<TimeHourDetail>();
495         for (TimeHourDetail thd : b.timeHourDetails) {
496             this.timeHourDetails.add(thd.copy());
497         }
498 
499         // TODO: For now, not copying TimeBlockHistory - The Object extends this one, which seems odd.
500         //private List<TimeBlockHistory> timeBlockHistories = new ArrayList<TimeBlockHistory>();
501     }
502 
503     /**
504      * @return A new copy of this TimeBlock.
505      */
506     public TimeBlock copy() {
507         return new TimeBlock(this);
508     }
509 
510     public String getEarnCodeType() {
511         return earnCodeType;
512     }
513 
514     public void setEarnCodeType(String earnCodeType) {
515         this.earnCodeType = earnCodeType;
516     }
517 
518     /**
519      * This is for distribute time block page to sort it by begin date/time
520      *
521      * @see java.lang.Comparable#compareTo(java.lang.Object)
522      */
523     public int compareTo(Object o) {
524         return compareTo((TimeBlock) o);
525     }
526 
527     public int compareTo(TimeBlock tb) {
528         return this.getBeginTimestamp().compareTo(tb.getBeginTimestamp());
529     }
530 
531     public Boolean getEditable() {
532         return TkServiceLocator.getTimeBlockService().isTimeBlockEditable(this);
533     }
534 
535     public String getPrincipalId() {
536         return principalId;
537     }
538 
539     public void setPrincipalId(String principalId) {
540         this.principalId = principalId;
541     }
542 
543     public String getOvertimePref() {
544         return overtimePref;
545     }
546 
547     public void setOvertimePref(String overtimePref) {
548         this.overtimePref = overtimePref;
549     }
550 
551     /* apply grace period rule to times of time block
552      * These strings are for GUI of Actual time inquiry
553     */
554     public String getActualBeginTimeString() {
555         if (this.getClockLogBeginId() != null) {
556             if (isOvernightTimeClockLog(clockLogEndId)) {
557                 return new DateTime(beginTimestamp).toString(TkConstants.DT_FULL_DATE_TIME_FORMAT);
558             } else {
559                 ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(this.getClockLogBeginId());
560                 if (cl != null) {
561                     return new DateTime(cl.getTimestamp()).toString(TkConstants.DT_FULL_DATE_TIME_FORMAT);
562                 }
563             }
564 
565         }
566         return "";
567     }
568 
569     public String getActualEndTimeString() {
570         if (this.getClockLogEndId() != null) {
571             if (isOvernightTimeClockLog(clockLogEndId)) {
572                 return new DateTime(endTimestamp).toString(TkConstants.DT_FULL_DATE_TIME_FORMAT);
573             } else {
574                 ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(this.getClockLogEndId());
575                 if (cl != null) {
576                     return new DateTime(cl.getTimestamp()).toString(TkConstants.DT_FULL_DATE_TIME_FORMAT);
577                 }
578             }
579 
580         }
581         return "";
582     }
583 
584     private Boolean isOvernightTimeClockLog(String clockLogId) {
585         // https://jira.kuali.org/browse/KPME-1179
586         Integer overnightTimeBlocks = TkServiceLocator.getTimeBlockService().getOvernightTimeBlocks(clockLogEndId).size();
587         if (overnightTimeBlocks >= 2) {
588             return true;
589         }
590 
591         return false;
592     }
593 
594 	public Boolean getDeleteable() {
595 		return TkServiceLocator.getPermissionsService().canDeleteTimeBlock(this);
596 	}
597 
598 	public Boolean getOvertimeEditable() {
599 		return TkServiceLocator.getPermissionsService().canEditOvertimeEarnCode(this);
600 	}
601 	
602 	public Boolean getRegEarnCodeEditable() {
603 		return TkServiceLocator.getPermissionsService().canEditRegEarnCode(this);
604 	}
605 
606     public Boolean getTimeBlockEditable(){
607         return TkServiceLocator.getPermissionsService().canEditTimeBlock(this);
608     }
609 
610     public boolean isLunchDeleted() {
611         return lunchDeleted;
612     }
613 
614     public void setLunchDeleted(boolean lunchDeleted) {
615         this.lunchDeleted = lunchDeleted;
616     }
617 
618 	public Person getUser() {
619 		return user;
620 	}
621 
622 	public void setUser(Person user) {
623 		this.user = user;
624 	}
625 	
626 	@Override
627 	public boolean equals(Object obj) {
628 		if (obj == null) { 
629 			return false;
630 		}
631 		if (obj == this) { 
632 			return true;
633 		}
634 		if (obj.getClass() != getClass()) {
635 			return false;
636 		}
637 		TimeBlock timeBlock = (TimeBlock) obj;
638 		return new EqualsBuilder()
639 			.append(jobNumber, timeBlock.jobNumber)
640 			.append(workArea, timeBlock.workArea)
641 			.append(task, timeBlock.task)
642 			.append(earnCode, timeBlock.earnCode)
643 			.append(beginTimestamp, timeBlock.beginTimestamp)
644 			.append(endTimestamp, timeBlock.endTimestamp)
645 			.append(hours, timeBlock.hours)
646 			.append(timeHourDetails, timeBlock.timeHourDetails)
647 			.isEquals();
648 	}
649 
650     @Override
651     public int hashCode() {
652     	return new HashCodeBuilder(17, 31)
653     		.append(jobNumber)
654     		.append(workArea)
655     		.append(task)
656     		.append(earnCode)
657     		.append(beginTimestamp)
658     		.append(endTimestamp)
659     		.append(hours)
660     		.append(timeHourDetails)
661     		.toHashCode();
662     }
663     
664 }