/// <summary> /// Set this outlook item's duration, but also end time and location, from this CRM item. /// </summary> /// <param name="crmType">The type of the CRM item.</param> /// <param name="crmItem">The CRM item.</param> /// <param name="olItem">The Outlook item.</param> private void SetOutlookItemDuration(string crmType, EntryValue crmItem, Outlook.AppointmentItem olItem) { int minutes = 0, hours = 0; try { if (!string.IsNullOrWhiteSpace(crmItem.GetValueAsString("duration_minutes"))) { minutes = int.Parse(crmItem.GetValueAsString("duration_minutes")); } if (!string.IsNullOrWhiteSpace(crmItem.GetValueAsString("duration_hours"))) { hours = int.Parse(crmItem.GetValueAsString("duration_hours")); } int durationMinutes = minutes + hours * 60; if (crmType == AppointmentSyncing.CrmModule) { olItem.Location = crmItem.GetValueAsString("location"); olItem.End = olItem.Start.AddMinutes(durationMinutes); } olItem.Duration = durationMinutes; } catch (Exception any) { Log.Error("AppointmentSyncing.SetOutlookItemDuration", any); } finally { this.SaveItem(olItem); } }
protected override void UpdateOutlookDetails(string crmType, EntryValue crmItem, DateTime date_start, Outlook.AppointmentItem olItem) { try { olItem.Start = date_start; var minutesString = crmItem.GetValueAsString("duration_minutes"); var hoursString = crmItem.GetValueAsString("duration_hours"); int minutes = string.IsNullOrWhiteSpace(minutesString) ? 0 : int.Parse(minutesString); int hours = string.IsNullOrWhiteSpace(hoursString) ? 0 : int.Parse(hoursString); olItem.Duration = minutes + hours * 60; olItem.Location = crmItem.GetValueAsString("location"); olItem.End = olItem.Start; if (hours > 0) { olItem.End.AddHours(hours); } if (minutes > 0) { olItem.End.AddMinutes(minutes); } SetOutlookRecipientsFromCRM(olItem, crmItem, crmItem.GetValueAsString("id"), crmType); } finally { this.SaveItem(olItem); } }
/// <summary> /// Update an existing Outlook item with values taken from a corresponding CRM item. Note that /// this just overwrites all values in the Outlook item. /// </summary> /// <param name="crmItem">The CRM item from which values are to be taken.</param> /// <param name="itemSyncState">The sync state of an outlook item assumed to correspond with the CRM item.</param> /// <returns>An appropriate sync state.</returns> private SyncState <Outlook.ContactItem> UpdateExistingOutlookItemFromCrm(EntryValue crmItem, SyncState <Outlook.ContactItem> itemSyncState) { if (!itemSyncState.IsDeletedInOutlook) { Outlook.ContactItem olItem = itemSyncState.OutlookItem; Outlook.UserProperty dateModifiedProp = olItem.UserProperties[SyncStateManager.ModifiedDatePropertyName]; Outlook.UserProperty shouldSyncProp = olItem.UserProperties["SShouldSync"]; this.LogItemAction(olItem, "ContactSyncing.UpdateExistingOutlookItemFromCrm"); if (CrmItemChanged(crmItem, olItem)) { DateTime crmDate = DateTime.Parse(crmItem.GetValueAsString("date_modified")); DateTime outlookDate = dateModifiedProp == null ? DateTime.MinValue : DateTime.Parse(dateModifiedProp.Value.ToString()); if (crmDate > this.LastRunCompleted && outlookDate > this.LastRunCompleted) { MessageBox.Show( $"Contact {olItem.FirstName} {olItem.LastName} has changed both in Outlook and CRM; please check which is correct", "Update problem", MessageBoxButtons.OK, MessageBoxIcon.Warning); } else if (crmDate > outlookDate) { this.SetOutlookItemPropertiesFromCrmItem(crmItem, olItem); } } this.LogItemAction(olItem, "ContactSyncing.UpdateExistingOutlookItemFromCrm"); itemSyncState.OModifiedDate = DateTime.ParseExact(crmItem.GetValueAsString("date_modified"), "yyyy-MM-dd HH:mm:ss", null); } return(itemSyncState); }
protected override SyncState <Outlook.TaskItem> AddOrUpdateItemFromCrmToOutlook(Outlook.MAPIFolder tasksFolder, string crmType, EntryValue crmItem) { SyncState <Outlook.TaskItem> result = null; Log.Debug($"TaskSyncing.AddOrUpdateItemFromCrmToOutlook\n\tSubject: {crmItem.GetValueAsString("name")}\n\tCurrent user id {RestAPIWrapper.GetUserId()}\n\tAssigned user id: {crmItem.GetValueAsString("assigned_user_id")}"); DateTime dateStart = crmItem.GetValueAsDateTime("date_start"); DateTime dateDue = crmItem.GetValueAsDateTime("date_due"); string timeStart = ExtractTime(dateStart); string timeDue = ExtractTime(dateDue); var syncState = this.GetExistingSyncState(crmItem); if (syncState == null) { /* check for howlaround */ var matches = this.FindMatches(crmItem); if (matches.Count == 0) { /* didn't find it, so add it to Outlook */ result = AddNewItemFromCrmToOutlook(tasksFolder, crmItem, dateStart, dateDue, timeStart, timeDue); } else { this.Log.Warn($"Howlaround detected? Task '{crmItem.GetValueAsString("name")}' offered with id {crmItem.GetValueAsString("id")}, expected {matches[0].CrmEntryId}, {matches.Count} duplicates"); } } else { result = UpdateExistingOutlookItemFromCrm(crmItem, dateStart, dateDue, timeStart, timeDue, syncState); } return(result); }
/// <summary> /// Update this Outlook appointment's start and duration from this CRM object. /// </summary> /// <param name="crmType">The CRM type of the item from which values are to be taken.</param> /// <param name="crmItem">The CRM item from which values are to be taken.</param> /// <param name="date_start">The state date/time of the item, adjusted for timezone.</param> /// <param name="olItem">The outlook item assumed to correspond with the CRM item.</param> private void UpdateOutlookStartAndDuration(string crmType, EntryValue crmItem, DateTime date_start, Outlook.AppointmentItem olItem) { try { olItem.Start = date_start; var minutesString = crmItem.GetValueAsString("duration_minutes"); var hoursString = crmItem.GetValueAsString("duration_hours"); int minutes = string.IsNullOrWhiteSpace(minutesString) ? 0 : int.Parse(minutesString); int hours = string.IsNullOrWhiteSpace(hoursString) ? 0 : int.Parse(hoursString); if (crmType == AppointmentSyncing.CrmModule) { olItem.Location = crmItem.GetValueAsString("location"); olItem.End = olItem.Start; if (hours > 0) { olItem.End.AddHours(hours); } if (minutes > 0) { olItem.End.AddMinutes(minutes); } SetRecipients(olItem, crmItem.GetValueAsString("id"), crmType); } olItem.Duration = minutes + hours * 60; } finally { this.SaveItem(olItem); } }
private void SetOutlookItemPropertiesFromCrmItem(EntryValue crmItem, Outlook.TaskItem olItem) { try { DateTime dateStart = crmItem.GetValueAsDateTime("date_start"); DateTime dateDue = crmItem.GetValueAsDateTime("date_due"); string timeStart = ExtractTime(dateStart); string timeDue = ExtractTime(dateDue); olItem.Subject = crmItem.GetValueAsString("name"); olItem.StartDate = MaybeChangeDate(dateStart, olItem.StartDate, "olItem.StartDate"); olItem.DueDate = MaybeChangeDate(dateDue, olItem.DueDate, "olItem.DueDate"); string body = crmItem.GetValueAsString("description"); olItem.Body = string.Concat(body, "#<", timeStart, "#", timeDue); olItem.Status = GetStatus(crmItem.GetValueAsString("status")); olItem.Importance = GetImportance(crmItem.GetValueAsString("priority")); EnsureSynchronisationPropertiesForOutlookItem(olItem, crmItem.GetValueAsString("date_modified"), DefaultCrmModule, CrmId.Get(crmItem.id)); } finally { this.SaveItem(olItem); } }
/// <summary> /// Set up synchronisation properties for this outlook item from this CRM item, assuming my default CRM module. /// </summary> /// <param name="olItem">The Outlook item.</param> /// <param name="crmItem">The CRM item.</param> /// <param name="type">The value for the SType property (CRM module name).</param> protected virtual void EnsureSynchronisationPropertiesForOutlookItem(OutlookItemType olItem, EntryValue crmItem, string type) { this.EnsureSynchronisationPropertiesForOutlookItem( olItem, crmItem.GetValueAsString("date_modified"), type, crmItem.GetValueAsString("id")); }
/// <summary> /// Add an item existing in CRM but not found in Outlook to Outlook. /// </summary> /// <param name="appointmentsFolder">The Outlook folder in which the item should be stored.</param> /// <param name="crmType">The CRM type of the item from which values are to be taken.</param> /// <param name="crmItem">The CRM item from which values are to be taken.</param> /// <param name="date_start">The state date/time of the item, adjusted for timezone.</param> /// <returns>A sync state object for the new item.</returns> private SyncState <Outlook.AppointmentItem> AddNewItemFromCrmToOutlook( Outlook.MAPIFolder appointmentsFolder, string crmType, EntryValue crmItem, DateTime date_start) { AppointmentSyncState newState = null; Outlook.AppointmentItem olItem = null; try { var crmId = crmItem.GetValueAsString("id"); /* * There's a nasty little bug (#223) where Outlook offers us back in a different thread * the item we're creating, before we're able to set up the sync state which marks it * as already known. By locking on the enqueueing lock here, we should prevent that. */ lock (enqueueingLock) { olItem = appointmentsFolder.Items.Add(Outlook.OlItemType.olAppointmentItem); newState = new AppointmentSyncState(crmType) { OutlookItem = olItem, OModifiedDate = DateTime.ParseExact(crmItem.GetValueAsString("date_modified"), "yyyy-MM-dd HH:mm:ss", null), CrmEntryId = crmId }; ItemsSyncState.Add(newState); olItem.Subject = crmItem.GetValueAsString("name"); olItem.Body = crmItem.GetValueAsString("description"); /* set the SEntryID property quickly, create the sync state and save the item, to reduce howlaround */ EnsureSynchronisationPropertiesForOutlookItem(olItem, crmItem, crmType); this.SaveItem(olItem); } LogItemAction(olItem, "AppointmentSyncing.AddNewItemFromCrmToOutlook"); if (!string.IsNullOrWhiteSpace(crmItem.GetValueAsString("date_start"))) { olItem.Start = date_start; SetOutlookItemDuration(crmType, crmItem, olItem); Log.Info("\tdefault SetRecepients"); SetRecipients(olItem, crmId, crmType); } } finally { if (olItem != null) { this.SaveItem(olItem); } } return(newState); }
private SyncState <Outlook.TaskItem> UpdateExistingOutlookItemFromCrm(EntryValue crmItem, SyncState <Outlook.TaskItem> syncStateForItem) { if (!syncStateForItem.IsDeletedInOutlook) { Outlook.TaskItem olItem = syncStateForItem.OutlookItem; Outlook.UserProperty oProp = olItem.UserProperties[SyncStateManager.ModifiedDatePropertyName]; if (oProp.Value != crmItem.GetValueAsString("date_modified")) { SetOutlookItemPropertiesFromCrmItem(crmItem, olItem); } syncStateForItem.OModifiedDate = DateTime.ParseExact(crmItem.GetValueAsString("date_modified"), "yyyy-MM-dd HH:mm:ss", null); } return(syncStateForItem); }
/// <summary> /// A CRM item is perceived to have changed if its modified date is different from /// that of its Outlook representation, or if its should sync flag is. /// </summary> /// <param name="crmItem">A CRM item.</param> /// <param name="olItem">An Outlook item, assumed to represent the same entity.</param> /// <returns>True if either of these propertyies differ between the representations.</returns> private bool CrmItemChanged(EntryValue crmItem, Outlook.ContactItem olItem) { Outlook.UserProperty dateModifiedProp = olItem.UserProperties[SyncStateManager.ModifiedDatePropertyName]; return(dateModifiedProp.Value != crmItem.GetValueAsString("date_modified") || ShouldSyncFlagChanged(olItem, crmItem)); }
/// <summary> /// Update a single appointment in the specified Outlook folder with changes from CRM, but /// only if its start date is fewer than five days in the past. /// </summary> /// <param name="folder">The folder to synchronise into.</param> /// <param name="crmType">The CRM type of the candidate item.</param> /// <param name="crmItem">The candidate item from CRM.</param> /// <returns>The synchronisation state of the item updated (if it was updated).</returns> protected override SyncState <Outlook.AppointmentItem> AddOrUpdateItemFromCrmToOutlook( Outlook.MAPIFolder folder, string crmType, EntryValue crmItem) { SyncState <Outlook.AppointmentItem> result = null; DateTime dateStart = crmItem.GetValueAsDateTime("date_start"); if (dateStart >= GetStartDate()) { /* search for the item among the sync states I already know about */ var syncState = this.GetExistingSyncState(crmItem); if (syncState == null) { /* check for howlaround */ var matches = this.FindMatches(crmItem); if (matches.Count == 0) { /* didn't find it, so add it to Outlook */ result = AddNewItemFromCrmToOutlook(folder, crmType, crmItem, dateStart); } else { this.Log.Warn($"Howlaround detected? Appointment '{crmItem.GetValueAsString("name")}' offered with id {crmItem.GetValueAsString("id")}, expected {matches[0].CrmEntryId}, {matches.Count} duplicates"); } } else { /* found it, so update it from the CRM item */ result = UpdateExistingOutlookItemFromCrm(crmType, crmItem, dateStart, syncState); result?.OutlookItem.Save(); } if (crmItem?.relationships?.link_list != null) { foreach (var list in crmItem.relationships.link_list) { foreach (var record in list.records) { var data = record.data.AsDictionary(); try { this.meetingRecipientsCache[data[AddressResolutionData.EmailAddressFieldName].ToString()] = new AddressResolutionData(list.name, data); Log.Debug($"Successfully cached recipient {data[AddressResolutionData.EmailAddressFieldName]} => {list.name}, {data[AddressResolutionData.ModuleIdFieldName]}."); } catch (KeyNotFoundException kex) { Log.Error($"Key not found while caching meeting recipients.", kex); } } } } } return(result); }
private bool IsProbablySameItem(EntryValue result, ContactItem contactItem) { string crmIdStr = contactItem.GetCrmId().ToString(); return(result != null && (result.id.Equals(crmIdStr) || result.GetValueAsString("outlook_id").Equals(contactItem.EntryID))); }
private void SetOutlookItemPropertiesFromCrmItem(EntryValue crmItem, Outlook.TaskItem olItem) { try { DateTime dateStart = crmItem.GetValueAsDateTime("date_start"); DateTime dateDue = crmItem.GetValueAsDateTime("date_due"); string timeStart = ExtractTime(dateStart); string timeDue = ExtractTime(dateDue); olItem.Subject = crmItem.GetValueAsString("name"); try { olItem.StartDate = MaybeChangeDate(dateStart, olItem.StartDate, "syncState.StartDate"); } catch (COMException comx) { #if DEBUG Log.Debug($"COM Exception while trying to set start date of task: '{comx.Message}'. Some otherwise-valid tasks don't support this"); #endif } try { olItem.DueDate = MaybeChangeDate(dateDue, olItem.DueDate, "syncState.DueDate"); } catch (COMException comx) { #if DEBUG Log.Debug($"COM Exception while trying to set start date of task: '{comx.Message}'. Do some otherwise-valid tasks not support this?"); #endif } string body = crmItem.GetValueAsString("description"); olItem.Body = string.Concat(body, "#<", timeStart, "#", timeDue); olItem.Status = GetStatus(crmItem.GetValueAsString("status")); olItem.Importance = GetImportance(crmItem.GetValueAsString("priority")); EnsureSynchronisationPropertiesForOutlookItem(olItem, crmItem.GetValueAsString("date_modified"), DefaultCrmModule, CrmId.Get(crmItem.id)); } finally { this.SaveItem(olItem); } }
protected override bool IsMatch(Outlook.AppointmentItem olItem, EntryValue crmItem) { var crmItemStart = crmItem.GetValueAsDateTime("date_start"); var crmItemName = crmItem.GetValueAsString("name"); var olItemStart = olItem.Start; var subject = olItem.Subject; return(subject == crmItemName && olItemStart == crmItemStart); }
protected override bool ShouldAddOrUpdateItemFromCrmToOutlook(Outlook.MAPIFolder folder, string crmType, EntryValue crmItem) { try { return(RestAPIWrapper.GetUserId().Equals(crmItem.GetValueAsString("assigned_user_id"))); } catch (TypeInitializationException tix) { Log.Warn("Bad CRM id?", tix); return(false); } }
/// <summary> /// Add an item existing in CRM but not found in Outlook to Outlook. /// </summary> /// <param name="contactFolder">The Outlook folder in which the item should be stored.</param> /// <param name="crmItem">The CRM item from which values are to be taken.</param> /// <returns>A sync state object for the new item.</returns> private SyncState <Outlook.ContactItem> AddNewItemFromCrmToOutlook(Outlook.MAPIFolder contactFolder, EntryValue crmItem) { Log.Info( (string)string.Format( "ContactSyncing.AddNewItemFromCrmToOutlook, entry id is '{0}', creating in Outlook.", crmItem.GetValueAsString("id"))); Outlook.ContactItem olItem = contactFolder.Items.Add(Outlook.OlItemType.olContactItem); this.SetOutlookItemPropertiesFromCrmItem(crmItem, olItem); return(this.AddOrGetSyncState(olItem)); }
/// <summary> /// Specialisation: also set end time and location. /// </summary> /// <param name="crmItem">The CRM item.</param> /// <param name="olItem">The Outlook item.</param> protected override void SetOutlookItemDuration(EntryValue crmItem, Outlook.AppointmentItem olItem) { try { base.SetOutlookItemDuration(crmItem, olItem); olItem.Location = crmItem.GetValueAsString("location"); olItem.End = olItem.Start.AddMinutes(olItem.Duration); } catch (System.Exception any) { ErrorHandler.Handle("Failed while setting Outlook item duration", any); } }
private SyncState <Outlook.TaskItem> UpdateExistingOutlookItemFromCrm(EntryValue crmItem, SyncState <Outlook.TaskItem> syncState) { if (!syncState.IsDeletedInOutlook) { Outlook.TaskItem olItem = syncState.OutlookItem; if (olItem.IsValid()) { Outlook.UserProperty oProp = olItem.UserProperties[SyncStateManager.ModifiedDatePropertyName]; if (oProp.Value != crmItem.GetValueAsString("date_modified")) { SetOutlookItemPropertiesFromCrmItem(crmItem, olItem); } syncState.OModifiedDate = DateTime.ParseExact(crmItem.GetValueAsString("date_modified"), "yyyy-MM-dd HH:mm:ss", null); } else { Log.Error($"Attempting to update invalid Outlook item '{crmItem.GetValueAsString("name")}'"); } } return(syncState); }
protected override SyncState <Outlook.TaskItem> AddOrUpdateItemFromCrmToOutlook(Outlook.MAPIFolder tasksFolder, string crmType, EntryValue crmItem) { SyncState <Outlook.TaskItem> result = null; Log.Debug( $"TaskSyncing.AddOrUpdateItemFromCrmToOutlook\n\tSubject: {crmItem.GetValueAsString("name")}\n\tCurrent user id {RestAPIWrapper.GetUserId()}\n\tAssigned user id: {crmItem.GetValueAsString("assigned_user_id")}"); var syncState = SyncStateManager.Instance.GetExistingSyncState(crmItem) as SyncState <Outlook.TaskItem>; result = syncState == null?MaybeAddNewItemFromCrmToOutlook(tasksFolder, crmItem) : UpdateExistingOutlookItemFromCrm(crmItem, syncState); return(result); }
/// <summary> /// Add an item existing in CRM but not found in Outlook to Outlook. /// </summary> /// <param name="contactFolder">The Outlook folder in which the item should be stored.</param> /// <param name="crmItem">The CRM item from which values are to be taken.</param> /// <returns>A sync state object for the new item.</returns> private SyncState <Outlook.ContactItem> AddNewItemFromCrmToOutlook(Outlook.MAPIFolder contactFolder, EntryValue crmItem) { Log.Info( (string)string.Format( "ContactSyncing.AddNewItemFromCrmToOutlook, entry id is '{0}', creating in Outlook.", crmItem.GetValueAsString("id"))); Outlook.ContactItem olItem = contactFolder.Items.Add(Outlook.OlItemType.olContactItem); this.SetOutlookItemPropertiesFromCrmItem(crmItem, olItem); var newState = new ContactSyncState { OutlookItem = olItem, OModifiedDate = DateTime.ParseExact(crmItem.GetValueAsString("date_modified"), "yyyy-MM-dd HH:mm:ss", null), CrmEntryId = crmItem.GetValueAsString("id"), }; ItemsSyncState.Add(newState); LogItemAction(newState.OutlookItem, "AppointmentSyncing.AddNewItemFromCrmToOutlook, saved item"); return(newState); }
private SyncState <Outlook.TaskItem> AddNewItemFromCrmToOutlook(Outlook.MAPIFolder tasksFolder, EntryValue crmItem, DateTime?date_start, DateTime?date_due, string time_start, string time_due) { Outlook.TaskItem olItem = tasksFolder.Items.Add(Outlook.OlItemType.olTaskItem); TaskSyncState newState = null; try { this.SetOutlookItemPropertiesFromCrmItem(crmItem, date_start, date_due, time_start, time_due, olItem); newState = new TaskSyncState { OutlookItem = olItem, OModifiedDate = DateTime.ParseExact(crmItem.GetValueAsString("date_modified"), "yyyy-MM-dd HH:mm:ss", null), CrmEntryId = crmItem.GetValueAsString("id"), }; ItemsSyncState.Add(newState); } finally { this.SaveItem(olItem); } return(newState); }
/// <summary> /// Get the existing sync state for this CRM item, if it exists, else null. /// </summary> /// <param name="crmItem">The item.</param> /// <returns>The appropriate sync state, or null if none.</returns> public SyncState GetExistingSyncState(EntryValue crmItem) { SyncState result; string outlookId = crmItem.GetValueAsString("outlook_id"); CrmId crmId = CrmId.Get(crmItem.id); if (this.byCrmId.ContainsKey(crmId)) { result = this.byCrmId[crmId]; } else if (this.byOutlookId.ContainsKey(outlookId)) { result = this.byOutlookId[outlookId]; } else if (this.byGlobalId.ContainsKey(outlookId)) { result = this.byGlobalId[outlookId]; } else { string simulatedGlobalId = SyncStateManager.SimulateGlobalId(crmId); if (this.byGlobalId.ContainsKey(simulatedGlobalId)) { result = this.byGlobalId[simulatedGlobalId]; } else { string distinctFields = GetDistinctFields(crmItem); if (string.IsNullOrEmpty(distinctFields)) { result = null; } else if (this.byDistinctFields.ContainsKey(distinctFields)) { result = this.byDistinctFields[distinctFields]; } else { result = null; } } } return(result); }
/// <summary> /// If a meeting was created in another Outlook we should NOT sync it with CRM because if we do we'll create /// duplicates. Only the Outlook which created it should sync it. /// </summary> /// <param name="folder">The folder to synchronise into.</param> /// <param name="crmType">The CRM type of the candidate item.</param> /// <param name="crmItem">The candidate item from CRM.</param> /// <returns>True if it's offered to us by CRM with its Outlook ID already populated.</returns> protected override bool ShouldAddOrUpdateItemFromCrmToOutlook(Outlook.MAPIFolder folder, string crmType, EntryValue crmItem) { var outlookId = crmItem.GetValueAsString("outlook_id"); /* we're good if it's a meeting... */ bool result = crmType == this.DefaultCrmModule; /* provided it doesn't already have an Outlook id */ result &= string.IsNullOrWhiteSpace(outlookId); /* and we're also good if it's an appointment; */ result |= crmType == AppointmentSyncing.AltCrmModule; /* and we're also good if we've already got it */ result |= (this.GetExistingSyncState(crmItem) != null); if (!result) { Log.Debug($"ShouldAddOrUpdateItemFromCrmToOutlook: not syncing meeting `{crmItem.GetValueAsString("name")}` as it appears to originate from another Outlook instance."); } return(result); }
/// <summary> /// Update a single appointment in the specified Outlook folder with changes from CRM, but /// only if its start date is fewer than five days in the past. /// </summary> /// <param name="folder">The folder to synchronise into.</param> /// <param name="crmType">The CRM type of the candidate item.</param> /// <param name="crmItem">The candidate item from CRM.</param> /// <returns>The synchronisation state of the item updated (if it was updated).</returns> protected override SyncState <Outlook.AppointmentItem> AddOrUpdateItemFromCrmToOutlook( Outlook.MAPIFolder folder, string crmType, EntryValue crmItem) { SyncState <Outlook.AppointmentItem> result = null; DateTime dateStart = crmItem.GetValueAsDateTime("date_start"); if (dateStart >= GetStartDate()) { /* search for the item among the sync states I already know about */ var syncState = this.GetExistingSyncState(crmItem); if (syncState == null) { /* check for howlaround */ var matches = this.FindMatches(crmItem); if (matches.Count == 0) { /* didn't find it, so add it to Outlook */ result = AddNewItemFromCrmToOutlook(folder, crmType, crmItem, dateStart); } else { this.Log.Warn($"Howlaround detected? Appointment '{crmItem.GetValueAsString("name")}' offered with id {crmItem.GetValueAsString("id")}, expected {matches[0].CrmEntryId}, {matches.Count} duplicates"); } } else { /* found it, so update it from the CRM item */ result = UpdateExistingOutlookItemFromCrm(crmType, crmItem, dateStart, syncState); } result?.OutlookItem.Save(); // TODO TODO TODO TODO: pull and cache the recipients! } return(result); }
/// <summary> /// Item creation really ought to happen within the context of a lock, in order to prevent duplicate creation. /// </summary> /// <param name="tasksFolder">The folder in which the item should be created.</param> /// <param name="crmItem">The CRM item it will represent.</param> /// <returns>A syncstate whose Outlook item is the Outlook item representing this crmItem.</returns> private SyncState <Outlook.TaskItem> MaybeAddNewItemFromCrmToOutlook(Outlook.MAPIFolder tasksFolder, EntryValue crmItem) { SyncState <Outlook.TaskItem> result; lock (creationLock) { /* check for howlaround */ var matches = this.FindMatches(crmItem); if (matches.Count == 0) { /* didn't find it, so add it to Outlook */ result = AddNewItemFromCrmToOutlook(tasksFolder, crmItem); } else { this.Log.Warn($"Howlaround detected? Task '{crmItem.GetValueAsString("name")}' offered with id {crmItem.GetValueAsString("id")}, expected {matches[0].CrmEntryId}, {matches.Count} duplicates"); result = matches[0]; } } return(result); }
/// <summary> /// Update an existing Outlook item with values taken from a corresponding CRM item. Note that /// this just overwrites all values in the Outlook item. /// </summary> /// <param name="crmType">The CRM type of the item from which values are to be taken.</param> /// <param name="crmItem">The CRM item from which values are to be taken.</param> /// <param name="date_start">The state date/time of the item, adjusted for timezone.</param> /// <param name="syncState">The outlook item assumed to correspond with the CRM item.</param> /// <returns>An appropriate sync state.</returns> private SyncState <Outlook.AppointmentItem> UpdateExistingOutlookItemFromCrm( string crmType, EntryValue crmItem, DateTime date_start, SyncState <Outlook.AppointmentItem> syncState) { LogItemAction(syncState.OutlookItem, "AppointmentSyncing.UpdateExistingOutlookItemFromCrm"); if (!syncState.IsDeletedInOutlook) { Outlook.AppointmentItem olItem = syncState.OutlookItem; Outlook.UserProperty olPropertyModifiedDate = olItem.UserProperties[ModifiedDatePropertyName]; if (olPropertyModifiedDate.Value != crmItem.GetValueAsString("date_modified")) { try { olItem.Subject = crmItem.GetValueAsString("name"); olItem.Body = crmItem.GetValueAsString("description"); if (!string.IsNullOrWhiteSpace(crmItem.GetValueAsString("date_start"))) { UpdateOutlookStartAndDuration(crmType, crmItem, date_start, olItem); } EnsureSynchronisationPropertiesForOutlookItem(olItem, crmItem, crmType); LogItemAction(syncState.OutlookItem, "AppointmentSyncing.UpdateExistingOutlookItemFromCrm, item saved"); } finally { this.SaveItem(olItem); } } Log.Warn((string)("Not default dResult.date_modified= " + crmItem.GetValueAsString("date_modified"))); syncState.OModifiedDate = DateTime.ParseExact(crmItem.GetValueAsString("date_modified"), "yyyy-MM-dd HH:mm:ss", null); } return(syncState); }
/// <summary> /// Get a string representing the values of the distinct fields of this crmItem, /// as a final fallback for identifying an otherwise unidentifiable object. /// </summary> /// <param name="crmItem">An item received from CRM.</param> /// <returns>An identifying string.</returns> /// <see cref="SyncState{ItemType}.IdentifyingFields"/> internal static string GetDistinctFields(EntryValue crmItem) { return($"subject: '{crmItem.GetValueAsString("name")}'; start: '{crmItem.GetValueAsDateTime("date_start")}'"); }
/// <summary> /// Specialisation: in addition to the standard properties, meetings also require an organiser property. /// </summary> /// <param name="olItem">The Outlook item.</param> /// <param name="crmItem">The CRM item.</param> /// <param name="type">The value for the SType property (CRM module name).</param> protected override void EnsureSynchronisationPropertiesForOutlookItem(Outlook.AppointmentItem olItem, EntryValue crmItem, string type) { base.EnsureSynchronisationPropertiesForOutlookItem(olItem, crmItem, type); if (this.DefaultCrmModule.Equals(type)) { this.EnsureSynchronisationPropertyForOutlookItem(olItem, OrganiserPropertyName, crmItem.GetValueAsString("assigned_user_id")); } }
/// <summary> /// Set all those properties of this outlook item whose values are different from the /// equivalent values on this CRM item. Update the synchronisation properties only if some /// other property has actually changed. /// </summary> /// <param name="crmItem">The CRM item from which to take values.</param> /// <param name="olItem">The Outlook item into which to insert values.</param> /// <returns>true if anything was changed.</returns> private void SetOutlookItemPropertiesFromCrmItem(EntryValue crmItem, Outlook.ContactItem olItem) { try { olItem.FirstName = crmItem.GetValueAsString("first_name"); olItem.LastName = crmItem.GetValueAsString("last_name"); olItem.Email1Address = crmItem.GetValueAsString("email1"); olItem.BusinessTelephoneNumber = crmItem.GetValueAsString("phone_work"); olItem.HomeTelephoneNumber = crmItem.GetValueAsString("phone_home"); olItem.MobileTelephoneNumber = crmItem.GetValueAsString("phone_mobile"); olItem.JobTitle = crmItem.GetValueAsString("title"); olItem.Department = crmItem.GetValueAsString("department"); olItem.BusinessAddressCity = crmItem.GetValueAsString("primary_address_city"); olItem.BusinessAddressCountry = crmItem.GetValueAsString("primary_address_country"); olItem.BusinessAddressPostalCode = crmItem.GetValueAsString("primary_address_postalcode"); olItem.BusinessAddressState = crmItem.GetValueAsString("primary_address_state"); olItem.BusinessAddressStreet = crmItem.GetValueAsString("primary_address_street"); olItem.Body = crmItem.GetValueAsString("description"); if (crmItem.GetValue("account_name") != null) { olItem.Account = crmItem.GetValueAsString("account_name"); olItem.CompanyName = crmItem.GetValueAsString("account_name"); } olItem.BusinessFaxNumber = crmItem.GetValueAsString("phone_fax"); olItem.Title = crmItem.GetValueAsString("salutation"); if (olItem.Sensitivity != Outlook.OlSensitivity.olNormal) { Log.Info($"ContactSyncing.SetOutlookItemPropertiesFromCrmItem: setting sensitivity of contact {crmItem.GetValueAsString("first_name")} {crmItem.GetValueAsString("last_name")} ({crmItem.GetValueAsString("email1")}) to normal"); olItem.Sensitivity = Outlook.OlSensitivity.olNormal; } EnsureSynchronisationPropertiesForOutlookItem( olItem, crmItem.GetValueAsString("date_modified"), crmItem.GetValueAsString("sync_contact"), CrmId.Get(crmItem.id)); } finally { this.SaveItem(olItem); } }
protected override bool IsMatch(Outlook.ContactItem olItem, EntryValue crmItem) { return(olItem.FirstName == crmItem.GetValueAsString("first_name") && olItem.LastName == crmItem.GetValueAsString("last_name") && olItem.Email1Address == crmItem.GetValueAsString("email1")); }