Friday, June 12, 2009

Summarizing an Appointment field on a Parent Entity


Imagine you have a custom field called number of participants on and appointment entity and you want to summarize this field for all the appointments under the same regarding parent.

This looks simple at first glance i.e. register a plug-in on the parent post Create and Update messages and you’re done. However, since this entity is part of dynamics scheduling engine and affects rescheduling you also need to register your plug-in on the Book and Reschedule messages.

Actually the Book and Reschedule messages somewhat replace the Create and Update messages. That means that when you create a new Appointment the Book message is fired and when you Update an existing appointment the reschedule message is fired.

So why do we need to also register the plug-in on the Create and Update messages?
The answer is that when you reschedule or book an appointment and the user is not available at that point in time the scheduling engine halts the execution and enables the user to cancel the operation. If the user decide to disregard (ignore) that warning and save anyway then the Create or Update are fired to complete the operation.

Since the summarizing of the field can be a long operation you should also register the plug-in for asynchronous execution.

Another thing that is worth mentioning is code synchronization. Since CRM does not lock record and two users can update the same appointment at the same time it is also advisable to lock the process that updates the parent field to avoid data corruption.

Last Important remark: the regarding lookup (parent entity) is required for this operation. Since the Book and Reschedule messages don’t allow you to register Post Images you need to send the regarding field in each operation. E.g.

//Put this code in the appointment onload event handler

if (crmForm.regardingobjectid.DataValue != null)
{
crmForm.regardingobjectid.ForceSumbit = true;
}


Plug-in Registered on the Reschedule, Book, Create and Update Asynchronous Parent Pipeline


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;

namespace GI.Crm.Sandbox
{

public class SummarizeAppointmentFieldHandler : IPlugin
{
private Object SyncObject = new Object();

public void Execute(IPluginExecutionContext context)
{
/* Validates that the user is available */
if (context.OutputParameters.Contains(ParameterName.ValidationResult))
{
ValidationResult validationResult =
context.OutputParameters[ParameterName.ValidationResult] as ValidationResult;
if (validationResult.ValidationSuccess == false)
{
return;
}
}

/* Validate Target Entity */
if (!context.InputParameters.Contains(ParameterName.Target))
{
return;
}

DynamicEntity Target = context.InputParameters[ParameterName.Target] as DynamicEntity;

/*
We need both regarding (parent) entity id and number of participants field
Client side must Force Submit on regarding field
*/
if ( !Target.Properties.Contains("regardingobjectid") ||
!Target.Properties.Contains("gi_noofpart"))
{
return;
}

Lookup regardingObjectId = Target.Properties["regardingobjectid"] as Lookup;
/* Validate that the Appointment is Regarding the Correct parent entity */
if (regardingObjectId.type != "gi_custom")
{
return;
}

CrmNumber noofPart = Target.Properties["gi_noofpart"] as CrmNumber;

/* Validate the number of Participants */
if (noofPart.Value == 0)
{
return;
}

/* Synchronize access to this code block */
lock(SyncObject)
{
#region Retrieve all Regarding entity Appointments
QueryExpression allPartQuery = new QueryExpression();
allPartQuery.ColumnSet = new ColumnSet();
allPartQuery.ColumnSet.AddColumn("gi_noofpart");
allPartQuery.Criteria.AddCondition(
"regardingobjectid" , ConditionOperator.Equal , regardingObjectId.Value
);
allPartQuery.Distinct = false;
allPartQuery.EntityName = EntityName.appointment.ToString();

RetrieveMultipleRequest retMultiRequest = new RetrieveMultipleRequest();
retMultiRequest.Query = allPartQuery;
retMultiRequest.ReturnDynamicEntities = true;

RetrieveMultipleResponse retMultiResponse =
(RetrieveMultipleResponse)context.CreateCrmService(true).Execute(retMultiRequest);

#endregion

#region Summaries all Appointments Number of Participants
Int32 Summery = 0;
foreach(DynamicEntity appointment in retMultiResponse.BusinessEntityCollection.BusinessEntities)
{
if (appointment.Properties.Contains("gi_noofpart"))
{
Summery += ((CrmNumber)appointment.Properties["gi_noofpart"]).Value;
}
}
#endregion

#region Update Parent entity Number of Participants
/* Key Property */
Key parentEntityKey = new Key(regardingObjectId.Value);
KeyProperty parentEntityKeyProp = new KeyProperty("gi_customid", parentEntityKey);
/* Number of participants Property */
CrmNumberProperty noofPartProp = new CrmNumberProperty("gi_noofpart",new CrmNumber(Summery));

DynamicEntity parentEntity = new DynamicEntity(regardingObjectId.type);
parentEntity.Properties.Add(parentEntityKeyProp);
parentEntity.Properties.Add(noofPartProp);

TargetUpdateDynamic targetEntity = new TargetUpdateDynamic();
targetEntity.Entity = parentEntity;
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.Target = targetEntity;
context.CreateCrmService(true).Execute(updateRequest);
#endregion
}
}
}
}

4 comments:

aarch said...

hi adi,

itied this..but the plugin itself is not triggering when i debug

Adi Katz said...

I posted a comment on ms forums. See if it helps.

Mohamed Rashwan® said...

I didn't know about the book message.. i was running crazy as why the create message is not fired..

thank you so much for letting me know about the book message.. really thx

Syed Muntazir Mehdi said...
This comment has been removed by the author.