Microsoft Dynamics AX for Retail

Hace ya bastante tiempo que Microsoft anunció en su Statement of Direction for Microsoft Dynamics AX (recientemente actualizado, por cierto) que pensaba incluir en su producto Dynamics AX una solución vertical orientada a la venta Retail (venta al detalle), ademas, recordemos, de soluciones para el sector público y servicios profesionales.

Pues bien, esa previsión se ha cumplido con el anuncio de disponibilidad de la solucion AX for Retail realizada estos dias y de la cual tenemos todavía poca información, simplemente que incorpora out-of-the-box desde una solucion POS (Point-Of-Sale, o Terminal Punto de Venta -TPV-) hasta control de tiendas, merchandising, etc...

Se publicará mucho próximamente sobre el tema pero dejo unos cuantos enlaces para ampliar información por si alguien le interesa, hasta que empiecen a publicarse documentación y manuales oficiales sobre el tema:

HOWTO: Crear controles y eventos dinámicamente en formularios AX

En este HOWTO me gustaría explicar el proceso completo para crear dinámicamente controles en un formulario existente (aunque también podría no existir, pero ya sería complicar demasiado) y además manejar los eventos generados por ese control tanto dentro como fuera del formulario en sí.

Lo haremos paso a paso, de lo mas facil a lo mas complicado:

Empezaremos con un formulario nuevo de AX totalmente estándar sin ninguna propiedad modificada:

Dynamic Runtime Forms | 1

El primer paso será generar controles en este formulario de forma dinámica, es decir, mediante código en tiempo de ejecución. Para ello modificaremos el método estandar run() del formulario de la siguiente manera:

public void run()
{
    FormButtonControl   fButtonCtr;
    ;
   
    super();
   
    element.lock();

    fButtonCtr = element.design().addControl(FormControlType::Button, "buttonName");
    fButtonCtr.text("Ejemplo");

    element.unLock();
}

Con lo que habremos añadido al diseño del formulario, un boton generado en tiempo de ejecución:

Dynamic Runtime Forms | 2

Añadimos otro control al formulario y establecemos alguna propiedad a los controles. Llevo todo este código a un nuevo método del formulario para mantener cierto orden:

public void run()
{
    super();

    element.drawControls();
}

private void drawControls()
{
    FormButtonControl   fButtonCtr;
    FormStringControl   fStringCtr;
    ;

    element.lock();

    // Control de texto (StringEdit), en negrita, ancho total
    fStringCtr = element.design().addControl(FormControlType::String, 'stringEditTexto');
    fStringCtr.bold(10);
    fStringCtr.widthMode(FormWidth::ColumnWidth);
    fStringCtr.text("Ejemplo");

    // Botón, con imagen incrustada y texto, ancho total
    fButtonCtr = element.design().addControl(FormControlType::Button, 'buttonUp');
    fButtonCtr.widthMode(FormWidth::ColumnWidth);
    fButtonCtr.buttonDisplay(FormButtonDisplay::TextAndImageLeft);
    fButtonCtr.normalResource(1067);
    fButtonCtr.text("Subir");

    // Botón, con imagen incrustada y texto, ancho total
    fButtonCtr = element.design().addControl(FormControlType::Button, 'buttonDown');
    fButtonCtr.widthMode(FormWidth::ColumnWidth);
    fButtonCtr.buttonDisplay(FormButtonDisplay::TextAndImageLeft);
    fButtonCtr.normalResource(1068);
    fButtonCtr.text("Bajar");

    element.unLock();
}

Esto es otra cosa!:

Dynamic Runtime Forms | 3

Bien, hasta aquí la parte fácil. Crear controles en runtime es bastante sencillo pero ahora, ¿qué hago con ellos?. Lo primero, tendremos que indicar al formulario donde puede encontrar el código manejador de eventos de los nuevos controles, primero la maner mas sencilla:

Lo primero que hay que hacer en cualquier caso es indicar a AX que tenemos métodos controladores de eventos propios, es decir, que estamos sobrecargando sus propios métodos controladores, igual que hacemos cuando "anulamos" métodos en tiempo de diseño (la traducción no es muy acertada):

public void run()
{
    // Sobrecargo los métodos de control
    element.controlMethodOverload(true);
    // ¿Dónde están los métodos de control?: Aquí mismo (this)
    element.controlMethodOverloadObject(this);

    super();

    element.drawControls();
}

Con el método controlMethodOverload indicamos que tenemos algún método sobrecargado, es decir, que busque primero nuestros métodos antes de ejecutar los suyos propios. El método controlMethodOverloadObject indica a AX dónde, en qué objeto, puede encontrar los métodos sobrecargados.

Hay que destacar dos puntos importantes:

  • El método se debe declarar siempre como public void
  • El nombre del método siempre ha de ser nombreObjeto_evento()

public void buttonUp_clicked()
{
    ;

    info(element.design().controlName('stringEditTexto').valueStr());
}

Funciona! Pero hay un problema. ¿Qué sentido tiene crear controles en tiempo de ejecución de una manera dinámica -por ejemplo, basandonos en opciones configurables en una tabla- si el código asociado a ellos debe permanecer estáticamente en el formulario? No tiene ningún sentido.

Es por esto que se hace necesaria la segunda opción, llevar tódo el código fuera del formulario, por ejemplo, a una clase, como es el caso. El formulario pués se libera del código y queda de una manera muy sencilla (e independiente totalmente de los controles añadidos en runtime):

public class FormRun extends ObjectRun
{
    // Clase manejadora de eventos
    JAEBaseForm_Mngr    baseFormMngr;
}

public void init()
{
    super();

    // Construyo una instancia de la clase manejadora asociada a
    // la instancia actual de este formulario
    baseFormMngr = JAEBaseForm_Mngr::construct(this);
}

public void run()
{
    // Sobrecargo los métödos de control
    element.controlMethodOverload(true);

    // ¿Dónde están los métödos de control?: Aquí mismo (this)
    element.controlMethodOverloadObject(baseFormMngr);

    super();

    // Crear los controles
    baseFormMngr.drawControls();
}

Y es la clase la que contiene todo el codigo dinámico, de manera que es posible reutilizarla en diferentes formularios:

class JAEBaseForm_Mngr
{
    FormRun     element;
}

private void new(FormRun _formRun)
{
    element = _formRun;
}

public static JAEBaseForm_Mngr construct(FormRun _element)
{
    return new JAEBaseForm_Mngr(_element);
}

public void drawControls()
{
    FormButtonControl   fButtonCtr;
    FormStringControl   fStringCtr;
    ;

    element.lock();

    // Control de texto (StringEdit), en negrita, ancho total
    fStringCtr = element.design().addControl(FormControlType::String, 'stringEditTexto');
    fStringCtr.bold(10);
    fStringCtr.widthMode(FormWidth::ColumnWidth);
    fStringCtr.text("Ejemplo");

    // Botón, con imagen incrustada y texto, ancho total
    fButtonCtr = element.design().addControl(FormControlType::Button, 'buttonUp');
    fButtonCtr.widthMode(FormWidth::ColumnWidth);
    fButtonCtr.buttonDisplay(FormButtonDisplay::TextAndImageLeft);
    fButtonCtr.normalResource(1067);
    fButtonCtr.text("Subir");

    // Botón, con imagen incrustada y texto, ancho total
    fButtonCtr = element.design().addControl(FormControlType::Button, 'buttonDown');
    fButtonCtr.widthMode(FormWidth::ColumnWidth);
    fButtonCtr.buttonDisplay(FormButtonDisplay::TextAndImageLeft);
    fButtonCtr.normalResource(1068);
    fButtonCtr.text("Bajar");

    element.unLock();
}

public void buttonUp_clicked()
{
    FormStringControl  fStringCtr = element.design().controlName('stringEditTexto');
    ;

    fStringCtr.text("Arriba!");
}

public void buttonDown_clicked()
{
    FormStringControl  fStringCtr = element.design().controlName('stringEditTexto');
    ;

    fStringCtr.text("Abajo!");
}

De esta manera conseguimos el objetivo. El objeto Form es estático y contendrá su propia funcionalidad que no cambiará en runtime. Sin embargo ejecuta métodos de la clase controladora que se encarga de toda la parte dinámica, tanto generar los controles, como manejar sus eventos.

Las posiblidades son enormes. Desde generar pequeños formularios a modo de diálogos, a construir formularios dinámicos basados en datos de tablas en la base de datos, igual que el estándar hace con el Configurador de Productos. De hecho las clases que generan el formulario del modelo de producto (las que empiezan por PBA*) son un buen banco de ejemplos sobre este tema.

Adjunto un proyecto con los dos objetos por si pudiera ser de utilidad.

Descargar objetos

Microsoft Dynamics AX 2009 RTM y SP1 Rollup 5

Ayer fue publicado el último rollup de actualizaciones para AX 2009 RTM y SP1. Esta es, como siempre, una actualización acumulativa (por tanto incluye los RU del 1 al 4) y se puede descargar desde PartnerSource y CustomerSource. En los siguientes artículos se encuentran los links de descarga e información de todos los hotfixes incluidos:

Artículos de conocimiento y descarga RU-5

Subida del IVA Julio 2010 vs AX

A principios del mes que viene empezará a aplicarse en España la subida del IVA aprobada por el gobierno recientemente. Para reflejar este cambio en Dynamics AX se puede optar por diferentes opciones de configuración las cuales ya se han comentado en varios lugares, algunos de ellos recopilo por si a alguien le fueran de utilidad.

En primer lugar Microsoft publicó hace ya uno tiempo una página en PartnerSource recopilando los hotfixes publicados para las adaptaciones de los modelos de declaración del IVA, la cual ya publiqué en este blog en febrero.

Mas recientemente han publicado el Whitepaper Configuración subida tipos de IVA para Microsoft
Dynamics AX
(requiere login) donde se explica oficialmente la manera de configurar los nuevos tipos en AX aplicables el mes que viene.

Por otro lado tenemos un magnífico artículo publicado por Antonio Gilabert en El Rincón Dynamics explicando de manera gráfica y detallada como realizar la configuración: Cambios de Gravamen en el IVA - Julio 2010.

HOWTO: Imprimir PDF desde AX mediante código

Habitualmente se presenta la necesidad de imprimir documentos desde Dynamics AX en formato PDF. Esta es una funcionalidad, presente en el estándar, se puede replicar por código según las necesidades.

El siguiente Job (basado en otro original de Giridhar Raj's Blog) indica como hacerlo para imprimir una factura pero es fácilmente extensible a otro tipo de documentos:


Job | Imprimir a PDF

Nuevo editor X++ en acción

Ya hace tiempo que vimos en el blog de Vincent's algunos de las nuevas funcionalidades del editor de X++ para la próxima version de Dynamics AX, por ejemplo, algunos detalles del auto-completar del código.

Hoy acaba de publicar un video con mas novedades, todas ellas interesantes, y lo mas importante es que se pueden ver ya funcionando. Cosas como la implementación (por fin) de la documentación XML integrada con el auto-completar (igual que en Visual Studio):

AX6 | X++ Editor

la edición multi-linea o la selección de texto en columnas:

AX6 | X++ Editor

dejan un muy buen sabor de boca.

Una pena que todavía quede bastante tiempo para poder disfrutar de estas novedades, y las que están por llegar.

Conector para SAP en Dynamics AX

Según podemos leer en el Microsoft Press Center, Microsoft ha publicado un conector para integrar la solución Dynamics AX con SAP Business Suite, de manera que pone de manifiesto su apuesta por soluciones "2-tier" permitiendo instalar su producto AX en oficinas, delegaciones, etc... manteniendo  una solución SAP en las oficinas principales.

La integración se sostiene sobre servicios BizTalk e incluye, según comentan, consolidación financiera, cadena de suministro multi-empresa y automatización de planta, entre otras, y ponen como caso de éxito de este sistema al grupo Würth Group.

Se puede ller el comunicado completo en Microsoft Press Center

Microsoft Dynamics Community

Sirva este post para anunciar que desde ayer este blog forma parte de la Microsoft Dynamics Community, lo cual es una gran motivación para seguir mejorando la calidad y cantidad del contenido publicado.

Microsoft Dynamics Community - Dynamics AX

Pensaba anunciar este hecho junto a unas modificaciones que tengo a punto de terminar para añadir unas pequeñas mejoras (como enlaces a perfiles de twitter y linkedIn), pero se me han adelantado.

HOWTO: Usar y crear Mapas (Maps) en Dynamics AX

Los mapas (Maps) son elementos particulares en Dynamics AX a los que vale la pena dedicar tiempo y aprender a utilizarlos, ya que resultan indispensables para ahorrar tiempo en modificaciones y desarrollos nuevos.

Para ver la utilidad de los Mapas pensemos un caso habitual: A una instalación estándar de AX se le añaden una serie de campos nuevos referentes a artículos (medidas, pesos, redondeos, precios, tallas, ...) particulares de esta instalación, con los cuales se tiene que trabajar en nuevos desarrollos para calculo de precios, tarifas, cantidades, ...

Lo tradicional con la mayoría de tecnologías sería añadir esos campos a las tablas necesarias (utilizando EDT's para mantener una coherencia de tipos), que podrían ser líneas de pedidos, facturas, compras, inventario, proyectos, producción ... (en resumen, muchas tablas), escribir el código necesario para la lógica requerida e implementarla en cada tabla, o utilizando clases, o ambas cosas.

Dynamics AX nos ofrece un objeto que encapsula esos campos, con su coherencia de tipos junto a la lógica que los maneja, de manera que se pueda aplicar toda esa información a las tablas necesarias de manera fácil (muy facil).

Lo vemos con un ejemplo, para el cual utilizo un mapa utilizado en muchísimas partes de la aplicación estándar y que encapsula la lógica y los campos del manejo de direcciones en AX, el mapa AddressMap.

Lo primero que haremos será crear una tabla nueva (se puede usar una existente) y buscar en el AOT el mapa estándar AddressMap (Data Dictionary > Maps > AddressMap). Desde el nodo del mapa en el AOT se pueden seleccionar los Fields necesarios y arrastrarlos a la tabla para crear los  campos.

jaestevan.com | Mapas 1

Bien, ya podemos guardar la tabla. El siguiente paso es "mapear" los campos de la nueva tabla, con el mapa, demanera que le digamos qué campo del mapa es qué campo en la tabla (el nombre no tiene por qué ser el mismo, aunque es recomenable para facilitar su comprensión).

Para ello vamos al nodo AddressMap en el AOT y añadimos un nuevo elemento a su sub-nodo Mappings. Al hacerlo nos aparecen todos los campos del mapa, para asignarlos a los campos necesarios de la tabla. Si algún campo del mapa no existe en la tabla puede dejarse sin asignar.

jaestevan.com | Mapas 2

Guardamos el mapa y con ésto ya tenemos los campos de nuestra tabla asignados al mapa estándar, vamos a probar este enlace para aplicar la lógica de direcciones a nuestra tabla, para lo cual anulamos el método modifiedField de la misma y añadimos una nueva línea:

jaestevan.com | Maps 3

Y con esto tenemos la lógica de direcciones implementada en nuestra tabla, de una manera totalmente optimizada ante futuros cambios del estándar, coherente con los tipos y las relaciones de los campos, evitando duplicar código, ... Se puede probar insertando un nuevo registro y modificando el código postal, por ejemplo.

Comentarios serán bien recibidos, los mapas es una cosa que cuanto mas se utiliza, más util parece.

Migrar control de código fuente en AX de VSS a TFS

Acaban de publicar un nuevo White Paper que indica el procedimiento para migrar una implementación de control de código fuente que utiliza Microsoft Visual SourceSafe al nuevo Microsoft Team Foundation Server incluido con el nuevo (cuantas cosas nuevas) Microsoft Visual Studio 2010.

Tiempo de novedades y de cambios. En mi humilde opinión el control de código fuente aún tiene que mejorar en AX para ser totalmente operativo y sería, en cualquier caso, una funcionalidad muy interesante para los desarrolladores.