001/** 002 * Copyright 2005-2016 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.rice.kew.actions; 017 018import org.apache.log4j.MDC; 019import org.kuali.rice.kew.actionrequest.ActionRequestValue; 020import org.kuali.rice.kew.actionrequest.Recipient; 021import org.kuali.rice.kew.actiontaken.ActionTakenValue; 022import org.kuali.rice.kew.doctype.DocumentTypePolicy; 023import org.kuali.rice.kew.api.exception.InvalidActionTakenException; 024import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; 025import org.kuali.rice.kew.service.KEWServiceLocator; 026import org.kuali.rice.kew.api.KewApiConstants; 027import org.kuali.rice.kim.api.identity.principal.PrincipalContract; 028 029 030import java.util.Iterator; 031import java.util.List; 032 033 034/** 035 * The ApproveAction records and processes an approve action. 036 * 037 * The routeheader is first checked to make sure the action is valid for the document. 038 * Next the user is checked to make sure he/she has not taken a previous action on this 039 * document at the actions responsibility or below. The action is recorded. 040 * Any requests related to this user are deactivated. 041 * 042 * @author Kuali Rice Team (rice.collab@kuali.org) 043 */ 044public class ApproveAction extends ActionTakenEvent { 045 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ApproveAction.class); 046 047 /** 048 * @param routeHeader 049 * RouteHeader for the document upon which the action is taken. 050 * @param principal 051 * User taking the action. 052 */ 053 public ApproveAction(DocumentRouteHeaderValue routeHeader, PrincipalContract principal) { 054 super(KewApiConstants.ACTION_TAKEN_APPROVED_CD, routeHeader, principal); 055 } 056 057 /** 058 * @param routeHeader 059 * RouteHeader for the document upon which the action is taken. 060 * @param principal 061 * User taking the action. 062 * @param annotation 063 * User comment on the action taken 064 */ 065 public ApproveAction(DocumentRouteHeaderValue routeHeader, PrincipalContract principal, String annotation) { 066 super(KewApiConstants.ACTION_TAKEN_APPROVED_CD, routeHeader, principal, annotation); 067 } 068 069 /* (non-Javadoc) 070 * @see org.kuali.rice.kew.actions.ActionTakenEvent#isActionCompatibleRequest(java.util.List) 071 */ 072 @Override 073 public String validateActionRules() { 074 return validateActionRules(getActionRequestService().findAllPendingRequests(routeHeader.getDocumentId())); 075 } 076 077 public String validateActionRules(List<ActionRequestValue> actionRequests) { 078 if (!getRouteHeader().isValidActionToTake(getActionPerformedCode())) { 079 return "Document is not in a state to be approved"; 080 } 081 List<ActionRequestValue> filteredActionRequests = filterActionRequestsByCode(actionRequests, KewApiConstants.ACTION_REQUEST_APPROVE_REQ); 082 if (!isActionCompatibleRequest(filteredActionRequests)) { 083 return "No request for the user is compatible " + "with the APPROVE action"; 084 } 085 return ""; 086 } 087 088 /* (non-Javadoc) 089 * @see org.kuali.rice.kew.actions.ActionTakenEvent#isActionCompatibleRequest(java.util.List) 090 */ 091 @Override 092 public boolean isActionCompatibleRequest(List<ActionRequestValue> requests) { 093 // we allow pre-approval 094 if (requests.isEmpty()) { 095 return true; 096 } 097 098 // can always cancel saved or initiated document 099 if (routeHeader.isStateInitiated() || routeHeader.isStateSaved()) { 100 return true; 101 } 102 103 boolean actionCompatible = false; 104 Iterator ars = requests.iterator(); 105 ActionRequestValue actionRequest = null; 106 107 while (ars.hasNext()) { 108 actionRequest = (ActionRequestValue) ars.next(); 109 String request = actionRequest.getActionRequested(); 110 111 // Approve action matches Complete, Approve, FYI, and ACK requests 112 if ( (KewApiConstants.ACTION_REQUEST_FYI_REQ.equals(request)) || 113 (KewApiConstants.ACTION_REQUEST_ACKNOWLEDGE_REQ.equals(request)) || 114 (KewApiConstants.ACTION_REQUEST_APPROVE_REQ.equals(request)) || 115 (KewApiConstants.ACTION_REQUEST_COMPLETE_REQ.equals(request)) ) { 116 actionCompatible = true; 117 break; 118 } 119 } 120 return actionCompatible; 121 } 122 123 /** 124 * Records the approve action. 125 * - Checks to make sure the document status allows the action. 126 * - Checks that the user has not taken a previous action. 127 * - Deactivates the pending requests for this user 128 * - Records the action 129 * 130 * @throws InvalidActionTakenException 131 * @throws org.kuali.rice.kew.api.exception.ResourceUnavailableException 132 */ 133 public void recordAction() throws InvalidActionTakenException { 134 MDC.put("docId", getRouteHeader().getDocumentId()); 135 updateSearchableAttributesIfPossible(); 136 LOG.debug("Approving document : " + annotation); 137 138 List actionRequests = getActionRequestService().findAllValidRequests(getPrincipal().getPrincipalId(), getDocumentId(), KewApiConstants.ACTION_REQUEST_APPROVE_REQ); 139 if (actionRequests == null || actionRequests.isEmpty()) { 140 DocumentTypePolicy allowUnrequested = getRouteHeader().getDocumentType().getAllowUnrequestedActionPolicy(); 141 if (allowUnrequested != null) { 142 if (!allowUnrequested.getPolicyValue()) { 143 throw new InvalidActionTakenException("No request for the user is compatible " + "with the APPROVE action. " + "Doctype policy ALLOW_UNREQUESTED_ACTION is set to false and someone else likely just took action on the document."); 144 } 145 } 146 } 147 String errorMessage = validateActionRules(actionRequests); 148 if (!org.apache.commons.lang.StringUtils.isEmpty(errorMessage)) { 149 throw new InvalidActionTakenException(errorMessage); 150 } 151 152 Recipient delegator = findDelegatorForActionRequests(actionRequests); 153 154 LOG.debug("Record the approve action"); 155 ActionTakenValue actionTaken = saveActionTaken(delegator); 156 157 LOG.debug("Deactivate all pending action requests"); 158 getActionRequestService().deactivateRequests(actionTaken, actionRequests); 159 notifyActionTaken(actionTaken); 160 161 boolean isException = getRouteHeader().isInException(); 162 boolean isSaved = getRouteHeader().isStateSaved(); 163 if (isException || isSaved) { 164 String oldStatus = getRouteHeader().getDocRouteStatus(); 165 LOG.debug("Moving document back to Enroute from "+KewApiConstants.DOCUMENT_STATUSES.get(oldStatus)); 166 getRouteHeader().markDocumentEnroute(); 167 String newStatus = getRouteHeader().getDocRouteStatus(); 168 notifyStatusChange(newStatus, oldStatus); 169 KEWServiceLocator.getRouteHeaderService().saveRouteHeader(getRouteHeader()); 170 } 171 } 172}