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:
- I created a Fetch to retrieve all users which have a Bookable Resource.
- 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 🙂
Is it feasible to add work order assigned to resources in Work Hours Calendar ?
Hi Sumanta,
Im not sure what you mean, can you give a detailed example?
Hi Thomas,
I`m using msdyn_SaveCalendar with flow, I`m passing this JSON to the unbound action:
{
“CalendarEventInfo”: “{\”CalendarId\”:\”71199c77-5875-ed11-81aa-000d3abad624\”,\”EntityLogicalName\”:\”bookableresource\”,\”TimeZoneCode\”:5,\”RulesAndRecurrences\”:[{\”Rules\”:[{\”StartTime\”:\”2023-07-01T00:00:00.000Z\”,\”EndTime\”:\”2023-07-22T00:00:00.000Z\”,\”Effort\”:1,\”WorkHourType\”:0}]}]}”
}
And I`m getting the following error:
The data contract type ‘Microsoft.Dynamics.UCICalendar.Plugins.SaveCalendarContract+CalendarEventInfo’ cannot be deserialized because the required data member ‘CalendarId’ was not found.
What could be the issue?
Looks correct to me. It seems to be complaining about not being able to find the property ‘CalendarId’ in your JSON, which I can see that it is indeed inside of your copied JSON. Have you validated you JSON to see if it is correct syntax?
The JSON string should be:
{“CalendarId”:”CalendarGUID”,”EntityLogicalName”:”bookableresource”,”TimeZoneCode”:201,”RulesAndRecurrences”:[{“Rules”:[{“StartTime”:”2024-01-20T00:00:00.000Z”,”EndTime”:”2028-01-22T00:00:00.000Z”,”Effort”:1,”WorkHourType”:0}]}]}