Добро пожаловать в мой блог! Изначально он не задумывался как блог CRM разработчика, но жизнь сама внесла нужные коррективы. Тут я публикою все свои наблюдения относительно обозначенных в заголовке систем. Если Вы найдете в нем что-то интересное для Вас, как для заказчика, то буду рад сотрудничать с Вами! В моей компетенции 100% задач по MS CRM 3.0/4.0/2011:
MVP 2010, 2011
- Консалтинг
- Проектирование
- Разработка
- Обучение
MVP 2010, 2011
Укрощение Advanced Find, часть 2
Запись от Артем Enot Грунин размещена 22.10.2009 в 19:53
Обновил(-а) Артем Enot Грунин 03.11.2009 в 15:49
Обновил(-а) Артем Enot Грунин 03.11.2009 в 15:49
Теги advanced find, java script, unsupport
В своем прошлом посте я рассматривал довольно типовую задачу быстрого поиска записей средствами Advanced Find, на основе очередной инкарнации известного всем "IFrame Fetch Viewer". Напомню, что задача в моем примере стояла не показать некую выборку в виде стандартной таблицы с результатами, а помочь менеджеру быстро приступить к поиску нужных вариантов. Иными словами, мне было нужно не столько показать результат, сколько настроить фильтр и показать его пользователю. Рассмотрим на примере: клиент приходит к менеджеру заказчика и говорит: "мне нужен красный шкаф таких-то размеров". Менеджер открывает новый Интерес и заполняет параметры заявки: размеры и цвет шкафа. Далее ему нужно открыть диалог расширенного поиска, выбрать поиск по Продуктам, далее забить фильтр с параметрами. Пусть мы имеем дело с прожженным пользователем CRM и подходящий фильтр у него уже заготовлен, но что если категорий продукта несколько десятков? Пусть теперь не найдется продукта полностью удовлетворяющего заказчика, тогда менеджеру придется поиграть с параметрами запроса, чтобы найти нечто отдаленно напоминающее то что хотел клиент. В случае успеха менеджер скажет: "Вы знаете, красный - это так пошло! Возмите-ка синий!". Клиент согласится подумать и придет отнимать время менеджера завтра и история повторится. Вот тут мы и приходим к понимаю того, что иногда на основании входных параметров удобно формировать сам фильтр, а не готовую выборку. Как это сделать?
Пройдем по шагам:
1. Кастомизируем сущность Продукт.
Добавим два атрибута типа decimal: new_width и new_height и один picklist new_color c опциями 'Red', 'Green', 'Blue'. Разместим все новые атрибуты на форме.
2. Создадим и сохраним запрос для поиска продуктов с ограничениями по этим полям. Нужен любой осмысленный запрос, нам потребуется только его прямой url и его FetchXML.
3. Кастомизируем сущность Интерес.
Добавим к ней все те же атрибуты что и к продукту, а так же разместим на ее форме фрейм IFrame_af, свойство src которого должно указывать на url нашего сохраненного запроса.
4. На событие OnLoad интереса вешаем следующий код:
Этот код выполняет подготовку нашего фрейма к использованию: скрывает лишние элементы окна, показывает мультик пока фрейм загружается и заполняет поисковый фильтр сохраненными значениями с формы. К сожалению, CRM 4.0 позволяет нам размещать свой код только в обработчиках событий, поэтому мы разместим функцию обновления фильтра в одном из них:
5. В событии OnChange поля new_color:
Функция RefreshFrame убеждается что фрейм готов к работе (он может находиться на разных вкладках с полями заявки и не быть инициализированным) после чего собирает стоку FetchXml фильтра и обновлет фрейм. Для удобства внесения изменений, код формирования запроса вынесен в отдельную функцию. Обратите внимание, что поля на форме могут не содержать значений. В этом случае требуется удалять соответствующие условия из фильтра, иначе Advanced Find будет требовать заполнить все пустые поля запроса.
6. В обработчик OnChange всех прочих полей заявки (new_height и new_width) следует поместить код:
Он только перевызывает описанные выше функции.
7. Если пользователь менял фильтр руками, форма будет запрашивать сохранение изменений. Чтобы этого избежать, на OnSave формы поместите код:
8. Публикуем изменения. Можно пользоваться.
Решение можно дополнить, создав связь многие ко многим между сущностями Продукт и Интерес для хранения "Предложенных вариантов". Тогда, например, по нажатию кастомной кнопки на тулбаре можно будет добавлять найденные продукты в список предложенных клиенту вариантов, чтобы их не приходилось искать повторно.
Изменение от 03.11.09: Реализацию смотрите в посте: Добавление связей из формы объекта.
p.s. Огромное спасибо Андрею a33ik Бутенко, за неоценимую помощь в отладке этого кода. Не будь тебя, друг, JS меня бы одолел!
Пройдем по шагам:
1. Кастомизируем сущность Продукт.
Добавим два атрибута типа decimal: new_width и new_height и один picklist new_color c опциями 'Red', 'Green', 'Blue'. Разместим все новые атрибуты на форме.
2. Создадим и сохраним запрос для поиска продуктов с ограничениями по этим полям. Нужен любой осмысленный запрос, нам потребуется только его прямой url и его FetchXML.
3. Кастомизируем сущность Интерес.
Добавим к ней все те же атрибуты что и к продукту, а так же разместим на ее форме фрейм IFrame_af, свойство src которого должно указывать на url нашего сохраненного запроса.
4. На событие OnLoad интереса вешаем следующий код:
Код:
var oFrame = crmForm.all.IFRAME_af; prepareAFFrame(oFrame); function prepareAFFrame(oFrame) { // Ставим мультик на загрузку фрейма var oLoadingTitle = "We roll'n roll'n roll'n!"; var odoc = oFrame.contentWindow.document; odoc.body.innerHTML = "<table height='100%' width='100%' style='cursor:wait'>" + "<tr>" + "<td valign='middle' align='center'>" + "<img alt='' src='/_imgs/AdvFind/progress.gif'/>" + "<br>" + oLoadingTitle + "</td>" + "</tr>" + "</table>"; // Инициализируем фрейм когда он прогрузится oFrame.attachEvent('onreadystatechange', function() { if (oFrame.readyState != 'complete') return; var odoc = oFrame.contentWindow.document; // Причесываем odoc.getElementById("crmMenuBar").style.display = "none"; var oCols = odoc.getElementsByTagName("COLGROUP")[0]; oCols.childNodes[0].style.display = "none"; oCols.childNodes[2].style.display = "none"; // Возможно мы захотим блокировать поиск других объектов //odoc.getElementById("slctPrimaryEntity").disabled = true; // Вся логика помещана в один из обработчиков window.setTimeout("crmForm.all.new_color.FireOnChange()",100); }); }
5. В событии OnChange поля new_color:
Код:
var oFrame = crmForm.all.IFRAME_af; RefreshFrame(oFrame); function RefreshFrame(oFrame) { if (oFrame != null && oFrame.contentWindow.document.readyState == "complete") { // получаем фильтр var oFetchXml = getFetchXML(); //var oFrame = crmForm.all.IFRAME_af; var odoc = oFrame.contentWindow.document; var oAf = odoc.getElementById("advFind"); oAf.Clear(true); // иногда валится если фрейм не успел загрузиться! oAf.FetchXml = oFetchXml; oAf.IsDirty = false; // Сбрасываем флаг изменений } } function getFetchXML() { var oFetchXml = '<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false">' + '<entity name="product"><attribute name="name"/>' + '<attribute name="productid"/>' + '<attribute name="new_width"/>' + '<attribute name="new_height"/>' + '<attribute name="new_color"/>' + '<order attribute="name" descending="false"/>' + '<filter type="and">' + (crmForm.all("new_color").DataValue != null? '<condition attribute="new_color" operator="eq" value="{0}"/>' : "") + (crmForm.all("new_height").DataValue != null? '<condition attribute="new_height" operator="ge" value="{1}"/>' : "") + (crmForm.all("new_width").DataValue != null? '<condition attribute="new_width" operator="ge" value="{2}"/>' : "") + '</filter>' + '</entity>' +'</fetch>'; // Поле 0 oFetchXml = oFetchXml.replace("{0}", crmForm.all("new_color").DataValue); // Поле 1 oFetchXml = oFetchXml.replace("{1}", crmForm.all("new_height").DataValue); // Поле 2 oFetchXml = oFetchXml.replace("{2}", crmForm.all("new_width").DataValue); // * * * return oFetchXml; }
6. В обработчик OnChange всех прочих полей заявки (new_height и new_width) следует поместить код:
Код:
crmForm.all.new_color.FireOnChange()
7. Если пользователь менял фильтр руками, форма будет запрашивать сохранение изменений. Чтобы этого избежать, на OnSave формы поместите код:
Код:
var oFrame = crmForm.all.IFRAME_af; DropFilterChanges(oFrame); function DropFilterChanges(oFrame) { var odoc = oFrame.contentWindow.document; var oAf = odoc.getElementById("advFind"); oAf.IsDirty = false; // Сбрасываем флаг изменений }
Решение можно дополнить, создав связь многие ко многим между сущностями Продукт и Интерес для хранения "Предложенных вариантов". Тогда, например, по нажатию кастомной кнопки на тулбаре можно будет добавлять найденные продукты в список предложенных клиенту вариантов, чтобы их не приходилось искать повторно.
Изменение от 03.11.09: Реализацию смотрите в посте: Добавление связей из формы объекта.
p.s. Огромное спасибо Андрею a33ik Бутенко, за неоценимую помощь в отладке этого кода. Не будь тебя, друг, JS меня бы одолел!
Всего комментариев 9
Комментарии
-
Запись от a33ik размещена 22.10.2009 в 20:05 -
Запись от Артем Enot Грунин размещена 22.10.2009 в 21:53 -
Запись от Gustav размещена 23.10.2009 в 10:55 -
Запись от Артем Enot Грунин размещена 23.10.2009 в 11:01 -
Запись от Lemming размещена 27.10.2009 в 16:45 -
Запись от Артем Enot Грунин размещена 27.10.2009 в 16:49 -
Цитата:
Может это доступно только модераторам или вообще отключили эту опцию, если там есть доступ к чистому html, который не проходит через фильтры?Запись от Lemming размещена 27.10.2009 в 17:04 -
Блин, атачи нельзя в каменты делать... У меня сразу под "Ваш блог" есть нужный пункт. Вам, наверно, к Mazzy тогда стоит обратиться.
Запись от Артем Enot Грунин размещена 27.10.2009 в 17:14 -
Запись от Lemming размещена 27.10.2009 в 17:19