CRM 2011: Pasar parámetros a un webresource de tipo HTML

20 05 2012

Uno de los temas a tener en cuenta cuando trabajamos con webresource de tipo HTML es que, para pasar parámetros al HTML, tan solo podemos usar la variable data en la URL. Así por ejemplo si llamamos a un webresource con otros parámetros que no sean el parámetro data obtenemos este error:

/webresource/Test.html?test=test

Por tanto, si queremos pasar parámetros al webresource, tendremos que usar la variable data. Pero, ¿como podemos entonces determinar que variables estamos pasando?. Imaginemos que queremos pasar dos variables al webresource de tipo HTML (var1 y var2), tenemos dos opciones:

Opción1. Establecer el formato de las variables y pasar los valores de las mismas separadas con un símbolo (por ejemplo, |). De este modo, en el HTML deberemos parsear el valor de la variable data y obtener los valores de las variables var1 y var2. Sin embargo esto nos obliga a establecer un orden de los valores en la URL. En el siguiente ejemplo, queremos pasar los valores test1 y test2 a las variables var1 y var2 respectivamente.

/webresource/Test.html?data=test1|test2

En el código del HTML debemos parsear la variable data a través del carácter | y asignar el valor de la posición 0 a var1 y la posición 1 a var2. El problema lo tenemos cuando quien usa el webresource no sabe el orden de asignación de las variables…

Opción 2. Componer en el parámetro data el nombre de variable y el valor separado por otro carácter (por ejemplo +), distinto del carácter que usemos para separar las asignaciones. De este modo podemos pasar los valores de las variables en el orden que queramos e incluso obviar alguna de las variables. En cualquier caso, en el código HTML debemos establecer las variables pasadas en la URL a través del parámetro data.

/webresource/Test.html?data=var1+test1|var2+test2

A partir de la variable data, parseamos para obtener las asignaciones y, para cada asignación parseamos el nombre de la variable del valor (parseando por el carácter +). Posteriormente, creamos una array con cada par y creamos una función que devuelva el valor para una variable o creamos un switch que a partir del nombre de la variable, asigne el valor…

<html>
<head>
<title>Asignación de variables a partir de parámetros en un webresourece de tipo HTML</title>
<script type="text/javascript">
var var1 = "";
var var2 = "";
function OnLoad() {
try {
var params = getUrlParam("data").split("|");
for (i = 0; i < params.length; i++) {
var vls = params[0].split("+");
switch (vls[0]) {
case "var1":
var1 = vls[1];
break;
case "var2":
var2 = vls[1];
break;
}
alert("La variable " + vls[0] + " se ha inicializado con el valor " + vls[1]);
}
}
catch (e) {
}
}
</script>
</head>
<body onload="OnLoad()">
<div id="results"></div>
</body>
</html>

Obviamente debemos tener en cuenta los casos en los que el usuario quiera pasar los caracteres que usemos como delimitadores en el propio texto… controlar cuando no pasa ningún parámetro… Pero eso entiendo que seréis capaces de controlarlo ampliando este código ;-).

Anuncios




CRM 2011: Mostrar datos de entidades relacionadas

15 05 2012

Una de las opciones solicitada a menudo por los clientes es poder visualizar (en modo de sólo lectura) información relacionada con un lookup en el formulario de detalle donde este reside. Así por ejemplo, en alguna ocasión se nos ha solicitado mostrar datos del contacto principal de una cuenta en el propio formulario de la cuenta. Normalmente, la respuesta suele ser que simplemente pulsando sobre el link del contacto en el lookup, puedes acceder al formulario de detalle del contacto. Sin embargo, a continuación os propongo una solución, extraída a partir del blog de Darren Turner, en la que de una forma sencilla, con un webresource configurable de tipo HTML podemos mostrar información en el propio formulario de cuenta (el ejemplo es valido para cualquier lookup en un formulario, no para partylist).

La solución consiste en crear una página HTML, a la que referenciamos varios webresource como json2.js y jquery1.4.1.min.js para poder realizar llamadas REST y algunas páginas de estilo estándar de CRM 2011. El código de la página HTML es el siguiente:

<HTML xmlns="<a href="http://www.w3.org/1999/xhtml&quot;>">http://www.w3.org/1999/xhtml"></a>
<HEAD>
<TITLE>Related Entity Information</TITLE>
<SCRIPT type=text/javascript src="jquery1.4.1.min.js"></SCRIPT>
<SCRIPT type=text/javascript src="json2.js"></SCRIPT>
<LINK REL=StyleSheet HREF="/_common/styles/global.css.aspx" TYPE="text/css">
<LINK REL=StyleSheet HREF="/_common/styles/fonts.css.aspx" TYPE="text/css">
<LINK REL=StyleSheet HREF="/_common/styles/theme.css.aspx" TYPE="text/css">
<LINK REL=StyleSheet HREF="/_common/styles/form.css.aspx" TYPE="text/css">
<META charset=utf-8></HEAD>
<BODY style="BACKGROUND-COLOR: #F6F8FA; MARGIN: 0px; font-family: Segoe UI, Tahoma, Arial; font-size: 11px;" onload=onload() contentEditable=true>
<DIV id=related></DIV>
<SCRIPT>
var FORM_TYPE_UPDATE = 2;
var FORM_TYPE_READ_ONLY = 3;
var FORM_TYPE_DISABLED = 4;
var ODataPath;
var serverUrl;
var entityName = "";
var id = "";
var fields;
var labels;
var lookup;
var rows = 2;
var entity;
var html = "<table cellpadding='3' cellspacing='0' height='1%' style='table-layout: fixed;' valign='top' width='100%'>";
html += "<col width='115'><col><col class='FormSection_WriteColumnHeaders_col' width='135'><col>";
function onload() {
init();
}
function init() {
serverUrl = parent.Xrm.Page.context.getServerUrl();
ODataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";
GetParameters();
if (parent.Xrm.Page.ui.getFormType() == FORM_TYPE_UPDATE ||
parent.Xrm.Page.ui.getFormType() == FORM_TYPE_READ_ONLY ||
parent.Xrm.Page.ui.getFormType() == FORM_TYPE_DISABLED) {
var value = parent.Xrm.Page.ui.controls.get(lookup).getAttribute().getValue();
if (value != null) {
id = value[0].id.replace('{', '').replace('}', '');
entityName = value[0].entityType;
}
}
retrieveRecord(id);
}
function LoadTable() {
var index = 1;
for (var i = 0; i < fields.length; i++) {
if (index == 1)
html += "<tr valign='top'>";
html += "<td class='ms-crm-FieldLabel-LeftAlign ms-crm-Field-Normal'><label>";
if (labels != null)
html += labels[i];
else
html += fields[i];
var val = "";
try {
if (typeof entity[fields[i]] == "object")
val = entity[fields[i]].Name;
else if (typeof entity[fields[i]] == "undefined")
val = "N/A";
else
val = entity[fields[i]];
}
catch (err) { }
html += "</label></td><td align=left>" + val + "</td>";
if (fields.length == 1)
html += "<td class='ms-crm-Text ms-crm-ReadOnly' colspan=2>&nbsp;4</td>";
if (index == rows) {
html += "</tr>";
index = 1;
}
else
index++;
}
html += "</table>";
related.innerHTML = html;
}
function retrieveRecord(Id) {
var retrieveReq = new XMLHttpRequest();
retrieveReq.open("GET", ODataPath + "/" + entityName.toTitleCase() + "Set(guid'" + Id + "')", true);
retrieveReq.setRequestHeader("Accept", "application/json");
retrieveReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
retrieveReq.onreadystatechange = function () {
retrieveReqCallBack(this);
};
retrieveReq.send();
}
function retrieveReqCallBack(retrieveReq) {
if (retrieveReq.readyState == 4 /* complete */) {
if (retrieveReq.status == 200) {
//Success
entity = JSON.parse(retrieveReq.responseText).d;
LoadTable();
}
else {
throw new Error("Error retrieving Record");
}
}
}
function GetParameters() {
var querystring = unescape(window.location.search.replace('?', '').replace('data=', ''));
var params = querystring.split('&');
for (var i = 0; i < params.length; i++) {
var param = params[i].split('=');
switch (param[0]) {
case "lookup":
lookup = param[1];
break;
case "fields":
fields = param[1].split(',');
break;
case "labels":
labels = param[1].split(',');
break;
}
}
}
String.prototype.toTitleCase = function () {
var A = this.split(' '), B = [];
for (var i = 0; A[i] !== undefined; i++) {
B[B.length] = A[i].substr(0, 1).toUpperCase() + A[i].substr(1);
}
return B.join(' ');
}
</SCRIPT>
</BODY>
</HTML>

Al añadir el webresource en un formulario, debemos pasarle como parámetros, el lookup, los campos a mostrar y los nombres que queremos mostrar en las etiquetas. Con esta simple configuración, el webresource al cargarse, recuperará la información del registro relacionado mediante una llamada REST y los mostrará en un formato de tabla 2xn con estilo CRM 2011. En el siguiente ejemplo, se muestran datos de dos campos lookup a contactos en una entidad personalizada:

Para ello se ha configurado cada webresource con los siguientes datos:

Como veréis en el webresource, se han configurado los parámetros lookup, fields y labels, separados por un &, en el que se indican respectivamente el nombre del lookup del que sacar el GUID, los nombres de esquema a retornar y las etiquetas de los campos a mostrar.





The Microsoft Dynamics Most Influential People for 2012

15 04 2012

Ya se han abierto las votaciones a The Microsoft Dynamics Most Influential People for 2012 (http://www.dynamicsworld.co.uk/top-100-voting-starts-now/). Espero contar con vuestro voto!!! ;-)

No hace falta decir que, a parte de las menciones profesionales que uno pueda conseguir, mi mayor placer es el de poder ser útil a todos aquellos que depositáis vuestra confianza en mi y por consiguiente, en Qurius Prodware, para vuestras necesidades en el ámbito Microsoft Dynamics. Muchas gracias.

Por último, recordaros que entre los nominados se encuentran también varios compañeros de Qurius Prodware España así como otros colegas de Qurius Prodware a nivel internacional.





MSCRM 4.0: Añadir icono de presencia OCS en una entidad personalizada

9 07 2011

Os transcribo a continuación un articulo publicado por nuestro colega Jordi Montaña en su blog. El articulo describe como podemos incluir un icono de presencia en OCS en cualquier entidad personalizada. Recordad que por defecto esta característica tan solo esta disponible en systemuser y contactos por lo que, esta utilidad nos permite ampliar esta utilidad a otras entidades en las que tengamos un email asociado. el articulo original lo podéis encontrar en http://jordi-montana.blogspot.com/2011/07/mscrm-40-customizar-control-de.html.

MSCRM 4 permite de manera soportada la integración con Office Communicator 2007 R2. Ahora bien esta integración estándar sólo está contemplada para entidades systemuser y contacts. Si queréis ver los requisitos para integrar con OCS los podéis encontrar aquí. (No olvidéis incluir el sitio de CRM en los sitios de confianza de IE para que funcione).

Después encontrar diversos artículos como el anterior link y de hacer ingeniería inversa de las dll’s de CRM, efectivamente está hardcoded en CRM el hecho de que sólo se pueda (de manera estándar) integrar con estas dos entitades.

Ahora bien…. eso no significa que no se pueda integrar con otras entidades… :)

Internamente, CRM usa una .dll que viene con la instalación de Office, y que carga como un ActiveObject desde javascript para mostrar el estado (imagen de estado) de OCS. Esta .dll se llama “Name.NameCtrl.1”. Podéis encontrar una definición sobre sus métodos y propieades aquí.

Este objeto recibe como parámetro una dirección de email ligada a un contacto de OCS, y os devuelve el estado de dicho contacto. Con lo cual, Name.NameCtrl.1 os sirve para añadir control de presencia a cualquier entidad de CRM, de hecho, a cualquier elemento que tenga asociado un email. (Evidentemente esto se ejecuta en lado cliente, por ello es necesario instalar OCS).

En concreto, el código Javascript necesario para obtener el estado asociado a un email es el siguiente:

//dirección de correo asociada a cuenta de OCS
var emailAddress = 'test';

//crear el objeto de presencia
var presenceObject = new ActiveXObject("Name.NameCtrl.1");

//la llamada al estado se realiza de manera asíncrona, así que es necesario indicarle
// un callback que recogerá el estado, en nuestro caso la función //se llama OnOcsStateChanged.
presenceObject.OnStatusChange = OnOcsStateChanged;

//llamada que lanzará el callback anterior
presenceObject.GetStatus(emailAddress, emailAddress);

//función que recoge el estado y asigna una imagen (el color asociado al estado) a un tag</pre>
<img alt="" />
<pre> de html
function OnOcsStateChanged(name, status, id) {
var ocsImg = document.getElementById("ocsImg");
ocsImg.setAttribute("src", getPresenceUri(status));
}

//función extraída de MSCRM que es la que usa para pintar el estado para el caso de systemuser's y contacts
//y que nosotros reutilizamos para el caso genérico
getPresenceUri = function (status) {
var showoffline = 1;
var img = "unknown.gif";
switch (state) {
case 0:
img = "imnon.png";
break;
case 11:
img = "imnonoof.png";
break;
case 1:
img = "imnoff.png";
break;
case 12:
img = "imnoffoof.png";
break;
case 2:
img = "imnaway.png";
break;
case 13:
img = "imnawayoof.png";
break;
case 3:
img = "imnbusy.png";
break;
case 14:
img = "imnbusyoof.png";
break;
case 4:
img = "imnaway.png";
break;
case 5:
img = "imnbusy.png";
break;
case 6:
img = "imnaway.png";
break;
case 7:
img = "imnbusy.png";
break;
case 8:
img = "imnaway.png";
break;
case 9:
img = "imndnd.png";
break;
case 15:
img = "imndndoof.png";
break;
case 10:
img = "imnbusy.png";
break;
case 16:
img = "imnidle.png";
break;
case 17:
img = "imnidleoof.png";
break;
case 18:
img = "imnblocked.png";
break;
case 19:
img = "imnidlebusy.png";
break;
case 20:
img = "imnidlebusyoof.png";
break;
}

return "/_imgs/presence/" + img;
}

Muchas gracias Jordi!!!





MSCRM 4.0: Cambiar atributo a requerido o recomendado

23 05 2011

Mostramos a continuación como aprovechar una de las funciones disponibles en Global.js en MSCRM 4.0 para cambiar un atributo a requerido o recomendado en un evento de un formulario.
De forma resumida, para pasar un atributo a requerido el código JavaScript es:

SetFieldRequiredOrRecommended(crmForm.all.<atributo>_c, FORM_FIELD_TYPE_REQUIRED, "Required");
crmForm.all.<atributo>.req = 2;

Para pasarlo a opcional:

SetFieldRequiredOrRecommended(crmForm.all.<atributo>_c, FORM_FIELD_TYPE_NORMAL, "");
crmForm.all.<atributo>.req = 0;

Y por fin, para pasarlo a recomandado:

SetFieldRequiredOrRecommended(crmForm.all.<atributo>_c, FORM_FIELD_TYPE_RECOMMENDED, "Recommended");
crmForm.all.<atributo>.req = 1;

La función SetFieldRequiredOrRecommended es una función del sistema MSCRM que se encuentra en Global.js por tanto, disponible desde cualquier formulario de edición de las entidades.





CRM 2011: Mostrar formulario en función del valor de un picklist

22 04 2011

La posibilidad de crear varios formularios para una misma entidad y su visualización en función de los roles del usuario abre un mundo de posibilidades. Sin embargo, muchos desarrolladores se preguntan inmediatamente si es posible mostrar un formulario u otro en función del valor de un atributo de la entidad. Así por ejemplo, nos puede interesar que al abrir un registro, se muestre un formulario u otro (todos ellos disponibles para el role del usuario) en función del valor de un picklist.

Pues bien, a continuación os describo un pequeño desarrollo que nos permitirá abrir un registro en un formulario u otro en función del valor de un picklist. Antes de mostrar el desarrollo es importante tener en cuenta que:

1. Cuando en una entidad un usuario tiene disponibles varios formularios por su role, CRM 2011 siempre mantiene el último formulario seleccionado. Así por ejemplo, si disponemos de dos formularios Form1 y Form2 y, al abrir un registro seleccionamos el Form2, al abrir un nuevo registro o al crear uno nuevo, se nos mostrará con el Form2

2. Los formularios que queremos mostrar en función del valor de un atributo deben estar todos disponibles para el role de los usuarios que accederán a los registros de la entidad

3. Al seleccionar por código un formulario u otro debemos ocultar el selector de formularios ya que si no, el desarrollo perdería su gracia ya que, a pesar de mostrar el formulario correcto el usuario podría cambiar a otro formulario

4. Al cambiar de formulario por el selector de formularios, si se ha realizado algún cambio en los valores de los atributos se nos muestra un mensaje de si queremos guardar antes de cambiar de formulario. Por tanto, nuestro desarrollo deberá contemplar la opción de ocultar el selector de formulario para que tan sólo se pueda cambiar de formulario al guardar y en función del valor del atributo

5. Debido al punto 1, cuando el usuario entre en un registro en el que se muestre por ejemplo el Form2, cuando entre en un registro que debe mostrar el Form1 se producirá una primera carga del registro con el Form2 e inmediatamente se cargará el registro en el Form1. Esto producirá un efecto de ‘flicker’ que puede no ser del todo ‘agradable’

6. Cada formulario definido en una entidad dispone de sus propias librerías y funciones por tanto, si queremos realizar un desarrollo común hay que crearlo en un webresource y adjuntarlo como librería en todos los formularios que definamos

Dicho esto, vemos a continuación el desarrollo que nos ha de permitir abrir un formulario u otro en función del valor de un atributo. En el siguiente ejemplo se ha creado el siguiente entorno:

1. Se ha creado una nueva entidad Test con tres atributos: Name (string), Select Form (picklist) y Test Field (string)

2. En el picklist de SelectForm se han creado dos valores 1-Form1 y 2-Form2

3. Se han definido dos formularios Form 1 y Form 2. En el Form 1 se muestran los tres atributos. En el Form 2 solo se muestra el atributo Name y el Select Form

4. Se ha definido un webresource new_OnLoad_Test de tipo JavaScript que se ha añadido como librería en el Form 1 y en el Form 2 (hay que tener en cuenta que cada formulario utiliza sus librerías y funciones!!!). El código de la librería es:


function showFormItems()
{
var lblShow = "Form1";
// Si el valor del picklist no se corresponde con el formulario seleccionado, lo cambia
if (Xrm.Page.getAttribute("new_selectform").getValue() == 2)
lblShow = "Form2";
// Cambia de formulario si no se corresponde con el que queremos mostrar para el valor del picklist
if (Xrm.Page.ui.formSelector.getCurrentItem().getLabel() != lblShow)
{
var items = Xrm.Page.ui.formSelector.items.get();
for (var i in items)
{
var item = items[i];
var itemId = item.getId();
var itemLabel = item.getLabel();
if (itemLabel == lblShow)
{
item.navigate();
}
}
}
// Oculta el selector de formularios
document.getElementById("crmFormSelector").style.display = "none";
}

5. Se ha añadido como función asociada al OnLoad la función showFormItems

Una vez configurado todo lo anterior podemos comprobar que al crear un nuevo formulario con el valor del picklist Form1, siempre se abrirá el registro con el formulario Form 1 y lo mismo para otro registro con Form2, para el cúal se abrirá el formulario Form 2.

Al abrir el Registro con Form1, se abrirá con el formulario de tipo Form1 ya que el picklist tiene el valor Form1 seleccionado:

Al abrir el Registro con Form2, se abrirá con el formulario de tipo Form2 ya que el picklist tiene el valor Form2 seleccionado:

Como comentaba al inicio, al abrir el segundo registro se produce una doble carga de formulario pues CRM 2011 intenta abrir el registro con el último tipo de formulario utilizado (Form1).

En este ejemplo he usado un picklist con dos valores pero, entiendo que sabréis extrapolarlo a cualquier otro tipo de atributo y lógica a aplicar.





MSCRM 4.0: Añadir confirmación a la Distribución de actividades de campaña

5 04 2011

Por defecto MSCRM 4.0 no solicita ningún tipo de confirmación antes de ejecutar la distribución de actividades de campaña cuando el usuario utiliza la opción Distribuir actividad de campaña.

En algunas ocasiones esa acción supone la creación de multitud de actividades (correos, llamadas telefónicas…). Para evitar que el usuario pueda usar esa acción por equivocación, hemos añadido un cuadro de diálogo de confirmación al ejecutar la opción Distribuir actividad de campaña. Para ello tan sólo hemos tenido que añadir el siguiente código en el evento OnLoad de la entidad Actividad de campaña:


var obj = document.getElementById("btnPropagate")

obj.onclick = null;

obj.attachEvent('onclick',

function(){

if (confirm("¿Está seguro que desea ejecutar la Distribución de la actividad de campaña?"))
  window.execScript(document.getElementById("btnPropagate").action);

});

De forma resumida lo que hacemos con este código es substituir el evento onclick del botón Distribuir actividad de campaña para añadirle posteriromente un evento que incluye una confirmación antes de ejecutar la acción del botón.

Tras integrar este código, el usuario deberá confirmar la acción antes de que esta se ejecute:

En el ejemplo mostrado no se ha indicado ninguna lista de marketing para la actividad por lo que, tras aceptar la confirmación nos muestra el mensaje del proceso de Distribuir actividad de campaña:

Este ejemplo tan sólo es válido para aquellas acciones en que el estándar utiliza el sistema executeScript(action). Sin embargo, con un poco de ingenio estamos seguros que sabréis adaptar este cambio a otros botones del ISV que no utilicen este sistema.