Create SharePoint Sequential Workflow with multiple tasks in Visual Studio 2010

What I wanted to Achieve:

  1. CityLink sends us an invoice (along with a csv file for travel details) every month. Before we made the payment, we need all e-Tag holders to review their travel details in the invoice and make sure they are correct.
  2. User uploads the TravelDetails.csv file into a document library.
  3. It triggers the workflow and creates tasks for e-Tag holders to review their travel details (These people are listed in a list called “e-Tag Holders”. This list contains people’s names in the TravelDetails.csv file and their corresponding user account in SharePoint)
  4. The workflow reads all the lines in the csv file and send diffrent part of the travel details to diffrent people via Emails. It also creates task for them so after they have reviewed their part of travel details, they can mark the task as complete.
  5. Once all tasks have been completed, it sends an email to a SharePoint group “CityLink Invoice Notification” telling them it is completed.

How I achived it:

Create a new project:

vswf1

Drag activities into the designer and it looks like this:

2015-09-18 10_50_10-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

This is the properties of onWorkflowActivated1:

2015-09-18 10_52_56-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

Properties of replicatorActivity1:

2015-09-18 10_55_53-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

Properties of createTask1:

2015-09-18 10_58_22-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

When binding the ListItemId, TaskId and TaskProperties, click the little … button on the right, and then click “Bind to a new member”, select “Create Field”:

2015-09-18 12_10_38-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

Properties of onTaskCreated1 (This activity does nothing, but deleting it made my workflow not working for some unknown reason):

2015-09-18 10_59_07-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

CodeAcitivity1:

2015-09-18 11_00_35-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

whileActivity1:

2015-09-18 11_01_52-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

onTaskChanged1:

2015-09-18 11_02_58-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

completeTask1:

2015-09-18 11_04_16-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

codeActivity2:

2015-09-18 11_05_09-SharePoint Development (Snapshot 1) [Running] - Oracle VM VirtualBox

Code Behind:

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
using System.Text;
using System.IO;
using System.Data;
using Microsoft.SharePoint.Utilities;
using System.Collections.Specialized;

namespace Workflow_CityLink_Invoices.Workflow1
{
public sealed partial class Workflow1 : SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
}

public Guid workflowId = default(System.Guid);
public SPWorkflowActivationProperties workflowProperties = new SPWorkflowActivationProperties();
int countTask = 0;
int countCompletedTask = 0;
int counter = 0;

string invoicePeriod = “”;

ArrayList assignees = null;
DataTable dtInvoice = new DataTable();
DataTable dtETagHolders = new DataTable();

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

public String taskOutcome = default(System.String);
private bool isFinished = false;

public Int32 createTask1_ListItemId1 = default(System.Int32);

private void LogComment(string logMessage, string outcome)
{
SPWorkflow.CreateHistoryEvent(workflowProperties.Web, this.WorkflowInstanceId, 0, workflowProperties.Web.CurrentUser, new TimeSpan(), outcome, logMessage, string.Empty);
}

private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e)
{
// Initialisation
countTask = 0;
countCompletedTask = 0;
counter = 0;
workflowId = Guid.NewGuid();

dtInvoice.Columns.Add(“Name”, Type.GetType(“System.String”));
dtInvoice.Columns.Add(“LicencePlate”, Type.GetType(“System.String”));
dtInvoice.Columns.Add(“eTag”, Type.GetType(“System.String”));
dtInvoice.Columns.Add(“TravelDetails”, Type.GetType(“System.String”));
dtInvoice.Columns.Add(“StartDate”, Type.GetType(“System.String”));
dtInvoice.Columns.Add(“StartTime”, Type.GetType(“System.String”));
dtInvoice.Columns.Add(“FinishDate”, Type.GetType(“System.String”));
dtInvoice.Columns.Add(“FinishTime”, Type.GetType(“System.String”));
dtInvoice.Columns.Add(“GST”, Type.GetType(“System.String”));
dtInvoice.Columns.Add(“Amount”, Type.GetType(“System.String”));

dtETagHolders.Columns.Add(“NameInCSV”, Type.GetType(“System.String”));
dtETagHolders.Columns.Add(“loginName”, Type.GetType(“System.String”));
dtETagHolders.Columns.Add(“travelDetails”, Type.GetType(“System.String”));
dtETagHolders.Columns.Add(“userEmail”, Type.GetType(“System.String”));
dtETagHolders.Columns.Add(“userID”, Type.GetType(“System.Int32”));
dtETagHolders.Columns.Add(“emailSent”, Type.GetType(“System.String”));

// Create a set of assignees for task.
assignees = new ArrayList();

SPList eTagHoldersList = workflowProperties.Web.Lists[“e-Tag Holders”];
SPListItemCollection listItems = eTagHoldersList.Items;
foreach (SPListItem item in listItems)
{
string nameInCSV = item[“Title”].ToString();
int userID = Convert.ToInt32(item[“Account Name”].ToString().Substring(0, item[“Account Name”].ToString().IndexOf(“;#”)));
SPUser theUser = workflowProperties.Web.SiteUsers.GetByID(userID);
string loginName = theUser.LoginName;
string email = theUser.Email;
assignees.Add(loginName);

DataRow dr = dtETagHolders.NewRow();
dr[0] = nameInCSV;
dr[1] = loginName;
dr[3] = email;
dr[4] = userID;
dtETagHolders.Rows.Add(dr);
}

SPListItem currentItem = workflowProperties.Item;

try
{
string contents = string.Empty;
string filePath = currentItem.Url;
SPFile spfile = workflowProperties.Web.GetFile(filePath);
if (spfile.Exists)
{

StreamReader file = new StreamReader(spfile.OpenBinaryStream());
string line;
int index = 0;
int flag = 1000;
while ((line = file.ReadLine()) != null)
{
char[] splitter = { ‘,’ };
String[] arrLine = line.ToString().Split(splitter);

if (arrLine[0] == “Invoice period”)
{
invoicePeriod = Convert.ToString(arrLine[1]);
}

if (arrLine[0] == “Fleet identifier”)
{
flag = index;
}
if (index > flag + 2)
{
if (!String.IsNullOrEmpty(Convert.ToString(arrLine[0])))
{
DataRow dr = dtInvoice.NewRow();
dr[0] = arrLine[0];
dr[1] = arrLine[1];
dr[2] = arrLine[2];
dr[3] = arrLine[3];
dr[4] = arrLine[4];
dr[5] = arrLine[5];
dr[6] = arrLine[6];
dr[7] = arrLine[7];
dr[8] = arrLine[8];
dr[9] = arrLine[9];
dtInvoice.Rows.Add(dr);
}
}

index++;
}

}

}
catch (Exception ex)
{
LogComment(ex.ToString(), “Error”);
}

}

private void replicateTasks_Initialized(object sender, EventArgs e)
{

replicatorActivity1.InitialChildData = assignees;
}

private string checkNameInCSV(string loginName)
{
string result = “”;
foreach (DataRow dr in dtETagHolders.Rows)
{
string nameInCSV = Convert.ToString(dr[0]);
string loginName2 = Convert.ToString(dr[1]);
if (loginName.ToUpper() == loginName2.ToUpper())
{
result = nameInCSV.ToUpper();
break;
}
}
return result;
}

private void createTask1_MethodInvoking(object sender, EventArgs e)
{
// Triggered When creating a new task

// Initialisation
isFinished = false;

createTask1_TaskProperties1 = new SPWorkflowTaskProperties();
createTask1_TaskId1 = Guid.NewGuid();
createTask1_TaskProperties1.Title = “Please review CityLink travel details ” + invoicePeriod;

string assignedTo = replicatorActivity1.InitialChildData[countTask].ToString();
string html = “”;
string nameInCSV = checkNameInCSV(assignedTo);

foreach (DataRow dr1 in dtInvoice.Rows)
{
bool matched = false;
if (Convert.ToString(dr1[0]).ToUpper() == nameInCSV)
{
matched = true;
}

if (matched)
{
html += “<tr><td>” + dr1[0] + “</td><td>” + dr1[1] + “</td><td>” + dr1[2] + “</td><td>” + dr1[3] + “</td><td>” + dr1[4] + ” ” + dr1[5] + “</td><td>” + dr1[6] + ” ” + dr1[7] + “</td><td>” + dr1[9] + “</td></tr>”;

}
}

foreach (DataRow dr1 in dtETagHolders.Rows)
{
if (Convert.ToString(dr1[1]).ToUpper() == assignedTo.ToUpper())
{
dr1[2] = html;
}
}

createTask1_TaskProperties1.AssignedTo = assignedTo;
createTask1_TaskProperties1.Description = “”;

countTask++;
}

private void codeActivity1_method(object sender, EventArgs e)
{
try
{
if (counter == 0) // only happen once
{
string currentFileName = workflowProperties.Item.Name;

SPListItemCollection listItems = workflowProperties.TaskList.Items;

foreach (SPListItem item in listItems) // check all items in the Task list
{
string relatedContent1 = Convert.ToString(item[“Related Content”]);
string status = item[“Status”].ToString();

if (relatedContent1 != null && relatedContent1 != “”)
{
relatedContent1 = relatedContent1.Substring(relatedContent1.LastIndexOf(“,”) + 2) + “.CSV”;

if (relatedContent1.ToUpper() == currentFileName.ToUpper() && status != “Completed”) // only if the tasks’ related item is current file
{
string theID = item[“ID”].ToString();
string assignedTo = item[“Assigned To”].ToString();
string taskTitle = Convert.ToString(item[“Title”]);
string relatedContent = item[“Related Content”].ToString();

string currentTaskURL = “http://sharepoint.xxxxx.com.au&#8221; + workflowProperties.TaskListUrl + “/EditForm.aspx?ID=” + theID;
int key = Convert.ToInt32(theID) * 3 + 4;
string completeTaskLink = “mailto:au.sharepoint@xxxxx.com.au?subject=MarkAsComplete**SPEmail**” + theID + “**” + key.ToString() + “**http://sharepoint.xxxxx.com.au/hr/**2fa6e7a8-23ab-46f3-96d9-d3bf24fd743e**” + taskTitle;
currentTaskURL = currentTaskURL.Replace(“/lits/”, “/lists/”);
foreach (DataRow dr1 in dtETagHolders.Rows) // check through all items in the “e-Tag Holders” DataTable
{
string name = Convert.ToString(dr1[0]);
string firstname = name .Substring(0, name .IndexOf(” “));

string loginName = Convert.ToString(dr1[1]);
string travelDetails = Convert.ToString(dr1[2]);

string userEmail = Convert.ToString(dr1[3]);
int userID = Convert.ToInt32(dr1[4]);
string emailSent = Convert.ToString(dr1[5]);

// If the email hasn’t been sent for this person AND the “assigned to” for the Task is the same as the userID
if (emailSent != “1” && userID.ToString() == assignedTo.Substring(0, assignedTo.IndexOf(“;”)))
{

string emailBody = “<style type=\”text/css\”>body, div, span { font-family:arial; font-size:10pt;} table.GridView { font-family:arial; margin:0; font-size: 8pt; width: 100%; text-align: left; border-left:1px solid #dddddd; border-top:1px solid #dddddd; } table.GridView tr th { border:0; border-right:1px solid #dddddd; border-bottom:1px solid #dddddd; background-color:#333333; color:white; padding:10px; } table.GridView tr td { font-size: 8pt; padding: 8px 6px; border:0; border-right:1px solid #dddddd; border-bottom:1px solid #dddddd; } .donotprint { display:none; } a, a:visited {     color:#0072bc;     text-decoration:none;} h2 { font-size:14pt; } .theGrid tr td { padding:8px; } div#tabs { position:relative; padding:0; } div#tabs span.number { display:none; } div#tabs a, div#tabs a:link, div#tabs a:visited { display:none; } div#tabs a.current, div#tabs a.currentHover { border:1px solid #cccccc; color:#333333; float:left; text-align:center; border-bottom:0;  display:block; padding:8px; color:#000000; background-color:#ffffff; text-decoration:none;border-radius:3px 10px 0px 0px; } div#tabs a.hidden {display:none;} div.panel { padding:15px; border:1px solid #cccccc; background-color:#ffffff;display:block; border-collapse:separate;border-radius:0 10px 10px 10px; box-shadow: 2px 2px 2px #cccccc;} div.detailsAreaHidden {display:none; } img { display:none; } table.tableForm tr th,table.tableForm tr td { text-align:left; font-size: 10pt; } table.tableForm tr.alt th, table.tableForm tr.alt td { background-color:#f6f6f6; } .clearall {clear:both;} .left { float:left; } .doNotEmail { display:none; } </style>”;
emailBody += ”

Hi ” + firstname + “,

A CityLink invoice with travel details has been uploaded to SharePoint. Please review the following CityLink travel details:

“;
emailBody += ”

“; emailBody += “”;string emailBody2 = ”

Name Licence Plate e-Tag Travel Details Start Date/Time Finish Date/Time Cost

If the travel details above are correct, please click here to open the task, change the status to \”Completed\”, and then click \”Save\”.

“;
emailBody2 += “Complete the task via Email:

“;
emailBody2 += “If you are not on the company’s network (i.e., you are not in the office or connected via VPN), you can complete this task via email.

“;
emailBody2 += “Click on one of the following links and it will open a new email window. Make sure you leave the email subject unchanged.

“;
emailBody2 += ”    > Approve via Email

“;
emailBody2 += “SharePoint will send you an email once it has received your email and completed the task for you (this may takes a few minutes).

“;
emailBody2 += “If you found any errors please notify the HR manager.

Kind regards,
SharePoint
Sent from Workflow developed in VS2010

“;

StringDictionary headers = new StringDictionary();
headers.Add(“to”, userEmail);
headers.Add(“cc”, “”);
headers.Add(“bcc”, “”);
headers.Add(“subject”, “Please review CityLink travel details ” + invoicePeriod);
headers.Add(“content-type”, “text/html”);
SPUtility.SendEmail(workflowProperties.Web, headers, emailBody + travelDetails + emailBody2);

//SPUtility.SendEmail(workflowProperties.Web, true, false, userEmail, “Please review CityLink travel details”, emailBody);
dr1[5] = “1”;

break; // found the person, stop the loop
}

}
}
}
}

counter++;
}
}
catch (Exception ex)
{
LogComment(ex.ToString(), “Error”);
}
}

private void codeActivity2_method(object sender, EventArgs e)
{
try
{
string to = “”;
foreach (SPGroup spg in workflowProperties.Web.SiteGroups)
{
if (spg.Name == “CityLink Invoice Notification”)
{
foreach (SPUser user in spg.Users)
{
to += user.Email + “;”;
}
break;
}
}

string emailBody = “<style type=\”text/css\”>body, div, span { font-family:arial; font-size:10pt;} table.GridView { font-family:arial; margin:0; font-size: 8pt; width: 100%; text-align: left; border-left:1px solid #dddddd; border-top:1px solid #dddddd; } table.GridView tr th { border:0; border-right:1px solid #dddddd; border-bottom:1px solid #dddddd; background-color:#333333; color:white; padding:10px; } table.GridView tr td { font-size: 8pt; padding: 8px 6px; border:0; border-right:1px solid #dddddd; border-bottom:1px solid #dddddd; } .donotprint { display:none; } a, a:visited {     color:#0072bc;     text-decoration:none;} h2 { font-size:14pt; } .theGrid tr td { padding:8px; } div#tabs { position:relative; padding:0; } div#tabs span.number { display:none; } div#tabs a, div#tabs a:link, div#tabs a:visited { display:none; } div#tabs a.current, div#tabs a.currentHover { border:1px solid #cccccc; color:#333333; float:left; text-align:center; border-bottom:0;  display:block; padding:8px; color:#000000; background-color:#ffffff; text-decoration:none;border-radius:3px 10px 0px 0px; } div#tabs a.hidden {display:none;} div.panel { padding:15px; border:1px solid #cccccc; background-color:#ffffff;display:block; border-collapse:separate;border-radius:0 10px 10px 10px; box-shadow: 2px 2px 2px #cccccc;} div.detailsAreaHidden {display:none; } img { display:none; } table.tableForm tr th,table.tableForm tr td { text-align:left; font-size: 10pt; } table.tableForm tr.alt th, table.tableForm tr.alt td { background-color:#f6f6f6; } .clearall {clear:both;} .left { float:left; } .doNotEmail { display:none; } </style>”;
emailBody += ”

Hello,

Just letting you know that this CityLink invoice/travel details ” + invoicePeriod + “ have been reviewed by all the e-Tag holders:

“;
//emailBody += “Click here to view workflow history

“;
emailBody += “Kind regards,
SharePoint
Sent from Workflow developed in VS2010

“;

StringDictionary headers = new StringDictionary();
headers.Add(“to”, to);
headers.Add(“cc”, “”);
headers.Add(“bcc”, “”);
headers.Add(“subject”, “CityLink travel details has been reviewed by all e-Tag holders”);
headers.Add(“content-type”, “text/html”);
SPUtility.SendEmail(workflowProperties.Web, headers, emailBody);
LogComment(“Workflow completed. Notification sent to ” + to, “Log”);
}
catch (Exception ex)
{
LogComment(ex.ToString(), “Error Message”);
}
}

private void while1Invoke(object sender, ConditionalEventArgs e)
{
e.Result = !isFinished;
}

private void task1Changed1Invoke(object sender, ExternalDataEventArgs e)
{

int tid = onTaskChanged1_AfterProperties1.TaskItemId;
SPListItem task = workflowProperties.TaskList.GetItemById(tid);

if (Convert.ToString(task[“Status”]) == “Completed”)
{
isFinished = true;
countCompletedTask++;
string assignedTo = Convert.ToString(task[“Assigned To”]);
assignedTo = assignedTo.Substring(assignedTo.IndexOf(“#”) + 1);
LogComment(assignedTo + ” has complete the task (Task ID: ” + tid + “). ” + countCompletedTask + ” of ” + assignees.Count + ” completed”, “Completed”);
}
}

private void completeTask1_MethodInvoking(object sender, EventArgs e)
{
try
{
}
catch (Exception ex)
{
LogComment(ex.ToString(), “”);
}

}

private void repCompleted(object sender, ConditionalEventArgs e)
{
e.Result = (countCompletedTask == assignees.Count);
}

public static DependencyProperty onTaskChanged1_AfterProperties1Property = DependencyProperty.Register(“onTaskChanged1_AfterProperties1”, typeof(Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties), typeof(Workflow_CityLink_Invoices.Workflow1.Workflow1));

[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute(“Misc”)]
public SPWorkflowTaskProperties onTaskChanged1_AfterProperties1
{
get
{
return ((Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties)(base.GetValue(Workflow_CityLink_Invoices.Workflow1.Workflow1.onTaskChanged1_AfterProperties1Property)));
}
set
{
base.SetValue(Workflow_CityLink_Invoices.Workflow1.Workflow1.onTaskChanged1_AfterProperties1Property, value);
}
}

public static DependencyProperty onTaskChanged1_BeforeProperties1Property = DependencyProperty.Register(“onTaskChanged1_BeforeProperties1”, typeof(Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties), typeof(Workflow_CityLink_Invoices.Workflow1.Workflow1));

[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute(“Misc”)]
public SPWorkflowTaskProperties onTaskChanged1_BeforeProperties1
{
get
{
return ((Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties)(base.GetValue(Workflow_CityLink_Invoices.Workflow1.Workflow1.onTaskChanged1_BeforeProperties1Property)));
}
set
{
base.SetValue(Workflow_CityLink_Invoices.Workflow1.Workflow1.onTaskChanged1_BeforeProperties1Property, value);
}
}

}
}

Deploy the workflow:

  1. Deploy the project on the production server, then go to your site collection’s root site;
  2. Go “Site Actions > Site Settings”;
  3. Go to “Site Collection Features” under “Site Collection Administration”, activate the feature for this workflow.
  4. Go to the library and then “Library > Workflow Settings”;
  5. Click “Add a workflow”, you can then select the workflow name, task list, and workflow history list.
Advertisements
This entry was posted in SharePoint 2010, Visual Studio 2010, Workflow. Bookmark the permalink.

One Response to Create SharePoint Sequential Workflow with multiple tasks in Visual Studio 2010

  1. Pingback: Create SharePoint Sequential Workflow with multiple tasks in Visual Studio 2010 | ИТ Блог

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s