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.balancetransfer.service; 017 018 import java.math.BigDecimal; 019 import java.sql.Date; 020 import java.util.ArrayList; 021 import java.util.Calendar; 022 import java.util.HashMap; 023 import java.util.List; 024 import java.util.Map; 025 026 import org.apache.commons.collections.CollectionUtils; 027 import org.apache.commons.lang.StringUtils; 028 import org.apache.commons.lang.time.DateUtils; 029 import org.joda.time.Interval; 030 import org.kuali.hr.lm.LMConstants; 031 import org.kuali.hr.lm.accrual.AccrualCategory; 032 import org.kuali.hr.lm.accrual.AccrualCategoryRule; 033 import org.kuali.hr.lm.balancetransfer.BalanceTransfer; 034 import org.kuali.hr.lm.balancetransfer.dao.BalanceTransferDao; 035 import org.kuali.hr.lm.employeeoverride.EmployeeOverride; 036 import org.kuali.hr.lm.leaveSummary.LeaveSummary; 037 import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow; 038 import org.kuali.hr.lm.leaveblock.LeaveBlock; 039 import org.kuali.hr.lm.leaveblock.LeaveBlockHistory; 040 import org.kuali.hr.lm.leaveplan.LeavePlan; 041 import org.kuali.hr.time.calendar.CalendarEntries; 042 import org.kuali.hr.time.principal.PrincipalHRAttributes; 043 import org.kuali.hr.time.service.base.TkServiceLocator; 044 import org.kuali.hr.time.util.TKUtils; 045 import org.kuali.hr.time.util.TkConstants; 046 import org.kuali.rice.kew.api.exception.WorkflowException; 047 import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName; 048 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 049 import org.kuali.rice.krad.maintenance.MaintenanceDocument; 050 import org.kuali.rice.krad.service.KRADServiceLocator; 051 import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 052 import org.kuali.rice.krad.util.KRADConstants; 053 import org.kuali.rice.krad.util.ObjectUtils; 054 055 public class BalanceTransferServiceImpl implements BalanceTransferService { 056 057 private BalanceTransferDao balanceTransferDao; 058 059 @Override 060 public List<BalanceTransfer> getAllBalanceTransfersForPrincipalId( 061 String principalId) { 062 return balanceTransferDao.getAllBalanceTransfersForPrincipalId(principalId); 063 } 064 065 @Override 066 public List<BalanceTransfer> getAllBalanceTransferForPrincipalIdAsOfDate( 067 String principalId, Date effectiveDate) { 068 return balanceTransferDao.getAllBalanceTransferForPrincipalIdAsOfDate(principalId,effectiveDate); 069 } 070 071 @Override 072 public List<BalanceTransfer> getAllBalanceTransferByEffectiveDate( 073 Date effectiveDate) { 074 return balanceTransferDao.getAllBalanceTransferByEffectiveDate(effectiveDate); 075 } 076 077 @Override 078 public BalanceTransfer getBalanceTransferById(String balanceTransferId) { 079 return balanceTransferDao.getBalanceTransferById(balanceTransferId); 080 } 081 082 @Override 083 public BalanceTransfer initializeTransfer(String principalId, String accrualCategoryRule, BigDecimal accruedBalance, Date effectiveDate) { 084 //Initially, principals may be allowed to edit the transfer amount when prompted to submit this balance transfer, however, 085 //a base transfer amount together with a forfeited amount is calculated to bring the balance back to its limit in accordance 086 //with transfer limits. 087 BalanceTransfer bt = null; 088 AccrualCategoryRule accrualRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualCategoryRule); 089 090 if(ObjectUtils.isNotNull(accrualRule) && ObjectUtils.isNotNull(accruedBalance)) { 091 bt = new BalanceTransfer(); 092 //Leave summary is not a requirement, per se, but the information it contains is. 093 //The only thing that is obtained from leave summary is the accrued balance of the leave summary row matching the 094 //passed accrualCategoryRules accrual category. 095 //These two objects are essential to balance transfers triggered when the employee submits their leave calendar for approval. 096 //Neither of these objects should be null, otherwise this method could not have been called. 097 AccrualCategory fromAccrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId()); 098 AccrualCategory toAccrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getMaxBalanceTransferToAccrualCategory(),effectiveDate); 099 BigDecimal fullTimeEngagement = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate); 100 101 BigDecimal transferConversionFactor = null; 102 if(ObjectUtils.isNotNull(accrualRule.getMaxBalanceTransferConversionFactor())) 103 transferConversionFactor = accrualRule.getMaxBalanceTransferConversionFactor(); 104 105 // AccrualRule.maxBalance == null -> no balance limit. No balance limit -> no accrual triggered transfer / payout / lose. 106 // execution point should not be here if max balance on accrualRule is null, unless there exists an employee override. 107 BigDecimal maxBalance = accrualRule.getMaxBalance(); 108 BigDecimal adjustedMaxBalance = maxBalance.multiply(fullTimeEngagement).setScale(2); 109 110 BigDecimal maxTransferAmount = null; 111 BigDecimal adjustedMaxTransferAmount = null; 112 if(ObjectUtils.isNotNull(accrualRule.getMaxTransferAmount())) { 113 maxTransferAmount = new BigDecimal(accrualRule.getMaxTransferAmount()); 114 adjustedMaxTransferAmount = maxTransferAmount.multiply(fullTimeEngagement).setScale(2); 115 } 116 else { 117 // no limit on transfer amount 118 maxTransferAmount = new BigDecimal(Long.MAX_VALUE); 119 adjustedMaxTransferAmount = maxTransferAmount; 120 } 121 122 BigDecimal maxCarryOver = null; 123 BigDecimal adjustedMaxCarryOver = null; 124 if(ObjectUtils.isNotNull(accrualRule.getMaxCarryOver())) { 125 maxCarryOver = new BigDecimal(accrualRule.getMaxCarryOver()); 126 adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2); 127 } 128 else { 129 //no limit to carry over. 130 maxCarryOver = new BigDecimal(Long.MAX_VALUE); 131 adjustedMaxCarryOver = maxCarryOver; 132 } 133 134 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, effectiveDate); 135 for(EmployeeOverride override : overrides) { 136 if(StringUtils.equals(override.getAccrualCategory(),fromAccrualCategory.getAccrualCategory())) { 137 if(StringUtils.equals(override.getOverrideType(),"MB")) 138 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 139 //override values are not pro-rated for FTE. 140 if(StringUtils.equals(override.getOverrideType(),"MTA")) 141 adjustedMaxTransferAmount = new BigDecimal(override.getOverrideValue()); 142 if(StringUtils.equals(override.getOverrideType(),"MAC")) 143 adjustedMaxCarryOver = new BigDecimal(override.getOverrideValue()); 144 } 145 } 146 147 148 BigDecimal transferAmount = accruedBalance.subtract(adjustedMaxBalance); 149 if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)) { 150 //Move all time in excess of employee's fte adjusted max balance to forfeiture. 151 bt.setForfeitedAmount(transferAmount); 152 //There is no transfer to another accrual category. 153 bt.setTransferAmount(BigDecimal.ZERO); 154 bt.setAmountTransferred(BigDecimal.ZERO); 155 // to accrual category is a required field on maintenance document. Set as from accrual category 156 // to remove validation errors when routing, approving, etc. 157 bt.setToAccrualCategory(fromAccrualCategory.getAccrualCategory()); 158 } 159 else { 160 // ACTION_AT_MAX_BAL = TRANSFER 161 bt.setToAccrualCategory(toAccrualCategory.getAccrualCategory()); 162 if(transferAmount.compareTo(adjustedMaxTransferAmount) > 0) { 163 //there's forfeiture. 164 //bring transfer amount down to the adjusted maximum transfer amount, and place excess in forfeiture. 165 //accruedBalance - adjustedMaxTransferAmount - adjustedMaxBalance = forfeiture. 166 //transferAmount = accruedBalance - adjustedMaxBalance; forfeiture = transferAmount - adjustedMaxTransferAmount. 167 BigDecimal forfeiture = transferAmount.subtract(adjustedMaxTransferAmount).setScale(2); 168 forfeiture = forfeiture.stripTrailingZeros(); 169 bt.setForfeitedAmount(forfeiture); 170 bt.setTransferAmount(adjustedMaxTransferAmount); 171 } 172 else { 173 bt.setTransferAmount(transferAmount); 174 bt.setForfeitedAmount(BigDecimal.ZERO); 175 } 176 } 177 178 // Max Carry Over logic for Year End transfers. 179 if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) { 180 if(ObjectUtils.isNotNull(maxCarryOver)) { 181 if(ObjectUtils.isNull(adjustedMaxCarryOver)) 182 adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2); 183 //otherwise, adjustedMaxCarryOver has an employee override value, which trumps accrual rule defined MAC. 184 //At this point, transfer amount and forfeiture have been set so that the new accrued balance will be the 185 //adjusted max balance, so this amount is used to check against carry over. 186 if(adjustedMaxBalance.compareTo(adjustedMaxCarryOver) > 0) { 187 BigDecimal carryOverDiff = adjustedMaxBalance.subtract(adjustedMaxCarryOver); 188 189 if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)){ 190 //add carry over excess to forfeiture. 191 bt.setForfeitedAmount(bt.getForfeitedAmount().add(carryOverDiff)); 192 } 193 else { 194 //maximize the transfer amount. 195 BigDecimal potentialTransferAmount = bt.getTransferAmount().add(carryOverDiff); 196 197 //Can this amount be added to the transfer amount?? 198 if(potentialTransferAmount.compareTo(adjustedMaxTransferAmount) <= 0) { 199 //yes 200 bt.setTransferAmount(bt.getTransferAmount().add(carryOverDiff)); 201 } 202 else { 203 //no 204 BigDecimal carryOverExcess = potentialTransferAmount.subtract(adjustedMaxTransferAmount); 205 //move excess to forfeiture 206 bt.setForfeitedAmount(bt.getForfeitedAmount().add(carryOverExcess)); 207 //the remainder (if any) can be added to the transfer amount ( unless action is LOSE ). 208 bt.setTransferAmount(bt.getTransferAmount().add(carryOverDiff.subtract(carryOverExcess))); 209 assert(bt.getTransferAmount().compareTo(adjustedMaxTransferAmount)==0); 210 } 211 } 212 } 213 //otherwise, given balance will be at or under the max annual carry over. 214 } 215 } 216 217 bt.setEffectiveDate(effectiveDate); 218 bt.setAccrualCategoryRule(accrualCategoryRule); 219 bt.setFromAccrualCategory(fromAccrualCategory.getAccrualCategory()); 220 bt.setPrincipalId(principalId); 221 if(ObjectUtils.isNotNull(transferConversionFactor)) 222 bt.setAmountTransferred(bt.getTransferAmount().multiply(transferConversionFactor).setScale(2)); 223 else 224 bt.setAmountTransferred(bt.getTransferAmount()); 225 } 226 return bt; 227 } 228 229 @Override 230 public BalanceTransfer transfer(BalanceTransfer balanceTransfer) { 231 if(ObjectUtils.isNull(balanceTransfer)) 232 throw new RuntimeException("did not supply a valid BalanceTransfer object."); 233 else { 234 List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>(); 235 BigDecimal transferAmount = balanceTransfer.getTransferAmount(); 236 LeaveBlock aLeaveBlock = null; 237 238 if(ObjectUtils.isNotNull(balanceTransfer.getAmountTransferred())) { 239 if(balanceTransfer.getAmountTransferred().compareTo(BigDecimal.ZERO) > 0 ) { 240 241 aLeaveBlock = new LeaveBlock(); 242 //Create a leave block that adds the adjusted transfer amount to the "transfer to" accrual category. 243 aLeaveBlock.setPrincipalId(balanceTransfer.getPrincipalId()); 244 aLeaveBlock.setLeaveDate(balanceTransfer.getEffectiveDate()); 245 aLeaveBlock.setEarnCode(balanceTransfer.getCreditedAccrualCategory().getEarnCode()); 246 aLeaveBlock.setAccrualCategory(balanceTransfer.getToAccrualCategory()); 247 aLeaveBlock.setDescription("Amount transferred"); 248 aLeaveBlock.setLeaveAmount(balanceTransfer.getAmountTransferred()); 249 aLeaveBlock.setAccrualGenerated(true); 250 aLeaveBlock.setTransactionDocId(balanceTransfer.getDocumentHeaderId()); 251 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER); 252 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 253 aLeaveBlock.setBlockId(0L); 254 255 //Want to store the newly created leave block id on this maintainable object 256 //when the status of the maintenance document encapsulating this maintainable changes 257 //the id will be used to fetch and update the leave block statuses. 258 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock); 259 260 balanceTransfer.setAccruedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 261 // save history 262 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock); 263 lbh.setAction(LMConstants.ACTION.ADD); 264 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh); 265 leaveBlocks.add(aLeaveBlock); 266 } 267 } 268 269 if(ObjectUtils.isNotNull(transferAmount)) { 270 if(transferAmount.compareTo(BigDecimal.ZERO) > 0) { 271 //Create leave block that removes the correct transfer amount from the originating accrual category. 272 aLeaveBlock = new LeaveBlock(); 273 aLeaveBlock.setPrincipalId(balanceTransfer.getPrincipalId()); 274 aLeaveBlock.setLeaveDate(balanceTransfer.getEffectiveDate()); 275 aLeaveBlock.setEarnCode(balanceTransfer.getDebitedAccrualCategory().getEarnCode()); 276 aLeaveBlock.setAccrualCategory(balanceTransfer.getFromAccrualCategory()); 277 aLeaveBlock.setDescription("Transferred amount"); 278 aLeaveBlock.setLeaveAmount(balanceTransfer.getTransferAmount().negate()); 279 aLeaveBlock.setAccrualGenerated(true); 280 aLeaveBlock.setTransactionDocId(balanceTransfer.getDocumentHeaderId()); 281 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER); 282 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 283 aLeaveBlock.setBlockId(0L); 284 285 //Want to store the newly created leave block id on this maintainable object. 286 //when the status of the maintenance document encapsulating this maintainable changes 287 //the id will be used to fetch and update the leave block statuses. 288 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock); 289 290 balanceTransfer.setDebitedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 291 // save history 292 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock); 293 lbh.setAction(LMConstants.ACTION.ADD); 294 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh); 295 296 leaveBlocks.add(aLeaveBlock); 297 } 298 } 299 300 BigDecimal forfeitedAmount = balanceTransfer.getForfeitedAmount(); 301 if(ObjectUtils.isNotNull(forfeitedAmount)) { 302 //Any amount forfeited must come out of the originating accrual category in order to bring balance back to max. 303 if(forfeitedAmount.compareTo(BigDecimal.ZERO) > 0) { 304 //for balance transfers with action = lose, transfer amount must be moved to forfeitedAmount 305 aLeaveBlock = new LeaveBlock(); 306 aLeaveBlock.setPrincipalId(balanceTransfer.getPrincipalId()); 307 aLeaveBlock.setLeaveDate(balanceTransfer.getEffectiveDate()); 308 aLeaveBlock.setEarnCode(balanceTransfer.getDebitedAccrualCategory().getEarnCode()); 309 aLeaveBlock.setAccrualCategory(balanceTransfer.getFromAccrualCategory()); 310 aLeaveBlock.setDescription("Forfeited balance transfer amount"); 311 aLeaveBlock.setLeaveAmount(forfeitedAmount.negate()); 312 aLeaveBlock.setAccrualGenerated(true); 313 aLeaveBlock.setTransactionDocId(balanceTransfer.getDocumentHeaderId()); 314 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER); 315 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 316 aLeaveBlock.setBlockId(0L); 317 318 //Want to store the newly created leave block id on this maintainable object 319 //when the status of the maintenance document encapsulating this maintainable changes 320 //the id will be used to fetch and update the leave block statuses. 321 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock); 322 323 balanceTransfer.setForfeitedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 324 // save history 325 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock); 326 lbh.setAction(LMConstants.ACTION.ADD); 327 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh); 328 329 leaveBlocks.add(aLeaveBlock); 330 } 331 } 332 333 return balanceTransfer; 334 } 335 } 336 337 //getMaxBalanceViolations... 338 @Override 339 public Map<String,ArrayList<String>> getEligibleTransfers(CalendarEntries calendarEntry, String principalId) throws Exception { 340 //Employee override check here, or return base-eligible accrual categories, 341 //filtering out those that have increased balance limits due to employee override in caller? 342 //null check inserted to fix LeaveCalendarWebTest failures on kpme-trunk-build-unit #2069 343 List<String> eligibleAccrualCategories = new ArrayList<String>(); 344 Map<String, ArrayList<String>> eligibilities = new HashMap<String,ArrayList<String>>(); 345 //TODO: create map for MAX_BAL_ACTION_FREQ in LMConstants 346 eligibilities.put(LMConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE, new ArrayList<String>()); 347 eligibilities.put(LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END, new ArrayList<String>()); 348 eligibilities.put(LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND, new ArrayList<String>()); 349 if(ObjectUtils.isNull(calendarEntry)) { 350 return eligibilities; 351 } 352 353 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, calendarEntry.getEndPeriodDate()); 354 if(ObjectUtils.isNull(pha)) { 355 return eligibilities; 356 } 357 358 List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), calendarEntry.getEndPeriodDate()); 359 360 org.kuali.hr.time.calendar.Calendar leaveCalendar = pha.getLeaveCalObj(); 361 CalendarEntries thisLeaveEntry = null; 362 Interval thisEntryInterval = new Interval(calendarEntry.getBeginPeriodDate().getTime(),calendarEntry.getEndPeriodDate().getTime()); 363 Date asOfDate = TKUtils.getCurrentDate(); 364 if(TKUtils.getCurrentDate().after(DateUtils.addSeconds(calendarEntry.getEndPeriodDate(),-1))) 365 asOfDate = new Date(DateUtils.addSeconds(calendarEntry.getEndPeriodDate(),-1).getTime()); 366 367 if(ObjectUtils.isNotNull(leaveCalendar)) { 368 for(CalendarEntries entry : leaveCalendar.getCalendarEntries()) { 369 if(thisEntryInterval.contains(DateUtils.addDays(entry.getEndPeriodTime(),-1).getTime())) 370 thisLeaveEntry = entry; 371 } 372 } 373 // this calendar entry interval does not contain a leave calendar's rollover date. 374 if(ObjectUtils.isNull(thisLeaveEntry)) 375 return eligibilities; 376 //TODO: Find the end period date for the corresponding leave calendar. 377 // must check if this date falls within the interval of the calendar entries begin / end. 378 // if so, get the leave blocks and calculate the accrued balance. 379 //LeaveSummary leaveSummary = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId, getCalendarEntry()); 380 if(!accrualCategories.isEmpty()) { 381 382 LeaveSummary summary = TkServiceLocator.getLeaveSummaryService().getLeaveSummaryAsOfDate(principalId, asOfDate); 383 BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, calendarEntry.getEndPeriodDate()); 384 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, calendarEntry.getEndPeriodDate()); 385 386 //null check inserted to fix LeaveCalendarWebTst failures on kpme-trunk-build-unit #2069 387 for(AccrualCategory accrualCategory : accrualCategories) { 388 //TODO: Iterate over Accrual Categories within this calendar entry. 389 AccrualCategoryRule rule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(accrualCategory, DateUtils.addDays(calendarEntry.getEndPeriodDate(), -1), pha.getServiceDate()); 390 //Employee overrides... 391 if(ObjectUtils.isNotNull(rule)) { 392 if(StringUtils.equals(rule.getMaxBalFlag(),"Y")) { 393 if(StringUtils.equals(rule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.TRANSFER) 394 || StringUtils.equals(rule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.LOSE)) { 395 //There is a disagreement between the constant value LMConstants.MAX_BAL_ACTION_FREQ, and the value being 396 //set on LM_ACCRUAL_CATEGORY_RULES_T table. Temporarily have changed the constant to reflect the field 397 //value being set for MAX_BAL_ACTION_FREQ when accrual category rule records are saved. 398 if(ObjectUtils.isNotNull(rule.getMaxBalanceActionFrequency())) { 399 BigDecimal maxBalance = rule.getMaxBalance(); 400 401 LeaveSummaryRow row = summary.getLeaveSummaryRowForAccrualCategory(accrualCategory.getLmAccrualCategoryId()); 402 if(row == null) 403 continue; 404 BigDecimal accruedBalance = row.getAccruedBalance(); 405 /* for(LeaveBlock leaveBlock : leaveBlockMap.get(accrualCategory.getAccrualCategory())) { 406 //TODO: limit leave blocks to those created on or after the calendar year period containing this calendar entry. 407 if(StringUtils.equals(leaveBlock.getRequestStatus(),LMConstants.REQUEST_STATUS.APPROVED)) 408 accruedBalance = accruedBalance.add(leaveBlock.getLeaveAmount()); 409 }*/ 410 BigDecimal adjustedMaxBalance = maxBalance.multiply(fte); 411 BigDecimal maxAnnualCarryOver = null; 412 if(ObjectUtils.isNotNull(rule.getMaxCarryOver())) { 413 maxAnnualCarryOver = new BigDecimal(rule.getMaxCarryOver()); 414 } 415 BigDecimal adjustedMaxAnnualCarryOver = null; 416 if(ObjectUtils.isNotNull(maxAnnualCarryOver)) { 417 adjustedMaxAnnualCarryOver = maxAnnualCarryOver.multiply(fte); 418 } 419 420 for(EmployeeOverride override : overrides) { 421 if(StringUtils.equals(override.getAccrualCategory(),accrualCategory.getAccrualCategory())) { 422 if(StringUtils.equals(override.getOverrideType(),"MB")) { 423 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 424 } 425 if(StringUtils.equals(override.getOverrideType(),"MAC")) { 426 adjustedMaxAnnualCarryOver = new BigDecimal(override.getOverrideValue()); 427 } 428 //override values are not pro-rated. 429 } 430 } 431 //should extend a BalanceTransferBase class, or use an algorithm swapping pattern. 432 //allow institutions to extend/customize/implement their own max_bal_action_frequency types. 433 if(StringUtils.equals(rule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) { 434 //For year end transfer frequencies... 435 //Should use an "asOfDate" or effective date for principalHRAttributes. If getting eligibilities for a past calendar, 436 //pha may not be the same. 437 LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(pha.getLeavePlan(),TKUtils.getCurrentDate()); 438 StringBuilder sb = new StringBuilder(""); 439 String calendarYearStart = lp.getCalendarYearStart(); 440 // mm/dd 441 sb.append(calendarYearStart+"/"); 442 if(lp.getCalendarYearStartMonth().equals("01") && calendarEntry.getBeginPeriodDate().getMonth() == 11) { 443 //a calendar may start on 01/15, with monthly intervals. 444 //calendarEntry.beginPeriodDate.year = calendarYearStart.year - 1 445 sb.append(DateUtils.toCalendar(DateUtils.addYears(calendarEntry.getBeginPeriodDate(),1)).get(Calendar.YEAR)); 446 } 447 else { 448 sb.append(DateUtils.toCalendar(calendarEntry.getBeginPeriodDateTime()).get(Calendar.YEAR)); 449 } 450 //if the calendar being submitted is the final calendar in the leave plans calendar year. 451 //must check the calendar year start month. If its the first month of the year, add a year to the date. 452 //otherwise, the end period date and the calendar year start date have the same year. 453 if(thisEntryInterval.contains(DateUtils.addDays(TKUtils.formatDateString(sb.toString()),-1).getTime())) { 454 //BigDecimal accruedBalanceLessPendingTransfers = lsr.getAccruedBalance().add(adjustment); 455 if(accruedBalance.compareTo(adjustedMaxBalance) > 0 || 456 (ObjectUtils.isNotNull(adjustedMaxAnnualCarryOver) && 457 accruedBalance.compareTo(adjustedMaxAnnualCarryOver) > 0)) { 458 eligibleAccrualCategories.add(rule.getLmAccrualCategoryRuleId()); 459 eligibilities.get(LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END).add(rule.getLmAccrualCategoryRuleId()); 460 } 461 } 462 //otherwise its not transferable under year end frequency. 463 } 464 else { 465 //BigDecimal accruedBalanceLessPendingTransfers = lsr.getAccruedBalance().add(adjustment); 466 if(accruedBalance.compareTo(adjustedMaxBalance) > 0 ) { 467 eligibleAccrualCategories.add(rule.getLmAccrualCategoryRuleId()); 468 eligibilities.get(rule.getMaxBalanceActionFrequency()).add(rule.getLmAccrualCategoryRuleId()); 469 } 470 } 471 } 472 } 473 } 474 } 475 } 476 } 477 return eligibilities; 478 479 } 480 481 482 /* //TODO: Move to LeaveCalendarService or implement as an accessor on LeaveCalendarDocument object. 483 @Override 484 public List<String> getEligibleTransfers(CalendarEntries calendarEntry, String principalId, final String actionFrequency) throws Exception { 485 List<String> eligibleAccrualCategories = new ArrayList<String>(); 486 //Employee override check here, or return base-eligible accrual categories, 487 //filtering out those that have increased balance limits due to employee override in caller? 488 if(ObjectUtils.isNotNull(calendarEntry)) { 489 //null check inserted to fix LeaveCalendarWebTest failures on kpme-trunk-build-unit #2069 490 491 LeaveSummary leaveSummary = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId,calendarEntry); 492 PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, calendarEntry.getEndPeriodDate()); 493 494 org.kuali.hr.time.calendar.Calendar leaveCalendar = pha.getLeaveCalObj(); 495 CalendarEntries thisLeaveEntry = null; 496 Interval thisEntryInterval = new Interval(calendarEntry.getBeginPeriodDate().getTime(),calendarEntry.getEndPeriodDate().getTime()); 497 498 if(ObjectUtils.isNotNull(leaveCalendar)) { 499 for(CalendarEntries entry : leaveCalendar.getCalendarEntries()) { 500 if(thisEntryInterval.contains(DateUtils.addSeconds(entry.getEndPeriodTime(),-1).getTime())) 501 thisLeaveEntry = entry; 502 } 503 } 504 505 // this calendar entry interval does not contain a leave calendar's rollover date. 506 if(ObjectUtils.isNull(thisLeaveEntry)) 507 return eligibleAccrualCategories; 508 509 if(ObjectUtils.isNotNull(leaveSummary)) { 510 //null check inserted to fix LeaveCalendarWebTst failures on kpme-trunk-build-unit #2069 511 512 for(LeaveSummaryRow lsr : leaveSummary.getLeaveSummaryRows()) { 513 514 String accrualCategoryRuleId = lsr.getAccrualCategoryRuleId(); 515 AccrualCategoryRule rule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualCategoryRuleId); 516 //Employee overrides... 517 if(ObjectUtils.isNotNull(rule)) { 518 if(StringUtils.equals(rule.getMaxBalFlag(),"Y")) { 519 if(StringUtils.equals(rule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.TRANSFER) 520 || StringUtils.equals(rule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.LOSE)) { 521 //There is a disagreement between the constant value LMConstants.MAX_BAL_ACTION_FREQ, and the value being 522 //set on LM_ACCRUAL_CATEGORY_RULES_T table. Temporarily have changed the constant to reflect the field 523 //value being set for MAX_BAL_ACTION_FREQ when accrual category rule records are saved. 524 if(StringUtils.equals(rule.getMaxBalanceActionFrequency(),actionFrequency)) { 525 BigDecimal maxBalance = rule.getMaxBalance(); 526 527 BigDecimal fullTimeEngagement = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate()); 528 BigDecimal adjustedMaxBalance = maxBalance.multiply(fullTimeEngagement); 529 BigDecimal maxAnnualCarryOver = null; 530 if(ObjectUtils.isNotNull(rule.getMaxCarryOver())) 531 maxAnnualCarryOver = new BigDecimal(rule.getMaxCarryOver()); 532 BigDecimal adjustedMaxAnnualCarryOver = null; 533 if(ObjectUtils.isNotNull(maxAnnualCarryOver)) 534 adjustedMaxAnnualCarryOver = maxAnnualCarryOver.multiply(fullTimeEngagement); 535 536 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate()); 537 for(EmployeeOverride override : overrides) { 538 if(StringUtils.equals(override.getAccrualCategory(),lsr.getAccrualCategoryId())) { 539 if(StringUtils.equals(override.getOverrideType(),"MB")) 540 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 541 if(StringUtils.equals(override.getOverrideType(),"MAC")) 542 adjustedMaxAnnualCarryOver = new BigDecimal(override.getOverrideValue()); 543 //override values are not pro-rated. 544 } 545 } 546 //should extend a BalanceTransferBase class, or use an algorithm swapping pattern. 547 //allow institutions to extend/customize/implement their own max_bal_action_frequency types. 548 if(StringUtils.equals(actionFrequency,LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) { 549 //For year end transfer frequencies... 550 LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(pha.getLeavePlan(),TKUtils.getCurrentDate()); 551 StringBuilder sb = new StringBuilder(""); 552 String calendarYearStart = lp.getCalendarYearStart(); 553 // mm/dd 554 sb.append(calendarYearStart+"/"); 555 if(lp.getCalendarYearStartMonth().equals("01") && calendarEntry.getBeginPeriodDate().getMonth() == 11) { 556 //a calendar may start on 01/15, with monthly intervals. 557 sb.append(DateUtils.toCalendar(DateUtils.addYears(calendarEntry.getBeginPeriodDate(),1)).get(Calendar.YEAR)); 558 } 559 else 560 sb.append(DateUtils.toCalendar(calendarEntry.getBeginPeriodDateTime()).get(Calendar.YEAR)); 561 //if the calendar being submitted is the final calendar in the leave plans calendar year. 562 //must check the calendar year start month. If its the first month of the year, add a year to the date. 563 //otherwise, the end period date and the calendar year start date have the same year. 564 if(thisEntryInterval.contains(DateUtils.addSeconds(TKUtils.formatDateString(sb.toString()),-1).getTime())) { 565 //BigDecimal accruedBalanceLessPendingTransfers = lsr.getAccruedBalance().add(adjustment); 566 if(lsr.getAccruedBalance().compareTo(adjustedMaxBalance) > 0 || 567 (ObjectUtils.isNotNull(adjustedMaxAnnualCarryOver) && 568 lsr.getAccruedBalance().compareTo(adjustedMaxAnnualCarryOver) > 0)) { 569 eligibleAccrualCategories.add(rule.getLmAccrualCategoryRuleId()); 570 } 571 } 572 //otherwise its not transferable under year end frequency. 573 } 574 else { 575 //BigDecimal accruedBalanceLessPendingTransfers = lsr.getAccruedBalance().add(adjustment); 576 if(lsr.getAccruedBalance().compareTo(adjustedMaxBalance) > 0 ) { 577 eligibleAccrualCategories.add(rule.getLmAccrualCategoryRuleId()); 578 } 579 } 580 } 581 } 582 } 583 } 584 } 585 } 586 } 587 return eligibleAccrualCategories; 588 } 589 */ 590 public BalanceTransferDao getBalanceTransferDao() { 591 return balanceTransferDao; 592 } 593 594 public void setBalanceTransferDao(BalanceTransferDao balanceTransferDao) { 595 this.balanceTransferDao = balanceTransferDao; 596 } 597 598 @Override 599 public void submitToWorkflow(BalanceTransfer balanceTransfer) 600 throws WorkflowException { 601 602 //balanceTransfer.setStatus(TkConstants.ROUTE_STATUS.ENROUTE); 603 EntityNamePrincipalName principalName = null; 604 if (balanceTransfer.getPrincipalId() != null) { 605 principalName = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(balanceTransfer.getPrincipalId()); 606 } 607 608 MaintenanceDocument document = KRADServiceLocatorWeb.getMaintenanceDocumentService().setupNewMaintenanceDocument(BalanceTransfer.class.getName(), 609 "BalanceTransferDocumentType",KRADConstants.MAINTENANCE_NEW_ACTION); 610 611 String personName = (principalName != null && principalName.getDefaultName() != null) ? principalName.getDefaultName().getCompositeName() : StringUtils.EMPTY; 612 String date = TKUtils.formatDate(new java.sql.Date(balanceTransfer.getEffectiveDate().getTime())); 613 document.getDocumentHeader().setDocumentDescription(personName + " (" + balanceTransfer.getPrincipalId() + ") - " + date); 614 Map<String,String[]> params = new HashMap<String,String[]>(); 615 616 KRADServiceLocatorWeb.getMaintenanceDocumentService().setupMaintenanceObject(document, KRADConstants.MAINTENANCE_NEW_ACTION, params); 617 BalanceTransfer btObj = (BalanceTransfer) document.getNewMaintainableObject().getDataObject(); 618 619 btObj.setAccrualCategoryRule(balanceTransfer.getAccrualCategoryRule()); 620 btObj.setEffectiveDate(balanceTransfer.getEffectiveDate()); 621 btObj.setForfeitedAmount(balanceTransfer.getForfeitedAmount()); 622 btObj.setFromAccrualCategory(balanceTransfer.getFromAccrualCategory()); 623 btObj.setPrincipalId(balanceTransfer.getPrincipalId()); 624 btObj.setToAccrualCategory(balanceTransfer.getToAccrualCategory()); 625 btObj.setTransferAmount(balanceTransfer.getTransferAmount()); 626 btObj.setAmountTransferred(balanceTransfer.getAmountTransferred()); 627 btObj.setSstoId(balanceTransfer.getSstoId()); 628 btObj.setDocumentHeaderId(document.getDocumentHeader().getWorkflowDocument().getDocumentId()); 629 /* TkServiceLocator.getBalanceTransferService().saveOrUpdate(btObj); 630 document.getNewMaintainableObject().setDataObject(btObj);*/ 631 KRADServiceLocatorWeb.getDocumentService().saveDocument(document); 632 document.getDocumentHeader().getWorkflowDocument().saveDocument(""); 633 634 document.getDocumentHeader().getWorkflowDocument().route(""); 635 636 637 638 639 } 640 641 @Override 642 public BalanceTransfer transferSsto(BalanceTransfer balanceTransfer) { 643 if(ObjectUtils.isNull(balanceTransfer)) 644 throw new RuntimeException("did not supply a valid BalanceTransfer object."); 645 else { 646 List<LeaveBlock> sstoLbList = TkServiceLocator.getLeaveBlockService().getSSTOLeaveBlocks(balanceTransfer.getPrincipalId(), balanceTransfer.getSstoId(), balanceTransfer.getEffectiveDate()); 647 String leaveDocId = ""; 648 if(CollectionUtils.isNotEmpty(sstoLbList)) { 649 leaveDocId = sstoLbList.get(0).getDocumentId(); 650 } 651 List<LeaveBlock> lbList = new ArrayList<LeaveBlock>(); 652 // create a new leave block with transferred amount, make sure system scheduled timeoff id is added to it 653 LeaveBlock aLeaveBlock = new LeaveBlock(); 654 aLeaveBlock.setPrincipalId(balanceTransfer.getPrincipalId()); 655 aLeaveBlock.setLeaveDate(balanceTransfer.getEffectiveDate()); 656 aLeaveBlock.setEarnCode(balanceTransfer.getCreditedAccrualCategory().getEarnCode()); 657 aLeaveBlock.setAccrualCategory(balanceTransfer.getToAccrualCategory()); 658 aLeaveBlock.setDescription("System Scheduled Time off Amount transferred"); 659 aLeaveBlock.setLeaveAmount(balanceTransfer.getAmountTransferred()); 660 aLeaveBlock.setAccrualGenerated(false); 661 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER); 662 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 663 aLeaveBlock.setBlockId(0L); 664 aLeaveBlock.setScheduleTimeOffId(balanceTransfer.getSstoId()); 665 aLeaveBlock.setDocumentId(leaveDocId); 666 667 lbList.add(aLeaveBlock); 668 TkServiceLocator.getLeaveBlockService().saveLeaveBlocks(lbList); 669 670 balanceTransfer.setAccruedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 671 return balanceTransfer; 672 } 673 } 674 675 @Override 676 public List<BalanceTransfer> getBalanceTransfers(String viewPrincipal, 677 Date beginPeriodDate, Date endPeriodDate) { 678 // TODO Auto-generated method stub 679 return balanceTransferDao.getBalanceTransfers(viewPrincipal, beginPeriodDate, endPeriodDate); 680 } 681 682 @Override 683 public void saveOrUpdate(BalanceTransfer balanceTransfer) { 684 // TODO Auto-generated method stub 685 balanceTransferDao.saveOrUpdate(balanceTransfer); 686 } 687 }