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 if (acRule != null && acRule.getMaxCarryOver() != null && acRule.getMaxCarryOver() < carryOver.longValue()) { 312 carryOver = new BigDecimal(acRule.getMaxCarryOver()); 313 } 314 } 315 316 lsr.setCarryOver(carryOver); 317 if (acRule != null && acRule.getMaxCarryOver() != null) { 318 lsr.setMaxCarryOver(new BigDecimal(acRule.getMaxCarryOver())); 319 } 320 321 //handle future leave blocks 322 assignPendingValuesToRow(lsr, ac.getAccrualCategory(), futureLeaveBlockMap.get(ac.getAccrualCategory())); 323 324 //compute Leave Balance 325 BigDecimal leaveBalance = lsr.getAccruedBalance().subtract(lsr.getPendingLeaveRequests()); 326 //if leave balance is set 327 //Employee overrides have already been taken into consideration and the appropriate values 328 //for usage have been set by this point. 329 // if (acRule != null && StringUtils.equals(acRule.getMaxBalFlag(), "Y")) { 330 //there exists an accrual category rule with max balance limit imposed. 331 //max bal flag = 'Y' has no precedence here with max-bal / balance transfers implemented. 332 //unless institutions are not required to define a max balance limit for action_at_max_bal = LOSE. 333 //Possibly preferable to procure forfeiture blocks through balance transfer 334 if(lsr.getUsageLimit()!=null) { //should not set leave balance to usage limit simply because it's not null. 335 BigDecimal availableUsage = lsr.getUsageLimit().subtract(lsr.getYtdApprovedUsage().add(lsr.getPendingLeaveRequests())); 336 if(leaveBalance.compareTo( availableUsage ) > 0) 337 lsr.setLeaveBalance(availableUsage); 338 else 339 lsr.setLeaveBalance(leaveBalance); 340 } else { //no usage limit 341 lsr.setLeaveBalance(leaveBalance); 342 } 343 344 rows.add(lsr); 345 } 346 } 347 // let's check for 'empty' accrual categories 348 if (leaveBlockMap.containsKey(null) 349 || futureLeaveBlockMap.containsKey(null)) { 350 LeaveSummaryRow otherLeaveSummary = new LeaveSummaryRow(); 351 //otherLeaveSummary.setAccrualCategory("Other"); 352 353 assignApprovedValuesToRow(otherLeaveSummary, null, leaveBlockMap.get(null), lp, startDate, endDate); 354 BigDecimal carryOver = BigDecimal.ZERO.setScale(2); 355 for (Map.Entry<String, BigDecimal> entry : otherLeaveSummary.getPriorYearsTotalAccrued().entrySet()) { 356 carryOver = carryOver.add(entry.getValue()); 357 BigDecimal use = otherLeaveSummary.getPriorYearsUsage().containsKey(entry.getKey()) ? otherLeaveSummary.getPriorYearsUsage().get(entry.getKey()) : BigDecimal.ZERO; 358 carryOver = carryOver.add(use); 359 } 360 otherLeaveSummary.setCarryOver(carryOver); 361 assignPendingValuesToRow(otherLeaveSummary, null, futureLeaveBlockMap.get(null)); 362 otherLeaveSummary.setAccrualCategory("Other"); 363 364 //compute Leave Balance 365 // blank the avail 366 otherLeaveSummary.setUsageLimit(null); 367 otherLeaveSummary.setLeaveBalance(null); 368 369 rows.add(otherLeaveSummary); 370 } 371 } 372 } 373 } 374 ls.setLeaveSummaryRows(rows); 375 return ls; 376 } 377 378 private PrincipalHRAttributes getPrincipalHrAttributes(String principalId, Date startDate, Date endDate) { 379 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, startDate); 380 if(pha == null) { // principal hr attributes does not exist at the beginning of this calendar entry 381 pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, endDate); 382 } 383 return pha; 384 } 385 386 private Set<String> getLeavePlans(String principalId, Date startDate, Date endDate) { 387 Set<String> lpStrings = new HashSet<String>(); // 388 389 PrincipalHRAttributes pha = getPrincipalHrAttributes(principalId, startDate, endDate); 390 // get list of principalHrAttributes that become active during this pay period 391 List<PrincipalHRAttributes> phaList = TkServiceLocator.getPrincipalHRAttributeService() 392 .getActivePrincipalHrAttributesForRange(principalId, startDate, endDate); 393 if(pha != null) { 394 lpStrings.add(pha.getLeavePlan()); 395 } 396 if(CollectionUtils.isNotEmpty(phaList)) { 397 for(PrincipalHRAttributes aPha : phaList) { 398 lpStrings.add(aPha.getLeavePlan()); 399 } 400 } 401 402 return lpStrings; 403 } 404 405 406 private Map<String, List<LeaveBlock>> mapLeaveBlocksByAccrualCategory(List<LeaveBlock> leaveBlocks) { 407 Map<String, List<LeaveBlock>> map = new HashMap<String, List<LeaveBlock>>(); 408 for (LeaveBlock lb : leaveBlocks) { 409 if (map.containsKey(lb.getAccrualCategory())) { 410 map.get(lb.getAccrualCategory()).add(lb); 411 } else { 412 List<LeaveBlock> splitLeaveBlocks = new ArrayList<LeaveBlock>(); 413 splitLeaveBlocks.add(lb); 414 map.put(lb.getAccrualCategory(), splitLeaveBlocks); 415 } 416 } 417 return map; 418 } 419 420 /** 421 * Use with button display for balance transfers available On-Demand. 422 * @param lsr 423 * @param accrualCategoryRule 424 * @param principalId 425 */ 426 private void markTransferable(LeaveSummaryRow lsr, AccrualCategoryRule accrualCategoryRule, String principalId) { 427 //return type must be changed to boolean, or an associated field element must be created for decision 428 //purposes. 429 //an accrual category's balance is transferable if the accrued balance is 430 //greater than the maximum balance allowed for the accrual category. action_at_max_balance must be TRANSFER 431 boolean transferable = false; 432 if(ObjectUtils.isNotNull(accrualCategoryRule)) { 433 if(ObjectUtils.isNotNull(accrualCategoryRule.getMaxBalance())) { 434 BigDecimal maxBalance = accrualCategoryRule.getMaxBalance(); 435 BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate()); 436 BigDecimal adjustedMaxBalance = maxBalance.multiply(fte); 437 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate()); 438 for(EmployeeOverride override : overrides) { 439 if(StringUtils.equals(override.getOverrideType(),TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MB")) 440 && override.isActive()) { 441 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 442 break; 443 } 444 } 445 if(adjustedMaxBalance.compareTo(lsr.getAccruedBalance()) < 0) { 446 if(StringUtils.equals(accrualCategoryRule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.TRANSFER) && 447 StringUtils.equals(accrualCategoryRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND)) 448 transferable = true; 449 } 450 } 451 } 452 lsr.setTransferable(transferable); 453 } 454 455 /** 456 * Use with button display for balance transfer payouts available On-Demand. 457 * @param lsr 458 * @param accrualCategoryRule 459 * @param principalId 460 */ 461 private void markPayoutable(LeaveSummaryRow lsr, AccrualCategoryRule accrualCategoryRule, String principalId) { 462 //return type must be changed to boolean, or an associated field element must be created for decision 463 //purposes. 464 //an accrual category's balance is transferable if max_bal_action_frequency is ON-DEMAND 465 //and action_at_max_balance is PAYOUT 466 boolean payoutable = false; 467 if(ObjectUtils.isNotNull(accrualCategoryRule)) { 468 if(ObjectUtils.isNotNull(accrualCategoryRule.getMaxBalance())) { 469 BigDecimal maxBalance = accrualCategoryRule.getMaxBalance(); 470 BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate()); 471 BigDecimal adjustedMaxBalance = maxBalance.multiply(fte); 472 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate()); 473 for(EmployeeOverride override : overrides) { 474 if(StringUtils.equals(override.getOverrideType(),TkConstants.EMPLOYEE_OVERRIDE_TYPE.get("MB")) 475 && override.isActive()) { 476 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 477 break; 478 } 479 } 480 if(adjustedMaxBalance.compareTo(lsr.getAccruedBalance()) < 0) { 481 if(StringUtils.equals(accrualCategoryRule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.PAYOUT) && 482 StringUtils.equals(accrualCategoryRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND)) 483 payoutable = true; 484 } 485 } 486 } 487 lsr.setPayoutable(payoutable); 488 } 489 490 private void assignApprovedValuesToRow(LeaveSummaryRow lsr, String accrualCategory, List<LeaveBlock> approvedLeaveBlocks, LeavePlan lp, Date ytdEarnedEffectiveDate, Date effectiveDate) { 491 492 SortedMap<String, BigDecimal> yearlyAccrued = new TreeMap<String, BigDecimal>(); 493 SortedMap<String, BigDecimal> yearlyUsage = new TreeMap<String, BigDecimal>(); 494 BigDecimal accrualedBalance = BigDecimal.ZERO.setScale(2); 495 BigDecimal approvedUsage = BigDecimal.ZERO.setScale(2); 496 BigDecimal fmlaUsage = BigDecimal.ZERO.setScale(2); 497 498 Date cutOffDateToCheck = ytdEarnedEffectiveDate != null ? ytdEarnedEffectiveDate : effectiveDate; 499 DateTime cutOffDate = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(lp.getLeavePlan(), cutOffDateToCheck).minus(1); 500 501 Timestamp priorYearCutOff = new Timestamp(cutOffDate.getMillis()); 502 503 if (CollectionUtils.isNotEmpty(approvedLeaveBlocks)) { 504 // create it here so we don't need to get instance every loop iteration 505 for(LeaveBlock aLeaveBlock : approvedLeaveBlocks) { 506 // check if leave date is before the next calendar start. 507 if(!aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER) 508 && aLeaveBlock.getLeaveDate().getTime() < effectiveDate.getTime()) { 509 if((StringUtils.isBlank(accrualCategory) && StringUtils.isBlank(aLeaveBlock.getAccrualCategory())) 510 || (StringUtils.isNotBlank(aLeaveBlock.getAccrualCategory()) 511 && StringUtils.equals(aLeaveBlock.getAccrualCategory(), accrualCategory))) { 512 if(aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) >= 0 513 && !aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR)) { 514 if(!(StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, aLeaveBlock.getRequestStatus()) || 515 StringUtils.equals(LMConstants.REQUEST_STATUS.DEFERRED, aLeaveBlock.getRequestStatus()))) { 516 if (aLeaveBlock.getLeaveDate().getTime() <= priorYearCutOff.getTime()) { 517 String yearKey = getYearKey(aLeaveBlock.getLeaveDate(), lp); 518 BigDecimal co = yearlyAccrued.get(yearKey); 519 if (co == null) { 520 co = BigDecimal.ZERO.setScale(2); 521 } 522 co = co.add(aLeaveBlock.getLeaveAmount()); 523 yearlyAccrued.put(yearKey, co); 524 } else if(aLeaveBlock.getLeaveDate().getTime() < ytdEarnedEffectiveDate.getTime()) { 525 accrualedBalance = accrualedBalance.add(aLeaveBlock.getLeaveAmount()); 526 } 527 } 528 } else { 529 BigDecimal currentLeaveAmount = aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) > 0 ? aLeaveBlock.getLeaveAmount().negate() : aLeaveBlock.getLeaveAmount(); 530 //we only want this for the current calendar!!! 531 if(!(StringUtils.equals(LMConstants.REQUEST_STATUS.DISAPPROVED, aLeaveBlock.getRequestStatus()) || 532 StringUtils.equals(LMConstants.REQUEST_STATUS.DEFERRED, aLeaveBlock.getRequestStatus()))) { 533 if (aLeaveBlock.getLeaveDate().getTime() > priorYearCutOff.getTime()) { 534 EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(aLeaveBlock.getEarnCode(), aLeaveBlock.getLeaveDate()); 535 if (ec != null && StringUtils.equals(ec.getAccrualBalanceAction(), LMConstants.ACCRUAL_BALANCE_ACTION.USAGE)){ 536 approvedUsage = approvedUsage.add(currentLeaveAmount); 537 } 538 if(ec != null && ec.getFmla().equals("Y")) { 539 fmlaUsage = fmlaUsage.add(aLeaveBlock.getLeaveAmount()); 540 } 541 } else { 542 //these usages are for previous years, to help figure out correct carry over values 543 String yearKey = getYearKey(aLeaveBlock.getLeaveDate(), lp); 544 BigDecimal use = yearlyUsage.get(yearKey); 545 if (use == null) { 546 use = BigDecimal.ZERO.setScale(2); 547 } 548 use = use.add(currentLeaveAmount); 549 yearlyUsage.put(yearKey, use); 550 } 551 } 552 } 553 554 //} 555 } 556 } else { 557 //we can actually use the carry over block!! 558 559 } 560 } 561 } 562 563 lsr.setPriorYearsTotalAccrued(yearlyAccrued); 564 lsr.setPriorYearsUsage(yearlyUsage); 565 lsr.setYtdAccruedBalance(accrualedBalance); 566 lsr.setYtdApprovedUsage(approvedUsage.negate()); 567 lsr.setFmlaUsage(fmlaUsage.negate()); 568 569 //lsr.setLeaveBalance(lsr.getYtdAccruedBalance().add(approvedUsage)); 570 } 571 572 private String getYearKey(Date leaveDate, LeavePlan lp){ 573 Calendar cal = Calendar.getInstance(); 574 Calendar leavePlanCal = Calendar.getInstance(); 575 cal.setTime(leaveDate); 576 String yearKey = Integer.toString(cal.get(Calendar.YEAR)); 577 leavePlanCal.set(Calendar.MONTH, Integer.parseInt(lp.getCalendarYearStartMonth()) - 1); 578 leavePlanCal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(lp.getCalendarYearStartDayOfMonth())); 579 leavePlanCal.set(Calendar.YEAR, cal.get(Calendar.YEAR)); 580 leavePlanCal.set(Calendar.HOUR_OF_DAY, 0); 581 leavePlanCal.set(Calendar.MINUTE, 0); 582 leavePlanCal.set(Calendar.SECOND, 0); 583 leavePlanCal.set(Calendar.MILLISECOND, 0); 584 585 if (cal.getTime().before(leavePlanCal.getTime())) { 586 yearKey = Integer.toString(cal.get(Calendar.YEAR) - 1); 587 } 588 return yearKey; 589 } 590 591 private void assignPendingValuesToRow(LeaveSummaryRow lsr, String accrualCategory, List<LeaveBlock> pendingLeaveBlocks ) { 592 BigDecimal pendingAccrual= BigDecimal.ZERO.setScale(2); 593 BigDecimal pendingRequests = BigDecimal.ZERO.setScale(2); 594 if (CollectionUtils.isNotEmpty(pendingLeaveBlocks)) { 595 for(LeaveBlock aLeaveBlock : pendingLeaveBlocks) { 596 if(!aLeaveBlock.getLeaveBlockType().equals(LMConstants.LEAVE_BLOCK_TYPE.CARRY_OVER)) { 597 if((StringUtils.isBlank(accrualCategory) && StringUtils.isBlank(aLeaveBlock.getAccrualCategory())) 598 || (StringUtils.isNotBlank(aLeaveBlock.getAccrualCategory()) 599 && StringUtils.equals(aLeaveBlock.getAccrualCategory(), accrualCategory))) { 600 if(aLeaveBlock.getLeaveAmount().compareTo(BigDecimal.ZERO) >= 0) { 601 pendingAccrual = pendingAccrual.add(aLeaveBlock.getLeaveAmount()); 602 } else { 603 pendingRequests = pendingRequests.add(aLeaveBlock.getLeaveAmount()); 604 } 605 } 606 } 607 } 608 } 609 lsr.setPendingLeaveAccrual(pendingAccrual); 610 lsr.setPendingLeaveRequests(pendingRequests.negate()); 611 } 612 613 @Override 614 public List<Date> getLeaveSummaryDates(CalendarEntries calendarEntry) { 615 List<Date> leaveSummaryDates = new ArrayList<Date>(); 616 617 DateTime start = calendarEntry.getBeginLocalDateTime().toDateTime(); 618 DateTime end = calendarEntry.getEndLocalDateTime().toDateTime(); 619 Interval interval = new Interval(start, end); 620 621 for (DateTime day = interval.getStart(); day.isBefore(interval.getEnd()); day = day.plusDays(1)) { 622 leaveSummaryDates.add(day.toLocalDate().toDateTimeAtStartOfDay().toDate()); 623 } 624 625 return leaveSummaryDates; 626 } 627 628 protected LeaveBlockService getLeaveBlockService() { 629 if (leaveBlockService == null) { 630 leaveBlockService = TkServiceLocator.getLeaveBlockService(); 631 } 632 return leaveBlockService; 633 } 634 635 636 } 637