Кастомизация
09
Май
10

Исследуем кнопку «Добавить существующую… »

В CRM 4 было довольно трудно удалить/изменить кнопку «Добавить существующую…». Для этого приходилось использовать не поддерживаемый JS-код. CRM же 2011 предоставляет для этих целей поддерживаемый способ изменить большинство элементов Ленты, включая удаление выше обозначенной кнопки.

Удаление кнопки «Добавить существующую… »

Для просто удаления достаточно отредактировать XML кастомизацию соответствующего объекта. Для примера удалим кнопку «Добавьте существующую сущность Контакта» со связанного представления Контактов, для родительской формы Организации. Что делаем:

  1. Создайте новое Решение и добавьте в него объект Контакт;
  2. Эскортируйте Решение (как неуправляемое) и откройте файл customization.xml, содержащийся в нем, в каком-либо текстовом редактор;
  3. Замените узел RibbonDiffXml таким кодом (или можете только добавить тэг HideCustomAction, в соответствующую область):
    <RibbonDiffXml>
      <CustomActions>
        <HideCustomAction Location="Mscrm.SubGrid.contact.AddExistingStandard" HideActionId="Sample.Mscrm.SubGrid.contact.AddExistingStandard.HideAction" />
      </CustomActions>
      <Templates>
        <RibbonTemplates Id="Mscrm.Templates"></RibbonTemplates>
      </Templates>
      <CommandDefinitions />
      <RuleDefinitions>
        <TabDisplayRules />
        <DisplayRules />
        <EnableRules />
      </RuleDefinitions>
      <LocLabels />
    </RibbonDiffXml>
    
  4. Импортируйте Решение обратно в CRM.


Обязательно для заполнения

По умолчанию, когда создаете отношение 1:N, у связанного Представления на основном объекте появятся 2 кнопки. Одна: «Добавить новый элемент…»; другая: «Добавить существующую сущность…».

Есть один очень простой способ скрыть кнопку «Добавить существующую сущность…» . Все, что нужно сделать так это сделать лукап на дочернем объекте обязательным для заполнения. После этого кнопка перестанет отображаться в связанном Представлении.



Фильтрация 1:N «лукапа»

Рассмотрим пример, когда нам нужно при добавлении дочерних Контактов к текущему Контакту, ограничить выбор записями, Фамилии которые начинаются с буквы «A».

Создайте новый плагин с таким кодом:

using System;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.Web;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;

namespace _1N
{
    public class Filter_1N : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            if (HttpContext.Current.Request.QueryString["currentid"] != null &&
                HttpContext.Current.Request.QueryString["LookupStyle"].ToString() == "multi")
            {
                Microsoft.Xrm.Sdk.IPluginExecutionContext context = (Microsoft.Xrm.Sdk.IPluginExecutionContext)serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
                if (context.InputParameters.Contains("Query"))
                {
                    // Получаем GUID записи, с формы которого открыт диалог лукапа
                    Guid contactGuid = new Guid(HttpContext.Current.Request.QueryString["currentid"].ToString());
                    
                    // Получаем сервис
                    IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                    IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
                    
                    // Создаем queryexpression, чтобы найти все Контакты, которые имеют lastname которых начинается с "A"
                    QueryExpression newQueryExpression = new QueryExpression();
                    
                    ConditionExpression condition1 = new ConditionExpression();
                    condition1.AttributeName = "lastname";
                    condition1.Operator = ConditionOperator.BeginsWith;
                    condition1.Values.Add("A");
                    
                    ConditionExpression condition2 = new ConditionExpression();
                    condition2.AttributeName = "contactid";
                    condition2.Operator = ConditionOperator.NotEqual;
                    condition2.Values.Add(contactGuid);
                    
                    newQueryExpression.EntityName = "contact";
                    newQueryExpression.ColumnSet = new ColumnSet();
                    newQueryExpression.ColumnSet.AllColumns = true;
                    newQueryExpression.Criteria.AddCondition(condition1);
                    newQueryExpression.Criteria.AddCondition(condition2);
                    
                    // Подменяем существющий Query новым
                    context.InputParameters["Query"] = newQueryExpression;
                }
            }
        }
    }
}

Что здесь происходит:

  1. Сначала мы определяем тип лукапа. Для этого мы получаем URL текущего окна и смотрим его параметры. URL лукапов CRM имеет примерно такой вид:
    /superfirma/_controls/lookup/lookupinfo.aspx?LookupStyle=multi&ShowNewButton=1&ShowPropButton=1&browse=0&currentid=%7b324B66E7-8577-E111-A939-000C29CDB72E%

    7d&objecttypes=2
    Из него мы можем получить параметр LookupStyle, если он равен single – то лукап позволяет выбрать только одну запись или multi – для выбора нескольких записей. Также из URL вытаскиваем GUID текущей записи.

  2. Далее мы подменяем InputParameter с именем Query, в котором собственно и содержится фильтр, своим QueryExpression.

Зарегистрируйте плагин с такими параметрами:

  • Сообщение: RetrieveMultiple;
  • Объект: contact;
  • Стадия: Pre;
  • Режим: Синхронный.

Идем смотреть…

З.Ы. Этот же метод можно использовать для фильтрации N:N.



Фильтрация N:N «лукапа»

А теперь попробуем отфильтровать связь N:N с помощью JS.

Скажем, у нас есть пользовательский объект под названием Проект, у которого есть N:N отношение с объектом Организация. Наша цель добавить некоторую дополнительную фильтрацию к кнопке «Добавить существующую сущность…» на примере Контактов, которые связаны через N:N с Организацией.

  • Для начала создайте JS Веб-ресурс с таким кодом:
    function addExistingCustomFilter(gridTypeCode, gridControl, primaryEntityName) {
        // Проверяет объект текущей формы. Если это не тот объект что нам нужен, то вызываем функцию по-умолчанию (вместо фильтра)
        if (primaryEntityName != "account") {
            Mscrm.GridRibbonActions.addExistingFromSubGridStandard(gridTypeCode, gridControl);
            return;
        }
    
        // Задаем новый Fetch и набор столбцов для объекта Контакт
        var fetch = '' +
        '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">' +
            '<entity name="contact">' +
                '<attribute name="fullname" />' +
                '<attribute name="parentcustomerid" />' +
                '<attribute name="telephone1" />' +
                '<attribute name="emailaddress1" />' +
                '<attribute name="contactid" />' +
                '<order attribute="fullname" descending="false" />' +
                '<filter type="and">' +
                    '<condition attribute="lastname" operator="like" value="A%" />' +
                '</filter>' +
            '</entity>' +
        '</fetch>';
    
        var layout = '' +
        '<grid name="resultset" object="2" jump="fullname" select="1" icon="1" preview="1">' +
            '<row name="result" id="accountid">' +
                '<cell name="fullname" width="300" />' +
                '<cell name="telephone1" width="100" />' +
                '<cell name="emailaddress1" width="100" />' +
            '</row>' +
        '</grid>';
    
        // Вызываем функцию addExistingFromSubGridCustom и передаем ей необходимые параметры
        addExistingFromSubGridCustom({
            gridTypeCode: gridTypeCode,
            gridControl: gridControl,
            fetchXml: fetch,
            name: "Супер фильтрованное представление",
            layoutXml: layout
        });
    }
    
    // Функция добавляет кастомное представление Представление в диалог лукапа
    function addExistingFromSubGridCustom(params) {
        var relName = params.gridControl.getParameter("relName"),
            roleOrd = params.gridControl.getParameter("roleOrd"),
            viewId = "{00000000-0000-0000-0000-000000000001}"; // GUID-пустышка
    
        var customView = {
            fetchXml: params.fetchXml,
            id: viewId,
            layoutXml: params.layoutXml,
            name: params.name,
            recordType: params.gridTypeCode,
            Type: 0
        };
    
        var lookupItems = LookupObjects(null, "multi", params.gridTypeCode, 0, null, "", null, null, null, null, null, null, viewId, [customView]);
        if (lookupItems && lookupItems.items.length > 0)
            AssociateObjects(crmFormSubmit.crmFormSubmitObjectType.value, crmFormSubmit.crmFormSubmitId.value, params.gridTypeCode, lookupItems, IsNull(roleOrd) || roleOrd == 2, "", relName);
    }
    

    Этот код состоит из двух функций. Самая Важная первая – в ней Вы задаете определение (с помощью Fetch и набора столбцов) динамического Представления, которое будет отображаться в диалоге выбора записей N:N. В первой же функции мы выполняем проверку на радетельский объект, чтобы фильтрация не производилась в не подходящих случаях;

  • Выгрузите Контакт как неуправляемое Решение из CRM и откройте на редактирование файл customization.xml. Далее нам нужно изменить стандартное поведение кнопки, а для этого сначала нужно получить ее определение (т.е. CommandDefinition данной кнопки) и уже в него внести изменение. Получить его можете либо в файле applicationribbon.xml, либо в файле ribbon.xml в SDK (для стандартных объектов). Или с помощью тулзы ExportedRibbonXml для кастомных объектов. Скопируйте стандартный узел в наш customization.xml (в узел RibbonDiffXml -> CommandDefinitions).
    Измените подузел JavaScriptFunction в стандартном определении так, чтобы он вызывал функцию addExistingCustomFilter из нашего JS Веб-ресурса созданного ранее. В итог XML узел CommandDefinition будет таким:

    <CommandDefinition Id="Mscrm.AddExistingRecordFromSubGridAssociated">
      <EnableRules>
        <EnableRule Id="Mscrm.AppendToPrimary" />
        <EnableRule Id="Mscrm.EntityFormIsEnabled" />
      </EnableRules>
      <DisplayRules>
        <DisplayRule Id="Mscrm.AddExisting" />
        <DisplayRule Id="Mscrm.ShowForManyToManyGrids" />
        <DisplayRule Id="Mscrm.AppendToPrimary" />
        <DisplayRule Id="Mscrm.AppendSelected" />
      </DisplayRules>
      <Actions>
        <JavaScriptFunction FunctionName="addExistingCustomFilter" Library="$webresource:new_acc.js">
          <CrmParameter Value="SelectedEntityTypeCode" />
          <CrmParameter Value="SelectedControl" />
          <CrmParameter Value="PrimaryEntityTypeName" />
        </JavaScriptFunction>
      </Actions>
    </CommandDefinition>
    
  • Импортируйте Решение обратно в CRM и открываем лукап выбора связи N:N – Вам автоматически отобразится динамическое отфильтрованное 9в соответствии с Fetch) Представление.

З.Ы. аналогичный метод можно использовать и для связи 1:N.



Комментарии (10)
  • Рагим 09.05.2012

    Огромное спасибо за статью!!!

  • Skyway 09.05.2012

    Попытался создать связь N:N и фильтрацию для кастомной сущности. В итоге все работает, связанный список не отображается, НО: кнопка «Добавить» в связанном представлении неактивна. При выборе записей в «Супер фильтрованное представление» снизу не отображается что они выбраны (пишет 0 выбрано). При выборе любого другого представления контакты добавляются. Не подскажете с чем может быть связанно?..

  • Skyway 09.05.2012

    Разобрался, ошибка была в этом месте:
    » +» +
    » +
    » +
    » +
    » +
    »;
    Вместо contactid было написано accountid.

  • Skyway 09.05.2012

    Разобрался, ошибка была в этом месте задания layout:

    row name=»result» id=»accountid»

    Вместо accountid следует написать contactid.

  • Michael 09.05.2012

    N:N filter:
    The name of the websource should not have .JS

  • slivka_83 09.05.2012

    Добрый день. Вопрос сформулируйте?

  • Alex 09.05.2012

    Сделал все по инструкции. Все работает, КРОМЕ того что при выборе нужной записи, она не добавляется в грид. В чем я ошибся?

  • Alex 09.05.2012

    В логе пишет
    «crmFormSubmit» не определено
    120

  • slivka_83 09.05.2012

    Если все сделали по инструкции, то могу предположить, что Ваша версия CRM (в том числе и ролапы) отличается от той, на которой я испытывал этот код.

  • Alex 09.05.2012

    все заработало когда вызвал эту функцию через parent.crmFormSubmit.

*

code