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 }