lunes, 30 de julio de 2012

DynamicForm 2.0: Convirtiendo un Lookup en RadioButton

Hoy vamos a mejorar el rendimiento en la introducción de datos de nuestros usuarios finales. Este rendimiento se verán mejorados teniendo en cuenta los campos de tipo Lookup (o de referencia). Inicialmente en un campo de este tipo, para introducir información, se necesita como mínimo un total de tres clicks. Empezamos con la versión anterior convirtiendo el Lookup en un combo, reduciendo los clicks a solo dos clicks.
Bien, hoy vamos a mejorarlo para que sea en un solo click. La idea es añadir la posibilidad de convertir un Lookup en un Radio button!
El resumen de la entrega de hoy:
Formulario: Cuenta
Evento: OnLoad()
Atributos: (new_paisid).
Funcionalidad: Al cargar el formulario, en un IFRAME se crea dinámicamente un desplegable con los posibles valores de un campo de lookup de Países. La idea es que el Lookup de país, se rellene mediante un listado de radio buttons.
Todo este ejemplo estará como siempre disponible en Codeplex:
http://elblogdedynamicscrm.codeplex.com/documentation
En el XML, he añadido una nueva acción:
   1: <action type="lookuptoradio" attributename="new_paisid" entityname="new_pais"
   2:  entityid="new_paisid" entityattributename="new_name" iframename="IFRAME_paisradio"/>

La nueva acción se llama “lookuptoradio” y como su nombre lo indica, convierte un lookup en un radio button. Los atributos son los siguientes (iguales al de la versión anterior de lookuptocombo):
  • attributename: nombre del campo de lookup en el formulario
  • entityname: nombre de la entidad a donde apunta el lookup
  • entityid: nombre del campo clave de la entidad
  • entityattributename: campo nombre de la entidad que será utilizado para enseñar los valores del desplegable.
  • iframename: nombre del iframe donde se incluirá el desplegable dinámicamente.
En la función LookuptoCombo, he realizado ciertas modificaciones, para que funcione tanto como combo como con radiobuttons. El funcionamiento es muy similar:

   1: function LookupToCombo(attributename,entityname,iframename,entityid,entityattributename,
   2:  type)
   3: {
   4:     var result=RetrieveEntitiesByFilter (entityname, "statecode", "Active", entityid+","+
   5:         entityattributename, false, "");
   6:     var i=0;
   7:     var svalue="";
   8:     var objvalue=getValue(attributename);
   9:     if (!objvalue)
  10:     {
  11:         svalue="";
  12:     }
  13:     else
  14:     {
  15:         svalue=objvalue[i].id;
  16:     }
  17:     var HTMLselect="";
  18:     
  19:     if (type=="lookuptocombo")
  20:     {
  21:        HTMLselect="<select style='font-size:11Px;font-family:Segoe UI, Tahoma, Arial;' ;
  22:         onchange='var select_list_selected_index = this.selectedIndex;var text = 

  23:         this.options[select_list_selected_index].text;var value = this.value;

  24:         parent.setLookupValue(value,text,document.all.entityname.value,

  25:         document.all.attributename.value);'>"

  26:        HTMLselect+="<option value=''></option>";
  27:     }
  28:     else
  29:     {
  30:        HTMLselect="<form style='font-size:11Px;font-family:Segoe UI, Tahoma, Arial;'>";
  31:     }
  32:     for (i=0;i<result.recordCount;i++)
  33:     {
  34:         var selected="";
  35:         var name=eval("result[i]."+entityattributename);
  36:         var id=eval("result[i]."+entityid);
  37:         if (svalue==id)
  38:         {
  39:             if (type=="lookuptocombo")
  40:             {
  41:                 selected="SELECTED";
  42:             }
  43:             else
  44:             {
  45:                 selected="CHECKED";
  46:             }
  47:         }
  48:         if (type=="lookuptocombo")
  49:         {
  50:            HTMLselect+="<option "+selected+" value='"+id+"'>"+name+"</option>";
  51:         }
  52:         else
  53:         {
  54:            var js="onclick='parent.setLookupValue(this.value,this.nombre,;
  55:                 document.all.entityname.value,document.all.attributename.value);'"

  56:            HTMLselect+="<input "+js+" type='radio' name='opt' nombre='"+name+"' +id+"' "+selected+" /> "+name+"<br />";
  57:                 value='"

  58:         }
  59:     }
  60:     if (type=="lookuptocombo")
  61:     {
  62:        HTMLselect+="</select>";
  63:     }
  64:     else
  65:     {
  66:         HTMLselect+="</form> ";
  67:     }
  68:     HTMLselect+="<input type='hidden' id='entityname' name='entityname' +entityname+"'/><input type='hidden' id='attributename' +attributename+"'/>";
  69:         value='"

  70:         name='attributename' value='"

  71:     var myIframe=document.getElementById(iframename);
  72:     myIframe.contentWindow.document.body.innerHTML = HTMLselect;
  73:     myIframe.contentWindow.document.body.style.margin="0px;";
  74: }


Básicamente, he añadido un nuevo parámetro (type) que me dice de que tipo es, y dependiendo del mismo, voy generando un combo o un radiobutton.

El ejemplo y la solución completa funcionando la he dejado como siempre en CodePlex.

Miren como se ve el formulario funcionando con el combo y los radiobuttons:

image


Un saludo!

martes, 24 de julio de 2012

Tercer aniversario del Blog y el futuro de CRM

Hola, ya han pasado 3 años de que creé este blog.
Pasado el tiempo y 189 artículos después, espero poder seguir con el mismo con las mismas ganas que hasta ahora.
En cuanto al objeto principal de mi Blog (Microsoft Dynamics CRM), tenemos perspectivas con muchas cosas nuevas que irán saliendo.
Por ejemplo en la última WPC 2012 de Toronto, ya nos han anunciado el CRM con "Metro Style" para el invierno de 2013. Se vería como lo siguiente:


Recomiendo ver la presentación entera de Bill Patterson "Microsoft Dynamics CRM Now and in the Future" en Youtube: http://www.youtube.com/watch?v=lR9t7aQVICw

un saludo,

lunes, 23 de julio de 2012

DynamicForm 1.9: Convirtiendo un Lookup en Picklist

Con unos días de retraso, en este nuevo artículo voy a exponer un ejemplo de una funcionalidad que suele ser necesaria, incluso sería una buena idea para proponer para futuras versiones de Dynamics CRM.
La idea consiste en que un campo de tipo Lookup en un formulario, pueda ser rellenado mediante un desplegable, en vez de abrir la típica ventada de búsqueda de Lookup haciendo click en las “lupas” de los mismos.
Como todo en esta librería, será dinámico y definible desde un recurso web en formato XML.
El resumen de la entrega de hoy:
Formulario: Cuenta
Evento: OnLoad()
Atributos: (new_paisid).
Funcionalidad: Al cargar el formulario, en un IFRAME se crea dinámicamente un desplegable con los posibles valores de un campo de lookup de Países. la idea es que el Lookup de país, se rellene mediante un desplegable.
Todo este ejemplo estará como siempre disponible en Codeplex:
http://elblogdedynamicscrm.codeplex.com/documentation
En el XML, he añadido una nueva acción:
   1: <action type="lookuptocombo" attributename="new_paisid" entityname="new_pais" 
   2:     entityid="new_paisid" entityattributename="new_name" iframename="IFRAME_pais"/>
La nueva acción se llama “lookuptocombo” y como su nombre lo indica, convierte un lookup en un desplegable. Los atributos son los siguientes:

  • attributename: nombre del campo de lookup en el formulario
  • entityname: nombre de la entidad a donde apunta el lookup
  • entityid: nombre del campo clave de la entidad
  • entityattributename: campo nombre de la entidad que será utilizado para enseñar los valores del desplegable.
  • iframename: nombre del iframe donde se incluirá el desplegable dinámicamente.
 Para añadir esta funcionalidad he tenido que añadir librerías nuevas en la solución y en el formulario:
  • ReadDataSyncFunctions.js
  • jquery
Estas dos librerías sirven para hacer las consultas a CRM y para trabajar con los objetos devueltos.

Además he creado una nueva entidad “Paises” y he añadido un Lookup de Pais en el formulario de Cuenta.

En la función “Actions” he añadido una nueva sección para el control de “lookupcombo”: 


   1: case "lookuptocombo":
   2:        var attributename= attributes.getNamedItem("attributename").nodeValue;
   3:        var entityname=attributes.getNamedItem("entityname").nodeValue;
   4:        var iframename=attributes.getNamedItem("iframename").nodeValue;
   5:        var entityid=attributes.getNamedItem("entityid").nodeValue;
   6:        var entityattributename=attributes.getNamedItem("entityattributename").nodeValue;
   7:        LookupToCombo(attributename,entityname,iframename,entityid,entityattributename);
   8:        break;

Como se ve aquí, recojo los valores de los atributos del XML y llamo a la función LookupToCombo:

   1: function LookupToCombo(attributename,entityname,iframename,entityid,entityattributename)
   2: {
   3:     var result=RetrieveEntitiesByFilter (entityname, "statecode", "Active", 
   4:                 entityid+","+entityattributename, false, "");
   5:     var i=0;
   6:     var svalue="";
   7:     var objvalue=getValue(attributename);
   8:     if (!objvalue)
   9:     {
  10:         svalue="";
  11:     }
  12:     else
  13:     {
  14:         svalue=objvalue[i].id;
  15:     }
  16:     var HTMLselect="<select style='font-size:11Px;font-family:Segoe UI, Tahoma, Arial;' ;
  17:         onchange='var select_list_selected_index = this.selectedIndex;var text = 
  18:         this.options[select_list_selected_index].text;var value = this.value;
  19:         parent.setLookupValue(value,text,document.all.entityname.value,
  20:         document.all.attributename.value);'>"
  21:  
  22:     HTMLselect+="<option value=''></option>";
  23:     for (i=0;i<result.recordCount;i++)
  24:     {
  25:         var selected="";
  26:         var name=eval("result[i]."+entityattributename);
  27:         var id=eval("result[i]."+entityid);
  28:         if (svalue==id)
  29:         {
  30:             selected="SELECTED";
  31:         }
  32:         
  33:         HTMLselect+="<option "+selected+" value='"+id+"'>"+name+"</option>";
  34:     }
  35:     HTMLselect+="</select><input type='hidden' id='entityname' name='entityname' +entityname+"'/><input type='hidden' id='attributename' +attributename+"'/>";
  36:         value='"
  37:         name='attributename' value='"
  38:     var myIframe=document.getElementById(iframename);
  39:     myIframe.contentWindow.document.body.innerHTML = HTMLselect;
  40:     myIframe.contentWindow.document.body.style.margin="0px;";
  41: }



Esta función lo que hace es primero hacer la consulta con los posibles valores a asignar (filtrando por los activos). Luego se genera un HTML dinámico con el desplegable que es asignado al IFRAME.
El código generado llama en el onchange del desplegable a un método llamado “setLookupValue”, que actualiza el valor del Lookup del formulario:


   1: function setLookupValue(id,name,entityName,attributename)
   2: {
   3:     if (id=="")
   4:     {
   5:         setValue(attributename,null);
   6:         return;
   7:     }
   8:     var olookup = new Object();
   9:     olookup.id = id;
  10:     olookup.entityType = entityName;
  11:     olookup.name = name;
  12:     var olookupValue = new Array();
  13:     olookupValue[0] = olookup;
  14:     setValue(attributename,olookupValue);
  15: }

El ejemplo y la solución completa funcionando la he dejado como siempre en CodePlex.

image

lunes, 9 de julio de 2012

DynamicForm 1.8: añadiendo estilos a los formularios

El día de hoy vamos a añadir nuevas funcionalidades para dar diseño a nuestros formularios. Esto es algo que CRM 2011 no tiene de forma estándar pero que puede ser muy útil para guiar a los usuarios en los campos de los formularios, o para resaltar algunos en particular.

La idea es poder modificar los colores de fondo de la letra de los campos de CRM:

Formulario: Cuenta
Evento: OnLoad(), OnChange(), OnSave()

Atributos: (address2_line1, address2_line2, address2_line3 y description).
Funcionalidad: Se han modificado por el XML los colores de fondo de todos esos campos, y los colores de las letras a 2 de ellos.

Todo este ejemplo estará como siempre disponible en Codeplex:

http://elblogdedynamicscrm.codeplex.com/documentation

En el XML, he añadido unas acciones nuevas:


 
 
 
 
 

Los tipos de acciones nuevos que he creado son:




  • backgroundcolor


  • fontcolor



Ambos dos reciben como parámetros los nombres de los atributos (attributename) y el color a asignar (color).



He tenido que mejorar la función de acciones añadiendo nuevos métodos:



function Actions()
{
 var nodePath = "//Root/actions/action";
   var nodelist = doc.selectNodes(nodePath);
 //Actions
  LoadXmlWebResource("new_new_test_xml");
  for (var i = 0; i < nodelist.length; i++) 
   {
  var attributes=nodelist(i).attributes;
  var type= attributes.getNamedItem("type").nodeValue;
  
  switch (type)
  {
     case "concat":
        var destattribute= attributes.getNamedItem("destattribute").nodeValue;
     var attributestoconcat= attributes.getNamedItem("attributestoconcat").nodeValue;
     var stringseparator= attributes.getNamedItem("stringseparator").nodeValue;
     Concat(destattribute,attributestoconcat,stringseparator);
     break;
     case "backgroundcolor":
        var attributename= attributes.getNamedItem("attributename").nodeValue;
     var color= attributes.getNamedItem("color").nodeValue;
     setBackgroundColor(attributename,color);
     break;
     case "fontcolor":
        var attributename= attributes.getNamedItem("attributename").nodeValue;
     var color= attributes.getNamedItem("color").nodeValue;
     setFontColor(attributename,color);
     break;
     }
   }
}

Y para la gestión de los colores en los atributos he añadido dos funciones nuevas:



function setFontColor(attributename,color)
{
 document.getElementById(attributename).style.color = color;
}
function setBackgroundColor(attributename,color)
{
 document.getElementById(attributename).style.backgroundColor = color;
}

El ejemplo y la solución completa funcionando la he dejado como siempre en CodePlex.



Miren como quedaría el formulario con este ejemplo:



image

viernes, 6 de julio de 2012

Como funciona la eliminación de valor de un Picklist (CRM 2011)

Hace casi un par de años, publiqué un artículo acerca de un tema que me sorprendió por su funcionamiento en relación con los Picklists en CRM 4.0 (http://crmtoall.blogspot.com.es/2010/09/como-funciona-la-eliminacion-de-un.html).

Hoy me propongo hacer lo mismo para comprobar si algo ha cambiado respecto a este funcionamiento.

La idea es explicar lo que ocurre cuando se elimina un valor de un campo de tipo Picklist (conjunto de opciones) en relación con los registros que ya tienen ese valor asignado.

Primero vamos a crear un atributo nuevo en la entidad de "Caso" y lo añadimos al formulario y publicamos. Será un desplegable con el nombre "new_categoria" con los valores "Uno", "Dos", "Tres":

image

Luego creamos un nuevo registro de Caso, con “Categoría” a “Dos” (valor 100.000.002). Luego hacemos un par de consultas en el SQL Server para ver que nos queda almacenado físicamente en la base de datos y en la FilteredView:

   1: SELECT * FROM IncidentExtensionBase  



   2: SELECT incidentid,New_Categoria,New_Categorianame FROM FilteredIncident  




image


Hasta este punto todo ok. Ahora imaginemos que necesitamos eliminar el valor del desplegable "Dos". Al eliminarlo nos aparece el siguiente mensaje:



image


Aceptamos, guardamos el campo y volvemos a publicar. Al hacer la consulta anterior ahora vemos lo siguiente:



image


Como se ve, en realidad en la base de datos seguimos teniendo el mismo valor, pero lo que ocurre, es que no puede recoger el texto del valor en la FilteredView, cosa que es normal ya que el valor se ha eliminado.


Imagínense que pasado el tiempo, añadimos un nuevo valor en este desplegable con el mismo valor (100.000.002) como índice, pero con otro texto que significa otra cosa:


image


Con esto sacamos como consecuencia, que al eliminar un valor de un desplegable, los registros relacionados con este valor, siguen teniendo el mismo, y no se eliminan, de hecho si hacemos una búsqueda avanzada por ese atributo filtrando por que "Contiene datos", lo encontrará ya que tiene datos, pero es un dato "inexistente". Además hay que tener en cuenta que si creamos nuevos elementos en el desplegable en índices ya existentes, podríamos estar reemplazando y modificando un valor antiguo que ya no nos interesaba.

La conclusión final y consejo que dejo es que se tenga mucho cuidado con los desplegables y en especial al eliminar los valores, creo que sería una buena práctica no permitir a usuarios finales gestionar este tipo de atributos. Además siempre se debería comprobar al crear un nuevo valor a un desplegable, si es que no hay registros que contengan ese valor, ya que podríamos estar dando un sentido diferente al deseado a los datos.


p>Por las pruebas hechas en CRM 2011, esta funcionalidad no ha cambiado en nada en relación con CRM 4.0. Lo único que sí que veo que ha cambiado es el mensaje de advertencia al eliminar el valor del picklist, que es un poco mas ajustado a la realidad, aunque es un poco confuso para mi gusto.


un saludo,

jueves, 5 de julio de 2012

Error con la ayuda en entornos con IFD

Al montar CRM 2011 con IFD (pasando sus interminables configuraciones y lo hemos conseguido), suele ocurrir que se estropee la ayuda estándar de CRM 2011.

Básicamente, lo que ocurre es que al estar a la ayuda, aparece un error de URL como que no se encuentra. Esto es así porque no se actualiza correctamente la dirección URL que apunta a la ayuda.

Para resolver esto, existen una serie de instrucciones que deben ejecutarse en Powershell y que solucionan el problema.

Lo primero que hay que hacer es abrir el Powershell y ejecutar la siguiente instrucción y confirmar con la letra “Y” (yes):

  • SET-ExecutionPolicy -ExecutionPolicy RemoteSigned

image

Luego hay que ejecutar en orden las siguientes instrucciones:

  • Add-PsSnapin Microsoft.Crm.PowerShell
  • $websetting = Get-CrmSetting WebAddressSettings
  • $websetting.HelpServerUrl = ""
  • Set-CrmSetting $websetting

image

De esta manera se actualiza la URL y se soluciona el problema de la ayuda que vuelve a funcionar.

Espero les sirva.