lunes, 2 de julio de 2018

DYN365 | Macros O Constantes

Vamos a explicar en contexto lo que es una macro y una constante en DYN365FO, como también porque usar una u otra.

Constante.

Una constante representa un valor que nunca cambia.En AX2012 no existía este tipo de variable que ahora en DYN365 ya cuenta con ello y estas constantes son campos cuyos valores se establecen en tiempo de compilación y no se pueden cambiar en tiempo de ejecución. 

Para definir una variable constante declaramos de esta manera:

class Constants
{
    public const double Pi = 3.14159;
    public const int SpeedOfLight = 600000;
}

Macros.

Son fragmentos de código los cuales no se puede ejecutar de forma independiente, pero se puede usar en cualquier lugar donde se pueda escribir código en el sistema, por ejemplo en Jobs, clases y formularios. En AX2012 como en DYN365 el nodo Macros contiene las macros utilizadas por la aplicación estándar. 




Además de ver el código existente, puede agregar sus propias macros.

Definición de macros

class Macros
{
  #define.MyMacro("Value");
}

Puede usar macros de la misma manera que lo hicimos en AX 2012. Sin embargo, en algunos casos es imprescindible esto para la reutilizar bloques de código. Por ejemplo

class SecurityDigitClass
{

    static void MyMacroInSelect()
    {
        Bom  bom;
        date emptyDate;
   
        // parameters: %1 = table instance, %2 date, %3 empty date value
        #localmacro.bomDateFilter
        && ( %2 == dateNull() || (
            ((%1.FromDate <= %2) && (%1.ToDate >= %2)) ||
            ((%1.FromDate == %3) && (%1.ToDate == %3)) ||
            ((%1.FromDate <= %2) && (%1.ToDate == %3)) ||
            ((%1.FromDate == %3) && (%1.ToDate >= %2))
            ))
    #endMacro
        ;
   
        while select bom
    where bom.ItemId == '123'
    #bomDateFilter(bom, systemDateGet(), emptyDate)
        {
            info(bom.bomid);
        }
    }

}

¿Macro o Constate?

En DYN365 X++ ahora el tipo de variable constante con la  palabra clave const es compatible, podemos y debemos usar CONS (Constantes) como vemos a continuación presentamos el uso de ambos.


 #define.macroValue("Macro value");

 const str constValue = "Constant value";
   info(strFmt("%1 - %2",#macroValue, constValue));



Para usar "const" de la misma manera que estábamos usando antes con un archivo Macro, debe reemplazarlo por una "clase estática". 

static class ConstClass()
 {
    static const public str constValue = "Constant value";
 }

Usando la clase constante

str constValue = constClass::ConsValue;



Microsoft recomienda usar "const" en lugar de "macro", y enumeran algunos beneficios:

  • IntelliSense en el editor.
  • Soporte para "Ir a definición" en el editor.
  • Control total del tipo de la constante.
  • Compilación más rápida: las macros son notoriamente difíciles de manejar para el compilador.



En resumen el uso de macros fue poderoso para también reutilizar bloques de codificación.
En la compilación, la parte macro se insertará en la codificación, lo que requiere tiempo de compilación adicional.Por esta razón, trate de evitar el uso de macros. 
Microsoft apunta a obtener compilaciones lo más rápido posible,por esta razón, podría ocurrir que las macros se deprecien en el futuro.

Además del rendimiento, las macros rompen muchas herramientas de desarrollo. Si usa macros para insertar bloques de código, ninguna de las herramientas de desarrollo puede ver ese código. Así que cosas como las herramientas de actualización de código, los complementos de Visual Studio y cualquier herramienta futura nunca podrán comprender las macros, ya que esas herramientas funcionan en contra del código, y las macros no se "resuelven" (reemplazan) hasta la compilación.

Por todos estos motivos ya descritos apostamos a que se utilice lo menos posibles macros y usemos ya constantes, y no arriesguemos a una penalizacion de rendimiento al usar macros.







lunes, 23 de abril de 2018

D365FO | Extensibilidad en tablas estándar (Eventos y Delegados)

Las extensiones de tabla proporcionan una forma de agregar campos a una tabla sin over-layering. Para agregar un campo a una tabla como una extensión, siga estos pasos:

- Localice la tabla SalesTable en el Explorador de Aplicaciones.
- Haz clic derecho y elige crear extensión.



- Recordando que todos los nombres de elementos deben ser globalmente únicos, cambie el nombre de la nueva extensión. Para nuestro caso SalesTable.HAExtension.
- Abra la extensión de la tabla y agregue el nuevo campo.



- Cree la clase de manejo de eventos. Para nuestro caso SalesTable_Extension.

 [ExtensionOf(tablestr(SalesTable))]  
 final class SalesTable_Extension  
 {  
   
 }  

- En el nodo Eventos de la tabla extendida SalesTable.HAExtension, haz clic derecho sobre el evento OnInserting; luego Copiar metodo de manejo de evento.


- Pegue el método en la clase de manejo de eventos creada, de la siguiente manera.

 [ExtensionOf(tablestr(SalesTable))]  
 final class SalesTable_Extension  
 {  
   /// <summary>  
   ///  
   /// </summary>  
   /// <param name="sender"></param>  
   /// <param name="e"></param>  
   [DataEventHandler(tableStr(SalesTable), DataEventType::Inserting)]  
   public static void SalesTable_onInserting(Common sender, DataEventArgs e)  
   {  
   
   }  
   
 }  

- Agregue dentro del evento el proceso que desee ejecutar, de la siguiente manera.

 [ExtensionOf(tablestr(SalesTable))]  
 final class SalesTable_Extension  
 {  
   /// <summary>  
   ///  
   /// </summary>  
   /// <param name="sender"></param>  
   /// <param name="e"></param>  
   [DataEventHandler(tableStr(SalesTable), DataEventType::Inserting)]  
   public static void SalesTable_onInserting(Common sender, DataEventArgs e)  
   {  
     SalesTable   salesTable = sender as SalesTable;  
   
     salesTable.HAName = salesTable.SalesName;  
     info(strFmt("Orden de venta %1 procesada",salesTable.SalesId));  
   }  
   
 }  

- Para nuestro ejemplo, el proceso se ejecutará cada vez que se cree una nueva Orden de venta.


- Adicionalmente, se almacenará el dato asignado en el nuevo campo.

martes, 13 de marzo de 2018

TIP | [X ++] Mejorar procesamiento de datos en Reportes SSRS

Cuando un reporte procesa una gran cantidad de datos y se considera que toma un tiempo considerable durante su ejecución, entonces se puede decidir incorporar preprocesamiento. Sigue estos pasos para habilitar el preprocesamiento en RDP y mejorar el procesamiento de datos en reportes:
Espero sea de su ayuda.


1. Extendemos la clase RDP de la clase SrsReportDataProviderPreProcess.


class HAVendPaymentDP extends SrsReportDataProviderPreProcess
{   
 

     //Tabla temporal utilizada por el RDP
     HAVendPaymentTmp   vendPaymentTmp;
}



2.  Establecemos las siguientes propiedades en la tabla temporal utilizada por el RDP.




3. Lo importante dentro del preprocesamiento en el RDP es insertar registros en las tablas que generen automáticamente un TransactionId al principio del proceso del reporte y establecer una conexión con el usuario.

El campo TransactionId permite que el reporte identifique los registros creados únicamente para la sesión del usuario.


public void processReport()
{
   
    vendPaymentTmp.setConnection(this.parmUserConnection());
   


    //super();
}


4. Adicionalmente para nuestro ejemplo en el ingreso de registros, hacemos uso de la clase RecordInsertList.

Esta clase permite insertar más de un registro en la base de datos a la vez, lo que reduce la comunicación entre la aplicación y la base de datos.


public void processReport()
{
   
    RecordInsertList    recordInsertList;

    vendPaymentTmp.setConnection(this.parmUserConnection());

    recordInsertList = new
RecordInsertList(tableNum(HAVendPaymentTmp),
                                            false,
                                            false,
                                            false,
                                            false,
                                            false,
                                            vendPaymentTmp);

    //Ingreso de registros en la tabla

    ttsbegin;
    vendPaymentTmp.clear();
    vendPaymentTmp.VendAccount = "VA00001";
     .
     .
     .
    recordInsertList.add(vendPaymentTmp);
    //

    recordInsertList.insertDatabase();
    ttsCommit;
    //super();



Los registros se insertan solo después de la llamada al método insertDatabase. 
RecordInsertList.insertDatabase() devuelve los registros acumulados por el método add().


5. Recordamos actualizar el DataSource del Reporte SSRS en Visual Studio, para refrescar el campo TransactionId.
   

viernes, 23 de febrero de 2018

X++ | USO DEL SysExtension Framework

USO DEL SysExtension Framework

El SysExtension Framework es incorporado en Dynamics AX para solventar el diseño de extensión de clases (jerarquías). En el siguiente ejemplo motraremos el uso de esta solución.
Espero sea de su ayuda.

1. Lo primero que necesitamos es un diseño de jerarquía de clases. De manera sencilla, para este ejemplo se crearán las clase HACustInvoice y HACustInvoice_Sales.


class HACustInvoice
{
}


public void run()
{
    info("clase HACustInvoice");
}


 
class HACustInvoice_Sales extends HACustInvoice
{
}


public void run()
{
    info("clase HACustInvoice_Sales");
    //super();
}


2. Ahora, creamos una clase que extiende de SysAttribute. Donde definiremos el atributo que en el contructor nos retorne la clase derivada.


 Esta clase almacena un valor recibido por parámetros y lo devuelve por un método parm.

class HACustInvoiceTypeAttribute extends SysAttribute
{
    str     classType;
}


public void new(str     _classType)
{
    super();

    if(_classType == "")
        throw error(error::missingParameter(this));

    classType = _classType;
}
 


public str parmClassType(str    _classType = classType)
{
    classType = _classType;

    return classType;
}


3. Editamos la classDeclaration de la clase (HACustInvoice_Sales)derivada para añadir este atributo.

[HACustInvoiceTypeAttribute("Sales")]
class HACustInvoice_Sales extends HACustInvoice
{
}


4. Por ultimo, editamos el constructor de la clase HACustInvoice con una nueva definición que utilice el atributo de la clase derivada.
 
public static HACustInvoice construct(str _classType)
{
    HACustInvoiceTypeAttribute     custInvoiceTypeAttribute;
    HACustInvoice                  custInvoice;

    custInvoiceTypeAttribute = new HACustInvoiceTypeAttribute(_classType);

    custInvoice = SysExtensionAppClassFactory::getClassFromSysAttribute(classStr(HACustInvoice),
                                                                        custInvoiceTypeAttribute);

    return custInvoice;
}


Segun éste diseño, no tendremos que modificar en absoluto nuevamente el constructor para añadir nuevas clases derivadas. Bastaría con indicar un nuevo atributo en la declaración de las nuevas clases derivadas.

5. Ejecutamos nuestro ejemplo de la siguiente manera:

static void JobHMAE(Args _args)
{
    HACustInvoice   custInvoice;

    custInvoice = HACustInvoice::construct("Sales");
    custInvoice.run();
}