View Javadoc

1   /**
2    * Copyright 2004-2014 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  /**
17    * Copyright 2004-2013 The Kuali Foundation
18   *
19   * Licensed under the Educational Community License, Version 2.0 (the "License");
20   * you may not use this file except in compliance with the License.
21   * You may obtain a copy of the License at
22   *
23   * http://www.opensource.org/licenses/ecl2.php
24   *
25   * Unless required by applicable law or agreed to in writing, software
26   * distributed under the License is distributed on an "AS IS" BASIS,
27   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28   * See the License for the specific language governing permissions and
29   * limitations under the License.
30   */
31  package org.kuali.kpme.tklm.leave.payout.web;
32  
33  import java.math.BigDecimal;
34  import java.util.Collections;
35  import java.util.Comparator;
36  import java.util.List;
37  
38  import javax.servlet.http.HttpServletRequest;
39  import javax.servlet.http.HttpServletResponse;
40  
41  import org.apache.commons.lang.StringUtils;
42  import org.apache.log4j.Logger;
43  import org.apache.struts.action.ActionForm;
44  import org.apache.struts.action.ActionForward;
45  import org.apache.struts.action.ActionMapping;
46  import org.apache.struts.action.ActionRedirect;
47  import org.joda.time.LocalDate;
48  import org.kuali.kpme.core.accrualcategory.AccrualCategory;
49  import org.kuali.kpme.core.accrualcategory.rule.AccrualCategoryRule;
50  import org.kuali.kpme.core.calendar.entry.CalendarEntry;
51  import org.kuali.kpme.core.service.HrServiceLocator;
52  import org.kuali.kpme.core.util.HrConstants;
53  import org.kuali.kpme.core.web.KPMEAction;
54  import org.kuali.kpme.tklm.leave.block.LeaveBlock;
55  import org.kuali.kpme.tklm.leave.calendar.LeaveCalendarDocument;
56  import org.kuali.kpme.tklm.leave.payout.LeavePayout;
57  import org.kuali.kpme.tklm.leave.payout.validation.LeavePayoutValidationUtils;
58  import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
59  import org.kuali.kpme.tklm.leave.workflow.LeaveCalendarDocumentHeader;
60  import org.kuali.kpme.tklm.time.service.TkServiceLocator;
61  import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
62  import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
63  import org.kuali.rice.krad.service.KRADServiceLocator;
64  import org.kuali.rice.krad.util.GlobalVariables;
65  import org.kuali.rice.krad.util.KRADConstants;
66  import org.kuali.rice.krad.util.ObjectUtils;
67  
68  public class LeavePayoutAction extends KPMEAction {
69  	
70  	private static final Logger LOG = Logger.getLogger(LeavePayoutAction.class);
71  
72  	public ActionForward leavePayoutOnLeaveApproval(ActionMapping mapping, ActionForm form,
73  			HttpServletRequest request, HttpServletResponse response) throws Exception {
74  
75  		//if action was submit, execute the payout
76  		LeavePayoutForm lpf = (LeavePayoutForm) form;
77  		LeavePayout leavePayout = lpf.getLeavePayout();
78  	
79  		boolean valid = LeavePayoutValidationUtils.validatePayout(leavePayout);
80  		
81  		//if payout amount has changed, and the resulting change produces forfeiture
82  		//or changes the forfeiture amount, prompt for confirmation with the amount of
83  		//forfeiture that the entered amount would produce.
84  
85  		if(valid) {
86  			
87  			String accrualRuleId = leavePayout.getAccrualCategoryRule();
88  			
89  			String documentId = leavePayout.getLeaveCalendarDocumentId();
90  			TimesheetDocumentHeader tsdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(documentId);
91  			LeaveCalendarDocumentHeader lcdh = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(documentId);
92  			CalendarEntry calendarEntry = null;
93  			String strutsActionForward = "";
94  			String methodToCall = "approveLeaveCalendar";
95  			if(ObjectUtils.isNull(tsdh) && ObjectUtils.isNull(lcdh)) {
96  				LOG.error("No document found");
97  //				throw new RuntimeException("No document found");
98  				GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "error.document.notfound");
99  				mapping.findForward("basic");
100 			}
101 			else if(ObjectUtils.isNotNull(tsdh)) {
102 				//Throws runtime exception, separate action forwards for timesheet/leave calendar payouts.
103 				TimesheetDocument tsd = TkServiceLocator.getTimesheetService().getTimesheetDocument(documentId);
104 				calendarEntry = tsd == null ? null : tsd.getCalendarEntry();
105 				strutsActionForward = "timesheetPayoutSuccess";
106 				methodToCall = "approveTimesheet";
107 			}
108 			else {
109 				LeaveCalendarDocument lcd = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(documentId);
110 				calendarEntry = lcd == null ? null : lcd.getCalendarEntry();
111 				strutsActionForward = "leaveCalendarPayoutSuccess";
112 				methodToCall = "approveLeaveCalendar";
113 			}
114 			
115 			if(ObjectUtils.isNull(calendarEntry)) {
116 				LOG.error("Could not retreive calendar entry for document " + documentId);
117 //				throw new RuntimeException("Could not retreive calendar entry for document " + documentId);
118 				GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "error.calendarentry.notfound", new String[] {documentId});
119 				return mapping.findForward("basic");
120 			}
121 			
122 			AccrualCategoryRule accrualRule = HrServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualRuleId);
123 			AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId());
124 			BigDecimal accruedBalance = LmServiceLocator.getAccrualService().getAccruedBalanceForPrincipal(leavePayout.getPrincipalId(), accrualCategory, leavePayout.getEffectiveLocalDate());
125 
126 			LeavePayout defaultBT = LmServiceLocator.getLeavePayoutService().initializePayout(leavePayout.getPrincipalId(), accrualRuleId, accruedBalance, leavePayout.getEffectiveLocalDate());
127 			if(leavePayout.getPayoutAmount().compareTo(defaultBT.getPayoutAmount()) != 0) {
128 				//employee changed the payout amount, recalculate forfeiture.
129 				//Note: payout form has been validated.
130 				leavePayout = defaultBT.adjust(leavePayout.getPayoutAmount());
131 				// showing the adjusted balance payout via the execution of another forward
132 				// would cause a loop that would break only if the original payout amount was re-established in the form.
133 				// javascript must be written if the forfeited amount is to be updated on the form object.
134 				// an alternative to javascript would be to render a "re-calculate" button attached to a dedicated action forward method.
135 				// must re-set leaveCalendarDocumentId, as leavePayout is now just an adjustment of the default initialized BT with no leave calendar doc id.
136 				leavePayout.setLeaveCalendarDocumentId(documentId);
137 			}
138 
139 			LmServiceLocator.getLeavePayoutService().submitToWorkflow(leavePayout);
140 			
141 			if(ObjectUtils.isNotNull(documentId)) {
142 				if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),HrConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE) ||
143 						StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(), HrConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
144 					
145 					ActionForward forward = new ActionForward(mapping.findForward(strutsActionForward));
146 					forward.setPath(forward.getPath()+"?documentId="+documentId+"&action=R&methodToCall="+methodToCall);
147 					return forward;
148 				}
149 				else  
150 					return mapping.findForward("closeLeavePayoutDoc");
151 			}
152 			else
153 				return mapping.findForward("closeLeavePayoutDoc");
154 		}
155 		else //show user errors.
156 			return mapping.findForward("basic");
157 	}
158 	
159 	public ActionForward cancel(ActionMapping mapping, ActionForm form,
160 			HttpServletRequest request, HttpServletResponse response)
161 			throws Exception {
162 		
163 		LeavePayoutForm lpf = (LeavePayoutForm) form;
164 		LeavePayout leavePayout = lpf.getLeavePayout();
165 		String accrualCategoryRuleId = leavePayout.getAccrualCategoryRule();
166 		AccrualCategoryRule accrualRule = HrServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualCategoryRuleId);
167 		String actionFrequency = accrualRule.getMaxBalanceActionFrequency();
168 		
169 		if(StringUtils.equals(actionFrequency,HrConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND))
170 			return mapping.findForward("closeLeavePayoutDoc");
171 		else 
172 			if(StringUtils.equals(actionFrequency, HrConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE) ||
173 					StringUtils.equals(actionFrequency, HrConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
174 				
175 				String documentId = leavePayout.getLeaveCalendarDocumentId();
176 				TimesheetDocumentHeader tsdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(documentId);
177 				LeaveCalendarDocumentHeader lcdh = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(documentId);
178 				String strutsActionForward = "";
179 				if(ObjectUtils.isNull(tsdh) && ObjectUtils.isNull(lcdh)) {
180 					strutsActionForward = "/";
181 				}
182 				else if(ObjectUtils.isNotNull(tsdh)) {
183 					//Throws runtime exception, separate action forwards for timesheet/leave calendar transfers.
184 					strutsActionForward = mapping.findForward("timesheetCancel").getPath() + "?documentId=" + leavePayout.getLeaveCalendarDocumentId();
185 				}
186 				else {
187 					strutsActionForward = mapping.findForward("leaveCalendarCancel").getPath() + "?documentId=" + leavePayout.getLeaveCalendarDocumentId();
188 				}
189 
190 				ActionRedirect redirect = new ActionRedirect();
191 				redirect.setPath(strutsActionForward);
192 				return redirect;
193 
194 			}
195 			else {
196 				LOG.warn("Action should only be reachable through triggers with frequency ON_DEMAND or LEAVE_APPROVE");
197 				GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "action.reachable.through.triggers");
198 //				throw new RuntimeException("Action should only be reachable through triggers with frequency ON_DEMAND or LEAVE_APPROVE");
199 				return mapping.findForward("basic");
200 			}
201 	}
202 	
203 	//Entry point for LeavePayout.do for accrual category rule triggered payouts with action frequency On Demand.
204 	//May be better suited in the LeaveCalendarAction class.
205 	public ActionForward leavePayoutOnDemand(ActionMapping mapping, ActionForm form,
206 			HttpServletRequest request, HttpServletResponse response)
207 			throws Exception {
208 		GlobalVariables.getMessageMap().putWarning("document.payoutAmount","leavePayout.payoutAmount.adjust");
209 
210 		LeavePayoutForm lpf = (LeavePayoutForm) form;
211 		//the leave calendar document that triggered this balance payout.
212 		String documentId = request.getParameter("documentId");
213 		String leaveBlockId = request.getParameter("accrualRuleId");
214 		String timesheet = request.getParameter("timesheet");
215 
216 		boolean isTimesheet = false;
217 		if(StringUtils.equals(timesheet, "true")) {
218 			lpf.isTimesheet(true);
219 			isTimesheet = true;
220 		}
221 		if(ObjectUtils.isNotNull(leaveBlockId)) {
222 			AccrualCategoryRule aRule = HrServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(leaveBlockId);
223 			if(ObjectUtils.isNotNull(aRule)) {
224 				//should somewhat safegaurd against url fabrication.
225 				if(!StringUtils.equals(aRule.getMaxBalanceActionFrequency(),HrConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND)) {
226 //					throw new RuntimeException("attempted to execute on-demand balance payout for accrual category with action frequency " + aRule.getMaxBalanceActionFrequency());
227 					LOG.error("attempted to execute on-demand balance payout for accrual category with action frequency " + aRule.getMaxBalanceActionFrequency());
228 					GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "attempted.payout.accrualcategory",new String[] { aRule.getMaxBalanceActionFrequency()});
229 					return mapping.findForward("basic");
230 				} else {
231 					TimesheetDocument tsd = null;
232 					LeaveCalendarDocument lcd = null;
233 					String principalId = null;
234 					CalendarEntry calendarEntry = null;
235 
236 					if(isTimesheet) {
237 						tsd = TkServiceLocator.getTimesheetService().getTimesheetDocument(documentId);
238 						principalId = tsd == null ? null : tsd.getPrincipalId();
239 					}
240 					else {
241 						lcd = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(documentId);
242 						principalId = lcd == null ? null : lcd.getPrincipalId();
243 					}
244 					
245 					AccrualCategoryRule accrualRule = HrServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(leaveBlockId);
246 					AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId());
247 					BigDecimal accruedBalance = LmServiceLocator.getAccrualService().getAccruedBalanceForPrincipal(principalId, accrualCategory, LocalDate.now());
248 
249 					LeavePayout leavePayout = LmServiceLocator.getLeavePayoutService().initializePayout(principalId, leaveBlockId, accruedBalance, LocalDate.now());
250 					leavePayout.setLeaveCalendarDocumentId(documentId);
251 					if(ObjectUtils.isNotNull(leavePayout)) {
252 						if(StringUtils.equals(aRule.getActionAtMaxBalance(),HrConstants.ACTION_AT_MAX_BALANCE.LOSE)) {	
253 							// this particular combination of action / action frequency does not particularly make sense
254 							// unless for some reason users still need to be prompted to submit the loss.
255 							// For now, we treat as though it is a valid use-case.
256 							//LmServiceLocator.getLeavePayoutService().submitToWorkflow(leavePayout);
257 							// May need to update to save the business object to KPME's tables for record keeping.
258 							leavePayout = LmServiceLocator.getLeavePayoutService().payout(leavePayout);
259 							// May need to update to save the business object to KPME's tables for record keeping.
260 							LeaveBlock forfeitedLeaveBlock = LmServiceLocator.getLeaveBlockService().getLeaveBlock(leavePayout.getForfeitedLeaveBlockId());
261 							forfeitedLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.APPROVED);
262 							LmServiceLocator.getLeaveBlockService().updateLeaveBlock(forfeitedLeaveBlock, principalId);
263 							return mapping.findForward("closeLeavePayoutDoc");
264 						}
265 						else {
266 							ActionForward forward = mapping.findForward("basic");
267 							lpf.setLeaveCalendarDocumentId(documentId);
268 							lpf.setLeavePayout(leavePayout);
269 							lpf.setPayoutAmount(leavePayout.getPayoutAmount());
270 							return forward;
271 						}
272 					}
273 					else {
274 //						throw new RuntimeException("could not initialize a balance payout");
275 						LOG.error("could not initialize a balance payout");
276 						GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "couldnot.initialize.payout");
277 						return mapping.findForward("basic");
278 					}
279 				}
280 			}
281 			else {
282 				LOG.error("No rule for this accrual category could be found");
283 //				throw new RuntimeException("No rule for this accrual category could be found");
284 				GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "no.acccatrule.found");
285 				return mapping.findForward("basic");
286 			}
287 		}
288 		else { 
289 			LOG.error("No accrual category rule id has been sent in the request.");
290 			GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "no.acccat.ruleid.sent");
291 			return mapping.findForward("basic");
292 //			throw new RuntimeException("No accrual category rule id has been sent in the request.");
293 		}
294 	}
295 
296 	//Entry point for LeavePayout.do for accrual category rule triggered transfers with action frequency Leave Approve.
297 	//TODO: Rename method to differentiate from ActionForward with same name in LeaveCalendarSubmit.
298 	public ActionForward approveLeaveCalendar(ActionMapping mapping, ActionForm form,
299 			HttpServletRequest request, HttpServletResponse response)
300 					throws Exception {
301 		
302 		GlobalVariables.getMessageMap().putWarning("document.newMaintainableObj.transferAmount","leavePayout.transferAmount.adjust");
303 		LeavePayoutForm btf = (LeavePayoutForm) form;
304 
305 		List<LeaveBlock> eligiblePayouts = (List<LeaveBlock>) request.getSession().getAttribute("eligibilities");
306 		if(!eligiblePayouts.isEmpty()) {
307 			
308 			Collections.sort(eligiblePayouts, new Comparator() {
309 
310                 @Override
311                 public int compare(Object o1, Object o2) {
312                     LeaveBlock l1 = (LeaveBlock) o1;
313                     LeaveBlock l2 = (LeaveBlock) o2;
314                     return l1.getLeaveDate().compareTo(l2.getLeaveDate());
315                 }
316 
317             });
318 			
319 			//This is the leave calendar document that triggered this balance transfer.
320 
321 			String leaveCalendarDocumentId = request.getParameter("documentId");
322 			ActionForward forward = new ActionForward(mapping.findForward("basic"));
323 			LeaveCalendarDocument lcd = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(leaveCalendarDocumentId);
324 			
325 			String principalId = lcd == null ? null : lcd.getPrincipalId();
326 			LeaveBlock leaveBlock = eligiblePayouts.get(0);
327 			LocalDate effectiveDate = leaveBlock.getLeaveLocalDate();
328             AccrualCategoryRule accrualRule = leaveBlock.getAccrualCategoryRule();
329 			if(accrualRule != null) {
330 				AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId());
331 				BigDecimal accruedBalance = LmServiceLocator.getAccrualService().getAccruedBalanceForPrincipal(principalId, accrualCategory, effectiveDate);
332 			
333 				LeavePayout leavePayout = LmServiceLocator.getLeavePayoutService().initializePayout(principalId, accrualRule.getLmAccrualCategoryRuleId(), accruedBalance, effectiveDate);
334 				leavePayout.setLeaveCalendarDocumentId(leaveCalendarDocumentId);
335 	
336 				if(ObjectUtils.isNotNull(leavePayout)) { 
337 	
338 				if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),HrConstants.ACTION_AT_MAX_BALANCE.LOSE)) {
339 					//payouts should never contain losses.
340 					//losses are treated as a special case of transfer
341 					//LmServiceLocator.getLeavePayoutService().submitToWorkflow(leavePayout);
342 					leavePayout = LmServiceLocator.getLeavePayoutService().payout(leavePayout);
343 					// May need to update to save the business object to KPME's tables for record keeping.
344 					LeaveBlock forfeitedLeaveBlock = LmServiceLocator.getLeaveBlockService().getLeaveBlock(leavePayout.getForfeitedLeaveBlockId());
345 					KRADServiceLocator.getBusinessObjectService().save(leavePayout);
346 					forfeitedLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.APPROVED);
347 					LmServiceLocator.getLeaveBlockService().updateLeaveBlock(forfeitedLeaveBlock, principalId);
348 					
349 					if(ObjectUtils.isNotNull(leaveCalendarDocumentId)) {
350 						if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),HrConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE) ||
351 								StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(), HrConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
352 							ActionForward loseForward = new ActionForward(mapping.findForward("leaveCalendarPayoutSuccess"));
353 							loseForward.setPath(loseForward.getPath()+"?documentId="+leaveCalendarDocumentId+"&action=R&methodToCall=approveLeaveCalendar");
354 							return loseForward;
355 						}
356 						//on demand handled in separate action forward.
357 					}
358 
359 				} else {
360 					btf.setLeaveCalendarDocumentId(leaveCalendarDocumentId);
361 					btf.setLeavePayout(leavePayout);
362 					btf.setPayoutAmount(leavePayout.getPayoutAmount());
363 					return forward;
364 				}
365 
366 			}  else {
367 				LOG.error("could not initialize balance transfer.");
368 				GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "couldnot.initialize.baltransfer");
369 				return mapping.findForward("basic");
370 	
371 //			throw new RuntimeException("could not initialize balance transfer");
372 			}
373 
374 		}
375 			else { 
376 				LOG.error("unable to fetch the accrual category that triggerred this transfer");
377 				GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "unable.fetch.acccat");
378 				return mapping.findForward("basic");
379 //				throw new RuntimeException("unable to fetch the accrual category that triggerred this transfer");
380 			}
381 		}
382 		else {
383 			LOG.error("No infractions given");
384 			GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "no.infractions.given");
385 			return mapping.findForward("basic");
386 //			throw new RuntimeException("No infractions given");
387 		}
388 		return mapping.findForward("basic");
389 	}
390 	
391 	public ActionForward approveTimesheet(ActionMapping mapping, ActionForm form,
392 			HttpServletRequest request, HttpServletResponse response)
393 					throws Exception {
394 		
395 		GlobalVariables.getMessageMap().putWarning("document.newMaintainableObj.transferAmount","leavePayout.transferAmount.adjust");
396 		LeavePayoutForm btf = (LeavePayoutForm) form;
397 
398 		List<LeaveBlock> eligiblePayouts = (List<LeaveBlock>) request.getSession().getAttribute("eligibilities");
399 		if(!eligiblePayouts.isEmpty()) {
400 			
401 			Collections.sort(eligiblePayouts, new Comparator() {
402 				
403 				@Override
404 				public int compare(Object o1, Object o2) {
405 					LeaveBlock l1 = (LeaveBlock) o1;
406 					LeaveBlock l2 = (LeaveBlock) o2;
407 					return l1.getLeaveDate().compareTo(l2.getLeaveDate());
408 				}
409 				
410 			});
411 			
412 			//This is the leave calendar document that triggered this balance transfer.
413 
414 			String timesheetDocumentId = request.getParameter("documentId");
415 			ActionForward forward = new ActionForward(mapping.findForward("basic"));
416 			TimesheetDocument tsd = TkServiceLocator.getTimesheetService().getTimesheetDocument(timesheetDocumentId);
417 			String principalId = tsd == null ? null : tsd.getPrincipalId();
418 			
419 			LeaveBlock leaveBlock = eligiblePayouts.get(0);
420 			LocalDate effectiveDate = leaveBlock.getLeaveLocalDate();
421 			String accrualCategoryRuleId = leaveBlock.getAccrualCategoryRuleId();
422             AccrualCategoryRule accrualRule = leaveBlock.getAccrualCategoryRule();
423 			if(accrualRule != null) {
424 				AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId());
425 				BigDecimal accruedBalance = LmServiceLocator.getAccrualService().getAccruedBalanceForPrincipal(principalId, accrualCategory, effectiveDate);
426 
427 				LeavePayout leavePayout = LmServiceLocator.getLeavePayoutService().initializePayout(principalId, accrualCategoryRuleId, accruedBalance, effectiveDate);
428 				leavePayout.setLeaveCalendarDocumentId(timesheetDocumentId);
429 	
430 				if(ObjectUtils.isNotNull(leavePayout)) {
431 	
432 					if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),HrConstants.ACTION_AT_MAX_BALANCE.LOSE)) {
433 						// TODO: Redirect user to prompt stating excess leave will be forfeited and ask for confirmation.
434 						// Do not submit the object to workflow for this max balance action.
435 						leavePayout = LmServiceLocator.getLeavePayoutService().payout(leavePayout);
436 						KRADServiceLocator.getBusinessObjectService().save(leavePayout);
437 	
438 						// May need to update to save the business object to KPME's tables for record keeping.
439 						LeaveBlock forfeitedLeaveBlock = LmServiceLocator.getLeaveBlockService().getLeaveBlock(leavePayout.getForfeitedLeaveBlockId());
440 						forfeitedLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.APPROVED);
441 						LmServiceLocator.getLeaveBlockService().updateLeaveBlock(forfeitedLeaveBlock, principalId);
442 
443 						if(ObjectUtils.isNotNull(timesheetDocumentId)) {
444 							if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),HrConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE) ||
445 									StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(), HrConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
446 								ActionForward loseForward = new ActionForward(mapping.findForward("timesheetPayoutSuccess"));
447 								loseForward.setPath(loseForward.getPath()+"?documentId="+timesheetDocumentId+"&action=R&methodToCall=approveTimesheet");
448 								return loseForward;
449 							}
450 							//on demand handled in separate action forward.
451 						}
452 	
453 					} else {
454 						btf.setLeaveCalendarDocumentId(timesheetDocumentId);
455 						btf.setLeavePayout(leavePayout);
456 						btf.setPayoutAmount(leavePayout.getPayoutAmount());
457 						return forward;
458 					}
459 	
460 				}  
461 //				throw new RuntimeException("could not initialize balance transfer");
462 				LOG.error("could not initialize balance transfer");
463 				GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "couldnot.initialize.baltransfer");
464 				return mapping.findForward("basic");
465 
466 		}
467 		else {
468 			LOG.error("unable to fetch the accrual category that triggerred this transfer");
469 			GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "unable.fetch.acccat");
470 			return mapping.findForward("basic");
471 			
472 //			throw new RuntimeException("unable to fetch the accrual category that triggerred this transfer");
473 		}
474 		}
475 		else {
476 			LOG.error("no eligible transfers exist");
477 			GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "error.eligible.transfer.notExist");
478 			return mapping.findForward("basic");
479 		
480 //			throw new RuntimeException("no eligible transfers exist");
481 		}
482 	}
483 	
484 	public ActionForward closeLeavePayoutDoc(ActionMapping mapping, ActionForm form,
485 			HttpServletRequest request, HttpServletResponse response)
486 			throws Exception {
487 		return mapping.findForward("closeLeavePayoutDoc");
488 	}
489 }