Project Server: Resource Plans and the PSI

I was doing some investigations this week on the Resource Plans Web Service and want to share this as it probably gives more information than we have in the SDK currently – or at least pulls it together.  The example I will be working towards is adding a resource to a resource plan, along with an assignment within that resource plan.  My work was specifically with 2007, but as far as the dataset manipulation is concerned this applies equally to Project Server 2010.

But first a quick look round the resource plan dataset and explaining how this maps to the UI.  The SDK article for 2007 is here and for 2010 is here – but to be honest you will be hard pressed to find much difference.

The dataset has four data tables, Utilization, Dates, PlanAssignmentCustomFields and PlanResources.

Utilization – This table shows the utilization type and date – which relate to the UI in this screenshots – 2007 and 2010. The types are as listed 0, 1 and 2 – with the date only applicable for type 2 (plan until:

image           image

From the SDK: The ResourcePlan.UtilizationType type is used in the RESPLAN_UTILIZATION_TYPE column of the ResourcePlanDataSet Utilization table. Values and descriptions for the ResourcePlan.UtilizationType type are provided solely for interpreting the information in this table.

This is not intended to be used directly from your code.

Dates – This table shows the start and end dates of the intervals defined for the Resource Plan.  The overall range will be set by the start and end dates entered when reading the dataset, and it will be broken into intervals according to the TimeScale specified.  The TimeScaleClass enumeration can be used when reading the dataset.  3 = Days, 4 = weeks, 5 = months and 7 = years – or (short)PSLibrary.TimeScaleClass.TimeScale.Months. 

PlanAssignmentCustomFields – You can have custom fields set for the resource’s assignments in a resource plan!  Who knew?  I’m guessing not many people have seen the following screen.  This is from 2007, but almost identical to the 2010 equivalent.  This is also the place you can change the resource from Committed to Proposed – or vice versa.

image

I’ve not played around with these via the PSI – but this is the table you would work with, and as is usual with Custom Fields, you need to add a row then set the properties.

PlanResource – This table is the one that I will be working with in my sample code where we will be adding a resource.  When you read a Resource Plan you will see it contains a ton of information on the resource, most of which you do not need to enter when adding one, along with a flag for Committed or Proposed (ASSN_BOOKING_TYPE) and a set of fields Interval0 to Interval’n’ which relates to the ‘n’ intervals you will see in the dates table.  When adding a resource you can also add an assignment by putting some value in one or more of these intervals.  The parameters for getting the resource plan dataset using ReadResourcePlan are:

filter – XML filter to limit data returned. For more information, see How to: Use a Filter Parameter with PSI Methods. I am not using filters, and as you can specify the project GUID there probably aren’t many circumstances when you’d need to filter down – unless you have a really large number of resources assigned.
projectUid – Project GUID.
startDate – Start date. This is the start date of the data you want to pull down from the server and manipulate – so it is potentially just a window on the entire resource plan, and need not relate to the date settings within the UI
endDate – End date.
timeScale – Time scale specified by the TimeScaleClass.TimeScale enumeration. Explained above – controls the time intervals
timephasedFTE – If true, return the full time equivalent resources.  This will govern if you enter FTEs, or, if false, then time (in 1/10th of a minutes) for the work values.
autoCheckOut – If true, check out the resource plan for editing.  This will depend exactly what you are doing.  You might want to check out just before the update, and you would also want to check that it wasn’t already checked out too.

A similar set of parameters also apply to the QueueUpdateResourcePlan method:

projectUid – Project GUID.
rpds – ResourcePlanDataSet that specifies changes in the specified resource plan.  Basically a manipulated version of the one you read in.  Don’t AcceptChanges!
timephasedFTE – If true, update with timephased full time equivalent resources. Same meaning as above.
autoCheckIn – If true, check in the resource plan after updating.
jobUid – Queue job GUID.

Also I am assuming there is already a Resource Plan for the project – if not my code would fail with a System.Web.Services.Protocols.SoapException: ProjectServerError(s) LastError=GeneralItemDoesNotExist and you would need to use the UI or QueueCreateResourcePlan method.  And finally you cannot publish the resource plan unless the project plan is published – or you will get another error – System.Web.Services.Protocols.SoapException: ProjectServerError(s) LastError=ResourcePlanProjectPublishIncomplete.  It would be unusual to get this condition as in most cases you create resource plans on published projects, but could happen with Activitity plans and proposals and server-side or other programmatically created projects.

Rather than repeat things I have added comments in the code where I think some explanation is necessary.  This snippet assumes you have a reference to PSLibrary = Microsoft.Office.Project.Server.Library; and a web reference to the Resource Plan web service.  I’ve hardcoded quite a bit of stuff just to show what is going on.

 

Guid projUid = new Guid("357afdea-0b68-46bc-8358-7557c8f90a8f");            
Guid resUid = new Guid("f249df7c-eca9-41c7-bd52-48def0ab50bc");                        
ResourcePlan resourcePlan = new ResourcePlan();           
resourcePlan.Credentials = CredentialCache.DefaultCredentials;           
resourcePlan.Url = "http://brismithupgrade/2k7/_vti_bin/PSI/resourceplan.asmx";
ResourcePlanDataSet dsResourcePlan = new ResourcePlanDataSet();
             
// Read in a resource plan dataset for our chosen project 
           
dsResourcePlan = resourcePlan.ReadResourcePlan(null, projUid               
 ,DateTime.Parse("01/01/2010"), DateTime.Parse("12/31/2010")
 ,(short)PSLibrary.TimeScaleClass.TimeScale.Months , false, false); 
                       
// Create a new Plan Resource Row and set some properties


ResourcePlanDataSet.PlanResourcesRow resourcePlanRow 
= dsResourcePlan.PlanResources.NewPlanResourcesRow();            
resourcePlanRow.PROJ_UID = projUid;            
resourcePlanRow.RES_UID = resUid; 
           
// Assignment UID does not need to be specified - it will be auto generated             
// The value used against intervalx will depend on the setting for the timePhasedFTE value            
// If set to true, then enter values like 1 for one Full Time Equivalent            
// If set to false the Intervalx values are entered as 1/10th Minutes - 600 = 1h, 4800 = 1d            
// This entry adds 4800 = 1d = 8h, to the 7th interval - August with the settings I am using
  
resourcePlanRow["Interval7"] = 4800;
            
// And this one adds 5 days to September
           
resourcePlanRow["Interval8"] = 48000;
            
// 0 = Committed and 1 = Proposed or use the enumeration
            
resourcePlanRow.ASSN_BOOKING_TYPE = (byte)PSLibrary.Resource.BookingType.Committed ;
             
// Add the row the the resource plan 
           
dsResourcePlan.PlanResources.AddPlanResourcesRow(resourcePlanRow);
                        
// Use the same jobUid for the update and checkin.
            
Guid jobUid = Guid.NewGuid();
                        
resourcePlan.CheckOutResourcePlans(new Guid[1] { projUid });            
System.Threading.Thread.Sleep(1000);                      
resourcePlan.QueueUpdateResourcePlan(projUid, dsResourcePlan, false, false, jobUid);  
                      
//  Need to be sure the project plan is published or this will fail

resourcePlan.QueuePublishResourcePlan(projUid, Guid.NewGuid());            
resourcePlan.QueueCheckInResourcePlans(new Guid[1] { projUid }, false, new Guid[1] { jobUid });
 

I hope this help you understand how to work with Resource Plans in a programmatic way.