View Khurram Jamshed's profile on linkedin

Thursday, January 27, 2011

Project Server - How to monitor Project milestone change

The following post title strike to me on TechNet forums few days back:

How to monitor and control the change in Project milestone tasks?

And here I decided to write a blog about the solution i chose some time back last year when I was involved in an EPM implementation project for one of the leading Bank in Middle East.

One of the not-OOB requirements of Project Management Office is about the monitor and control they look for over the projects based on the project milestones. The first quick thing you can propose is the Dashboard reports, where you can develop indicators based on the PMO criteria and they can reflect a bird eye view of the Project status at the milestone level. But think about the scenario where you have more than 50 big project schedules and each with over dozen of milestones, the dashboard solution would not really be welcomed if the program officer has to scroll down to all the milestones of each project to see which of the indicator is turned red due to the change in milestone cost/duration/ etc.

The solution i developed and accepted by the customer with smile was:  
  • Generate an email notification to the PMO,
  • if any project milestone task estimation varies with the baseline estimation.
  • This will intimate the PMO rite away about the change with Project name, Project Manager Name, and the exact changed milestone task within the project.
This could further compliment with the SSRS dashboard report, where then program officer can go and browse that particular project for details instead of scrolling to each and every project. Also the PMO don't have to view the reports every now and then, instead they can only access when they receive any change notification - this can also be called more productive at the PMO end.

The way I managed to do that is as follows:
  • Project manager changes the project schedule and publish the project 
  • Trap the OnPublished event 
  • Check for the change at the milestone level. For instance compare the Project milestone cost value with the Project milestone baseline Cost value.
  • If found any variation, create and send an email to the PMO or etc.
The solution however based on the baseline of the project, and also the permission to save the baseline should not be allowed to the Project manager. Because the variance can only be determined after the comparison of current and the baseline values, customizing the event also requires coding.

You need to set you IDE environment to access the Project web service methods. There is an excellent article available on MSDN about How to Create and Debug the project custom event handler, so i will not go in that detail. We will override the OnPublished event, the reason i prefer to use OnPublished is because its a post publish event triggers after completing the required publishing activities and wont effect the publishing in-case of any abnormal termination due to the exception in the custom code.




The next step you have to do is to define a Project Object, call its method ReadProject using project GUID and save the project data in a dataset.


Once the project data is saved in the dataset, you can now evaluate each milestone in the project to check for any variance in the cost values:

foreach (NotificationEventHandler.Project.ProjectDataSet.TaskRow dr in drs)
iTaskCostVar = (dr.IsTASK_COST_VARNull() ? 0 : Convert.ToInt32((dr.TASK_COST_VAR)));
.
.
.
}

If finds any difference, save the task name with all the other details required and draft an email based on that data. For instance:

_______________________________________________________________________________

sEmailStart.Append("<html><body>"); 
sEmailStart.Append("Dear Finance,<br> CC: PMO<br><br>");
sEmailStart.Append(string.Format("Project Name: {0} has been modified. Please review the Project Milestones as below:<br><br>", ds.Project[0].PROJ_NAME));
sEmailStart.Append("<table border=2 cellpadding=2 cellspacing=2 width=100%><tr><td>Name</td><td>Baseline Start</td><td>Actual Start</td><td>Baseline Finish</td><td>Actual Finish</td><td>Baseline Cost</td><td>Cost</td><td>%Complete</td></tr>");
sEmailStart.Append(string.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td><td>{6}</td><td>{7}</td></tr>", dr.TASK_NAME, sBaselineStart, sActualStart, sBaselineFinish, sBaselineFinish, sBaselineCost ,sCost, sPctComplete));
sEmailStart.Append("<br>Please review them by following the link: ");
sEmailStart.Append(configurations["ProjectServerURL"].ToString() + "/project.aspx" + "<br><br><br>");

 sEmailStart.Append("Regards, <br> EPM Team  <br><br><br> Note: This is an auto-generated email, please don't respond");
sEmailStart.Append("</body></html>");
_________________________________________________________________________________
Once done with the development , add the library in the windows assembly and register the event handler with the appropriate Project server event. Follow the MSDN article i have mentioned above for debugging and deployment, all the steps are explained in detail in that article.


Add in windows assembly


Register Project Server event handler


And based on your requirement you can send this email through the code either to the pre-defined users (you can use web.config for this) or you can read any particular user's email on the fly from DB based on some defined criteria. Now if the project manager changes the milestone cost, our code will execute when he will publish the project after the change. And like the below project schedule, the change in milestone could be detected and the email will be generated to the PMO.



Your email might look like this in the end:



This email contains all the changed milestones with the exact change details, project center link to access the project other details and the effect this change propagates to the whole project schedule. Now imagine being a part of the PMO, you receive a notification on your outlook about the change in the project milestone by one of your Project Manager, you can quickly log on to your PWA, verify the change and its impact and can take the necessary steps. Doesn't it sound simple and quick? trust me it indeed is the quickest way for PMO to monitor the change in the project :) 

As always any comments and suggestion are more than welcome.

Updated (26th Jan): The solution will work for Project Server 2007 as well as the PS 2010. Forgot to mention this earlier, adding it now after the valuable feedback of none other than Christophe Fiessinger.

Below is the complete code snippet of the solution for reference, but do note that the code attached has been written for the testing, and specific to the requirement, purposes only and attached as is. The code should be improved and revised, as per the requirements, prior to the deployment on the production server.

 ______________________________________________________________________________
public override void OnPublished(Microsoft.Office.Project.Server.Library.PSContextInfo contextInfo, Microsoft.Office.Project.Server.Events.ProjectPostPublishEventArgs e)
{

base.OnPublished(contextInfo, e);

try
{

string Task_Cost_Var = string.Empty;
string sEmailFrom = string.Empty;
string sToFinance = string.Empty;
string sToPMO = string.Empty;
string sEmailSubject = string.Empty;
string sSMTPServer = string.Empty;
string sBaselineStart = string.Empty;
string sActualStart = string.Empty;
string sBaselineFinish = string.Empty; 
string sActualFinish = string.Empty;
string sBaselineCost = string.Empty;
string sCost = string.Empty; 
string sPctComplete = string.Empty;

int iTaskCostVar;


//string sConnString = string.Empty;


StringBuilder sEmailStart = new StringBuilder();
int iSMTPPort = 0;
int iMilestoneCount = 0;

XmlTextReader reader = new XmlTextReader(@"C:\EPMConfigurations\Config.xml");
Hashtable configurations = new Hashtable();

while (reader.Read())
{

string lastKey = String.Empty;

if (reader.NodeType == XmlNodeType.Element)
{

if (reader.Name.ToLower() == "add")

{

configurations.Add(reader[0], reader[1]);

}

}

}


reader.Close();

NotificationEventHandler.Project.Project p = new NotificationEventHandler.Project.Project();

p.Url = configurations["ProjectServerURL"].ToString() + configurations["PROJECT_WEBSERVICE"].ToString();


p.Credentials = CredentialCache.DefaultCredentials;

NotificationEventHandler.Project.ProjectDataSet ds = p.ReadProject(e.ProjectGuid, NotificationEventHandler.Project.DataStoreEnum.WorkingStore);

//ds.b


NotificationEventHandler.Project.ProjectDataSet.TaskRow[] drs = (NotificationEventHandler.Project.ProjectDataSet.TaskRow[])ds.Task.Select(ds.Task.TASK_IS_MILESTONEColumn.ColumnName + " = 1");


//sEmailStart = ds.Project[0].PROJ_NAME + Environment.NewLine + Environment.NewLine + "Below are the latest Milestone Tasks Cost changes occur:" + Environment.NewLine;

sEmailStart.Append("<html><body>");

sEmailStart.Append("Dear Finance,<br> CC: PMO<br><br>");

sEmailStart.Append(string.Format("Project Name: {0} has been modified. Please review the Project Milestones as below:<br><br>", ds.Project[0].PROJ_NAME));

sEmailStart.Append("<table border=2 cellpadding=2 cellspacing=2 width=100%><tr><td>Name</td><td>Baseline Start</td><td>Actual Start</td><td>Baseline Finish</td><td>Actual Finish</td><td>Baseline Cost</td><td>Cost</td><td>%Complete</td></tr>");

//check if there are any milestones in the project,if not simply return
if (drs.Length == 0)
return;
foreach (NotificationEventHandler.Project.ProjectDataSet.TaskRow dr in drs)
{

//check if milestone cost changed, if not skip for this and check the other row

iTaskCostVar = (dr.IsTASK_COST_VARNull() ? 0 : Convert.ToInt32((dr.TASK_COST_VAR)));
if (iTaskCostVar == 0)
continue;

sBaselineStart = (dr.IsTB_STARTNull() ? "0" : (dr.TB_START).ToString());
sActualStart = (dr.IsTASK_ACT_STARTNull() ? "0" : (dr.TASK_ACT_START).ToString());
sBaselineFinish = (dr.IsTB_FINISHNull() ? "0" : (dr.TB_FINISH).ToString());
sActualFinish = (dr.IsTASK_ACT_FINISHNull() ? "0" : (dr.TASK_ACT_FINISH).ToString());
sBaselineCost = (dr.IsTB_COSTNull() ? "0" : (dr.TB_COST/100).ToString());
sCost = (dr.IsTASK_COSTNull() ? "0" : (dr.TASK_COST).ToString());
sPctComplete = (dr.IsTASK_PCT_COMPNull() ? "0" : (dr.TASK_PCT_COMP).ToString());

sEmailStart.Append(string.Format("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td><td>{6}</td><td>{7}</td></tr>", dr.TASK_NAME, sBaselineStart, sActualStart, sBaselineFinish, sBaselineFinish, sBaselineCost ,sCost, sPctComplete));

iMilestoneCount = iMilestoneCount + 1;
}
sEmailStart.Append("</table>");

//check if there are any milestones appended in the string because of cost variance,if not just return


if (iMilestoneCount == 0)
return;

//sEmailStart = sEmailStart + Task_Cost_Var; 
sEmailStart.Append("<br>Please review them by following the link: ");
sEmailStart.Append(configurations["ProjectServerURL"].ToString() + "/project.aspx" + "<br><br><br>");
sEmailStart.Append("Regards, <br> EPM Team <br><br><br> Note: This is an auto-generated email, please don't respond");
sEmailStart.Append("</body></html>");
sEmailFrom = configurations["EmailFrom"].ToString();
sToFinance = configurations["ToFinance"].ToString();
sToPMO = configurations["ToPMO"].ToString();
sEmailSubject = configurations["EmailSubject"].ToString();
sSMTPServer = configurations["SMTPServer"].ToString();
iSMTPPort = Convert.ToInt32(configurations["SMTPPort"].ToString());
MailMessage MilestoneEmail = new MailMessage(); //(sEmailFrom, sEmailTo,);
MailAddress MailFrom = new MailAddress(sEmailFrom);
MilestoneEmail.From = MailFrom;
MilestoneEmail.To.Add(sToFinance);
MilestoneEmail.CC.Add(sToPMO);


AlternateView htmlView = AlternateView.CreateAlternateViewFromString(sEmailStart.ToString(), null, "text/html");
MilestoneEmail.AlternateViews.Add(htmlView);
MilestoneEmail.IsBodyHtml = true;
MilestoneEmail.Subject = sEmailSubject;
//MilestoneEmail.Body = sEmailStart;
SmtpClient smtpClient = new SmtpClient(sSMTPServer, iSMTPPort);
smtpClient.Send(MilestoneEmail);

}


catch (Exception err)
{

string errorMessage = "Code crashed OnPublished Method" + err.Message + " " + err.Source + err.StackTrace;
el = new ErrorLog();
el.CreateFile();
el.WriteToFile(errorMessage);

}
______________________________________________________________________________


Tuesday, January 18, 2011

Project Server 2010 SDK updated

Folks,

Great work by Jim Corbin on Janudary 2011 update of the the Project 2010 SDK, its available online on MSDN. The new update is available online only, the download will be available within 2 months.

The SDK contains documentation, code samples, how-to articles, and programming references to help customize and integrate the Project 2010 clients and Microsoft Project Server 2010 with a wide variety of other desktop and business applications for enterprise project management.

The new udpate contains the following topics:
It also contains the new and updated description of the Project Server API references, and new code samples all based on WCF.

For more details, browse the Jim's blog and for the complete reference check the SDK on MSDN online. Also i am quite excited about the beta release of the feature pack for integration between Project Server 2010 and TSF 2010. I would definetly share my experience once i will try the integration, the release was indeed worth waiting.

Monday, January 10, 2011

Nintex Workflows for Project Server 2010

Without any doubt the one of the most exciting feature of Project Server 2010, at least to me,  is the Demand Management Process. The Demand Management let you automate your Project life cycle with great control and visibility, and let you capture your project rite from its inception stage till the closure stage. In Project Server, server settings the new section has been added 'workflow and project detail pages' that consist of EPT, Phases, Stages and Project Detail Pages. Using all of these options together can give you a great leverage of creating a Project workflow.And the more great deal about this is that you can automate more than one Project life cycle process, to fulfill the demand of different processes within one organization, by creating different workflows and associating them with the different EPT. And further filter them on the basis of Departments (another awesome feature of the Project Server 2010, will discuss in my future post) to be available only for the target audience.

Now this is not the only reason i am writing this post, because there is already a lot of excellent material available related to Demand Management capabilities. The feature has also led this misconception among the users that they are going to get the workflows also as an OOB features of Project Server 2010. And that they will be able to use it straight away by simply configuring the workflow section available within the server settings of the Projec Server. Or the workflow is something which they can download from the internet and use it, thanks to the dynamic workflow available in the solution starter kit :). The conclusion is based on my personal experience and often i have to work really hard in clearing this wrong concept from the customers head.

And this, developing a workflow, requires effort and expertise. As they say that not all the ease can come without small amount of pain :). The fact of the matter is that  Project Server 2010 workflow cannot be built using Sharepoint designer and thus leaves the customer with only two options:
  1. Leverage the Dynamic Workflow Solution Starter, a Microsoft released tool that enables simple approval type linear workflows to be built from within a web page without any code; or
  2. Get a .net developer to use Visual Studio 2010 to design, develop and test a  workflow that meets all the organizational requirements.
However, now there is a third option available i.e Nintex Workflows for Project Server 2010, an extension of the popular Nintex Workflow 2010. The new NWFPS 2010 brings a simplified drag and drop interface for building workflows and integration into the Project Server workflow components to users via the web browser. The tool is looking promising. and the deployment and its integration with the Project Server is seamless.




Unfortunately i was not getting much time to explore its capabilities, but look for my next blog where i will write in more detail about the usability and capability to create a workflow using NWFPS 2010.

Saturday, January 8, 2011

Project Server 2010 - Team Member Group Permissions Bug

In my blog, i would like to highlight one of the issue been fixed after applying the December CU of Project Server 2010. Few weeks back i come across this issue while i was trying accessing Projects as Team Member group user. Initially i thought it was me who forgot to check an appropriate category permission but after confirming few times all the settings, my doubt became concrete when i browse through a techNet site - where more than a few users have registered a similar incident as an Issue.

To make my self more meaningful, below is the scenario:

Scenario:
  • Resource is a member of Team Member group.
  • Resource is only allowed to view the Project plan
  • Resource is not allowed to edit any Project custom field information
  • Resource is not allowed to edit Project Schedule
Following is the screen shot of the Team member group permission following the above scenario:


Result:

When user of the above mentioned group try to access the project detail from the Project Center, the user will end up with the following error.
When access project, Error.
First Solution:
For the sake of work around, if you only allow user to Edit Project Custom fields (which ideally shouldn't be allowed), it will work.




The user is now able to edit the Project summary fields, but not able to change the Project schedule.

Second Solution:
If you deny the Edit Project Summary fields and allow user to Save project to project server and publish project (ideally this shouldn't be allowed to team members) it will work: The user will not be able to edit the Project custom fields, but enable to update the Project Schedule :)


The fact of the matter is, both the above solutions are not acceptable for customers. It does not make sense that the user of the Team Member group can update Project custom field or Project Schedule or both. I have also found a comment of one of the senior moderator at techNet, and he wasnt agreed of calling it an issue and rather explained it as design behavior of new Project Server 2010. But on a contrary, i stick to my opinion of calling it a Issue, as to me no design is good if its unable to fulfill customers basic requirement.

Although the Microsoft has released a hot fix ( http://support.microsoft.com/kb/2459112/) to fix the above issue. The more good news is that MS has also released December CU for Project Server 2010 (http://support.microsoft.com/kb/2459258/) which contains this fix along with the other fixes. Applying this CU will enable Team member users to access the Project detail only by allowing them to Open Project group permission.


Friday, January 7, 2011

Project Server 2010 - December CU released

This is to echo Brian Smith announcement of the release of the latest Project Server 2010 Cumilative Update.  Cumulative update packages for Microsoft Project Server 2010 contain hotfixes for the Project Server 2010 issues that were fixed since the release of Project Server 2010.

It fixes few of the really required issues, such as team member permission to view projects and setting the project owner of the project. The more detail of the CU can be viewed on the following link:

In order to find out the current version of CU or Hofix of your Project Server 2010, follow the Brian Smith article for details: http://blogs.msdn.com/b/brismith/archive/2010/09/23/how-to-tell-which-cumulative-update-hotfix-or-service-pack-version-of-project-server-2010-and-project-2010-you-are-running.aspx.

Look for my upcoming blog where i am going to write in detail about 2 of the issues fixed after applying this CU. Please also note that, do not apply this CU in your production environment without testing.

Share