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