001 /** 002 * Copyright 2004-2012 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.hr.time.timeblock; 017 018 import java.math.BigDecimal; 019 import java.sql.Time; 020 import java.sql.Timestamp; 021 import java.util.ArrayList; 022 import java.util.Date; 023 import java.util.List; 024 025 import javax.persistence.Transient; 026 027 import org.apache.commons.lang.builder.EqualsBuilder; 028 import org.apache.commons.lang.builder.HashCodeBuilder; 029 import org.joda.time.DateTime; 030 import org.kuali.hr.time.assignment.Assignment; 031 import org.kuali.hr.time.assignment.AssignmentDescriptionKey; 032 import org.kuali.hr.time.clocklog.ClockLog; 033 import org.kuali.hr.time.service.base.TkServiceLocator; 034 import org.kuali.hr.time.util.TkConstants; 035 import org.kuali.hr.time.workflow.TimesheetDocumentHeader; 036 import org.kuali.rice.kim.api.identity.Person; 037 import org.kuali.rice.krad.bo.PersistableBusinessObjectBase; 038 039 public class TimeBlock extends PersistableBusinessObjectBase implements Comparable { 040 041 /** 042 * 043 */ 044 private static final long serialVersionUID = -4164042707879641855L; 045 046 private String tkTimeBlockId; 047 private String documentId; 048 private Long jobNumber; 049 private Long workArea; 050 private Long task; 051 private String hrJobId; 052 private String tkWorkAreaId; 053 private String tkTaskId; 054 private String earnCode; 055 private String earnCodeType; 056 private Timestamp beginTimestamp; 057 private Timestamp endTimestamp; 058 059 @Transient 060 private java.sql.Date beginDate; 061 @Transient 062 private java.sql.Date endDate; 063 @Transient 064 private Time beginTime; 065 @Transient 066 private Time endTime; 067 068 private Boolean clockLogCreated; 069 private BigDecimal hours = TkConstants.BIG_DECIMAL_SCALED_ZERO; 070 private BigDecimal amount = TkConstants.BIG_DECIMAL_SCALED_ZERO; 071 private String principalId; 072 private String userPrincipalId; 073 private Timestamp timestamp; 074 private String beginTimestampTimezone; 075 private String endTimestampTimezone; 076 private DateTime beginTimeDisplay; 077 private DateTime endTimeDisplay; 078 private String clockLogBeginId; 079 private String clockLogEndId; 080 private String assignmentKey; 081 private String overtimePref; 082 private boolean lunchDeleted; 083 084 @Transient 085 private Boolean deleteable; 086 087 @Transient 088 private Boolean overtimeEditable; 089 090 @Transient 091 private Boolean regEarnCodeEditable; 092 093 094 // the two variables below are used to determine if a time block needs to be visually pushed forward / backward 095 @Transient 096 private Boolean pushBackward = false; 097 098 private TimesheetDocumentHeader timesheetDocumentHeader; 099 private Person user; 100 101 private List<TimeHourDetail> timeHourDetails = new ArrayList<TimeHourDetail>(); 102 private List<TimeBlockHistory> timeBlockHistories = new ArrayList<TimeBlockHistory>(); 103 104 public TimeBlock() { 105 } 106 107 public String getDocumentId() { 108 return documentId; 109 } 110 111 public void setDocumentId(String documentId) { 112 this.documentId = documentId; 113 } 114 115 public Long getJobNumber() { 116 return jobNumber; 117 } 118 119 public void setJobNumber(Long jobNumber) { 120 this.jobNumber = jobNumber; 121 } 122 123 public String getEarnCode() { 124 return earnCode; 125 } 126 127 public void setEarnCode(String earnCode) { 128 this.earnCode = earnCode; 129 } 130 131 public Timestamp getBeginTimestamp() { 132 return beginTimestamp; 133 } 134 135 public void setBeginTimestamp(Timestamp beginTimestamp) { 136 this.beginTimestamp = beginTimestamp; 137 } 138 139 public Timestamp getEndTimestamp() { 140 return endTimestamp; 141 } 142 143 public void setEndTimestamp(Timestamp endTimestamp) { 144 this.endTimestamp = endTimestamp; 145 } 146 147 public java.sql.Date getBeginDate() { 148 if (beginDate == null && this.getBeginTimestamp() != null) { 149 setBeginDate(new java.sql.Date(this.getBeginTimestamp().getTime())); 150 } 151 return beginDate; 152 } 153 154 public void setBeginDate(java.sql.Date beginDate) { 155 this.beginDate = beginDate; 156 } 157 158 public java.sql.Date getEndDate() { 159 if (endDate == null && this.getEndTimestamp() != null) { 160 setEndDate(new java.sql.Date(this.getEndTimestamp().getTime())); 161 } 162 return endDate; 163 } 164 165 public void setEndDate(java.sql.Date endDate) { 166 this.endDate = endDate; 167 } 168 169 public Time getBeginTime() { 170 if (beginTime == null && this.getBeginTimestamp() != null) { 171 setBeginTime(new java.sql.Time(this.getBeginTimestamp().getTime())); 172 } 173 return beginTime; 174 } 175 176 public void setBeginTime(Time beginTime) { 177 this.beginTime = beginTime; 178 } 179 180 public Time getEndTime() { 181 if (endTime == null && this.getEndTimestamp() != null) { 182 setEndTime(new java.sql.Time(this.getEndTimestamp().getTime())); 183 } 184 return endTime; 185 } 186 187 public void setEndTime(Time endTime) { 188 this.endTime = endTime; 189 } 190 191 192 public Boolean getClockLogCreated() { 193 return clockLogCreated; 194 } 195 196 public void setClockLogCreated(Boolean clockLogCreated) { 197 this.clockLogCreated = clockLogCreated; 198 } 199 200 public BigDecimal getHours() { 201 return hours; 202 } 203 204 public void setHours(BigDecimal hours) { 205 if (hours != null) { 206 this.hours = hours.setScale(TkConstants.BIG_DECIMAL_SCALE, TkConstants.BIG_DECIMAL_SCALE_ROUNDING); 207 } else { 208 this.hours = hours; 209 } 210 } 211 212 public BigDecimal getAmount() { 213 return amount; 214 } 215 216 public void setAmount(BigDecimal amount) { 217 if (amount != null) { 218 this.amount = amount.setScale(TkConstants.BIG_DECIMAL_SCALE, TkConstants.BIG_DECIMAL_SCALE_ROUNDING); 219 } else { 220 this.amount = amount; 221 } 222 } 223 224 public String getUserPrincipalId() { 225 return userPrincipalId; 226 } 227 228 public void setUserPrincipalId(String userPrincipalId) { 229 this.userPrincipalId = userPrincipalId; 230 } 231 232 public Timestamp getTimestamp() { 233 return timestamp; 234 } 235 236 public void setTimestamp(Timestamp timestamp) { 237 this.timestamp = timestamp; 238 } 239 240 public String getBeginTimestampTimezone() { 241 return beginTimestampTimezone; 242 } 243 244 public void setBeginTimestampTimezone(String beginTimestampTimezone) { 245 this.beginTimestampTimezone = beginTimestampTimezone; 246 } 247 248 public String getEndTimestampTimezone() { 249 return endTimestampTimezone; 250 } 251 252 public void setEndTimestampTimezone(String endTimestampTimezone) { 253 this.endTimestampTimezone = endTimestampTimezone; 254 } 255 256 public String toCSVString() { 257 StringBuffer sb = new StringBuffer(); 258 sb.append(this.beginTimestampTimezone + ","); 259 sb.append(this.earnCode + ","); 260 sb.append(this.endTimestampTimezone + ","); 261 sb.append(this.userPrincipalId + ","); 262 sb.append(this.amount + ","); 263 sb.append(this.beginTimestamp + ","); 264 sb.append(this.clockLogCreated + ","); 265 sb.append(this.endTimestamp + ","); 266 sb.append(this.hours + ","); 267 sb.append(this.jobNumber + ","); 268 sb.append(this.task + ","); 269 sb.append(this.tkTimeBlockId + ","); 270 sb.append(this.timestamp + ","); 271 sb.append(this.workArea + System.getProperty("line.separator")); 272 return sb.toString(); 273 } 274 275 public String getTkTimeBlockId() { 276 return tkTimeBlockId; 277 } 278 279 public void setTkTimeBlockId(String tkTimeBlockId) { 280 this.tkTimeBlockId = tkTimeBlockId; 281 } 282 283 public Long getWorkArea() { 284 return workArea; 285 } 286 287 public void setWorkArea(Long workArea) { 288 this.workArea = workArea; 289 } 290 291 public Long getTask() { 292 return task; 293 } 294 295 public void setTask(Long task) { 296 this.task = task; 297 } 298 299 public String getHrJobId() { 300 return hrJobId; 301 } 302 303 public void setHrJobId(String hrJobId) { 304 this.hrJobId = hrJobId; 305 } 306 307 public String getTkWorkAreaId() { 308 return tkWorkAreaId; 309 } 310 311 public void setTkWorkAreaId(String tkWorkAreaId) { 312 this.tkWorkAreaId = tkWorkAreaId; 313 } 314 315 public String getTkTaskId() { 316 return tkTaskId; 317 } 318 319 public void setTkTaskId(String tkTaskId) { 320 this.tkTaskId = tkTaskId; 321 } 322 323 public List<TimeHourDetail> getTimeHourDetails() { 324 return timeHourDetails; 325 } 326 327 public void setTimeHourDetails(List<TimeHourDetail> timeHourDetails) { 328 this.timeHourDetails = timeHourDetails; 329 } 330 331 public Boolean isPushBackward() { 332 return pushBackward; 333 } 334 335 public void setPushBackward(Boolean pushBackward) { 336 this.pushBackward = pushBackward; 337 } 338 339 /** 340 * Use this call for all GUI/Display related rendering of the BEGIN 341 * timestamp of the given time block. Timeblocks require pre-processing 342 * before there will be a non-null return value here. 343 * 344 * @return The Timeblock Begin time to display, with the Users Timezone 345 * taken into account and applied to this DateTime object. 346 */ 347 public DateTime getBeginTimeDisplay() { 348 return beginTimeDisplay; 349 } 350 351 /** 352 * Helper to call DateTime.toDate(). 353 * 354 * @return a java.util.Date representing the getBeginTimeDisplay() DateTime. 355 */ 356 public Date getBeginTimeDisplayDate() { 357 return getBeginTimeDisplay().toDate(); 358 } 359 360 /* 361 * fix timezone issues caused by JScript, for GUI use only, 362 */ 363 public String getBeginTimeDisplayDateOnlyString() { 364 return this.getBeginTimeDisplay().toString(TkConstants.DT_BASIC_DATE_FORMAT); 365 } 366 367 public String getBeginTimeDisplayTimeOnlyString() { 368 return this.getBeginTimeDisplay().toString(TkConstants.DT_BASIC_TIME_FORMAT); 369 } 370 371 public String getEndTimeDisplayDateOnlyString() { 372 return this.getEndTimeDisplay().toString(TkConstants.DT_BASIC_DATE_FORMAT); 373 } 374 375 public String getEndTimeDisplayTimeOnlyString() { 376 return this.getEndTimeDisplay().toString(TkConstants.DT_BASIC_TIME_FORMAT); 377 } 378 379 /** 380 * Set this value with a DateTime that is in the current users Timezone. This 381 * should happen as a pre processing step for display purposes. Do not use these 382 * values for server-side computation. 383 * 384 * @param beginTimeDisplay 385 */ 386 public void setBeginTimeDisplay(DateTime beginTimeDisplay) { 387 this.beginTimeDisplay = beginTimeDisplay; 388 } 389 390 /** 391 * Use this call for all GUI/Display related rendering of the END 392 * timestamp of the given time block. Timeblocks require pre-processing 393 * before there will be a non-null return value here. 394 * 395 * @return The Timeblock end time to display, with the Users Timezone 396 * taken into account and applied to this DateTime object. 397 */ 398 public DateTime getEndTimeDisplay() { 399 return endTimeDisplay; 400 } 401 402 /** 403 * Helper to call DateTime.toDate(). 404 * 405 * @return a java.util.Date representing the getEndTimeDisplay() DateTime. 406 */ 407 public Date getEndTimeDisplayDate() { 408 return getEndTimeDisplay().toDate(); 409 } 410 411 /** 412 * Set this value with a DateTime that is in the current users Timezone. This 413 * should happen as a pre processing step for display purposes. Do not use these 414 * values for server-side computation. 415 * 416 * @param endTimeDisplay 417 */ 418 public void setEndTimeDisplay(DateTime endTimeDisplay) { 419 this.endTimeDisplay = endTimeDisplay; 420 } 421 422 public TimesheetDocumentHeader getTimesheetDocumentHeader() { 423 if (timesheetDocumentHeader == null && this.getDocumentId() != null) { 424 setTimesheetDocumentHeader(TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(this.getDocumentId())); 425 } 426 return timesheetDocumentHeader; 427 } 428 429 public void setTimesheetDocumentHeader( 430 TimesheetDocumentHeader timesheetDocumentHeader) { 431 this.timesheetDocumentHeader = timesheetDocumentHeader; 432 } 433 434 public List<TimeBlockHistory> getTimeBlockHistories() { 435 return timeBlockHistories; 436 } 437 438 public void setTimeBlockHistories(List<TimeBlockHistory> timeBlockHistories) { 439 this.timeBlockHistories = timeBlockHistories; 440 } 441 442 public String getClockLogBeginId() { 443 return clockLogBeginId; 444 } 445 446 public void setClockLogBeginId(String clockLogBeginId) { 447 this.clockLogBeginId = clockLogBeginId; 448 } 449 450 public String getClockLogEndId() { 451 return clockLogEndId; 452 } 453 454 public void setClockLogEndId(String clockLogEndId) { 455 this.clockLogEndId = clockLogEndId; 456 } 457 458 public String getAssignmentKey() { 459 if (assignmentKey == null) { 460 AssignmentDescriptionKey adk = new AssignmentDescriptionKey(this.getJobNumber().toString(), this.getWorkArea().toString(), this.getTask().toString()); 461 this.setAssignmentKey(adk.toAssignmentKeyString()); 462 } 463 return assignmentKey; 464 } 465 466 public void setAssignmentKey(String assignmentDescription) { 467 this.assignmentKey = assignmentDescription; 468 } 469 470 public String getAssignmentDescription() { 471 AssignmentDescriptionKey adk = new AssignmentDescriptionKey(this.getJobNumber().toString(), this.getWorkArea().toString(), this.getTask().toString()); 472 Assignment anAssignment = TkServiceLocator.getAssignmentService().getAssignment(adk, this.getBeginDate()); 473 return anAssignment == null ? this.getAssignmentKey() : anAssignment.getAssignmentDescription(); 474 } 475 476 477 /** 478 * Word on the street is that Object.clone() is a POS. We only need some 479 * basics for comparison, so we'll implement a simple copy constructor 480 * instead. 481 * <p/> 482 * TODO: Check whether or not it matters if the History is copied, this 483 * operation needs to be as inexpensive as possible. 484 * 485 * @param b The TimeBlock to copy values from when creating this instance. 486 */ 487 protected TimeBlock(TimeBlock b) { 488 // TODO : Implement "copy" constructor. 489 this.tkTimeBlockId = b.tkTimeBlockId; 490 this.documentId = b.documentId; 491 this.jobNumber = b.jobNumber; 492 this.workArea = b.workArea; 493 this.task = b.task; 494 this.hrJobId = b.hrJobId; 495 this.tkWorkAreaId = b.tkWorkAreaId; 496 this.tkTaskId = b.tkTaskId; 497 this.earnCode = b.earnCode; 498 this.beginTimestamp = new Timestamp(b.beginTimestamp.getTime()); 499 this.endTimestamp = new Timestamp(b.endTimestamp.getTime()); 500 this.clockLogCreated = b.clockLogCreated; 501 this.hours = b.hours; 502 this.amount = b.amount; 503 this.userPrincipalId = b.userPrincipalId; 504 this.timestamp = new Timestamp(b.timestamp.getTime()); 505 this.beginTimeDisplay = b.beginTimeDisplay; 506 this.endTimeDisplay = b.endTimeDisplay; 507 this.pushBackward = b.pushBackward; 508 this.clockLogBeginId = b.clockLogBeginId; 509 this.clockLogEndId = b.clockLogEndId; 510 511 // We just set the reference for this object, since splitting the 512 // TimeBlock would be abnormal behavior. 513 this.timesheetDocumentHeader = b.timesheetDocumentHeader; 514 515 //private List<TimeHourDetail> timeHourDetails = new ArrayList<TimeHourDetail>(); 516 for (TimeHourDetail thd : b.timeHourDetails) { 517 this.timeHourDetails.add(thd.copy()); 518 } 519 520 // TODO: For now, not copying TimeBlockHistory - The Object extends this one, which seems odd. 521 //private List<TimeBlockHistory> timeBlockHistories = new ArrayList<TimeBlockHistory>(); 522 } 523 524 /** 525 * @return A new copy of this TimeBlock. 526 */ 527 public TimeBlock copy() { 528 return new TimeBlock(this); 529 } 530 531 public String getEarnCodeType() { 532 return earnCodeType; 533 } 534 535 public void setEarnCodeType(String earnCodeType) { 536 this.earnCodeType = earnCodeType; 537 } 538 539 /** 540 * This is for distribute time block page to sort it by begin date/time 541 * 542 * @see java.lang.Comparable#compareTo(java.lang.Object) 543 */ 544 public int compareTo(Object o) { 545 return compareTo((TimeBlock) o); 546 } 547 548 public int compareTo(TimeBlock tb) { 549 return this.getBeginTimestamp().compareTo(tb.getBeginTimestamp()); 550 } 551 552 public Boolean getEditable() { 553 return TkServiceLocator.getTimeBlockService().isTimeBlockEditable(this); 554 } 555 556 public String getPrincipalId() { 557 return principalId; 558 } 559 560 public void setPrincipalId(String principalId) { 561 this.principalId = principalId; 562 } 563 564 public String getOvertimePref() { 565 return overtimePref; 566 } 567 568 public void setOvertimePref(String overtimePref) { 569 this.overtimePref = overtimePref; 570 } 571 572 /* apply grace period rule to times of time block 573 * These strings are for GUI of Actual time inquiry 574 */ 575 public String getActualBeginTimeString() { 576 if (this.getClockLogBeginId() != null) { 577 if (isOvernightTimeClockLog(clockLogEndId)) { 578 return new DateTime(beginTimestamp).toString(TkConstants.DT_FULL_DATE_TIME_FORMAT); 579 } else { 580 ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(this.getClockLogBeginId()); 581 if (cl != null) { 582 return new DateTime(cl.getTimestamp()).toString(TkConstants.DT_FULL_DATE_TIME_FORMAT); 583 } 584 } 585 586 } 587 return ""; 588 } 589 590 public String getActualEndTimeString() { 591 if (this.getClockLogEndId() != null) { 592 if (isOvernightTimeClockLog(clockLogEndId)) { 593 return new DateTime(endTimestamp).toString(TkConstants.DT_FULL_DATE_TIME_FORMAT); 594 } else { 595 ClockLog cl = TkServiceLocator.getClockLogService().getClockLog(this.getClockLogEndId()); 596 if (cl != null) { 597 return new DateTime(cl.getTimestamp()).toString(TkConstants.DT_FULL_DATE_TIME_FORMAT); 598 } 599 } 600 601 } 602 return ""; 603 } 604 605 private Boolean isOvernightTimeClockLog(String clockLogId) { 606 // https://jira.kuali.org/browse/KPME-1179 607 Integer overnightTimeBlocks = TkServiceLocator.getTimeBlockService().getOvernightTimeBlocks(clockLogEndId).size(); 608 if (overnightTimeBlocks >= 2) { 609 return true; 610 } 611 612 return false; 613 } 614 615 public Boolean getDeleteable() { 616 return TkServiceLocator.getPermissionsService().canDeleteTimeBlock(this); 617 } 618 619 public Boolean getOvertimeEditable() { 620 return TkServiceLocator.getPermissionsService().canEditOvertimeEarnCode(this); 621 } 622 623 public Boolean getRegEarnCodeEditable() { 624 return TkServiceLocator.getPermissionsService().canEditRegEarnCode(this); 625 } 626 627 public Boolean getTimeBlockEditable(){ 628 return TkServiceLocator.getPermissionsService().canEditTimeBlock(this); 629 } 630 631 public boolean isLunchDeleted() { 632 return lunchDeleted; 633 } 634 635 public void setLunchDeleted(boolean lunchDeleted) { 636 this.lunchDeleted = lunchDeleted; 637 } 638 639 public Person getUser() { 640 return user; 641 } 642 643 public void setUser(Person user) { 644 this.user = user; 645 } 646 647 @Override 648 public boolean equals(Object obj) { 649 if (obj == null) { 650 return false; 651 } 652 if (obj == this) { 653 return true; 654 } 655 if (obj.getClass() != getClass()) { 656 return false; 657 } 658 TimeBlock timeBlock = (TimeBlock) obj; 659 return new EqualsBuilder() 660 .append(jobNumber, timeBlock.jobNumber) 661 .append(workArea, timeBlock.workArea) 662 .append(task, timeBlock.task) 663 .append(earnCode, timeBlock.earnCode) 664 .append(beginTimestamp, timeBlock.beginTimestamp) 665 .append(endTimestamp, timeBlock.endTimestamp) 666 .append(hours, timeBlock.hours) 667 .append(timeHourDetails, timeBlock.timeHourDetails) 668 .isEquals(); 669 } 670 671 @Override 672 public int hashCode() { 673 return new HashCodeBuilder(17, 31) 674 .append(jobNumber) 675 .append(workArea) 676 .append(task) 677 .append(earnCode) 678 .append(beginTimestamp) 679 .append(endTimestamp) 680 .append(hours) 681 .append(timeHourDetails) 682 .toHashCode(); 683 } 684 685 }