Добро пожаловать в мой блог! Изначально он не задумывался как блог 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






?