001 /**
002 * Copyright 2004-2013 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.lm.leaveblock.service;
017
018 import java.math.BigDecimal;
019 import java.sql.Timestamp;
020 import java.util.*;
021
022 import org.apache.commons.collections.CollectionUtils;
023 import org.apache.commons.lang.StringUtils;
024 import org.apache.commons.lang.time.DateUtils;
025 import org.apache.log4j.Logger;
026 import org.joda.time.DateTime;
027 import org.joda.time.DateTimeConstants;
028 import org.joda.time.DateTimeZone;
029 import org.joda.time.Interval;
030 import org.kuali.hr.lm.LMConstants;
031 import org.kuali.hr.lm.leaveblock.LeaveBlock;
032 import org.kuali.hr.lm.leaveblock.LeaveBlockHistory;
033 import org.kuali.hr.lm.leaveblock.dao.LeaveBlockDao;
034 import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader;
035 import org.kuali.hr.time.assignment.Assignment;
036 import org.kuali.hr.time.calendar.CalendarEntries;
037 import org.kuali.hr.time.earncode.EarnCode;
038 import org.kuali.hr.time.service.base.TkServiceLocator;
039 import org.kuali.hr.time.util.TKContext;
040 import org.kuali.hr.time.util.TKUtils;
041 import org.kuali.hr.time.util.TkConstants;
042 import org.kuali.hr.time.workflow.TimesheetDocumentHeader;
043 import org.kuali.rice.krad.service.KRADServiceLocator;
044
045 public class LeaveBlockServiceImpl implements LeaveBlockService {
046
047 private static final Logger LOG = Logger.getLogger(LeaveBlockServiceImpl.class);
048
049 private LeaveBlockDao leaveBlockDao;
050
051 @Override
052 public LeaveBlock getLeaveBlock(String leaveBlockId) {
053 return leaveBlockDao.getLeaveBlock(leaveBlockId);
054 }
055
056 public LeaveBlockDao getLeaveBlockDao() {
057 return leaveBlockDao;
058 }
059
060 public void setLeaveBlockDao(LeaveBlockDao leaveBlockDao) {
061 this.leaveBlockDao = leaveBlockDao;
062 }
063
064 public List<LeaveBlock> getLeaveBlocksForDocumentId(String documentId) {
065 return leaveBlockDao.getLeaveBlocksForDocumentId(documentId);
066 }
067
068
069 @Override
070 public List<LeaveBlock> getLeaveBlocks(String principalId, Date beginDate,
071 Date endDate) {
072 return leaveBlockDao.getLeaveBlocks(principalId, beginDate, endDate);
073 }
074
075 @Override
076 public List<LeaveBlock> getLeaveBlocksWithAccrualCategory(String principalId, Date beginDate,
077 Date endDate, String accrualCategory) {
078 return leaveBlockDao.getLeaveBlocksWithAccrualCategory(principalId, beginDate, endDate, accrualCategory);
079 }
080
081 @Override
082 public List<LeaveBlock> getLeaveBlocksWithType(String principalId, Date beginDate,
083 Date endDate, String leaveBlockType) {
084 return leaveBlockDao.getLeaveBlocksWithType(principalId, beginDate, endDate, leaveBlockType);
085 }
086
087 @Override
088 public List<LeaveBlock> getLeaveBlocksSinceCarryOver(String principalId, Map<String, LeaveBlock> carryOver, DateTime endDate, boolean includeAllAccrualCategories) {
089 return leaveBlockDao.getLeaveBlocksSinceCarryOver(principalId, carryOver, endDate, includeAllAccrualCategories);
090 }
091
092 @Override
093 public Map<String, LeaveBlock> getLastCarryOverBlocks(String principalId, Date asOfDate) {
094 if (StringUtils.isEmpty(principalId)) {
095 return Collections.emptyMap();
096 }
097 return leaveBlockDao.getLastCarryOverBlocks(principalId, LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER, asOfDate);
098 }
099
100 @Override
101 public void saveLeaveBlocks(List<LeaveBlock> leaveBlocks) {
102 KRADServiceLocator.getBusinessObjectService().save(leaveBlocks);
103
104 List<LeaveBlockHistory> leaveBlockHistories = new ArrayList<LeaveBlockHistory>();
105 for (LeaveBlock leaveBlock : leaveBlocks) {
106 LeaveBlockHistory lbh = new LeaveBlockHistory(leaveBlock);
107 lbh.setAction(LMConstants.ACTION.ADD);
108 leaveBlockHistories.add(lbh);
109 }
110
111 KRADServiceLocator.getBusinessObjectService().save(leaveBlockHistories);
112 }
113
114 @Override
115 public void deleteLeaveBlock(String leaveBlockId, String principalId) {
116 LeaveBlock leaveBlock = getLeaveBlock(leaveBlockId);
117
118 // leaveBlock.setPrincipalIdModified(TKContext.getTargetPrincipalId());
119 // leaveBlock.setTimestamp(TKUtils.getCurrentTimestamp());
120
121 // Make entry into LeaveBlockHistory table
122 LeaveBlockHistory leaveBlockHistory = new LeaveBlockHistory(leaveBlock);
123 leaveBlockHistory.setPrincipalIdDeleted(principalId);
124 leaveBlockHistory.setTimestampDeleted(new Timestamp(System.currentTimeMillis()));
125 leaveBlockHistory.setAction(LMConstants.ACTION.DELETE);
126
127 // deleting leaveblock
128 KRADServiceLocator.getBusinessObjectService().delete(leaveBlock);
129
130 // creating history
131 KRADServiceLocator.getBusinessObjectService().save(leaveBlockHistory);
132
133
134 }
135
136 @Override
137 public void saveLeaveBlock(LeaveBlock leaveBlock, String principalId) {
138
139 // first delete and create new entry in the database
140 KRADServiceLocator.getBusinessObjectService().delete(leaveBlock);
141
142 // create new
143 leaveBlock.setLmLeaveBlockId(null);
144 leaveBlock.setTimestamp(new Timestamp(System.currentTimeMillis()));
145 leaveBlock.setPrincipalIdModified(principalId);
146 KRADServiceLocator.getBusinessObjectService().save(leaveBlock);
147
148 // save history
149 LeaveBlockHistory lbh = new LeaveBlockHistory(leaveBlock);
150 lbh.setAction(LMConstants.ACTION.MODIFIED);
151 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh);
152
153 }
154
155 @Override
156 public void addLeaveBlocks(DateTime beginDate, DateTime endDate, CalendarEntries ce, String selectedEarnCode,
157 BigDecimal hours, String description, Assignment selectedAssignment, String spanningWeeks, String leaveBlockType, String principalId) {
158 DateTime calBeginDateTime = beginDate;
159 DateTime calEndDateTime = endDate;
160 if(ce != null) {
161 calBeginDateTime = ce.getBeginLocalDateTime().toDateTime();
162 calEndDateTime = ce.getEndLocalDateTime().toDateTime();
163 } else {
164 throw new RuntimeException("Calendar Entry parameter is null.");
165 }
166 Interval calendarInterval = new Interval(calBeginDateTime, calEndDateTime);
167
168 // To create the correct interval by the given begin and end dates,
169 // we need to plus one day on the end date to include that date
170 List<Interval> leaveBlockIntervals = TKUtils.createDaySpan(beginDate.toDateMidnight().toDateTime(), endDate.plusDays(1).toDateMidnight().toDateTime(), TKUtils.getSystemDateTimeZone());
171 // need to use beginDate and endDate of the calendar to find all leaveBlocks since LeaveCalendarDocument Id is not always available
172 List<LeaveBlock> currentLeaveBlocks = getLeaveBlocks(principalId, calBeginDateTime.toDate(), calEndDateTime.toDate());
173
174 // use the current calendar's begin and end date to figure out if this pay period has a leaveDocument
175 LeaveCalendarDocumentHeader lcdh = TkServiceLocator.getLeaveCalendarDocumentHeaderService()
176 .getDocumentHeader(principalId, ce.getBeginLocalDateTime().toDateTime().toDate(), ce.getEndLocalDateTime().toDateTime().toDate());
177 String docId = lcdh == null ? null : lcdh.getDocumentId();
178
179 // TODO: need to integrate with the scheduled timeoff.
180 for (Interval leaveBlockInt : leaveBlockIntervals) {
181 if (calendarInterval.contains(leaveBlockInt)) {
182 // KPME-1446 if "Include weekends" check box is checked, don't add Sat and Sun to the leaveblock list
183 if (StringUtils.isEmpty(spanningWeeks) &&
184 (leaveBlockInt.getStart().getDayOfWeek() == DateTimeConstants.SATURDAY ||leaveBlockInt.getStart().getDayOfWeek() == DateTimeConstants.SUNDAY)) {
185
186 // do nothing
187 } else {
188 // Currently, we store the accrual category value in the leave code table, but store accrual category id in the leaveBlock.
189 // That's why there is a two step server call to get the id. This might be changed in the future.
190
191 java.sql.Date sqlDate = new java.sql.Date(ce.getEndLocalDateTime().toDateTime().toDate().getTime());
192 if ((leaveBlockType.equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR)
193 || leaveBlockType.equals((LMConstants.LEAVE_BLOCK_TYPE.TIME_CALENDAR)))
194 && BigDecimal.ZERO.compareTo(hours) < 0) {
195 hours = hours.negate();
196 }
197
198 CalendarEntries calendarEntry = TkServiceLocator.getCalendarEntriesService().getCurrentCalendarEntriesByCalendarId(ce.getHrCalendarId(), TKUtils.getCurrentDate());
199 Date leaveBlockDate = new DateTime(leaveBlockInt.getStartMillis()).toDate();
200
201 String requestStatus = LMConstants.REQUEST_STATUS.USAGE;
202 if (TkServiceLocator.getLeaveApprovalService().isActiveAssignmentFoundOnJobFlsaStatus(principalId, TkConstants.FLSA_STATUS_NON_EXEMPT, true)) {
203 TimesheetDocumentHeader tdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeaderForDate(principalId, leaveBlockDate);
204 if (tdh != null) {
205 if (DateUtils.isSameDay(leaveBlockDate, tdh.getEndDate()) || leaveBlockDate.after(tdh.getEndDate())) {
206 requestStatus = LMConstants.REQUEST_STATUS.PLANNED;
207 }
208 } else {
209 requestStatus = LMConstants.REQUEST_STATUS.PLANNED;
210 }
211 } else {
212 if (DateUtils.isSameDay(leaveBlockDate, calendarEntry.getEndPeriodDateTime()) || leaveBlockDate.after(calendarEntry.getEndPeriodDateTime())) {
213 requestStatus = LMConstants.REQUEST_STATUS.PLANNED;
214 }
215 }
216
217 EarnCode earnCodeObj = TkServiceLocator.getEarnCodeService().getEarnCode(selectedEarnCode, sqlDate);
218 LeaveBlock leaveBlock = new LeaveBlock.Builder(new DateTime(leaveBlockInt.getStartMillis()), docId, principalId, selectedEarnCode, hours)
219 .description(description)
220 .principalIdModified(principalId)
221 .timestamp(TKUtils.getCurrentTimestamp())
222 .scheduleTimeOffId("0")
223 .accrualCategory(earnCodeObj.getAccrualCategory())
224 .workArea(selectedAssignment.getWorkArea())
225 .jobNumber(selectedAssignment.getJobNumber())
226 .task(selectedAssignment.getTask())
227 .requestStatus(requestStatus)
228 .leaveBlockType(leaveBlockType)
229 .build();
230 if (!currentLeaveBlocks.contains(leaveBlock)) {
231 currentLeaveBlocks.add(leaveBlock);
232 }
233 }
234 }
235 }
236
237 saveLeaveBlocks(currentLeaveBlocks);
238 }
239
240 // KPME-1447
241 @Override
242 public void updateLeaveBlock(LeaveBlock leaveBlock, String principalId) {
243 //verify that if leave block is usage, leave amount is negative
244 if ((LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR.equals(leaveBlock.getLeaveBlockType())
245 || LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR.equals(leaveBlock.getLeaveBlockType()))
246 && BigDecimal.ZERO.compareTo(leaveBlock.getLeaveAmount()) < 0) {
247 leaveBlock.setLeaveAmount(leaveBlock.getLeaveAmount().negate());
248 }
249
250 // Make entry into LeaveBlockHistory table
251 LeaveBlockHistory leaveBlockHistory = new LeaveBlockHistory(leaveBlock);
252 leaveBlockHistory.setPrincipalIdDeleted(principalId);
253 leaveBlockHistory.setTimestampDeleted(new Timestamp(System.currentTimeMillis()));
254 leaveBlockHistory.setAction(LMConstants.ACTION.MODIFIED);
255
256 KRADServiceLocator.getBusinessObjectService().save(leaveBlock);
257
258 // creating history
259 KRADServiceLocator.getBusinessObjectService().save(leaveBlockHistory);
260 }
261
262 public static List<Interval> createDaySpan(DateTime beginDateTime, DateTime endDateTime, DateTimeZone zone) {
263 beginDateTime = beginDateTime.toDateTime(zone);
264 endDateTime = endDateTime.toDateTime(zone);
265 List<Interval> dayIntervals = new ArrayList<Interval>();
266
267 DateTime currDateTime = beginDateTime;
268 while (currDateTime.isBefore(endDateTime)) {
269 DateTime prevDateTime = currDateTime;
270 currDateTime = currDateTime.plusDays(1);
271 Interval daySpan = new Interval(prevDateTime, currDateTime);
272 dayIntervals.add(daySpan);
273 }
274
275 return dayIntervals;
276 }
277
278 @Override
279 public List<LeaveBlock> getLeaveBlocks(String principalId, String leaveBlockType, String requestStatus, Date currentDate) {
280 return leaveBlockDao.getLeaveBlocks(principalId, leaveBlockType, requestStatus, currentDate);
281 }
282
283 @Override
284 public List<LeaveBlock> getLeaveBlocksForDate(String principalId, Date leaveDate) {
285 return leaveBlockDao.getLeaveBlocksForDate(principalId, leaveDate);
286 }
287
288 @Override
289 public List<LeaveBlock> getNotAccrualGeneratedLeaveBlocksForDate(String principalId, Date leaveDate) {
290 return leaveBlockDao.getNotAccrualGeneratedLeaveBlocksForDate(principalId, leaveDate);
291 }
292
293 public List<LeaveBlock> getLeaveBlocksForTimeCalendar(String principalId, Date beginDate, Date endDate, List<String> assignmentKeys) {
294 List<LeaveBlock> col = leaveBlockDao.getCalendarLeaveBlocks(principalId, beginDate, endDate);
295 List<LeaveBlock> leaveBlocks = filterLeaveBlocksForTimeCalendar(col, assignmentKeys);
296 return leaveBlocks;
297 }
298
299 public List<LeaveBlock> getLeaveBlocksForLeaveCalendar(String principalId, Date beginDate, Date endDate, List<String> assignmentKeys) {
300 List<LeaveBlock> col = leaveBlockDao.getLeaveBlocks(principalId, beginDate, endDate);
301 List<LeaveBlock> leaveBlocks = filterLeaveBlocksForLeaveCalendar(col, assignmentKeys);
302 return leaveBlocks;
303 }
304
305 public List<LeaveBlock> filterLeaveBlocksForTimeCalendar(List<LeaveBlock> lbs, List<String> assignmentKeys) {
306 if(CollectionUtils.isEmpty(assignmentKeys)) {
307 return lbs;
308 }
309 List<LeaveBlock> results = new ArrayList<LeaveBlock> ();
310 for(LeaveBlock lb : lbs) {
311 if(lb != null) {
312 if (StringUtils.equals(lb.getLeaveBlockType(), LMConstants.LEAVE_BLOCK_TYPE.ACCRUAL_SERVICE)
313 && StringUtils.isNotEmpty(lb.getScheduleTimeOffId())
314 && lb.getLeaveAmount().compareTo(BigDecimal.ZERO) < 0) {
315 // display usage leave blocks generated by system scheduled time off
316 results.add(lb);
317 } else if(StringUtils.isNotEmpty(lb.getAssignmentKey()) && assignmentKeys.contains(lb.getAssignmentKey())) {
318 if (StringUtils.equals(lb.getLeaveBlockType(), LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR)) {
319 // only add approved leave blocks that are created from leave calendar
320 if (StringUtils.equals(lb.getRequestStatus(), LMConstants.REQUEST_STATUS.APPROVED)) {
321 results.add(lb);
322 }
323 } else if(StringUtils.equals(lb.getLeaveBlockType(), LMConstants.LEAVE_BLOCK_TYPE.TIME_CALENDAR)) {
324 results.add(lb);
325 }
326 }
327 }
328 }
329 return results;
330 }
331
332 public List<LeaveBlock> filterLeaveBlocksForLeaveCalendar(List<LeaveBlock> lbs, List<String> assignmentKeys) {
333 if(CollectionUtils.isEmpty(assignmentKeys)) {
334 return lbs;
335 }
336 List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>();
337 for(LeaveBlock lb : lbs) {
338 if(lb != null) {
339 if(lb.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.TIME_CALENDAR)) {
340 if(StringUtils.isNotEmpty(lb.getAssignmentKey()) && assignmentKeys.contains(lb.getAssignmentKey())) {
341 leaveBlocks.add(lb);
342 }
343 } else {
344 leaveBlocks.add(lb);
345 }
346 }
347 }
348 return leaveBlocks;
349 }
350
351 @Override
352 public void deleteLeaveBlocksForDocumentId(String documentId){
353 leaveBlockDao.deleteLeaveBlocksForDocumentId(documentId);
354 }
355
356
357 @Override
358 public List<LeaveBlock> getAccrualGeneratedLeaveBlocks(String principalId, Date beginDate, Date endDate) {
359 return leaveBlockDao.getAccrualGeneratedLeaveBlocks(principalId, beginDate, endDate);
360 }
361
362 @Override
363 public List<LeaveBlock> getSSTOLeaveBlocks(String principalId, String sstoId, Date accruledDate) {
364 return leaveBlockDao.getSSTOLeaveBlocks(principalId, sstoId, accruledDate);
365 }
366
367 @Override
368 public List<LeaveBlock> getABELeaveBlocksSinceTime(String principalId, Timestamp lastRanTime) {
369 return leaveBlockDao.getABELeaveBlocksSinceTime(principalId, lastRanTime);
370 }
371 }