j.a.estevan
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:

- Descargar Job (.xpo)
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.

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.

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:

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.
HOWTO: AX – Distribuir ficheros con SysFileDeployment
A veces es util poder distribuir a los clientes de AX ficheros auxiliares que van a necesitar para la ejecución del programa, por ejemplo, controles ocx, librerias dll, iconos, imágenes, etc.
Gracias a André Arnaud, que amablemente respondió a una duda mía en los foros de la Microsoft Dynamics Community he descubierto el framework de AX SysFileDeployment que viene perfecto para esta tarea.
La idea de este framework es heredar clases para indicar a AX qué ficheros deben copiarse a la parte cliente cada vez que se realiza una nueva instalación de AX. Este framework tiene bastantes limitaciones pero la ventaja es que en su código se gestiona bastante bien el tema de permisos para copiar ficheros situados en el servidor hacia el cliente.
Comparto las pruebas que yo he realizado por si a alguien le pudieran servir. En primer lugar, hay que generar una nueva clase heredada de SysFileDeploymentFile indicando a AX qué fichero hay que copiar, dónde esta y hacia donde copiarlo. Esta es la estructura básica de la clase heredada, que esta bien autodocumentada por si se necesita mas personalización:
{
public Filename filename()
{
return 'jaestevan.png';
}
//AOSRunMode::Server
protected FilenameOpen sourcePath()
{
return "C:\\Archivos de programa\\Microsoft Dynamics AX\\50\\Application\\Share\\Include\\";
}
//AOSRunMode::Client
protected FilenameSave destinationPath()
{
return xInfo::directory(DirectoryType::Include);
}
}
Una de las limitaciones de este framework es que sólo se ejecuta la primera vez que se ejecuta el cliente en un equipo determinado. Por tanto para distribuir nuevos ficheros o para hacer pruebas, como es mi caso, habría que modificar el método applBuildNo() de la clase ApplicationVersion, para forzar al sistema a actualizar la versión. Haciéndolo de este modo, AX detectará que tenemos ficheros pendientes de actualizar en el servidor y nos mostrará el siguiente diálogo:

Como no me apetecía andar modificando la build de mi entorno de pruebas, he hecho un pequeño Job que simula el código que se ejecutaría automáticamente de manera normal:
{
container classes;
SysFileDeployment sysFileDeployment;
container classVersion;
classId classId;
;
classId = classNum(SysFileDeploymentCesserIcon);
sysFileDeployment = classfactory.createClass(classId);
classes += [[classId,sysFileDeployment.getServerVersion()]];
classVersion = conpeek(classes, 1);
classId = conpeek(classVersion, 1);
sysFileDeployment = classfactory.createClass(classId);
sysFileDeployment.setServerVersion(conpeek(classVersion, 2));
sysFileDeployment.run();
}
Otra de las desventajas es que parece que esta clase no funciona demasiado bien en AX 4. Yo las pruebas que acabo de hacer en 2009 SP1 sí han funcionado, salvo un pequeño problema con las rutas de origen/destino, supongo que por ejecutar todo el entorno en la misma máquina (cliente, aplicación, AOS, ..).
A partir de aquí que cada uno investigue lo que necesite.
HOWTO: AX – Expandir LMAT por código
AX nos deja expandir, o explotar, una línea de lista de materiales (L.MAT, o BOM en inglés) de manera que podamos manejar sus elementos al mismo nivel que el artículo L.MAT. La funcionalidad se llama Expandir L.MAT y la podemos ver, por ejemplo, en las líneas de un pedido:

Esta función nos presenta un formulario para elegir las líneas que queremos “Explotar” fuera de la lista de materiales para su tratamiento:

El código que este formulario ejecuta para realizar la expansión de la L.MAT se encuentra, en el caso de líneas de pedido de venta, en la tabla SalesLine) concretamente en el método: SalesLine.expandBOM(), por tanto para simular esta funcionalidad en X++, el mínimo código que podemos utilizar es el siguiente:
{
TmpFrmVirtual tmpFrmVirtual;
// Línea de la LMAT que se quiere expandir
BOM tmpBOM = BOM::findRecId(5637146401, false);
// Línea de venta desde la que expandir
SalesLine auxSalesLine = SalesLine::find('00000003_050', 3, true);
;
// Registro temporal que marca la línea a expandir
tmpFrmVirtual.TableNum = tablenum(BOM);
tmpFrmVirtual.RecordNo = tmpBOM.RecId;
tmpFrmVirtual.write();
// Expande la LMAT y genera la línea de venta nueva (función estándar)
auxSalesLine.expandBOM(tmpFrmVirtual, tmpBOM);
// la nueva línea de venta puede necesitar modificaciones despues de este proceso
}
y digo mímino porque la línea se insertará con la mayoría de sus opciones por defecto, por lo que es seguro que habra que añadir código extra para completar los datos necesarios según las necesidades.
HOWTO: AX – Modificar el menu contextual de un objeto
Al hacer “click derecho” sobre un objeto en Dynamics Axapta se ejecuta el metodo showContextMenu, el cual como siempre, podemos sobrecargar. Esta es la pinta que tiene inicialmente:
{
int ret;
ret = super(MenuHandle);
return ret;
}
Es como la mayoría de métodos sobrecargados. Vamos a sustituirlo por este otro código:
{
int ret;
PopupMenu menu;
int menuItem;
;
// Crea un nuevo menú utilizando el manejador recibido por parámetros
menu = PopupMenu::create(MenuHandle, this.hWnd());
// Añade un nuevo Menu Item al menú recién creado
menuItem = menu.insertItem("Mi nuevo menú");
// Abre el menú
ret = menu.draw();
// Comprueba el Menu Item que se ha pulsado
if (ret == menuItem)
{
// Si es el mío, pongo código aqui
Box::info('Pon tu código aquí!');
return 0;
}
// Si ha pulsado alguno que no es el mío, se lo devuelvo al estándar
return ret;
}
Como se puede ver en el ejemplo, podemos añadir por código un nuevo MenuItem al menu contextual y a la vez, capturar la pulsación del mismo para poder ejecutar nuestro propio código de respuesta.
HOWTO: AX – Ventana de progreso para procesos largos
Me gustaría empezar a colgar pequeños “trucos” o trozos de código útiles, por un lado con un fin divulgativo para que si a alguien le sirve, lo utilice; y por otro lado con un fin documental para hacer una pequeña biblioteca de código y poder consultarla en caso necesario. Sugerencias seran bien recibidas, como siempre. Como es el primero, empezare por uno muy sencillo:
Se trata de utilizar la clase SysOperationProgress para mostrar el diálogo estandar de progreso, que podemos (y debemos) utilizar en procesos que se puedan alargar mas de unos pocos segundos.
int i;
;
#AviFiles
p.setCaption("Título del diálogo");
p.setAnimation(#AviTransfer);
p.setTotal(100000);
for (i=1; i <= 100000; i++)
{
p.setText(strfmt("Progreso %1", i));
p.incCount();
}
Su utilización es bien sencilla y se puede complicar, por ejemplo para añadir varias barras de progreso en el mismo diálogo, jugando, por ejemplo, con los parámetros del constructor de la clase.