XML desde cero: procesamiento con Access

XmlAccess

Si usas Access para desarrollar aplicaciones de gestión en España y aún no lo has hecho, tienes que ir poniéndote al día en el manejo de archivos en formato XML. Tarde o temprano te encontrarás con la norma SEPA, obligatoria desde el año pasado en los ficheros de intercambio bancario, o con el formato Facturae, obligatorio desde mayo de este año para las empresas que quieran facturar a las administraciones públicas.

El próximo jueves 18 de junio, a las 21:00, hora de España, en el Grupo Virtual de Usuarios Access España, Emilio Verástegui nos explicará, empezando desde lo más básico, algunos aspectos referentes al trabajo con XML. Responderá a preguntas tales como qué es XML, qué características lo definen, qué reglas se aplican, etc., para pasar posteriormente a descubrirnos de qué manera puede generarse un XML desde Access, utilizando para ello diferentes sistemáticas de trabajo: Print, ADO y DOM.

Emilio ya es un conocido del grupo, con interesantísimas intervenciones sobre Web Services (SOAP y REST). Es un entusiasta (realmente entusiasta) de Access, colaborador en diferentes foros de Access y miembro activo en Access User Groups Latinoamérica.

================= DETALLES  DE LA CONEXIÓN ===============================

Nos reunimos el tercer jueves de cada mes a las 9:00 pm CEST.

Nuestra reunión será usando el siguiente enlace:

https://meetings.webex.com/collabs/meetings/join?uuid=M5O4DBOS1QSIMYKRAL7Z61BTW6-OXTF

Puedes usar cualquier navegador para ver y escuchar la reunión.

Para escuchar la charla vía teléfono: Marca el número de teléfono:

United States +1-415-655-0001 US TOLL
España              Barcelona +34.93.393.4037               Madrid +34.91.201.2149

Access Code 194 689 173

Números internacionales disponibles

Publicado por Chea con no comments

Descubra el poder de sus datos. Seminario/Kedada Access

 

MS Access

 

Cuando se va a cumplir un año de la primera reunión del Grupo Virtual de Usuarios Access España,  la reunión virtual del mes de mayo no tratará sobre Access sino sobre kedada, viaje, comidas, restaurantes, bares, hoteles, pues la vamos a aprovechar para quedar para el evento Descubra el poder de sus datos que tendrá lugar el viernes de la siguiente semana, el 29 de mayo, en la sede de Microsoft España.

Tenemos preparado un seminario en la sede de Microsoft en el que se tratarán, en vivo y en directo, varios temas muy interesantes acerca de Access y al que esperamos que asista mucha gente. Pero también, aprovechando que bastante gente se desplazará desde fuera de Madrid, queremos enlazar el seminario con una kedada para saludarnos y/o conocernos en persona muchos de los que ya nos conocemos desde hace años a través de los distintos foros sobre Access que hay en la Web.

Sigue los enlaces para saber de qué va a tratar el seminario y para inscribirte en el evento ¡las plazas son limitadas!

Para quedar para vernos y saludarnos antes y después del seminario no hace falta registro, pero sí conviene que asistas a la reunión virtual del tercer jueves del mes de mayo para ponernos de acuerdo.

Si conoces el Grupo Virtual de Usuarios Access España, sabrás que es un grupo completamente abierto en el que participan miembros de los foros sobre Access mas conocidos en español, entre ellos los autores de varias de las páginas sobre Access en español con mas seguidores. Todos hemos sido novatos y todos hemos aprendido mucho de nuestros compañeros a través de internet, de manera que todo el mundo es bien recibido con independencia de su nivel en Access, basta con que sea un entusiasta.

¡Anímate a venir a Madrid el 29 de mayo, te esperamos!

 

 

Publicado por Chea con no comments

Access y MySQL con recorsets desconectados

RecordsetDesconectado

 

El próximo jueves 16/04/15 a las 21:00, Carlos González (Guarracuco) nos hablará de lo que son recordsets desconectados y cómo utilizarlos a través de una conexión en web con MySql. Nos ofrecerá diversas sistemáticas de trabajo con varios ejemplos basándose en recordsets desconectados, y culminará la exposición con una aplicación impactante: un chat en tiempo real.

Para poder operar con el ejemplo de chat os podéis descargar este Access (https://www.tucondominioaldia.net/archivos/AccessMysql1.zip). Tened en cuenta que es necesario tener instalado el driver de MySql (da igual la versión). Si no lo tenéis os lo podéis descargar aquí:http://dev.mysql.com/get/Downloads/Connector-ODBC/5.1/mysql-connector-odbc-5.1.13-win32.msi

Carlos es Técnico Superior Universitario en Construcción Civil con 15 años de experiencia en obras civiles de ingeniería. Su pasión por el mundo de la informática se plasma en una amplia labor en desarrollo web, con el manejo de Php, MySql y Ajax, y desarrollo en Access, siendo el creador de una aplicación administrativa que actualmente comercializa y que aprovecha la combinación de ambas tecnologías.

Carlos es, además, moderador desde hace más de una década en el foro http://www.mvp-access.com/

================= DETALLES  DE LA CONEXIÓN ===============================

Nos reunimos el tercer jueves de cada mes a las 9:00 pm CEST.

Nuestra reunión será usando el siguiente enlace:

https://meetings.webex.com/collabs/meetings/join?uuid=M5O4DBOS1QSIMYKRAL7Z61BTW6-OXTF

Puedes usar cualquier navegador para ver y escuchar la reunión.

Para escuchar la charla vía teléfono: Marca el número de teléfono:

United States +1-415-655-0001 US TOLL
España              Barcelona +34.93.393.4037               Madrid +34.91.201.2149

Access Code 194 689 173

Números internacionales disponibles

Publicado por Chea con no comments

Cómo utilizar Google Calendar para "convertir" en Web un calendario creado en Ms Access.

GCalendarAccess

El próximo jueves, 19 de marzo, a las 21:00, hora de España, en la reunión mensual del grupo virtual de usuarios de Access, Luis Viadel nos hablará de cómo utilizar Google Calendar para "convertir" en Web un calendario creado en Ms Access.

http://accessusergroups.org/espana/como-utilizar-google-calendar-para-convertir-en-web-un-calendario-creado-con-ms-access/

La necesidad de poder consultar los calendarios desde cualquier sitio es algo que siempre ha acompañado a las aplicaciones de escritorio creadas con Microsoft Access. Mediante su API, podemos sincronizar Google calendar con nuestra aplicación de forma que siempre la tengamos disponible en la web. El objetivo de este webinar es conocer cómo conectar nuestra aplicación con un calendario de Google. Mediante un ejemplo práctico veremos las posibilidades que ofrecen ambas herramientas al trabajar juntas.

Luis Viadel es consultor de empresa especializado en organización y estrategia. Apasionado por la comunicación, los sistemas de información y la tecnología en las Pymes. Albañil de Access.

================= DETALLES  DE LA CONEXIÓN ===============================

Nos reunimos el tercer jueves de cada mes a las 9:00 pm CEST.

Nuestra reunión será usando el siguiente enlace:

https://meetings.webex.com/collabs/meetings/join?uuid=M5O4DBOS1QSIMYKRAL7Z61BTW6-OXTF

Puedes usar cualquier navegador para ver y escuchar la reunión.

Para escuchar la charla vía teléfono: Marca el número de teléfono:

United States +1-415-655-0001 US TOLL
España              Barcelona +34.93.393.4037               Madrid +34.91.201.2149

Access Code 194 689 173

Números internacionales disponibles

Publicado por Chea con no comments

Web Services con REST desde Access

access-ws

El próximo jueves 19 de febrero, a las 9:00 PM (Cest), Emilio Verástegui, continuará con su magnífica explicación sobre cómo trabajar con Web Services y Access, esta vez utilizando la tecnología REST y nos mostrará unos resultados realmente impresionantes.

Retomando lo que comentábamos para el SOAP podemos decir que los Web servicios son una herramienta para el intercambio de información entre aplicaciones a través de internet , los web servicios más comunes hoy en día son REST y SOAP. En esta sesión veremos REST, pues más del 70% de todos los Web Servicios lo utilizan, por lo que lo más probable es que tarde o temprano nos topemos con él.

REST significa estado de transferencia representacional y es usado por Google, Microsoft, Amazon, Ebay, Twitter e infinidad de sitios más. No es un protocolo de comunicación, sino una forma de programar, y vamos a ver algunos ejemplos para poder sacarle partida.

Emilio es un entusiasta (realmente entusiasta) de Access, colaborador en diferentes foros de Access y miembro activo en Access User Groups Latinoamérica.

================= DETALLES  DE LA CONEXIÓN ===============================

Nos reunimos el tercer jueves de cada mes a las 9:00 pm CEST.

Nuestra reunión será usando el siguiente enlace:

https://meetings.webex.com/collabs/meetings/join?uuid=M5O4DBOS1QSIMYKRAL7Z61BTW6-OXTF

Puedes usar cualquier navegador para ver y escuchar la reunión.

Para escuchar la charla vía teléfono: Marca el número de teléfono:

United States +1-415-655-0001 US TOLL
España              Barcelona +34.93.393.4037               Madrid +34.91.201.2149

Access Code 194 689 173

Números internacionales disponibles

Publicado por Chea con no comments

UserForms en Access

UserForms

El próximo Jueves, 15 de enero, en la reunión mensual del grupo virtual de usuarios Access España, Emilio Sancha nos contará las ventajas de usar en Access los UserForms de Office.

Descubriremos una herramienta de Office, inexplicablemente oculta en Access, pero fácilmente accesible: los UserForms, dichos UserForms nos permitirán diseñar aplicaciones que incluso podrán ser exportadas al resto de aplicaciones de Office, de hecho incluso nos permitirá, de manera nativa, hacer algo que Access por si mismo no permite, soltar y arrastrar (drag and drop).

Como novedad, este mes utilizaremos Webex para la reunión con lo que esperamos que se solución los problemas técnicos que , especialmente con el sonido, hemos tenido en otras reuniones.

Emilio Sancha es ingeniero técnico industrial y trabaja a diario con Access desde la versión 2.0. Afirma que sigue aprendiendo, especialmente en foros y grupos de noticias. Fue galardonado como MVP Access entre 2006 y 2011. Es administrador del Foro de Access y VBA y autor de uno de los sites sobre Access mas leídos en español: http://www.mvp-access.es/emilio.

================= DETALLES  DE LA CONEXIÓN ===============================

Nuestra reunión será usando el siguiente enlace:

https://meetings.webex.com/collabs/meetings/join?uuid=M5O4DBOS1QSIMYKRAL7Z61BTW6-OXTF

Puedes usar cualquier navegador para ver y escuchar la reunión.

Para escuchar la charla vía teléfono: marca el número de teléfono:

United States +1-415-655-0001+1-415-655-0001 US TOLL
España              Barcelona +34.93.393.4037+34.93.393.4037             Madrid +34.91.201.2149+34.91.201.2149

Publicado por Chea con no comments

Conectar un Servicio Web SOAP con Access

access-ws

Un Web Service SOAP es un protocolo que sirve para poder intercambiar información a través de internet. El web service SOAP solo actúa como mensajero mandando la información de una computadora a otra. SOAP significa Protocolo simple de acceso a objetos.

El próximo día 18 de diciembre a las 21:00, hora de España, en la reunión mensual del grupo virtual de usuarios Access España, Emilio Verástegui nos va a hablar de todo esto. Vamos a ver cómo probar los servicios web, cómo desde Access hacer la solicitud, recibir la respuesta y, como esta es un archivo xml, cómo poder leerlo, e interactuar con él en Access. Hay otros protocolos para web service aparte de SOAP, especialmente REST, pero el tiempo no da para mucho y en esta ocasión solo veremos SOAP; contamos con que el propio Emilio nos prepare otra charla sobre ese otro protocolo.

Un ejemplo práctico de la utilidad de los servicios web es lo que hizo que Emilio tuviera que empezar a usarlos: Desde Enero de 2014 en México toda transacción debe realizarse con un comprobante electrónico ya sea Factura, Recibo de Renta, Recibo de Honorarios o Recibo de Nómina. Y este comprobante es enviado a Hacienda que lo valida, le agrega algunos datos encriptados y es devuelto para dárselo al usuario. Todo ese intercambio de información se realiza a través de un web servicio SOAP.

Para quien no conozca a Emilio, debemos decir que él es un entusiasta (realmente entusiasta) de Access, impulsor incansable de AccessUserGroups Latinoamérica y miembro activo de AccessUserGroups España. Quien siga habitualmente alguno de los foros de Access, seguro que conoce a EmilioV, siempre atento a echar una mano y con respuestas siempre certeras.

Nuestra reunión será usando el siguiente enlace:

https://join.me/accessusergroups

Puedes usar cualquier navegador para ver y escuchar la reunión. Si estas usando un teléfono o tableta usa la aplicación join.me app y usa el código de reunión: 842-599-774842-599-774

Para escuchar la charla: Marca el número de teléfono:

United States ++1.213.226.1066+1.213.226.1066
España +34.81.061.0479
Access Code 842-599-774842-599-774#

Números internacionales disponibles

Por computadora vía Internet: Únete a la reunión usando el enlace https://join.me/accessusergroups. Una vez en la reunión presiona el icono de teléfono y selecciona ‘Call vía Internet’ (se puede necesitar una pequeña descarga).

Publicado por Chea con no comments

Access vs MySQL

AccessMySQL

 

Access vive. No solo vive, sino que, además, goza de una estupenda salud. Quienes piensen que internet convierte Access en algo anticuado, no conocen Access o no acaban de entender el sentido del cambio de los tiempos.

Nadie va a trabajar en la oficina con una aplicación de gestión en una tablet o un smarphone, sino que seguirá trabajando con un PC y, de manera complementaria, usará la tablet, o mejor una Surface o equivalente, cuando necesite movilidad. En aplicaciones de escritorio, Access sigue sin competencia como herramienta de desarrollo rápido de aplicaciones, Access 2013 facilita la creación de aplicaciones web y la eclosión de los servidores de datos en la nube (Azure, Amazon, Google) libra al desarrollador de Access, a menudo freelance, de los costes, en tiempo y en dinero, del mantenimiento de la infraestructura necesaria, al tiempo que le garantiza que su aplicación puede acceder a sus datos desde cualquier parte del mundo.

En el Grupo Virtual de Usuarios Access España estamos charlando de todo esto, entre otras muchas cosas, los terceros jueves de mes a las 21:00 hora de España. El último jueves, Juan Soto nos explicó la mejor forma de sacarle partido a los procedimientos almacenados de SQL Server desde Access y el mes anterior Ángel Gil nos contó cómo conectar Access con SQL Azure, que él usa con tablets windows. Cada mes un ponente de relumbrón, o no tan conocido pero con mucho fundamento, presenta un tema interesante y luego charlamos sobre ello o sobre cualquier cosa que se nos ocurra.

Esta semana, el próximo jueves 20 de noviembre, a las 21:00 hablaremos de Access con MySQL. César Augusto Cardona, CEAC, un viejo conocido en los foros, nos hablará de cómo trabajar con Access y MySQL en la red.

En esta ocasión César dará respuesta, entre otros, a los siguientes puntos destacados:

– ¿Por qué MySql?
– Creación de Tablas MySql
– Conversión de Access a MySql
– ODBC de conexión
– Configuración ODBC para distribución automática
– Consultas Access y MySql

César es Licenciado en Tecnología e Informática. Ha trabajado como “freelancer” y también en PwC (Price Waterhouse Coopers). Actualmente es funcionario público de la Secretaría de Educación Municipal. Dispone de canal propio en YouTube: https://www.youtube.com/channel/UC070O4HA_eUa4PeeFJu2YdA

Nuestra reunión será usando el siguiente enlace:

https://join.me/accessusergroups

Puedes usar cualquier navegador para ver y escuchar la reunión. Si estas usando un teléfono o tableta usa la aplicación join.me app y usa el código de reunión: 842-599-774842-599-774

Para escuchar la charla: Marca el número de teléfono:

United States ++1.213.226.1066+1.213.226.1066
España +34.81.061.0479
Access Code 842-599-774842-599-774#

Números internacionales disponibles

Por computadora vía Internet: Únete a la reunión usando el enlace https://join.me/accessusergroups . Una vez en la reunión presiona el icono de teléfono y selecciona ‘Call vía Internet’ (se puede necesitar una pequeña descarga).

Publicado por Chea con no comments

Access en la nube. Pon tus datos en SQL Azure.

imagesazure

¿Qué puede haber mejor que una aplicación de escritorio hecho en Access con los datos en internet? Empezar usando Access contra SQL Azure, utilizando ODBC, es una forma muy sencilla de dar los primeros pasos sin que se comprometan futuras mejoras que garanticen la escalabilidad

El próximo jueves, día 18 de septiembre, en el grupo virtual de usuarios Access España Ángel Gil, Conrrad69 en los foros, que trabaja habitualmente con esa combinación, nos explicará paso a paso y con todo detalle cómo crear una base de datos en SQL Azure y cómo vincularla desde Access con ODBC. No es la solución definitiva, no es así como trabaja Ángel, pues hay que trabajarlo bastante mas para conseguir una seguridad apropiada, mejorar el rendimiento o solucionar qué pasa cuando no hay conexión a internet, pero sí que es la mejor manera de empezar y resulta muy buena para cosas sencillas.

Ángel cuenta con 20 años de experiencia en el desarrollo de sistemas de gestión empresarial, utilizando tecnologías varias como FileMaker, MS Access, VBA y SQL Azure, combinando además esta experiencia profesional con actividades de formación presencial y on-line. Comparte sus conocimientos a través de su página http://www.accessyexcel.com, y colabora en otras webs como http://www.docebit.com y http://www.louesfera.com. Y, para un mayor abundamiento, también podéis encontrarlo en YouTube (http://m.youtube.com/user/angilmen).

Últimamente está desarrollando aplicaciones de Access que corren en tablets con Windows 8 con conexión a internet. Algo realmente interesante que hace mas atractivo el tema de la próxima reunión.


No debemos confundir los grupos Access España y Access Latino. Son dos grupos distintos que se reúnen en días y con horarios distintos, ambos están abierto a quien quiera participar y puede hacerse en los dos.

Os esperamos el 18, a las 21 h, en http://accessusergroups.org/espana/meetings/. ¡Os aseguro que es para no perdérselo!

¡Nos vemos el jueves!

virtual-chapters-295x300

Publicado por Chea con no comments

Presentación sobre el BackStage de Access 2010 en AccessUserGroups España

LogoCaballoNckModerno

El próximo jueves, 21 de agosto de 2104 a las 21:00, tendrá lugar la reunión mensual del Grupo Virtual de Usuarios Access España. En ella,  Neckkito hará una presentación sobre el BackStage de Access 2010, qué es, cómo funciona y cómo modificarlo. Después tendremos un rato de charla sobre lo que se acaba de explicar, lo que queramos aportar o preguntar, o, simplemente, lo que nos apetezca.

Si Access te interesa, estás invitado a participar.

Publicado por Chea con no comments

El jueves, segunda reunión del grupo virtual de usuarios Access España

imagesCYY1731NEl próximo jueves, 18 de junio, a las 21:00 tendrá lugar la segunda reunión del grupo virtual de usuarios Access España.

Emilio Sancha, administrador del Foro de Access y VBA, que ha sido MVP en Access varios años, nos hará una presentación sobre automatización con Office. Nos contará cómo podemos preparar un proceso que, mediante automatización, nos permita extraer el código de un archivo de Office y exportarlo, formateado, a un documento de Word.

Para unirte a la reunión, sigue el siguiente enlace: http://accessusergroups.org/espana/meetings/. Se estará usando el enlace para todas las futuras reuniones del Access User Groups. Ahora utilizara el mismo ID para todas las reuniones de cada grupo

Únete a nosotros si Access te apasiona. Entre todos vamos a construir un grupo en el que reunirnos online para exponer temas interesantes, intercambiar código, experiencias y dudas sobre todo lo que tenga algo que ver con Access.

Publicado por Chea con no comments

Primera reunión del grupo virtual de usuarios Access España

Ya está convocada la primera reunión del grupo virtual de usuarios Access España.

bandera

Únete a nosotros si Access te apasiona. Entre todos vamos a construir un grupo en el que reunirnos online para exponer temas interesantes, intercambiar código, experiencias y dudas sobre todo lo que tenga algo que ver con Access.

Publicado por Chea con no comments
Archivado en:

Grupos Virtuales de Usuarios de Access

La madrugada del miércoles al jueves he asistido,entusiasmado, a la primera reunión del Grupo Virtual de Usuarios Access Latino América, un capítulo de los Virtual Access User Groups que está impulsando Juan Soto. El propio Juan Soto hizo la presentación del grupo y nos estuvo hablando de las aplicaciones web de Access 2013 y la posibilidad de usarlas en aplicaciones híbridas accediendo a las tablas que crea en Azure desde una aplicación de escritorio. En el blog del grupo nos lo cuenta Juan y nos facilita la presentación que utilizó.

 

virtual-chapters-295x300

 

Yo quiero un sitio así para España, que el de América Latina coincide con la madrugada española. Eso le dije a Juan Soto y la respuesta es que contamos con todo el apoyo y la infraestructura de AccessUserGroups.org para hacerlo. Ya tenemos página reservada para el Grupo Access España y ya estamos moviéndonos, aquí en España, para formar un grupo de gente dispuesta a impulsarlo. Ya contamos con unos cuantos expertos dispuestos no solo a participar en el grupo, sino también a presentar algún tema.

De lo que se trata es de reunirse virtualmente una vez al mes a través de internet para charlar sobre Access, intercambiar experiencias y código y plantear dudas y soluciones. El formato será empezar por una presentación de algún tema interesante, que puede hacer un miembro del grupo o un experto ajeno a él, y luego abrir un coloquio en el que debatir sobre el tema expuesto, sugerir próximos temas y plantar problemas. Aunque no es definitivo, va cobrando fuerza la idea de que los días de reunión sean los terceros jueves de cada mes a las 19,00 y la primera reunión, que anunciaremos, será en mayo.

Ya tenemos expertos dispuestos a hacer presentaciones sobre el uso de la cinta de opciones (Ribbon), de los Store Procedures de SQL Server como origen de formularios Access, de las macros de datos, de la automatización y de Azure como back-end de aplicaciones de Access usadas en las nuevas tablets con Windows 8.1. También nos han sugerido temas que aún están a falta de expertos que nos hablen de ellas: uso de librerías .net desde Access, aplicaciones híbridas (espero convencer a Juan Soto) y MySQL con Access.

El grupo está abierto a todo el que sea un apasionado de Access. Si estás interesado en participar, si quieres exponer algún tema, o si quieres sugerir algún tema que tratar, la página está aún en construcción, pero el formulario de contacto ya lleva unos días funcionando: ponte en contacto con nosotros.

Publicado por Chea con 1 comment(s)

Utilizar un subinforme como sustituto de un control TreeView en Access 2010

Con los problemas y vulnerabilidades cada vez mas frecuentes de los Activex, aunque existan parches para ir tirando, muchos programadores en Access han decidido finiquitar el único control del vetusto Common Controls que seguía mereciendo la pena y que carecía de una alternativa nativa: el TreeView Control. El que mas y el que menos se ha elaborado sus propias soluciones recurriendo a lo que tenían a mano en Access, en general jugando con cuadros de lista o subformularios y el resultado, variable, a veces sorprende por lo sencillo y lo eficiente. Sin embargo, no conozco a nadie que haya optado por utilizar un informe.

En Access una estructura en árbol se muestra fácilmente, de toda la vida, con un informe agrupado. Puede ser como una fotografía, mas rica, de un Control TreeView, pero es difícil imaginarse un informe que no sea completamente estático, sin capacidad de contraer o expandir las ramas. Sin embargo las versiones 2007 y 2010 de Access aportaron nuevas características, la Vista Informe en 2007 y la posibilidad de incrustar un subinforme en un formulario en 2010 que, bien utilizadas, pueden convertirse en una alternativa, mas sencilla de usar, mas potente y mas bonita que el control original.

 

El subinforme como alternativa al TreeView Control

Desde Access 2007, existe una nueva vista para los informes, la llamada Vista Informe que, al contrario que los informes tradicionales, no está pensada para imprimir, sino para ver en pantalla; por eso no reparte el informe en páginas, por lo que tampoco se producen los eventos Format o Print, sino que lo muestra todo seguido, y, también por eso, admite búsquedas y filtros directamente o eventos mas propios de los formularios, como Click, DblClick, keyDown, etc.

Desde Access 2010, los subInformes, en Vista Informe, se pueden incluir en un formulario exactamente igual que si fueran subformularios. Pueden ser independientes, o vinculados al formulario principal a través de las propiedades LinkMasterFields y LinkChildFields.

Con todos estos ingredientes es fácil imaginar que un informe podría sustituir con ventaja a un control TreeView. Los informes podemos vincularlos a un origen de datos de forma muy sencilla y sin escribir una sola línea de código, y podemos agrupar datos con facilidad, mostrando una estructura en ramas. Podemos usar distintos formatos de texto, colores, fondos, formatos condicionales, añadir los campos que queramos, resúmenes, totales… solo con unos clics de ratón.

Parece que en todo, en facilidad y posibilidades, superan al control TreeView y que solo les falta una cosa: La facilidad de los controles TreeView para contraer y expandir ramas. Pues bien, esto también es posible con un poco de programación, mas bien muy poco, un poco de SQL y cuidando algunos detalles del diseño, nada que no esté al alcance la la inmensa mayoría de los desarrolladores en Access.

Aquí tenemos una imagen de un ejemplo de un subinforme ejerciendo de TreeView. Puede descargase la aplicación de ejemplo en http://www.bengoechea.net/utilidades-1/reporttreeview

image

 

 

¿Funciona? Sí, y solo un parpadeo casi imperceptible a la hora de contraer o expandir una rama hace que no sea cien veces mejor que un control TreeView.

Aunque hay que cuidar algunos detalles en el diseño convencional del informe para que el resultado sea el que buscamos, esta parte no se aleja de la manera de diseñar un informe con grupos a la que estamos acostumbrados. Lo que tiene su pequeño truco, que necesita mas explicación, es la manera de hacer que las ramas se expandan o contraigan.

La consulta origen del informe

Un informe con grupos se basa en una única consulta en la que están contenidas todas las tablas relacionadas entre sí por campos en común. Si a estos campos en común añadiéramos uno mas de cada lado, cambiando su valor podríamos hacer que la consulta no devolviera los registros correspondientes de esa rama, si hacemos, a continuación de cambiar el valor de ese campo, un requery del informe basado en esa consulta, el efecto será el de contraer o expandir la rama.

A ver si me explico un poco mejor. Tenemos dos tablas, Género y Especie, con un campo en común idGenero; con una consulta que vincule ambas tablas mediante ese campo el resultado será una relación de todos los géneros y las especies de cada género. Pero podemos incluir un campo mas en cada tabla, bExpandir y bExpandiendo, por ejemplo, y bExpandiendo tendría siempre valor True. Si relacionamos también ambas tablas por esos campos, cambiando el campo bExpandir de la tabla Género coincidirá o no con el de la tabla Especie y, en consecuencia, se mostrarán o no las especies de ese género.

Ese planteamiento inicial obligaría a a cambiar el diseño de las tablas implicadas y a andar cambiando datos en ellas de manera poco justificada, por lo que, partiendo de esa idea, lo que hacemos es utilizar una tabla auxiliar, con el campo en común y el campo bExpandir, que interponemos entre las dos tablas relacionadas. No necesitamos modificar ni andar tocando las tablas originales, sino que escribimos en la tabla auxiliar y, en vez utilizar una tabla auxiliar para cada par de tablas relacionadas, utilizamos una sola, con distintas consultas filtradas por el nombre de la tabla del lado uno, que son las que interponemos.

Así sería la consulta original del informe que mostramos arriba si fuera estático:

 

image

 

O, si lo prefiere, así sería el texto SQL:

SELECT Títulos.*, Grupos.*, cuentas.* 
FROM (Títulos LEFT JOIN Grupos ON Títulos.ID_TITULO = Grupos.ID_TITULO) LEFT JOIN cuentas ON Grupos.ID_GRUPO = cuentas.ID_GRUPO;

Sin embargo, para poder ocultar o mostrar las ramas a voluntad, la consulta qArbolCuentas ha quedado como ésta:


image

 

 
SELECT Títulos.*, qExpandirTitulo.NombreTablaPadre, qExpandirTitulo.Expandir 
AS ExpandirTitulo, Grupos.*, qExpandirGrupo.NombreTablaPadre, qExpandirGrupo.Expandir 
AS ExpandirGrupo, cuentas.* 
FROM (((Títulos LEFT JOIN qExpandirTitulo ON Títulos.ID_TITULO = qExpandirTitulo.idPadre) 
LEFT JOIN Grupos ON qExpandirTitulo.idPadre = Grupos.ID_TITULO) 
LEFT JOIN qExpandirGrupo ON Grupos.ID_GRUPO = qExpandirGrupo.idPadre) 
LEFT JOIN cuentas ON qExpandirGrupo.idPadre = cuentas.ID_GRUPO;

 

Es decir, entre Títulos y Grupos hemos interpuesto la consulta qExpandirTitulo, y, entre Grupos y Cuentas, qExpandirGrupo. También hemos añadido los respectivos campos Expandir, que cambiamos de nombre.

Las consultas qExpandirTitulo y aExpandirGrupo, se basan ambas en una única tabla filtrada porque Expandir sea Verdadero y por un nombre, que podría se aleatorio, y que servirá luego para filtrar en la tabla que registros modificar.


imageimage

SELECT tblExpandir.Id, tblExpandir.idPadre, tblExpandir.NombreTablaPadre, tblExpandir.Expandir 
FROM tblExpandir 
WHERE (((tblExpandir.NombreTablaPadre)="Grupos") AND ((tblExpandir.Expandir)=True));

 

SELECT tblExpandir.Id, tblExpandir.idPadre, tblExpandir.NombreTablaPadre, tblExpandir.Expandir 
FROM tblExpandir 
WHERE (((tblExpandir.NombreTablaPadre)="Títulos") AND ((tblExpandir.Expandir)=True)); 

Como la tabla tblExpandir está de momento vacía, ahora la consulta qArbolCuentas solo mostraría los Títulos, pues no existen los campo idPadre que se interponen entre las distintas tablas. Si le añadimos el ID de un título, lo identificamos con el nombre “Titulos” y le damos al campo Expandir valor Verdadero, se mostrarán todos los Grupos de ese Título, y si cambiamos Expandir a Falso, no se mostrarán, pues qExpandirTitulo solo devuelve los registros en que Expandir es verdadero.

Con las consultas creadas, solo nos falta crear el informe y añadirle un par de procedimientos para que busque y modifique el campo Expandir correspondiente y lo añada si no existe.

 

El diseño del informe

El diseño del informe es bien sencillo, aunque hay algunos detalles que no se deben dejar escapar.

image

 

Tiene su origen en la consulta qArbolCuentas, está agrupado por Titulo y por Grupo y muestra los campos Titulo, Grupo y Cuenta y, a la izquierda de los dos primeros, el valor correspondiente de Expandir y, a la derecha de cada uno de ellos, oculto, el ID correspondiente.

Debemos cuidar que las propiedad Autocomprimible, tanto de los controles que se expanden como de las secciones que los contienen, sea y, además, la propiedad Alto Automático de las distintas secciones (Encabezado de Grupo y Detalle) sea también .

Si los controles no se comprimen, o si no se comprime la sección que los contiene, aunque no haya contenido para ese grupo, se muestra en blanco el espacio correspondiente a ese control o a esa sección y, aparte del derroche de espacio innecesario, el resultado es menos estético.

Lo controles solo se comprimen cuando no tienen nada que mostrar y las secciones cuando todos los controles están comprimidos, así que no podemos añadir a las secciones prácticamente nada que no sean campos de texto que puedan estar vacíos; no podemos, por ejemplo, añadir casillas de verificación o botones. Tampoco podemos ocultarlos o cambiar de tamaño sobre la marcha, pues no tenemos los eventos Format y Prine

La pega que tenemos es que ExpandirGrupo y ExpandiTitulo, devuelven valores 0 y –1, aunque no haya registros que mostrar y que, además, no queremos mostrarlos así, sino como + y –.

En el campo ExpandirTitulo es mas sencillo, pues, como, al ser el tronco, siempre se mostrará, y no importa que no se comprima. Lo que hacemos con el campo ExpandirGrupo es cambiarlo por un campo calculado que devuelve un nulo si el Grupo es nulo.

=SiInm(EsNulo([Grupo]);Nulo;[ExpandirGrupo])

Y, en ambos campos, para que se muestren como + y –, lo que utilizamos es la propiedad Formato, que tiene distintas secciones, separadas por punto y coma, para valores positivos, negativos, ceros y nulos. En ambos casos, la propiedad formato es:

+;-;+

 

En el caso de la cuenta, no hay un campo Expandir, por lo que utilizamos un campo calculado. txtPunto, cuyo origen es:

=SiInm(EsNulo([Cuenta]);Nulo;0)

Y, como lo que queremos es que se muestre una viñeta, la propiedad Formato es:

●;●;●

Volviendo a ExpandirTitulo, y puesto que siempre muestra algún registro, no nos ha importado adornarlo un poco poniendo un botón de forma redonda y fondo transparente sobre él, para darle un resultado mas aparente image

 

El código VBA

Lo que básicamente hace el código es que, cuando pulsamos sobre el campo ExpandirTitulo o ExpandirGrupo, que en el informe se muestran con + o, se cambia por el contrario el valor correspondiente en la tabla tblExpandir para la tabla Titulos o Grupos y, si no existía el registro en la tabla tblExpandir, se añade. A continuación, hacemos un Requery del informe, que será lo que produzca el efecto de contraer o expandir la rama.

Private Sub ExpandirTitulo_Click()
fFindEditTblexpandir Nz(Me.Títulos_ID_TITULO), "Títulos", Not Nz(Me.ExpandirTitulo)
Echo False
Me.Requery
Echo True
End Sub
Private Sub TxtExpandirGrupo_Click()
fFindEditTblexpandir Nz(Me.Grupos_ID_GRUPO), "Grupos", Not Nz(Me.TxtExpandirGrupo)
Echo False
Me.Requery
Echo True
End Sub

 

Estos eventos llaman a un procedimiento en un módulo general que busca en la tabla tblExpandir el ID y el texto identificativo de la tabla (“Títulos” o “Grupos”) que pasamos como argumento y cambia el valor de el campo Expandir por el que también le pasamos. Si no encuentra el registro, llama a otro procedimiento que lo añade con los valores de los argumentos que hemos pasado al primero. Ninguna cosa del otro mundo, pero, para que quede mas claro, pego el código.

'---------------------------------------------------------------------------
' Procedure : fFindEditTblexpandir
' DateTime  : 11/02/13 20:33
' Author    : Chea
' Purpose   : Abrir un recordset de DAO , buscar por un campo y asignar valor
'---------------------------------------------------------------------------

Public Function fFindEditTblexpandir(BuscarlidPadre As Long, sNombreTablaPadre As String, bExpandir As Boolean) As Long

Dim miBD As DAO.Database
Dim rstblExpandir As DAO.Recordset

    On Error GoTo fFindEditTblexpandir_Error

Set miBD = CurrentDb()
Set rstblExpandir = miBD.OpenRecordset("tblExpandir", dbOpenDynaset)
With rstblExpandir
    If Not (.EOF And .BOF) Then
        .FindFirst "(idPadre = " & BuscarlidPadre & ") AND ( NombreTablaPadre = '" & sNombreTablaPadre & "')"
        If Not .NoMatch Then
            .Edit
                !Expandir = bExpandir
            .Update
            .Bookmark = .LastModified
            fFindEditTblexpandir = !Id
        Else
            fAddTblexpandir BuscarlidPadre, sNombreTablaPadre, bExpandir
        End If
    Else
        fAddTblexpandir BuscarlidPadre, sNombreTablaPadre, bExpandir
    End If
    .Close
End With
Set rstblExpandir = Nothing
Set miBD = Nothing

fFindEditTblexpandir_Salida:

   On Error GoTo 0
   Exit Function

fFindEditTblexpandir_Error:

   MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure fFindEditTblexpandir "
   GoTo fFindEditTblexpandir_Salida:

End Function

'---------------------------------------------------------------------------
' Procedure : fAddTblexpandir
' DateTime  : 11/02/13 21:11
' Author    : Chea
' Purpose   : Abrir un recordset de DAO , añadir un nuevo registro y asignar valor
'           : a los campos de ese registro con los valores que se pasen como parámetros.
'---------------------------------------------------------------------------

Public Function fAddTblexpandir(lidPadre As Long, sNombreTablaPadre As String, bExpandir As Boolean) As Long

Dim miBD As DAO.Database
Dim rstblExpandir As DAO.Recordset

    On Error GoTo fAddTblexpandir_Error

Set miBD = CurrentDb()
Set rstblExpandir = miBD.OpenRecordset("tblExpandir", dbOpenDynaset)
With rstblExpandir
        .AddNew
                !idPadre = lidPadre
                !NombreTablaPadre = sNombreTablaPadre
                !Expandir = bExpandir
        .Update
        .Bookmark = .LastModified
    fAddTblexpandir = !Id
    .Close
End With
Set rstblExpandir = Nothing
Set miBD = Nothing

fAddTblexpandir_Salida:

   On Error GoTo 0
   Exit Function

fAddTblexpandir_Error:

   MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure fAddTblexpandir "
   GoTo fAddTblexpandir_Salida:

End Function

Hay algo mas de código, por ejemplo, para llamar al evento ExpandirTitulo_Click, cuando pulsamos sobre el botón que cubre el campo, o para detectar pulsaciones de teclas y pasarlas al evento Click correspondiente.  No lo ponemos para que quede mas claro el funcionamiento básico.

 

Conclusión

Los informes, usados generalmente como subinformes, son una alternativa al Control Treeview, sencilla, completamente funcional y con mas posibilidades. Estamos utilizando elementos nativos de Access y algo tan sumamente familiar como son los informes. No vamos a tener que instalar o registrar nada ajeno a Acces, ni vamos a tener nunca problemas por cambio de versiones o características obsoletas. Se necesita menos código que con un TreeView Control y el diseño gráfico de los informe facilita muchísimo la tarea.

Se pueden añadir todos los campos que se quiera, del tipo que se quiera y en la posición que se quiera. No tenemos por qué limitarnos al clásico TreeView.

Seguramente es mas lento que un TreeView y produce un ligero parpadeo cada vez que hacemos Requery (contraemos o expandimos) Este parpadeo es mas notorio cuanto mas cosas le pidamos al informe, por ejemplo, formatos condicionales, totales y en general todo lo que signifique cálculos en el informe, pues los campos afectados tardan una pizca mas el mostrarse. Pero algún precio habría que pagar.

 
Publicado por Chea con no comments

Apuntes para una seguridad a nivel de usuario en Access 2010 II. Macros de datos

Después de casi dos años tomando impulso, por fin me animo a continuar con la seguridad a nivel de usuario usando las macros de datos. El que consiga acabar este artículo me disculpará por el retraso, pues se dará cuenta de que hacía falta tener ánimo para ponerse a hablar de las macros de datos: Apenas nadie utiliza macros, el editor y muchas características son completamente nuevos y las macros de datos son una novedad absoluta en Access 2010, a lo que se añade que apenas existen ayuda o facilidades al respecto.

Tendría que explicar el funcionamiento de las macros, los tipos, sus posibilidades, las macros de datos y, cuando empezara por la forma de usarlas para asegurar una tabla a nivel de usuario, ya habría escrito todo un tratado, seguramente muy aburrido. Así que voy directamente al grano, a empezar dando unas ideas de cómo crear macros que eviten que los usuarios no autorizados expresamente manipulen los datos de las tablas y, a medida que las voy explicando, mencionaré pormenores y detalles de las macros y de su editor. Espero que se entienda algo.

En el artículo anterior veíamos que, para asegurar una base de datos Access, es necesario hacer inaccesible el back-end y dábamos unas pistas acerca de cómo protegerlo. Si un usuario sin permiso expreso puede acceder directamente al contenido de las tablas, o, peor aún, a su diseño, todo lo hablado aquí no sirve para nada. Una vez conseguido esto, podemos abordar la utilización de macros de datos para que solo los usuarios autorizados puedan añadir, editar o borrar datos solo en las tablas para las que se les haya concedido permiso. Tenga en cuenta que solo son unos apuntes y que es su propia responsabilidad procurar que no queden agujeros en la seguridad.

Grosso modo, lo que vamos a hacer con las macros es verificar si el usuario tiene permisos para la operación que está pretendiendo hacer y, si no los tiene, abortarla. Un paso mas adelante, aprovechando de que conocemos el  usuario, vamos a guardar en cada registro qué usuario lo crea,  lo que aparte de un rudimentario seguimiento de cambios, nos permite dar permisos para que sólo el autor de un registro pueda modificarlo.

Las macros de datos

Las macros de datos son nuevas en Access 2.010 y el editor de macros, de datos o no, también es nuevo, por lo que no es de extrañar que haya muy poco publicado al respecto.

Las macros de datos están disponibles en la Vista Diseño de la tabla, en la pestaña Diseño de la cinta de opciones, en el grupo de “Eventos de campo, registro y tabla”

 

image

 

Es decir, en Access 2010 las tablas tienen eventos y podemos escribir macros en respuesta a estos eventos. Los evento son Después de insertar, Después de actualizar, Después de eliminar, Validar eliminación y Antes del Cambio. Desde el punto de vista de la seguridad por usuarios, los tres primeros son apropiados para registrar al usuario que produce el evento, y los dos últimos para verificar si el usuario tiene los permisos necesarios y, en caso contrario, abortar la operación.

Vamos a empezar por algo muy, muy básico, impedir que se pueda borrar un registro accidentalmente, pero antes debemos abrir el editor de macros.

 

El editor de macros

Da la sensación de que el editor de macros en Access 2010 está a medio acabar, pues le faltan cosas tan elementales como un menú contextual para copiar y pegar, que, sin embargo, ya existe en 2013, y la ayuda es mínima. No obstante, lo esencial funciona correctamente y sirve a la perfección para nuestro propósito.

Pinchando en uno de los eventos del desplegable de la cinta de opciones que veíamos antes, se abre el editor de macros. Las acciones disponibles varían según el tipo de macro y el tipo de eventos. Es decir, las macros de datos tienen muchas menos acciones disponibles y los eventos Validar eliminación y Antes del cambio, menos aún. Y, sin embargo, con estas macros se pueden conseguir cosas antes impensables.

image

 

Hemos abierto el editor de macros para el evento Validar eliminación. A la derecha se muestran todas las acciones disponibles, bien pocas, y a la izquierda una ventana de edición, más grande con un símbolo mas image y un cuadro desplegable en el que podemos elegir la acción que queremos anotar. Si no se mostrara el catálogo de acciones, podemos hacerlo visible pulsado en el botón “Catálogo de acciones” de la cinta de opciones.

Las acciones se eligen en el cuadro desplegable o se arrastran desde el Catálogo de acciones que tenemos a la derecha. No es un editor donde podamos escribir código, solo elegir entre las opciones que tenemos y tan solo escribiremos algunas expresiones cuando sean parámetros de la acción que hemos elegido.

Nuestra primera macro

Vamos a crear nuestra primera macro. En el desplegable elegimos Provocar error.

image

 

Escribimos el valor de los parámetros que nos pide, el Número de error, que es el que queramos, y la Descripción del error, que será el mensaje que se muestre al producirse el error. Guardamos y ya tenemos nuestra primera macro de datos

image

 

Abra la tabla e intente borrar un registro. Imposible, nos sale el mensaje que hemos puesto en la descripción del error: ¡Prohibido borrar! y ni en un formulario, ni por código, ni por consulta de eliminación, ni directamente en la Vista Hoja de datos de la tabla podremos borrar ningún registro.

 

image

 

Bueno, nos hemos pasado, hemos de dar alguna posibilidad de borrar un registro. Por ejemplo, podemos añadir a la tabla un campo Sí/No que se llame Borrar, y, si es verdadero, sí se puede borrar el registro.

Ahora, lo que tenemos que hacer con la macro es comprobar el valor del Campo Borrar y, si no es verdadero, provocar el error.

Vamos a la Vista Diseño de la tabla y abrimos la macro del evento Validar eliminación. Arrastramos el grupo Si al editor e introducimos las expresiones, para lo cual contamos con la ayuda del estupendo generador de expresiones

 

image

 

image

 

En la condición escribimos [Borrar] <> True  image… Entonces, lo que tenemos que hacer es provocar el error, como hemos hecho antes. Pero el código para provocar el error se encuentra después de Finalizar si, es decir fuera del bloque If..,Endif. Vamos a arreglarlo.

 

Observe que el bloque donde estamos introduciendo las expresiones está marcado, delimitado por un cuadrado y con fondo mas oscuro. Si pinchamos con el ratón sobre el bloque de ProvocarError, es éste el que queda marcado y, si movemos el ratón manteniéndolo pulsado, el bloque se mueve.

image

 

Pues bien, arrastramos el bloque de ProvocarError dentro del bloque Si…Finalizar si y voilà, ya tenemos nuestra macro:

image

 

Guardamos la macro, cerramos y comprobamos si podemos borrar. Podremos hacerlo si, previamente, para ese registro hemos marcado el campo Borrar como verdadero. Es sencillo y, tal como está, vale para cualquier tabla, por lo que vamos a copiarla para pegar en otras tablas y mandarnos el código por correo al trabajo, para hacerlo también allí.

Editamos de nuevo la macro.

image

 

Como no tenemos marcado ningún bloque, el código se queda mas limpio, mas claro. Podemos observar que nos ha cambiado True por Verdadero, que a las macros les gusta el castellano, incluso a veces no entienden el inglés.

Arrastramos el ratón para seleccionar el texto, pero no se selecciona nada, en cambio, si pinchamos, se seleccionan los bloques que hemos visto antes. Si tenemos cuidado y/o pulsamos también
Mayúsculas o Control al tiempo que el ratón, podemos seleccionar todos los bloques en un bloque único.

Buscamos un botón para copiar, pero no existe, pulsamos el botón derecho del ratón y no aparece ningún menú (bueno, si tienes instalado también Access 2013, sí aparece) ¿Cómo copiamos? Pues como hacíamos antes de los ratones, con Control+C.

Probamos, abrimos otra macro y pegamos. Se pega correctamente y se replica el texto la macro que habíamos copiado.

Llegados a este punto de lectura, usted ya tiene ganas de probar el editor de macros, pero quiere darse prisa y empezar copiando el código desde este blog ¿Desde dónde, si todo son imágenes? Está bien, ahí va el código de la macro:

<?xml version="1.0" encoding="UTF-16" standalone="no"?> 
<DataMacros
xmlns="http://schemas.microsoft.com/office/accessservices/2009/11/application"><DataMacro
Event="BeforeDelete"><Statements><ConditionalBlock><If><Condition>[Borrar]&lt;&gt;True</Condition><Statements><Action
Name="RaiseError"><Argument
Name="Number">1000</Argument><Argument
Name="Description">¡Prohibido
borrar!</Argument></Action></Statements></If></ConditionalBlock></Statements></DataMacro></DataMacros>

Efectivamente, las macros se guardan como código xml y, si usted copia ese texto y lo pega en el editor de macros, el resultado se mostrará como la macro original. Para pegar, no pinche sobre la ventana de edición de la macro, sino sobre la pestaña de la ventana.

 

Verificar permisos de un usuario

Ya tenemos nuestra primera macro de datos y consigue algo que resulta imposible en cualquier versión anterior de Access, pero de lo que trata este artículo es de la seguridad a nivel de usuarios y lo que queremos es poder discriminar qué usuarios pueden borrar y cuáles no. Para ello debemos empezar por una tabla con los permisos de los usuarios, buscar si el usuario actual tiene permiso de borrado y, en caso contrario, provocar un error.

La cuestión ahora es cómo pasamos a la macro el nombre del usuario para que lo busque en la tabla permisos. Las macros de datos no pueden usar ninguna función personalizada, ni siquiera Variables Temporales (Variables Locales, que no son globales, sí) ni existe ninguna propiedad o función incorporada que nos devuelva el usuario de Access o el de Windows y que sea accesible para la macro de datos.

El truco está en que, aunque no se muestren en el generador de expresiones, se pueden hacer búsquedas en consultas y las consultas pueden utilizar una función personalizada como origen de un campo calculado y que las macros de datos pueden buscar en una consulta igual que en una tabla, aunque, para despistar, no se muestren en el generador de expresiones ni el Intellisense para las macros. O sea, que podemos tener una consulta que tenga una función que devuelva el nombre del usuario y la macro de datos lo lea como si fuera un campo.

Las macros de datos y las consultas se ejecutan en el Front-End, aunque se alojen en el Back-End, por lo que la función que devuelva al usuario debe encontrarse en el Front-End y, si no queremos ir haciendo pruebas mientras diseñamos la macro, conviene que el Back-End tenga una copia de esta función. Cómo se gestiona el login de cada usuario y cómo se obtiene la función que devuelve el nombre del usuario actual es cosa personal de cada cual y no se va a tratar aquí, aunque adelanto que para eso también se podrían utilizar macros de datos.

En resumen, la macro debe buscar en una consulta los permisos del usuario actual, obtenido en la consulta mediante una función, y si carece de permiso para borrar en esta tabla, producir un error.

De momento, nos vale con una cosa muy sencillita, basta con que, para cada tabla que queremos proteger, recoja los permisos de cada usuario autorizado.

 

image

 

Se supone que cada cual tendrá construido un sólido sistema para el control de los usuarios que pueden entrar en nuestra aplicación, con usuarios, contraseñas, registro de logins, etc., y la forma de recuperar el usuario actual. Sin embargo, para las pruebas no vale cualquier cosa que nos devuelva un usuario, por ejemplo, el usuario de Windows; así que nos hacemos esta función.

Public Function fUsuarioActual()
fUsuarioActual = Environ("USERNAME")
End Function

Y con la tabla de permisos y la función que devuelve el usuario actual, nos podemos montar la consulta que nos devuelva los permisos del usuario actual:

SELECT tblPermisos.Usuario, tblPermisos.Tabla, tblPermisos.Insertar, tblPermisos.Editar, tblPermisos.Borrar
FROM tblPermisos
WHERE (((tblPermisos.Usuario)=fUsuarioActual()));

Y se ve así:

image

 

Macro Validar Eliminación

 

Ahora en nuestra macro Validar eliminación podemos consultar fácilmente si el usuario actual tiene permiso para borrar y, en caso contrario, se lo impedimos. Esto en una macro, se refleja así:

image

 

Estudiemos un poco la macro, pues hemos metido alguna cosa nueva y, aunque parezca muy sencilla, tiene su intríngulis.

Está claro que hacemos una búsqueda en la consulta qPermisos con la condición de que el campo Tabla sea “Contactos”.

En vez de comprobar directamente el campo [Borrar], lo hemos pasado a una variable local de nombre PermisoBorrar, que es la que comprobamos después ¿Porqué?

Fíjese en tenemos en bloques distintos Buscar un registro en y Si Nz(PermisoBorrar…. Ocurre que las macros son muy puñeteras y hay que tener mucho cuidado con dónde va y qué va en cada bloque y, dependiendo de esto, la lógica cambia por completo. Fuera del bloque Buscar un registro en qPermisos, el campo [Permisos].[Borrar] no existe y por eso lo pasamos a una VariableLocal, PermisosBorrar, que tiene alcance en cualquier lugar de la macro. Observe que luego la forma de llamar a la variable es como a un campo, con el nombre entre corchetes.

¿Podríamos haber metido el bloque de Si Nz(PermisoBorrar…. dentro del de Buscar un registro en y así nos ahorrábamos la VariableLocal? Efectivamente, pero ya hemos comentado que al meter un bloque en otro cambia la lógica.

En un bloque Buscar un registro en las acciones que vayan dentro de él solo se ejecutan si se ha encontrado el registro buscado, de manera que, si no encuentra los permisos del UsuariActual, no se ejecutaría el código que provoca el error y el registro se borraría sin ningún problema.

Puede tener su lógica que los usuarios para los que no se han establecido permisos para una tabla puedan borrar en ésta, pero resulta mucho mas seguro al revés, que solo los usuarios a los que expresamente se ha dado permiso puedan borrar en la tabla. Tal como está la macro, si no existe el usuario para la tabla Contactos, no se ejecuta lo que hay dentro del bloque Buscar un registro en y, por tanto, no se establece la VariableLocal, de manera que en el siguiente bloque no la encuentra y Access provoca un error y muestra un msgbox con “No se encontró el identificador ‘[PermisoBorrar]’.”, de forma que no se puede borrar un registro. Es decir, si no existe el usuario, se produce un error de Access en la macro y no se puede borrar.

Ya tenemos completa la macro Validar eliminación y abajo pegamos el código xml. Si quiere trasladar esta macro a cualquier tabla, cópielo al portapapeles, abra la tabla en Vista Diseño, edite la macro, posicione el ratón sobre la pestaña y pulse Control+V. Solo le falta cambiar en la Condición Where el nombre de la tabla.

 

<?xml version="1.0" encoding="UTF-16" 
standalone="no"?>
<DataMacros xmlns="http://schemas.microsoft.com/office/accessservices/2009/11/application"><DataMacro

Event="BeforeDelete"><Statements><ConditionalBlock><If><Condition>[Borrar]&lt;&gt;True</Condition><Statements><Action

Name="RaiseError"><Argument
Name="Number">1000</Argument><Argument Name="Description">No se
puede borrar sin haber marcado
previamente el campo

[Borrar]</Argument></Action></Statements></If></ConditionalBlock><LookUpRecord><Data

Alias="Permisos"><Reference>qPermisos</Reference><WhereCondition>[Tabla]="Contactos"</WhereCondition></Data><Statements><Action

Name="SetLocalVar"><Argument
Name="Name">PermisoBorrar</Argument><Argument

Name="Value">Nz([Permisos].[Borrar])</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>Nz([PermisoBorrar])&lt;&gt;True</Condition><Statements><Action

Name="RaiseError"><Argument
Name="Number">2000</Argument><Argument Name="Description">Usted
no tiene permisos para

borrar</Argument></Action></Statements></If></ConditionalBlock></Statements></DataMacro></DataMacros>
 

Macro Antes del cambio

 

Todo lo que hemos contado para la macro Validar eliminación es válido para la macro Antes del cambio, por tanto, copiar y pegar y corregir algunas cosillas nos puede valer para empezar, aunque en la macro Antes del cambio hay que tener en cuanta alguna cosa mas.

Copiamos al portapapeles la macro Validar eliminación, abrimos la macro Antes de cambio, nos posicionamos en la pestaña y pegamos pulsando Control+V. Borramos el bloque de If Borrar <> Verdadero... Dentro del bloque Buscar un registro en, cambiamos la variable local PermisoBorrar por PermisoEditar y la expresión Nz([Permisos].[Borrar]) por Nz([Permisos].[Editar]) y, después, en el bloque If, [PermisoBorrar] por [PermisEditar] y en el mensaje de error borrar por editar, y el código de error por un número distinto.

La macro queda así:

image

 

Con esto debería valernos. Probamos y funciona. Sin embargo, falta algo…

¿Macro Antes de insertar?

No. No existe la macro de datos Antes de insertar, pero tenemos que saber de alguna manera si estamos intentando insertar un nuevo registro. Para ello contamos con la propiedad EsInsertar

Propiedad EsInsertar

En la macro Antes del cambio (y también en la macro Después de Actualizar) podemos consultar la propiedad EsInsertar. (La escasa información que hay sobre macros de datos de Access en internet está inglés, y en inglés es IsInsert, sin embargo, si tratamos de utilizar IsInsert en nuestro Access en Castellano, no lo reconocerá)

Entonces, en nuestra macro Antes del cambio debemos comprobar también si se trata de insertar y, en ese caso, comprobar en la tabla permisos si hay permiso para insertar y actuamos en consecuencia.

No es necesaria una nueva búsqueda. En la misma búsqueda añadimos una variable local mas, PermisoInsertar, y le asignamos el valor del campo insertar.. En un bloque siguiente, comprobamos la propiedad EsInsertar y, si es verdadera, verificamos el valor de PermisoInsertar y, si no, el de PermisoEditar y, si no lo hay, provocamos un error.

 

image

 

Lo probamos y funciona, así que ya podemos pegar el código xml por si alguien mas lo quiere probar.

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<DataMacros xmlns="http://schemas.microsoft.com/office/accessservices/2009/11/application"><DataMacro Event="BeforeChange"><Statements><LookUpRecord><Data Alias="Permisos"><Reference>qPermisos</Reference><WhereCondition>[Tabla]="Contactos"</WhereCondition></Data><Statements><Action Name="SetLocalVar"><Argument Name="Name">PermisoEditar</Argument><Argument Name="Value">Nz([Permisos].[Editar])</Argument></Action><Action Name="SetLocalVar"><Argument Name="Name">PermisoInsertar</Argument><Argument Name="Value">[permisos].[insertar]</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>[IsInsert]</Condition><Statements><ConditionalBlock><If><Condition>Nz([PermisoInsertar])&lt;&gt;True</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">4000</Argument><Argument Name="Description">Usted no tiene permisos para Insertar</Argument></Action></Statements></If></ConditionalBlock></Statements></If><Else><Statements><ConditionalBlock><If><Condition>Nz([PermisoEditar])&lt;&gt;True</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">3000</Argument><Argument Name="Description">Usted no tiene permisos para Editar</Argument></Action></Statements></If></ConditionalBlock></Statements></Else></ConditionalBlock></Statements></DataMacro></DataMacros>

Lo mismo que con la macro Validar eliminación, podemos pegarlo en cualquier tabla y solo tendríamos que cambiar en la Condición Where el nombre de la tabla.

Incluyendo estas dos macros en cualquier tabla, conseguimos que ningún usuario que no esté expresamente autorizado pueda insertar, editar o borrar ningún registro. Obviamente, antes hay que asegurarse de que no tenga acceso directamente al back-end, pues podría modificar las macros o la tabla permisos, a la que también deberíamos dar permisos, y de que contamos con un sistema de identificación de usuario y verificación del inicio de sesión suficientemente seguros, pero eso corresponde al lector y va mas allá de lo que se puede tratar en este artículo.

Identificar al usuario que crea cada registro

Tal como hemos dejado las macros, son suficientes para asegurar una tabla, pero hemos afirmado que íbamos a dar un paso mas y lo vamos a hacer. Primero, vamos a identificar en cada registro qué usuario lo ha creado y. luego, vamos a añadir un tercer tipo de permiso, aparte de sí o no, que solo el usuario que ha creado el registro pueda editarlo o borrarlo.

Lo de identificar al usuario es sencillo. Tenemos que añadirle un campo mas a nuestra tabla, que llamaremos CreadoPor. En la macro añadimos una VariableLocal mas, [Usuario], y, si EsInsertar asignamos a CreadoPor el valor de [Usuario] utilizando la acción de macro EstablecerCampo. Así:

image

 

Pero surge una pega: cualquiera podría modificar el valor del campo CreadoPor y, por tanto editar o borrar el registro. Tenemos que evitarlo; si no EsInsertar y alguien intenta modificar el campo CreadoPor debemos provocar un error, pero ¿Cómo saber si se intenta modificar un campo concreto?

Función Actualizado

En la macro Antes del cambio la función Actualizado (“Nombre de campo”) nos dice si el campo cuyo nombre pasamos en una cadena como argumento se ha cambiado. Entonces, en nuestra macro podemos comprobar, si no EsInsertar, si Actualizado (“CreadoPor”) es verdadero y, en ese caso provocamos un error para evitarlo. La parte final de la macro (no cabe entera en la pantalla para copiarla) queda así:

 

image

 

Objeto Antiguo

Provocando un error hemos evitado que se cambie el valor de CreadoPor, pero resulta muy poco amable bloquear la edición de todo un registro porque el usuario ha intentado modificar un campo que no debía ¡A lo mejor ha sido sin querer! Lo que podemos hacer es, en vez de provocar el error, volver el valor del campo a su estado original, y para eso contamos con el objeto Antiguo. Basta con escribir Antiguo. en una expresión y el Intellisense nos muestra los campos a los que se puede aplicar

 image

Si queremos se mas amables, podemos sustituir la acción de ProvocarError por una de EstablecerCampo en la que el valor original se sustituye por el Antiguo. El final de la macro modificada queda de la siguiente manera.

image

 

Y el código xml:

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<DataMacros xmlns="http://schemas.microsoft.com/office/accessservices/2009/11/application"><DataMacro Event="BeforeChange"><Statements><LookUpRecord><Data Alias="Permisos"><Reference>qPermisos</Reference><WhereCondition>[Tabla]="Contactos"</WhereCondition></Data><Statements><Action Name="SetLocalVar"><Argument Name="Name">PermisoEditar</Argument><Argument Name="Value">Nz([Permisos].[Editar])</Argument></Action><Action Name="SetLocalVar"><Argument Name="Name">PermisoInsertar</Argument><Argument Name="Value">[permisos].[insertar]</Argument></Action><Action Name="SetLocalVar"><Argument Name="Name">UsuarioActual</Argument><Argument Name="Value">[permisos].[Usuario]</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>[IsInsert]</Condition><Statements><ConditionalBlock><If><Condition>Nz([PermisoInsertar])&lt;&gt;True</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">4000</Argument><Argument Name="Description">Usted no tiene permisos para Insertar</Argument></Action></Statements></If></ConditionalBlock><Action Name="SetField"><Argument Name="Field">CreadoPor</Argument><Argument Name="Value">[UsuarioActual]</Argument></Action></Statements></If><Else><Statements><ConditionalBlock><If><Condition>Nz([PermisoEditar])&lt;&gt;True</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">3000</Argument><Argument Name="Description">Usted no tiene permisos para Editar</Argument></Action></Statements></If></ConditionalBlock><ConditionalBlock><If><Condition>Updated("Contactos.CreadoPor")</Condition><Statements><Action Name="SetField"><Argument Name="Field">CreadoPor</Argument><Argument Name="Value">[Old].[CreadoPor]</Argument></Action></Statements></If></ConditionalBlock></Statements></Else></ConditionalBlock></Statements></DataMacro></DataMacros>

Permitir cambios solo al usuario que ha creado el registro

No tiene mucho sentido identificar al usuario que ha creado el registro y que luego lo pueda editar cualquiera. Una alternativa es identificar también al usuario que modifica el registro, para lo necesitaríamos un nuevo campo, ModificadoPor, al que le daríamos valor de la misma manera que a CreadoPor, solo que cuando no sea EsInsertar (estaría bien añadir también campos de fecha para la creación y la modificación). Otra alternativa, que no es incompatible con la anterior, es crear un sistema de permisos en el que, en los que Borrar y Editar tengan valores equivalentes “Sí”, “No” y “Sólo los registros creados por mí”, que es lo que vamos a hacer ahora.

Empezamos por modificar la tabla de permisos, cambiando Editar y Borrar de Sí/No a numérico con los valores 0,1,2 (No,Sí y ‘Los propios’)

 

image

 

Ahora, en la macro tenemos que comprobar, si no es EsInsertar, si tiene permisos para editar todo o solo para editar los registros propios y, en ese caso, verificar el creador del registro.

 

La parte de la macro que comprueba los permisos para modificar queda así:

 

image

 

Y el código xml de la macro definitiva Antes del cmbio es el siguiente:

 

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<DataMacros xmlns="http://schemas.microsoft.com/office/accessservices/2009/11/application"><DataMacro Event="BeforeChange"><Statements><LookUpRecord><Data Alias="Permisos"><Reference>qPermisos</Reference><WhereCondition>[Tabla]="Contactos"</WhereCondition></Data><Statements><Action Name="SetLocalVar"><Argument Name="Name">PermisoEditar</Argument><Argument Name="Value">Nz([Permisos].[Editar])</Argument></Action><Action Name="SetLocalVar"><Argument Name="Name">PermisoInsertar</Argument><Argument Name="Value">[permisos].[insertar]</Argument></Action><Action Name="SetLocalVar"><Argument Name="Name">UsuarioActual</Argument><Argument Name="Value">[permisos].[Usuario]</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>[IsInsert]</Condition><Statements><ConditionalBlock><If><Condition>Nz([PermisoInsertar])&lt;&gt;True</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">4000</Argument><Argument Name="Description">Usted no tiene permisos para Insertar</Argument></Action></Statements></If></ConditionalBlock><Action Name="SetField"><Argument Name="Field">CreadoPor</Argument><Argument Name="Value">[UsuarioActual]</Argument></Action></Statements></If><Else><Statements><ConditionalBlock><If><Condition>Nz([PermisoEditar])&lt;&gt;True</Condition><Statements><ConditionalBlock><If><Condition>[PermisoEditar]=0</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">3000</Argument><Argument Name="Description">Usted no tiene permisos para Editar</Argument></Action></Statements></If></ConditionalBlock><ConditionalBlock><If><Condition>[PermisoEditar]=2</Condition><Statements><ConditionalBlock><If><Condition>[Contactos].[CreadoPor]&lt;&gt;[usuarioActual]</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">6000</Argument><Argument Name="Description">Sólo puede modificar registros en la tabla 'Contactos' si es el creador del registro</Argument></Action></Statements></If></ConditionalBlock></Statements></If></ConditionalBlock></Statements></If></ConditionalBlock><ConditionalBlock><If><Condition>Updated("Contactos.CreadoPor")</Condition><Statements><Action Name="SetField"><Argument Name="Field">CreadoPor</Argument><Argument Name="Value">[Old].[CreadoPor]</Argument></Action></Statements></If></ConditionalBlock></Statements></Else></ConditionalBlock></Statements></DataMacro></DataMacros>

 

Ya solo nos queda hacer los cambios en la macro Validar eliminación para que pueda permitir borrar los registros propios. Como es similar a lo que hemos visto, me limito a pegar el código xml.

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<DataMacros xmlns="http://schemas.microsoft.com/office/accessservices/2009/11/application"><DataMacro Event="BeforeDelete"><Statements><ConditionalBlock><If><Condition>[Borrar]&lt;&gt;True</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">1000</Argument><Argument Name="Description">No se puede borrar sin haber marcado previamente el campo [Borrar]</Argument></Action></Statements></If></ConditionalBlock><LookUpRecord><Data Alias="Permisos"><Reference>qPermisos</Reference><WhereCondition>[Tabla]="Contactos"</WhereCondition></Data><Statements><Action Name="SetLocalVar"><Argument Name="Name">PermisoBorrar</Argument><Argument Name="Value">Nz([Permisos].[Borrar])</Argument></Action><Action Name="SetLocalVar"><Argument Name="Name">UsuarioActual</Argument><Argument Name="Value">Nz([Permisos].[Usuario])</Argument></Action></Statements></LookUpRecord><ConditionalBlock><If><Condition>Nz([PermisoBorrar])=False</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">2000</Argument><Argument Name="Description">Usted no tiene permisos para borrar</Argument></Action></Statements></If></ConditionalBlock><ConditionalBlock><If><Condition>[PermisoBorrar]=2</Condition><Statements><ConditionalBlock><If><Condition>[Contactos].[CreadoPor]&lt;&gt;[UsuarioActual]</Condition><Statements><Action Name="RaiseError"><Argument Name="Number">5000</Argument><Argument Name="Description">Sólo puede borrar aquellos registros creados por  vd.</Argument></Action></Statements></If></ConditionalBlock></Statements></If></ConditionalBlock></Statements></DataMacro></DataMacros>

Conclusión

Las macros en Access 2010 no serían difíciles si las utilizáramos con frecuencia, pero eso nunca va a ser ocurrir pues, para casi todo, es mas sencillo y mas cómodo utilizar VBA con el que, además, podemos llegar mucho mas lejos. Sin embargo, las macros de datos nos permiten hacer cosas impresionantes, impensables en versiones anteriores de Access e imposibles de hacer con código VBA, por lo tanto, merece la pena pagar el precio de trabajar, aunque sea torpemente, con estas macros pesadas y puñeteras e ir aprendiendo algo.

A las macros de datos les queda recorrido aparte de lo tratado aquí. No hemos vistos nada de las otras macros de datos, las de “después de”, ni de las macros de datos con nombre. Desde el punto de vista de la seguridad a nivel de usuario, nos permitirían, por ejemplo, hacer un seguimiento de cambios, con registro del usuario que los realiza y del valor anterior. Algún día, espero no tardar tanto como en esta ocasión, escribiré sobre ello.

Publicado por Chea con 1 comment(s)

Apuntes para una seguridad a nivel de usuario en Access 2010. Proteger el back-end

Las versiones de Access anteriores a la 2007 incluían un sistema de seguridad a nivel de usuario basado en grupos de trabajo bastante completo… salvo por el hecho de que, al menos en los últimos años, no servía absolutamente para nada. Resultaba del todo inútil identificar usuario y contraseña y asignar permisos en consecuencia si, con una sencilla búsqueda en interné, encontrábamos varias aplicaciones gratuitas con las que piratear las contraseñas. En consecuencia, en Access 2007 se abandona esta seguridad tan insegura.

A cambio, no se ha implementado ningún sistema de seguridad a nivel de usuario, aunque se afirma que la seguridad ha mejorado notablemente, pues ahora las bases de datos se pueden proteger por contraseña al tiempo que se cifran por un algoritmo muy seguro. A partir de ese cifrado con contraseña, cada uno debe implementar su propio sistema de seguridad por usuarios. También podríamos guardar los datos en SQL-Server o en Sharepoint, utilizar la seguridad de éstos y olvidarnos de crear la nuestra propia, pero esto a menudo no es asequible.

Con archivos accdb, que es de lo que vamos a tratar, una posibilidad interesante es crear un sistema basado en los eventos de datos de Access 2010, de esa manera la lógica de la seguridad se traslada al back-end y se centra en las tablas en vez de hacerlo en la aplicación que las maneja.

Pero, para que el sistema sea seguro, tenemos que conseguir que el back-end sea completamente inaccesible para quien no cuente con los permisos necesarios y que ello no le impida ser accesible desde el fornt-end. La solución para esto ya está estudiada y vale lo mismo para Access 2007 que para Access 2010; será de lo que tratemos en este artículo y dejaremos para otro posterior cómo usar las macros de eventos de datos de Access 2010 para asegurar nuestros datos y, en un tercero, veremos cón utilizar las macros de datos para hacer un archivo de seguimiento, un log, de los cambios en los datos del back-end.

Proteger el back-end en Access 2007/2010

A partir de Access 2007, si protegemos nuestros datos con una contraseña, quedan a buen recaudo, puesto que la contraseña es mucho más segura que en versiones anteriores y, además, los datos se cifran.

El problema está en cómo hacer que nuestra aplicación acceda a esos datos sin que la contraseña quede expuesta. Podemos evitar tener tablas vinculadas y abrir los recordsets de todos los objetos mediante código, pero eso resulta pesado y se pierde la sencillez, que es la principal ventaja de Access. Al vincular tablas de una BD protegida, Access pide la contraseña y la guarda en la propiedad Connect del objeto TableDef correspondiente a esa tabla, de manera que basta con leer la propiedad Connect de la tabla correspondiente para conocer la contraseña

Es lo que se plantea en este hilo en UtterAccess http://www.utteraccess.com/forum/Access-2007-security-t1242310.html&p=1243573#entry1243573 y ahí mismo proponen una solución que, aunque luego se complica, es bastante sencilla y es de la que partimos: Las tablas se vinculan sin contraseña, lo cual al intentar abrirlas produciría un error, pero al inicio de la aplicación creamos, mediante código en el que indicamos la contraseña, un recordset que se mantiene abierto toda la aplicación y, al quedar abierta la conexión con la BD protegida, es innecesario indicarle de nuevo la contraseña, por lo que podemos abrir las tablas vinculadas.

Ocurre algo parecido cuando utilizamos conexiones ODBC, que la contraseña se guarda y queda accesible, y la solución es la misma, no guardar la conexión con contraseña, abrir una conexión mediante código y mantenerla abierta durante toda la aplicación. En http://blogs.office.com/b/microsoft-access/archive/2011/04/08/power-tip-improve-the-security-of-database-connections.aspx, otra vez en guiri, nos cuentan cómo hacerlo.

El código puede hacerse inaccesible, por ejemplo, convirtiendo la aplicación en ACCDE y/o protegiendo a su vez, previamente, el proyecto VBA con otra contraseña. De esta manera, las tablas sólo se pueden abrir desde nuestra aplicación y, desde ella, controlamos quién y cómo puede acceder a los datos. Cualquiera podría importar nuestras tablas vinculadas desde otra BD, pero no sabiendo la contraseña, no le serviría de nada. También podría, si es un usuario autorizado, abrir directamente a las tablas de nuestra aplicación y saltarse así los permisos que damos mediante código, pero eso también podemos evitarlo, por ejemplo, comprobando que la aplicación se ejecuta en modo runtime justo antes de abrir el recordset que proporciana la clave al resto de la aplicación y, cuando sepamos asegurar los datos con macros de datos, ni siquiera podrán modicar nada sin permiso, aunque accedan a las tablas.

En resumen, los pasos para proteger nuestro back-end de una forma muy sencilla serían los siguientes:

1º- Vincular las tablas del back-end antes de cifrarlo con contraseña (de esa manera la contraseña no se guardará en la propiedad Connect)

2º - Cifrar con contraseña el back-end

image    image

3º -  Al inicio de nuestra aplicación abrir un formulario invisible (o un módulo de clase) en el que asignaremos la contraseña mediante código, y que mantendrá abierta la conexión con el back-end durante toda la aplicación.

Este último punto puede parecer difícil, pero es sencillo haciéndolo de la siguiente manera:

  • Vinculamos una tabla con datos irrelevantes. Nos pedirá contraseña y se guardará, pero más tarde la borraremos. Como ya existiría esa misma tabla vinculada, se guardará con el mismo nombre y un ordinal, por ejemplo TablaTonta1.

image

  • A partir de la tabla recién vinculada, creamos el formulario con sus campos.

image

  • Cambiamos la propiedad “Origen del registro” del formulario para dejarla en blanco.

image

  • En el evento Load del formulario abrimos un recordset que tome los datos de la BD protegida y lo asignamos como recordset del formulario.
Private Sub Form_Load()
Dim rs As DAO.Recordset
Set rs = CurrentDb.OpenRecordset("SELECT TablaTonta.* FROM TablaTonta IN '' [MS Access;PWD=Anchoas de Santoña;DATABASE=C:\Users\Chea\Documents\Back-end protegido.accdb]", dbOpenDynaset)
Set Me.Recordset = rs
End Sub
  • Probamos que el formulario se abre correctamente y cambiamos la propiead Visible del formulario a No.

    Hay un truco para obtener fácilmente una cadena SQL correcta que abra un recordset de una tabla en una BD externa protegida por contraseña:

    •  
      •  
        • En el editor de VB abrimos la ventana de inmediato, decimos que nos imprima la propiedad Connect de la tabla que tenemos vinculada con contraseña y copiamos el texto de la propiedad connect al portapapeles

    image

    •  
      •  
        • Abrimos el asistente para consultas para crear una nueva consulta y, antes de añadir una nueva tabla, copiamos el texto de la propiedad Connect a la propiedad de la consulta Cadena de conexión de origen

    image

    •  
      •  
        • Añadimos ahora la tabla que queramos. Observe que lo que se muestra ahora son las tablas y consultas de la base protegida (Sí, he dicho consultas, se puede hacer un Select a una consulta de una BD externa)
        • Seleccionamos los campos que queramos y copiamos el texto SQL resultante.

    image

    Con ese texto SQL podemos crear el recordset que asignaremos como origen de nuestro formulario.

    •  
      • Borramos la tabla que acabamos de vincular con contraseña: Ya no la necesitas, la única mención a la contraseña que existe en nuestra aplicación está oculta en el código que asigna el recordset al formulario oculto.

    Ya tenemos la manera de abrir una conexión con el back-end protegido que, permaneciendo abierta durante toda la aplicación, permita abrir otras tablas de ese back-end sin necesidad de indicar de nuevo la contraseña. Es decir, tenemos la llave para abrir los datos protegidos, sólo queda verificar el usuario antes de usar esa llave, por ejemplo, en el mismo formulario oculto, antes de asignar el recordset, podemos comprobar el usuario y sus permisos y, si no son los adecuados, en vez de asignar el recordset del formulario cerramos la aplicación. La manera de controlar usuario y permisos depende de cada cuál y no tiene mayor complicación, cuanto más personal sea, más segura, pero una forma muy sencilla podría ser tener una tabla con usuarios contraseñas y permisos en el back-end, con la que abriremos, mediante código, un recordset de la misma manera que hacíamos con la tabla tonta, para verificar los permisos, procurando primero haber borrado cualquier vínculo a la tabla.

    Con estos sencillos pasos conseguimos que no se pueda acceder a nuestros datos directamente, sino solo a través de nuestra aplicación, de manera que podemos controlar quién y cómo lo hace. Es bastante seguro para lo fácil que resulta, pero en el propio hilo en UtterAccess que citábamos más arriba, advierten de posibles grietas y posibles soluciones.

    La pega es que estamos protegiendo, o accediendo, a todo el archivo de back- end a la vez, y, si queremos dar permisos ppersonalizados por usuarios y tablas, debemos hacerlo por código, que sólo controla la edición de datos cuanto se hace a través de un formulario. El problema más obvio es que desde una instalación completa de Access, un usuario autorizado para abrir la aplicación puede pulsaf F11 para hacer que se muestren las tablas y editar los datos, saltándose todo el sistema de autorizaciones que hayamos implementado mediante código en los formularios.

    Si estamos usando Access 2007, se me ocurre la solución de forzar a que la aplicación se ejecute en modo runtime, que no muestra las tabla; simplemente bastaría con verificar en el formulario de apertura si SysCmd(acSysCmdRuntime) es verdadero y, en caso contrario, cerrar la aplicación. Otra opción sería tener varios archivos de back-end agrupando las tablas por niveles de privilegios.

    En cambio, si usamos Access 2010, cabe otra posibilidad, utilizar macros de datos en el back-end que comprueben los permisos del usuario antes de insertar, modificar o borrar datos en la tabla y, si carece de ellos, provocar un error que impida la actualización. Toda la lógica de la seguridad por usuarios se traslada al back-end, se centraliza y se hace más sencilla y, por tanto, más segura. De ello trataremos en un próximos artículo.

    Lo que aquí se ha expuesto no es más que unos apuntes que puedan servir de punto de partida para que cada cual desarrolle su propio sistema de seguridad; no obstante, sin complicar demasiado las cosas, podemos tomar algunas medidas adicionales:

    • Verificar que UserControl sea verdadero para evitar automatización
    • Comprobar que CodeProject es el mismo que CurrentProject para evitar la carga como librería
    • Proteger con contraseña el proyecto VBA y convertirlo en ACCDE.
    • Sustituir el formulario por un módulo de clase en el que el recordset sea Friend.
    • Ocultar las tablas utilizando el atributo dbHiddenObject.

    En un próximo artículo veremos, cómo utilizando un back-end cifrado con contraseña, podemos valernos de las macros de eventos de datos de Access 2010 para impedir que un usuario no autorizado pueda insertar, modificar o borrar datos, incluso aunque tuviera acceso a las tablas vinculadas del front-ed.

    Gestión de turnos de trabajo con Access

    En determinadas empresas, o en determinados servicios, se trabajan muchas más horas a las semana de las que puede hacer un trabajador. Servicios médicos, policía, bomberos, fundiciones, funerarias, etc. dan servicio 24 horas al día 7 días a la semana y, como no hay ningún trabajador capaz de tanto, deben hacer distintos turnos que cubran todo el servicio. También es necesario hacerlos en empresas, como centros comerciales, que sin abrir 24 horas abren más horas, o durante más días, de lo que corresponde a una jornada laboral normal.

    Los turnos son distintos dependiendo del tipo de trabajo, necesidades de la empresa, etc., por ejemplo, puede resultar que, dado el tipo de trabajo, por la noche se necesito menos personal, que se cierren los domingos o que no se cierre nunca, que se tenga derecho a más o menos días de descanso, etc. Dependiendo de esas necesidades, cada empresa organiza sus turnos de una manera distinta pero los resultados tienen cosas en común: Se elabora una secuencia de los distintos turnos o tipos de jornada que debe hacer un trabajador y se organiza a los trabajadores en grupos, de manera que empezando cada uno la secuencia en una fecha distinta, se solapen unos con otros y cubran toda la jornada.

    Supongamos un caso muy sencillo: Cuatro trabajadores deben cubrir mañana tarde y noche y libran un día de cada cuatro. La secuencia sería mañana, tarde, noche, libre MTNL y cada trabajador la iniciaría con un día de diferencia.

    Trabajador 1: MTNLMTNLMTNLMTNLMTNL…

    Trabajador 2: TNLMTNLMTNLMTNLMTNLM…

    Trabajador 3: NLMTNLMTNLMTNLMTNLMT…

    Trabajador 4: LMTNLMTNLMTNLMTNLMTN…

    Cada columna representa un día y con este esquema se garantiza que todos los días haya un trabajador de tarde, otro de noche y otro librando.

    La mayor parte de los esquemas serán más complejos que éste, pero lo normal es que todos tengan una secuencia de turnos fija comenzando en distinto momento para cada grupo de trabajadores. Tomando esas secuencias y esos grupos como punto de partida, vamos a proponer una serie de soluciones que faciliten la gestión de grupos de trabajo utilizando una aplicación hecha en Access.

    Averiguar el turno para una fecha

    Necesitamos saber qué turno de trabajo le corresponde a un trabajador o grupo en determinada fecha. La primera tentación puede ser recorrer un bucle partiendo desde un turno conocido, sin embargo, hay otro planteamiento que resulta familiar, pues es el del calendario, es decir, de la misma manera que ordenamos las fechas, que son números consecutivos, en 7 columnas, una para cada día de la semana, y luego podemos mediante una función saber qué día de la semana es una determinada fecha,  podemos ordenarlas en tantas columnas como tenga la secuencia.

    Enero:

    M

    T

    N

    L

    1

    2

    3

    4

    4

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

     

    Para averiguar la columna que corresponde a un determinado item, hace años que descubrí el Mediterráneo al hacerme esta función:

     

    '---------------------------------------------------------------------------------------
    ' Procedure : fColumna
    ' DateTime  : 17/10/10 15:10
    ' Author    : Chea
    ' Purpose   : Dada una relación de números consecutivos ordenados en columnas, devuelve la
    '             columna que le corresponde a un determinado item
    '---------------------------------------------------------------------------------------
    '
    Public Function fColumna(item As Long, Columnas As Long) As Long
    fColumna = 1 + ((item + Columnas - 1) Mod (Columnas))
    End Function

     

    La función vale para resolver muchos problemas si se pueden plantear a partir de números consecutivos ordenados en columnas, pero es algo genérico que presupone que la primera columna es el número 1, lo cual, como en el ejemplo, no siempre es el caso y tenemos que considerar en qué columna se encuentra el número 1. Para eso con los turnos de trabajo he creado una función específica.

    '---------------------------------------------------------------------------------------
    ' Procedure : fTurnoFecha
    ' DateTime  : 17/10/10 15:12
    ' Author    : Chea
    ' Purpose   : Dada una determinada cadencia de turnos expresada en una cadena, sCadena, en la que
    '             los turnos vienen separados por comas, obtiene el turno que corresponde a una determinada
    '             fecha, FechaCalculo. Es necesario conocer una fecha de referencia, PrimerDiaTurno,
    '             que coincida con el primer día de la cadencia.
    '             Usando una fecha fija como PrimerDiaturno,podemos pasar un parámetro opcional,
    '             lDesplazamiento, para indicar cuantos días nos desplazamos del PrimerDiaturno.
    '             Por ejemplo, podemos tener distintos grupos, cada uno empezando en un día
    '             distinto, el primero tendrá desplazamiento 0, el segundo empezará un día más tarde, etc.
    '---------------------------------------------------------------------------------------
    '
    Public Function fTurnoFecha(FechaCalculo As Date, sCadencia As String, PrimerDiaTurno As Date, Optional lDesplazamiento As Long) As String
    Dim v As Variant
    Dim lColumnas As Long
    Dim lCompensacion As Long
    
    v = Split(sCadencia, ",")
    lColumnas = 1 + UBound(v)
    
    lCompensacion = fColumna(Int(PrimerDiaTurno + lDesplazamiento), lColumnas) - 1
    fTurnoFecha = v(fColumna(Int(FechaCalculo) - lCompensacion, lColumnas) - 1)
     
    End Function

     

    A la función le pasamos FechaCalculo, que es la fecha para la que queremos obtener el turno, sCadencia, que es una cadena en la que indicamos la secuencia del turno, con los items separados por comas, por ejemplo “M,T,N,L” o “Mañana,Tarde,Noche,Libre”. También le pasamos PrimerDiaTurno que es una fecha de la que sepamos con certeza que se corresponde con el primer día de la secuencia o, de manera opcional, lDesplazamiento, que podemos utilizar para indicar cuantos días se desplaza de una fecha fija, por ejemplo 0, el primer día del turno, calculándolo por tanteo.

    Por ejemplo, el día en que, tras una ardua negociación de empresa y trabajadores, se elaboraron las planillas, se comprobó que el trabajador 2 debía iniciar su secuencia el 4 de enero del 2007 y se anotó esta fecha para posteriores cálculos, según esto, para cálcular cómo le toca la Nochebuena del 2010, el cálculo en la ventana de inmediato sería:

     

    ? fTurnoFecha (#2010/12/24#,"Mañana,Tarde,Noche,Libre",#2007/01/04#)
    Noche

    Aparte de para hacerle la puñeta al trabajador 2, la función se puede utilizar en una consulta para obtener una relación de todos los turnos de todos los trabajadores para todos los días de un periodo determinado.

     

    Elaborar un calendario con turnos

    Tomemos una tabla de empleados, con indicación de a qué grupo pertenecen

    EmpleadosTurnos
    Id Empleado Grupo
    1 Pedro 1
    2 Andrés 1
    3 Jaime 1
    4 Juan 2
    5 Felipe 2
    6 Bartolomé 2
    7 Tomás 3
    8 Mateo 3
    9 Santiago 3
    10 Judas 4
    11 Simón 4
    13 Matías 4

    Aunque el último ID sea 13, se trata de 12 empleados, pues despidieron a uno por conducta desleal. Están organizados en 4 grupos cada uno de los cuales se inicia al día siguiente del grupo anterior. Vamos a crear el calendario correspondiente a octubre del 2010 utilizando una consulta.

    Sabemos que si disponemos de una tabla Numeros poblada de numeros consecutivos empezando por el 1, podemos obtener una secuencia de fechas a base de sumar una FechaInicial menos uno al campo Numero. Este truco y el de no relacionar tablas para que el resultado sea un producto cartesiano lo vamos a utilizar en la siguiente consulta.

     

     

    image

     

    El texto SQL es el siguiente:

     

    SELECT EmpleadosTurnos.Empleado, [Numero]+CDate("01-10-10")-1 AS FechaVirtual, EmpleadosTurnos.Grupo, fturnofecha([fechavirtual],"Mañana,Tarde,Noche, Libre",CDate("04-01-07"),[Grupo]-1) AS Turno
    FROM Numeros, EmpleadosTurnos
    WHERE ((([Numero]+CDate("01-10-10")-1)<=CVDate("31-10-10")))
    ORDER BY EmpleadosTurnos.Empleado, [Numero]+CDate("01-10-10")-1;

    Y el resultado, resumiendo, porque serían 372 filas, mostraría algo parecido a lo siguiente completado hasta mostrar todos los empleados con todas las fechas de octubre del 2010 y el turno de cada fecha

     

    qTurnosVirtuales
    Empleado FechaVirtual Grupo Turno
    Andrés 01/10/10 1 Noche
    Andrés 02/10/10 1 Libre
    Andrés 03/10/10 1 Mañana
    Andrés 04/10/10 1 Tarde
    Andrés 05/10/10 1 Noche
    Andrés 06/10/10 1 Libre
    Andrés 07/10/10 1 Mañana
    Andrés 08/10/10 1 Tarde
    Andrés 09/10/10 1 Noche
    Andrés 10/10/10 1 Libre
    Andrés 27/10/10 1 Mañana
    Andrés 28/10/10 1 Tarde
    Andrés 29/10/10 1 Noche
    Andrés 30/10/10 1 Libre
    Andrés 31/10/10 1 Mañana
    Bartolomé 01/10/10 2 Tarde
    Bartolomé 02/10/10 2 Noche
    Bartolomé 03/10/10 2 Libre
    Bartolomé 04/10/10 2 Mañana
    Bartolomé 05/10/10 2 Tarde
    Bartolomé 06/10/10 2 Noche
    Bartolomé 07/10/10 2 Libre
    ..

     

    La consulta la podemos personalizar para pasarle las fechas inicial y final del periodo como parámetros y también podemos tomarla como puento de partida para una consulta de datos anexado o de actualización que para cada día del año y cada empleado deje anotado el turno de trabajo. Aunque no es necesario volcar los datos en una tabla, pues podemos utilizar la consulta con cualquier periodo de fechas, si resulta muy conveniente, pues además de los distintos turnos pueden darse otras situaciones, como bajas, vacaciones, cambios de turno que hacen necesario trabajar con datos guardados.

    A menudo se pretende realizar una planilla que refleje los trabajadores que entran en cada turno cada día, utilizando los días como encabezado de columna.

     

     

    qTurnosVirtuales_Tabla de referencias cruzadas
    Empleado Grupo 01/10/10 02/10/10 25/10/10 26/10/10 27/10/10 28/10/10 29/10/10 30/10/10 31/10/10
    Andrés 1 Noche Libre Noche Libre Mañana Tarde Noche Libre Mañana
    Jaime 1 Noche Libre Noche Libre Mañana Tarde Noche Libre Mañana
    Pedro 1 Noche Libre Noche Libre Mañana Tarde Noche Libre Mañana
    Bartolomé 2 Tarde Noche Tarde Noche Libre Mañana Tarde Noche Libre
    Felipe 2 Tarde Noche Tarde Noche Libre Mañana Tarde Noche Libre
    Juan 2 Tarde Noche Tarde Noche Libre Mañana Tarde Noche Libre
    Mateo 3 Mañana Tarde Mañana Tarde Noche Libre Mañana Tarde Noche
    Santiago 3 Mañana Tarde Mañana Tarde Noche Libre Mañana Tarde Noche
    Tomás 3 Mañana Tarde Mañana Tarde Noche Libre Mañana Tarde Noche
    Judas 4 Libre Mañana Libre Mañana Tarde Noche Libre Mañana Tarde
    Matías 4 Libre Mañana Libre Mañana Tarde Noche Libre Mañana Tarde
    Simón 4 Libre Mañana Libre Mañana Tarde Noche Libre Mañana Tarde

     

    Evidentemente es fácil de hacer con una consulta de referencias cruzadas.

     

    image

     

    El texto SQL sería:

    TRANSFORM First(qTurnosVirtuales.Turno) AS PrimeroDeTurno
    SELECT qTurnosVirtuales.Empleado, qTurnosVirtuales.Grupo
    FROM qTurnosVirtuales
    GROUP BY qTurnosVirtuales.Empleado, qTurnosVirtuales.Grupo
    ORDER BY qTurnosVirtuales.Grupo
    PIVOT qTurnosVirtuales.FechaVirtual;

     

    Si nuestra consulta de referencias cruzadas la convertimos en un informe y usamos formato condicional, el resultado es la mar de vistoso (o la mar de hortera, según gustos). En el ejemplo le hemos quitado columnas para que nos quepa bien en la página.

    image

     

    En fin, se trata de una serie de ideas que pueden servir a alguien que debe plantearse la gestión de turnos de trabajo, pero no pretende ser ni “la solución” ni la forma canónica de plantearlo.

    Publicado por Chea con 8 comment(s)
    Archivado en: ,

    Hola a Office 365, adiós a Office Live Small Business

    Al tiempo que se anunciaba el lanzamiento de la beta de Office 365, el Access Team Blog anunciaba que Office 365 incluirá Access Services y, por tanto, Access web Databases y en la comunidad online de Office Live Small Business (OLSB) daban la noticia de que, ante la llegada de Office 365, OLSB desaperecerá en el plazo de un año.

    Habrá un periodo de transición para migrar de OLSB a Office 365 para pequeñas empresas a finales del 2011 o principios del siguiente, con un periodo gratuito de tres meses, pero después habrá que pagar por el servicio. Hay quien dice que a Manolete le mató el toro porque la corrida era de beneficencia y eso mismo ha debido pensar Microsoft. Para quienes migren desde OLSB, el precio parece que no será muy caro, unos 6$ al mes por cuenta, muy poco para una empresa incluso pequeña, pero bastante para sustituir a algo que se prometió como gratis de por vida (claro, de la vida del producto que ahora eliminan Triste).

    Para quienes queremos jugar con las posibilidades de las aplicaciones web de Access 2010 o utilizar Sharepoint como back end de nuestras aplicaciones Access de escritorio, el anuncio es realmente prometedor pues el rendimiento Access Services es muy superior al de las listas vinculadas en Sharepoint 2007, por el volumen de datos que es capaz de gestionar sin problemas y por la velocidad notablemente superior y porque, sencillamente, las aplicaciones web de Access 2010 son impensables en Sharepoint 2007. Hasta ahora, lo más barato con Access Services era AccessHosting a partir de 19$ mes el pack developer, por lo que el precio de Office 365 para pequeñas empresas resulta muy competitivo, menos de un tercio, y eso sin contar con que los Access Services son la parte más pequeña del paquete de Office 365, que incluye Office Professional, Exchange Online, Sharepoint Online y Lync Online.

    Para quienes hemos utilizado OLSB precisamente porque era un recurso gratuito de por vida, no deja de ser una faena. Algunas aplicaciones de escritorio en Access 2007 podían compartir unos pocos datos en la web de forma muy sencilla y sin coste alojándolos en OLSB. Algunos nos hemos entretenido en diseñar en OLSB páginas web sencillas para otros que no se pueden permitir un coste superior a gratis, el pequeño negocio del amigo que empieza, una actividad del colegio, etc., precisamente porque OLSB ofrecía un alojamiento gratuito con una calidad aceptable. Unos y otros, en ambos casos yo, ahora se ven decepcionados, pues no necesitaban nada más y ahora se lo quitan, ofreciéndoles a cambio, de pago, lo que no han pedido.

    Así que mi entusiasmo por lo uno frena mi decepción por lo otro y viceversa y, de momento, ando más bien perplejo.

    Tengo muy claro que contrataré Office 365 para la pequeña empresa por utilizar los Access Services y para seguir alojando mi página, pero para aquellas cosas en las que utilizaba OLSB por ser gratuito, empezaré a irme pasando a Google Sites ¿Habrá valorado Microsoft que esta última decisión será la lógica para miles de usuarios de OLSB?

    Algunas joyas escondidas en Access 2010

    Access Services

    Estoy impresionado. He aprovechado la oferta de Access Hosting que el otro día anunciaba el Access Team Blog para abrir una cuenta “trial” de Access Services. He probado a crear y subir una aplicación web hecha con una plantilla de Access 2010 y, como era de esperar, ha funcionado a la perfección.

    Las aplicaciones web funcionando en Sharepoint 2010 son la novedad estrella de Access 2010, sin embargo, lo que me ha impresionado ha sido algo mucho más simple: He subido una sencilla aplicación de escritorio y funciona de maravilla, a una velocidad equiparabla a la de cualquier aplicación web; naturalmente, no se ejecuta en Sharepoint, sino que lo usa de Back End, pero hace mucho más, pues no sólo aloja y sirve datos, sino que también guarda todos los demás objetos de la aplicación.

    Navegando por el sitio creado en Sharepoint para nuestra aplicación, tenemos una opción para modificar la base de datos:

    image

    Pinchas en ella y, en pocos minutos, se ha descargado una copia de la aplicación perfectamente operativa que trabaja con los datos alojados en el sitio. La siguiente vez que pinches, sólo tardará un poco más de lo que tardarías en abrir la aplicación en el escritorio. Para conseguir esto, durante el proceso de subida a Access Services se han ido sincronizando no sólo tablas y consultas, sino todos los objetos; así, cuando realizamos una modificación en nuestra BD, en cualquier objeto, no sólo en los datos, el cambio se realiza también en el sitio Sharepoint, de manera que siempre está disponible desde la web la última versión. Una maravilla.

    Como la opción para modificar la base de datos es un acceso directo, podemos copiarlo y usarlo directamente o, remitirlo a otros  usuarios, en vez de andar navegando por el sitio.

    Esto me ha reconciliado con la proyección a la nube de Access 2010. Las listas vinculadas en Access 2007 son una opción interesante, pero bastante más limitada y lenta de lo que se prometía, para colmo, lo que tuviéramos hecho con listas vinculadas ya no nos vale ahora para los Access Services, de manera que no estaba muy seguro de que el premio mereciera el trabajo de aprender a manejar las macros, el formulario de navegación, etc.. Ahora, sólo por esta posibilidad de tener los datos en la web de manera fácil u operativa, ya merece la pena subirlos y, ya puestos….

    Access 2010 viene con grandes novedades de las que, desde el inicio de la Vista Previa Técnica, el Access Team Blog nos viene poniendo al día. Eventos y macros de datos, campos calculados en tablas, aplicaciones en la web, formularios de navegación, control WebBrowser vinculado a datos… son serias innovaciones que cambiarán la forma de trabajar con Access y que parecen todas orientadas a las aplicaciones web. Había dejado para cuando estuviera con más ánimo el ir probando todo lo referente a esas novedades estrella,  pero, mientras, he ido descubriendo cosas más terrenales, mejoras sobre cosas que ya se venían usando, que o no se mencionan en los blogs, o lo hacen de pasada, pero que por sí solas ya justificarían una nueva versión. A esas pequeñas joyas voy a referirme ahora.

    Formato condicional

    Casi parece que no ha cambiado en nada, a simple vista, los asistentes parecen los mismos, pero uno se pone a meter hasta cuarenta y nueve formatos condicionales y no se queja, mientras que antes venía limitado a tres.

    image

    Si, además, nos fijamos bien en el asistente, vemos que tiene un desplegable, “Seleccionar tipo de regla” con una opción, comparar con otros registros.

    image image

     

    Seleccionamos entre distintas opciones, y el resultado es una barra proporcional a los distintos valores de nuestro formulario o informe, un estilo a lo que ya existía en Excel

    image

    No sólo funciona con informes y formularios continuos. También se pueden usar, y funciona correctamente, en formularios estándar

    image

    Texto enriquecido en subformularios

    Aunque parece que lo corregirán en breve, en Access 2007 no era posible editar el texto enriquecido en los subformularios. Ahora sí, en cualquier lugar, incluso en subformulario continuos, aunque sean en vista Hoja de Datos.

    image

    SubInformes en formularios

    Estamos tan acostumbrados a que no se puedan meter SubInformes en formularios que hasta cuesta pensar en la utilidad que pueden tener.

    Por ejemplo, haciendo un refrito, en muy pocos minutos hemos convertido el  informe “Listín de teléfonos” en panel de navegación del formulario “Detalle de clientes” de la BD “NorthWind”.

    image

    Se me ocurre que navegar de manera semejante por un Diario o un Libro Mayor correctamente formateados en un subinforme, con sus Totales por asiento, Sumas Contínuas, etc., dentro de un formulario para editar los asientos, puede resultar muy vistoso, incluso muy útil, y realmente fácil de hacer.

    Se trata de una “Vista Informe” por lo que faltan las opciones de impresión que tiene la “Vista Previa”, pero, a cambio, podemos disponer de eventos (Clic, doble-clic…) filtros, etc.. Nada cuesta poner un botón en el informe o en el Ribbon para que nos muestre una “vista previa”

    En otros casos, como corresponde a un SubInforme, está vinculado al Formulario principal por las propiedades LinkChildFields y LinkMasterFields, de manera que, al cambiar de registro el formulario, el subinforme a la vista se filtra según estos campos.

    También existe la posibilidad contraria, la de mostrar subformularios en informes. El subformulario no es editable por lo que no le veo ninguna ventaja sobre un subinforme en vista informe, salvo ahorrarse el trabajo de crearlo, pero seguro que, entre las muchísimas barrabasadas que se harán con esta posibilidad, alguien le encontrará un uso brillante.

    Plantilla de ajuste de controles

    Se trata de que los controles se ajustan a una plantilla o tabla, como en las hojas de estilo en el diseño web, en la que podemos añadir o quitar filas y columnas, cambiar alto y ancho de celdas, dividir éstas o combinar varias entre sí.

    Access 2007 ya había aportado algo a la colocación de los controles, agrupándolos en formato tabular o apilado, que sigue siendo posible, pero, aunque era una forma muy rápida de poner cierto orden visual, no era una solución muy lograda, ya que todos los controles apilados en el mismo grupo, tenían el mismo ancho.

    En Access 2010 cuando agrupamos controles, de forma apilada o tabular, podemos moverlos a un lado, encima o debajo, como si añadieramos filas y columnas a una tabla de word; de la misma manera, podemos combinar o dividir esas celdas y los controles que coloquemos se ajustarán a ellas. Es como si debajo de los controles hubiera una tabla o plantilla invisible. Verla es fácil utilizando otra de las novedades de Access 2010, las líneas de división también en formularios.

    image image image imageimage

    Botones con formas, colores y relieves

    Los botones, incluidos los de los TabControls, pueden tener distintas formas colores y texturas. Como muestra, unos pocos:

    image

     

    El botón “Estilos rápidos” muestra una galería rápida, pero también podemos combinar “Cambiar forma”, “Relleno de forma”, “Contorno de forma” y “Efectos de forma” para conseguir un montón de posibilidades.

    imageimage image image image image imageimage

         

    Las formas y colores de los botones no quitan para que puedan seguir teniendo imágenes y pie de imagen, por lo que podemos conseguir unos botones resultados muy vistosos en los que, además, se sigue visualizando el efecto de apretar el botón:

    image

    Los botones con formas y colores se ven afectados por el “Tema” que hayamos elegido. Al cambiar de tema, cambia la galería de “Estilos rápidos” y, si ya lo hemos creado, también cambia la apariencia de nuestro botón.

    image image

     

    Si queremos cambiar la imagen de un viejo formulario Access 2007, de entrada puede que no tengamos habilitadas estas opciones. Se soluciona fácilmente cambiando la propiedad “Usar tema” del control.

    Si nuestra aplicación es en formato mdb, no existe esta propiedad y, por tanto, tendremos que conformarnos con los botones tradicionales.

     

    Generador de expresiones

    Cambia la interface del generador de expresiones.

    image

    Es más completa y con grafía que diferencia los objetos, pero lo más novedoso es que aplica el “intellisense” en aquellas propiedades en que se puede usar el generador.

    image 

     

    Imágenes compartidas

    Aunque ya en Access 2007 las imágenes se guardan en el tamaño del formato original, no deja de ser un desperdicio de espacio repetir la misma imagen, por ejemplo el logotipo de la empresa, en cada formulario o informe. Aunque había soluciones, ésta síque es sencilla de manejar.

    image

    Una vez que para una imagen en la propiedad “Tipo de imagen” hemos elegido “Compartidas”, queda guardada y a disposición de los demás objetos de la aplicación en la galería que se despliega al pulsar el botón “Insertar imagen”.

    image 

    Las imágenes compartidas se guardan como datos adjuntos en la tabla MSysResources, de manera que sólo ocupan espacio una sóla vez. Contiuará existiendo en nuestra BD, y ocupando ese espacio, mientras no la borremos de aquí. Si queremos cambiar el anagrama de nuestra empresa en todos los formularios e informes, basta con que lo cambiemos aquí.

     

    Título en la barra de navegación

    Un pequeño detalle, casi insignificante, pero que refleja cómo en Access 2010 nos encontramos con mejoras a cada paso. Con la propiedad “Título de navegación” podemos indicar qué queremos que ponga en la barra de navegación en vez de “Registro”. Sin pasarse, eh, que el espacio reservado para ello no aumente ;-)

     

    imageimage

     

     

    Publicado por Chea con 15 comment(s)
    Archivado en:

    Magia Potagia II: El desenlace

    En el capítulo anterior habíamos usado ingredientes mágicos de Access 2007 para conseguir crear una serie de calendarios sin apenas usar código. Hoy se trata de licenciarnos como aprendices de brujo haciendo que la magia trabaje para nosotros resolviendo problemas de la vida real.

    Necesitaba crear un subinforme que, para cada uno de los elementos del informe principal, me mostrara un conjunto de fechas representadas en tantos calendarios mensuales como fueran necesarios para mostrar todo el periodo entre la primera y la última. Más o menos, como en las imágenes.

     image     image

    Evidentemente, ya lo he hecho y el caso real lleva tiempo funcionando correctamente. Hay una demo, la de las imágenes, que se puede descargar en Access siglo XXI.

    La magia en la vida real tiene un problema: que no hay quien entienda cómo funciona. Lo mágico de estos calendarios es que apenas usan código, pero el código se puede leer, está estructurado, se autodocumenta; es como un plano de la aplicación. Sin código es muy difícil ver cómo está hecho algo, tanto que, para que el que quiera pueda adaptar mi ejemplo, he tenido que incluir un pequeño asistente.

    No obstante, voy a intentar explicar cómo está construido, aunque me temo que será bastante pesado. Cuando empieces a aburrirte, salta directamente al último punto, al desenlace.

    El punto de partida es una tabla con un campo de fecha y un campo ID numérico, en este caso de usuario, es decir, con un conjunto de fechas, que son las que queremos mostrar en calendario, para cada usuario.

    image

    El idEmpleado es númerico y si se muestran nombres en la imágen es porque uso un campo de búsqueda.

    En el ejemplo del capítulo anterior utilizaba una tabla tipo Calendar, con campos StartDate y EndDate, y mediante una consulta, la convertía en un resultado parecido a éste. Sin embargo, aunque no lo decía, trabajar directamente con la consulta afectaba mucho al rendimiento y sería preferible volcar los resultados en una tabla temporal.

    Obtener todas las fechas de cualquier año

    El primero de los ingredientes mágicos , es una tabla Numeros, con un campo Num con número consecutivos. La mía tiene poco más de mil.

    image

    A partir de esta tabla, mediante una consulta construimos todas las fechas que se corresponden con las del calendario “de fondo”

    SELECT [Numero]+DateSerial(nz(TempVars!jbAnyoInicial,Year(Date())),1,1)-1 AS Fecha, Format([Numero]+DateSerial(nz(TempVars!jbAnyoInicial,Year(Date())),1,1)-1,"dddd") AS Dsemana, (Month([Numero]+DateSerial(nz(TempVars!jbAnyoInicial,Year(Date())),1,1)-1)) AS Mes, (1+(([Numero]+DateSerial(nz(TempVars!jbAnyoInicial,Year(Date())),1,1)-1-2)\7)) AS Fila, Year([Numero]+DateSerial(nz(TempVars!jbAnyoInicial,Year(Date())),1,1)-1) AS Anyo
    FROM Numeros
    WHERE (((Year([Numero]+DateSerial(nz([TempVars]![jbAnyoInicial],Year(Date())),1,1)-1))=nz([TempVars]![jbAnyoInicial],Year(Date()))));

    Mediante el procedimiento de sumar al campo Num una fecha inicial menos uno y hacer que sea menor o igual que una fecha final, podemos obtener cualquier rango de fechas que no supere el total de registros de nuestra tabla Numeros. Pero en el informe podemos superar este límite usando otro de los ingredientes mágicos de Access 2007, TempVars. Al utilizar una variable TempVars en el cálculo de la fecha inicial, podemos cambiar ésta sobre la marcha en el informe de manera que la consulta se vaya recalculando las veces que sea necesario. Ya nos pararemos en ello más adelante; de momento, guardamos esa consulta como jbFechasVirtuales y los resultados sería algo así:

    image

     

    Resaltar los festivos de cada empleado

    Sobre ese “calendario de fondo” se trata de resaltar las fechas de nuestra tabla tblFechasEmpleados usando el tercer ingrediente mágico de A2007, el formato de texto enriquecido. Comparamos tabla y consulta y en las fechas coincidentes añadimos las etiquetas html necesarias para mostrar el resultado resaltado, en nuestro caso en rojo, tal como habíamos visto en el capítulo anterior.

    Sin embargo, antes vamos a anticiparnos con un problema con el que nos toparíamos más adelante en el caso real. El objetivo es presentar los calendarios como subinformes, pero si construimos y formateamos como calendarios todo el conjunto de datos, al vincular por un campo ID informe y subinforme y, por tanto, filtrar los datos de éste, destrozamos el formato de calendario que hemos dado a todo el conjunto. En resumen, que debemos dar forma de calendario a cada subconjunto en vez de filtrar el conjunto de datos total y para ello la mejor solución que se me ha ocurrido es volver a usar TempVars de manera que, cambiando sobre la marcha la variable TempVars!jbID cada vez que cambie el ID del informe principal se recalcula la consulta en la que se basa el subinforme calendario filtrándose por ese ID.

    Así, en vez de usar directamente la tabla tblFechasEmpleados, usaré la consulta que he llamado jbqFestivosVirtuales y que tiene el siguiente SQL.

    SELECT tblFechasEmpleados.idEmpleado, tblFechasEmpleados.Fecha
    FROM tblFechasEmpleados
    WHERE (((tblFechasEmpleados.idEmpleado)=[TempVars]![jbID]));

    Ahora ya podemos juntar las dos últimas consultas para resaltar con formato de texto enriquecido los festivos de los empleados.

    SELECT jbqFechasVirtuales.*, IIf(Not IsNull([jbqFestivosFiltrados].Fecha),"<font color=red>" & Day(jbqFechasVirtuales.fecha) & " </font>",Day(jbqFechasVirtuales.fecha)) AS Expr1
    FROM jbqFechasVirtuales LEFT JOIN jbqFestivosFiltrados ON jbqFechasVirtuales.Fecha = jbqFestivosFiltrados.Fecha
    ORDER BY IIf(Not IsNull([jbqFestivosFiltrados].Fecha),"<font color=red>" & Day(jbqFechasVirtuales.fecha) & " </font>",Day(jbqFechasVirtuales.fecha));

    Guardamos la consulta como jbFechasyFestivosVirtuales y el resultado es como en la imagen siguiente (hasta 365 días) donde destaca un campo que en ocasiones pone algo como <font color=red>10</font> con el que más adelante conseguirems que el número 10 se muestre en rojo.

    image

     

     

    Ya tenemos una secuencia con todas las fechas del año para un determinado usuario con los festivos formateados para que más adelante se muestren en rojo. Para que se parezca a un calendario falta ordenarlo en columnas por días de la semana.

    Dar a los datos forma de calendario

    Ya habíamos visto en el capítulo anterior que para ordenar en columnas por días de la semana una secuencia de fechas podemos usar una Consulta de Referencias Cruzadas que voy a llamar jbqCalendarioFormateado y que será el origen de datos del subinforme calendario:

    TRANSFORM Min([jbqFechasyFestivosVirtuales].Expr1) AS MínDeExpr1
    SELECT [jbqFechasyFestivosVirtuales].Mes, [jbqFechasyFestivosVirtuales].Anyo, [jbqFechasyFestivosVirtuales].Fila
    FROM jbqFechasyFestivosVirtuales
    GROUP BY [jbqFechasyFestivosVirtuales].Mes, [jbqFechasyFestivosVirtuales].Anyo, [jbqFechasyFestivosVirtuales].Fila
    PIVOT [jbqFechasyFestivosVirtuales].Dsemana In ("lunes","martes","miércoles","jueves","viernes","sábado","domingo");

    El resultado, algo parecido a esto:

    image

    El subinforme calendario

    Es pequeñito, para que me quepan más calendarios en una hoja, que hay que ahorrar papel,

    image

    El Origen de Datos es la consulta jbaCalendarioFormateado y el truco principal es que los campos Lunes, Martes, Miércoles … están ocultos (en la imagen en amarillo) y sobre ellos se superponen sendos campos calculados dLunes, dMartes, dMiércoles… que tienen por origen el campo que está debajo. Al ser campos calculados, pueden tener formato de Texto enriquecido, que no es posible en campos de texto, de manera que el campo que antes habíamos construido con etiquetas html del estilo <font color=red>10</font>, ahora se mostrá como 10.

    También están ocultos los campos Mes y Anyo, que nos van a servir para vincular con el subinforme MesesAnyos.

    Este subinforme no tiene ni una sola línea de código.

    El subinforme Meses-Año

    El subinforme anterior muestra un solo calendario, pero necesitamos que nos muestre tantos calendarios como sean necesarios en el subinforme. Por eso lo incrustamos en otro subinforme Meses-Año que tiene todos los meses necesarios para mostrar las fechas de cada empleado.

    image

    El origen del subinforme es la consulta jbqMesesAnyos y tiene por objetivo obtener tantos registros identificados por mes y año como sean necesarios para mostrar todas las fechas de un empleado:

    SELECT ((([Numero]-1)\12)) AS Fila, 1+([Numero]-1) Mod 12 AS numMes, MonthName([NumMes]) AS NombreMes, (([Numero]-1)\12)+Year(DMin("[Fecha]","jbqFestivosFiltrados")) AS Anyo, DateSerial(((([Numero]-1)\12))+Year(nz(DMin("[Fecha]","jbqFestivosFiltrados"))),1+([Numero]-1) Mod (12),1) AS Expr1, [TempVars]![jbID] AS jbID
    FROM Numeros
    WHERE (((DateSerial(((([Numero]-1)\12))+Year(nz(DMin("[Fecha]","jbqFestivosFiltrados"))),1+([Numero]-1) Mod (12),1)) Between nz(DMin("[Fecha]","jbqFestivosFiltrados"))-30 And nz(DMax("[Fecha]","jbqFestivosFiltrados")) And (DateSerial(((([Numero]-1)\12))+Year(nz(DMin("[Fecha]","jbqFestivosFiltrados"))),1+([Numero]-1) Mod (12),1))>0));

    image

    La consultita se las trae, pero, resumiendo, obtiene los meses y año del subconjunto de fechas de un empleado a partir de la tabla Numeros y usando como filtro las fechas mayor y menor de la consulta jbqFestivosFiltrados. Además, obtiene el campo jbID, por el que se vincula con el informe principal, de la Variable Temporal TempVars!jbID.

    En la seccion detalle tenemos el subinforme jbSubRptCalendarioFormateado y, cada vez que se formatea esa sección, cambiamos mediante código la variable temporal TempVars!jbAnyoInicial que, como vimos se usaba en la primera consulta, jbFechasVirtuales, de manera que al cambiarse, esto sí que es magia, se se recalcula toda la consulta y se refresca el subinforme jbSubRptCalendarioFormateado.

    Este es el todo código del subinforme:

    Option Compare Database
    Option Explicit
    
    Private Sub Detalle_Format(Cancel As Integer, FormatCount As Integer)
    TempVars!jbAnyoInicial = Me.Anyo.Value
    Me.SubRptCalendario.Requery
    End Sub
    
    Private Sub Report_Close()
    TempVars.Remove ("jbAnyoInicial")
    End Sub
    
    
    Private Sub Report_Open(Cancel As Integer)
    TempVars!jbAnyoInicial = Year(DMin("Fecha", "jbqFestivosFiltrados"))
    End Sub

    El informe principal

    En el informe principal incrustamos el subinforme jbSubRptMesesAnyo y necesitamos un campo ID por el que vincular con éste.

    image

    Es necesario que tengamos una agrupación por ese campo ID y que ese campo se encuentre en la sección encabezado de ese grupo.

    ¿Por qué? Por que es la única forma que he conseguido que me funcione el abracadabrante rizado del rizo:

    Los campos por los que se vinculan informe y subinforme son respectivamente id y jbid.

    image

    En el evento Format de la sección EncabezadoDelGrupo0 le doy a la variable temporal TempVars!jbID el valor de ese campo ID y, como resulta que el campo jbID del subinforme lo toma de esa variable temporal, estoy asignando sobre la marcha y mediante código en el informe principal el valor del campo del subinforme por el que éste se vincula con el informe principal ¡Con dos c…!

    Para más magia potagia, cambiar por código TempVars!jbID, como forma parte de la primera consulta, jbFechasVirtuales, hace que ésta se refresque y haga los cálculos sólo para el empleado del ID. Como también está en la consulta jbqFestivosFiltrados y del máximo y mínimo de ésta depende la consulta jbqMesesAnyo, no sólo se restringen los festivos a los del empleado, sino que se recalcula jbqMesesAnyo, que es precisamente el origen del subinforme.

    Todo el código necesario es el siguiente:

    Option Compare Database
    Option Explicit
    
    Private Sub EncabezadoDelGrupo0_Format(Cancel As Integer, FormatCount As Integer)
    TempVars!jbID = Me.ID.Value
    Me.subRptMesesAnyo.Requery
    End Sub
    
    Private Sub Report_Close()
    TempVars.Remove ("jbID")
    End Sub
    
    Private Sub Report_Open(Cancel As Integer)
    TempVars!jbID = DFirst("id", Me.RecordSource)
    End Sub

    El desenlace

    Yo ya advertí que iba a ser aburrido. Resumiendo, usando TempVars y consultas basadas en una tabla Numeros, se pueden hacer cosas inimaginables de otra manera.

    Es magia empaquetada. No es necesario entenderla toda ni seguir complicados rituales para usarla en una aplicación propia pues, de todos los objetos y consultas que utiliza sólo es necesario cambiar el informe principal y la consulta jbqFestivosFiltrados. En Access siglo XXI puedes descargarte la aplicación de ejemplo que, además, tiene un asistente elemental para modificar esa consulta y el código del informe principal.

    ¿De dónde he sacado yo estas cosas de mágia? Ni idea, supongo que andaré hechizado, aunque algo de inspiración se me habrá pegado de Ramón Poch o de Julián Sánchez, aficionados también a jugar con una tabla Numeros.

    Por cierto Dominguín iba a las siete de la mañana… ¡A contarlo!

    Más artículos Página siguiente >