Nov 20, 2017

Enforce Calculation of Rollup fields

Rollup fields are handy in many scenarios, it takes one hours to update by default. If the business case needs real time updating, we can write plugin/WF to enforce the recalculation based on trigger. In such situation, below generic method can be used.

public static void EnforceCalculationRollup(IOrganizationService OrganizationService, EntityReference record, string rollupField)
{
CalculateRollupFieldRequest rollupRequest = new CalculateRollupFieldRequest { Target = record, FieldName = rollupField };
CalculateRollupFieldResponse response = (CalculateRollupFieldResponse)OrganizationService.Execute(rollupRequest);
Entity entity = response.Entity;
OrganizationService.Update(entity);
}

Hope this helps!

Nov 6, 2017

Use Service Calendar programmatically to check user availability

Service Calendar is a key component used in Field service for scheduling the work. Anyway, Calendar alone can be used for many different usages since its showing the working hours/ availability of a User. Here I am illustrating how to pro-grammatically access the calendar.

First of all, lets see how to set users availability in the calendar. In order to make it simple I am going to set if someone is available in given day or not. (not going into different time slots, which is also possible). Go to any User and go to Work Hours. Then go to New Weekly Schedule as below;


Please set the schedule as you wish in the resulting pane as below; Since I am interested in daily basis, I select 24 hours.


Once save you will see calendar been updated.


Now, this is the code snippet to check the availability of the User through calendar;

public static bool IsUserAvailable(IOrganizationService OrganizationService, Guid UserId)
{
    bool isUserAvailable = false;

    QueryScheduleRequest scheduleRequest = new QueryScheduleRequest
    {
        ResourceId = UserId,
        Start = DateTimeUtility.RetrieveLocalTimeFromUTCTime(OrganizationService, DateTime.Now),
        End = DateTimeUtility.RetrieveLocalTimeFromUTCTime(OrganizationService, DateTime.Now.AddSeconds(5)),
        TimeCodes = new TimeCode[] { TimeCode.Available }
    };
    QueryScheduleResponse scheduleResponse = (QueryScheduleResponse)OrganizationService.Execute(scheduleRequest);
    if (scheduleResponse.TimeInfos.Length > 0)
        isUserAvailable = true;

    return isUserAvailable;
}

You may notice that I am passing the local time in QueryScheduleRequest in above code since I am setting the local time zone in the Calendar also. I am also giving here the methods to be be used in time conversions;

internal static DateTime RetrieveLocalTimeFromUTCTime(IOrganizationService service, DateTime utcTime)
{
    return RetrieveLocalTimeFromUTCTime(utcTime, RetrieveCurrentUsersSettings(service), service);
}

internal static int? RetrieveCurrentUsersSettings(IOrganizationService service)
{
    var currentUserSettings = service.RetrieveMultiple(
        new QueryExpression("usersettings")
        {
            ColumnSet = new ColumnSet("timezonecode"),
            Criteria = new FilterExpression
            {
                Conditions =
                {
                            new ConditionExpression("systemuserid", ConditionOperator.EqualUserId)
                }
            }
        }).Entities[0].ToEntity<Entity>();
    return (int?)currentUserSettings.Attributes["timezonecode"];
}

internal static DateTime RetrieveLocalTimeFromUTCTime(DateTime utcTime, int? timeZoneCode, IOrganizationService service)
{
    if (!timeZoneCode.HasValue)
        return DateTime.Now;
    var request = new LocalTimeFromUtcTimeRequest
    {
        TimeZoneCode = timeZoneCode.Value,
        UtcTime = utcTime.ToUniversalTime()
    };
    var response = (LocalTimeFromUtcTimeResponse)service.Execute(request);
    return response.LocalTime;
}

Hope this helps!

Oct 19, 2017

Programmatically Share, Retrieve Shared Users and UnShare a Dynamics 365 record with a User OR Team

This is just to share some simple code snippets relates to sharing. This works well.

Sharing

using Microsoft.Crm.Sdk.Messages;

public static void ShareRecord(IOrganizationService OrganizationService, string entityName, Guid recordId, Guid UserId)
{
    EntityReference recordRef = new EntityReference(entityName, recordId);
    EntityReference User = new EntityReference(SystemUser.EntityLogicalName, UserId);

    //If its a team, Principal should be supplied with the team
    //EntityReference Team = new EntityReference(Team.EntityLogicalName, teamId);

    GrantAccessRequest grantAccessRequest = new GrantAccessRequest
    {
        PrincipalAccess = new PrincipalAccess
        {
            AccessMask = AccessRights.ReadAccess | AccessRights.WriteAccess | AccessRights.AppendToAccess | AccessRights.,
            Principal = User
            //Principal = Team
        },
        Target = recordRef
    };
    OrganizationService.Execute(grantAccessRequest);
}

Its great that VS intellisense would help you identify which Access Right you can set.


Retrieve user who has been shared with
public static void RetrieveSharedUsers(IOrganizationService OrganizationService, EntityReference entityRef)
{
    var accessRequest = new RetrieveSharedPrincipalsAndAccessRequest
    {
        Target = entityRef
    };
    var accessResponse = (RetrieveSharedPrincipalsAndAccessResponse)OrganizationService.Execute(accessRequest);
    foreach (var principalAccess in accessResponse.PrincipalAccesses)
    {
        // principalAccess.Principal.Id - User Id
    }
}

Revoke the Share (UnShare)
 public static void RevokeShareRecord(IOrganizationService OrganizationService, string TargetEntityName, Guid TargetId, Guid UserId)
 {
    EntityReference target = new EntityReference(TargetEntityName, TargetId);
    EntityReference User = new EntityReference(SystemUser.EntityLogicalName, UserId);

    RevokeAccessRequest revokeAccessRequest = new RevokeAccessRequest
    {
        Revokee = User,
        Target = target
    };
    OrganizationService.Execute(revokeAccessRequest);
}