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?