Le formulaire et sa minuterie

Info
Pour qu'Access puisse déclencher une action une fois par jour, il faut que le logiciel lui-même soit déjà en cours d'exécution. Cet article ne traite pas du démarrage automatique d'Access. Consultez cette fiche du grenier pour plus de détails.
On suppose donc qu'Access sera en cours d'exécution à l'heure souhaitée. Pour que le logiciel puisse décider de cette exécution, il faut qu'il "surveille" en permanence le temps et - l'heure venue - déclenche l'action qui vous intéresse. Le principe consiste à construire un formulaire, et à programmer son événement Minuterie, lequel est prévu pour exécuter du code à intervalles réguliers.

Construction du formulaire

Le formulaire n'a pas besoin d'être très joli : c'est juste un outil qui tournera en arrière plan :o)
  1. Cliquez sur l'onglet Formulaires de votre fenêtre Base de données.
  2. Double-cliquez ensuite sur l'option Créer un formulaire en mode Création (ou sur Access 97 : bouton Nouveau, puis option Mode Création).
  3. Vous obtenez un beau formulaire gris, vide. C'est tout ce qu'il nous faut !
  4. Enregistrez ce formulaire sous le nom frm Minuterie.

Mise en place du formulaire

Pour que le formulaire réagisse à intervalles réguliers, il faut programmer sa minuterie. Voici comment faire, sur un exemple simple :
  1. Le formulaire étant toujours ouvert en mode Création, double-cliquez sur le pavé gris à gauche de la règle graduée horizontale.
  2. Une fenêtre de propriétés vient de s'ouvrir. Activez son onglet Evénément.
  3. Au bas de liste figure une propriété Intervalle minuterie. Tapez-y par exemple 5000 (vous pourrez ultérieurement y placer une valeur supérieure). Concrètement : la minuterie sera appelée toutes les 5000 millisecondes, soit toutes les 5 secondes.
  4. Cliquez maintenant dans l'événement Sur minuterie. A droite, une petite flèche (un triangle en fait !), qui vous permet de sélectionner l'option Procédure événementielle.
  5. Un peu plus à droite encore, 3 points de suspension. Cliquez dessus pour accéder au code VBA de l'événement Sur minuterie. Faites en sorte que la procédure ressemble à ce qui suit : 
Public Sub Form_Timer()
  MsgBox "Alarme !", vbInformation
End Sub
Remarque
Au lieu d'un simple MsgBox, vous pouvez bien sûr ouvrir une requête existante (DoCmd.OpenQuery), exécuter une requête SQL (CurrentDb.Execute), imprimer un état (DoCmd.OpenReport) ou exécuter n'importe quelle portion de code VBA...

Tester !

Ouvrez maintenant votre formulaire en mode standard, et attendez 5 secondes. Le message "Alarme !" s'affiche automatiquement après ce délai.

Le problème est que ce message va s'afficher toutes les 5 secondes. En fait, ce n'est pas un problème, c'est le fonctionnement normal d'une minuterie. Pour n'exécuter l'action qu'une fois par jour, il va nous falloir aménager tout ça, et "tracer" le fait que le code ait déjà été exécuté ou non...

Mémoriser les temps dans une table

Comme il vient d'être dit : la minuterie s'exécute à intervalles réguliers. Après sa première exécution, il faut pouvoir mémoriser un indicateur quelconque (par exemple : la date et l'heure de l'exécution). En d'autres termes : "si l'exécution a déjà eu lieu aujourd'hui, ne pas la relancer".
Remarque
Une 1ère solution consisterait à désactiver la minuterie, en réglant la propriété Intervalle Minuterie sur 0, ou en fermant tout simplement le formulaire frm Minuterie ! Mais on peut supposer que la base de données reste ouverte sur plusieurs jours, et que le code continue de fonctionner. C'est pour cette raison que nous optons pour une solution différente...

Il existe plusieurs façons de stocker une information. Comme nous travaillons en bases de données, autant utiliser une table ! Construisez la table qui suit, que vous nommerez tbl Minuterie :

Nom du champ Type de données Description
Id NuméroAuto Juste une clef primaire automatique.
Heure Exécution Date/Heure L'heure à laquelle notre programme doit s'exécuter une fois par jour.
Le fait de stocker cette heure dans une table et non dans le code VBA permet plus de souplesse lorsqu'il s'agira de modifier cette info.
Dernière Exécution Date/Heure Date et heure de la dernière fois où notre programme s'est exécuté.

Passez en mode Saisie, et tapez une heure d'exécution (par exemple : 17:00). Ceci crée un enregistrement numéroté 1, la date de dernière exécution restant vide. A priori, notre table ne devrait contenir que cet unique enregistrement.

Amélioration du timer

Il faut maintenant que votre événement Sur minuterie gère cette table. Voici un code amélioré :
Private Sub Form_Timer()
Dim varHeureExec As Variant
Dim varDerniereExec As Variant

' Lire l'heure d'exécution
varHeureExec = DLookup("[Heure Exécution]", "tbl Minuterie")
If IsNull(varHeureExec) Then Exit Sub

' Lire l'heure de dernière exécution
varDerniereExec = DLookup("[Dernière Exécution]", "tbl Minuterie")
If IsNull(varDerniereExec) Then varDerniereExec = #1/1/1900 12:00:00 PM#

' Si une exécution a eu lieu aujourd'hui, annuler le processus
If Format(varDerniereExec, "dd/mm/yyyy") = Format(Date, "dd/mm/yyyy") Then Exit Sub

' Si l'heure actuelle est supérieure à celle de base, exécuter le processus
If Time > varHeureExec Then
  ' Mettre à jour la date d'exécution
  CurrentDb.Execute "UPDATE [tbl Minuterie] SET [Dernière exécution]=#" & _
      Format(Now, "mm/dd/yyyy hh:nn:ss") & "#"

  ' Exécuter une action...
  MsgBox "Alarme !", vbInformation
End If
End Sub

Le listing commenté

varHeureExec = DLookup("[Heure Exécution]", "tbl Minuterie")
If IsNull(varHeureExec) Then Exit Sub

On commence par lire le champ Heure Exécution de la table tbl Minuterie. La fonction DLookup() permet ce type d'opération sans ouvrir directement la table. On suppose bien sûr que tbl Minuterie ne contient qu'un enregistrement (sinon, seul le champ Heure Exécution du 1er enregistrement serait pris en compte).

Si ce champ est vide (Null), l'heure d'exécution n'a pas été définie. On arrête immédiatement le processus.

varDerniereExec = DLookup("[Dernière Exécution]", "tbl Minuterie")
If IsNull(varDerniereExec) Then varDerniereExec = #1/1/1900 12:00:00 PM#

On lit également le champ Dernière Exécution, toujours dans tbl Minuterie.
Cette fois, si le champ est Null, c'est que la minuterie n'a jamais été exécutée. Dans ce cas, on part du principe que la dernière exécution - fictive - remonte au 1er janvier 1900 à midi. Ca facilitera les tests à venir. 

If Format(varDerniereExec, "dd/mm/yyyy") = Format(Date, "dd/mm/yyyy") Then Exit Sub
On annule l'exécution de la minuterie si la date du jour (donnée par la fonction VBA Date) et la date de dernière exécution (varDerniereExec) sont égales. Sous-entendu : le programme a déjà été lancé une fois aujourd'hui...

A noter que, pour faciliter les comparaisons de dates, on formate les 2 valeurs sous la forme jj/mm/aaaa (en anglais puisqu'on est en VBA ;-)). En effet, varDerniereExec contient non seulement la date, mais également l'heure, ce qui ne nous intéresse pas ici.
 
If Time > varHeureExec Then
  MsgBox "Alarme !", vbInformation
End If

On est maintenant sûr que l'exécution n'a pas été lancée aujourd'hui. Reste à vérifier si l'heure de lancement (varHeureExec) est passée, en la comparant à l'heure actuelle (donnée par la fonction VBA Time). On ne peut pas vraiment tester une égalité, puisque la minuterie s'exécute seulement toutes les 5 secondes.

CurrentDb.Execute "UPDATE [tbl Minuterie] SET [Dernière exécution]=#" & _
  Format(Now, "mm/dd/yyyy hh:nn:ss") & "#"

Cette ligne met à jour la table tbl Minuterie en mémorisant la date actuelle (la fonction VBA Now est une combinaison de Date et de Time, donnant à la fois la date et l'heure). Pour être traitée en SQL, il faut que la date soit au format #mm/jj/yyyy hh:nn:ss# (sachant que les "n" désignent les minutes, pour éviter la confusion avec les mois).

Ouverture automatique du formulaire

Votre minuterie est désormais opérationnelle. La touche finale consiste à faire en sorte que le formulaire s'ouvre automatiquement avec la base de données (il sera d'ailleurs masqué pour "tourner" en arrière-plan).
  1. Dans la fenêtre Base de données, activez l'onglet Macros.
  2. Cliquez sur le bouton Nouveau.
  3. Dans la première cellule de la grille de macro, sélectionnez l'action OuvrirFormulaire.
  4. En bas de l'écran, un certain nombre d'arguments apparaissent. Réglez l'argument Nom Formulaire sur frm Minuterie, et l'argument Mode Fenêtre sur Masquée.
  5. Enregistrez la macro en la nommant précisément AutoExec (les majuscules/minuscules sont sans importance).
  6. Videz éventuellement la date de dernière exécution dans votre table tbl Minuterie, fermez la base, rouvrez-la, et attendez...
Rappel
Si vous ne souhaitez pas que la macro AutoExec ne s'exécute au démarrage, ouvrez votre base de données tout en maintenant la touche [Majuscule] enfoncée.