Разработка
05
Окт
11

CRM Add’он для Word’а

Сегодня нарисуем маленьнький Add’он для текстового редактора MS Word 2007. Представлять он из себя будет панель, плавующую или пристыкованную к одной из сторон окна Word’а. Эта панель будет выбирать из CRM список Контактов (по началу их fullname’а) и выводить на самой панели в ниспадающем списке. Когда пользователь выбирет запись какого-либо Контакта в ниспадающем списке и щелкнет по кнопке, его адресные данные подтянуться на страницу Word’а.

Приступим:

  • Откройте Visual Studio 2008 и создайте новый проект. Среди шаблонов выберите Visual C# — Office — 2007 Add-ins — Word Add-in;
  • Добавьте в проект ссылку на System.Web.Services: правой кнопкой по References (в Solution Explorer) — Add Reference – на вкладке .Net выберите System.Web.Services — Ок;
  • А также ссылки на сборки SDK: правой кнопкой по References — Add Reference – перейдите к вкладке Browse и укажите путь к сборкам microsoft.crm.sdk.dll и microsoft.crm.sdktypeproxy.dll — Ок;
  • Добавим саму «форму» будущего аддона: щелкните правой кнопкой по заголовку проекта в Solution Explorer — Add — New Item — среди шаблонов выберите User Control — Ок;
  • После добавления формы VS переключится на ее редактирование в режиме дизайнера. Перетащите на форму из Toolbox’а следующие элементы:
    • Два Labels: один со значением «Поиск по имени», а второй со значение «Контакты»;
    • Две кнопки: у одной надпись «Поиск» и имя «btnSearchGo», а у второй надпись «Добавить адрес» и имя «btnInsertAddress»;
    • Один TextBox с именем «txtSearchText»;
    • Один ComboBox с именем «cbxSearchResults» и свойством DropDownStyle равным DropDownList;

    Сгруппируйте (визуально на форме) первую надпись с TextBox’ом и первой кнопкой, и, соответственно, вторую надпись с ComboBox’ом и второй кнопкой;




  • Переключитесь на код файла UserControl1.cs (по кнопке вверху в Solution Explorer) и замените кода на такой (у Вас возможно будет отличаться название проекта, поэтому исправьте его):
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    
    using Microsoft.Crm.Sdk;
    using Microsoft.Crm.SdkTypeProxy;
    using Microsoft.Crm.Sdk.Query;
    using Word = Microsoft.Office.Interop.Word;
    
    
    namespace WordAddIn2
    {
        public partial class UserControl1 : UserControl
        {
            private Word.Range myRange;
    
            public UserControl1()
            {
                InitializeComponent();
    
                myRange = null;
            }
    
            // По нажатию кнопки btnInsertAddress, вставляем адрес Контакта, выбранного в ComboBox'е на страницу
            private void btnInsertAddress_Click(object sender, EventArgs e)
            {
                myRange = Globals.ThisAddIn.Application.ActiveDocument.Content;
                try
                {
                    string selectedAddress = ((contact)cbxSearchResults.SelectedItem).fullname + "\n";
                    selectedAddress += ((contact)cbxSearchResults.SelectedItem).address1_line1 + "\n";
                    selectedAddress += ((contact)cbxSearchResults.SelectedItem).address1_city + ", ";
                    selectedAddress += ((contact)cbxSearchResults.SelectedItem).address1_stateorprovince + " ";
                    selectedAddress += ((contact)cbxSearchResults.SelectedItem).address1_postalcode;
                    myRange.InsertAfter(selectedAddress);
                }
                catch
                {
                    MessageBox.Show("Адрес выбранного Контакта не может быть добавлен в документ");
                }
            }
    
            // Rо нажатию кнопки btnSearchGo, производим поиск всех Контактов начинающихся с букв введенных в TextBox'е и принадлежащих текущему пользователю
            private void btnSearchGo_Click(object sender, EventArgs e)
            {
                // очищаем ComboBox
                cbxSearchResults.Items.Clear();
    
                // Подключаемся к CRM сервису
                CrmAuthenticationToken token = new CrmAuthenticationToken();
                token.OrganizationName = "superfirma";
                CrmService crmService = new CrmService();
                crmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
                crmService.Url = "http://win-n22hj23d1b1/MSCrmServices/2007/CrmService.asmx";
                crmService.CrmAuthenticationTokenValue = token;
    
                // Запрашиваем в CRM GUID пользователя
                WhoAmIRequest userRequest = new WhoAmIRequest();
                WhoAmIResponse user = (WhoAmIResponse)crmService.Execute(userRequest);
                Guid UserId = user.UserId;
    
                // Задаем поля, которые требуется вернуть
                string[] attributes = new string[] {
                    "contactid",
                    "fullname",
                    "address1_line1",
                    "address1_city",
                    "address1_stateorprovince",
                    "address1_postalcode"
                };
                ColumnSet cols = new ColumnSet(attributes);
    
                // Задаем условие: вернуть только Контакты принадлежащие текущему пользователю, у которых fullname начинается с символов введенных в TextBox'е
                ConditionExpression conditionPrincipal = new ConditionExpression();
                conditionPrincipal.AttributeName = "ownerid";
                conditionPrincipal.Operator = ConditionOperator.Equal;
                conditionPrincipal.Values = new object[1];
                conditionPrincipal.Values[0] = UserId;
                ConditionExpression conditionFullname = new ConditionExpression();
                conditionFullname.AttributeName = "fullname";
                conditionFullname.Operator = ConditionOperator.Like;
                conditionFullname.Values = new object[1];
                conditionFullname.Values[0] = txtSearchText.Text + "%";
                FilterExpression filterPrincipal = new FilterExpression();
                filterPrincipal.FilterOperator = LogicalOperator.And;
                filterPrincipal.Conditions.Add(conditionPrincipal);
                filterPrincipal.Conditions.Add(conditionFullname);
    
                // Создаем запрос
                QueryExpression query = new QueryExpression();
                query.EntityName = EntityName.contact.ToString();
                query.ColumnSet = cols;
    
                // Отправляем запрос в CRM и получаем ответ
                BusinessEntityCollection retrievedContacts = crmService.RetrieveMultiple(query);
    
                // Просматриваем полученный ответ и заполняем им ComboBox
                for (int i = 0; i < retrievedContacts.BusinessEntities.Count; i++)
                {
                    cbxSearchResults.Items.Add(((contact)retrievedContacts.BusinessEntities[i]));
                }
                // Определяем атрибут который будет отображаться в ComboBox'е
                cbxSearchResults.DisplayMember = "fullname";
            }
        }
    }
    

    Что тут происходит:

    • Здесь присутствуют два обработчика: первый реагирует на щелчок кнопки под полем поиска, а второй на щелчок кнопки под ниспадающим списком;
    • Код в первом обработчике:
      • Очищает ниспадающий список;
      • Подключается к веб-сервису CRM;
      • Выполняет запрос WhoAmIRequest, чтобы определить текущего пользователя (и запоминет его GUID);
      • Формирует запрос Контактов в CRM, в которых fullnamу начинается с символов введенных в текстовое поле и принадлежащих текущему пользователю;
      • Выполняет запрос Контактов и полученный ответ заносит в ниспадающий список (а выводит в нем толлько их fullname).
    • Код второго обработчика вытаскивает данные адреса из Контакта, выбранного в ниспадающем списке и вставляет их в документ Word.
  • Откройте код файла ThisAddin.cs и добавьте такую строчку в начало класса ThisAddIn:
    private Microsoft.Office.Tools.CustomTaskPane ctpContact = null;
    

    А такие строки внутрь функции ThisAddIn_Startup:

    ctpContact = this.CustomTaskPanes.Add(new UserControl1(), "Адрес Контактов");
    ctpContact.Visible = true;
    

    И, наконец, такой код в функцию ThisAddIn_Shutdown:

    this.CustomTaskPanes.Remove(ctpContact);
    
  • Откройте снова ContactsControl.cs в режиме дизайна и для кнопок назначьте для событий click соответствующие им обработчики. Для этого выделите кнопку, на панели Properties перейдите к представлению Events (щелкните по кнопке «молнии») и для действия click из ниспадающего списка выберите обработчик (для кнопки btnSearchGo это btnSearchGo_Click, а для btnInsertAddress это btnInsertAddress_Click);
  • Готово! Тестируем! Запустите приложение в режиме отладки: Debug — Start Debugging. Откроется Word с нашей открытой панелью. Введите в поле поиска начало названия какого-либо Контакта — все найденные Контакты отобразятся в ниспадающем списке — выберите какой-либо из них и щелкните по кнопке Добавить адрес — адресные данные этого Контакта будут вставлены в документ Word. А чтобы эта панелька постоянно появлялась при открытии Word’а, необходимо скомпилированную сборку проекта (обычно из папки <Папка проекта>\bin\Debug) поместить в папку C:\Program Files\Microsoft Office\Office12\ADDINS (начало пути может отличаться – заваист от параметров установки офиса).




Комментарии (11)
  • Дмитрий 05.10.2010

    Добрый день!

    помогите плиз решить задачу с маппингом хмл данных в ворд.
    из срм данные о заказе приходят в xml и необходимо их сохранить в ворд. в ворде существует возможность использования xml схем, только вот непонятно как это сделать из кода

  • slivka_83 05.10.2010

    Добрый день…

    Неесколько вопросов:
    >маппингом хмл данных в ворд
    Что такое маппинг полей CSV файла и атрибутов CRM мне понятно, а что такое «маппингом хмл данных в ворд» не очень 🙂

    >из срм данные о заказе приходят в xml
    Куда приходят?

    >и необходимо их необходимо их сохранить в ворд
    Т.е. преобразовать формат XML в формат DOC(X)?

    >в ворде существует возможность использования xml схем, только вот непонятно как это сделать из кода.
    Что значит возможность использования? Вы имеете ввиду что Word умеет открывать файлы с расширением XML? И о каком коде речь?

  • Дмитрий 05.10.2010

    привет 😉
    в worde на вкладке Разработчик, есть возможность подключать xml схемы. Подключив такую вот схему — появляется возможность вставлять структурированные данные в документ в заданных местах. Аналогичный функционал есть в ексел, и использовать его в последнем достаточно просто. С каждой книгой связан массив объектов XmlMaps, которому достаточно передать сформированный XmlDocument.
    К сожалению я так и не разобрался как использовать аналогичные возможности в ворде — пришлось использовать старый добрый способ search and replace 😉 а повторяющиеся данные (таблицы) создавать руками. Конечно все работает, но если есть встроенные возможности — хотелось бы использовать их

  • slivka_83 05.10.2010

    Добрый день 🙂
    Как-то приходилось встречать такую реализацию — к ворду подключалась какая-то надстройка, затем формировался документ Wors, в котором с помощью специальных инструкций вносилась разметка. Этот шаблон выкладывался на сервере CRM. Из CRM делалась ссылка на этот документ. После открытия шаблон он автоматом заполнялся данными с той карточки, с которой его открыли.
    Но технических подробностей реализации мне к сожалению не известно 🙁

  • Дмитрий 05.10.2010

    привет,
    не мудрствуя лукаво, задачу заполнения шаблонов ворд я решил следующим образом http://ficha.iqforyou.ru/2011/02/14/ms-dynamics-crm-%D1%84%D0%BE%D1%80%D0%BC%D0%B8%D1%80%D1%83%D0%B5%D0%BC-word-%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D1%8B-%D0%B8%D0%B7-ms-crm/

  • Сергей 05.10.2010

    А где функции ThisAddIn_Startup
    и ThisAddIn_Shutdown
    Можно показать весь код файла ThisAddin.cs с этими функциями

  • slivka_83 05.10.2010

    Добрый день 🙂 Извините что так долго 🙂 был немного занят 🙂
    Этот файли и эти функции создаются автоматом VS при создании проекта word’а 🙂

  • Taker 05.10.2010

    А не могли бы вы, расписать алгоритм заполнения шаблона Word данными из CRM 4.0 при нажатии на кнопку созданную для определенного тулбара. Генерация этого документа должна осуществляться с помощью OpenXML SDK. Создать шаблон не проблема по сути, не понятно как именно в код c# вставлять поля из CRM, сам синтаксис интересует. И что делать если надо вытащить в шаблон данные из лукапа? Еще трабла в том, чтобы сделать генерацию этого шаблона по кнопке, ведь в файле ISV.config указывается javascript и как запустить генерацию этого шаблона с помощью него не понятно. Заранее спасибо за помощь!

  • slivka_83 05.10.2010

    Можно попробовать…

    Только CRM 4.0 у меня уже давно нету. Поэтому эксперименты будт производиться на CRM 2011.

    И что Вас интересует? Excel или Word.
    И скорее всего пример будет не скоро… работы много 🙂 плюс я в отпуск ухожу…

  • Endymion 05.10.2010

    Вопрос немного не по теме. Сколько у вас ушло времени на реализацию этой задачи «CRM Add’он для Word’а»,а точнее на написание кода. Мне это нужно для корректной оценки моей скорости работы.

  • slivka_83 05.10.2010

    На вскидку, написание самого когда заняло часа 4.

*

code