Creando usuarios con Power Shell

Se abre el telón, salgo yo en jueves santo con media empresa de vacaciones. En eso que entra un ticket mas o menos urgente que hay que resolver de un cliente con el que más o menos no trabajo habitualmente. Me conecto al entorno de desarrollo, hago el cambio y cuando voy a probarlo… ZAS! En toda la boca. Mi usuario no está creado en la base de datos. Soy administrador del servidor, del SQL, puedo tocar objetos, etc. Pero como mi usuario no esta creado en la base de datos y no tiene roles activos… en definitiva, no puedo ni llegar a listar las empresas que hay en la base de datos.

Entonces viene CMD se mete en una cabina de teléfonos, se quita las gafas de pasta y sale Power Shell! Guapooo! Salaooo! (Creo que me estoy creando un problema de la ostia si algún día decido traducir este blog).

Aquí vienen los pasos que he seguido para crear un usuario en NAV sin entrar en NAV:

  • Ponte el antifaz de malo: Un gorro de lana negro combina a la perfección. Vas a darte acceso a algo a lo que no tienes acceso. Es lo más cerca que has estado o vas a estar nunca de ser un hacker así que disfrútalo.
  • Abre Power Shell en modo Administrador: Que te voy a contar. Te vas al servidor donde tengas instalado el servicio de NAV, Botón derecho en el icono de Power Shell y ejecutar como administrador.
  • Cargar la librería de funciones de NAV: Esto es algo que se les olvida explicar a muchos blogs o manuales de ayuda de Microsoft. El primer comando que tenemos que picar sirve para agregar funciones en la sesión de Power Shell que nos permitan interactuar con NAV:

    Import-Module 'C:Program FilesMicrosoft Dynamics NAV71ServiceNavAdminTool.ps1'

    Actualización: Si este paso ta da problemas, es posible que primero tengas que permitirte usar scripts de PowerShell. He encontrado por ahí que se hace metiendo este comando:

    Set-ExecutionPolicy RemoteSigned

    Hijos míos, no hace falta que os diga que esa ruta es dónde yo he encontrado el fichero y que necesitaréis adaptarla a vuestro servidor.

  • Crea tu usuario en NAV: Con este comando estarás creando un usuario sin permisos en NAV:

    New-NAVServerUser NOMBRE_DE_LA_INSTANCIA_DE_NAV -WindowsAccount DOMINIOUSUARIO_NUEVO

    Felicidades. Ya estás en Matrix.

  • Dáte permisos: Tienes barra libre y te pides un agua del grifo? Nos vamos a dar permisos de SUPER usando este comando:

    New-NAVServerUserPermissionSet

    Verás que Power Shell te empieza a pedir los parámetros básicos como usuario, rol e Instancia.

Ya está. Seguramente ya puedes entrar. Si no, siempre puedes usar esta herramienta. No os olvidéis de sacaros el antifaz y el gorro. A la empresas no les suele gustar tener gente con antifaz junto a la máquina del café.

RTF a Texto Plano

Siguiendo con mis batallas de integraciones de NAV con otros sistemas, me he topado con largas notas de texto, que una vez importadas en NAV, he visto que tenían una serie de metatags asociados. Indagando un poco he visto que se trataba de Rich Text Format. Por el momento NAV no tiene un editor nativo RTF y, aunque había visto que hay add-ons para 2009 he pensado que no era necesario llegar a tanto, dado que los datos solo son necesarios para consultas puntuales. Necesitaba el texto plano.

Es casi imposible que tantos planetas queden alienados, pero si andas buscando la forma de pasar de RTF a texto plano en NAV, la solución pasa por utilizar el dotNet System.Windows.Forms.RichTextBox. A continuación os dejo con el ejemplo en NAV 2013 R2:

GetPlainNoteText() : Text

l_txtComment := '';
CALCFIELDS(NoteText);
IF NOT NoteText.HASVALUE THEN BEGIN
  l_txtComment := '';
END ELSE BEGIN
  NoteText.CREATEINSTREAM(l_instStream);
  REPEAT
    l_instStream.READTEXT(l_txtComment250,250);
    l_txtComment += l_txtComment250;
  UNTIL l_instStream.EOS;

  l_dnetRichTextBox := l_dnetRichTextBox.RichTextBox();
  l_dnetRichTextBox.Rtf(l_txtComment);
  l_txtComment := l_dnetRichTextBox.Text;
END;

EXIT(l_txtComment);

Las variables utilizadas son:

l_txtComment Text  
l_instStream InStream  
l_txtComment250 Text 250
l_dnetRichTextBox DotNet System.Windows.Forms.RichTextBox.’System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′

Aclararos que NoteText es un campo BLOB que tengo en la propia tabla donde se aloja la función y que contiene fragmentos RTF.

Como veis lo único que hago es asignar por la propiedad RTF y recoger por la propiedad Text. Tan sencillo como eso. El control se encarga de hacer todo lo necesario en background. Además ahora que por fin podemos crear variables text sin longitud máxima, no es necesario preocuparnos por lo largo que pueda ser el texto.

Algunas funciones internas de Sales Header y Sales Line

Una de las tablas con las que seguramente más trabajamos es la tabla 36 (Sales Header) y la tabla 37 (Sales Line). Como ya sabemos en ellas empiezan muchos de los procesos de venta pues es ahí donde los usuarios crearan los documentos de venta que posteriormente generaran albaranes, facturas, etc.

Es habitual por lo tanto que tengamos que trabajar continuamente sobre ellas por ejemplo, automatizando procesos que crean cabeceras, líneas o que modifican su información.

Hay unas cuantas funciones que por las prisas del día a día no siempre nos detenemos a observar, pero que es muy interesante conocer y analizar cómo funciona:

SuspendStatusCheck:
Esta función se encuentra en la tabla 37. Su funcionamiento es muy sencillo ya que lo único que nos permite es modificar el valor de una variable global de la tabla. La gracia está en que esa variable condiciona muchos de los controles que se ejecutan con las distintas reglas de negocio. Por lo tanto podemos entender que es como dar un pase VIP a las validaciones que hagamos sobre ese registro desde ese momento en adelante.

SetHideValidationDialog:
Esta función se encuentra en la tabla 36. Su funcionamiento es muy parecido a la función que acabamos de ver, solo que en este caso nos permite pasar por encima de muchos CONFIRM que podemos encontrar en el código. Es muy común utilizarla en procesos que tienen que generar varios documentos de venta de forma automática ya que a través de esta llamada se consigue que el proceso no se pare por un mensaje de confirmación al usuario.

GetSalesHeader/SetSalesHeader:
Por desgracia, estas dos funciones son las grandes desconocidas para muchos y son muy importantes. Se encuentran dentro de la tabla 37 y sirven para situar la variable global SalesHeader en la cabecera relacionada a la línea en la que estamos. Os animo a que busquéis en toda la tabla 37 una sola línea de código que diga SalesHeader.GET. Encontrareis muy pocas o ninguna en una base de datos limpia y en cambio veréis que hay varios montones en bases de datos muy desarrolladas. Ahora os animo a que busquéis GetSalesHeader… hay muchísimas… ¿El motivo? Principalmente hay dos:

  • La función esta optimizada para que solo se haga el GET si es necesario. Si tenemos a la variable colocada sobre la cabecera que toca, ¿para qué volver a hacer el GET? Menos accesos a disco, mayor rendimiento.
  • La función se basa en la colocación de la variable SalesHeader y eso muy bueno cuando la misma variable puede venir referenciada por SetSalesHeader puesto que puede tratarse, por ejemplo de una variable TEMPORAL.
GetSalesHeder y SetSalesHeader
GetSalesHeder y SetSalesHeader

Todo esto de poder jugar a hacer GET de un registro que no existe está muy bien y funciona de maravilla hasta que alguien viene e incrusta su primer SalesHeader.GET en alguno de los disparadores de la tabla 37. A partir de ese momento, nada de todo esto funciona.

Si queréis un ejemplo de todo esto pensad es que tenéis que hacer un simulador de precios en el que tienen que intervenir todas las reglas de negocio que se deberían de aplicar en la confección de un pedido. Podéis empezar creando una cabecera Temporal. En esta cabecera, podéis validar varios campos llamando al inicio a SetHideValidationDialog para ahorrar al usuario el tener que ir interviniendo. Luego podríamos pasar a las líneas que, llamando previamente a SetSalesHeader no tendrían problema en encontrar la cabecera aunque esta no se encuentre realmente en la base de datos. Si además utilizamos SuspendStatusCheck, seguramente también nos ahorraremos algún que otro control que se haga durante la inserción de las líneas.

Error Stack

GETLASTERRORTEXT es una función que permite capturar el último error que se ha dado en la ejecución de un proceso. A priori, no parece algo muy útil, pero cuando pensamos en la ejecución condicional de codeunits, esta función cobra sentido.

A partir de ahí se me ocurrió una idea: ¿Y si utilizo esta función para capturar los posibles errores que pudiera tener en la ejecución de un proceso? ¿Podría diseñar una herramienta capaz de indicarle las acciones o campos que quiero validar y que me devuelva la lista de resultados que obtengo?

Para llegar a tener esta herramienta, pensé en que funciones debían de ser importantes:

  • Primero, necesitaría una función para indicarle el registro que quiero validar, el campo y el valor que quiero informar.
  • Segundo, seria necesario una función para lanzar la validación del propio campo, con los valores aportados anteriormente. El éxito o error de la validación del campo, se tendría que guardar en una pila o stack con el resultado de la ejecución.
  • Tercero A, disponer de una función para mostrar la lista de errores encontrados hasta el momento.
  • Tercero B, si no deseo mostrar la lista de errores, tener la posibilidad de recoger los resultados para tratarlos posteriormente.
  • Cuarto, disponer de una función para decidir manualmente cuando quiero limpiar la pila de errores.

Las utilidades de una herramienta como esta pueden ser varias como por ejemplo la importación masiva de datos, registro masivo de documentos, etc. Es cierto que sólo está pensado para validar campos, pero yo puedo perfectamente crear un campo booleano que al validarse, lance un pedido, por ejemplo.

Os dejo la herramienta para que la utilicéis a vuestro antojo. Espero en breve poder añadir uno o varios ejemplos de como utilizarla:

Sergisoft Error Stack v1

Primeros pasos con OData (Parte 1)

Con el lanzamiento de NAV 2013, disponemos de una nueva forma de acceder a los datos de NAV. Esta nueva vía ofrece de manera muy sencilla, segura y rápida autonomía a todo tipo de usuarios para poder extraer la información y construir sus propios listados interactivos.

En este ejemplo vamos a ver paso a paso como utilizar los servicios de OData con un plug-in de Excel que puede obtenerse totalmente gratis: PowerPivot. De forma práctica veremos cómo podemos construir una tabla dinámica segmentada por prácticamente cualquier campo que pueda afectar al Stock. Simplemente es una ejemplo didáctico, aunque las posibilidad son infinitas.

Lo primero que tenemos que identificar es el puerto con el que se ha configurado la instalación de NAV. En mi caso, es el puerto 7078, pero en una instalación estándar se utiliza el puerto 7048:

Microsoft Dynamics NAV Administration

Podemos consultar el puerto que se configuró durante la instalación consultando el nuevo Microsoft Dynamics NAV Administration.

A continuación, abrimos una nueva hoja de excel y buscamos la opción de PowerPivot:

Acceso a Power Pivot desde Excel

Para poder insertar datos en PowerPivot, estos tienen que estar publicados en NAV. Para ver y modificar los elementos que están publicados iremos a Departamentos – Administración – Administración de IT – General – Servicios web.

Para este ejemplo vamos a publicar las siguientes páginas de NAV:

Servicios Web publicados

Ahora ya está todo listo. Para indicarle a PowerPivot cómo tiene que recoger nuestros datos utilizaremos la opción de Obtener datos externos de una fuente de distribución de datos:

Obtener datos externos de una fuente de distribución de datos

En este punto, indicaremos en el segundo campo la ruta de acceso a los servicios de OData (teniendo en cuenta lo que hemos comentado más arriba sobre el puerto):

Acceso a los datos publicados

En la siguiente pantalla le indicaremos que conjuntos de datos queremos obtener:

Acceso a los datos publicados

En este punto, PowerPivot hará la conexión con nuestra base de datos y recuperará todos los datos:

Datos recuperados correctamente

Una vez hemos recuperado los datos, tenemos que indicarle a PowerPivot la relación que hay entre las distintas tablas. En este ejemplo, hemos recuperado la tabla de productos y la tabla de movimientos de producto. Lo que hacemos es decirle que el campo Item_No de los movimientos de producto está relacionado con el campo No de la tabla producto:

Crear relación de PowerPivot

Crear relación de PowerPivot

En este punto ya tenemos los datos en PowerPivot y las relaciones que hay entre todos ellos. Por lo tanto, podemos cerrar PowerPivot y regresar a la hoja de Excel origen.

Ya en Excel vamos a insertar una nueva tabla dinámica, pero atención, esta tabla dinámica va ser una tabla dinámica de PowerPivot y no una tabla dinámica convencional. Pronto veremos las diferencias:

Insertar tabla dinámica

Como vemos, tenemos a nuestra disposición todos los datos de PowerPivot y ya podemos empezar a jugar colocando campos como etiquetas de fila, suma de valores y segmentos. En este ejemplo hemos montado esta configuración (todos los campos pertenecen a mov. producto):

Configuración de tabla dinámica de stock con segmentos

Y el resultado es el siguiente:

Tabla dinámica de stock con segmentos

La principal ventaja de los segmentos es que podemos interactuar con ellos seleccionando uno o varios. Al seleccionar los datos que se muestran varían pero también afecta al resto de segmentos de la tabla dinámica.

Tabla dinámica de stock con segmentos

 Por lo tanto, se convierte en una herramienta muy ágil para la explotación avanzada de los datos.

 

Galerías de imágenes para NAV

Cuando tengáis que añadir nuevos botones a RTC, es útil que tengáis a mano estas chuletas para conocer todas las imágenes predefinidas que trae el cliente de roles. Ambas imágenes se han extraído de msdn de microsoft:

http://msdn.microsoft.com/en-us/library/dd568709.aspx

http://msdn.microsoft.com/en-us/library/dd568728.aspx

Pasar de XMLDocument a BigText y viceversa

Después de bastante tiempo utilizando mis propias funciones para hacer la conversión de BigText a XML y de XML a BigText, he descubierto que ya estaban hechas de forma estándar en NAV.  Para copiar y reutilizar estas funciones simplemente hay que editar la Codeunit 8701 – Mobile Document Management:

Como consuelo me queda el ver que lo que yo había pensado se parece bastante a lo que habían pensado los MS Redmond Guys.

Cómo crear y colocar charts en tus pages

Con la llegada de la versión de roles de NAV, tenemos a nuestra disposición la posibilidad de incluir gráficos a las pages que vayamos creando. La principal duda que se nos plantea a la hora de hacer nuestro primer chart es saber cómo se crean.

En Administración > Configuración de la aplicación > Cliente adaptado a roles > Gráficos hay un formulario preparado para crear nuevos charts:

Desde esta pantalla podemos crear nuevos charts, pero vemos que no hay una forma fácil de diseñarlos. Únicamente disponemos de una opción para importar su estructura definida en un fichero XML. Hay varias páginas y artículos que hablan sobre la estructura que tienen que tener estos ficheros, pero creo que lo mejor es utilizar la herramienta Chart Generation Tool, crear un chart sencillo y después ver el XML que se genera.

Esta herramienta está disponible para su descarga en:
http://blogs.msdn.com/b/nav/archive/2011/06/03/chart-generator-tool-for-rtc-cgtrtc.aspx

Su uso es muy sencillo. Una vez importados los objetos, buscaremos el formulario 72004 – Chart Generator Card. En este caso vamos a suponer que queremos mostrar un gráfico en el listado de clientes que muestre el número de clientes activos que hay en cada país:

Lo primero que vamos a hacer es rellenar una nueva ficha con los siguientes datos: 

En la pantalla de filtros indicaremos que únicamente queremos mostrar los clientes que no tienen ningún tipo de bloqueo:

Para terminar sólo nos queda indicar qué cálculo vamos a mostrar en el eje de las Y. NAV nos permite incluir más de una columna para contabilizar más de un tipo de dato. Una vez hecho esto, desde la ficha procederemos a crear el chart, que directamente será añadido a la pantalla de gráficos que hemos comentado al principio del post:

Ahora únicamente nos queda decidir en qué pagina vamos a mostrar el gráfico. Para ello buscamos la page 22 – Customer List, la editamos y anidamos dentro del contenedor principal una part de tipo chart. En sus propiedades indicaremos a que gráfico nos estamos refiriendo:

Al ejecutar nuestra página, el resultado no puede ser más vistoso:

Campos no editables dinámicamente

Cuando trabajamos con formularios, la forma de indicar si un campo es editable o no de forma dinámica se hace a través del cambio de propiedad editable. Más o menos sería algo así:

CurrForm.MiCampo.EDITABLE(TRUE);

En el caso de las pages es diferente, ya que la forma de acceder a las propiedades de un control ha cambiado. Ahora lo que hacemos es indicar que una propiedad va a seguir un valor estático o un valor dinámico (a través de una variable).

Como podemos ver en la imagen, pasa lo mismo con la propiedad visible o enabled.

NOTA: Recordad, que las variables que utilizamos en la página tienen que tener la propiedad IncludeInDataSet con el valor Yes.