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