Developing Project Essential Applications – Working with Cost Center Values

Events, Project Essentials, SDK

The current article is a follow up to the previous one, detailing how to read and update budget values for a project. We will show how to work with cost center values.

Cost Centers are elements which help enterprises track and control cost more efficiently, generally a segment of a business or other organization in which costs can be segregated. Each department for example could be a cost center. You globally define cost centers, you can group them in various combinations and similar to cost nodes in cost templates, you can select just a subset of cost centers and associate them to Enterprise Project Types and Projects.

From an API point-of-view to determine which Cost Center Template is assigned to a project, you must refer the financial project instance (the data row in the CostInfo.cmInstances table corresponding to your project). You can only have a Cost Center Template associated to a project if that project has a Cost Tree Template association.

The instance data row, has a property called CostCentersTemplateGUID, that indicates the template; for instances that have no template assigned, the IsCostCentersTemplateGUID() method will evaluate to true.

To find out all the information about a template, you use the ReadCostCentersTemplate(Guid templateUid, bool includeStructure) method of the ProjectCost service:

  • templateUid – the identifier of the cost tree template
  • includeStructure – if set to true, the returned dataset will also include details about the cost centers in the CostInfo.cmCostCenters data table (the name and the descriptions) and the cost center groups in the CostInfo.cmCostCenterGroups data table, otherwise only the list of cost center identifiers is available in the CostInfo.cmCostCentersTemplate data table

Project Essentials stores financial values for each granular time period, per cost node and cost center. To ReadFinancialValues method from the FinancialValues service, has a parameter called costCenters (a Guid array):

  • if you specify null, you will get all the values, for all the cost centers
  • if you pass an array with Guid.Empty as the only element, you will get the values aggregated for each cost node (the cost center breakdown is ignored, all the cost center values for a node are summed up to get the total node value)
  • if you specify unique identifiers that correspond to just a subset of the cost centers included in the template, the returned dataset will contain the filtered values for only those centers

The costCenters parameter, allows you to mimic with the API the behavior that you get when you use the Cost Centers filter in the financial web parts.


In the financial web parts, for each cost node, besides the cost centers from the template that you select in the filter, you get two additional rows:

  • Total – this is the sum of all cost centers values; it is read-only and you cannot edit the value
  • Unallocated – this is computed as the difference between the Total value and the sum of all cost center values


In the dataset returned by the ReadFinancialValues method, there is no row that corresponds to the Total node value. You will have to roll out your own logic to compute the value, but it is fairly easy:

decimal total = valuesDs.cmFinancialValues
               .Where(financialValue => financialValue.NodeGUID == nodeUid)
               .Sum(financialValue => financialValue.Value);


In the default usage scenario, the business logic of Project Essentials does not allow you to add values to the Unallocated cost center, this node is read-only in the financial web parts. However, if you add values to the project before associating a cost center template, when you do assign a template, the existing values will end up in the Unallocated center. When you add a value on a cost center for a cost node, the value is subtracted from the Unallocated value, and will not increase the Total value if it is smaller than the unallocated amount; if it is greater, the Unallocated value will be zeroed out.

To exemplify this, let us consider the situation of the Expenses node, having an Unallocated value of 10,000$, and two cost centers Fin1 and HR2:


Initial state

Cost Center Sum > Unallocated

Cost Center Sum <= Unallocated

















In the response dataset, when you read the values through the API, the rows corresponding to the Unallocated virtual cost center are identified by having Guid.Empty (00000000-0000-0000-0000-000000000000) in the CostCenterGUID column. It is up to you to manage these rows and update their values appropriately when modifying rows for the other cost center value, if you wish to get the same results you would by manually editing values in the web parts.

The sample application provided with the previous article has been updated to also include functionality for cost centers.

Sample files (2012_06_11)

What do you think?