Microsoft Dynamics CRM 2011 | Benutzer Arbeitszeitenkalender

Neulich bin ich gefragt worden, welche Möglichkeiten bestehen zwischen einer Arbeitszeiterfassung und Microsoft Dynamics CRM 2011 einen geeigneten Austausch von Daten zu ermöglichen.

Den meisten dürfte bekannt sein, dass innerhalb des CRM Systems für jeden Benutzer ein so genannter Arbeitszeitenkalender angelegt und auch editiert werden kann. Die Arbeitszeiten finden Anwendung bei der Planung von so genannten Services. Und da ich mich gerade intensiv mit den Möglichkeiten des Kalenders auseinandergesetzt habe, konnte ich eine Variante der Anbindung vorschlagen.

Zunächst muss man wissen, dass es via SDK durchaus Möglichkeiten gibt, Einträge in den jeweiligen Arbeitszeitenkalender vorzunehmen.

Hier finden sich einige Beispiele mittlerweile im Netz, die jedoch von einigen 1:1 übernommen werden, ohne sich der Konsequenzen bewusst zu sein.

Daher stelle ich Euch die Code-Zeilen einmal etwas ausführlicher dar.

Ein Arbeitszeitenkalendereintrag besteht immer aus einem Kalendereintrag + einem so genannten inneren Kalendereintrag.

Der Kalendereintrag kann wie folgt erzeugt werden:

         // Get the calendar id of the user
         Entity systemUserEntity = slos.Retrieve(„systemuser“, userid, new ColumnSet(new String[] { „calendarid“}));
         // Retrieve the calendar of the user
         Entity userCalendarEntity = slos.Retrieve(„calendar“, ((Microsoft.Xrm.Sdk.EntityReference)(systemUserEntity.Attributes[„calendarid“])).Id, new ColumnSet(true));
         // Retrieve the calendar rules defined in the calendar
         EntityCollection calendarRules = (EntityCollection)userCalendarEntity.Attributes[„calendarrules“];
         // Create a new inner calendar
         Entity newInnerCalendar = new Entity(„calendar“);
         newInnerCalendar.Attributes[„businessunitid“] = new EntityReference(„businessunit“, ((Microsoft.Xrm.Sdk.EntityReference)(userCalendarEntity[„businessunitid“])).Id);
         Guid innerCalendarId = slos.Create(newInnerCalendar);
         // Create a new calendar rule and assign the inner calendar id to it
         Entity calendarRule = new Entity(„calendarrule“);
         calendarRule.Attributes[„description“] = „Time Off Rule“;
         calendarRule.Attributes[„name“] = „Reason“;
         calendarRule.Attributes[„duration“] = 60;
         calendarRule.Attributes[„extentcode“] = 2;
         calendarRule.Attributes[„pattern“] = „FREQ=DAILY;INTERVAL=1;COUNT=1“;
         calendarRule.Attributes[„rank“] = 0;
         calendarRule.Attributes[„timezonecode“] = _timeZoneCode;
         calendarRule.Attributes[„innercalendarid“] = new EntityReference(„calendar“, innerCalendarId);
         // starting at 12:00 on 7 May
         calendarRule.Attributes[„starttime“] = new DateTime(2013, 5, 7,12,0,0,DateTimeKind.Utc);
         calendarRules.Entities.Add(calendarRule);
         // assign all the calendar rule back to the user calendar
         userCalendarEntity.Attributes[„calendarrules“] = calendarRules;
         // update the user calendar entity that has the new rule
         slos.Update(userCalendarEntity);

Wie Ihr seht habe ich zwei Zeilen besonders hervorgehoben, da Sie in zahlreichen anderen Beispielcodes schlichtweg vergessen oder direkt gesetzt werden.

Zum Einen handelt es sich um die Angabe eines Abwesenheitsgrundes. Vergeblich sucht man in der Kalenderentität nach einem Attribut “Reason”. Hier muss schlichtweg der “Name” gesetzt werden, um im Kalender einen vernünftigen Eintrag zu erzeugen.

Zum Anderen sollte der Zeitzonencode nicht fest gesetzt werden, sondern in Abhängigkeit des Benutzers, für den ein Eintrag erzeugt werden soll.

Glücklicherweise kann man auch diese mit einfachen Mitteln auslesen.

 //Retrieve TimeZoneCode
var currentUserSettings = _serviceProxy.RetrieveMultiple(
              new QueryExpression(UserSettings.EntityLogicalName)
              {
                  ColumnSet = new ColumnSet(„localeid“, „timezonecode“),
                  Criteria = new FilterExpression
                  {
                      Conditions =
                {
                    new ConditionExpression(„systemuserid“, ConditionOperator.EqualUserId)
                }
                  }
              }).Entities[0].ToEntity<UserSettings>();

_localeId = currentUserSettings.LocaleId;
_timeZoneCode = currentUserSettings.TimeZoneCode;

Damit jedoch nicht genug, denn es fehlt noch die Definition des inneren Kalenders.

Entity calendarRule1 = new Entity(„calendarrule“);
         // duration of 60 minutes
         calendarRule1.Attributes[„duration“] = 60;
         calendarRule1.Attributes[„effort“] = 1.0;
         calendarRule1.Attributes[„issimple“] = true;
         calendarRule1.Attributes[„offset“] = 0;
         calendarRule1.Attributes[„rank“] = 0;
         // subcode 6= vacation
         calendarRule1.Attributes[„subcode“] = 6;
         // time code 2 = unavailable
         calendarRule1.Attributes[„timecode“] = 2;
         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;
         slos.Update(newInnerCalendar);

 

1_hour_TimeOff_SubCode6Wenn man diesen Code nunmehr verwendet, um einen Eintrag im Arbeitszeitenkalender zu erzeugen, erhält man einen ordnungsgemäß erstellten Eintrag mit gewählter Auszeit.

User_Calendar_TimeOff_Subcode6_visible

Und selbst im Kalender des jeweiligen Benutzers ist der Eintrag gelistet. Etwas verwun-derlich, ist allen-falls die Tat-sache, dass nach dem Eintrag die weitere verfügbare Zeit nicht hellblau markiert ist.

Doch dies ist meiner Meinung nach lediglich ein Darstellungsfehler, den wir schon seit geraumer Zeit im System haben. (Bei manuell erstellten Einträge ist die Darstellung nämlich zu 100% identisch).

Nun habe ich im obigen Quellcode noch eine weitere Zeile hervorgehoben. Auch hierfür finden sich im SDK einige Beschreibungen:

Member name

Description

Appointment

A block of time that is already scheduled for an appointment. Value = 7.

Break

A block of time that cannot be committed due to a scheduled break. Value = 4.

Committed

A block of time that is committed to perform an action. Value = 2.

Holiday

A block of time that cannot be scheduled due to a scheduled holiday. Value = 5.

ResourceCapacity

Specifies the capacity of a resource for the specified time interval. Value = 10.

ResourceServiceRestriction

A restriction for a resource for the specified service. Value = 9.

ResourceStartTime

Specifies to filter a resource start time. Value = 8.

Schedulable

A schedulable block of time. Value = 1.

ServiceCost

An override to the service cost for the specified time block. Value = 12.

ServiceRestriction

Specifies that a service is restricted during the specified block of time. Value = 11.

Uncommitted

A block of time that is tentatively scheduled but not committed. Value = 3.

Unspecified

Specifies free time with no specified restrictions. Value = 0.

Vacation

A block of time that cannot be scheduled due to a scheduled vacation. Value = 6

Wie es scheint können wir also noch weitere Einträge mit anderen Subcodes erstellen.

1_hour_TimeOff_SubCode4Und natürlich habe ich es mir nicht nehmen lassen, für Euch zu experimentieren. Wir nehmen als nächstes den Subcode 4 und erstellen eine Pause.

Das Arbeitszeitenmodell wird entsprechend angepasst. Die Pause ist ebenfalls berücksichtigt, wenn auch nicht angezeigt.

1_hour_TimeOff_SubCode4_cannot_edit_break

Wollte man die Pause gar bearbeiten und klickt auf den Arbeitszeiten-eintrag des Tages, so stellt man fest, dass die Pause hier nicht angezeigt wird.

Die Pause wird ebenfalls nicht im Benutzer-Kalender angezeigt. 

Dennoch ist sie im System und wird beispielsweise von der Service-Planung berücksichtigt.

Was passiert, wenn wir mit dem Subcode 7 einen Termin erzeugen?

1_hour_TimeOff_SubCode7_9th_May

Wie schon bei der Pause, wird auch hier ein geändertes Arbeitszeitenprofil ausgegeben, ein Termin jedoch nicht angezeigt.

Nun könnte man meinen, es handelt sich um einen Termin, also müsste dieser in den Aktivitäten oder aber im Kalender des Benutzers zu finden sein.

User_Calendar_9th_May_No_Appointment

Doch weit gefehlt. Weder unter den Aktivitäten, noch im Kalender des Benutzers ist dieser Termin sichtbar.

Ist der Termin also gar nicht im System?

Doch, denn auch hier ergibt ein Test mit der Service-Planung, dass für den Benutzer zu dieser Zeit kein Service-Termin geplant werden kann, da er/sie nicht verfügbar ist.

Widmen wir uns noch einem weiteren interessanten Subcode zu. In diesem Fall den Ferien (Subcode 5).

1_hour_TimeOff_SubCode5

Eine Anzeige im Arbeits-zeitenkalender erfolgt. Doch will man den Eintrag verändern (Doppelklick),

Working_Hour_Trying_to_edit_Error

so erhält man eine Fehler-meldung.

Was also, wollte man den Benutzer-Kalender anzeigen?

User_Calendar_13th_May_Error_Message_SubCode5

Auch hier erhält man eine Fehlermeldung für den kompletten Tag. Und in der Folge kann man keine Einträge für diesen Tag vornehmen.

Wer jetzt ein On-Premise-System besitzt, der kann direkt in der SQL-Datenbank die erstellten Kalender-Einträge löschen und erhält im Anschluss wieder Zugang zu dem Benutzer-Kalender. Wer hingegen eine Online-Version besitzt, der darf nunmehr mit dem Support ein Ticket bearbeiten.

Darum aufgepasst bei den SubCodes !!!

Nicht jeder Subcode der in der Beschreibung einen geeigneten Eintrag enthält, lässt sich auf diese einfache Art und Weise erzeugen.

Kommen wir nach diesem kurzen Testausflug zurück auf die mögliche Lösung zur Anbindung einer Arbeitszeiterfassung und Microsoft Dynamics CRM.

Mit obigem Beispiel können wir ein Plug-In programmieren. Wir brauchen in unserem CRM-System nunmehr noch eine Benutzerdefinierte Entität “TimeOffs”. Immer dann, wenn hier für einen bestimmten Benutzer eine “Arbeitsfreie Zeit” eingetragen wird, erstellt unser Plug-In einen Eintrag in seinem/ihrem Arbeitszeitenkalender. Auf diese Art und Weise haben wir eine Möglichkeit, per Export-/Import aus dem Arbeitszeitenerfassungssystem Einträge in unsere neue Entität zu übernehmen und diese dann in den Arbeitszeitenkalender vom CRM zu übertragen.

Hierfür benötigen wir die Felder:

Abwesenheitsgrund, Start- und Enddatum der Abwesenheit, Suchfeld zu Benutzern (zur Auswahl), Dauer.

Obiger Code muss nun noch verbessert werden, so dass die Zeitzone für den Benutzer abgefragt wird, für den eine Abwesenheit eingetragen werden soll. Und die anderen Werte sind ebenfalls dynamisch aus den jeweiligen Werten der Entität zu übernehmen.

Ich hoffe, dieses Beispiel hat Euch gezeigt, dass noch eine Menge Flexibilität im System steckt, dessen man sich (manchmal mangels Dokumentation) nicht immer bewusst ist.

Viel Spaß beim Experimentieren und bis zum nächsten Mal

 

Technorati Tags:

Hinterlasse einen Kommentar

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..