How to implement reject in parallel approval workflow

mosssharepointsharepoint-2007workflowworkflow-foundation

I develop a SharePoint workflow with a Replicator activity to replicate a custom activity for every approver. The custom activity implements an approval branch for a particular user. It has classic form with CreateTask, While, OnTaskChanged and CompleteTask activities.

I setup UntilCondition on the replicator to cancel execution after one approver chooses to reject the approval and then workflow finishes. The problem happens with other uncompleted tasks which "hang" in their current state. User does not see this state when open the task.

I put UpdateAllTasks after the replacator to set the task status to Cancelled. But since there is no event activities between CompleteTask (for the rejected task) and UpdateAllTasks, the UpdateAllTask activity set Cancelled for the rejected task also.

The question, what can I do to flush the pending change made by CompleteTask before UpdateAllTasks?

Or perhaps, there is another way to implement such workflow. I was thinking about the way to implement Cancel handler for the custom activity with UpdateTask. But I do not know how to implement it and tell to the cancel handler that it executes in the case of the rejection.

Best Answer

After being faced with the same problem and spending a lot of time researching and trying out different options, I think I found a really good solution. I'm posting it here for posterity.

  1. Create a custom activity that extends SequenceActivity called ReviewActivity
  2. The ReviewActivity includes the typical CreateTask -> While -> OnTaskChanged -> CompleteTask scenario
  3. In my workflow, I have a Replicator that is creating many instances of the ReviewActivity (and thus many Tasks).
  4. The replicator implements an UntilCondition that checks to see if a task was rejected (this is set in the ChildCompleted)
  5. After the Replicator, I have an UpdateAllTasks to close the remaining tasks

If you have any experience with this scenario at all, you are getting ready to tell me that UpdateAllTasks also updates the originally rejected task since the "CompleteTask" has not been persisted to the database yet. The magic is in an attribute that you can define for the custom activity (ReviewActivity) called PersistOnClose.

[Designer(typeof(ActivityDesigner), typeof(IDesigner))]
[PersistOnClose]
public partial class ReviewActivity : SequenceActivity

This attribute ensures that once the ReviewActivity is complete, all changes are persisted to the database. Since the last activity in the ReviewActivity is the "CompleteTask", the task is saved to the DB. Therefore, the UpdateAllTasks will not touch it.

I hope this helps someone.

Related Topic