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 }