Показаны сообщения с ярлыком plug-in. Показать все сообщения
Показаны сообщения с ярлыком plug-in. Показать все сообщения

2009-11-09

Salesorder Fulfill state

I was trying to catch the Fulfill state of sales order and my try was successful, with registering plugin to Update message at Child pipeline. But there was a one little problem - you can't get the salesorderdetail thru service. I don't know why, but the simple code like in "Example 1" was falling down with Generic SQL error x80044150.

//  Example 1
QueryByAttribute qba = new QueryByAttribute();
qba.EntityName = EntityName.salesorderdetail.ToString();
qba.ColumnSet = new AllColumns();
qba.Attributes = new string[] { "salesorderid" };
qba.Values = new object[] { soid };
BusinessEntityCollection bec = crmService.RetrieveMultiple(qba);


Then I was switch on the Fulfill Sdk Message and everything start working fine!
1. open up the SdkMessage view, filter by Name and find the SdkMessageId
2. open up the SdkMessageFilter view, filter by SdkMessageId you got from step 1.
3. change IsCustomProcessingStepAllowed to 'True'

update dbo.SdkMessageFilter
set IsCustomProcessingStepAllowed=1
where SdkMessageId=
(select top 1 SdkMessageId from dbo.SdkMessage where name ='Fulfill')

I have a question, why the Fulfill message has been unplugged from customization?

2009-10-29

How to determine on which pipeline a plugin is executing - Parent or Childe

The InvocationSource property is an integer value that you can use to determine whether the current plug-in is running in a child pipeline.

MessageInvocationSource Values
FieldValueDescription
Child 1 Specifies a child pipeline
Parent 0 Specifies a parent pipeline

2009-10-13

How to retrieve from many-to-many entity.

If you need to retrieve something from many-to-many relationship entity, and there is no such entity in CRM, then you should use the FetchXml, instead of Query.
// Base bodel of FetchXml for get relationship entity
string fetchXML =
"<fetch distinct='false' mapping='logical'>" +
"<entity name='" + relationshipName + "'><filter type='and'>" +
"<condition attribute='" + relatedEntityName + "id' operator='eq' value='" + relatedId + "' />" +
"<condition attribute='" + typeEntityName + "id' operator='eq' value='" + profileId + "' />" +
"</filter></entity>" + "</fetch>";

For example, next code snippet is checking a count of relations between the Account entity and New_Industry entity, wich relationship is many-to-many. To achieve this, I need to check the relationship entity - New_industry_account, wich is contains all of relation of our two entities.
ICrmService crmService = context.CreateCrmService(true);

// check for relation
#region FetchXML
string fetchXML =
"<fetch distinct='false' mapping='logical'>" +
"<entity name='new_industry_account'><filter type='and'>" +
"<condition attribute='accountid' operator='eq' value='" + accountid.ToString() + "' />" +
"</filter></entity></fetch>";
#endregion

string fetchresult = crmService.Fetch(fetchXML);
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(fetchresult);
XmlNodeList xnodlist = xmldoc.SelectNodes("resultset/result");
if (xnodlist.Count > 0) { return; }

2009-10-12

Registering plugins for event on many-to-many entities

I found that from the box MS CRM 4.0 are not supported the events on many-to-many relationship entities(bridge entities). But Aaron Elder has hack it.

All you need is just register it at AssociateEntities or DiassociateEntities event with empty Primary and Secondary Entity fields.

-- ============================================================================
-- Enable Associate and Disassociate Plug-in Events Script v1.0
-- ----------------------------------------------------------------------------
-- (c) 2009 Aaron Elder
-- ============================================================================
-- DISCLAIMER:
-- This script is provided "AS IS" with no warranties, and confers no rights.
-- ============================================================================
-- While this is obviously "unsupported", I think the fact that these events
-- are not available is a bug and hopefully it will be fixed in a rollup.
-- ============================================================================

USE AscentiumCrmDev_MSCRM
GO

-- Find the deployments SDK Filter ID for the
-- Associate and Disassociate Entity SDK Messages
DECLARE @DisassociateEntitiesFilterId uniqueidentifier
DECLARE @AssociateEntitiesFilterId uniqueidentifier
SET @DisassociateEntitiesFilterId = (SELECT SdkMessageId FROM SdkMessageBase WHERE [Name] = 'DisassociateEntities')
SET @AssociateEntitiesFilterId = (SELECT SdkMessageId FROM SdkMessageBase WHERE [Name] = 'AssociateEntities')

-- Enable the Associate and Disassociate Filters to be valid for custom processing
-- Custom Processing means "you register plug-ins against it"
-- Note: We only do this for the "generic" (OTC == 0) case, just to be safer
UPDATE SdkMessageFilterBase SET IsCustomProcessingStepAllowed = 1
       WHERE SdkMessageId = @DisassociateEntitiesFilterId AND PrimaryObjectTypeCode = 0


UPDATE SdkMessageFilterBase SET IsCustomProcessingStepAllowed = 1
       WHERE SdkMessageId = @AssociateEntitiesFilterId AND PrimaryObjectTypeCode = 0
(C)
http://consulting.ascentium.com/blog/crm/Post533.aspx

2008-09-17

Plugin's Starter Execution Method

Have found excellent method at David Fronk blog Starter Execution Method and slightly update it in to separate method.

// based on Starter Execution Method

/// 
/// Return the opportunity id
/// 
/// Plugin context/// opportunityid
public string GetEntityId(IPluginExecutionContext context)
{
 string opportunityid = "";
 DynamicEntity opportunityClose = null;

 switch (context.MessageName)
 {
  case "Create":
   if (context.OutputParameters.Properties.Contains("id"))
   {
    opportunityid = context.OutputParameters.Properties["id"].ToString();
   }
   break;
  case "Update":
   if (context.InputParameters.Properties.Contains(ParameterName.Target) && context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)
   {
    DynamicEntity entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];
    opportunityid = ((Key)entity.Properties["opportunityid"]).Value.ToString();
   }
   break;
  case "SetState":
   if (context.InputParameters.Properties.Contains("EntityMoniker"))
   {
    if (context.InputParameters.Properties.Contains("State"))
    {
     Moniker entityMoniker = (Moniker)context.InputParameters.Properties[ParameterName.EntityMoniker];
     opportunityid = entityMoniker.Id.ToString();
    }
   }
   break;
  case "SetStateDynamicEntity":
   if (context.InputParameters.Properties.Contains("EntityMoniker"))
   {
    if (context.InputParameters.Properties.Contains("State"))
    {
     Moniker entityMoniker = (Moniker)context.InputParameters.Properties[ParameterName.EntityMoniker];
     opportunityid = entityMoniker.Id.ToString();
    }
   }
   break;
  case "Win":
   opportunityClose = (DynamicEntity)context.InputParameters["OpportunityClose"];
   Lookup WonLook = (Lookup)opportunityClose.Properties["opportunityid"];
   opportunityid = WonLook.Value.ToString();
   break;
  case "Lose":
   opportunityClose = (DynamicEntity)context.InputParameters["OpportunityClose"];
   Lookup LoseLook = (Lookup)opportunityClose.Properties["opportunityid"];
   opportunityid = LoseLook.Value.ToString();
   break;
  case "Assign":
   if (context.InputParameters.Properties.Contains("Assignee") && context.InputParameters.Properties["Assignee"] is SecurityPrincipal)
   {
    Moniker assignEntity = (Moniker)context.InputParameters.Properties["Target"];
    opportunityid = assignEntity.Id.ToString();
   }
   break;
  case "Delete":
   if (context.InputParameters.Properties.Contains("Target"))
   {
    Moniker entityMoniker = null;
    entityMoniker = (Moniker)context.InputParameters.Properties[ParameterName.Target];
    opportunityid = entityMoniker.Id.ToString();
   }
   break;
 }
 return opportunityid;
}

2008-09-09

Сущность template, plug-in и Javascript

Начну с того, что сущность template не изменяемая, но настраиваемая. Однако, это не преимущество. Я не понимаю по каким причинам сущность template была скрыта от нас, но ни форма ни события на ней нам недоступны.
Более того, plug-in на шаг PreCreate для этой сущности дает поразительный результат - при выбрасывании InvalidPluginExecutionException стирает тело и тему самого template.
А задача простая - не дать или хотя бы уведомить пользователя о том, что шаблон с таким именем уже существует. Ну чтобы пользователь потом не выбирал из десятка шаблонов с одинаковым title.
Как же решить? Plug-in не функционален, скрипт вставить некуда. Решил внедриться в оригинальную страничку. Называется она emailtemplateeditor.aspx. Там есть функция Save в нее и написал свой код.
Но проблема обнаружилась и тут. Оказывается CRM не дает обратиться к значению поля templateid (нужно, чтобы title не сравнивать с самим собой), т.е. crmForm.all.templateid.DataValue не работает.
Что ж решил действовать старым проверенным способом через crmForm.FormType, чтобы узнать создаем новый шаблон(FormType=1) или редактируем старый(FormType=2). И тут Microsoft окончательно убедил меня в том, что его сотрудники видимо произошли не от углеродной формы жизни - FormType=0 (Undefined form type). Т.е. согласно SDK сущность создана для системных нужд "For internal use only".

Спасло свойство crmForm.ObjectId, которое и содержало значение поля templateid.
В результате скрипт был закончен и стал иметь такой вид:


var url = "http://mcrm4/wsTemplateCreateCheckDup/CheckTemplateTitleDup.asmx/IsDup";
var templateid="null";
if (crmForm.ObjectId != null)
{
templateid=crmForm.ObjectId;
templateid=templateid.slice(1, templateid.indexOf('}'));
}
var paramstr = "TemplateTitle="+crmForm.all.title.DataValue+"&"+"TemplateId="+templateid;
try{
var oXmlHTTP = new ActiveXObject("Msxml2.XMLHTTP");
oXmlHTTP.open("POST", url,false);
oXmlHTTP.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
oXmlHTTP.send(paramstr);
if(oXmlHTTP.status == 200)
{
var retstr=oXmlHTTP.responseText;
var retidx=retstr.indexOf(">found");
if(retidx>0)
{
alert("Имя шаблона '"+crmForm.all.title.value+"' уже есть в списке.\n Пожалуйста, задайте другое имя.");
SubjectEditor.focus();
event.returnValue=false;
return false;
}
}
else
{
alert("Ошибка "+ oXmlHTTP.status+" при проверке на повторное использование имени шаблона.");
}
}
catch(e){alert(e);}

2008-08-18

Plug-in signing

При разработке plug-in'а, нужно обязательно задавать сильное имя(strong name) и не забывать при релизе переподписывать(re-sign) библиотеку, если был выставлен атрибут Delay Sign Only.

sn - R <assembly> <keyfile>

"Not have enough privilege to complete Create operation for an Sdk entity" при регистрации плагина в CRM 4.0

Эта ошибка возникает даже если пользователь обладает ролью System Administrator. Причина её возникновения, в том, что пользователь не является членом группы Deployment Administrators group в утилите CRM Deployment Manager.

Вы написали plug-in и пытаетесь его зарегистрировать. Не важно, делаете ли вы это с помощью Plug-in Registration Tool из SDK или с помощью собственного инсталлера, однако, даже если вы Системный Администратор, то возможно вам все-равно не удастся зарегистрировать plug-in, и вы получите следующую ошибку:

"Not have enough privilege to complete Create operation for an Sdk entity"

Оказывается System Administrator больше не царь и бог, и эта роль не обладает привилегиями развертывания(deploy). Есть отдельная роль Deployment Administrator. Изначально ее имеет тот, кто устанавливал CRM на локальную машину.
Чтобы регистрировать плагины, понадобится добавить пользователя из-под которого происходит регистрация в Deployment Manager.

  1. Откройте CRM 4.0 Deployment Manager (Start -> All Programs -> Microsoft Dynamics CRM -> Deployment Manager)

  2. Добавьте пользователя из Active Directory в Deployment Administrator'ы


После этого попробуйте еще раз зарегистрировать свой plug-in.