Project Financial Server Events

Code Samples, Project Financial Server, SDK

If you already had a look at the SDK documentation, you know that Project Financial Server exposes a few server-side events. These events can be used to enforce custom rules for data saved or to launch different notifications when data is updated.

The events are:

Event Name Event Type PFSEventID value Description
CostValuesChanging Pre-event PFSEventID.CostValuesChanging (0) The event fires just before values are committed to the database
CostValuesChanged Post-event PFSEventID.CostValuesChanged (1) The event fires to notify that values were committed to the database
BenefitsValuesChanging Pre-event PFSEventID.BenefitsValuesChanging (2) The event fires just before values are committed to the database
BenefitsValuesChanged Post-event PFSEventID.BenefitsValuesChanged (3) The event fires to notify that values were committed to the database
CostStructureChanging Pre-event PFSEventID.CostStructureChanging (4) The event fires just before the new global cost structure is committed to the database
CostStructureChanged Post-event PFSEventID.CostStructureChanged (5) The event fires to notify that a new global cost structure was committed to the database
BenefitsStructureChanging Pre-event PFSEventID.BenefitsStructureChanging (6) The event fires just before the new global benefits structure is committed to the database
BenefitsStructureChanged Post-event PFSEventID.BenefitsStructureChanged (7) The event fires to notify that a new global benefits structure was committed to the database
StageValidating Pre-event PFSEventID.StageValidating (8) The event fires when workflow stage validation is called
StageTransition Post-event PFSEventID.StageTransition (9) The event fires when workflow transitioned to a new stage

 

In order to use these events, first you have to create an event receiver. The event receiver is derived from a special class found in the UMT.CostModule.Events assembly, as follows:

– FinancialValuesEventReceiver: This class provides 4 overridable methods, for CostValuesChanging, CostValuesChanged, BenefitsValuesChanging and BenefitsValuesChanged.
– StructureEventReceiver: Provides 4 overridable methods for CostStructureChanging, CostStructureChanged, BenefitsStructureChanging, BenefitsStructureChanged.
– WorkflowEventReceiver: Provides 2 overridable methods for StageValidating and StageTransition

The assembly containing the event receiver must be deployed to the GAC and then it has to be associated with an event in Project Financial Server.

We do not expose an UI for registering events at this point, however, this can be achieved by writing a simple application to use the PFSI Events service. The PFSI Events service exposes the following methods:

– ReadAllSubscriptions() : returns an EventsInfo DataSet object containing the list of subscriptions already active in the system;
– ReadEventSubscriptions(PFSEventID eventID): returns an EventsInfo  DataSet containing the list of subscriptions for a particular event
– UpdateSubscriptions(EventsInfo events): updates the list of subscriptions in the database.

The sample code below shows how to create an event handler for the CostValuesChanging event. This event handler will validate that all values being saved do not exceed a certain threshold. If validation fails, the update is cancelled and a message is displayed to the user. 

public class PFSValuesEventHandler : FinancialValuesEventsReceiver
   {

       // sample inferior limit to test values against
       private static readonly decimal maxValue = 1000000;

       /// <summary>
       /// Called before cost values are updated. If any value in the dataset is smaller than 
       /// minValue, the event will be canceled and values will not be updated.
       /// </summary>
       /// <param name="context"></param>
       /// <param name="e"></param>
       public override void OnCostValuesChanging(PSContextInfo context, ValuesPreEventArgs e)
       {
           try
           {
               // check if any value is below limit
               bool anyValuesAboveThreshold = e.Values.cmFinancialValues.Any(row => 
                 row.RowState != DataRowState.Deleted && row.IsCost && row.Value > maxValue);

               // if any value failed the test, cancel the event, specifying the reason
               if (anyValuesAboveThreshold)
               {
                   e.CancelReason = "Not all values are below " + 
                                        maxValue.ToString(context.LocaleCulture);
                   e.Cancel = true;
               }
           }
           catch
           {
               //TODO: handle exception
           }
           
       }
   }
Then we have to register the event handler. For this, we can use the following piece of code:
     private void UnregisterEvents()
     {
         EventsInfo ei = eventsService.ReadAllSubscriptions();

         //clean our test subscriptions
         foreach (EventsInfo.cmEventSubscriptionsRow row in ei.cmEventSubscriptions)
         {
             if (row.SubscriptionName == sampleEventSubscription)
             {
                 row.Delete();
             }
         }
         eventsService.UpdateSubscriptions(ei);
     }

     private void RegisterEvents()
     {
         //clean up first
         UnregisterEvents();
        
         EventsInfo ei = eventsService.ReadAllSubscriptions();

         //add the subscriptions for the two events we're interested  in
         ei.cmEventSubscriptions.AddcmEventSubscriptionsRow(
             Guid.NewGuid(),
             PFSEventID.CostValuesChanging.ToString(),
             sampleEventSubscription, 0, 
             GetEventsAssemblyFullName(), 
             "PFSEventHandlers.PFSValuesEventHandler");
         ei.cmEventSubscriptions.AddcmEventSubscriptionsRow(
             Guid.NewGuid(),
             PFSEventID.CostValuesChanged.ToString(),
             sampleEventSubscription, 0, 
             GetEventsAssemblyFullName(), 
             "PFSEventHandlers.PFSValuesEventHandler");

         //update subscriptions
         eventsService.UpdateSubscriptions(ei);
     }

The full sample code is in the Project Financial Server SDK. We welcome your comments and questions.

What do you think?