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