Utiliser des images dans un Planning

BALLAT Jean

Souvent nous rencontrons sur le forum des questions concernant l'implantation d'images dans un formulaire de type continu.
On pourra trouver un premier tuto de Dolphy35 : Implantation d'images dans un formulaire continu.
Pour compléter cet article, je vous présente comment implanter, dans un planning, des images représentant des situations ou des activités. (Ne concerne que les versions Access 2007 et supérieures.)

Vos commentaires : Commentez

- Titre : Utiliser des images dans un Planning

- Auteur : BALLAT Jean

- Parution : 10 mai 2010

- Licence :

Sommaire

- I. Contexte

- II. Les tables

- II-A. T_Employes

- II-B. T_Activite

- II-C. T_Contrats

- II-D. T_PlanningPreparation

- II-E. T_TempsTravail

- II-F. T_Mois

- II-G. Le modèle relationnel

- III. Présentation du Planning

- IV. Les différents formulaires

- IV-A. Le choix du Planning

- IV-A-1. Implantation de la liste et de la zone de texte

- IV-A-2. Implantation du bouton "Ouvrir Planning"

- IV-A-3. Le code dans ce formulaire

- IV-B. Création du Formulaire du Planning

- IV-B-1. Section Détail

- IV-B-1-a. Les deux zones de texte

- IV-B-1-b. Les contrôles "Image"

- IV-B-1-c. Les listes déroulantes

- IV-B-1-d. Le code attaché aux listes déroulantes

- IV-B-2. Section En-tête de Formulaire

- IV-B-2-a. Les étiquettes du planning

- IV-B-2-b. Le Code du Formulaire

- IV-B-2-b-i. Une petite fonction pour la requête

- IV-B-2-b-ii. La structure de la requête

- IV-B-2-b-iii. Le code à l'ouverture du formulaire

- IV-B-2-c. Les boutons de commande

- IV-B-2-c-i. Le bouton "Semaine Précédente"

- IV-B-2-c-ii. Le bouton "Semaine Suivante"

- IV-B-2-c-iii. Le bouton "Imprimer le Planning"

- IV-B-2-c-iv. Le bouton "Nouveau Salarié"

- IV-C-3. Les propriétés du formulaire F_PreparationPlanning

- IV-C. Création d'un nouveau salarié

- IV-C-1. Le formulaire de saisie

- IV-C-1-a. La liste déroulante et son code

- IV-C-1-b. Le sous-formulaire

- IV-C-1-c. Implanter le sous-formulaire

- IV-C-2. Le code de génération des enregistrements

- IV-D. L'impression

- IV-D-1. Le formulaire du choix de l'impression

- IV-D-1-a. La liste déroulante

- IV-D-1-b. Le bouton d'impression et son code

- IV-D-2. Le Planning

- IV-D-2-a. Le structure de l'état

- IV-D-2-a-i. La section En-tête d'état

- IV-D-2-a-ii. La section Détail

- IV-D-2-b. Le code attaché à l'état

- IV-D-2-b-i. À l'ouverture de l'état

- IV-D-2-b-ii. Au formatage de la section détail

- V. Conclusion

- VI. Remerciements

- VII. Téléchargement

I - Contexte

Cette petite application est une extraction d'une base plus globale qui gère le personnel et les chiffres d'une petite entreprise familiale. Les tables, utilisées ici, ne reprennent que les informations utiles à la génération du planning. Je n'aborderai pas la validation du planning qui avait pour but de modifier directement les données de la table T_TempsTravail qui contient les temps de travail des employés. Par contre, en lieu et place du bouton de validation du planning, j'ai ajouté dans le formulaire un bouton qui donnera la possibilité d'ajouter un salarié afin de voir comment la table T_PlanningPreparation est alimentée.

II - Les tables

Pour bien appréhender la base, je vais vous présenter les différentes tables qui entrent en jeu.

II-A - T_Employes

Reprend les informations basiques du salarié.

Nom du Champ

Type

Description

Matricule

NuméroAuto

Clé Primaire

NomEmploye

Texte

Reprend le patronyme de l'employé

PrenomEmploye

Texte

Reprend le prénom de l'employé

Pour les débutants : on pourra remarquer que dans les noms de champs, je n'utilise ni accent, ni espace.

II-B - T_Activite

Reprend les informations des différents types d'activités ou de situations pouvant être affectées à l'employé dans l'entreprise.

Nom du Champ

Type de Données

Description

CodeActivite

NuméroAuto

Clé Primaire

LibelActivite

Texte

Intitulé de l'activité ou de la situation

ImageActivite

Texte

Contient le chemin des images liées à l'activité ou à la situation d'un employé

II-C - T_Contrats

Reprend les informations relatives au contrat du salarié. Cette table a été très simplifiée par rapport à l'application originale.

Nom du Champ

Type de Données

Description

CodeContrat

NuméroAuto

Clé Primaire

Matricule

Numérique

En relation avec la table T_Employes

TypeContrat

Texte

Indique s'il s'agit d'un CDI, CDD, Extra, Apprenti

DebutContrat

Date

Date du Début du Contrat

FinContrat

Date

Date de Fin de Contrat

DebutAmContractuel

Numérique

Heure de début de travail service du Midi (Théorique)

FinAmContractuel

Numérique

Heure de fin de travail service du Midi (Théorique)

DebutPmContractuel

Numérique

Heure de début de travail service du soir (Théorique)

FinPmContractuel

Numérique

Heure de fin de travail service du soir (Théorique)

Afin de faciliter la saisie, des valeurs par défaut seront implantées :
- DebutAmContractuel = 10.00 ;
- FinAmContractuel = 14.30 ;
- DebutPmContractuel = 18.30 ;
- FinPmContractuel = 22.00 ;
- CodeActivite = 1 (Travail).

II-D - T_PlanningPreparation

C'est la table qui reprend toute l'activité du salarié.

Nom du Champ

Type de Données

Description

CodeContrat

Numérique

Est en relation avec la table des contrats

MoisPlanning

Numérique

Contient le quantième du mois. Il y aura autant d'enregistrements dans la table que de mois correspondant à la durée du contrat

AnneePlanning

Numérique

Contient l'année

Activite1

Numérique

Contient le code d'activité du salarié (1 = Travail (par défaut)) pour le 1er jour du mois

....

Activite31

Numérique

Contient le code d'activité du salarié (1 = Travail (par défaut))

Il y a 31 champs "Activité". Chaque champ représente un jour du mois. On considère que tous les mois ont 31 jours. Le traitement en arrière-plan ne fera apparaître, dans le planning, que les jours correspondant au mois en cours.

Ne pas oublier que cette table ne sert que pour la préparation des plannings et que les données seront ensuite stockées dans la table T_TempsTravail.

II-E - T_TempsTravail

Je vous présente cette table par rapport à la modélisation de l'application. En effet, la table T_PlanningPréparation, de par sa structure, ne répond pas aux règles de normalisation d'une base de données (cf : la suite des champs Activite1 à Activite31).
Cette table contient toutes les données de travail des employés. Elle est modifiée :
1. lors de la validation du planning ;
2. lors de la saisie des temps de travail des employés ;
3. lors de la saisie d'une observation pour une date donnée et un salarié particulier.

Ces traitements ne seront pas abordés car ils débordent du sujet de ce tutoriel.

Nom du Champ

Type de Données

Description

CodeContrat

Numérique

Contient le Numéro de contrat du salarié

JourTravail

Date/Heure

Contient la date du jour de travail

CodeActivite

Numérique

Contient le Code de l'activité assurée par l'employé

HeureDebutAm

Numérique

Contient l'heure de début du service du midi (Format : Fixe)

HeureFinAm

Numérique

Contient l'heure de fin du service du midi (Format : Fixe)

HeureDebutPm

Numérique

Contient l'heure de début du service du soir (Format : Fixe)

HeureFinPm

Numérique

Contient l'heure de fin du service du soir (Format : Fixe)

Observation

Texte

Contient toute remarque afférente à la journée de travail du salarié

II-F - T_Mois

Petite table d'information permettant d'avoir les libellés des mois dans différentes situations (Liste déroulante, Impression ...).

Nom du Champ

Type de Données

Description

CodeMois

Numérique

Contient le Quantième du mois de l'année

LibelleMois

Texte

Continent le nom du mois

II-G - Le modèle relationnel

On remarquera l'absence de relation entre T_Activite et T_PlanningPréparation. Celle-ci sera établie lors de la génération des requêtes pour afficher les images dans le planning.

III - Présentation du Planning

Dans l'image ci-dessus, nous avons une vue d'ensemble du formulaire servant à la préparation du planning.
1. Les différents boutons de gestion (navigation par semaine, nouvelle saisie, impression).
2. Les étiquettes et les dates à mettre à jour pour chaque changement de semaine.
3. La liste des situations ou activités liées à une image.

IV - Les différents formulaires

IV-A - Le choix du Planning

IV-A-1 - Implantation de la liste et de la zone de texte

Petit formulaire simple permettant à l'utilisateur de sélectionner le mois sur lequel il souhaite intervenir :
- soit pour la préparation ;
- soit pour imprimer le planning.

Ce formulaire contient une liste déroulante basée sur la table T_Mois et une zone de texte pour y saisir l'année concernée.
Nous modifierons les propriétés Nom des deux objets :
- pour la liste déroulante : cboMois ;
- pour la zone de texte : txtAnneeConcernee.

IV-A-2 - Implantation du bouton "Ouvrir Planning"

Avant d'implanter les boutons, désactivez l'assistant contrôle comme indiqué dans l'image ci-contre.
Cliquez sur l'outil Bouton puis cliquez sur le formulaire pour déposer l'objet.
Dans le volet des propriétés du bouton :
- onglet Autres : changez le nom de l'objet (btnOuvrirPlanning) ;
- onglet Format : saisissez la légende (Ouvrir le planning) ;
- onglet Évènements : sur clic, choisissez Procédure évènementielle.

IV-A-3 - Le code dans ce formulaire

Nous aurons besoin de passer des informations d'un formulaire à un autre (le mois et l'année du planning).
Pour faciliter le transit de celles-ci, nous allons utiliser des Variables Publiques. Il faut les déclarer dans un module spécial.
1. Ouvrez la fenêtre de l'éditeur VBA (Alt F11).
2. Cliquez sur l'outil Ajouter un module.
3. Dans le volet des propriétés, changez le nom du module en saisissant : Déclaration Globale.

Nous allons déclarer les deux variables nécessaires :

Variables pour la préparation du planning

' Récupèrera le mois choisi dans la liste déroulante : cboMois
Public p_intMoisPlanning As Integer
' Récupèrera l'année saisie dans la zone de texte : txtAnneeConcernee
Public p_intAnneePlanning As Integer

IV-B - Création du Formulaire du Planning

IV-B-1 - Section Détail

Cette première phase permettra de créer les différents objets du planning c'est-à-dire :
- une zone de texte pour afficher le n° de contrat ;
- une zone de texte pour afficher le nom du salarié ;
- sept contrôles "Image" pour afficher les images des différentes situations ou activités ;
- sept listes déroulantes qui afficheront les choix possibles issus de la table T_Activite.
Ces différents éléments seront alimentés par une requête affectée au formulaire :
- soit à l'ouverture du formulaire ;
- soit à l'utilisation des boutons de navigation.
C'est dans cet objectif que nous affecterons une Source contrôle à chaque objet dont le nom sera repris dans chaque requête.

IV-B-1-a - Les deux zones de texte

Pour ces deux contrôles, pas de difficultés particulières.
Dans un formulaire indépendant en mode création :
1. Cliquez sur l'outil "ab" ;
2. Placez vos contrôles ;
3. Affichez les propriétés ;
4. Dans l'onglet : Données, propriété : Source conctrôle, saisissez les intitulés suivants :
- CodeContrat ;
- idEmploye.

IV-B-1-b - Les contrôles "Image"

Pour cette série de sept contrôles, plusieurs propriétés seront à modifier.
Nous allons créer totalement un objet avec toutes ses propriétés et nous allons ensuite le recopier six fois.
1. Cliquez sur l'outil : Cadre d'objet indépendant.
2. Placez le contrôle sur le formulaire.

La fenêtre des Propriétés étant affichée, il nous faut maintenant modifier les propriétés suivantes (après avoir cliqué sur l'onglet Toutes) :
- Nom : imgAct1 ;
- Source contrôle : srcImage1 ;
- Mode affichage : Echelle ;
- Largeur : 2,152cm ;
- Hauteur : 1,201cm.

Vous allez maintenant recopier ce contrôle six fois puis modifier les propriétés Nom et Source Contrôle des nouveaux objets en respectant bien l'ordre :
- Nom : imgAct2, imgAct3, [...], imgAct7
- Source contrôle : srcImage2, srcImage3, [...], srcImage7

IV-B-1-c - Les listes déroulantes

Comme précédemment, nous allons créer une liste déroulante et la recopier six fois.
Cliquez sur l'outil : Liste Déroulante :
- Étape 1 : les données sont issues d'une table ;
- Étape 2 : choisissez la table T_Activite ;
- Étape 3 : sélectionnez les deux premiers champs ;
- Étape 4 : comme bon vous semble, vous pouvez trier les données de la liste ;
- Étape 5 : vous ajusterez la largeur de la colonne (même principe que dans Excel) ;
- Étape 6 : vous pouvez laisser l'intitulé de l'étiquette par défaut, celle-ci sera supprimée.

Il nous faut maintenant modifier l'identité, la source et les dimensions de notre contrôle.
Affichez les propriétés de la liste déroulante et cliquez sur l'onglet : Toutes
- Nom : cboActivite1 ;
- Source contrôle : cboActivite1 ;
- Largeur : 0,451cm ;
- Hauteur : 0,423cm.

Après avoir supprimé l'étiquette, il nous suffit de recopier ce contrôle six fois puis de modifier les propriétés Nom et Source contrôle des nouveaux objets en respectant bien l'ordre :
- Nom : cboActivite2, cboActivite3, [...], cboActivite7 ;
- Source contrôle : cboActivite2, cboActivite3, [...], cboActivite7.

Positionnez les contrôles pour obtenir la disposition du modèle.

IV-B-1-d - Le code attaché aux listes déroulantes

Le code placé sur l'évènement Après MAJ concerne le statut de l'apprenti. En effet, celui-ci suit sa formation au CFA.
J'ai donc pensé qu'en affectant l'activité "CFA", il était intéressant d'agir sur l'ensemble de la semaine. La procédure permet donc à l'utilisateur du planning d'implanter l'activité automatiquement à partir de n'importe quel jour avec mise en place de la récupération le samedi et le dimanche qui suivent la semaine de formation.

Le principe est de rechercher la position du lundi par rapport à la position du jour de la semaine modifié.
Voici le code :

Recherche du jour concernant la modif

l_intJourModif = Weekday(l_dteDateModif)
Select Case l_intJourModif
' Cas du Lundi
Case Is = 2
l_sngCompteurJourSem = 0
' Cas du Mardi
Case Is = 3
l_sngCompteurJourSem = -1
' Cas du Mercredi
Case Is = 4
l_sngCompteurJourSem = -2
' Cas du Jeudi
Case Is = 5
l_sngCompteurJourSem = -3
' Cas du Vendredi
Case Is = 6
l_sngCompteurJourSem = -4
' Cas du Samedi ou du Dimanche
Case Else
Me.ActiveControl = 1
MsgBox "Impossibilité de placer des journées de CFA un Week-End." & vbCrLf & "Opération annulée", vbInformation, "Préparation du Planning - DVP"
Exit Sub
End Select

Dans un second temps, par une boucle il faut réécrire les nouvelles valeurs dans l'enregistrement correspondant.
La boucle s'interrompt si la fin de mois est atteinte.
Voici le code :

Modification de l'enregistrement

' Récupération des données du planning de l'employé afin de modifier les CodeActivité sur toute la semaine
strSqlPlanning = "SELECT * FROM T_PlanningPreparation WHERE CodeContrat = " & Me.CodeContrat & " AND MoisPlanning = " _
& p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning
Set m_rsPlanning = CurrentDb.OpenRecordset(strSqlPlanning)
With m_rsPlanning
For l_intBoucleCfa = 0 To 6
' Contrôle de la fin de mois
If intChampActiviteConcerne + (sngCompteurJourSem + intBoucleCfa) > m_intFinMois Then
Exit For
Else
.Edit
If l_intBoucleCfa <= 4 Then
' Implantation du code CFA sur tous les jours du Lundi au Vendredi
.Fields("Activite" & l_intChampActiviteConcerne + (l_sngCompteurJourSem + l_intBoucleCfa)) = 5
Else
' Implantation du code ReposJournée pour les Samedi et Dimanche de la semaine
.Fields("Activite" & l_intChampActiviteConcerne + (l_sngCompteurJourSem + l_intBoucleCfa)) = 4
End If
.Update
End If
Next
End With

Une procédure est affectée à l'ensemble des listes déroulantes du formulaire.

Private Sub cboActivite1_AfterUpdate()
Call SauvegardeEnregistrement
End Sub

On remarquera dans cette procédure l'appel à la routine SauvegardeEnregistrement.

Sub SauvegardeEnregistrement()

' Déclaration des variables locales
' utilisables uniquement dans cette procédure
Dim l_rsContrat As DAO.Recordset
Dim l_intJourModif As Integer, l_intBoucleCfa As Integer, l_intChampActiviteConcerne As Integer
Dim l_strSqlContrat As String
Dim l_dteDateModif As Date
Dim l_sngCompteurJourSem As Single

' Sauvegarde de l'enregistrement modifé
DoCmd.RunCommand acCmdSaveRecord

' Automatisation de la gestion de l'indicatif CFA
If Me.ActiveControl = 5 Then
' Contrôle du type de Contrat du salarié
l_strSqlContrat = "SELECT TypeContrat FROM T_Contrats WHERE CodeContrat = " & Me.CodeContrat
Set l_rsContrat = CurrentDb.OpenRecordset(l_strSqlContrat)

If l_rsContrat.Fields(0) <> "Apprentissage" Then
'Repositionnement sur la valeur par défaut (travail)
Me.ActiveControl = 1
MsgBox "L'employé modifié n'est pas un apprenti !!!" & vbCrLf & "La modification du planning a été annulée.", vbExclamation, "Préparation du Planning - Forum DVP"
Exit Sub
End If
' Récupération de la date concernée
l_dteDateModif = CDate(Me.Controls("lblJour" & Right(Me.ActiveControl.Name, 1)).Caption)
l_intChampActiviteConcerne = Day(l_dteDateModif)

' Recherche du jour concernant la modif
l_intJourModif = Weekday(l_dteDateModif)
Select Case l_intJourModif
' Cas du Lundi
Case Is = 2
l_sngCompteurJourSem = 0
' Cas du Mardi
Case Is = 3
l_sngCompteurJourSem = -1
' Cas du Mercredi
Case Is = 4
l_sngCompteurJourSem = -2
' Cas du Jeudi
Case Is = 5
l_sngCompteurJourSem = -3
' Cas du Vendredi
Case Is = 6
l_sngCompteurJourSem = -4
Case Else
Me.ActiveControl = 1
MsgBox "Impossibilité de placer des journées de CFA un Week-End." & vbCrLf & "Opération annulée", vbInformation, "Préparation du Planning - DVP"
Exit Sub
End Select
' Récupération des données du planning de l'employé afin de modifier les CodeActivité sur toute la semaine
strSqlPlanning = "SELECT * FROM T_PlanningPreparation WHERE CodeContrat = " & Me.CodeContrat & " AND MoisPlanning = " _
& p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning
Set m_rsPlanning = CurrentDb.OpenRecordset(strSqlPlanning)
With m_rsPlanning
For l_intBoucleCfa = 0 To 6
' Contrôle de la fin de mois
If intChampActiviteConcerne + (sngCompteurJourSem + intBoucleCfa) > m_intFinMois Then
Exit For
Else
.Edit
If l_intBoucleCfa <= 4 Then
' Implantation du code CFA sur tous les jours du Lundi au Vendredi
.Fields("Activite" & l_intChampActiviteConcerne + (l_sngCompteurJourSem + l_intBoucleCfa)) = 5
Else
' Implantation du code ReposJournée pour les Samedi et Dimanche de la Semaine
.Fields("Activite" & l_intChampActiviteConcerne + (l_sngCompteurJourSem + l_intBoucleCfa)) = 4
End If
.Update
End If
Next
End With
End If
End Sub

IV-B-2 - Section En-tête de Formulaire

Durant cette phase, nous allons implanter les étiquettes affichant les dates et les jours de la semaine puis les différents boutons (Navigation, Ajout de salarié, Impression).

IV-B-2-a - Les étiquettes du planning

Pour afficher l'en-tête et le pied de formulaire :
1. cliquez droit dans la section Détail ;
2. cliquez sur la commande En-tête et Pied de Formulaire ;
3. réduisez la section Pied de Formulaire qui ne sera pas utilisée.

Nous allons préparer les deux premières étiquettes puis comme précédemment faire une recopie des deux éléments :
1. cliquez sur l'outil "Aa" (étiquette) ;
2. cliquez dans la section En-tête de Formulaire ;
3. saisissez un espace pour "valider" l'objet dessiné.

Nous allons maintenant modifier les propriétés de l'étiquette :
1. cliquez sur l'onglet Toutes de la fenêtre des propriétés ;
2. modifiez les deux propriétés suivantes :
- Nom : lblJourSem1,
- Largeur : 2,619cm.

Il nous suffit de recopier cette étiquette treize fois en renommant :
- les six premières : lblJourSem2, lblJourSem3..., lblJourSem7 ;
- les sept suivantes : lblJour1, lblJour2..., lblJour7.

Il nous reste à les disposer comme sur le modèle.

IV-B-2-b - Le Code du Formulaire

IV-B-2-b-i - Une petite fonction pour la requête

Notre formulaire étant indépendant, il nous faudra lui amener une source de données lors de l'ouverture afin que celui-ci affiche des informations.
Nous utiliserons pour cela un jeu de requêtes paramétrées.
Dans notre formulaire, le N° de contrat, l'identité du salarié concaténée, le mois et l'année du planning ainsi que deux informations, répétées sept fois, sont affichées :
- une valeur dans la liste déroulante représentant le code de l'activité ou de la situation du salarié ;
- une image associée à la valeur de la liste déroulante.
Ce qui nous donne l'image de la requête ci-dessous :

Remarquez la répétition de la table T_Activite pour chaque champ Activite de la table T_PreparationPlanning.
Il nous faut maintenant associer l'image d'une activité à chaque CodeActivite de T_PreparationPlanning. Or, le champ ImageActivite contient une partie du chemin de stockage du fichier image (exemple : images\imgTravail.jpg).
L'accès complet à l'image étant tributaire de l'organisation des fichiers de chacun, il nous faudra écrire une petite fonction qui nous permettra de récupérer le chemin complet de l'image par rapport à son emplacement sur votre disque :
1. ouvrez la fenêtre VBA (Alt F11) ;
2. insérez un nouveau Module ;
3. renommez ce module : Fonctions Globales ;
4. saisissez ou copiez le code ci-dessous.

Public Function fncCheminImage(pRelativePath As String) As String
'Concaténation du chemin de la base et de l'image
fncCheminImage = CurrentProject.Path & "\" & pRelativePath
End Function

IV-B-2-b-ii - La structure de la requête

Cette requête, que nous avons dessinée précédemment, devra être évolutive puisqu'elle n'affichera que sept jours à la fois. Nous serons donc amené à la manipuler via le Code VBA.
Reprenons la requête précédente et ajoutons les champs nécessaires :
- CodeContrat ;
- IdSalarie représentant l'identité du salarié par concaténation des deux champs NomEmploye et PrenomEmploye ;
- MoisPlanning ;
- AnneePlanning ;
- Activite1 ;
- srcImage1 représentant le chemin de l'image (utilisation de la fonction personnalisée créée ci-dessus) ;
- Ces deux derniers champs seront répétés sept fois (avec les champs Activite2 à Activite7).
Nous obtenons l'image suivante :

Cette requête devra alimenter notre formulaire dès son ouverture. Cliquez dans le volet des propriétés du formulaire sur l'onglet Évènements et recherchez l'évènement sur Ouverture puis choisissez Procédure évènementielle.
Comme nous devrons paramétrer notre requête, nous allons commencer par déclarer des variables de portée Module, ainsi elles seront reconnues de toutes les procédures du module attaché à notre formulaire.
1. ouvrez la fenêtre de VBA (Alt F11).
1. Dans la zone de Déclarations, Access affiche la ligne Option Compare Database,
2. saisissez : Option explicit. Cette option a pour but d'obliger la déclaration des variables avant utilisation. Le code en sera plus performant.

Voici les variables à déclarer :

Déclarations des variables de portée module

' Déclaration des variables de portée Module
' Ces variables ne sont utilisables que dans ce module

' Variable qui récupère les enregistrements sélectionnés
Dim m_rsPlanning As DAO.Recordset
' Variable qui contient la syntaxe SQL source du formulaire
Dim m_strSqlPrepaPlanning As String
' Variable contenant la partie de la syntaxe SQL contenant les critères de sélection
Dim m_strSqlWHERE As String
' Divers compteurs
Dim m_intCompteurSemaine As Integer, m_intCompteurJour As Integer
' Variable de gestion de la fin de mois
Dim m_boolFinMois As Boolean

IV-B-2-b-iii - Le code à l'ouverture du formulaire

Lors de l'ouverture de la fenêtre VBA, une procédure était déjà prête à être complétée. Access l'a préparée lorsque nous avons demandé une procédure évènementielle sur ouverture du formulaire.
Positionnez-vous avant l'instruction End Sub et saisissez votre code :

Private Sub Form_Open(Cancel As Integer)

' Variable pour gérer la boucle de création des étiquettes de planning
Dim l_intCompteur As Integer
' Variable pour gérer les dates des étiquettes de planning
Dim l_dteDatePlanning As Date

' initialisation des variables de portée Module
m_intCompteurSemaine = 1
m_intCompteurJour = 0

' Appel de la routine de création de la source du formulaire
Call ChangementSourcePlanning

' Création des étiquettes calendrier
For l_intCompteur = 1 To 7
l_dteDatePlanning = CDate(Format(l_intCompteur, "00") & "/" & Format(p_intMoisPlanning, "00") & "/" & Format(p_intAnneePlanning, "00"))
Me.Controls("lblJourSem" & l_intCompteur).Caption = Format(l_dteDatePlanning, "dddd")
Me.Controls("lblJour" & l_intCompteur).Caption = Format(l_dteDatePlanning, "dd/mm/yyyy")
Next

End Sub

Remarquez l'appel à la routine ChangementSourcePlanning. C'est dans cette procédure que nous sélectionnerons la syntaxe SQL qui servira de source à notre planning.
Quelques remarques sur le code :

La requete source

m_strSqlPrepaPlanning =
"SELECT T_PlanningPreparation.CodeContrat, [PrenomEmploye] & ' ' & [NomEmploye] AS IdEmploye, " _
& "T_PlanningPreparation.Activite" & 1 + m_intCompteurJour & " AS cboActivite1, T_PlanningPreparation.Activite" & 2 + m_intCompteurJour & " AS cboActivite2, " _
& "T_PlanningPreparation.Activite" & 3 + m_intCompteurJour & " AS cboActivite3, T_PlanningPreparation.Activite" & 4 + m_intCompteurJour & " AS cboActivite4, " _
& "T_PlanningPreparation.Activite" & 5 + m_intCompteurJour & " AS cboActivite5, T_PlanningPreparation.Activite" & 6 + m_intCompteurJour & " AS cboActivite6, " _
& "T_PlanningPreparation.Activite" & 7 + m_intCompteurJour & " AS cboActivite7, T_PlanningPreparation.MoisPlanning, T_PlanningPreparation.AnneePlanning, " _
& "fncCheminImage([T_Activite].[ImageActivite]) AS srcImage1, fncCheminImage([T_Activite_1].[ImageActivite]) AS srcImage2, " _
& "fncCheminImage([T_Activite_2].[ImageActivite]) AS srcImage3, fncCheminImage([T_Activite_3].[ImageActivite]) AS srcImage4, " _
& "fncCheminImage([T_Activite_4].[ImageActivite]) AS srcImage5, fncCheminImage([T_Activite_5].[ImageActivite]) AS srcImage6, " _
& "fncCheminImage([T_Activite_6].[ImageActivite]) AS srcImage7 " _
& "FROM T_Activite AS T_Activite_6 " _
& "INNER JOIN (T_Activite AS T_Activite_5 " _
& "INNER JOIN (T_Activite AS T_Activite_4 " _
& "INNER JOIN (T_Activite AS T_Activite_3 " _
& "INNER JOIN (T_Activite AS T_Activite_2 " _
& "INNER JOIN (T_Activite AS T_Activite_1 " _
& "INNER JOIN (T_Activite INNER JOIN ((T_PlanningPreparation INNER JOIN T_Contrats " _
& "ON T_PlanningPreparation.CodeContrat = T_Contrats.CodeContrat) " _
& "INNER JOIN T_Employes ON T_Contrats.Matricule = T_Employes.Matricule) " _
& "ON T_Activite.CodeActivite = T_PlanningPreparation.Activite" & 1 + m_intCompteurJour & ") " _
& "ON T_Activite_1.CodeActivite = T_PlanningPreparation.Activite" & 2 + m_intCompteurJour & ") " _
& "ON T_Activite_2.CodeActivite = T_PlanningPreparation.Activite" & 3 + m_intCompteurJour & ") " _
& "ON T_Activite_3.CodeActivite = T_PlanningPreparation.Activite" & 4 + m_intCompteurJour & ") " _
& "ON T_Activite_4.CodeActivite = T_PlanningPreparation.Activite" & 5 + m_intCompteurJour & ") " _
& "ON T_Activite_5.CodeActivite = T_PlanningPreparation.Activite" & 6 + m_intCompteurJour & ") " _
& "ON T_Activite_6.CodeActivite = T_PlanningPreparation.Activite" & 7 + m_intCompteurJour

J'ai géré la modularité du planning en utilisant le compteur m_intCompteurJour :

exemple

"T_PlanningPreparation.Activite" & 1 + m_intCompteurJour & " AS cboActivite1

Dans la concaténation du nom et du prénom de l'employé, j'ai remplacé les "" par de simples quotes, cela facilite la gestion des "".

Concaténation du champ idEmploye

"...[PrenomEmploye] & ' ' & [NomEmploye] AS IdEmploye,..."

Il nous faut maintenant récupérer dans la clause WHERE les critères saisis dans le formulaire F_ChoixPlanning. Je rappelle qu'ils "transitent" par des variables publiques.

Clause WHERE

' Création de la clause WHERE
m_strSqlWHERE = " WHERE MoisPlanning = " & p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning _
& " ORDER BY T_Employes.NomEmploye, T_Employes.PrenomEmploye "

Pour terminer, il faut affecter notre nouvelle source à notre formulaire :

Affectation de la source

' Initialisation de la Source du formulaire
Me.RecordSource = m_strSqlPrepaPlanning & m_strSqlWHERE

Dans le reste de la procédure, on retrouvera le même type de syntaxe pour la gestion des fins de mois.
Voici ci-dessous la procédure complète :

Procédure complète

Sub ChangementSourcePlanning(Optional intFinMois As Integer)
' Récupération de la syntaxe SQL pour les jours de la semaine
If m_boolFinMois = False Then
m_strSqlPrepaPlanning = "SELECT T_PlanningPreparation.CodeContrat, [PrenomEmploye] & ' ' & [NomEmploye] AS IdEmploye, " _
& "T_PlanningPreparation.Activite" & 1 + m_intCompteurJour & " AS cboActivite1, T_PlanningPreparation.Activite" & 2 + m_intCompteurJour & " AS cboActivite2, " _
& "T_PlanningPreparation.Activite" & 3 + m_intCompteurJour & " AS cboActivite3, T_PlanningPreparation.Activite" & 4 + m_intCompteurJour & " AS cboActivite4, " _
& "T_PlanningPreparation.Activite" & 5 + m_intCompteurJour & " AS cboActivite5, T_PlanningPreparation.Activite" & 6 + m_intCompteurJour & " AS cboActivite6, " _
& "T_PlanningPreparation.Activite" & 7 + m_intCompteurJour & " AS cboActivite7, T_PlanningPreparation.MoisPlanning, T_PlanningPreparation.AnneePlanning, " _
& "fncCheminImage([T_Activite].[ImageActivite]) AS srcImage1, fncCheminImage([T_Activite_1].[ImageActivite]) AS srcImage2, " _
& "fncCheminImage([T_Activite_2].[ImageActivite]) AS srcImage3, fncCheminImage([T_Activite_3].[ImageActivite]) AS srcImage4, " _
& "fncCheminImage([T_Activite_4].[ImageActivite]) AS srcImage5, fncCheminImage([T_Activite_5].[ImageActivite]) AS srcImage6, " _
& "fncCheminImage([T_Activite_6].[ImageActivite]) AS srcImage7 " _
& "FROM T_Activite AS T_Activite_6 " _
& "INNER JOIN (T_Activite AS T_Activite_5 " _
& "INNER JOIN (T_Activite AS T_Activite_4 " _
& "INNER JOIN (T_Activite AS T_Activite_3 " _
& "INNER JOIN (T_Activite AS T_Activite_2 " _
& "INNER JOIN (T_Activite AS T_Activite_1 " _
& "INNER JOIN (T_Activite INNER JOIN ((T_PlanningPreparation INNER JOIN T_Contrats " _
& "ON T_PlanningPreparation.CodeContrat = T_Contrats.CodeContrat) " _
& "INNER JOIN T_Employes ON T_Contrats.Matricule = T_Employes.Matricule) " _
& "ON T_Activite.CodeActivite = T_PlanningPreparation.Activite" & 1 + m_intCompteurJour & ") " _
& "ON T_Activite_1.CodeActivite = T_PlanningPreparation.Activite" & 2 + m_intCompteurJour & ") " _
& "ON T_Activite_2.CodeActivite = T_PlanningPreparation.Activite" & 3 + m_intCompteurJour & ") " _
& "ON T_Activite_3.CodeActivite = T_PlanningPreparation.Activite" & 4 + m_intCompteurJour & ") " _
& "ON T_Activite_4.CodeActivite = T_PlanningPreparation.Activite" & 5 + m_intCompteurJour & ") " _
& "ON T_Activite_5.CodeActivite = T_PlanningPreparation.Activite" & 6 + m_intCompteurJour & ") " _
& "ON T_Activite_6.CodeActivite = T_PlanningPreparation.Activite" & 7 + m_intCompteurJour

Else
Select Case intFinMois
' Récupération de la syntaxe SQL pour la fin de mois
' Année bissextile
Case Is = 29
m_strSqlPrepaPlanning = "SELECT T_PlanningPreparation.CodeContrat, [PrenomEmploye] & ' ' & [NomEmploye] AS IdEmploye, " & _
"T_PlanningPreparation.Activite29 AS cboActivite1, T_PlanningPreparation.MoisPlanning, T_PlanningPreparation.AnneePlanning, " & _
"fncCheminImage([T_Activite].[ImageActivite]) AS srcImage1 " & _
"FROM T_Activite INNER JOIN (T_Employes INNER JOIN (T_PlanningPreparation INNER JOIN T_Contrats " & _
"ON T_PlanningPreparation.CodeContrat = T_Contrats.CodeContrat) ON T_Employes.Matricule = T_Contrats.Matricule) " & _
"ON T_Activite.CodeActivite = T_PlanningPreparation.Activite29 "
' Mois à 30 jours
Case Is = 30
m_strSqlPrepaPlanning = "SELECT T_PlanningPreparation.CodeContrat, [PrenomEmploye] & ' ' & [NomEmploye] AS IdEmploye, " & _
"T_PlanningPreparation.Activite29 AS cboActivite1,T_PlanningPreparation.Activite30 AS cboActivite2, T_PlanningPreparation.MoisPlanning, T_PlanningPreparation.AnneePlanning, " & _
"fncCheminImage([T_Activite].[ImageActivite]) AS srcImage1, fncCheminImage([T_Activite1].[ImageActivite]) AS srcImage2 " & _
"FROM (T_Activite INNER JOIN (T_Employes " & _
"INNER JOIN (T_PlanningPreparation INNER JOIN T_Contrats " & _
"ON T_PlanningPreparation.CodeContrat = T_Contrats.CodeContrat) ON T_Employes.Matricule = T_Contrats.Matricule) " & _
"ON T_Activite.CodeActivite = T_PlanningPreparation.Activite29) INNER JOIN T_Activite AS T_Activite1 ON T_PlanningPreparation.Activite30 = T_Activite1.CodeActivite "
' Mois à 31 jours
Case Is = 31
m_strSqlPrepaPlanning = "SELECT T_PlanningPreparation.CodeContrat, [PrenomEmploye] & ' ' & [NomEmploye] AS IdEmploye, " & _
"T_PlanningPreparation.Activite29 AS cboActivite1, T_PlanningPreparation.Activite30 AS cboActivite2, T_PlanningPreparation.Activite31 AS cboActivite3, " & _
"T_PlanningPreparation.MoisPlanning, T_PlanningPreparation.AnneePlanning, " & _
"fncCheminImage([T_Activite].[ImageActivite]) AS srcImage1, fncCheminImage([T_Activite1].[ImageActivite]) AS srcImage2, " & _
"fncCheminImage([T_Activite2].[ImageActivite]) AS srcImage3 " & _
"FROM T_Activite AS T_Activite2 " & _
"INNER JOIN (T_Activite AS T_Activite1 " & _
"INNER JOIN (T_Activite INNER JOIN ((T_PlanningPreparation INNER JOIN T_Contrats " & _
"ON T_PlanningPreparation.CodeContrat = T_Contrats.CodeContrat) INNER JOIN T_Employes " & _
"ON T_Contrats.Matricule = T_Employes.Matricule) " & _
"ON T_Activite.CodeActivite = T_PlanningPreparation.Activite29) " & _
"ON T_Activite1.CodeActivite = T_PlanningPreparation.Activite30) " & _
"ON T_Activite2.CodeActivite = T_PlanningPreparation.Activite31"

End Select
End If

' Création de la clause WHERE
m_strSqlWHERE = " WHERE MoisPlanning = " & p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning _
& " ORDER BY T_Employes.NomEmploye, T_Employes.PrenomEmploye "

' Initialisation de la Source du formulaire
Me.RecordSource = m_strSqlPrepaPlanning & m_strSqlWHERE
End Sub

IV-B-2-c - Les boutons de commande

IV-B-2-c-i - Le bouton "Semaine Précédente"

L'assistant étant inactif, cliquez sur l'outil Bouton et implantez celui-ci sur le formulaire.
Nous allons dans un premier temps modifier ses propriétés :
- onglet Format :
Image : cliquez sur l'outil à droite de la propriété et allez rechercher une image représentant une flèche gauche.
- onglet Données :
Activé : choisissez Non.
- onglet Autres :
Nom : saisissez btnSemainePrecedente.
Texte info-bulle : tapez Semaine précédente.

Pour ajouter le code du bouton, cliquez dans l'onglet Évènements puis sur l'évènement sur clic, choisissez Procédure évènementielle.

Private Sub btnSemainePrecedente_Click()

' Variable locale de comptage de la boucle
Dim l_intCompteur As Integer

m_intCompteurSemaine = m_intCompteurSemaine - 1

If m_intCompteurSemaine = 0 Then
m_intCompteurSemaine = 1
Else
' Récupération de la requête source
Select Case m_intCompteurSemaine
Case Is = 1
m_intCompteurJour = 0
' Atteindre le bouton Semaine Suivante pour...
DoCmd.GoToControl "btnSemaineSuivante"
' ... Désactivation du bouton Semaine Précedente.
Me.btnSemainePrecedente.Enabled = False
Case Is = 2
m_intCompteurJour = 7
Case Is = 3
m_intCompteurJour = 14
' Réactivation du bouton Semaine Suivante si le mois à 28 jours
Me.btnSemaineSuivante.Enabled = True
m_boolFinMois = False
Case Is = 4
m_intCompteurJour = 21
' Réactivation du bouton Semaine Suivante si le mois à + de 28 jours
Me.btnSemaineSuivante.Enabled = True
m_boolFinMois = False
Case Is = 5
m_intCompteurJour = 28
End Select

' Récupération de la source
Call ChangementSourcePlanning

' Boucle de génération des différentes étiquettes de la zone En-tête de Formulaire
For l_intCompteur = 1 To 7
Me.Controls("lblJour" & l_intCompteur).Caption = Format(m_intCompteurJour + l_intCompteur, "00") & "/" & Format(p_intMoisPlanning, "00") & "/" & Format(p_intAnneePlanning, "00")
Me.Controls("lblJourSem" & l_intCompteur).Caption = Format(CDate(Me.Controls("lblJour" & l_intCompteur).Caption), "dddd")
Next
End If
End Sub

IV-B-2-c-ii - Le bouton "Semaine Suivante"

Procédez comme ci-dessus pour implanter le nouveau bouton et modifiez les mêmes propriétés :
- onglet Format :
Image : cliquez sur l'outil à droite de la propriété et allez rechercher une image représentant une flèche droite.
- onglet Autres :
Nom : saisissez btnSemaineSuivante.
Texte info-bulle : tapez Semaine suivante.

Pour ajouter le code du bouton, cliquez dans l'onglet Évènements puis sur l'évènement sur clic, choisissez Procédure évènementielle.

Private Sub btnSemaineSuivante_Click()

' Variable pour calculer le quantième du jour
Dim l_intCompteur As Integer
' Variable qui récupère le quantième correspondant à la fin de mois
Dim l_intFinMois As Integer

' Compteur du page du planning (1semaine = 1 page)
m_intCompteurSemaine = m_intCompteurSemaine + 1
' Récupération de la requête source
Select Case m_intCompteurSemaine
Case Is = 1
m_intCompteurJour = 0
Case Is = 2
m_intCompteurJour = 7
' Réactivation du bouton semaine précédente (nous sommes en semaine 2)
Me.btnSemainePrecedente.Enabled = True
Case Is = 3
m_intCompteurJour = 14
Case Is = 4
m_intCompteurJour = 21
' Cas du mois de février à 28 jours
If l_intFinMois = 28 Then
' Atteindre le bouton Semaine Précédente pour...
DoCmd.GoToControl "btnSemainePrecedente"
' ... Désactivation du bouton Semaine Suivante.
Me.btnSemaineSuivante.Enabled = False
End If
Case Is = 5
' Semaine 5 : Semaine de toutes les fins de mois (à 29 j, 30 j ou 31 j)
m_intCompteurJour = 28
' Activation de la variable booléenne qui permettra de retrouver la structure Sql correspondant au nombre de jours du mois
m_boolFinMois = True
' Atteindre le bouton Semaine Suivante pour...
DoCmd.GoToControl "btnSemainePrecedente"
' ... Désactivation du bouton Semaine Suivante.
Me.btnSemaineSuivante.Enabled = False
End Select

' Récupération de la source
Call ChangementSourcePlanning(l_intFinMois)

' Boucle d'initialisation
For l_intCompteur = 1 To 7
' Contrôle la fin de mois
If m_intCompteurJour + l_intCompteur > l_intFinMois Then
' Mise à Blanc des contrôles supérieurs à la date de fin de mois
Me.Controls("lblJour" & l_intCompteur).Caption = " "
Me.Controls("lblJourSem" & l_intCompteur).Caption = " "
Else
' Génération des étiquettes des dates du jour
Me.Controls("lblJour" & l_intCompteur).Caption = Format(m_intCompteurJour + l_intCompteur, "00") & "/" & Format(p_intMoisPlanning, "00") & "/" & Format(p_intAnneePlanning, "00")
Me.Controls("lblJourSem" & l_intCompteur).Caption = Format(CDate(Me.Controls("lblJour" & l_intCompteur).Caption), "dddd")
End If
Next
End Sub

IV-B-2-c-iii - Le bouton "Imprimer le Planning"

Implantez le bouton comme nous l'avons fait précédemment et modifiez les propriétés suivantes :
- onglet Autres :
- Nom : btnImprimerLePlanning.
- onglet Format :
- Image : recherchez une image évocatrice de l'impression.

Pour ajouter le code du bouton, cliquez dans l'onglet Évènements puis sur l'évènement sur clic, choisissez Procédure évènementielle.

Private Sub btnImprimerLePlanning_Click()
DoCmd.OpenForm "F_ChoixEtendueImpression"
End Sub

Pour lire la partie concernant l'impression, cliquez ici : Détail de l'impression

IV-B-2-c-iv - Le bouton "Nouveau Salarié"

Bien que ne faisant pas partie de l'objet de l'article, j'ai voulu détailler ce chapitre pour montrer comment la table T_PlanningPreparation est alimentée.
Cela suppose donc la création d'un formulaire. Pour lire la partie concernant la création du formulaire, cliquez ici : Création du formulaire de saisie d'un nouveau salarié

IV-C-3 - Les propriétés du formulaire F_PreparationPlanning

Nous allons maintenant modifier les propriétés de notre formulaire.
Affichez le volet des propriétés et cliquez sur l'onglet Format ;
- Affichage par défaut : Formulaires continus ;
- (tous les enregistrements s'affichent les uns dessous des autres) ;
- Autoriser le mode Formulaire : Oui ;
- Autres modes d'affichage : mettre Non partout ;
- (évite un autre type d'affichage) ;
- Auto centrer : Oui ;
- (place automatiquement notre formulaire au centre de notre écran) ;
- Diviseur d'enregistrement : Non ;
- (empêche l'affichage d'une ligne entre chaque enregistrement) ;
- Barre de défilement : Aucune ;
- (sauf si nous traitons beaucoup d'enregistrements, auquel cas on pourrait laisser l'option : Verticale) ;
- Boîte contrôle : Non ;
- Bouton Fermer : Non ;
- (Nous gérerons le bouton) ;
- Boutons Min/Max : Non ;
- (la fenêtre ne pourra pas être réduite) ;

IV-C - Création d'un nouveau salarié

IV-C-1 - Le formulaire de saisie

Ce formulaire a été simplifié au strict minimum. Il contient :
- une liste déroulante basée sur la table T_Employes ;
- celle-ci représente le champ Père du formulaire.
- J'en ai profité pour rappeler comment alimenter une liste déroulante (Voir la FAQ) ;
- un sous-formulaire qui reprend l'essentiel du contrat.

IV-C-1-a - La liste déroulante et son code

Dans un formulaire indépendant en mode création (indépendant = rattaché à aucune source de données), implantez une liste déroulante en suivant les diverses étapes de la création :
Étape 1 : la source est une table ou une requête ;
Étape 2 : sélectionnez la table T_Employes ;
Étape 3 : choisissez les premiers champs (Matricule, NomEmploye).
Étape 3 : Le prénom sera ajouté par concaténation lors de la modification de la requête source ;
Étape 4 : positionnez un tri sur le NomEmploye ;
Étape 5 : ne changez rien à la largeur de la colonne ;
Étape 6 : saisissez l'étiquette : Choisir dans la liste.

Nous allons maintenant modifier les propriétés de la liste. Dans le volet des propriétés :
- cliquez sur l'onglet Données
Contenu : Ouvrez la requête en cliquant sur l'outil à droite du volet ;
Contenu : Modifiez le champ NomEmploye en créant un champ calculé (idEmploye) ;
Contenu : Ajoutez une seconde fois le champ NomEmploye et placez-y le tri en désactivant l'option d'affichage ;
Contenu : Fermer la fenêtre du générateur de requête et validez.
- cliquez sur l'onglet Format
Nbre Colonnes : 2 ;
Largeurs colonnes : 0cm, 5cm.
- cliquez sur l'onglet Autres
Nom : cboEmploye.
- cliquez onglet Évènements
Sur absence dans liste : Choisir procédure évènementielle.

En cliquant sur l'outil à droite de la procédure évènementielle, on entre dans l'environnement du code.
Au préalable quelques remarques sur le code :

Utilisation de la fonction Split(expression,séparateur) qui permet d'éclater la chaîne de caractères saisie dans un tableau :
- L'argument "Expression" représente la chaîne à traiter ;
- L'argument "Séparateur" représente le caractère séparant les éléments de la chaîne à éclater.

Utilisation de la fonction Split

' Eclatement de la chaîne saisie dans un tableau
l_tabTrav = Split(NewData, " ")

' Récupération des données dans les variables
l_strNom = l_tabTrav(1)
l_strPrenom = l_tabTrav(0)

Exécution d'une requête Ajout pour implanter les valeurs dans la table source de la liste

' Génération et exécution de la chaine SQL pour ajouter les infos à la table
l_strSqlEmploye = "INSERT INTO T_Employes ( NomEmploye, PrenomEmploye)" _
& " SELECT  '" & l_strNom & "','" & l_strPrenom & "' ;"

With DoCmd
' Désactivation Message système
.SetWarnings False
' Exécution de la requête Ajout
.RunSQL l_strSqlEmploye
' Réactivation des messages système
.SetWarnings True
End With

Voici, ci-dessous, le code complet de la procédure :

Alimenter la liste avec une nouvelle valeur

Private Sub cboEmploye_NotInList(NewData As String, Response As Integer)
Dim l_strSqlEmploye As String
Dim l_strNom As String, l_strPrenom As String
Dim l_tabTrav() As String

' Eclatement de la chaine saisie dans un tableau
l_tabTrav = Split(NewData, " ")

' Récupération des données dans les variables
l_strNom = l_tabTrav(1)
l_strPrenom = l_tabTrav(0)

' Génération et exécution de la chaine SQL pour ajouter les infos à la table
l_strSqlEmploye = "INSERT INTO T_Employes ( NomEmploye, PrenomEmploye)" _
& " SELECT  '" & l_strNom & "','" & l_strPrenom & "' ;"

With DoCmd
' Désactivation Message système
.SetWarnings False
' Exécution de la requête Ajout
.RunSQL l_strSqlEmploye
' Réactivation des messages système
.SetWarnings True
End With

' Mise à jour du contenu de la liste
Response = acDataErrAdded
End Sub

IV-C-1-b - Le sous-formulaire

Créez un nouveau formulaire basé sur la table T_Contrats :
1. affichez l'entête de formulaire ;
2. placez les étiquettes dans la section Entête de formulaire ;
3. placez les zones de texte dans la section Détail ;
4. vérifiez vos alignements ;
5. modifiez les propriétés du formulaire comme ci-dessous :

Onglet Format

Onglet Données

Pour en terminer avec le sous formulaire, il faut implanter une procédure sur les deux zones de texte DebutContrat et FinContrat. Ce code sera placé sur l'évènement "après mise à jour" du contrôle.
Son objectif : récupérer les valeurs du formulaire nécessaires à la création des enregistrements de T_PlanningPréparation

DebutContrat

Private Sub DebutContrat_AfterUpdate()
p_dteDebutContrat = Me.DebutContrat
End Sub

FinContrat

Private Sub FinContrat_AfterUpdate()
p_dteFinContrat = Me.FinContrat
p_lngCodeContrat = Me.CodeContrat
DoCmd.GoToControl "btnFermer"
End Sub

Je n'ai pas fait de contrôle de saisie pour vérifier la cohérence des dates, ce n'est pas l'objet du tuto.

IV-C-1-c - Implanter le sous-formulaire

Ouvrez le formulaire principal de saisie des contrats puis cliquez en laissant appuyé sur le nom du sous-formulaire et glissez-le dans le formulaire à l'endroit souhaité.
Modifiez les propriétés Champ Père et Champ Fils comme ci-dessus.

IV-C-2 - Le code de génération des enregistrements

Lors de la création d'un contrat, la table T_PlanningPreparation est alimentée d'un enregistrement par mois de contrat et la table T_TempsTravail est alimentée d'un enregistrement par jour de travail.
Le code s'exécute sur la fermeture du formulaire de saisie du contrat.

Dans un premier temps, on alimente la table T_PreparationPlanning :

Alimentation de T_PreparationPlanning

Private Sub btnFermer_Click()
' Déclaration des variables locales
Dim l_rsPrepaPlanning As DAO.Recordset
Dim l_strSqlPrepaPlanning As String
Dim l_intCompteur As Integer

' Initialisation des variables de travail
l_strSqlPrepaPlanning = "SELECT * FROM T_PlanningPreparation"
Set l_rsPrepaPlanning = CurrentDb.OpenRecordset(l_strSqlPrepaPlanning)

'Ajout des enregistrements pour les mois du contrat
For l_intCompteur = Month(p_dteDebutContrat) To Month(p_dteFinContrat)
With l_rsPrepaPlanning
.AddNew
.Fields!CodeContrat = p_lngCodeContrat
.Fields!MoisPlanning = l_intCompteur
' Remarque : le planning est régénéré tous les ans,
' si un employé est embauché lors de la génération du premier planning,
' l'année planning correspond à l'année de la date d'embauche.
.Fields!AnneePlanning = Year(p_dteDebutContrat)
.Update
End With
Next

Dans un second temps, on alimente T_TempsTravail :

Alimentation de T_TempsTravail

' Initialisation des variables de travail
l_strSqlPrepaPlanning = "SELECT * FROM  T_TempsTravail"
Set l_rsPrepaPlanning = CurrentDb.OpenRecordset(l_strSqlPrepaPlanning)

'Ajout des enregistrements pour les jours travaillés du contrat
For l_intCompteur = p_dteDebutContrat To p_dteFinContrat
With l_rsPrepaPlanning
.AddNew
.Fields!CodeContrat = p_lngCodeContrat
.Fields!JourTravail = l_intCompteur
.Update
End With
Next

Voici donc le code complet de la procédure évènementielle "sur Clic" du bouton de fermeture du formulaire :

Procédure complète

Private Sub btnFermer_Click()
Dim l_rsPrepaPlanning As DAO.Recordset
Dim l_strSqlPrepaPlanning As String
Dim l_varCompteur As Variant

If Me.cboEmploye > "" Then
' Alimentation de T_PreparationPlanning
'-----------------------------------------------
' Initialisation des variables de travail
l_strSqlPrepaPlanning = "SELECT * FROM T_PlanningPreparation"
Set l_rsPrepaPlanning = CurrentDb.OpenRecordset(l_strSqlPrepaPlanning)

'Ajout des enregistrements pour les mois du contrat
For l_varCompteur = Month(p_dteDebutContrat) To Month(p_dteFinContrat)
With l_rsPrepaPlanning
.AddNew
.Fields!CodeContrat = p_lngCodeContrat
.Fields!MoisPlanning = l_varCompteur
' Remarque : le planning est régénéré tous les ans,
' si un employé est embauché lors de la génération du premier planning,
' l'année planning correspond à l'année de la date d'embauche.
.Fields!AnneePlanning = Year(p_dteDebutContrat)
.Update
End With
Next

' Alimentation de T_TempsTravail
' --------------------------------------
' Initialisation des variables de travail
l_strSqlPrepaPlanning = "SELECT * FROM  T_TempsTravail"
Set l_rsPrepaPlanning = CurrentDb.OpenRecordset(l_strSqlPrepaPlanning)

'Ajout des enregistrements pour les jours travaillés du contrat
For l_varCompteur = p_dteDebutContrat To p_dteFinContrat
With l_rsPrepaPlanning
.AddNew
.Fields!CodeContrat = p_lngCodeContrat
.Fields!jourtravail = l_varCompteur
.Update
End With
Next

' Réactualisation du Planning
Forms!F_PreparationPlanning.Requery
End If
DoCmd.Close acForm, "F_SaisieContrats"
End Sub

IV-D - L'impression

L'impression est lancée à partir du bouton btnImprimerLePlanning. L'ouverture du formulaire ci-dessus à gauche permettra de choisir l'étendue de l'impression tandis que l'image de droite nous montre le résultat obtenu.
On remarquera que les intitulés des Week-End sont affichés en rouge.

IV-D-1 - Le formulaire du choix de l'impression

IV-D-1-a - La liste déroulante

Dans un nouveau formulaire indépendant, implantez une liste déroulante et suivez les diverses étapes de l'assistant :
Étape 1 : choisissez l'option "Je taperai les valeurs souhaitées" ;
Étape 2 : choisissez le nombre de colonne : 2 ;
Étape 2 : saisissez les valeurs à afficher dans la liste :
1ère Quinzaine,
2ème Quinzaine,
Le mois complet ;
Étape 3 : saisissez l'étiquette à associer à la liste.

IV-D-1-b - Le bouton d'impression et son code

Il ne reste plus qu'à implanter un bouton qui lancera le processus d'impression.

Private Sub btnImprimerPlanning_Click()

' Selection de l'impression
Select Case Me.cboChoixImprimer
Case Is = 1
' Impression Quinzaine 1
p_intCompteurImpressDeb = 1
DoCmd.OpenReport "E_ImpressionPlanning", acViewNormal, , "MoisPlanning = " & p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning
Case Is = 2
' Impression Quinzaine 2
p_intCompteurImpressDeb = 17
DoCmd.OpenReport "E_ImpressionPlanning", acViewNormal, , "MoisPlanning = " & p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning
Case Else
' Impression Quinzaine 1
p_intCompteurImpressDeb = 1
DoCmd.OpenReport "E_ImpressionPlanning", acViewNormal, , "MoisPlanning = " & p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning
' Impression Quinzaine 2
p_intCompteurImpressDeb = 17
DoCmd.OpenReport "E_ImpressionPlanning", acViewNormal, , "MoisPlanning = " & p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning
End Select

' Fermeture du formulaire de lancement
DoCmd.Close acForm, "F_ChoixEtendueImpression"
End Sub

IV-D-2 - Le Planning

IV-D-2-a - Le structure de l'état

IV-D-2-a-i - La section En-tête d'état

Cette section contient une étiquette qui affichera le titre de l'état :
1. cliquez sur l'outil Aa ;
2. tapez un espace pour mettre un contenu à l'étiquette ;
3. changez les propriétés :
- Nom : lblTitre,
- Largeur : 18.794cm.

On implantera également quinze étiquettes pour les jours de planning. Comme plus haut, on créera une étiquette que l'on recopiera quinze fois.
1. cliquez sur l'outil Aa ;
2. tapez Date1 pour mettre un contenu à l'étiquette ;
3. changez les propriétés :
- Nom : lblDate1,
- Largeur : 1.492cm,
- Hauteur : 0.503cm ;

Recopiez cette étiquette quinze fois en prenant soin de modifier la propriété Nom (onglet : Autres) comme ci-dessous :
lblDate2, lblDate3, [...], lblDate16.

IV-D-2-a-ii - La section Détail

Nous positionnerons dans cette section, une zone de texte et seize contrôles Image indépendants. Ceux-ci seront alimentés par le code.
Pour la zone de texte :
1. cliquez sur l'outil ab puis cliquez dans la section Détail ;
2. changez les propriétés :
- Nom : Employe,
- Source Contrôle : Employe,
- Largeur : 3cm.
Pour les contrôle image, nous agirons comme ci-dessus en créant un contrôle et en le recopiant.
1. cliquez sur l'outil : Cadre d'objet indépendant ;
2. modifiez les propriétés :
- Nom : img1,
- Largeur : 1.492cm,
- Hauteur : 0.804cm,
- Mode d'affichage : Echelle.
Recopiez cette étiquette quinze fois en prenant soin de modifier la propriété Nom (onglet : Autres) comme ci-dessous :
img2, img3, [...], img16.

IV-D-2-b - Le code attaché à l'état

IV-D-2-b-i - À l'ouverture de l'état

Durant cette procédure, nous allons :

1. générer la source de l'état :

Source de l'état

' Déclaration de la variable
Dim strSqlPrepaImpress As String

' Création de la requête source
strSqlPrepaImpress = "SELECT [PrenomEmploye] & ' ' & [NomEmploye] AS Employe, T_PlanningPreparation.CodeContrat, T_Mois.LibelleMois, MoisPlanning, AnneePlanning, " _
& "Activite1, Activite2, Activite3, Activite4, Activite5, Activite6, Activite7,  Activite8, Activite9, Activite10, Activite11, " _
& "Activite12, Activite13, Activite14, Activite15, Activite16, " _
& "Activite17, Activite18, Activite19, Activite20, Activite21, Activite22, Activite23, Activite24, Activite25, Activite26, " _
& "Activite27 , Activite28, Activite29, Activite30, Activite31 " _
& "FROM T_Employes INNER JOIN ((T_PlanningPreparation " _
& "INNER JOIN T_Contrats ON T_PlanningPreparation.CodeContrat = T_Contrats.CodeContrat) " _
& "INNER JOIN T_Mois ON T_PlanningPreparation.MoisPlanning = T_Mois.CodeMois) ON T_Employes.Matricule = T_Contrats.Matricule " _
& "WHERE MoisPlanning = " & p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning

' Affectation de la source
Me.RecordSource = strSqlPrepaImpress

2. rechercher la fin de mois pour gérer les étiquettes vierges :

Récupération de la fin de Mois

' Recherche fin de mois
If p_intMoisPlanning = 12 Then
intFinMois = Day(DateSerial(p_intAnneePlanning + 1, p_intMoisPlanning + 1, 1) - 1)
Else
intFinMois = Day(DateSerial(p_intAnneePlanning, p_intMoisPlanning + 1, 1) - 1)
End If

3. générer les étiquettes des dates de planning par rapport à la plage à imprimer :

Création des étiquettes du planning

'Récupération du Jeu d'enregistrements
Set rsImpressPlanning = CurrentDb.OpenRecordset(strSqlPrepaImpress)

' Génération des étiquettes
With rsImpressPlanning
' Génération du titre
Me.lblTitre.Caption = "Planning de l'équipe pour " & .Fields(2) & " " & .Fields(4)
For intCompteur = 0 To 15
' Gestion de la fin de mois
If intCompteur + p_intCompteurImpressDeb > intFinMois Then
Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).Caption = ""
Else
' Implantation de la date
Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).Caption = Format(DateSerial(.Fields("AnneePlanning"), _
.Fields("MoisPlanning"), intCompteur + p_intCompteurImpressDeb), "dddd dd")
' Modification de la couleur en fonction du jour de la semaine
If Left(Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).Caption, 1) = "S" Or _
Left(Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).Caption, 1) = "D" Then
Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).ForeColor = 255
Else
Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).ForeColor = 0
End If
End If
Next
End With

Voici donc le code complet à placer dans l'évènement "A l'ouverture" de l'état :

Private Sub Report_Open(Cancel As Integer)
' Déclaration de la variable
Dim strSqlPrepaImpress As String

' Création de la requête source
strSqlPrepaImpress = "SELECT [PrenomEmploye] & ' ' & [NomEmploye] AS Employe, T_PlanningPreparation.CodeContrat, T_Mois.LibelleMois, MoisPlanning, AnneePlanning, " _
& "Activite1, Activite2, Activite3, Activite4, Activite5, Activite6, Activite7, "  _
& "Activite8, Activite9, Activite10, Activite11, " _
& "Activite12, Activite13, Activite14, Activite15, Activite16, " _
& "Activite17, Activite18, Activite19, Activite20, Activite21, Activite22, " _
& "Activite23, Activite24, Activite25, Activite26, " _
& "Activite27 , Activite28, Activite29, Activite30, Activite31 " _
& "FROM T_Employes INNER JOIN ((T_PlanningPreparation " _
& "INNER JOIN T_Contrats ON T_PlanningPreparation.CodeContrat = T_Contrats.CodeContrat) " _
& "INNER JOIN T_Mois ON T_PlanningPreparation.MoisPlanning = T_Mois.CodeMois) ON T_Employes.Matricule = T_Contrats.Matricule " _
& "WHERE MoisPlanning = " & p_intMoisPlanning & " AND AnneePlanning = " & p_intAnneePlanning

' Affectation de la source
Me.RecordSource = strSqlPrepaImpress

' Recherche fin de mois
If p_intMoisPlanning = 12 Then
intFinMois = Day(DateSerial(p_intAnneePlanning + 1, p_intMoisPlanning + 1, 1) - 1)
Else
intFinMois = Day(DateSerial(p_intAnneePlanning, p_intMoisPlanning + 1, 1) - 1)
End If

'Récupération du Jeu d'enregistrements
Set rsImpressPlanning = CurrentDb.OpenRecordset(strSqlPrepaImpress)

' Génération des étiquettes
With rsImpressPlanning
' Génération du titre
Me.lblTitre.Caption = "Planning de l'équipe pour " & .Fields(2) & " " & .Fields(4)
For intCompteur = 0 To 15
' Gestion de la fin de mois
If intCompteur + p_intCompteurImpressDeb > intFinMois Then
Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).Caption = ""
Else
' Implantation de la date
Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).Caption = Format(DateSerial(.Fields("AnneePlanning"), _
.Fields("MoisPlanning"), intCompteur + p_intCompteurImpressDeb), "dddd dd")
' Modification de la couleur en fonction du jour de la semaine
If Left(Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).Caption, 1) = "S" _
Or Left(Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).Caption, 1) = "D" Then
Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).ForeColor = 255
Else
Me.ZoneEntêtePage.Controls("lblDate" & intCompteur + 1).ForeColor = 0
End If
End If
Next
End With
End Sub

IV-D-2-b-ii - Au formatage de la section détail

Dans cette procédure, il nous faut gérer les images.
Cette procédure lit le code Activité de chaque champ et recherche le chemin de l'image afin de créer la source pour chaque objet image de la section Détail.

Création du chemin de l'image

For intCompteur = 0 To 15
' Gestion de la fin de mois
If intCompteur + p_intCompteurImpressDeb > intFinMois Then
strPathImage = "/Images/imgVierge.jpg"
Else
' Ajout de 4 au compteur d'impression parce qu'il y a 4 champs dans la requête source avant le premier champ Activité
Select Case .Fields(intCompteur + p_intCompteurImpressDeb + 4)
Case Is = 1
strPathImage = "\Images\imgTravail.jpg"
Case Is = 2
strPathImage = "\Images\imgReposM.jpg"
Case Is = 3
strPathImage = "\Images\imgReposS.jpg"
Case Is = 4
strPathImage = "\Images\imgReposJ.jpg"
Case Is = 5
strPathImage = "\Images\imgCFA.jpg"
Case Is = 6
strPathImage = "\Images\imgAbsenceJ.jpg"
Case Is = 7
strPathImage = "\Images\imgAbsenceNJ.jpg"
Case Is = 8
strPathImage = "\Images\imgRecupM.jpg"
Case Is = 9
strPathImage = "\Images\imgRecupS.jpg"
Case Is = 10
strPathImage = "\Images\imgRecupJ.jpg"
Case Is = 11
strPathImage = "\Images\imgRecupMReposS.jpg"
Case Is = 12
strPathImage = "\Images\imgReposMRecupS.jpg"
Case Is = 13
strPathImage = "\Images\imgVacances.jpg"
Case Is = 14
strPathImage = "\Images\imgMaladie.jpg"
Case Is = 15
strPathImage = "\Images\imgRupture.jpg"
End Select
End If
' Implantation de l'image dans le contrôle
Me.Controls("img" & intCompteur + 1).Picture = CurrentProject.Path & strPathImage
Next

Cette partie de procédure est incluse dans la lecture de tous les enregistrements du jeu sélectionné.

' Boucle de lecture des enregistrements sélectionnés
With rsImpressPlanning
' Boucle pour sélectionner l'image à imprimer
' ...
' Lecture de l'enregistrement suivant
.MoveNext
End With

Voici donc le code complet affecté à l'évènement "Sur Formatage" de la section Détail :

Private Sub Détail_Format(Cancel As Integer, FormatCount As Integer)
' Déclaration de la variable
Dim strPathImage As String

' Boucle de lecture des enregistrements sélectionnés
With rsImpressPlanning
' Boucle pour sélectionner l'image à imprimer
For intCompteur = 0 To 15
' Gestion de la fin de mois
If intCompteur + p_intCompteurImpressDeb > intFinMois Then
strPathImage = "/Images/imgVierge.jpg"
Else
' Ajout de 4 au compteur d'impression parce qu'il y a 4 champs dans la requête source avant le premier champ Activité
Select Case .Fields(intCompteur + p_intCompteurImpressDeb + 4)
Case Is = 1
strPathImage = "\Images\imgTravail.jpg"
Case Is = 2
strPathImage = "\Images\imgReposM.jpg"
Case Is = 3
strPathImage = "\Images\imgReposS.jpg"
Case Is = 4
strPathImage = "\Images\imgReposJ.jpg"
Case Is = 5
strPathImage = "\Images\imgCFA.jpg"
Case Is = 6
strPathImage = "\Images\imgAbsenceJ.jpg"
Case Is = 7
strPathImage = "\Images\imgAbsenceNJ.jpg"
Case Is = 8
strPathImage = "\Images\imgRecupM.jpg"
Case Is = 9
strPathImage = "\Images\imgRecupS.jpg"
Case Is = 10
strPathImage = "\Images\imgRecupJ.jpg"
Case Is = 11
strPathImage = "\Images\imgRecupMReposS.jpg"
Case Is = 12
strPathImage = "\Images\imgReposMRecupS.jpg"
Case Is = 13
strPathImage = "\Images\imgVacances.jpg"
Case Is = 14
strPathImage = "\Images\imgMaladie.jpg"
Case Is = 15
strPathImage = "\Images\imgRupture.jpg"
End Select
End If
' Implantation de l'image dans le contrôle
Me.Controls("img" & intCompteur + 1).Picture = CurrentProject.Path & strPathImage
Next
' Lecture de l'enregistrement suivant
.MoveNext
End With
End Sub

V - Conclusion

Voilà donc comment placer des images dans vos formulaires en mode continu (à partir de la version 2007) ou dans un état. Ceci n'est qu'un exemple, à vous de vous lancer maintenant.

VI - Remerciements

Merci à Arkham46 pour ses conseils techniques éclairés.
Merci à blade159 et jacques-jean pour leur relecture et leurs conseils orthographiques.
Je remercie également toute l'équipe Office de DVP qui est toujours présente lors de l'élaboration et la finalisation d'un nouveau projet (en particulier : Tofalu, Philippe JOCHMANS et User)

VII - Téléchargement

Téléchargement : Préparation d'un planning