“Cannot insert duplicate key” cuando clonas una actividad

En más de un proyecto nos hemos visto obligados a tener que clonar/duplicar actividades. Un motivo que nos encontramos a menudo para hacer esto es que el campo Regarding (Referente a) está limitado a un solo registro, por ejemplo, un caso. Pero existen circunstancias en las que nos gustaría que un email -o culaquier otra actividad- se quedase como referente a más de un elemento, como podrían ser una sesión de USD y todos los casos creados en la misma sesión. Supongamos que un email de un cliente incluye varias incidencias. Cada una de ellas debería tratarse como un caso separado, con un SLA distinto, y también es posible que cada uno de los casos sea resuelto por un departamento distinto de la empresa. Para que en cada uno de los casos quede guardada toda la traza de emails/tareas en el panel de actividades nos vemos obligados a duplicarlos. Dejando el motivo un poco de lado, si nos vemos en la necesidad de duplicar una actividad, nos daremos cuenta que al intentar hacerlo con la SDK nos encontramos con el error “Cannot insert duplicate key”.

Un ejemplo de código que devuelve ese error sería el siguiente:

Al ejecutarlo obtenemos el error de clave duplicada:

Error "Cannot insert duplicate key" al crear actividad duplicada

Error “Cannot insert duplicate key” al crear actividad duplicada

 

El origen del error está en los campos de tipo PartyList que tienen todas las actividades en los campos “from”, “to”, “cc” y otros. Al intentar duplicar la actividad estamos duplicando también las referencias a estos campos, y nos encontramos con que estas referencias ya existen. Para ello solo tenemos que regenerar los IDs de los campos PartyList de la actividad origen, generando nuevos IDs para que no esté duplicados.

Para ello, antes de duplicar todos los campos en la actividad clonada pasamos un proceso de regeneración de IDs mediante la funcion RegenerateIdsActivityPartiesInActivity:

 

Con esto podremos completar la clonación correctamente.

 

Versión 3.2 de USD

Tenemos nueva versión de Unified Service Desk para CRM Dynamics. Hoy jueves Microsoft ha liberado la última versión 3.2 de USD, su famoso cliente pesado de escritorio que promete mejoras en grandes puntos flacos de antiguas versiones.

En particular, se han centrado en cuatro puntos: mejora de rendimiento para Hosted Control de tipo CRM Page, mejoras de rendimiento para diagnósticos de error, solución de errores fatales de Hosted control de tipo CRM Page o Web Application en modalidad Internet Explorer Process, y mejora seguridad.

Mejoras de rendimiento en CRM Page

Microsoft nos promete que la nueva versión de USD mejorará el rendimiento en uno de sus grandes puntos críticos: la carga de formularios CRM.

Todos los que hemos trabajado con USD nos hemos enfrentado a este problema. Muchas veces, cuando solicitamos que se abra un registro de CRM observamos que los tiempos de carga son en ocasiones muy elevados (varios segundos). Hasta el momento podiamos utilizar “trucos” para mejorar este rendimiento, como precargar en backgroud el formulario para agilizar el siguiente proceso de carga real.

Con la nueva versión se introduce una nueva Option global de configuración: InternetExplorerPooling. Añadiendo esta Option a la configuración de USD, con valor true, y siempre que hayamos actualizado la versión de USD instalada en nuestro PC, podremos activar esta subida de rendimiento de la que hablamos.

Es importante indicar que por defecto este proceso está desactivado.

En particular, para activar el incremento de rendimiento tendremos que incluir esta Option en nuestra configuración del siguiente modo:

Desde el menú superior navegamos a la configuración USD desde Configuracion -> Unified Service Desk.

Configuración de USD

Configuración de USD

Una vez ahí, entramos en las Options:

Opción InternetExplorerPooling=true

Opción InternetExplorerPooling = true

Creamos una nueva Optiton y le ponemos como nombre InternetExplorerPooling y como valor true

Opciones globales de USD

Opciones globales de USD

Guardamos y listo. Únicamente tendríamos que añadir la Option a la Configuración de USD que deseemos para poder empezar a notar las mejoras de rendimiento.

Tenéis más info del proceso en este link.

 

Mejoras de rendimiento para diagnósticos de error

La nueva versión de USD también trae mejoras en la gestión del rendimiento de la aplicación. En particular, con la nueva versión se podrá activar bajo demanda la monitorización del rendimiento de la aplicación mediante el shortcut Ctrl + Alt + Q y pararlo mediante Ctrl + Alt + P.

Mientras que esté activo, el proceso de diagnóstico generará trazas sobre el rendimiento de la aplicación que se guardarán en la carpeta %APPDATA%\Roaming\Microsoft\Microsoft Dynamics 365 Unified Service Desk\ para poder ser consultados posteriormente.

Puedes leer más información al respecto en el siguiente link.

 

Control de errores fatales de procesos de IE

Otra mejora que trae la nueva versión es la gestión de errores fatales de procesos de Internet Explorer. En particular, con la actualización el USD será capaz de recuperarse ante los siguientes escenarios:

  • Cuando el Internet Explorer cierra de manera inesperada
  • Cuando manualmente terminas un proceso de IE desde el administrador de tareas
  • Cuando un script dentro de un Hosted Control sobrepasa el tiempo establecido de timeout de ejecución.

Para todas estas situaciones, el nuevo USD será capaz de recuperar la instancia, manteniendo las propiedades de la misma como páginas abiertas, urls etc.

En este caso, por defecto el USD tendrá activa esta funcionalidad. Si se desea desactivar se podrá hacer incluyendo una Option global denominada IEWebPageRecovery con valor false.

Option global IEWebPageRecovery=false

Option global IEWebPageRecovery = false

Puedes leer más información en el siguiente link.

Mejoras en seguridad

En la publicación de Microsoft nos indican que se han mejorado algunos puntos de seguridad del USD aunque no especifica cuales.

 

Podéis descargar la última versión de USD en este link.

Fuente: MSDN Microsoft

Request directa a WEB API de CRM Online mediante Azure AD

Cómo hacer una petición directa a la WEB API de CRM para la versión Online mediante Azure AD.

Normalmente cuando queremos hacer una consulta a nuestra base de datos en CRM Dynamics utilizamos la SDK que Microsoft nos proporciona y escribimos un código donde instanciaremos el objeto OrganizationService desde el cual podremos ejecutar un RetrieveMultiple para obtener la información que deseemos de nuestro CRM.

Cuando disponemos de un CRM On Premise podemos también recurrir a la invocación de webservices con autenticación delegada o NTLM, cosa que no podemos hacer en el CRM Online.

Pero ¿qué ocurre si la aplicación que estamos desarrollando no está ideada en C#? En principio no podremos usar la SDK que nos proporciona Microsoft para integrarnos con el CRM. Una solución podría ser utilizar Xamarin para compilar nuestro código en otro lenguaje que necesitemos pero Xamarin no cubre todo el abanico de lenguajes. ¿Cómo podríamos integrarnos con un CRM Online si nuestra aplicación está hecha en PHP, NodeJS, Javascript, etc? ¿Podríamos conectar un CRM Online con otro CRM Online mediante Javascript y sin utilizar un Middleware?, ¿Un sistema tercero podría conectarse con nuestro CRM para enviar información sin necesidad de un Middleware? ¿Un JIRA podría integrarse con nuestro CRM directamente? La respuesta es que sí. Podemos realizar una petición a la API Rest de CRM directamente desde cualquier entorno utilizando el protocolo OAuth2 del Azure Active Directory.

Existe un modo genérico de integrarse con un CRM Online que podemos explotar desde cualquier lenguaje de programación que nos proporcione herramientas para ejecutar peticiones web.

El proceso es el siguiente: primero lanzamos una petición a Azure con nuestros credenciales y la URL del CRM sobre el que queremos ejecutar la query y será el propio Azure el que nos los validará. Si son correctos, Azure nos responderá a nuestra petición con un token. Este token es un “codigo de acceso temporal” a nuestro CRM. Podremos utilizar este código para hacer queries al CRM durante un tiempo limitado (normalmente 3600 segundos) y después caducará. Podremos entonces volver a pedir otro token y repetir el proceso.

Tenemos pues 2 peticiones web: obtener el token y realizar la query en CRM.

Vamos a ver cómo configurarlo paso a paso. Lo primero de todo que tenemos que tener en cuenta es que nuestro Dynamics 365 Online tendrá asociado siempre una cuenta de Azure.

Accedemos a nuestro CRM Online:

Página de inicio de CRM Dynamics 365

Página de inicio de CRM Dynamics 365

 

Una vez logados en nuestro CRM accedemos a Azure mediante el portal de acceso: https://portal.azure.com. El usuario con el que nos hará automáticamente el login será el mismo que nuestro CRM Online.

Página de inicio de Azure

Página de inicio de Azure

 

Accedemos a la sección Registro de aplicaciones mediante la barra de búsqueda superior.

Búsqueda de "Registro de aplicaciones" en azure

Búsqueda de “Registro de aplicaciones” en azure

 

Accedemos a Puntos de conexión en la parte superior.

Puntos de conexiones o "endpoints"

Puntos de conexiones o “endpoints”

 

Bajamos un poco y copiamos la URL del Punto de conexión de token de OAuth 2.0. La URL será del estilo: https://login.microsoftonline.com/d627e6dd-343b-4323-8d8d-b9381c722c7a/oauth2/token

Descripción de puntos de conexión. Token de OAuth 2.0

Descripción de puntos de conexión. Token de OAuth 2.0

 

Bien, esta será la URL a la que tendremos que solicitar el token. Nos la guardamos para más adelante.

Volvemos a Registro de aplicaciones y creamos una nueva aplicación.

Nuevo registro de aplicación

Nuevo registro de aplicación

 

De nombre pondremos “Acceso API CRM”, tipo de aplicación “Aplicación web o API” y URL de inicio de sesión podremos la URL de nuestro Dynamics:

Detalles de la nueva aplicación

Detalles de la nueva aplicación

 

Una vez creada la buscamos entre la lista y la abrimos. Vamos al apartado Propiedades y copiamos el código  Id de aplicación. Este ID lo tendremos que incluir también en petición de token. Lo guardamos junto con la URL.

Propiedades de la nueva aplicación

Propiedades de la nueva aplicación

 

Vamos al apartado Claves:

Claves de la aplicación

Claves de la aplicación

 

Creamos una nueva Clave con descripción “Token” y que expire en 1 año (eres libre de seleccionar lo que más te convenga en este punto). Pulsamos “Guardar” y nos aparecerá el valor (denominado secreto). Debemos copiarlo y guardarlo en este instante pues no tendremos otra posibilidad de volver a conseguirlo:

"Secreto" del token de la nueva aplicación

“Secreto” del token de la nueva aplicación

 

Vamos al apartado Permisos necesarios y pulsamos en Agregar.

Agregar permisos de aplicación

Agregar permisos de aplicación

 

Como API seleccionamos Dynamics CRM Online.

Seleccionar aplicación Dynamics CRM Online

Seleccionar aplicación Dynamics CRM Online

 

Como permisos seleccionamos Access CRM Online as organization users.

Seleccionar permisos delegados

Seleccionar permisos delegados

 

Una vez agregado tenemos que pulsar “Conceder permisos” en la parte superior. Importante: si estás en un Azure de una compañía esta acción la tendrá que realizar un administrador.

Conceder permisos

Conceder permisos

 

Con esto ya tenemos configurado lo necesario en Azure.

Los datos que tendremos por el momento son los siguientes:

Dato Valor Descripción
URL OAuth2.0  https://login.microsoftonline.com/d627e6dd-343b-4323-8d8d-b9381c722c7a/oauth2/token La URL de petición de token
URL CRM  https://micrm.crm4.dynamics.com/ La URL de nuestro CRM
Usuario CRM  ddiaz@micrm.onmicrosoft.com Usuario de nuestro CRM
Password CRM  MiPassw0rd! El password de nuestro usuario
Id Aplicación  b68b3b21-9f40-4923-85e1-7f8e3bc5b746 Id de la aplicación creada en Azure
Secreto  I/sRo1MyR6wRFUS9RgYnCJmCMWiIbq2IPx9evCm45Qc= Disponible solo por 1 año

Con esta información ya podemos ejecutar una query sobre nuestro CRM.

 

Peticiones web

Obtención de token

Los datos que tenemos que incluir en la petición de token son los siguientes

Nombre Key Value
ID Cliente client_id b68b3b21-9f40-4923-85e1-7f8e3bc5b746
URL CRM* resource https://micrm.crm4.dynamics.com/
Usuario* username ddiaz@micrm.onmicrosoft.com
Contraseña* password MiPassw0rd!
Tipo acceso grant_type password
Secreto client_secret I/sRo1MyR6wRFUS9RgYnCJmCMWiIbq2IPx9evCm45Qc=

(*) Los valores tendrán que ser incluidos con encodeURIComponent, por ejemplo el usuario ddiaz@micrm.onmicrosoft.com quedaría como ddiaz%40micrm.onmicrosoft.com

La petición que tendremos que lanzar será

Un ejemplo de petición con datos reales

La respuesta a la petición será como la siguiente:

Y el parámetro en cuestión que nos interesa es el access_token. Con esto ya tenemos el token.

 

Ejecución de query

Una vez que tenemos el token podemos acceder a la WEB API de CRM invocando cualquiera de sus métodos, siempre teniendo en cuenta el nivel de seguridad del usuario para el que hemos obtenido el token. Esto significa que si el usuario ddiaz@micrm.onmicrosoft.com no tiene permisos en CRM para editar contactos, no podrá ejecutar una petición de actualización de un contacto.

Otro punto importante es que mediante este método solo podremos acceder a la WEB API de tipo Rest de CRM y no podremos invocar peticiones directas al OrganizationService con SOAP.

La petición que hagamos tendrá que incluir en el header la propiedad “Authorization” con el valor “Bearer:” + token. Esto será suficiente para obtener acceso a la base de datos de CRM.

El ejemplo que vamos a mostrar es una petición a la API Rest para crear un contacto en CRM.

La petición sería:

 

La URL a donde  enviaremos la petición es de la API Rest de CRM. En particular nos referimos a la entidad “Contacto”. Como vemos, en el header incluimos el token con la palabra “Bearer:” antes. El método POST indica que queremos crear un registro de la entidad. Y en el objeto Data incluimos los logicalName de los campos de la entidad y sus correspondientes valores.

Para sacar partido a la WEB API de CRM existe mucha documentación oficial al respecto para ejecutar cualquier acción sobre nuestra base de datos.

 

Resultados

He implementado un pequeño código de ejemplo en Javascript para hacer la prueba. En particular he utilizado la librería Angular para realizar las peticiones http, pero podría implementarse sin Angular o con cualquier otro framework.

Los archivos que he utilizado son los siguientes:

Archivos del ejemplo de autenticación por OAuth 2.0

Archivos del ejemplo de autenticación por OAuth 2.0

El framework de Angular lo podéis descargar del sitio oficial. Los archivos indexOauth.html y js_api_rest_oauth.js los detallo a continuación:

indexOauth.html:

js_api_rest_oauth.js:

El resultado de la ejecución del código anterior genera la siguiente respuesta:

Resultados de ejecución del ejemplo

Resultados de ejecución del ejemplo

Si lo ejecutáis desde vuestro PC en local es posible que obtengáis el error de No ‘Access-Control-Allow-Origin’. Para solucionarlo existen extensiones de Google Chrome que lo resuelven.

Como veis hemos creado un contacto en CRM a través de un token obtenido mediante OAuth del Azure Active Directory ejecutando una petición a la WEB API.

Espero que os sirva!

 

Obtener el nombre del workflow padre con ParentContext

Cuando desplegamos un plugin o un workflow en nuestra instancia de CRM Dynamics estamos activando un flujo de trabajo que se ejecutará ante un determinado evento. En el caso del workflow podemos configurar mediante el menú de edición de Dynamics unas condiciones extra que se deben de dar para que se ejecute el código –como podría ser que determinado campo del registro modificado esté valorizado-.

Podría darse el caso de que este registro del que hablamos sea modificado por el usuario mediante un formulario de CRM, y además también podría ser modificado mediante la ejecución de otro workflow o plugin de, por ejemplo, actualización de datos nocturnos. Y rizando el rizo podemos suponer que un workflow puede llamar a otro, y este a otro, y así repetidas veces, pudiendo encontrar problemas si no controlamos bien el contexto en el que se ejecuta nuestro código.

En la ejecución de un Workflow o plugin siempre podemos obtener el objeto Context que vendrá heredado y que contendrá información sobre la profundidad –accesible mediante la propiedad Depth del objeto- además de otros parámetros de interés.

Cuando la profundidad es igual o mayor a uno implica que la ejecución de nuestro código ha sido invocada desde otro workflow o plugin. Cuando esto ocurre,  podremos acceder al ParentContext que será un objeto del mismo tipo pero que contendrá información del contexto del workflow que precede al nuestro que incluso podría darse el caso de que sea el mismo workflow.

La idea es poder obtener el nombre literal de ese workflow padre para poder poder condicionar la ejecución de nuestro workflow mediante un if.

Nombre literal del workflow en la entidad "Trabajos del sistema"

Nombre literal del workflow en la entidad “Trabajos del sistema”

Como hemos dicho, el ParentContext contiene información del contexto del workflow invocador de nuestro workflow, y entre otros datos contiene el parámetro CorrelationId que sería el id único del registro de tarea del sistema creado por este workflow. Con este id seremos capaces de obtener el nombre de éste workflow y condicionar la ejecución, como se indica en el siguiente ejemplo:

Peticiones SOAP con AngularJS en CRM Dynamics

Soap request con AngularJS en Microsoft Dynamics CRM

La librearía AngularJS nos proporciona un potente framework de herramientas y procedimientos con los que implementar páginas web basadas en MVC. Toda esta funcionalidad se puede también utilizar dentro de nuestros recursos web –web resources- en CRM. En este post vamos a ver cómo implementar llamadas Soap con AngularJS y encadenar respuestas con los objetos promises.

Como sabemos, cuando se hace una query a CRM desde el frontal web utilizando la API debemos trabajar con funciones Callback –ya sea para peticiones utilizando la librería SDK.Rest o peticiones Soap a través del Organization Service- y en determinadas ocasiones, cuando tenemos que encadenar varias queries una detrás de otra el modo común de realizarlo es anidando Callbacks:

Con la librería AngularJS, los objetos promises que veremos a continuación y los servicios “$http” y “$q”, este ejemplo anterior se transformaría en algo del estilo:

Como vemos, el código queda más limpio en el segundo caso. Además de dejar el código más bonito la librería angular nos ofrece una mayor libertad para controlar procesos asíncronos permitiéndonos acceder al proceso de carga o ejecutando varias peticiones en paralelo.

Recordemos que para realizar una llamada Soap necesitamos tres cosas: la URL del endpoint, la acción que vamos a utilizar, y el cuerpo del mensaje. Con estas tres cosas podemos realizar la petición Soap con AngularJS.

Para construir la llamada utilizaremos el servicio “$http” de Angular como se muestra en el ejemplo.

La propiedad data del objeto response en el primer then del método RequestData contendrá el resultado de la petición. En este caso el cuerpo del mensaje es una petición de RetrieveMultiple con un filtro y nos devolverá las cuentas cuyos nombres empiecen por “A”. Recordar que esta es una respuesta http y por defecto el endpoint de CRM que estamos atacando nos devolverá mensajes serializados en XML -no es posible cambiar la respuesta a JSON como sí permite la API REST OData-. Para deserializar el XML podemos recurrir a herramientas externas o implementar nuestro propio deserializador utilizando la versión reducida de jQuery que trae angular, analizando los nodos. Yo he utilizado como base este ejemplo con una pequeña modificación.

El resultado de la ejecución nos mostraría un array de todos los Ids de las cuentas que empiezan por “A” en nuestro CRM.

Ids de todas las cuentas de CRM que empiezan por “A”

Pero donde realmente se puede sacar partido a esta librería es en el uso del servicio “$q”. Este servicio nos permite añadirle N objetos promesa y ejecutarlo. El código que esté dentro del then sucesivo se ejecutará cuando todas las peticiones hayan terminado y nos devolverá N respuestas, una para cada una de las peticiones ejecutadas. Esto es una gran noticia puesto que nos avisará cuando todas las peticiones tengan respuesta, y no se ejecutará cada vez que termine una de ellas. Así permitirá controlar el flujo de información de nuestro código de una manera mucho más limpia y clara.

Siguiendo con el ejemplo anterior, imaginemos que después de obtener todas las cuentas que empiecen por “A”, para cada una de ellas queremos obtener el nombre de todos los contactos asociados a esas cuentas.

El resultado de esta ejecución nos proporcionaría el siguiente array de nombres:

Nombres de todos los contactos que pertenecen a alguna de las empresas del ejemplo anterior

Nombres de todos los contactos que pertenecen a alguna de las empresas del ejemplo anterior

Estos son sólo dos ejemplos de lo que esta librería puede ofrecernos en CRM Dynamics  pero realmente las posibilidades son infinitas cuando juntamos peticiones Soap con AngularJS.

String connection CRM

 

String connection

Las string connections son variables de tipo string que contienen los datos necesarios para realizar una conexión a un entorno. Se utilizan mucho para, por ejemplo, conectarse a una base de datos. Estas cadenas de texto contienen todos los datos necesarios para que el cliente que realiza la petición sepa a qué base de datos debe conectarse y bajo qué usuario y password debe realizarlo. Son muy útiles porque nos permiten definir todos los datos necesarios para conectarse en una única variable, lo que simplifica el mantenimiento de la aplicación.

CRM Dynamics no es distinto y nos ofrece la posibilidad de conectarnos a una instancia de CRM únicamente indicando la string connection de esa instancia. El procedimiento ha cambiando un poco desde la versiones antiguas de CRM y la nueva versión 365 pero en esencia es igual. Vamos a ver cómo utilizar una string connection desde una aplicación hecha en C#:

SDK CRM 2011, 2013, 2015 y 2016

 

SDK CRM 365

 

Las diferencias entre las versiones son simplemente en el objeto que se instancia y de qué clase proviene. En las versiones antiguas se generaba la interface IOrganizationService desde el objeto OrganizationService mientras que en la nueva versión 365 el IOrganizationService se instancia desde el objeto CrmServiceClient del namespace Microsoft.Xrm.Tooling.Connector. También observar que la string connection para 365 tiene el parámetro extra de Authtype.

Cómo construir nuestra propia String Connection

Las String Connection por desgracia no son siempre iguales. En función del tipo de instancia que tengamos, o del tipo de autentificación se construirán de un tipo u otro. Vamos a ver qué parámetros se pueden incluir y algunos ejemplos:

 

Nombre de parámetro Parámetro Descripción
Server, Url, or Service Uri Url, Server, ServiceUri  La URL de nuestro CRM.

  • Si es una instancia on premise, la URL debería incluir la organización. Por ejemplo http://servidorcrm:puerto/nombre-organizacion.
  • Si es una instancia online, la URL será la que utilizamos para acceder. Por ejemplo: http://nuestra-organizacion.crm4.dynamics.com/

Para la string connection, funcionará igual si utilizamos la palabra Url, Server o ServiceUri

Domain Domain  El dominio de nuestra organización. Estaría relacionado con el Active Directory si estamos hablando de una instancia on premise. Para los online no es necesario incluir este parámetro.
Username or User ID Username Nuestro nombre de usuario.
Password Password Nuestro password. Podemos incluir dobles comillas al inicio y al final si nuetro password incluye espacios u otros caracteres especiales.
Timeout Timeout Por defecto el timeout de las conexiones a CRM son 2 minutos, pero si lo deseamos podemos aumentar o reducir esta cantidad. El formato sería timespan del tipo “hh:mm:ss”.
 Authtype Authtype  Necesario solo para instancias CRM 365. Si tenemos un CRM Dynamics 365 la string connection deberá incluir este parámetro. Los valores que puede tomar son:

  • Authtype=”Office365″ si nuestro CRM 365 es Online
  • Authtype=”AD” si nuestro CRM es on premise y validamos los credenciales contra el Active Directory
  • Authtype=”IFD” si nuestro CRM es on premise y validamos los credenciales mediante IFD

Los parámetros deberán incluirse uno detrás de otro separados por punto y coma en formato texto dentro de nuestra String. No son necesarias las comillas para indicar un parámetro a no ser que el valor de éste incluya carácteres especiales o espacios. Para las versiones on premise es posible también conectarse utilizando seguridad integrada de Windows, no incluyendo los parámetros Username y Password en la string connection. Si hacemos esto, la ejecución de las peticiones se realizará con el usuario que esté ejecutando la sesión de windows.

Algunos ejemplos:

CRM 365 online

“ServiceUri=https://ourorganization.crm4.dynamics.com/; Username=name@ourorganization.onmicrosoft.com; Password=OurPassw0rd; Authtype=Office365;”

 

CRM 365 on premise

“ServiceUri=http://ipcrm:5555/OrganizationName; Domain=ourdomain; Username=username; Password=OurPassw0rd; Authtype=AD;”

 

CRM 2011/2013/2015/2016 online

“ServiceUri=https://ourorganization.crm4.dynamics.com/; Username=name@ourorganization.onmicrosoft.com; Password=OurPassw0rd;”

 

CRM 2011/2013/2015/2016 on premise

“ServiceUri=http://ipcrm:5555/OrganizationName; Domain=ourdomain; Username=username; Password=OurPassw0rd;”

 

CRM 365 on premise con seguridad integrada

“ServiceUri=http://ipcrm:5555/OrganizationName; Authtype=AD;”

 

CRM 2011/2013/2015/2016 on premise con seguridad integrada

“ServiceUri=http://ipcrm:5555/OrganizationName;”

 

Fuentes: Microsoft, Ax3Group

Descargar un Workflow o Plugin de CRM

En este post vamos a ver cómo descargar un Workflow o Plugin registrado en CRM. Sería algo así como el proceso inverso que realiza el Plugin Registration Tool que trae la SDK de CRM. Mediante esta herramienta podemos registrar (o subir) ensamblados DLL a CRM pero no se nos permite descargarlos.

Para hacer la prueba vamos a crear un Workflow muy simple, lo vamos a registrar y a continuación lo vamos a descargar. El Workflow únicamente escribirá un nuevo registro dentro de una entidad de Log denominada new_log.

Además, para tener una pequeña verificación de que el Workflow que nos descargaremos luego sea el mismo que hemos registrado voy a darle una versión de compilado a la DLL con el valor 3.1.4.1.

Versión compilado Visual Studio

Versión compilado Visual Studio

 

Recordad que además para que un Workflow pueda ser registrado debe contener firma. En las propiedades del proyecto, en el apartado Firma (Signing) añadimos una nueva firma.

Añadir firma a un Workfow

Añadir firma a un Workfow

Compilamos y obtenemos la DLL con nuestro Workflow.

DLL del Workflow compilado

DLL del Workflow compilado

Abrimos el Plugin Registration Tool y registramos nuestro Workflow en nuestra organización.

 Workflow registrado en CRM

Workflow registrado en CRM

Hasta aquí sería el trabajo que solemos hacer para cargar un ensamblado en CRM. Ahora vamos a ver cómo descargarlo. Lo primero de todo es entender cómo se guardan en CRM los ensamblados. Un ensamblado se guarda en la base de datos del mismo modo que cualquier otro registro cuando seleccionamos este modo de registro en el Plugin Registration Tool en la opción Location : Database, que es la opción por defecto. En este caso cuando registramos una DLL en CRM se creará un registro que contendrá, entre otros datos, el contenido del ensamblado. La entidad donde se guardan los ensamblados tiene como nombre lógico (logical name) pluginassembly y, como el resto de entidades, cada registro tendrá un ID único. Para conocer el ID debemos ir a CRM y mediante una búsqueda avanzada a la entidad Ensamblados de Complementos encontraremos el nuestro Workflow. El ID lo podemos obtener abriendo la consola del navegador y localizando la linea en el código.

Obtener un ID de un registro mediante búsqueda avanzada

Obtener un ID de un registro mediante búsqueda avanzada

Ahora, teniendo la entidad y el ID único podemos hacer un Retrieve. Los campos que debemos descargar son el nombre (name) y el contenido del ensamblado (content). Este segundo campo contiene el contenido de la DLL codificado en Base 64. Desde C# es sencillo decodificarlo mediante la clase Convert.

Con todo esto ya podemos descargar y guardar en un archivo la DLL, como en el siguiente ejemplo:

Si ejecutamos ese comando y abrimos la carpeta donde está el ejecutable encontraremos la DLL y podremos comprobar la versión.

DLL descargada con su versión

DLL descargada con su versión

 

SDK Rest Retrieve con Expand

La librería SDK.REST es la mejor herramienta que tenemos para hacer queries a CRM desde Javascript. Esta librería ofrece una serie de métodos con los cuales podemos interactuar con la Api REST de CRM. Uno de esos métodos es el Retrieve que incluye el parámetro Expand que vamos a analizar.

Con el método Retrieve podemos obtener un registro de CRM indicándole la entidad y el Id del mismo. Además es siempre recomendable que le indiquemos los campos que necesitamos puesto que por defecto te devolverá todos, cosa que en la mayoría de los casos no necesitaremos y además ahorraremos recursos.

La definición del método RetrieveRecord según la librería es el siguiente:

El primer y el segundo parámetro son el Id y entidad del registro que queremos recuperar. Es importante notar que el nombre de la entidad es el Nombre de Esquema (Schema name) que se escribe con mayúsculas/minúsculas según está definido en la solución.

El tercer parámetro son los campos que queremos recuperar. En este caso sería una cadena de texto con los nombres (de esquema) de los campos que queremos obtener, separados por comas.

Las dos ultimas, son las funciones Callback de Exito/Error que se ejecutarán si el registro se ha devuelto bien o si se ha encontrado algún error respectivamente.

Finalmente, como cuarto parámetro tenemos el Expand. El Expand es una cadena de texto donde se incluirán los nombres de las relaciones que queremos expandir siempre en referencia al registro que estamos pidiendo. Se podrán incluir relaciones 1:N y N:1. En cada caso obtendremos un objeto distinto como resultado.

Relaciones 1:N de la entidad Account

Relaciones 1:N de la entidad Account

Es interesante recalcar que con los Expands no se indican qué campos de la entidad expandida quieres, sino que te devuelve todos. Es por esto que aunque en la definición de la librería diga que se pueden hasta expandir 6 relaciones, en mi opinión no es nada recomendable puesto que te devolverá en una sola llamada un volumen muy grande de datos [ 1 registro padre + (N1 registros hijos relación 1 * M1 campos cada registro)  + (N2 registros hijos relación 2 * M2 campos cada registro) + …  ]. Si multiplicamos esto por cada usuario de CRM que esté trabajando podemos sobrecargar el sistema.

Expand con relación 1:N

Un ejemplo sería obtener una cuenta y además todos los contactos asociados a la cuenta. Como es lógico, cuando hacemos esto obtenemos 1 resultado padre, la cuenta, y N hijos, todos los contactos relacionados con esa cuenta. La petición y deserialización sería del siguiente modo:

Como vemos en la siguiente imagen, el objeto response que nos devuelve como parámetro en el SuccessCallBack tiene primero los campos pedidos en el Select del registro padre (cuenta) y además, colgando del objeto contact_customer_accounts.results tenemos un Array con cada uno de los resultados hijos (contactos) y todos sus atributos.

Estructura del objeto response en un Retrieve con Expand 1:N

Estructura del objeto response en un Retrieve con Expand 1:N

Para recorrer el array de respuesta lo haremos como en se muestra en el código superior, con un bucle For que recorra todos los results de la expansión.

Expand con relación N:1

Ahora, en el caso opuesto, vamos a expandir una relación N:1. Esto es, queremos el padre del registro que estamos solicitando. Como no puede ser de otra manera, en este caso solo esperamos que nos devuelva un resultado y no N como en el ejemplo anterior.

Siguiendo con el ejemplo anterior, vamos a expandir la relación de “Contacto principal” de una Cuenta.

El código quedaría algo del estilo:

En este caso, al disponer de un solo resultado en la expansión, se ahorra el results del objeto account_primary_contact dentro del objeto response.

Estructura del objeto response en un Retrieve con Expand N:1

Estructura del objeto response en un Retrieve con Expand N:1

Expand con relación N:N

Desconozco si es posible expandir una relación N:N pero todas las pruebas que he hecho no han funcionado. Si alguien sabe cómo hacerlo le agradecería que indicase cómo en los comentarios!

Mezclando expansiones

Como hemos indicado con anterioridad, es totalmente posible expandir varias relaciones en la misma petición (hasta 6) y por cada una de ellas obtendremos un objeto colgando del response con el nombre de la relación. Si es 1:N además éste contendrá un Array denominado results con todos los hijos del registro.


Para terminar, indicar con la extensión para Chrome HUDCRM, en la pestaña “Query constructor” podéis obtener todos estos códigos mostrados en el post de manera muy sencilla.

Ejemplo constructor de queries SDK.REST con la extensión para Chrome HUDCRM

Ejemplo constructor de queries SDK.REST con la extensión para Chrome HUDCRM

 

Parametros que se envian a Web Resource desde formulario

Cuando creamos un Web Resource desde el menú de edición del formulario de la entidad, podemos configurar que se envíen parámetros al mismo en la iniciación del formulario. Esto se consigue marcando la casilla “Pasar código tipo de objeto de registro e id. Único como parámetros”.

Configuración de Web Resource para pasar parámetros desde el formulario

Configuración de Web Resource para pasar parámetros desde el formulario

Cuando tenemos esta casilla activada durante la carga del Web Resource en nuestro formulario se invocará la URL y se añadirán algunos parámetros del contexto donde se está abriendo ese Web Resource. Esto es algo bueno puesto que mediante este contexto de información podremos desarrollar un Web Resource personalizado, por ejemplo, para el cliente que se esté viendo en ese momento.

Los parámetros que se envían por defecto cuando seleccionamos esta casilla son los siguientes:

# Parámetro Descripción
1 OrgLCID Id del código de lengua de la organización
2 UserLCID Id del código de lengua del usuario
3 Id Id del registro que estamos cargando
4 Orgname Nombre de la organización
5 Type Tipo de formulario.  0 = Undefined, 1 = Create, 2 = Update, 3 = Read Only, 4 = Disabled, 5 = Quick Create, 6 =  Bulk Edit, 11 = Read Optimized
6 Typename Nombre de la entidad donde se ha abierto el WR

Un ejemplo de URL completa para nuestro sería el siguiente:

new_prueba_web_resource?OrgLCID=3082&UserLCID=3082&id={42DCC3D2-7339-E611-80D6-C4346BAC8D78}&orgname=NuestraOrganizacion&type=2&typename=contact

El modo en el que podemos capturar estos parámetros desde nuestro javascript se ha indicado ya en este post antiguo. En este caso el proceso sería similar.

CRM también nos permite enviar parámetros personalizados introduciéndolos en el cuadro de texto que está encima del check indicado con anterioridad, en las propiedades del Web Resource. Si fuese este el caso, estos parámetros se enviarán a continuación de los ya indicados.

Podéis ver los parámetros que se han enviado a un Web Resource fácilmente con la extensión HUDCRM.

Obtener parámetros de carga de un web resource desde HUDCRM

Obtener parámetros de carga de un web resource desde HUDCRM