Códigos de etiquetas de Microsoft Dynamics AX 2012 usando ramas de Team Foundation Server (ALM-VIII)

Felizmente para nosotros, la integración de etiquetas con TFS en Microsoft Dynamics AX 2012 ha mejorado muchísimo hasta hacerla casi transparente, lo que es una mejora notable respecto a versiones anteriores. Cuando creamos una etiqueta en una instancia de AX gestionada por TFS, se crea un código de etiqueta personal que es único sólo en esa instancia (en la forma @$XXX).

Lo único a tener en cuenta es que en el momento de hacer check-in debemos incluir tanto el objeto donde se utiliza la etiqueta temporal, como el propio fichero de etiquetas donde este código ha sido creado. En este momento, la integración con TFS detecta el código temporal y lo sustituye por un código definitivo coherente con la versión del fichero de etiquetas en el servidor.

Continue Reading…

Manejar Enumerados de Dynamics AX en SQL Server (SSIS, SSRS, SSAS, …)

Manejar los valores de campos de tipo BaseEnum de Microsoft Dynamics AX en la base de datos (ya sea en T-SQL, en SSRS, SSIS, SSAS, …) puede ser complicado si se intenta hacer manualmente. El valor de los enumerados se guarda en la base de datos como el número del elemento pero cuando hay que presentar ese valor al usuario (en un informe, un cubo, etc…) hay que mostrar la descripción, el Label del campo, no el número que hay en la tabla, pero este valor no está disponible en AX de manera inmediata así que ¿Cómo hacerlo?

Afortunadamente, Microsoft ha solucionado ese problema para su propia integración con informes de Reporting Services y esa solución es fácilmente adaptable a nuestros requerimientos:

Durante la instalación de las Extensiones de Bussines Intelligence de Microsoft Dynamisc AX se actualiza automáticamente, dependiendo de la configuración elegida, la tabla del sistema SRSANALYSISENUMS con los valores y descripciones, por idioma, de todos los enumerados utilizados en todas las tablas que pertenecen a una Perspectiva.

Si a posteriori se quiere relanzar esta configuración para añadir nuevas tablas o nuevos idiomas, es posible hacerlo utilizando el Asistente para proyecto de SQL Server Analysis Services y seleccionando la opción Configurar:

Suponiendo que esta configuración ya está realizada, y suponiendo que lo que queremos es “traducir” las cadenas de texto en el idioma que corresponda a su valor numérico desde SQL Server Integration Services (el procedimiento se puede aplicar a otras áreas como SSAS o vistas de SQL por ejemplo) he optado por crear un procedimiento almacenado que realice la consulta de una manera reutilizable:

Continue Reading…

AX TIP: Mostrar label con saltos de línea mediante X++

Una característica bastante molesta que te encuentras programando para Microsoft Dynamics AX es la manera en la que se tratan los saltos de líneas al presentar texto en formularios utilizando etiquetas. Por ejemplo, la siguiente línea:

Box::yesNo("Línea 1 \n Línea 2", DialogButton::Yes, "Prueba multi línea!");

Presenta un diálogo normal con un texto en varias líneas (hace caso del carácter \n para poner un salto de línea):

strFmtLB 1 | Bien

Sin embargo al convertir el mismo texto en una etiqueta, el resultado es distinto:

// @TST001: Línea 1 \n Línea 2
Box::yesNo("@TST001", DialogButton::Yes, "Prueba multi línea!");

strFmtLB 2 | Mal

Y ocurre lo mismo si se utiliza una etiqueta con comodines (%1, %2, …) y la función strFmt de esta manera:

// @TST001: Línea 1 \n Línea 2
Box::yesNo(strFmt("@TST001"), DialogButton::Yes, "Prueba multi línea!");

Para que la etiqueta haga caso del salto de línea hay que utilizar la función strFmtLB:

// @TST001: Línea 1 \n Línea 2
Box::yesNo(strFmtLB("@TST001"), DialogButton::Yes, "Prueba multi línea!");

Y así si funcionará:

strFmtLB 3 | Bien

Pero esta función no sustituye a la anterior. Esto es, en caso de tener comodines y saltos de página habrá que incluir las dos funciones. Esto es bastante molesto cuando modificas una etiqueta y pones un salto de línea. Da la impresión de haberse roto algo que antes sí funcionaba, y es que esta función no esta incluida nunca en el código si no es necesaria de forma explícita.

Traducción automática de etiquetas con BING

El pasado viernes Arijit Basu publicó en su blog un experimento, mediante el cual explicaba brevemente como utilizar el servicio web publicado por Microsoft de su producto BING para traducir cadenas de texto desde AX.

Este artículo me llevo a una idea propia de como sacarle utilidad a esa funcionalidad. Cuando uno trabaja en un entorno con varios idiomas, puede ser útil que al generar una etiqueta en nuestro idioma, se generen automáticamente el resto de idiomas ya traducidos.

Yo no he llegado tan lejos (todavía) pero he hecho una pequeña aproximación que paso a compartir, este es el resultado:

InfoLog Result

despues de ejecutar el Job:

/*
   JAEE - www.jaestevan.com

   @created 10/10/2009 tested in AX2009 SP1
*/

static void MicrosoftTranslatorTest(Args _args)
{
   MicrosoftTranslator msTrans = new MicrosoftTranslator();
   ;
   msTrans.createLabel("Ahora axapta puede traducir etiquetas! (jaestevan.com)", "es");
}

Desde aquí (AX class is documented in english too) se puede descargar un fichero XPO con la clase que paso a comentar. En la construcción de la clase indicamos nuestro código de BING (el cual se solicita en su web de forma gratuíta e instantanea) así como una lista de los idiomas que queremos traducir, indicando el código de idioma de AX así como el código del idioma en BING (usualmente el mismo). Por ultimo y de manera opcional podemos indicar el fichero de etiquetas donde se generarán, aunque por defecto utilizaremos el módulo por defecto por lo que este parámetro es opcional. Este es el método en cuestión:

void new(LabelModuleId _modulo = SysLabel::defaultModuleId())
{
   // Axapta Label Module Id
   moduleId = _modulo;

   // Microsoft BING AppID - http://www.bing.com/developers
   appId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

   // List of languages to translate (AX lang, BING lang) (Idiomas a traducir)
   transLang = new Map(Types::String, Types::String);
   transLang.insert('de','de');
   transLang.insert('ru','ru');
   transLang.insert('en-us','en-us');
   transLang.insert('fr','fr');
}

Esta es la manera de configurar la clase para su uso (si bien está construida para ser un ejemplo, no para servir de cualquier utilidad real).

El kit de la cuestión lo encontramos en el siguiente método, el cual genera la etiqueta (si no existe) en el idioma predeterminado y luego busca la traducción en BING para todos los idiomas configurados en la clase:

public void createLabel(str _txt, str _txtLang)
{
   MapEnumerator       langEnum;
   Label               l;
   str                 res;

   LabelId             labelId;
   SysLabelEdit        labelEd     = new SysLabelEdit();
   SysLabel            sysLabel    = new SysLabel('es');
   ;

   // Find or create the label (busca la etiqueta y si no existe la crea)
   labelId = labelEd.findLabel(_txtLang, _txt);
   if (!labelId)
   {
       sysLabel.insert(_txt, _txt, this.parmModuleId());
       labelId = labelEd.findLabel(_txtLang, _txt);
   }

   //Log
   InfoLog.add(Exception::Info, strfmt("Traduciendo: (%1) %2", _txtLang, _txt));

   // Update the label for each language (actualiza la etiqueta para cada idioma)
   langEnum = new MapEnumerator(this.parmLangs());
   while (langEnum.moveNext())
   {
       // Call to translate (función de traducir)
       res = this.strTranslate(_txt, _txtLang, langEnum.currentValue());

       // Modify label with new translated text (modifica la etiqueta con el texto traducido)
       labelEd.labelModify(langEnum.currentValue(), labelId, res, _txt, SysLabelApplModule::None, true);

       // Log
       InfoLog.add(Exception::Info, strfmt("(%1) %2", langEnum.currentKey(), res));
   }

}

El trabajo de integrar esta funcionalidad con el editor de etiquetas estándar queda pendiente para otra entrega.

Enlaces

Descarga