miércoles, 24 de julio de 2019

D365FO | Cómo personalizar el diseño de un reporte SSRS

En nuestro ejemplo procederemos con el desarrollo y la personalización de un reporte SSRS en D365FO. El objetivo aquí es agregar un campo de extensión al diseño de un reporte estándar sin over-layering. Utilizaremos el reporte de Confirmación de pedido de venta para agregar el nombre del grupo de venta.

Cree un nuevo proyecto en un nuevo modelo, el mismo que podrá ser utilizado como un nuevo paquete de extensión. Es una buena práctica tener un nuevo modelo para la personalización de reportes. Ya que al usar el concepto de extensiones, este nos permite agregar funcionalidad a los elementos de modelos existentes.


Los reportes, como el de Confirmación de pedido de venta, utilizan tablas temporales para facilitar la escritura del diseño. Por lo que crearemos una extensión de dicha tabla.
- Localice la tabla SalesConfirmHeaderTmp en el Explorador de aplicaciones.
- Haga clic derecho en la tabla y elija Crear extensión.



- Cambie el nombre de la extensión. En nuestro caso SalesConfirmHeaderTmp.HAExtension.
- Abra la tabla en el diseñador y agregue el nuevo campo.


  
- Localice el reporte SalesConfirm en el Explorador de aplicaciones.
- Haga clic derecho en el reporte y elija Duplicar en proyecto.



- Cambie el nombre del reporte. En nuestro caso HASalesConfirm.
- Abra el reporte en el diseñador y expanda el nodo Datasets y SalesConformHeaderDS. Luego, busque el nuevo campo de extensión en el nodo Campos.



- Si este no aparece, haga clic derecho en el Dataset y seleccione Restaurar.



- Ahora puede proceder a diseñar el reporte según sus requisitos.



- Cree una nueva clase de extensión que amplíe la clase de control de reportes estándar.


 class HASalesConfirmControllerExt extends SalesConfirmController  
 {  
   public static HASalesConfirmControllerExt construct()  
   {  
     return new HASalesConfirmControllerExt();  
   }  
 }  

- Agregue el método main, que haga referencia al diseño del reporte personalizado.


 class HASalesConfirmControllerExt extends SalesConfirmController  
 {  
   public static void main(Args _args)  
   {  
     SrsReportRunController formLetterController = HASalesConfirmControllerExt::construct();  
     HASalesConfirmControllerExt controller;  
     controller = formLetterController;  
     controller.initArgs(_args, ssrsReportStr(HASalesConfirm, Report));  
     if (classIdGet(_args.caller()) == classNum(SalesConfirmJournalPrint))  
     {  
       formLetterController.renderingCompleted += eventhandler(SalesConfirmJournalPrint::renderingCompleted);  
     }  
     formLetterController.startOperation();  
   }  
 }  

- Finalmente, agregue el método outputReport.


 class HASalesConfirmControllerExt extends SalesConfirmController  
 {  
   /// <summary>  
   ///  
   /// </summary>  
   protected void outputReport()  
   {  
     SRSCatalogItemName reportDesign;  
     reportDesign = ssrsReportStr(HASalesConfirm,Report);  
     this.parmReportName(reportDesign);  
     this.parmReportContract().parmReportName(reportDesign);  
     formletterReport.parmReportRun().settingDetail().parmReportFormatName(reportDesign);  
     super();  
   }  
 }  

- Necesitará implementar el reporte HASalesConfirm. Para hacer esto, haga clic derecho en el reporte y elija Implementar reportes.

- Cree una nueva clase de control de eventos y añada el evento onInserting de la tabla temporal de la siguiente manera:
Para agregar la lógica que ingresará el dato del campo extendido.

 class HASalesConfirmReportManager  
 {  
   /// <summary>  
   ///  
   /// </summary>  
   /// <param name="sender"></param>  
   /// <param name="e"></param>  
   [DataEventHandler(tableStr(SalesConfirmHeaderTmp), DataEventType::Inserting)]  
   public static void SalesConfirmHeaderTmp_onInserting(Common sender, DataEventArgs e)  
   {  
     SalesConfirmHeaderTmp header = sender;  
     SalesTable salesTable;  
     select SalesPoolId from salesTable  
       where SalesTable.SalesId == header.SalesId;  
     header.HASalesPoolName = SalesPool::find(SalesTable.SalesPoolId).Name;  
   }  
 }  

- Para probar esto, procedemos a confirmar un pedido de venta en D365FO.



jueves, 18 de julio de 2019

D365FO | Creación de métodos para control de eventos de datos

Los controladores de eventos de datos manejan los delegados expuestos en cada tabla, y estos se activan cuando ocurre el evento.
Hay dos métodos para cada uno: un método presente-continuo (ing) y un método en tiempo pasado (ed). Los métodos ing se invocan antes de que se active el evento, y el método ed se dispara después del evento.  
En nuestro ejemplo, el controlador de eventos se utiliza para asignar, validar y actualizar un campo. Por lo tanto, vamos a colocar estos métodos en una clase de ayuda. 

En nuestro caso, tenemos como escenario de prueba la aplicación para generar Renta de vehículos. Los registros se almacenan en la tabla HAVehicleRentTable.



- Crear una nueva clase. En nuestro caso HAVehicleRentManager.
- Abra el diseño de la tabla en cuestión, expanda el nodo Eventos y ubique onInserting.



- Haga clic derecho en el evento y elija Copiar el método del controlador de eventos.



- Abra la clase HAVehicleRentManager y pegue el código generado anteriormente en el cuerpo de la clase, como se muestra aquí:
 class HAVehicleRentManager  
 {  
   /// <summary>  
   ///  
   /// </summary>  
   /// <param name="sender"></param>  
   /// <param name="e"></param>  
   [DataEventHandler(tableStr(HAVehicleRentTable), DataEventType::Inserting)]  
   public static void HAVehicleRentTable_onInserting(Common sender, DataEventArgs e)  
   {
  
   }  
 }  

- A continuación, tendremos que escribir el código de la siguiente manera:
 class HAVehicleRentManager  
 {  
   /// <summary>  
   ///  
   /// </summary>  
   /// <param name="sender"></param>  
   /// <param name="e"></param>  
   [DataEventHandler(tableStr(HAVehicleRentTable), DataEventType::Inserting)]  
   public static void HAVehicleRentTable_onInserting(Common sender, DataEventArgs e)  
   {  
     HAVehicleRentTable vehicleRentTable = sender;  
     vehicleRentTable.numberDays = vehicleRentTable.toDate - vehicleRentTable.fromDate;  
     info(strFmt("Renta %1",vehicleRentTable.RentId));  
   }  
 }  

- Para probar esto, realice una compilación completa que incluya la sincronización de la base de datos. Y al crear una nueva renta de vehículo, el evento ejecuta nuestro código.



- Asignando el número de días y notificando el número de renta creado.



Al controlar campos en un diseño, también necesitamos controlar eventos como modifiedField y validateFieldPara controlarlos, nos suscribiremos al evento de datos apropiado.

- Considerando lo antes mencionado. Abra el diseño de la tabla HAVehicleRentTable, haga clic derecho en el evento onValidatedField y elija Copiar el método del controlador de eventos.



- Pegue el código generado en el cuerpo de la clase HAVehicleRentManager, y escriba el código del evento de la siguiente manera:
 class HAVehicleRentManager  
 {  
   /// <summary>  
   ///  
   /// </summary>  
   /// <param name="sender"></param>  
   /// <param name="e"></param>  
   [DataEventHandler(tableStr(HAVehicleRentTable), DataEventType::ValidatedField)]  
   public static void HAVehicleRentTable_onValidatedField(Common sender, DataEventArgs e)  
   {  
     ValidateFieldEventArgs fieldArgs = e;  
     HAVehicleRentTable vehicleRentTable = sender;  
     Boolean ok = true;  
     switch(fieldArgs.parmFieldId())  
     {  
       case fieldnum(HAVehicleRentTable,toDate):  
         if(vehicleRentTable.toDate < vehicleRentTable.fromDate)  
         {  
           ok = checkFailed("Fecha");  
         }  
         break;  
     }  
     if(!ok)  
     {  
       fieldArgs.parmValidateResult(false);  
     }  
   }  
 }  

Para probar esto, realice una compilación completa que incluya la sincronización de la base de datos. Y al modificar el valor del campo de fecha "Hasta", el evento ejecuta nuestra validación.



- Finalmente, abra el diseño de la tabla HAVehicleRentTable, haga clic derecho en el evento onModifyingField y elija Copiar el método del controlador de eventos.



Pegue el código generado en el cuerpo de la clase HAVehicleRentManager, y escriba el código del evento de la siguiente manera:
 class HAVehicleRentManager  
 {  
   /// <summary>  
   ///  
   /// </summary>  
   /// <param name="sender"></param>  
   /// <param name="e"></param>  
   [DataEventHandler(tableStr(HAVehicleRentTable), DataEventType::ModifyingField)]  
   public static void HAVehicleRentTable_onModifyingField(Common sender, DataEventArgs e)  
   {  
     ModifyFieldEventArgs fieldArgs = e;  
     HAVehicleRentTable vehicleRentTable = sender;  
     //Boolean ok = true;  
     switch(fieldArgs.parmFieldId())  
     {  
       case fieldnum(HAVehicleRentTable,toDate):  
         vehicleRentTable.numberDays = vehicleRentTable.toDate - vehicleRentTable.fromDate;  
         break;  
     }  
   }  
 }  

Para probar esto, realice una compilación completa que incluya la sincronización de la base de datos. Y al modificar el valor del campo de fecha "Hasta", el evento ejecuta nuestra actualización.



- Otras clases especializadas en DataEventArgs que son útiles son las siguientes: