Источник:
http://axforum.info/forums/blog.php?b=421
==============
В одном из своих прошлых постов, я писал про сходства и отличия стандартных и кастомных типов действий:
CRM 2011 Custom Activitys - Особенонсти. Тогда я коснулся только верхнеуровневых объектов действий, но не затрагивал такой интересный "подобъект" как "Стороны действия" (activity party). Что такое стороны действия, вы можете подробно почитать в SDK. Если кратко, все действия (кроме задачи) связывают несколько сторон, или участников. Например, действие «Электронная почта» имеет «Получателей» и «Отправителя», встреча имеет «Участников» и «Организатора», и т.д. Все разновидности участников называются Сторонами действия. Кроме того, стороны привязываются к особым системным типам полей множественного выбора "PartyList". Это и есть те самые поля "Получатель" (почта), "Обязательные участники" (встреча) и пр.
Впрочем, оставим этот ликбез и перейдем, собственно, к задаче. Недавно мой хороший знакомый и недавний коллега по 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);
p.s. Для простоты тут я использую метод Create а не Book.
Результат эксперимента показал следующее:
- Встречу МОЖНО создать с кастомным участником!
- НУЖНО включить для объекта 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 };
Второй неприятный момент заключается в том, что данный функционал активно использует числовой ObjectTypeCode, который, вообще-то deprecated и в следующих версиях должен быть полностью заменен на строковое свойство LogicalName. Исходя из этого, опасно кодировать подобную функциональность непосредственно в тексте программы. Для того чтобы облегчить переносимость, реализация принимает параметры из настроек обработчика события формы:
Настройки задаются как строка JSON:
X++:
{ имя поля1: [ массив добавляемых типов участников ], имя поля2: [ массив добавляемых типов участников ] }
Сами типы участники задаются в формате:
X++:
{ oct: , schema: , schemaName: , isDefault: , DefaultViewId: }
Приятно удивил тот факт, что система автоматически приводит текст JSON параметра к JS объекту, поэтому нет необходимости парсить его самостоятельно.
Заключение
Ни я ни Андрей не смогли выяснить, является ли это решение поддерживаемым хотя бы частично. Визуальная часть - не поддерживается абсолютно! Остается вопрос, можно ли делать такие вещи через вызовы SDK. На мой взгляд это ограничение - не более чем недосмотр разработчиков. Время покажет!
Вложения
FixRMCustomActivityParty_1_0_0_0.zip (21.0 Кб, 0 просмотров)
Источник:
http://axforum.info/forums/blog.php?b=421