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.util; 017 018 import java.sql.Time; 019 import java.util.ArrayList; 020 import java.util.Collections; 021 import java.util.Comparator; 022 import java.util.List; 023 024 import org.joda.time.DateTime; 025 import org.joda.time.DateTimeZone; 026 import org.joda.time.Interval; 027 import org.joda.time.LocalDateTime; 028 import org.joda.time.LocalTime; 029 import org.kuali.hr.time.calendar.Calendar; 030 import org.kuali.hr.time.calendar.CalendarEntries; 031 import org.kuali.hr.time.flsa.FlsaDay; 032 import org.kuali.hr.time.flsa.FlsaWeek; 033 import org.kuali.hr.time.service.base.TkServiceLocator; 034 import org.kuali.hr.time.timeblock.TimeBlock; 035 036 public class TkTimeBlockAggregate { 037 public List<List<TimeBlock>> dayTimeBlockList = new ArrayList<List<TimeBlock>>(); 038 private CalendarEntries payCalendarEntry; 039 private Calendar payCalendar; 040 041 /** 042 * Defaults to using SYSTEM time zone. 043 * 044 * @param timeBlocks 045 * @param payCalendarEntry 046 */ 047 public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntries payCalendarEntry){ 048 this(timeBlocks, payCalendarEntry, TkServiceLocator.getCalendarService().getCalendar(payCalendarEntry.getHrCalendarId())); 049 } 050 051 /** 052 * Defaults to using SYSTEM time zone. 053 * 054 * @param timeBlocks 055 * @param payCalendarEntry 056 * @param payCalendar 057 */ 058 public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntries payCalendarEntry, Calendar payCalendar) { 059 this(timeBlocks, payCalendarEntry, payCalendar, false); 060 } 061 062 /** 063 * Provides the option to refer to the time zone adjusted time for the current 064 * user. 065 * @param timeBlocks 066 * @param payCalendarEntry 067 * @param payCalendar 068 * @param useUserTimeZone 069 */ 070 public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntries payCalendarEntry, Calendar payCalendar, boolean useUserTimeZone) { 071 this.payCalendarEntry = payCalendarEntry; 072 this.payCalendar = payCalendar; 073 074 List<Interval> dayIntervals = TKUtils.getDaySpanForCalendarEntry(payCalendarEntry); 075 for(Interval dayInt : dayIntervals){ 076 List<TimeBlock> dayTimeBlocks = new ArrayList<TimeBlock>(); 077 for(TimeBlock timeBlock : timeBlocks){ 078 079 // Assumption: Timezones can only be switched at pay period end boundaries. 080 // If the above assumption becomes false, the logic below will need to 081 // accommodate virtual chopping of time blocks to have them fit nicely 082 // in the "days" that are displayed to users. 083 084 DateTime beginTime = useUserTimeZone ? timeBlock.getBeginTimeDisplay() : new DateTime(timeBlock.getBeginTimestamp(), TKUtils.getSystemDateTimeZone()); 085 DateTime endTime = useUserTimeZone ? timeBlock.getEndTimeDisplay() : new DateTime(timeBlock.getEndTimestamp(), TKUtils.getSystemDateTimeZone()); 086 if(dayInt.contains(beginTime)){ 087 if(dayInt.contains(endTime) || endTime.compareTo(dayInt.getEnd()) == 0){ 088 // determine if the time block needs to be pushed forward / backward 089 if(beginTime.getHourOfDay() < dayInt.getStart().getHourOfDay()) { 090 timeBlock.setPushBackward(true); 091 } 092 093 dayTimeBlocks.add(timeBlock); 094 } 095 } 096 } 097 dayTimeBlockList.add(dayTimeBlocks); 098 } 099 } 100 101 public TkTimeBlockAggregate(List<TimeBlock> timeBlocks, CalendarEntries payCalendarEntry, Calendar payCalendar, boolean useUserTimeZone, List<Interval> dayIntervals) { 102 this.payCalendarEntry = payCalendarEntry; 103 this.payCalendar = payCalendar; 104 105 for(Interval dayInt : dayIntervals){ 106 List<TimeBlock> dayTimeBlocks = new ArrayList<TimeBlock>(); 107 for(TimeBlock timeBlock : timeBlocks){ 108 109 // Assumption: Timezones can only be switched at pay period end boundaries. 110 // If the above assumption becomes false, the logic below will need to 111 // accommodate virtual chopping of time blocks to have them fit nicely 112 // in the "days" that are displayed to users. 113 114 DateTime beginTime = useUserTimeZone ? timeBlock.getBeginTimeDisplay() : new DateTime(timeBlock.getBeginTimestamp(), TKUtils.getSystemDateTimeZone()); 115 DateTime endTime = useUserTimeZone ? timeBlock.getEndTimeDisplay() : new DateTime(timeBlock.getEndTimestamp(), TKUtils.getSystemDateTimeZone()); 116 if(dayInt.contains(beginTime)){ 117 if(dayInt.contains(endTime) || endTime.compareTo(dayInt.getEnd()) == 0){ 118 // determine if the time block needs to be pushed forward / backward 119 if(beginTime.getHourOfDay() < dayInt.getStart().getHourOfDay()) { 120 timeBlock.setPushBackward(true); 121 } 122 123 dayTimeBlocks.add(timeBlock); 124 } 125 } 126 } 127 dayTimeBlockList.add(dayTimeBlocks); 128 } 129 130 } 131 132 133 public List<TimeBlock> getFlattenedTimeBlockList(){ 134 List<TimeBlock> lstTimeBlocks = new ArrayList<TimeBlock>(); 135 for(List<TimeBlock> timeBlocks : dayTimeBlockList){ 136 lstTimeBlocks.addAll(timeBlocks); 137 } 138 139 Collections.sort(lstTimeBlocks, new Comparator<TimeBlock>() { // Sort the Time Blocks 140 public int compare(TimeBlock tb1, TimeBlock tb2) { 141 if (tb1 != null && tb2 != null) 142 return tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp()); 143 return 0; 144 } 145 }); 146 147 return lstTimeBlocks; 148 } 149 150 /** 151 * Provides a way to access all of the time blocks for a given week. 152 * 153 * Outer list is 0 indexed list representing days in a week. 154 * Inner List are all of the time blocks for that day. 155 * 156 * Ex. 157 * 158 * List<List<TimeBlock>> week0 = getWeekTimeBlocks(0); 159 * List<TimeBlock> day0 = week0.get(0); 160 * 161 * @param week 162 * @return 163 */ 164 public List<List<TimeBlock>> getWeekTimeBlocks(int week){ 165 int startIndex = week*7; 166 int endIndex = (week*7)+7; 167 endIndex = endIndex > dayTimeBlockList.size() ? dayTimeBlockList.size() : endIndex; 168 169 // Need to sort each day by clock time. 170 List<List<TimeBlock>> wList = dayTimeBlockList.subList(startIndex, endIndex); 171 for (List<TimeBlock> dList : wList) { 172 Collections.sort(dList, new Comparator<TimeBlock>() { // Sort the Time Blocks 173 public int compare(TimeBlock tb1, TimeBlock tb2) { 174 if (tb1 != null && tb2 != null) 175 return tb1.getBeginTimestamp().compareTo(tb2.getBeginTimestamp()); 176 return 0; 177 } 178 }); 179 } 180 181 return wList; 182 } 183 184 /** 185 * When consuming these weeks, you must be aware that you could be on a 186 * virtual day, ie noon to noon schedule and have your FLSA time start 187 * before the virtual day start, 188 * but still have a full 7 days for your week. 189 * 190 * @param zone The TimeZone to use when constructing this relative sorting. 191 */ 192 public List<FlsaWeek> getFlsaWeeks(DateTimeZone zone){ 193 int flsaDayConstant = payCalendar.getFlsaBeginDayConstant(); 194 Time flsaBeginTime = payCalendar.getFlsaBeginTime(); 195 196 // We can use these to build our interval, we have to make sure we 197 // place them on the proper day when we construct it. 198 LocalTime flsaBeginLocalTime = LocalTime.fromDateFields(flsaBeginTime); 199 200 // Defines both the start date and the start virtual time. 201 // We will add 1 day to this to move over all days. 202 // 203 // FLSA time is set. This is an FLSA start date. 204 LocalDateTime startLDT = payCalendarEntry.getBeginLocalDateTime(); 205 // DateTime startDate = new DateTime(payCalendarEntry.getBeginPeriodDateTime()); 206 // startDate = startDate.toLocalDate().toDateTime(flsaBeginLocalTime,TKUtils.getSystemDateTimeZone()); 207 208 List<FlsaWeek> flsaWeeks = new ArrayList<FlsaWeek>(); 209 List<TimeBlock> flatSortedBlockList = getFlattenedTimeBlockList(); 210 FlsaWeek currentWeek = new FlsaWeek(flsaDayConstant, flsaBeginLocalTime, LocalTime.fromDateFields(payCalendarEntry.getBeginPeriodDateTime())); 211 // commented additional week addition, as causing an extra day on UI 212 flsaWeeks.add(currentWeek); 213 214 for (int i = 0; i<dayTimeBlockList.size(); i++) { 215 LocalDateTime currentDate = startLDT.plusDays(i); 216 FlsaDay flsaDay = new FlsaDay(currentDate, flatSortedBlockList, zone); 217 218 if (currentDate.getDayOfWeek() == flsaDayConstant) { 219 currentWeek = new FlsaWeek(flsaDayConstant, flsaBeginLocalTime, flsaBeginLocalTime); 220 flsaWeeks.add(currentWeek); 221 222 currentWeek.addFlsaDay(flsaDay); 223 } else { 224 // add to existing week. 225 currentWeek.addFlsaDay(flsaDay); 226 } 227 } 228 229 return flsaWeeks; 230 } 231 232 /** 233 * @return the total number of weeks that this object represents. 234 */ 235 public int numberOfAggregatedWeeks() { 236 int weeks = 0; 237 238 if (this.dayTimeBlockList.size() > 0) { 239 weeks = this.dayTimeBlockList.size() / 7; 240 if (this.dayTimeBlockList.size() % 7 > 0) 241 weeks++; 242 } 243 244 return weeks; 245 } 246 247 public List<List<TimeBlock>> getDayTimeBlockList() { 248 return dayTimeBlockList; 249 } 250 251 public CalendarEntries getPayCalendarEntry() { 252 return payCalendarEntry; 253 } 254 255 public void setPayCalendarEntry(CalendarEntries payCalendarEntry) { 256 this.payCalendarEntry = payCalendarEntry; 257 } 258 259 public Calendar getPayCalendar() { 260 return payCalendar; 261 } 262 263 public void setPayCalendar(Calendar payCalendar) { 264 this.payCalendar = payCalendar; 265 } 266 267 }