Como muchos sabréis AX cuenta con unas funcionalidades específicas para exportar ficheros bancarios. La mayoría de estos ficheros se generaban mediante clases convencionales de AX como estas:

Las cuales se podían debuguear y modificar con relativa facilidad, pero con la entrada en vigor de los formatos SEPA, se implementaron dos nuevos tipos de ficheros para clientes y proveedores

Estos ficheros, presentan un funcionamiento, completamente distinto, ya que si bien es cierto que siguen manteniendo una parte de su lógica en AX (mediante clases convencionales) la creación del fichero XML propiamente dicha, se hace mediante el uso de un puerto de salida AIF que a su vez está configurado por un fichero de script, que es el que genera toda la estructura del fichero.
A continuación, vamos a describir de forma somera como funciona esto, de forma que podamos corregir errores y hacer pequeñas modificaciones en el fichero. Para ello deberemos seguir los siguientes pasos.

Acceso a la definición del fichero:

1. Acceso a los puestos de salida:

Tal y como comentábamos, la generación de los ficheros se hace mediante unos servicios de salida (outbounds port) los cuales podemos localizar en el siguiente menú:

Aquí encontraremos varios puertos entre ellos los relativos a los dos tipos de ficheros SEPA

2. Acceso y edición de los procesos de generación:

El formato del fichero viene definido en los proceso de salida, al cual podemos acceder a través del botón “Procesos de salida”. Una vez pulsamos el botón accedemos al proceso de transformación que debería llamarse AifXMLTransform (aunque dicho nombre puede variar)

3. Acceso a la selección de definición del fichero de salida:

Una vez llegado aquí, podremos acceder a los diferentes esquemas que definen el fichero a generar. Para ello pulsaremos el botón “Configurar” lo cual nos permitirá acceder al propio fichero XSLT que es por así decirlo “el código fuente” de este fichero

Esto es especialmente interesante, ya que esto implica que podemos tener varios esquemas almacenados configurando el que nos interese en un momento dado, así como tenemos la posibilidad de guardar distintas versiones, como si versiones del código se trataran.

4. Consulta y modificación del esquema:

Si seleccionamos el esquema y usamos la funcionalidad “Ver registro” accederemos al formulario de esquemas y desde aquí podremos crear, importar y editar esquemas.

Si pulsamos el botón “Ver” podremos acceder al contenido del esquema con todas las reglas que se usan para construir el fichero

Obviamente, desde aquí no podremos editarlo, pero tendremos la posibilidad de exportarlo, modificarlo en el bloc de notas o cualquier otro editor, e importarlo de nuevo sobrescribiendo el esquema antiguo o creando otro nuevo

Edición del fichero:

Hasta ahora hemos visto como acceder a la definición del fichero, exportarla e importarla, pero como podemos modificarlo. Vamos a dejar las modificaciones complejas y explicar cómo interpretar el fichero, comprobar errores y hacer modificaciones sencillas, para ello, primero es necesario conocer como está estructurado el fichero.

En primer lugar, el fichero funciona como cualquier otro programa mediante manipulación de variables, métodos del propio fichero y consulta sobre los datos de AX:

Variables: en el propio fichero se declaran ciertas variables que luego pueden ser utilizadas en diferentes partes del fichero por ejemplo

Estas variables pueden ser manipuladas en cualquier parte, mediante sentencias condicionales o mediante consultas de datos.

Métodos: En la parte final del fichero, tenemos los métodos del fichero, al igual que en AX, se trata de pequeñas subrutinas que pueden ser invocadas a lo largo de todo el fichero, recibiendo y devolviendo parámetros

Estos métodos, pueden ser modificados y tiene su propia gestión de errores, por lo que puede ser que excepciones que nos devuelva AX vengan de errores en estos métodos. Como aquí no contamos con un compilador, solo veremos los errores en tiempo de ejecución, así que debemos modificarlos con prudencia.

Acceso a datos: Como es lógico, el fichero deberá construirse a partir de datos de AX, estos datos se obtienen mediante sentencias como esta

En esta fragmento, estaríamos declarando la variable ”FileName” y asignándole un valor, para ello el fichero se alimenta de la query “VendPayments”, concretamente este campo (En la instrucción se van indicando los sucesivos niveles de la query y por último el campo)

Por tanto podríamos añadir o modificar un valor del fichero, añadiéndolo sencillamente a esta query e implementando el código necesario para leerlo y volcarlo en una variable.

Como conclusión con esta serie de pistas, deberíamos ser capaces de hacer modificaciones sencillas en el fichero SEPA sin necesidad de tocar el código de AX, para modificaciones más complejas deberíamos editar más a fondo el fichero de script cosa que podría explicarse en otro artículo.

A veces puede surgir la necesidad hacer que una parte del código se ejecute con un usuario distinto del actual. Se puede dar el caso de que sea necesario impersonar ciertas de acciones de AX, por ejemplo, al recibir informacion desde una aplicación externa (mediante un fichero de texto plano o un web service), también puede ser necesario chequear los permisos de un usuario distinto del actual para acceder a una determinada funcionalidad de AX.

La función RunAs, es una funcionalidad que puede ser de una gran utilidad ya que nos permite impersonar, y ejecutar ciertas funcionalidades con un usuario distinto del actual, esto puede ser muy útil dentro del ámbito de testeo de seguridad, asi como para ejecutar funcionalidades de AX desde aplicaciones externas, no obstante hay que tener cuidado con su uso ya que puede contribuir a vulnerar la seguridad del sistema. La funcionalidad es bastante sencilla de utilizar si se tiene en cuenta los siguientes puntos:

  • Solo pueden invocarse métodos estáticos
  • El método solo podrá enviar y recibir parámetros por medio de contenedores, si no se utilizan correctamente dara un error bastante genérico (y frustrante) diciendo que se ha llamado al método con un parámetro incorrecto
  • Se debe tener mucho cuidado cuando se use dentro de una transacción (ttsbegin, ttscommit) ya que el cambio de usuario a media transacción tendrá consecuencias indeseadas, como que todos los cambios realizados dentro de la transacción previos al RunAs no se vean (cosa logica, dado que hemos cambiado de usuario a todos los efectos)

En el ejemplo que adjunto, se usaba la funcionalidad de AX «RunAs» dentro del marco de una inserción automatizada de datos, para chequear si el usuario que genero dichos datos (y que no es el actual) en la aplicación externa, tenia permisos para confirmar el pedido, también podría usarse para que los campos de auditoria (createdBy, ModifiedBy) se rellenen con el usuario que dio de alta los datos en la aplicación externa.

En primer lugar el método CheckPermission nos indica como ejecutar un método estático mediante la función RunAS, el método RunAs siempre envía y devuelve  parámetros al método invocado mediante un contenedor

Boolean CheckPermissions(string       _extUser)

{

RunAsPermission         perm;

UserId                  runAsUser;

SysUserInfo             userInfo;

Container               Parms;

Boolean                 ret;

;

userInfo = SysUserInfo::find(_extUser);

runasuser = userInfo.Id ? userInfo.Id : curuserid();  //En este caso, si el usuario no existe se usara el actual

perm = new RunAsPermission(runAsUser);

perm.assert();

Parms = runas(runAsUser,classnum(CHAESWSCreateSalesOrder),»CheckPost»);

CodeAccessPermission::revertAssert();

return ret;

}

El siguiente método (que se ha invocado mediante la funcion RunAs), chequea si el usuario actual tiene permisos para acceder a la opción de confirmación de pedidos, este chequeo se hará con el usuario incado en la llamada RunAs

server static container CheckPost(container     _parms)

{

Boolean                 ret;

MenuFunction            menufunction;

container               retval;

;

//Si el usuario tiene permiso para confirmar

menufunction = new  menufunction(menuitemactionstr(SalesFormLetter_confirmation),menuitemtype::Action);

if (menufunction.checkAccessRights())

ret = true;

retval = conpoke(retval,1,ret);

return retval;

}

Como se puede observar, se ha adaptado el metodo explicitamente para funcionar con RunAs, ya que el booleano que normalmente devolveria, se ha introducido dentro de un contenedor,  en caso contrario el RunAs no sabría como tratarlo.

En resumen, la funcionalidad RunAs es una utilidad muy potente que debe usarse con precaución, preferentemente para la invocación de métodos pequeños y/o simples. puede provocar un funcionamiento defectuoso en el ámbito de las transacciones. Por lo demás, yo lo he usado para realizar chequeos de seguridad, pero puede tener muchas otras utilidades.

Dentro del ámbito de AX 2009 existe una funcionalidad que permite el marcado de transacciones de inventario de un pedido de venta. Mediante esta función, se permite seleccionar que transacción de compra se utilizara para cubrir la salida de inventario de un pedido de venta. Esta función se realiza de forma manual desde el formulario de pedidos de venta, en la parte de líneas botón Stock->Marking, esta función muestra un formulario que las transacciones cuyas dimensiones y articulo coinciden con la línea de venta seleccionada, y permite marcar una determinada cantidad (si existe inventario de la misma).

Ocasionalmente me ha sido necesario automatizar este proceso, de forma que se puedan marcar pedidos completos sin necesidad de ejecutar el marcado línea a línea, o para realizar algún proceso masivo de reparación de pedidos. Para este fin he usado un código como el que posteo a continuación el cual me ha salvado en varias ocasiones

 

static void MNFMarkInventoryTransactions(Args _args)
{

InventTrans         fIssueTrans;

TmpInventTransMark  fTitm;

Map                 mapMarkNow;

container           con;

real                qty;

Map                 mapTmp;

MapIterator         it;

SalesLine           salesline;

;

While select salesline

Where salesline.SalesId == «XXXXXXXXX»

{

//Transaccion de origen (Venta)

fIssueTrans = InventTrans::findTransId(salesline.InventTransId);

//obtencion de transacciones susceptibles de ser marcadas

[con,qty]   = TmpInventTransMark::packTmpMark(fIssueTrans.ItemId,

fIssueTrans.inventDim(), fIssueTrans.InventTransId, fIssueTrans.Qty);

mapTmp = Map::create(con);

it = new MapIterator(mapTmp);

while ( it.more() )

{

fTitm = mapTmp.lookup(it.key());

//Aqui se pueden realizar cualquier clase de filtro para marcar la transaccion deseada

if (   )

{

fTitm.QtyMarkNow = salesline.SalesQty;

fTitm.QtyRemain -= fTitm.QtyMarkNow;

mapMarkNow = new Map(typeId2Type(typeid(recId)), Types::Record);

mapMarkNow.insert(fTitm.RecId,fTitm);

TmpInventTransMark::updateTmpMark(fIssueTrans.InventTransId,fIssueTrans.inventDim(),-5, mapMarkNow.pack());

break;

}

it.next();

}

}

}

 

Este job recorre las líneas del pedido seleccionado, y para cada una de ellas selecciona las transacciones de compra susceptibles de marcado permitiendo el marcado de cualquiera de ellas en base al filtro deseado (se puede optar por marcar directamente la primera de ellas). En mi caso particular, se uso para la reparación de pedidos de entrega directa cancelados erróneamente, aunque puede tener muchas otras utilidades.

Como muchos otros desarrolladores, constantemente me he estado apoyando en el trabajo y las aportaciones de otros profesionales cuando me he visto en algún problema especialmente peliagudo o he tenido que hacer algo nuevo. A menudo he tenido que recurrir a «San Google» cuando me he atascado en algún desarrollo y he sentido deseos de abrazar al autor cuando he encontrado la solución en uno u otro blog ( a menudo tan solo unas pocas líneas de código que no lo hubiera encontrado por mi  mismo ni en una semana)

Obtenemos ayuda gracias la acción desinteresada de esos profesionales, a menudo de otras nacionalidades, y frecuentemente sin posibilidad de agradecérselo, ya sea porque el post es demasiado viejo y este a todas luces abandonado, ya sea por falta de tiempo. En agradecimiento a todas estas personas, he querido crear este pequeño blog, en el que compartir aquellas funcionalidades y pequeños trucos que me han sacado de apuros o que considero especialmente útiles sobre todo en el ámbito de Microsoft Dynamics Ax. No pretendo ser un gurú,  pero si algo de lo que aporte pueden ser de utilidad a otros programadores, me daré por satisfecho. Espero que os sea útil.