Записки на лету

All posts in Статьи

На днях обнаружил быстрый способ узнать EntityTypeCode по имени сущности

var accountEntityTypeCode = Mscrm.EntityPropUtil.EntityTypeName2CodeMap["account"];

 


С версии 2013 появилось несколько новых методов нотификации пользователя при работе на форме. Расскажу про них и в каких случаях можно их использовать.

1. Поддерживаемая версия alert().

Раньше мы использовали стандартный alert("Сообщение!");

Теперь у нас есть два варианта:

  1. Простое сообщение: Xrm.Utility.alertDialog(message,onCloseCallback);
    где onCloseCallback колбэк функция выполняемая после нажатия кнопки ОК окошка сообщения.
    Пример использования (Проверять на загрузке формы заполнение поля и если оно не заполнено то просить его заполнить и ставить в него фокус):

    function formOnLoad() {
        /// <summary>Функция, вызываемая при загрузки формы</summary>
        if (Xrm.Page.getAttribute("mobilephone") && !Xrm.Page.getAttribute("mobilephone").getValue()) {
            Xrm.Utility.alertDialog("Не заполнен мобильный телефон",
                function () {
                    Xrm.Page.getControl("mobilephone").setFocus();
                });
        }
    }

    AlertMessage

  2. Диалог подтверждения: Xrm.Utility.confirmDialog(message,yesCloseCallback,noCloseCallback);
    где yesCloseCallback колбэк функция выполняемая после нажатия кнопки ОК окошка сообщения и noCloseCallback колбэк функция выполняемая после нажатия кнопки ОК окошка сообщения.
    Пример использования (Проверять на сохранении формы заполнение поля и если оно не заполнено то предложить его заполнить и ставить в него фокус):

    function formOnSave(context) {
        /// <summary>Функция, вызываемая при сохранении формы</summary>  
        if (Xrm.Page.getAttribute("mobilephone") && !Xrm.Page.getAttribute("mobilephone").getValue()) {
            Xrm.Utility.confirmDialog("Не заполнен мобильный телефон. Заполнить?",
                // Если нажали ОК
                function () {
                    // Ставим фокус
                    Xrm.Page.getControl("mobilephone").setFocus();
                    //Отменяем сохранение
                    var eventArgs = context.getEventArgs();
                    eventArgs.preventDefault();
                },
                // Если нажали Cancel
                function () {
                    // Ничего не делаем
                });
        }
    }

    AlertDialogMessage

2. Панель нотификации.

Тоже самое можно реализовать с помощью панели нотификации.

Xrm.Page.ui.setFormNotification(message, level, uniqueId);

где:

  • message (string) — сам текст сообщения.
  • level (string) — тип иконки, возможные варианты: «ERROR» , «WARNING», «INFO».
  • uniqueId (string) — идентификатор сообщения (используется для убирания конкретного сообщения).

При добавлении исполнении следующих строк:

Xrm.Page.ui.setFormNotification('Произошла ошибка...!', 'ERROR', 'id1');
Xrm.Page.ui.setFormNotification('Предупреждение...', 'WARNING', 'id2');
Xrm.Page.ui.setFormNotification('Для информации', 'INFORMATION', 'id3');

Мы увидим следующее.

NotificationMessages

Но тут необходимо помнить, что свое сообщение необходимо скрывать явно, т.к. иначе оно будет висеть до перезагрузки формы. Для чего нам и необходим третий параметр uniqueId, без него мы сможем только скрыть все сообщения, а на форме могут быть еще другие сообщения, как наши так и системные.

Скрываются сообщения так:

Xrm.Page.ui.clearFormNotification('id1');  // Сообщение с идентификатором 'id1'

Попробуем реализовать пример выше с использованием понели нотификации. Нам понадобятся две функции, на загрузку формы formOnLoad() и на изменение поля Мобильный телефон mobilePhoneOnChange().

function formOnLoad() {
    /// <summary>Функция, вызываемая при загрузки формы</summary>
    if (Xrm.Page.getAttribute("mobilephone") && !Xrm.Page.getAttribute("mobilephone").getValue()) {
        Xrm.Page.ui.setFormNotification("Не заполнен мобильный телефон", 'WARNING', "mobilephoneAlert");
    }
}

function mobilePhoneOnChange() {
    /// <summary>Функция, вызываемая при изменении поля телефон</summary>
    if (Xrm.Page.getAttribute("mobilephone") && !Xrm.Page.getAttribute("mobilephone").getValue()) {
        Xrm.Page.ui.setFormNotification("Не заполнен мобильный телефон", 'WARNING', "mobilephoneAlert");
    } else {
        Xrm.Page.ui.clearFormNotification("mobilephoneAlert");
    }
}

Выглядеть будет так.NotificationMessages2

3. Нотификация поля.

Третий вариант, который мы можем использовать это нотификация непосредственно на поле, что актуально при сообщении касающегося какого-то конкретного поля. Это очень похоже на предыдущий вариант:

Xrm.Page.getControl(fieldName).setNotification(message, uniqueId);  // Показать сообщение
Xrm.Page.getControl(fieldName).clearNotification(uniqueId);         // Скрыть сообщение

где:

  • fieldName (string) — имя поля.
  • message (string) — текст сообщния.
  • uniqueId (string) — идентификатор сообщения (используется для убирания конкретного сообщения).

Выглядит так:NotificationMessages3

Кстати, пока Вы видите такое сообщение CRM не даст Вам сохранить форму.

Соответственно, наш пример будет выглядеть так:

function formOnLoad() {
    /// <summary>Функция, вызываемая при загрузки формы</summary>
    if (Xrm.Page.getAttribute("mobilephone") && !Xrm.Page.getAttribute("mobilephone").getValue()) {
        Xrm.Page.getControl("mobilephone").setNotification("Не заполнен мобильный телефон", "EmptyMobilephoneAlert");
    }
    else {
        Xrm.Page.getControl("mobilephone").clearNotification("EmptyMobilephoneAlert");
    }
}

function mobilePhoneOnChange() {
    /// <summary>Функция, вызываемая при изменении поля телефон</summary>
    if (Xrm.Page.getAttribute("mobilephone")) {
        if (!Xrm.Page.getAttribute("mobilephone").getValue()) {
            Xrm.Page.getControl("mobilephone").setNotification("Не заполнен мобильный телефон", "EmptyMobilephoneAlert");
        } else {
            Xrm.Page.getControl("mobilephone").clearNotification("EmptyMobilephoneAlert");
        }
    }
}

Такого же результата мы можем достигнуть создав «Бизнес-правило».AlertBusinessRule

В результате, CRM также добавит скрипт на форму, сгенеренный по этому правилу. Если кому интересно, ниже скрипт сгенеренный для приведенного правила.

 

function pbl_eb4eb395d711e61180ce00155de610f8() {
    try {
        var v0 = Xrm.Page.data.entity.attributes.get('mobilephone');
        var v2 = '';
        if ((v0 == undefined || v0 == null || v0 === ""))
        { return; }
        var v1 = v0.getValue();
        if ((v1) == undefined || (v1) == null || (v1) === "") {
            v0.controls.forEach(function (c, i) {
                c.setNotification(Mscrm.BusinessRulesScript.GetResourceString('e59db157-30ca-9aa2-05c3-d3da4ac312c0', 'Не заполнен мобильный телефон'), 'eb4eb395-d711-e611-80ce-00155de610f8SetMessageStep4');
            });
            v2 = v2 + 'eb4eb395-d711-e611-80ce-00155de610f8SetMessageStep4\x3b';
        }
        var v3 = [{ 'CId': 'mobilephone', 'SId': 'eb4eb395-d711-e611-80ce-00155de610f8SetMessageStep4' }];
        for (var i = 0; i < v3.length; i++) {
            var l1 = v3[i];
            if (v2.indexOf(l1.SId + '\x3b') === -1) {
                Xrm.Page.data.entity.attributes.get(l1.CId).controls.forEach(function (c, i) { c.clearNotification(l1.SId); });
            }
        }
    } catch (e) {
        Mscrm.BusinessRules.ErrorHandlerFactory.getHandler(e, arguments.callee).handleError();
    }
}
Mscrm.BusinessRulesScript.Initialize = function () {
    Mscrm.BusinessRulesScript.AttributesOnChangeHandlers = {};
    (function () {
        var onchangehandler = function () {
            pbl_eb4eb395d711e61180ce00155de610f8();
        };
        Mscrm.BusinessRulesScript.AttributesOnChangeHandlers['mobilephone'] = onchangehandler;
        var attributeObject = Xrm.Page.data.entity.attributes.get('mobilephone');
        if (attributeObject != null && attributeObject != undefined) {
            attributeObject.addOnChange(onchangehandler);
        }
    })(); pbl_eb4eb395d711e61180ce00155de610f8();
};

4. Всплывающая подсказка tooltip.

Также, следует помнить, что всплывающие подсказки для полей в CRM уже реализованы. По-умолчанию всплывающей подсказкой для поля является описание в настройках поля.AlertTooltipSettings

AlertTooltip

 


Вышла очередная версия MS Dynamics CRM версия 2016. Хотя кое-что в ней до сих пор от 2011 версии, диалоги например.

Но сегодня речь пойдет не об этом. А о том с какими сложностями я встретился при переносе решения с 2013/2015 на 2016.

Начнем с той части где оказалось больше всего проблем, а именно, с форм. Начиная с версии 2015 Update 1 в CRM появился новый движок рендеринга форм (подробнее здесь Microsoft Dynamics CRM Online 2015 Update 1 – New Form Rendering Engine) на котором перестал работать почти весь «Unsupported» код, который я использовал еще с версии 2011. А кому сейчас легко, клиент всегда прав и если он говорит — «Хочу так!», то приходится делать как говорят.

Для тех, кто не хочет вникать в детали и хочет просто чтобы все работало как раньше скажу сразу, что есть волшебная галочка которая отключает новые формы и возвращает старые.

Находится она по такому пути: Параметры -> Администрирование -> Системные параметры Вкладка Общие сведения где в самом низу есть секция Использовать отображение форм предыдущих версий где есть единственный пункт Для совместимости использовать подсистему отображения форм предыдущих версий. Учтите, что это может отрицательно влиять на производительность вот напротив него ставим Да.

Перво наперво с форм пропал jQuery. И почти на каждой странице я стал получать ошибку: «ReferenceError: $ is not defined»

Оказалось, что кастомные скрипты теперь грузятся в отдельном фрейме и jQuery на самом деле есть но уровнем выше.

Лечится так:

 

if (typeof($) === 'undefined') {
    $ = parent.$;
}
if (typeof(jQuery) === 'undefined') {
    jQuery = parent.jQuery;
}

Так же исчезла внутренняя функция Mscrm.InlineDialogUtility.createInlineDialog(), которую я использовал для создания кастомных диалогов. Пример использования такой:

 

var url = Mscrm.CrmUri.create(String.format("$webresource:{0}", "gz_/html/datainputdialog.html"));
var dlgOptions = {};
dlgOptions.param1 = "Some param";   
var url = Mscrm.CrmUri.create(String.format("$webresource:{0}", "YouWebResourceName")

Mscrm.InlineDialogUtility.createInlineDialog(url, dlgOptions, width, height, left, top, zindex);

Но, осталась функция Xrm.Internal.openDialog(), пример использования такой:

 

var url = Mscrm.CrmUri.create(String.format("$webresource:{0}", "gz_/html/datainputdialog.html"));
var dlgOptions = {};
params.sl_contact = { id: Xrm.Page.data.entity.getId(), typename: 'contact' };       
var DialogOption = new Xrm.DialogOptions;
DialogOption.width = 640; DialogOption.height = 440;
Xrm.Internal.openDialog(url.toString(), DialogOption, params, null, CallbackFunction);
function CallbackFunction(returnValue){
}

И в диалоге, если надо вернуть параметр в колбэк фунцию то это делается с помощью следующего скрипта:

 

var oReturn = {};
oReturn.smeparam = ""someparam;
Mscrm.Utilities.setReturnValue(oReturn);
 try {
    closeWindow(true);
}
catch (e) { }

И в самом веб-ресурсе необходимо добавить ссылку на ClientGlobalContext:

 

<script src="../../ClientGlobalContext.js.aspx" type="text/javascript"></script>

Еще есть у меня функция, которая пробегалась по полям формы и проверяла заполнены ли обязательные поля. Она стала возвращать false когда такого быть не должно. Исходная функция ниже:

 

function checkIfAllFieldsFilled() {
    var attributes = Xrm.Page.data.entity.attributes.get();
    for (var i = 0; i > attributes.length; i++) {
        if (attributes[i].getRequiredLevel() == "required") {
            if (attributes[i].getValue() == null) {
                return false;
            }
        }
    }
    return true;
}

Как оказалось, функция Xrm.Page.data.entity.attributes.get() теперь возвращает поля не только текущей сущности но и связных сущностей тоже, т.е. для Возможной сделки, например, я получил еще и поля связных звонков и т.д.
Решается дополнительной проверкой родителя у поля:

 

function checkIfAllFieldsFilled() {
    var isOk = true;
    Xrm.Page.data.entity.attributes.forEach(function (attribute, index) {
        if (attribute.getParent() 
            && attribute.getParent().getEntityName() === "opportunity" 
            && attribute.getRequiredLevel() === "required") {
            if (attribute.getValue() === null) {
                isOk = false;
            }
        }
    });
    return isOk;
}

Изменилась вестка гридов и объектная модель работы с ними. Про новые методы можно посмотреть здесь Grid objects and methods (client-side reference).

Соответственно, старые «Unsupported» скрипты по подучению выбранных записей не работают. А вот по фильтрации работают, надо только грид надо теперь получать через parent, как и jQuery.

Но зато теперь, получить все или выбранные записи в гриде весьма легко. 🙂

В связи со всем вышеперечисленным возникает вопрос: — «А как я пойму включены ли новые формы или нет из самого скрипта?». Ведь получается, что местами придется писать два варианта кода, для новых форм и старых.

К счастью, покапавшись в дебаггере, я нашел функцию, которая возвращает включен новый движок форм или нет.

 

Xrm.Internal.isTurboForm()

Это, что касается скриптов. Также, обнаружил что в CRM Online эта функция всегда возвращает false.

С плагинами и процессами проблем почти не было.

Единственное на что я пока наткнулся это ошибка «Method not found: ‘!!0[] System.Array.Empty()’.»

Появилаcь она из-за того что я обновил dll с плагинами ипользуя SDK v.8.0.2 а в нем используется .NET Framework 4.6.1.1 а при установке CRM 2016 ставится 4.6.0.

Лечится установкой .NET Framework 4.6.1.1 на сервере CRM.

Продолжение следует…


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

В поисках решения этой проблемы я нашел два решения:

Вариант первый — изменить настройки клиентов Visual Studio.

За

  • Изменение применяется один раз на клиенте и работает со всеми TFS серверами к которым подключается.
  • Оставляет возможность поставить задачу в состояние «Завершена» при чекине.

Против

  • Изменения должны быть произведены на клиенте и для каждого пользователя.

В большинстве случаев где бы Вы не инициировали CheckIn в Visual Studio Вы окажетесь на панели «Pending Changes». Это вариант изменяет выбор по умолчанию с «Resolve» на «Associate».

Для этого, всего навсего необходимо изменить запись реестра ResolveAsDefaultCheckinAction с True на False, которая находится по адресу: HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\14.0\TeamFoundation\SourceControl\BehaviorTFS-Resolve-Reg

Это для студии 2015, для версий ниже нужно поменять 14.0 на соответствующий номер.

После того как Вы изменили реестр, для того что быть уверенным, что изменения применились в студии необходимо закрыть все экземпляры студии и запустить Developer Command Prompt for VS, где выполнить следующую команду:

devenv /setup

Вариант второй — удалить действие чекина из шаблона процесса.

За

  • Изменения производятся на сервере в Team Project и не затрагивает клиентов.

Против

  • Изменения должны быть применены ко всем текущим Team Projects и потребуется обновить Process Template для будущих Team Projects.
  • Полностью удаляет поставить задачу в состояние «Завершена» при чекине.

Для этого варианта нам понадобится редактор шаблонов рабочих элементов. Я использовал TFS Power Tools, которые ставятся как аддон к Visual Studio.

Открываем студию и идем в меню TOOLS -> Process Editor -> Work Item Types -> Open WIT from Server.

TFS_Open_WIT

Открываем нужный проект и выбираем Task.

TFS_Open_WIT_2

В окне редактора рабочего элемента переходим на вкладку Workflow и ищем Transition, где есть Actions Microsoft.VSTS.Actions.Checkin.

TFS_Open_WIT-3

Кликаем ПКМ на заголовке окна и выбираем Open Details, в появившемся окне переключаемся на вкладку Actions выбираем Microsoft.VSTS.Actions.Checkin и кликаем Delete.

TFS_Open_WIT_4

В стандартном шаблоне у таска удалить надо в двух переходах и у бага в одном.

Вот, собствено, и все.


На днях встретился с проблемой, что в связном гриде не видно сущностей хотя у пользователя есть права на эту сущность и форму он может открыть.

Оказалось, что на форме для одного из полей была включена безопасность на уровне поля (Field level security).

Для того чтобы понять так-ли это я написал следующий SQL скрипт, который показывает все поля для организации, для которых эта опция включена.

 

SELECT org.Name AS 'Organization Name'
      ,sl.UniqueName AS 'Solution Friendly Name'
    ,sl.FriendlyName AS 'Solution Name'
    ,en.Name AS 'Entity Name'
      ,fps.EntityName AS 'Entity Type Code'
      ,fps.AttributeLogicalName AS 'Field Name'
  FROM dbo.FieldPermission AS fps
  JOIN dbo.EntityView AS en
  ON fps.EntityName = en.ObjectTypeCode 
  JOIN dbo.Solution AS sl
  ON fps.SolutionId = sl.SolutionId
  JOIN dbo.Organization AS org
  ON fps.OrganizationId = org.OrganizationId

 


Недавно сдал экзамен MB2-876 Extending Microsoft Dynamics CRM 2011.

Те кому лень могут сразу качать дамп здесь и зубрить а дальше я, в кратце расскажу, что надо знать и по чему я готовился.

Дамп, имеет комментарии почти к каждому ответу, хотя не все мне понятны. Я брал каждый вопрос из дампа и старался прорабатывать каждый вариант ответа правильный и неправильный.

Очень помогли заметки при подготовке MB2-876 Exam Study Notes некоего Jason Weibel, которые он выложил в своем блоге Microsoft CRM Geek.

Что покрывает экзамен описано на странице экзамена в разделе Skills Measured.

Вот некоторые вещи, которые я выделил для себя на отдельном листе когда готовился к экзамену.

Библиотеки

Для работы с Microsoft Dynamics CRM 2011 есть несколько библиотек: Microsoft.Crm.Sdk.Proxy.dll, Microsoft.Xrm.Sdk.dll, Microsoft.Xrm.Sdk.Workflow.dll, Microsoft.Crm.Tools.EmailProviders.dll, Microsoft.Xrm.Sdk.Deployment.dll, все остальное неймспейсы, поэтому когда спрашивают файл надо указывать файл.

Assembly name Description
Microsoft.Crm.Sdk.Proxy.dll Defines requests and responses for messages business data model specific (non-core) messages as well as enumerations required for working with organization data.
Microsoft.Crm.Sdk Contains enumerations of possible picklists and integer values for some attributes. The naming convention of the classes is <EntityName><AttributeName> to make it easier to locate the specific attribute.
Microsoft.Crm.Sdk.Messages Contains request and responses for business data model specific (non-core) messages.
Microsoft.Xrm.Sdk.dll Defines the core xRM methods and types, including proxy classes to make the connection to Microsoft Dynamics CRM simpler, authentication methods, and the service contracts.
Microsoft.Xrm.Sdk Defines the data contracts for attribute types, interfaces for authoring plug-ins, and other general purpose xRM types and methods.
Microsoft.Xrm.Sdk.Client Defines classes for use by client-code, including a data context, proxy classes to ease the connection to Microsoft Dynamics CRM, and the LINQ provider.
Microsoft.Xrm.Sdk.Discovery Defines all classes required to communicate with the Discovery Service, including the service contract, all request/responses and supporting classes.
Microsoft.Xrm.Sdk.Messages Defines request/response classes for Create, Retrieve, Update, Delete, Associate , Disassociate, and the metadata classes.
Microsoft.Xrm.Sdk.Metadata Defines the data contracts for Microsoft Dynamics CRM metadata.
Microsoft.Xrm.Sdk.Query Defines query classes required to connect to Microsoft Dynamics CRM.
Microsoft.Xrm.Sdk.Workflow.dll Defines types and methods required to author a custom workflow activity.
Microsoft.Xrm.Sdk.Workflow Defines the attribute and dependency property classes required to author a custom workflow activity.
Microsoft.Xrm.Sdk.Workflow.Activities Defines the workflow activities that are used by the Microsoft Dynamics CRM workflow designer.
Microsoft.Xrm.Sdk.Workflow.Designers Defines a Microsoft Visual Studio designer for displaying a Microsoft Dynamics CRM workflow in Visual Studio.
Microsoft.Crm.Tools.EmailProviders.dll Defines methods and types needed for developing a custom email provider component for the Microsoft Dynamics CRM Email Router.
Microsoft.Crm.Tools.Email.Management Defines the email provider management types.
Microsoft.Crm.Tools.Email.Providers Defines the base class for a custom email provider and supporting types.
Microsoft.Xrm.Sdk.Deployment.dll Defines types and methods for interacting with the Deployment Web Service.
Microsoft.Xrm.Sdk.Deployment Defines the data contracts necessary to communicate with the Deployment Web Service.
Microsoft.Xrm.Sdk.Deployment.Proxy
Defines a helper class to generate a proxy for the Deployment Web Service.

Плагины

IOrganizationService is the primary web service for accessing data and metadata in Microsoft Dynamics CRM 2011 is the Organization web service.
IDiscoveryService is a single Microsoft Dynamics CRM installation can host multiple organizations on multiple servers therefore it is important to specify which organizations need to be accessed. The Discovery Web service returns a list of the organization the specified user belongs to and the URL endpoint address for each organization.
Messages: Assign, Create, Delete, Merge, Retrieve, RetrieveMultiple, SetState, Update.
Event
Stage name
Stage number
Description
Pre-Event
Pre-validation
10
Stage in the pipeline for plug-ins that are to execute before the main system operation.
Plug-ins registered in this stage may execute outside the database transaction.
Pre-Event
Pre-operation
20
Stage in the pipeline for plug-ins that are to execute before the main system operation.
Plug-ins registered in this stage are executed within the database transaction.
Post-Event
Post-operation
40
Stage in the pipeline for plug-ins which are to execute after the main operation.
Plug-ins registered in this stage are executed within the database transaction.

Отличие REST и SOAP

REST and SOAP Endpoints
Developers can use JScript and Silverlight Web resources to access Microsoft Dynamics CRM data from inside the application. There are two web services available, each provides specific strengths. The following table describes the appropriate web service to use, depending on the task that you have to perform.
Task Web Service
Create, Retrieve, Update and Delete records. REST Endpoint
Associate and Disassociate records REST Endpoint
Assign Records SOAP Endpoint
Retrieve Metadata SOAP Endpoint
Execute Messages SOAP Endpoint

Both Web services rely on the authentication provided by the Microsoft Dynamics CRM application. They cannot be used by code that executes outside the context of the application and they are effectively limited to use within Silverlight, JScript libraries, or JScript that is included in Web Page (HTML)

Microsoft Dynamics CRM 2011 supports two authentication methods: Basic Claims Authentication and Active Directory Authentication.

Веб-ресурсы

File File extensions Type
Webpage (HTML) .htm, .html 1
Style Sheet (CSS) .css 2
Script (JScript) .js 3
Data (XML) .xml 4
Image (PNG) .png 5
Image (JPG) .jpg 6
Image (GIF) .gif 7
Silverlight (XAP) .xap 8
StyleSheet (XSL) .xsl, .xslt 9
Image (ICO) .ico 10

 

Типы полей

Не путайте тип даных и тип поля, например OptionSetValue это тип данных для поля Option Set.
When creating custom fields in Microsoft Dynamics CRM (CRM), the following data types are available to you as a customizer.
Single Line of Text – This is the simplest field type and is a string attribute.  The length can be defined between 1 and 4000 characters.  This field has special formatting if desired for storing Email, Text Area, Ticker Symbol, and Url.  Using email will create a mailto link for that field.  Ticker Symbol will provide a quote for the ticker entered into the field when the value is clicked.  Url will display a link to the value entered in the field.  Text Area can be displayed as more than one line on the form.
Option Set – This is commonly referred to as a pick list or drop down field.  A user is only allowed to select from the choices provided.  A “blank” value is acceptable.  A default value can also be defined.  NOTE: If you have an option set that will be used on other entities in your deployment, you will want to create a global option set for system consistency.
Two Options – This is similar to an option set but only contains two values, Zero (0) and One (1).  The display of those values can be changed to represent whatever you like. No (0) and Yes (1) are common display values for this field.  Another interesting note about this field is that it can be displayed on the form as a pick list, radio buttons, or check box.  The value is set in the form designer after the field is placed on the form.
Multiple Lines of Text – This field is similar to Single Line of Text, however, it can store much more data than Single Line of Text.  This field will be displayed as more than one line on the form.
Date and Time – This field stores date and time data.  You can choose to have both the Date and Time displayed or only the Date portion.
Lookup – This field represents a link to another entity.  It will create a 1:N relationship in the database with this field representing the “1” side of the relationship.
The fields below are different ways to store numerical values in CRM.  In all cases, you can set minimum and maximum values.  This is valuable if you want to constrain data entry to non-negative values or from 0 to 100 for example.  The minimum and maximum values are different for each data type and are set at the minimum and maximum range when the field is created.
Whole Number – This field allows you to store round (or whole) numbers, meaning no decimal points.  The whole number field has different types which can be selected for Duration (activity), Time Zone, and Language (multilingual support).
Floating Point Number – This field allows for numeric values with up to five (5) decimal points.  The precision of this field is arbitrary, which means it can be used to represent both very large numbers as well as very small numbers.
Decimal Number – This field stores numeric values with up to ten (10) decimal points.  The precision of this field is absolute.
Currency – This field is used to store monetary values.  Based on your currency settings, the correct currency symbol is also displayed such as the dollar sign or euro symbol.  It can also hold up to four (4) decimal points.

Роли безопасности

Privilege
Description
Create
Create an entity instance.
Write
Change entities instances.
Delete
Remove an entity instance.
Read
View entities.
Append
Associate a selected entity instance to another entity instance.
Append To
Associate another entity instance to this entity instance.
Assign
Give ownership of an entity instance to another user.
Share
Give access to entity instances to another user and maintain ownership.
Reparent
Assign a different parent to an entity instance.

Windows Azure

Что касается вопросов по Windows Azure то мы можем использовать AppFabric Service Bus для связи с облачными приложениями, соединяться можно с помощью Access Control Services (ACS) или Active Directory Federation Services 2.0 (ADFS) и это все.


Все начинается с малого, как и скрипты на форме сущности, сначала одна функция, затем еще одна и т.д.

На одном проекте, я помню, дошло до того, что общий размер файлов скриптов пошел на мегабайты и количество функций перевалило за сотню. Все это находилось в разных файлах и писалось разными людьми и в результате всего этого так называемого «Спагетти кода» полезли ошибки, появлялись функции и переменные с одинаковыми именами и т.д.

В общем, хаос рос с объемом кода. 🙂

Чтобы решить эту проблему я задумался, какой же способ организации скриптов, называя более умными словами — паттерн, применить.

Сначала пришла мысль взять так называемый Неймспейс паттерн (Namespace Pattern), который используется, можно сказать, у нас прямо под носом, в примерах SDK.

Выглядит он так:

if (typeof (MYCODE) == "undefined")
{ MYCODE = { __namespace: true }; }
MYCODE.QuoteFormEvents = {
    //Приватные функции
    _doSomethingPrivate: function (param) {

    },
    //Публичные функции
    doSomethingPublic: function (param) {

    },
    __namespace: true
};

Грубо говоря, запихиваем функции в объект, который внутри другого объекта, которые олицетворяют пространства имен. На самом деле, приватные функции все равно доступны, также встает вопрос, где хранить приватные переменные, чтобы они были доступны только для приватных функций.

Для меня ответ пришел, когда я начал разрабатывать приложения для Windows 8 на HTML5 и JScript, а именно начал разбираться, как там принято писать на JScript. Естественно, в этих приложениях очень много скриптов, и также среда исполнения накладывает некоторые ограничения. Так вот, организованы они там по паттерну раскрывающийся модуль, мой вольный перевод — Revealing Module Pattern.

Немного переделанный для использования в CRM он выглядит так:

var QuoteFormEvents = function () {
    //Переменные
    var tempString = "";
    var userId = Xrm.Page.context.getUserId();

    //Функции
    function doSomething() {

    }

    function formOnLoad() {
        doSomething();
    }
    //Объявления
    return {
        UserId: userId,
        FormOnLoad: formOnLoad
    };
}();

Здесь также есть объекты, которые олицетворяют пространство имен, но суть в том, что мы весь наш код запихиваем в самовызывающуюся функцию, т.к. область видимости в JS определяется именно функцией. Внутри этой функции друг другу видны и переменные, и функции, а в конце мы объявляем только те функции и переменные, которые будут публичными, все остальное снаружи не видно.

Если Вы собираетесь написать что-то вроде библиотеки, которая будет использоваться в нескольких местах и для которой понадобится возможность переопределения функций, я бы предложил паттерн раскрывающийся прототип (Revealing Prototype Pattern) как более подходящий.

Про Revealing Module Pattern подробнее здесь: Revealing Module Pattern: Structuring JavaScript Code – Part III

Про Revealing Prototype Pattern подробнее здесь: Revealing Prototype Pattern: Structuring JavaScript Code – Part IV

 


Что такое Solution Packager?

Solution packager это маленькое приложение командной строки, которое позволяет распаковывать и запаковывать zip-архивы решений MS Dynamics CRM 2011. Это приложение не просто распаковывает архив, оно разбивает решение на отдельные файлы для каждой формы, представления, веб-ресурса и т.д. Благодаря этому Solution packager позволяет перейти на новый уровень контроля версий для решений. Раньше было весьма трудно эффективно хранить Решения в системе контроля версий. Обычно все сводилось к тому, что в TFS чекинились архивы решений, что к контролю версий имело мало отношения.

Скачать его можно вместе с пакетом SDK скачать который можно здесь.

Использование Solution Packager

Обязательные параметры:

  • /action: {Extract|Pack} (Распаковать|Запаковать)
  • /zipfile: (Путь к файлу Решения)
  • /folder: {(Путь к папке с распакованным решением)

Для того чтобы распаковать решение c:\Solutions\TestSolution_1_0.zip в папку c:\Solutions\TestSolution_1_0
Полная команда

solutionpackager.exe /action:Extract /zipfile:c:\Solutions\TestSolution_1_0.zip /folder:c:\solutions\TestSolution_1_0

Сокращенная версия

solutionpackager.exe /a:Extract /z:c:\Solutions\TestSolution_1_0.zip /f:c:\solutions\TestSolution_1_0

В результате мы получим папку с довольно большим количеством подпапочек с файлами .xml, которые уже куда удобнее хранить в TFS.

SolPackager

Для того чтобы запаковать решение c:\Solutions\TestSolution_1_0 обратно в пакет c:\Solutions\TestSolution_1_0.zip
Полная команда

solutionpackager.exe /action:Pack /zipfile:c:\Solutions\TestSolution_1_0.zip /folder:c:\solutions\TestSolution_1_0

Сокращенная версия

solutionpackager.exe /a:Pack /z:c:\Solutions\TestSolution_1_0.zip /f:c:\solutions\TestSolution_1_0

Дополнительные параметры

Есть, также, дополнительные параметры (жирным выделены параметры по-умолчанию):

  • /packagetype: {Unmanaged|Managed|Both} (тип решения, пока не очень разобрался зачем);
  • /allowWrite: {Yes|No} (используется только при распаковке и регулирует удаление и перезапись уже существующих файлов в папке для распаковки);
  • /allowDelete: {Yes|No|Prompt} (используется только при распаковке и регулирует удаление лишних файлов в папке для распаковки);
  • /clobber (используется только при распаковке и разрешает удаление и перезапись уже существующих файлов с пометкой «Только для чтения» в папке для распаковки);
  • /map: (путь к файлу .xml с настройками маппинга);
  • /errorlevel: {Off|Error|Warining|Info|Verbose} (уровень логирования);
  • /log: (путь к файлу лога).

Более подробно смотрите документацию CRM 2011 SDK.

Что я успел заметить это то, что Solution packager не работает с русскими символами в путях и, так же, не забывайте заключать путь в кавычки если он имеет пробелы.


Я думаю, что уже многие знают как это сделать, а для остальных и себя, в том числе, т.к. время от времени забываю как это сделать.

Итак, чтобы IntelliSence показвал нам функции и методы, предоставляемые нам скриптами CRM необходимо скачать и добавить файл XrmPage-vsdoc.js в проект и затем добавить на него ссылку в необходимый .js файл.

///<reference path="IntelliSense\XrmPage-vsdoc.js"/>

Продолжая эту тему добавляю так же ссылку на проект снипетов для студии CRM 2011 Code Snippets for Visual Studio.


На днях возникла у меня, казалось бы, простая задача, сделать в отчете ссылки на форму сущности. В JScript это 5 секунд, в плагине сложнее но тоже можно. Где это искать в БД SQL непонятно.

После некоторых поисков и капаний обнаружилось, что есть некий скрытый параметр CRM_URL, который возвращает ссылку вида «http://имя_сервера/имя_организации/CRMReports/viewer/drillopen.aspx«, которая используется для так называемых Drill-Down отчетов.

Что использовать этот параметр надо добавить скрытый текстовый параметр с именем «CRM_URL» в проект отчета и поставить галочку напротив Allow null value.

В дизайнере в режиме Preview, этот параметр все-равно был пустой, но, при выгрузке в CRM он отрабатывал.

Далее дело техники, в моем случае мне надо было вывести древовидную структуру подразделений с ссылками на сами сущности.

Как сделать отчет с древовидной структурой написано здесь.

В отчете в свойствах нужного нам плейсхолдера на закладке General ставим Markup type — HTML, затем на закладке Action ставим Enable as an action: Go to URL.

Далее в поле Select URL: нужно вставить необходимую ссылку. Обычно ссылка выгладит так: «http://имя_сервера/имя_организации/biz/business/edit.aspx?id=%7bGuid_подразделения%7d» но в нашем случае сработает и вот так:  «http://имя_сервера/имя_организации/CRMReports/viewer/drillopen.aspx?LogicalName=businessunit&ID=%7bGuid_подразделения%7d«.

Для этого в поле Select URL: вставляем следующее выражение.

=Parameters!CRM_URL.Value + "?LogicalName=businessunit&amp;ID=" + Fields!businessunitid.Value.ToString()

Вот, собственно, и все.
Файл отчета можно скачать здесь.

Вместо параметра LogicalName можно использовать OTC, где LogicalName это имя схемы а OTC это код сущности. Полный список кодов можно посмотреть здесь http://msdn.microsoft.com/en-us/library/bb887791.aspx

Больше спасибо Андрею за наставление на путь истинный (см. комментарии).