Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Siguiendo con mi idiosincrasia a la hora de redactar mis ejemplos, y relacionado con lo que os
escribía en el párrafo anterior, en algunos puntos me detendré a explicar “qué pasaría si...”,
dado que eso me lleva automáticamente a que veáis el “por qué lo hago así”. Creo que es una
manera muy práctica de que podáis ver “los problemas con los que me he topado” y cómo los
he ido resolviendo.
PRIMERA PARTE
1
Visítame en http://bit.ly/NckAccess
Y, a mayor abundamiento, los datos que recogerá serán del estilo:
Vamos a necesitar una tabla auxiliar que nos va a recoger el intervalo de horas que vamos a
querer que se muestre en nuestra agenda semanal, y que llamaremos THoras. Por ejemplo, y
para este ejemplo, yo he creado un intervalo de horas desde las 8:00 a las 20:00, con una
periodicidad de cada media hora.
Ni que decir tiene que la información de esta tabla puede ser la que queráis. Por ejemplo, yo
podría haber empezado por otra hora y que los bloques fueran de dos horas de duración, así:
Hasta aquí nuestras tablas básicas. Sin embargo, nos falta una tabla temporal que recogerá los
datos filtrados por la semana que el usuario haya seleccionado. Así que crearemos otra tabla
que llamaremos TTempAgenda, con la siguiente estructura inicial:
2
Visítame en http://bit.ly/NckAccess
Vamos a tener que realizar un pequeño ajuste a nuestra tabla temporal, pero lo veremos más
adelante.
Dicho de otra manera, voy a necesitar que si se selecciona la semana del 21 al 27 de agosto se
creen tantas horas necesarias para el 21 como horas hemos determinado en THoras. Lo mismo
para el día 22, 23… hasta el 27.
Eso no lo haremos con la consulta CTempAgenda, sino que lo haremos después, al trabajar
con el código. Lo que sí os mostraré serán los resultados parciales de esa acción:
Una primera aproximación para esa consulta sería relacionar ambas tablas por el primer
elemento del binomio fecha/hora: la fecha. Si empezamos la consulta con esa idea
obtendríamos:
Sin embargo, lo que obtenemos es un resultado extraño, que no nos da los resultados
correctos. Si tuviéramos una cita el 21/08 a las 08:30, por ejemplo, obtendríamos:
3
Visítame en http://bit.ly/NckAccess
Es decir, solo nos mostraría el día de la cita y la cita en todas las horas. Si quisiéramos
arreglar lo anterior, y dado que los 175 registros que nos interesan inicialmente (insisto en lo
de inicialmente aunque podrían ser más, pero eso os lo explicaré más adelante: como os decía
en la introducción, resulta muy difícil explicaros “linealmente” el ejemplo), podríamos cambiar
el tipo de combinación de la relación y establecer que se nos muestren todos los registros de
TTempAgenda y solo los coincidentes en TCitas:
Ahora nuestra consulta sí nos mostrará los 175 registros, pero, ¡oh desgracia!, en todos los
registros muestra la misma cita:
¿Qué se nos ocurre? Pues relacionar también por el campo de la hora de la cita. ¿Sencillo, no?
Sencillo, quizás, pero efectivo…
4
Visítame en http://bit.ly/NckAccess
Y cuando queremos ejecutar la consulta nuestro amigo Access nos
echa un cubo de agua fría sobre la cabeza
Vemos que necesitamos dos campos para poder relacionar en la consulta. Sin embargo,
resulta imposible, así como hemos hecho la aproximación, que nuestra consulte se ponga a
andar. ¿Cómo podemos resolver esta situación?
La respuesta está en: si con dos campos no funciona, convirtamos esos dos campos en un solo
campo. Y, a efectos de este campo, voy a dejar que Access haga todo el trabajo. Así pues, lo
que haremos será situar la tabla TCitas en vista diseño y añadir un campo calculado, llamado
[FHCita], de la siguiente manera:
Fijaos que la expresión del campo calculado concatena ambos campos, el de la fecha y el de la
hora.
5
Visítame en http://bit.ly/NckAccess
Fijaos que, para que me queden los datos bien ordenados, he fijado el criterio de ordenación
en “Ascendente” tanto en la fecha como en la hora.
Ahora el resultado de la consulta, siguiendo con la idea de nuestra cita del 21/08 a las 8:30, se
mostrará de la siguiente manera:
Y, dado que nos hemos construido esta consulta, vamos a aprovecharla para añadir una serie
de campos calculados que nos proporcionarán unos datos que vamos a necesitar más
adelante. De nuevo haced un acto de fe y ya os explicaré después por qué vamos a necesitar
esos datos.
Así que en CTempAgenda vamos a añadir tres campos calculados, que serán los siguientes:
El primero, para conseguir del nombre del día de la semana (teniendo en cuenta que el último
cero significa que estamos indicando que la semana empieza en lunes):
NomDiaSem: NombreDíaDeLaSemana(DíaSemana([FechaCitaTemp];0))
El segundo nos va a concatenar la fecha con su nombre de día de la semana. Este campo me
dará el trabajo hecho cuando tenga que configurar los encabezados del informe (que veremos
más adelante), y por eso lo he llamado [Cabecera]:
El tercero, que nos va a decir qué dia de la semana es en número; esto es, nos dará un 1 si es
lunes, un 2 si es martes, y así hasta 7, domingo.
DiaSem: DíaSemana([FechaCitaTemp];0)
6
Visítame en http://bit.ly/NckAccess
Y los resultados, según veníamos trabajando con el ejemplo del 21/08, serían:
Así que, para conseguir los datos mínimos, a mano vamos a introducir directamente en
TTempAgenda los siguientes datos: elegimos una semana cualquiera y, muy importante,
introducimos siete registros, uno por cada día de la semana, con la hora que queramos,
empezando por el lunes. Por ejemplo, si yo elijo la semana del 7 de agosto de 2017 mis datos
serían:
¿Qué formato necesitamos, pues? La respuesta es obvia: un formato de agenda. Es decir, algo
del estilo…
7
Visítame en http://bit.ly/NckAccess
XX/XX/XX XX/XX/XX
lunes ... domingo
8:00
8:30 Cita Z
9:00 Cita W
...
2.- Como título de fila, si seguimos nuestro formato de ejemplo, vemos que debemos elegir las
horas; esto es, [HoraCitaTemp]
3.- Como encabezado de columna vamos a elegir… uhmmm… ¿qué elegimos? Lo lógico sería
pensar que, en atención a nuestra tabla de formato, el campo que deberíamos elegir sería
[Cabecera]. Sin embargo, hacerlo así sería un error, y al hablar del futuro informe que
necesitamos crear os explicaré por qué. Por ahora, y de nuevo, haced un acto de fe y confiad
en mí: elegid el campo [DiaSem]
8
Visítame en http://bit.ly/NckAccess
4.- Como estamos hablando que los datos que se van a mostrar en las intersecciones de fila y
de columna van a ser los motivos de la cita, esto es, datos de texto, no necesitamos realizar
operaciones matemáticas con ellos. Así que el campo a elegir será [MotivoCita] y la función
que utilizaremos para mostrar esos datos será la de “Primero”. E, importante (que si no queda
un churro), desmarcad el check para incluir sumas de filas (sumar texto es algo un poco…
¿estrambótico?)
9
Visítame en http://bit.ly/NckAccess
Como esos encabezados no nos van a servir cuando
saquemos nuestra agenda definitiva vamos a situar el
informe en vista diseño. Lo que haremos será coger el
encabezado automático (en la ilustración,
“CBaseRAgenda”), vamos a sacar sus propiedades, pestaña
Otras, y en la propiedad Nombre vamos a sustituir el
nombre que hay por lblTitulo
A modo de ejemplo...
Hechos estos pasos necesarios después ya podemos meter el informe en la lavadora y que
cada uno le dé el formato que más le guste.
En mi caso he reducido el tamaño de fuente y alto de los campos de la sección de talle porque
al tener tantas franjas horarias el informe parecía más un ejercicio de práctica de scroll vertical
que otra cosa. Así que cada uno deberá ajustarlo en función de cómo considere que se adapta
mejor a sus circunstancias… o al menos eso os recomendaría yo.
Si utilizáramos solo la tabla de referencias cruzadas para ver la información que nos interesa,
sin necesidad de utilizar informe alguno, sí hubiéramos podido elegir el campo que hubiéramos
querido. Eso es así porque los datos, en la tabla de referencias cruzadas, se adaptan en todo
momento a la información origen que queremos extraer.
Ahora bien, con la necesidad de tener que utilizar un informe la cosa cambia. Los informes,
una vez construidos, permanecen estáticos en sus elementos (olvidemos que existe VBA, a
estos efectos). Ojo, digo en sus elementos, no en la información que muestran, que se adapta
perfectamente a la que se obtiene de la consulta o tabla de origen.
10
Visítame en http://bit.ly/NckAccess
Imaginemos que hemos pedido la semana del 21 de agosto de 2017. La consulta de
referencias cruzadas hubiera devuelto un valor de cabecera, para ese día, así: 21/08/17 lunes,
así:
¿Qué pasa cuando cambiamos de semana? Supongamos que ahora elegimos la semana
siguiente, la del 28. Se nos remozan los datos en nuestra tabla TTempAgenda y, con esos
nuevos datos, abrimos la consulta de referencias cruzadas:
Es decir, que nuestro informe no adapta dinámicamente los nombres de los campos del
informe y, en consecuencia, si cambia el nombre del campo nuestro informe “casca”.
De lo anterior se deriva que necesitamos utilizar un campo que, aunque cambien las fechas, su
valor no cambie. Y precisamente ese campo es [DiaSem], que es el que hemos utilizado.
Obviamente, el valor que devuelve [DiaSem] para el 21/08/17 es 1 (porque es lunes), y el
valor que devuelve para el 28/08/17 también es 1, porque sigue siendo lunes. Ergo, que no
cambie nos permite utilizarlo en un informe sin que este nos “casque” por cambios en los
nombres de los campos.
11
Visítame en http://bit.ly/NckAccess
Espero que hayáis podido entenderme… y, si no, os animo a probarlo ;-)
En la base de datos que acompaña a este ejemplo he creado el textbox y el botón de comando
directamente en FMenu, pero podemos ponerlo en el formulario que queramos.
Así, imaginando que estamos en el formulario deseado, añadiremos un cuadro de texto al que
pondremos de nombre2 txtFechUser. Para evitar problemas de introducción de datos, en sus
propiedades → Pestaña Formato, elegiremos el formato de “Fecha corta”.
…
Private Sub cmdAbreAgenda_Click()
Dim diaElegido As Byte
Fijaos que este código lo único que hace es tratar el día elegido por el usuario y transformarlo
a la fecha correspondiente al lunes. Dicho de otra manera, supongamos que elijo el 26/08/17,
sábado.
Con ese valor podemos calcular qué día correspondería a lunes, y lo asignamos a una variable
pública llamada pSemana. La función DateAdd() nos dice:
Añade “n” días a la fecha elegida por el usuario. ¿Qué valor toma n? Pues es -6+1, es decir,
añádele -5 (o, de otra forma, resta cinco días) a la fecha seleccionada por el usuario.
En consecuencia, el día 26, menos 5 días, se queda en el 21, que es el lunes 21/08/17.
2 Para asignar un nombre a un control lo que debemos hacer es sacar las propiedades de ese control e irnos a la Pestaña > Otras >
Nombre. Ahí escribimos el nombre que queramos.
3 Para generar código debemos sacar las propiedades del control > Pestaña Eventos, y nos situamos en la parte “blanca” a la
derecha del evento que queremos programar. Veremos un pequeño botón de puntos suspensivos. Si hacemos clic sobre él nos
aparecerá una ventana que nos pedirá qué operación deseamos realizar. Le indicamos que queremos “generar código”.
12
Visítame en http://bit.ly/NckAccess
Tras eso llama al procedimiento subPreparoAgenda.
…
Option Compare Database
Option Explicit
2.- Inserta en TTempAgenda todos los rangos horarios que hayamos dado de alta en THoras
en función de los días de la semana seleccionada. Recordemos que el primer día de la semana
nos lo da la variable pSemana (¡siempre debería recoger un lunes, insisto!), y a esa fecha de
pSemana le vamos añadiendo 1 día más hasta llegar al domingo.
Recordad que, cuando empezamos a hablar de la consulta CTempAgenda, os decía que, en una
primera fase, los datos se rellenaban por código. Esta parte 2 es la SQL que rellena dichos
datos.
3.- Una vez hecho lo anterior ya sabemos que TTempAgenda nos recoge cada uno de los días
con las franjas horarias. Es decir, lunes a las 8:00, lunes a las 8:30, lunes a las 9:00, etc.
Hasta aquí, todo claro.
¿Qué pasa si hemos dado de alta una cita a las 7:00 de la mañana? ¿O a las 8:45? Porque, en
el primer caso, esa hora no está en nuestros rangos horarios y, en el segundo, aunque cae
dentro de nuestros rangos horarios no está contemplada esa hora en TTempAgenda.
En consecuencia, este tercer paso lo que hace es mirar coincidencias entre los datos de
TTempAgenda y TCitas para el día en concreto que se esté analizando. Si no existe la hora de
la cita lo que hace es añadirla a TTempAgenda.
a) Cita a las 8:00 → Ya existe la franja horaria de las 8:00 en TTempAgenda → No hago nada
b) Cita a las 8:45 → No existe esa franja horaria en TTempAgenda → Se la agrego
4 Para insertar un módulo estándar podemos abrir el editor de VB (ALT+F11) y nos vamos a Menú > Insertar > Módulo
13
Visítame en http://bit.ly/NckAccess
6.- Cambiamos los textos de las etiquetas 1, 2, 3,…, 7 y la etiqueta del encabezado del
informe.
…
Public Sub subPreparoAgenda()
'2.- Insertamos los días de la semana solicitada y todos los rangos horarios que
'hayamos establecido en THoras
For i = 0 To 6
For j = 1 To DCount("*", "THoras")
CurrentDb.Execute ("INSERT INTO TTempAgenda(FechaCitaTemp,HoraCitaTemp)" _
& " VALUES ('" & pSemana + i & "','" _
& DLookup("Hora", "THoras", "IdHora=" & j) & "')")
Next j
Next i
Compruebo_Existen_Datos:
'4.- Comprobamos si existen citas para la semana seleccionada. Si no, avisamos, aunque dejamos seguir el
proceso
If DCount("*", "CTempAgenda", "MotivoCita<>NULL") = 0 Then
MsgBox "No hay citas para la semana seleccionada", vbInformation, "SIN CITAS"
End If
14
Visítame en http://bit.ly/NckAccess
'5.- Abrimos el informe
DoCmd.OpenReport "RAgenda", acViewReport
'6.- Le cambiamos el nombre de las etiquetas del encabezado, sabiendo que su nombre es lblX, con 1<=X<=7
For i = 1 To 7
For Each ctl In Reports!RAgenda
If ctl.ControlType = acLabel Then
If ctl.Name = "lbl" & i Then
ctl.Caption = DLookup("Cabecera", "CTempAgenda", "DiaSem=" & i)
End If
End If
Next ctl
Next i
SEGUNDA PARTE
Podría darse el caso de que alguien dijera: “Sería fantástico tener un botón para poder ir a la
semana anterior o a la semana siguiente desde el propio informe, ¿no?”
15
Visítame en http://bit.ly/NckAccess
Y esto es lo que vamos a programar en esta segunda parte del ejemplo.
Porque, básicamente, tenemos una variable pública pSemana que podemos manipular
fácilmente y tenemos un procedimiento público en un módulo estándar, lo que significa que
puede llamarse desde cualquier parte de la aplicación.
Veamos…
MODIFICANDO RAGENDA
Vamos a situar RAgenda en vista diseño y vamos a añadirle dos botones de comando, que
llamaremos cmdSemAnterior y cmdSemSiguiente. Creo que no hará falta deciros cuál es cuál
en la imagen:
…
Private Sub cmdSemAnterior_Click()
Call subCambioSemana(1)
End Sub
…
…
Private Sub cmdSemSiguiente_Click()
Call subCambioSemana(2)
End Sub
…
Ahora, en el módulo asociado al informe, bajo la/s línea/s Option, escribimos el siguiente
procedimiento:
16
Visítame en http://bit.ly/NckAccess
…
Option Compare Database
Option Explicit
'Cerramos el informe
DoCmd.Close acReport, Me.Name
'Restamos o sumamos una semana en función del valor del argumento elMov
If elMov = 1 Then 'Si elegimos la semana anterior...
pSemana = DateAdd("d", -7, pSemana) 'le restamos 7 días al valor de pSemana
Else 'Si elegimos la semana siguiente...
pSemana = DateAdd("d", 7, pSemana) 'le sumamos 7 días al valor de pSemana
End If
'Llamamos a nuestro procedimiento en el módulo para que recalcule todos los datos
'y nos abra de nuevo el informe
Call subPreparoAgenda
Como veis, el “quid” de la cuestión estaba simplemente en saber si tenemos que sumar o
restar 7 días a pSemana. El resto se hace prácticamente solo.
TERCERA PARTE
Esta parte quizá sea un poco más complicadilla, pero no demasiado. ¿Qué veremos en esta
tercera parte? Pues veremos la posibilidad de, en nuestro informe RAgenda, poder hacer doble
clic sobre una intersección de fila y columna y abrir el formulario para, si no hay cita, poder
dar una de alta y, si la hay, poder modificarla.
Sí, sí… estoy seguro de que si no incluyo esta tercera parte alguien me hubiera enviado un
mensaje diciendo: “en tu ejemplo de la agenda semanal, ¿sería posible ir a la fecha y hora
haciendo doble clic en el campo correspondiente del informe?” Je, je… ¡Que nos conocemos
todos! xDD
17
Visítame en http://bit.ly/NckAccess
MODIFICANDO NUESTRO INFORME RAGENDA
Pensemos, pensemos… con el dinero, ¿qué hacemos?
Para poder acceder a una cita desde RAgenda necesitamos dos datos: la fecha de la “celda”
que seleccionamos (columna) y la hora de la “celda” que seleccionamos (fila).
Para conseguir la hora no tenemos mayor problema, pues eso nos lo da el valor del campo
[HoraCitaTemp] del registro en el que estemos (el que hemos seleccionado).
Sin embargo, la fecha ya parece algo más complicado (no demasiado, realmente). ¿De dónde
me saco el dato, pues?
Y lo que vemos es que, independientemente de donde hagamos doble clic (porque ese será el
evento que utilizaremos para desencadenar el proceso) en el informe en vista Informes, el
valor que no cambia es el nombre del campo (de la sección detalle, por supuesto): 1, 2, 3, 4,
5, 6 o 7.
Y si tenemos el nombre del campo, y tenemos el valor del lunes almacenado en nuestra
maravillosa variable pSemana, ¿no tenemos ya los elementos necesarios para saber qué día
representa nuestra selección?
Dicho de otra manera, si pSemana almacena el valor 07/08/17 y hacemos doble clic sobre el
campo 1, la fecha que estamos seleccionando es: 07/08/17 + 1 – 1 = 07/08/17.
18
Visítame en http://bit.ly/NckAccess
Y, lógicamente, así sucesivamente.
Sin embargo, como manejar fechas y horas es un poco “pesado”, y como, repito, somos muy
listos, vamos a simplificar el filtro utilizando un solo valor. ¿Cuál? El que nos devuelve nuestro
campo calculado [FHCita] (por eso somos muy listos: porque lo teníamos todo previsto). Eso,
además de manejar solo un valor nos permitirá manejar un valor de texto, sin tener que
preocuparnos de formatos de fechas, almohadillas y demás.
Así que, en RAgenda, vamos a seleccionar el campo [1] de la sección detalle y en su evento
“Al hacer doble clic” generaremos el siguiente código (no os preocupéis si veis que Access
renombra, en el código, el “1” como “Ctl1”):
…
Private Sub Ctl1_DblClick(Cancel As Integer)
Call subAbroCita(Me.HoraCitaTemp, 1)
End Sub
…
En el evento “Al hacer doble clic” del campo [2] (siempre de la sección detalle) generaremos el
siguiente código:
…
Private Sub Ctl2_DblClick(Cancel As Integer)
Call subAbroCita(Me.HoraCitaTemp, 2)
End Sub
…
Y como estoy seguro de que le habéis cogido “el tranquillo” el resto de códigos serán
exactamente iguales, solo que haciendo coincidir el segundo argumento del procedimiento
subAbroCita() por el número del nombre del campo. Si tenéis dudas tenéis la base de datos
que acompaña a este ejemplo para examinar los códigos a vuestro gusto ;-)
…
Private Sub subAbroCita(laHora As Date, numFecha As Byte)
Dim laFH As String
Dim laFecha As Date
19
Visítame en http://bit.ly/NckAccess
'Miramos si hay cita en la selección del usuario
If DCount("*", "TCitas", "FHCita='" & laFH & "'") = 0 Then 'No hay cita. Abrimos
el formulario para añadir
DoCmd.OpenForm "FCitas", , , , acFormAdd
'Y lo dejamos todo preparado para añadir el motivo de la cita
With Forms!FCitas
.FechaCita = laFecha
.HoraCita = laHora
.MotivoCita.SetFocus
End With
Else 'Hay cita. Abrimos el formulario filtrado para modificar
DoCmd.OpenForm "FCitas", , , "FHCita='" & laFH & "'"
End If
'Cerramos el informe
DoCmd.Close acReport, Me.Name
End Sub
…
Fijaos que calculamos la fecha solicitada (variable laFecha) según el planteamiento algebraico
que os mostraba al principio de este epígrafe. A continuación creamos nuestra variable laFH
que nos concatena la fecha y la hora (el mismo sistema utilizado para el campo calculado de la
tabla). Finalmente, analizamos si existe o no la cita y abrimos FCitas en el modo adecuado.
Remarcar que hemos aprovechado la situación para que, si abrimos el formulario en una nueva
cita, automáticamente ya se nos rellenen los valores de los campos de la fecha y de la hora
(eso para que el usuario no tenga que trabajar tanto, pobrecito…).
Un saludo, y…
¡suerte!
20
Visítame en http://bit.ly/NckAccess