Para saber si un parámetro (Por ejemplo un código de almacén) de un dataset esta seleccionado en la lista de Almacenes de nuestro parámetro multiValue utilizaremos la función Array.IndexOf.
La manera serí así:
Array.IndexOf(Parameters!LISTA_DE_ALMACENES.Value, Fields!CODIGO_DE_ALMACEN.Value)
Devuelve -1 si no esta dentro de la lista.
Con esto, por ejemplo podemos ocultar o mostrar una casilla en función de si han seleccionado en los parámetros el valor de nuestro resultado:
Hidden
IIF(Array.IndexOf(Parameters!DESGLOSEALMACENES.Value, Fields!Almacen.Value) > -1,false,true)
lunes, 13 de febrero de 2017
sábado, 24 de diciembre de 2016
Error al exportar Excel desde Navision
Cuando pinchas en el icono de exportar a Excel, si no tienes la librería de Microsoft msxm.dll para parsear XML da el siguiente error
Aunque tengas instalado el office, es posible que no tengas estos componentes que permiten la integración de Excel.
Lo único que hay que hacer es instalar las siguientes librerías:
domingo, 17 de julio de 2016
Sumar en Reporting Services solamente un valor por grupo
Cuando tenemos una consulta que devuelve varios registros por grupo donde tenemos unos datos en el grupo y no en el detalle, tenemos el problema que al mostrarlo en reporting y hacer una suma, nos lo va a sumar tantas veces como detalle tenga y no una vez por grupo.
Se puede solucionar haciendo una sub-consulta pero aquí vamos a explicar como mostrarlo en una única consulta.
Por ejemplo, tenemos la siguiente estructura:
Lo primero, es agregar una columna que cuente el número de registro dentro del grupo con la siguiente línea:
ROW_NUMBER() OVER (PARTITION BY Grupo ORDER BY Hijo ASC) as Fila
De esta manera sabemos cual es la línea dentro del grupo:
Ahora desde reporting es muy sencillo filtrar los datos solamente para la fila 1 de cada grupo.
Por ejemplo, podemos ocultar la línea de detalle primera y mostrarla directamente en la cabecera poniéndole la siguiente expresión de visibilidad en las líneas de detalle:
Para sumar, le agregamos la condición de que solo sume la línea 1 de cada grupo. ojo, para que funcione debéis transformar los valores, bien a int, adecimal o a lo que necesite para que no de error:
=Sum(IIF(Fields!Fila.Value = 1,CDbl(Fields!Horas_Previstas.Value),CDbl(0)))
Se puede solucionar haciendo una sub-consulta pero aquí vamos a explicar como mostrarlo en una única consulta.
Por ejemplo, tenemos la siguiente estructura:
Lo primero, es agregar una columna que cuente el número de registro dentro del grupo con la siguiente línea:
ROW_NUMBER() OVER (PARTITION BY Grupo ORDER BY Hijo ASC) as Fila
De esta manera sabemos cual es la línea dentro del grupo:
Ahora desde reporting es muy sencillo filtrar los datos solamente para la fila 1 de cada grupo.
Por ejemplo, podemos ocultar la línea de detalle primera y mostrarla directamente en la cabecera poniéndole la siguiente expresión de visibilidad en las líneas de detalle:
Para sumar, le agregamos la condición de que solo sume la línea 1 de cada grupo. ojo, para que funcione debéis transformar los valores, bien a int, adecimal o a lo que necesite para que no de error:
=Sum(IIF(Fields!Fila.Value = 1,CDbl(Fields!Horas_Previstas.Value),CDbl(0)))
sábado, 16 de abril de 2016
Merge filas en Reporting Services
El título de esta entrada es engañoso. Realmente no vamos a ver como se combinan celdas de varias filas, lo que vamos a ver es como simularlo para que quede algo así:
Lo primero que hay que hacer es cambiar la propiedad de la celda HideDuplicate y ponerle el ámbito del DataSet.
Esto lo que hará es que no pintará los valores repetidos, pero las líneas de separación siguen apareciendo. Ahora solamente tenemos que ocultar las líneas intermedias para simular una única casilla.
Para esto, nos situamos sobre la celda y ocultamos la línea inferior (None). La línea superior la mostramos supeditada a una función que haremos en la sección de código.
La expresión para el Top es:
=Code.FGrupoSexo(Fields!sexo.Value)
Ahora en Informe - Propiedades del Informe declaramos una variable para controlar el valor del campo sexo en el que estamos. Creamos una función sencilla en vb que pasándole el valor del campo sexo actual lo compara con el anterior y devuelve Solid o None si ha cambiado.
Para finalizalizar, creamos una línea fuera del grupo, únicamente para pintar la línea inferior del último registro de la tabla ya que esta no lo pintará nuestro código personalizado al ser la última
Lo primero que hay que hacer es cambiar la propiedad de la celda HideDuplicate y ponerle el ámbito del DataSet.
Esto lo que hará es que no pintará los valores repetidos, pero las líneas de separación siguen apareciendo. Ahora solamente tenemos que ocultar las líneas intermedias para simular una única casilla.
Para esto, nos situamos sobre la celda y ocultamos la línea inferior (None). La línea superior la mostramos supeditada a una función que haremos en la sección de código.
La expresión para el Top es:
=Code.FGrupoSexo(Fields!sexo.Value)
Ahora en Informe - Propiedades del Informe declaramos una variable para controlar el valor del campo sexo en el que estamos. Creamos una función sencilla en vb que pasándole el valor del campo sexo actual lo compara con el anterior y devuelve Solid o None si ha cambiado.
Para finalizalizar, creamos una línea fuera del grupo, únicamente para pintar la línea inferior del último registro de la tabla ya que esta no lo pintará nuestro código personalizado al ser la última
martes, 16 de febrero de 2016
Gráficos en reporting con las etiquetas en vertical
Para que las etiquetas de un gráfico salgan en vertical. Seleccionamos las etiquetas. Botón de la derecha propiedades del eje horizontal y seleccionamos la opción etiquetas:
Deshabilitamos el eje automático y le ponemos los grados de inclinación para nuestras etiquetas.
Deshabilitamos el eje automático y le ponemos los grados de inclinación para nuestras etiquetas.
miércoles, 25 de noviembre de 2015
Desactivar el Zoom en un formulario
Cuando creamos un formulario elegimos que campos son los que el usuario ve en pantalla. Mediante el Ctrl+F8 puede hacer un zoom y ver la totalidad de los campos del registro.
Si nos encontramos en la situación de que tenemos un campo cuyo valor no queremos que el usuario pueda ver, no disponemos de ningún método a priori sencillo en el formulario para evitarlo.
Se pude desactivar para un usuario concreto el Zoom con el rol object - 5330 Tools, Zoom. Pero esto lo desactiva en todas las pantallas y queremos desactivarla en una pantalla concreta.
Para estos casos, lo que se puede hacer es que en la tabla que contiene el campo con la información sensible se añade en el CaptionClass lo siguiente: '99999,2000000002'
Luego se va a la CodeUnit1 y en la función CaptionClassTranslate se añade lo siguiente:
Una opción nueva donde el CASE es el número del captionclass (En este caso el 99999):
CommaPosition := STRPOS(CaptionExpr,',');
IF (CommaPosition > 0) THEN BEGIN
CaptionArea := COPYSTR(CaptionExpr,1,CommaPosition - 1);
CaptionRef := COPYSTR(CaptionExpr,CommaPosition + 1);
CASE CaptionArea OF
'1' : EXIT(DimCaptionClassTranslate(Language,CaptionRef));
'2' : EXIT(VATCaptionClassTranslate(Language,CaptionRef));
'99999' : ERROR('');//<---------Desactivo el ZOOM
END;
END;
EXIT('');
Si nos encontramos en la situación de que tenemos un campo cuyo valor no queremos que el usuario pueda ver, no disponemos de ningún método a priori sencillo en el formulario para evitarlo.
Se pude desactivar para un usuario concreto el Zoom con el rol object - 5330 Tools, Zoom. Pero esto lo desactiva en todas las pantallas y queremos desactivarla en una pantalla concreta.
Para estos casos, lo que se puede hacer es que en la tabla que contiene el campo con la información sensible se añade en el CaptionClass lo siguiente: '99999,2000000002'
Luego se va a la CodeUnit1 y en la función CaptionClassTranslate se añade lo siguiente:
Una opción nueva donde el CASE es el número del captionclass (En este caso el 99999):
CommaPosition := STRPOS(CaptionExpr,',');
IF (CommaPosition > 0) THEN BEGIN
CaptionArea := COPYSTR(CaptionExpr,1,CommaPosition - 1);
CaptionRef := COPYSTR(CaptionExpr,CommaPosition + 1);
CASE CaptionArea OF
'1' : EXIT(DimCaptionClassTranslate(Language,CaptionRef));
'2' : EXIT(VATCaptionClassTranslate(Language,CaptionRef));
'99999' : ERROR('');//<---------Desactivo el ZOOM
END;
END;
EXIT('');
martes, 25 de agosto de 2015
Renderizar Informes desde NAvision mediante XMLHTTP sin cache
Para llamar a un informe de Reporting (O cualquier página web) utilizamos el Automation XMLHTTP.
Capturamos la respuesta y generamos un pdf (En el caso de Reporting). El problema viene que XMLHTTP guarda en cache la petición por lo que puede darse el caso que no renderice el informe con los últimos datos.
Para forzarlo añadimos en la cabecera la opción Cache-Control:no-cache.
Quedaría así:
CREATE(XMLHTTP);
XMLHTTP.open('GET','Mi_Ruta_Informe_Reporting',FALSE);
XMLHTTP.setRequestHeader('Content-Type','application/pdf');
XMLHTTP.setRequestHeader('Cache-Control', 'no-cache');
XMLHTTP.send();
IF (XMLHTTP.status = 200) THEN BEGIN CLEAR(vRespuesta);
vRespuesta := XMLHTTP.responseStream;
isArchivoRecibido := vRespuesta;
F.CREATE('c:\mifichero.pdf');
F.CREATEOUTSTREAM(osEscribeArchivoRecibido);
COPYSTREAM(osEscribeArchivoRecibido,isArchivoRecibido);
F.CLOSE;
END;
CLEAR(XMLHTTP);
Capturamos la respuesta y generamos un pdf (En el caso de Reporting). El problema viene que XMLHTTP guarda en cache la petición por lo que puede darse el caso que no renderice el informe con los últimos datos.
Para forzarlo añadimos en la cabecera la opción Cache-Control:no-cache.
Quedaría así:
| Name | DataType | Subtype |
| XMLHTTP | Automation | 'Microsoft XML, v3.0'.XMLHTTP |
| isArchivoRecibido | InStream | |
| osEscribeArchivoRecibido | OutStream | |
| vRespuesta | Variant |
CREATE(XMLHTTP);
XMLHTTP.open('GET','Mi_Ruta_Informe_Reporting',FALSE);
XMLHTTP.setRequestHeader('Content-Type','application/pdf');
XMLHTTP.setRequestHeader('Cache-Control', 'no-cache');
XMLHTTP.send();
IF (XMLHTTP.status = 200) THEN BEGIN CLEAR(vRespuesta);
vRespuesta := XMLHTTP.responseStream;
isArchivoRecibido := vRespuesta;
F.CREATE('c:\mifichero.pdf');
F.CREATEOUTSTREAM(osEscribeArchivoRecibido);
COPYSTREAM(osEscribeArchivoRecibido,isArchivoRecibido);
F.CLOSE;
END;
CLEAR(XMLHTTP);
martes, 14 de abril de 2015
Quitar tabulaciones, intros y demás de las casillas de texto en Navision
Cuando copiamos una casilla de excell que contiene un valor para pegarla en una casilla de Navision nos añade el retorno de carro que debemos quitar a mano (Si nos damos cuenta).
Se puede poner un pequeño código para que estos caracteres los elimine de manera automática. PAra eso hay que hacer:
CodeUnit 1 dentro de la función MakeText poner nada más comenzar lo siguiente:
myChr = Char;
//Eliminados caracteres raros del Excel en todas las ediciones.
myChr:=13; //CR
Text := DELCHR(Text,'=',FORMAT(myChr));
myChr:=10; //LF
Text := DELCHR(Text,'=',FORMAT(myChr));
myChr:=9; //TAB
Text := CONVERTSTR(Text,FORMAT(myChr),' ');
myChr:=255; //???
Text := CONVERTSTR(Text,FORMAT(myChr),' ');
Se puede poner un pequeño código para que estos caracteres los elimine de manera automática. PAra eso hay que hacer:
CodeUnit 1 dentro de la función MakeText poner nada más comenzar lo siguiente:
myChr = Char;
//Eliminados caracteres raros del Excel en todas las ediciones.
myChr:=13; //CR
Text := DELCHR(Text,'=',FORMAT(myChr));
myChr:=10; //LF
Text := DELCHR(Text,'=',FORMAT(myChr));
myChr:=9; //TAB
Text := CONVERTSTR(Text,FORMAT(myChr),' ');
myChr:=255; //???
Text := CONVERTSTR(Text,FORMAT(myChr),' ');
jueves, 19 de febrero de 2015
Selector de Carpetas en Navision
Este post lo he copiado de mibuso pero tenia que ponerlo aquí para que veáis que sencillo es:
Name DataType Subtype Length
ShellControl Automation 'Microsoft Shell Controls And Automation'.Shell
Folder Automation 'Microsoft Shell Controls And Automation'.Folder3
FolderItems Automation 'Microsoft Shell Controls And Automation'.FolderItems3
FolderItem Automation 'Microsoft Shell Controls And Automation'.FolderItem2
IF ISCLEAR(ShellControl) THEN
CREATE(ShellControl);
Folder := ShellControl.BrowseForFolder(0,'',0);
IF NOT ISCLEAR( Folder ) THEN BEGIN
FolderItems := Folder.Items();
FolderItem := FolderItems.Item;
EXIT(FORMAT(FolderItem.Path));
END;
CLEAR( ShellControl );
CLEAR( Folder );
CLEAR( FolderItems );
CLEAR( FolderItem );
EXIT('');
Name DataType Subtype Length
ShellControl Automation 'Microsoft Shell Controls And Automation'.Shell
Folder Automation 'Microsoft Shell Controls And Automation'.Folder3
FolderItems Automation 'Microsoft Shell Controls And Automation'.FolderItems3
FolderItem Automation 'Microsoft Shell Controls And Automation'.FolderItem2
IF ISCLEAR(ShellControl) THEN
CREATE(ShellControl);
Folder := ShellControl.BrowseForFolder(0,'',0);
IF NOT ISCLEAR( Folder ) THEN BEGIN
FolderItems := Folder.Items();
FolderItem := FolderItems.Item;
EXIT(FORMAT(FolderItem.Path));
END;
CLEAR( ShellControl );
CLEAR( Folder );
CLEAR( FolderItems );
CLEAR( FolderItem );
EXIT('');
miércoles, 18 de febrero de 2015
Manipular Ficheros físicos mediante Web Services
No se si alguna vez os ha surgido la necesidad de copiar o mover ficheros desde el cliente por roles o a través de Web Services.
Desde el cliente clásico funciona sin problemas, pero a la hora de hacerlo funcionar por Web Services surge el quebradero de cabeza.
Hay por ahí teorías que dan una posible solución que tiene que ver con que las funciones COPY,EXISTS,ERASE,etc.... no funcionan en la capa de WS.
Pero este no es el problema, el problema es que tenemos tres niveles en tres equipos. Es decir: que en nuestra arquitectura tenemos el cliente del Web Service (Capa 1), El Web Service en el servidor (Capa 2) y el servidor donde están los ficheros (Capa 3).
No se si habréis podido comprobar que si los ficheros a manipular están en el mismo servidor que donde tenemos instalada la capa de Web Service el problema desaparece. es más, es una de las recomendaciones que hace Microsoft para solucionar el problema.
La solución correcta consiste en Delegar servicios. Como ya he comentado, Microsoft recomienda que los archivos estén en el mismo servidor que la capa de Web Service para ahorrarte la enrevesada configuración de la Delegación.
Desde el cliente clásico funciona sin problemas, pero a la hora de hacerlo funcionar por Web Services surge el quebradero de cabeza.
Hay por ahí teorías que dan una posible solución que tiene que ver con que las funciones COPY,EXISTS,ERASE,etc.... no funcionan en la capa de WS.
Pero este no es el problema, el problema es que tenemos tres niveles en tres equipos. Es decir: que en nuestra arquitectura tenemos el cliente del Web Service (Capa 1), El Web Service en el servidor (Capa 2) y el servidor donde están los ficheros (Capa 3).
No se si habréis podido comprobar que si los ficheros a manipular están en el mismo servidor que donde tenemos instalada la capa de Web Service el problema desaparece. es más, es una de las recomendaciones que hace Microsoft para solucionar el problema.
La solución correcta consiste en Delegar servicios. Como ya he comentado, Microsoft recomienda que los archivos estén en el mismo servidor que la capa de Web Service para ahorrarte la enrevesada configuración de la Delegación.
miércoles, 17 de diciembre de 2014
Llamar a Web-Services de Navision desde Navision
PAra consumir los webservices de un servidor de Navision desde otro servidor de Navision tenemos que montar una petición httpRequest pasándole un XML bien formado y recuperar la respuesta para saber que es lo que ha ocurrido en el servidor de destino.
Para ello primero declaramos las variables:
| Name | DataType | Subtype | Length |
| XmlHttpRequest | Automation | 'Microsoft XML, v4.0'.XMLHTTP | |
| XmlDoc | Automation | 'Microsoft XML, v4.0'.DOMDocument | |
| XmlString | Text | 1024 | |
| BTSoap | BigText | ||
| BTValores | BigText |
Creamos un BigText y le añadimos la cabecera de la petición:
BTPasado.ADDTEXT('<?xml version="1.0" encoding="utf-8"?>');
BTPasado.ADDTEXT('<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">');
BTPasado.ADDTEXT('<soap:Body>');
BTPasado.ADDTEXT(STRSUBSTNO('<%1 xmlns="urn:microsoft-dynamics-schemas/codeunit/%2">','MiFuncion','MiCodeUnit'));
BTValores.ADDTEXT('<nombre_parametro_que_espera_MiCodeunit_MiFuncion>');
BTValores.ADDTEXT('MI-VALOR');
BTValores.ADDTEXT('</nombre_parametro_que_espera_MiCodeunit_MiFuncion>');
BTPasado.ADDTEXT(STRSUBSTNO('</%1>','MiFuncion'));
BTPasado.ADDTEXT('</soap:Body>');
BTPasado.ADDTEXT('</soap:Envelope>');
Como veis es bien sencillo montar la petición de nuestro httprequest.
Ahora hacemos la llamada:
| Name | DataType | Subtype |
| XmlDocPasado Automation | 'Microsoft XML, v4.0'.DOMDocument |
IF (ISCLEAR(XmlHttpRequest)) THEN
CREATE(XmlHttpRequest);
XmlHttpRequest.open('POST','http://servidor:7047/DynamicsNAV/WS/empresa/Codeunit/MiCodeunit',FALSE,'DOMINIO\usuario','password');
XmlHttpRequest.setRequestHeader('Host', 'localhost');
XmlHttpRequest.setRequestHeader('Content-Type', 'text/xml; charset=utf-8');
XmlHttpRequest.setRequestHeader('Content-Length', FORMAT(BTPasado.LENGTH));
XmlHttpRequest.setRequestHeader('SOAPAction','MiFuncion');
XmlHttpRequest.send(BTPasado);
XmlDocPasado := XmlHttpRequest.responseXML;
Con esto obtendríamos en XmlDocPasado un XML con el resultado.
viernes, 28 de noviembre de 2014
Convertir líneas a columna en una consulta sql
Se puede pasar un resultado de una consulta sql de líneas a un varchar que representaría una columna con la función: COALESCE siempre que no contenga ningún null el resultado de la consulta.
Ejemplo:
DECLARE @valores VARCHAR(1000)
SELECT @valores= COALESCE(@valores + ',', '') + valor FROM MiConsulta-Tabla
Valor es uno de los campos de MiConsulta-Tabla.
El resultado es un varchar concatenado por comas.
Tenemos la limitación de la longitud del varchar (Aunque lo definamos con MAX) pero nos puede ser útil para salir del paso.
Ejemplo:
DECLARE @valores VARCHAR(1000)
SELECT @valores= COALESCE(@valores + ',', '') + valor FROM MiConsulta-Tabla
Valor es uno de los campos de MiConsulta-Tabla.
El resultado es un varchar concatenado por comas.
Tenemos la limitación de la longitud del varchar (Aunque lo definamos con MAX) pero nos puede ser útil para salir del paso.
viernes, 21 de noviembre de 2014
Crear un informe Recursivo con Reporting Services
Crear un informe recursivo es extremadamente fácil con Reporting Services.
Supongamos un listado que muestra los siguientes datos:
Y ya esta, ya tenemos nuestro informe recursivo.
Para que visualmente quede mas bonito se cambia el padding del campo para que vaya indentando las líneas y quede así:
También se puede cambiar la visualización del grupo para poder plegarlo o desplegarlo:
Supongamos un listado que muestra los siguientes datos:
- Primera Línea Padre
- Primera Línea Hija Primera Línea Padre
- Segunda Línea Hija Primera Línea Padre
- Primera Línea HijaSegunda Línea Hija Primera Línea Padre
- Tercera Línea Hija Primera Línea Padre
- Segunda Línea Padre
- Tercera Línea Padre
- ....
En el grupo ponemos como campo de grupo el ID que identifica la línea.
En propiedades avanzadas indicamos cual es el campo recursivo de cada línea, es decir, el campo que identifica cual es su padre.Y ya esta, ya tenemos nuestro informe recursivo.
Para que visualmente quede mas bonito se cambia el padding del campo para que vaya indentando las líneas y quede así:
También se puede cambiar la visualización del grupo para poder plegarlo o desplegarlo:
jueves, 10 de abril de 2014
Obtener la lista de informes cuanto cuestan de sacar en tiempo
Esta consulta muestra una lista de los informes que mas cuestan de sacar por tiempo. Con esto podemos determinar que informes son los más pesados e intentar optimizarlos.
SELECT EL.UserNAme,Name Informe, b.path Ruta, Parameters Parametros,
FORMAT Formato,TimeStart Inicio, TimeEnd Fin, ByteCount,
(TimedataRetrieval+Timeprocessing+TimeRendering)/1000 AS Seg_TiempoTotal
FROM
[ReportServer].[dbo].[ExecutionLog] EL
JOIN [ReportServer].[dbo].[CATALOG] b
ON EL.reportid = b.itemid
--WHERE Name =@INFORME
ORDER BY (TimedataRetrieval+Timeprocessing+TimeRendering)/1000 DESC
SELECT EL.UserNAme,Name Informe, b.path Ruta, Parameters Parametros,
FORMAT Formato,TimeStart Inicio, TimeEnd Fin, ByteCount,
(TimedataRetrieval+Timeprocessing+TimeRendering)/1000 AS Seg_TiempoTotal
FROM
[ReportServer].[dbo].[ExecutionLog] EL
JOIN [ReportServer].[dbo].[CATALOG] b
ON EL.reportid = b.itemid
--WHERE Name =@INFORME
ORDER BY (TimedataRetrieval+Timeprocessing+TimeRendering)/1000 DESC
jueves, 3 de abril de 2014
Leer active directory desde SQL
Para acceder desde SQL al LDAP hay que linkar nuestro servidor del Active directory con el servidor de SQL donde queremos hacer la consulta.
La lista de campos disponibles desde el LDAP la podeis encontrar en el siguiente link: lista de campos
Lo primero es linkar el servidor de la siguiente manera_
Exec
sp_addlinkedserver 'ADSI'
, 'Active Directory Service Interfaces'
, 'ADSDSOObject'
, 'adsdatasource'
Le ponemos un usuario del dominio para que tenga acceso, bien con la instrucción:
EXEC sp_addlinkedsrvlogin 'ADSI', 'false', NULL, 'UsuarioDominio'
O desde las propiedades de la conexión creada (Servidores Vinculados)
En caso de que tengas el "Ad Hoc Distributed Queries" desabilitado, entonces corre esto para habilitarlo:
sp_configure 'show advanced options'
, 1
reconfigure
go
sp_configure 'Ad Hoc Distributed Queries'
, 1
reconfigure
go
Para ejcutar las consultas, se hace utilizando OpenQuery y de la siguiente manera:
SELECT * FROM OpenQuery(ADSI, 'SELECT physicalDeliveryOfficeName,sAMAccountName,cn,Name,displayName, sn,givenName,initials,moniker, personalTitle,generationQualifier ,userPrincipalName ,distinguishedName ,adsPath FROM ''LDAP://DC=mi-dominio,DC=local'' WHERE objectCategory=''user'' ')
La lista de campos disponibles desde el LDAP la podeis encontrar en el siguiente link: lista de campos
Lo primero es linkar el servidor de la siguiente manera_
Exec
sp_addlinkedserver 'ADSI'
, 'Active Directory Service Interfaces'
, 'ADSDSOObject'
, 'adsdatasource'
Le ponemos un usuario del dominio para que tenga acceso, bien con la instrucción:
EXEC sp_addlinkedsrvlogin 'ADSI', 'false', NULL, 'UsuarioDominio'
O desde las propiedades de la conexión creada (Servidores Vinculados)
En caso de que tengas el "Ad Hoc Distributed Queries" desabilitado, entonces corre esto para habilitarlo:
sp_configure 'show advanced options'
, 1
reconfigure
go
sp_configure 'Ad Hoc Distributed Queries'
, 1
reconfigure
go
Para ejcutar las consultas, se hace utilizando OpenQuery y de la siguiente manera:
SELECT * FROM OpenQuery(ADSI, 'SELECT physicalDeliveryOfficeName,sAMAccountName,cn,Name,displayName, sn,givenName,initials,moniker, personalTitle,generationQualifier ,userPrincipalName ,distinguishedName ,adsPath FROM ''LDAP://DC=mi-dominio,DC=local'' WHERE objectCategory=''user'' ')
jueves, 27 de marzo de 2014
Imagenes externas en Reporting Services
En una red corporativa, podemos definir en los informes de reporting las imágenes como externas y darles la ruta donde se encuentran para que el servidor de informes las renderice, se hace de la siguiente manera:
file://datos/ImagenesProductos/Historico/imagen.jpg
file://datos/ImagenesProductos/Historico/imagen.jpg
jueves, 20 de marzo de 2014
Split en Reporting Services
Para utilizar la función de Split en Reporting Services y combinar así nuestra función de Web Services con Reportin Services, es muy útil la función Split de Reporting Services cuya definicón es la siguiente:
CDec(Split(Fields!RESULTADO.Value,"|")(1))
CDec(Split(Fields!RESULTADO.Value,"|")(1))
jueves, 13 de marzo de 2014
Arreglo de la funcion pasar filtros de navision a SQL
Utilizando la función pasar filtros de navision a SQL he detectado algunas deficiencias.
Donde pone :
SET @From = substring(@parFilter, @IndexToRead + 2, @NextIndexToRead - (@IndexToRead + 1))
Poner :
if LEN(@parFilter) = @NextIndexToRead
BEGIN
SET @From = substring(@parFilter, @IndexToRead + 2, @NextIndexToRead - (@IndexToRead + 1))
END
ELSE
BEGIN
SET @From = substring(@parFilter, @IndexToRead + 2, @NextIndexToRead - (@IndexToRead + 2))
END
Donde pone :
SET @From = substring(@parFilter, @IndexToRead + 2, @NextIndexToRead - (@IndexToRead + 1))
Poner :
if LEN(@parFilter) = @NextIndexToRead
BEGIN
SET @From = substring(@parFilter, @IndexToRead + 2, @NextIndexToRead - (@IndexToRead + 1))
END
ELSE
BEGIN
SET @From = substring(@parFilter, @IndexToRead + 2, @NextIndexToRead - (@IndexToRead + 2))
END
jueves, 27 de febrero de 2014
Pasar BigText a XML en Navision
¡La de problemas que me he encontrado para trabajar con los BigText en Navision!.
Su manejo no es intuitivo y se necesita de un poco de conocimiento previo a la hora de utilizarlo.
En particular, mi necesidad de las variables del tipo BigText surgen en un proyecto en el que desde un servidor de navision consumia los web services de otro servidor de Navision. En futuros posts comentare los pormenores de este proyecto, pero el post actual es para comentar uno de los quebraderos de cabeza que mas amargado me ha tenido.
En un determinado momento y por necesidades varias, me veo en el plan de transformar un BigText a XML. Sencillo, ¿No? aqui por ejemplo hay una solucion para leer bigtext y pasarlo a XML.
Pues bien, así utilice mi variable BigText, poniendo FORMAT() para que me leyese el contenido y en principio me funciono estupendamente.
Pero no podia ser tan bonito, si leemos la definicion de la funcion format vemos que indica que es para variables de tipo simple ysi leemos la definicion del tipo de datos BIGTEXT vemos que es compleja. Por lo que en teoria no deberia de funcionar, pero funciona!!!.
ERROR!!!!!!!!
No funciona!!!!!!!!!!
Bueno, para ser correctos, funciona cuando el BigText no contiene muchos caracteres, el error que da es el de Excedido el tamaño del bufer.
Por lo que la única solución correcta es la de utilizar InStream y OutStream.
Por lo que en realidad se deberia hacer así:
XmlDocPasado.async := FALSE;
TempBlob.HTML.CREATEOUTSTREAM(ostream);
BTPasado.WRITE(ostream);
TempBlob.CALCFIELDS(HTML);
TempBlob.HTML.CREATEINSTREAM(istream);
XmlDocPasado.load(istream);
Ocurre exactamento lo mismo con la variable Automation 'Navision Attain Hash 1.0'.BSTRConverter y su metodo BSTR. PArece que con este metodo nos va a devolver un bonito chorro de carácteres para pasar a nuesto XML y idem de idem, Excedido el tamaño del bufer.
Su manejo no es intuitivo y se necesita de un poco de conocimiento previo a la hora de utilizarlo.
En particular, mi necesidad de las variables del tipo BigText surgen en un proyecto en el que desde un servidor de navision consumia los web services de otro servidor de Navision. En futuros posts comentare los pormenores de este proyecto, pero el post actual es para comentar uno de los quebraderos de cabeza que mas amargado me ha tenido.
En un determinado momento y por necesidades varias, me veo en el plan de transformar un BigText a XML. Sencillo, ¿No? aqui por ejemplo hay una solucion para leer bigtext y pasarlo a XML.
Pues bien, así utilice mi variable BigText, poniendo FORMAT() para que me leyese el contenido y en principio me funciono estupendamente.
Pero no podia ser tan bonito, si leemos la definicion de la funcion format vemos que indica que es para variables de tipo simple ysi leemos la definicion del tipo de datos BIGTEXT vemos que es compleja. Por lo que en teoria no deberia de funcionar, pero funciona!!!.
ERROR!!!!!!!!
No funciona!!!!!!!!!!
Bueno, para ser correctos, funciona cuando el BigText no contiene muchos caracteres, el error que da es el de Excedido el tamaño del bufer.
Por lo que la única solución correcta es la de utilizar InStream y OutStream.
Por lo que en realidad se deberia hacer así:
XmlDocPasado.async := FALSE;
TempBlob.HTML.CREATEOUTSTREAM(ostream);
BTPasado.WRITE(ostream);
TempBlob.CALCFIELDS(HTML);
TempBlob.HTML.CREATEINSTREAM(istream);
XmlDocPasado.load(istream);
Ocurre exactamento lo mismo con la variable Automation 'Navision Attain Hash 1.0'.BSTRConverter y su metodo BSTR. PArece que con este metodo nos va a devolver un bonito chorro de carácteres para pasar a nuesto XML y idem de idem, Excedido el tamaño del bufer.
jueves, 20 de febrero de 2014
Saber si una columna concreta existe en una tabla
Estoy trabajando en un proyecto que pretende ser adaptable a varias bases de datos. En el, surge la necesidad de saber en una consulta de SQL si un campo existe en una tabla concreta o no existe.La consulta para averiguarlo es así: SELECTCASE WHEN (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = 'Empresa$Tabla' AND
COLUMN_NAME
= 'Columna') = 0 THEN 'NO TIENE COLUMNA Columna' ELSE 'SI TIENE COLUMNA Columna' END
Para averiguar si la Tabla existe sería así:
SELECTCASE WHEN (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_NAME = 'Empresa$Tabla') = 0 THEN 'NO TIENE TABLA Empresa$Tabla' ELSE 'SI TIENE TABLA Empresa$Tabla' END
WHERE
TABLE_NAME = 'Empresa$Tabla' AND
COLUMN_NAME
= 'Columna') = 0 THEN 'NO TIENE COLUMNA Columna' ELSE 'SI TIENE COLUMNA Columna' END
Para averiguar si la Tabla existe sería así:
SELECTCASE WHEN (SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_NAME = 'Empresa$Tabla') = 0 THEN 'NO TIENE TABLA Empresa$Tabla' ELSE 'SI TIENE TABLA Empresa$Tabla' END
Suscribirse a:
Entradas (Atom)















