Asignar campo con valores predeterminados

El título es entendible para cumplir con el objetivo. Ahora bien, no es tan simple desde el punto de vista del proceso para lograr dicho objetivo.
 

Básicamente, para lograrlo debemos agregar el campo necesario en el método initValue() de la tabla en cuestión. Debe ser el método de tabla para que se replique en todos los objetos que utilizan dicho campo.

En este ejemplo, modificaremos el campo InclTax de tipo NoYes a marcarlo siempre (True por defecto). En la tabla VendTable, agregamos la siguiente línea de código dentro del método initValue():

void initValue()
{
super();

this.Party = 0; // Reset PartyId, it might be set when templates are used

//ax-ca. 20170629. Por defecto, marcamos la opción de incluir impuesto
this.InclTax = NoYes::Yes;
}

De esta manera hemos logrado el objetivo de marcar el campo de manera predeterminada al insertar un nuevo proveedor dentro del sistema.

[AX 2012 R3 – How To] Agregar elementos a un contenedor

En Microsoft Dynamics AX un contenedor (container) se refiere a un vector (array). A diferencia de otros lenguajes, como C# por ejemplo, el vector acepta múltiple tipos de datos dentro de él, al mismo tiempo.

El siguiente código demuestra cómo agregar los elementos a un contenedor:


int numero = 1;
str cadena = "hola";
long largo = 15684965;
container contenedor;

contenedor += numero;
contenedor += cadena;
contenedor += largo;

// Imprimimos "hola"
info(strfmt("Vamos a saludar: %1", conPeek(contenedor, 2)));

De esta manera los elementos fueron agregados al vector (contenedor).

[AX 2012 R3 – How To] Obtener dimensión desde cuenta de proveedor

Cuando trabajamos con diario de facturas, en ocasiones, es necesario obtener la dimensión para la cuenta del proveedor y asignar el número de cuenta (AccountNum) como dimensión. Es por ello, que la siguiente línea de código puede ayudarnos a realizar esa tarea:


DimensionDynamicAccount cuentaDinamica;
RecId recLedgerDimension, codProv = 50123657;
AccountNum numeroCuenta;

// Obtenemos el número de cuenta del proveedor
accountNum = VendTable::findRecId(codProv).AccountNum;

// Obtenemos la cuenta del proveedor. Si no existe el AX lo crea. Es decir, siempre retorna un valor
recLedgerDimension = DimensionStorage::getDynamicAccount(accountNum, LedgerJournalACType::Vend);

// recLedgerDimension posee el código de la dimensión que necesitamos agregar.

Con esto logramos obtener la dimensión para el número de cuenta del proveedor.

[AX 2012 R3 – How To] Crear dimensiones desde un container

En ocasiones no tenemos definidas las dimensiones en AX de manera directa, pero lo obtenemos de otras fuentes externas. Entonces, debemos crear un contenedor donde adicionamos los valores necesarios para convertirlos en una dimensión (generalmente hablamos de LedgerDimension).

Para lograr nuestro objetivo debemos escribir las siguientes líneas de código:


container offsetDim;
RecId recIdResult;

// Primero el valor a mostrar (Display Value).
// Luego la cuenta principal (Main account, generalmente el mismo que Display Value)
// Cantidad de dimensiones (número entero)
// Dimensiones separadas por comas: Nombre, Valor.
offsetDim = ["1.2.01.01.0.0.01.001", "1.2.01.01.0.0.01.001", 2, "AGENCIA", "SUCURSAL1", "CENTRO_DE_COSTO", "101"];

//Básicamente en AX se mostraría "1.2.01.01.0.0.01.001-SUCURSAL1-101".

recIdResult = AxdDimensionUtil::getLedgerAccountId(offsetDim);

info(int642str(recIdResult));

Con esto logramos crear u obtener las dimensiones a partir de un contenedor.

[AX 2012 R3 – How To] Convertir un tipo genérico a un tipo específico

Muchas veces tenemos la necesidad de utilizar una variable genérica al momento de colocarlo como parámetro de manera que al utilizarlo podamos convertirlo al tipo de dato necesario para la ejecución de la aplicación/rutina con la que estamos trabajando.

En esta entrada voy a poner como ejemplo el uso de tablas para poder realizar una determinada acción.

En este caso, vamos a recibir como parámetro el tipo de dato Common y dentro del método vamos a convertir en la tabla CustTable o VendTable según sea necesario.


private void probarFuncion(Common tabla)
{
VendTable vendTable;
CustTable custTable;

switch(tabla.TableId)
{
case tableNum(CustTable):
custTable = tabla as CustTable;
info(custTable.name());
break;
case tableNum(VendTable):
vendTable = tabla as VendTable;
info(vendTable.name());
break;
}
}

[AX 2012 R3 – How To] Crear un diario de factura con X++

En esta entrada mostraré cómo crear un diario de factura con X++, no es muy complicado pero debemos tener en cuenta una cierta cantidad de puntos para poder hacerlo de manera correcta. El código incluye comentarios.

Creación del diario de facturas desde X++


public boolean createVendorInvoiceService()
{
int i;
LedgerJournalCheckPost jourCheckPost;
LedgerJournalTable jourTable;

AxLedgerJournalTable header = new AxLedgerJournalTable();
AxLedgerJournalTrans trans;

container offsetDim, offsetDimAccount;

// Esta es la entrada contra la cuenta del proveedor.
DimensionAttributeValueCombination davc;

//Tabla con personalizaciones de campos
ServiceParameters parametros;
LedgerJournalNameId ledgerJournalNameId;

//Generar números aleatorios
RandomGenerate randomGenerate = RandomGenerate::construct();
int monto = randomGenerate.randomInt(1000, 32000);

//Asignación general de variables
parametros = ServiceParameters::find(); //Parametros generales personalizados
ledgerJournalNameId = parametros.InvoiceJournalName; //Nombre del diario para Cuentas por Pagar

try
{
ttsBegin;
//Creación de cabecera para el diario de facturas del proveedor
header.parmJournalName(ledgerJournalNameId);
header.parmJournalType(LedgerJournalName::find(ledgerJournalNameId).JournalType); //asignamos el tipo de Diario desde los nombres de diarios
header.parmApprover(HcmWorker::findByPersonnelNumber("000001").Person); //El nombre de la persona autorizada.
header.save();

//Creación las líneas. Tres líneas para este caso
//1. Cuenta del proveedor con el crédito total
//2. Débito del pago.
//3. Débito del pago.
for (i=1; i<=3; i++)
{
trans = new AxLedgerJournalTrans();
trans.parmLineNum(i);
trans.parmJournalNum(header.ledgerJournalTable().JournalNum);
trans.parmTransDate(systemDateGet());//Le pasamos la fecha de hoy
trans.parmTxt(strFmt("Descripción Número %1",i)); //Texto identificatorio

trans.parmOffsetAccountType(LedgerJournalACType::Ledger);
trans.parmCurrencyCode("USD"); //Dólares

//Primeramente, procedemos a crear la línea que corresponde al proveedor
if (i == 1)
{
// Nota: Esto funciona únicamente si el registro DimensionValueAttributeCombination
// existe.
select firstonly RecId from davc
where davc.DisplayValue == "000000104"; //Este número pertenece a la cuenta del proveedor.

trans.parmLedgerDimension(davc.RecId);
trans.parmAccountType(LedgerJournalACType::Vend); //Proveedor
trans.parmInvoice("FC-001-001-123456"); //Número de factura
trans.parmAmountCurCredit(monto); //Esto se calculó desde Random.

//Primero el Display value, luego Main account, Cantidad de dimensiones, Dimensión: Nombre, Valor.
offsetDim = ["1.1.01.03.0.0.01.001", "1.1.01.03.0.0.01.001", 2, "AGENCIA", "ADMINISTRACION", "CENTRODECOSTO", "200"];
trans.parmOffsetLedgerDimension(AxdDimensionUtil::getLedgerAccountId(offsetDim));

trans.parmTaxGroup("CONTC"); //Contado para compras
trans.parmTaxItemGroup("C-IVADIR10"); //Tipo Impuesto. IVA 10% en Paraguay
}
else
{
trans.parmAccountType(LedgerJournalACType::Ledger);
trans.parmAmountCurDebit(monto / 2); //El monto Random divido 2.

offsetDimAccount = ["1.1.01.03.0.0.01.001", "1.1.01.03.0.0.01.001", 2, "AGENCIA", "ADMINISTRACION", "CENTRODECOSTO", "200"];
trans.parmLedgerDimension(AxdDimensionUtil::getLedgerAccountId(offsetDimAccount));

trans.parmTaxGroup("CONTC"); //Contado para compras
trans.parmTaxItemGroup("C-IVADIR10"); //Tipo Impuesto. IVA 10% para Paraguay.
}

trans.parmApprover(HcmWorker::findByPersonnelNumber("000001").Person); //Persona que aprueba los pagos
trans.parmApproved(NoYes::Yes);

trans.save();
}

jourTable = header.ledgerJournalTable();
ttsCommit;

//Verificamos si está marcada la opción de Registro automático (esta es una personalización).
//Si el parámetro es verdadero, procedemos a Registrar el diario.
jourCheckPost = LedgerJournalCheckPost::newLedgerJournalTable(jourTable ,parametros.IsAutomaticPostingGL);

jourCheckPost.run();

return true;
}
catch
{
ttsAbort;
}

return false;
}

[AX 2012 R3 – How To] Utilizar el objeto RecordInsertList

La clase RecordInsertList consiste en una lista de elementos (objetos previamente tratados) el cual permite insertar valores de una lista directamente a la base de datos, realizando esta tarea en una sola llamada sin realizar varios viajes.

Generalmente, vamos realizando algún cambio en un registro y ya lo actualizamos en la base de datos. Si son pocos registros, no hay drama. Pero si hablamos de miles de registros la performance se ve afectada. Donde más útil resulta esta clase es en los reportes.

Forma normal


void probarMetodo(RecId _recId, str _algunParametro)
{
TableTemp tabla, nuevaTabla;

while select tabla
where tabla.RecId == _recId
{
nuevaTabla.data(tabla);
nuevaTabla.CampoX = _algunParametro;
//Llama N veces al metodo insertar. Por cada registro inserta en base de datos.
nuevaTabla.insert();
}
}

Utilizando la clase


void probarMetodo(RecId _recId, str _algunParametro)
{
RecordInsertList tempLista;
TableTemp tabla, nuevaTabla;

tempLista = new RecordInsertList(tableNum(TableTemp));

while select tabla
where tabla.RecId == _recId
{
nuevaTabla.data(tabla);
nuevaTabla.CampoX = _algunParametro;
//Agrega el nuevo registro a la lista.
tempLista.add(nuevaTabla);
}
//Inserta toda la lista en la base de datos. Bulk insert.
tempLista.insertDatabase();
}

[AX 2012 R3 – How To] Administrar excepciones desde X++

En AX, las excepciones funcionan de igual manera que en cualquier lenguaje de programación. Lo que deseo demostrar aquí es un bloque de excepciones completo desde X++ y otro que también pueda administrar errores que se producen en una librería DLL.

Excepciones en X++

En este caso, el bloque de excepciones se presenta de la siguiente manera:


try
{
//Esto producirá un error de división por cero
resultado = num1 / 0;
}
catch (Exception::Numeric)
{
//Error específico que se captura cuando trabajamos con números
error("Error específico");
}
catch
{
//Si el error no pudo ser atrapado por la excepción anterior, se procesa éste
error("Error general.");
}

Excepciones para .NET

En este caso, el bloque de excepciones atrapa errores de librerías .NET:


System.Exception ex;

try
{
//Se llama a la librería .NET, si da error pasa al catch
objetoNET.metodo();
}
catch (Exception::CLRError)
{
ex= CLRInterop::getLastException();
info(ex.ToString());
}

[AX 2012 R3 – How To] Index versus Index Hint

Debido a este punto he tenido algunas discusiones con mis colegas debido a las diferencias que existen entre estas cláusulas.

Index Hint

Básicamente, Index Hint en AX 2012 ya está en desuso, para describirlo mejor el AX 2012 lo ignora (es decir, no existe para el).

Un ejemplo de Index Hint podrías ser:


select * from InventTable index hint GroupItemIdx;

Para el AX 2012, la consulta la lee de la siguiente manera:


select * from InventTable;

Como vimos el Index Hint ya no se utiliza en AX 2012.

Index

Esta clásula indica que la consulta en cuestión ordenará, por dicho campo, los registros obtenidos


select * from InventTable index GroupItemIdx;

Para el AX 2012, la consulta la lee de la siguiente manera:


select * from InventTable group by ItemGroupId, ItemId;

[AX 2012 R3 – How To] Implementar todos los reportes SSRS

Muchas veces queremos implementar todos los reportes de una vez. Se complica un poco, si no tenemos la herramienta correcta para realizar dicha tarea.

Básicamente se realiza la tarea ejecutando una línea de comandos desde el powershell de Microsoft Dynamics AX.

A modo de introducción podemos decir que existen unas cuantas formas de implementar un reporte en el servidor de informes (SSRS – SQL Server Reporting Services). Aquí trataremos los 3 más utilizados y necesarios.

Visual Studio

Esta es la opción que más utilizo por su facilidad y rapidez. De hecho los reportes se diseñan/desarrollan desde el Visual Studio.
Abrir proyecto de reporte > Clic derecho sobre el proyecto > Implementar (Deploy). 1

Una vez realizada la acción anterior recibiremos el mensaje de buen funcionamiento o el de error si lo hay.
2

AOT

Esta segunda opción permite implementar el reporte desde el AX. Un buen escenario sería al importar un proyecto que ha definido cambios en los reportes, entonces éstos deben ser implementados para que puedan ser reflejados.
AOT > SSRS Reports > Reports > Clic derecho sobre el reporte > Implementar elemento.
3

 Una vez realizada la acción anterior recibiremos el mensaje de buen funcionamiento o el de error si lo hay.
4

Microsoft Dynamics AX Powershell

Desde aquí podemos implementar un sólo reporte o implementar todos los reportes. Para ello debemos ingresar al powershell de Microsoft Dynamics AX. Nos dirigimos a Inicio > Herramientas administrativas > Microsoft Dynamics AX 2012 Management Shell. Allí veremos el powershell de manera similar a la siguiente:
5

Implementación de un reporte específico

Aquí ejecutamos la acción de implementar un reporte (VendAdvanceInvoice, por ejemplo) desde el powershell:

Publish-AXReport -ReportName VendAdvanceInvoice

Implementación de un grupo de reportes

Si deseamos implementar un grupo de reportes (para compras, por ejemplo):

Publish-AXReport -ReportName Purch*

Implementación de todos reportes

Si deseamos implementar todos los reportes existentes dentro del AX, utilizamos el comodín *:

Publish-AXReport -ReportName *

En cualquiera de los tres casos, el resultado será similar al siguiente: 6