Bulk update user work hours for the Schedule Board

If you have lots of users which need their work hours added into CRM, it can take a long time to manually set up each user through the user interface.

Below is the code to update user’s work hours in bulk for a regular 9am-5pm schedule and will also reflect on the Universal Resource Scheduling board.

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Crm.Sdk.Messages;
using System.Net;
using System.ServiceModel.Description;
using System;
using Microsoft.Xrm.Sdk.Query;
using System.Collections.Generic;

namespace BulkCreateWorkHours
{
    class Program
    {
        private static Guid Systemusers;
        private static string Names;

        static void Main(string[] args)
        {
            IOrganizationService organizationService = null;

            try
            {
                //Login details
                ClientCredentials clientCredentials = new ClientCredentials();
                clientCredentials.UserName.UserName = "email";
                clientCredentials.UserName.Password = "password";

                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

                //CRM Organisation URL details
                organizationService = (IOrganizationService)new OrganizationServiceProxy(new Uri("https://XXXXXXX.api.XX.dynamics.com/XRMServices/2011/Organization.svc"),
                 null, clientCredentials, null);

                if (organizationService != null)
                {
                    Guid userid = ((WhoAmIResponse)organizationService.Execute(new WhoAmIRequest())).UserId;

                    Console.WriteLine("Connection Established Successfully...");

                    //Fetches all Users which have a Bookable Resource
                    string fetchxml = "<?xml version='1.0'?>" +
                                    "<fetch distinct='true' mapping='logical' output-format='xml-platform' version='1.0'>" +
                                    "<entity name='systemuser'>" +
                                    "<attribute name='systemuserid'/>" +
                                    "<attribute name='firstname'/>" +
                                    "<attribute name='lastname'/>" +
                                    "<order descending='false' attribute='firstname'/>" +
                                    "<link-entity name='bookableresource' alias='ac' link-type='inner' to='systemuserid' from='userid'/>" +
                                    "</entity>" +
                                    "</fetch>";

                    EntityCollection result = organizationService.RetrieveMultiple(new FetchExpression(fetchxml));

                    Console.WriteLine("There are {0} entities found", result.Entities.Count);

                    foreach (var c in result.Entities)
                    {
                        Systemusers = ((Guid)c.Attributes["systemuserid"]);
                        Names = ((string)c.Attributes["firstname"]) + " " + ((string)c.Attributes["lastname"]);

                        //Clears original Calander Rules for all of the Users retrieved from the fetch
                        ClearCalenderRules(organizationService, Systemusers);

                        //Add new Calander Rules for all of the Users retrieved from the fetch
                        AddCalenderRules(organizationService, Systemusers);                       
                    }

                    Console.WriteLine("COMPLETE");
                }
                else
                {
                    Console.WriteLine("Failed to Established Connection!!!");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught - " + ex.Message);
            }
            Console.ReadKey();
        }

        public static void AddCalenderRules(IOrganizationService organizationService, Guid userid)
        {
            if (userid != Guid.Empty)
            {

                    Entity systemUserEntity = organizationService.Retrieve("systemuser", Systemusers, new ColumnSet(new String[] { "calendarid" }));
                    Entity userCalendarEntity = organizationService.Retrieve("calendar", ((Microsoft.Xrm.Sdk.EntityReference)(systemUserEntity.Attributes["calendarid"])).Id, new ColumnSet(true));
                    EntityCollection calendarRules = (EntityCollection)userCalendarEntity.Attributes["calendarrules"];

                    Entity newInnerCalendar = new Entity("calendar");
                    newInnerCalendar.Attributes["businessunitid"] = new EntityReference("businessunit", ((Microsoft.Xrm.Sdk.EntityReference)(userCalendarEntity["businessunitid"])).Id);
                    Guid innerCalendarId = organizationService.Create(newInnerCalendar);

                    //Create a new calendar rule and assign the inner calendar id to it
                    Entity calendarRule = new Entity("calendarrule");
                    calendarRule.Attributes["duration"] = 1440;
                    calendarRule.Attributes["extentcode"] = 1;
                    //Create a pattern of Mon-Fri
                    calendarRule.Attributes["pattern"] = "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR";
                    calendarRule.Attributes["rank"] = 0;
                    //85 = UK Time Code
                    calendarRule.Attributes["timezonecode"] = 85;
                    calendarRule.Attributes["innercalendarid"] = new EntityReference("calendar", innerCalendarId);
                    //Starting at 12:00 on 1 January 2018
                    calendarRule.Attributes["starttime"] = new DateTime(2018, 01, 01, 0, 0, 0, DateTimeKind.Utc);
                    calendarRules.Entities.Add(calendarRule);

                    //Assign all the calendar rule back to the user calendar
                    userCalendarEntity.Attributes["calendarrules"] = calendarRules;
                    //Please refer to here for Calander Types https://msdn.microsoft.com/en-us/library/dn689038.aspx
                    userCalendarEntity.Attributes["type"] = new OptionSetValue(-1);
                    organizationService.Update(userCalendarEntity);

                    //Creates a new Calendar Rule 
                    Entity calendarRule1 = new Entity("calendarrule");
                    //Duration of 9 hours (540 mins)
                    calendarRule1.Attributes["duration"] = 540;
                    calendarRule1.Attributes["effort"] = 1.0;
                    calendarRule1.Attributes["issimple"] = true;
                    //Offset of 8 hours (480 mins) - Start Time is 8am
                    calendarRule1.Attributes["offset"] = 480;
                    calendarRule1.Attributes["rank"] = 0;
                    calendarRule1.Attributes["subcode"] = 1;
                    calendarRule1.Attributes["timecode"] = 0;
                    calendarRule1.Attributes["timezonecode"] = -1;
                    calendarRule1.Attributes["calendarid"] = new EntityReference("calendar", innerCalendarId);

                    EntityCollection innerCalendarRules = new EntityCollection();
                    innerCalendarRules.EntityName = "calendarrule";
                    innerCalendarRules.Entities.Add(calendarRule1);

                    newInnerCalendar.Attributes["calendarrules"] = innerCalendarRules;
                    newInnerCalendar.Attributes["calendarid"] = innerCalendarId;
                    organizationService.Update(newInnerCalendar);

                    Console.WriteLine("Work Hours Added to " + Names);
                
            }
        }

public static void ClearCalenderRules(IOrganizationService organizationService, Guid userId)
        {
            if (userId != null)
            {
                //Retrieves all CalanderId's for all the users
                string fetchxml = "<?xml version='1.0'?>" +
                                "<fetch distinct='true' mapping='logical' output-format='xml-platform' version='1.0'>" +
                                "<entity name='calendar' >" +
                                "    <attribute name='calendarid' />" +
                                "    <filter type='and' >" +
                                "      <condition attribute='primaryuserid' operator='eq' value='" + userId + "' />" +
                                "    </filter>" +
                                "  </entity>" +
                                "</fetch>";

                EntityCollection result = organizationService.RetrieveMultiple(new FetchExpression(fetchxml));

                Console.WriteLine("There are {0} entities found", result.Entities.Count);

                foreach (var c in result.Entities)
                {
                    Entity Calendar = organizationService.Retrieve("calendar", ((Guid)c.Attributes["calendarid"]), new ColumnSet(true));
                    EntityCollection calendarRules = (EntityCollection)Calendar.Attributes["calendarrules"];

                    int num = 0;
                    List<int> list = new List<int>();

                    foreach (Entity current in calendarRules.Entities)
                    {
                        list.Add(num);
                        num++;
                    }

                    list.Sort();
                    list.Reverse();

                    for (int i = 0; i < list.Count; i++)
                    {
                        //Remove all Calander Rules from Collection
                        calendarRules.Entities.Remove(calendarRules.Entities[list[i]]);
                    }

                    //Assign Calander Rules to empty Entity Collection
                    Calendar.Attributes["calendarrules"] = calendarRules;
                    organizationService.Update(Calendar);

                    Console.WriteLine("Work Hours Deleted for " + Names);
                }
            }
        }
    }
}

This code works in 2 steps:

  1. I created a Fetch to retrieve all users which have a Bookable Resource.
  2. I run a for each loop through each user and pass that UserId into two methods:
ClearCalenderRules(organizationService, Systemusers);

This method clears the current Calendar of the users.

AddCalenderRules(organizationService, Systemusers);

This method adds the Calendar rule specified for the users.

Important parts of the code that needs expanding:

userCalendarEntity.Attributes["type"] = new OptionSetValue(-1);

You need to specify the ‘type’ attribute as -1 because this will then reflect on the Schedule Board. Here is a link for more information https://msdn.microsoft.com/en-us/library/dn689038.aspx

calendarRule.Attributes["pattern"] = "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR";

This pattern string specifies a schedule frequency for Monday to Friday. You can see more examples of different patterns here https://www.syncfusion.com/kb/3719/what-is-recurrencerule-in-the-schedule-control

Hopefully this post helps 🙂

2 thoughts on “Bulk update user work hours for the Schedule Board

Leave a Reply

Your email address will not be published. Required fields are marked *