sábado, diciembre 05, 2015

Profundizando en los FlowFields

Para mí una de las funcionalidades de la plataforma NAV más útil son los FlowFields. Son fáciles de construir, rápidos de ejecutar, exactos en cuanto al resultado, seguros en cuanto a las transacciones y funcionalmente potentes.

Vamos a ver en detalle como NAV conjuntamente con SQL utilizan esta tecnología.

Creación de un FlowField

Para ello vamos a crear una tabla en la que anotaremos los rappels que otorgamos a los clientes de forma periódica por haber alcanzado un determinado volumen de ventas o de margen comercial. Omitiremos el proceso de cálculo y nos centraremos sólo en la tabla.

Diseño de la tabla:


















Veámosla en SQL:










Y ahora añadimos el FlowField correspondiente en la tabla de clientes:



















Veamos que ha sucedido con nuestra tabla de rappels en SQL:










Efectivamente. ¡No ha sucedido nada!
El único índice continua siendo la Primary Key del campo ID.

Entonces ¿que hará NAV y SQL cuando se le pida un listado de, por ejemplo, los clientes 10000 y 30000 con los rappels para el mes de enero de 2017? Pues construir una sentencia SQL parecida a esta:








Está claro que si la tabla de rappels tiene muchos registros, al SQL le costará filtrar los registros por los campos en los que no dispone de ningún índice. (Aunque SQL tiene sus propios trucos, como las estadísticas).

Creación de un SumIndexField

Antes, en versiones anteriores, recuerdo que al crear el campo "Rappel Amount (LCY)" en la tabla de clientes, NAV hubiera obligado a construir un SumIndexField en la tabla "Customer Rappel" para no penalizar el rendimiento.

Pero desde hace algunas versiones esta obligación se ha eliminado, supongo que se confía en que SQL será suficientemente eficiente, ya que SQL incorpora cierto grado de inteligencia para saber qué datos pueden ser optimizados, según la frecuencia y forma de las consultas realizadas.

En nuestro caso, procederemos a crear un SumIndexField en la tabla "Customer Rappel" tal como se indica:


















Veámosla en SQL:











Efectivamente, ahora sí se ha añadido un nuevo índice que contiene los campos "Customer No." y Date. ¿Que hará ahora NAV y SQL cuando se le pida el listado anterior, recordemos, los clientes 10000 y 30000 con los rappels para el mes de enero de 2017?

















Podemos observar como ahora la consulta se ejecutará el doble de rápido al poder aprovechar el nuevo índice e iniciar así 2 procesos en paralelo.

Aunque también deberemos tener en cuenta que si se van a producir muchas alteraciones (altas, bajas, etc.) en esta tabla se va a penalizar el rendimiento de estas operaciones ya que para cada transacción SQL deberá mantener los índices.

Y entonces ¿por qué hemos indicado los campos que se van a calcular en la creación del índice si no han intervenido?

Las SIFT (SumIndexField Tables)

Antiguamente en NAV, al crear un SumIndexField se creaba una tabla en la BBDD con una estructura de combinación de campos y sus correspondientes subtotales.

En nuestro ejemplo anterior, NAV hubiera creado una tabla con los campos "Customer No.", "Date" y "Amount (LCY)". Los 2 primeros porque son los que van a intervenir en los filtros de la consulta y el tercero a modo de subtotal, o sea, 1 solo registro para cada coincidencia en cliente y fecha.

Actualmente estas tablas ya no se crean, en su lugar se crea una consulta indizada, o lo que es lo mismo, una vista junto con un índice.




La utilización de vistas indizadas tiene como ventaja, entre otras, que las agregaciones pueden calcularse previamente y almacenarse en el índice para minimizar los costosos cálculos durante la ejecución de consultas. Además, el optimizador de consultas puede seleccionar la vista si determina que ésta puede sustituirse por parte o por toda la consulta del plan de consultas si es de un coste menor.

Si volvemos al diseño de la tabla y en el SumIndexField accedemos a sus propiedades, aparecerá la siguiente ventana:


Apreciamos como la propiedad MaintainSQLIndex nos indica que sí existe índice en la tabla y como la propiedad MaintainSIFTIndex nos indica que sí existe una vista indizada. Además existen otras propiedades que nos permiten alterar las propiedades del índice directamente en SQL desde NAV.

Como se ha mencionado anteriormente cabe destacar que el rendimiento completo no existe, o primamos las consultas o primamos las modificaciones, pero todo a la vez es imposible. Por tanto en un NAV en producción hay que buscar el equilibrio en cada caso.

Por ejemplo, en una migración o un upgrade, es conveniente desactivar claves o propiedades de los índices con el objetivo de que las altas en la tabla se produzcan los más rápido posible. Posteriormente ya activaremos o no estas propiedades en función del uso que se vaya a hacer (muchas altas o muchas consultas).

Conclusiones

Al crear FlowFields deberemos tener en cuenta que la tabla sobre la que se efectúa el cálculo no se halla optimizada, deberemos hacerlo nosotros.

En dicha optimización NAV utiliza un índice de tabla y una vista indizada, ambos orientados a facilitar el trabajo a SQL siempre que se consultan datos, en nuestro caso, mediante FlowFields.

Si se va a priorizar el rendimiento para las consultas, desde NAV se deberá crear un índice y adicionalmente un SumIndexField, que es el que indica que se va a crear la vista indizada.

Si se van a primar las altas hay que desactivar los índices, ya que éstos se han de actualizar al realizar transacciones en la tabla, a más índices menos rendimiento.

Las propiedades del índice en NAV nos permitirán hacer combinaciones (índice sin vista o vista sin índice).

Es muy recomendable desactivar los índices al realizar migraciones o upgrades ya que en ese momento sabemos de cierto que se van a producir muchísimas altas y pocas consultas.

Más tarde, en fase de producción y tuning, se activarán los índices que procedan.

NOTA: Antes existían los "Database Key Groups" que se utilizaban para activar y desactivar conjuntos de índices, pero ahora están obsoletos.

viernes, agosto 21, 2015

Integrando código en Dynamics NAV

Supongo que muchos conocéis las AMU (Application Merge Utilities) que son geniales para las migraciones de código estándar modificado.

A partir de la versión actual sin modificar (ORIGINAL) comparado con esta misma modificada (MODIFIED) se obtienen los archivos diferenciales (DELTA) que se aplican a la nueva versión estándar (TARGET) y finalmente se obtiene la nueva versión modificada (RESULT).

Más en: https://www.linkedin.com/pulse/integrando-c%C3%B3digo-en-dynamics-nav-josep-pages

lunes, agosto 10, 2015

Controlando código en Dynamics NAV

Como probablemente sabréis, Dynamics NAV no dispone de ningún componente para control ni versionado de código, ni lo va a tener de momento. Hay que hacerlo manualmente a base de exportar los objetos, depositarlos en algún repositorio donde los programadores del proyecto tengan acceso controlado, y posteriormente si se desea restaurar a un punto anterior, o incorporar alguna nueva funcionalidad, trabajar con el “version list”, la fecha y hora de modificación, y utilizar una buena herramienta de comparación de ficheros.

Está claro que en este escenario el propietario del código del proyecto tiene serias dificultades para controlar las ramas, puntos de restauración, copias y saber cuál es la última versión buena conocida (Master). Además, estas dificultades se trasladan a los distintos programadores que tampoco saben con exactitud si el código con el que están trabajando es el adecuado y cuál es el bueno.

Más en https://www.linkedin.com/pulse/controlando-c%C3%B3digo-en-dynamics-nav-josep-pages

sábado, agosto 01, 2015

¿Cómo acceder directamente a SQL Server desde Dynamics NAV?

En ocasiones se necesita acceder desde Navision a alguna base de datos de SQL Server. Vamos a ver cómo hacerlo.

Hay que tener en cuenta las siguientes consideraciones:
- Este codigo utiliza .NET Interoperability para interfasar con componentes propios de Windows.
- Estos componentes se ejecutan en el NST, es decir, en el servidor de NAV, por lo tanto, es éste el que debe tener acceso a SQL Server.
- El control de transacciones va totalmente a parte del propio control de transacciones de NAV.

Declaración de variables:
SqlConnection : DotNet "System.Data.SqlClient.SqlConnection"
SqlCommand : DotNet "System.Data.SqlClient.SqlCommand"
SqlDataReader : DotNet "System.Data.SqlClient.SqlDataReader";

Codigo para abrir una conexión:
connString := 'Data Source=TheSQLsrv\AndInstance;Initial Catalog=TheBBDD;Integrated Security=false;User ID=TheUsername;Password=ThePassword';
SqlConnection := SqlConnection.SqlConnection(connString);
SqlConnection.Open();

Código para ejecutar un comando:
SQLstring := 'SELECT * FROM TheTableOrView ORDER BY TheOrderField';
SqlCommand := SqlConnection.CreateCommand();
SqlCommand.CommandText := SQLstring;
SqlDataReader := SqlCommand.ExecuteReader();

Codigo para leer un DataReader:
WHILE SqlDataReader.Read() DO BEGIN 
      myVar := SqlDataReader.Item(SomeField);
END;

Cerrar la conexión:
SqlConnection.Close();

viernes, julio 03, 2015

10 años para estar convencido...


El día en que Navision entró en mi vida recuerdo como me sorprendió su "beauty of simplicity" y aún hoy, cuando en el Master de Dynamics NAV explico los FlowFields y FlowFilters, o las relaciones condicionales, o los triggers a nivel de campo, o la automatización de transacciones, veo la cara de los agradablemente sorprendidos recién titulados en informática o de los experimentados profesionales, la misma cara que debía tener yo aquel día en que puse mis manos encima de un Navision.

En este tiempo he tenido el honor de conocer algunas eminencias del mundo Navision, como Alejandro Pradas, una gran persona al que le debo innumerables buenos consejos, que fue el que empezó a distribuir Navision en España y por lo tanto el culpable de que tantos de nosotros nos ganemos la vida en esto. Y por supuesto que he hecho muchos amigos entre mis clientes y colaboradores, aunque también algún enemigo, también he conocido a grandes personas y profesionales. Hay mucho talento repartido en el canal.

Hoy, a mis 49, Microsoft me ha reconocido por décima vez consecutiva como MVP en Dynamics NAV, y (permitidme la broma) es hoy cuando empiezo a estar convencido de que alguna cosa habré aprendido durante este tiempo y por lo tanto alguna cosa hago bien en el desempeño de mi trabajo diario.

En fin, que quiero dar las gracias a todos, a Microsoft, clientes, colaboradores, amigos, competidores y enemigos. Sin vosotros estoy seguro que no habría conseguido este reconocimiento número 10.

http://mvp.microsoft.com/es-es/mvp/Josep%20Pages-35659

sábado, junio 13, 2015

¿Cómo utilizar activos fijos presupuestados para comparar con la realidad?

Hace unos días me encontré con la necesidad de utilizar activos fijos presupuestados para posteriormente compararlos con la realidad. Vamos a explicar como:

Primero deberemos crear la ficha del activo presupuestado utilizando para ello el "check" correspondiente y posteriormente le añadimos coste mediante el diario de activos fijos, no por el diario contable de activos fijos, ya que no debe tener integración con contabilidad.































Más adelante, al comprar definitivamente el activo fijo, se deberá crear la ficha del activo fijo definitiva.































Para registrarle coste mediante la factura de compra correspondiente. Se deberá indicar en este momento el activo fijo presupuestado que se va a liquidar.




























Si el coste de la compra es superior al presupuestado, Dynamics NAV nos informará del error y no será posible continuar hasta que no se regularice la situación.












Una vez la factura haya sido registrada, el activo fijo presupuestado ya no le "quedará" coste para asignar a otros activos fijos reales.































El coste se ha "movido" al activo fijo real recien comprado.































Curiosamente, en Dynamics NAV no existe un informe que compare los activos fijos presupuestados con sus activos fijos reales, aunque como puede observarse en los movimientos de los activos fijos, éstos quedan relacionados, con lo que diseñar un nuevo informe para este propósito es perfectamente posible.








Incluso podría añadirse esta información el la ficha de estadísticas de los activos fijos para poder navegar con facilidad.



























lunes, marzo 30, 2015

¿Cómo montar un tenant en Dynamics NAV en 8 pasos?

Desplegar un tenant mediante PowerShell a partir de la instalación básica de Microsoft Dynamics NAV (Navision)


En este artículo voy a explicar los pasos a seguir para, a partir de una instancia de Dynamics NAV, montar un tenant y tener así el código de la aplicación albergado por la instancia y poder tener cuantos tenants queramos en esa misma instancia, cada tenant con su propia base de datos (de datos), compartiendo el codigo fuente.

Figura 1: Escenario multi-instancia


Figura 2: Escenario multi-tenant


Para ello vamos a utilizar Windows PowerShell ISE.

1er paso: Importar el módulo de administración de Dynamics NAV
Set-ExecutionPolicy RemoteSigned -Force
Import-Module 'C:\Program Files\Microsoft Dynamics NAV\80\Service\NAVAdminTool.ps1'

2do paso: Parar el servicio que está ejecutando la instancia a exportar.
Set-NAVServerInstance -ServerInstance 'DynamicsNAV80' -Stop

3er paso: Exportar el código fuente de la base de datos actual y separarlo en una base de datos independiente.
Export-NAVApplication -DatabaseServer 'MySQLServer' -DatabaseInstance 'NAVDEMO' -DatabaseName 'Demo Database NAV (8-0)' -DestinationDatabaseName 'NAV App'

4to paso: Quitar el código fuente de la base de datos para dejarla preparada para tenant.
Remove-NAVApplication -DatabaseName 'Demo Database NAV (8-0)' -Force

5to paso: Modificar el setting de la instancia para quitar la base de datos (de datos) y albergar multi-tenant.
Set-NAVServerConfiguration -ServerInstance 'DynamicsNAV80' -Element appSettings -KeyName 'DatabaseName' -KeyValue ''
Set-NAVServerConfiguration -ServerInstance 'DynamicsNAV80' -Element appSettings -KeyName 'MultiTenant' -KeyValue 'True'

6to paso: Iniciar el servicio parado en el paso 2 pero que ahora está preparado para multi-tenant.
Set-NAVServerInstance -ServerInstance 'DynamicsNAV80' -Start

7mo paso: Montar la base de datos del código fuente que será utilizado por todos los tenants.
Mount-NAVApplication -ServerInstance 'DynamicsNAV80' -DatabaseServer 'MySQLServer' -DatabaseInstance 'NAVDEMO' -DatabaseName 'NAV App'

8vo paso: Montar el tenant. Cada tenant de la instancia alberga una base de datos (de datos).
Mount-NAVTenant -ServerInstance 'DynamicsNAV80' -Id tenant1 -DatabaseServer 'MySQLServer' -DatabaseInstance 'NAVDEMO' -DatabaseName 'Demo Database NAV (8-0)' -OverwriteTenantIdInDatabase

Finalmente sólo nos queda acceder a NAV para comprobar que todo funciona correctamente. Ahora deberemos añadir a la cadena de conexión al servidor el tenant al que deseamos conectar. Por ejemplo: MyNAVSERVER.cloudapp.net:7046/DynamicsNAV80/tenant1

jueves, marzo 12, 2015

sábado, febrero 14, 2015

El primer Posgrado Universitario en Dynamics NAV a punto para empezar

Gracias a la universidad UVIC-UCC y Quonext, junto con otras entidades como el Ayuntamiento y CREACCIÓ, el próximo día 20 de febrero va a empezar el primer Posgrado Universitario en Dynamics NAV, del cual Josep Pages, MVP en Dynamics NAV desde el año 2006, es el impulsor y formador.


Es interesante destacar que los estudiantes tendran asegurados 6 meses de contrato remunerado en el CEDART, el Centro de Desarrollo de Alto Rendimiento, que se está creando al lado de la universidad y gracias al convenio con estas entidades.

jueves, febrero 12, 2015

Microsoft lanza herramienta de traducción para NAV y AX

Microsoft Dynamics ERP Translation Solution utiliza el formato estandar XLIFF y automatiza la traducción a través de Microsoft Translator Hub de Microsoft Research.

Más info: http://blogs.msdn.com/b/nav/archive/2015/02/12/announcing-the-microsoft-dynamics-erp-translation-solution.aspx

viernes, febrero 06, 2015

Dynamics NAV Especificaciones Técnicas

Lista de especificaciones técnicas en Navision

Recientemente un cliente me pidió que le rellenara una hoja de requisitos sobre especificaciones técnicas de Navision y aprovecho para reproducirla. (He cambiado los enunciados de las preguntas). Espero que os sirva.

https://www.linkedin.com/pulse/dynamics-nav-tech-specs-josep-pages

viernes, enero 23, 2015

Continuan las promociones 'Give me 5'

Debido al éxito obtenido, Microsoft decide continuar y ampliar  las promociones ‘Give me 5’ & 'Give me 5+2' para Microsoft Dynamics NAV

Las promociones "Give me 5" que terminaron el pasado 27 de diciembre se han visto prorrogadas y mejoradas para estos 2 próximo trimestres.
Este es el detalle:

1er trimestres 2015
- "Give Me 5 Starter" - Starter Pack mas 2 Usuarios full por EUR 3.750 para clientes en España.
- "Give Me 5 + 2 Extended" - Starter Pack mas el Extended Pack mas 4 Usuarios Full por EUR 12.000 para clientes en España.

2do trimestre 2015
- "Give Me 5 Starter" - Starter Pack mas 2 Usuarios Full por EUR 4.500 para clientes en España.
- "Give Me 5 + 2 Extended" - Starter Pack mas el Extended Pack mas 4 Usuarios Full por EUR 12.750 para clientes en España.

Además, para el 1er semestre de 2015, existe una promoción para los clientes existentes:

- "Extend NAV For Less" – Clientes existentes pueden licenciar Microsoft Dynamics NAV Extended Pack mas 1 Usuario Full por EUR 7.200

Si quieres ampliar esta información, contacta con Quonext

Modificaciones en 340 y 347 según IVA de Caja

Os dejo unos links al blog del equipo español de Soporte de Dynamics NAV en donde explican las modificaciones del 340 y 347 para cumplir con las últimas normas de la Agencia Tributaria.

Para 340: http://blogs.msdn.com/b/eneauve/archive/2015/01/22/modelo-340-modificaciones-adicionales-en-cuanto-al-criterio-de-iva-de-caja.aspx

Para 347: http://blogs.msdn.com/b/eneauve/archive/2015/01/22/modelo-347-iva-de-caja.aspx

sábado, enero 10, 2015

Y II, mejorar y extender el diseño de cualquier cliente de Dynamics NAV

Siguiendo con el post anterior, que recomiendo su lectura, de cómo mejorar y extender el diseño de Dynamics NAV, se nos plantea la necesidad evidente de permitir que el control Add-in sea accesible desde cualquier cliente, incluido Web y Tablet. Para ello seguí la siguiente documentación de MSDN.

Las diferencias con el control Windows Forms indicado en el post anterior es que aquí sólo utilizaremos el código C# como interfase para, desde C/AL, ejecutar el código Java Script que, éste sí, es ejecutable desde los clientes que no son nativos Windows.

Creando un Add-in para cualquier cliente

Empezaremos creando un proyecto C# desde Visual Studio de tipo Biblioteca de clases y añadiremos la referencia a la librería del cliente NAV que encontraras en el directorio donde tengas instalado el cliente:

Microsoft.Dynamics.Framework.UI.Extensibility

Ahora deberemos referenciarla en el código de la clase:

using Microsoft.Dynamics.Framework.UI.Extensibility;

Y empezamos a definir el interfase:

namespace CEDARTjs500x150
{
    [ControlAddInExport("CEDART.controls.labelJs500x150")]
    public interface CDRTlabelJs500x150
    {
        [ApplicationVisible]
        event ApplicationEventHandler ControlAddInReady;
 
        [ApplicationVisible]
        void SetHtml(string value);
        [ApplicationVisible]
        void SetImage(string name);
    }
}

Una vez definido el interfase, necesitamos dotar de código al control para que haga ciertas cosas, en nuestro caso, muy básico, lo que hace es que visualiza cualquier código HTML con la función SetHtml o visualiza una imagen con la función SetImage.

Siguiendo este ejemplo de MSDN podrás ver que es necesario crear una estructura de carpetas en el proyecto:


Me refiero a la carpeta Resources en la que podremos poner, básicamente 3 carpetas más, una con imágenes, otra con el código Java Script y otra con hojas de estilo. Fijaros también que en la carpeta Resources deberemos tener un fichero Manifest.xml que es el que indica donde está cada cosa.

Como veremos más adelante esta carpeta y su contenido empaquetado en un archivo .zip se deberá incluir al registrar el control en Dynamics NAV.

<?xml version="1.0" encoding="utf-8"?>
<Manifest>
  <ScriptUrls>
    <ScriptUrl>http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.3.min.js</ScriptUrl>
  </ScriptUrls>

  <Resources>
    <Image>LogoCEDART150.png</Image>
    <Script>Script.js</Script>
    <StyleSheet>Stylesheet.css</StyleSheet>
  </Resources>

  <Script>
    <![CDATA[
        $(document).ready(
           function() {
               InitializeLabel();
               Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('ControlAddInReady', null);
           });
    ]]>
  </Script>

  <RequestedHeight>150</RequestedHeight>
  <RequestedWidth>500</RequestedWidth>
  <VerticalStretch>true</VerticalStretch>
  <HorizontalStretch>true</HorizontalStretch>
</Manifest>

En ScriptUrls podremos indicar scripts externos, que como podeis suponer podemos utilizar cualquiera de las librerías externas existentes, en mi caso he utilizado la conocidísima jQuery.

En Resources definiremos todos nuestros archivos utilizados en el proyecto, posteriormente lo veremos más en profundidad.

La parte Script es el código JavaScript que se va a ejecutar (jQuery en nuestro caso). Evidentemente que podríamos poner todo el código en esta sección, pero por cuestiones de Readability es preferible ponerlo en los archivos de script externos.

Como veies, en mi caso, lo que hace es que cuando el documento está "ready" se ejecuta una función para inicializar el control y acto seguido se dispara el evento ControlAddInReady. Fijaos que con la función InvokeExtensibilityMethod podemos disparar cualquier evento en NAV, previamente definido en el interfase en C#.

Finalmente en las 4 últimas propiedades definimos las dimensiones mínimas que debe tener el control y si debe permitir Stretch. Vuelvo a insistir en este punto que las capacidades de resizing tienen mucho por mejorar.

Es importante destacar que los controles diseñados en JavaScript no pueden disponer de propiedades, sólo métodos.

Contenido del paquete .zip de la carpeta Resources

Vamos ahora a centrarnos en los 3 tipos de ficheros que podemos incluir.

El primer tipo de contenido que podemos incluir en Resources son imágenes que podremos recuperar desde el código, tal como veremos más adelante.

El segundo tipo de contenido es Script que evidentemente contiene las funciones. Veamos un ejemplo:

function InitializeLabel() {
    SetHtml("controlId");
}

function SetHtml(value) {
    $('#controlAddIn').html(value);
}

function SetImage(name) {
    var imageURL = Microsoft.Dynamics.NAV.GetImageResource(name);
    $('#controlAddIn').html("<div><img src='" + imageURL + "'/></div>");
}

La primera función es la que se llama desde el Manifest.xml y es la encargada de inicializar el control.
La segunda es la que inserta cualquier texto o código HTML en el control.

La tercera nos permite tener acceso a cualquier imagen, que hayamos incluido en el paquete, mediante la función GetImageResource.

El tercer tipo de contenido son hojas de estilo. En StyleSheet incluiremos las hojas de estilo que vayamos a utilizar, creadas por nosotros o conseguidas de los numerosos recursos externos. Un ejemplo:

body
{
    font-family: Verdana;
    color: black;
    background-color: white;
}

p
{
    padding: 10px;
}

.title
{
    font-size: 14;
    font-weight: bold;
}

.normal
{
    font-size: 12;
    font-weight: normal;
}

.forbr
{
    font-size: 6;
}

Esto es todo lo que necesitamos para nuestro sencillo ejemplo. Vamos a compilarlo y a registrarlo en Navision.

Compilar el control Add-in y registrarlo en Dynamics NAV

Para compilar, deberéis seguir el ejemplo propuesto en el post anterior, toda la parte que explica cómo firmar y compilar el proyecto.

La parte donde explica como registrar el control hay que hacer lo mismo y adicionalmente lo siguiente: Como los controles Add-in pueden replicarse desde el servidor al cliente deberemos cargar la carpeta Resources para que pueda descargarse y ejecutarse localmente.

Para ello lo que haremos será empaquetarla en un archivo .zip y al registrar el control, mediante la acción de Importar importaremos el archivo.

Diseñar utilizando nuestro control Add-in

Diseñaremos una Page en la que incluiremos un Field que incluirá el control Add-in diseñado anteriormente:


Ya añadiremos el siguiente código en el evento Label3::ControlAddInReady()

CurrPage.Label3.SetHtml(
  '<p>' +
  '<span class="title">Ratios de liquidez</span>' +
  '<span class="forbr"><br /><br /></span>' +
  '<span class="normal">' +
  'La liquidez es la capacidad potencial que tiene la empresa para pagar sus obligaciones. ' +
  'La comparación entre la cantidad de riqueza disponible (activo circulante) y las deudas ' +
  'que habrá que atender a corto plazo ' +
  '(pasivo circulante) proporciona una medida de esta liquidez.</span>' +
  '<span class="forbr"><br /><br /></span>' +
  '<span class="normal">' +
  'Dependerá del grado de realización de los elementos del activo, ' +
  'es decir, si están cerca de su conversión en liquidez (derechos de cobro que venzan ' +
  'a corto plazo, existencias que se vayan a vender, etc.), y del grado de exigibilidad del pasivo, ' +
  'es decir, vencimiento de las deudas y necesidad de su devolución.</span>' +
  '</p>');

Compilamos y ejecutamos. Este es el resultado:


Y probamos desde el cliente Web, en mi caso: http://localhost/DynamicsNAV80/WebClient/list.aspx?page=60199 y este es el resultado:


Y desde el cliente tablet:


Vamos a probar ahora la visualización de una imagen incluida en el paquete, para ello vamos a sustuir el código de la Page por este:

CurrPage.Label3.SetImage('LogoCEDART150.png');

Compilamos y ejecutamos en los diferentes clientes:




Objetivo conseguido

Ahora somos capaces de visualizar cualquier contenido, utilizando comandos Html5, programando en JavaScript utilizando librerías como jQuery y hojas de estilos en cualquiera de los clientes de Dynamics NAV.

Cómo mejorar y extender el diseño en Dynamics NAV?

Recientemente necesitaba incluir un texto largo en una Page y utilizando el control del propio NAV con la opción de multlinea no terminaba de gustarme el resultado:


Entonces se me ocurrió que podía hacer un control Add-in que permitiera visualizar cualquier texto en una Page. Así que busqué información en mi muy a menudo consultado MSDN, aquí tenéis un poco de información sobre la arquitectura, y posteriormente estudié este artículo de MSDN y me atreví a desarrollar mi propio objeto Label con el que poder visualizar texto multilinea en un formato mejor.

Creando un control Add-in para el cliente Windows

Empecé creando un nuevo proyecto en Visual Studio en C# de tipo Biblioteca de clases. Añadí las 2 referencias a las librería de NAV que encontrarás en el directorio donde tengas instalado el cliente NAV:

Microsoft.Dynamics.Framework.UI.Extensibility
Microsoft.Dynamics.Framework.UI.Extensibility.WinForms

Y otras 2 al propio .NET Framework:

System.Windows.Forms
System.Drawing;

Así mismo las referencié en el código de la clase:

using Microsoft.Dynamics.Framework.UI.Extensibility;
using Microsoft.Dynamics.Framework.UI.Extensibility.WinForms;
using System.Windows.Forms;
using System.Drawing;

Y empecé a declarar la clase:

[ControlAddInExport("CEDART.controls.labelWF")]
public class CDRTlabelWF : WinFormsControlAddInBase

La primera línea es importante porque es la que utilizaremos posteriormente para declarar y registrar el Add-in dentro de NAV.

En cuanto a la segunda línea, podía haber utilizado la clase abstracta StringControlAddInBase en lugar de WinFormsControlAddInBase pero posteriormente me di cuenta que necesitaba que mi control admitiera resizing para que si el contenedor cambiaba de tamaño mi control debía adaptarse a este nuevo tamaño y al parecer la clase abstracta StringControlAddInBase no lo admite tal como veremos más adelante.

En este punto debo decir que según todas las pruebas que he efectuado y algunos comentarios que he leído en foros, los controles Add-in no se llevan muy bien con el resizing, con lo que será importante que lo defináis con unas dimensiones ajustadas a vuestro propósito.

Continuamos con el código de creación del control:

Label _label = new Label();
protected override void OnInitialize()
{
    base.OnInitialize();
    this.ApplySize(new DisplaySize(1, 100, 600), new DisplaySize(1, 20, 300));
}

protected override Control CreateControl()
{
    _label.AutoSize = true;
    _label.Click += (sender, args) => { ControlClick(_label, null); };
    _label.ParentChanged += (sender, args) =>
        {
            if (ControlAddInReady != null)
            {
                ControlAddInReady(_label, null);
            }
        };
     return _label;
}

Continuando con el resizing, necesitamos declarar la función onInitialize() para poder indicar los tamaños mínimos, iniciales y máximos que tomará el control en ancho y largo al iniciar y redimensionar la pantalla principal, en nuestro caso la pantalla del cliente NAV. Esto concretamente es lo que no admite la clase abstracta StringControlAddInBase.

Otra cosa importante es que desde C/AL no podemos hacer nada con el control hasta que esté creado, con lo que debemos disponer de un evento que nos informe cuándo nuestro control Add-in está listo y preparado para admitir que utilicemos sus funciones y propiedades. Esto es lo que hace la última parte del código cuando dispara el evento ControlAddinReady.

Este es el código de definición de los eventos:

[ApplicationVisible]
public event EventHandler ControlAddInReady;
[ApplicationVisible]
public event EventHandler ControlClick;

Como puedes ver he definido un evento que se dispara al dispararse el evento Click del control Label.

Continuamos con la definición de métodos y propiedades:

[ApplicationVisible]
public void SetForeColor(bool black, bool white, bool gray)
{
    if (black)
    {
        _label.ForeColor = Color.Black;
    }
    if (white)
    {
        _label.ForeColor = Color.White;
    }
    if (gray)
    {
        _label.ForeColor = Color.DarkGray;
    }
}

[ApplicationVisible]
public void SetBackColor(bool transparent, bool white, bool gray)
{
    if (transparent)
    {
        _label.BackColor = Color.Transparent;
    }
    if (white)
    {
        _label.BackColor = Color.White;
    }
    if (gray)
    {
        _label.BackColor = Color.LightGray;
    }
}

[ApplicationVisible]
public void SetFont(string family, int size, bool bold)
{
    if (family == null)
    {
        family = "Arial";
    }
    if (size == 0)
    {
        size = 9;
    }
    if (bold == true)
    {
        _label.Font = new Font(family, size, FontStyle.Bold);
    }
    if (bold == false)
    {
        _label.Font = new Font(family, size);
    }
}

[ApplicationVisible]
public string Caption
{
    get { return _label.Text; }
    set { _label.Text = value; }
}

En esta parte los límites son vuestra creatividad, podemos hacer tantas cosas con el control como se nos ocurran (o como se deje el propio control). No es olvidéis de poner la etiqueta [ApplicationVisible] antes de la declaración del evento, método o propiedad para que sea visible desde NAV.

Finalmente deberemos firmar el proyecto. Para ello iremos a las propiedades del proyecto y pulsaremos en la opción Firma para marcar la opción Firmar el ensamblado y crear un nuevo archivo de firma, al que le daremos un nombre y desmarcaremos la opción Proteger mi archivo de clave mediante contraseña:


Tan sólo nos queda compilar el control, pero para registrarlo en NAV deberemos conocer su clave pública, con lo que en las propiedades del proyecto accederemos a la opción Eventos de compilación y en la ventana de Línea de comandos del evento posterior a la compilación añadiremos el siguiente comando para que al compilar nos proporcione el ID de la clave pública:

"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\sn.exe" -T "$(TargetPath)"

Hay una parte del directorio que puede cambiar según vuestra versión de Windows o versión de .NET Framework que utilicéis. 

Con lo que finalmente, al compilar deberemos ver en la ventana Resultados algo similar a esto:
 
No la cerréis, vamos a necesitar el Token en seguida.
 
Registrando el control Add-in en el cliente Windows
 
Primero debemos copiar la librería DLL al directorio de Add-ins en NAV, en mi caso al utilizar NAV 2015 utilizaré el directorio:
 
C:\Program Files (x86)\Microsoft Dynamics NAV\80\RoleTailored Client\Add-ins
 
En el que podemos crear un directorio y copiar la librería desde el directorio (el que os indique vuestra ventana de Resultados), en mi caso:
 
C:\Users\Josep\Documents\Visual Studio 2013\Projects\CEDART\CEDARTwf\bin\Debug\CEDARTwf.dll
 
Abrimos NAV y buscamos Complementos de control para crear un nuevo registro en la tabla e informarlo como sigue:
 
El Nombre complemento control ha de ser idéntico al indicado en nuestro proyecto .NET en la definición [ControlAddInExport("CEDART.controls.labelWF")]

El Token de clave pública es el proporcionado al compilar el proyecto en la ventana Resultados o mediante otros métodos tal como se explica aquí.

Diseñar utilizando nuestro control Add-in

En este punto, y si todo ha ido bien, ya estamos en disposición de utilizar nuestro control Add-in para visualizar un texto dentro de una Page. Con lo que voy a utilizar la misma página mostrada al principio para reacondicionarla:

 
Lo que quiero es sustituir los 2 controles de textos actuales por los de nuestro Add-in, para ello voy a crear una línea adicional y lo pondré nombre Label1, después accederé a las propiedades e indicaré, en la propiedad ControlAddIn el Add-in utilizando el Assist (F6):

 
Accederemos al código (F9) y comprobaremos como disponemos de 2 nuevos eventos:
 
 
El primero se disparará cuando el control Add-in esté listo para ser utilizado y el segundo cuando se haga Click en el control. Vamos a poner código en el primero:

CurrPage.Label1.SetFont('', 14, TRUE);
CurrPage.Label1.Caption := 'Ratios de Liquidez';
 
Crearemos una segunda Label e igualmente introduciremos código en el evento ControlAddInReady:

CurrPage.Label2.Caption :=
  'La liquidez es la capacidad potencial que tiene la empresa ' + 
  'para pagar sus obligaciones. La comparación entre la cantidad ' + 
  'de riqueza disponible (activo circulante) y las deudas que ' + 
  'habrá que atender a corto plazo ' +
  '(pasivo circulante) proporciona una medida de esta liquidez.' +
  'Dependerá del grado de realización de los elementos del activo, ' +
  'es decir, si están cerca de su conversión en liquidez (derechos ' + 
  'de cobro que venzan a corto plazo, existencias que se vayan a ' + 
  'vender, etc.), y del grado de exigibilidad del pasivo, ' +
  'es decir, vencimiento de las deudas y necesidad de su devolución.';
 
Borramos los 2 controles anteriores, compilamos y ejecutamos la Page:
 

 
Mucho mejor que la inicial, ¿no os parece?:



Este Add-in al haber sido creado mediante Windows Forms no es ejecutable desde el cliente Web. En un segundo post os mostraré como construir el Add-in para que pueda ser ejecutado tanto en cliente Windows como en cliente Web.