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,

lunes, 28 de mayo de 2012

Javascript: Añadiendo notificaciones al formulario

Esta vez voy a publicar un artículo en donde vamos a añadir una funcionalidad que CRM 2011 no dispone, pero que a veces es muy útil teniendo en cuenta que a veces a los usuarios se los debe “Guiar” o añadir información necesaria para la introducción de información en los formulario de CRM.
La descripción del ejemplo de hoy es la siguiente:
Formulario: Cuenta
Evento: OnLoad() y OnChange()
Atributos: para este ejemplo, todos los requeridos y recomendados. 
Funcionalidad: Añade una notificación general de información del formulario, y verifica todos los atributos requeridos o recomendados y añade una alerta si es que están vacíos. Además en el OnChange() se actualiza el mensaje, dependiendo de si se ha introducido o no.
La idea es que en el formulario se enseñen notificaciones de la siguiente manera:
image
El javascript puede descargarse desde la página de Codeplex de EBDDCRM, en donde también iremos añadiendo mas funciones:
http://elblogdedynamicscrm.codeplex.com/documentation
El método principal que debe ser llamado tanto desde el OnLoad() como en el OnChange() es el siguiente:
function Verificaciones()
{
   addNotification ("",1);

   addNotification ("Formulario con la información general de la Cuenta.",2);
   
   var attributes = Xrm.Page.data.entity.attributes.get();
   for (var i in attributes)
    {
        var attribute = attributes[i];

        var requiredLevel = attribute.getRequiredLevel();
        var value="";
  if (attribute.getValue()!=null){value=attribute.getValue()};
  var control=Xrm.Page.getControl(attribute.getName());
  
  switch (requiredLevel)
  {
     case "required":
        if (value=="")
     {
    addNotification ("El campo "+control.getLabel()+" es requerido.",1);
     }
        break;
     case "recommended":
     if (value=="")
     {
    addNotification ("El campo "+control.getLabel()+" es opcional.",3);
     }
        break;
  }
  
    }
}


Lo que hacemos aquí es primero eliminar todas las notificaciones y luego recorrer los atributos del formulario y añadir notificaciones según estén rellenos o no los mismos.


Para hacer las llamadas a añadir notificaciones simplemente se debe llamar a la función “addNotificacion” con el texto y el tipo de notificación (1 crítico, 2 información, 3 advertencia).


El método que añade la notificación es el siguiente:


 function  addNotification (message, level) {
        /// The warning level of the message: [1 critial, 2 information, 3 warning]
        var notificationsArea = document.getElementById('crmNotifications');
        if (notificationsArea == null) {
            alert('div not found'); return;
        }
        if (level == 1) {
            //critical  
            notificationsArea.AddNotification('mep1', 1, 'source', message);
        }

        if (level == 2) {
            //Info  
            notificationsArea.AddNotification('mep3', 3, 'source', message);
        }
        if (level == 3) {
            //Warning  
            notificationsArea.AddNotification('mep2', 2, 'source', message);
        }
        if (message == "") {
            notificationsArea.SetNotifications(null, null);
        }
    }


Este método se apoya en ciertos métodos de CRM que no están documentados en la SDK y que por lo tanto no están soportados, que hay que tener en cuenta, pero que aporta mucha funcionalidad de cara a los usuarios finales.


Lo bueno de este ejemplo es verlo “en acción”, en donde se puede ver como va cambiando el listado de notificaciones según se van rellenando los valores del formulario.


Espero les sirva.


Un abrazo!

jueves, 24 de mayo de 2012

Excel PowerPivot y Dynamics CRM 2011 OnPremise

Como algunos sabrán, con la salida del SQL Server 2012, existen una serie de herramientas de explotación de información nuevas. Una de ellas es PowerPivot, que básicamente consiste en un Add-In del Excel, que permite explotar mejor la información extendiendo las propias funcionalidades del Excel.

En este artículo enseñaré como utilizar sencillamente PowerPivot conectándose a la base de datos de CRM.

Lo primero que hace falta, es descargar e instalar el PowerPivot (gratuito) de aquí:http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=7609

Luego nos vamos a nuestro CRM a los recursos del desarrollador:

image

De allí copiamos la URL del servicio OData:

image

Luego abrimos el Excel y la ventana de PowerPivot:

clip_image001

Luego al botón de obtener Feed:

image

Introducimos la ruta REST de las Accounts y Contacts (/AccountSet y /ContactSet):

image

Y dejamos que acabe la importación:

image

Y luego se pueden relacionar mediante la opción de “Crear relación”:

image

Creamos un gráfico y una tabla:

image

y listo nuestro primer informe en PowerPivot con datos de CRM (REST):

image

Un saludo,

lunes, 21 de mayo de 2012

Javascript: Como hacer una consulta al CRM

Hola, en este artículo empezaremos a ver ejemplos de como utilizar pequeñas funciones de javascript que suelen ser muy útiles y que suelen ser necesarias en muchos de los proyectos a los que nos solemos enfrentar en nuestro día a día.
Para empezar vamos a crear una funcionalidad relacionada con la realización de consultas de datos al CRM desde un formulario de CRM.
La descripción del método es la siguiente:

Formulario: Cuenta
Evento: OnChange()
Atributo: primarycontactid
Funcionalidad: consulta el télefono movil y el email del contacto seleccionado y los rellena en "Otro teléfono" y "Fax" respectivamente

image
El javascript puede descargarse desde la página de Codeplex de EBDDCRM, en donde también iremos añadiendo mas funciones:
http://elblogdedynamicscrm.codeplex.com/documentation
La función principal es llamada en el "OnChange" del Contacto principal:

image2
Y la función es la siguiente:

function contact_change()
{
   var contactid="";
   var telephone2=""; var fax="";
   
   var lookupItem = new Array(); 
   lookupItem = Xrm.Page.getAttribute("primarycontactid").getValue();
   if (lookupItem!=null) contactid=lookupItem[0].id;

   
   if (contactid!="")
   {
      var obj_contact=RetrieveEntityById("contact",contactid,
"mobilephone,emailaddress1");
      telephone2=obj_contact.mobilephone;
      fax=obj_contact.emailaddress1;
  

 }
   
   Xrm.Page.getAttribute("telephone2").setValue(telephone2);         
   Xrm.Page.getAttribute("fax").setValue(fax); 


}
Esta función llama al método "RetrieveEntityById" que recibe tres parámetros y nos devuelve un objeto de Javascript, con los atributos que le hemos solicitado, para que podamos recoger los valores del mismo por ejemplo simplemente haciendo "obj_contact.mobilephone".


En el código adjunto se encuentra el código completo que se puede hacer copy/paste directamente en el formulario y es totalmente funcional.


El método que realiza la consulta "RetrieveEntityById", es el siguiente:


function RetrieveEntityById(EntityName, EntityId, EntityColumns) {
    var resultXml, errorCount, msg, xmlHttpRequest;
    var arrayEntityColumns = EntityColumns.split(",");
    var xmlEntityColumns = '';
    for (var i = 0; i < arrayEntityColumns.length; i++) {
        xmlEntityColumns += "" + arrayEntityColumns[i] + "";
    }
    var soap = PopulateSoapObject(); // //Prepare the SOAP message.
    var xml = soap.Header
        + ""
        + "" + EntityName + ""
        + "" + EntityId + ""
        + ""
        + "" + xmlEntityColumns + ""
        + ""
        + ""
        + soap.Footer;
    //call function to create Soap Request to ms crm webservice 
    xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
    xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false); // false = don't make async call 
    xmlHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Retrieve");
    xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
    xmlHttpRequest.send(xml);
    // debugger;
    resultXml = xmlHttpRequest.responseXML;
    return ObjectFromXML(resultXml, arrayEntityColumns, "//RetrieveResult");
}

function PopulateSoapObject() {
    var soap = new Object();
    soap.Header = ""
        + ""
        + Xrm.Page.context.getAuthenticationHeader() + "";
    soap.Footer = "";
    return soap;
}

ObjectFromXML = function (xml, arrFields, prefix, ignoreNulls) {
    var r = {}; // Return object
    r.ok = false;
    if (xml == null) { r.errorMsg = "No XML Returned by platform!"; return r };
    var errorCount = xml.selectNodes('//error').length;
    if (errorCount != 0) {
        r.errorMsg = xml.selectSingleNode('//description').nodeTypedValue;
        return r;
    }
    if (xml == null) {
        r.errorMsg = "Null XML returned.";
        return r;
    }
    /* var recordCount = 0;
    $(xml).find(prefix.substr(2)).each(function () {
    recordCount++;
    });
    if (recordCount == 0) {
    r.errorMsg = "No Records returned.";
    return r;
    } */
    if (prefix != "") { xml = xml.selectSingleNode(prefix) }; // Grab just the first node with the prefix
    // Everything ok, now format as an object
    r.ok = true;
    for (var i = 0; i < arrFields.length; i++) {
        if (!ignoreNulls) { r[arrFields[i]] = "" }
        var node = xml.selectSingleNode("q1:" + arrFields[i]);
        if (node != null) {
            var lookup = false;
            if (arrFields[i].indexOf("id") >= 0) {
                try { // Add try loop as .text fails when atribute not present
                    var name = node.getAttributeNode("name").text;
                    var type = node.getAttributeNode("type").text;
                    if (name != null && type != null) { lookup = true }
                } catch (err) { };
            }
            if (lookup) {
                r[arrFields[i]] = { name: name, id: node.text, entityType: type} // return lookup object 
            } else {
                r[arrFields[i]] = node.text;
            }
        }
    } // for
    return r;
}             // ObjectFromXML






Espero les sea útil este post. un abrazo!