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.request.approval.web;
017    
018    import java.sql.Date;
019    import java.text.DateFormat;
020    import java.text.SimpleDateFormat;
021    import java.util.ArrayList;
022    import java.util.Collections;
023    import java.util.Comparator;
024    import java.util.HashMap;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    
029    import javax.servlet.http.HttpServletRequest;
030    import javax.servlet.http.HttpServletResponse;
031    
032    import org.apache.commons.collections.CollectionUtils;
033    import org.apache.commons.lang.ObjectUtils;
034    import org.apache.commons.lang.StringUtils;
035    import org.apache.log4j.Logger;
036    import org.apache.struts.action.ActionForm;
037    import org.apache.struts.action.ActionForward;
038    import org.apache.struts.action.ActionMapping;
039    import org.hsqldb.lib.StringUtil;
040    import org.json.simple.JSONArray;
041    import org.json.simple.JSONValue;
042    import org.kuali.hr.lm.leaveblock.LeaveBlock;
043    import org.kuali.hr.lm.workflow.LeaveRequestDocument;
044    import org.kuali.hr.time.assignment.Assignment;
045    import org.kuali.hr.time.base.web.ApprovalAction;
046    import org.kuali.hr.time.service.base.TkServiceLocator;
047    import org.kuali.hr.time.util.TKContext;
048    import org.kuali.hr.time.util.TKUser;
049    import org.kuali.hr.time.util.TKUtils;
050    import org.kuali.hr.time.workarea.WorkArea;
051    import org.kuali.rice.kew.api.KewApiServiceLocator;
052    import org.kuali.rice.kew.api.action.ActionItem;
053    import org.kuali.rice.kim.api.identity.Person;
054    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
055    
056    public class LeaveRequestApprovalAction  extends ApprovalAction {
057            
058        private static final Logger LOG = Logger.getLogger(LeaveRequestApprovalAction.class);
059        public static final String DOC_SEPARATOR = "----";  // separator for documents
060        public static final String ID_SEPARATOR = "____";   // separator for documentId and reason string
061        public static final String DOC_NOT_FOUND = "Leave request document not found with id ";
062        public static final String LEAVE_BLOCK_NOT_FOUND = "Leave Block not found for Leave request document ";
063        
064            @Override
065            public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
066            ActionForward forward = super.execute(mapping, form, request, response);
067                    LeaveRequestApprovalActionForm lraaForm = (LeaveRequestApprovalActionForm) form;
068                    
069                    Date currentDate = TKUtils.getCurrentDate();
070                    Set<Long> workAreas = TkServiceLocator.getTkRoleService().getWorkAreasForApprover(TKContext.getPrincipalId(), currentDate);
071                    List<String> principalIds = new ArrayList<String>();
072            for (Long workArea : workAreas) {
073                List<Assignment> assignments = TkServiceLocator.getAssignmentService().getActiveAssignmentsForWorkArea(workArea, currentDate);
074                for (Assignment a : assignments) {
075                    principalIds.add(a.getPrincipalId());
076                }
077            }
078    
079            // Set calendar groups
080            List<String> calGroups =  new ArrayList<String>();
081            if (CollectionUtils.isNotEmpty(principalIds)) {
082                calGroups = TkServiceLocator.getLeaveApprovalService().getUniqueLeavePayGroupsForPrincipalIds(principalIds);
083            }
084            lraaForm.setPayCalendarGroups(calGroups);               
085                    
086                    List<String> depts = new ArrayList<String>(TKContext.getUser().getReportingApprovalDepartments().keySet());
087                    Collections.sort(depts);
088                lraaForm.setDepartments(depts);
089                    
090                    // build employee rows to display on the page
091                List<ActionItem> items = filterActionsWithSeletedParameters(lraaForm.getSelectedPayCalendarGroup(),
092                                    lraaForm.getSelectedDept(), this.getWorkAreaList(lraaForm));
093                    List<LeaveRequestApprovalEmployeeRow> rowList = this.getEmployeeRows(items);
094                    lraaForm.setEmployeeRows(rowList);
095                    return forward;
096            }
097            
098            public ActionForward loadApprovalTab(ActionMapping mapping, ActionForm form,
099                            HttpServletRequest request, HttpServletResponse response) throws Exception {
100                    ActionForward fwd = mapping.findForward("basic");
101                    LeaveRequestApprovalActionForm lraaForm = (LeaveRequestApprovalActionForm) form;
102            Date currentDate = TKUtils.getCurrentDate();
103            
104            //reset state
105            if(StringUtils.isEmpty(lraaForm.getSelectedDept())) {
106                    resetState(form, request);
107            
108                    Set<Long> workAreas = TkServiceLocator.getTkRoleService().getWorkAreasForApprover(TKContext.getPrincipalId(), currentDate);
109                    List<String> principalIds = new ArrayList<String>();
110                    for (Long workArea : workAreas) {
111                        List<Assignment> assignments = TkServiceLocator.getAssignmentService().getActiveAssignmentsForWorkArea(workArea, currentDate);
112                        for (Assignment a : assignments) {
113                            principalIds.add(a.getPrincipalId());
114                        }
115                    }
116                    // Set calendar groups
117                    List<String> calGroups =  new ArrayList<String>();
118                    if (CollectionUtils.isNotEmpty(principalIds)) {
119                        calGroups = TkServiceLocator.getLeaveApprovalService().getUniqueLeavePayGroupsForPrincipalIds(principalIds);
120                    }
121                    lraaForm.setPayCalendarGroups(calGroups);
122                    if (StringUtils.isBlank(lraaForm.getSelectedPayCalendarGroup())
123                            && CollectionUtils.isNotEmpty(calGroups)) {
124                            lraaForm.setSelectedPayCalendarGroup(calGroups.get(0));
125                    }
126                    // set departments
127                            List<String> depts = new ArrayList<String>(TKContext.getUser().getReportingApprovalDepartments().keySet());
128                            Collections.sort(depts);
129                        lraaForm.setDepartments(depts);
130                        if (StringUtils.isBlank(lraaForm.getSelectedDept())
131                            && lraaForm.getDepartments().size() == 1) {
132                            lraaForm.setSelectedDept(lraaForm.getDepartments().get(0));
133                            lraaForm.getWorkAreaDescr().clear();
134                            List<WorkArea> workAreaList = TkServiceLocator.getWorkAreaService().getWorkAreas(lraaForm.getSelectedDept(), currentDate);
135                        for(WorkArea wa : workAreaList){
136                            if (TKContext.getUser().getApproverWorkAreas().contains(wa.getWorkArea())
137                                            || TKContext.getUser().getReviewerWorkAreas().contains(wa.getWorkArea())) {
138                                    lraaForm.getWorkAreaDescr().put(wa.getWorkArea(),wa.getDescription()+"("+wa.getWorkArea()+")");
139                            }
140                        }
141                    }                   
142            }
143            
144            // build employee rows to display on the page
145            List<ActionItem> items = filterActionsWithSeletedParameters(lraaForm.getSelectedPayCalendarGroup(),
146                                    lraaForm.getSelectedDept(), this.getWorkAreaList(lraaForm));
147            List<LeaveRequestApprovalEmployeeRow> rowList = this.getEmployeeRows(items);
148            lraaForm.setEmployeeRows(rowList);           
149            
150            return fwd;
151            }
152            
153            public ActionForward selectNewPayCalendar(ActionMapping mapping, ActionForm form,
154                            HttpServletRequest request, HttpServletResponse response)
155                            throws Exception {
156                    // resets the common fields for approval pages
157                    super.resetMainFields(form);
158                    LeaveRequestApprovalActionForm lraaForm = (LeaveRequestApprovalActionForm) form;
159                    lraaForm.setEmployeeRows(new ArrayList<LeaveRequestApprovalEmployeeRow>());
160                    
161                    return loadApprovalTab(mapping, form, request, response);
162            }       
163            
164            public ActionForward selectNewDept(ActionMapping mapping, ActionForm form,
165                            HttpServletRequest request, HttpServletResponse response)
166                            throws Exception {
167                    LeaveRequestApprovalActionForm lraaForm = (LeaveRequestApprovalActionForm) form;
168                    
169                    Date currentDate = TKUtils.getCurrentDate();
170                    
171                    lraaForm.getWorkAreaDescr().clear();
172            List<WorkArea> workAreas = TkServiceLocator.getWorkAreaService().getWorkAreas(lraaForm.getSelectedDept(), currentDate);
173            for(WorkArea wa : workAreas){
174                    if (TKContext.getUser().getApproverWorkAreas().contains(wa.getWorkArea())
175                                    || TKContext.getUser().getReviewerWorkAreas().contains(wa.getWorkArea())) {
176                            lraaForm.getWorkAreaDescr().put(wa.getWorkArea(),wa.getDescription()+"("+wa.getWorkArea()+")");
177                    }
178            }
179            // filter actions with selected calendarGroup, Dept and workarea
180            List<ActionItem> items = filterActionsWithSeletedParameters(lraaForm.getSelectedPayCalendarGroup(),
181                                    lraaForm.getSelectedDept(), this.getWorkAreaList(lraaForm));
182            List<LeaveRequestApprovalEmployeeRow> rowList = this.getEmployeeRows(items);
183                    lraaForm.setEmployeeRows(rowList);      
184            
185                    return mapping.findForward("basic");
186            }
187            
188            public ActionForward selectNewWorkArea(ActionMapping mapping, ActionForm form,
189                            HttpServletRequest request, HttpServletResponse response)
190                            throws Exception {
191                    LeaveRequestApprovalActionForm lraaForm = (LeaveRequestApprovalActionForm) form;
192                        
193                    // filter actions with selected calendarGroup, Dept and workarea
194            List<ActionItem> items = filterActionsWithSeletedParameters(lraaForm.getSelectedPayCalendarGroup(),
195                                    lraaForm.getSelectedDept(), this.getWorkAreaList(lraaForm));
196            List<LeaveRequestApprovalEmployeeRow> rowList = this.getEmployeeRows(items);
197                    lraaForm.setEmployeeRows(rowList);
198            
199                    return mapping.findForward("basic");
200            }
201            
202            private List<ActionItem> filterActionsWithSeletedParameters(String calGroup, String dept, List<String> workAreaList) {
203                    String principalId = TKUser.getCurrentTargetPerson().getPrincipalId();
204                    List<ActionItem> actionList = KewApiServiceLocator.getActionListService().getActionItemsForPrincipal(principalId);
205                    List<ActionItem> resultsList = new ArrayList<ActionItem>();
206    
207                    Date currentDate = TKUtils.getCurrentDate();
208                    List<String> principalIds = TkServiceLocator.getLeaveApprovalService()
209                            .getLeavePrincipalIdsWithSearchCriteria(workAreaList, calGroup, currentDate, currentDate, currentDate);    
210                    
211                    if(CollectionUtils.isNotEmpty(principalIds)) {
212                            for(ActionItem anAction : actionList) {
213                                    String docId = anAction.getDocumentId();
214                                    if(anAction.getDocName().equals(LeaveRequestDocument.LEAVE_REQUEST_DOCUMENT_TYPE)) {
215                                            LeaveRequestDocument lrd = TkServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocument(docId);
216                                            if(lrd != null) {
217                                                    LeaveBlock lb = lrd.getLeaveBlock();
218                                                    if(lb != null) {
219                                                            if(principalIds.contains(lb.getPrincipalId())) {
220                                                                    resultsList.add(anAction);
221                                                            }
222                                                    }
223                                            }
224                                    }
225                            }
226                    }
227            
228            return resultsList;
229            }
230            
231            private List<String> getWorkAreaList(LeaveRequestApprovalActionForm lraaForm) {
232                    List<String> workAreaList = new ArrayList<String>();
233                if(StringUtil.isEmpty(lraaForm.getSelectedWorkArea())) {
234                    for(Long aKey : lraaForm.getWorkAreaDescr().keySet()) {
235                            workAreaList.add(aKey.toString());
236                    }
237                } else {
238                    workAreaList.add(lraaForm.getSelectedWorkArea());
239                }
240                return workAreaList;
241            }
242            
243            public void resetState(ActionForm form, HttpServletRequest request) {
244                    LeaveRequestApprovalActionForm lraaForm = (LeaveRequestApprovalActionForm) form;
245            lraaForm.getDepartments().clear();
246            lraaForm.getWorkAreaDescr().clear();
247            lraaForm.setEmployeeRows(new ArrayList<LeaveRequestApprovalEmployeeRow>());
248            lraaForm.setSelectedDept(null);
249            lraaForm.setSearchField(null);
250            lraaForm.setSearchTerm(null);
251            }
252            
253            public ActionForward takeActionOnEmployee(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
254                    LeaveRequestApprovalActionForm lraaForm = (LeaveRequestApprovalActionForm) form;
255                    
256                    if(StringUtils.isNotEmpty(lraaForm.getApproveList())) {
257                            String[] approveList = lraaForm.getApproveList().split(DOC_SEPARATOR);
258                            for(String eachAction : approveList){
259                                    String[] fields = eachAction.split(ID_SEPARATOR);
260                                    String docId = fields[0];       // leave request document id
261                                    String reasonString = fields.length > 1 ? fields[1] : "";    // approve reason text, could be empty
262                                    TkServiceLocator.getLeaveRequestDocumentService().approveLeave(docId, TKContext.getPrincipalId(), reasonString);
263                                    // leave block's status is changed to "approved" in postProcessor of LeaveRequestDocument
264                            }
265                    }
266                    if(StringUtils.isNotEmpty(lraaForm.getDisapproveList())) {
267                            String[] disapproveList = lraaForm.getDisapproveList().split(DOC_SEPARATOR);
268                            for(String eachAction : disapproveList){
269                                    String[] fields = eachAction.split(ID_SEPARATOR);
270                                    String docId = fields[0];       // leave request document id
271                                    String reasonString = fields.length > 1 ? fields[1] : "";    // disapprove reason
272                                    TkServiceLocator.getLeaveRequestDocumentService().disapproveLeave(docId, TKContext.getPrincipalId(), reasonString);
273                                    // leave block's status is changed to "disapproved" in postProcessor of LeaveRequestDocument    
274                            }
275                    }
276                    if(StringUtils.isNotEmpty(lraaForm.getDeferList())) {
277                            String[] deferList = lraaForm.getDeferList().split(DOC_SEPARATOR);
278                            for(String eachAction : deferList){
279                                    String[] fields = eachAction.split(ID_SEPARATOR);
280                                    String docId = fields[0];       // leave request document id
281                                    String reasonString =  fields.length > 1 ? fields[1] : "";   // defer reason
282                                    TkServiceLocator.getLeaveRequestDocumentService().deferLeave(docId, TKContext.getPrincipalId(), reasonString);
283                                    // leave block's status is changed to "deferred" in postProcessor of LeaveRequestDocument       
284                            }
285                    }
286                    return mapping.findForward("basic");
287            }
288    
289            private List<LeaveRequestApprovalEmployeeRow> getEmployeeRows(List<ActionItem> actionList) {
290                    List<LeaveRequestApprovalEmployeeRow> empRowList = new ArrayList<LeaveRequestApprovalEmployeeRow>();
291                    Map<String, List<LeaveRequestDocument>> docMap = new HashMap<String, List<LeaveRequestDocument>>();
292                    for(ActionItem action : actionList) {
293                            if(action.getDocName().equals(LeaveRequestDocument.LEAVE_REQUEST_DOCUMENT_TYPE)) {
294                                    String docId = action.getDocumentId();
295                                    LeaveRequestDocument lrd = TkServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocument(docId);
296                                    if(lrd != null) {
297                                            LeaveBlock lb = lrd.getLeaveBlock();
298                                            if(lb != null) {
299                                                    String lbPrincipalId = lb.getPrincipalId();
300                                                    List<LeaveRequestDocument> docList = docMap.get(lbPrincipalId) == null ? new ArrayList<LeaveRequestDocument>() : docMap.get(lbPrincipalId);
301                                                    docList.add(lrd);
302                                                    docMap.put(lbPrincipalId, docList);
303                                            }
304                                    }
305                                    
306                            }
307                    }
308                    for (Map.Entry<String, List<LeaveRequestDocument>> entry : docMap.entrySet()) {
309                            LeaveRequestApprovalEmployeeRow aRow = this.getAnEmployeeRow(entry.getKey(), entry.getValue());
310                            if(aRow != null) {
311                                    empRowList.add(aRow);
312                            }
313                    }
314                    // sort list by employee name
315                    Collections.sort(empRowList, new Comparator<LeaveRequestApprovalEmployeeRow>() {
316                            @Override
317                            public int compare(LeaveRequestApprovalEmployeeRow row1, LeaveRequestApprovalEmployeeRow row2) {
318                                    return ObjectUtils.compare(row1.getEmployeeName(), row2.getEmployeeName());
319                            }
320            });             
321                    
322                    return empRowList;
323            }
324            
325            private LeaveRequestApprovalEmployeeRow getAnEmployeeRow(String principalId, List<LeaveRequestDocument> docList) {
326                    if(CollectionUtils.isEmpty(docList) || StringUtils.isEmpty(principalId)) {
327                            return null;
328                    }
329                    Person principal = KimApiServiceLocator.getPersonService().getPerson(principalId);
330                    if(principal == null) {
331                            return null;
332                    }
333                    LeaveRequestApprovalEmployeeRow empRow = new LeaveRequestApprovalEmployeeRow();
334                    empRow.setPrincipalId(principalId);
335                    empRow.setEmployeeName(principal.getName());
336                    List<LeaveRequestApprovalRow> rowList = new ArrayList<LeaveRequestApprovalRow>();
337                    for(LeaveRequestDocument lrd : docList) {
338                            if(lrd == null) {
339                                    return null;
340                            }
341                            LeaveBlock lb = lrd.getLeaveBlock();
342                            if(lb == null) {
343                                    return null;
344                            }
345                            LeaveRequestApprovalRow aRow = new LeaveRequestApprovalRow();
346                            aRow.setLeaveRequestDocId(lrd.getDocumentNumber());
347                            aRow.setLeaveCode(lb.getEarnCode());
348                            aRow.setRequestedDate(TKUtils.formatDate(lb.getLeaveDate()));
349                            aRow.setRequestedHours(lb.getLeaveAmount().toString());
350                            aRow.setDescription(lrd.getDescription());
351                            DateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
352                            aRow.setSubmittedTime(df.format( new Date(lrd.getDocumentHeader().getWorkflowDocument().getDateCreated().getMillis())));
353                            rowList.add(aRow);
354                    }
355                    // sort list by date
356                    if(CollectionUtils.isNotEmpty(rowList)) {
357                            Collections.sort(rowList, new Comparator<LeaveRequestApprovalRow>() {
358                                    @Override
359                                    public int compare(LeaveRequestApprovalRow row1, LeaveRequestApprovalRow row2) {
360                                            return ObjectUtils.compare(row1.getRequestedDate(), row2.getRequestedDate());
361                                    }
362                    });
363                            empRow.setLeaveRequestList(rowList);
364                    }
365                    return empRow;
366            }
367    
368            @SuppressWarnings("unchecked")
369            public ActionForward validateActions(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
370                    LeaveRequestApprovalActionForm lraaForm = (LeaveRequestApprovalActionForm) form;
371                    JSONArray errorMsgList = new JSONArray();
372            List<String> errors = new ArrayList<String>();
373            if(StringUtils.isEmpty(lraaForm.getApproveList()) 
374                            && StringUtils.isEmpty(lraaForm.getDisapproveList())
375                            && StringUtils.isEmpty(lraaForm.getDeferList())) {
376                    errors.add("No Actions selected. Please try again.");
377            } else {
378                            if(StringUtils.isNotEmpty(lraaForm.getApproveList())) {
379                                    String[] approveList = lraaForm.getApproveList().split(DOC_SEPARATOR);
380                                    for(String eachAction : approveList){
381                                            String[] fields = eachAction.split(ID_SEPARATOR);
382                                            String docId = fields[0];       // leave request document id
383                                            LeaveRequestDocument lrd = TkServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocument(docId);
384                                            if(lrd == null) {
385                                                    errors.add(DOC_NOT_FOUND + docId);
386                                                    break;
387                                            } else {
388                                                    LeaveBlock lb = TkServiceLocator.getLeaveBlockService().getLeaveBlock(lrd.getLmLeaveBlockId());
389                                                    if(lb == null) {
390                                                            errors.add(LEAVE_BLOCK_NOT_FOUND + docId);
391                                                            break;
392                                                    }
393                                            }
394                                    }
395                            }
396                            if(StringUtils.isNotEmpty(lraaForm.getDisapproveList())) {
397                                    String[] disapproveList = lraaForm.getDisapproveList().split(DOC_SEPARATOR);
398                                    for(String eachAction : disapproveList){
399                                            String[] fields = eachAction.split(ID_SEPARATOR);
400                                            String docId = fields[0];       // leave request document id
401                                            String reasonString = fields.length > 1 ? fields[1] : "";    // disapprove reason
402                                            if(StringUtils.isEmpty(reasonString)) {
403                                                    errors.add("Reason is required for Disapprove action");
404                                                    break;
405                                            } else {
406                                                    LeaveRequestDocument lrd = TkServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocument(docId);
407                                                    if(lrd == null) {
408                                                            errors.add(DOC_NOT_FOUND + docId);
409                                                            break;
410                                                    }else {
411                                                            LeaveBlock lb = TkServiceLocator.getLeaveBlockService().getLeaveBlock(lrd.getLmLeaveBlockId());
412                                                            if(lb == null) {
413                                                                    errors.add(LEAVE_BLOCK_NOT_FOUND + docId);
414                                                                    break;
415                                                            }
416                                                    }
417                                            }
418                                    }
419                            }
420                            if(StringUtils.isNotEmpty(lraaForm.getDeferList())) {
421                                    String[] deferList = lraaForm.getDeferList().split(DOC_SEPARATOR);
422                                    for(String eachAction : deferList){
423                                            String[] fields = eachAction.split(ID_SEPARATOR);
424                                            String docId = fields[0];       // leave request document id
425                                            String reasonString =  fields.length > 1 ? fields[1] : "";   // defer reason
426                                            if(StringUtils.isEmpty(reasonString)) {
427                                                    errors.add("Reason is required for Defer action");
428                                                    break;
429                                            } else {
430                                                    LeaveRequestDocument lrd = TkServiceLocator.getLeaveRequestDocumentService().getLeaveRequestDocument(docId);
431                                                    if(lrd == null) {
432                                                            errors.add(DOC_NOT_FOUND + docId);
433                                                            break;
434                                                    }else {
435                                                            LeaveBlock lb = TkServiceLocator.getLeaveBlockService().getLeaveBlock(lrd.getLmLeaveBlockId());
436                                                            if(lb == null) {
437                                                                    errors.add(LEAVE_BLOCK_NOT_FOUND + docId);
438                                                                    break;
439                                                            }
440                                                    }
441                                            }
442                                    }
443                            }
444            }
445            errorMsgList.addAll(errors);
446            lraaForm.setOutputString(JSONValue.toJSONString(errorMsgList));
447            return mapping.findForward("ws");
448        }   
449    }