View Javadoc

1   /**
2    * Copyright 2004-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.hr.lm.leavepayout.service;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.lang.time.DateUtils;
20  import org.joda.time.Interval;
21  import org.kuali.hr.lm.LMConstants;
22  import org.kuali.hr.lm.accrual.AccrualCategory;
23  import org.kuali.hr.lm.accrual.AccrualCategoryRule;
24  import org.kuali.hr.lm.leavepayout.LeavePayout;
25  import org.kuali.hr.lm.employeeoverride.EmployeeOverride;
26  import org.kuali.hr.lm.leaveSummary.LeaveSummary;
27  import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow;
28  import org.kuali.hr.lm.leaveblock.LeaveBlock;
29  import org.kuali.hr.lm.leaveblock.LeaveBlockHistory;
30  import org.kuali.hr.lm.leavecalendar.LeaveCalendarDocument;
31  import org.kuali.hr.lm.leavepayout.LeavePayout;
32  import org.kuali.hr.lm.leavepayout.dao.LeavePayoutDao;
33  
34  import org.kuali.hr.lm.leaveplan.LeavePlan;
35  import org.kuali.hr.time.calendar.CalendarEntries;
36  import org.kuali.hr.time.earncode.EarnCode;
37  import org.kuali.hr.time.principal.PrincipalHRAttributes;
38  import org.kuali.hr.time.service.base.TkServiceLocator;
39  import org.kuali.hr.time.util.TKContext;
40  import org.kuali.hr.time.util.TKUtils;
41  import org.kuali.hr.time.util.TkConstants;
42  import org.kuali.rice.kew.api.exception.WorkflowException;
43  import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
44  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
45  import org.kuali.rice.krad.maintenance.MaintenanceDocument;
46  import org.kuali.rice.krad.service.KRADServiceLocator;
47  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
48  import org.kuali.rice.krad.util.KRADConstants;
49  import org.kuali.rice.krad.util.ObjectUtils;
50  
51  import java.math.BigDecimal;
52  import java.sql.Date;
53  import java.util.ArrayList;
54  import java.util.Calendar;
55  import java.util.HashMap;
56  import java.util.List;
57  import java.util.Map;
58  
59  public class LeavePayoutServiceImpl implements LeavePayoutService {
60  
61      private LeavePayoutDao leavePayoutDao;
62  
63      @Override
64      public List<LeavePayout> getAllLeavePayoutsForPrincipalId(
65              String principalId) {
66          return leavePayoutDao.getAllLeavePayoutsForPrincipalId(principalId);
67      }
68  
69      @Override
70      public List<LeavePayout> getAllLeavePayoutsForPrincipalIdAsOfDate(
71              String principalId, Date effectiveDate) {
72          return leavePayoutDao.getAllLeavePayoutsForPrincipalIdAsOfDate(principalId,effectiveDate);
73      }
74  
75      @Override
76      public List<LeavePayout> getAllLeavePayoutsByEffectiveDate(
77              Date effectiveDate) {
78          return leavePayoutDao.getAllLeavePayoutsByEffectiveDate(effectiveDate);
79      }
80  
81      @Override
82      public LeavePayout getLeavePayoutById(String lmLeavePayoutId) {
83          return leavePayoutDao.getLeavePayoutById(lmLeavePayoutId);
84      }
85  
86      public LeavePayoutDao getLeavePayoutDao() {
87          return leavePayoutDao;
88      }
89  
90      public void setLeavePayoutDao(LeavePayoutDao leavePayoutDao) {
91          this.leavePayoutDao = leavePayoutDao;
92      }
93  
94  	@Override
95  	public LeavePayout initializePayout(String principalId,
96  			String accrualCategoryRule, BigDecimal accruedBalance,
97  			Date effectiveDate) {
98  		//Initially, principals may be allowed to edit the transfer amount when prompted to submit this balance transfer, however,
99  		//a base transfer amount together with a forfeited amount is calculated to bring the balance back to its limit in accordance
100 		//with transfer limits.
101 		LeavePayout leavePayout = null;
102 		AccrualCategoryRule accrualRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualCategoryRule);
103 
104 		if(ObjectUtils.isNotNull(accrualRule) && ObjectUtils.isNotNull(accruedBalance)) {
105 			leavePayout = new LeavePayout();
106 			//Leave summary is not a requirement, per se, but the information it contains is.
107 			//The only thing that is obtained from leave summary is the accrued balance of the leave summary row matching the
108 			//passed accrualCategoryRules accrual category.
109 			//These two objects are essential to balance transfers triggered when the employee submits their leave calendar for approval.
110 			//Neither of these objects should be null, otherwise this method could not have been called.
111 			AccrualCategory fromAccrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId());
112 			BigDecimal fullTimeEngagement = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate);
113 			
114 			// AccrualRule.maxBalance == null -> no balance limit. No balance limit -> no accrual triggered transfer / payout / lose.
115 			// execution point should not be here if max balance on accrualRule is null, unless there exists an employee override.
116 			BigDecimal maxBalance = accrualRule.getMaxBalance();
117 			BigDecimal adjustedMaxBalance = maxBalance.multiply(fullTimeEngagement).setScale(2);
118 			
119 			BigDecimal maxPayoutAmount = null;
120 			BigDecimal adjustedMaxPayoutAmount = null;
121 			if(ObjectUtils.isNotNull(accrualRule.getMaxPayoutAmount())) {
122 				maxPayoutAmount = new BigDecimal(accrualRule.getMaxPayoutAmount());
123 				adjustedMaxPayoutAmount = maxPayoutAmount.multiply(fullTimeEngagement).setScale(2);
124 			}
125 			else {
126 				// no limit on transfer amount
127 				maxPayoutAmount = new BigDecimal(Long.MAX_VALUE);
128 				adjustedMaxPayoutAmount = maxPayoutAmount;
129 			}
130 
131 			BigDecimal maxCarryOver = null;
132 			BigDecimal adjustedMaxCarryOver = null;
133 			if(ObjectUtils.isNotNull(accrualRule.getMaxCarryOver())) {
134 				maxCarryOver = new BigDecimal(accrualRule.getMaxCarryOver());
135 				adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2);
136 			}
137 			else {
138 				//no limit to carry over.
139 				maxCarryOver = new BigDecimal(Long.MAX_VALUE);
140 				adjustedMaxCarryOver = maxCarryOver;
141 			}
142 			
143 			List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, effectiveDate);
144 			for(EmployeeOverride override : overrides) {
145 				if(StringUtils.equals(override.getAccrualCategory(),fromAccrualCategory.getAccrualCategory())) {
146 					if(StringUtils.equals(override.getOverrideType(),"MB"))
147 						adjustedMaxBalance = new BigDecimal(override.getOverrideValue());
148 					//override values are not pro-rated for FTE.
149 					if(StringUtils.equals(override.getOverrideType(),"MPA"))
150 						adjustedMaxPayoutAmount = new BigDecimal(override.getOverrideValue());
151 					if(StringUtils.equals(override.getOverrideType(),"MAC"))
152 						adjustedMaxCarryOver = new BigDecimal(override.getOverrideValue());
153 				}
154 			}
155 			
156 			
157 			BigDecimal transferAmount = accruedBalance.subtract(adjustedMaxBalance);
158 			if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)) {
159 				//TODO: REMOVE CONDITIONAL
160 				//Move all time in excess of employee's fte adjusted max balance to forfeiture.
161 				leavePayout.setForfeitedAmount(transferAmount);
162 				//There is no transfer to another accrual category.
163 				leavePayout.setPayoutAmount(BigDecimal.ZERO);
164 				// to accrual category is a required field on maintenance document. Set as from accrual category
165 				// to remove validation errors when routing, approving, etc.
166 				leavePayout.setEarnCode(fromAccrualCategory.getEarnCode());
167 			}
168 			else {
169 				// ACTION_AT_MAX_BAL = PAYOUT
170 				leavePayout.setEarnCode(accrualRule.getMaxPayoutEarnCode());
171 				if(transferAmount.compareTo(adjustedMaxPayoutAmount) > 0) {
172 					//there's forfeiture.
173 					//bring transfer amount down to the adjusted maximum transfer amount, and place excess in forfeiture.
174 					//accruedBalance - adjustedMaxPayoutAmount - adjustedMaxBalance = forfeiture.
175 					//transferAmount = accruedBalance - adjustedMaxBalance; forfeiture = transferAmount - adjustedMaxPayoutAmount.
176 					BigDecimal forfeiture = transferAmount.subtract(adjustedMaxPayoutAmount).setScale(2);
177 					forfeiture = forfeiture.stripTrailingZeros();
178 					leavePayout.setForfeitedAmount(forfeiture);
179 					leavePayout.setPayoutAmount(adjustedMaxPayoutAmount);
180 				}
181 				else {
182 					leavePayout.setPayoutAmount(transferAmount);
183 					leavePayout.setForfeitedAmount(BigDecimal.ZERO);
184 				}
185 			}
186 			
187 			// Max Carry Over logic for Year End transfers.
188 			if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
189 				if(ObjectUtils.isNotNull(maxCarryOver)) {
190 					if(ObjectUtils.isNull(adjustedMaxCarryOver))
191 						adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2);
192 					//otherwise, adjustedMaxCarryOver has an employee override value, which trumps accrual rule defined MAC.
193 					//At this point, transfer amount and forfeiture have been set so that the new accrued balance will be the
194 					//adjusted max balance, so this amount is used to check against carry over.
195 					if(adjustedMaxBalance.compareTo(adjustedMaxCarryOver) > 0) {
196 						BigDecimal carryOverDiff = adjustedMaxBalance.subtract(adjustedMaxCarryOver);
197 						
198 						if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)){
199 							//add carry over excess to forfeiture.
200 							leavePayout.setForfeitedAmount(leavePayout.getForfeitedAmount().add(carryOverDiff));
201 						}
202 						else {
203 							//maximize the transfer amount.
204 							BigDecimal potentialPayoutAmount = leavePayout.getPayoutAmount().add(carryOverDiff);
205 	
206 							//Can this amount be added to the transfer amount??
207 							if(potentialPayoutAmount.compareTo(adjustedMaxPayoutAmount) <= 0) {
208 								//yes
209 								leavePayout.setPayoutAmount(leavePayout.getPayoutAmount().add(carryOverDiff));
210 							}
211 							else {
212 								//no
213 								BigDecimal carryOverExcess = potentialPayoutAmount.subtract(adjustedMaxPayoutAmount);
214 								//move excess to forfeiture
215 								leavePayout.setForfeitedAmount(leavePayout.getForfeitedAmount().add(carryOverExcess));
216 								//the remainder (if any) can be added to the transfer amount ( unless action is LOSE ).
217 								leavePayout.setPayoutAmount(leavePayout.getPayoutAmount().add(carryOverDiff.subtract(carryOverExcess)));
218 								assert(leavePayout.getPayoutAmount().compareTo(adjustedMaxPayoutAmount)==0);
219 							}
220 						}
221 					}
222 					//otherwise, given balance will be at or under the max annual carry over.
223 				}
224 			}
225 			
226 			leavePayout.setEffectiveDate(effectiveDate);
227 			leavePayout.setAccrualCategoryRule(accrualCategoryRule);
228 			leavePayout.setFromAccrualCategory(fromAccrualCategory.getAccrualCategory());
229 			leavePayout.setPrincipalId(principalId);
230 		}
231 		return leavePayout;
232 	}
233 
234 	@Override
235 	public LeavePayout payout(LeavePayout leavePayout) {
236 		if(ObjectUtils.isNull(leavePayout))
237 			throw new RuntimeException("did not supply a valid LeavePayout object.");
238 		else {
239 			List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>();
240 			BigDecimal transferAmount = leavePayout.getPayoutAmount();
241 			LeaveBlock aLeaveBlock = null;
242 			
243 			if(ObjectUtils.isNotNull(transferAmount)) {
244 				if(transferAmount.compareTo(BigDecimal.ZERO) > 0) {
245 		
246 					aLeaveBlock = new LeaveBlock();
247 					//Create a leave block that adds the adjusted transfer amount to the "transfer to" accrual category.
248 					aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId());
249 					aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate());
250 					aLeaveBlock.setEarnCode(leavePayout.getEarnCode());
251 					aLeaveBlock.setAccrualCategory(leavePayout.getEarnCodeObj().getAccrualCategory());
252 					aLeaveBlock.setDescription("Amount payed out");
253 					aLeaveBlock.setLeaveAmount(leavePayout.getPayoutAmount());
254 					aLeaveBlock.setAccrualGenerated(true);
255 					aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId());
256 					aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
257 					aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED);
258 					aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId());
259 					aLeaveBlock.setBlockId(0L);
260 
261 					//Want to store the newly created leave block id on this maintainable object
262 					//when the status of the maintenance document encapsulating this maintainable changes
263 					//the id will be used to fetch and update the leave block statuses.
264 			    	aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock);
265 
266 			    	leavePayout.setPayoutLeaveBlockId(aLeaveBlock.getLmLeaveBlockId());
267 			        // save history
268 			        LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock);
269 			        lbh.setAction(LMConstants.ACTION.ADD);
270 			        TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh);
271 					leaveBlocks.add(aLeaveBlock);
272 					
273 					//Create leave block that removes the correct transfer amount from the originating accrual category.
274 					aLeaveBlock = new LeaveBlock();
275 					aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId());
276 					aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate());
277 					aLeaveBlock.setEarnCode(leavePayout.getFromAccrualCategoryObj().getEarnCode());
278 					aLeaveBlock.setAccrualCategory(leavePayout.getFromAccrualCategory());
279 					aLeaveBlock.setDescription("Payout amount");
280 					aLeaveBlock.setLeaveAmount(leavePayout.getPayoutAmount().negate());
281 					aLeaveBlock.setAccrualGenerated(true);
282 					aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId());
283 					aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
284 					aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED);
285 					aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId());
286 					aLeaveBlock.setBlockId(0L);
287 					
288 					//Want to store the newly created leave block id on this maintainable object.
289 					//when the status of the maintenance document encapsulating this maintainable changes
290 					//the id will be used to fetch and update the leave block statuses.
291 			    	aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock);
292 
293 			    	leavePayout.setPayoutFromLeaveBlockId(aLeaveBlock.getLmLeaveBlockId());
294 			        // save history
295 			        lbh = new LeaveBlockHistory(aLeaveBlock);
296 			        lbh.setAction(LMConstants.ACTION.ADD);
297 			        TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh);
298 					
299 					leaveBlocks.add(aLeaveBlock);
300 				}
301 			}
302 			
303 			BigDecimal forfeitedAmount = leavePayout.getForfeitedAmount();
304 			if(ObjectUtils.isNotNull(forfeitedAmount)) {
305 				//Any amount forfeited must come out of the originating accrual category in order to bring balance back to max.
306 				if(forfeitedAmount.compareTo(BigDecimal.ZERO) > 0) {
307 					//for balance transfers with action = lose, transfer amount must be moved to forfeitedAmount
308 					aLeaveBlock = new LeaveBlock();
309 					aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId());
310 					aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate());
311 					aLeaveBlock.setEarnCode(leavePayout.getFromAccrualCategoryObj().getEarnCode());
312 					aLeaveBlock.setAccrualCategory(leavePayout.getFromAccrualCategory());
313 					aLeaveBlock.setDescription("Forfeited payout amount");
314 					aLeaveBlock.setLeaveAmount(forfeitedAmount.negate());
315 					aLeaveBlock.setAccrualGenerated(true);
316 					aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId());
317 					aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
318 					aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED);
319 					aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId());
320 					aLeaveBlock.setBlockId(0L);
321 					
322 					//Want to store the newly created leave block id on this maintainable object
323 					//when the status of the maintenance document encapsulating this maintainable changes
324 					//the id will be used to fetch and update the leave block statuses.
325 			    	aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock);
326 
327 			    	leavePayout.setForfeitedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId());
328 			        // save history
329 			        LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock);
330 			        lbh.setAction(LMConstants.ACTION.ADD);
331 			        TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh);
332 					
333 					leaveBlocks.add(aLeaveBlock);
334 				}
335 			}
336 
337 			return leavePayout;
338 		}
339 	}
340 
341 	@Override
342 	public Map<String,ArrayList<String>> getEligiblePayouts(CalendarEntries calendarEntry,
343 			String principalId) throws Exception {
344 		//Employee override check here, or return base-eligible accrual categories,
345 		//filtering out those that have increased balance limits due to employee override in caller?
346 		//null check inserted to fix LeaveCalendarWebTest failures on kpme-trunk-build-unit #2069	
347 		List<String> eligibleAccrualCategories = new ArrayList<String>();
348 		Map<String, ArrayList<String>> eligibilities = new HashMap<String,ArrayList<String>>();
349 		//TODO: create map for MAX_BAL_ACTION_FREQ in LMConstants
350 		eligibilities.put(LMConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE, new ArrayList<String>());
351 		eligibilities.put(LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END, new ArrayList<String>());
352 		eligibilities.put(LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND, new ArrayList<String>());
353 
354 		PrincipalHRAttributes pha = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, calendarEntry.getEndPeriodDate());
355 		List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveAccrualCategoriesForLeavePlan(pha.getLeavePlan(), calendarEntry.getEndPeriodDate());
356 		
357 		org.kuali.hr.time.calendar.Calendar leaveCalendar = pha.getLeaveCalObj();
358 		CalendarEntries thisLeaveEntry = null;
359 		Interval thisEntryInterval = new Interval(calendarEntry.getBeginPeriodDate().getTime(),calendarEntry.getEndPeriodDate().getTime());
360 		Date asOfDate = TKUtils.getCurrentDate();
361 		if(TKUtils.getCurrentDate().after(DateUtils.addSeconds(calendarEntry.getEndPeriodDate(),-1)))
362 			asOfDate = new Date(DateUtils.addSeconds(calendarEntry.getEndPeriodDate(), -1).getTime());
363 		if(ObjectUtils.isNotNull(leaveCalendar)) {
364 			for(CalendarEntries entry : leaveCalendar.getCalendarEntries()) {
365 				if(thisEntryInterval.contains(DateUtils.addDays(entry.getEndPeriodTime(),-1).getTime()))
366 					thisLeaveEntry = entry;
367 			}
368 		}
369 		// this calendar entry interval does not contain a leave calendar's rollover date.
370 		if(ObjectUtils.isNull(thisLeaveEntry)) {
371 			return eligibilities;
372         }
373 		//TODO: Find the end period date for the corresponding leave calendar.
374 		// must check if this date falls within the interval of the calendar entries begin / end.
375 		// if so, get the leave blocks and calculate the accrued balance.
376 		//LeaveSummary leaveSummary = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId, getCalendarEntry());
377 		if(!accrualCategories.isEmpty()) {
378             LeaveSummary summary = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId, calendarEntry);
379             //null check inserted to fix LeaveCalendarWebTst failures on kpme-trunk-build-unit #2069
380 			for(AccrualCategory accrualCategory : accrualCategories) {
381 				//TODO: Iterate over Accrual Categories within this calendar entry.
382 				AccrualCategoryRule rule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(accrualCategory, DateUtils.addDays(calendarEntry.getEndPeriodDate(),-1), pha.getServiceDate());
383 				//Employee overrides...
384 				if(ObjectUtils.isNotNull(rule)) {
385 					if(StringUtils.equals(rule.getMaxBalFlag(),"Y")) {
386 						if(StringUtils.equals(rule.getActionAtMaxBalance(), LMConstants.ACTION_AT_MAX_BAL.PAYOUT)) {
387 							//There is a disagreement between the constant value LMConstants.MAX_BAL_ACTION_FREQ, and the value being
388 							//set on LM_ACCRUAL_CATEGORY_RULES_T table. Temporarily have changed the constant to reflect the field
389 							//value being set for MAX_BAL_ACTION_FREQ when accrual category rule records are saved.
390 							if(ObjectUtils.isNotNull(rule.getMaxBalanceActionFrequency())) {
391 								BigDecimal maxBalance = rule.getMaxBalance();
392 								
393 								LeaveSummaryRow row = summary.getLeaveSummaryRowForAccrualCategory(accrualCategory.getLmAccrualCategoryId());
394 								BigDecimal accruedBalance = row.getAccruedBalance();
395 /*								for(LeaveBlock leaveBlock : leaveBlockMap.get(accrualCategory.getAccrualCategory())) {
396 									//TODO: limit leave blocks to those created on or after the calendar year period containing this calendar entry.
397 									if(StringUtils.equals(leaveBlock.getRequestStatus(),LMConstants.REQUEST_STATUS.APPROVED))
398 										accruedBalance = accruedBalance.add(leaveBlock.getLeaveAmount());
399 								}*/
400 								BigDecimal fte = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, TKUtils.getCurrentDate());
401 								BigDecimal adjustedMaxBalance = maxBalance.multiply(fte);
402 								BigDecimal maxAnnualCarryOver = null;
403 								if(ObjectUtils.isNotNull(rule.getMaxCarryOver()))
404 									maxAnnualCarryOver = new BigDecimal(rule.getMaxCarryOver());
405 								BigDecimal adjustedMaxAnnualCarryOver = null;
406 								if(ObjectUtils.isNotNull(maxAnnualCarryOver)) {
407 									adjustedMaxAnnualCarryOver = maxAnnualCarryOver.multiply(fte);
408                                 }
409 									
410 								List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, TKUtils.getCurrentDate());
411 								for(EmployeeOverride override : overrides) {
412 									if(StringUtils.equals(override.getAccrualCategory(),accrualCategory.getAccrualCategory())) {
413 										if(StringUtils.equals(override.getOverrideType(),"MB")) {
414 											adjustedMaxBalance = new BigDecimal(override.getOverrideValue());
415                                         }
416 										if(StringUtils.equals(override.getOverrideType(),"MAC")) {
417 											adjustedMaxAnnualCarryOver = new BigDecimal(override.getOverrideValue());
418                                         }
419 										//override values are not pro-rated.
420 									}
421 								}
422 								//should extend a BalanceTransferBase class, or use an algorithm swapping pattern.
423 								//allow institutions to extend/customize/implement their own max_bal_action_frequency types.
424 								if(StringUtils.equals(rule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
425 									//For year end transfer frequencies...
426 									//Should use an "asOfDate" or effective date for principalHRAttributes. If getting eligibilities for a past calendar,
427 									//pha may not be the same.
428 									LeavePlan lp = TkServiceLocator.getLeavePlanService().getLeavePlan(pha.getLeavePlan(),TKUtils.getCurrentDate());
429 									StringBuilder sb = new StringBuilder("");
430 									String calendarYearStart = lp.getCalendarYearStart();
431 									// mm/dd
432 									sb.append(calendarYearStart+"/");
433 									if(lp.getCalendarYearStartMonth().equals("01") && calendarEntry.getBeginPeriodDate().getMonth() == 11) {
434 										//a calendar may start on 01/15, with monthly intervals.
435 										//calendarEntry.beginPeriodDate.year = calendarYearStart.year - 1
436 										sb.append(DateUtils.toCalendar(DateUtils.addYears(calendarEntry.getBeginPeriodDate(),1)).get(Calendar.YEAR));
437 									}
438 									else {
439 										sb.append(DateUtils.toCalendar(calendarEntry.getBeginPeriodDateTime()).get(Calendar.YEAR));
440                                     }
441 									//if the calendar being submitted is the final calendar in the leave plans calendar year.
442 									//must check the calendar year start month. If its the first month of the year, add a year to the date.
443 									//otherwise, the end period date and the calendar year start date have the same year.
444 									if(thisEntryInterval.contains(DateUtils.addDays(TKUtils.formatDateString(sb.toString()),-1).getTime())) {
445 										//BigDecimal accruedBalanceLessPendingTransfers = lsr.getAccruedBalance().add(adjustment);
446 										if(accruedBalance.compareTo(adjustedMaxBalance) > 0 ||
447 												(ObjectUtils.isNotNull(adjustedMaxAnnualCarryOver) &&
448 												accruedBalance.compareTo(adjustedMaxAnnualCarryOver) > 0)) {
449 											eligibleAccrualCategories.add(rule.getLmAccrualCategoryRuleId());
450 											eligibilities.get(LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END).add(rule.getLmAccrualCategoryRuleId());
451 										}
452 									}
453 									//otherwise its not transferable under year end frequency.
454 								}
455 								else {
456 									//BigDecimal accruedBalanceLessPendingTransfers = lsr.getAccruedBalance().add(adjustment);
457 									if(accruedBalance.compareTo(adjustedMaxBalance) > 0 ) {
458 										eligibleAccrualCategories.add(rule.getLmAccrualCategoryRuleId());
459 										eligibilities.get(rule.getMaxBalanceActionFrequency()).add(rule.getLmAccrualCategoryRuleId());
460 									}
461 								}
462 							}
463 						}
464 					}
465 				}
466 			}
467 		}
468 		return eligibilities;
469 	}
470 
471 	@Override
472 	public void submitToWorkflow(LeavePayout leavePayout)
473 			throws WorkflowException {
474 		//leavePayout.setStatus(TkConstants.ROUTE_STATUS.ENROUTE);
475         EntityNamePrincipalName principalName = null;
476         if (leavePayout.getPrincipalId() != null) {
477             principalName = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(leavePayout.getPrincipalId());
478         }
479 
480 		MaintenanceDocument document = KRADServiceLocatorWeb.getMaintenanceDocumentService().setupNewMaintenanceDocument(LeavePayout.class.getName(),
481 				"LeavePayoutDocumentType",KRADConstants.MAINTENANCE_NEW_ACTION);
482 
483         String personName = (principalName != null  && principalName.getDefaultName() != null) ? principalName.getDefaultName().getCompositeName() : StringUtils.EMPTY;
484         String date = TKUtils.formatDate(new java.sql.Date(leavePayout.getEffectiveDate().getTime()));
485         document.getDocumentHeader().setDocumentDescription(personName + " (" + leavePayout.getPrincipalId() + ")  - " + date);
486 		Map<String,String[]> params = new HashMap<String,String[]>();
487 		
488 		KRADServiceLocatorWeb.getMaintenanceDocumentService().setupMaintenanceObject(document, KRADConstants.MAINTENANCE_NEW_ACTION, params);
489 		LeavePayout lpObj = (LeavePayout) document.getNewMaintainableObject().getDataObject();
490 		
491 		lpObj.setAccrualCategoryRule(leavePayout.getAccrualCategoryRule());
492 		lpObj.setEffectiveDate(leavePayout.getEffectiveDate());
493 		lpObj.setForfeitedAmount(leavePayout.getForfeitedAmount());
494 		lpObj.setFromAccrualCategory(leavePayout.getFromAccrualCategory());
495 		lpObj.setPrincipalId(leavePayout.getPrincipalId());
496 		lpObj.setEarnCode(leavePayout.getEarnCode());
497 		lpObj.setPayoutAmount(leavePayout.getPayoutAmount());
498 		lpObj.setDocumentHeaderId(document.getDocumentHeader().getWorkflowDocument().getDocumentId());
499 		
500 		document.getNewMaintainableObject().setDataObject(lpObj);
501 		KRADServiceLocatorWeb.getDocumentService().saveDocument(document);
502 		document.getDocumentHeader().getWorkflowDocument().saveDocument("");
503 
504 		document.getDocumentHeader().getWorkflowDocument().route("");
505 	}
506 
507 	@Override
508 	public List<LeavePayout> getLeavePayouts(String viewPrincipal,
509 			Date beginPeriodDate, Date endPeriodDate) {
510 		// TODO Auto-generated method stub
511 		return leavePayoutDao.getLeavePayouts(viewPrincipal, beginPeriodDate, endPeriodDate);
512 	}
513 
514 	@Override
515 	public void saveOrUpdate(LeavePayout payout) {
516 		leavePayoutDao.saveOrUpdate(payout);
517 	}
518 }