jueves, 29 de octubre de 2009

Guía de diseño de MSCRM 4.0

Este artículo trata del tema del diseño y de las hojas de estilo del CRM (tipos de letras, tamaños colores, etc).
En la SDK, en la carpeta raiz hay un fichero con el nombre "crm4 ux design guide.pdf" que muestra todo el diseño del CRM, para que podamos hacer nuestros desarrollos con una apariencia lo mas parecido al CRM.
Lo puedes descargar de aqui:
crm4uxdesignguide.pdf



un saludo

martes, 27 de octubre de 2009

Utilizando plantillas en Visual Studio 2008

La última versión de la SDK del CRM (Version 4.0.9 de Junio de 2009) nos ofrece una serie de plantillas quie considero muy útiles para el momento en que nos pongamos a desarrollar para CRM.
Existen 3 plantillas:

  • Para actividades de workflow y de plugins ("CrmWorkflowActivity" y "MSCRM Plug-in")




  • Para hacer una página web con una ventana de diálogo ("AddOnWebPage")




  • La SDK la podéis descargar de aquí: http://www.microsoft.com/downloads/details.aspx?FamilyID=82E632A7-FAF9-41E0-8EC1-A2662AAE9DFB&displaylang=en
    Estas plantillas nos facilitarán la vida a la hora de comenzar con este tipo de proyectos.
    Se encuentran en la carpeta "visualstudiotemplates\cs\" dentro de la carpeta donde esta instalada la SDK de CRM.
    Para registrarlas simplemente debemos copiar los ficheros .ZIP de cada una de las carpetas en la carpeta %Documents and Settings%\[usuario]\Mis Documentos\Visual Studio 2008\Templates\ProjectTemplates\Visual C#\.

    Un abrazo!

    sábado, 24 de octubre de 2009

    Sugerencias con Microsoft Connect

    Microsoft dispone de un sitio donde podemos dejar comentarios acerca de Microsoft Dynamics CRM, como por ejemplo sugerencias para nuevas versiones, o votar por las sugerencias que han propuesto otras personas.
    Lo que sí estoy seguro, es que hay gente de microsoft leyendo estos mensajes, ya ya he puesto un par de sugerencias.


    Bueno, ha sugerir!:
    https://connect.microsoft.com/dynamicssuggestions

    viernes, 23 de octubre de 2009

    Añadiendo HttpModules al CRM

    Los "HttpModules" son clases que implementan la interfaz System.Web.IHttpModule que nos permiten capturar los métodos que se lanzan en las diferentes etapas del proceso de las peticiones web.
    Consiste básicamente en una clase (.dll) que es llamada directamente cuando ocurren determinados eventos.
    De esta forma, podríamos crear nuestras propias clases en .NET que sean llamadas cuando se navegue por el CRM.
    Para mas información acerca de los HttpModules recomiendo ir a http://msdn.microsoft.com/es-es/library/9b9dh535(VS.80).aspx
    Antes de continuar, responderé a la pregunta que se estarán haciendo, obviamente esta funcionalidad no esta soportada por CRM.
    La funcionalidad que nos puede aportar esto es practicamente infinita, ya que podremos "meter" código nuestro en la ejecución del propio CRM.
    Voy a poner un ejemplo sencillo. En este caso, lo que voy a hacer es que mientras se navegue por el CRM, vaya escribiendo en la barra de "status" en la parte inferior del navegador, escriba las URL de las páginas que esta cargando.
    Los pasos para esto son los siguientes:
    1) Lo primero que hay que hacer, es crearse la clase en cuestión y compilarla.
    El código es el siguiente:

    using System;
    using System.Web.UI;
    using System.Web;

    namespace Httpmodulo
    {
    public class ExtensionCRM : IHttpModule
    {
    public event EventHandler BeginRequest;
    public void Dispose()
    {}
    public void Init(HttpApplication app)
    {
    app.PreRequestHandlerExecute += new EventHandler(this.OnPreRequestHandlerExecute);
    }
    public void OnPreRequestHandlerExecute(object o, EventArgs args)
    {
    try
    {
    HttpContext context = ((HttpApplication)o).Context;
    IHttpHandler handler = context.Handler;
    ((Page)handler).PreRender += new EventHandler(this.OnPagePreRender);
    }
    catch (System.Exception ex){ }
    }
    public void OnPagePreRender(object sender, EventArgs eventArgs)
    {
    Page page = (Page)sender;

    if (page.Request.QueryString["redirect"] == null)
    {
    if (!page.Request.FilePath.Contains("css") &&
    !page.Request.FilePath.Contains("icon.aspx") &&
    !page.Request.FilePath.Contains("fonts.aspx"))
    {
    page.Response.Write(@"");
    }
    }
    }

    }

    Como se ve, simplemente en el evento "OnPagePreRender" meto el cñodigo que quiere añadir y de "page.Request.FilePath"recojo el nombre de la página. De todos modos como estamos en el "contexto" de ejecución del CRM podemos recoger mas información.

    2) Una vez realizada mi clase, debemos firmar la misma, compilarla y registrarla en la GAC (Global Assembly Cache).

    3) El último, que por cierto es lo que no esta soportado por el CRM, es simplemente añadir una línea en el Web.Config del sitio web del CRM, para que cargue nuestra dll:





    La "PublicKeyToken" la podemos ver al momento de registrar la dll en la GAC, seleccionandola y pinchando en "propiedades". De allí la copiamos y la ponemos.

    Listo, de esta forma bastantemente no soportada, podemos meter código propio en la ejecución del CRM. Por ejemplo puede servir para hacer Logs de paginas abiertas, poder cargar javascripts externos en los "onloads" de los formularios, etc, etc, y lo que se les pueda ocurrir!.

    un abrazo!

    jueves, 22 de octubre de 2009

    Ha salido el Rollup 7 de CRM

    Hoy han publicado el Rollup 7 para Microsoft Dynamics CRM.
    Puede ser descargado de aqui:
    http://www.microsoft.com/downloads/details.aspx?FamilyID=a4893988-7804-4e23-ab58-740441cc696e&displayLang=en
    Algunos comentarios acerca del mismo pueden leerse desde aquí:
    http://blogs.msdn.com/crm/archive/2009/10/22/update-rollup-7-for-microsoft-dynamics-crm-4-0.aspx
    Como siempre, primero instalarlo en un servidor de desarrollo, para probar todas las funcionalidades, desarrollos, etc.

    Un cordial saludo.

    lunes, 19 de octubre de 2009

    Contador de registros y páginas

    El funcionamiento de las vistas y las búsquedas avanzadas permite a los usuarios de forma muy sencilla acceder a la información almacenada en el CRM.
    A pesar de su sencillez, tiene una carencia importante y que casi siempre preguntan los usuarios finales: "¿Cómo sé cuantos registros totales me devuelve una vista?".
    Existe una forma de facilitar la vida a los usuarios, creando un Plugin que se dispare recoja cuando se ejecute una vista (FetchXml) y que añada un registro como resultado en la primera posición, que nos dé esa información. Realmente será un registro "simulado" que permitirá al usuario saber fácilmente cuantos registros hay en una consulta. La idea la he recogido del Blog de a33ik.
    El código siguiente muestra un contador de registros y páginas para las Cuentas y Contactos:

    public void Execute(IPluginExecutionContext context)
    {
    if (context.Depth != 1) return;

    if (context.MessageName == "Execute" && context.InputParameters.Contains("FetchXml"))
    {
    XmlDocument indoc = new XmlDocument();
    indoc.LoadXml((string)context.InputParameters["FetchXml"]);

    string entityName = indoc.SelectSingleNode("//fetch/entity").Attributes["name"].InnerText;

    if (entityName != EntityName.account.ToString() && entityName != EntityName.contact.ToString()) return; //solo para cuentas y contactos

    IMetadataService mservice = context.CreateMetadataService(false);

    RetrieveEntityRequest request = new RetrieveEntityRequest();
    request.RetrieveAsIfPublished = false;
    request.LogicalName = entityName;
    request.EntityItems = EntityItems.EntityOnly;
    string primaryFieldName = ((RetrieveEntityResponse)mservice.Execute(request)).EntityMetadata.PrimaryField;

    ICrmService crmService = context.CreateCrmService(true);

    int pagecount = int.Parse(indoc.DocumentElement.Attributes["count"].InnerText);

    indoc.DocumentElement.Attributes.Remove(indoc.DocumentElement.Attributes["count"]);
    indoc.DocumentElement.Attributes.Remove(indoc.DocumentElement.Attributes["page"]);

    foreach (XmlNode node in indoc.SelectNodes("//fetch/entity/attribute"))
    indoc.SelectSingleNode("//fetch/entity").RemoveChild(node);

    foreach (XmlNode node in indoc.SelectNodes("//fetch/entity/order"))
    indoc.SelectSingleNode("//fetch/entity").RemoveChild(node);

    foreach (XmlNode node in indoc.SelectNodes("//fetch/entity/link-entity"))
    foreach(XmlNode subnode in node.SelectNodes("./attribute"))
    node.RemoveChild(subnode);

    XmlAttribute aggrAttr = indoc.CreateAttribute("aggregate");
    aggrAttr.Value = "true";
    indoc.DocumentElement.Attributes.Append(aggrAttr);

    XmlNode field = indoc.CreateNode(XmlNodeType.Element, "attribute", null);

    XmlAttribute nameAttr = indoc.CreateAttribute("name");
    nameAttr.Value = string.Format("{0}id", (entityName == EntityName.activitypointer.ToString() ? "activity" : entityName));
    field.Attributes.Append(nameAttr);

    XmlAttribute aggregateAttr = indoc.CreateAttribute("aggregate");
    aggregateAttr.Value = "count";
    field.Attributes.Append(aggregateAttr);

    XmlAttribute aliasAttr = indoc.CreateAttribute("alias");
    aliasAttr.Value = "C";
    field.Attributes.Append(aliasAttr);

    indoc.SelectSingleNode("//fetch/entity").AppendChild(field);

    //Xml con el resultado (sin paginación)
    string fullResult = crmService.Fetch(indoc.OuterXml);

    XmlDocument fullResultDocument = new XmlDocument();
    fullResultDocument.LoadXml(fullResult);

    //registros por fetch
    int totalRecordCount = int.Parse(fullResultDocument.SelectSingleNode("//resultset/result/C").InnerText);
    int totalPageCount = (totalRecordCount / pagecount) + ((totalRecordCount % pagecount) == 0 ? 0 : 1);

    string result = string.Format("Total de registros = {0}, Total de Páginas = {1}", totalRecordCount, totalPageCount);

    //XML de Resultado que se muestra en el Grid
    XmlDocument outdoc = new XmlDocument();
    outdoc.LoadXml((string)context.OutputParameters["FetchXmlResult"]);

    //Creacion del registro que muestra los totales
    XmlNode ResultNodeText = outdoc.CreateNode(XmlNodeType.Element, primaryFieldName, null);
    ResultNodeText.InnerText = result;

    XmlNode ResultNodeId = outdoc.CreateNode(XmlNodeType.Element, string.Format("{0}id", (entityName == EntityName.activitypointer.ToString() ? "activity" : entityName)), null);
    ResultNodeId.InnerText = Guid.Empty.ToString();

    XmlNode res = outdoc.CreateNode(XmlNodeType.Element, "result", null);
    res.AppendChild(ResultNodeText);
    res.AppendChild(ResultNodeId);

    //Añadir en registro con la etiqueta del contador de páginas y registros como primer registro en el recordset
    outdoc.SelectSingleNode("//resultset").InsertBefore(res, outdoc.SelectSingleNode("//resultset").FirstChild);
    context.OutputParameters["FetchXmlResult"] = outdoc.OuterXml;
    }


    La dll se puede descargar de aquí: RecordCounter y se debe registrar de la siguiente forma:


    esto se verá de la siguiente forma:


    espero les sirva...

    un saludo

    jueves, 15 de octubre de 2009

    Herramientas útiles para trabajar con CRM (Stunnware Tools)

    En este artículo intentaré realizar comentarios acerca de una solución que nos ofrece muchas herramientas para trabajar con Microsoft Dynamics CRM 4.0 y que permite hacernos la vida mas "facil".
    Esta herramienta es la llamada "Stunnware Tools for Microsoft Dynamics CRM 4.0" que ha realizado Michael Höhne, un MVP de Microsoft CRM de Alemania, que por cierto tiene un Blog muy bueno al cual recomiendo: stunnware's CRM Corner.
    La herramienta la pueden descargar de esta direccion: http://www.stunnware.com/. El funcionamiento es muy sencillo, simplemente es una aplicación a la cual mediante el botón de "Options" se les puede ir añadiendo al menú de tipo "Ribbon" (el del Office 2007) los distintos módulos que se deseen (recomiendo añadir todos).
    Las distintas funcionalidades que contiene y que recomiendo su uso son:
  • Herramienta para modificar y actualizar el ISV y el SiteMap

  • Herramienta para comparar dos ficheros de Personalizaciones diferentes

  • Herramienta para creación de Consultas que genera automáticamente el XML para una consulta FetchXML y el código para el QueryExpression

  • Acceso y consulta de todos los MetaDatos de forma sencilla (entidades, atributos, etc)

  • etc.

  • Espero les sirva esta solución, la considero muy buena.

    un saludo

    jueves, 8 de octubre de 2009

    Como copiar una entidad con todos sus atributos

    Muchas veces necesitamos copiar toda la estructura de una entidad, incluyendo sus atributos, relaciones, etiquetas, etc.
    Esto suele ocurrir cuando se tiene una entidad a la que no le hemos definido Notas o Actividades, y luego se desea modificar dicha propiedad (que solo se puede definir en la creación de la entidad).
    He desarrollado una pequeña función que lo que hace es copiar toda la información de esquema (es decir atributos, relaciones y la definición general de la entidad) de una entidad original, a una nueva. Este desarrollo esta basado en la documentación que aparece en la SDK y es totalmente soportado.
    Simplemente la llamada a la misma podria ser:

    CopiarEntidad("localhost","Contoso","new_entidadoriginal", "new_nuevaentidadoriginal", true,true);
    Los parámetros serían los siguientes:
    1) Ip del servidor
    2) Empresa
    3) Nombre de esquema de la entidad original
    4) Nombre de esquema de la nueva entidad a crear
    5) true/false si la nueva entidad contendrá notas
    6) true/false si la nueva entidad contendrá actividades

    El código de la función es un poco largo así que no lo pego directamente aquí, sino que dejo este enlace para descargárselo.

    El funcionamiento del código es bastante sencillo. En primer lugar, se recoge dinámicamente los metadatos de la entidad original. Luego creo la nueva entidad y le creo todos los atributos relacionados recorriendo los atributos de la entidad original. Finalmente recorre las relaciones de N:1 de la entidad (campos de lookup de la entidad) y crea las relaciones.

    Si utilizan la función deben tener en cuenta:
    1) La nueva entidad que se crea, se crea con el nombre para mostrar "copia de ". Esto es así ya que el CRM no permite tener 2 entidades con los mismos nombres para mostrar.
    2) Utilizo la clase CrmServiceUtility para la creación de los "CrmLabel". Esta clase esta documentada en la SDK.
    3) Sólo esta preparado para copiar una entidad personalizada.
    4) Por ahora esta función no copia ni vistas ni personalizaciones de formulario.
    5) Se deben añadir las referencias a los web services de CRM ("CrmSdk" y "metadataService")

    Espero les sirva este código, intentaré ir mejorandolo de a poco en siguientes artículos.

    Un saludo