Добро пожаловать в мой блог! Изначально он не задумывался как блог CRM разработчика, но жизнь сама внесла нужные коррективы. Тут я публикою все свои наблюдения относительно обозначенных в заголовке систем. Если Вы найдете в нем что-то интересное для Вас, как для заказчика, то буду рад сотрудничать с Вами! В моей компетенции 100% задач по MS CRM 3.0/4.0/2011:
MVP 2010, 2011
- Консалтинг
- Проектирование
- Разработка
- Обучение
MVP 2010, 2011
Как создать звонок или встречу для кастомного объекта
Запись от Артем Enot Грунин размещена 28.08.2013 в 14:46
Обновил(-а) Артем Enot Грунин 07.10.2013 в 10:07
Обновил(-а) Артем Enot Грунин 07.10.2013 в 10:07
В одном из своих прошлых постов, я писал про сходства и отличия стандартных и кастомных типов действий: CRM 2011 Custom Activitys - Особенности. Тогда я коснулся только верхнеуровневых объектов действий, но не затрагивал такой интересный "подобъект" как "Стороны действия" (activity party). Что такое стороны действия, вы можете подробно почитать в SDK. Если кратко, все действия (кроме задачи) связывают несколько сторон, или участников. Например, действие «Электронная почта» имеет «Получателей» и «Отправителя», встреча имеет «Участников» и «Организатора», и т.д. Все разновидности участников называются Сторонами действия. Кроме того, стороны привязываются к особым системным типам полей множественного выбора "PartyList". Это и есть те самые поля "Получатель" (почта), "Обязательные участники" (встреча) и пр.
Впрочем, оставим этот ликбез и перейдем, собственно, к задаче. Недавно мой хороший знакомый и недавний коллега по MS Андрей Слепицкий обратился ко мне с вопросом: можно ли использовать кастомные объекты как стороны действия? Например, мы ввели в систему отдельную сущность Партнер и хотим позвать партнера на встречу. Сперва задача показалась мне простой: в чем проблема - разрешить действия в настройках объекта?:
Однако, все оказалось сложнее. К сожалению, система позволяет выбор кастомного объекта только в поле "В отношении", но не в других полях, таких как "Обязательные участники" встречи. Небольшое исключение составляет форма Электронной почты. Если отметить вторую галочку: Sending e-mail, система позволит выбирать кастомный объект в поле "Получатель" (to) на форме электронной почты. Но как же быть с остальными полями?
Для того чтобы исследовать феномен, я решил провести зловещий эксперимент. Для этого я модифицировал пример создания встречи из SDK Sample: Book an Appointment чтобы добавить в список участников встречи свой кастомный объект:
p.s. Для простоты тут я использую метод Create а не Book.
Результат эксперимента показал следующее:
Иными словами, использовать кастомные объекты в действиях можно, единственная проблема - это как-то добавить эту возможность в пользовательский интерфейс. И вот тут начинается небольшой, но грязный unsupport… Ниже к посту приложено неуправляемое решение, которое реализует искомую функциональность.
Трюк заключается в том чтобы средствами JS DOM изменить атрибуты lookuptypes, lookuptypeIcons и lookuptypenames у нужного lookup контрола при загрузке формы. Как это часто бывает с ансаппортом, непонятно на что влияет последний атрибут - все работает и без него, однако его я для порядка тоже привожу в соответствие.
Второй неприятный момент заключается в том, что данный функционал активно использует числовой ObjectTypeCode, который, вообще-то deprecated и в следующих версиях должен быть полностью заменен на строковое свойство LogicalName. Исходя из этого, опасно кодировать подобную функциональность непосредственно в тексте программы. Для того чтобы облегчить переносимость, реализация принимает параметры из настроек обработчика события формы:
Настройки задаются как строка JSON:
Сами типы участники задаются в формате:
Приятно удивил тот факт, что система автоматически приводит текст JSON параметра к JS объекту, поэтому нет необходимости парсить его самостоятельно.
Заключение
Ни я ни Андрей не смогли выяснить, является ли это решение поддерживаемым хотя бы частично. Визуальная часть - не поддерживается абсолютно! Остается вопрос, можно ли делать такие вещи через вызовы SDK. На мой взгляд это ограничение - не более чем недосмотр разработчиков. Время покажет!
Впрочем, оставим этот ликбез и перейдем, собственно, к задаче. Недавно мой хороший знакомый и недавний коллега по MS Андрей Слепицкий обратился ко мне с вопросом: можно ли использовать кастомные объекты как стороны действия? Например, мы ввели в систему отдельную сущность Партнер и хотим позвать партнера на встречу. Сперва задача показалась мне простой: в чем проблема - разрешить действия в настройках объекта?:
Однако, все оказалось сложнее. К сожалению, система позволяет выбор кастомного объекта только в поле "В отношении", но не в других полях, таких как "Обязательные участники" встречи. Небольшое исключение составляет форма Электронной почты. Если отметить вторую галочку: Sending e-mail, система позволит выбирать кастомный объект в поле "Получатель" (to) на форме электронной почты. Но как же быть с остальными полями?
Для того чтобы исследовать феномен, я решил провести зловещий эксперимент. Для этого я модифицировал пример создания встречи из SDK Sample: Book an Appointment чтобы добавить в список участников встречи свой кастомный объект:
X++:
String crmconnection = "Server=http://crm/FixRM"; CrmConnection connection = CrmConnection.Parse(crmconnection); OrganizationService service = new OrganizationService(connection); WhoAmIRequest userRequest = new WhoAmIRequest(); WhoAmIResponse userResponse = (WhoAmIResponse) service.Execute(userRequest); // Create the ActivityParty instance. ActivityParty me = new ActivityParty { PartyId = new EntityReference(SystemUser.EntityLogicalName, userResponse.UserId) }; ActivityParty customparty= new ActivityParty { // PartyId = new EntityReference("fixrm_customparty", new Guid("6793AD07-E70E-E311-8E30-080027004A52")) }; // Create the appointment instance. Appointment appointment = new Appointment { Subject = "Test Appointment", Description = "Test Appointment created using the BookRequest Message.", ScheduledStart = DateTime.Now.AddHours(1), ScheduledEnd = DateTime.Now.AddHours(2), Location = "Office", RequiredAttendees = new ActivityParty[] { me, customparty}, Organizer = new ActivityParty[] { me } }; // Use the Book request message. Guid id = service.Create(appointment);
Результат эксперимента показал следующее:
- Встречу МОЖНО создать с кастомным участником!
- НУЖНО включить для объекта Sending e-mail, иначе вы получите ошибку "Invalid party type code" при попытке добавить его как участника
- НЕ ТРЕБУЕТСЯ разрешать активности (Activities) для объекта, чтобы можно было добавить кастомного участника
Иными словами, использовать кастомные объекты в действиях можно, единственная проблема - это как-то добавить эту возможность в пользовательский интерфейс. И вот тут начинается небольшой, но грязный unsupport… Ниже к посту приложено неуправляемое решение, которое реализует искомую функциональность.
Трюк заключается в том чтобы средствами JS DOM изменить атрибуты lookuptypes, lookuptypeIcons и lookuptypenames у нужного lookup контрола при загрузке формы. Как это часто бывает с ансаппортом, непонятно на что влияет последний атрибут - все работает и без него, однако его я для порядка тоже привожу в соответствие.
X++:
if (typeof (FixRM) == "undefined") { FixRM = { __namespace: true }; } /* Events FixRM.CustomActivityParty.AddPartyTypeOnLoad */ FixRM.CustomActivityParty = { AddPartyTypeOnLoad: function (settings) { for (var setting in settings) { var parties = settings[setting]; for (var i = 0; i < parties.length; i++) { var party = parties[i]; this.AddPartyType(setting, party.otc, party.schema, party.schemaName); if (party.isDefault == true) { this.SetDefaultParty(setting, party.otc, party.DefaultViewId); } } } }, AddPartyType: function (name, otc, schema, schemaName) { function ApendAttributeValue(node, name, separator, value) { var attribute = node.getAttribute(name); var attributeValues = attribute.split(separator); attributeValues.push(value); attribute = attributeValues.join(separator); node.setAttribute(name, attribute); } var lookup = document.getElementById(name); if (lookup && lookup.attributes) { ApendAttributeValue(lookup, "lookuptypes", ",", otc); var icoPath = Xrm.Page.context.prependOrgName("/_Common/icon.aspx?cache=1&iconType=GridIcon&objectTypeCode=" + otc); ApendAttributeValue(lookup, "lookuptypeIcons", ":", icoPath); if (schema && schemaName) { var lookuptypename = schema + ":" + otc + ":" + schemaName; ApendAttributeValue(lookup, "lookuptypenames", ",", lookuptypename); } } }, SetDefaultParty: function (name, otc, view) { var lookup = document.getElementById(name); if (lookup && lookup.attributes) { lookup.setAttribute("defaulttype", otc); if (view) { lookup.setAttribute("defaultViewId", view); } } }, __namespace: true };
Настройки задаются как строка JSON:
X++:
{ 1: [ ], 2: [ ] }
X++:
{ otc: < >, schema: < >, schemaName: < ( REST)>, isDefault: < . >, DefaultViewId: < . , > }
Заключение
Ни я ни Андрей не смогли выяснить, является ли это решение поддерживаемым хотя бы частично. Визуальная часть - не поддерживается абсолютно! Остается вопрос, можно ли делать такие вещи через вызовы SDK. На мой взгляд это ограничение - не более чем недосмотр разработчиков. Время покажет!
Всего комментариев 0