Разработка
21
Июл
0

Массовое удаление записей в CRM

Если Вам приходилось когда-либо удалять записи в огромных количествах… ну, например, под 100 000 🙂 То Вы должны быть значкомы с утилитой Bulk Delete Launcher. Утилита вобщем то хорошая, но помоему излишняя, т.к. для решения это задачи вполне можно обойтись и веб-компонентами!

Итак, что нам требуется: удалить все записи представления по нажатию кнопки на панели инструментов этого представления.

В чем заключается решение? Оно состоит из двух компонентов:

  1. Кнопки над представлением, которая при нажатии будет выцеплять из представления его GUID и отравлять кастомной ASPX-страницы. Которая в свою очередь будет возвращать GUID созданной операции массового удаления записей. И в последнюю очередь, будем открывать карточку записи массового удаления записей;
  2. Кастомной ASPX-странички, которая будет получать GUID представления. C помощью этого GUID’а она будет вытаскивать Fetch-запрос соответствующий этому представленияю и на основе его создавать новую операцию группового удаления записей. После чего возвращать GUID этой операции.

Приступим:

ASPX-страничка

  • Создайте в Visual Studio новую ASPX-страницу;
  • Переименуте default.aspx в bulkdelete.aspx и удалитеиз нее весь контент кроме первой строчки:
    <%@ Page Language="C#" AutoEventWireup="true"  CodeFile="bulkdelete.aspx.cs" Inherits="_Default" %>
    
  • Подключите к странице web-сервис CRM (http://<servername:port>/mscrmservices/2007/crmservice.asmx,с именем CrmSdk);
  • Добавьте на страницу bulkdelete.aspx такой код:
    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using CrmSdk;
    
    public partial class _Default : System.Web.UI.Page 
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                // Получаем GUID Электронной почты из строки запроса
                if (Request.QueryString["viewid"] == null) return;
                string viewidId = Request.QueryString["viewid"].Replace("{", "").Replace("}", "");
    
                // Конфигурируем CrmService
                CrmAuthenticationToken token = new CrmAuthenticationToken();
                token.AuthenticationType = 0;
                token.OrganizationName = "superfirma";
                CrmService service = new CrmService();
                service.Url = "http://win-n22hj23d1b1/mscrmservices/2007/crmservice.asmx";
                service.CrmAuthenticationTokenValue = token;
                service.Credentials = System.Net.CredentialCache.DefaultCredentials;
    
                // Вытаскиваем fetch-запрос представления
                ColumnSet cols = new ColumnSet();
                cols.Attributes = new string[] { "fetchxml", "name" };
                Guid savedqueryGuid = new Guid(viewidId);
    
                savedquery savedquery = (savedquery)service.Retrieve("savedquery", savedqueryGuid, cols);
    
                // Конвертируем fetch в QueryExpression
                FetchXmlToQueryExpressionRequest fetch = new FetchXmlToQueryExpressionRequest();
                fetch.FetchXml = savedquery.fetchxml;
                FetchXmlToQueryExpressionResponse qe = (FetchXmlToQueryExpressionResponse)service.Execute(fetch);
    
                // Создаем операцию по массовому удалению записи
                Guid[] emptyRecipients = new Guid[0];
                BulkDeleteRequest request = new BulkDeleteRequest();
                request.JobName = "Удаление всех записей из представления \"" + savedquery.name + "\"";
                request.QuerySet = new QueryBase[] { qe.Query };
                request.ToRecipients = emptyRecipients;
                request.CCRecipients = emptyRecipients;
                request.SendEmailNotification = false;
                request.RecurrencePattern = string.Empty;
                CrmDateTime crmdateTime = new CrmDateTime();
                DateTime dateTime = DateTime.Now;
                crmdateTime.Value = dateTime.ToString(string.Format("yyyy-MM-ddTHH:mm:ss"));
                request.StartDateTime = crmdateTime;
    
                BulkDeleteResponse response = (BulkDeleteResponse)service.Execute(request);
    
                // Запрашиваем в CRM GUID операции массовго удаления записей
                QueryByAttribute bulkQuery = new QueryByAttribute();
                bulkQuery.ColumnSet = new AllColumns();
                bulkQuery.EntityName = EntityName.bulkdeleteoperation.ToString();
                bulkQuery.Attributes = new string[] { "asyncoperationid" };
                bulkQuery.Values = new object[1];
                bulkQuery.Values[0] = response.JobId;
    
                BusinessEntityCollection bulkEntityCollection = service.RetrieveMultiple(bulkQuery);
    
                // Смотрим на возвращенный результат:
                // Если он равен нулю (опреация осинхронная и запись создается не сразу), то ждем секунду и повторяем запрос (и так до 60 раз)
                // Если запрос содержит данные вытаскиваем их в объект bulkdeleteoperation
                bulkdeleteoperation bdo = null;
                const int ARBITRARY_MAX_POLLING_TIME = 60;
                int secondsTicker = ARBITRARY_MAX_POLLING_TIME;
                while (secondsTicker > 0)
                {
                    if (bulkEntityCollection.BusinessEntities.Length > 0)
                    {
                        bdo = (bulkdeleteoperation)bulkEntityCollection.BusinessEntities[0]; // Вытаскиваем запись из массива
                        secondsTicker = 0; // Обнуляем счетчик
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(1000); // Ждем секунду
                        secondsTicker--; // Уменьшаем счетчик
    
                        bulkEntityCollection = service.RetrieveMultiple(bulkQuery); // Повторяем запрос
                    }
                }
    
                // Возвращаем GUID записи групового удаления данных
                Response.Write(bdo.bulkdeleteoperationid.Value.ToString());
            }
            catch
            {
                // В случаи какой-либо ошибки возвращаем кодовое слово "Ошибка"
                Response.Write("Ошибка");
            }  
        }
    }
    

    Этот код делает следующее:

    • Вытаскивает из строки запроса GUID представления;
    • Подключается к CRM-сервису (соответственно, поменяйте настройки на Ваши);
    • Вытаскиваем из объекта savedquery Fetch-запрос представления (GUID объекта savedquery равен GUID’у отображаемого представления);
    • Конвертируем полученный Fetch-запрос в QueryExpression, т.к. именно этот тип выборки понимает операция групового удаления записей;
    • Создаем новую операцию массового удаления записей. Обратите внимаение, что в этом примере я не рассылаю уведомления о завершении ее работы, но Вы можете это изменить, если Вам требуется. Также обратите внимание, что я не задал цикличность повторения этой операции. делается это примерно так:
      request.RecurrencePattern = "FREQ=DAILY;INTERVAL=3;";

      В даннном случаи задается интервал повторения «каждые три дня». Более подробно об этом смотрите на MSDN: http://msdn.microsoft.com/en-us/library/cc189846.aspx

    • Далее запрашиваем в CRM GUID созданной операции группового удаления данных. Т.к. эта операция асинхронная, то она создастся не сразу, поэтому требуется выполнить несколько запросов с некоторым интервалом (в данном случаи в 1 секунду), пока она не будет создана;
    • Возвращаем полученый GUID (если же произошла ошибка, то возвращаем ключевое слово «Ошибка»);
  • В web.config поместите «стандартный» код:
    <?xml version="1.0"?>
    <configuration>
    	<appSettings>
    	<add key="CrmSdk.CrmServiceWsdl" value="http://win-n22hj23d1b1/MSCrmServices/2007/CrmService.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\bulkdelete (предварительно создав ее) сайта CRM;

ISV.Config

  • Выгрузите из CRM ISV.Config и откройте его в каком-нибудь текстовом редакторе;
  • Добавьте на панель инструментов нужного Вам представления (в данном случаи это Контакт) кнопку, содержащую такой JS-код:
    <Grid>
    	<MenuBar>
    		<Buttons>
    			<Button Icon="/_imgs/ico_16_5010.gif" JavaScript="
    				// Получаем GUID представления
    				var viewid = document.getElementById('viewid').getAttribute('value');
    
    				// Отправляем асинхронный запрос в страницу bulkdelete.aspx и передаем ей GUID текущего представления
    				var forwardPath = '/isv/bulkdelete/bulkdelete.aspx?viewid=' + viewid;
    				var objXMLHTTP = new ActiveXObject('Microsoft.XMLHTTP');
    				objXMLHTTP.Open('POST',forwardPath,false);
    				objXMLHTTP.Send('');
                      
    				// Получаем ответ
    				var response = objXMLHTTP.ResponseText;
    
    				// Если ответ не пустой и не равен 'Ошибка', то...
    				if (response != null &amp;&amp; response.length != 0 &amp;&amp; response != 'Ошибка') {
    					// Открываем форму оюъекта Массового удаления записей
    					var width  = 900;
    					var height = 600;
    					var left   = (screen.width  - width)/2;
    					var top    = (screen.height - height)/2;
    					var params = 'width='+width+', height='+height;
                        params += ', top='+top+', left='+left;
                        params += ', directories=no';
                        params += ', location=no';
                        params += ', menubar=no';
                        params += ', resizable=yes';
                        params += ', scrollbars=no';
                        params += ', status=yes';
                        params += ', toolbar=no';
                        window.open('http://win-n22hj23d1b1/superfirma/tools/bulkdelete/edit.aspx?id={' + response + '}' ,'', params);
                      } else {
                        alert('Ошибка');
                      }
    			">
    				<Titles>
    					<Title LCID="1049" Text="Удалить все" />
    				</Titles>
    				<ToolTips>
    					<ToolTip LCID="1049" Text="Удалить все" />
    				</ToolTips>
    			</Button>
    		</Buttons>
    	</MenuBar>
    </Grid>
    
  • Код на кнопке выполняет следующие действия:
    • Выцепляет из текущего представления его GUID;
    • На основе него формируется запрос к странице /isv/bulkdelete/bulkdelete.aspx (GUID передается в строке запроса URL);
    • Смотрите полученный ответ: если это не «Ошибка», то открывается форма записи операции группового удаления записей, на которой Вы можете просмотреть ее результат. Если же было возвращено ключевое слово «Ошибка», то юзверу выдается алерт;
  • Импортируйте ISV.Config обратно в CRM.

Тестирование

Откройте Ваше представление, на которое Вы поместили кнопку и которое содержит какие-либо записи (ненужнеы, разумеется 🙂 ) и нажмити на кнопку «Удалить все». Все, смотрим результат (в открывшейся карточке группового удаления записей, Вы, возможно, увидите что операция еще не завершилась, поэтому обновите ее – F5) 🙂





З.Ы. Было бы весьма неплохо, если бы Вы оградили юзверово от доступа к этой кнопке 🙂

Комментарии (0)

*

code