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    /**
017      * Copyright 2004-2013 The Kuali Foundation
018     *
019     * Licensed under the Educational Community License, Version 2.0 (the "License");
020     * you may not use this file except in compliance with the License.
021     * You may obtain a copy of the License at
022     *
023     * http://www.opensource.org/licenses/ecl2.php
024     *
025     * Unless required by applicable law or agreed to in writing, software
026     * distributed under the License is distributed on an "AS IS" BASIS,
027     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
028     * See the License for the specific language governing permissions and
029     * limitations under the License.
030     */
031    package org.kuali.kpme.tklm.leave.payout.web;
032    
033    import java.math.BigDecimal;
034    import java.util.Collections;
035    import java.util.Comparator;
036    import java.util.List;
037    
038    import javax.servlet.http.HttpServletRequest;
039    import javax.servlet.http.HttpServletResponse;
040    
041    import org.apache.commons.lang.StringUtils;
042    import org.apache.log4j.Logger;
043    import org.apache.struts.action.ActionForm;
044    import org.apache.struts.action.ActionForward;
045    import org.apache.struts.action.ActionMapping;
046    import org.apache.struts.action.ActionRedirect;
047    import org.joda.time.LocalDate;
048    import org.kuali.kpme.core.accrualcategory.AccrualCategory;
049    import org.kuali.kpme.core.accrualcategory.rule.AccrualCategoryRule;
050    import org.kuali.kpme.core.calendar.entry.CalendarEntry;
051    import org.kuali.kpme.core.service.HrServiceLocator;
052    import org.kuali.kpme.core.util.HrConstants;
053    import org.kuali.kpme.core.web.KPMEAction;
054    import org.kuali.kpme.tklm.leave.block.LeaveBlock;
055    import org.kuali.kpme.tklm.leave.calendar.LeaveCalendarDocument;
056    import org.kuali.kpme.tklm.leave.payout.LeavePayout;
057    import org.kuali.kpme.tklm.leave.payout.validation.LeavePayoutValidationUtils;
058    import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
059    import org.kuali.kpme.tklm.leave.workflow.LeaveCalendarDocumentHeader;
060    import org.kuali.kpme.tklm.time.service.TkServiceLocator;
061    import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
062    import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
063    import org.kuali.rice.krad.service.KRADServiceLocator;
064    import org.kuali.rice.krad.util.GlobalVariables;
065    import org.kuali.rice.krad.util.KRADConstants;
066    import org.kuali.rice.krad.util.ObjectUtils;
067    
068    public class LeavePayoutAction extends KPMEAction {
069            
070            private static final Logger LOG = Logger.getLogger(LeavePayoutAction.class);
071    
072            public ActionForward leavePayoutOnLeaveApproval(ActionMapping mapping, ActionForm form,
073                            HttpServletRequest request, HttpServletResponse response) throws Exception {
074    
075                    //if action was submit, execute the payout
076                    LeavePayoutForm lpf = (LeavePayoutForm) form;
077                    LeavePayout leavePayout = lpf.getLeavePayout();
078            
079                    boolean valid = LeavePayoutValidationUtils.validatePayout(leavePayout);
080                    
081                    //if payout amount has changed, and the resulting change produces forfeiture
082                    //or changes the forfeiture amount, prompt for confirmation with the amount of
083                    //forfeiture that the entered amount would produce.
084    
085                    if(valid) {
086                            
087                            String accrualRuleId = leavePayout.getAccrualCategoryRule();
088                            
089                            String documentId = leavePayout.getLeaveCalendarDocumentId();
090                            TimesheetDocumentHeader tsdh = TkServiceLocator.getTimesheetDocumentHeaderService().getDocumentHeader(documentId);
091                            LeaveCalendarDocumentHeader lcdh = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(documentId);
092                            CalendarEntry calendarEntry = null;
093                            String strutsActionForward = "";
094                            String methodToCall = "approveLeaveCalendar";
095                            if(ObjectUtils.isNull(tsdh) && ObjectUtils.isNull(lcdh)) {
096                                    LOG.error("No document found");
097    //                              throw new RuntimeException("No document found");
098                                    GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "error.document.notfound");
099                                    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                            String accrualCategoryRuleId = leaveBlock.getAccrualCategoryRuleId();
329                            if(!StringUtils.isBlank(accrualCategoryRuleId)) {
330                                    AccrualCategoryRule accrualRule = HrServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualCategoryRuleId);
331                                    AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId());
332                                    BigDecimal accruedBalance = LmServiceLocator.getAccrualService().getAccruedBalanceForPrincipal(principalId, accrualCategory, effectiveDate);
333                            
334                                    LeavePayout leavePayout = LmServiceLocator.getLeavePayoutService().initializePayout(principalId, accrualCategoryRuleId, accruedBalance, effectiveDate);
335                                    leavePayout.setLeaveCalendarDocumentId(leaveCalendarDocumentId);
336            
337                                    if(ObjectUtils.isNotNull(leavePayout)) { 
338            
339                                    if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),HrConstants.ACTION_AT_MAX_BALANCE.LOSE)) {
340                                            //payouts should never contain losses.
341                                            //losses are treated as a special case of transfer
342                                            //LmServiceLocator.getLeavePayoutService().submitToWorkflow(leavePayout);
343                                            leavePayout = LmServiceLocator.getLeavePayoutService().payout(leavePayout);
344                                            // May need to update to save the business object to KPME's tables for record keeping.
345                                            LeaveBlock forfeitedLeaveBlock = LmServiceLocator.getLeaveBlockService().getLeaveBlock(leavePayout.getForfeitedLeaveBlockId());
346                                            KRADServiceLocator.getBusinessObjectService().save(leavePayout);
347                                            forfeitedLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.APPROVED);
348                                            LmServiceLocator.getLeaveBlockService().updateLeaveBlock(forfeitedLeaveBlock, principalId);
349                                            
350                                            if(ObjectUtils.isNotNull(leaveCalendarDocumentId)) {
351                                                    if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),HrConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE) ||
352                                                                    StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(), HrConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
353                                                            ActionForward loseForward = new ActionForward(mapping.findForward("leaveCalendarPayoutSuccess"));
354                                                            loseForward.setPath(loseForward.getPath()+"?documentId="+leaveCalendarDocumentId+"&action=R&methodToCall=approveLeaveCalendar");
355                                                            return loseForward;
356                                                    }
357                                                    //on demand handled in separate action forward.
358                                            }
359    
360                                    } else {
361                                            btf.setLeaveCalendarDocumentId(leaveCalendarDocumentId);
362                                            btf.setLeavePayout(leavePayout);
363                                            btf.setPayoutAmount(leavePayout.getPayoutAmount());
364                                            return forward;
365                                    }
366    
367                            }  else {
368                                    LOG.error("could not initialize balance transfer.");
369                                    GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "couldnot.initialize.baltransfer");
370                                    return mapping.findForward("basic");
371            
372    //                      throw new RuntimeException("could not initialize balance transfer");
373                            }
374    
375                    }
376                            else { 
377                                    LOG.error("unable to fetch the accrual category that triggerred this transfer");
378                                    GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "unable.fetch.acccat");
379                                    return mapping.findForward("basic");
380    //                              throw new RuntimeException("unable to fetch the accrual category that triggerred this transfer");
381                            }
382                    }
383                    else {
384                            LOG.error("No infractions given");
385                            GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "no.infractions.given");
386                            return mapping.findForward("basic");
387    //                      throw new RuntimeException("No infractions given");
388                    }
389                    return mapping.findForward("basic");
390            }
391            
392            public ActionForward approveTimesheet(ActionMapping mapping, ActionForm form,
393                            HttpServletRequest request, HttpServletResponse response)
394                                            throws Exception {
395                    
396                    GlobalVariables.getMessageMap().putWarning("document.newMaintainableObj.transferAmount","leavePayout.transferAmount.adjust");
397                    LeavePayoutForm btf = (LeavePayoutForm) form;
398    
399                    List<LeaveBlock> eligiblePayouts = (List<LeaveBlock>) request.getSession().getAttribute("eligibilities");
400                    if(!eligiblePayouts.isEmpty()) {
401                            
402                            Collections.sort(eligiblePayouts, new Comparator() {
403                                    
404                                    @Override
405                                    public int compare(Object o1, Object o2) {
406                                            LeaveBlock l1 = (LeaveBlock) o1;
407                                            LeaveBlock l2 = (LeaveBlock) o2;
408                                            return l1.getLeaveDate().compareTo(l2.getLeaveDate());
409                                    }
410                                    
411                            });
412                            
413                            //This is the leave calendar document that triggered this balance transfer.
414    
415                            String timesheetDocumentId = request.getParameter("documentId");
416                            ActionForward forward = new ActionForward(mapping.findForward("basic"));
417                            TimesheetDocument tsd = TkServiceLocator.getTimesheetService().getTimesheetDocument(timesheetDocumentId);
418                            String principalId = tsd == null ? null : tsd.getPrincipalId();
419                            
420                            LeaveBlock leaveBlock = eligiblePayouts.get(0);
421                            LocalDate effectiveDate = leaveBlock.getLeaveLocalDate();
422                            String accrualCategoryRuleId = leaveBlock.getAccrualCategoryRuleId();
423                            if(!StringUtils.isBlank(accrualCategoryRuleId)) {
424                                    AccrualCategoryRule accrualRule = HrServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualCategoryRuleId);
425                                    AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId());
426                                    BigDecimal accruedBalance = LmServiceLocator.getAccrualService().getAccruedBalanceForPrincipal(principalId, accrualCategory, effectiveDate);
427    
428                                    LeavePayout leavePayout = LmServiceLocator.getLeavePayoutService().initializePayout(principalId, accrualCategoryRuleId, accruedBalance, effectiveDate);
429                                    leavePayout.setLeaveCalendarDocumentId(timesheetDocumentId);
430            
431                                    if(ObjectUtils.isNotNull(leavePayout)) {
432            
433                                            if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),HrConstants.ACTION_AT_MAX_BALANCE.LOSE)) {
434                                                    // TODO: Redirect user to prompt stating excess leave will be forfeited and ask for confirmation.
435                                                    // Do not submit the object to workflow for this max balance action.
436                                                    leavePayout = LmServiceLocator.getLeavePayoutService().payout(leavePayout);
437                                                    KRADServiceLocator.getBusinessObjectService().save(leavePayout);
438            
439                                                    // May need to update to save the business object to KPME's tables for record keeping.
440                                                    LeaveBlock forfeitedLeaveBlock = LmServiceLocator.getLeaveBlockService().getLeaveBlock(leavePayout.getForfeitedLeaveBlockId());
441                                                    forfeitedLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.APPROVED);
442                                                    LmServiceLocator.getLeaveBlockService().updateLeaveBlock(forfeitedLeaveBlock, principalId);
443    
444                                                    if(ObjectUtils.isNotNull(timesheetDocumentId)) {
445                                                            if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),HrConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE) ||
446                                                                            StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(), HrConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
447                                                                    ActionForward loseForward = new ActionForward(mapping.findForward("timesheetPayoutSuccess"));
448                                                                    loseForward.setPath(loseForward.getPath()+"?documentId="+timesheetDocumentId+"&action=R&methodToCall=approveTimesheet");
449                                                                    return loseForward;
450                                                            }
451                                                            //on demand handled in separate action forward.
452                                                    }
453            
454                                            } else {
455                                                    btf.setLeaveCalendarDocumentId(timesheetDocumentId);
456                                                    btf.setLeavePayout(leavePayout);
457                                                    btf.setPayoutAmount(leavePayout.getPayoutAmount());
458                                                    return forward;
459                                            }
460            
461                                    }  
462    //                              throw new RuntimeException("could not initialize balance transfer");
463                                    LOG.error("could not initialize balance transfer");
464                                    GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "couldnot.initialize.baltransfer");
465                                    return mapping.findForward("basic");
466    
467                    }
468                    else {
469                            LOG.error("unable to fetch the accrual category that triggerred this transfer");
470                            GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "unable.fetch.acccat");
471                            return mapping.findForward("basic");
472                            
473    //                      throw new RuntimeException("unable to fetch the accrual category that triggerred this transfer");
474                    }
475                    }
476                    else {
477                            LOG.error("no eligible transfers exist");
478                            GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "error.eligible.transfer.notExist");
479                            return mapping.findForward("basic");
480                    
481    //                      throw new RuntimeException("no eligible transfers exist");
482                    }
483            }
484            
485            public ActionForward closeLeavePayoutDoc(ActionMapping mapping, ActionForm form,
486                            HttpServletRequest request, HttpServletResponse response)
487                            throws Exception {
488                    return mapping.findForward("closeLeavePayoutDoc");
489            }
490    }