org.kuali.kpme.tklm.time.clock.web.ClockAction.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kpme.tklm.time.clock.web.ClockAction.java

Source

/**
 * Copyright 2004-2013 The Kuali Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.opensource.org/licenses/ecl2.php
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kuali.kpme.tklm.time.clock.web;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.json.simple.JSONArray;
import org.json.simple.JSONValue;
import org.kuali.kpme.core.KPMENamespace;
import org.kuali.kpme.core.assignment.Assignment;
import org.kuali.kpme.core.assignment.AssignmentDescriptionKey;
import org.kuali.kpme.core.document.calendar.CalendarDocument;
import org.kuali.kpme.core.role.KPMERole;
import org.kuali.kpme.core.service.HrServiceLocator;
import org.kuali.kpme.core.util.HrConstants;
import org.kuali.kpme.core.util.HrContext;
import org.kuali.kpme.core.util.TKUtils;
import org.kuali.kpme.core.workarea.WorkArea;
import org.kuali.kpme.tklm.common.TkConstants;
import org.kuali.kpme.tklm.time.clocklog.ClockLog;
import org.kuali.kpme.tklm.time.rules.lunch.department.DeptLunchRule;
import org.kuali.kpme.tklm.time.rules.timecollection.TimeCollectionRule;
import org.kuali.kpme.tklm.time.service.TkServiceLocator;
import org.kuali.kpme.tklm.time.timeblock.TimeBlock;
import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
import org.kuali.kpme.tklm.time.timesheet.web.TimesheetAction;
import org.kuali.rice.krad.exception.AuthorizationException;
import org.kuali.rice.krad.util.GlobalVariables;
import org.springframework.cache.annotation.CacheEvict;

public class ClockAction extends TimesheetAction {

    public static final SimpleDateFormat SDF = new SimpleDateFormat("EEE, MMMM d yyyy HH:mm:ss, zzzz");
    public static final String SEPERATOR = "[****]+";

    @Override
    protected void checkTKAuthorization(ActionForm form, String methodToCall) throws AuthorizationException {
        super.checkTKAuthorization(form, methodToCall); // Checks for read access first.

        ClockActionForm clockActionForm = (ClockActionForm) form;

        String principalId = GlobalVariables.getUserSession().getPrincipalId();
        CalendarDocument timesheetDocument = TkServiceLocator.getTimesheetService()
                .getTimesheetDocument(clockActionForm.getDocumentId());
        // Check for write access to Timeblock.
        if (StringUtils.equals(methodToCall, "clockAction") || StringUtils.equals(methodToCall, "addTimeBlock")
                || StringUtils.equals(methodToCall, "editTimeBlock")
                || StringUtils.equals(methodToCall, "distributeTimeBlocks")
                || StringUtils.equals(methodToCall, "saveNewTimeBlocks")
                || StringUtils.equals(methodToCall, "deleteTimeBlock")) {
            if (!HrServiceLocator.getHRPermissionService().canEditCalendarDocument(principalId,
                    timesheetDocument)) {
                throw new AuthorizationException(GlobalVariables.getUserSession().getPrincipalId(), "ClockAction",
                        "");
            }
        }
    }

    @Override
    public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        ActionForward actionForward = super.execute(mapping, form, request, response);

        ClockActionForm clockActionForm = (ClockActionForm) form;

        TimesheetDocument timesheetDocument = clockActionForm.getTimesheetDocument();

        if (timesheetDocument != null) {
            if (!timesheetDocument.getDocumentHeader().getDocumentStatus().equals(HrConstants.ROUTE_STATUS.ENROUTE)
                    && !timesheetDocument.getDocumentHeader().getDocumentStatus()
                            .equals(HrConstants.ROUTE_STATUS.FINAL)) {

                String targetPrincipalId = HrContext.getTargetPrincipalId();
                if (targetPrincipalId != null) {
                    clockActionForm.setPrincipalId(targetPrincipalId);
                }
                clockActionForm.setAssignmentDescriptions(timesheetDocument.getAssignmentDescriptions(true));

                if (clockActionForm.getEditTimeBlockId() != null) {
                    clockActionForm.setCurrentTimeBlock(TkServiceLocator.getTimeBlockService()
                            .getTimeBlock(clockActionForm.getEditTimeBlockId()));
                }

                ClockLog lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog(targetPrincipalId);
                if (lastClockLog != null) {
                    DateTime lastClockDateTime = lastClockLog.getClockDateTime();
                    String lastClockZone = lastClockLog.getClockTimestampTimezone();
                    if (StringUtils.isEmpty(lastClockZone)) {
                        lastClockZone = TKUtils.getSystemTimeZone();
                    }
                    // zone will not be null. At this point is Valid or Exception.
                    // Exception would indicate bad data stored in the system. We can wrap this, but
                    // for now, the thrown exception is probably more valuable.
                    DateTimeZone zone = DateTimeZone.forID(lastClockZone);
                    DateTime clockWithZone = lastClockDateTime.withZone(zone);
                    clockActionForm.setLastClockTimeWithZone(clockWithZone.toDate());
                    clockActionForm.setLastClockTimestamp(lastClockDateTime.toDate());
                    clockActionForm.setLastClockAction(lastClockLog.getClockAction());
                }

                if (lastClockLog == null
                        || StringUtils.equals(lastClockLog.getClockAction(), TkConstants.CLOCK_OUT)) {
                    clockActionForm.setCurrentClockAction(TkConstants.CLOCK_IN);
                } else {
                    if (StringUtils.equals(lastClockLog.getClockAction(), TkConstants.LUNCH_OUT)
                            && TkServiceLocator.getSystemLunchRuleService().isShowLunchButton()) {
                        clockActionForm.setCurrentClockAction(TkConstants.LUNCH_IN);
                    } else {
                        clockActionForm.setCurrentClockAction(TkConstants.CLOCK_OUT);
                    }
                    // if the current clock action is clock out, displays only the clocked-in assignment
                    String selectedAssignment = new AssignmentDescriptionKey(lastClockLog.getJobNumber(),
                            lastClockLog.getWorkArea(), lastClockLog.getTask()).toAssignmentKeyString();
                    clockActionForm.setSelectedAssignment(selectedAssignment);
                    Assignment assignment = timesheetDocument
                            .getAssignment(AssignmentDescriptionKey.get(selectedAssignment));
                    Map<String, String> assignmentDesc = HrServiceLocator.getAssignmentService()
                            .getAssignmentDescriptions(assignment);
                    clockActionForm.setAssignmentDescriptions(assignmentDesc);
                }

                if (StringUtils.equals(GlobalVariables.getUserSession().getPrincipalId(),
                        HrContext.getTargetPrincipalId())) {
                    clockActionForm.setClockButtonEnabled(true);
                } else {
                    boolean isApproverOrReviewerForCurrentAssignment = false;
                    String selectedAssignment = StringUtils.EMPTY;
                    if (clockActionForm.getAssignmentDescriptions() != null) {
                        if (clockActionForm.getAssignmentDescriptions().size() == 1) {
                            for (String assignment : clockActionForm.getAssignmentDescriptions().keySet()) {
                                selectedAssignment = assignment;
                            }
                        } else {
                            selectedAssignment = clockActionForm.getSelectedAssignment();
                        }
                    }

                    Assignment assignment = HrServiceLocator.getAssignmentService()
                            .getAssignment(AssignmentDescriptionKey.get(selectedAssignment), LocalDate.now());
                    if (assignment != null) {
                        String principalId = GlobalVariables.getUserSession().getPrincipalId();
                        Long workArea = assignment.getWorkArea();
                        isApproverOrReviewerForCurrentAssignment = HrServiceLocator.getKPMERoleService()
                                .principalHasRoleInWorkArea(principalId, KPMENamespace.KPME_HR.getNamespaceCode(),
                                        KPMERole.APPROVER.getRoleName(), workArea, new DateTime())
                                || HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(principalId,
                                        KPMENamespace.KPME_HR.getNamespaceCode(),
                                        KPMERole.APPROVER_DELEGATE.getRoleName(), workArea, new DateTime())
                                || HrServiceLocator.getKPMERoleService().principalHasRoleInWorkArea(principalId,
                                        KPMENamespace.KPME_HR.getNamespaceCode(), KPMERole.REVIEWER.getRoleName(),
                                        workArea, new DateTime());
                    }
                    clockActionForm.setClockButtonEnabled(isApproverOrReviewerForCurrentAssignment);
                }

                clockActionForm
                        .setShowLunchButton(TkServiceLocator.getSystemLunchRuleService().isShowLunchButton());
                assignShowDistributeButton(clockActionForm);

                if (clockActionForm.isShowLunchButton()) {
                    // We don't need to worry about the assignments and lunch rules
                    // if the global lunch rule is turned off.

                    // Check for presence of department lunch rule.
                    Map<String, Boolean> assignmentDeptLunchRuleMap = new HashMap<String, Boolean>();
                    for (Assignment a : timesheetDocument.getAssignments()) {
                        String key = AssignmentDescriptionKey.getAssignmentKeyString(a);
                        DeptLunchRule deptLunchRule = TkServiceLocator.getDepartmentLunchRuleService()
                                .getDepartmentLunchRule(a.getDept(), a.getWorkArea(),
                                        clockActionForm.getPrincipalId(), a.getJobNumber(), LocalDate.now());
                        assignmentDeptLunchRuleMap.put(key, deptLunchRule != null);
                    }
                    clockActionForm.setAssignmentLunchMap(assignmentDeptLunchRuleMap);
                }
            } else {
                clockActionForm.setErrorMessage(
                        "Your current timesheet is already submitted for Approval. Clock action is not allowed on this timesheet.");
            }
        }

        return actionForward;
    }

    public void assignShowDistributeButton(ClockActionForm caf) {
        caf.setShowDistrubuteButton(false);

        TimesheetDocument timesheetDocument = caf.getTimesheetDocument();
        if (timesheetDocument != null) {
            int eligibleAssignmentCount = 0;
            for (Assignment assignment : timesheetDocument.getAssignments()) {
                Long workArea = assignment.getWorkArea();
                WorkArea aWorkArea = HrServiceLocator.getWorkAreaService().getWorkArea(workArea,
                        timesheetDocument.getDocEndDate());
                if (aWorkArea != null && aWorkArea.isHrsDistributionF()) {
                    eligibleAssignmentCount++;
                }

                // Only show the distribute button if there is more than one eligible assignment
                if (eligibleAssignmentCount > 1) {
                    caf.setShowDistrubuteButton(true);
                    break;
                }
            }
        }
    }

    @CacheEvict(value = { WorkArea.CACHE_NAME }, allEntries = true)
    public ActionForward clockAction(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        ClockActionForm caf = (ClockActionForm) form;

        // TODO: Validate that clock action is valid for this user
        // TODO: this needs to be integrated with the error tag
        if (StringUtils.isBlank(caf.getSelectedAssignment())) {
            caf.setErrorMessage("No assignment selected.");
            return mapping.findForward("basic");
        }
        ClockLog previousClockLog = TkServiceLocator.getClockLogService()
                .getLastClockLog(HrContext.getTargetPrincipalId());
        if (previousClockLog != null
                && StringUtils.equals(caf.getCurrentClockAction(), previousClockLog.getClockAction())) {
            caf.setErrorMessage("The operation is already performed.");
            return mapping.findForward("basic");
        }
        String ip = TKUtils.getIPAddressFromRequest(request.getRemoteAddr());
        Assignment assignment = caf.getTimesheetDocument()
                .getAssignment(AssignmentDescriptionKey.get(caf.getSelectedAssignment()));

        List<Assignment> lstAssingmentAsOfToday = HrServiceLocator.getAssignmentService()
                .getAssignments(HrContext.getTargetPrincipalId(), LocalDate.now());
        boolean foundValidAssignment = false;
        for (Assignment assign : lstAssingmentAsOfToday) {
            if ((assign.getJobNumber().compareTo(assignment.getJobNumber()) == 0)
                    && (assign.getWorkArea().compareTo(assignment.getWorkArea()) == 0)
                    && (assign.getTask().compareTo(assignment.getTask()) == 0)) {
                foundValidAssignment = true;
                break;
            }
        }

        if (!foundValidAssignment) {
            caf.setErrorMessage("Assignment is not effective as of today");
            return mapping.findForward("basic");
        }

        ClockLog clockLog = TkServiceLocator.getClockLogService().processClockLog(new DateTime(), assignment,
                caf.getCalendarEntry(), ip, LocalDate.now(), caf.getTimesheetDocument(),
                caf.getCurrentClockAction(), true, HrContext.getTargetPrincipalId());

        caf.setClockLog(clockLog);

        return mapping.findForward("basic");
    }

    public ActionForward distributeTimeBlocks(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        ClockActionForm caf = (ClockActionForm) form;
        caf.findTimeBlocksToDistribute();
        return mapping.findForward("tb");
    }

    public ActionForward editTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) {
        ClockActionForm caf = (ClockActionForm) form;
        TimeBlock tb = caf.getCurrentTimeBlock();
        caf.setCurrentAssignmentKey(tb.getAssignmentKey());

        ActionForward forward = mapping.findForward("et");

        return new ActionForward(forward.getPath() + "?editTimeBlockId=" + tb.getTkTimeBlockId().toString());

    }

    public ActionForward addTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) {
        ClockActionForm caf = (ClockActionForm) form;
        TimeBlock currentTb = caf.getCurrentTimeBlock();
        List<TimeBlock> newTimeBlocks = caf.getTimesheetDocument().getTimeBlocks();
        List<TimeBlock> referenceTimeBlocks = new ArrayList<TimeBlock>(
                caf.getTimesheetDocument().getTimeBlocks().size());
        for (TimeBlock tb : caf.getTimesheetDocument().getTimeBlocks()) {
            referenceTimeBlocks.add(tb.copy());
        }
        //call persist method that only saves added/deleted/changed timeblocks
        TkServiceLocator.getTimeBlockService().saveTimeBlocks(referenceTimeBlocks, newTimeBlocks,
                HrContext.getPrincipalId());

        ActionForward forward = mapping.findForward("et");

        return new ActionForward(forward.getPath() + "?editTimeBlockId=" + currentTb.getTkTimeBlockId().toString());
    }

    public ActionForward saveNewTimeBlocks(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) {
        ClockActionForm caf = (ClockActionForm) form;
        String tbId = caf.getTbId();
        String timesheetDocId = caf.getTsDocId();

        String[] assignments = caf.getNewAssignDesCol().split(SEPERATOR);
        String[] beginDates = caf.getNewBDCol().split(SEPERATOR);
        String[] beginTimes = caf.getNewBTCol().split(SEPERATOR);
        String[] endDates = caf.getNewEDCol().split(SEPERATOR);
        String[] endTimes = caf.getNewETCol().split(SEPERATOR);
        String[] hrs = caf.getNewHrsCol().split(SEPERATOR);
        String earnCode = TkServiceLocator.getTimeBlockService().getTimeBlock(tbId).getEarnCode();

        List<TimeBlock> newTbList = new ArrayList<TimeBlock>();
        for (int i = 0; i < hrs.length; i++) {
            BigDecimal hours = new BigDecimal(hrs[i]);
            DateTime beginDateTime = TKUtils.convertDateStringToDateTime(beginDates[i], beginTimes[i]);
            DateTime endDateTime = TKUtils.convertDateStringToDateTime(endDates[i], endTimes[i]);
            String assignString = assignments[i];
            Assignment assignment = HrServiceLocator.getAssignmentService().getAssignment(assignString);

            TimesheetDocument tsDoc = TkServiceLocator.getTimesheetService().getTimesheetDocument(timesheetDocId);

            TimeBlock tb = TkServiceLocator.getTimeBlockService().createTimeBlock(tsDoc, beginDateTime, endDateTime,
                    assignment, earnCode, hours, BigDecimal.ZERO, false, false, HrContext.getPrincipalId());
            newTbList.add(tb);
        }
        TkServiceLocator.getTimeBlockService().resetTimeHourDetail(newTbList);
        TkServiceLocator.getTimeBlockService().saveTimeBlocks(newTbList);
        TimeBlock oldTB = TkServiceLocator.getTimeBlockService().getTimeBlock(tbId);
        TkServiceLocator.getTimeBlockService().deleteTimeBlock(oldTB);
        return mapping.findForward("basic");
    }

    public ActionForward validateNewTimeBlock(ActionMapping mapping, ActionForm form, HttpServletRequest request,
            HttpServletResponse response) {
        ClockActionForm caf = (ClockActionForm) form;
        String tbId = caf.getTbId();
        String[] assignments = caf.getNewAssignDesCol().split(SEPERATOR);
        String[] beginDates = caf.getNewBDCol().split(SEPERATOR);
        String[] beginTimes = caf.getNewBTCol().split(SEPERATOR);
        String[] endDates = caf.getNewEDCol().split(SEPERATOR);
        String[] endTimes = caf.getNewETCol().split(SEPERATOR);
        String[] hrs = caf.getNewHrsCol().split(SEPERATOR);

        List<Interval> newIntervals = new ArrayList<Interval>();
        JSONArray errorMsgList = new JSONArray();

        // validates that all fields are available
        if (assignments.length != beginDates.length || assignments.length != beginTimes.length
                || assignments.length != endDates.length || assignments.length != endTimes.length
                || assignments.length != hrs.length) {
            errorMsgList.add("All fields are required");
            caf.setOutputString(JSONValue.toJSONString(errorMsgList));
            return mapping.findForward("ws");
        }

        for (int i = 0; i < hrs.length; i++) {
            String index = String.valueOf(i + 1);

            // validate the hours field
            BigDecimal dc = new BigDecimal(hrs[i]);
            if (dc.compareTo(new BigDecimal("0")) == 0) {
                errorMsgList.add("The entered hours for entry " + index + " is not valid.");
                caf.setOutputString(JSONValue.toJSONString(errorMsgList));
                return mapping.findForward("ws");
            }

            // check if the begin / end time are valid
            // should not include time zone in consideration when conparing time intervals
            DateTime beginDateTime = TKUtils.convertDateStringToDateTimeWithoutZone(beginDates[i], beginTimes[i]);
            DateTime endDateTime = TKUtils.convertDateStringToDateTimeWithoutZone(endDates[i], endTimes[i]);
            if ((beginDateTime.compareTo(endDateTime) > 0 || endDateTime.compareTo(beginDateTime) < 0)) {
                errorMsgList.add("The time or date for entry " + index + " is not valid.");
                caf.setOutputString(JSONValue.toJSONString(errorMsgList));
                return mapping.findForward("ws");
            }

            // check if new time blocks overlap with existing time blocks
            Interval addedTimeblockInterval = new Interval(beginDateTime, endDateTime);
            newIntervals.add(addedTimeblockInterval);
            for (TimeBlock timeBlock : caf.getTimesheetDocument().getTimeBlocks()) {
                if (timeBlock.getTkTimeBlockId().equals(tbId)) { // ignore the original time block
                    continue;
                }
                if (timeBlock.getHours().compareTo(BigDecimal.ZERO) == 0) { // ignore time blocks with zero hours
                    continue;
                }
                DateTimeZone dateTimeZone = HrServiceLocator.getTimezoneService().getUserTimezoneWithFallback();
                DateTime timeBlockBeginTimestamp = new DateTime(timeBlock.getBeginTimestamp().getTime(),
                        dateTimeZone).withZone(TKUtils.getSystemDateTimeZone());
                DateTime timeBlockEndTimestamp = new DateTime(timeBlock.getEndTimestamp().getTime(), dateTimeZone)
                        .withZone(TKUtils.getSystemDateTimeZone());
                Interval timeBlockInterval = new Interval(timeBlockBeginTimestamp, timeBlockEndTimestamp);
                if (timeBlockInterval.overlaps(addedTimeblockInterval)) {
                    errorMsgList.add("The time block you are trying to add for entry " + index
                            + " overlaps with an existing time block.");
                    caf.setOutputString(JSONValue.toJSONString(errorMsgList));
                    return mapping.findForward("ws");
                }
            }
        }
        // check if new time blocks overlap with each other
        if (newIntervals.size() > 1) {
            for (Interval intv1 : newIntervals) {
                for (Interval intv2 : newIntervals) {
                    if (intv1.equals(intv2)) {
                        continue;
                    }
                    if (intv1.overlaps(intv2)) {
                        errorMsgList.add("There is time overlap between the entries.");
                        caf.setOutputString(JSONValue.toJSONString(errorMsgList));
                        return mapping.findForward("ws");
                    }
                }
            }
        }

        caf.setOutputString(JSONValue.toJSONString(errorMsgList));
        return mapping.findForward("ws");
    }

}