Разработка
01
Май
11

Программный рендеринг отчетов CRM

Как Вы знаете в Reporting Services присутствует функционал по автоматической рассылке отчетов по расписанию. Одним из его недостатков является невозможность динамически передавать в формируемые отчеты какие-либо параметры. Но к счастью эти же самые отчеты можно формировать программно, используя веб-службу SSRS. Рассмотрим несколько вариантов (более-менее) жизненных вариантов ее использования…

Но прежде чем приступим создадим простой тестовы отчет и выполним кое-какие предпосылки для его программного рендеринга:

  1. Я не буду в этой статье вдаваться в детали разработки отчетов (для этого можете посмотреть эти посты: http://mmcrm.ru/?cat=25), скажу лишь что я создал небольшой отчет, который выводит в текстовых полях информацию об одном (каком-либо) Контакте. Этот отчет принимает два параметра: GUID Контакта, информацию о котором необходимо отобразить и Цвет (в виде любого HTML-цвета), в который необходимо покрасить заголовок отчета.
  2. Далее я этот отчет опубликовал в CRM. И тут нас поджидает первая «проблема», котороая состоит в том, что Вы видите только GUID’ы отчетов CRM в менеджере отчетов SSRS. Чтобы иметь возможность программно выполнить отчет по его CRM’ному имени, Вы должны опубликовать его для наружного применения. Для этого, выделите Ваш отчет в CRM и нажмите Изменить – откроется окно редактирования отчета – далее меню Действия – Опубликовать отчет для внешнего использования.
    Если Вы теперь откроете Менеджер отчетов SSRS (находится по такому пути http://<сервер_отчетов>/reports) и перейдете в папку Вашей организации, то Вы увидите только что опубликованный для наружного применения отчет (и любой другой который в нем используется). Теперь Вы можете использовать веб-службу SSRS для рендеринга этого отчета обратившись к нему по его имени.



Сохранение отчетов на жестком диске и отправка электронных писем с вложенными отчетами из CRM

В этом примере создадим плагин который будет делать две вещи:

  1. Сохранять нужный нам отчет (в сгенерированном виде) на жестком диске в формате PDF
  2. Создавать и отправлять из CRM электронной письмо с вложенным динамически сгенерированным отчетом

Приступим…

  • Создайте новый CRM-плагин;
  • Добавьте в проект web reference на веб-службу SSRS. Ссылка имеет такой вид:
    http://<сервер_отчетов>/ReportServer/ReportExecution2005.asmx

    Задайте для нее имя ReportService;

  • Добавьте в проект ссылки на SDK-сборки;
  • Подпишите плагин;
  • Поместите в плагин такой код:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Crm.Sdk;  
    using Microsoft.Crm.SdkTypeProxy; 
    using ClassLibrary2.ReportService;
    using System.Web.Services.Protocols;
    using System.IO;
    
    namespace ClassLibrary2
    {
        public class Class1: IPlugin  
        {
            public void Execute(IPluginExecutionContext context)
            {
                // Настраиваем Crm Service
                ICrmService service = context.CreateCrmService(true);
                
                // Получаем GUID обновяемой записи Контакта (информацию о которой будем выводить в отчете)
                DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties["Target"];  
                Key ContactId = (Key)entity.Properties["contactid"];
    
                byte[] result = null;
    
                // Настраиваем API Reporting Service
                ReportService.ReportExecutionService rs = new ReportExecutionService();
                //rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
                rs.Credentials = new System.Net.NetworkCredential("Administrator", "1qaz@WSX", "d2008");
                rs.Url = "http://win-n22hj23d1b1/ReportServer/ReportExecution2005.asmx";
    
                // Задаем служебные параметры для выполнения отчета:
                // 1. Путь к отчету (относительно Reporting Service)
                // 2. Выходной формат
                // 3. Информация об устройстве
                string reportPath = "/superfirma_MSCRM/КонтактИнфо";
                string format = "PDF";
                string historyID = null;
                string devInfo = @"<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>";
    
                // Передаем параметры в отчет
                ParameterValue[] parameters = new ParameterValue[2]; // Указываем сколько всего параметров
                parameters[0] = new ParameterValue();
                parameters[0].Name = "contactguid"; // Передаем GUID Контакта
                parameters[0].Value = ContactId.Value.ToString();
                parameters[1] = new ParameterValue();
                parameters[1].Name = "color"; // Передаем цвет заголовка отчета
                parameters[1].Value = "red";
    
                // Всякая служебная инфа
                DataSourceCredentials[] credentials = null;
                string showHideToggle = null;
                string encoding;
                string mimeType;
                string extension;
                Warning[] warnings = null;
                ParameterValue[] reportHistoryParameters = null;
                string[] streamIDs = null;
    
                ExecutionInfo execInfo = new ExecutionInfo();
                ExecutionHeader execHeader = new ExecutionHeader();
                rs.ExecutionHeaderValue = execHeader;
                execInfo = rs.LoadReport(reportPath, historyID);
                rs.SetExecutionParameters(parameters, "en-us");
                String SessionId = rs.ExecutionHeaderValue.ExecutionID;
    
                try
                {
                    // Генерируем отчет
                    result = rs.Render(format, devInfo, out extension, out encoding, out mimeType, out warnings, out streamIDs);
                }
                catch (SoapException err)
                {
                    throw new Exception(err.Detail.OuterXml);
                }
                // Записываем полученный отчет в файл
                try
                {
                    FileStream stream = File.Create(@"c:\test\КонтактИнфо.pdf", result.Length);
                    stream.Write(result, 0, result.Length);
                    stream.Close();
                }
                catch (Exception error)
                {
                    throw new Exception(error.Message);
                }
    
                // Отправление электронной почты с вложенным файлом отчета
                try
                {
                    // Создаем получателя электропочты
                    activityparty partyTo = new activityparty();
                    partyTo.partyid = new Lookup();
                    partyTo.partyid.type = EntityName.contact.ToString();
                    partyTo.partyid.Value = ContactId.Value;
    
                    // Создаем отправителя электропочты
                    activityparty partyFrom = new activityparty();
                    partyFrom.partyid = new Lookup();
                    partyFrom.partyid.type = EntityName.systemuser.ToString();
                    partyFrom.partyid.Value = context.UserId;
    
                    // Создаем запись Электронной почты
                    email Email = new email();
                    Email.subject = "Пример мыла";
                    Email.torecipients = ContactId.Value.ToString();
                    Email.description = "Текст сообщение.";
                    Email.to = new activityparty[] { partyTo };
                    Email.from = new activityparty[] { partyFrom };
                    Email.regardingobjectid = new Lookup();
                    Email.regardingobjectid.type = EntityName.contact.ToString();
                    Email.regardingobjectid.Value = ContactId.Value;
                    Guid EmailId = service.Create(Email);
    
                    // Добавляем к письму вложение
                    activitymimeattachment setupEmailAttachment = new activitymimeattachment();
                    setupEmailAttachment.subject = "Заголовок Вложения";
                    setupEmailAttachment.filename = "КонтактИнфо.pdf";
                    setupEmailAttachment.body = Convert.ToBase64String(result);
                    setupEmailAttachment.filesize = new CrmNumber(Convert.ToInt32(result.Length.ToString()));
                    setupEmailAttachment.mimetype = "text/plain";
                    setupEmailAttachment.attachmentnumber = new CrmNumber();
                    setupEmailAttachment.attachmentnumber.Value = 1;
                    setupEmailAttachment.activityid = new Lookup();
                    setupEmailAttachment.activityid.type = EntityName.email.ToString();
                    setupEmailAttachment.activityid.Value = EmailId;
                    service.Create(setupEmailAttachment);
    
                    // Отправляем письмо
                    SendEmailRequest req = new SendEmailRequest();
                    req.EmailId = EmailId;
                    req.TrackingToken = "";
                    req.IssueSend = true;
                    SendEmailResponse res = (SendEmailResponse)service.Execute(req);
                }
                catch (Exception error)
                {
                    throw new Exception(error.Message);
                }
            }
        }
    }
    

    Разбор полетов:

    • Сначала (как всегда 🙂 ) настраиваем Crm Service;
    • Из контекста плгина вытаскиваем GUID обновяемой записи Контакта;
    • Настраиваем подключение к API Reporting Service;
    • Настраиваем служебные параметры для рендеринга отчета:
      • Путь к отчету (относительно Reporting Service). Обычно имеет вид:
        /<название_организации>_MSCRM/<имя_отчета>
      • Выходной формат. В данном случае мы генерируем PDF файл (для этого указываем «PDF»), но Вы также можете получить отчет в других форматах:
        • MHT веб-архив – «MHTML»
        • XML файл – «XML»
        • CSV файл – «CSV»
        • Рисунок – «IMAGE»
        • EXCEL файл – «EXCEL»
        • HTML страница – «HTML4.0», «HTML3.2», «HTMLOWC»
    • Информация об устройстве – используется для передачи параметров подготовки к просмотру модуля подготовки отчетов. Более подробно смотрите тут: http://msdn.microsoft.com/ru-ru/library/ms155397.aspx
  • Определяем пользовательские параметры, которые будут переданы в отчет (в данном случае это GUID Контакта и цвет заголовка отчета);
  • Далее настраиваем всякую служебную инфу…
  • Ну и наконец рендерим сам отчет;
  • Далее отрендеренный отчет записываем в файл…
  • … а также приаттачиваем к (тут же) созданному письму (которое затем и отправляем);
  • Соберите плагин и зарегистрируйте в CRM на событие Update объекта Contact;
  • Тестируем: откройте какую-нибудь запись Контакта, обновите ее и идите смотреть на созданный на жестком диске файл отчета и тот же файл приаттаченный к созданному письму.





Возвратить отчет в виде файла порльзователю

А сейчас реализуем такую вещь: выдадим пользователю диалоговое окно предлагающее сохранить отчет. Т.е. открывает, например, юзвер запись Контакта, а ему диалог с предложение сохранить ии открыть отчет. Что-то подобное мы уже реализовывали (Выгрузка/загрузка файлов из CRM) – осталось только прикрутить к этому решению рендеринг отчетов…

  • Создайте в Visual Studio новый веб-сайт (ASP.Net Web Site) и добавьте в него ссылки на web-сервис Reporting Services и SDK-сборки;
  • Добавьте в файл *.aspx.cs такой код:
    using System;
    using System.Configuration;
    using System.Data;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Net;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using System.Web.Services.Protocols;
    using Microsoft.Win32;
    using Microsoft.Crm.Sdk;
    using Microsoft.Crm.Sdk.Query;
    using Microsoft.Crm.SdkTypeProxy;
    using ReportService;
    
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                // Выполняем код под юзвером открывашем страницу
                using (new CrmImpersonator())
                {
                    // Проверяем что через URL передан параметр guid, иначе прекращаем выполнение
                    if (Request.QueryString["contactguid"] == null || Request.QueryString["color"] == null) return;
    
                    byte[] result = null;
    
                    // Настраиваем API Reporting Service
                    ReportService.ReportExecutionService rs = new ReportExecutionService();
                    //rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
                    rs.Credentials = new System.Net.NetworkCredential("Administrator", "1qaz@WSX", "d2008");
                    rs.Url = "http://win-n22hj23d1b1/ReportServer/ReportExecution2005.asmx";
    
                    // Задаем параметры для выполнения отчета: путь к отчету (относительно Reporting Service), выходной формат и информация об устройстве
                    string reportPath = "/superfirma_MSCRM/КонтактИнфо";
                    string format = "PDF";
                    string historyID = null;
                    string devInfo = @"<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>";
    
                    // Передаем параметры в отчет
                    ParameterValue[] parameters = new ParameterValue[2]; // Общее количество параметров
                    parameters[0] = new ParameterValue();
                    parameters[0].Name = "contactguid";
                    parameters[0].Value = Request.QueryString["contactguid"]; // Выцепляем GUID Контакта из строки URL
                    parameters[1] = new ParameterValue();
                    parameters[1].Name = "color";
                    parameters[1].Value = Request.QueryString["color"]; // Выцепляем цвет заголовка отчета из строки URL
    
                    // Всякая служебная инфа
                    DataSourceCredentials[] credentials = null;
                    string showHideToggle = null;
                    string encoding;
                    string mimeType;
                    string extension;
                    Warning[] warnings = null;
                    ParameterValue[] reportHistoryParameters = null;
                    string[] streamIDs = null;
    
                    ExecutionInfo execInfo = new ExecutionInfo();
                    ExecutionHeader execHeader = new ExecutionHeader();
                    rs.ExecutionHeaderValue = execHeader;
                    execInfo = rs.LoadReport(reportPath, historyID);
                    rs.SetExecutionParameters(parameters, "en-us");
                    String SessionId = rs.ExecutionHeaderValue.ExecutionID;
    
                    try
                    {
                        // Генерируем отчет
                        result = rs.Render(format, devInfo, out extension, out encoding, out mimeType, out warnings, out streamIDs);
                    }
                    catch (SoapException err)
                    {
                        throw new Exception(err.Detail.OuterXml);
                    }
    
                    // Обнуляем изначальный ответ и возвращяем ответ в виде файла
                    string filename = "КонтактИнфо.pdf";
                    Response.ClearContent();
                    Response.AddHeader("content-disposition", "attachment; filename=" + filename);
                    Response.ContentType = "text/plain";
                    Response.HeaderEncoding = Encoding.Default;
                    Response.BinaryWrite(result);
                    Response.End();
                }
            }
            catch (SoapException sexc)
            {
                throw new Exception(sexc.Detail.InnerText);
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
    }
    

    Код в в плане работы с RS повторяет предыдущий пример, только инормацию о пользовательских параметрах берет из строки URL. А затем формирует отчет и возвращает его в виде бинарного файла пользователю;

  • Код в web.config’е замените на такой:
    <?xml version="1.0"?>
    	<configuration>
    		<appSettings>
    			<add key="ReportService.ReportExecution2005" value="http://win-n22hj23d1b1/ReportServer/ReportExecution2005.asmx"/>
    		</appSettings>
    	<connectionStrings/>
    	<system.web>
    		<httpModules>
    			<add name="MapOrg" type="Microsoft.Crm.MapOrgEngine, Microsoft.Crm, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    			<add name="CrmAuthentication" type="Microsoft.Crm.Authentication.AuthenticationEngine, Microsoft.Crm, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    		</httpModules>
    		<identity impersonate="true"/>
    		<compilation debug="true">
    			<assemblies>
    				<add assembly="Microsoft.Crm.Sdk, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    				<add assembly="Microsoft.Crm.SdkTypeProxy, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    			</assemblies>
    		</compilation>
    	</system.web>
    </configuration>
    
  • Опубликуйте отчет в папке /ISV/dlreport/ сата CRM (для этого в Visual Studio перейдите Build — Publish Web Site — в диалоговом окне укажите путь к созданной папке и жмите ОК);
  • Чтобы отчет автоматически скачивался при загрузке формы поместите на onLoad формы Контакта такой JavaScript-код:
    if (crmForm.FormType == 2) {
    	var url= window.location.protocol + "//" + window.location.host + "//ISV//dlreport//Default.aspx?" + crmForm.ObjectId + "&color=blue";  
    	window.open(url,'popup','width=100,height=50,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=100,top=200');
    }
    

    Этот код проверет является ли данная форма, формой обновления записи (иначе у записи не будет GUID’а), а затем формирует строку URL к созданной ASPX-страничке и открывает ее в новом окне. В результате чего юзвер получает свой отчет 🙂

  • Ну и для тестирования просто откройте какую-либо запись контакта…



Лирическое отступление

  1. В случае плагинов, бизнес-пртнеров и ASPX-страниц операция сохранения файла на диск будет использовать учетные данные пула приложения CRM;
  2. В примере я использую подключение к веб-сервису под админом, но для того, чтобы юзверы CRM не могли получить данные к которым у них нет доступа, необходимо подключатся под Default credentials;
  3. Полную документацию по веб-службе SSRS Вы можете посмотреть в MSDN’е: http://msdn.microsoft.com/ru-ru/library/ms152787(v=SQL.90).aspx
Комментарии (11)
  • nasikosha 01.05.2010

    не могу найти ссылку на сервис ReportingService. Где мне его можно найти или скачать?
    Заранее спасибо за ответ.

  • slivka_83 01.05.2010

    Если речь идет об установке самого Reporting Services, то он устанавливается как компонент SQL Server’а 🙂

  • nasikosha 01.05.2010

    а если нету?т.е не установился

  • nasikosha 01.05.2010

    и еще e меня почему-то не может найти класс ReportingService

  • nasikosha 01.05.2010

    Sorry не ReportingService a ReportService

  • slivka_83 01.05.2010

    Значит нужно установить! 🙂

    А класс появится после того как Вы добавите ссылку на веб-сервис (как показано выше в статье)!

  • nasikosha 01.05.2010

    А вы не скинете ссылку,откуда смогу установить его.

  • nasikosha 01.05.2010

    и чтот за
    007 using ClassLibrary2.ReportService;
    что это за сборка и для чего она?

  • slivka_83 01.05.2010

    Ну установить Вы сможете с дистрибутива SQL Server. Причем Вам нужен точно такой же какй у Вас установле! Иначе ничего не полуиться 🙂

    using ClassLibrary2.ReportService;
    Это не сборка а ссылка на веб-сервис Reporting Service’а 🙂

  • real life 01.05.2010

    Microsoft.Crm.MapOrgEngine, Microsoft.Crm.Authentication.AuthenticationEngine
    а где я могу найти эти библиотеки?
    скачивала Microsoft Dynamics Crm 4.0 но там нет их.

  • slivka_83 01.05.2010

    Либо в папке bin сайта CRM, либо в GAC’е. И если не ошибаюсь это одна сборка — Microsoft.Crm. А MapOrgEngine и Authentication это пространства имен или калыссы в них.

*

code