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.leaveSummary.service; 017 018 import org.apache.commons.collections.CollectionUtils; 019 import org.apache.commons.lang.StringUtils; 020 import org.apache.commons.lang.time.DateUtils; 021 import org.joda.time.DateMidnight; 022 import org.joda.time.DateTime; 023 import org.joda.time.Interval; 024 import org.joda.time.LocalDateTime; 025 import org.kuali.hr.lm.LMConstants; 026 import org.kuali.hr.lm.accrual.AccrualCategory; 027 import org.kuali.hr.lm.accrual.AccrualCategoryRule; 028 import org.kuali.hr.lm.employeeoverride.EmployeeOverride; 029 import org.kuali.hr.lm.leaveSummary.LeaveSummary; 030 import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow; 031 import org.kuali.hr.lm.leaveblock.LeaveBlock; 032 import org.kuali.hr.lm.leaveblock.service.LeaveBlockService; 033 import org.kuali.hr.lm.leaveplan.LeavePlan; 034 import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader; 035 import org.kuali.hr.time.calendar.CalendarEntries; 036 import org.kuali.hr.time.earncode.EarnCode; 037 import org.kuali.hr.time.principal.PrincipalHRAttributes; 038 import org.kuali.hr.time.service.base.TkServiceLocator; 039 import org.kuali.hr.time.util.TKUtils; 040 import org.kuali.hr.time.util.TkConstants; 041 import org.kuali.rice.krad.util.ObjectUtils; 042 043 import java.math.BigDecimal; 044 import java.sql.Timestamp; 045 import java.text.DateFormat; 046 import java.text.ParseException; 047 import java.text.SimpleDateFormat; 048 import java.util.*; 049 050 public class LeaveSummaryServiceImpl implements LeaveSummaryService { 051 private LeaveBlockService leaveBlockService; 052 053 @Override 054 public LeaveSummary getLeaveSummaryAsOfDate(String principalId, java.sql.Date asOfDate) { 055 return getLeaveSummary(principalId, asOfDate, asOfDate, null, true); 056 } 057 058 public LeaveSummary getLeaveSummaryAsOfDateWithoutFuture(String principalId, java.sql.Date asOfDate) { 059 return getLeaveSummary(principalId, asOfDate, asOfDate, null, false); 060 } 061 062 @Override 063 public LeaveSummary getLeaveSummary(String principalId, CalendarEntries calendarEntry) { 064 return getLeaveSummary(principalId, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate(), null, true); 065 } 066 067 @Override 068 public LeaveSummary getLeaveSummaryAsOfDateForAccrualCategory(String principalId, java.sql.Date asOfDate, String accrualCategory) { 069 return getLeaveSummary(principalId, asOfDate, asOfDate, accrualCategory, true); 070 } 071 072 @Override 073 // startDate is the leave request start date, endDat is leave request end date, usageEndDate is the date before next accrual interval date for leave requst end date 074 // will get leave balance up to the next earn interval for a certain date, including usage up to that next earn interval 075 public BigDecimal getLeaveBalanceForAccrCatUpToDate(String principalId, 076 java.sql.Date startDate, 077 java.sql.Date endDate, 078 String accrualCategory, 079 Date usageEndDate) { 080 BigDecimal leaveBalance = BigDecimal.ZERO; 081 if(StringUtils.isEmpty(principalId) || startDate == null || endDate == null || StringUtils.isEmpty(accrualCategory) || usageEndDate == null) { 082 return leaveBalance; 083 } 084 085 LeaveSummaryRow lsr = new LeaveSummaryRow(); 086 AccrualCategory ac = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualCategory, endDate); 087 088 if(ac != null) { 089 LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(ac.getLeavePlan(), ac.getEffectiveDate()); 090 if(lp == null) { 091 return leaveBalance; 092 } 093 PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate); 094 //until we have something that creates carry over, we need to grab everything. 095 // Calculating leave bLocks from Calendar Year start instead of Service Date 096 Map<String, LeaveBlock> carryOverBlocks = getLeaveBlockService().getLastCarryOverBlocks(principalId, startDate); 097 //remove unwanted carry over blocks from map 098 LeaveBlock carryOverBlock = carryOverBlocks.get(accrualCategory); 099 carryOverBlocks = new HashMap<String, LeaveBlock>(1); 100 if(ObjectUtils.isNotNull(carryOverBlock)) 101 carryOverBlocks.put(carryOverBlock.getAccrualCategory(), carryOverBlock); 102 103 List<LeaveBlock> leaveBlocks = getLeaveBlockService().getLeaveBlocksSinceCarryOver(principalId, carryOverBlocks, (new LocalDateTime(endDate)).toDateTime(), true); 104 List<LeaveBlock> acLeaveBlocks = new ArrayList<LeaveBlock>(); 105 for(LeaveBlock lb : leaveBlocks) { 106 if(StringUtils.equals(lb.getAccrualCategory(), accrualCategory)) { 107 acLeaveBlocks.add(lb); 108 } 109 } 110 // get all leave blocks from the requested date to the usageEndDate 111 List<LeaveBlock> futureLeaveBlocks = getLeaveBlockService().getLeaveBlocksWithAccrualCategory(principalId, endDate, usageEndDate, accrualCategory); 112 EmployeeOverride maxUsageOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), accrualCategory, "MU", new java.sql.Date(usageEndDate.getTime())); 113 114 //get max balances 115 AccrualCategoryRule acRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(ac, TKUtils.getCurrentDate(), pha.getServiceDate()); 116 //accrual category rule id set on a leave summary row will be useful in generating a relevant balance transfer 117 //document from the leave calendar display. Could put this id in the request for balance transfer document. 118 lsr.setAccrualCategoryRuleId(acRule == null ? null : acRule.getLmAccrualCategoryRuleId()); 119 if(acRule != null && 120 (acRule.getMaxBalance()!= null 121 || acRule.getMaxUsage() != null)) { 122 if (acRule.getMaxUsage() != null) { 123 lsr.setUsageLimit(new BigDecimal(acRule.getMaxUsage()).setScale(2)); 124 } else { 125 lsr.setUsageLimit(null); 126 } 127 } else { 128 lsr.setUsageLimit(null); 129 } 130 131 if(maxUsageOverride !=null) 132 lsr.setUsageLimit(new BigDecimal(maxUsageOverride.getOverrideValue())); 133 134 //Fetching leaveblocks for accCat with type CarryOver -- This is logic according to the CO blocks creatLed from scheduler job. 135 BigDecimal carryOver = BigDecimal.ZERO.setScale(2); 136 lsr.setCarryOver(carryOver); 137 138 assignApprovedValuesToRow(lsr, ac.getAccrualCategory(), acLeaveBlocks, lp, startDate, endDate); 139 140 //merge key sets 141 if (carryOverBlocks.containsKey(lsr.getAccrualCategory())) { 142 carryOver = carryOverBlocks.get(lsr.getAccrualCategory()).getLeaveAmount(); 143 } 144 Set<String> keyset = new HashSet<String>(); 145 keyset.addAll(lsr.getPriorYearsUsage().keySet()); 146 keyset.addAll(lsr.getPriorYearsTotalAccrued().keySet()); 147 for (String key : keyset) { 148 BigDecimal value = lsr.getPriorYearsTotalAccrued().get(key); 149 if (value == null) { 150 value = BigDecimal.ZERO; 151 } 152 carryOver = carryOver.add(value); 153 BigDecimal use = lsr.getPriorYearsUsage().containsKey(key) ? lsr.getPriorYearsUsage().get(key) : BigDecimal.ZERO; 154 carryOver = carryOver.add(use); 155 if (acRule != null && acRule.getMaxCarryOver() != null && acRule.getMaxCarryOver() < carryOver.longValue()) { 156 carryOver = new BigDecimal(acRule.getMaxCarryOver()); 157 } 158 } 159 160 lsr.setCarryOver(carryOver); 161 //handle future leave blocks 162 assignPendingValuesToRow(lsr, ac.getAccrualCategory(), futureLeaveBlocks); 163 //compute Leave Balance 164 leaveBalance = lsr.getAccruedBalance().subtract(lsr.getPendingLeaveRequests()); 165 if(lsr.getUsageLimit()!=null) { //should not set leave balance to usage limit simply because it's not null. 166 BigDecimal availableUsage = lsr.getUsageLimit().subtract(lsr.getYtdApprovedUsage().add(lsr.getPendingLeaveRequests())); 167 if(leaveBalance.compareTo( availableUsage ) > 0) 168 lsr.setLeaveBalance(availableUsage); 169 else 170 lsr.setLeaveBalance(leaveBalance); 171 } else { //no usage limit 172 lsr.setLeaveBalance(leaveBalance); 173 } 174 } 175 leaveBalance = lsr.getLeaveBalance(); 176 return leaveBalance; 177 } 178 179 protected LeaveSummary getLeaveSummary(String principalId, 180 java.sql.Date startDate, 181 java.sql.Date endDate, 182 String accrualCategory, 183 boolean includeFuture) { 184 LeaveSummary ls = new LeaveSummary(); 185 List<LeaveSummaryRow> rows = new ArrayList<LeaveSummaryRow>(); 186 187 if(StringUtils.isEmpty(principalId) || startDate == null || endDate == null) { 188 return ls; 189 } 190 191 Set<String> leavePlans = getLeavePlans(principalId, startDate, endDate); 192 PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate); 193 if (CollectionUtils.isNotEmpty(leavePlans)) { 194 for(String aLpString : leavePlans) { 195 LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(aLpString, startDate); 196 if(lp == null) { 197 continue; 198 } 199 DateFormat formatter = new SimpleDateFormat("MMMM d"); 200 DateFormat formatter2 = new SimpleDateFormat("MMMM d yyyy"); 201 DateTime entryEndDate = new LocalDateTime(endDate).toDateTime(); 202 if (entryEndDate.getHourOfDay() == 0) { 203 entryEndDate = entryEndDate.minusDays(1); 204 } 205 String aString = formatter.format(startDate) + " - " + formatter2.format(entryEndDate.toDate()); 206 ls.setPendingDatesString(aString); 207 208 LeaveCalendarDocumentHeader approvedLcdh = TkServiceLocator.getLeaveCalendarDocumentHeaderService().getMaxEndDateApprovedLeaveCalendar(principalId); 209 if(approvedLcdh != null) { 210 Date endApprovedDate = new java.sql.Date(approvedLcdh.getEndDate().getTime()); 211 LocalDateTime aLocalTime = new DateTime(approvedLcdh.getEndDate()).toLocalDateTime(); 212 DateTime endApprovedTime = aLocalTime.toDateTime(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback()); 213 if(endApprovedTime.getHourOfDay() == 0) { 214 endApprovedDate = TKUtils.addDates(endApprovedDate, -1); 215 } 216 String datesString = formatter.format(approvedLcdh.getBeginDate()) + " - " + formatter2.format(endApprovedDate); 217 ls.setYtdDatesString(datesString); 218 } 219 220 //until we have something that creates carry over, we need to grab everything. 221 // Calculating leave bLocks from Calendar Year start instead of Service Date 222 Map<String, LeaveBlock> carryOverBlocks = getLeaveBlockService().getLastCarryOverBlocks(principalId, startDate); 223 224 boolean filterByAccrualCategory = false; 225 if (StringUtils.isNotEmpty(accrualCategory)) { 226 filterByAccrualCategory = true; 227 //remove unwanted carry over blocks from map 228 LeaveBlock carryOverBlock = carryOverBlocks.get(accrualCategory); 229 carryOverBlocks = new HashMap<String, LeaveBlock>(1); 230 if(ObjectUtils.isNotNull(carryOverBlock)) 231 carryOverBlocks.put(carryOverBlock.getAccrualCategory(), carryOverBlock); 232 } 233 List<LeaveBlock> leaveBlocks = getLeaveBlockService().getLeaveBlocksSinceCarryOver(principalId, carryOverBlocks, (new LocalDateTime(endDate)).toDateTime(), filterByAccrualCategory); 234 235 List<LeaveBlock> futureLeaveBlocks = new ArrayList<LeaveBlock>(); 236 if (includeFuture) { 237 if (!filterByAccrualCategory) { 238 futureLeaveBlocks = getLeaveBlockService().getLeaveBlocks(principalId, endDate, (new LocalDateTime(endDate)).toDateTime().plusMonths(Integer.parseInt(lp.getPlanningMonths())).toDate()); 239 } else { 240 futureLeaveBlocks = getLeaveBlockService().getLeaveBlocksWithAccrualCategory(principalId, endDate, (new LocalDateTime(endDate)).toDateTime().plusMonths(Integer.parseInt(lp.getPlanningMonths())).toDate(), accrualCategory); 241 } 242 } 243 Map<String, List<LeaveBlock>> leaveBlockMap = mapLeaveBlocksByAccrualCategory(leaveBlocks); 244 Map<String, List<LeaveBlock>> futureLeaveBlockMap = mapLeaveBlocksByAccrualCategory(futureLeaveBlocks); 245 List<AccrualCategory> acList = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(lp.getLeavePlan(), endDate); 246 if(CollectionUtils.isNotEmpty(acList)) { 247 for(AccrualCategory ac : acList) { 248 if(ac.getShowOnGrid().equals("Y")) { 249 250 LeaveSummaryRow lsr = new LeaveSummaryRow(); 251 lsr.setAccrualCategory(ac.getAccrualCategory()); 252 lsr.setAccrualCategoryId(ac.getLmAccrualCategoryId()); 253 //get max balances 254 AccrualCategoryRule acRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(ac, endDate, pha.getServiceDate()); 255 //accrual category rule id set on a leave summary row will be useful in generating a relevant balance transfer 256 //document from the leave calendar display. Could put this id in the request for balance transfer document. 257 EmployeeOverride maxUsageOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), ac.getAccrualCategory(), "MU", endDate); 258 259 lsr.setAccrualCategoryRuleId(acRule == null ? null : acRule.getLmAccrualCategoryRuleId()); 260 if(acRule != null && 261 (acRule.getMaxBalance()!= null 262 || acRule.getMaxUsage() != null)) { 263 if (acRule.getMaxUsage() != null) { 264 lsr.setUsageLimit(new BigDecimal(acRule.getMaxUsage()).setScale(2)); 265 } else { 266 lsr.setUsageLimit(null); 267 } 268 269 } else { 270 lsr.setUsageLimit(null); 271 } 272 273 if(maxUsageOverride !=null) 274 lsr.setUsageLimit(new BigDecimal(maxUsageOverride.getOverrideValue())); 275 276 //Fetching leaveblocks for accCat with type CarryOver -- This is logic according to the CO blocks created from scheduler job. 277 BigDecimal carryOver = BigDecimal.ZERO.setScale(2); 278 lsr.setCarryOver(carryOver); 279 280 //handle up to current leave blocks 281 //CalendarEntry.getEndPeriodDate passed to fetch leave block amounts on last day of Calendar period 282 assignApprovedValuesToRow(lsr, ac.getAccrualCategory(), leaveBlockMap.get(ac.getAccrualCategory()), lp, startDate, endDate); 283 284 //how about the leave blocks on the calendar entry being currently handled?? 285 /* if(carryOverBlocks.containsKey(lsr.getAccrualCategory())) { 286 LeaveBlock carryOverBlock = carryOverBlocks.get(lsr.getAccrualCategory()); 287 DateTime carryOverBlockLPStart = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), carryOverBlock.getLeaveDate()); 288 DateTime currentLPStart = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), TKUtils.getCurrentDate()); 289 if(carryOverBlockLPStart.equals(currentLPStart)) 290 carryOver = carryOverBlock.getLeaveAmount(); 291 }*/ 292 //figure out past carry over values!!! 293 //We now have list of past years accrual and use (with ordered keys!!!) 294 295 //merge key sets 296 if (carryOverBlocks.containsKey(lsr.getAccrualCategory())) { 297 carryOver = carryOverBlocks.get(lsr.getAccrualCategory()).getLeaveAmount(); 298 carryOver = carryOver.setScale(2); 299 } 300 Set<String> keyset = new HashSet<String>(); 301 keyset.addAll(lsr.getPriorYearsUsage().keySet()); 302 keyset.addAll(lsr.getPriorYearsTotalAccrued().keySet()); 303 for (String key : keyset) { 304 BigDecimal value = lsr.getPriorYearsTotalAccrued().get(key); 305 if (value == null) { 306 value = BigDecimal.ZERO; 307 } 308 carryOver = carryOver.add(value); 309 BigDecimal use = lsr.getPriorYearsUsage().containsKey(key) ? lsr.getPriorYearsUsage().get(key) : BigDecimal.ZERO; 310 carryOver = carryOver.add(use); 311 EmployeeOverride carryOverOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), ac.getAccrualCategory(), "MAC", endDate); 312 if (acRule != null && acRule.getMaxCarryOver() != null) { 313 BigDecimal carryOverDisplay = BigDecimal.ZERO; 314 if(carryOverOverride != null) 315 carryOverDisplay = new BigDecimal(carryOverOverride.getOverrideValue() < carryOver.longValue() ? carryOverOverride.getOverrideValue() : carryOver.longValue()); 316 else 317 carryOverDisplay = new BigDecimal(acRule.getMaxCarryOver() < carryOver.longValue() ? acRule.getMaxCarryOver() : carryOver.longValue()); 318 carryOver = carryOverDisplay; 319 } 320 } 321 322 lsr.setCarryOver(carryOver); 323 if (acRule != null && acRule.getMaxCarryOver() != null) { 324 EmployeeOverride carryOverOverride = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverride(principalId, lp.getLeavePlan(), ac.getAccrualCategory(), "MAC", endDate); 325 if(carryOverOverride != null) 326 lsr.setMaxCarryOver(new BigDecimal(carryOverOverride.getOverrideValue())); 327 else 328 lsr.setMaxCarryOver(new BigDecimal(acRule.getMaxCarryOver() < carryOver.longValue() ? acRule.getMaxCarryOver() : carryOver.longValue())); 329 } 330 331 //handle future leave blocks 332 assignPendingValuesToRow(lsr, ac.getAccrualCategory(), futureLeaveBlockMap.get(ac.getAccrualCategory())); 333 334 //compute Leave Balance 335 BigDecimal leaveBalance = lsr.getAccruedBalance().subtract(lsr.getPendingLeaveRequests()); 336 //if leave balance is set 337 //Employee overrides have already been taken into consideration and the appropriate values 338 //for usage have been set by this point. 339 // if (acRule != null && StringUtils.equals(acRule.getMaxBalFlag(), "Y")) { 340 //there exists an accrual category rule with max balance limit imposed. 341 //max bal flag = 'Y' has no precedence here with max-bal / balance transfers implemented. 342 //unless institutions are not required to define a max balance limit for action_at_max_bal = LOSE. 343 //Possibly preferable to procure forfeiture blocks through balance transfer 344 if(lsr.getUsageLimit()!=null) { //should not set leave balance to usage limit simply because it's not null. 345 BigDecimal availableUsage = lsr.getUsageLimit().subtract(lsr.getYtdApprovedUsage().add(lsr.getPendingLeaveRequests())); 346 if(leaveBalance.compareTo( availableUsage ) > 0) 347 lsr.setLeaveBalance(availableUsage); 348 else 349 lsr.setLeaveBalance(leaveBalance); 350 } else { //no usage limit 351 lsr.setLeaveBalance(leaveBalance); 352 } 353 354 rows.add(lsr); 355 } 356 } 357 // let's check for 'empty' accrual categories 358 if (leaveBlockMap.containsKey(null) 359 || futureLeaveBlockMap.containsKey(null)) { 360 LeaveSummaryRow otherLeaveSummary = new LeaveSummaryRow(); 361 //otherLeaveSummary.setAccrualCategory("Other"); 362 363 assignApprovedValuesToRow(otherLeaveSummary, null, leaveBlockMap.get(null), lp, startDate, endDate); 364 BigDecimal carryOver = BigDecimal.ZERO.setScale(2); 365 for (Map.Entry<String, BigDecimal> entry : otherLeaveSummary.getPriorYearsTotalAccrued().entrySet()) { 366 carryOver = carryOver.add(entry.getValue()); 367 BigDecimal use = otherLeaveSummary.getPriorYearsUsage().containsKey(entry.getKey()) ? otherLeaveSummary.getPriorYearsUsage().get(entry.getKey()) : BigDecimal.ZERO; 368 carryOver = carryOver.add(use); 369 } 370 otherLeaveSummary.setCarryOver(carryOver); 371 assignPendingValuesToRow(otherLeaveSummary, null, futureLeaveBlockMap.get(null)); 372 otherLeaveSummary.setAccrualCategory("Other"); 373 374 //compute Leave Balance 375 // blank the avail 376 otherLeaveSummary.setUsageLimit(null); 377 otherLeaveSummary.setLeaveBalance(null); 378 379 rows.add(otherLeaveSummary); 380 } 381 } 382 } 383 } 384 ls.setLeaveSummaryRows(rows); 385 return ls; 386 } 387 388 private PrincipalHRAttributes getPrincipalHrAttributes(String principalId, Date startDate, Date endDate) { 389 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, startDate); 390 if(pha == null) { // principal hr attributes does not exist at the beginning of this calendar entry 391 pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, endDate); 392 } 393 return pha; 394 } 395 396 private Set<String> getLeavePlans(String principalId, Date startDate, Date endDate) { 397 Set<String> lpStrings = new HashSet<String>(); // 398 399 PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate); 400 // get list of principalHrAttributes that become active during this pay period 401 List<PrincipalHRAttributes> phaList = TkServiceLocator.getPrincipalHRAttributeService() 402 .getActivePrincipalHrAttributesForRange(principalId, startDate, endDate); 403 if(pha != null) { 404 lpStrings.add(pha.getLeavePlan()); 405 } 406 if(CollectionUtils.isNotEmpty(phaList)) { 407 for(PrincipalHRAttributes aPha : phaList) { 408 lpStrings.add(aPha.getLeavePlan()); 409 } 410 } 411 412 return lpStrings; 413 } 414 415 416 private Map<String, List<LeaveBlock>> mapLeaveBlocksByAccrualCategory(List<LeaveBlock> leaveBlocks) { 417 Map<String, List<LeaveBlock>> map = new HashMap<String, List<LeaveBlock>>(); 418 for (LeaveBlock lb : leaveBlocks) { 419 if (map.containsKey(lb.getAccrualCategory())) { 420 map.get(lb.getAccrualCategory()).add(lb); 421 } else { 422 List<LeaveBlock> splitLeaveBlocks = new ArrayList<LeaveBlock>(); 423 splitLeaveBlocks.add(lb); 424 map.put(lb.getAccrualCategory(), splitLeaveBlocks); 425 } 426 } 427 return map; 428 } 429 430 /** 431 * Use with button display for balance transfers available On-Demand. 432 * @param lsr 433 * @param accrualCategoryRule 434 * @param principalId 435 */ 436 private void markTransferable(LeaveSummaryRow lsr, AccrualCategoryRule accrualCategoryRule, String principalId) { 437 //return type must be changed to boolean, or an associated field element must be created for decision 438 //purposes. 439 //an accrual category's balance is transferable if the accrued balance is 440 //greater than the maximum balance allowed for the accrual category. action_at_max_balance must be TRANSFER 441 boolean transferable = false; 442 if(ObjectUtils.isNotNull(accrualCategoryRule)) { 443 if(ObjectUtils.isNotNull(accrualCategoryRule.getMaxBalance())) { 444 BigDecimal maxBalance = accrualCategoryRule.getMaxBalance(); 445 BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate()); 446 BigDecimal adjustedMaxBalance = maxBalance.multiply(fte); 447 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate()); 448 for(EmployeeOverride override : overrides) { 449 if(StringUtils.equals(override.getOverrideType(),TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MB")) 450 && override.isActive()) { 451 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 452 break; 453 } 454 } 455 if(adjustedMaxBalance.compareTo(lsr.getAccruedBalance()) < 0) { 456 if(StringUtils.equals(accrualCategoryRule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.TRANSFER) && 457 StringUtils.equals(accrualCategoryRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND)) 458 transferable = true; 459 } 460 } 461 } 462 lsr.setTransferable(transferable); 463 } 464 465 /** 466 * Use with button display for balance transfer payouts available On-Demand. 467 * @param lsr 468 * @param accrualCategoryRule 469 * @param principalId 470 */ 471 private void markPayoutable(LeaveSummaryRow lsr, AccrualCategoryRule accrualCategoryRule, String principalId) { 472 //return type must be changed to boolean, or an associated field element must be created for decision 473 //purposes. 474 //an accrual category's balance is transferable if max_bal_action_frequency is ON-DEMAND 475 //and action_at_max_balance is PAYOUT 476 boolean payoutable = false; 477 if(ObjectUtils.isNotNull(accrualCategoryRule)) { 478 if(ObjectUtils.isNotNull(accrualCategoryRule.getMaxBalance())) { 479 BigDecimal maxBalance = accrualCategoryRule.getMaxBalance(); 480 BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate()); 481 BigDecimal adjustedMaxBalance = maxBalance.multiply(fte); 482 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate()); 483 for(EmployeeOverride override : overrides) { 484 if(StringUtils.equals(override.getOverrideType(),TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MB")) 485 && override.isActive()) { 486 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 487 break; 488 } 489 } 490 if(adjustedMaxBalance.compareTo(lsr.getAccruedBalance()) < 0) { 491 if(StringUtils.equals(accrualCategoryRule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.PAYOUT) && 492 StringUtils.equals(accrualCategoryRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND)) 493 payoutable = true; 494 } 495 } 496 } 497 lsr.setPayoutable(payoutable); 498 } 499 500 private void assignApprovedValuesToRow(LeaveSummaryRow lsr, String accrualCategory, List<LeaveBlock> approvedLeaveBlocks, LeavePlan lp, Date ytdEarnedEffectiveDate, Date effectiveDate) { 501 502 SortedMap<String, BigDecimal> yearlyAccrued = new TreeMap<String, BigDecimal>(); 503 SortedMap<String, BigDecimal> yearlyUsage = new TreeMap<String, BigDecimal>(); 504 BigDecimal accrualedBalance = BigDecimal.ZERO.setScale(2); 505 BigDecimal approvedUsage = BigDecimal.ZERO.setScale(2); 506 BigDecimal fmlaUsage = BigDecimal.ZERO.setScale(2); 507 508 Date cutOffDateToCheck = ytdEarnedEffectiveDate != null ? ytdEarnedEffectiveDate : effectiveDate; 509 DateTime cutOffDate = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), cutOffDateToCheck).minus(1); 510 511 Timestamp priorYearCutOff = new Timestamp(cutOffDate.getMillis()); 512 513 if (CollectionUtils.isNotEmpty(approvedLeaveBlocks)) { 514 // create it here so we don't need to get instance every loop iteration 515 for(LeaveBlock aLeaveBlock : approvedLeaveBlocks) { 516 // check if leave date is before the next calendar start. 517 if(!aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER) 518 && aLeaveBlock.getLeaveDate().getTime() < effectiveDate.getTime()) { 519 if((StringUtils.isBlank(accrualCategory) && StringUtils.isBlank(aLeaveBlock.getAccrualCategory())) 520 || (StringUtils.isNotBlank(aLeaveBlock.getAccrualCategory()) 521 && StringUtils.equals(aLeaveBlock.getAccrualCategory(), accrualCategory))) { 522 if(aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) >= 0 523 && !aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR)) { 524 if(!(StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, aLeaveBlock.getRequestStatus()) || 525 StringUtils.equals(LMConstants.REQUEST_STATUS.DEFERRED, aLeaveBlock.getRequestStatus()))) { 526 if (aLeaveBlock.getLeaveDate().getTime() <= priorYearCutOff.getTime()) { 527 String yearKey = getYearKey(aLeaveBlock.getLeaveDate(), lp); 528 BigDecimal co = yearlyAccrued.get(yearKey); 529 if (co == null) { 530 co = BigDecimal.ZERO.setScale(2); 531 } 532 co = co.add(aLeaveBlock.getLeaveAmount()); 533 yearlyAccrued.put(yearKey, co); 534 } else if(aLeaveBlock.getLeaveDate().getTime() < ytdEarnedEffectiveDate.getTime()) { 535 accrualedBalance = accrualedBalance.add(aLeaveBlock.getLeaveAmount()); 536 } 537 } 538 } else { 539 BigDecimal currentLeaveAmount = aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) > 0 ? aLeaveBlock.getLeaveAmount().negate() : aLeaveBlock.getLeaveAmount(); 540 //we only want this for the current calendar!!! 541 if(!(StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, aLeaveBlock.getRequestStatus()) || 542 StringUtils.equals(LMConstants.REQUEST_STATUS.DEFERRED, aLeaveBlock.getRequestStatus()))) { 543 544 EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(aLeaveBlock.getEarnCode(), aLeaveBlock.getLeaveDate()); 545 546 if ((ec != null && StringUtils.equals(ec.getAccrualBalanceAction(), LMConstants.ACCRUAL_BALANCE_ACTION.USAGE)) 547 || aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER) 548 || aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT) 549 || aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_ADJUSTMENT_MAINT) 550 || aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.DONATION_MAINT)){ 551 if (aLeaveBlock.getLeaveDate().getTime() > priorYearCutOff.getTime()) { 552 553 approvedUsage = approvedUsage.add(currentLeaveAmount); 554 555 if(ec != null && ec.getFmla().equals("Y")) { 556 fmlaUsage = fmlaUsage.add(aLeaveBlock.getLeaveAmount()); 557 } 558 } else { 559 //these usages are for previous years, to help figure out correct carry over values 560 String yearKey = getYearKey(aLeaveBlock.getLeaveDate(), lp); 561 BigDecimal use = yearlyUsage.get(yearKey); 562 if (use == null) { 563 use = BigDecimal.ZERO.setScale(2); 564 } 565 use = use.add(currentLeaveAmount); 566 yearlyUsage.put(yearKey, use); 567 } 568 } 569 } 570 } 571 572 //} 573 } 574 } else { 575 //we can actually use the carry over block!! 576 577 } 578 } 579 } 580 581 lsr.setPriorYearsTotalAccrued(yearlyAccrued); 582 lsr.setPriorYearsUsage(yearlyUsage); 583 lsr.setYtdAccruedBalance(accrualedBalance); 584 lsr.setYtdApprovedUsage(approvedUsage.negate()); 585 lsr.setFmlaUsage(fmlaUsage.negate()); 586 587 //lsr.setLeaveBalance(lsr.getYtdAccruedBalance().add(approvedUsage)); 588 } 589 590 private String getYearKey(Date leaveDate, LeavePlan lp){ 591 Calendar cal = Calendar.getInstance(); 592 Calendar leavePlanCal = Calendar.getInstance(); 593 cal.setTime(leaveDate); 594 String yearKey = Integer.toString(cal.get(Calendar.YEAR)); 595 leavePlanCal.set(Calendar.MONTH, Integer.parseInt(lp.getCalendarYearStartMonth()) - 1); 596 leavePlanCal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(lp.getCalendarYearStartDayOfMonth())); 597 leavePlanCal.set(Calendar.YEAR, cal.get(Calendar.YEAR)); 598 leavePlanCal.set(Calendar.HOUR_OF_DAY, 0); 599 leavePlanCal.set(Calendar.MINUTE, 0); 600 leavePlanCal.set(Calendar.SECOND, 0); 601 leavePlanCal.set(Calendar.MILLISECOND, 0); 602 603 if (cal.getTime().before(leavePlanCal.getTime())) { 604 yearKey = Integer.toString(cal.get(Calendar.YEAR) - 1); 605 } 606 return yearKey; 607 } 608 609 private void assignPendingValuesToRow(LeaveSummaryRow lsr, String accrualCategory, List<LeaveBlock> pendingLeaveBlocks ) { 610 BigDecimal pendingAccrual= BigDecimal.ZERO.setScale(2); 611 BigDecimal pendingRequests = BigDecimal.ZERO.setScale(2); 612 if (CollectionUtils.isNotEmpty(pendingLeaveBlocks)) { 613 for(LeaveBlock aLeaveBlock : pendingLeaveBlocks) { 614 if(!aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER)) { 615 if((StringUtils.isBlank(accrualCategory) && StringUtils.isBlank(aLeaveBlock.getAccrualCategory())) 616 || (StringUtils.isNotBlank(aLeaveBlock.getAccrualCategory()) 617 && StringUtils.equals(aLeaveBlock.getAccrualCategory(), accrualCategory))) { 618 if(aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) >= 0) { 619 pendingAccrual = pendingAccrual.add(aLeaveBlock.getLeaveAmount()); 620 } else { 621 pendingRequests = pendingRequests.add(aLeaveBlock.getLeaveAmount()); 622 } 623 } 624 } 625 } 626 } 627 lsr.setPendingLeaveAccrual(pendingAccrual); 628 lsr.setPendingLeaveRequests(pendingRequests.negate()); 629 } 630 631 @Override 632 public List<Date> getLeaveSummaryDates(CalendarEntries calendarEntry) { 633 List<Date> leaveSummaryDates = new ArrayList<Date>(); 634 635 DateTime start = calendarEntry.getBeginLocalDateTime().toDateTime(); 636 DateTime end = calendarEntry.getEndLocalDateTime().toDateTime(); 637 Interval interval = new Interval(start, end); 638 639 for (DateTime day = interval.getStart(); day.isBefore(interval.getEnd()); day = day.plusDays(1)) { 640 leaveSummaryDates.add(day.toLocalDate().toDateTimeAtStartOfDay().toDate()); 641 } 642 643 return leaveSummaryDates; 644 } 645 646 protected LeaveBlockService getLeaveBlockService() { 647 if (leaveBlockService == null) { 648 leaveBlockService = TkServiceLocator.getLeaveBlockService(); 649 } 650 return leaveBlockService; 651 } 652 653 654 } 655