Creating SharePoint State Machine Workflow

State machine workflow is the powerful feature of SharePoint, which can be easily done using Visual Studio.
click here to read my previous tutorial for Creating a SharePoint Sequential Visual Studio Workflow (2 or n Level) 
click here to read my previous tutorial on Creating a SharePoint Sequential Visual Studio Workflow (1 Level) 
Let me explain how to create a state machine workflow with a example scenario.
Scenario:
Let's consider a scenario in college.
  • When a student requests for some document with the Administration office, first it goes to an officer, when he approves, it goes to higher officer for approval.
  • When first officer approves, it has to go to next officer and when the 2nd officer approves, the workflow is complete.
  • When first officer rejects, it has to go back to student for re-submission.
  • When first officer approves, and second officer rejects, it has to go back to the first officer for re-approval.
Let us create a state machine workflow that matches above scenario. Let's go through step by step.
1.Open Visual studio (Run as administrator).
 Select State Machine Workflow under SharePoint 2010 template. Enter the project name

2. Enter your site address and select farm solution and click next

3. Select List workflow and click next
  
4. Select the list , in our case Shared Documents.

5. A workflow can be started (triggered) in 3 ways:
    a) Manually
    b) When item is created
    c) When item is updated
Select 'when an item created' and click finish

6. Now you can see the screen as below with your workflow design and toolbox at left side.

7. Use the toolbox and drag and drop 2 State Activities to your workflow design, so your final design will look as below.

  8. Select the Workflow1InitialState and press F4 to open its properties. Rename this to InitialState as shown below

9. Similarly rename the other 2 stateActivities to stateApproval and stateComplete.

10. Right click on InitialState and select Set as Initial State
  
11. Right click stateComplete   and select Set as Completed State
 12. Double click on EventDrivenActivity1 in Initial state. Below screen would appear.

13. Drag and drop SetState Activity below onWorkflowActivated1


14. Select setStateActivity1 press F4, in the properties panel set stateApproval as TargetStateName

15.  Click on Workflow1 to go back to the main design window.

16. Now your design would look like this

17. Right click on stateApproval, select Add StateInitialization

18.  Below screen would appear,

19. Drag and drop Create Task from the toolbox

20. Select CreateTask1, press F4, in the properties window type Correlation token as task token and OwnerActivityName as stateApproval.

 21. Click TaskId, click on the small bubble that appears at the end or double click on small yellow box. Below screen would appear and select "Bind to a new member" and hit "Create Field". click OK

22. Repeat the above process for TaskProperties.

23 . Now right click on stateApproval and select Add EventDriven




24. Now below the eventdriveActivity drag and drop OnTaskChanged, 2 IfElse (one inside another), and 3 SetState activities and make your design look like below.

25. select setStateActivity2 , press F4, and Choose the TargetStateName as stetComplete.

26. Repeat the above process for setStateActivity3 and setStateActivity4 by giving TargetStateName as stateApproval. Now the design will look like below.

27. select OntaskChanged1 and press F4, in the properties panel, double click on the small bubble appearing at the end of textbox for AfterProperties and choose Bind to a new member, select create field.

 28. repeat above step for BeforeProperties as well.
 29. select correlation token as task token, Click the bubble near Taskd and select Bind to existing member, select CreateTask1_TaskId1, click OK.

30. Now we are done with the design part and go back to  main design window, your design should look like this.

31. In the InitialState, double click on EventDrivenActivity1 and now double click on onWorkflowActivated1:
        List<string> approverList = new List<string>();
        int currentLevel;
        private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e)
        {
            //you can read approvers from sharepoint list as well and save to approverList
            approverList.Add(@"domain\user1");
            approverList.Add(@"
domain\user2");

            currentLevel = 1;
        }
add these 2 lines above the method
List<string> approverList = new List<string>();
        int currentLevel;
32. Now go back to main design window, double click on stateInitializationActivity1 under stateApproval and now double click on createTask1. and enter below code.
       private void createTask1_MethodInvoking(object sender, EventArgs e)
        {
            createTask1_TaskId1 = Guid.NewGuid();
            if (currentLevel == 0)
            {
                string user = workflowProperties.Item["Author"].ToString();
                if (user.Contains("#"))
                {
                    user = user.Substring(user.IndexOf("#"));
                }
                SPUser user1 = workflowProperties.Web.EnsureUser(user);
                createTask1_TaskProperties1.AssignedTo = user1.LoginName;
            }
            else
            {
                SPUser user = workflowProperties.Web.EnsureUser(approverList[currentLevel - 1]);
                createTask1_TaskProperties1.AssignedTo = user.LoginName;
            }
            createTask1_TaskProperties1.Title = "please Approve";
        }

33. Now go back to main design and double click on eventDrivenActivity2 and now double click on onTaskChanged1, enter the below code.
private void onTaskChanged1_Invoked(object sender, ExternalDataEventArgs e)
        {
            onTaskChanged1_AfterProperties1 = onTaskChanged1.AfterProperties;
            onTaskChanged1_BeforeProperties1 = onTaskChanged1.BeforeProperties;
        }
34. Now copy below 2 methods
     private void isApproved(object sender, ConditionalEventArgs e)
        {
            if (onTaskChanged1_AfterProperties1.PercentComplete == 1.0)
            {
                currentLevel++;
                e.Result = true;
            }
            else
            {
                if (currentLevel == 1)
                {
                    //assign to requestor
                    currentLevel = 0;
                                  e.Result = false;
                }
                else
                    if (currentLevel > 1)
                    {
                        currentLevel--;
                        e.Result = false;

                    }
            }
        }

        private void isApprovalSequenceComplete(object sender, ConditionalEventArgs e)
        {
            if (currentLevel > approverList.Count)
            {
                //Approval complete
                e.Result = true;
            }
            else
            {
                //goto to next approver
                e.Result = false;
            }
        }
 35. Now select IfElseBranchActivity1, press F4, in the properties panel, choose CodeCondition for Condition, and select isApproved method for condition.

36. Repeat above procedure for IfElseBranchActivity3 and select codition as isApprovalSequenceComplete
37. Now we are done with the code, build the solution, and then deploy

38. Go to Shared Documents and upload some document

39. Now you can see your workflow showing in a new column with status as InProgress Since we have selected workflow to begin "when an item created"


40. Click on InProgress and it will open the WorkFlow properties. Edit the created task as shown.
  
41. Enter 100 for %Complete field and Save

42.  As you can see, the task has been created for the next approver.

43.  Edit it as we have done previously, make the %complete to 100 and save.
Now go back to the list. As you can see the workflow is Completed.
 You can check this for our scenario where,
  1. If 1st person approves, goes to 2nd person, 2nd person approves, workflow complete
  2. 1st person rejects, task goes to document requestor
  3. 2nd person rejects task assigns to 1st person
  Below is the complete code for reference:
namespace StateMWF.Workflow1
{
    public sealed partial class Workflow1 : StateMachineWorkflowActivity
    {
        public Workflow1()
        {
            InitializeComponent();
        }

        public SPWorkflowActivationProperties workflowProperties = new SPWorkflowActivationProperties();
        public Guid createTask1_TaskId1 = default(System.Guid);
        public SPWorkflowTaskProperties createTask1_TaskProperties1 = new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();
        public SPWorkflowTaskProperties onTaskChanged1_AfterProperties1 = new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();
        public SPWorkflowTaskProperties onTaskChanged1_BeforeProperties1 = new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();

        List<string> approverList = new List<string>();
        int currentLevel;
        private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e)
        {
            //you can read approvers from sharepoint list as well and save to approverList
            approverList.Add(@"domain\user");
            approverList.Add(@"domain\user");

            currentLevel = 1;
        }

        private void createTask1_MethodInvoking(object sender, EventArgs e)
        {
            createTask1_TaskId1 = Guid.NewGuid();
            if (currentLevel == 0)
            {
                string user = workflowProperties.Item["Author"].ToString();
                if (user.Contains("#"))
                {
                    user = user.Substring(user.IndexOf("#"));
                }
                SPUser user1 = workflowProperties.Web.EnsureUser(user);
                createTask1_TaskProperties1.AssignedTo = user1.LoginName;
            }
            else
            {
                SPUser user = workflowProperties.Web.EnsureUser(approverList[currentLevel - 1]);
                createTask1_TaskProperties1.AssignedTo = user.LoginName;
            }
            createTask1_TaskProperties1.Title = "please Approve";
        }

        private void onTaskChanged1_Invoked(object sender, ExternalDataEventArgs e)
        {
            onTaskChanged1_AfterProperties1 = onTaskChanged1.AfterProperties;
            onTaskChanged1_BeforeProperties1 = onTaskChanged1.BeforeProperties;
        }
        private void isApproved(object sender, ConditionalEventArgs e)
        {
            if (onTaskChanged1_AfterProperties1.PercentComplete == 1.0)
            {
                currentLevel++;
                e.Result = true;
            }
            else
            {
                if (currentLevel == 1)
                {
                    //assign to requestor
                    currentLevel = 0;
                              e.Result = false;
                }
                else
                    if (currentLevel > 1)
                    {
                        currentLevel--;
                        e.Result = false;

                    }
            }
        }
        private void isApprovalSequenceComplete(object sender, ConditionalEventArgs e)
        {
            if (currentLevel > approverList.Count)
            {
                //Approval complete
                e.Result = true;
            }
            else
            {
                //goto to next approver
                e.Result = false;
            }
        }
    }
}
Explanation:
  • When an item in Shared Document is created, the workflow is triggered.
  • Initially when workflow is activated, we are getting data for list of approvers. Here you can modify the code to get the approvers from sharepoint list as well.
  •  Task is created for the first approver.
  • onTaskChanged will assign the task after properties and before properties to global variables.
  • Then using IfElse condition we will check if the currect task is approved, by checking the percent complete. If complete we will move to the next approval level and create task for next approver. If not we will go back to previous level. (if task is rejected by 1st approver, it goes to the requestor, i.e. creates task for requestor).
  • It repeats until the approval sequence is complete.
happy coding :)

Comments

Popular posts from this blog

SharePoint Online (O365) OAuth Authentication | Authorizing REST API calls against SharePoint Online Site | Get Access token from SharePoint Online | Set up OAuth for SharePoint Online Office 365

SharePoint 2013 REST API Reference

Simple Risk Assessment Matrix table with resultant risk calculation

Kendo UI (Core JavaScript) grid Server Paging, Server Sorting and Server Filtering with Dynamic SQL queries

Sharepoint- Using an Image From formatmap32x32.png in a Ribbon Control