I. Introduction

Sur le forum Access de nombreuses questions sont relatives à la planification.

Voici donc un nouveau cas relativement simple de planification des présences mensuelles d'employés dans une entreprise :

Ce planning mensuel présentera, en en-têtes de lignes, les noms des personnes et, en en-têtes de colonnes, les jours du mois.

En plus du numéro de badge de l'employé, il nous faudra aussi sauvegarder dans une table la date du jour de présence et la période de la journée (matin, après-midi).

En outre, pour simplifier la saisie de la présence ou de l'absence, il devra être possible de cocher une case et ainsi d'ajouter la présence éventuelle d'un employé, pour un jour donné et une période, par un simple clic.

Enfin, pour faciliter son développement et assurer sa portabilité, l'application n'utilisera pas de composants externes à Access (ActiveX ou autre).

II. Rendu final du planning

Voici le rendu du planning qui présente les présences des différents employés pour le mois choisi.

Image non disponible
Aperçu du planning mensuel.

III. Les tables nécessaires

Nous allons tout d'abord définir les tables et les champs qui contiendront les données :

Pour réaliser ce planning on aura besoin de 3 tables, les tables T_Personnel, T_PeriodeJ et T_Presence.

III-A. La table "T_Personnel"

la table contient la liste des noms et prénoms des employés dans l'entreprise accompagnés de leur numéro de badge.

Nom du champ Type de données Description
NB Entier long Numéro de badge de l'employé.
Nom Texte Nom de l'employé.
Prenom Texte Prénom de l'employé.

III-B. La table "T_PeriodeJ"

la table contient les 2 périodes de la journée : matin ou après-midi.

Nom du champ Type de données Description
PeriodeJ Texte Période de la journée : matin ou après-midi.

III-C. La table "T_Presence"

Elle sert à enregistrer les présences des employés dans l'entreprise.

Nom du champ Type de données Description
NB Entier long Numéro de badge de l'employé.
DateJ Date Date du jour de présence ou d'absence de l'employé.
PeriodeJ Texte Période de la journée : matin ou après-midi.
Presence Booléen Champ vrai/faux pour présence/absence de l'employé.

IV. Les requêtes

Pour pouvoir afficher le tableau des présences, avec en lignes les employés et en colonnes les jours du mois, il nous faut créer une requête R_Presence_Analyse croisée : Pour cela, on va d'abord créer les 3 sous-requêtes R_Pres, R_PersonnelPeriodeJ et R_Presence. La 3ème requête étant basée sur les 2 premières.

IV-A. La requête "R_Pres"

Cette requête, basée sur la table T_Presence, affiche la liste des présences du mois et de l'année choisis, accompagnées des jours du mois et des numéros de badge : Elle a donc comme paramètres la liste déroulante Mois du formulaire F_Planning (Forms!F_Planning!Mois) et la liste An de ce même formulaire (Forms!F_Planning!An).

Image non disponible
Aperçu de la requête R_Pres en mode création.

Exemple d'affichage de la requête pour le mois d'octobre de l'année 2009.

Image non disponible
Affichage de la requête R_Pres.

IV-B. La requête "R_PersonnelPeriodeJ"

Cette requête, basée sur les tables T_Personnel et T_PeriodeJ, affiche tous les noms des employés de l'entreprise, accompagnés des périodes de la journée (matin, après-midi). On effectue le produit cartésien entre ces 2 tables.

Image non disponible
Aperçu de la requête R_Presence en mode création.

Exemple d'affichage de la requête :

Image non disponible
Affichage de la requête R_Presence.

IV-C. La requête "R_Presence"

Cette requête, basée sur les requêtes R_PersonnelPeriodeJ et R_Pres, affiche tous les noms des employés de l'entreprise, ainsi que les jours du mois choisi accompagnés de la période de la journée et de la situation de l'employé (P:Présent,A:Absent). On effectue une jointure gauche entre les 2 requêtes pour pouvoir afficher tous les employés.

Image non disponible
Aperçu de la requête R_Presence en mode création.

Exemple d'affichage de la requête pour le mois d'octobre de l'année 2009 :

Image non disponible
Affichage de la requête R_Presence.

IV-D. La requête "R_Presence_Analyse croisée"

Cette requête, basée sur la requête R_Presence, affiche un tableau avec en en-têtes de lignes les noms et numéros de badge des employés, en en-têtes de colonnes les jours du mois choisi (1,2,3...31), et à l'intersection des lignes et des colonnes la situation de l'employé (P:Présent,A:Absent).

Image non disponible
Aperçu de la requête R_Presence_Analyse croisée en mode création.

Exemple d'affichage de la requête pour le mois d'octobre de l'année 2009 :

Image non disponible
Affichage de la requête R_Presence_Analyse croisée.

V. Les formulaires

La troisième étape consiste à créer les formulaires.

V-A. Le sous-formulaire "SF_Planning"

Ce formulaire, en mode continu, est relié à la requête analyse croisée. Il contient les zones de texte Employe et NB, pour les en-têtes de lignes, et les 31 zones de texte reliées aux jours du mois (1,2...31).

Image non disponible
Aperçu du sous-formulaire SF_Planning en mode création

V-A-1. Les zones de texte "Jour.."

Les 31 zones de texte (Jour1..Jour31) reliées aux champs 1..31, correspondant aux 31 colonnes de la requête analyse croisée.

V-A-1-a. La mise en forme conditionnelle

En mode création du sous-formulaire, dans le menu Format, on choisit mise en forme conditionnellemise en forme conditionnelle : Puis on définit la couleur turquoise pour les lignes correspondantes aux matin et le blanc pour l'après-midi.

Image non disponible
Définition de la mise en forme conditionnelle des zones de texte.

V-A-1-b. La procédure évènementielle sur Doucle-Clic

Cette procédure permet d'ouvrir le formulaire F_Saisie sur l'employé et le jour correspondant à la case double-cliquée sur le planning.

Exemple de code sur double-clic de la zone de texte Jour1 :

 
Sélectionnez

Private Sub Jour1_DblClick(Cancel As Integer)

   If Not IsNull(Me!NB) And (Me!NB <> "") Then ' Si la zone de texte NB n'est pas vide.
' Ouvre le formulaire F_Saisie sur le jour et l'employé correspondant à la case sur le planning.
      OuvrirFormSaisie CLng(Me!NB), 1, Me!PeriodeJ 
   End If

End Sub

V-A-2. La procédure "OuvrirFormSaisie"

Cette procédure fait partie du module du sous-formulaire et permet d'ouvrir le formulaire F_Saisie sur l'employé et le jour correspondant à la case double-cliquée sur le planning :

 
Sélectionnez

Public Sub OuvrirFormSaisie(NB As Long, j As Integer)
' Prend en arguments le numéro de badge et l'indice du jour du mois, et
' ouvre le formulaire F_Saisie sur l'employé et le jour correspondants.

Dim DateJ As Date

' Date correspondant à l'indice de colonne (jour) sur le planning.
DateJ = DateSerial(Forms!F_Planning!An, Forms!F_Planning!Mois, j)

' Ouvre le formulaire filtré sur le numéro de badge et sur le jour correspondants.
DoCmd.OpenForm "F_Saisie", , , "[NB]=" & NB & " and [DateJ]=" & FDateUs(DateJ)

Forms!F_Saisie!NB.Value = NB
Forms!F_Saisie!Jour.Value = DateJ

End Sub

V-B. Le formulaire "F_Planning"

Il contient les zones de listes déroulantes Mois et An, les boutons de commande pour avancer ou reculer d'un mois, et le sous-formulaire SF_Planning.

Image non disponible
Aperçu du formulaire F_Planning en mode création

V-B-1. La liste déroulante "Mois"

Ce contrôle est utilisé pour permettre à l'utilisateur de sélectionner un mois, et ainsi d'afficher le planning du mois choisi.

V-B-1-a. La procédure évènementielle sur AfterUpdate

Cette procédure s'exécute quand on choisit un mois dans la liste déroulante : Elle permet d'afficher le planning du mois choisi.

 
Sélectionnez

Private Sub Mois_AfterUpdate() 
' Mise à jour du planning.
MajPlanning

End Sub

V-B-2. La liste déroulante "An"

Ce contrôle est utilisé pour permettre à l'utilisateur de sélectionner une année, et ainsi d'afficher le planning du mois et de l'année choisis.

V-B-2-a. La procédure évènementielle sur AfterUpdate

Cette procédure s'exécute quand on choisit une année dans la liste déroulante : Elle permet d'afficher le planning du mois et de l'année choisis.

 
Sélectionnez

Private Sub An_AfterUpdate()
   
' Mise à jour du planning.
MajPlanning

End Sub

V-B-3. Boutons de commande "CmdPrecedent" et "CmdSuivant"

Ces boutons de commande situés en haut à droite permettent d'avancer ou de reculer d'un mois sur le planning.

V-B-3-a. La procédure évènementielle sur clic du bouton "CmdPrecedent"

Ce code permet de reculer d'un mois et de mettre à jour le planning.

 
Sélectionnez

Private Sub CmdPrecedent_Click()
' Recule d'1 mois sur le planning.

   If (Me!Mois.ListIndex > 0) Then ' Si le mois est supérieur à 1 (Janvier),
      Me.Mois = Me.Mois.ItemData(Me!Mois.ListIndex - 1) ' on affiche le mois précédent.
      
   Else
      Me.Mois = Me.Mois.ItemData(11) ' Sinon on affiche le dernier mois
      Me.Année = Me.Année - 1 ' et l'année précédente.
      
   End If
   
' Mise à jour du planning.
MajPlanning

End Sub

V-B-3-b. La procédure évènementielle sur clic du bouton "CmdSuivant"

Ce code permet d'avancer d'un mois et de mettre à jour le planning.

 
Sélectionnez

Private Sub CmdSuivant_Click()
' Avance d'1 mois sur le planning.

   If (Me!Mois.ListIndex < 11) Then ' Si le mois est inférieur à 12 (Décembre),
      Me.Mois = Me.Mois.ItemData(Me!Mois.ListIndex + 1) ' on affiche le mois suivant.
      
   Else
      Me.Mois = Me.Mois.ItemData(0) ' Sinon on affiche le premier mois
      Me.Année = Me.Année + 1 ' et l'année suivante.
   End If

' Met à jour le planning.   
MajPlanning

End Sub

V-C. Le formulaire "F_Saisie"

Formulaire de saisie d'une présence ou absence d'un employé dans l'entreprise.

Image non disponible
Aperçu du formulaire F_Saisie

V-C-1. Bouton de commande "CmdValider"

Permet de valider la saisie.

V-C-1-a. La procédure évènementielle sur Clic

 
Sélectionnez

Me.Requery ' Actualise le formulaire.
MajPlanning ' Met à jour le planning.
DoCmd.Close acForm, "F_Saisie" ' Ferme le formulaire.

V-C-2. Bouton de commande "CmdAnnuler"

Permet d'annuler la saisie.

V-C-2-a. La procédure évènementielle sur Clic

 
Sélectionnez

Private Sub CmdAnnuler_Click()
Me.Undo ' Annule la saisie.
DoCmd.Close acForm, "F_Saisie" ' Ferme le formulaire.
End Sub

V-C-3. Bouton de commande "CmdNonRens"

Permet de supprimer la présence ou absence de l'employé en cours..

V-C-3-a. La procédure évènementielle sur Clic

 
Sélectionnez

Private Sub CmdNonRens_Click()
' Supprime l'enregistrement en cours.
Dim rep As Integer

' Affiche une boîte de dialogue pour confirmer la suppression. 
rep = MsgBox("Souhaitez-vous supprimer l'enregistrement ?", vbYesNo)
   
   If (rep = vbYes) Then ' Si la réponse est oui.
   Me.Undo ' On annule la dernière modification.
   
      If Not (Me.RecordsetClone.EOF) Then ' S'il y a un enregistrement en cours.
        Me.RecordsetClone.Delete ' Supprime cet enregistrement.
      End If

   MajPlanning ' Met à jour le planning.
   DoCmd.Close acForm, "F_Saisie" ' Ferme le formulaire.
   
   End If

End Sub

VI. Le module "M_Planning" de l'application

Enfin nous présentons le module de l'application.

VI-A. Description de quelques routines

Quelques routines utilisant les fonctions Date et Heurefonctions Date et Heure :

 
Sélectionnez

Option Compare Database
Option Explicit

' Les fonctions du module.

Public Function FDateUS(laDate As Date) As String
' Formate la date passée en argument en date US.

FDateUs = "#" & Format(vDate, "mm/dd/yyyy") & "#"  ' Date au format US.
End Function

Public Function EstWeekEnd(dt As Date) As Boolean
EstWeekEnd = (Weekday(dt) = 1) Or (Weekday(dt) = 7) ' est un samedi ou un dimanche.

End Function

Public Function DaysInMonth(ByVal nMonth As Integer, ByVal nYear As Integer) As Integer
' Retourne le nombre de jours d'un mois donné, en utilisant Day(), DateSerial et DateAdd()

DaysInMonth = Day(DateAdd("d", -1, DateAdd("m", 1, DateSerial(nYear, nMonth, 1))))

End Function

VI-B. La procédure "MajPlanning"

Pour mettre à jour le planning après avoir choisi un mois et une année sur les listes du même nom, il suffit d'actualiser les jours du mois en masquant les jours en trop, puis de raffraichir le sous-formulaire lié à la requête analyse croisée :

 
Sélectionnez

Public Sub MajPlanning()
' Met à jour le planning mensuel.

Dim ND As Integer, j As Integer
Dim DateJ As Date

' Nombre de jours du mois.
ND = DaysInMonth(Forms!F_Planning!Mois, Forms!F_Planning!An)
' 1er jour du mois.
DateJ = DateSerial(Forms!F_Planning!An, Forms!F_Planning!Mois, 1)

' parcours des jours du mois
For j = 1 To ND
   If EstWeekEnd(DateJ) Or IsJourFerie(DateJ) Then ' Si un samedi, un dimanche ou un jour férié.
                                                   ' Utilise la fonction IsJourFerie de Philben.
   ' Colorie l'en-tête de colonne.   
      Forms!F_Planning!SF_Planning.Form("Col" & j).BackColor = 14281983 
   ' Colorie les jours du week-end. 
      Forms!F_Planning!SF_Planning.Form("Jour" & j).FormatConditions.Item(0).BackColor = 14281983
      Forms!F_Planning!SF_Planning.Form("Jour" & j).FormatConditions.Item(0).Modify _ 
	                                                acExpression, , "[PeriodeJ]<>''" 
   Else ' Sinon.
      Forms!F_Planning!SF_Planning.Form("Col" & j).BackColor = 16761024
      Forms!F_Planning!SF_Planning.Form("Jour" & j).FormatConditions.Item(0).BackColor = 16777164
      Forms!F_Planning!SF_Planning.Form("Jour" & j).FormatConditions.Item(0).Modify _
	                                                acExpression, , "[PeriodeJ]='Matin'"
   
   End If

DateJ = DateJ + 1 ' Jour suivant.
Next j

For j = 29 To ND
Forms!F_Planning!SF_Planning.Form("Col" & j).Visible = True
Forms!F_Planning!SF_Planning.Form("Jour" & j).Visible = True
Next j

' Masque les jours ne faisant pas partie du mois.
For j = (ND + 1) To 31
Forms!F_Planning!SF_Planning.Form("Col" & j).Visible = False
Forms!F_Planning!SF_Planning.Form("Jour" & j).Visible = False
Next j

' Actualise le sous-formulaire.
Forms!F_Planning!SF_Planning.Requery

End Sub

VII. Les applications à télécharger

La base exempleplanningv1 est au format Access 2000.

Autres exemplesplanningv2 basés sur ce travail.

Divers applicationsplanningv3 basées sur ce travail.

VIII. Remerciements

Un grand Merci à toute l'équipe de Dvp et plus particulièrement à :

Pour leurs remarques et conseils avisés :
. Gayot
. Philippe JOCHMANS
. Domi2
. Jeannot45
. Arkham46

Pour leurs relectures :
. Pierre Fauconnier
. Jeannot45