miércoles, 27 de junio de 2012
Primera reunión virtual con los expertos en Microsoft Dynamics 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:
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;
}
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:
Espero les guste, seguiré avanzando con estas funcionalidades.
lunes, 11 de junio de 2012
Javascript: Formulario dinámico desde Web Resource (DynamicForm 1.0)
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); } } } } }
/* 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
lunes, 4 de junio de 2012
Javascript: Ocultamiento de campos desde un XML como Web Resource
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:
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”:
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:
- Microsoft Download Center: http://www.microsoft.com/downloads/details.aspx?FamilyID=f72b8001-2bd3-4a21-8845-d0f284dc21b6
- Microsoft Knowledge Base Article: http://support.microsoft.com/kb/2600644
- Microsoft Update: http://catalog.update.microsoft.com/v7/site/Search.aspx?q=2600644
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,