AX Retail 2012 R2: Crear un skin personalizado

29 03 2013

Una de las características de AX Retail 2012 R2 es la posibilidad de crear un nuevo estilo (skin) para la aplicación POS. Si bien es muy sencillo crear un nuevo skin, debe validarse completamente el comportamiento de cada estilo en el POS tras realizar la personalización. Para personalizar el skin seguiremos los pasos siguientes:

1. Instalamos la aplicación Developer Express v2011 vol 2 específica para AX Retail. Para ello, deberemos adquirir una licencia del producto (necesaria para personalizar el POS de AX Retail) en http://www.devexpress.com y solicitar esa versión específica

2. Una vez instalada la aplicación ya dispondremos de la herramienta SkinEditor, la ejecutamos:

3. Podemos crear un skin desde cero pero, os recomiendo que utilicéis como base el skin por defecto de AX Retail 2012 R2 y a partir de este skin personalicéis lo que necesitéis. Para ello, en primer lugar tendremos que usar la opción File/import para importar la librería por defecto de skins de AX Retail 2012 R2. Encontrareis la librería de skins por defecto, POSThemes.dll, en el directorio C:Program Files (x86)Microsoft Dynamics AX60Retail POSSkins.

4. Seleccionad el archivo POSThemes.dll tras seleccionar el tipo de archivos Skin assembly(*.dll)

5. Tras realizar la importación, los estilos por defecto de AX Retail 2012 R2 estarán disponibles como plantillas para iniciar vuestro skin personalizado (blue, red, green…)

6. Creamos un nuevo proyecto de skin a partir de uno de los skins importados en el paso anterior. Utilizamos la opción File/New y seleccionamos como Template skin, alguno de los estilos de AX Retail 2012 R2 (gray, green, light, pink, blue o dark):

7. En este ejemplo, hemos nombrado el proyecto como TestSkinTheme y el nuevo skin como TestSkin (este nombre lo usaremos posteriormente en AX HQ para asignarlo a un POS):

8. Una vez creado el proyecto, ya podemos personalizar las características que deseemos. En concreto, quizás el que más os interese personalizar sea el formato del formulario y de los botones, que encontrareis en la opción Common, y el aspecto de las tablas, que encotnrareis en el apartado Grid

9. En el apartado Grid podréis personalizar todos los controles relacionados con las tablas. En nuestro ejemplo hemos modificado algunos colores del background de la cabecera y de las filas y en el apartado Common hemos modificado el color de fondo y de la fuente para ajustar el formato a la imagen corporativa de nuestro cliente

10. Una vez realizados los cambios, Guardamos el proyecto y creamos el ensamblado con la opción File/Create assembly

11. Observaremos que se ha creado un nuevo ensamblado en el directorio donde hemos indicado. En nuestro ejemplo, tras completar el proceso:

12. Comprobamos que se ha generado el ensamblado

13. A continuación vamos a aplicar nuestro nuevo skin a un POS. Para ello, tendremos que añadir el nuevo skin a las opciones de AX HQ. En los perfiles visuales, nos situamos en el perfil visual que queramos personalizar y añadimos el nuevo skin al campo Tema y seleccionamos Ver detalles. En el enumerado correspondiente añadimos la opción TestSkin.

14. Una vez configurado el Perfil visual, podemos sincronizar los datos con el POS y continuar con la configuración del skin en el POS.

15. Para configurar el POS, debemos copiar el ensamblado del skin en la carpeta C:Program Files (x86)Microsoft Dynamics AX60Retail POSSkins

16. Tras copiar el ensamblado y haber sincronizado la configuración del Perfil visual, al iniciar la aplicación veremos que utiliza nuestro nuevo skin

17. Podemos probar directamente el skin modificando el campo POSSKINNAME de la tabla RETAILVISUALPROFILE en la base de datos de tienda. De este modo no tenemos que modificar el visual profile en HQ. Sin embargo, una vez validado, deberemos aplicarlo en HQ para que cuando se sincronice el JOB correspondiente no nos sobrescriba ese cambio





AX Retail 2012 R2: Como modificar los numeradores (seed values) en POS

27 02 2013

En Microsoft Dynamics AX 2012 R2 los numeradores se registran en la tabla POSSEEDVALUES de la base de datos de tienda. Sin embargo, si por algún motivo (probablemente para una migración desde 2009) deseamos alterar alguno de los valores de esta numeración nos encontraremos en que no es suficiente cambiar el valor del numerador en esta tabla. A continuación describimos los pasos a realizar para alterar una de las numeraciones almacenadas en POSSEEDVALUES.

Aparentemente, modificando el valor de la tabla POSSEEDVALUES para un tipo de numerador especifico, por ejemplo el TYPEID = 2 que corresponde al tipo ReceiptSale (Ventas), debería cambiar la numeración del ticket de la siguiente transacción. Sin embargo, comprobamos que a pesar de modificar ese valor, POS recupera el valor anterior incrementándolo. Pero, ¿de dónde obtiene la numeración anterior?. En primer lugar podríamos pensar que lo obtiene a partir de una búsqueda del máximo número de ticket, sin embargo, esto sería bastante complicado pues esa numeración se utiliza en la máscara correspondiente al formato de numeración de ticket.

Pues bien, tras investigar el procedimiento usado para recuperar el siguiente valor del numerador correspondiente a uno de los tipos de numerador (a partir del código del service Application) hemos descubierto que localmente POS guarda el último numerador en un archivo encriptado en un LocalStorage. En concreto el archivo lo podéis encontrar en una ruta aleatoria en el path c:ProgramDataIsolatedStorage… El path concreto debéis localizarlo realizando una búsqueda del archivo donde se almacena esos datos que se llama RetailPosSettings.config.

Por ejemplo, en mi portátil, la ubicación del archivo es:

C:\ProgramData\IsolatedStorage\3adcmvnc.yhyroqnoco1.13j\Publisher.m5qskfiokkoxzszdm3chhfwlcduqzom2\AssemFiles

Así pues, si queremos alterar la numeración de algún tipo de numerador en POS, debemos seguir los siguientes pasos:

  1. Localizar el archivo RetailPosSettings.config en el path C:\ProgramData\IsolatedStorage
  2. Renombrar o eliminar el archivo RetailPosSettings.config
  3. Alterar el numerador correspondiente en la tabla POSSEEDVALUES para el terminal del que queramos modificar el numerador. Los tipos de numerador (TYPEID) disponibles se muestran en la siguiente tabla
  4. Iniciar el POS en el terminal y realizar una acción que utilice el numerador (por ejemplo, si modificamos el numerador con TYPEID=2 podemos realizar una venta normal) y observar que ya ha recuperado el nuevo numerador

Tipos de numerador

ReceiptDefault 1
ReceiptSale 2
ReceiptReturn 3
ReceiptSalesOrder 4
ReceiptSalesInvoice 5
ReceiptPayment 6
BatchId 7
TransactionId 8
ReceiptCustomerSalesOrder 9
ReceiptCustomerQuote 10

No he encontrado ninguna información ‘oficial’ de Microsoft sobre cómo realizar este proceso de renumeración por lo que la solución que se propone en este artículo es completamente ‘no soportada’ y se ofrece tal cual, sin ningún tipo de responsabilidad sobre los efectos que pueda producir sobre cualquier instalación de POS.





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 ;-).





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.





AX Retail 2009: Control del password de usuario en POS

30 04 2012

Una de las características importantes del POS de AX Retail 2009 es la utilización de Transaction Service para realizar operaciones desde el POS al BackOffice. Una de estas operaciones remotas puede aplicarse a la validación del password de usuario (LogOn). Sin embargo, una configuración específica de este servicio puede tener como consecuencia que el usuario pueda entrar en el POS sin que se valide su clave de usuario, o lo que es lo mismo, que el POS no tenga ninguna validación de la clave del usuario. A continuación describimos brevemente esa situación. Entendemos que esto se trata de un bug de la aplicación (por lo que así lo hemos reportado), sin embargo, mientras esa solución no llega, consideramos importante tener en cuenta la configuración que explicamos a continuación.

En la configuración del Perfil de Transaction Service disponemos de un check que permite indicar si la validación de la clave del usuario se realizará en el equipo local o bien a través de Transaction Service contra el BackOffice.

Si marcamos este flag, debemos tener en cuenta que la validación de la clave se realizará en BackOffice por lo que debemos asegurar la conexión entre el POS y el BackOffice y que el servicio Transaction Service funciona correctamente. Si alguno de esos aspectos falla, la validación del usuario no podrá realizarse por lo que el usuario no podrá acceder al POS tras esperar un tiempo a que el sistema detecte el fallo de conexión y, apareciendo el siguiente mensaje:

Para evitar esta situación, con el flag de autenticación por Transaction Service, uno puede usar otro parámetro de configuración a nivel de usuario que permite obviar los mensajes de error de Transaction Service. Si, manteniendo el flag de autenticación por Transaction Service, marcamos la opción de continuar con errores en Transaction Service en un usuario:

Al intentar acceder al POS, el sistema intentará validar la clave en BackOffice a través de Transaction Service, si encuentra un error lo evitará y accederá al POS sin haber validado la contraseña!!!.

Por tanto, es muy importante que, a menos de que el fabricante aporte otra solución, no se realice la autenticación del usuario en BackOffice a través de Transaction Service y se utilice la autenticación local, es decir que NO se marque el flag de Personal de Retail Transaction Service (traducción poco acertada por cierto…).





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.





CRM 2011: Editor Visual de Ribbon

3 03 2012

Desde hace algún tiempo he estado usando una herramienta que me parece excepcional, CRM 2011 Visual Ribbon Editor. Se trata de un editor visual de Ribbon publicado en CodePlex. Podéis descargar esta herramienta en http://crmvisualribbonedit.codeplex.com/.

Antes de descubrir esta herramienta cada vez que tenia que añadir, modificar, ocultar… un botón el la Ribbon, todo se me hacía cuesta arriba!!! Si bien una vez te has puesto con el XML del Ribbon terminas entendiéndolo, si no tienes que hacer esa tarea a menudo, terminas olvidándote. Por ese motivo esta herramienta es francamente útil (tanto que incluso no entiendo como Microsoft no incluye un editor de este tipo en el propio producto CRM).

En este post os describo rápidamente como añadir un grupo de botones a una Ribbon con esta herramienta. Para seguir este ejemplo tenéis que descargaros en primer lugar la herramienta del link anterior. Todavía no he podido sacar u obtener una relación de los entornos para los que funciona ni para que rollups (aparentemente para RU6 está dando muchos problemas, aunque en la MV donde lo he usado para este ejemplo tiene RU6…). Por mi parte, lo he usado con CRM OnLine y con CRM OnPremise con autenticación AD y me ha funcionado. NO he conseguido que me funcionara con CRM OnPremise con IFD… Si en la conexión donde queréis usarla no funciona, podéis utilizar un entorno virtual donde funcione y exportar la Ribbon e importarla en vuestro entorno.

En el siguiente ejemplo, añadiremos tres botones en un nuevo grupo en la Ribbon de la entidad cuenta para el formulario de detalle. Seguro que encontrareis rápidamente como editar otras Ribbons como la del grid o la principal.

1. En primer lugar iniciamos la aplicación

2. Nos conectamos a la organización que queramos editar

3. Pulsamos sobre la opción Open para seleccionar la entidad que queremos modificar

4. La carga del Ribbon puede tardar un rato…

5. Utilizamos la opción New Group para añadir un nuevo grupo

6. Podemos modificar el campo Sequence para ubicarlo en otra posición. También he usado el campo Template para indicar el formato de los botones (Flexible3)

7. Tras seleccionar el nuevo grupo y configurar los parámetros del mismo, utilizamos la opción New Button para añadir los tres botones. En este ejemplo utilizo tres imágenes del directorio /_imgs de CRM pero podéis usar otras imágenes. He usado el campo Template Alias para asignar el formato o1 (Large)

8. Una vez configurado el grupo y los botones utilizamos la opción Save para guardar y publicar la nueva Ribbon. Accedemos a un registro de cuenta y ya tenemos la Ribbon lista!!!

A partir de este sencillo ejemplo, podéis profundizar más en las distintas funcionalidades de la aplicación para ocultar botones estándar, cambiar las acciones, añadir las acciones a los nuevos botones… Espero que la herramienta os sea tan útil como lo es para mi!