miércoles, 27 de junio de 2012

Primera reunión virtual con los expertos en Microsoft Dynamics CRM en Español

Hola, se está organizando la primera reunión virtual con los expertos en CRM en Español!
En la charla hablarán de las nuevas novedades que vienen en la "R8".

Estará liderada por:
* Gustavo Gonzalez (Zero2Ten, www.zero2ten.com)
* Pablo Peralta (UruIT Dynamix, www.uruitdynamix.com)
* Jimmy Larrauri (1to1 SAC, www.corp1to1.com)
La charla será el día 11/07/2012 11:00 a.m. EDT y pueden registrarse en: https://www149.livemeeting.com/lrs/8000684734/Registration.aspx?pageName=wzv6c0dw8cm173t9

Para mas información: http://elblogdedynamicscrm.com/post/2012/06/21/Primera-reunion-virtual-con-los-expertos-en-Microsoft-Dynamics-CRM-en-Espanol.aspx

Un saludo,

lunes, 25 de junio de 2012

DynamicForm 1.3: Concatenaciones de campos

Siguiendo con el progreso de nuestro componente, iremos añadiendo funcionalidades que creo que todos nosotros hemos utilizado alguna vez, o que sino, seguro que necesitarán en el futuro alguna vez.

La idea de hoy es añadir la posibilidad de concatenar varios campos en uno solo. Esto suele ser muy utilizado especialmente en algunos casos como en entidades de relación que simulan relaciones N-N y así rellenar en el campo “new_name” la relación.

El resumen de la funcionalidad propuesta hoy es el siguiente:

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

Atributos: para este ejemplo, el campo description, y todos los relacionados con la dirección (address1_*).
Funcionalidad: Además de la funcionalidad que ya se disponía, he añadido en el XML la posibilidad de definir “actions”, en un primer momento de tipo “concat” para concatenar campos en uno de destino.

Todo este ejemplo estará como siempre disponible en Codeplex:

http://elblogdedynamicscrm.codeplex.com/documentation

En el XML he añadido una nueva sección:







La idea es que por cada “action” se defina:




  • type: define si la acción a realizar es de tipo “concat” (concatenación)


  • destattribute: define el nombre del atributo donde queremos que se escriba el resultado de la concatenación


  • attributestoconcat: listado de atributos separados por comas y en orden que se quieren concatenar.


  • stringseparator: cadena utilizada para separa los valores regogidos



En cuando a nuestro Javascript en esta versión he añadido un par de funciones nuevas:




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;
var destattribute= attributes.getNamedItem("destattribute").nodeValue;
var attributestoconcat= attributes.getNamedItem("attributestoconcat").nodeValue;
var stringseparator= attributes.getNamedItem("stringseparator").nodeValue;
switch (type)
{
case "concat":
Concat(destattribute,attributestoconcat,stringseparator);
break;
}
}
}

function Concat(destattribute,attributestoconcat,stringseparator)
{
var strConcat="";
var arrattributestoconcat=attributestoconcat.split(",");
for (j=0;j {
if (strConcat!="")
{
strConcat+=stringseparator;
}
var value=getValue(arrattributestoconcat[j]);
if (value!=null)
strConcat+=getValue(arrattributestoconcat[j]);
}
setValue(destattribute,strConcat);

}

Los métodos nuevos son:



  • Actions: que es llamado en Load() y en el OnChange() de los campos definidos para concatenar.


  • Concat: método que recibe los parámetros y concatena los valores en el campo de destino.


Además he añadido una nueva función que no tenía hasta ahora, a pesar de ser bastante básica y creo que no requiere mayor explicación:



function setValue(attributename,value)
{
if (getAttribute(attributename))
getAttribute(attributename).setValue(value);
}


Todo este ejemplo, junto con el resto de versiones completas y funcionando podéis descargarlas de Codeplex, en donde además del código completo, está también la solución completa y funcional.



Les dejo una imagen con este ejemplo funcionando en CRM:



image



Un Saludo!

lunes, 18 de junio de 2012

Javascript: Validaciones de campos (DynamicForm 1.2)

Hola, esta semana intentaré ir mejorando y añadiendo funcionalidad a nuestro “componente” DynamicForm. En esta entrega, he añadido a nuestro XML de definición, una serie de reglas de validación de campos. De esta manera nuestro formulario que ya validaba y ocultaba campos, además validará con reglas el contenido introducido en los mismos.

El resumen de la funcionalidad propuesta hoy es el siguiente:

Formulario: Cuenta
Evento: OnLoad(), OnChange() y OnSave()
Atributos: para este ejemplo, el desplegable de Categoría.
Funcionalidad: Además de la funcionalidad que ya se disponía, he añadido validaciones de introducción de campos numéricos y de tipo email. En los numéricos además se controla que no se puedan escribir campos que no sean numéricos. Por último no se permite guardar el formulario en caso de que alguna validación falle. en este ejemplo en el campo “Número de Cuenta” solo se podrán introducir números y en el nombre de la dirección un campo con formato de Email.

Todo este ejemplo estará como siempre disponible en Codeplex:

http://elblogdedynamicscrm.codeplex.com/documentation

Para estas nuevas funcionalidades, he tenido que hacer el XML de configuración, algo mas completo:








showattributes="primarycontactid,accountnumber" hideattributes=""
showsections="general/address,general/shipping information" hidesections=""
showtabs="notes and activities,administration" hidetabs=""
setrequiredlevel="required:fax-address1_city,recommended:address1_name"/>
hideattributes="primarycontactid,accountnumber" showsections=""
hidesections="general/address,general/shipping information"
showtabs="" hidetabs="notes and activities,administration"
setrequiredlevel="none:fax-address1_city-address1_name-websiteurl"/>




En esta versión he hecho una serie de cambios en el XML mejorando su estructura:




  • Añadido un nodo nuevo “Root” que agrupa toda la definción


  • Nuevo nodo de “validations” que incluye los campos y los tipos a validar



La función de validaciones es la siguiente:




function Validations(ExecutionObj)
{
form_validation=true;
LoadXmlWebResource("new_new_test_xml");
//Validations
var nodePath = "//Root/validations/validation";
var nodelist = doc.selectNodes(nodePath);
for (var i = 0; i < nodelist.length; i++)
{
var attributes=nodelist(i).attributes;
var attributename= attributes.getNamedItem("attributename").nodeValue;
var type= attributes.getNamedItem("type").nodeValue;
var RegEx= attributes.getNamedItem("RegEx").nodeValue;
switch (type)
{
case "number":
//var control=document.getElementById(attributename);
//control.attachEvent("onkeyup",ValidateNumberCharacters);
document.getElementById(attributename).onkeypress = ValidateNumberCharacters;
form_validation=ValidateFieldNumber(attributename);
break;
case "email":
form_validation=ValidateFieldEmail(attributename);
break;
}
}
if (!form_validation && ExecutionObj!=null)
{
ExecutionObj.getEventArgs().preventDefault();
return false;
}
return true;
}




Este trozo de código, lee el XML definido, y realiza las validaciones correspondientes.


Finalmente, si alguno de las validaciones de campos ha fallado, se impide guardar el formulario con un ExecutionObj.getEventArgs().preventDefault();.



Para hacer la validación en el OnSave(), se debe llamar a esta función pasándole el contexto (ExecutionObj) como lo siguiente:


image



Los métodos comunes para las validaciones son los siguientes:




function ValidateNumberCharacters ()
{
var key = event.keyCode;
if(!(((key >= 48) && (key <= 57)) || key == 45 ||
key == 46 || key == 32 || key == 40 || key == 41))
{
return false;
}
}
function ValidateField (fieldName, RegEx)
{
var regularExpression = RegEx;
try {
var control = getControl(fieldName);
var fieldValue = getValue(fieldName);
if (fieldValue=="") return true;
if (regularExpression.test(fieldValue) == false) {
alert("Debe introducir el campo " + control.getLabel()
+ " en el formato adecuado.");
return false;
}
return true;
}
catch (e) {
alert('Field validation failed with exception ' + e.Message);
return false;
}
}
function ValidateFieldNumber (fieldName) {
var number = /(^-?\d\d*$)/;
return ValidateField(fieldName,number);
}
function ValidateFieldEmail (fieldName) {
var email = /(^[a-z]([a-z_\.]*)@([a-z_\.]*)([.][a-z]{3})$)|
(^[a-z]([a-z_\.]*)@([a-z_\.]*)(\.[a-z]{3})(\.[a-z]{2})*$)/i;
return ValidateField(fieldName,email);
}


Los métodos añadidos son:




  • ValidateNumberCharacters:


  • ValidateField


  • ValidateFieldNumber


  • ValidateFieldEmail



Las validaciones de campos se realizan por medio de Expresiones Regulares (“RegEx”).



Además de estos cambios, he realizado cambios en el método principal “DynamicForm”.



En Codeplex, además de subir el código fuente del Javascript, he subido también la solución completa, para que sea mas fácil implementarla.



La solución contiene:



image



Espero les guste, seguiré avanzando con estas funcionalidades.

lunes, 11 de junio de 2012

Javascript: Formulario dinámico desde Web Resource (DynamicForm 1.0)

Hola, partiendo de la base de mi último post en donde ocultábamos campos de forma declarativa desde un XML y dinámica dependiendo de un valor seleccionado de un desplegable, he decidido avanzar con la misma idea para crear una especie de componente algo mas “robusto”. Al que he llamado “DynamicForm”.
La idea sigue siendo la misma que planteé aquella vez, pero con mas opciones.
El resumen de la funcionalidad propuesta hoy es el siguiente:
Formulario: Cuenta
Evento: OnLoad() y OnChange()
Atributos: para este ejemplo, el desplegable de Categoría.
Funcionalidad: La idea es que dependiendo del valor de un desplegable, se realicen acciones en el formulario, como ocultar campos, secciones, o tabs y cambiar el nivel de requerimiento de campos. Está lógica se define en un Web Resource en formato XML.
Todo este ejemplo estará como siempre disponible en Codeplex:
http://elblogdedynamicscrm.codeplex.com/documentation
Para estas nuevas funcionalidades, he tenido que hacer el XML de configuración, algo mas completo:

    
    

Básicamente, los atributos del xml quedaría definido así:


  • name: nombre del campo que definirá el “disparo” de las acciones


  • value: valor del campo que definirá el disparo de las acciones


  • showattributes/hideattributes: define los campos a mostrar/ocultar, separados por comas (,).


  • showsections/hidesections: para introducir las secciones a ocultar/mostrar. para esto se debe añadir con el formato “nombre_tab/nombre_sección” además de poder añadir varias secciones separadas por comas (,).


  • showtabs/hidetabs: para añadir nombres de tabs o pestañas a ocultar/mostrar


  • setrequiredlevel: para modificar el nivel de requerimiento de uno o varios campos, separados por comas (,). El formato es el siguiente: “required|recommended|none:campo1-campo2-campo3”


Bien, una vez definido y explicado el tema del xml con las acciones que queremos disparar, veamos como nos queda el código javascript, algo mas largo, pero lo he ordenado algo mejor:


/*
DynamicForm method.
*/
function DynamicForm()
{
   LoadXmlWebResource("new_test_xml");
   
   var nodePath = "//attributes/attribute";
   var nodelist = doc.selectNodes(nodePath);
   for (var i = 0; i < nodelist.length; i++) 
   {
      var attributes=nodelist(i).attributes;
	  
	  
      var attributename= attributes.getNamedItem("name").nodeValue;
      var attributevalue= attributes.getNamedItem("value").nodeValue;
      var showattributes= attributes.getNamedItem("showattributes").nodeValue;
      var hideattributes= attributes.getNamedItem("hideattributes").nodeValue;
	  var showsections= attributes.getNamedItem("showsections").nodeValue;
	  var hidesections= attributes.getNamedItem("hidesections").nodeValue;
	  var showtabs= attributes.getNamedItem("showtabs").nodeValue;
	  var hidetabs= attributes.getNamedItem("hidetabs").nodeValue;
	  var setrequiredlevel=attributes.getNamedItem("setrequiredlevel").nodeValue;
	  
if (attributevalue==""+getValue(attributename))
      {
		/*Mostrar campos*/
		var arrshowattributes=showattributes.split(","); 
		for (j=0;j<arrshowattributes.length;j++) 
		{
			showhideAttribute(arrshowattributes[j],true);
		}
		
		/*Ocultar campo*/
		var arrhideattributes=hideattributes.split(","); 
		for (j=0;j<arrhideattributes.length;j++) 
		{
            showhideAttribute(arrhideattributes[j],false);
		}
		
		/*Mostrar secciones*/
		var arrshowsections=showsections.split(","); 
		for (j=0;j<arrshowsections.length;j++) 
		{
			var tab_section=arrshowsections[j].split("/");
			var tabname=tab_section[0];
			var sectionname=tab_section[1];
            showhideSection(tabname, sectionname,true);
		}
		
		/*Ocultar secciones*/
		var arrhidesections=hidesections.split(","); 
		for (j=0;j<arrhidesections.length;j++) 
		{
			var tab_section=arrhidesections[j].split("/");
			var tabname=tab_section[0];
			var sectionname=tab_section[1];
            showhideSection(tabname, sectionname,false);
		}
		
		/*Mostrar tabs*/
		var arrshowtabs=showtabs.split(","); 
		for (j=0;j<arrshowtabs.length;j++) 
		{
             showhideTab(arrshowtabs[j],true);
		}
		
		/*Ocultar tabs		*/
		var arrhidetabs=hidetabs.split(","); 
		for (j=0;j<arrhidetabs.length;j++) 
		{
             showhideTab(arrhidetabs[j],false);
		}
		
		/*Requiredlevel de los atributos*/
		
		var arrsetrequiredlevel=setrequiredlevel.split(","); 
		for (j=0;j<arrsetrequiredlevel.length;j++) 
		{
			/*required:fax-address1_city,recommended:address1_name*/
			var req_level=arrsetrequiredlevel[j].split(":");
			var slevel=req_level[0];
			var attrs_req=req_level[1].split("-");
			for (v=0;v<attrs_req.length;v++) 
			{
				setRequiredLevel(attrs_req[v],slevel);
			}
		}
      }
   }
}

          
La idea de esta parte del código, es simplemente leer e interpretar el XML y disparar las acciones que hay definidas en el mismo.
Todas las funciones genéricas las he definido aparte:
/*
Métodos comunes de Dynamics CRM 2011 Forms.
*/
function getValue(attributename)
{
	if (Xrm.Page.getAttribute(attributename))
		return Xrm.Page.getAttribute(attributename).getValue();
	else
		return null;
}

function showhideAttribute(attributename,show)
{
	if (Xrm.Page.getControl(attributename))
		Xrm.Page.getControl(attributename).setVisible(show);
}

function showhideSection(tabname, sectionname, show) {
    var tab = Xrm.Page.ui.tabs.get(tabname);
    if (tab != null && sectionname != null) {
        var section = tab.sections.get(sectionname);
        if (section != null) { section.setVisible(show); }
    }
}
function showhideTab(tabname,show) 
{ 
	if (Xrm.Page.ui.tabs.get(tabname))
		Xrm.Page.ui.tabs.get(tabname).setVisible(show);
}

function setRequiredLevel(attributename,req_level)
{
	if (Xrm.Page.getAttribute(attributename))
		Xrm.Page.getAttribute(attributename).setRequiredLevel(req_level);
}


var xml_loaded=false;
var xmlPath = "../WebResources/";
var doc = new ActiveXObject("Microsoft.XMLDOM");
function LoadXmlWebResource(WebResource_name)
{
   if (!xml_loaded)
   {
      doc.preserveWhiteSpace = true;
      doc.async = false;
      doc.load(xmlPath+WebResource_name);
      xml_loaded=true;
   }
}
El listado de métodos definidos el es siguiente:
  • getValuegetValue(attributename): recoger el valor de un campo
  • showhideAttribute(attributename,show): ocultar/mostrar campos
  • showhideSection(tabname, sectionname, show): ocultar/mostrar secciones
  • showhideTab(tabname,show) : ocultar/mostrar tabs (o pestañas)
  • setRequiredLevel(attributename,req_level): para cambiar campos a nivel requerido, recomendado u opcional.
  • LoadXmlWebResource(WebResource_name): para cargar un XML desde un WebResource
Vamos a ver este código de ejemplo en acción:
image image

lunes, 4 de junio de 2012

Javascript: Ocultamiento de campos desde un XML como Web Resource

Hola, muchas veces necesitamos que los formularios de CRM cambien dependiendo de diferentes motivos. Para esto CRM 2011 provee la funcionalidad de múltiples formularios por Roles de seguridad, que permite que diferentes usuarios vean los formularios de información diferente según el rol de seguridad que tengan.
Otras veces es necesario que los campos se enseñen u oculten dependiendo de alguna otra condición. Un ejemplo típico que intentaré hoy explicar es como definir que se muestren/oculten determinados campos dependiendo de un valor de un campo (un desplegable por ejemplo para este caso).
Esto no es muy complicado de hacer, e imagino que muchos ya lo habrán hecho. Pero la idea que quiero transmitir hoy, es que esa “lógica” del formulario se defina en un Web Resource en formato XML, de forma que si se quiere reutilizar o modificar este código, puede valor para cualquier otro ejemplo.
La descripción del ejemplo de hoy es la siguiente:
Formulario: Cuenta
Evento: OnLoad() y OnChange()
Atributos: para este ejemplo, el desplegable de Categoría.
Funcionalidad: La idea es que dependiendo del valor de un desplegable, se muestren u oculten determinados campos. Está lógica se define en un Web Resource en formato XML.
Todo este ejemplo estará como siempre disponible en Codeplex:
http://elblogdedynamicscrm.codeplex.com/documentation
El funcionamiento empieza definiendo un XML con la lógica deseada:

    
    
Este XML dice que cuando el campo “name” tenga en valor “value”, se enseñarán y ocultarán unos campos que se definen separados por comas “,” (showattributes y hideattributes).
Luego ese XML se debe crear como un web resource:
image
Por último simplemente nos queda añadir el siguiente “js” en formulario de Cuentas y llamar al método “GestionVisibilidad” desde el load y desde el campo de Categoría:

/*
Ejemplo de XML en el web resource "test_xml":

    
    

*/
var xml_loaded=false;
var xmlPath = "../WebResources/new_test_xml";
var doc = new ActiveXObject("Microsoft.XMLDOM");

function GestionVisibilidad()
{
   if (!xml_loaded)
   {
      doc.preserveWhiteSpace = true;
      doc.async = false;
      doc.load(xmlPath);
      xml_loaded=true;
   }
   var nodelist;
   var nodePath = "//attributes/attribute";
   nodelist = doc.selectNodes(nodePath);
   for (var i = 0; i < nodelist.length; i++) 
   {
      var attributename= nodelist(i).attributes[0].value;
      var attributevalue= nodelist(i).attributes[1].value;
      var showattributes= nodelist(i).attributes[2].value;
      var hideattributes= nodelist(i).attributes[3].value;
      if (attributevalue==""+Xrm.Page.getAttribute(attributename).getValue())
      {
		//realizar las acciones
		var arrshowattributes=showattributes.split(","); 
		var arrhideattributes=hideattributes.split(","); 
		for (j=0;j		{
             if (Xrm.Page.getControl(arrshowattributes[j])) 
				Xrm.Page.getControl(arrshowattributes[j]).setVisible(true);
		}
		for (j=0;j		{
             if (Xrm.Page.getControl(arrhideattributes[j]))   
				Xrm.Page.getControl(arrhideattributes[j]).setVisible(false);
		}
      }
   }
}

Este javascript, básicamente lo que hace es cargar la primera vez un XML almacenado en un WebResource, y luego ejecutar la lógica definida en el mismo.

Creo que es una forma muy cómoda de crear formularios dinámicos, sin necesidad de reprogramar la lógica cada vez que es necesario.

Por último les dejo un par de capturas que podáis verlo en “acción”:

image

image

Espero les haya gustado, un abrazo!

sábado, 2 de junio de 2012

Rollup 8 para CRM 2011

Hace unos 10 días se ha publicado el Rollup 8 para Dynamics CRM 2011. Seguramente tendrá soluciones a diferentes incidencias reportadas.

Los enlaces para mas información son los siguientes:

Una cosa que he detectado como cambio importante pero que no encuentro en los documentos d ela KB, es que la página que aparece de "caducidad" de sesión (cuando CRM está configurado como IFD), es diferente y además se mejora senciblemente el tiempo de respuesta. Sinceramente no estoy seguro que cambio han hecho en este tema, pero era un problema que cada vez que caducaba la sesión en IFD, tardaba unos cuantos segundos en volver a la página de login.

Un saludo,