001/** 002 * Copyright 2005-2015 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.routeheader; 017 018import org.apache.commons.lang.ObjectUtils; 019import org.apache.commons.lang.StringUtils; 020import org.apache.log4j.Logger; 021import org.joda.time.DateTime; 022import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 023import org.kuali.rice.core.api.exception.RiceRuntimeException; 024import org.kuali.rice.kew.actionitem.ActionItem; 025import org.kuali.rice.kew.actionlist.CustomActionListAttribute; 026import org.kuali.rice.kew.actionlist.DefaultCustomActionListAttribute; 027import org.kuali.rice.kew.actionrequest.ActionRequestFactory; 028import org.kuali.rice.kew.actionrequest.ActionRequestValue; 029import org.kuali.rice.kew.actiontaken.ActionTakenValue; 030import org.kuali.rice.kew.api.KewApiConstants; 031import org.kuali.rice.kew.api.WorkflowRuntimeException; 032import org.kuali.rice.kew.api.action.ActionType; 033import org.kuali.rice.kew.api.document.Document; 034import org.kuali.rice.kew.api.document.DocumentContract; 035import org.kuali.rice.kew.api.document.DocumentStatus; 036import org.kuali.rice.kew.api.document.DocumentUpdate; 037import org.kuali.rice.kew.api.exception.InvalidActionTakenException; 038import org.kuali.rice.kew.api.exception.ResourceUnavailableException; 039import org.kuali.rice.kew.api.exception.WorkflowException; 040import org.kuali.rice.kew.api.util.CodeTranslator; 041import org.kuali.rice.kew.docsearch.DocumentSearchCriteriaEbo; 042import org.kuali.rice.kew.doctype.ApplicationDocumentStatus; 043import org.kuali.rice.kew.doctype.DocumentTypePolicy; 044import org.kuali.rice.kew.doctype.bo.DocumentType; 045import org.kuali.rice.kew.engine.CompatUtils; 046import org.kuali.rice.kew.engine.node.Branch; 047import org.kuali.rice.kew.engine.node.BranchState; 048import org.kuali.rice.kew.engine.node.RouteNode; 049import org.kuali.rice.kew.engine.node.RouteNodeInstance; 050import org.kuali.rice.kew.mail.CustomEmailAttribute; 051import org.kuali.rice.kew.mail.CustomEmailAttributeImpl; 052import org.kuali.rice.kew.notes.CustomNoteAttribute; 053import org.kuali.rice.kew.notes.CustomNoteAttributeImpl; 054import org.kuali.rice.kew.notes.Note; 055import org.kuali.rice.kew.quicklinks.dao.impl.QuickLinksDAOJpa; 056import org.kuali.rice.kew.routeheader.dao.impl.DocumentRouteHeaderDAOJpa; 057import org.kuali.rice.kew.service.KEWServiceLocator; 058import org.kuali.rice.kim.api.identity.Person; 059import org.kuali.rice.kim.api.identity.principal.Principal; 060import org.kuali.rice.krad.bo.DataObjectBase; 061import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator; 062 063import javax.persistence.Cacheable; 064import javax.persistence.CascadeType; 065import javax.persistence.Column; 066import javax.persistence.Entity; 067import javax.persistence.GeneratedValue; 068import javax.persistence.Id; 069import javax.persistence.JoinColumn; 070import javax.persistence.JoinTable; 071import javax.persistence.ManyToMany; 072import javax.persistence.NamedAttributeNode; 073import javax.persistence.NamedEntityGraph; 074import javax.persistence.NamedEntityGraphs; 075import javax.persistence.NamedQueries; 076import javax.persistence.NamedQuery; 077import javax.persistence.OneToMany; 078import javax.persistence.OrderBy; 079import javax.persistence.Table; 080import javax.persistence.Transient; 081 082import java.sql.Timestamp; 083import java.util.ArrayList; 084import java.util.HashMap; 085import java.util.Iterator; 086import java.util.List; 087import java.util.Map; 088 089 090 091/** 092 * A document within KEW. A document effectively represents a process that moves through 093 * the workflow engine. It is created from a particular {@link DocumentType} and follows 094 * the route path defined by that DocumentType. 095 * 096 * <p>During a document's lifecycle it progresses through a series of statuses, starting 097 * with INITIATED and moving to one of the terminal states (such as FINAL, CANCELED, etc). 098 * The list of status on a document are defined in the {@link KewApiConstants} class and 099 * include the constants starting with "ROUTE_HEADER_" and ending with "_CD". 100 * 101 * <p>Associated with the document is the document content. The document content is XML 102 * which represents the content of that document. This XML content is typically used 103 * to make routing decisions for the document. 104 * 105 * <p>A document has associated with it a set of {@link ActionRequestValue} object and 106 * {@link ActionTakenValue} objects. Action Requests represent requests for user 107 * action (such as Approve, Acknowledge, etc). Action Takens represent action that 108 * users have performed on the document, such as approvals or cancelling of the document. 109 * 110 * <p>The instantiated route path of a document is defined by it's graph of 111 * {@link RouteNodeInstance} objects. The path starts at the initial node of the document 112 * and progresses from there following the next nodes of each node instance. The current 113 * active nodes on the document are defined by the "active" flag on the node instance 114 * where are not marked as "complete". 115 * 116 * @see DocumentType 117 * @see ActionRequestValue 118 * @see ActionItem 119 * @see ActionTakenValue 120 * @see RouteNodeInstance 121 * @see KewApiConstants 122 * 123 * @author Kuali Rice Team (rice.collab@kuali.org) 124 */ 125@Entity 126@Table(name="KREW_DOC_HDR_T") 127@NamedQueries({ 128 @NamedQuery(name=QuickLinksDAOJpa.FIND_WATCHED_DOCUMENTS_BY_INITIATOR_WORKFLOW_ID_NAME, 129 query= QuickLinksDAOJpa.FIND_WATCHED_DOCUMENTS_BY_INITIATOR_WORKFLOW_ID_QUERY), 130 @NamedQuery(name= DocumentRouteHeaderDAOJpa.GET_APP_DOC_ID_NAME, query= 131 DocumentRouteHeaderDAOJpa.GET_APP_DOC_ID_QUERY), 132 @NamedQuery(name=DocumentRouteHeaderDAOJpa.GET_APP_DOC_STATUS_NAME, query= 133 DocumentRouteHeaderDAOJpa.GET_APP_DOC_STATUS_QUERY), 134 @NamedQuery(name=DocumentRouteHeaderDAOJpa.GET_DOCUMENT_HEADERS_NAME, query= 135 DocumentRouteHeaderDAOJpa.GET_DOCUMENT_HEADERS_QUERY), 136 @NamedQuery(name=DocumentRouteHeaderDAOJpa.GET_DOCUMENT_STATUS_NAME, query= 137 DocumentRouteHeaderDAOJpa.GET_DOCUMENT_STATUS_QUERY), 138 @NamedQuery(name=DocumentRouteHeaderDAOJpa.GET_DOCUMENT_ID_BY_DOC_TYPE_APP_ID_NAME, query = 139 DocumentRouteHeaderDAOJpa.GET_DOCUMENT_ID_BY_DOC_TYPE_APP_ID_QUERY) 140}) 141@NamedEntityGraphs({ 142 @NamedEntityGraph(name="DocumentRouteHeaderValue.ActionListAttributesOnly" 143 ,attributeNodes={ 144 @NamedAttributeNode("approvedDate") 145 ,@NamedAttributeNode("createDate") 146 ,@NamedAttributeNode("docRouteStatus") 147 ,@NamedAttributeNode("initiatorWorkflowId") 148 ,@NamedAttributeNode("appDocStatus") 149 ,@NamedAttributeNode("documentId") 150 ,@NamedAttributeNode("docRouteLevel") 151 ,@NamedAttributeNode("documentTypeId") 152 ,@NamedAttributeNode("docVersion") 153 }) 154}) 155@Cacheable(false) 156public class DocumentRouteHeaderValue extends DataObjectBase implements DocumentContract, DocumentSearchCriteriaEbo { 157 158 private static final long serialVersionUID = -4700736340527913220L; 159 private static final Logger LOG = Logger.getLogger(DocumentRouteHeaderValue.class); 160 161 private static final String TERMINAL = ""; 162 private static final boolean FINAL_STATE = true; 163 protected static final HashMap<String,String> legalActions; 164 protected static final HashMap<String,String> stateTransitionMap; 165 static { 166 stateTransitionMap = new HashMap<String,String>(); 167 stateTransitionMap.put(KewApiConstants.ROUTE_HEADER_INITIATED_CD, KewApiConstants.ROUTE_HEADER_SAVED_CD + KewApiConstants.ROUTE_HEADER_ENROUTE_CD + KewApiConstants.ROUTE_HEADER_CANCEL_CD); 168 169 stateTransitionMap.put(KewApiConstants.ROUTE_HEADER_SAVED_CD, KewApiConstants.ROUTE_HEADER_SAVED_CD + KewApiConstants.ROUTE_HEADER_ENROUTE_CD + KewApiConstants.ROUTE_HEADER_CANCEL_CD + KewApiConstants.ROUTE_HEADER_PROCESSED_CD); 170 171 stateTransitionMap.put(KewApiConstants.ROUTE_HEADER_ENROUTE_CD, KewApiConstants.ROUTE_HEADER_DISAPPROVED_CD + 172 KewApiConstants.ROUTE_HEADER_CANCEL_CD + KewApiConstants.ROUTE_HEADER_PROCESSED_CD + KewApiConstants.ROUTE_HEADER_EXCEPTION_CD + KewApiConstants.ROUTE_HEADER_SAVED_CD 173 + DocumentStatus.RECALLED.getCode()); 174 stateTransitionMap.put(KewApiConstants.ROUTE_HEADER_DISAPPROVED_CD, TERMINAL); 175 stateTransitionMap.put(KewApiConstants.ROUTE_HEADER_CANCEL_CD, TERMINAL); 176 stateTransitionMap.put(KewApiConstants.ROUTE_HEADER_FINAL_CD, TERMINAL); 177 stateTransitionMap.put(DocumentStatus.RECALLED.getCode(), TERMINAL); 178 stateTransitionMap.put(KewApiConstants.ROUTE_HEADER_EXCEPTION_CD, KewApiConstants.ROUTE_HEADER_EXCEPTION_CD + KewApiConstants.ROUTE_HEADER_ENROUTE_CD + KewApiConstants.ROUTE_HEADER_CANCEL_CD + KewApiConstants.ROUTE_HEADER_PROCESSED_CD + KewApiConstants.ROUTE_HEADER_DISAPPROVED_CD + KewApiConstants.ROUTE_HEADER_SAVED_CD); 179 stateTransitionMap.put(KewApiConstants.ROUTE_HEADER_PROCESSED_CD, KewApiConstants.ROUTE_HEADER_FINAL_CD + KewApiConstants.ROUTE_HEADER_PROCESSED_CD); 180 181 legalActions = new HashMap<String,String>(); 182 legalActions.put(KewApiConstants.ROUTE_HEADER_INITIATED_CD, KewApiConstants.ACTION_TAKEN_FYI_CD + KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KewApiConstants.ACTION_TAKEN_SAVED_CD + KewApiConstants.ACTION_TAKEN_COMPLETED_CD + KewApiConstants.ACTION_TAKEN_ROUTED_CD + KewApiConstants.ACTION_TAKEN_CANCELED_CD + KewApiConstants.ACTION_TAKEN_ADHOC_CD + KewApiConstants.ACTION_TAKEN_ADHOC_REVOKED_CD + KewApiConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + KewApiConstants.ACTION_TAKEN_MOVE_CD); 183 legalActions.put(KewApiConstants.ROUTE_HEADER_SAVED_CD, KewApiConstants.ACTION_TAKEN_FYI_CD + KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KewApiConstants.ACTION_TAKEN_SAVED_CD + KewApiConstants.ACTION_TAKEN_COMPLETED_CD + KewApiConstants.ACTION_TAKEN_ROUTED_CD + KewApiConstants.ACTION_TAKEN_APPROVED_CD + KewApiConstants.ACTION_TAKEN_CANCELED_CD + KewApiConstants.ACTION_TAKEN_ADHOC_CD + KewApiConstants.ACTION_TAKEN_ADHOC_REVOKED_CD + KewApiConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + KewApiConstants.ACTION_TAKEN_MOVE_CD); 184 /* ACTION_TAKEN_ROUTED_CD not included in enroute state 185 * ACTION_TAKEN_SAVED_CD removed as of version 2.4 186 */ 187 legalActions.put(KewApiConstants.ROUTE_HEADER_ENROUTE_CD, /*KewApiConstants.ACTION_TAKEN_SAVED_CD + KewApiConstants.ACTION_TAKEN_ROUTED_CD + */KewApiConstants.ACTION_TAKEN_APPROVED_CD + KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KewApiConstants.ACTION_TAKEN_FYI_CD + KewApiConstants.ACTION_TAKEN_ADHOC_CD + KewApiConstants.ACTION_TAKEN_ADHOC_REVOKED_CD + KewApiConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + KewApiConstants.ACTION_TAKEN_CANCELED_CD + KewApiConstants.ACTION_TAKEN_COMPLETED_CD + KewApiConstants.ACTION_TAKEN_DENIED_CD + KewApiConstants.ACTION_TAKEN_SU_APPROVED_CD + KewApiConstants.ACTION_TAKEN_SU_CANCELED_CD + KewApiConstants.ACTION_TAKEN_SU_DISAPPROVED_CD + KewApiConstants.ACTION_TAKEN_SU_ROUTE_LEVEL_APPROVED_CD + KewApiConstants.ACTION_TAKEN_RETURNED_TO_PREVIOUS_CD + KewApiConstants.ACTION_TAKEN_SU_RETURNED_TO_PREVIOUS_CD + KewApiConstants.ACTION_TAKEN_MOVE_CD + ActionType.RECALL.getCode()); 188 /* ACTION_TAKEN_ROUTED_CD not included in exception state 189 * ACTION_TAKEN_SAVED_CD removed as of version 2.4.2 190 */ 191 legalActions.put(KewApiConstants.ROUTE_HEADER_EXCEPTION_CD, /*KewApiConstants.ACTION_TAKEN_SAVED_CD + */KewApiConstants.ACTION_TAKEN_FYI_CD + KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KewApiConstants.ACTION_TAKEN_ADHOC_CD + KewApiConstants.ACTION_TAKEN_ADHOC_REVOKED_CD + KewApiConstants.ACTION_TAKEN_APPROVED_CD + KewApiConstants.ACTION_TAKEN_BLANKET_APPROVE_CD + KewApiConstants.ACTION_TAKEN_CANCELED_CD + KewApiConstants.ACTION_TAKEN_COMPLETED_CD + KewApiConstants.ACTION_TAKEN_DENIED_CD + KewApiConstants.ACTION_TAKEN_SU_APPROVED_CD + KewApiConstants.ACTION_TAKEN_SU_CANCELED_CD + KewApiConstants.ACTION_TAKEN_SU_DISAPPROVED_CD + KewApiConstants.ACTION_TAKEN_SU_ROUTE_LEVEL_APPROVED_CD + KewApiConstants.ACTION_TAKEN_RETURNED_TO_PREVIOUS_CD + KewApiConstants.ACTION_TAKEN_SU_RETURNED_TO_PREVIOUS_CD + KewApiConstants.ACTION_TAKEN_MOVE_CD); 192 legalActions.put(KewApiConstants.ROUTE_HEADER_FINAL_CD, KewApiConstants.ACTION_TAKEN_FYI_CD + KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KewApiConstants.ACTION_TAKEN_ADHOC_REVOKED_CD); 193 legalActions.put(KewApiConstants.ROUTE_HEADER_CANCEL_CD, KewApiConstants.ACTION_TAKEN_FYI_CD + KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KewApiConstants.ACTION_TAKEN_ADHOC_REVOKED_CD); 194 legalActions.put(KewApiConstants.ROUTE_HEADER_DISAPPROVED_CD, KewApiConstants.ACTION_TAKEN_FYI_CD + KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KewApiConstants.ACTION_TAKEN_ADHOC_REVOKED_CD); 195 legalActions.put(KewApiConstants.ROUTE_HEADER_PROCESSED_CD, KewApiConstants.ACTION_TAKEN_FYI_CD + KewApiConstants.ACTION_TAKEN_ACKNOWLEDGED_CD + KewApiConstants.ACTION_TAKEN_ADHOC_REVOKED_CD); 196 legalActions.put(DocumentStatus.RECALLED.getCode(), TERMINAL); 197 } 198 199 public static final String CURRENT_ROUTE_NODE_NAME_DELIMITER = ", "; 200 201 @Id 202 @GeneratedValue(generator = "KREW_DOC_HDR_S") 203 @PortableSequenceGenerator(name = "KREW_DOC_HDR_S") 204 @Column(name = "DOC_HDR_ID", nullable = false) 205 private String documentId; 206 207 @Column(name = "DOC_TYP_ID") 208 private String documentTypeId; 209 210 @Column(name = "DOC_HDR_STAT_CD", nullable = false) 211 private String docRouteStatus; 212 213 @Column(name = "RTE_LVL", nullable = false) 214 private Integer docRouteLevel; 215 216 @Column(name = "STAT_MDFN_DT", nullable = false) 217 private Timestamp dateModified; 218 219 @Column(name = "CRTE_DT", nullable = false) 220 private Timestamp createDate; 221 222 @Column(name = "APRV_DT") 223 private Timestamp approvedDate; 224 225 @Column(name = "FNL_DT") 226 private Timestamp finalizedDate; 227 228 @Column(name = "TTL") 229 private String docTitle; 230 231 @Column(name = "APP_DOC_ID") 232 private String appDocId; 233 234 @Column(name = "DOC_VER_NBR", nullable = false) 235 private Integer docVersion = new Integer(KewApiConstants.DocumentContentVersions.NODAL); 236 237 @Column(name = "INITR_PRNCPL_ID", nullable = false) 238 private String initiatorWorkflowId; 239 240 @Column(name = "RTE_PRNCPL_ID") 241 private String routedByUserWorkflowId; 242 243 @Column(name = "RTE_STAT_MDFN_DT") 244 private Timestamp routeStatusDate; 245 246 @Column(name = "APP_DOC_STAT") 247 private String appDocStatus; 248 249 @Column(name = "APP_DOC_STAT_MDFN_DT") 250 private Timestamp appDocStatusDate; 251 252 @ManyToMany(cascade = CascadeType.ALL) 253 @JoinTable(name = "KREW_INIT_RTE_NODE_INSTN_T", joinColumns = @JoinColumn(name = "DOC_HDR_ID"), inverseJoinColumns = @JoinColumn(name = "RTE_NODE_INSTN_ID")) 254 private List<RouteNodeInstance> initialRouteNodeInstances = new ArrayList<RouteNodeInstance>(); 255 256 /** 257 * The appDocStatusHistory keeps a list of Application Document Status transitions 258 * for the document. It tracks the previous status, the new status, and a timestamp of the 259 * transition for each status transition. 260 */ 261 @OneToMany(cascade = CascadeType.ALL, mappedBy = "documentRouteHeaderValue") 262 @OrderBy("statusTransitionId ASC") 263 private List<DocumentStatusTransition> appDocStatusHistory = new ArrayList<DocumentStatusTransition>(); 264 265 @OneToMany(cascade = CascadeType.ALL) 266 @JoinColumn(name = "DOC_HDR_ID") 267 @OrderBy("noteId ASC") 268 private List<Note> notes = new ArrayList<Note>(); 269 270 @Transient private DocumentRouteHeaderValueContent documentContent; 271 @Transient private boolean routingReport = false; 272 @Transient private List<ActionRequestValue> simulatedActionRequests; 273 274 public Principal getInitiatorPrincipal() { 275 // if we are running a simulation, there will be no initiator 276 if ( StringUtils.isBlank( getInitiatorWorkflowId() ) ) { 277 return null; 278 } 279 return KEWServiceLocator.getIdentityHelperService().getPrincipal(getInitiatorWorkflowId()); 280 } 281 282 public Principal getRoutedByPrincipal() 283 { 284 if (getRoutedByUserWorkflowId() == null) { 285 return null; 286 } 287 return KEWServiceLocator.getIdentityHelperService().getPrincipal(getRoutedByUserWorkflowId()); 288 } 289 290 public String getInitiatorDisplayName() { 291 try { 292 Person person = KEWServiceLocator.getIdentityHelperService().getPerson(getInitiatorWorkflowId()); 293 return person.getName(); 294 } catch (RiceIllegalArgumentException e) { 295 return getInitiatorWorkflowId(); 296 } 297 } 298 299 public String getRoutedByDisplayName() { 300 String routedByUserWorkflowId = getRoutedByUserWorkflowId(); 301 if (StringUtils.isBlank(routedByUserWorkflowId)) { 302 return ""; 303 } 304 return KEWServiceLocator.getIdentityHelperService().getPerson(getRoutedByUserWorkflowId()).getName(); 305 } 306 307 public String getCurrentRouteLevelName() { 308 String name = "Not Found"; 309 // TODO the isRouteLevelDocument junk can be ripped out 310 if(routingReport){ 311 name = "Routing Report"; 312 } else if (CompatUtils.isRouteLevelDocument(this)) { 313 int routeLevelInt = getDocRouteLevel().intValue(); 314 if ( LOG.isInfoEnabled() ) { 315 LOG.info("Getting current route level name for a Route level document: " + routeLevelInt+CURRENT_ROUTE_NODE_NAME_DELIMITER+documentId); 316 } 317 List<RouteNode> routeLevelNodes = CompatUtils.getRouteLevelCompatibleNodeList(getDocumentType()); 318 if ( LOG.isInfoEnabled() ) { 319 LOG.info("Route level compatible node list has " + routeLevelNodes.size() + " nodes"); 320 } 321 if (routeLevelInt < routeLevelNodes.size()) { 322 name = routeLevelNodes.get(routeLevelInt).getRouteNodeName(); 323 } 324 } else { 325 List<String> currentNodeNames = getCurrentNodeNames(); 326 name = StringUtils.join(currentNodeNames, CURRENT_ROUTE_NODE_NAME_DELIMITER); 327 } 328 return name; 329 } 330 331 public List<String> getCurrentNodeNames() { 332 return KEWServiceLocator.getRouteNodeService().getCurrentRouteNodeNames(getDocumentId()); 333 } 334 335 public String getRouteStatusLabel() { 336 return CodeTranslator.getRouteStatusLabel(getDocRouteStatus()); 337 } 338 339 public String getDocRouteStatusLabel() { 340 return CodeTranslator.getRouteStatusLabel(getDocRouteStatus()); 341 } 342 /** 343 * 344 * This method returns the Document Status Policy for the document type associated with this Route Header. 345 * The Document Status Policy denotes whether the KEW Route Status, or the Application Document Status, 346 * or both are to be displayed. 347 * 348 * @return 349 */ 350 public String getDocStatusPolicy() { 351 return getDocumentType().getDocumentStatusPolicy().getPolicyStringValue(); 352 } 353 354 public List<ActionItem> getActionItems() { 355 return (List<ActionItem>) KEWServiceLocator.getActionListService().findByDocumentId(documentId); 356 } 357 358 public List<ActionTakenValue> getActionsTaken() { 359 return KEWServiceLocator.getActionTakenService().findByDocumentIdIgnoreCurrentInd(documentId); 360 } 361 362 public List<ActionRequestValue> getActionRequests() { 363 if (this.simulatedActionRequests == null || this.simulatedActionRequests.isEmpty()) { 364 return KEWServiceLocator.getActionRequestService().findByDocumentIdIgnoreCurrentInd(documentId); 365 } else { 366 return this.simulatedActionRequests; 367 } 368 } 369 370 public List<ActionRequestValue> getSimulatedActionRequests() { 371 if (this.simulatedActionRequests == null) { 372 this.simulatedActionRequests = new ArrayList<ActionRequestValue>(); 373 } 374 return this.simulatedActionRequests; 375 } 376 377 public void setSimulatedActionRequests(List<ActionRequestValue> simulatedActionRequests) { 378 this.simulatedActionRequests = simulatedActionRequests; 379 } 380 381 public DocumentType getDocumentType() { 382 return KEWServiceLocator.getDocumentTypeService().findById(getDocumentTypeId()); 383 } 384 385 public java.lang.String getAppDocId() { 386 return appDocId; 387 } 388 389 public void setAppDocId(java.lang.String appDocId) { 390 this.appDocId = appDocId; 391 } 392 393 public java.sql.Timestamp getApprovedDate() { 394 return approvedDate; 395 } 396 397 public void setApprovedDate(java.sql.Timestamp approvedDate) { 398 this.approvedDate = approvedDate; 399 } 400 401 public java.sql.Timestamp getCreateDate() { 402 return createDate; 403 } 404 405 public void setCreateDate(java.sql.Timestamp createDate) { 406 this.createDate = createDate; 407 } 408 409 public java.lang.String getDocContent() { 410 return getDocumentContent().getDocumentContent(); 411 } 412 413 public void setDocContent(java.lang.String docContent) { 414 DocumentRouteHeaderValueContent content = getDocumentContent(); 415 content.setDocumentContent(docContent); 416 } 417 418 public java.lang.Integer getDocRouteLevel() { 419 return docRouteLevel; 420 } 421 422 public void setDocRouteLevel(java.lang.Integer docRouteLevel) { 423 this.docRouteLevel = docRouteLevel; 424 } 425 426 public java.lang.String getDocRouteStatus() { 427 return docRouteStatus; 428 } 429 430 public void setDocRouteStatus(java.lang.String docRouteStatus) { 431 this.docRouteStatus = docRouteStatus; 432 } 433 434 public java.lang.String getDocTitle() { 435 return docTitle; 436 } 437 438 public void setDocTitle(java.lang.String docTitle) { 439 this.docTitle = docTitle; 440 } 441 442 @Override 443 public String getDocumentTypeId() { 444 return documentTypeId; 445 } 446 447 public void setDocumentTypeId(String documentTypeId) { 448 this.documentTypeId = documentTypeId; 449 } 450 451 public java.lang.Integer getDocVersion() { 452 return docVersion; 453 } 454 455 public void setDocVersion(java.lang.Integer docVersion) { 456 this.docVersion = docVersion; 457 } 458 459 public java.sql.Timestamp getFinalizedDate() { 460 return finalizedDate; 461 } 462 463 public void setFinalizedDate(java.sql.Timestamp finalizedDate) { 464 this.finalizedDate = finalizedDate; 465 } 466 467 public java.lang.String getInitiatorWorkflowId() { 468 return initiatorWorkflowId; 469 } 470 471 public void setInitiatorWorkflowId(java.lang.String initiatorWorkflowId) { 472 this.initiatorWorkflowId = initiatorWorkflowId; 473 } 474 475 public java.lang.String getRoutedByUserWorkflowId() { 476 if ( (isEnroute()) && (StringUtils.isBlank(routedByUserWorkflowId)) ) { 477 return initiatorWorkflowId; 478 } 479 return routedByUserWorkflowId; 480 } 481 482 public void setRoutedByUserWorkflowId(java.lang.String routedByUserWorkflowId) { 483 this.routedByUserWorkflowId = routedByUserWorkflowId; 484 } 485 486 @Override 487 public String getDocumentId() { 488 return documentId; 489 } 490 491 public void setDocumentId(java.lang.String documentId) { 492 this.documentId = documentId; 493 } 494 495 public java.sql.Timestamp getRouteStatusDate() { 496 return routeStatusDate; 497 } 498 499 public void setRouteStatusDate(java.sql.Timestamp routeStatusDate) { 500 this.routeStatusDate = routeStatusDate; 501 } 502 503 public java.sql.Timestamp getDateModified() { 504 return dateModified; 505 } 506 507 public void setDateModified(java.sql.Timestamp dateModified) { 508 this.dateModified = dateModified; 509 } 510 511 /** 512 * 513 * This method returns the Application Document Status. 514 * This status is an alternative to the Route Status that may be used for a document. 515 * It is configurable per document type. 516 * 517 * @see ApplicationDocumentStatus 518 * @see DocumentTypePolicy 519 * 520 * @return 521 */ 522 public java.lang.String getAppDocStatus() { 523 if ( StringUtils.isBlank(appDocStatus) ){ 524 return KewApiConstants.UNKNOWN_STATUS; 525 } 526 return appDocStatus; 527 } 528 529 public void setAppDocStatus(java.lang.String appDocStatus){ 530 this.appDocStatus = appDocStatus; 531 } 532 533 /** 534 * 535 * This method returns a combination of the route status label and the app doc status. 536 * 537 * @return 538 */ 539 public String getCombinedStatus(){ 540 String routeStatus = getRouteStatusLabel(); 541 String appStatus = getAppDocStatus(); 542 if ( StringUtils.isNotEmpty(routeStatus)){ 543 if (StringUtils.isNotEmpty(appStatus)){ 544 routeStatus += ", "+appStatus; 545 } 546 } else { 547 return appStatus; 548 } 549 return routeStatus; 550 } 551 552 /** 553 * 554 * This method sets the appDocStatus. 555 * It firsts validates the new value against the defined acceptable values, if defined. 556 * It also updates the AppDocStatus date, and saves the status transition information 557 * 558 * @param appDocStatus 559 * @throws WorkflowRuntimeException 560 */ 561 public void updateAppDocStatus(java.lang.String appDocStatus) throws WorkflowRuntimeException{ 562 //validate against allowable values if defined 563 if (appDocStatus != null && appDocStatus.length() > 0 && !appDocStatus.equalsIgnoreCase(this.appDocStatus)){ 564 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findById(this.getDocumentTypeId()); 565 if (documentType.getValidApplicationStatuses() != null && documentType.getValidApplicationStatuses().size() > 0){ 566 Iterator<ApplicationDocumentStatus> iter = documentType.getValidApplicationStatuses().iterator(); 567 boolean statusValidated = false; 568 while (iter.hasNext()) 569 { 570 ApplicationDocumentStatus myAppDocStat = iter.next(); 571 if (appDocStatus.compareToIgnoreCase(myAppDocStat.getStatusName()) == 0) 572 { 573 statusValidated = true; 574 break; 575 } 576 } 577 if (!statusValidated){ 578 WorkflowRuntimeException xpee = new WorkflowRuntimeException("AppDocStatus value " + appDocStatus + " not allowable."); 579 LOG.error("Error validating nextAppDocStatus name: " + appDocStatus + " against acceptable values.", xpee); 580 throw xpee; 581 } 582 } 583 584 // set the status value 585 String oldStatus = this.appDocStatus; 586 this.appDocStatus = appDocStatus; 587 588 // update the timestamp 589 setAppDocStatusDate(new Timestamp(System.currentTimeMillis())); 590 591 // save the status transition 592 this.appDocStatusHistory.add(new DocumentStatusTransition(documentId, oldStatus, appDocStatus)); 593 } 594 595 } 596 597 598 public java.sql.Timestamp getAppDocStatusDate() { 599 return appDocStatusDate; 600 } 601 602 public void setAppDocStatusDate(java.sql.Timestamp appDocStatusDate) { 603 this.appDocStatusDate = appDocStatusDate; 604 } 605 606 public Object copy(boolean preserveKeys) { 607 throw new UnsupportedOperationException("The copy method is deprecated and unimplemented!"); 608 } 609 610 /** 611 * @return True if the document is in the state of Initiated 612 */ 613 public boolean isStateInitiated() { 614 return KewApiConstants.ROUTE_HEADER_INITIATED_CD.equals(docRouteStatus); 615 } 616 617 /** 618 * @return True if the document is in the state of Saved 619 */ 620 public boolean isStateSaved() { 621 return KewApiConstants.ROUTE_HEADER_SAVED_CD.equals(docRouteStatus); 622 } 623 624 /** 625 * @return true if the document has ever been inte enroute state 626 */ 627 public boolean isRouted() { 628 return !(isStateInitiated() || isStateSaved()); 629 } 630 631 public boolean isInException() { 632 return KewApiConstants.ROUTE_HEADER_EXCEPTION_CD.equals(docRouteStatus); 633 } 634 635 public boolean isDisaproved() { 636 return KewApiConstants.ROUTE_HEADER_DISAPPROVED_CD.equals(docRouteStatus); 637 } 638 639 public boolean isCanceled() { 640 return KewApiConstants.ROUTE_HEADER_CANCEL_CD.equals(docRouteStatus); 641 } 642 643 public boolean isFinal() { 644 return KewApiConstants.ROUTE_HEADER_FINAL_CD.equals(docRouteStatus); 645 } 646 647 public boolean isEnroute() { 648 return KewApiConstants.ROUTE_HEADER_ENROUTE_CD.equals(docRouteStatus); 649 } 650 651 /** 652 * @return true if the document is in the processed state 653 */ 654 public boolean isProcessed() { 655 return KewApiConstants.ROUTE_HEADER_PROCESSED_CD.equals(docRouteStatus); 656 } 657 658 public boolean isRoutable() { 659 return KewApiConstants.ROUTE_HEADER_ENROUTE_CD.equals(docRouteStatus) || 660 //KewApiConstants.ROUTE_HEADER_EXCEPTION_CD.equals(docRouteStatus) || 661 KewApiConstants.ROUTE_HEADER_SAVED_CD.equals(docRouteStatus) || 662 KewApiConstants.ROUTE_HEADER_PROCESSED_CD.equals(docRouteStatus); 663 } 664 665 /** 666 * Return true if the given action code is valid for this document's current state. 667 * This method only verifies statically defined action/state transitions, it does not 668 * perform full action validation logic. 669 * @see org.kuali.rice.kew.actions.ActionRegistry#getValidActions(org.kuali.rice.kim.api.identity.principal.PrincipalContract, DocumentRouteHeaderValue) 670 * @param actionCd The action code to be tested. 671 * @return True if the action code is valid for the document's status. 672 */ 673 public boolean isValidActionToTake(String actionCd) { 674 String actions = legalActions.get(docRouteStatus); 675 return actions.contains(actionCd); 676 } 677 678 public boolean isValidStatusChange(String newStatus) { 679 return stateTransitionMap.get(getDocRouteStatus()).contains(newStatus); 680 } 681 682 public void setRouteStatus(String newStatus, boolean finalState) throws InvalidActionTakenException { 683 if (newStatus != getDocRouteStatus()) { 684 // only modify the status mod date if the status actually changed 685 setRouteStatusDate(new Timestamp(System.currentTimeMillis())); 686 } 687 if (stateTransitionMap.get(getDocRouteStatus()).contains(newStatus)) { 688 LOG.debug("changing status"); 689 setDocRouteStatus(newStatus); 690 } else { 691 LOG.debug("unable to change status"); 692 throw new InvalidActionTakenException("Document status " + CodeTranslator.getRouteStatusLabel(getDocRouteStatus()) + " cannot transition to status " + CodeTranslator 693 .getRouteStatusLabel(newStatus)); 694 } 695 setDateModified(new Timestamp(System.currentTimeMillis())); 696 if (finalState) { 697 LOG.debug("setting final timeStamp"); 698 setFinalizedDate(new Timestamp(System.currentTimeMillis())); 699 } 700 } 701 702 /** 703 * Mark the document as being processed. 704 * 705 * @throws org.kuali.rice.kew.api.exception.ResourceUnavailableException 706 * @throws InvalidActionTakenException 707 */ 708 public void markDocumentProcessed() throws InvalidActionTakenException { 709 if ( LOG.isDebugEnabled() ) { 710 LOG.debug(this + " marked processed"); 711 } 712 setRouteStatus(KewApiConstants.ROUTE_HEADER_PROCESSED_CD, !FINAL_STATE); 713 } 714 715 /** 716 * Mark document cancled. 717 * 718 * @throws org.kuali.rice.kew.api.exception.ResourceUnavailableException 719 * @throws InvalidActionTakenException 720 */ 721 public void markDocumentCanceled() throws InvalidActionTakenException { 722 if ( LOG.isDebugEnabled() ) { 723 LOG.debug(this + " marked canceled"); 724 } 725 setRouteStatus(KewApiConstants.ROUTE_HEADER_CANCEL_CD, FINAL_STATE); 726 } 727 728 /** 729 * Mark document recalled. 730 * 731 * @throws org.kuali.rice.kew.api.exception.ResourceUnavailableException 732 * @throws InvalidActionTakenException 733 */ 734 public void markDocumentRecalled() throws InvalidActionTakenException { 735 if ( LOG.isDebugEnabled() ) { 736 LOG.debug(this + " marked recalled"); 737 } 738 setRouteStatus(DocumentStatus.RECALLED.getCode(), FINAL_STATE); 739 } 740 741 /** 742 * Mark document disapproved 743 * 744 * @throws ResourceUnavailableException 745 * @throws InvalidActionTakenException 746 */ 747 public void markDocumentDisapproved() throws InvalidActionTakenException { 748 if ( LOG.isDebugEnabled() ) { 749 LOG.debug(this + " marked disapproved"); 750 } 751 setRouteStatus(KewApiConstants.ROUTE_HEADER_DISAPPROVED_CD, FINAL_STATE); 752 } 753 754 /** 755 * Mark document saved 756 * 757 * @throws org.kuali.rice.kew.api.exception.ResourceUnavailableException 758 * @throws InvalidActionTakenException 759 */ 760 public void markDocumentSaved() throws InvalidActionTakenException { 761 if ( LOG.isDebugEnabled() ) { 762 LOG.debug(this + " marked saved"); 763 } 764 setRouteStatus(KewApiConstants.ROUTE_HEADER_SAVED_CD, !FINAL_STATE); 765 } 766 767 /** 768 * Mark the document as being in the exception state. 769 * 770 * @throws org.kuali.rice.kew.api.exception.ResourceUnavailableException 771 * @throws InvalidActionTakenException 772 */ 773 public void markDocumentInException() throws InvalidActionTakenException { 774 if ( LOG.isDebugEnabled() ) { 775 LOG.debug(this + " marked in exception"); 776 } 777 setRouteStatus(KewApiConstants.ROUTE_HEADER_EXCEPTION_CD, !FINAL_STATE); 778 } 779 780 /** 781 * Mark the document as being actively routed. 782 * 783 * @throws ResourceUnavailableException 784 * @throws InvalidActionTakenException 785 */ 786 public void markDocumentEnroute() throws InvalidActionTakenException { 787 if ( LOG.isDebugEnabled() ) { 788 LOG.debug(this + " marked enroute"); 789 } 790 setRouteStatus(KewApiConstants.ROUTE_HEADER_ENROUTE_CD, !FINAL_STATE); 791 } 792 793 /** 794 * Mark document finalized. 795 * 796 * @throws ResourceUnavailableException 797 * @throws InvalidActionTakenException 798 */ 799 public void markDocumentFinalized() throws InvalidActionTakenException { 800 if ( LOG.isDebugEnabled() ) { 801 LOG.debug(this + " marked finalized"); 802 } 803 setRouteStatus(KewApiConstants.ROUTE_HEADER_FINAL_CD, FINAL_STATE); 804 } 805 806 /** 807 * This method takes data from a VO and sets it on this route header 808 * @param routeHeaderVO 809 * @throws org.kuali.rice.kew.api.exception.WorkflowException 810 */ 811 public void setRouteHeaderData(Document routeHeaderVO) throws WorkflowException { 812 if (!ObjectUtils.equals(getDocTitle(), routeHeaderVO.getTitle())) { 813 KEWServiceLocator.getActionListService().updateActionItemsForTitleChange(getDocumentId(), routeHeaderVO.getTitle()); 814 } 815 setDocTitle(routeHeaderVO.getTitle()); 816 setAppDocId(routeHeaderVO.getApplicationDocumentId()); 817 setDateModified(new Timestamp(System.currentTimeMillis())); 818 updateAppDocStatus(routeHeaderVO.getApplicationDocumentStatus()); 819 820 /* set the variables from the routeHeaderVO */ 821 for (Map.Entry<String, String> kvp : routeHeaderVO.getVariables().entrySet()) { 822 setVariable(kvp.getKey(), kvp.getValue()); 823 } 824 } 825 826 public void applyDocumentUpdate(DocumentUpdate documentUpdate) { 827 if (documentUpdate != null) { 828 String thisDocTitle = getDocTitle() == null ? "" : getDocTitle(); 829 String updateDocTitle = documentUpdate.getTitle() == null ? "" : documentUpdate.getTitle(); 830 if (!StringUtils.equals(thisDocTitle, updateDocTitle)) { 831 KEWServiceLocator.getActionListService().updateActionItemsForTitleChange(getDocumentId(), documentUpdate.getTitle()); 832 } 833 setDocTitle(updateDocTitle); 834 setAppDocId(documentUpdate.getApplicationDocumentId()); 835 setDateModified(new Timestamp(System.currentTimeMillis())); 836 updateAppDocStatus(documentUpdate.getApplicationDocumentStatus()); 837 List<String> dirtyFields = documentUpdate.getDirtyFields(); 838 LOG.debug("Applying document update for document " + this.getDocumentId() + " dirtyFields = " + dirtyFields); 839 if(dirtyFields == null || dirtyFields.contains("title")) { 840 setDocTitle(updateDocTitle); 841 } 842 if(dirtyFields == null || dirtyFields.contains("applicationDocumentId")) { 843 setAppDocId(documentUpdate.getApplicationDocumentId()); 844 } 845 setDateModified(new Timestamp(System.currentTimeMillis())); 846 if(dirtyFields == null || dirtyFields.contains("applicationDocumentStatus")) { 847 updateAppDocStatus(documentUpdate.getApplicationDocumentStatus()); 848 } 849 850 Map<String, String> variables = documentUpdate.getVariables(); 851 for (String variableName : variables.keySet()) { 852 setVariable(variableName, variables.get(variableName)); 853 } 854 855 } 856 } 857 858 /** 859 * Convenience method that returns the branch of the first (and presumably only?) initial node 860 * @return the branch of the first (and presumably only?) initial node 861 */ 862 public Branch getRootBranch() { 863 if (!this.initialRouteNodeInstances.isEmpty()) { 864 return getInitialRouteNodeInstance(0).getBranch(); 865 } 866 return null; 867 } 868 869 /** 870 * Looks up a variable (embodied in a "BranchState" key/value pair) in the 871 * branch state table. 872 */ 873 private BranchState findVariable(String name) { 874 Branch rootBranch = getRootBranch(); 875 if (rootBranch != null) { 876 List<BranchState> branchState = rootBranch.getBranchState(); 877 Iterator<BranchState> it = branchState.iterator(); 878 while (it.hasNext()) { 879 BranchState state = it.next(); 880 if (ObjectUtils.equals(state.getKey(), BranchState.VARIABLE_PREFIX + name)) { 881 return state; 882 } 883 } 884 } 885 return null; 886 } 887 888 /** 889 * Gets a variable 890 * @param name variable name 891 * @return variable value, or null if variable is not defined 892 */ 893 public String getVariable(String name) { 894 BranchState state = findVariable(name); 895 if (state == null) { 896 if ( LOG.isDebugEnabled() ) { 897 LOG.debug("Variable not found: '" + name + "'"); 898 } 899 return null; 900 } 901 return state.getValue(); 902 } 903 904 public void removeVariableThatContains(String name) { 905 List<BranchState> statesToRemove = new ArrayList<BranchState>(); 906 for (BranchState state : this.getRootBranchState()) { 907 if (state.getKey().contains(name)) { 908 statesToRemove.add(state); 909 } 910 } 911 this.getRootBranchState().removeAll(statesToRemove); 912 } 913 914 /** 915 * Sets a variable 916 * @param name variable name 917 * @param value variable value, or null if variable should be removed 918 */ 919 public void setVariable(String name, String value) { 920 BranchState state = findVariable(name); 921 Branch rootBranch = getRootBranch(); 922 if (rootBranch != null) { 923 List<BranchState> branchState = rootBranch.getBranchState(); 924 if (state == null) { 925 if (value == null) { 926 if ( LOG.isDebugEnabled() ) { 927 LOG.debug("set non existent variable '" + name + "' to null value"); 928 } 929 return; 930 } 931 LOG.debug("Adding branch state: '" + name + "'='" + value + "'"); 932 state = new BranchState(); 933 state.setBranch(rootBranch); 934 state.setKey(BranchState.VARIABLE_PREFIX + name); 935 state.setValue(value); 936 rootBranch.addBranchState(state); 937 } else { 938 if (value == null) { 939 if ( LOG.isDebugEnabled() ) { 940 LOG.debug("Removing value: " + state.getKey() + "=" + state.getValue()); 941 } 942 branchState.remove(state); 943 } else { 944 if ( LOG.isDebugEnabled() ) { 945 LOG.debug("Setting value of variable '" + name + "' to '" + value + "'"); 946 } 947 state.setValue(value); 948 } 949 } 950 } 951 } 952 953 public List<BranchState> getRootBranchState() { 954 if (this.getRootBranch() != null) { 955 return this.getRootBranch().getBranchState(); 956 } 957 return null; 958 } 959 960 public CustomActionListAttribute getCustomActionListAttribute() throws WorkflowException { 961 CustomActionListAttribute customActionListAttribute = null; 962 if (this.getDocumentType() != null) { 963 customActionListAttribute = this.getDocumentType().getCustomActionListAttribute(); 964 if (customActionListAttribute != null) { 965 return customActionListAttribute; 966 } 967 } 968 customActionListAttribute = new DefaultCustomActionListAttribute(); 969 return customActionListAttribute; 970 } 971 972 public CustomEmailAttribute getCustomEmailAttribute() throws WorkflowException { 973 CustomEmailAttribute customEmailAttribute = null; 974 try { 975 if (this.getDocumentType() != null) { 976 customEmailAttribute = this.getDocumentType().getCustomEmailAttribute(); 977 if (customEmailAttribute != null) { 978 customEmailAttribute.setRouteHeaderVO(DocumentRouteHeaderValue.to(this)); 979 return customEmailAttribute; 980 } 981 } 982 } catch (Exception e) { 983 LOG.warn("Error in retrieving custom email attribute", e); 984 } 985 customEmailAttribute = new CustomEmailAttributeImpl(); 986 customEmailAttribute.setRouteHeaderVO(DocumentRouteHeaderValue.to(this)); 987 return customEmailAttribute; 988 } 989 990 public CustomNoteAttribute getCustomNoteAttribute() throws WorkflowException 991 { 992 CustomNoteAttribute customNoteAttribute = null; 993 try { 994 if (this.getDocumentType() != null) { 995 customNoteAttribute = this.getDocumentType().getCustomNoteAttribute(); 996 if (customNoteAttribute != null) { 997 customNoteAttribute.setRouteHeaderVO(DocumentRouteHeaderValue.to(this)); 998 return customNoteAttribute; 999 } 1000 } 1001 } catch (Exception e) { 1002 LOG.warn("Error in retrieving custom note attribute", e); 1003 } 1004 customNoteAttribute = new CustomNoteAttributeImpl(); 1005 customNoteAttribute.setRouteHeaderVO(DocumentRouteHeaderValue.to(this)); 1006 return customNoteAttribute; 1007 } 1008 1009 public ActionRequestValue getDocActionRequest(int index) { 1010 List<ActionRequestValue> actionRequests = getActionRequests(); 1011 while (actionRequests.size() <= index) { 1012 ActionRequestValue actionRequest = new ActionRequestFactory(this).createBlankActionRequest(); 1013 actionRequest.setNodeInstance(new RouteNodeInstance()); 1014 actionRequests.add(actionRequest); 1015 } 1016 return actionRequests.get(index); 1017 } 1018 1019 public ActionTakenValue getDocActionTaken(int index) { 1020 List<ActionTakenValue> actionsTaken = getActionsTaken(); 1021 while (actionsTaken.size() <= index) { 1022 actionsTaken.add(new ActionTakenValue()); 1023 } 1024 return actionsTaken.get(index); 1025 } 1026 1027 public ActionItem getDocActionItem(int index) { 1028 List<ActionItem> actionItems = getActionItems(); 1029 while (actionItems.size() <= index) { 1030 actionItems.add(new ActionItem()); 1031 } 1032 return actionItems.get(index); 1033 } 1034 1035 private RouteNodeInstance getInitialRouteNodeInstance(int index) { 1036 if (initialRouteNodeInstances.size() >= index) { 1037 return initialRouteNodeInstances.get(index); 1038 } 1039 return null; 1040 } 1041 1042 public boolean isRoutingReport() { 1043 return routingReport; 1044 } 1045 1046 public void setRoutingReport(boolean routingReport) { 1047 this.routingReport = routingReport; 1048 } 1049 1050 public List<RouteNodeInstance> getInitialRouteNodeInstances() { 1051 return initialRouteNodeInstances; 1052 } 1053 1054 public void setInitialRouteNodeInstances(List<RouteNodeInstance> initialRouteNodeInstances) { 1055 this.initialRouteNodeInstances = initialRouteNodeInstances; 1056 } 1057 1058 public List<Note> getNotes() { 1059 return notes; 1060 } 1061 1062 public void setNotes(List<Note> notes) { 1063 this.notes = notes; 1064 } 1065 1066 public DocumentRouteHeaderValueContent getDocumentContent() { 1067 if (documentContent == null) { 1068 documentContent = KEWServiceLocator.getRouteHeaderService().getContent(getDocumentId()); 1069 } 1070 return documentContent; 1071 } 1072 1073 public void setDocumentContent(DocumentRouteHeaderValueContent documentContent) { 1074 this.documentContent = documentContent; 1075 } 1076 1077 public List<DocumentStatusTransition> getAppDocStatusHistory() { 1078 return this.appDocStatusHistory; 1079 } 1080 1081 public void setAppDocStatusHistory( 1082 List<DocumentStatusTransition> appDocStatusHistory) { 1083 this.appDocStatusHistory = appDocStatusHistory; 1084 } 1085 1086 @Override 1087 public DocumentStatus getStatus() { 1088 return DocumentStatus.fromCode(getDocRouteStatus()); 1089 } 1090 1091 @Override 1092 public DateTime getDateCreated() { 1093 if (getCreateDate() == null) { 1094 return null; 1095 } 1096 return new DateTime(getCreateDate().getTime()); 1097 } 1098 1099 @Override 1100 public DateTime getDateLastModified() { 1101 if (getDateModified() == null) { 1102 return null; 1103 } 1104 return new DateTime(getDateModified().getTime()); 1105 } 1106 1107 @Override 1108 public DateTime getDateApproved() { 1109 if (getApprovedDate() == null) { 1110 return null; 1111 } 1112 return new DateTime(getApprovedDate().getTime()); 1113 } 1114 1115 @Override 1116 public DateTime getDateFinalized() { 1117 if (getFinalizedDate() == null) { 1118 return null; 1119 } 1120 return new DateTime(getFinalizedDate().getTime()); 1121 } 1122 1123 @Override 1124 public String getTitle() { 1125 return docTitle; 1126 } 1127 1128 @Override 1129 public String getApplicationDocumentId() { 1130 return appDocId; 1131 } 1132 1133 @Override 1134 public String getInitiatorPrincipalId() { 1135 return initiatorWorkflowId; 1136 } 1137 1138 @Override 1139 public String getRoutedByPrincipalId() { 1140 return routedByUserWorkflowId; 1141 } 1142 1143 @Override 1144 public String getDocumentTypeName() { 1145 return getDocumentType().getName(); 1146 } 1147 1148 @Override 1149 public String getDocumentHandlerUrl() { 1150 return getDocumentType().getResolvedDocumentHandlerUrl(); 1151 } 1152 1153 @Override 1154 public String getApplicationDocumentStatus() { 1155 return appDocStatus; 1156 } 1157 1158 @Override 1159 public DateTime getApplicationDocumentStatusDate() { 1160 if (appDocStatusDate == null) { 1161 return null; 1162 } 1163 return new DateTime(appDocStatusDate.getTime()); 1164 } 1165 1166 @Override 1167 public Map<String, String> getVariables() { 1168 Map<String, String> documentVariables = new HashMap<String, String>(); 1169 /* populate the routeHeaderVO with the document variables */ 1170 // FIXME: we assume there is only one for now 1171 Branch routeNodeInstanceBranch = getRootBranch(); 1172 // Ok, we are using the "branch state" as the arbitrary convenient repository for flow/process/edoc variables 1173 // so we need to stuff them into the VO 1174 if (routeNodeInstanceBranch != null) { 1175 List<BranchState> listOfBranchStates = routeNodeInstanceBranch.getBranchState(); 1176 for (BranchState bs : listOfBranchStates) { 1177 if (bs.getKey() != null && bs.getKey().startsWith(BranchState.VARIABLE_PREFIX)) { 1178 if ( LOG.isDebugEnabled() ) { 1179 if ( LOG.isDebugEnabled() ) { 1180 LOG.debug("Setting branch state variable on vo: " + bs.getKey() + "=" + bs.getValue()); 1181 } 1182 } 1183 documentVariables.put(bs.getKey().substring(BranchState.VARIABLE_PREFIX.length()), bs.getValue()); 1184 } 1185 } 1186 } 1187 return documentVariables; 1188 } 1189 1190 public static Document to(DocumentRouteHeaderValue documentBo) { 1191 if (documentBo == null) { 1192 return null; 1193 } 1194 Document.Builder builder = Document.Builder.create(documentBo); 1195 return builder.build(); 1196 } 1197 1198 public static DocumentRouteHeaderValue from(Document document) { 1199 DocumentRouteHeaderValue documentBo = new DocumentRouteHeaderValue(); 1200 documentBo.setAppDocId(document.getApplicationDocumentId()); 1201 if (document.getDateApproved() != null) { 1202 documentBo.setApprovedDate(new Timestamp(document.getDateApproved().getMillis())); 1203 } 1204 if (document.getDateCreated() != null) { 1205 documentBo.setCreateDate(new Timestamp(document.getDateCreated().getMillis())); 1206 } 1207 if (StringUtils.isEmpty(documentBo.getDocContent())) { 1208 documentBo.setDocContent(KewApiConstants.DEFAULT_DOCUMENT_CONTENT); 1209 } 1210 documentBo.setDocRouteStatus(document.getStatus().getCode()); 1211 documentBo.setDocTitle(document.getTitle()); 1212 if (document.getDocumentTypeName() != null) { 1213 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(document.getDocumentTypeName()); 1214 if (documentType == null) { 1215 throw new RiceRuntimeException("Could not locate the given document type name: " + document.getDocumentTypeName()); 1216 } 1217 documentBo.setDocumentTypeId(documentType.getDocumentTypeId()); 1218 } 1219 if (document.getDateFinalized() != null) { 1220 documentBo.setFinalizedDate(new Timestamp(document.getDateFinalized().getMillis())); 1221 } 1222 documentBo.setInitiatorWorkflowId(document.getInitiatorPrincipalId()); 1223 documentBo.setRoutedByUserWorkflowId(document.getRoutedByPrincipalId()); 1224 documentBo.setDocumentId(document.getDocumentId()); 1225 if (document.getDateLastModified() != null) { 1226 documentBo.setDateModified(new Timestamp(document.getDateLastModified().getMillis())); 1227 } 1228 documentBo.setAppDocStatus(document.getApplicationDocumentStatus()); 1229 if (document.getApplicationDocumentStatusDate() != null) { 1230 documentBo.setAppDocStatusDate(new Timestamp(document.getApplicationDocumentStatusDate().getMillis())); 1231 } 1232 1233 1234 // Convert the variables 1235 Map<String, String> variables = document.getVariables(); 1236 if( variables != null && !variables.isEmpty()){ 1237 for(Map.Entry<String, String> kvp : variables.entrySet()){ 1238 documentBo.setVariable(kvp.getKey(), kvp.getValue()); 1239 } 1240 } 1241 1242 return documentBo; 1243 } 1244 1245 @Override 1246 public void refresh() { 1247 } 1248 1249 public DocumentRouteHeaderValue deepCopy(Map<Object, Object> visited) { 1250 if (visited.containsKey(this)) { 1251 return (DocumentRouteHeaderValue)visited.get(this); 1252 } 1253 DocumentRouteHeaderValue copy = new DocumentRouteHeaderValue(); 1254 visited.put(this, copy); 1255 copy.documentId = documentId; 1256 copy.documentTypeId = documentTypeId; 1257 copy.docRouteStatus = docRouteStatus; 1258 copy.docRouteLevel = docRouteLevel; 1259 copy.dateModified = copyTimestamp(dateModified); 1260 copy.createDate = copyTimestamp(createDate); 1261 copy.approvedDate = copyTimestamp(approvedDate); 1262 copy.finalizedDate = copyTimestamp(finalizedDate); 1263 copy.docTitle = docTitle; 1264 copy.appDocId = appDocId; 1265 copy.docVersion = docVersion; 1266 copy.initiatorWorkflowId = initiatorWorkflowId; 1267 copy.routedByUserWorkflowId = routedByUserWorkflowId; 1268 copy.routeStatusDate = copyTimestamp(routeStatusDate); 1269 copy.appDocStatus = appDocStatus; 1270 copy.appDocStatusDate = copyTimestamp(appDocStatusDate); 1271 if (documentContent != null) { 1272 copy.documentContent = documentContent.deepCopy(visited); 1273 } 1274 copy.routingReport = routingReport; 1275 if (initialRouteNodeInstances != null) { 1276 List<RouteNodeInstance> copies = new ArrayList<RouteNodeInstance>(); 1277 for (RouteNodeInstance routeNodeInstance : initialRouteNodeInstances) { 1278 copies.add(routeNodeInstance.deepCopy(visited)); 1279 } 1280 copy.setInitialRouteNodeInstances(copies); 1281 } 1282 if (appDocStatusHistory != null) { 1283 List<DocumentStatusTransition> copies = new ArrayList<DocumentStatusTransition>(); 1284 for (DocumentStatusTransition documentStatusTransition : appDocStatusHistory) { 1285 copies.add(documentStatusTransition.deepCopy(visited)); 1286 } 1287 copy.setAppDocStatusHistory(copies); 1288 } 1289 if (notes != null) { 1290 List<Note> copies = new ArrayList<Note>(); 1291 for (Note note : notes) { 1292 copies.add(note.deepCopy(visited)); 1293 } 1294 copy.setNotes(copies); 1295 } 1296 1297 return copy; 1298 } 1299 1300 private Timestamp copyTimestamp(Timestamp timestamp) { 1301 if (timestamp == null) { 1302 return null; 1303 } 1304 return new Timestamp(timestamp.getTime()); 1305 } 1306 1307}