001/**
002 * Copyright 2004-2014 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 */
016package org.kuali.kpme.tklm.leave.calendar.web;
017
018import java.math.BigDecimal;
019import java.sql.Timestamp;
020import java.text.DateFormat;
021import java.text.SimpleDateFormat;
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.Date;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028import java.util.Map.Entry;
029import java.util.Properties;
030import java.util.Set;
031
032import javax.servlet.http.HttpServletRequest;
033import javax.servlet.http.HttpServletResponse;
034
035import org.apache.commons.collections.CollectionUtils;
036import org.apache.commons.lang.StringUtils;
037import org.apache.commons.lang.SystemUtils;
038import org.apache.commons.lang.time.DateUtils;
039import org.apache.log4j.Logger;
040import org.apache.struts.action.ActionForm;
041import org.apache.struts.action.ActionForward;
042import org.apache.struts.action.ActionMapping;
043import org.apache.struts.action.ActionRedirect;
044import org.joda.time.DateTime;
045import org.joda.time.Interval;
046import org.joda.time.LocalDate;
047import org.joda.time.LocalDateTime;
048import org.joda.time.format.DateTimeFormat;
049import org.joda.time.format.DateTimeFormatter;
050import org.kuali.kpme.core.KPMENamespace;
051import org.kuali.kpme.core.accrualcategory.AccrualCategory;
052import org.kuali.kpme.core.accrualcategory.rule.AccrualCategoryRule;
053import org.kuali.kpme.core.assignment.Assignment;
054import org.kuali.kpme.core.assignment.AssignmentDescriptionKey;
055import org.kuali.kpme.core.calendar.Calendar;
056import org.kuali.kpme.core.calendar.entry.CalendarEntry;
057import org.kuali.kpme.core.department.Department;
058import org.kuali.kpme.core.document.calendar.CalendarDocument;
059import org.kuali.kpme.core.earncode.EarnCode;
060import org.kuali.kpme.core.job.Job;
061import org.kuali.kpme.core.principal.PrincipalHRAttributes;
062import org.kuali.kpme.core.role.KPMERole;
063import org.kuali.kpme.core.service.HrServiceLocator;
064import org.kuali.kpme.core.util.HrConstants;
065import org.kuali.kpme.core.util.HrContext;
066import org.kuali.kpme.core.util.TKUtils;
067import org.kuali.kpme.tklm.common.CalendarFormAction;
068import org.kuali.kpme.tklm.common.LMConstants;
069import org.kuali.kpme.tklm.leave.block.LeaveBlock;
070import org.kuali.kpme.tklm.leave.block.LeaveBlockAggregate;
071import org.kuali.kpme.tklm.leave.calendar.LeaveCalendar;
072import org.kuali.kpme.tklm.leave.calendar.LeaveCalendarDocument;
073import org.kuali.kpme.tklm.leave.calendar.validation.LeaveCalendarValidationUtil;
074import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
075import org.kuali.kpme.tklm.leave.summary.LeaveSummary;
076import org.kuali.kpme.tklm.leave.summary.LeaveSummaryRow;
077import org.kuali.kpme.tklm.leave.transfer.BalanceTransfer;
078import org.kuali.kpme.tklm.leave.transfer.validation.BalanceTransferValidationUtils;
079import org.kuali.kpme.tklm.leave.workflow.LeaveCalendarDocumentHeader;
080import org.kuali.kpme.tklm.leave.workflow.LeaveRequestDocument;
081import org.kuali.kpme.tklm.time.detail.web.ActionFormUtils;
082import org.kuali.kpme.tklm.time.util.TkContext;
083import org.kuali.rice.core.api.config.property.ConfigContext;
084import org.kuali.rice.kew.api.KewApiServiceLocator;
085import org.kuali.rice.kew.api.document.DocumentStatus;
086import org.kuali.rice.kew.service.KEWServiceLocator;
087import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName;
088import org.kuali.rice.kim.api.services.KimApiServiceLocator;
089import org.kuali.rice.krad.exception.AuthorizationException;
090import org.kuali.rice.krad.util.GlobalVariables;
091import org.kuali.rice.krad.util.KRADConstants;
092import org.kuali.rice.krad.util.UrlFactory;
093
094public class LeaveCalendarAction extends CalendarFormAction {
095
096        private static final Logger LOG = Logger.getLogger(LeaveCalendarAction.class);
097
098    @Override
099    protected void checkTKAuthorization(ActionForm form, String methodToCall) throws AuthorizationException {
100        LeaveCalendarForm leaveCalendarForm = (LeaveCalendarForm) form;
101        
102        String principalId = GlobalVariables.getUserSession().getPrincipalId();
103        
104        if (StringUtils.isNotBlank(leaveCalendarForm.getDocumentId())) {
105                CalendarDocument leaveCalendarDocument = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(leaveCalendarForm.getDocumentId());
106                if (!HrServiceLocator.getHRPermissionService().canViewCalendarDocument(principalId, leaveCalendarDocument)) {
107                    throw new AuthorizationException(GlobalVariables.getUserSession().getPrincipalId(), "LeaveCalendarAction: docid: " + leaveCalendarDocument.getDocumentId(), "");
108                }
109        }
110    }
111    
112    public ActionForward docHandler(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
113        ActionForward forward = mapping.findForward("basic");
114        String command = request.getParameter("command");
115        
116        if (StringUtils.equals(command, "displayDocSearchView") 
117                        || StringUtils.equals(command, "displayActionListView")
118                        || StringUtils.equals(command, "displaySuperUserView")) {
119                String documentId = (String) request.getParameter("docId");
120                LeaveCalendarDocument leaveCalendarDocument = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(documentId);
121                String leaveCalendarPrincipalName = KimApiServiceLocator.getPersonService().getPerson(leaveCalendarDocument.getPrincipalId()).getPrincipalName();
122                
123                String principalId = HrContext.getTargetPrincipalId();
124                String principalName = KimApiServiceLocator.getPersonService().getPerson(principalId).getPrincipalName();
125                
126                StringBuilder builder = new StringBuilder();
127                if (!StringUtils.equals(principalName, leaveCalendarPrincipalName)) {
128                        if (StringUtils.equals(command, "displayDocSearchView")
129                                        || StringUtils.equals(command, "displaySuperUserView")) {
130                        builder.append("changeTargetPerson.do?methodToCall=changeTargetPerson");
131                        builder.append("&documentId=");
132                        builder.append(documentId);
133                        builder.append("&principalName=");
134                        builder.append(leaveCalendarPrincipalName);
135                        builder.append("&targetUrl=LeaveCalendar.do");
136                        builder.append("?documentId=" + documentId);
137                        builder.append("&returnUrl=LeaveApproval.do");
138                } else {
139                        builder.append("LeaveApproval.do");
140                        builder.append("?documentId=");
141                        builder.append(documentId);
142                }
143                } else {
144                        builder.append("LeaveCalendar.do");
145                        builder.append("?documentId=" + documentId);
146                }
147                
148                forward = new ActionRedirect(builder.toString());
149        }
150        
151        return forward;
152    }
153    
154        @Override
155        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
156                LeaveCalendarForm leaveCalendarForm = (LeaveCalendarForm) form;
157                String documentId = leaveCalendarForm.getDocumentId();
158                String principalId = HrContext.getTargetPrincipalId();
159                CalendarEntry calendarEntry = null;
160                LeaveCalendarDocument leaveCalendarDocument = null;
161                if (StringUtils.isNotBlank(documentId)) {
162                        leaveCalendarDocument = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(documentId);
163                        
164                        if (leaveCalendarDocument != null) {
165                                calendarEntry = leaveCalendarDocument.getCalendarEntry();
166                        }
167                } else {
168                        if (StringUtils.isNotBlank(leaveCalendarForm.getHrCalendarEntryId())) {
169                                calendarEntry = HrServiceLocator.getCalendarEntryService().getCalendarEntry(leaveCalendarForm.getHrCalendarEntryId());
170                        } else {
171                                calendarEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarDatesForLeaveCalendar(principalId, new LocalDate().toDateTimeAtStartOfDay());
172                        }
173                        
174                        if (calendarEntry != null) {
175                                if (LmServiceLocator.getLeaveCalendarService().shouldCreateLeaveDocument(principalId, calendarEntry)) {
176                                        leaveCalendarDocument = LmServiceLocator.getLeaveCalendarService().openLeaveCalendarDocument(principalId, calendarEntry);
177                                } else {
178                                        LeaveCalendarDocumentHeader header = LmServiceLocator.getLeaveCalendarDocumentHeaderService().getDocumentHeader(principalId, calendarEntry.getBeginPeriodFullDateTime(), calendarEntry.getEndPeriodFullDateTime());
179                                        if (header != null) {
180                                                leaveCalendarDocument = LmServiceLocator.getLeaveCalendarService().getLeaveCalendarDocument(header.getDocumentId());
181                                        }
182                                }
183                }
184                }
185
186        if (calendarEntry != null) {
187                leaveCalendarForm.setHrCalendarEntryId(calendarEntry.getHrCalendarEntryId());
188                leaveCalendarForm.setCalendarEntry(calendarEntry);
189                leaveCalendarForm.setBeginCalendarEntryDate(calendarEntry.getBeginPeriodDateTime());
190                leaveCalendarForm.setEndCalendarEntryDate(DateUtils.addMilliseconds(calendarEntry.getEndPeriodDateTime(), -1));
191
192                CalendarEntry previousCalendarEntry = HrServiceLocator.getCalendarEntryService().getPreviousCalendarEntryByCalendarId(calendarEntry.getHrCalendarId(), calendarEntry);
193            if (previousCalendarEntry != null) {
194                LocalDate previousBeginDate = previousCalendarEntry.getBeginPeriodFullDateTime().toLocalDate();
195                LocalDate previousEndDate = previousCalendarEntry.getEndPeriodFullDateTime().toLocalDate().minusDays(1);
196
197                        PrincipalHRAttributes principalHRAttributes = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, previousEndDate);
198                        if (principalHRAttributes != null) {
199                                LocalDate serviceDate = principalHRAttributes.getServiceLocalDate();
200                                if (serviceDate != null) {
201                                        if (previousBeginDate.isEqual(serviceDate) || previousBeginDate.isAfter(serviceDate)) {
202                                                leaveCalendarForm.setPrevHrCalendarEntryId(previousCalendarEntry.getHrCalendarEntryId());
203                                } 
204                        } else {
205                                leaveCalendarForm.setPrevHrCalendarEntryId(previousCalendarEntry.getHrCalendarEntryId());
206                                }
207                        }
208            }
209
210                int planningMonths = ActionFormUtils.getPlanningMonthsForEmployee(principalId);
211            if (planningMonths != 0) {
212                List<CalendarEntry> futureCalendarEntries = HrServiceLocator.getCalendarEntryService().getFutureCalendarEntries(calendarEntry.getHrCalendarId(), new LocalDate().toDateTimeAtStartOfDay(), planningMonths);
213                if (!futureCalendarEntries.isEmpty()) {
214                    CalendarEntry nextCalendarEntry = HrServiceLocator.getCalendarEntryService().getNextCalendarEntryByCalendarId(calendarEntry.getHrCalendarId(), calendarEntry);
215                        CalendarEntry lastFutureCalendarEntry = futureCalendarEntries.get(futureCalendarEntries.size() - 1);
216
217                    if (nextCalendarEntry != null && futureCalendarEntries != null) {
218                        DateTime nextCalendarEntryBeginDate = nextCalendarEntry.getBeginPeriodFullDateTime();
219                        DateTime lastFutureCalendarEntryBeginDate = lastFutureCalendarEntry.getBeginPeriodFullDateTime();
220                        if (nextCalendarEntryBeginDate.isBefore(lastFutureCalendarEntryBeginDate) || nextCalendarEntryBeginDate.isEqual(lastFutureCalendarEntryBeginDate)) {
221                                leaveCalendarForm.setNextHrCalendarEntryId(nextCalendarEntry.getHrCalendarEntryId());
222                        }
223                    }
224                }
225            }
226                
227                setCalendarFields(request, leaveCalendarForm);
228        } else {
229                EntityNamePrincipalName entityNamePrincipalName = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
230            GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_ERRORS, "error.missing.leaveCalendar", entityNamePrincipalName.getPrincipalName());
231        }
232        
233        ActionForward actionForward = super.execute(mapping, form, request, response);
234        
235        if (calendarEntry != null) {
236                        List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(principalId, calendarEntry);
237                        List<String> assignmentKeys = new ArrayList<String>();
238                for (Assignment assignment : assignments) {
239                        assignmentKeys.add(assignment.getAssignmentKey());
240                }
241                
242                // use the logged in user's id to retrieve assignments so that approver can only see assignments they have permission to edit
243                String loggedInUserId = HrContext.getPrincipalId();
244                new ArrayList<Assignment>();
245                DateTime asOfDate = calendarEntry.getBeginPeriodFullDateTime();
246                // if user is working on his/her own calendar, use the original assignment list,
247                // otherwise, call the method to make sure the user has permission for the assignments
248                List<Assignment> loggedInUserassignments = loggedInUserId.equals(principalId) ? assignments : this.availableAssignmentsForLoggedUser(assignments, loggedInUserId, asOfDate);
249                
250                        List<String> loggedInUserAssignmentKeys = new ArrayList<String>();
251                for (Assignment assignment : loggedInUserassignments) {
252                        loggedInUserAssignmentKeys.add(assignment.getAssignmentKey());
253                }
254                if (leaveCalendarDocument != null) {
255                        leaveCalendarForm.setLeaveCalendarDocument(leaveCalendarDocument);
256                        leaveCalendarForm.setDocumentId(leaveCalendarDocument.getDocumentId());
257                        List<Assignment> docAssignments = new ArrayList<Assignment>();
258                        for(Assignment anAssignment : leaveCalendarDocument.getAssignments()) {
259                                if(loggedInUserAssignmentKeys.contains(anAssignment.getAssignmentKey()))
260                                        docAssignments.add(anAssignment);
261                        }
262                        leaveCalendarForm.setAssignmentDescriptions(HrServiceLocator.getAssignmentService().getAssignmentDescriptionsForAssignments(docAssignments));
263                } else {
264                        leaveCalendarForm.setAssignmentDescriptions(HrServiceLocator.getAssignmentService().getAssignmentDescriptionsForAssignments(loggedInUserassignments));
265                }
266                
267                        if (HrServiceLocator.getHRPermissionService().canViewLeaveTabsWithNEStatus()) {
268                                if (LocalDate.now().isBefore(calendarEntry.getEndPeriodFullDateTime().toLocalDate()) || LocalDate.now().isEqual(calendarEntry.getEndPeriodFullDateTime().toLocalDate())) {
269                        setDocEditable(leaveCalendarForm, leaveCalendarDocument);
270                                }
271                        } else {
272                    setDocEditable(leaveCalendarForm, leaveCalendarDocument);
273                        }
274
275                        runAccrualService(leaveCalendarForm);
276
277                        List<LeaveBlock> leaveBlocks = getLeaveBlocks(principalId, calendarEntry, leaveCalendarDocument, assignmentKeys);
278
279                        setLeaveBlocks(leaveCalendarForm, principalId, leaveBlocks, assignmentKeys);
280                setLeaveSummary(leaveCalendarForm);
281                setMessages(leaveCalendarForm, leaveBlocks);
282                
283                setBlockSubmittable(leaveCalendarForm, leaveCalendarDocument);
284                
285                boolean leavePlanningCalendar = LmServiceLocator.getLeaveCalendarService().isLeavePlanningCalendar(principalId, calendarEntry.getBeginPeriodFullDateTime().toLocalDate(), calendarEntry.getEndPeriodFullDateTime().toLocalDate());
286                leaveCalendarForm.setLeavePlanningCalendar(leavePlanningCalendar);
287        }
288
289                return actionForward;
290        }
291
292    //TODO - performance
293        private List<Assignment> availableAssignmentsForLoggedUser(List<Assignment> fullAssignmentList, String principalId, DateTime asOfDate) {
294                List<Assignment> loggedInUserassignments = new ArrayList<Assignment>();
295                if(HrServiceLocator.getKPMEGroupService().isMemberOfSystemAdministratorGroup(principalId, asOfDate)
296                                || HrServiceLocator.getKPMERoleService().principalHasRole(principalId, KPMENamespace.KPME_TK.getNamespaceCode(), KPMERole.TIME_SYSTEM_ADMINISTRATOR.getRoleName(), asOfDate)) {
297                        loggedInUserassignments.addAll(fullAssignmentList);
298                } else {
299            //get logged in user information from role service
300
301                        for(Assignment anAssignment : fullAssignmentList) {
302                                // if user has approver/approver delegates/reviewer roles to the workarea, then the user has access to the assignment
303                                if(HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.REVIEWER.getRoleName(), anAssignment.getWorkArea(), asOfDate)
304                                || HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.APPROVER.getRoleName(), anAssignment.getWorkArea(), asOfDate)
305                                || HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.APPROVER_DELEGATE.getRoleName(), anAssignment.getWorkArea(), asOfDate)) {
306                                        loggedInUserassignments.add(anAssignment);
307                                continue;
308                                }
309                                Job aJob = HrServiceLocator.getJobService().getJob(anAssignment.getPrincipalId(), anAssignment.getJobNumber(), asOfDate.toLocalDate());
310                                if(aJob != null) {
311                                        // Payroll Processor / Payroll Processor Delegate
312                                    if(HrServiceLocator.getKPMERoleService().principalHasRoleInDepartment(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.PAYROLL_PROCESSOR.getRoleName(), aJob.getDept(), asOfDate)
313                                                || HrServiceLocator.getKPMERoleService().principalHasRoleInDepartment(principalId, KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.PAYROLL_PROCESSOR_DELEGATE.getRoleName(), aJob.getDept(), asOfDate)) {
314                                        loggedInUserassignments.add(anAssignment);
315                                        continue;
316                                }
317                                 // if user is location admin, then the user can access this assignment
318                                        // use job to find the department, then use the location from Department to get the location roles
319                                    // aJob.getDeptObj() does not reliably return a Department Object.
320                                        Department aDept = HrServiceLocator.getDepartmentService().getDepartmentWithoutRoles(aJob.getDept(), asOfDate.toLocalDate());
321                                        if(aDept != null) {
322                                            if(HrServiceLocator.getKPMERoleService()
323                                                        .principalHasRoleInLocation(principalId, KPMENamespace.KPME_TK.getNamespaceCode(), KPMERole.TIME_LOCATION_ADMINISTRATOR.getRoleName(), aDept.getLocation(), asOfDate)
324                                                || HrServiceLocator.getKPMERoleService()
325                                                        .principalHasRoleInLocation(principalId, KPMENamespace.KPME_TK.getNamespaceCode(), KPMERole.LEAVE_LOCATION_ADMINISTRATOR.getRoleName(), aDept.getLocation(), asOfDate)) {
326                                                loggedInUserassignments.add(anAssignment);
327                                                continue;
328                                            }
329                                        }
330                                }                               
331                        }
332                }
333                return loggedInUserassignments;
334        }
335
336        public ActionForward addLeaveBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
337                LeaveCalendarForm lcf = (LeaveCalendarForm) form;
338                LeaveCalendarDocument lcd = lcf.getLeaveCalendarDocument();
339                String principalId = HrContext.getPrincipalId();
340                String targetPrincipalId = HrContext.getTargetPrincipalId();
341                CalendarEntry calendarEntry = lcf.getCalendarEntry();
342                String selectedAssignment = lcf.getSelectedAssignment();
343                
344                //KPME-2832: validate leave entry prior to save.
345                //This duplicates validation done on submissions that went through LeaveCalendarWSAction, i.e. typical leave calendar transactions.
346        List<String> errorMsgList = LeaveCalendarValidationUtil.validateLeaveEntry(lcf);
347        if(!errorMsgList.isEmpty()) {
348                lcf.setErrorMessages(errorMsgList);
349                return mapping.findForward("basic");
350        }
351                
352                DateTime beginDate = null;
353                DateTime endDate = null;
354                
355                /** -- Jignasha : if earchcode type is 'T' then change the date and time with timezone.
356                // Surgery point - Need to construct a Date/Time with Appropriate Timezone.
357                 * */
358                LOG.debug("Start time is "+lcf.getStartTime());
359                LOG.debug("Emnd time is "+lcf.getEndTime());
360                if(lcf.getStartTime() != null && lcf.getEndTime() != null) {
361                        beginDate = TKUtils.convertDateStringToDateTimeWithoutZone(lcf.getStartDate(), lcf.getStartTime());
362                        endDate   = TKUtils.convertDateStringToDateTimeWithoutZone(lcf.getEndDate(), lcf.getEndTime());
363                }  else {
364                        beginDate = TKUtils.formatDateTimeStringNoTimezone(lcf.getStartDate());
365                        endDate = TKUtils.formatDateTimeStringNoTimezone(lcf.getEndDate());
366                }
367        LOG.debug("Begin Date is>> "+beginDate);
368        LOG.debug("End Date is>> "+endDate);
369                
370                String selectedEarnCode = lcf.getSelectedEarnCode();
371                BigDecimal hours = lcf.getLeaveAmount();
372                String desc = lcf.getDescription();
373                String spanningWeeks = lcf.getSpanningWeeks();  // KPME-1446
374                String approval = lcf.getApproval(); // KPME-2540
375                
376                String documentId = lcd != null ? lcd.getDocumentId() : "";
377                
378                Assignment assignment = null;
379                if(lcd != null) {
380                        assignment = lcd.getAssignment(AssignmentDescriptionKey.get(selectedAssignment));
381                        if(assignment == null)
382                                LOG.warn("No matched assignment found");
383                } else {
384                        List<Assignment> assignments = HrServiceLocator.getAssignmentService().getAssignmentsByCalEntryForLeaveCalendar(targetPrincipalId, calendarEntry);
385                        assignment = HrServiceLocator.getAssignmentService().getAssignment(assignments, selectedAssignment, calendarEntry.getBeginPeriodFullDateTime().toLocalDate());
386                }
387                List<LeaveBlock> newLeaveBlocks = LmServiceLocator.getLeaveBlockService().addLeaveBlocks(beginDate, endDate, calendarEntry, selectedEarnCode, hours, desc, assignment, spanningWeeks, 
388                                LMConstants.LEAVE_BLOCK_TYPE.LEAVE_CALENDAR, targetPrincipalId);
389
390                generateLeaveCalendarChangedNotification(principalId, targetPrincipalId, documentId, calendarEntry.getHrCalendarEntryId());
391                
392                // after adding the leave block, set the fields of this form to null for future new leave blocks
393                lcf.setLeaveAmount(null);
394                lcf.setDescription(null);
395                
396                // call accrual service if earn code is not eligible for accrual
397                if(calendarEntry != null) {
398                        this.rerunAccrualForNotEligibleForAccrualChanges(selectedEarnCode, null, endDate.toLocalDate(), calendarEntry.getBeginPeriodFullDateTime().toLocalDate(), calendarEntry.getEndPeriodFullDateTime().toLocalDate());
399                 }
400                // recalculate summary
401                if (calendarEntry != null) {
402                        LeaveSummary ls = LmServiceLocator.getLeaveSummaryService().getLeaveSummary(targetPrincipalId, calendarEntry);
403                    lcf.setLeaveSummary(ls);
404                }
405                
406                // KPME-2540 replicate submitForApproval method in LeaveRequestAction here
407                if (!StringUtils.isEmpty(approval)) {
408                        for(LeaveBlock leaveBlock : newLeaveBlocks) {
409                                LeaveRequestDocument lrd = LmServiceLocator.getLeaveRequestDocumentService().createLeaveRequestDocument(leaveBlock.getLmLeaveBlockId());
410                                LmServiceLocator.getLeaveRequestDocumentService().requestLeave(lrd.getDocumentNumber());
411                        }
412                }
413                
414                return mapping.findForward("basic");
415        }
416
417        public ActionForward deleteLeaveBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
418                LeaveCalendarForm lcf = (LeaveCalendarForm) form;
419                LeaveCalendarDocument lcd = lcf.getLeaveCalendarDocument();
420
421                String principalId = HrContext.getPrincipalId();
422                String targetPrincipalId = HrContext.getTargetPrincipalId();
423                CalendarEntry calendarEntry = lcf.getCalendarEntry();
424                String leaveBlockId = lcf.getLeaveBlockId();
425                
426                String documentId = lcd != null ? lcd.getDocumentId() : "";
427
428        LeaveBlock blockToDelete = LmServiceLocator.getLeaveBlockService().getLeaveBlock(leaveBlockId);
429        if (blockToDelete != null && LmServiceLocator.getLMPermissionService().canDeleteLeaveBlock(HrContext.getPrincipalId(), blockToDelete)) {
430                //if leave block is a pending leave request, cancel the leave request document
431                if(blockToDelete.getRequestStatus().equals(HrConstants.REQUEST_STATUS.REQUESTED)) {
432                        List<LeaveRequestDocument> lrdList = LmServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocumentsByLeaveBlockId(blockToDelete.getLmLeaveBlockId());
433                        if(CollectionUtils.isNotEmpty(lrdList)) {
434                                for(LeaveRequestDocument lrd : lrdList) { 
435                                        DocumentStatus status = KewApiServiceLocator.getWorkflowDocumentService().getDocumentStatus(lrd.getDocumentNumber());
436                                        if(DocumentStatus.ENROUTE.getCode().equals(status.getCode())) {
437                                                // cancel the leave request document as the employee.
438                                                LmServiceLocator.getLeaveRequestDocumentService().recallAndCancelLeave(lrd.getDocumentNumber(), targetPrincipalId, "Leave block deleted by user " + principalId);
439                                        }
440                                }
441                        }
442                }
443                
444                List<String> approverList = new ArrayList<String>();
445                //if leave block is an approved leave request, get list of approver's id
446                if(blockToDelete.getRequestStatus().equals(HrConstants.REQUEST_STATUS.APPROVED)) {
447                        List<LeaveRequestDocument> lrdList = LmServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocumentsByLeaveBlockId(blockToDelete.getLmLeaveBlockId());
448                        if(CollectionUtils.isNotEmpty(lrdList)) {
449                                for(LeaveRequestDocument lrd : lrdList) { 
450                                        DocumentStatus status = KewApiServiceLocator.getWorkflowDocumentService().getDocumentStatus(lrd.getDocumentNumber());
451                                        if(DocumentStatus.FINAL.getCode().equals(status.getCode())) {
452                                                // get approver's id for sending out email notification later
453                                                approverList = LmServiceLocator.getLeaveRequestDocumentService().getApproverIdList(lrd.getDocumentNumber());
454                                        }
455                                }
456                        }
457                }
458
459                LmServiceLocator.getLeaveBlockService().deleteLeaveBlock(leaveBlockId, principalId);
460                    generateLeaveCalendarChangedNotification(principalId, targetPrincipalId, documentId, calendarEntry.getHrCalendarEntryId());
461                    if(CollectionUtils.isNotEmpty(approverList)) {
462                        this.generateLeaveBlockDeletionNotification(approverList, targetPrincipalId, principalId, TKUtils.formatDate(blockToDelete.getLeaveLocalDate()), blockToDelete.getLeaveAmount().toString());
463                    }
464                
465                    // recalculate accruals
466                    if(lcf.getCalendarEntry() != null) {
467                        rerunAccrualForNotEligibleForAccrualChanges(blockToDelete.getEarnCode(), null, blockToDelete.getLeaveLocalDate(), calendarEntry.getBeginPeriodFullDateTime().toLocalDate(), calendarEntry.getEndPeriodFullDateTime().toLocalDate());
468                    }   
469        }
470                // recalculate summary
471                if(lcf.getCalendarEntry() != null) {
472                        LeaveSummary ls = LmServiceLocator.getLeaveSummaryService().getLeaveSummary(targetPrincipalId, calendarEntry);
473                    lcf.setLeaveSummary(ls);
474                }
475                return mapping.findForward("basic");
476        }
477        
478        
479    @Override
480    protected List<CalendarEntry> getCalendarEntries(CalendarEntry currentCalendarEntry) {
481        return HrServiceLocator.getCalendarEntryService().getAllCalendarEntriesForCalendarIdUpToPlanningMonths(currentCalendarEntry.getHrCalendarId(), HrContext.getTargetPrincipalId());
482    }
483    
484    protected void runAccrualService(LeaveCalendarForm leaveCalendarForm) {
485        String principalId = HrContext.getTargetPrincipalId();
486        CalendarEntry calendarEntry = leaveCalendarForm.getCalendarEntry();
487        
488        // check configuration setting for allowing accrual service to be ran from leave calendar
489                String runAccrualFlag = ConfigContext.getCurrentContextConfig().getProperty(LMConstants.RUN_ACCRUAL_FROM_CALENDAR);
490                if (StringUtils.equals(runAccrualFlag, "true")) {
491                        // run accrual for future dates only, use planning month of leave plan for accrual period
492                        // only run the accrual if the calendar entry contains future dates
493                        if (calendarEntry.getEndPeriodDate().after(LocalDate.now().toDate())) {
494                                if (LmServiceLocator.getLeaveAccrualService().statusChangedSinceLastRun(principalId)) {
495                                        LmServiceLocator.getLeaveAccrualService().calculateFutureAccrualUsingPlanningMonth(principalId, calendarEntry.getBeginPeriodFullDateTime().toLocalDate(), HrContext.getPrincipalId());
496                                }
497                        }
498                }
499    }
500    
501    protected List<LeaveBlock> getLeaveBlocks(String principalId, CalendarEntry calendarEntry, LeaveCalendarDocument leaveCalendarDocument, List<String> assignmentKeys) {
502        List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>();
503        
504        if (leaveCalendarDocument != null) {
505            leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksForLeaveCalendar(leaveCalendarDocument.getPrincipalId(), leaveCalendarDocument.getAsOfDate(), leaveCalendarDocument.getDocEndDate(), assignmentKeys);
506        } else {
507            leaveBlocks = LmServiceLocator.getLeaveBlockService().getLeaveBlocksForLeaveCalendar(principalId, calendarEntry.getBeginPeriodFullDateTime().toLocalDate(), calendarEntry.getEndPeriodFullDateTime().toLocalDate(), assignmentKeys);
508        }
509        
510        return leaveBlocks;
511    }
512    
513    protected void setLeaveBlocks(LeaveCalendarForm leaveCalendarForm, String principalId, List<LeaveBlock> leaveBlocks, List<String> assignmentKeys) {
514        CalendarEntry calendarEntry = leaveCalendarForm.getCalendarEntry();
515        
516        leaveCalendarForm.setLeaveCalendar(new LeaveCalendar(principalId, calendarEntry, assignmentKeys));
517        
518        LeaveBlockAggregate aggregate = new LeaveBlockAggregate(leaveBlocks, calendarEntry, leaveCalendarForm.getLeaveCalendar());
519        leaveCalendarForm.setLeaveBlockString(LeaveActionFormUtils.getLeaveBlocksJson(aggregate.getFlattenedLeaveBlockList()));
520    }
521    
522    protected void setLeaveSummary(LeaveCalendarForm leaveCalendarForm) throws Exception {
523        String principalId = HrContext.getTargetPrincipalId();
524        CalendarEntry calendarEntry = leaveCalendarForm.getCalendarEntry();
525        PrincipalHRAttributes principalHRAttributes = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, calendarEntry.getEndPeriodFullDateTime().toLocalDate());
526
527        //check to see if we are on a previous leave plan
528        if (principalHRAttributes != null) {
529            DateTime currentYearBeginDate = HrServiceLocator.getLeavePlanService().getFirstDayOfLeavePlan(principalHRAttributes.getLeavePlan(), LocalDate.now());
530            DateTime calEntryEndDate = calendarEntry.getEndPeriodFullDateTime();
531            if (calEntryEndDate.getMillis() > currentYearBeginDate.getMillis()) {
532                //current or future year
533                LeaveSummary ls = LmServiceLocator.getLeaveSummaryService().getLeaveSummary(principalId, calendarEntry);
534                leaveCalendarForm.setLeaveSummary(ls);
535            } else {
536                //current year roll over date has been passed, all previous calendars belong to the previous leave plan calendar year.
537                DateTime effDate = HrServiceLocator.getLeavePlanService().getRolloverDayOfLeavePlan(principalHRAttributes.getLeavePlan(), calEntryEndDate.minusDays(1).toLocalDate());
538                LeaveSummary ls = LmServiceLocator.getLeaveSummaryService().getLeaveSummaryAsOfDateWithoutFuture(principalId, effDate.toLocalDate());
539                //override title element (based on date passed in)
540                DateFormat formatter = new SimpleDateFormat("MMMM d");
541                DateFormat formatter2 = new SimpleDateFormat("MMMM d yyyy");
542                DateTime entryEndDate = new LocalDateTime(calendarEntry.getEndPeriodDate()).toDateTime();
543                if (entryEndDate.getHourOfDay() == 0) {
544                    entryEndDate = entryEndDate.minusDays(1);
545                }
546                String aString = formatter.format(calendarEntry.getBeginPeriodDate()) + " - " + formatter2.format(entryEndDate.toDate());
547                ls.setPendingDatesString(aString);
548                DateTimeFormatter fmt = DateTimeFormat.forPattern("MMM d, yyyy");
549                ls.setNote("Values as of: " + fmt.print(effDate));
550                leaveCalendarForm.setLeaveSummary(ls);
551            }
552        }
553    }
554    
555    protected void setMessages(LeaveCalendarForm leaveCalendarForm, List<LeaveBlock> leaveBlocks) {
556        String principalId = HrContext.getTargetPrincipalId();
557        CalendarEntry calendarEntry = leaveCalendarForm.getCalendarEntry();
558        PrincipalHRAttributes principalHRAttributes = HrServiceLocator.getPrincipalHRAttributeService().getPrincipalCalendar(principalId, calendarEntry.getEndPeriodFullDateTime().toLocalDate());
559        
560        Map<String, Set<String>> allMessages = LeaveCalendarValidationUtil.getWarningMessagesForLeaveBlocks(leaveBlocks, calendarEntry.getBeginPeriodDate(), calendarEntry.getEndPeriodDate());
561
562        // add warning message for accrual categories that have exceeded max balance.
563        // Could set a flag on the transferable rows here so that LeaveCalendarSubmit.do knows
564        // which row(s) to transfer when user submits the calendar for approval.
565        List<BalanceTransfer> losses = new ArrayList<BalanceTransfer>();
566
567        Interval calendarInterval = new Interval(calendarEntry.getBeginPeriodDate().getTime(), calendarEntry.getEndPeriodDate().getTime());
568        Map<String,Set<LeaveBlock>> maxBalInfractions = new HashMap<String,Set<LeaveBlock>>();
569        
570        Date effectiveDate = LocalDate.now().toDate();
571        if (!calendarInterval.contains(effectiveDate.getTime())) {
572                effectiveDate = calendarEntry.getEndPeriodDate();
573        }
574        
575        if (principalHRAttributes != null) {
576                maxBalInfractions = LmServiceLocator.getAccrualCategoryMaxBalanceService().getMaxBalanceViolations(calendarEntry, principalId);
577                
578                LeaveSummary summary = leaveCalendarForm.getLeaveSummary();
579                for (Entry<String,Set<LeaveBlock>> entry : maxBalInfractions.entrySet()) {
580                        for (LeaveBlock lb : entry.getValue()) {
581                                AccrualCategory accrualCat = lb.getAccrualCategoryObj();
582                                AccrualCategoryRule aRule = lb.getAccrualCategoryRule();
583                                List<LeaveSummaryRow> summaryRows = summary.getLeaveSummaryRows();
584                                if (StringUtils.equals(aRule.getActionAtMaxBalance(),HrConstants.ACTION_AT_MAX_BALANCE.LOSE)) {
585                                        DateTime aDate = null;
586                                        if (StringUtils.equals(aRule.getMaxBalanceActionFrequency(), HrConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
587                                                aDate = HrServiceLocator.getLeavePlanService().getRolloverDayOfLeavePlan(principalHRAttributes.getLeavePlan(), lb.getLeaveLocalDate());
588                                        } else {
589                                                Calendar cal = HrServiceLocator.getCalendarService().getCalendarByPrincipalIdAndDate(principalId, new LocalDate(lb.getLeaveDate()), true);
590                                                CalendarEntry leaveEntry = HrServiceLocator.getCalendarEntryService().getCurrentCalendarEntryByCalendarId(cal.getHrCalendarId(), new DateTime(lb.getLeaveDate()));
591                                                aDate = new DateTime(leaveEntry.getEndPeriodDate());
592                                        }
593                                        aDate = aDate.minusDays(1);
594                                        if (calendarInterval.contains(aDate.getMillis()) && aDate.toDate().compareTo(calendarEntry.getEndPeriodDate()) <= 0) {
595                                                //may want to calculate summary for all rows, displayable or not, and determine displayability via tags.
596                                                AccrualCategory accrualCategory = HrServiceLocator.getAccrualCategoryService().getAccrualCategory(aRule.getLmAccrualCategoryId());
597                                                BigDecimal accruedBalance = LmServiceLocator.getAccrualService().getAccruedBalanceForPrincipal(principalId, accrualCategory, lb.getLeaveLocalDate());
598                                                
599                                                BalanceTransfer loseTransfer = LmServiceLocator.getBalanceTransferService().initializeTransfer(principalId, lb.getAccrualCategoryRuleId(), accruedBalance, lb.getLeaveLocalDate());
600                                                boolean valid = BalanceTransferValidationUtils.validateTransfer(loseTransfer);
601                                                if (valid) {
602                                                        //validates again before the "transfer" action is triggered on the forfeiture.
603                                                        losses.add(loseTransfer);
604                                                }
605                                        }
606                                } else if (StringUtils.equals(HrConstants.MAX_BAL_ACTION_FREQ.ON_DEMAND, aRule.getMaxBalanceActionFrequency())) {
607                                        if (calendarInterval.contains(lb.getLeaveDate().getTime())) {
608                                                // accrual categories within the leave plan that are hidden from the leave summary will not appear.
609                                                List<LeaveSummaryRow> updatedSummaryRows = new ArrayList<LeaveSummaryRow>(summaryRows.size());
610                                                //AccrualCategoryRule currentRule = HrServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRuleForDate(accrualCat, effectiveDate, principalCalendar.getServiceDate());
611                                                for (LeaveSummaryRow summaryRow : summaryRows) {
612                                                        if (StringUtils.equals(summaryRow.getAccrualCategory(),accrualCat.getAccrualCategory())) {
613                                                                if (StringUtils.equals(aRule.getActionAtMaxBalance(),HrConstants.ACTION_AT_MAX_BALANCE.PAYOUT)) {
614                                                                        summaryRow.setPayoutable(true);
615                                                                } else {
616                                                                        if (StringUtils.equals(aRule.getActionAtMaxBalance(),HrConstants.ACTION_AT_MAX_BALANCE.TRANSFER)) {
617                                                                                summaryRow.setTransferable(true);
618                                                                        }
619                                                                }
620
621                                                                summaryRow.setInfractingLeaveBlockId(lb.getLmLeaveBlockId());
622                                                        }
623                                                        updatedSummaryRows.add(summaryRow);
624                                                }
625                                                summary.setLeaveSummaryRows(updatedSummaryRows);
626                                        }
627                                }
628
629                                if (calendarInterval.contains(lb.getLeaveDate().getTime())) {
630                                // accrual categories within the leave plan that are hidden from the leave summary WILL appear.
631                                        String message = "You have exceeded the maximum balance limit for '" + accrualCat.getAccrualCategory() + "' as of " + lb.getLeaveDate() + ". " 
632                                                        + "Depending upon the accrual category rules, leave over this limit may be forfeited.";
633                        //  leave blocks are sorted in getMaxBalanceViolations() method, so we just take the one with the earliest leave date for an accrual category.
634                                        if (!StringUtils.contains(allMessages.get("warningMessages").toString(), "You have exceeded the maximum balance limit for '" + accrualCat.getAccrualCategory())) {
635                            allMessages.get("warningMessages").add(message);
636                                        }
637                                }
638                        }
639                }
640                
641             // check for negative available balance for accrual category.
642                        for (LeaveSummaryRow summaryRow : summary.getLeaveSummaryRows()) {
643                        if(summaryRow.getLeaveBalance() != null && summaryRow.getLeaveBalance().compareTo(BigDecimal.ZERO) < 0) {
644                                String message = "Negative available balance found for the accrual category '"+summaryRow.getAccrualCategory()+ "'.";
645                                allMessages.get("warningMessages").add(message);
646                        }
647                        }
648                leaveCalendarForm.setLeaveSummary(summary);
649        }
650        leaveCalendarForm.setForfeitures(losses);
651        
652        Map<String,Set<String>> transactionalMessages = LeaveCalendarValidationUtil.validatePendingTransactions(principalId, calendarEntry.getBeginPeriodFullDateTime().toLocalDate(), calendarEntry.getEndPeriodFullDateTime().toLocalDate());
653        allMessages.get("infoMessages").addAll(transactionalMessages.get("infoMessages"));
654        allMessages.get("warningMessages").addAll(transactionalMessages.get("warningMessages"));
655        allMessages.get("actionMessages").addAll(transactionalMessages.get("actionMessages"));
656
657        // add warning messages based on max carry over balances for each accrual category
658        if (principalHRAttributes != null) {
659                        List<AccrualCategory> accrualCategories = HrServiceLocator.getAccrualCategoryService().getActiveLeaveAccrualCategoriesForLeavePlan(principalHRAttributes.getLeavePlan(), calendarEntry.getEndPeriodFullDateTime().toLocalDate());
660                        for (AccrualCategory accrualCategory : accrualCategories) {
661                                if (LmServiceLocator.getAccrualCategoryMaxCarryOverService().exceedsAccrualCategoryMaxCarryOver(accrualCategory.getAccrualCategory(), principalId, calendarEntry, calendarEntry.getEndPeriodFullDateTime().toLocalDate())) {
662                                        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.";
663                                        if (!allMessages.get("warningMessages").contains(message)) {
664                        allMessages.get("warningMessages").add(message);
665                                        }
666                                }
667                        }
668                }
669
670        leaveCalendarForm.setWarningMessages(new ArrayList<String>(allMessages.get("warningMessages")));
671        leaveCalendarForm.setInfoMessages(new ArrayList<String>(allMessages.get("infoMessages")));
672        leaveCalendarForm.setActionMessages(new ArrayList<String>(allMessages.get("actionMessages")));
673    }
674        
675        /**
676         * Recalculate accrual when a leave block with not-eligible-for-accrual earn code is added/deleted/updated
677         * calculate accrual only for the calendar entry period
678         * @param earnCode
679         * @param previousEarnCode
680         * @param asOfDate
681         * @param startDate
682         * @param endDate
683         */
684        private void rerunAccrualForNotEligibleForAccrualChanges(String earnCode, String previousEarnCode, LocalDate asOfDate, LocalDate startDate, LocalDate endDate) {
685                EarnCode ec = HrServiceLocator.getEarnCodeService().getEarnCode(earnCode, asOfDate);
686                EarnCode previousEc = null;
687                if(StringUtils.isNotBlank(previousEarnCode)) {
688                        previousEc = HrServiceLocator.getEarnCodeService().getEarnCode(previousEarnCode, asOfDate);
689                }
690                if((ec != null && ec.getEligibleForAccrual().equals("N"))
691                                || (previousEc != null && previousEc.getEligibleForAccrual().equals("N")) ) {
692                        if(startDate != null && endDate != null) {
693                                // since we are only recalculating accrual for this pay period, we use "false" to not record the accrual run data
694                                LmServiceLocator.getLeaveAccrualService().runAccrual(HrContext.getTargetPrincipalId(), startDate.toDateTimeAtStartOfDay(), endDate.toDateTimeAtStartOfDay(), false);
695                        }
696                }
697        }
698        
699        // KPME-1447
700        public ActionForward updateLeaveBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
701                LeaveCalendarForm lcf = (LeaveCalendarForm) form;
702                LeaveCalendarDocument lcd = lcf.getLeaveCalendarDocument();
703                
704                String principalId = HrContext.getPrincipalId();
705                String targetPrincipalId = HrContext.getTargetPrincipalId();
706                CalendarEntry calendarEntry = lcf.getCalendarEntry();
707                String selectedEarnCode = lcf.getSelectedEarnCode();
708                String leaveBlockId = lcf.getLeaveBlockId();
709                String approval = lcf.getApproval(); // KPME-2540
710                
711                String documentId = lcd != null ? lcd.getDocumentId() : "";
712                
713                LeaveBlock updatedLeaveBlock = null;
714                updatedLeaveBlock = LmServiceLocator.getLeaveBlockService().getLeaveBlock(leaveBlockId);
715                String previousEarnCode = null;
716                if(updatedLeaveBlock != null) {
717                        previousEarnCode = updatedLeaveBlock.getEarnCode();
718                }
719                
720                //KPME-2832: validate leave entry prior to save. 
721                //This duplicates validation done on submissions that went through LeaveCalendarWSAction, i.e. typical leave calendar transactions.
722                List<String> errorMsgList = LeaveCalendarValidationUtil.validateLeaveEntry(lcf);
723                if(!errorMsgList.isEmpty()) {
724                        lcf.setErrorMessages(errorMsgList);
725                        return mapping.findForward("basic");
726                }
727                
728        if (updatedLeaveBlock.isEditable()) {
729            if (StringUtils.isNotBlank(lcf.getDescription())) {
730                updatedLeaveBlock.setDescription(lcf.getDescription().trim());
731            }
732            if (!updatedLeaveBlock.getLeaveAmount().equals(lcf.getLeaveAmount())) {
733                updatedLeaveBlock.setLeaveAmount(lcf.getLeaveAmount());
734            }
735            
736            DateTime beginDate = null;
737                DateTime endDate = null;
738                
739                        beginDate = TKUtils.formatDateTimeStringNoTimezone(lcf.getStartDate());
740                        endDate = TKUtils.formatDateTimeStringNoTimezone(lcf.getEndDate());
741                updatedLeaveBlock.setLeaveDate(new Date(beginDate.getMillis()));
742                
743            EarnCode earnCode =  HrServiceLocator.getEarnCodeService().getEarnCode(selectedEarnCode, updatedLeaveBlock.getLeaveLocalDate()); // selectedEarnCode = hrEarnCodeId
744            if(earnCode != null && earnCode.getRecordMethod().equalsIgnoreCase(HrConstants.EARN_CODE_TIME)) {
745                if(lcf.getStartTime() != null && lcf.getEndTime() != null) {
746                                beginDate = TKUtils.convertDateStringToDateTimeWithoutZone(lcf.getStartDate(), lcf.getStartTime());
747                                endDate   = TKUtils.convertDateStringToDateTimeWithoutZone(lcf.getEndDate(), lcf.getEndTime());
748                        }  else {
749                                beginDate = TKUtils.formatDateTimeStringNoTimezone(lcf.getStartDate());
750                                endDate = TKUtils.formatDateTimeStringNoTimezone(lcf.getEndDate());
751                        }
752                updatedLeaveBlock.setBeginTimestamp(new Timestamp(beginDate.getMillis()));
753                updatedLeaveBlock.setEndTimestamp(new Timestamp(endDate.getMillis()));
754                updatedLeaveBlock.setLeaveAmount(TKUtils.getHoursBetween(beginDate.getMillis(), endDate.getMillis()));
755            }
756            
757            if (!updatedLeaveBlock.getEarnCode().equals(earnCode.getEarnCode())) {
758                updatedLeaveBlock.setEarnCode(earnCode.getEarnCode());
759            }
760            
761            LmServiceLocator.getLeaveBlockService().updateLeaveBlock(updatedLeaveBlock, principalId);
762            generateLeaveCalendarChangedNotification(principalId, targetPrincipalId, documentId, calendarEntry.getHrCalendarEntryId());
763            
764            lcf.setLeaveAmount(null);
765            lcf.setDescription(null);
766            lcf.setSelectedEarnCode(null);
767                // recalculate summary
768                if(lcf.getCalendarEntry() != null) {
769                        LeaveSummary ls = LmServiceLocator.getLeaveSummaryService().getLeaveSummary(targetPrincipalId, calendarEntry);
770                    lcf.setLeaveSummary(ls);
771                    // call accrual service if earn code is not eligible for accrual
772                    this.rerunAccrualForNotEligibleForAccrualChanges(selectedEarnCode, previousEarnCode, TKUtils.formatDateTimeStringNoTimezone(lcf.getEndDate()).toLocalDate(), calendarEntry.getBeginPeriodFullDateTime().toLocalDate(), calendarEntry.getEndPeriodFullDateTime().toLocalDate());
773                }
774                
775                // KPME-2540 replicate submitForApproval method in LeaveRequestAction here
776                if (!StringUtils.isEmpty(approval)) {
777                    LeaveRequestDocument lrd = LmServiceLocator.getLeaveRequestDocumentService().createLeaveRequestDocument(updatedLeaveBlock.getLmLeaveBlockId());
778                    LmServiceLocator.getLeaveRequestDocumentService().requestLeave(lrd.getDocumentNumber());
779                }
780        }
781        return mapping.findForward("basic");
782    }
783
784    private void setDocEditable(LeaveCalendarForm leaveForm, LeaveCalendarDocument lcd) {
785        leaveForm.setDocEditable(false);
786        if(lcd == null) {
787                // working on own calendar
788                 if(HrContext.getTargetPrincipalId().equals(GlobalVariables.getUserSession().getPrincipalId())) {
789                         leaveForm.setDocEditable(true); 
790                 } else {
791                         if(HrContext.isSystemAdmin()
792                     || TkContext.isLocationAdmin()
793                     || HrContext.isReviewer()
794                     || HrContext.isAnyApprover()
795                     || HrContext.isAnyPayrollProcessor()) {
796                                        leaveForm.setDocEditable(true);
797                         }
798             }
799        } else {
800                DocumentStatus documentStatus = KewApiServiceLocator.getWorkflowDocumentService().getDocumentStatus(lcd.getDocumentId());
801                if (HrContext.isSystemAdmin() && !StringUtils.equals(lcd.getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())) {
802                    leaveForm.setDocEditable(true);
803                } else {
804                    if (!DocumentStatus.FINAL.equals(documentStatus) 
805                                && !DocumentStatus.CANCELED.getCode().equals(documentStatus)
806                                        && !DocumentStatus.DISAPPROVED.getCode().equals(documentStatus)) {
807                        if(StringUtils.equals(lcd.getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId())
808                                || HrContext.isSystemAdmin()
809                                || TkContext.isLocationAdmin()
810                                || HrContext.isReviewer()
811                                || HrContext.isAnyApprover()
812                                || HrContext.isAnyPayrollProcessor()) {
813                            leaveForm.setDocEditable(true);
814                        }
815        
816                        //if the leave Calendar has been approved by at least one of the approvers, the employee should not be able to edit it
817                        if (StringUtils.equals(lcd.getPrincipalId(), GlobalVariables.getUserSession().getPrincipalId()) && DocumentStatus.ENROUTE.equals(documentStatus)) {
818                            Collection actions = KEWServiceLocator.getActionTakenService().findByDocIdAndAction(lcd.getDocumentId(), HrConstants.DOCUMENT_ACTIONS.APPROVE);
819                            if(!actions.isEmpty()) {
820                                leaveForm.setDocEditable(false);
821                            }
822                        }
823                    }
824                }
825        }
826    }
827    
828    // KPME-2540 
829    // To find out if this is a future leave calendar period, compare current date to the calendar entry begin date
830    // and check document id (if this is a future period, document id is empty).
831    // If it is a future calendar entry, set blockSubmittable to true and retrieve it in LeaveCalendar.jsp
832    // Note if current date is 8/5 and the calendar period is 8/1 - 8/15, it is considered a current period
833    private void setBlockSubmittable(LeaveCalendarForm leaveForm, LeaveCalendarDocument lcd) {
834        
835        leaveForm.setBlockSubmittable(false); 
836        
837        if(leaveForm != null) {
838                // Do NOT use leaveForm.getStartDate - We don't know why it's there
839            if (leaveForm.getCalendarEntry().getBeginPeriodDate() != null && StringUtils.isBlank(leaveForm.getDocumentId())) {
840                if (LocalDate.now().isBefore(leaveForm.getCalendarEntry().getBeginPeriodLocalDateTime().toLocalDate())) {
841                        leaveForm.setBlockSubmittable(true);
842                }
843            }             
844        }
845    }
846    
847        
848        private void generateLeaveCalendarChangedNotification(String principalId, String targetPrincipalId, String documentId, String hrCalendarEntryId) {
849                if (!StringUtils.equals(principalId, targetPrincipalId)) {
850                        EntityNamePrincipalName person = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(principalId);
851                        if (person != null && person.getDefaultName() != null) {
852                                String subject = "Leave Calendar Modification Notice";
853                                StringBuilder message = new StringBuilder();
854                                message.append("Your Leave Calendar was changed by ");
855                                message.append(person.getDefaultName().getCompositeNameUnmasked());
856                                message.append(" on your behalf.");
857                                message.append(SystemUtils.LINE_SEPARATOR);
858                                message.append(getLeaveCalendarURL(documentId, hrCalendarEntryId));
859                                
860                                HrServiceLocator.getKPMENotificationService().sendNotification(subject, message.toString(), targetPrincipalId);
861                        }
862                }
863        }
864        
865        private void generateLeaveBlockDeletionNotification(List<String> approverIdList, String employeeId, String userId, String dateString, String hrString) {
866        EntityNamePrincipalName employee = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(employeeId);
867        EntityNamePrincipalName user = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(userId);
868                if (employee != null
869                && user != null
870                && employee.getDefaultName() != null
871                && user.getDefaultName() != null) {
872                        String subject = "Leave Request Deletion Notice";
873                        StringBuilder message = new StringBuilder();
874                        message.append("An Approved leave request of ").append(hrString).append(" hours on Date ").append(dateString);
875                        message.append(" for ").append(employee.getDefaultName().getCompositeNameUnmasked()).append(" was deleted by ");
876                        message.append(user.getDefaultName().getCompositeNameUnmasked());
877                        for(String anId : approverIdList) {
878                                HrServiceLocator.getKPMENotificationService().sendNotification(subject, message.toString(), anId);
879                        }
880                }
881        }
882        
883        @SuppressWarnings("deprecation")
884        private String getLeaveCalendarURL(String documentId, String hrCalendarEntryId) {
885                Properties params = new Properties();
886                params.put("documentId", documentId);
887                params.put("hrCalendarEntryId", hrCalendarEntryId);
888                return UrlFactory.parameterizeUrl(getApplicationBaseUrl() + "/LeaveCalendar.do", params);
889        }
890
891}