001    /**
002     * Copyright 2004-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.hr.lm.leavecalendar.web;
017    
018    import org.apache.commons.collections.CollectionUtils;
019    import org.apache.commons.lang.StringUtils;
020    import org.apache.commons.lang.SystemUtils;
021    import org.apache.commons.lang.time.DateUtils;
022    import org.apache.log4j.Logger;
023    import org.apache.struts.action.ActionForm;
024    import org.apache.struts.action.ActionForward;
025    import org.apache.struts.action.ActionMapping;
026    import org.apache.struts.action.ActionRedirect;
027    import org.joda.time.DateTime;
028    import org.joda.time.DateTimeZone;
029    import org.joda.time.LocalDateTime;
030    import org.joda.time.format.DateTimeFormat;
031    import org.joda.time.format.DateTimeFormatter;
032    import org.kuali.hr.lm.LMConstants;
033    import org.kuali.hr.lm.accrual.AccrualCategory;
034    import org.kuali.hr.lm.accrual.AccrualCategoryRule;
035    import org.kuali.hr.lm.balancetransfer.BalanceTransfer;
036    import org.kuali.hr.lm.leaveSummary.LeaveSummary;
037    import org.kuali.hr.lm.leaveSummary.LeaveSummaryRow;
038    import org.kuali.hr.lm.leaveblock.LeaveBlock;
039    import org.kuali.hr.lm.leavecalendar.LeaveCalendarDocument;
040    import org.kuali.hr.lm.leavecalendar.validation.LeaveCalendarValidationUtil;
041    import org.kuali.hr.lm.leavepayout.LeavePayout;
042    import org.kuali.hr.lm.leaveplan.LeavePlan;
043    import org.kuali.hr.lm.util.LeaveBlockAggregate;
044    import org.kuali.hr.lm.workflow.LeaveCalendarDocumentHeader;
045    import org.kuali.hr.lm.workflow.LeaveRequestDocument;
046    import org.kuali.hr.time.assignment.Assignment;
047    import org.kuali.hr.time.base.web.TkAction;
048    import org.kuali.hr.time.calendar.CalendarEntries;
049    import org.kuali.hr.time.calendar.LeaveCalendar;
050    import org.kuali.hr.time.detail.web.ActionFormUtils;
051    import org.kuali.hr.time.earncode.EarnCode;
052    import org.kuali.hr.time.principal.PrincipalHRAttributes;
053    import org.kuali.hr.time.roles.TkUserRoles;
054    import org.kuali.hr.time.roles.UserRoles;
055    import org.kuali.hr.time.service.base.TkServiceLocator;
056    import org.kuali.hr.time.util.TKContext;
057    import org.kuali.hr.time.util.TKUser;
058    import org.kuali.hr.time.util.TKUtils;
059    import org.kuali.hr.time.util.TkConstants;
060    import org.kuali.rice.core.api.config.property.ConfigContext;
061    import org.kuali.rice.kew.api.KewApiServiceLocator;
062    import org.kuali.rice.kew.api.document.DocumentStatus;
063    import org.kuali.rice.kew.service.KEWServiceLocator;
064    import org.kuali.rice.kim.api.identity.Person;
065    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
066    import org.kuali.rice.krad.exception.AuthorizationException;
067    import org.kuali.rice.krad.util.GlobalVariables;
068    import org.kuali.rice.krad.util.ObjectUtils;
069    import org.kuali.rice.krad.util.UrlFactory;
070    
071    import javax.servlet.http.HttpServletRequest;
072    import javax.servlet.http.HttpServletResponse;
073    import java.math.BigDecimal;
074    import java.sql.Date;
075    import java.text.DateFormat;
076    import java.text.SimpleDateFormat;
077    import java.util.*;
078    import java.util.Map.Entry;
079    
080    public class LeaveCalendarAction extends TkAction {
081    
082            private static final Logger LOG = Logger.getLogger(LeaveCalendarAction.class);
083    
084        @Override
085        protected void checkTKAuthorization(ActionForm form, String methodToCall) throws AuthorizationException {
086            UserRoles roles = TkUserRoles.getUserRoles(GlobalVariables.getUserSession().getPrincipalId());
087            LeaveCalendarDocument doc = TKContext.getCurrentLeaveCalendarDocument();
088    
089            if (doc != null && !roles.isDocumentReadable(doc)) {
090                throw new AuthorizationException(GlobalVariables.getUserSession().getPrincipalId(), "LeaveCalendarAction: docid: " + (doc == null ? "" : doc.getDocumentId()), "");
091            }
092        }
093        
094            @Override
095            public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
096                    LeaveCalendarForm lcf = (LeaveCalendarForm) form;
097                    String documentId = lcf.getDocumentId();
098                    
099            if (StringUtils.equals(request.getParameter("command"), "displayDocSearchView")
100                            || StringUtils.equals(request.getParameter("command"), "displayActionListView") ) {
101                    documentId = (String) request.getParameter("docId");
102            }
103    
104            LOG.debug("DOCID: " + documentId);
105            
106                    // if the reload was trigger by changing of the selectedPayPeriod, use the passed in parameter as the calendar entry id
107                    String calendarEntryId = StringUtils.isNotBlank(request.getParameter("selectedPP")) ? request.getParameter("selectedPP") : lcf.getCalEntryId();
108                    
109                    // Here - viewPrincipal will be the principal of the user we intend to
110                    // view, be it target user, backdoor or otherwise.
111                    String viewPrincipal = TKUser.getCurrentTargetPerson().getPrincipalId();
112                    CalendarEntries calendarEntry = null;
113    
114                    LeaveCalendarDocument lcd = null;
115                    LeaveCalendarDocumentHeader lcdh = null;
116    
117                    // By handling the prev/next in the execute method, we are saving one
118                    // fetch/construction of a LeaveCalendarDocument. If it were broken out into
119                    // methods, we would first fetch the current document, and then fetch
120                    // the next one instead of doing it in the single action.
121                    if (StringUtils.isNotBlank(documentId)) {
122                            lcd = TkServiceLocator.getLeaveCalendarService()
123                                            .getLeaveCalendarDocument(documentId);
124                            calendarEntry = lcd.getCalendarEntry();
125                    } else if (StringUtils.isNotBlank(calendarEntryId)) {
126                            // do further procedure
127                            calendarEntry = TkServiceLocator.getCalendarEntriesService()
128                                            .getCalendarEntries(calendarEntryId);
129                    } else {
130                            // Default to whatever is active for "today".
131                            Date currentDate = TKUtils.getTimelessDate(null);
132                            calendarEntry = TkServiceLocator.getCalendarService()
133                                            .getCurrentCalendarDatesForLeaveCalendar(viewPrincipal, currentDate);
134                    }
135                    lcf.setCalendarEntry(calendarEntry);
136                    if(calendarEntry != null) {
137                            lcf.setCalEntryId(calendarEntry.getHrCalendarEntriesId());
138                    }
139                    // check configuration setting for allowing accrual service to be ran from leave calendar
140                    String runAccrualFlag = ConfigContext.getCurrentContextConfig().getProperty(LMConstants.RUN_ACCRUAL_FROM_CALENDAR);
141                    if(StringUtils.equals(runAccrualFlag, "true")) {
142                            // run accrual for future dates only, use planning month of leave plan for accrual period
143                            // only run the accrual if the calendar entry contains future dates
144                            if(calendarEntry != null && calendarEntry.getEndPeriodDate().after(TKUtils.getCurrentDate())) {
145                                    if(TkServiceLocator.getLeaveAccrualService().statusChangedSinceLastRun(viewPrincipal)) {
146                                            TkServiceLocator.getLeaveAccrualService().calculateFutureAccrualUsingPlanningMonth(viewPrincipal, calendarEntry.getBeginPeriodDate());
147                                    }
148                            }
149                    }
150                    
151                    if(lcd == null) {
152                            // use jobs to find out if this leave calendar should have a document created or not
153                            boolean createFlag = TkServiceLocator.getLeaveCalendarService().shouldCreateLeaveDocument(viewPrincipal, calendarEntry);
154                            if(createFlag) {
155                                    lcd = TkServiceLocator.getLeaveCalendarService().openLeaveCalendarDocument(viewPrincipal, calendarEntry);
156                            }
157                    }
158                    List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(viewPrincipal, calendarEntry);
159                    List<String> assignmentKeys = new ArrayList<String>();
160            for(Assignment assign : assignments) {
161                    assignmentKeys.add(assign.getAssignmentKey());
162            }
163                    if (lcd != null) {
164                            lcf.setDocumentId(lcd.getDocumentId());
165                            lcf.setAssignmentDescriptions(TkServiceLocator.getAssignmentService().getAssignmentDescriptions(lcd));
166                lcdh = lcd.getDocumentHeader();
167                    } else {
168                            lcf.setAssignmentDescriptions(TkServiceLocator.getAssignmentService().getAssignmentDescriptionsForAssignments(assignments));  
169                    }
170                    setupDocumentOnFormContext(lcf, lcd);
171                    ActionForward forward = super.execute(mapping, form, request, response);
172                    //no window exists if mapping->forward = closeBalanceTransferDoc.
173                    if (forward.getRedirect()) {
174                            return forward;
175                    }
176    
177            LeaveCalendar calendar = null;
178            if (calendarEntry != null) {
179                calendar = new LeaveCalendar(viewPrincipal, calendarEntry, assignmentKeys);
180                lcf.setLeaveCalendar(calendar);
181            }
182                    
183                    this.populateCalendarAndPayPeriodLists(request, lcf);
184    
185                    // KPME-1447
186            List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>();
187            if (lcdh != null && lcdh.getPrincipalId() != null && lcdh.getBeginDate() != null && lcdh.getEndDate() != null) {
188                leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForLeaveCalendar(lcdh.getPrincipalId(), lcdh.getBeginDate(), lcdh.getEndDate(), assignmentKeys);
189            } else if(calendarEntry != null){
190                leaveBlocks = TkServiceLocator.getLeaveBlockService().getLeaveBlocksForLeaveCalendar(viewPrincipal, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate(), assignmentKeys);
191            } 
192            
193            // leave summary
194            if (calendarEntry != null) {
195                //check to see if we are on a previous leave plan
196                PrincipalHRAttributes principalCal = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(viewPrincipal, calendarEntry.getEndPeriodDate());
197                if(principalCal != null) {
198    
199                    DateTime currentYearBeginDate = TkServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(principalCal.getLeavePlan(), TKUtils.getCurrentDate());
200                    DateTime calEntryEndDate = new DateTime(calendarEntry.getEndPeriodDate());
201                        if (calEntryEndDate.getMillis() > currentYearBeginDate.getMillis()) {
202                            //current or future year
203                            LeaveSummary ls = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(viewPrincipal, calendarEntry);
204                            lcf.setLeaveSummary(ls);
205                    } else {
206                        //current year roll over date has been passed, all previous calendars belong to the previous leave plan calendar year.
207                        DateTime effDate = TkServiceLocator.getLeavePlanService().getRolloverDayOfLeavePlan(principalCal.getLeavePlan(), calEntryEndDate.toDate()).minus(1);
208                        LeaveSummary ls = TkServiceLocator.getLeaveSummaryService().getLeaveSummaryAsOfDateWithoutFuture(viewPrincipal, new java.sql.Date(effDate.getMillis()));
209                        //override title element (based on date passed in)
210                        DateFormat formatter = new SimpleDateFormat("MMMM d");
211                        DateFormat formatter2 = new SimpleDateFormat("MMMM d yyyy");
212                        DateTime entryEndDate = new LocalDateTime(calendarEntry.getEndPeriodDate()).toDateTime();
213                        if (entryEndDate.getHourOfDay() == 0) {
214                            entryEndDate = entryEndDate.minusDays(1);
215                        }
216                        String aString = formatter.format(calendarEntry.getBeginPeriodDate()) + " - " + formatter2.format(entryEndDate.toDate());
217                        ls.setPendingDatesString(aString);
218                        DateTimeFormatter fmt = DateTimeFormat.forPattern("MMM d, yyyy");
219                        ls.setNote("Values as of: " + fmt.print(effDate));
220                        lcf.setLeaveSummary(ls);
221                    }
222    
223                }
224            }
225            
226            // add warning messages based on earn codes of leave blocks
227            Map<String, Set<String>> allMessages = LeaveCalendarValidationUtil.getWarningMessagesForLeaveBlocks(leaveBlocks);
228    
229            // add warning message for accrual categories that have exceeded max balance.
230            Map<String,ArrayList<String>> transfers = new HashMap<String,ArrayList<String>>();
231            Map<String,ArrayList<String>> payouts = new HashMap<String,ArrayList<String>>();
232            // Could set a flag on the transferable rows here so that LeaveCalendarSubmit.do knows
233            // which row(s) to transfer when user submits the calendar for approval.
234    
235            if(ObjectUtils.isNotNull(calendarEntry)) {
236                PrincipalHRAttributes principalCalendar = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(viewPrincipal, calendarEntry.getEndPeriodDate());
237                    List<BalanceTransfer> losses = new ArrayList<BalanceTransfer>();
238    
239                if(ObjectUtils.isNotNull(principalCalendar)) {
240                            transfers = TkServiceLocator.getBalanceTransferService().getEligibleTransfers(calendarEntry, viewPrincipal);
241                            payouts = TkServiceLocator.getLeavePayoutService().getEligiblePayouts(calendarEntry,viewPrincipal);
242                            // Prepare LOSE max balance actions for leave approve and year end.
243                            for(String accrualRuleId : transfers.get(LMConstants.MAX_BAL_ACTION_FREQ.LEAVE_APPROVE)) {
244                                    AccrualCategoryRule aRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualRuleId);
245                                    if(StringUtils.equals(aRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)) {
246                                            BigDecimal accruedBalance = lcf.getLeaveSummary().getLeaveSummaryRowForAccrualCategory(aRule.getLmAccrualCategoryId()).getAccruedBalance();
247                                            Date effectiveDate = TKUtils.getCurrentDate();
248                                            if(TKUtils.getCurrentDate().after(calendarEntry.getEndPeriodDate()))
249                                                    effectiveDate = new Date(DateUtils.addDays(calendarEntry.getEndPeriodDate(),-1).getTime());
250                                            BalanceTransfer loseTransfer = TkServiceLocator.getBalanceTransferService().initializeTransfer(viewPrincipal, accrualRuleId, accruedBalance, effectiveDate);
251                                            losses.add(loseTransfer);
252                                    }
253                            }
254                            for(String accrualRuleId : transfers.get(LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
255                                    AccrualCategoryRule aRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualRuleId);
256                                    if(StringUtils.equals(aRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)) {
257                                            BigDecimal accruedBalance = lcf.getLeaveSummary().getLeaveSummaryRowForAccrualCategory(aRule.getLmAccrualCategoryId()).getAccruedBalance();
258                                            Date effectiveDate = TKUtils.getCurrentDate();
259                                            if(TKUtils.getCurrentDate().after(calendarEntry.getEndPeriodDate()))
260                                                    effectiveDate = new Date(DateUtils.addDays(calendarEntry.getEndPeriodDate(),-1).getTime());
261                                            BalanceTransfer loseTransfer = TkServiceLocator.getBalanceTransferService().initializeTransfer(viewPrincipal, accrualRuleId, accruedBalance, effectiveDate);
262                                            losses.add(loseTransfer);
263                                    }
264                            }
265                            // mark summary rows for on demand transfer or payout.
266                            LeaveSummary summary = lcf.getLeaveSummary();
267                            for(String accrualRuleId : transfers.get(LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND)) {
268                                    List<LeaveSummaryRow> summaryRows = lcf.getLeaveSummary().getLeaveSummaryRows();
269                                    List<LeaveSummaryRow> updatedSummaryRows = new ArrayList<LeaveSummaryRow>(summaryRows.size());
270                                    AccrualCategoryRule aRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualRuleId);
271                                    AccrualCategory accrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(aRule.getLmAccrualCategoryId());
272                                    for(LeaveSummaryRow summaryRow : summaryRows) {
273                                            if(StringUtils.equals(summaryRow.getAccrualCategory(),accrualCategory.getAccrualCategory()))
274                                                    summaryRow.setTransferable(true);
275                                            updatedSummaryRows.add(summaryRow);
276                                    }
277                                    summary.setLeaveSummaryRows(updatedSummaryRows);
278                            }
279                            for(String accrualRuleId : payouts.get(LMConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND)) {
280                                    List<LeaveSummaryRow> summaryRows = lcf.getLeaveSummary().getLeaveSummaryRows();
281                                    List<LeaveSummaryRow> updatedSummaryRows = new ArrayList<LeaveSummaryRow>(summaryRows.size());
282                                    AccrualCategoryRule aRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualRuleId);
283                                    AccrualCategory accrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(aRule.getLmAccrualCategoryId());
284                                    for(LeaveSummaryRow summaryRow : summaryRows) {
285                                            if(StringUtils.equals(summaryRow.getAccrualCategory(),accrualCategory.getAccrualCategory()))
286                                                    summaryRow.setTransferable(true);
287                                            updatedSummaryRows.add(summaryRow);
288                                    }
289                                    summary.setLeaveSummaryRows(updatedSummaryRows);
290                            }
291                            lcf.setLeaveSummary(summary);
292                }
293                    lcf.setForfeitures(losses);
294                    
295                    for(Entry<String, ArrayList<String>> entry : transfers.entrySet()) {
296                            if(!entry.getValue().isEmpty()) {
297                                    for(String accrualRuleId : entry.getValue()) {
298                                            AccrualCategoryRule aRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualRuleId);
299                                            AccrualCategory aCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(aRule.getLmAccrualCategoryId());
300                                            String message = "You have exceeded the maximum balance limit for '" + aCat.getAccrualCategory() + "'. " +
301                                                    "Depending upon the accrual category rules, leave over this limit may be forfeited.";
302                                            if(!allMessages.get("warningMessages").contains(message)) {
303                                    allMessages.get("warningMessages").add(message);
304                                            }
305                                    }
306                            }
307                    }
308                    for(Entry<String, ArrayList<String>> entry : payouts.entrySet()) {
309                            if(!entry.getValue().isEmpty()) {
310                                    for(String accrualRuleId : entry.getValue()) {
311                                            AccrualCategoryRule aRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualRuleId);
312                                            AccrualCategory aCat = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(aRule.getLmAccrualCategoryId());
313                                            String message = "You have exceeded the maximum balance limit for '" + aCat.getAccrualCategory() + "'. " +
314                                                    "Depending upon the accrual category rules, leave over this limit may be forfeited.";
315                                            if(!allMessages.get("warningMessages").contains(message)) {
316                                    allMessages.get("warningMessages").add(message);
317                                            }
318                                    }
319                            }
320                    }
321            }
322    
323            Map<String,Set<String>> transactions = LeaveCalendarValidationUtil.validatePendingTransactions(viewPrincipal, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate());
324    
325            allMessages.get("infoMessages").addAll(transactions.get("infoMessages"));
326            allMessages.get("warningMessages").addAll(transactions.get("warningMessages"));
327            allMessages.get("actionMessages").addAll(transactions.get("actionMessages"));
328            
329            // add warning messages based on max carry over balances for each accrual category
330            if(calendarEntry != null) {
331                    PrincipalHRAttributes principalCalendar = TkServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(viewPrincipal, calendarEntry.getEndPeriodDate());
332                            if (principalCalendar != null) {
333                                    List<AccrualCategory> accrualCategories = TkServiceLocator.getAccrualCategoryService().getActiveLeaveAccrualCategoriesForLeavePlan(principalCalendar.getLeavePlan(), new java.sql.Date(calendarEntry.getEndPeriodDate().getTime()));
334                                    for (AccrualCategory accrualCategory : accrualCategories) {
335                                            if (TkServiceLocator.getAccrualCategoryMaxCarryOverService().exceedsAccrualCategoryMaxCarryOver(accrualCategory.getAccrualCategory(), viewPrincipal, calendarEntry, calendarEntry.getEndPeriodDate())) {
336                                                    String message = "Your pending leave balance is greater than the annual max carry over for accrual category '" + accrualCategory.getAccrualCategory() + "' and upon approval, the excess balance will be lost.";
337                                                    if (!allMessages.get("warningMessages").contains(message)) {
338                                allMessages.get("warningMessages").add(message);
339                                                    }
340                                            }
341                                    }
342                            }
343            }
344    
345            List<String> warningMessages = new ArrayList<String>();
346            List<String> infoMessages = new ArrayList<String>();
347            List<String> actionMessages = new ArrayList<String>();
348            
349            warningMessages.addAll(allMessages.get("warningMessages"));
350            infoMessages.addAll(allMessages.get("infoMessages"));
351            actionMessages.addAll(allMessages.get("actionMessages"));
352    
353            lcf.setWarningMessages(warningMessages);
354            lcf.setInfoMessages(infoMessages);
355            lcf.setActionMessages(actionMessages);
356            
357                    // KPME-1690
358    //        LeaveCalendar leaveCalender = new LeaveCalendar(viewPrincipal, calendarEntry);
359            if (calendarEntry != null) {
360                LeaveBlockAggregate aggregate = new LeaveBlockAggregate(leaveBlocks, calendarEntry, calendar);
361                lcf.setLeaveBlockString(LeaveActionFormUtils.getLeaveBlocksJson(aggregate.getFlattenedLeaveBlockList()));
362            }
363            //lcf.setLeaveBlockString(ActionFormUtils.getLeaveBlocksJson(aggregate.getFlattenedLeaveBlockList()));
364                    
365    //        System.out.println("Leave block string : "+lcf.getLeaveBlockString());
366                    return forward;
367            }
368            
369            private void populateCalendarAndPayPeriodLists(HttpServletRequest request, LeaveCalendarForm lcf) {
370                    
371            SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
372            // find all the calendar entries up to the planning months of this employee
373            List<CalendarEntries> ceList = lcf.getCalendarEntry() == null ? new ArrayList<CalendarEntries>() : TkServiceLocator.getCalendarEntriesService()
374                    .getAllCalendarEntriesForCalendarIdUpToPlanningMonths(lcf.getCalendarEntry().getHrCalendarId(), TKUser.getCurrentTargetPerson().getPrincipalId());
375            
376            if(lcf.getCalendarYears().isEmpty()) {
377                    // get calendar year drop down list contents
378                    Set<String> yearSet = new HashSet<String>();
379                    for(CalendarEntries ce : ceList) {
380                            yearSet.add(sdf.format(ce.getBeginPeriodDate()));
381                    }
382                    List<String> yearList = new ArrayList<String>(yearSet);
383                    Collections.sort(yearList);
384                    Collections.reverse(yearList);  // newest on top
385                    lcf.setCalendarYears(yearList);
386            }
387            // if selected calendar year is passed in
388            if(request.getParameter("selectedCY")!= null) {
389                    lcf.setSelectedCalendarYear(request.getParameter("selectedCY").toString());
390            }
391            // if there is no selected calendr year, use the year of current pay calendar entry
392            if(StringUtils.isEmpty(lcf.getSelectedCalendarYear())
393                    && lcf.getCalendarEntry() != null) {
394                    lcf.setSelectedCalendarYear(sdf.format(lcf.getCalendarEntry().getBeginPeriodDate()));
395            }
396            if(lcf.getPayPeriodsMap().isEmpty()) {
397            List<CalendarEntries> yearCEList = ActionFormUtils.getAllCalendarEntriesForYear(ceList, lcf.getSelectedCalendarYear());
398                    lcf.setPayPeriodsMap(ActionFormUtils.getPayPeriodsMap(yearCEList));
399            }
400            if(request.getParameter("selectedPP")!= null) {
401                    lcf.setSelectedPayPeriod(request.getParameter("selectedPP").toString());
402            }
403            if(StringUtils.isEmpty(lcf.getSelectedPayPeriod())
404                    && lcf.getCalendarEntry() != null) {
405                    lcf.setSelectedPayPeriod(lcf.getCalendarEntry().getHrCalendarEntriesId());
406            }
407            }       
408    
409            public ActionForward addLeaveBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
410                    LeaveCalendarForm lcf = (LeaveCalendarForm) form;
411                    LeaveCalendarDocument lcd = lcf.getLeaveCalendarDocument();
412                    
413                    String principalId = TKContext.getPrincipalId();
414                    String targetPrincipalId = TKContext.getTargetPrincipalId();
415                    CalendarEntries calendarEntry = lcf.getCalendarEntry();
416                    String selectedAssignment = lcf.getSelectedAssignment();
417                    DateTime beginDate = new DateTime(TKUtils.convertDateStringToTimestampNoTimezone(lcf.getStartDate()));
418                    DateTime endDate = new DateTime(TKUtils.convertDateStringToTimestampNoTimezone(lcf.getEndDate()));
419                    String selectedEarnCode = lcf.getSelectedEarnCode();
420                    BigDecimal hours = lcf.getLeaveAmount();
421                    String desc = lcf.getDescription();
422                    String spanningWeeks = lcf.getSpanningWeeks();  // KPME-1446
423                    
424                    String documentId = lcd != null ? lcd.getDocumentId() : "";
425                    
426                    Assignment assignment = null;
427                    if(lcd != null) {
428                            assignment = TkServiceLocator.getAssignmentService().getAssignment(lcd, selectedAssignment);
429                    } else {
430                            List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(targetPrincipalId, calendarEntry);
431                            assignment = TkServiceLocator.getAssignmentService().getAssignment(assignments, selectedAssignment, calendarEntry.getBeginPeriodDate());
432                    }
433    
434                    TkServiceLocator.getLeaveBlockService().addLeaveBlocks(beginDate, endDate, calendarEntry, selectedEarnCode, hours, desc, assignment, spanningWeeks, 
435                                    LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR, targetPrincipalId);
436    
437                    generateLeaveCalendarChangedNotification(principalId, targetPrincipalId, documentId, calendarEntry.getHrCalendarEntriesId());
438                    
439                    // after adding the leave block, set the fields of this form to null for future new leave blocks
440                    lcf.setLeaveAmount(null);
441                    lcf.setDescription(null);
442                    
443                    // call accrual service if earn code is not eligible for accrual
444                    if(calendarEntry != null) {
445                            java.sql.Date sqlDate = new java.sql.Date(endDate.getMillis());
446                            this.rerunAccrualForNotEligibleForAccrualChanges(selectedEarnCode, sqlDate, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate());
447                     }
448                    // recalculate summary
449                    if (calendarEntry != null) {
450                            LeaveSummary ls = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(targetPrincipalId, calendarEntry);
451                        lcf.setLeaveSummary(ls);
452                    }
453                    
454                    return mapping.findForward("basic");
455            }
456    
457            public ActionForward deleteLeaveBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
458                    LeaveCalendarForm lcf = (LeaveCalendarForm) form;
459                    LeaveCalendarDocument lcd = lcf.getLeaveCalendarDocument();
460    
461                    String principalId = TKContext.getPrincipalId();
462                    String targetPrincipalId = TKContext.getTargetPrincipalId();
463                    CalendarEntries calendarEntry = lcf.getCalendarEntry();
464                    String leaveBlockId = lcf.getLeaveBlockId();
465                    
466                    String documentId = lcd != null ? lcd.getDocumentId() : "";
467    
468            LeaveBlock blockToDelete = TkServiceLocator.getLeaveBlockService().getLeaveBlock(leaveBlockId);
469            if (blockToDelete != null && TkServiceLocator.getPermissionsService().canDeleteLeaveBlock(blockToDelete)) {
470                    //if leave block is a pending leave request, cancel the leave request document
471                    if(blockToDelete.getRequestStatus().equals(LMConstants.REQUEST_STATUS.REQUESTED)) {
472                            List<LeaveRequestDocument> lrdList = TkServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocumentsByLeaveBlockId(blockToDelete.getLmLeaveBlockId());
473                            if(CollectionUtils.isNotEmpty(lrdList)) {
474                                    for(LeaveRequestDocument lrd : lrdList) { 
475                                            DocumentStatus status = KewApiServiceLocator.getWorkflowDocumentService().getDocumentStatus(lrd.getDocumentNumber());
476                                            if(DocumentStatus.ENROUTE.getCode().equals(status.getCode())) {
477                                                    // cancel the leave request document as the employee.
478                                                    TkServiceLocator.getLeaveRequestDocumentService().recallAndCancelLeave(lrd.getDocumentNumber(), targetPrincipalId, "Leave block deleted by user " + principalId);
479                                            }
480                                    }
481                            }
482                    }
483                    
484                    List<String> approverList = new ArrayList<String>();
485                    //if leave block is an approved leave request, get list of approver's id
486                    if(blockToDelete.getRequestStatus().equals(LMConstants.REQUEST_STATUS.APPROVED)) {
487                            List<LeaveRequestDocument> lrdList = TkServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocumentsByLeaveBlockId(blockToDelete.getLmLeaveBlockId());
488                            if(CollectionUtils.isNotEmpty(lrdList)) {
489                                    for(LeaveRequestDocument lrd : lrdList) { 
490                                            DocumentStatus status = KewApiServiceLocator.getWorkflowDocumentService().getDocumentStatus(lrd.getDocumentNumber());
491                                            if(DocumentStatus.FINAL.getCode().equals(status.getCode())) {
492                                                    // get approver's id for sending out email notification later
493                                                    approverList = TkServiceLocator.getLeaveRequestDocumentService().getApproverIdList(lrd.getDocumentNumber());
494                                            }
495                                    }
496                            }
497                    }
498    
499                    TkServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlockId, principalId);
500                        generateLeaveCalendarChangedNotification(principalId, targetPrincipalId, documentId, calendarEntry.getHrCalendarEntriesId());
501                        if(CollectionUtils.isNotEmpty(approverList)) {
502                            this.generateLeaveBlockDeletionNotification(approverList, targetPrincipalId, principalId, TKUtils.formatDate(blockToDelete.getLeaveDate()), blockToDelete.getLeaveAmount().toString());
503                        }
504                    
505                        // recalculate accruals
506                        if(lcf.getCalendarEntry() != null) {
507                            rerunAccrualForNotEligibleForAccrualChanges(blockToDelete.getEarnCode(), blockToDelete.getLeaveDate(), calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate());
508                        }   
509            }
510                    // recalculate summary
511                    if(lcf.getCalendarEntry() != null) {
512                            LeaveSummary ls = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(targetPrincipalId, calendarEntry);
513                        lcf.setLeaveSummary(ls);
514                    }
515                    return mapping.findForward("basic");
516            }
517            
518            /**
519             * Recalculate accrual when a leave block with not-eligible-for-accrual earn code is added or deleted
520             * calculate accrual only for the calendar entry period
521             * @param earnCode
522             * @param asOfDate
523             * @param startDate
524             * @param endDate
525             */
526            private void rerunAccrualForNotEligibleForAccrualChanges(String earnCode, Date asOfDate, Date startDate, Date endDate) {
527                    EarnCode ec = TkServiceLocator.getEarnCodeService().getEarnCode(earnCode, asOfDate);
528                    if(ec != null && ec.getEligibleForAccrual().equals("N")) {
529                            if(startDate != null && endDate != null) {
530                                    // since we are only recalculating accrual for this pay period, we use "false" to not record the accrual run data
531                                    TkServiceLocator.getLeaveAccrualService().runAccrual(TKContext.getTargetPrincipalId(), startDate, endDate, false);
532                            }
533                    }
534            }
535            
536            // KPME-1447
537            public ActionForward updateLeaveBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
538                    LeaveCalendarForm lcf = (LeaveCalendarForm) form;
539                    LeaveCalendarDocument lcd = lcf.getLeaveCalendarDocument();
540                    
541                    String principalId = TKContext.getPrincipalId();
542                    String targetPrincipalId = TKContext.getTargetPrincipalId();
543                    CalendarEntries calendarEntry = lcf.getCalendarEntry();
544                    String selectedEarnCode = lcf.getSelectedEarnCode();
545                    String leaveBlockId = lcf.getLeaveBlockId();
546                    
547                    String documentId = lcd != null ? lcd.getDocumentId() : "";
548                    
549                    LeaveBlock updatedLeaveBlock = null;
550                    updatedLeaveBlock = TkServiceLocator.getLeaveBlockService().getLeaveBlock(leaveBlockId);
551            if (updatedLeaveBlock.isEditable()) {
552                if (StringUtils.isNotBlank(lcf.getDescription())) {
553                    updatedLeaveBlock.setDescription(lcf.getDescription().trim());
554                }
555                if (!updatedLeaveBlock.getLeaveAmount().equals(lcf.getLeaveAmount())) {
556                    updatedLeaveBlock.setLeaveAmount(lcf.getLeaveAmount());
557                }
558                EarnCode earnCode =  TkServiceLocator.getEarnCodeService().getEarnCode(selectedEarnCode, updatedLeaveBlock.getLeaveDate()); // selectedEarnCode = hrEarnCodeId
559                if (!updatedLeaveBlock.getEarnCode().equals(earnCode.getEarnCode())) {
560                    updatedLeaveBlock.setEarnCode(earnCode.getEarnCode());
561                }
562                TkServiceLocator.getLeaveBlockService().updateLeaveBlock(updatedLeaveBlock, principalId);
563                generateLeaveCalendarChangedNotification(principalId, targetPrincipalId, documentId, calendarEntry.getHrCalendarEntriesId());
564                
565                lcf.setLeaveAmount(null);
566                lcf.setDescription(null);
567                lcf.setSelectedEarnCode(null);
568                    // recalculate summary
569                    if(lcf.getCalendarEntry() != null) {
570                            LeaveSummary ls = TkServiceLocator.getLeaveSummaryService().getLeaveSummary(targetPrincipalId, calendarEntry);
571                        lcf.setLeaveSummary(ls);
572                    }
573            }
574            return mapping.findForward("basic");
575        }
576    
577            protected void setupDocumentOnFormContext(LeaveCalendarForm leaveForm,
578                            LeaveCalendarDocument lcd) {
579                    CalendarEntries futureCalEntry = null;
580                    String viewPrincipal = TKUser.getCurrentTargetPerson().getPrincipalId();
581                    CalendarEntries calEntry = leaveForm.getCalendarEntry();
582    
583                    // some leave calendar may not have leaveCalendarDocument created based on the jobs status of this employee
584                    if(lcd != null) {
585                            if (lcd.getDocumentHeader() != null) {
586                                    TKContext.setCurrentLeaveCalendarDocumentId(lcd.getDocumentId());
587                                    leaveForm.setDocumentId(lcd.getDocumentId());
588                            }
589                            TKContext.setCurrentLeaveCalendarDocument(lcd);
590                    TKContext.setCurrentLeaveCalendarDocumentId(lcd.getDocumentId());
591                            leaveForm.setLeaveCalendarDocument(lcd);
592                    leaveForm.setDocumentId(lcd.getDocumentId());
593                    calEntry = lcd.getCalendarEntry();
594                    }
595            // -- put condition if it is after current period
596                    boolean isFutureDate = calEntry != null && TKUtils.getTimelessDate(null).compareTo(calEntry.getEndPeriodDateTime()) <= 0;
597                    
598                    // fetch previous entry
599            if (calEntry != null) {
600                CalendarEntries calPreEntry = TkServiceLocator
601                        .getCalendarEntriesService()
602                        .getPreviousCalendarEntriesByCalendarId(
603                                calEntry.getHrCalendarId(),
604                                calEntry);
605                if (calPreEntry != null) {
606                    leaveForm.setPrevCalEntryId(calPreEntry
607                            .getHrCalendarEntriesId());
608                }
609    
610                int planningMonths = ActionFormUtils.getPlanningMonthsForEmployee(viewPrincipal);
611                if(planningMonths != 0) {
612                    List<CalendarEntries> futureCalEntries = TkServiceLocator
613                            .getCalendarEntriesService()
614                            .getFutureCalendarEntries(
615                                    calEntry.getHrCalendarId(),
616                                    TKUtils.getTimelessDate(null),
617                                    planningMonths);
618    
619                    if (futureCalEntries != null && !futureCalEntries.isEmpty()) {
620                        futureCalEntry = futureCalEntries.get(futureCalEntries
621                                .size() - 1);
622    
623                        CalendarEntries calNextEntry = TkServiceLocator
624                                .getCalendarEntriesService()
625                                .getNextCalendarEntriesByCalendarId(
626                                        calEntry.getHrCalendarId(),
627                                        calEntry);
628    
629                        if (calNextEntry != null
630                                && futureCalEntries != null
631                                && calNextEntry
632                                        .getBeginPeriodDateTime()
633                                        .compareTo(
634                                                futureCalEntry
635                                                        .getBeginPeriodDateTime()) <= 0) {
636                            leaveForm.setNextCalEntryId(calNextEntry
637                                    .getHrCalendarEntriesId());
638                        }
639                    }
640                }
641            }
642                    if(leaveForm.getViewLeaveTabsWithNEStatus()) {
643                            if(isFutureDate) {
644                    setDocEditable(leaveForm, lcd);
645                            } else {
646                                    // retrieve current pay calendar date
647                                    Date currentDate = TKUtils.getTimelessDate(null);
648                                    CalendarEntries calendarEntry = TkServiceLocator.getCalendarService()
649                                                    .getCurrentCalendarDatesForLeaveCalendar(viewPrincipal, currentDate);
650                                    if(calendarEntry != null) {
651                                            leaveForm.setCurrentPayCalStart(calendarEntry.getBeginLocalDateTime().toDateTime(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback()));
652                                            leaveForm.setCurrentPayCalEnd(calendarEntry.getEndLocalDateTime().toDateTime(TkServiceLocator.getTimezoneService().getUserTimezoneWithFallback()));
653                                    }
654                            }
655                    } else {
656                setDocEditable(leaveForm, lcd);
657                    }
658                    leaveForm.setCalendarEntry(calEntry);
659                    if(calEntry != null) {
660                            leaveForm.setCalEntryId(calEntry.getHrCalendarEntriesId());
661                    }
662                    leaveForm.setOnCurrentPeriod(ActionFormUtils.getOnCurrentPeriodFlag(calEntry));
663    
664            }
665    
666        private void setDocEditable(LeaveCalendarForm leaveForm, LeaveCalendarDocument lcd) {
667            leaveForm.setDocEditable(false);
668            if(lcd == null) {
669                    // working on own calendar
670                     if(TKUser.getCurrentTargetPerson().getPrincipalId().equals(GlobalVariables.getUserSession().getPrincipalId())) {
671                             leaveForm.setDocEditable(true); 
672                     } else {
673                             if(TKContext.getUser().isSystemAdmin()
674                         || TKContext.getUser().isLocationAdmin()
675                         || TKContext.getUser().isReviewer()
676                         || TKContext.getUser().isApprover()) {
677                                            leaveForm.setDocEditable(true);
678                             }
679                 }
680            } else {
681                    if (TKContext.getUser().isSystemAdmin() && !StringUtils.equals(lcd.getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())) {
682                        leaveForm.setDocEditable(true);
683                    } else {
684                        boolean docFinal = lcd.getDocumentHeader().getDocumentStatus().equals(TkConstants.ROUTE_STATUS.FINAL);
685                        if (!docFinal) {
686                            if(StringUtils.equals(lcd.getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())
687                                    || TKContext.getUser().isSystemAdmin()
688                                    || TKContext.getUser().isLocationAdmin()
689                                    || TKContext.getUser().isReviewer()
690                                    || TKContext.getUser().isApprover()) {
691                                leaveForm.setDocEditable(true);
692                            }
693            
694                            //if the leave Calendar has been approved by at least one of the approvers, the employee should not be able to edit it
695                            if (StringUtils.equals(lcd.getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())
696                                    && lcd.getDocumentHeader().getDocumentStatus().equals(TkConstants.ROUTE_STATUS.ENROUTE)) {
697                                Collection actions = KEWServiceLocator.getActionTakenService().findByDocIdAndAction(lcd.getDocumentHeader().getDocumentId(), TkConstants.DOCUMENT_ACTIONS.APPROVE);
698                                if(!actions.isEmpty()) {
699                                    leaveForm.setDocEditable(false);
700                                }
701                            }
702                        }
703                    }
704            }
705        }
706            
707            public ActionForward gotoCurrentPayPeriod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
708                    LeaveCalendarForm lcf = (LeaveCalendarForm) form;
709                    String viewPrincipal = TKUser.getCurrentTargetPerson().getPrincipalId();
710                    Date currentDate = TKUtils.getTimelessDate(null);
711                    CalendarEntries calendarEntry = TkServiceLocator.getCalendarService().getCurrentCalendarDatesForLeaveCalendar(viewPrincipal, currentDate);
712                    lcf.setCalendarEntry(calendarEntry);
713                    if(calendarEntry != null) {
714                            lcf.setCalEntryId(calendarEntry.getHrCalendarEntriesId());
715                    }
716                    lcf.setOnCurrentPeriod(ActionFormUtils.getOnCurrentPeriodFlag(calendarEntry));
717            
718                    LeaveCalendarDocument lcd = null;
719                    // use jobs to find out if this leave calendar should have a document created or not
720                    boolean createFlag = TkServiceLocator.getLeaveCalendarService().shouldCreateLeaveDocument(viewPrincipal, calendarEntry);
721                    if(createFlag) {
722                             lcd = TkServiceLocator.getLeaveCalendarService().openLeaveCalendarDocument(viewPrincipal, calendarEntry);
723                    }
724                    if (lcd != null) {
725                            lcf.setAssignmentDescriptions(TkServiceLocator.getAssignmentService().getAssignmentDescriptions(lcd));
726                    } else {
727                            List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(viewPrincipal, calendarEntry);
728                            lcf.setAssignmentDescriptions(TkServiceLocator.getAssignmentService().getAssignmentDescriptionsForAssignments(assignments));  
729                    }
730                    setupDocumentOnFormContext(lcf, lcd);
731                    return mapping.findForward("basic");
732              }
733            
734            //Triggered by changes of pay period drop down list, reload the whole page based on the selected pay period
735            public ActionForward changeCalendarYear(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
736                      
737                    LeaveCalendarForm lcf = (LeaveCalendarForm) form;
738                    if(request.getParameter("selectedCY") != null) {
739                            lcf.setSelectedCalendarYear(request.getParameter("selectedCY").toString());
740                    }
741                    return mapping.findForward("basic");
742            }
743              
744            //Triggered by changes of pay period drop down list, reload the whole page based on the selected pay period
745            public ActionForward changePayPeriod(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
746                    LeaveCalendarForm lcf = (LeaveCalendarForm) form;
747                    if(request.getParameter("selectedPP") != null) {
748                            lcf.setSelectedPayPeriod(request.getParameter("selectedPP").toString());
749                    CalendarEntries ce = TkServiceLocator.getCalendarEntriesService()
750                                    .getCalendarEntries(request.getParameter("selectedPP").toString());
751                            if(ce != null) {
752                                    String viewPrincipal = TKUser.getCurrentTargetPerson().getPrincipalId();
753                                    lcf.setCalEntryId(ce.getHrCalendarEntriesId());
754                                    LeaveCalendarDocument lcd = null;
755                                    // use jobs to find out if this leave calendar should have a document created or not
756                                    boolean createFlag = TkServiceLocator.getLeaveCalendarService().shouldCreateLeaveDocument(viewPrincipal, ce);
757                                    if(createFlag) {
758                                             lcd = TkServiceLocator.getLeaveCalendarService().openLeaveCalendarDocument(viewPrincipal, ce);
759                                    }
760                                    if(lcd != null) {
761                                            lcf.setAssignmentDescriptions(TkServiceLocator.getAssignmentService().getAssignmentDescriptions(lcd));
762                                    } else {
763                                            List<Assignment> assignments = TkServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(viewPrincipal, ce);
764                                            lcf.setAssignmentDescriptions(TkServiceLocator.getAssignmentService().getAssignmentDescriptionsForAssignments(assignments));  
765                                    }
766                                    setupDocumentOnFormContext(lcf, lcd);
767                            }
768                    }
769                    return mapping.findForward("basic");
770            }
771            
772            private void generateLeaveCalendarChangedNotification(String principalId, String targetPrincipalId, String documentId, String hrCalendarEntryId) {
773                    if (!StringUtils.equals(principalId, targetPrincipalId)) {
774                            Person person = KimApiServiceLocator.getPersonService().getPerson(principalId);
775                            if (person != null) {
776                                    String subject = "Leave Calendar Modification Notice";
777                                    StringBuilder message = new StringBuilder();
778                                    message.append("Your Leave Calendar was changed by ");
779                                    message.append(person.getNameUnmasked());
780                                    message.append(" on your behalf.");
781                                    message.append(SystemUtils.LINE_SEPARATOR);
782                                    message.append(getLeaveCalendarURL(documentId, hrCalendarEntryId));
783                                    
784                                    TkServiceLocator.getKPMENotificationService().sendNotification(subject, message.toString(), targetPrincipalId);
785                            }
786                    }
787            }
788            
789            private void generateLeaveBlockDeletionNotification(List<String> approverIdList, String employeeId, String userId, String dateString, String hrString) {
790                    Person employee = KimApiServiceLocator.getPersonService().getPerson(employeeId);
791                    Person user = KimApiServiceLocator.getPersonService().getPerson(userId);
792                    if (employee != null && user != null) {
793                            String subject = "Leave Request Deletion Notice";
794                            StringBuilder message = new StringBuilder();
795                            message.append("An Approved leave request of " + hrString +" hours on Date " + dateString);
796                            message.append(" for " + employee.getNameUnmasked() +" was deleted by ");
797                            message.append(user.getNameUnmasked());
798                            for(String anId : approverIdList) {
799                                    TkServiceLocator.getKPMENotificationService().sendNotification(subject, message.toString(), anId);
800                            }
801                    }
802            }
803            
804            @SuppressWarnings("deprecation")
805            private String getLeaveCalendarURL(String documentId, String hrCalendarEntryId) {
806                    Properties params = new Properties();
807                    params.put("documentId", documentId);
808                    params.put("calEntryId", hrCalendarEntryId);
809                    return UrlFactory.parameterizeUrl(getApplicationBaseUrl() + "/LeaveCalendar.do", params);
810            }
811        
812        /**
813             * Handles the PAYOUT action of balance transfers issued from the leave calendar with frequency "on demand".
814             * 
815             * This action should be triggered after the user submits to a prompt generated by clicking a "PAYOUT" button on the leave
816             * calendar. This button should only be displayed if, for the current pay period, a max balance has been reached
817             * and the max balance action frequency is set to "On-Demand". The prompt must allow the user to edit the transfer amount.
818             * It may or may not need to show the "to" and "from" accrual categories in the initial prompt, but could on a confirmation
819             * prompt - along with the transfer amount adjusted by the max balance conversion factor.
820             * 
821             * Balance transfers with frequency of leave approval should be handled during the submission of the
822             * leave calendar document for approval and should be automated.
823             * 
824         * @param mapping
825         * @param form
826         * @param request
827         * @param response
828         * @return
829         */
830        public ActionForward payoutOnDemandBalanceTransfer(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
831            /**
832             * TODO: create one new leave block, if applicable; the amount forfeited by this transfer action.
833             * 
834             * The amount transfered, pending adjustment via the max balance conversion factor, will be put into a pay out earn code
835             * that can be redeemed/used by the employee at a later time.
836             */
837            
838            return mapping.findForward("basic");
839        }
840    
841        /**
842         * Leave Payout
843         *
844         * @param mapping
845         * @param form
846         * @param request
847         * @param response
848         * @return
849         */
850        public ActionForward leavePayout(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
851    
852            return mapping.findForward("basic");
853        }
854        
855        public ActionForward docHandler(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
856            ActionForward forward = mapping.findForward("basic");
857            String command = request.getParameter("command");
858            
859            if (StringUtils.equals(command, "displayDocSearchView") || StringUtils.equals(command, "displayActionListView")) {
860                    String docId = (String) request.getParameter("docId");
861                    LeaveCalendarDocument leaveCalendarDocument = TkServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(docId);
862                    String timesheetPrincipalName = KimApiServiceLocator.getPersonService().getPerson(leaveCalendarDocument.getPrincipalId()).getPrincipalName();
863                    
864                    String principalId = TKUser.getCurrentTargetPerson().getPrincipalId();
865                    String principalName = KimApiServiceLocator.getPersonService().getPerson(principalId).getPrincipalName();
866                    
867                    StringBuilder builder = new StringBuilder();
868                    if (!StringUtils.equals(principalName, timesheetPrincipalName)) {
869                            if (StringUtils.equals(command, "displayDocSearchView")) {
870                            builder.append("changeTargetPerson.do?methodToCall=changeTargetPerson");
871                            builder.append("&documentId=");
872                            builder.append(docId);
873                            builder.append("&principalName=");
874                            builder.append(timesheetPrincipalName);
875                            builder.append("&targetUrl=LeaveCalendar.do");
876                            builder.append("?docmentId=" + docId);
877                            builder.append("&returnUrl=LeaveApproval.do");
878                    } else {
879                            builder.append("LeaveApproval.do");
880                    }
881                    } else {
882                            builder.append("LeaveCalendar.do");
883                            builder.append("?docmentId=" + docId);
884                    }
885                    
886                    forward = new ActionRedirect(builder.toString());
887            }
888            
889            return forward;
890        }
891    
892    }